summaryrefslogtreecommitdiff
path: root/trans/magic.c
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>2003-03-06 02:24:53 +0000
committerRoland McGrath <roland@gnu.org>2003-03-06 02:24:53 +0000
commit63d9931e8ccf1ca11ddcbad2eb4eaf4f33153892 (patch)
tree56028695d409d019e0842ff6b416e26de6fe7f33 /trans/magic.c
parent3da5f3cdb060edb2194a3ecbb6334e71cd7aff48 (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/magic.c')
-rw-r--r--trans/magic.c359
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;
}