diff options
Diffstat (limited to 'procfs/procfs_dir.c')
-rw-r--r-- | procfs/procfs_dir.c | 691 |
1 files changed, 79 insertions, 612 deletions
diff --git a/procfs/procfs_dir.c b/procfs/procfs_dir.c index bd1e49d6..c250aa48 100644 --- a/procfs/procfs_dir.c +++ b/procfs/procfs_dir.c @@ -1,667 +1,134 @@ -/* procfs -- a translator for providing GNU/Linux compatible - proc pseudo-filesystem +/* Hurd /proc filesystem, infrastructure for directories. + Copyright (C) 2010 Free Software Foundation, Inc. - procfs_dir.c -- This file contains definitions to perform - directory operations such as creating, - removing and refreshing directories. - - Copyright (C) 2008, FSF. - Written as a Summer of Code Project - + This file is part of the GNU Hurd. - procfs is free software; you can redistribute it and/or + The GNU Hurd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - procfs is distributed in the hope that it will be useful, but + The GNU Hurd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. - - A portion of the code in this file is based on ftpfs code - present in the hurd repositories copyrighted to FSF. The - Copyright notice from that file is given below. - - Copyright (C) 1997,98,2002 Free Software Foundation, Inc. - Written by Miles Bader <miles@gnu.org> - This file is part of the GNU Hurd. -*/ - - -#include <stdio.h> -#include <unistd.h> -#include <hurd/netfs.h> -#include <hurd/ihash.h> -#include <sys/stat.h> + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <stdlib.h> +#include <string.h> #include "procfs.h" +#include "procfs_dir.h" -/* Initial HASHTABLE length for the new directories to be created. */ -#define INIT_HTABLE_LEN 5 - -struct procfs_dir_entry **cur_entry; - -/* Return in DIR a new procfs directory, in the filesystem FS, - with node NODE and path PATH. */ -error_t procfs_dir_create (struct procfs *fs, struct node *node, - const char *path, struct procfs_dir **dir) +struct procfs_dir_node { - struct procfs_dir *new = malloc (sizeof (struct procfs_dir)); - if (!new) - return ENOMEM; - struct procfs_dir_entry **htable = calloc (INIT_HTABLE_LEN, - sizeof (struct procfs_dir_entry *)); - if (!htable) - return ENOMEM; - - /* Hold a reference to the new dir's node. */ - spin_lock (&netfs_node_refcnt_lock); - node->references++; - spin_unlock (&netfs_node_refcnt_lock); + const struct procfs_dir_ops *ops; + void *hook; +}; - new->num_entries = 0; - new->num_live_entries = 0; - new->htable_len = INIT_HTABLE_LEN; - new->htable = htable; - new->ordered = NULL; - new->fs_path = path; - new->fs = fs; - new->node = node; - new->stat_timestamp = 0; - new->name_timestamp = 0; - - *dir = new; - - if (fs->root != 0) - node->nn->dir = new; - - return 0; -} - -/* Put the directory entry DIR_ENTRY into the hash table HTABLE. */ -static void -insert (struct procfs_dir_entry *dir_entry, - struct procfs_dir_entry **htable, size_t htable_len) +static int +entry_exists (struct procfs_dir_node *dir, const struct procfs_dir_entry *ent) { - struct procfs_dir_entry **new_htable = &htable[dir_entry->hv % htable_len]; - if (*new_htable) - (*new_htable)->self_p = &dir_entry->next; - dir_entry->next = *new_htable; - dir_entry->self_p = new_htable; - *new_htable = dir_entry; -} + if (ent->ops.exists) + return ent->ops.exists (dir->hook, ent->hook); + if (dir->ops->entry_ops.exists) + return dir->ops->entry_ops.exists (dir->hook, ent->hook); -/* Calculate NAME's hash value. */ -static size_t -hash (const char *name) -{ - size_t hash_value = 0; - while (*name) - hash_value = ((hash_value << 5) + *name++) & 0xFFFFFF; - return hash_value; + return 1; } -/* Extend the existing hashtable for DIR to accomodate values for new length - NEW_LEN. We retain all the previous entries. */ static error_t -rehash (struct procfs_dir *dir, size_t new_len) +procfs_dir_get_contents (void *hook, char **contents, ssize_t *contents_len) { - int count; - size_t old_len = dir->htable_len; - struct procfs_dir_entry **old_htable = dir->htable; - struct procfs_dir_entry **new_htable = (struct procfs_dir_entry **) - malloc (new_len * sizeof (struct procfs_dir_entry *)); + static const char dot_dotdot[] = ".\0.."; + struct procfs_dir_node *dir = hook; + const struct procfs_dir_entry *ent; + int pos; - if (! new_htable) - return ENOMEM; - - bzero (new_htable, new_len * sizeof (struct procfs_dir_entry *)); - - for (count = 0; count < old_len; count++) - while (old_htable[count]) - { - struct procfs_dir_entry *dir_entry = old_htable[count]; - - /* Remove DIR_ENTRY from the old table */ - old_htable[count] = dir_entry->next; - - insert (dir_entry, new_htable, new_len); - } - - free (old_htable); - - dir->htable = new_htable; - dir->htable_len = new_len; - - return 0; -} - -/* Lookup NAME in DIR and return its entry. If there is no such entry, and - DNEW, the decision variable, is true, then a new entry is allocated and - returned, otherwise 0 is returned (if DNEW is true then 0 can be returned - if a memory allocation error occurs). */ -struct procfs_dir_entry * -lookup_entry (struct procfs_dir *dir, const char *name, int dnew) -{ - size_t hv = hash (name); - struct procfs_dir_entry *dir_entry = dir->htable[hv % dir->htable_len]; - - while (dir_entry && strcmp (name, dir_entry->name) != 0) - dir_entry = dir_entry->next; - - if (!dir_entry && dnew) - { - if (dir->num_entries > dir->htable_len) - /* Grow the hash table. */ - if (rehash (dir, (dir->htable_len + 1) * 2 - 1) != 0) - return 0; - - dir_entry = - (struct procfs_dir_entry *) malloc (sizeof (struct procfs_dir_entry)); - - if (dir_entry) - { - dir_entry->hv = hv; - dir_entry->name = strdup (name); - dir_entry->node = 0; - dir_entry->dir = dir; - dir_entry->stat_timestamp = 0; - bzero (&dir_entry->stat, sizeof dir_entry->stat); - dir_entry->symlink_target = 0; - dir_entry->noent = 0; - dir_entry->valid = 0; - dir_entry->name_timestamp = 0; - dir_entry->ordered_next = 0; - dir_entry->ordered_self_p = 0; - dir_entry->next = 0; - dir_entry->self_p = 0; - insert (dir_entry, dir->htable, dir->htable_len); - dir->num_entries++; - } - } + /* Evaluate how much space is needed. Note that we include the hidden + entries, just in case their status changes between now and then. */ + pos = sizeof dot_dotdot; + for (ent = dir->ops->entries; ent->name; ent++) + pos += strlen (ent->name) + 1; - return dir_entry; -} - - -/* Lookup NAME in DIR, returning its entry, or an error. - *NODE will contain the result node, locked, and with - an additional reference, or 0 if an error occurs. */ -error_t procfs_dir_lookup (struct procfs_dir *dir, const char *name, - struct node **node) -{ - struct procfs_dir_entry *dir_entry = 0; - error_t err = 0; - char *fs_path = dir->fs_path; - - struct timeval tv; - maptime_read (procfs_maptime, &tv); - - time_t timestamp = tv.tv_sec; - - if (*name == '\0' || strcmp (name, ".") == 0) - /* Current directory -- just add an additional reference to DIR's node - and return it. */ - { - netfs_nref (dir->node); - *node = dir->node; - return 0; - } - else if (strcmp (name, "..") == 0) - /* Parent directory. */ - { - if (dir->node->nn->dir_entry) - { - *node = dir->node->nn->dir_entry->dir->node; - mutex_lock (&(*node)->lock); - netfs_nref (*node); - } - else - { - err = ENOENT; /* No .. */ - *node = 0; - } - - mutex_unlock (&dir->node->lock); - - return err; - } - - err = procfs_dir_refresh (dir, dir->node == dir->fs->root); - if (!err && !dir_entry) - dir_entry = lookup_entry (dir, name, 0); - - if (! err) - { - if (dir_entry && !dir_entry->noent) - /* We've got a dir entry, get a node for it. */ - { - /* If there's already a node, add a ref so that it doesn't go - away. */ - spin_lock (&netfs_node_refcnt_lock); - if (dir_entry->node) - dir_entry->node->references++; - spin_unlock (&netfs_node_refcnt_lock); - - if (! dir_entry->node) - /* No node; make one and install it into E. */ - { - if (! fs_path) - err = EROFS; - - if (! err) - { - err = procfs_create_node (dir_entry, fs_path, &dir_entry->node); - - if (!err && dir->num_live_entries++ == 0) - /* Keep a reference to dir's node corresponding to - children. */ - { - spin_lock (&netfs_node_refcnt_lock); - dir->node->references++; - spin_unlock (&netfs_node_refcnt_lock); - } - } - } - - if (! err) - { - *node = dir_entry->node; - /* We have to unlock DIR's node before locking the child node - because the locking order is always child-parent. We know - the child node won't go away because we already hold the - additional reference to it. */ - mutex_unlock (&dir->node->lock); - mutex_lock (&dir_entry->node->lock); - } - } - else - err = ENOENT; - } - - if (err) - { - *node = 0; - mutex_unlock (&dir->node->lock); - } - -#if 0 - if (fs_path) - free (fs_path); -#endif - - return err; -} - -/* Lookup the null name in DIR, and return a node for it in NODE. Unlike - procfs_dir_lookup, this won't attempt to validate the existance of the - entry (to avoid opening a new connection if possible) -- that will happen - the first time the entry is refreshed. Also unlink ftpfs_dir_lookup, this - function doesn't expect DIR to be locked, and won't return *NODE locked. - This function is only used for bootstrapping the root node. */ -error_t -procfs_dir_null_lookup (struct procfs_dir *dir, struct node **node) -{ - struct procfs_dir_entry *dir_entry; - error_t err = 0; - - dir_entry = lookup_entry (dir, "", 1); - if (! dir_entry) + *contents = malloc (pos); + if (! *contents) return ENOMEM; - if (! dir_entry->noent) - /* We've got a dir entry, get a node for it. */ - { - /* If there's already a node, add a ref so that it doesn't go away. */ - spin_lock (&netfs_node_refcnt_lock); - if (dir_entry->node) - dir_entry->node->references++; - spin_unlock (&netfs_node_refcnt_lock); - - if (! dir_entry->node) - /* No node; make one and install it into DIR_ENTRY. */ - { - err = procfs_create_node (dir_entry, dir->fs_path, &dir_entry->node); - - if (!err && dir->num_live_entries++ == 0) - /* Keep a reference to dir's node corresponding to children. */ - { - spin_lock (&netfs_node_refcnt_lock); - dir->node->references++; - spin_unlock (&netfs_node_refcnt_lock); - } - } - - if (! err) - *node = dir_entry->node; - } - else - err = ENOENT; - - return err; -} - -/* Free the directory entry DIR_ENTRY and all resources it consumes. */ -void -free_entry (struct procfs_dir_entry *dir_entry) -{ - - assert (! dir_entry->self_p); /* We should only free deleted nodes. */ - free (dir_entry->name); - if (dir_entry->symlink_target) - free (dir_entry->symlink_target); - free (dir_entry->node->nn->dir); - free (dir_entry->node->nn); - free (dir_entry->node); - free (dir_entry); -} - -/* Remove DIR_ENTRY from its position in the ordered_next chain. */ -static void -ordered_unlink (struct procfs_dir_entry *dir_entry) -{ - if (dir_entry->ordered_self_p) - *dir_entry->ordered_self_p = dir_entry->ordered_next; - if (dir_entry->ordered_next) - dir_entry->ordered_next->self_p = dir_entry->ordered_self_p; -} - -/* Delete DIR_ENTRY from its directory, freeing any resources it holds. */ -static void -delete (struct procfs_dir_entry *dir_entry, struct procfs_dir *dir) -{ - dir->num_entries--; - - /* Take out of the hash chain. */ - if (dir_entry->self_p) - *dir_entry->self_p = dir_entry->next; - if (dir_entry->next) - dir_entry->next->self_p = dir_entry->self_p; - - /* Take out of the directory ordered list. */ - ordered_unlink (dir_entry); - - /* If there's a node attached, we'll delete the entry whenever it goes - away, otherwise, just delete it now. */ - if (! dir_entry->node) - free_entry (dir_entry); -} - -/* Make all the directory entries invalid */ -static void -make_dir_invalid (struct procfs_dir *dir) -{ - int count; - size_t len = dir->htable_len; - struct procfs_dir_entry **htable = dir->htable; - struct procfs_dir_entry *dir_entry; - - for (count = 0; count < len; count++) - { - dir_entry = htable[count]; - while (dir_entry) - { - dir_entry->valid = 0; - dir_entry = dir_entry->next; - } - } -} - -/* Delete any entries in DIR which don't have their valid bit set. */ -static void -sweep (struct procfs_dir *dir) -{ - size_t len = dir->htable_len, i; - struct procfs_dir_entry **htable = dir->htable, *dir_entry; - - for (i = 0; i < len; i++) - { - dir_entry = htable[i]; - while (dir_entry) - { - if (!dir_entry->valid && !dir_entry->noent && dir->num_entries) - delete (dir_entry, dir); - dir_entry = dir_entry->next; - } - if (htable[i]) - { - free (htable[i]); - htable[i] = 0; - } - - } - -} - -/* Remove the specified DIR and free all its allocated - storage. */ -void procfs_dir_entries_remove (struct procfs_dir *dir) -{ - /* Free all entries. */ - make_dir_invalid (dir); - sweep (dir); -} - -/* Checks if the DIR name is in list of - Active pids. */ -int is_in_pid_list (struct procfs_dir *dir) -{ - int dir_name; - int count; - pid_t *pids = NULL; - int pidslen = 0; - error_t err; - - if (dir->node->nn) + memcpy (*contents, dot_dotdot, sizeof dot_dotdot); + pos = sizeof dot_dotdot; + for (ent = dir->ops->entries; ent->name; ent++) { - dir_name = atoi (dir->node->nn->dir_entry->name); - err = proc_getallpids (getproc (), &pids, &pidslen); + if (! entry_exists (dir, ent)) + continue; - for (count = 0; count < pidslen; ++count) - if (pids[count] == dir_name) - return 1; + strcpy (*contents + pos, ent->name); + pos += strlen (ent->name) + 1; } + *contents_len = pos; return 0; - } -/* Checks if DIR is a directory that - represents a pid. */ -int check_parent (struct procfs_dir *dir) +static error_t +procfs_dir_lookup (void *hook, const char *name, struct node **np) { - if (dir == dir->fs->root) - return 0; - else - if (is_in_pid_list (dir)) - return 1; - else - return 0; + struct procfs_dir_node *dir = hook; + const struct procfs_dir_entry *ent; -} + for (ent = dir->ops->entries; ent->name && strcmp (name, ent->name); ent++); + if (! ent->name) + return ENOENT; -/* Refresh DIR. */ -error_t procfs_dir_refresh (struct procfs_dir *dir, int isroot) -{ - error_t err; - int is_parent_pid; - struct node *node; - - struct timeval tv; - maptime_read (procfs_maptime, &tv); - - time_t timestamp = tv.tv_sec; - cur_entry = &dir->ordered; - if (isroot) - err = procfs_fill_root_dir(dir, timestamp); + if (ent->ops.make_node) + *np = ent->ops.make_node (dir->hook, ent->hook); + else if (dir->ops->entry_ops.make_node) + *np = dir->ops->entry_ops.make_node (dir->hook, ent->hook); else - { - err = update_dir_entries (dir, timestamp); - is_parent_pid = check_parent (dir); - if (is_parent_pid) - err = procfs_create_files (dir, &node, timestamp); - } + return EGRATUITOUS; - return err; -} - -/* Update the directory entry for NAME to reflect STAT and SYMLINK_TARGET. - This also creates a valid linked list of entries imposing ordering on - them. */ -struct procfs_dir_entry* -update_entries_list (struct procfs_dir *dir, const char *name, - const struct stat *stat, time_t timestamp, - const char *symlink_target) -{ - ino_t ino; - struct procfs_dir_entry *dir_entry = lookup_entry (dir, name, 1); - struct procfs *fs = dir->fs; - - if (! dir_entry) + if (! *np) return ENOMEM; - if (dir_entry->stat.st_ino) - ino = dir_entry->stat.st_ino; - else - ino = fs->next_inode++; - - dir_entry->name_timestamp = timestamp; - - if (stat) - /* The ST and SYMLINK_TARGET parameters are only valid if ST isn't 0. */ - { - dir_entry->stat = *stat; - dir_entry->stat_timestamp = timestamp; - - if (!dir_entry->symlink_target || !symlink_target - || strcmp (dir_entry->symlink_target, symlink_target) != 0) - { - if (dir_entry->symlink_target) - free (dir_entry->symlink_target); - dir_entry->symlink_target = symlink_target ? strdup (symlink_target) : 0; - } - } - - /* The st_ino field is always valid. */ - dir_entry->stat.st_ino = ino; - dir_entry->stat.st_fsid = fs->fsid; - dir_entry->stat.st_fstype = PROCFILESYSTEM; - - dir_entry->valid = 1; - - if (! dir_entry->ordered_self_p) - /* Position DIR_ENTRY in the ordered chain following the previously seen entry. */ - { - /* The PREV_ENTRY_NEXT_P field holds a pointer to the NEXT-field of the - previous entry, or a pointer to the ORDERED field in the directory. */ - dir_entry->ordered_self_p = cur_entry; - - if (*dir_entry->ordered_self_p) - /* Update the self_p pointer of the previous successor. */ - (*dir_entry->ordered_self_p)->ordered_self_p = &dir_entry->ordered_next; - - /* DIR_ENTRY comes before the previous successor. */ - dir_entry->ordered_next = *dir_entry->ordered_self_p; + return 0; +} - *dir_entry->ordered_self_p = dir_entry; /* Put DIR_ENTRY there. */ - } +static void +procfs_dir_cleanup (void *hook) +{ + struct procfs_dir_node *dir = hook; - /* Put the next entry after this one. */ - cur_entry = &dir_entry->ordered_next; + if (dir->ops->cleanup) + dir->ops->cleanup (dir->hook); - return dir_entry; + free (dir); } -/* Fills DIR, the root directory with all the pids of - processes running in the system as directories. */ -error_t -procfs_fill_root_dir(struct procfs_dir *dir, time_t timestamp) +struct node * +procfs_dir_make_node (const struct procfs_dir_ops *dir_ops, void *dir_hook) { - error_t err; - char *data; - pid_t *pids; - int pidslen; - struct stat stat; - stat.st_mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | - S_IROTH | S_IXOTH; - stat.st_nlink = 1; - stat.st_size = 0; + static const struct procfs_node_ops ops = { + .get_contents = procfs_dir_get_contents, + .lookup = procfs_dir_lookup, + .cleanup_contents = procfs_cleanup_contents_with_free, + .cleanup = procfs_dir_cleanup, + }; + struct procfs_dir_node *dir; - int count; - char *dir_name_pid; - struct node *node; - struct procfs_dir *new_dir; - struct procfs_dir_entry *dir_entry; - struct proc_stat *ps; - - pids = NULL; - pidslen = 0; - err = proc_getallpids (getproc (), &pids, &pidslen); - - if (!err) + dir = malloc (sizeof *dir); + if (! dir) { - for (count = 0; count < pidslen; count++) - { - if (asprintf (&dir_name_pid, "%d", pids[count]) == -1) - return errno; - -#if 0 - node = (struct node *) malloc (sizeof (struct node)); - new_dir = (struct procfs_dir *) malloc (sizeof (struct procfs_dir )); - - if (! node || ! new_dir ) - return ENOMEM; -#endif - err = _proc_stat_create (pids[count], ps_context, &ps); - if (! err) - { - err = set_field_value (ps, PSTAT_PROC_INFO); - if (! err) - { - stat.st_uid = proc_stat_proc_info (ps)->owner; - stat.st_gid = proc_stat_proc_info (ps)->pgrp; - - dir_entry = update_entries_list (dir, dir_name_pid, - &stat, timestamp, NULL); - err = procfs_create_node (dir_entry, dir_name_pid, &node); + if (dir_ops->cleanup) + dir_ops->cleanup (dir_hook); - procfs_dir_create (dir->fs, node, - dir_name_pid, &new_dir); - free(dir_name_pid); - _proc_stat_free (ps); - } - } - } + return NULL; } - if ((err = procfs_create_uptime (dir, &node, timestamp)) != 0) - return err; + dir->ops = dir_ops; + dir->hook = dir_hook; - if ((err = procfs_create_stat (dir, &node, timestamp)) != 0) - return err; - - if ((err = procfs_create_version (dir, &node, timestamp)) != 0) - return err; - - if ((err = procfs_create_meminfo (dir, &node, timestamp)) != 0) - return err; - - if ((err = procfs_create_loadavg (dir, &node, timestamp)) != 0) - return err; - - if ((err = procfs_create_mounts (dir, &node, timestamp)) != 0) - return err; - - return 0; + return procfs_make_node (&ops, dir); } -error_t update_dir_entries (struct procfs_dir *dir) -{ - /* STUB */ - return 0; -} |