diff options
-rw-r--r-- | dircat.c | 99 | ||||
-rw-r--r-- | dircat.h | 9 | ||||
-rw-r--r-- | main.c | 41 | ||||
-rw-r--r-- | procfs.c | 8 | ||||
-rw-r--r-- | procfs.h | 8 | ||||
-rw-r--r-- | proclist.c | 1 |
6 files changed, 156 insertions, 10 deletions
diff --git a/dircat.c b/dircat.c new file mode 100644 index 00000000..857ba72b --- /dev/null +++ b/dircat.c @@ -0,0 +1,99 @@ +#include <stdlib.h> +#include <string.h> +#include "procfs.h" + +struct dircat_node +{ + struct node **dirs; +}; + +static error_t +dircat_get_contents (void *hook, void **contents, size_t *contents_len) +{ + struct dircat_node *dcn = hook; + int i, sz, pos; + error_t err; + + pos = 0; + *contents = malloc (sz = 512); + + for (i=0; dcn->dirs[i]; i++) + { + void *subcon; + size_t sublen; + + err = procfs_get_contents (dcn->dirs[i], &subcon, &sublen); + if (err) + { + free (*contents); + *contents = NULL; + return err; + } + + while (pos + sublen > sz) + *contents = realloc (*contents, sz *= 2); + + memcpy (*contents + pos, subcon, sublen); + pos += sublen; + } + + *contents_len = pos; + return 0; +} + +static error_t +dircat_lookup (void *hook, const char *name, struct node **np) +{ + struct dircat_node *dcn = hook; + error_t err; + int i; + + err = ENOENT; + for (i=0; err && dcn->dirs[i]; i++) + err = procfs_lookup (dcn->dirs[i], name, np); + + return err; +} + +static void +dircat_release_dirs (struct node **dirs) +{ + int i; + + for (i=0; dirs[i]; i++) + netfs_nrele (dirs[i]); +} + +static void +dircat_cleanup (void *hook) +{ + struct dircat_node *dcn = hook; + + dircat_release_dirs (dcn->dirs); + free (dcn); +} + +struct node * +dircat_make_node (struct node **dirs) +{ + static struct procfs_node_ops ops = { + .get_contents = dircat_get_contents, + .cleanup_contents = procfs_cleanup_contents_with_free, + .lookup = dircat_lookup, + .cleanup = dircat_cleanup, + /* necessary so that it propagates to proclist */ + .enable_refresh_hack_and_break_readdir = 1, + }; + struct dircat_node *dcn; + + dcn = malloc (sizeof *dcn); + if (! dcn) + { + dircat_release_dirs (dirs); + return NULL; + } + + dcn->dirs = dirs; + return procfs_make_node (&ops, dcn); +} + diff --git a/dircat.h b/dircat.h new file mode 100644 index 00000000..cb228526 --- /dev/null +++ b/dircat.h @@ -0,0 +1,9 @@ +/* Append the contents of multiple directories. DIRS is a + NULL-terminated array of directory nodes. One reference is consumed + for each of them, even on ENOMEM, in which case NULL is returned. + DIRS has to be static data for now, or at list remain available and + unchanged for the duration of the created node's life. Strange + things will happen if they have entries with the same name or if one + of them is not a directory. */ +struct node * +dircat_make_node (struct node **dirs); @@ -7,25 +7,46 @@ #include "procfs_file.h" #include "procfs_dir.h" #include "proclist.h" +#include "dircat.h" -static struct node *make_file (void *dir_hook, void *ent_hook) +static struct node * +make_file (void *dir_hook, void *ent_hook) { return procfs_file_make_node (ent_hook, -1, NULL); } -static struct node *make_proclist (void *dir_hook, void *ent_hook) +error_t +root_make_node (struct node **np) { - return proclist_make_node (getproc ()); + static const struct procfs_dir_entry static_entries[] = { + { "hello", make_file, "Hello, World!\n" }, + { "goodbye", make_file, "Goodbye, cruel World!\n" }, + }; + /* We never have two root nodes alive simultaneously, so it's ok to + have this as static data. */ + static struct node *root_dirs[3]; + + root_dirs[0] = procfs_dir_make_node (static_entries, NULL, NULL); + if (! root_dirs[0]) + return ENOMEM; + + root_dirs[1] = proclist_make_node (getproc ()); + if (! root_dirs[1]) + { + netfs_nrele (root_dirs[0]); + return ENOMEM; + } + + root_dirs[2] = NULL; + *np = dircat_make_node (root_dirs); + if (! *np) + return ENOMEM; + + return 0; } int main (int argc, char **argv) { - static const struct procfs_dir_entry entries[] = { - { "hello", make_file, "Hello, World!\n" }, - { "goodbye", make_file, "Goodbye, cruel World!\n" }, - { "proclist", make_proclist, }, - { } - }; mach_port_t bootstrap; argp_parse (&netfs_std_startup_argp, argc, argv, 0, 0, 0); @@ -35,7 +56,7 @@ int main (int argc, char **argv) error (1, 0, "Must be started as a translator"); netfs_init (); - netfs_root_node = procfs_dir_make_node (entries, NULL, NULL); + root_make_node (&netfs_root_node); netfs_startup (bootstrap, 0); for (;;) @@ -66,6 +66,14 @@ fail: error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) { + if (np->nn->ops->enable_refresh_hack_and_break_readdir && np->nn->contents) + { + if (np->nn->ops->cleanup_contents) + np->nn->ops->cleanup_contents (np->nn->hook, np->nn->contents, + np->nn->contents_len); + np->nn->contents = NULL; + } + if (! np->nn->contents && np->nn->ops->get_contents) { void *contents; @@ -27,6 +27,14 @@ struct procfs_node_ops /* Destroy this node. */ void (*cleanup) (void *hook); + + /* FIXME: This is needed because the root node is persistent, and we + want the list of processes to be updated. However, this means that + readdir() on the root node runs the risk of returning incoherent + results if done in multiple runs and processes are added/removed in + the meantime. The right way to fix this is probably to add a + getroot() user hook function to libnetfs. */ + int enable_refresh_hack_and_break_readdir; }; /* These helper functions can be used as procfs_node_ops.cleanup_contents. */ @@ -74,6 +74,7 @@ proclist_make_node (process_t process) .lookup = proclist_lookup, .cleanup_contents = procfs_cleanup_contents_with_free, .cleanup = free, + .enable_refresh_hack_and_break_readdir = 1, }; struct proclist_node *pl; |