commit-hurd
[Top][All Lists]
Advanced

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

[hurd] 01/04: New upstream snapshot


From: Samuel Thibault
Subject: [hurd] 01/04: New upstream snapshot
Date: Mon, 02 May 2016 23:48:18 +0000

This is an automated email from the git hooks/post-receive script.

sthibault pushed a commit to branch master
in repository hurd.

commit 6f85231adcfd0724a63f7035ac9aafb0ea04d964
Author: Samuel Thibault <address@hidden>
Date:   Mon May 2 23:06:01 2016 +0000

    New upstream snapshot
---
 NEWS                                       |  19 ++
 console/console.c                          |  64 +++---
 doc/hurd.texi                              |   4 +-
 ext2fs/ext2_fs_i.h                         |   1 -
 ext2fs/inode.c                             |  20 +-
 ftpfs/dir.c                                |  27 +--
 ftpfs/node.c                               |   8 +-
 hostmux/mux.c                              |   6 +-
 hurd/hurd_types.defs                       |  20 +-
 isofs/isofs.h                              |   1 +
 isofs/main.c                               |   9 +-
 isofs/pager.c                              |   2 +
 libdiskfs/boot-start.c                     |   3 +-
 libdiskfs/io-seek.c                        |   8 +
 libdiskfs/rdwr-internal.c                  |   7 +-
 libfshelp/fetch-root.c                     |   2 +-
 libfshelp/start-translator-long.c          |   3 +-
 libihash/ihash.c                           |  26 ++-
 libihash/ihash.h                           |  11 +
 libnetfs/dir-mkfile.c                      |   2 +-
 libnetfs/drop-node.c                       |   1 -
 libnetfs/init-init.c                       |   2 -
 libnetfs/make-node.c                       |   2 +-
 libnetfs/netfs.h                           |  42 ++--
 libnetfs/nput.c                            |  27 ++-
 libnetfs/nref.c                            |  10 +-
 libnetfs/nrele.c                           |  37 +++-
 libpager/demuxer.c                         |   1 +
 libshouldbeinlibc/Makefile                 |   2 +
 libshouldbeinlibc/assert-backtrace.c       |  79 ++++++++
 libshouldbeinlibc/assert-backtrace.h       |  60 ++++++
 libshouldbeinlibc/refcount.h               |  41 ++--
 libtrivfs/fsys-getroot.c                   |  16 +-
 libtrivfs/io-reauthenticate.c              |   3 +-
 libtrivfs/io-restrict-auth.c               |   4 +-
 libtrivfs/open.c                           |   2 +-
 libtrivfs/priv.h                           |   9 +
 libtrivfs/times.c                          |  22 +-
 libtrivfs/trivfs.h                         |   3 +-
 login/utmp.c                               |   2 +-
 mach-defpager/default_pager.c              |   6 +-
 nfs/cache.c                                |  40 ++--
 nfs/ops.c                                  |   5 +-
 pfinet/linux-src/include/linux/ext2_fs_i.h |   1 -
 pfinet/main.c                              |   1 -
 procfs/netfs.c                             |   4 -
 procfs/rootdir.c                           |   4 +-
 startup/Makefile                           |   9 +-
 startup/startup.c                          | 310 ++++++++++++++++-------------
 sutils/Makefile                            |   2 +-
 sutils/swapon.c                            |  10 +-
 trans/fakeroot.c                           |  45 +++--
 trans/ifsock.c                             |   2 +-
 trans/proxy-defpager.c                     |   2 -
 usermux/mux.c                              |   6 +-
 utils/settrans.c                           |  68 ++++++-
 56 files changed, 753 insertions(+), 370 deletions(-)

diff --git a/NEWS b/NEWS
index 413f249..091840c 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,22 @@
+Version 0.8 (2016-04-XX)
+
+The netfs library is using the lockless reference-counting primitives
+for both peropen and node objects now, and the global reference
+counting lock has been removed.
+
+The integer hashing library gained a new interface to use non-integer
+keys.  It is now used in libdiskfs' and nfs' node cache, and the ftpfs
+translator.
+
+Several bugs in our native fakeroot tool have been fixed improving
+stability and correctness of the translation.
+
+The devnode translator and the 'hurd-slab' library have been merged.
+
+The code has been cleaned up, and we fixed numerous bugs, most notably
+a crash in pfinet, a locking bug in libdiskfs, and an out-of-bounds
+access in ext2fs' block cache.
+
 Version 0.7 (2015-10-31)
 
 The node cache in ext2fs has been improved, generalized, and moved to
diff --git a/console/console.c b/console/console.c
index 57ae813..9c5869d 100644
--- a/console/console.c
+++ b/console/console.c
@@ -415,47 +415,51 @@ new_node (struct node **np, vcons_t vcons, 
vcons_node_type type)
 
 /* Node management.  */
 
-/* Node NP has no more references; free all its associated
-   storage.  */
+/* We need to drop the soft references on NP.  */
 void
-netfs_node_norefs (struct node *np)
+netfs_try_dropping_softrefs (struct node *np)
 {
   vcons_t vcons = np->nn->vcons;
+  int release = FALSE;
 
-  /* The root node does never go away.  */
-  assert (!np->nn->cons && np->nn->vcons);
-
-  /* Avoid deadlock.  */
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
-
-  /* Find the back reference to ourself in the virtual console
-     structure, and delete it.  */
   pthread_mutex_lock (&vcons->lock);
-  pthread_spin_lock (&netfs_node_refcnt_lock);
-  if (np->references)
+  if (np == vcons->dir_node)
     {
-      /* Someone else got a reference while we were attempting to go
-        away.  This can happen in netfs_attempt_lookup.  In this
-        case, just unlock the node and do nothing else.  */
-      pthread_mutex_unlock (&vcons->lock);
-      pthread_mutex_unlock (&np->lock);
-      return;
+      release = TRUE;
+      vcons->dir_node = 0;
     }
-  if (np == vcons->dir_node)
-    vcons->dir_node = 0;
   else if (np == vcons->cons_node)
-    vcons->cons_node = 0;
+    {
+      release = TRUE;
+      vcons->cons_node = 0;
+    }
   else if (np == vcons->disp_node)
-    vcons->disp_node = 0;
-  else
     {
-      assert (np == vcons->inpt_node);
+      release = TRUE;
+      vcons->disp_node = 0;
+    }
+  else if (np == vcons->inpt_node)
+    {
+      release = TRUE;
       vcons->inpt_node = 0;
     }
+  if (release)
+    netfs_nrele_light (np);
   pthread_mutex_unlock (&vcons->lock);
 
   /* Release our reference.  */
-  vcons_release (vcons);
+  if (release)
+    vcons_release (vcons);
+
+}
+
+/* Node NP has no more references; free all its associated
+   storage.  */
+void
+netfs_node_norefs (struct node *np)
+{
+  /* The root node does never go away.  */
+  assert (!np->nn->cons && np->nn->vcons);
 
   free (np->nn);
   free (np);
@@ -634,7 +638,10 @@ netfs_attempt_lookup (struct iouser *user, struct node 
*dir,
             the virtual console.  */
          err = new_node (node, vcons, VCONS_NODE_DIR);
          if (!err)
-           vcons->dir_node = *node;
+            {
+              vcons->dir_node = *node;
+              netfs_nref_light (*node);
+            }
          else
            release_vcons = 1;
        }
@@ -663,6 +670,7 @@ netfs_attempt_lookup (struct iouser *user, struct node *dir,
              if (!err)
                {
                  vcons->cons_node = *node;
+                  netfs_nref_light (*node);
                  ref_vcons = 1;
                }
            }
@@ -682,6 +690,7 @@ netfs_attempt_lookup (struct iouser *user, struct node *dir,
              if (!err)
                {
                  vcons->disp_node = *node;
+                  netfs_nref_light (*node);
                  ref_vcons = 1;
                }
            }
@@ -701,6 +710,7 @@ netfs_attempt_lookup (struct iouser *user, struct node *dir,
              if (!err)
                {
                  vcons->inpt_node = *node;
+                  netfs_nref_light (*node);
                  ref_vcons = 1;
                }
            }
diff --git a/doc/hurd.texi b/doc/hurd.texi
index 525bfa7..2bcf561 100644
--- a/doc/hurd.texi
+++ b/doc/hurd.texi
@@ -2284,7 +2284,9 @@ and functions:
 @deftypevarx {extern int} trivfs_fsid
 These variables are returned in the @var{st_fstype} and @var{st_fsid}
 fields of @code{struct stat}.  @var{trivfs_fstype} should be chosen
-from the @code{FSTYPE_*} constants found in @code{<hurd/hurd_types.h>}.
+from the @code{FSTYPE_*} constants found in
address@hidden<hurd/hurd_types.h>}.  If @code{trivfs_fsid} is zero, trivfs
+will use the pid of the translator instead.
 @end deftypevar
 
 @deftypevar {extern int} trivfs_allow_open
diff --git a/ext2fs/ext2_fs_i.h b/ext2fs/ext2_fs_i.h
index 72bcd5c..eefdbfa 100644
--- a/ext2fs/ext2_fs_i.h
+++ b/ext2fs/ext2_fs_i.h
@@ -35,7 +35,6 @@ struct ext2_inode_info {
        __u32   i_next_alloc_goal;
        __u32   i_prealloc_block;
        __u32   i_prealloc_count;
-       __u32   i_high_size;
        int     i_new_inode:1;  /* Is a freshly allocated inode */
 };
 
diff --git a/ext2fs/inode.c b/ext2fs/inode.c
index d83bedc..ccc8d69 100644
--- a/ext2fs/inode.c
+++ b/ext2fs/inode.c
@@ -198,13 +198,18 @@ diskfs_user_read_node (struct node *np, struct 
lookup_context *ctx)
   else
     {
       info->i_dir_acl = 0;
-      info->i_high_size = di->i_size_high;
-      if (info->i_high_size)   /* XXX */
+      if (sizeof (off_t) >= 8)
+       /* 64bit file size */
+       st->st_size += ((off_t) di->i_size_high) << 32;
+      else
        {
-         dino_deref (di);
-         ext2_warning ("cannot handle large file inode %Ld", np->cache_id);
-         diskfs_end_catch_exception ();
-         return EFBIG;
+         if (di->i_size_high)  /* XXX */
+           {
+             dino_deref (di);
+             ext2_warning ("cannot handle large file inode %Ld", np->cache_id);
+             diskfs_end_catch_exception ();
+             return EFBIG;
+           }
        }
     }
   info->i_block_group = inode_group_num (np->cache_id);
@@ -426,6 +431,9 @@ write_node (struct node *np)
        {
          di->i_dtime = 0;
          di->i_size = st->st_size;
+         if (sizeof (off_t) >= 8 && !S_ISDIR (st->st_mode))
+           /* 64bit file size */
+           di->i_size_high = st->st_size >> 32;
          di->i_blocks = st->st_blocks;
        }
 
diff --git a/ftpfs/dir.c b/ftpfs/dir.c
index 733a2dc..2ea29b5 100644
--- a/ftpfs/dir.c
+++ b/ftpfs/dir.c
@@ -654,10 +654,8 @@ ftpfs_dir_lookup (struct ftpfs_dir *dir, const char *name,
        {
          /* If there's already a node, add a ref so that it doesn't go
              away.  */
-         pthread_spin_lock (&netfs_node_refcnt_lock);
-         if (e->node)
-           e->node->references++;
-         pthread_spin_unlock (&netfs_node_refcnt_lock);
+          if (e->node)
+            netfs_nref (e->node);
 
          if (! e->node)
            /* No node; make one and install it into E.  */
@@ -682,11 +680,7 @@ ftpfs_dir_lookup (struct ftpfs_dir *dir, const char *name,
                  if (!err && dir->num_live_entries++ == 0)
                    /* Keep a reference to dir's node corresponding to
                       children.  */
-                   {
-                     pthread_spin_lock (&netfs_node_refcnt_lock);
-                     dir->node->references++;
-                     pthread_spin_unlock (&netfs_node_refcnt_lock);
-                   }
+                    netfs_nref (dir->node);
                }
            }
 
@@ -737,10 +731,8 @@ ftpfs_dir_null_lookup (struct ftpfs_dir *dir, struct node 
**node)
     /* 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.  */
-      pthread_spin_lock (&netfs_node_refcnt_lock);
       if (e->node)
-       e->node->references++;
-      pthread_spin_unlock (&netfs_node_refcnt_lock);
+        netfs_nref (e->node);
 
       if (! e->node)
        /* No node; make one and install it into E.  */
@@ -749,11 +741,7 @@ ftpfs_dir_null_lookup (struct ftpfs_dir *dir, struct node 
**node)
 
          if (!err && dir->num_live_entries++ == 0)
            /* Keep a reference to dir's node corresponding to children.  */
-           {
-             pthread_spin_lock (&netfs_node_refcnt_lock);
-             dir->node->references++;
-             pthread_spin_unlock (&netfs_node_refcnt_lock);
-           }
+            netfs_nref (dir->node);
        }
 
       if (! err)
