diff options
author | Roland McGrath <roland@gnu.org> | 2003-03-06 02:24:53 +0000 |
---|---|---|
committer | Roland McGrath <roland@gnu.org> | 2003-03-06 02:24:53 +0000 |
commit | 63d9931e8ccf1ca11ddcbad2eb4eaf4f33153892 (patch) | |
tree | 56028695d409d019e0842ff6b416e26de6fe7f33 /trans | |
parent | 3da5f3cdb060edb2194a3ecbb6334e71cd7aff48 (diff) |
2003-03-05 Roland McGrath <roland@frob.com>
Make /hurd/magic support translator delegation via /servers/magic.
* magic.c (struct magic): New structure.
(magic, directory, dirbuf, dirbufsize): Variables removed,
not members of struct magic.
(trivfs_modify_stat, magic_getroot, trivfs_S_dir_readdir): Use a
struct magic hanging off CNTL->hook rather than global variables.
(trivfs_append_args): Likewise.
(trivfs_S_dir_lookup): Likewise. Let magic string be null to indicate
prepending nothing in --directory mode.
(parse_opt): Use struct magic in STATE->input instead of globals.
If no argument, turn on --directory and leave M->magic null.
(argp): New static variable, instead of auto in main.
(main): Set both trivfs hook functions unconditionally.
Use a struct magic, pass it to argp_parse and store it in FSYS->hook.
Move directory buffer setup to ...
(parse_opt): ... here, do it on ARGP_KEY_SUCCESS.
(magic_open): Track users with a count.
(magic_protid_destroy): New function to track users.
(main): Set trivfs_protid_destroy_hook to that.
(trivfs_goaway): Call ports_destroy_right if FSYS has no users.
(all_fsys): New variable, list of all filesystems.
(trivfs_clean_cntl): New function, exit if no filesystems left.
(main): Put FSYS on that list. On timeout with no RPCs, try
trivfs_goaway on each filesystem on the list.
(trivfs_S_fsys_forward): New function.
(delegate): New variable.
(options): New option -U/--use-server to set it.
Diffstat (limited to 'trans')
-rw-r--r-- | trans/magic.c | 359 |
1 files changed, 272 insertions, 87 deletions
diff --git a/trans/magic.c b/trans/magic.c index 2febff68..2e47f911 100644 --- a/trans/magic.c +++ b/trans/magic.c @@ -1,6 +1,6 @@ /* A translator for returning FS_RETRY_MAGIC strings. - Copyright (C) 1999,2001,02 Free Software Foundation, Inc. + Copyright (C) 1999,2001,02, 03 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -19,6 +19,7 @@ #include <hurd.h> #include <hurd/ports.h> #include <hurd/trivfs.h> +#include <hurd/fshelp.h> #include <hurd/fsys.h> #include <version.h> @@ -36,23 +37,38 @@ #include <assert.h> const char *argp_program_version = STANDARD_HURD_VERSION (magic); -static char args_doc[] = "MAGIC"; -static char doc[] = "A translator that returns the magic retry result MAGIC."; -static const struct argp_option options[] = + +/* This structure has all the state about one filesystem. + It hangs off trivfs_control->hook. */ +struct magic { - {"directory", 'd', 0, 0, "Provide virtual (empty) directory node"}, - {0} -}; + /* We chain all filesystems together so we can tell easily when they are + all unused. */ + struct trivfs_control *next; + + /* The magic string we return for lookups. */ + char *magic; -/* The magic string we return for lookups. */ -static char *magic; + int directory; /* --directory flag */ -static int directory; /* --directory flag */ + /* Pre-fab contents of dummy directory for dir_readdir. + Set up only under --directory. */ + void *dirbuf; + size_t dirbufsize; -/* Pre-fab contents of dummy directory for dir_readdir. - Set up only under --directory. */ -static void *dirbuf; -static size_t dirbufsize; + unsigned int nusers; /* Count of users, only with --directory. */ +}; + +static inline void +free_magic (struct magic *m) +{ + free (m->magic); + if (m->dirbuf) + munmap (m->dirbuf, m->dirbufsize); + free (m); +} + +static struct trivfs_control *all_fsys; /* Trivfs hooks */ @@ -68,7 +84,9 @@ int trivfs_allow_open = O_READ; void trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st) { - st->st_size = dirbufsize; + struct magic *const m = cred->po->cntl->hook; + + st->st_size = m->dirbufsize; st->st_blocks = getpagesize () / S_BLKSIZE; st->st_mode = ((st->st_mode & ~S_IFMT & ~ALLPERMS) @@ -79,9 +97,45 @@ trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st) error_t trivfs_goaway (struct trivfs_control *fsys, int flags) { - exit (0); + struct magic *const m = fsys->hook; + + /* We are single-threaded, so no fancy stuff is needed here. */ + + if (m->nusers > 0 && !(flags & FSYS_GOAWAY_FORCE)) + return EBUSY; + + /* No more communication with the parent filesystem. + This running RPC should now be the only ref keeping FSYS alive. */ + ports_destroy_right (fsys); + return 0; } + +/* Clean pointers in a struct trivfs_control when its last reference + vanishes before it's freed. This overrides the libtrivfs version + so we can clean up our hook data. */ +void +trivfs_clean_cntl (void *arg) +{ + struct trivfs_control *cntl = arg; + struct magic *const m = cntl->hook; + + /* Remove us from the list of all filesystems. */ + struct trivfs_control **lastp = &all_fsys; + while (*lastp != cntl) + lastp = &((struct magic *) (*lastp)->hook)->next; + *lastp = m->next; + + if (all_fsys == 0) + /* Nothing more to do in this life. */ + exit (0); + + mach_port_destroy (mach_task_self (), cntl->filesys_id); + mach_port_destroy (mach_task_self (), cntl->file_id); + mach_port_deallocate (mach_task_self (), cntl->underlying); + + free_magic (m); +} /* This hook is used when running without --directory; it circumvents basically all the trivfs machinery. */ @@ -96,7 +150,12 @@ magic_getroot (struct trivfs_control *cntl, retry_type *do_retry, char *retry_name, mach_port_t *node, mach_msg_type_name_t *node_type) { - strcpy (retry_name, magic); + struct magic *const m = cntl->hook; + + if (m->directory) + return EAGAIN; /* Do normal trivfs getroot processing. */ + + strcpy (retry_name, m->magic); *do_retry = FS_RETRY_MAGICAL; *node = MACH_PORT_NULL; *node_type = MACH_MSG_TYPE_COPY_SEND; @@ -125,10 +184,20 @@ magic_open (struct trivfs_control *cntl, assert_perror (err); err = mach_port_deallocate (mach_task_self (), dotdot); assert_perror (err); + struct magic *const m = cntl->hook; + m->nusers++; } return err; } +/* We have this hook only for simple tracking of the live user ports. */ +static void +magic_protid_destroy (struct trivfs_protid *cred) +{ + struct magic *const m = cred->po->cntl->hook; + m->nusers--; +} + /* Do a directory lookup. */ @@ -154,44 +223,49 @@ trivfs_S_dir_lookup (struct trivfs_protid *cred, if (name[0] != '\0') { - if (!directory) + struct magic *const m = cred->po->cntl->hook; + + if (!m->directory) return ENOTDIR; - else - { - /* We have a real lookup in the dummy directory. - Handle `.' and `..' specially, and anything else - gets redirected to the magical retry. */ + /* We have a real lookup in the dummy directory. + Handle `.' and `..' specially, and anything else + gets redirected to the magical retry. */ + + while (*name == '/') + ++name; + while (!strncmp (name, "./", 2)) + { + name += 2; while (*name == '/') ++name; - while (!strncmp (name, "./", 2)) - { - name += 2; - while (*name == '/') - ++name; - } + } - if (!strcmp (name, "..") || !strncmp (name, "../", 3)) - { - name += 2; - while (*name == '/') - ++name; - strcpy (retry_name, name); - *retry_type = FS_RETRY_REAUTH; - *retrypt = (mach_port_t) cred->po->hook; - *retrypt_type = MACH_MSG_TYPE_COPY_SEND; - return 0; - } - else if (name[0] != '\0' && strcmp (name, ".")) + if (!strcmp (name, "..") || !strncmp (name, "../", 3)) + { + name += 2; + while (*name == '/') + ++name; + strcpy (retry_name, name); + *retry_type = FS_RETRY_REAUTH; + *retrypt = (mach_port_t) cred->po->hook; + *retrypt_type = MACH_MSG_TYPE_COPY_SEND; + return 0; + } + else if (name[0] != '\0' && strcmp (name, ".")) + { + if (m->magic == 0) + strcpy (retry_name, name); + else { - char *p = stpcpy (retry_name, magic); + char *p = stpcpy (retry_name, m->magic); *p++ = '/'; strcpy (p, name); - *retry_type = FS_RETRY_MAGICAL; - *retrypt = MACH_PORT_NULL; - *retrypt_type = MACH_MSG_TYPE_COPY_SEND; - return 0; } + *retry_type = FS_RETRY_MAGICAL; + *retrypt = MACH_PORT_NULL; + *retrypt_type = MACH_MSG_TYPE_COPY_SEND; + return 0; } } @@ -253,23 +327,25 @@ trivfs_S_dir_readdir (struct trivfs_protid *cred, if (!cred) return EOPNOTSUPP; + struct magic *const m = cred->po->cntl->hook; + if (entry > 0) { void *p; int i; i = 0; - for (p = dirbuf; p < dirbuf + dirbufsize; + for (p = m->dirbuf; p < m->dirbuf + m->dirbufsize; p += ((struct dirent *) p)->d_reclen) if (i++ == entry) break; *data = p; - *datalen = dirbuf + dirbufsize - p; + *datalen = m->dirbuf + m->dirbufsize - p; *amount = 2 - entry; } else { - *data = dirbuf; - *datalen = dirbufsize; + *data = m->dirbuf; + *datalen = m->dirbufsize; *amount = 2; } @@ -278,32 +354,71 @@ trivfs_S_dir_readdir (struct trivfs_protid *cred, } +#include <hurd/paths.h> +#define _SERVERS_MAGIC _SERVERS "magic" + +/* To whom should we try to delegate on startup? */ +static const char *delegate = _SERVERS_MAGIC; + +static const struct argp_option options[] = +{ + {"directory", 'd', 0, 0, "Provide virtual (empty) directory node"}, + {"use-server", 'U', "NAME", 0, + "Delegate to server NAME instead of " _SERVERS_MAGIC}, + {0} +}; static error_t parse_opt (int opt, char *arg, struct argp_state *state) { + struct magic *const m = state->input; + switch (opt) { - case 'd': - directory = 1; + case 'U': + /* This is only valid for the startup options, not delegates. */ + if (all_fsys != 0) + return EINVAL; + delegate = arg; return 0; + case 'd': case ARGP_KEY_NO_ARGS: - argp_usage (state); - return EINVAL; + m->directory = 1; + return 0; - case ARGP_KEY_ARGS: - if (state->next != state->argc - 1) + case ARGP_KEY_ARG: + if (m->magic != 0) { argp_usage (state); return EINVAL; } - else + m->magic = strdup (arg); + return m->magic == 0 ? ENOMEM : 0; + + case ARGP_KEY_SUCCESS: + if (m->directory) { - magic = state->argv[state->next]; - return 0; + inline struct dirent *add (struct dirent *d, const char *name) + { + d->d_fileno = 2; /* random */ + d->d_type = DT_DIR; + d->d_namlen = strlen (name); + strcpy (d->d_name, name); + d->d_name[d->d_namlen] = '\0'; + d->d_reclen = &d->d_name[d->d_namlen + 1] - (char *) d; + d->d_reclen = ((d->d_reclen + __alignof (struct dirent) - 1) + & ~(__alignof (struct dirent) - 1)); + return (struct dirent *) ((char *) d + d->d_reclen); + } + struct dirent *d; + m->dirbuf = mmap (0, getpagesize (), PROT_READ|PROT_WRITE, + MAP_ANON, 0, 0); + d = add (m->dirbuf, "."); + d = add (d, ".."); + m->dirbufsize = (char *) d - (char *) m->dirbuf; } - break; + return 0; } return ARGP_ERR_UNKNOWN; @@ -313,9 +428,16 @@ error_t trivfs_append_args (struct trivfs_control *fsys, char **argz, size_t *argz_len) { - return ((directory ? argz_add (argz, argz_len, "--directory") : 0) - ?: argz_add (argz, argz_len, magic)); + struct magic *const m = fsys->hook; + return ((m->directory ? argz_add (argz, argz_len, "--directory") : 0) + ?: (m->magic ? argz_add (argz, argz_len, m->magic) : 0)); } + +static struct argp argp = + { + options, parse_opt, "MAGIC", + "A translator that returns the magic retry result MAGIC." + }; int main (int argc, char **argv) @@ -323,48 +445,111 @@ main (int argc, char **argv) error_t err; mach_port_t bootstrap; struct trivfs_control *fsys; - struct argp argp = { options, parse_opt, args_doc, doc }; + struct magic *m = calloc (1, sizeof *m); - argp_parse (&argp, argc, argv, 0, 0, 0); + argp_parse (&argp, argc, argv, 0, 0, m); task_get_bootstrap_port (mach_task_self (), &bootstrap); if (bootstrap == MACH_PORT_NULL) error (1, 0, "Must be started as a translator"); + if (delegate != 0) + { + /* First, try to have the canonical server sitting on /servers/magic + take over for us. */ + err = fshelp_delegate_translation (delegate, bootstrap, argv); + if (err == 0) + return 0; + } + + /* Nope, we are doing it ourselves. */ + + trivfs_getroot_hook = &magic_getroot; + trivfs_open_hook = &magic_open; + trivfs_protid_destroy_hook = &magic_protid_destroy; + /* Reply to our parent */ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys); mach_port_deallocate (mach_task_self (), bootstrap); if (err) error (3, err, "Contacting parent"); + fsys->hook = m; + all_fsys = fsys; - if (directory) + /* Launch. */ + while (1) { - inline struct dirent *add (struct dirent *d, const char *name) + ports_manage_port_operations_one_thread (fsys->pi.bucket, trivfs_demuxer, + 10 * 60 * 1000); + + /* That returns when 10 minutes pass without an RPC. Try shutting down + as if sent fsys_goaway; if we have any users who need us to stay + around, this returns EBUSY and we loop to service more RPCs. */ + + struct trivfs_control *fs = all_fsys; + do { - d->d_fileno = 2; /* random */ - d->d_type = DT_DIR; - d->d_namlen = strlen (name); - strcpy (d->d_name, name); - d->d_name[d->d_namlen] = '\0'; - d->d_reclen = &d->d_name[d->d_namlen + 1] - (char *) d; - d->d_reclen = ((d->d_reclen + __alignof (struct dirent) - 1) - & ~(__alignof (struct dirent) - 1)); - return (struct dirent *) ((char *) d + d->d_reclen); - } - struct dirent *d; - dirbuf = mmap (0, getpagesize (), PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); - d = add (dirbuf, "."); - d = add (d, ".."); - dirbufsize = (char *) d - (char *) dirbuf; + struct magic *const m = fs->hook; + struct trivfs_control *const next = m->next; + trivfs_goaway (fs, 0); + fs = next; + } while (fs != 0); + } + + return 0; +} + - trivfs_open_hook = &magic_open; +/* Handle delegated filesystems. */ +error_t +trivfs_S_fsys_forward (mach_port_t server, + mach_port_t reply, + mach_msg_type_name_t replytype, + mach_port_t requestor, + char *argz, size_t argz_len) +{ + struct trivfs_protid *cred + = ports_lookup_port (all_fsys->pi.bucket, server, + trivfs_protid_portclasses[0]); + if (!cred) + return EOPNOTSUPP; + ports_port_deref (cred); + + /* Allocate a new structure for parameters, and parse the arguments + to fill it in. */ + struct magic *m = calloc (1, sizeof *m); + if (!m) + return ENOMEM; + + int argc = argz_count (argz, argz_len); + char *argv[argc + 1]; + argz_extract (argz, argz_len, argv); + error_t err = argp_parse (&argp, argc, argv, + ARGP_NO_ERRS | ARGP_NO_HELP, 0, m); + if (err) + { + free_magic (m); + return err; } - else - trivfs_getroot_hook = &magic_getroot; - /* Launch. */ - ports_manage_port_operations_one_thread (fsys->pi.bucket, trivfs_demuxer, - 2 * 60 * 1000); + /* Now we are ready to start up the filesystem. Contact the parent. */ + struct trivfs_control *fsys; + err = trivfs_startup (requestor, 0, + trivfs_cntl_portclasses[0], all_fsys->pi.bucket, + trivfs_protid_portclasses[0], all_fsys->pi.bucket, + &fsys); + if (err) + { + free_magic (m); + return err; + } + mach_port_deallocate (mach_task_self (), requestor); + + /* The new filesystem is all hooked up. + Put it on the list of all filesystems we are serving. */ + m->next = all_fsys; + fsys->hook = m; + all_fsys = fsys; return 0; } |