commit-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

hurd/trans magic.c


From: Roland McGrath
Subject: hurd/trans magic.c
Date: Wed, 05 Mar 2003 21:24:53 -0500

CVSROOT:        /cvsroot/hurd
Module name:    hurd
Changes by:     Roland McGrath <address@hidden> 03/03/05 21:24:53

Modified files:
        trans          : magic.c 

Log message:
        2003-03-05  Roland McGrath  <address@hidden>
        
        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.

CVSWeb URLs:
http://savannah.gnu.org/cgi-bin/viewcvs/hurd/hurd/trans/magic.c.diff?tr1=1.16&tr2=1.17&r1=text&r2=text

Patches:
Index: hurd/trans/magic.c
diff -u hurd/trans/magic.c:1.16 hurd/trans/magic.c:1.17
--- hurd/trans/magic.c:1.16     Sat Jun 15 18:37:16 2002
+++ hurd/trans/magic.c  Wed Mar  5 21:24:53 2003
@@ -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 @@
 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 @@
 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 @@
               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 @@
       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 @@
 
   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 @@
   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 @@
 }
 
 
+#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 @@
 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 @@
   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;
+}
+
+
+/* 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);
 
-      trivfs_open_hook = &magic_open;
+  /* 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;
 }




reply via email to

[Prev in Thread] Current Thread [Next in Thread]