@@ -783,10 +771,7 @@ ftpfs_dir_create (struct ftpfs *fs, struct node *node, 
const char *rmt_path,
       return ENOMEM;
     }
 
-  /* Hold a reference to the new dir's node.  */
-  pthread_spin_lock (&netfs_node_refcnt_lock);
-  node->references++;
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
+  netfs_nref (node);
 
   hurd_ihash_init (&new->htable, offsetof (struct ftpfs_dir_entry, dir_locp));
   hurd_ihash_set_gki (&new->htable, ihash_hash, ihash_compare);
diff --git a/ftpfs/node.c b/ftpfs/node.c
index 74cd402..cc9bf43 100644
--- a/ftpfs/node.c
+++ b/ftpfs/node.c
@@ -84,10 +84,7 @@ netfs_node_norefs (struct node *node)
 {
   struct netnode *nn = node->nn;
 
-  /* Ftpfs_detach_node does ref count frobbing (of other nodes), so we have
-     to unlock NETFS_NODE_REFCNT_LOCK during it.  */
-  node->references++;
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
+  netfs_nref (node);
 
   /* Remove NODE from any entry it is attached to.  */
   ftpfs_detach_node (node);
@@ -108,7 +105,4 @@ netfs_node_norefs (struct node *node)
 
   free (nn);
   free (node);
-
-  /* Caller expects us to leave this locked... */
-  pthread_spin_lock (&netfs_node_refcnt_lock);
 }
diff --git a/hostmux/mux.c b/hostmux/mux.c
index 81d3961..ddca89d 100644
--- a/hostmux/mux.c
+++ b/hostmux/mux.c
@@ -240,10 +240,8 @@ lookup_cached (struct hostmux *mux, const char *host, int 
purge,
 
       if (strcasecmp (host, nm->name) == 0)
        {
-         pthread_spin_lock (&netfs_node_refcnt_lock);
-         if (nm->node)
-           nm->node->references++;
-         pthread_spin_unlock (&netfs_node_refcnt_lock);
+          if (nm->node)
+            netfs_nref (nm->node);
 
          if (nm->node)
            {
diff --git a/hurd/hurd_types.defs b/hurd/hurd_types.defs
index 1837500..4d7013c 100644
--- a/hurd/hurd_types.defs
+++ b/hurd/hurd_types.defs
@@ -349,16 +349,16 @@ serverprefix SERVERPREFIX;
 type data_t = array[] of char;
 type string_t = c_string[1024]; /* XXX */
 type io_statbuf_t = struct[32] of int;
-type uid_t = unsigned32;
-type gid_t = unsigned32;
-type mode_t = unsigned32;
-type retry_type = unsigned32;
-type pid_t = int32;
-type wait_status_t = int32;
-type loff_t = int64;
-type ino64_t = int64;
-type file_changed_type_t = unsigned32;
-type dir_changed_type_t = unsigned32;
+type uid_t = uint32_t;
+type gid_t = uint32_t;
+type mode_t = uint32_t;
+type retry_type = uint32_t;
+type pid_t = int32_t;
+type wait_status_t = int32_t;
+type loff_t = int64_t;
+type ino64_t = int64_t;
+type file_changed_type_t = uint32_t;
+type dir_changed_type_t = uint32_t;
 
 type portarray_t = array[] of mach_port_send_t;
 type intarray_t = array[] of int;
diff --git a/isofs/isofs.h b/isofs/isofs.h
index 3f6690b..2ba013c 100644
--- a/isofs/isofs.h
+++ b/isofs/isofs.h
@@ -75,6 +75,7 @@ char *mounted_on;
 
 /* Mapped image of disk */
 void *disk_image;
+size_t disk_image_len;
 
 /* Processed sblock info */
 
diff --git a/isofs/main.c b/isofs/main.c
index 95c90fe..c07cf3f 100644
--- a/isofs/main.c
+++ b/isofs/main.c
@@ -72,17 +72,13 @@ static void
 read_sblock ()
 {
   struct voldesc *vd;
-  error_t err;
   struct sblock * volatile sb = 0;
 
-  err = diskfs_catch_exception ();
-  if (err)
-    error (4, err, "reading superblock");
-
   /* Start at logical sector 16 and keep going until
      we find a matching superblock */
   for (vd = disk_image + (logical_sector_size * 16);
-       (void *) vd < disk_image + (logical_sector_size * 500); /* for sanity */
+       (void *) vd < disk_image + (logical_sector_size * 500) /* for sanity */
+         && (void *) vd + logical_sector_size < disk_image + disk_image_len;
        vd = (void *) vd + logical_sector_size)
     {
       if (vd->type == VOLDESC_END)
@@ -105,7 +101,6 @@ read_sblock ()
   if (!sblock)
     error (1, errno, "Could not allocate memory for superblock");
   memcpy (sblock, sb, sizeof (struct sblock));
-  diskfs_end_catch_exception ();
 
   /* Parse some important bits of this */
   logical_block_size = isonum_723 (sblock->blksize);
diff --git a/isofs/pager.c b/isofs/pager.c
index 35de37e..b4be4e2 100644
--- a/isofs/pager.c
+++ b/isofs/pager.c
@@ -28,6 +28,7 @@ struct port_bucket *pager_bucket;
 
 /* Mapped image of the disk */
 void *disk_image;
+size_t disk_image_len;
 
 
 /* Implement the pager_read_page callback from the pager library.  See
@@ -148,6 +149,7 @@ create_disk_pager (void)
   upi->np = 0;
   pager_bucket = ports_create_bucket ();
   diskfs_start_disk_pager (upi, pager_bucket, 1, 0, store->size, &disk_image);
+  disk_image_len = store->size;
   upi->p = diskfs_disk_pager;
 }
 
diff --git a/libdiskfs/boot-start.c b/libdiskfs/boot-start.c
index 60bf33d..0fb3e89 100644
--- a/libdiskfs/boot-start.c
+++ b/libdiskfs/boot-start.c
@@ -291,12 +291,13 @@ diskfs_start_bootstrap ()
                   /* Supply no intarray, since we have no info for it.
                      With none supplied, it will use the defaults.  */
                   NULL, 0, 0, 0, 0, 0);
+  if (err)
+    error (1, err, "Executing '%s'", exec_argv);
   free (exec_argv);
   free (exec_env);
   mach_port_deallocate (mach_task_self (), root_pt);
   mach_port_deallocate (mach_task_self (), startup_pt);
   mach_port_deallocate (mach_task_self (), bootpt);
-  assert_perror (err);
 }
 
 /* We look like an execserver to the execserver itself; it makes this
diff --git a/libdiskfs/io-seek.c b/libdiskfs/io-seek.c
index 9e3ff09..0018a71 100644
--- a/libdiskfs/io-seek.c
+++ b/libdiskfs/io-seek.c
@@ -46,6 +46,14 @@ diskfs_S_io_seek (struct protid *cred,
       offset += np->dn_stat.st_size;
     case SEEK_SET:
     check:
+      /* pager_memcpy inherently uses vm_offset_t, which may be smaller than
+         off_t.  */
+      if (sizeof(off_t) > sizeof(vm_offset_t) &&
+         offset > ((off_t) 1) << (sizeof(vm_offset_t) * 8))
+       {
+         err = EFBIG;
+         break;
+       }
       if (offset >= 0)
        {
          *newoffset = cred->po->filepointer = offset;
diff --git a/libdiskfs/rdwr-internal.c b/libdiskfs/rdwr-internal.c
index 18a4ae1..0d40551 100644
--- a/libdiskfs/rdwr-internal.c
+++ b/libdiskfs/rdwr-internal.c
@@ -58,7 +58,12 @@ _diskfs_rdwr_internal (struct node *np,
   if (memobj == MACH_PORT_NULL)
     return errno;
 
-  err = pager_memcpy (diskfs_get_filemap_pager_struct (np), memobj,
+  /* pager_memcpy inherently uses vm_offset_t, which may be smaller than 
off_t.  */
+  if (sizeof(off_t) > sizeof(vm_offset_t) &&
+      offset + *amt > ((off_t) 1) << (sizeof(vm_offset_t) * 8))
+    err = EFBIG;
+  else
+    err = pager_memcpy (diskfs_get_filemap_pager_struct (np), memobj,
                      offset, data, amt, prot);
 
   if (!diskfs_check_readonly () && !notime)
diff --git a/libfshelp/fetch-root.c b/libfshelp/fetch-root.c
index cc9fa50..eb0f315 100644
--- a/libfshelp/fetch-root.c
+++ b/libfshelp/fetch-root.c
@@ -134,7 +134,7 @@ fshelp_fetch_root (struct transbox *box, void *cookie,
 
       fds[STDERR_FILENO] = reauth (getdport (STDERR_FILENO));
 
-      err = fshelp_start_translator_long (fetch_underlying, NULL,
+      err = fshelp_start_translator_long (fetch_underlying, cookie,
                                          argz, argz, argz_len,
                                          fds, MACH_MSG_TYPE_COPY_SEND,
                                          STDERR_FILENO + 1,
diff --git a/libfshelp/start-translator-long.c 
b/libfshelp/start-translator-long.c
index 8b00e08..da6f52e 100644
--- a/libfshelp/start-translator-long.c
+++ b/libfshelp/start-translator-long.c
@@ -24,6 +24,7 @@
 #include <errno.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <stdint.h>
 #include <string.h>
 #include <assert.h>
 #include "fshelp.h"
@@ -107,7 +108,7 @@ service_fsys_startup (fshelp_open_fn_t underlying_open_fn, 
void *cookie,
     {
       union
       {
-       unsigned32_t word;
+        uint32_t word;
        mach_msg_type_t type;
       } t, c;
       t.type = *type;
diff --git a/libihash/ihash.c b/libihash/ihash.c
index 4bc54fd..ae1cf12 100644
--- a/libihash/ihash.c
+++ b/libihash/ihash.c
@@ -81,15 +81,9 @@ find_index (hurd_ihash_t ht, hurd_ihash_key_t key)
 
   idx = hash (ht, key) & mask;
 
-  if (ht->items[idx].value == _HURD_IHASH_EMPTY
-      || compare (ht, ht->items[idx].key, key))
-    return idx;
-
   up_idx = idx;
-
   do
     {
-      up_idx = (up_idx + 1) & mask;
       if (ht->items[up_idx].value == _HURD_IHASH_EMPTY)
         return first_deleted_set ? first_deleted : up_idx;
       if (compare (ht, ht->items[up_idx].key, key))
@@ -97,6 +91,7 @@ find_index (hurd_ihash_t ht, hurd_ihash_key_t key)
       if (! first_deleted_set
           && ht->items[up_idx].value == _HURD_IHASH_DELETED)
         first_deleted = up_idx, first_deleted_set = 1;
+      up_idx = (up_idx + 1) & mask;
     }
   while (up_idx != idx);
 
@@ -136,6 +131,7 @@ hurd_ihash_init (hurd_ihash_t ht, intptr_t locp_offs)
   ht->cleanup = 0;
   ht->fct_hash = NULL;
   ht->fct_cmp = NULL;
+  ht->nr_free = 0;
 }
 
 
@@ -251,6 +247,11 @@ add_one (hurd_ihash_t ht, hurd_ihash_key_t key, 
hurd_ihash_value_t value)
   if (index_empty (ht, idx))
     {
       ht->nr_items++;
+      if (ht->items[idx].value == _HURD_IHASH_EMPTY)
+        {
+          assert (ht->nr_free > 0);
+          ht->nr_free--;
+        }
       ht->items[idx].value = value;
       ht->items[idx].key = key;
 
@@ -288,13 +289,15 @@ hurd_ihash_locp_add (hurd_ihash_t ht, hurd_ihash_locp_t 
locp,
       || item == NULL
       || item->value == _HURD_IHASH_DELETED
       || ! compare (ht, item->key, key)
-      || hurd_ihash_get_load (ht) > ht->max_load)
+      || hurd_ihash_get_effective_load (ht) > ht->max_load)
     return hurd_ihash_add (ht, key, value);
 
   if (item->value == _HURD_IHASH_EMPTY)
     {
       item->key = key;
       ht->nr_items += 1;
+      assert (ht->nr_free > 0);
+      ht->nr_free -= 1;
     }
   else
     {
@@ -328,18 +331,21 @@ hurd_ihash_add (hurd_ihash_t ht, hurd_ihash_key_t key, 
hurd_ihash_value_t item)
   if (ht->size)
     {
       /* Only fill the hash table up to its maximum load factor.  */
-      if (hurd_ihash_get_load (ht) <= ht->max_load)
+      if (hurd_ihash_get_effective_load (ht) <= ht->max_load)
       add_one:
        if (add_one (ht, key, item))
          return 0;
     }
 
-  /* The hash table is too small, and we have to increase it.  */
+  /* If the load exceeds the configured maximal load, then the hash
+     table is too small, and we have to increase it.  Otherwise we
+     merely rehash the table to get rid of the tombstones.  */
   ht->nr_items = 0;
   if (ht->size == 0)
       ht->size = HURD_IHASH_MIN_SIZE;
-  else
+  else if (hurd_ihash_get_load (&old_ht) > ht->max_load)
       ht->size <<= 1;
+  ht->nr_free = ht->size;
 
   /* calloc() will initialize all values to _HURD_IHASH_EMPTY implicitly.  */
   ht->items = calloc (ht->size, sizeof (struct _hurd_ihash_item));
diff --git a/libihash/ihash.h b/libihash/ihash.h
index 356f647..80679f1 100644
--- a/libihash/ihash.h
+++ b/libihash/ihash.h
@@ -113,6 +113,9 @@ struct hurd_ihash
   /* User-supplied functions for the generalized key interface.  */
   hurd_ihash_fct_hash_t fct_hash;
   hurd_ihash_fct_cmp_t fct_cmp;
+
+  /* Number of free slots.  */
+  size_t nr_free;
 };
 typedef struct hurd_ihash *hurd_ihash_t;
 
@@ -225,6 +228,14 @@ hurd_ihash_get_load (hurd_ihash_t ht)
   return d >= 0 ? ht->nr_items >> d : ht->nr_items << -d;
 }
 
+/* Similar, but counts tombstones as well.  */
+static inline unsigned int
+hurd_ihash_get_effective_load (hurd_ihash_t ht)
+{
+  int d = __builtin_ctzl (ht->size) - 7;
+  return
+    d >= 0 ? (ht->size - ht->nr_free) >> d : (ht->size - ht->nr_free) << -d;
+}
 
 /* Add ITEM to the hash table HT under the key KEY.  If there already
    is an item under this key, call the cleanup function (if any) for
diff --git a/libnetfs/dir-mkfile.c b/libnetfs/dir-mkfile.c
index fcbc9da..6cd7917 100644
--- a/libnetfs/dir-mkfile.c
+++ b/libnetfs/dir-mkfile.c
@@ -37,7 +37,7 @@ netfs_S_dir_mkfile (struct protid *diruser, int flags, mode_t 
mode,
   if (!err)
     {
       /* the dir is now unlocked and NP is locked */
-      flags &= OPENONLY_STATE_MODES;
+      flags &= ~OPENONLY_STATE_MODES;
       err = iohelp_dup_iouser (&user, diruser->user);
       if (! err)
         {
diff --git a/libnetfs/drop-node.c b/libnetfs/drop-node.c
index 2fe5ce9..f0d69be 100644
--- a/libnetfs/drop-node.c
+++ b/libnetfs/drop-node.c
@@ -25,5 +25,4 @@ netfs_drop_node (struct node *np)
 {
   fshelp_drop_transbox (&np->transbox);
   netfs_node_norefs (np);
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
 }
diff --git a/libnetfs/init-init.c b/libnetfs/init-init.c
index a088ad5..9ca1aac 100644
--- a/libnetfs/init-init.c
+++ b/libnetfs/init-init.c
@@ -24,8 +24,6 @@
 /* For safe inlining of netfs_node_netnode and netfs_netnode_node.  */
 size_t const _netfs_sizeof_struct_node = sizeof (struct node);
 
-pthread_spinlock_t netfs_node_refcnt_lock = PTHREAD_SPINLOCK_INITIALIZER;
-
 struct node *netfs_root_node = 0;
 struct port_bucket *netfs_port_bucket = 0;
 struct port_class *netfs_protid_class = 0;
diff --git a/libnetfs/make-node.c b/libnetfs/make-node.c
index 6bd8109..a292dc6 100644
--- a/libnetfs/make-node.c
+++ b/libnetfs/make-node.c
@@ -27,7 +27,7 @@ init_node (struct node *np, struct netnode *nn)
   np->nn = nn;
 
   pthread_mutex_init (&np->lock, NULL);
-  np->references = 1;
+  refcounts_init (&np->refcounts, 1, 0);
   np->sockaddr = MACH_PORT_NULL;
   np->owner = 0;
 
diff --git a/libnetfs/netfs.h b/libnetfs/netfs.h
index 67a6a9a..6c989a4 100644
--- a/libnetfs/netfs.h
+++ b/libnetfs/netfs.h
@@ -82,8 +82,8 @@ struct node
 
   pthread_mutex_t lock;
 
-  /* The number of references to this node.  */
-  int references;
+  /* Hard and soft references to this node.  */
+  refcounts_t refcounts;
 
   mach_port_t sockaddr;
 
@@ -397,10 +397,6 @@ netfs_netnode_node (struct netnode *netnode)
   return (struct node *) ((char *) netnode - _netfs_sizeof_struct_node);
 }
 
-/* Whenever node->references is to be touched, this lock must be
-   held.  Cf. netfs_nrele, netfs_nput, netfs_nref and netfs_drop_node.  */
-extern pthread_spinlock_t netfs_node_refcnt_lock;
-
 /* Normally called in main.  This function sets up some of the netfs
    server's internal state.  */
 void netfs_init (void);
@@ -425,22 +421,38 @@ struct protid *netfs_make_protid (struct peropen *po, 
struct iouser *user);
 struct peropen *netfs_make_peropen (struct node *, int,
                                    struct peropen *context);
 
-/* Add a reference to node NP. Unless you already hold a reference,
+/* Add a hard reference to node NP. Unless you already hold a reference,
    NP must be locked.  */
 void netfs_nref (struct node *np);
 
-/* Releases a node.  Drops a reference to node NP, which must not be
-   locked by the caller.  If this was the last reference, drops the
-   node.  The node cannot be used again without first obtaining a
-   reference to it.  */
+/* Add a light reference to a node.  */
+void netfs_nref_light (struct node *np);
+
+/* Releases a hard reference on NP. If NP is locked by anyone, then
+   this cannot be the last hard reference (because you must hold a
+   hard reference in order to hold the lock). If this is the last
+   hard reference then request soft references to be dropped.  */
 void netfs_nrele (struct node *np);
 
-/* Puts a node back.  Drops a reference to the node NP, which must be
-   locked by the caller (this lock will be released by netfs_nput).
-   If this was the last reference, drops the node.  The node cannot be
-   used again without first obtaining a reference to it.  */
+/* Release a soft reference on NP. If NP is locked by anyone, then
+   this cannot be the last reference (because you must hold a hard
+   reference in order to hold the lock).  */
+void netfs_nrele_light (struct node *np);
+
+/* Puts a node back by releasing a hard reference on NP, which must
+   be locked by the caller (this lock will be released by netfs_nput).
+   If this was the last reference, then request soft references to be
+   dropped.  */
 void netfs_nput (struct node *np);
 
+/* The user must define this function in order to drop the soft references
+   that this node may have. When this function is called, node NP has just
+   lost its hard references and is now trying to also drop its soft references.
+   If the node is stored in another data structure (for caching purposes),
+   this allows the user to remove it so that the node can be safely deleted
+   from memory.  */
+void netfs_try_dropping_softrefs (struct node *np);
+
 /* Called internally when no more references to node NP exist. */
 void netfs_drop_node (struct node *np);
 
diff --git a/libnetfs/nput.c b/libnetfs/nput.c
index 522c714..b04fc4b 100644
--- a/libnetfs/nput.c
+++ b/libnetfs/nput.c
@@ -23,15 +23,24 @@
 void
 netfs_nput (struct node *np)
 {
-  pthread_spin_lock (&netfs_node_refcnt_lock);
-  assert (np->references);
-  np->references--;
-  if (np->references == 0)
+  struct references result;
+
+  refcounts_demote (&np->refcounts, &result);
+
+  if (result.hard == 0)
+    netfs_try_dropping_softrefs (np);
+
+  refcounts_deref_weak (&np->refcounts, &result);
+
+  if (result.hard == 0 && result.weak == 0)
     netfs_drop_node (np);
-    /* netfs_drop_node drops netfs_node_refcnt_lock for us.  */
   else
-    {
-      pthread_spin_unlock (&netfs_node_refcnt_lock);
-      pthread_mutex_unlock (&np->lock);
-    }
+    pthread_mutex_unlock (&np->lock);
+}
+
+/* The last hard reference to NP has gone away; the user must define
+   this function in order to drop all the soft references.  */
+void __attribute__ ((weak))
+netfs_try_dropping_softrefs (struct node *np)
+{
 }
diff --git a/libnetfs/nref.c b/libnetfs/nref.c
index 86b4992..a40cf4d 100644
--- a/libnetfs/nref.c
+++ b/libnetfs/nref.c
@@ -23,7 +23,11 @@
 void
 netfs_nref (struct node *np)
 {
-  pthread_spin_lock (&netfs_node_refcnt_lock);
-  np->references++;
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
+  refcounts_ref (&np->refcounts, NULL);
+}
+
+void
+netfs_nref_light (struct node *np)
+{
+  refcounts_ref_weak (&np->refcounts, NULL);
 }
diff --git a/libnetfs/nrele.c b/libnetfs/nrele.c
index 6f9a014..4dddd1f 100644
--- a/libnetfs/nrele.c
+++ b/libnetfs/nrele.c
@@ -23,15 +23,38 @@
 void
 netfs_nrele (struct node *np)
 {
-  pthread_spin_lock (&netfs_node_refcnt_lock);
-  assert (np->references);
-  np->references--;
-  if (np->references == 0)
+  struct references result;
+  int locked = FALSE;
+
+  refcounts_demote (&np->refcounts, &result);
+
+  if (result.hard == 0)
+    {
+      pthread_mutex_lock (&np->lock);
+      netfs_try_dropping_softrefs (np);
+      locked = TRUE;
+    }
+
+  refcounts_deref_weak (&np->refcounts, &result);
+
+  if (result.hard == 0 && result.weak == 0)
+    {
+      if (! locked)
+        pthread_mutex_lock (&np->lock);
+      netfs_drop_node (np);
+    } else if (locked)
+      pthread_mutex_unlock (&np->lock);
+}
+
+void
+netfs_nrele_light (struct node *np)
+{
+  struct references result;
+
+  refcounts_deref_weak (&np->refcounts, &result);
+  if (result.hard == 0 && result.weak == 0)
     {
       pthread_mutex_lock (&np->lock);
       netfs_drop_node (np);
-      /* netfs_drop_node drops netfs_node_refcnt_lock for us.  */
     }
-  else
-    pthread_spin_unlock (&netfs_node_refcnt_lock);
 }
diff --git a/libpager/demuxer.c b/libpager/demuxer.c
index 59dd1c5..3fd0516 100644
--- a/libpager/demuxer.c
+++ b/libpager/demuxer.c
@@ -15,6 +15,7 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
 
+#include <assert.h>
 #include <error.h>
 #include <mach/mig_errors.h>
 #include <pthread.h>
diff --git a/libshouldbeinlibc/Makefile b/libshouldbeinlibc/Makefile
index 633d60e..04c085b 100644
--- a/libshouldbeinlibc/Makefile
+++ b/libshouldbeinlibc/Makefile
@@ -29,10 +29,12 @@ SRCS = termsize.c timefmt.c exec-reauth.c maptime-funcs.c \
        ugids-auth.c ugids-xinl.c ugids-merge.c ugids-imply.c ugids-posix.c \
        ugids-verify-auth.c nullauth.c \
        refcount.c \
+       assert-backtrace.c \
 
 installhdrs = idvec.h timefmt.h maptime.h \
              wire.h portinfo.h portxlate.h cacheq.h ugids.h nullauth.h \
              refcount.h \
+             assert-backtrace.h \
 
 installhdrsubdir = .
 
diff --git a/libshouldbeinlibc/assert-backtrace.c 
b/libshouldbeinlibc/assert-backtrace.c
new file mode 100644
index 0000000..ca23c8d
--- /dev/null
+++ b/libshouldbeinlibc/assert-backtrace.c
@@ -0,0 +1,79 @@
+/* Augment failing assertions with backtraces.
+
+   Copyright (C) 2015,2016 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   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.
+
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef NDEBUG
+
+#include <error.h>
+#include <errno.h>
+#include <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "assert-backtrace.h"
+
+static void __attribute__ ((noreturn))
+__assert_fail_base_backtrace (const char *fmt,
+                             const char *assertion,
+                             const char *file,
+                             unsigned int line,
+                             const char *function)
+{
+  const size_t size = 128;
+  const size_t skip = 2;
+  int nptrs;
+  void *buffer[size];
+
+  nptrs = backtrace(buffer, size);
+  if (nptrs == 0)
+    error (1, *__errno_location (), "backtrace");
+
+  fprintf (stderr,
+          fmt, program_invocation_name, file, line, function, assertion);
+  backtrace_symbols_fd (&buffer[skip], nptrs - skip, STDERR_FILENO);
+  fflush (stderr);
+
+  /* Die.  */
+  abort ();
+}
+
+void
+__assert_fail_backtrace (const char *assertion, const char *file,
+                        unsigned int line, const char *function)
+{
+  __assert_fail_base_backtrace ("%s: %s:%u: %s: Assertion '%s' failed.\n",
+                               assertion, file, line, function);
+}
+
+void
+__assert_perror_fail_backtrace (int errnum,
+                               const char *file,
+                               unsigned int line,
+                               const char *function)
+{
+  char errbuf[1024];
+
+  char *e = strerror_r (errnum, errbuf, sizeof errbuf);
+  __assert_fail_base_backtrace ("%s: %s:%u: %s: Unexpected error: %s.\n",
+                               e, file, line, function);
+
+}
+
+#endif /* ! defined NDEBUG */
diff --git a/libshouldbeinlibc/assert-backtrace.h 
b/libshouldbeinlibc/assert-backtrace.h
new file mode 100644
index 0000000..c54b810
--- /dev/null
+++ b/libshouldbeinlibc/assert-backtrace.h
@@ -0,0 +1,60 @@
+/* Augment failing assertions with backtraces.
+
+   Copyright (C) 1994-2015 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   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.
+
+   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 the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef __ASSERT_BACKTRACE__
+#define __ASSERT_BACKTRACE__
+
+#ifdef NDEBUG
+
+#define assert_backtrace(expr)         ((void) 0)
+#define assert_backtrace_perror(errnum)        ((void) 0)
+
+#else /* NDEBUG */
+
+/* This prints an "Assertion failed" message, prints a stack trace,
+   and aborts. */
+void __assert_fail_backtrace (const char *assertion,
+                             const char *file,
+                             unsigned int line,
+                             const char *function)
+  __attribute__ ((noreturn, unused));
+
+/* Likewise, but prints the error text for ERRNUM.  */
+void __assert_perror_fail_backtrace (int errnum,
+                                    const char *file,
+                                    unsigned int line,
+                                    const char *function)
+  __attribute__ ((noreturn, unused));
+
+#define assert_backtrace(expr)                                         \
+  ((expr)                                                              \
+   ? (void) 0                                                          \
+   : __assert_fail_backtrace (__STRING(expr),                          \
+                             __FILE__, __LINE__,                       \
+                             __PRETTY_FUNCTION__))
+
+#define assert_perror_backtrace(expr)                                  \
+  ((expr == 0)                                                         \
+   ? (void) 0                                                          \
+   : __assert_perror_fail_backtrace (expr,                             \
+                                    __FILE__, __LINE__,                \
+                                    __PRETTY_FUNCTION__))
+
+#endif /* NDEBUG */
+#endif /* __ASSERT_BACKTRACE__ */
diff --git a/libshouldbeinlibc/refcount.h b/libshouldbeinlibc/refcount.h
index e8b0f5b..0d1fa28 100644
--- a/libshouldbeinlibc/refcount.h
+++ b/libshouldbeinlibc/refcount.h
@@ -28,7 +28,7 @@
 #define REFCOUNT_EI __extern_inline
 #endif
 
-#include <assert.h>
+#include <assert-backtrace.h>
 #include <limits.h>
 #include <stdint.h>
 
@@ -41,7 +41,7 @@ typedef unsigned int refcount_t;
 REFCOUNT_EI void
 refcount_init (refcount_t *ref, unsigned int references)
 {
-  assert (references > 0 || !"references must not be zero!");
+  assert_backtrace (references > 0 || !"references must not be zero!");
   *ref = references;
 }
 
@@ -57,7 +57,7 @@ refcount_unsafe_ref (refcount_t *ref)
 {
   unsigned int r;
   r = __atomic_add_fetch (ref, 1, __ATOMIC_RELAXED);
-  assert (r != UINT_MAX || !"refcount overflowed!");
+  assert_backtrace (r != UINT_MAX || !"refcount overflowed!");
   return r;
 }
 
@@ -69,7 +69,7 @@ refcount_ref (refcount_t *ref)
 {
   unsigned int r;
   r = refcount_unsafe_ref (ref);
-  assert (r != 1 || !"refcount detected use-after-free!");
+  assert_backtrace (r != 1 || !"refcount detected use-after-free!");
   return r;
 }
 
@@ -81,7 +81,7 @@ refcount_deref (refcount_t *ref)
 {
   unsigned int r;
   r = __atomic_sub_fetch (ref, 1, __ATOMIC_RELAXED);
-  assert (r != UINT_MAX || !"refcount underflowed!");
+  assert_backtrace (r != UINT_MAX || !"refcount underflowed!");
   return r;
 }
 
@@ -129,7 +129,8 @@ union _references {
 REFCOUNT_EI void
 refcounts_init (refcounts_t *ref, uint32_t hard, uint32_t weak)
 {
-  assert ((hard != 0 || weak != 0) || !"references must not both be zero!");
+  assert_backtrace ((hard != 0 || weak != 0)
+                    || !"references must not both be zero!");
   ref->references = (struct references) { .hard = hard, .weak = weak };
 }
 
@@ -147,7 +148,8 @@ refcounts_unsafe_ref (refcounts_t *ref, struct references 
*result)
   const union _references op = { .references = { .hard = 1 } };
   union _references r;
   r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
-  assert (r.references.hard != UINT32_MAX || !"refcount overflowed!");
+  assert_backtrace (r.references.hard != UINT32_MAX
+                    || !"refcount overflowed!");
   if (result)
     *result = r.references;
 }
@@ -161,7 +163,7 @@ refcounts_ref (refcounts_t *ref, struct references *result)
 {
   struct references r;
   refcounts_unsafe_ref (ref, &r);
-  assert (! (r.hard == 1 && r.weak == 0)
+  assert_backtrace (! (r.hard == 1 && r.weak == 0)
           || !"refcount detected use-after-free!");
   if (result)
     *result = r;
@@ -177,7 +179,8 @@ refcounts_deref (refcounts_t *ref, struct references 
*result)
   const union _references op = { .references = { .hard = 1 } };
   union _references r;
   r.value = __atomic_sub_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
-  assert (r.references.hard != UINT32_MAX || !"refcount underflowed!");
+  assert_backtrace (r.references.hard != UINT32_MAX
+                    || !"refcount underflowed!");
   if (result)
     *result = r.references;
 }
@@ -207,8 +210,10 @@ refcounts_promote (refcounts_t *ref, struct references 
*result)
     { .references = { .weak = ~0U, .hard = 1} };
   union _references r;
   r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
-  assert (r.references.hard != UINT32_MAX || !"refcount overflowed!");
-  assert (r.references.weak != UINT32_MAX || !"refcount underflowed!");
+  assert_backtrace (r.references.hard != UINT32_MAX
+                    || !"refcount overflowed!");
+  assert_backtrace (r.references.weak != UINT32_MAX
+                    || !"refcount underflowed!");
   if (result)
     *result = r.references;
 }
@@ -235,8 +240,10 @@ refcounts_demote (refcounts_t *ref, struct references 
*result)
   const union _references op = { .references = { .hard = ~0U } };
   union _references r;
   r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
-  assert (r.references.hard != UINT32_MAX || !"refcount underflowed!");
-  assert (r.references.weak != UINT32_MAX || !"refcount overflowed!");
+  assert_backtrace (r.references.hard != UINT32_MAX
+                    || !"refcount underflowed!");
+  assert_backtrace (r.references.weak != UINT32_MAX
+                    || !"refcount overflowed!");
   if (result)
     *result = r.references;
 }
@@ -255,7 +262,8 @@ refcounts_unsafe_ref_weak (refcounts_t *ref, struct 
references *result)
   const union _references op = { .references = { .weak = 1 } };
   union _references r;
   r.value = __atomic_add_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
-  assert (r.references.weak != UINT32_MAX || !"refcount overflowed!");
+  assert_backtrace (r.references.weak != UINT32_MAX
+                    || !"refcount overflowed!");
   if (result)
     *result = r.references;
 }
@@ -269,7 +277,7 @@ refcounts_ref_weak (refcounts_t *ref, struct references 
*result)
 {
   struct references r;
   refcounts_unsafe_ref_weak (ref, &r);
-  assert (! (r.hard == 0 && r.weak == 1)
+  assert_backtrace (! (r.hard == 0 && r.weak == 1)
           || !"refcount detected use-after-free!");
   if (result)
     *result = r;
@@ -285,7 +293,8 @@ refcounts_deref_weak (refcounts_t *ref, struct references 
*result)
   const union _references op = { .references = { .weak = 1 } };
   union _references r;
   r.value = __atomic_sub_fetch (&ref->value, op.value, __ATOMIC_RELAXED);
-  assert (r.references.weak != UINT32_MAX || !"refcount underflowed!");
+  assert_backtrace (r.references.weak != UINT32_MAX
+                    || !"refcount underflowed!");
   if (result)
     *result = r.references;
 }
diff --git a/libtrivfs/fsys-getroot.c b/libtrivfs/fsys-getroot.c
index 2bc11f5..c44e535 100644
--- a/libtrivfs/fsys-getroot.c
+++ b/libtrivfs/fsys-getroot.c
@@ -69,8 +69,20 @@ trivfs_S_fsys_getroot (struct trivfs_control *cntl,
   flags &= O_HURD;
   flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS);
 
-  err = io_restrict_auth (cntl->underlying,
-                         &new_realnode, uids, nuids, gids, ngids);
+  struct idvec idvec = {
+    .ids = uids,
+    .num = nuids,
+    .alloced = nuids,
+  };
+
+  if (idvec_contains (&idvec, 0))
+    /* Root should be given all our rights.  */
+    err = io_duplicate (cntl->underlying, &new_realnode);
+  else
+    /* Non-root, restrict rights.  */
+    err = io_restrict_auth (cntl->underlying,
+                           &new_realnode, uids, nuids, gids, ngids);
+
   if (err)
     return err;
 
diff --git a/libtrivfs/io-reauthenticate.c b/libtrivfs/io-reauthenticate.c
index 35775e5..72684e3 100644
--- a/libtrivfs/io-reauthenticate.c
+++ b/libtrivfs/io-reauthenticate.c
@@ -59,8 +59,7 @@ trivfs_S_io_reauthenticate (struct trivfs_protid *cred,
     return err;
 
   mach_port_deallocate (mach_task_self (), newright);
-  if (idvec_contains (newcred->user->uids, 0))
-    newcred->isroot = 1;
+  newcred->isroot = _is_privileged (newcred->user->uids);
 
   newcred->hook = cred->hook;
   newcred->po = cred->po;
diff --git a/libtrivfs/io-restrict-auth.c b/libtrivfs/io-restrict-auth.c
index cb4224d..6c807f1 100644
--- a/libtrivfs/io-restrict-auth.c
+++ b/libtrivfs/io-restrict-auth.c
@@ -109,11 +109,9 @@ trivfs_S_io_restrict_auth (struct trivfs_protid *cred,
       return err;
     }
 
-  newcred->isroot = 0;
   newcred->po = cred->po;
   refcount_ref (&newcred->po->refcnt);
-  if (cred->isroot && idvec_contains (user->uids, 0))
-    newcred->isroot = 1;
+  newcred->isroot = cred->isroot && _is_privileged (user->uids);
   newcred->user = user;
   newcred->hook = cred->hook;
 
diff --git a/libtrivfs/open.c b/libtrivfs/open.c
index 97e70a1..35a9452 100644
--- a/libtrivfs/open.c
+++ b/libtrivfs/open.c
@@ -56,7 +56,7 @@ trivfs_open (struct trivfs_control *cntl,
       if (! err)
        {
          new->user = user;
-         new->isroot = idvec_contains (user->uids, 0);
+         new->isroot = _is_privileged (user->uids);
 
          new->po = po;
          new->hook = 0;
diff --git a/libtrivfs/priv.h b/libtrivfs/priv.h
index d92fe33..4bdd4f7 100644
--- a/libtrivfs/priv.h
+++ b/libtrivfs/priv.h
@@ -21,6 +21,15 @@
 #include <mach.h>
 #include <hurd.h>
 #include <hurd/ports.h>
+#include <idvec.h>
+#include <unistd.h>
 #include "trivfs.h"
 
+/* Returns true if UIDS contains either 0 or our user id.  */
+static inline int
+_is_privileged (struct idvec *uids)
+{
+  return idvec_contains (uids, 0) || idvec_contains (uids, getuid ());
+}
+
 #endif
diff --git a/libtrivfs/times.c b/libtrivfs/times.c
index 5f08cb1..42e668d 100644
--- a/libtrivfs/times.c
+++ b/libtrivfs/times.c
@@ -20,29 +20,37 @@
 error_t
 trivfs_set_atime (struct trivfs_control *cntl)
 {
+  error_t err;
   struct stat st;
   time_value_t atime;
   time_value_t mtime;
-  
-  io_stat (cntl->underlying, &st);
+
+  err = io_stat (cntl->underlying, &st);
+  if (err)
+    return err;
+
   mtime.seconds = st.st_mtim.tv_sec;
   mtime.microseconds = st.st_mtim.tv_nsec / 1000;
   atime.microseconds = -1;
-  file_utimes (cntl->underlying, atime, mtime);
-  return 0;
+
+  return file_utimes (cntl->underlying, atime, mtime);
 }
 
 error_t
 trivfs_set_mtime (struct trivfs_control *cntl)
 {
+  error_t err;
   struct stat st;
   time_value_t atime;
   time_value_t mtime;
 
-  io_stat (cntl->underlying, &st);
+  err = io_stat (cntl->underlying, &st);
+  if (err)
+    return err;
+
   atime.seconds = st.st_atim.tv_sec;
   atime.microseconds = st.st_atim.tv_nsec / 1000;
   mtime.microseconds = -1;
-  file_utimes (cntl->underlying, atime, mtime);
-  return 0;
+
+  return file_utimes (cntl->underlying, atime, mtime);
 }
diff --git a/libtrivfs/trivfs.h b/libtrivfs/trivfs.h
index d81c4f9..49cc765 100644
--- a/libtrivfs/trivfs.h
+++ b/libtrivfs/trivfs.h
@@ -30,7 +30,8 @@ struct trivfs_protid
 {
   struct port_info pi;
   struct iouser *user;
-  int isroot;
+  int isroot;                  /* Opened by a privileged user, either
+                                  root or our own user.  */
   /* REALNODE will be null if this protid wasn't fully created (currently
      only in the case where trivfs_protid_create_hook returns an error).  */
   mach_port_t realnode;                /* restricted permissions */
diff --git a/login/utmp.c b/login/utmp.c
index c7c1ac0..f366d44 100644
--- a/login/utmp.c
+++ b/login/utmp.c
@@ -364,7 +364,7 @@ S_login_get_login_collection(file_t utmp, int *pid)
 #define PT_PROC 2              /* Our process handle. */
 
 int trivfs_fstype = FSTYPE_MISC;
-int trivfs_fsid = 0; /* ??? */
+int trivfs_fsid = 0;
 
 int trivfs_support_read = 0;
 int trivfs_support_write = 0;
diff --git a/mach-defpager/default_pager.c b/mach-defpager/default_pager.c
index 5379795..c79a82a 100644
--- a/mach-defpager/default_pager.c
+++ b/mach-defpager/default_pager.c
@@ -3268,7 +3268,7 @@ S_default_pager_storage_info (mach_port_t pager,
        {
                kr = vm_allocate(default_pager_self, &addr,
                                 round_page(m * sizeof(*size)), TRUE);
-               if (kr != KERN_SUCCESS);
+               if (kr != KERN_SUCCESS)
                        goto nomemory;
                *size = (vm_size_array_t) addr;
        }
@@ -3278,7 +3278,7 @@ S_default_pager_storage_info (mach_port_t pager,
        {
                kr = vm_allocate(default_pager_self, &addr,
                                 round_page(m * sizeof(*free)), TRUE);
-               if (kr != KERN_SUCCESS);
+               if (kr != KERN_SUCCESS)
                        goto nomemory;
                *free = (vm_size_array_t) addr;
        }
@@ -3288,7 +3288,7 @@ S_default_pager_storage_info (mach_port_t pager,
        {
                kr = vm_allocate(default_pager_self, &addr,
                                 round_page(len), TRUE);
-               if (kr != KERN_SUCCESS);
+               if (kr != KERN_SUCCESS)
                        goto nomemory;
                *name = (data_t) addr;
        }
diff --git a/nfs/cache.c b/nfs/cache.c
index b48152e..2015603 100644
--- a/nfs/cache.c
+++ b/nfs/cache.c
@@ -49,6 +49,8 @@ static struct hurd_ihash nodehash =
                               + offsetof (struct netnode, slot), NULL, NULL,
                               ihash_hash, ihash_compare);
 
+pthread_mutex_t nodehash_ihash_lock = PTHREAD_MUTEX_INITIALIZER;
+
 /* Lookup the file handle HANDLE in the hash table.  If it is
    not present, initialize a new node structure and insert it into the
    hash table.  Whichever course, a new reference is generated and the
@@ -60,12 +62,12 @@ lookup_fhandle (struct fhandle *handle, struct node **npp)
   struct node *np;
   struct netnode *nn;
 
-  pthread_spin_lock (&netfs_node_refcnt_lock);
+  pthread_mutex_lock (&nodehash_ihash_lock);
   np = hurd_ihash_find (&nodehash, (hurd_ihash_key_t) handle);
   if (np)
     {
-      np->references++;
-      pthread_spin_unlock (&netfs_node_refcnt_lock);
+      netfs_nref (np);
+      pthread_mutex_unlock (&nodehash_ihash_lock);
       pthread_mutex_lock (&np->lock);
       *npp = np;
       return;
@@ -84,9 +86,9 @@ lookup_fhandle (struct fhandle *handle, struct node **npp)
   nn->dead_name = 0;
   
   hurd_ihash_add (&nodehash, (hurd_ihash_key_t) &nn->handle, np);
+  netfs_nref_light (np);
+  pthread_mutex_unlock (&nodehash_ihash_lock);
   pthread_mutex_lock (&np->lock);
-
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
   
   *npp = np;
 }
@@ -114,9 +116,7 @@ forked_node_delete (void *arg)
 };
 
 /* Called by libnetfs when node NP has no more references.  (See
-   <hurd/libnetfs.h> for details.  Just clear its local state and
-   remove it from the hash table.  Called and expected to leave with
-   NETFS_NODE_REFCNT_LOCK held.  */
+   <hurd/libnetfs.h> for details.  */
 void
 netfs_node_norefs (struct node *np)
 {
@@ -129,8 +129,7 @@ netfs_node_norefs (struct node *np)
       args = malloc (sizeof (struct fnd));
       assert (args);
 
-      np->references++;
-      pthread_spin_unlock (&netfs_node_refcnt_lock);
+      netfs_nref (np);
 
       args->dir = np->nn->dead_dir;
       args->name = np->nn->dead_name;
@@ -149,19 +148,26 @@ netfs_node_norefs (struct node *np)
          errno = err;
          perror ("pthread_create");
        }
-
-      /* Caller expects us to leave this locked... */
-      pthread_spin_lock (&netfs_node_refcnt_lock);
     }
   else
     {
-      hurd_ihash_locp_remove (&nodehash, np->nn->slot);
       if (np->nn->dtrans == SYMLINK)
-       free (np->nn->transarg.name);
+        free (np->nn->transarg.name);
       free (np);
     }
 }
 
+/* When dropping soft refs, we simply remove the node from the
+   node cache.  */
+void
+netfs_try_dropping_softrefs (struct node *np)
+{
+  pthread_mutex_lock (&nodehash_ihash_lock);
+  hurd_ihash_locp_remove (&nodehash, np->nn->slot);
+  netfs_nrele_light (np);
+  pthread_mutex_unlock (&nodehash_ihash_lock);
+}
+
 /* Change the file handle used for node NP to be the handle at P.
    Make sure the hash table stays up to date.  Return the address
    after the handle.  The lock on the node should be held.  */
@@ -179,7 +185,7 @@ recache_handle (int *p, struct node *np)
     }
   
   /* Unlink it */
-  pthread_spin_lock (&netfs_node_refcnt_lock);
+  pthread_mutex_lock (&nodehash_ihash_lock);
   hurd_ihash_locp_remove (&nodehash, np->nn->slot);
 
   /* Change the name */
@@ -189,6 +195,6 @@ recache_handle (int *p, struct node *np)
   /* Reinsert it */
   hurd_ihash_add (&nodehash, (hurd_ihash_key_t) &np->nn->handle, np);
   
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
+  pthread_mutex_unlock (&nodehash_ihash_lock);
   return p + len / sizeof (int);
 }
diff --git a/nfs/ops.c b/nfs/ops.c
index 79cd3a6..33ab38b 100644
--- a/nfs/ops.c
+++ b/nfs/ops.c
@@ -1267,7 +1267,10 @@ netfs_attempt_unlink (struct iouser *cred, struct node 
*dir,
      one we just got; if so, we must give this file another link
      so that when we delete the one we are asked for it doesn't go
      away entirely. */
-  if (np->references > 1)
+  struct references result;
+  refcounts_references (&np->refcounts, &result);
+
+  if (result.hard > 1)
     {
       char *newname = 0;
       int n = 0;
diff --git a/pfinet/linux-src/include/linux/ext2_fs_i.h 
b/pfinet/linux-src/include/linux/ext2_fs_i.h
index 8f01f8a..7df9021 100644
--- a/pfinet/linux-src/include/linux/ext2_fs_i.h
+++ b/pfinet/linux-src/include/linux/ext2_fs_i.h
@@ -35,7 +35,6 @@ struct ext2_inode_info {
        __u32   i_next_alloc_goal;
        __u32   i_prealloc_block;
        __u32   i_prealloc_count;
-       __u32   i_high_size;
        int     i_new_inode:1;  /* Is a freshly allocated inode */
 };
 
diff --git a/pfinet/main.c b/pfinet/main.c
index 4bfa318..5e4b749 100644
--- a/pfinet/main.c
+++ b/pfinet/main.c
@@ -283,7 +283,6 @@ main (int argc,
   pfinet_bucket = ports_create_bucket ();
   addrport_class = ports_create_class (clean_addrport, 0);
   socketport_class = ports_create_class (clean_socketport, 0);
-  trivfs_fsid = getpid ();
   mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
                      &fsys_identity);
 
diff --git a/procfs/netfs.c b/procfs/netfs.c
index 276c57c..0b3d31a 100644
--- a/procfs/netfs.c
+++ b/procfs/netfs.c
@@ -222,12 +222,8 @@ error_t netfs_attempt_lookup (struct iouser *user, struct 
node *dir,
    free all its associated storage. */
 void netfs_node_norefs (struct node *np)
 {
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
-
   procfs_cleanup (np);
   free (np);
-
-  pthread_spin_lock (&netfs_node_refcnt_lock);
 }
 
 /* The user may define this function (but should define it together
diff --git a/procfs/rootdir.c b/procfs/rootdir.c
index dd693c8..8e7c659 100644
--- a/procfs/rootdir.c
+++ b/procfs/rootdir.c
@@ -548,7 +548,7 @@ rootdir_gc_swaps (void *hook, char **contents, ssize_t 
*contents_len)
       goto out_fclose;
     }
 
-  err = default_pager_storage_info (defpager, &free, &nfree, &size, &nsize,
+  err = default_pager_storage_info (defpager, &size, &nsize, &free, &nfree,
                                    &names, &names_len);
   if (err)
     goto out;
@@ -557,7 +557,7 @@ rootdir_gc_swaps (void *hook, char **contents, ssize_t 
*contents_len)
   name = names;
   for (i = 0; i < nfree; i++)
     {
-      fprintf (m, "/dev/%s\tpartition\t%zu\t%zu\t-1\n",
+      fprintf (m, "%s\tpartition\t%zu\t%zu\t-1\n",
               name, size[i] >> 10, (size[i] - free[i]) >> 10);
       name = argz_next (names, names_len, name);
     }
diff --git a/startup/Makefile b/startup/Makefile
index ee2ecdd5..bda3ffb 100644
--- a/startup/Makefile
+++ b/startup/Makefile
@@ -21,15 +21,20 @@ makemode := server
 SRCS = startup.c
 OBJS = $(SRCS:.c=.o) \
        startupServer.o notifyServer.o startup_replyUser.o msgServer.o \
-       startup_notifyUser.o fsysServer.o
+       startup_notifyUser.o fsysServer.o fsServer.o ioServer.o
 target = startup
 HURDLIBS = shouldbeinlibc
 
 # startup does not use libports.  Disable the default payload to port
 # conversion.
 MIGSFLAGS="-DHURD_DEFAULT_PAYLOAD_TO_PORT=1"
+notify-MIGSFLAGS="-DNOTIFY_IMPORTS=import <hurd.h>;"
+notifyServer-CFLAGS="-DMIG_EOPNOTSUPP=EOPNOTSUPP"
+fsysServer-CFLAGS="-DMIG_EOPNOTSUPP=EOPNOTSUPP"
+fsServer-CFLAGS="-DMIG_EOPNOTSUPP=EOPNOTSUPP"
+ioServer-CFLAGS="-DMIG_EOPNOTSUPP=EOPNOTSUPP"
 
 include ../Makeconf
 
 mung_msg_S.h: msg_S.h
-       sed 's/msg_server/mung_msg_server/' < $< > $@
+       sed 's/_msg_server/_mung_msg_server/' < $< > $@
diff --git a/startup/startup.c b/startup/startup.c
index 9c45f4b..f90b524 100644
--- a/startup/startup.c
+++ b/startup/startup.c
@@ -51,16 +51,22 @@
 #include <version.h>
 #include <argp.h>
 #include <pids.h>
+#include <idvec.h>
 
 #include "startup_notify_U.h"
 #include "startup_reply_U.h"
 #include "startup_S.h"
 #include "notify_S.h"
 #include "mung_msg_S.h"
+#include "fsys_S.h"
+#include "fs_S.h"
+#include "io_S.h"
 
 /* host_reboot flags for when we crash.  */
 static int crash_flags = RB_AUTOBOOT;
 
+static int verbose = 0;
+
 #define BOOT(flags)    ((flags & RB_HALT) ? "halt" : "reboot")
 
 
@@ -75,6 +81,7 @@ options[] =
   {"crash-debug",  'H', 0, 0, "On system crash, go to kernel debugger"},
   {"debug",       'd', 0, 0 },
   {"fake-boot",   'f', 0, 0, "This hurd hasn't been booted on the raw 
machine"},
+  {"verbose",     'v', 0, 0, "be verbose"},
   {0,             'x', 0, OPTION_HIDDEN},
   {0}
 };
@@ -166,17 +173,15 @@ reboot_mach (int flags)
 {
   if (fakeboot)
     {
-      printf ("%s: Would %s Mach with flags %#x\n",
-             program_invocation_short_name, BOOT (flags), flags);
-      fflush (stdout);
+      fprintf (stderr, "%s: Would %s Mach with flags %#x\n",
+               program_invocation_short_name, BOOT (flags), flags);
       exit (1);
     }
   else
     {
       error_t err;
-      printf ("%s: %sing Mach (flags %#x)...\n",
-             program_invocation_short_name, BOOT (flags), flags);
-      fflush (stdout);
+      fprintf (stderr, "%s: %sing Mach (flags %#x)...\n",
+               program_invocation_short_name, BOOT (flags), flags);
       sleep (5);
       while ((err = host_reboot (host_priv, flags)))
        error (0, err, "reboot");
@@ -200,17 +205,16 @@ notify_shutdown (const char *msg)
   for (n = ntfy_tasks; n != NULL; n = n->next)
     {
       error_t err;
-      printf ("%s: notifying %s of %s...",
-             program_invocation_short_name, n->name, msg);
-      fflush (stdout);
+      fprintf (stderr, "%s: notifying %s of %s...",
+               program_invocation_short_name, n->name, msg);
+
       err = startup_dosync (n->notify_port, 60000); /* 1 minute to reply */
       if (err == MACH_SEND_INVALID_DEST)
-       puts ("(no longer present)");
+       fprintf (stderr, "(no longer present)\n");
       else if (err)
-       puts (strerror (err));
+       fprintf (stderr, "%s\n", strerror (err));
       else
-       puts ("done");
-      fflush (stdout);
+       fprintf (stderr, "done\n");
     }
 }
 
@@ -269,20 +273,18 @@ reboot_system (int flags)
                }
              if (!(pi->state & PI_NOPARENT))
                {
-                 printf ("%s: Killing pid %d\n",
-                         program_invocation_short_name, pp[ind]);
-                 fflush (stdout);
+                 fprintf (stderr, "%s: Killing pid %d\n",
+                           program_invocation_short_name, pp[ind]);
                  task_terminate (task);
                }
              if (noise_len > 0)
                munmap (noise, noise_len);
            }
        }
-      printf ("%s: Killing proc server\n", program_invocation_short_name);
-      fflush (stdout);
+      fprintf (stderr, "%s: Killing proc server\n",
+               program_invocation_short_name);
       task_terminate (proctask);
-      printf ("%s: Exiting", program_invocation_short_name);
-      fflush (stdout);
+      fprintf (stderr, "%s: Exiting", program_invocation_short_name);
     }
   reboot_mach (flags);
 }
@@ -352,7 +354,7 @@ run (const char *server, mach_port_t *ports, task_t *task)
 
   if (bootstrap_args & RB_INITNAME)
     {
-      printf ("Server file name (default %s): ", server);
+      fprintf (stderr, "Server file name (default %s): ", server);
       if (getstring (buf, sizeof (buf)))
        prog = buf;
     }
@@ -374,7 +376,7 @@ run (const char *server, mach_port_t *ports, task_t *task)
                       0, task);
          if (bootstrap_args & RB_KDB)
            {
-             printf ("Pausing for %s\n", prog);
+             fprintf (stderr, "Pausing for %s\n", prog);
              getchar ();
            }
          err = file_exec (file, *task, 0,
@@ -390,17 +392,16 @@ run (const char *server, mach_port_t *ports, task_t *task)
          error (0, err, "%s", prog);
        }
 
-      printf ("File name for server %s (or nothing to reboot): ", server);
+      fprintf (stderr, "File name for server %s (or nothing to reboot): ",
+               server);
       if (getstring (buf, sizeof (buf)))
        prog = buf;
       else
        crash_system ();
     }
 
-#if 0
-  printf ("started %s\n", prog);
-  fflush (stdout);
-#endif
+  if (verbose)
+    fprintf (stderr, stderr, "started %s\n", prog);
 
   /* Dead-name notification on the task port will tell us when it dies,
      so we can crash if we don't make it to a fully bootstrapped Hurd.  */
@@ -426,7 +427,7 @@ run_for_real (char *filename, char *args, int arglen, 
mach_port_t ctty,
   char buf[512];
   do
     {
-      printf ("File name [%s]: ", filename);
+      fprintf (stderr, "File name [%s]: ", filename);
       if (getstring (buf, sizeof (buf)) && *buf)
        filename = buf;
       file = file_name_lookup (filename, O_EXEC, 0);
@@ -462,7 +463,7 @@ run_for_real (char *filename, char *args, int arglen, 
mach_port_t ctty,
     }
   if (bootstrap_args & RB_KDB)
     {
-      printf ("Pausing for %s\n", filename);
+      fprintf (stderr, "Pausing for %s\n", filename);
       getchar ();
     }
   progname = strrchr (filename, '/');
@@ -498,19 +499,63 @@ run_for_real (char *filename, char *args, int arglen, 
mach_port_t ctty,
 
 /** Main program and setup **/
 
+/* XXX: The libc should provide this function.  */
+static void
+mig_reply_setup (
+       const mach_msg_header_t *in,
+       mach_msg_header_t       *out)
+{
+      static const mach_msg_type_t RetCodeType = {
+               /* msgt_name = */               MACH_MSG_TYPE_INTEGER_32,
+               /* msgt_size = */               32,
+               /* msgt_number = */             1,
+               /* msgt_inline = */             TRUE,
+               /* msgt_longform = */           FALSE,
+               /* msgt_deallocate = */         FALSE,
+               /* msgt_unused = */             0
+       };
+
+#define        InP     (in)
+#define        OutP    ((mig_reply_header_t *) out)
+      OutP->Head.msgh_bits =
+       MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InP->msgh_bits), 0);
+      OutP->Head.msgh_size = sizeof *OutP;
+      OutP->Head.msgh_remote_port = InP->msgh_remote_port;
+      OutP->Head.msgh_local_port = MACH_PORT_NULL;
+      OutP->Head.msgh_seqno = 0;
+      OutP->Head.msgh_id = InP->msgh_id + 100;
+      OutP->RetCodeType = RetCodeType;
+      OutP->RetCode = MIG_BAD_ID;
+#undef InP
+#undef OutP
+}
+
 static int
 demuxer (mach_msg_header_t *inp,
         mach_msg_header_t *outp)
 {
-  extern int notify_server (mach_msg_header_t *, mach_msg_header_t *);
-  extern int startup_server (mach_msg_header_t *, mach_msg_header_t *);
-  extern int msg_server (mach_msg_header_t *, mach_msg_header_t *);
-  extern int fsys_server (mach_msg_header_t *, mach_msg_header_t *);
-
-  return (notify_server (inp, outp) ||
-         msg_server (inp, outp) ||
-         fsys_server (inp, outp) ||
-         startup_server (inp, outp));
+  mig_routine_t routine;
+
+  if (verbose > 1)
+    error (0, 0, "%d", inp->msgh_id);
+
+  mig_reply_setup (inp, outp);
+
+  if ((routine = notify_server_routine (inp)) ||
+      (routine = msg_server_routine (inp)) ||
+      (routine = fsys_server_routine (inp)) ||
+      (routine = fs_server_routine (inp)) ||
+      (routine = io_server_routine (inp)) ||
+      (routine = startup_server_routine (inp)))
+    {
+      (*routine) (inp, outp);
+
+      if (verbose > 1)
+        error (0, ((mig_reply_header_t *) outp)->RetCode, "%d", inp->msgh_id);
+      return TRUE;
+    }
+  else
+    return FALSE;
 }
 
 error_t
@@ -550,6 +595,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
     case 'n': bootstrap_args |= RB_INITNAME; break;
     case 'f': fakeboot = 1; break;
     case 'H': crash_flags = RB_DEBUGGER; break;
+    case 'v': verbose++; break;
     case 'x': /* NOP */ break;
     default: return ARGP_ERR_UNKNOWN;
     }
@@ -640,11 +686,11 @@ main (int argc, char **argv, char **envp)
 
   default_ports[INIT_PORT_BOOTSTRAP] = startup;
   run ("/hurd/proc", default_ports, &proctask);
-  printf (" proc");
-  fflush (stdout);
+  if (! verbose)
+    fprintf (stderr, " proc");
   run ("/hurd/auth", default_ports, &authtask);
-  printf (" auth");
-  fflush (stdout);
+  if (! verbose)
+    fprintf (stderr, " auth");
   default_ports[INIT_PORT_BOOTSTRAP] = MACH_PORT_NULL;
 
   /* Wait for messages.  When both auth and proc have started, we
@@ -663,6 +709,9 @@ launch_core_servers (void)
   mach_port_t authproc, fsproc, procproc;
   error_t err;
 
+  if (verbose)
+    fprintf (stderr, "Launching core servers\n");
+
   /* Reply to the proc and auth servers.   */
   err = startup_procinit_reply (procreply, procreplytype, 0,
                                mach_task_self (), authserver,
@@ -675,6 +724,9 @@ launch_core_servers (void)
       device_master = 0;
     }
 
+  if (verbose)
+    fprintf (stderr, "proc launched\n");
+
   /* Mark us as important.  */
   err = proc_mark_important (procserver);
   assert_perror (err);
@@ -699,12 +751,18 @@ launch_core_servers (void)
     /* Good luck.  Who knows, maybe it's an old installation.  */
     error (0, err, "Failed to bind to " _SERVERS_STARTUP);
 
+  if (verbose)
+    fprintf (stderr, "Installed on /servers/startup\n");
+
   err = startup_authinit_reply (authreply, authreplytype, 0, authproc,
                                MACH_MSG_TYPE_COPY_SEND);
   assert_perror (err);
   err = mach_port_deallocate (mach_task_self (), authproc);
   assert_perror (err);
 
+  if (verbose)
+    fprintf (stderr, "auth launched\n");
+
   /* Give the library our auth and proc server ports.  */
   _hurd_port_set (&_hurd_ports[INIT_PORT_AUTH], authserver);
   _hurd_port_set (&_hurd_ports[INIT_PORT_PROC], procserver);
@@ -743,12 +801,7 @@ launch_core_servers (void)
   err = proc_mark_exec (fsproc);
   assert_perror (err);
 
-#if 0
-  printf ("Init has completed.\n");
-  fflush (stdout);
-#endif
-  printf (".\n");
-  fflush (stdout);
+  fprintf (stderr, ".\n");
 
   /* Tell the proc server our msgport.  Be sure to do this after we are all
      done making requests of proc.  Once we have done this RPC, proc
@@ -761,11 +814,17 @@ launch_core_servers (void)
   if (old != MACH_PORT_NULL)
     mach_port_deallocate (mach_task_self (), old);
 
+  if (verbose)
+    fprintf (stderr, "Message port registered\n");
+
   /* Give the bootstrap FS its proc and auth ports.  */
   err = fsys_init (bootport, fsproc, MACH_MSG_TYPE_COPY_SEND, authserver);
   mach_port_deallocate (mach_task_self (), fsproc);
   if (err)
     error (0, err, "fsys_init"); /* Not necessarily fatal.  */
+
+  if (verbose)
+    fprintf (stderr, "Fixed up bootstrap filesystem\n");
 }
 
 /* Set up the initial value of the standard exec data. */
@@ -832,6 +891,9 @@ frob_kernel_process (void)
   task_t task;
   process_t proc, kbs;
 
+  if (verbose)
+    fprintf (stderr, "Frobbing kernel process\n");
+
   err = proc_pid2task (procserver, HURD_PID_KERNEL, &task);
   if (err)
     {
@@ -1052,6 +1114,9 @@ start_child (const char *prog, char **progargs)
     }
   assert_perror (err);
 
+  if (verbose)
+    fprintf (stderr, "Going to execute '%s'\n", args);
+
   file = file_name_lookup (args, O_EXEC, 0);
   if (file == MACH_PORT_NULL)
     {
@@ -1071,7 +1136,7 @@ start_child (const char *prog, char **progargs)
 
   if (bootstrap_args & RB_KDB)
     {
-      printf ("Pausing for %s\n", args);
+      fprintf (stderr, "Pausing for %s\n", args);
       getchar ();
     }
 
@@ -1138,6 +1203,9 @@ void
 launch_system (void)
 {
   launch_something (0);
+
+  if (verbose)
+    fprintf (stderr, "Init has completed\n");
 }
 
 /** RPC servers **/
@@ -1158,6 +1226,9 @@ S_startup_procinit (startup_t server,
     /* Only one proc server.  */
     return EPERM;
 
+  if (verbose)
+    fprintf (stderr, "Received startup message from proc\n");
+
   procserver = proc;
 
   procreply = reply;
@@ -1184,6 +1255,9 @@ S_startup_authinit (startup_t server,
     /* Only one auth server.  */
     return EPERM;
 
+  if (verbose)
+    fprintf (stderr, "Received startup message from auth\n");
+
   authserver = auth;
 
   /* Save the reply port until we get startup_procinit.  */
@@ -1216,6 +1290,10 @@ S_startup_essential_task (mach_port_t server,
   if (credential != host_priv)
     return EPERM;
 
+  if (verbose)
+    fprintf (stderr, "Received startup essential message from '%s'\n",
+             name);
+
   fail = record_essential_task (name, task);
   if (fail)
     return fail;
@@ -1234,8 +1312,13 @@ S_startup_essential_task (mach_port_t server,
       else if (!strcmp (name, "proc"))
        procinit = 1;
 
+      if (verbose)
+        fprintf (stderr, "  still waiting for:");
+
       if (authinit && execinit && procinit)
        {
+          if (verbose)
+            fprintf (stderr, " none!\n");
          /* Reply to this RPC, after that everything
             is ready for real startup to begin. */
          startup_essential_task_reply (reply, replytype, 0);
@@ -1249,6 +1332,17 @@ S_startup_essential_task (mach_port_t server,
 
          return MIG_NO_REPLY;
        }
+
+      if (verbose)
+        {
+          if (! authinit)
+            fprintf (stderr, " auth");
+          if (! execinit)
+            fprintf (stderr, " exec");
+          if (! procinit)
+            fprintf (stderr, " proc");
+          fprintf (stderr, "\n");
+        }
     }
 
   return 0;
@@ -1349,41 +1443,6 @@ S_startup_reboot (mach_port_t server,
   for (;;);
 }
 
-/* Stubs for unused notification RPCs.  */
-
-kern_return_t
-do_mach_notify_port_destroyed (mach_port_t notify,
-                              mach_port_t rights)
-{
-  return EOPNOTSUPP;
-}
-
-kern_return_t
-do_mach_notify_send_once (mach_port_t notify)
-{
-  return EOPNOTSUPP;
-}
-
-kern_return_t
-do_mach_notify_no_senders (mach_port_t port, mach_port_mscount_t mscount)
-{
-  return EOPNOTSUPP;
-}
-
-kern_return_t
-do_mach_notify_port_deleted (mach_port_t notify,
-                            mach_port_t name)
-{
-  return EOPNOTSUPP;
-}
-
-kern_return_t
-do_mach_notify_msg_accepted (mach_port_t notify,
-                            mach_port_t name)
-{
-  return EOPNOTSUPP;
-}
-
 /* msg server */
 
 kern_return_t
@@ -1632,74 +1691,55 @@ S_fsys_getroot (mach_port_t fsys_t,
 }
 
 error_t
-S_fsys_goaway (mach_port_t control, int flags)
+S_fsys_get_options (mach_port_t control,
+                   char **data, mach_msg_type_number_t *len)
 {
+  /* XXX Implement me.  */
   return EOPNOTSUPP;
 }
 
 error_t
-S_fsys_startup (mach_port_t bootstrap, int flags, mach_port_t control,
-               mach_port_t *real, mach_msg_type_name_t *realtype)
+S_file_check_access (mach_port_t server,
+                     int *allowed)
 {
-  return EOPNOTSUPP;
+  if (server != startup)
+    return EOPNOTSUPP;
+  *allowed = 0;
+  return 0;
 }
 
 error_t
-S_fsys_syncfs (mach_port_t control,
-              int wait,
-              int recurse)
+S_io_stat (mach_port_t server,
+           struct stat *st)
 {
-  return EOPNOTSUPP;
-}
+  if (server != startup)
+    return EOPNOTSUPP;
 
-error_t
-S_fsys_set_options (mach_port_t control,
-                   char *data, mach_msg_type_number_t len,
-                   int do_children)
-{
-  return EOPNOTSUPP;
-}
+  memset (st, 0, sizeof *st);
 
-error_t
-S_fsys_get_options (mach_port_t control,
-                   char **data, mach_msg_type_number_t *len)
-{
-  return EOPNOTSUPP;
-}
+  st->st_fstype = FSTYPE_MISC;
+  st->st_fsid = getpid ();
+  st->st_mode = S_IFCHR | S_IROOT;
 
-error_t
-S_fsys_getfile (mach_port_t control,
-               uid_t *uids, size_t nuids,
-               uid_t *gids, size_t ngids,
-               char *handle, size_t handllen,
-               mach_port_t *pt,
-               mach_msg_type_name_t *pttype)
-{
-  return EOPNOTSUPP;
+  return 0;
 }
 
 error_t
-S_fsys_getpriv (mach_port_t control,
-               mach_port_t *host_priv, mach_msg_type_name_t *host_priv_type,
-               mach_port_t *dev_master, mach_msg_type_name_t *dev_master_type,
-               task_t *fs_task, mach_msg_type_name_t *fs_task_type)
+S_io_restrict_auth (mach_port_t server,
+                    mach_port_t *newport,
+                    mach_msg_type_name_t *newporttype,
+                    uid_t *uids, size_t nuids,
+                    uid_t *gids, size_t ngids)
 {
-  return EOPNOTSUPP;
-}
+  struct idvec user = { uids, (unsigned) nuids, (unsigned) nuids };
 
-error_t
-S_fsys_init (mach_port_t control,
-          mach_port_t reply,
-          mach_msg_type_name_t replytype,
-          mach_port_t proc,
-          auth_t auth)
-{
-  return EOPNOTSUPP;
-}
+  if (server != startup)
+    return EOPNOTSUPP;
 
-error_t
-S_fsys_forward (mach_port_t server, mach_port_t requestor,
-               char *argz, size_t argz_len)
-{
-  return EOPNOTSUPP;
+  if (! idvec_contains (&user, 0))
+    return EPERM;
+
+  *newport = server;
+  *newporttype = MACH_MSG_TYPE_COPY_SEND;
+  return 0;
 }
diff --git a/sutils/Makefile b/sutils/Makefile
index f4e1811..b74a506 100644
--- a/sutils/Makefile
+++ b/sutils/Makefile
@@ -38,6 +38,6 @@ $(progs): %: %.o ../libshouldbeinlibc/libshouldbeinlibc.a
 
 install: $(prefix)/dev/MAKEDEV
 $(prefix)/dev/MAKEDEV: $(prefix)/dev
-       ln -s ../sbin/MAKEDEV $@
+       ln -sf ../sbin/MAKEDEV $@
 $(prefix)/dev:
        @$(MKINSTALLDIRS) $@
diff --git a/sutils/swapon.c b/sutils/swapon.c
index ae830ba..40f5a20 100644
--- a/sutils/swapon.c
+++ b/sutils/swapon.c
@@ -424,9 +424,7 @@ swaponoff (const char *file, int add, int skipnotexisting)
     {
       /* The default pager does not support the new protocol.
         We tried it in a previous call (below) and got MIG_BAD_ID.  */
-      char pname[sizeof "/dev/" + strlen (store->name) + 1];
-      strcpy (stpcpy (pname, "/dev/"), store->name);
-      err = default_pager_paging_file (def_pager, dev_master, pname, add);
+      err = default_pager_paging_file (def_pager, dev_master, file, add);
     }
   else
     {
@@ -439,7 +437,7 @@ swaponoff (const char *file, int add, int skipnotexisting)
          runs[j++] = store->runs[i].length;
        }
       err = default_pager_paging_storage (def_pager, store->port,
-                                         runs, j, store->name, add);
+                                         runs, j, file, add);
       if (err == MIG_BAD_ID)
        {
          /* The default pager does not support the new protocol.
@@ -571,7 +569,7 @@ main (int argc, char *argv[])
 
       get_def_pager();
 
-      err = default_pager_storage_info (def_pager, &free, &nfree, &size, 
&nsize,
+      err = default_pager_storage_info (def_pager, &size, &nsize, &free, 
&nfree,
                                        &names, &names_len);
       if (err)
        error (3, 0, "Can not get default pager storage information");
@@ -580,7 +578,7 @@ main (int argc, char *argv[])
       name = names;
       for (i = 0; i < nfree; i++)
        {
-         printf ("/dev/%s\tpartition\t%zuM\t%zuM\t-1\n",
+         printf ("%s\tpartition\t%zuM\t%zuM\t-1\n",
                  name, size[i] >> 20, (size[i] - free[i]) >> 20);
          name = argz_next (names, names_len, name);
        }
diff --git a/trans/fakeroot.c b/trans/fakeroot.c
index cb4f818..ad7bec9 100644
--- a/trans/fakeroot.c
+++ b/trans/fakeroot.c
@@ -108,6 +108,9 @@ new_node (file_t file, mach_port_t idport, int locked, int 
openmodes,
     }
   nn->faked = FAKE_DEFAULT;
 
+  /* The light reference allows us to safely keep the node in the
+     hash table.  */
+  netfs_nref_light (*np);
   if (!locked)
     pthread_mutex_lock (&idport_ihash_lock);
   err = hurd_ihash_add (&idport_ihash, nn->idport, *np);
@@ -155,22 +158,31 @@ set_faked_attribute (struct node *np, unsigned int faked)
     }
 }
 
+void
+netfs_try_dropping_softrefs (struct node *np)
+{
+  /* We have to drop our light reference by removing the node from the
+     idport_ihash hash table.  */
+  pthread_mutex_lock (&idport_ihash_lock);
+
+  hurd_ihash_locp_remove (&idport_ihash, netfs_node_netnode (np)->idport_locp);
+  pthread_mutex_unlock (&idport_ihash_lock);
+
+  netfs_nrele_light (np);
+}
+
 /* Node NP has no more references; free all its associated storage. */
 void
 netfs_node_norefs (struct node *np)
 {
   pthread_mutex_unlock (&np->lock);
-  pthread_spin_unlock (&netfs_node_refcnt_lock);
 
-  pthread_mutex_lock (&idport_ihash_lock);
-  hurd_ihash_locp_remove (&idport_ihash, netfs_node_netnode (np)->idport_locp);
-  pthread_mutex_unlock (&idport_ihash_lock);
+  /* NP was already removed from idport_ihash through
+     netfs_try_dropping_softrefs.  */
 
   mach_port_deallocate (mach_task_self (), netfs_node_netnode (np)->file);
   mach_port_deallocate (mach_task_self (), netfs_node_netnode (np)->idport);
   free (np);
-
-  pthread_spin_lock (&netfs_node_refcnt_lock);
 }
 
 /* This is the cleanup function we install in netfs_protid_class.  If
@@ -363,29 +375,27 @@ netfs_S_dir_lookup (struct protid *diruser,
  redo_hash_lookup:
   pthread_mutex_lock (&idport_ihash_lock);
   pthread_mutex_lock (&dnp->lock);
-  /* The hashtable may not hold a true reference on the node.  Acquire the
-     refcount lock so that, if a node is found, its reference counter cannot
-     drop to 0 before we get our own reference.  */
-  pthread_spin_lock (&netfs_node_refcnt_lock);
   np = hurd_ihash_find (&idport_ihash, idport);
   if (np != NULL)
     {
-      /* We already know about this node.  */
+      /* We quickly check that NP has hard references. If the node is being
+         removed, netfs_try_dropping_softrefs is attempting to drop the light
+         reference on this.  */
+      struct references result;
+
+      refcounts_references (&np->refcounts, &result);
 
-      if (np->references == 0)
+      if (result.hard == 0)
        {
-         /* But it might be in the process of being released.  If so,
-            unlock the hash table to give the node a chance to actually
+         /* If so, unlock the hash table to give the node a chance to actually
             be removed and retry.  */
-         pthread_spin_unlock (&netfs_node_refcnt_lock);
          pthread_mutex_unlock (&dnp->lock);
          pthread_mutex_unlock (&idport_ihash_lock);
          goto redo_hash_lookup;
        }
 
       /* Otherwise, reference it right away.  */
-      np->references++;
-      pthread_spin_unlock (&netfs_node_refcnt_lock);
+      netfs_nref (np);
 
       mach_port_deallocate (mach_task_self (), idport);
 
@@ -405,7 +415,6 @@ netfs_S_dir_lookup (struct protid *diruser,
     }
   else
     {
-      pthread_spin_unlock (&netfs_node_refcnt_lock);
       err = new_node (file, idport, 1, flags & (O_RDWR|O_EXEC), &np);
       pthread_mutex_unlock (&dnp->lock);
       if (!err)
diff --git a/trans/ifsock.c b/trans/ifsock.c
index 13a2133..908fe13 100644
--- a/trans/ifsock.c
+++ b/trans/ifsock.c
@@ -51,7 +51,7 @@ struct port_class *node_class;
 struct port_bucket *port_bucket;
 
 int trivfs_fstype = FSTYPE_IFSOCK;
-int trivfs_fsid = 0; /* ??? */
+int trivfs_fsid = 0;
 
 int trivfs_support_read = 0;
 int trivfs_support_write = 0;
diff --git a/trans/proxy-defpager.c b/trans/proxy-defpager.c
index 9a8436a..e2b15ea 100644
--- a/trans/proxy-defpager.c
+++ b/trans/proxy-defpager.c
@@ -286,8 +286,6 @@ main (int argc, char **argv)
   if (bootstrap == MACH_PORT_NULL)
     error (1, 0, "Must be started as a translator");
 
-  trivfs_fsid = getpid ();
-
   err = trivfs_add_protid_port_class (&trivfs_protid_class);
   if (err)
     error (1, 0, "error creating protid port class");
diff --git a/usermux/mux.c b/usermux/mux.c
index bfa95fd..7c57f94 100644
--- a/usermux/mux.c
+++ b/usermux/mux.c
@@ -298,10 +298,8 @@ lookup_cached (struct usermux *mux, const char *user, int 
purge,
 
       if (strcasecmp (user, nm->name) == 0)
        {
-         pthread_spin_lock (&netfs_node_refcnt_lock);
-         if (nm->node)
-           nm->node->references++;
-         pthread_spin_unlock (&netfs_node_refcnt_lock);
+          if (nm->node)
+            netfs_nref (nm->node);
 
          if (nm->node)
            {
diff --git a/utils/settrans.c b/utils/settrans.c
index 00cc358..e01906b 100644
--- a/utils/settrans.c
+++ b/utils/settrans.c
@@ -64,6 +64,8 @@ static struct argp_option options[] =
   {"exclusive",   'x', 0, 0, "Only set the translator if there is not one 
already"},
   {"orphan",      'o', 0, 0, "Disconnect old translator from the filesystem "
                             "(do not ask it to go away)"},
+  {"underlying",  'U', "NODE", 0, "Open NODE and hand it to the translator "
+                                 "as the underlying node"},
 
   {"chroot",      'C', 0, 0,
    "Instead of setting the node's translator, take following arguments up to"
@@ -88,6 +90,44 @@ static char *args_doc = "NODE [TRANSLATOR ARG...]";
 static char *doc = "Set the passive/active translator on NODE."
 "\vBy default the passive translator is set.";
 
+/* Authentication of the current process.  */
+uid_t *uids;
+gid_t *gids;
+size_t uids_len, gids_len;
+
+/* Initialize and populate the uids and gids vectors.  */
+error_t
+get_credentials (void)
+{
+  /* Fetch uids...  */
+  uids_len = geteuids (0, 0);
+  if (uids_len < 0)
+    return errno;
+
+  uids = malloc (uids_len * sizeof (uid_t));
+  if (! uids)
+    return ENOMEM;
+
+  uids_len = geteuids (uids_len, uids);
+  if (uids_len < 0)
+    return errno;
+
+  /* ... and gids.  */
+  gids_len = getgroups (0, 0);
+  if (gids_len < 0)
+    return errno;
+
+  gids = malloc (gids_len * sizeof (gid_t));
+  if (! uids)
+    return ENOMEM;
+
+  gids_len = getgroups (gids_len, gids);
+  if (gids_len < 0)
+    return errno;
+
+  return 0;
+}
+
 /* ---------------------------------------------------------------- */
 
 int
@@ -119,6 +159,7 @@ main(int argc, char *argv[])
   char *pid_file = NULL;
   int excl = 0;
   int timeout = DEFAULT_TIMEOUT * 1000; /* ms */
+  char *underlying_node_name = NULL;
   char **chroot_command = 0;
   char *chroot_chdir = "/";
 
@@ -164,6 +205,11 @@ main(int argc, char *argv[])
          break;
 
        case 'o': orphan = 1; break;
+       case 'U':
+         underlying_node_name = strdup (arg);
+         if (underlying_node_name == NULL)
+           error(3, ENOMEM, "Failed to duplicate argument");
+         break;
 
        case 'C':
          if (chroot_command)
@@ -292,7 +338,20 @@ main(int argc, char *argv[])
              return open_err;
            }
 
-         *underlying = node;
+         if (underlying_node_name)
+           {
+             *underlying = file_name_lookup (underlying_node_name,
+                                             flags | lookup_flags, 0666);
+             if (! MACH_PORT_VALID (*underlying))
+               {
+                 /* For the error message.  */
+                 node_name = underlying_node_name;
+                 open_err = errno;
+                 return open_err;
+               }
+           }
+         else
+           *underlying = node;
          *underlying_type = MACH_MSG_TYPE_COPY_SEND;
 
          return 0;
@@ -340,9 +399,14 @@ main(int argc, char *argv[])
          mach_port_t root;
          file_t executable;
          char *prefixed_name;
+
+         err = get_credentials ();
+         if (err)
+           error (6, err, "getting credentials");
+
          err = fsys_getroot (active_control,
                              MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND,
-                             NULL, 0, NULL, 0, 0,
+                             uids, uids_len, gids, gids_len, 0,
                              &do_retry, retry_name, &root);
          mach_port_deallocate (mach_task_self (), active_control);
          if (err)

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/pkg-hurd/hurd.git



reply via email to

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