commit-hurd
[Top][All Lists]
Advanced

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

[hurd] 01/01: New upstream snapshot


From: Samuel Thibault
Subject: [hurd] 01/01: New upstream snapshot
Date: Sun, 09 Nov 2014 11:05:07 +0000

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

sthibault pushed a commit to branch upstream-merged
in repository hurd.

commit 0845eb9b7e42b40c85c7f0784cf96f12167aa3ee
Author: Samuel Thibault <address@hidden>
Date:   Sat Nov 8 19:53:34 2014 +0000

    New upstream snapshot
---
 Makeconf                                 |   4 +
 Makefile                                 |   4 +-
 NEWS                                     |  22 ++
 TODO                                     |   2 -
 boot/boot.c                              |   2 +-
 configure.ac                             |  10 +-
 console-client/Makefile                  |   8 +-
 console/pager.c                          |  29 +--
 doc/hurd.texi                            |   4 +-
 exec/elfcore.c                           |   4 +-
 exec/exec.c                              |  10 +
 exec/main.c                              | 106 +++++++-
 ext2fs/dir.c                             |  32 +--
 ext2fs/pager.c                           |  30 +--
 fatfs/pager.c                            |  28 +--
 ftpfs/dir.c                              |  20 +-
 hurd/default_pager.defs                  |   2 +-
 hurd/fs.defs                             |   4 +-
 hurd/hurd_types.h                        |   1 +
 hurd/paths.h                             |   2 +
 hurd/shared.h                            |   2 +-
 hurd/term.defs                           |  14 +-
 include/refcount.h                       |  77 +++++-
 libcons/extra-version.c                  |   2 +-
 libcons/vcons-add.c                      |   2 +-
 libcons/vcons-remove.c                   |   2 +-
 libdiskfs/boot-start.c                   |   7 +-
 libdiskfs/dir-rename.c                   |   1 -
 libdiskfs/disk-pager.c                   |  28 +--
 libdiskfs/diskfs.h                       |  33 ++-
 libdiskfs/extra-version.c                |   2 +-
 libdiskfs/file-chflags.c                 |   8 +
 libdiskfs/file-get-children.c            |   9 +-
 libdiskfs/file-statfs.c                  |   2 +
 libdiskfs/get-source.c                   |   2 +-
 libdiskfs/init-init.c                    |   4 +
 libdiskfs/io-duplicate.c                 |   3 +
 libdiskfs/io-reauthenticate.c            |   2 +
 libdiskfs/io-restrict-auth.c             |   3 +
 libdiskfs/lookup.c                       |  31 ---
 libdiskfs/name-cache.c                   | 410 ++++++++++++++++++++-----------
 libdiskfs/node-make.c                    |  39 ++-
 libdiskfs/peropen-make.c                 |   2 +-
 libdiskfs/protid-make.c                  |   6 +-
 libdiskfs/readonly-changed.c             |   2 +-
 libdiskfs/sync-default.c                 |   2 +-
 libdiskfs/validate-author.c              |   2 +-
 libdiskfs/validate-flags.c               |   2 +-
 libdiskfs/validate-group.c               |   2 +-
 libdiskfs/validate-mode.c                |   2 +-
 libdiskfs/validate-owner.c               |   2 +-
 libdiskfs/validate-rdev.c                |   2 +-
 libfshelp/fshelp.h                       |   9 +-
 libfshelp/translator-list.c              |  28 ++-
 libihash/ihash.h                         |   3 +-
 libmachdev/net.c                         |   6 +-
 libnetfs/file-get-children.c             |   9 +-
 libnetfs/file-get-storage-info-default.c |   2 +-
 libnetfs/get-source.c                    |   2 +-
 libnetfs/init-init.c                     |   3 +
 libnetfs/make-node.c                     |  29 ++-
 libnetfs/netfs.h                         |  27 ++
 libnetfs/set-get-trans.c                 |   4 +-
 libnetfs/trans-callback.c                |   5 +-
 libpager/Makefile                        |   2 +-
 libpager/chg-compl.c                     |   4 +-
 libpager/data-request.c                  |   3 -
 libpager/data-return.c                   |   4 -
 libpager/data-unlock.c                   |   5 -
 libpager/demuxer.c                       | 287 +++++++++++++++++++++-
 libpager/lock-completed.c                |   2 -
 libpager/no-senders.c                    |   1 -
 libpager/notify-stubs.c                  |  10 -
 libpager/object-init.c                   |   2 -
 libpager/object-terminate.c              |   8 +-
 libpager/pager.h                         |   7 +-
 libpager/priv.h                          |   4 -
 libpager/queue.h                         |  61 +++++
 libpager/seqnos.c                        |  79 ------
 libpager/stubs.c                         |   9 -
 libpipe/pipe.c                           |   6 +-
 libports/bucket-iterate.c                |  22 +-
 libports/claim-right.c                   |   5 +-
 libports/class-iterate.c                 |  10 +-
 libports/complete-deallocate.c           |  19 +-
 libports/create-bucket.c                 |   6 -
 libports/create-class.c                  |   1 -
 libports/create-internal.c               |  22 +-
 libports/dead-name.c                     |   2 +-
 libports/destroy-right.c                 |   5 +-
 libports/get-right.c                     |   2 +-
 libports/import-port.c                   |  22 +-
 libports/inhibit-all-rpcs.c              |  27 +-
 libports/inhibit-bucket-rpcs.c           |   3 +-
 libports/inhibit-class-rpcs.c            |  27 +-
 libports/init.c                          |   7 +-
 libports/lookup-port.c                   |  29 +--
 libports/port-deref-weak.c               |  10 +-
 libports/port-deref.c                    |  34 ++-
 libports/port-ref-weak.c                 |   6 +-
 libports/port-ref.c                      |   6 +-
 libports/ports.h                         |  26 +-
 libports/reallocate-from-external.c      |  17 +-
 libports/reallocate-port.c               |   9 +-
 libports/transfer-right.c                |  20 +-
 libshouldbeinlibc/idvec-impgids.c        |  14 +-
 libshouldbeinlibc/timefmt.c              |   2 +-
 libstore/part.c                          |  11 +
 libthreads/cthreads.c                    |   9 +-
 libtrivfs/get-source.c                   |   2 +-
 libtrivfs/protid-clean.c                 |   4 +-
 mach-defpager/default_pager.c            |  11 +
 mach-defpager/mig-decls.h                |   6 +
 mach-defpager/mig-mutate.h               |   3 +
 pfinet/linux-src/net/ipv4/af_inet.c      |   1 +
 pfinet/main.c                            |   2 +-
 procfs/Makefile                          |  77 +++---
 procfs/process.c                         | 106 +++++++-
 procfs/rootdir.c                         | 212 ++++++++++------
 random/random.c                          |  21 +-
 storeio/pager.c                          |  26 +-
 sutils/MAKEDEV.sh                        |   4 +-
 term/main.c                              |  24 +-
 {mach-defpager => term}/mig-decls.h      |  24 +-
 term/mig-mutate.h                        |  10 +-
 term/term.h                              |   5 +
 term/users.c                             |   4 +-
 tmpfs/tmpfs.c                            |   3 +-
 trans/fakeroot.c                         | 136 +++++-----
 trans/hello-mt.c                         |  28 ++-
 trans/hello.c                            |  21 +-
 trans/ifsock.c                           |   2 +-
 trans/mtab.c                             |  15 +-
 trans/proxy-defpager.c                   |   2 +
 utils/login.c                            |   4 +-
 utils/mount.c                            |  39 ++-
 utils/rpctrace.c                         |  10 +-
 utils/settrans.c                         |  48 +++-
 utils/umount.c                           |   5 +-
 139 files changed, 1873 insertions(+), 1007 deletions(-)

diff --git a/Makeconf b/Makeconf
index 5cf995d..32eec13 100644
--- a/Makeconf
+++ b/Makeconf
@@ -576,13 +576,17 @@ vpath %.defs $(top_srcdir)/hurd
 mach_defs_names = bootstrap exc mach mach4 \
        mach_host mach_port mach_timer_reply memory_object \
        memory_object_default notify
+mach_debug_defs_names = mach_debug
 device_defs_names = dev_forward device device_reply device_request
 
 mach_defs = $(addsuffix .defs,$(mach_defs_names))
+mach_debug_defs = $(addsuffix .defs,$(mach_debug_defs_names))
 device_defs = $(addsuffix .defs,$(device_defs_names))
 
 $(mach_defs): %.defs:
        echo '#include <mach/$@>' > $@
+$(mach_debug_defs): %.defs:
+       echo '#include <mach_debug/$@>' > $@
 $(device_defs): %.defs:
        echo '#include <device/$@>' > $@
 
diff --git a/Makefile b/Makefile
index 106f2f6..0b9eff2 100644
--- a/Makefile
+++ b/Makefile
@@ -37,7 +37,9 @@ prog-subdirs = auth proc exec init term \
               login daemons boot console \
               hostmux usermux ftpfs trans \
               console-client utils sutils \
-              benchmarks fstests
+              benchmarks fstests \
+              random \
+              procfs \
 
 ifeq ($(HAVE_SUN_RPC),yes)
 prog-subdirs += nfs nfsd
diff --git a/NEWS b/NEWS
index 72a2e2d..b047aed 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,25 @@
+2013-XX-XX
+Version 0.6
+
+Numerous cleanups and stylistic fixes of the code base.  Several
+problems have been identified using static analysis and exercising
+tools, and have subsequently been fixed.
+
+The message dispatching code in the Hurd servers has been improved.
+
+The embedded gz and bz2 decompressor code has been removed, libz and
+libbz2 is used instead.
+
+The native fakeroot tool has been greatly improved and is now able to
+build many packages.  The portinfo and rpctrace tools now offer a
+better debugging experience.
+
+The performance of the integer hashing library has been improved.
+
+The procfs translator has been merged.
+
+The random translator has been merged.
+
 2013-09-27
 Version 0.5
 
diff --git a/TODO b/TODO
index d2500dc..0387e9f 100644
--- a/TODO
+++ b/TODO
@@ -108,8 +108,6 @@ See `tasks', the exported task list.
    Rename the rest to libhurdutil or somesuch.
 
 ** libdiskfs
-*** file_chflags does not do proper permission checking (non-root isn't
-    supposed to be able to change the low bits)
 *** Add the short-circuited-but-not-builtin translator startup code from
     dir-lookup to fsys_getroot.  Compare and match carefully these two
     routines and then share common code.
diff --git a/boot/boot.c b/boot/boot.c
index ed29014..03617f5 100644
--- a/boot/boot.c
+++ b/boot/boot.c
@@ -1876,7 +1876,7 @@ S_io_revoke (mach_port_t obj,
    support on the console device.  */
 
 kern_return_t
-S_termctty_open_terminal (mach_port_t object,
+S_termctty_open_terminal (ctty_t object,
                          int flags,
                          mach_port_t *result,
                          mach_msg_type_name_t *restype)
diff --git a/configure.ac b/configure.ac
index 873ced8..f8856db 100644
--- a/configure.ac
+++ b/configure.ac
@@ -83,6 +83,13 @@ AC_PROG_INSTALL
 AC_PROG_AWK
 AC_PROG_SED
 
+if test "x$cross_compiling" = "xyes"; then
+   # It may be that we don't have a working libc yet, for instance
+   # because we're bootstrapping the cross-compilation tool chain.
+   # Thus, use this undocumented Autoconf macro designed for this.
+   AC_NO_EXECUTABLES
+   AC_MSG_WARN("cross-compiling, disabling linking")
+fi
 AC_PROG_CC
 # Require GCC.
 if test x$GCC != xyes; then
@@ -156,9 +163,6 @@ else
 fi
 AC_SUBST(VERSIONING)
 
-# Check if libc contains getgrouplist and/or uselocale.
-AC_CHECK_FUNCS(getgrouplist uselocale)
-
 
 # From glibc HEAD, 2007-11-07.
 AC_CACHE_CHECK(for -fgnu89-inline, libc_cv_gnu89_inline, [dnl
diff --git a/console-client/Makefile b/console-client/Makefile
index f576cbe..03dad1f 100644
--- a/console-client/Makefile
+++ b/console-client/Makefile
@@ -26,13 +26,9 @@ PC_KBD_SO_SRCS = pc-kbd.c kbd-repeat.c
 PC_MOUSE_SO_SRCS = pc-mouse.c
 GENERIC_SPEAKER_SO_SRCS = generic-speaker.c
 CURRENT_VCS_SO_SRCS = current-vcs.c
-ifneq ($(LIBNCURSESW),)
-NCURSESW_SO_SRCS = ncursesw.c
-endif
 SRCS = $(CONSOLE_SRCS) \
   $(VGA_SO_SRCS) $(PC_KBD_SO_SRCS) $(PC_MOUSE_SO_SRCS) \
-  $(GENERIC_SPEAKER_SO_SRCS) $(CURRENT_VCS_SO_SRCS) $(NCURSESW_SO_SRCS) \
-  $(XKB_SRCS)
+  $(GENERIC_SPEAKER_SO_SRCS) $(CURRENT_VCS_SO_SRCS) $(XKB_SRCS)
 
 VPATH += $(srcdir)/xkb
 OBJS = $(addsuffix .o,$(basename $(notdir $(SRCS)))) kdioctlServer.o
@@ -71,6 +67,8 @@ generic_speaker.so.$(hurd-version): $(patsubst 
%.c,%_pic.o,$(GENERIC_SPEAKER_SO_
 current_vcs.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(CURRENT_VCS_SO_SRCS))
 
 ifneq ($(LIBNCURSESW),)
+NCURSESW_SO_SRCS = ncursesw.c
+SRCS += $(NCURSESW_SO_SRCS)
 modules += ncursesw
 ncursesw.so.$(hurd-version): $(patsubst %.c,%_pic.o,$(NCURSESW_SO_SRCS))
 ncursesw-CPPFLAGS = $(NCURSESW_INCLUDE)
diff --git a/console/pager.c b/console/pager.c
index 87c36f0..3568211 100644
--- a/console/pager.c
+++ b/console/pager.c
@@ -119,22 +119,6 @@ void
 pager_dropweak (struct user_pager_info *upi)
 {
 }
-
-
-/* A top-level function for the paging thread that just services paging
-   requests.  */
-static void *
-service_paging_requests (void *arg)
-{
-  struct port_bucket *pager_bucket = arg;
-  for (;;)
-    ports_manage_port_operations_multithread (pager_bucket,
-                                              pager_demuxer,
-                                              1000 * 60 * 2,
-                                              1000 * 60 * 10, 0);
-  return NULL;
-}    
-
 
 /* Initialize the pager for the display component.  */
 void
@@ -148,15 +132,10 @@ user_pager_init (void)
   if (! pager_bucket)
     error (5, errno, "Cannot create pager bucket");
 
-  /* Make a thread to service paging requests.  */
-  err = pthread_create (&thread, NULL, service_paging_requests, pager_bucket);
-  if (!err)
-    pthread_detach (thread);
-  else
-    {
-      errno = err;
-      perror ("pthread_create");
-    }
+  /* Start libpagers worker threads.  */
+  err = pager_start_workers (pager_bucket);
+  if (err)
+    error (5, err, "Cannot start pager worker threads");
 }
 
 
diff --git a/doc/hurd.texi b/doc/hurd.texi
index 07ddfb4..8fa6da7 100644
--- a/doc/hurd.texi
+++ b/doc/hurd.texi
@@ -4431,13 +4431,13 @@ provides.
 @deftypefun error_t diskfs_create_protid (@w{struct peropen address@hidden, 
@w{struct iouser address@hidden, @w{struct protid address@hidden)
 Create and return a protid for an existing peropen @var{po} in
 @var{cred}, referring to user @var{user}.  The node @address@hidden>np}
-must be locked.
+must be locked.  On success, a reference to @var{po} is consumed.
 @end deftypefun
 
 @deftypefun error_t diskfs_start_protid (@w{struct peropen address@hidden, 
@w{struct protid address@hidden)
 Build and return in @var{cred} a protid which has no user
 identification, for peropen @var{po}.  The node @address@hidden>np} must
-be locked.
+be locked.  On success, a reference to @var{po} is consumed.
 @end deftypefun
 
 @deftypefun void diskfs_finish_protid (@w{struct protid address@hidden, 
@w{struct iouser address@hidden)
diff --git a/exec/elfcore.c b/exec/elfcore.c
index 1f9238b..b09f669 100644
--- a/exec/elfcore.c
+++ b/exec/elfcore.c
@@ -334,7 +334,7 @@ dump_core (task_t task, file_t file, off_t corelimit,
     mach_msg_type_number_t num_waits = 0;
     char pibuf[offsetof (struct procinfo, threadinfos[2])];
     struct procinfo *pi = (void *) &pibuf;
-    mach_msg_type_number_t pi_size = sizeof pibuf;
+    mach_msg_type_number_t pi_size = sizeof pibuf / sizeof (*(procinfo_t)0);
 
     memset (&pstatus.data, 0, sizeof pstatus.data);
     memset (&psinfo.data, 0, sizeof psinfo.data);
@@ -395,7 +395,7 @@ dump_core (task_t task, file_t file, off_t corelimit,
        psinfo.data.pr_wstat = pi->exitstatus;
 
        if ((void *) pi != &pibuf)
-         munmap (pi, pi_size);
+         munmap (pi, pi_size * sizeof (*(procinfo_t) 0));
       }
     if (err == 0)
       {
diff --git a/exec/exec.c b/exec/exec.c
index 2fc1e44..0ecf2d3 100644
--- a/exec/exec.c
+++ b/exec/exec.c
@@ -1116,6 +1116,16 @@ do_exec (file_t file,
        mach_port_destroy (oldtask, destroynames[i]);
     }
 
+  /* Map page zero redzoned.  */
+  {
+    vm_address_t addr = 0;
+    e.error = vm_map (newtask,
+                     &addr, vm_page_size, 0, 0, MACH_PORT_NULL, 0, 1,
+                     VM_PROT_NONE, VM_PROT_NONE, VM_INHERIT_COPY);
+    if (e.error)
+      goto out;
+  }
+
 /* XXX this should be below
    it is here to work around a vm_map kernel bug. */
   if (interp.file != MACH_PORT_NULL)
diff --git a/exec/main.c b/exec/main.c
index 78faebd..784000b 100644
--- a/exec/main.c
+++ b/exec/main.c
@@ -45,6 +45,7 @@ int trivfs_cntl_nportclasses = 1;
 struct trivfs_control *fsys;
 
 char **save_argv;
+mach_port_t opt_device_master;
 
 
 #include "exec_S.h"
@@ -104,16 +105,104 @@ deadboot (void *p)
   ports_enable_class (trivfs_cntl_portclasses[0]);
 }
 
+#define OPT_DEVICE_MASTER_PORT (-1)
+
+static const struct argp_option options[] =
+{
+  {"device-master-port", OPT_DEVICE_MASTER_PORT, "PORT", 0,
+   "If specified, a boot-time exec server can print "
+   "diagnostic messages earlier.", 0},
+  {0}
+};
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+  switch (opt)
+    {
+    default:
+      return ARGP_ERR_UNKNOWN;
+    case ARGP_KEY_INIT:
+    case ARGP_KEY_SUCCESS:
+    case ARGP_KEY_ERROR:
+      break;
+
+    case OPT_DEVICE_MASTER_PORT:
+      opt_device_master = atoi (arg);
+      break;
+    }
+  return 0;
+}
+
+/* This will be called from libtrivfs to help construct the answer
+   to an fsys_get_options RPC.  */
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+                   char **argz, size_t *argz_len)
+{
+  error_t err = 0;
+  char *opt;
+
+  if (MACH_PORT_VALID (opt_device_master))
+    {
+      asprintf (&opt, "--device-master-port=%d", opt_device_master);
+
+      if (opt)
+       {
+         err = argz_add (argz, argz_len, opt);
+         free (opt);
+       }
+    }
+
+  return err;
+}
+
+static struct argp argp =
+{ options, parse_opt, 0, "Hurd standard exec server." };
+
+/* Setting this variable makes libtrivfs use our argp to
+   parse options passed in an fsys_set_options RPC.  */
+struct argp *trivfs_runtime_argp = &argp;
+
+/* Get our stderr set up to print on the console, in case we have to
+   panic or something.  */
+error_t
+open_console (mach_port_t device_master)
+{
+  static int got_console = 0;
+  mach_port_t cons;
+  error_t err;
+
+  if (got_console)
+    return 0;
+
+  err = device_open (device_master, D_READ|D_WRITE, "console", &cons);
+  if (err)
+    return err;
+
+  stdin = mach_open_devstream (cons, "r");
+  stdout = stderr = mach_open_devstream (cons, "w");
+
+  got_console = 1;
+  mach_port_deallocate (mach_task_self (), cons);
+  return 0;
+}
 
 int
 main (int argc, char **argv)
 {
   error_t err;
   mach_port_t bootstrap;
-  struct argp argp = { 0, 0, 0, "Hurd standard exec server." };
 
   argp_parse (&argp, argc, argv, 0, 0, 0);
 
+  if (MACH_PORT_VALID (opt_device_master))
+    {
+      err = open_console (opt_device_master);
+      assert_perror (err);
+      mach_port_deallocate (mach_task_self (), opt_device_master);
+    }
+
   save_argv = argv;
 
   task_get_bootstrap_port (mach_task_self (), &bootstrap);
@@ -236,18 +325,9 @@ S_exec_init (struct trivfs_protid *protid,
   err = get_privileged_ports (&host_priv, &device_master);
   assert_perror (err);
 
-  {
-    /* Get our stderr set up to print on the console, in case we have
-       to panic or something.  */
-    mach_port_t cons;
-    error_t err;
-    err = device_open (device_master, D_READ|D_WRITE, "console", &cons);
-    assert_perror (err);
-    mach_port_deallocate (mach_task_self (), device_master);
-    stdin = mach_open_devstream (cons, "r");
-    stdout = stderr = mach_open_devstream (cons, "w");
-    mach_port_deallocate (mach_task_self (), cons);
-  }
+  err = open_console (device_master);
+  assert_perror (err);
+  mach_port_deallocate (mach_task_self (), device_master);
 
   proc_register_version (procserver, host_priv, "exec", "", HURD_VERSION);
 
diff --git a/ext2fs/dir.c b/ext2fs/dir.c
index a7eeaaa..470b7e9 100644
--- a/ext2fs/dir.c
+++ b/ext2fs/dir.c
@@ -99,7 +99,7 @@ diskfs_null_dirstat (struct dirstat *ds)
 
 static error_t
 dirscanblock (vm_address_t blockoff, struct node *dp, int idx,
-             const char *name, int namelen, enum lookup_type type,
+             const char *name, size_t namelen, enum lookup_type type,
              struct dirstat *ds, ino_t *inum);
 
 
@@ -137,16 +137,16 @@ diskfs_lookup_hard (struct node *dp, const char *name, 
enum lookup_type type,
 {
   error_t err;
   ino_t inum;
-  int namelen;
+  size_t namelen;
   int spec_dotdot;
   struct node *np = 0;
-  int retry_dotdot = 0;
+  ino_t retry_dotdot = 0;
   vm_prot_t prot =
     (type == LOOKUP) ? VM_PROT_READ : (VM_PROT_READ | VM_PROT_WRITE);
   memory_object_t memobj;
   vm_address_t buf = 0;
   vm_size_t buflen = 0;
-  int blockaddr;
+  vm_address_t blockaddr;
   int idx, lastidx;
   int looped;
 
@@ -379,11 +379,11 @@ diskfs_lookup_hard (struct node *dp, const char *name, 
enum lookup_type type,
    return ENOENT.  */
 static error_t
 dirscanblock (vm_address_t blockaddr, struct node *dp, int idx,
-             const char *name, int namelen, enum lookup_type type,
+             const char *name, size_t namelen, enum lookup_type type,
              struct dirstat *ds, ino_t *inum)
 {
-  int nfree = 0;
-  int needed = 0;
+  size_t nfree = 0;
+  size_t needed = 0;
   vm_address_t currentoff, prevoff;
   struct ext2_dir_entry_2 *entry = 0;
   int nentries = 0;
@@ -421,7 +421,7 @@ dirscanblock (vm_address_t blockaddr, struct node *dp, int 
idx,
 
       if (looking || countcopies)
        {
-         int thisfree;
+         size_t thisfree;
 
          /* Count how much free space this entry has in it. */
          if (entry->inode == 0)
@@ -527,11 +527,11 @@ diskfs_direnter_hard (struct node *dp, const char *name, 
struct node *np,
                      struct dirstat *ds, struct protid *cred)
 {
   struct ext2_dir_entry_2 *new;
-  int namelen = strlen (name);
-  int needed = EXT2_DIR_REC_LEN (namelen);
-  int oldneeded;
+  size_t namelen = strlen (name);
+  size_t needed = EXT2_DIR_REC_LEN (namelen);
+  size_t oldneeded;
   vm_address_t fromoff, tooff;
-  int totfreed;
+  size_t totfreed;
   error_t err;
   size_t oldsize = 0;
 
@@ -577,7 +577,7 @@ diskfs_direnter_hard (struct node *dp, const char *name, 
struct node *np,
        {
          struct ext2_dir_entry_2 *from = (struct ext2_dir_entry_2 *)fromoff;
          struct ext2_dir_entry_2 *to = (struct ext2_dir_entry_2 *) tooff;
-         int fromreclen = from->rec_len;
+         size_t fromreclen = from->rec_len;
 
          if (from->inode != 0)
            {
@@ -823,7 +823,7 @@ diskfs_drop_dirstat (struct node *dp, struct dirstat *ds)
    write the answer down in its dirents array.  As a side affect
    fill BUF with the block.  */
 static error_t
-count_dirents (struct node *dp, int nb, char *buf)
+count_dirents (struct node *dp, block_t nb, char *buf)
 {
   size_t amt;
   char *offinblk;
@@ -868,8 +868,8 @@ diskfs_get_directs (struct node *dp,
                    vm_size_t bufsiz,
                    int *amt)
 {
-  int blkno;
-  int nblks;
+  block_t blkno;
+  block_t nblks;
   int curentry;
   char buf[DIRBLKSIZ];
   char *bufp;
diff --git a/ext2fs/pager.c b/ext2fs/pager.c
index ce5bc6d..298dae7 100644
--- a/ext2fs/pager.c
+++ b/ext2fs/pager.c
@@ -957,7 +957,7 @@ disk_cache_block_ref (block_t block)
   int index;
   void *bptr;
 
-  assert (0 <= block && block < store->size >> log2_block_size);
+  assert (block < store->size >> log2_block_size);
 
   ext2_debug ("(%u)", block);
 
@@ -1192,21 +1192,6 @@ disk_cache_block_is_ref (block_t block)
   return ref;
 }
 
-/* A top-level function for the paging thread that just services paging
-   requests.  */
-static void *
-service_paging_requests (void *arg)
-{
-  struct port_bucket *pager_bucket = arg;
-  ports_manage_port_operations_multithread (pager_bucket,
-                                           pager_demuxer,
-                                           1000,
-                                           0,
-                                           NULL);
-  /* Not reached.  */
-  return NULL;
-}
-
 /* Create the disk pager, and the file pager.  */
 void
 create_disk_pager (void)
@@ -1231,17 +1216,10 @@ create_disk_pager (void)
   /* The file pager.  */
   file_pager_bucket = ports_create_bucket ();
 
-#define STACK_SIZE (64 * 1024)
-  pthread_attr_init (&attr);
-  pthread_attr_setstacksize (&attr, STACK_SIZE);
-#undef STACK_SIZE
-
-  /* Make a thread to service file paging requests.  */
-  err = pthread_create (&thread, &attr,
-                       service_paging_requests, file_pager_bucket);
+  /* Start libpagers worker threads.  */
+  err = pager_start_workers (file_pager_bucket);
   if (err)
-    error (2, err, "pthread_create");
-  pthread_detach (thread);
+    ext2_panic ("can't create libpager worker threads: %s", strerror (err));
 }
 
 /* Call this to create a FILE_DATA pager and return a send right.
diff --git a/fatfs/pager.c b/fatfs/pager.c
index f855ecf..0c59084 100644
--- a/fatfs/pager.c
+++ b/fatfs/pager.c
@@ -756,21 +756,6 @@ pager_dropweak (struct user_pager_info *p __attribute__ 
((unused)))
 {
 }
 
-/* A top-level function for the paging thread that just services paging
-   requests.  */
-static void *
-service_paging_requests (void *arg)
-{
-  struct port_bucket *pager_bucket = arg;
-  ports_manage_port_operations_multithread (pager_bucket,
-                                           pager_demuxer,
-                                           1000,
-                                           0,
-                                           NULL);
-  /* Not reached.  */
-  return NULL;
-}
-
 /* Create the disk pager.  */
 void
 create_fat_pager (void)
@@ -790,17 +775,10 @@ create_fat_pager (void)
   /* The file pager.  */
   file_pager_bucket = ports_create_bucket ();
 
-#define STACK_SIZE (64 * 1024)
-  pthread_attr_init (&attr);
-  pthread_attr_setstacksize (&attr, STACK_SIZE);
-#undef STACK_SIZE
-
-  /* Make a thread to service file paging requests.  */
-  err = pthread_create (&thread, &attr,
-                       service_paging_requests, file_pager_bucket);
+  /* Start libpagers worker threads.  */
+  err = pager_start_workers (file_pager_bucket);
   if (err)
-    error (2, err, "pthread_create");
-  pthread_detach (thread);
+    error (2, err, "can't create libpager worker threads");
 }
 
 /* Call this to create a FILE_DATA pager and return a send right.
diff --git a/ftpfs/dir.c b/ftpfs/dir.c
index da5ddbe..a9fea22 100644
--- a/ftpfs/dir.c
+++ b/ftpfs/dir.c
@@ -384,14 +384,18 @@ refresh_dir (struct ftpfs_dir *dir, int update_stats, 
time_t timestamp,
   if (! err)
     err = update_ordered_name ("..", &dfs);
 
-  /* Refetch the directory from the server.  */
-  if (update_stats)
-    /* Fetch both names and stat info.  */
-    err = ftp_conn_get_stats (conn, dir->rmt_path, 1,
-                             update_ordered_entry, &dfs);
-  else
-    /* Just fetch names.  */
-    err = ftp_conn_get_names (conn, dir->rmt_path, update_ordered_name, &dfs);
+  if (! err)
+    {
+      /* Refetch the directory from the server.  */
+      if (update_stats)
+       /* Fetch both names and stat info.  */
+       err = ftp_conn_get_stats (conn, dir->rmt_path, 1,
+                                 update_ordered_entry, &dfs);
+      else
+       /* Just fetch names.  */
+       err = ftp_conn_get_names (conn, dir->rmt_path,
+                                 update_ordered_name, &dfs);
+    }
 
   if (! err)
     /* GC any directory entries that weren't seen this time.  */
diff --git a/hurd/default_pager.defs b/hurd/default_pager.defs
index 4885503..1a4290d 100644
--- a/hurd/default_pager.defs
+++ b/hurd/default_pager.defs
@@ -43,7 +43,7 @@ DEFAULT_PAGER_IMPORTS
 routine        default_pager_object_create(
                default_pager           : mach_port_t;
        out     memory_object           : memory_object_t =
-                       MACH_MSG_TYPE_MAKE_SEND;
+                       MACH_MSG_TYPE_PORT_SEND;
                object_size             : vm_size_t);
 
 routine default_pager_info(
diff --git a/hurd/fs.defs b/hurd/fs.defs
index 2452682..a4a48cc 100644
--- a/hurd/fs.defs
+++ b/hurd/fs.defs
@@ -354,8 +354,8 @@ routine file_reparent (
        out new_file: mach_port_send_t);
 
 /* Return any active translators bound to nodes below FILE.  CHILDREN
-   is an argz vector containing file names relative to the root of the
-   receiving translator.  */
+   is an argz vector containing file names relative to the path of
+   FILE.  */
 routine file_get_children (
        file: file_t;
        RPT
diff --git a/hurd/hurd_types.h b/hurd/hurd_types.h
index 8eac206..4341177 100644
--- a/hurd/hurd_types.h
+++ b/hurd/hurd_types.h
@@ -49,6 +49,7 @@ typedef mach_port_t fs_notify_t;
 typedef mach_port_t exec_startup_t;
 typedef mach_port_t interrupt_t;
 typedef mach_port_t proccoll_t;
+typedef mach_port_t ctty_t;
 
 #include <errno.h>             /* Defines `error_t'.  */
 
diff --git a/hurd/paths.h b/hurd/paths.h
index 4877132..92875b2 100644
--- a/hurd/paths.h
+++ b/hurd/paths.h
@@ -52,5 +52,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 
02139, USA.  */
 #define        _HURD_FIFO      _HURD "fifo" /* S_IFIFO */
 #define        _HURD_IFSOCK    _HURD "ifsock" /* S_IFSOCK */
 
+/* Symbolic names for all non-essential translators.  */
+#define _HURD_MTAB     _HURD "mtab"
 
 #endif /* hurd/paths.h */
diff --git a/hurd/shared.h b/hurd/shared.h
index 6f13637..25747eb 100644
--- a/hurd/shared.h
+++ b/hurd/shared.h
@@ -66,7 +66,7 @@ struct shared_io
   int use_read_size;           /* read_size is meaningful */
   off_t read_size;
 
-  size_t optimal_transfer_size;        /* users should try to have the
+  blksize_t optimal_transfer_size; /* users should try to have the
                                   arguments to io_prenotify, etc. be
                                   multiples of this value if it is
                                   nonzero. */ 
diff --git a/hurd/term.defs b/hurd/term.defs
index 17ba4f3..45d825d 100644
--- a/hurd/term.defs
+++ b/hurd/term.defs
@@ -29,6 +29,18 @@ TERM_IMPORTS
 
 INTR_INTERFACE
 
+type ctty_t = mach_port_copy_send_t
+#ifdef CTTY_INTRAN
+intran: CTTY_INTRAN
+#endif
+#ifdef CTTY_OUTTRAN
+outtran: CTTY_OUTTRAN
+#endif
+#ifdef CTTY_DESTRUCTOR
+destructor: CTTY_DESTRUCTOR
+#endif
+;
+
 /* Find out what the controlling terminal ID port is.  */
 routine term_getctty (
        terminal: io_t;
@@ -109,7 +121,7 @@ routine term_on_pty (
    not be made to terminal I/O ports.  Return an unauthenticated I/O
    port for the terminal opened as with flags FLAGS. */
 routine termctty_open_terminal (
-       ctty: mach_port_t;
+       ctty: ctty_t;
        flags: int;
        out terminal: mach_port_send_t);
 
diff --git a/include/refcount.h b/include/refcount.h
index 5c3302d..ebde42d 100644
--- a/include/refcount.h
+++ b/include/refcount.h
@@ -31,18 +31,23 @@
 /* An opaque type.  You must not access these values directly.  */
 typedef unsigned int refcount_t;
 
-/* Initialize REF with REFERENCES.  */
+/* Initialize REF with REFERENCES.  REFERENCES must not be zero.  */
 static inline void
 refcount_init (refcount_t *ref, unsigned int references)
 {
+  assert (references > 0 || !"references must not be zero!");
   *ref = references;
 }
 
 /* Increment REF.  Return the result of the operation.  This function
    uses atomic operations.  It is not required to serialize calls to
-   this function.  */
+   this function.
+
+   This is the unsafe version of refcount_ref.  refcount_ref also
+   checks for use-after-free errors.  When in doubt, use that one
+   instead.  */
 static inline unsigned int
-refcount_ref (refcount_t *ref)
+refcount_unsafe_ref (refcount_t *ref)
 {
   unsigned int r;
   r = __atomic_add_fetch (ref, 1, __ATOMIC_RELAXED);
@@ -50,6 +55,18 @@ refcount_ref (refcount_t *ref)
   return r;
 }
 
+/* Increment REF.  Return the result of the operation.  This function
+   uses atomic operations.  It is not required to serialize calls to
+   this function.  */
+static inline unsigned int
+refcount_ref (refcount_t *ref)
+{
+  unsigned int r;
+  r = refcount_unsafe_ref (ref);
+  assert (r != 1 || !"refcount detected use-after-free!");
+  return r;
+}
+
 /* Decrement REF.  Return the result of the operation.  This function
    uses atomic operations.  It is not required to serialize calls to
    this function.  */
@@ -101,19 +118,25 @@ union _references {
   uint64_t value;
 };
 
-/* Initialize REF with HARD and WEAK references.  */
+/* Initialize REF with HARD and WEAK references.  HARD and WEAK must
+   not both be zero.  */
 static inline void
 refcounts_init (refcounts_t *ref, uint32_t hard, uint32_t weak)
 {
+  assert ((hard != 0 || weak != 0) || !"references must not both be zero!");
   ref->references = (struct references) { .hard = hard, .weak = weak };
 }
 
 /* Increment the hard reference count of REF.  If RESULT is not NULL,
    the result of the operation is written there.  This function uses
    atomic operations.  It is not required to serialize calls to this
-   function.  */
+   function.
+
+   This is the unsafe version of refcounts_ref.  refcounts_ref also
+   checks for use-after-free errors.  When in doubt, use that one
+   instead.  */
 static inline void
-refcounts_ref (refcounts_t *ref, struct references *result)
+refcounts_unsafe_ref (refcounts_t *ref, struct references *result)
 {
   const union _references op = { .references = { .hard = 1 } };
   union _references r;
@@ -123,6 +146,21 @@ refcounts_ref (refcounts_t *ref, struct references *result)
     *result = r.references;
 }
 
+/* Increment the hard reference count of REF.  If RESULT is not NULL,
+   the result of the operation is written there.  This function uses
+   atomic operations.  It is not required to serialize calls to this
+   function.  */
+static inline void
+refcounts_ref (refcounts_t *ref, struct references *result)
+{
+  struct references r;
+  refcounts_unsafe_ref (ref, &r);
+  assert (! (r.hard == 1 && r.weak == 0)
+          || !"refcount detected use-after-free!");
+  if (result)
+    *result = r;
+}
+
 /* Decrement the hard reference count of REF.  If RESULT is not NULL,
    the result of the operation is written there.  This function uses
    atomic operations.  It is not required to serialize calls to this
@@ -160,7 +198,7 @@ refcounts_promote (refcounts_t *ref, struct references 
*result)
      So we just add a hard reference.  In combination, this is the
      desired operation.  */
   const union _references op =
-    { .references = { .weak = ~0, .hard = 1} };
+    { .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!");
@@ -188,7 +226,7 @@ refcounts_demote (refcounts_t *ref, struct references 
*result)
      significant bits.  When we add ~0 to the hard references, it will
      overflow into the weak references.  This is the desired
      operation.  */
-  const union _references op = { .references = { .hard = ~0 } };
+  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!");
@@ -200,9 +238,13 @@ refcounts_demote (refcounts_t *ref, struct references 
*result)
 /* Increment the weak reference count of REF.  If RESULT is not NULL,
    the result of the operation is written there.  This function uses
    atomic operations.  It is not required to serialize calls to this
-   function.  */
+   function.
+
+   This is the unsafe version of refcounts_ref_weak.
+   refcounts_ref_weak also checks for use-after-free errors.  When in
+   doubt, use that one instead.  */
 static inline void
-refcounts_ref_weak (refcounts_t *ref, struct references *result)
+refcounts_unsafe_ref_weak (refcounts_t *ref, struct references *result)
 {
   const union _references op = { .references = { .weak = 1 } };
   union _references r;
@@ -212,6 +254,21 @@ refcounts_ref_weak (refcounts_t *ref, struct references 
*result)
     *result = r.references;
 }
 
+/* Increment the weak reference count of REF.  If RESULT is not NULL,
+   the result of the operation is written there.  This function uses
+   atomic operations.  It is not required to serialize calls to this
+   function.  */
+static inline void
+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)
+          || !"refcount detected use-after-free!");
+  if (result)
+    *result = r;
+}
+
 /* Decrement the weak reference count of REF.  If RESULT is not NULL,
    the result of the operation is written there.  This function uses
    atomic operations.  It is not required to serialize calls to this
diff --git a/libcons/extra-version.c b/libcons/extra-version.c
index 4ff54d8..1021c87 100644
--- a/libcons/extra-version.c
+++ b/libcons/extra-version.c
@@ -21,4 +21,4 @@
 
 #include "priv.h"
 
-char *cons_extra_version = "";
+char *cons_extra_version __attribute__ ((weak)) = "";
diff --git a/libcons/vcons-add.c b/libcons/vcons-add.c
index 1a6eb20..c46f5e0 100644
--- a/libcons/vcons-add.c
+++ b/libcons/vcons-add.c
@@ -24,7 +24,7 @@
 
 /* The virtual console entry VCONS_ENTRY was just added.  CONS is
    locked.  */
-void
+void __attribute__ ((weak))
 cons_vcons_add (cons_t cons, vcons_list_t vcons_entry)
 {
 }
diff --git a/libcons/vcons-remove.c b/libcons/vcons-remove.c
index 34b31d6..273c5a0 100644
--- a/libcons/vcons-remove.c
+++ b/libcons/vcons-remove.c
@@ -24,7 +24,7 @@
 
 /* The virtual console VCONS_ENTRY is going to be removed.
    VCONS_ENTRY->cons is locked.  */
-void
+void __attribute__ ((weak))
 cons_vcons_remove (cons_t cons, vcons_list_t vcons_entry)
 {
   assert (!vcons_entry->vcons);
diff --git a/libdiskfs/boot-start.c b/libdiskfs/boot-start.c
index a60a1d0..a590975 100644
--- a/libdiskfs/boot-start.c
+++ b/libdiskfs/boot-start.c
@@ -178,7 +178,7 @@ diskfs_start_bootstrap ()
       /* Attempt to set the active translator for the exec server so that
         filesystems other than the bootstrap can find it.  */
       err = dir_lookup (root_pt, _SERVERS_EXEC, O_NOTRANS, 0,
-                       &retry, pathbuf, &execnode);
+                       &retry, retry_name, &execnode);
       if (err)
        {
          error (0, err, "cannot set translator on %s", _SERVERS_EXEC);
@@ -217,8 +217,9 @@ diskfs_start_bootstrap ()
       while (*initname == '/')
        initname++;
 
-      exec_argvlen = asprintf (&exec_argv, "/%s%c", initname, '\0');
-      assert (exec_argvlen != -1);
+      int len = asprintf (&exec_argv, "/%s%c", initname, '\0');
+      assert (len != -1);
+      exec_argvlen = (size_t) len;
       err = argz_add_sep (&exec_argv, &exec_argvlen,
                          diskfs_boot_command_line, ' ');
       assert_perror (err);
diff --git a/libdiskfs/dir-rename.c b/libdiskfs/dir-rename.c
index ff9dead..9ac4839 100644
--- a/libdiskfs/dir-rename.c
+++ b/libdiskfs/dir-rename.c
@@ -77,7 +77,6 @@ diskfs_S_dir_rename (struct protid *fromcred,
       if (pthread_mutex_trylock (&renamedirlock))
        {
          diskfs_nrele (fnp);
-         pthread_mutex_lock (&renamedirlock);
          goto try_again;
        }
       err = diskfs_rename_dir (fdp, fnp, fromname, tdp, toname, fromcred,
diff --git a/libdiskfs/disk-pager.c b/libdiskfs/disk-pager.c
index 9a0d9d8..4083ef2 100644
--- a/libdiskfs/disk-pager.c
+++ b/libdiskfs/disk-pager.c
@@ -33,39 +33,19 @@ static struct hurd_signal_preemptor preemptor =
   handler: (sighandler_t) &fault_handler,
   };
 
-/* A top-level function for the paging thread that just services paging
-   requests.  */
-static void *
-service_paging_requests (void *arg)
-{
-  struct port_bucket *pager_bucket = arg;
-  for (;;)
-    ports_manage_port_operations_multithread (pager_bucket,
-                                             pager_demuxer,
-                                             1000 * 60 * 2,
-                                             1000 * 60 * 10, 0);
-  return NULL;
-}
-
 void
 diskfs_start_disk_pager (struct user_pager_info *upi,
                         struct port_bucket *pager_bucket,
                         int may_cache, int notify_on_evict,
                         size_t size, void **image)
 {
-  pthread_t thread;
   error_t err;
   mach_port_t disk_pager_port;
 
-  /* Make a thread to service paging requests.  */
-  err = pthread_create (&thread, NULL, service_paging_requests, pager_bucket);
-  if (!err)
-    pthread_detach (thread);
-  else
-    {
-      errno = err;
-      perror ("pthread_create");
-    }
+  /* Start libpagers worker threads.  */
+  err = pager_start_workers (pager_bucket);
+  if (err)
+    error (2, err, "creating pager worker threads failed");
 
   /* Create the pager.  */
   diskfs_disk_pager = pager_create (upi, pager_bucket,
diff --git a/libdiskfs/diskfs.h b/libdiskfs/diskfs.h
index ae1a150..e59ba99 100644
--- a/libdiskfs/diskfs.h
+++ b/libdiskfs/diskfs.h
@@ -56,7 +56,7 @@ struct protid
 /* One of these is created for each node opened by dir_lookup. */
 struct peropen
 {
-  int filepointer;
+  off_t filepointer;
   int lock_status;
   refcount_t refcnt;
   int openstat;
@@ -686,6 +686,33 @@ diskfs_notice_filechange (struct node *np, enum 
file_changed_type type,
    The new node will have one hard reference and no light references.  */
 struct node *diskfs_make_node (struct disknode *dn);
 
+/* Create a new node structure.  Also allocate SIZE bytes for the
+   disknode.  The address of the disknode can be obtained using
+   diskfs_node_disknode.  The new node will have one hard reference
+   and no light references.  */
+struct node *diskfs_make_node_alloc (size_t size);
+
+/* To avoid breaking the ABI whenever sizeof (struct node) changes, we
+   explicitly provide the size.  The following two functions will use
+   this value for offset calculations.  */
+extern const size_t _diskfs_sizeof_struct_node;
+
+/* Return the address of the disknode for NODE.  NODE must have been
+   allocated using diskfs_make_node_alloc.  */
+static inline struct disknode *
+diskfs_node_disknode (struct node *node)
+{
+  return (struct disknode *) ((char *) node + _diskfs_sizeof_struct_node);
+}
+
+/* Return the address of the node for DISKNODE.  DISKNODE must have
+   been allocated using diskfs_make_node_alloc.  */
+static inline struct node *
+diskfs_disknode_node (struct disknode *disknode)
+{
+  return (struct node *) ((char *) disknode - _diskfs_sizeof_struct_node);
+}
+
 
 /* The library also exports the following functions; they are not generally
    useful unless you are redefining other functions the library provides. */
@@ -793,12 +820,12 @@ diskfs_create_node (struct node *dir, const char *name, 
mode_t mode,
                    struct dirstat *ds);
 
 /* Create and return a protid for an existing peropen PO in CRED,
-   referring to user USER.  */
+   referring to user USER.  On success, consume a reference to PO.  */
 error_t diskfs_create_protid (struct peropen *po, struct iouser *user,
                              struct protid **cred);
 
 /* Build and return in CRED a protid which has no user identification, for
-   peropen PO.  */
+   peropen PO.  On success, consume a reference to PO.  */
 error_t diskfs_start_protid (struct peropen *po, struct protid **cred);
 
 /* Finish building protid CRED started with diskfs_start_protid;
diff --git a/libdiskfs/extra-version.c b/libdiskfs/extra-version.c
index b1d7808..51920aa 100644
--- a/libdiskfs/extra-version.c
+++ b/libdiskfs/extra-version.c
@@ -21,4 +21,4 @@
 
 #include "priv.h"
 
-char *diskfs_extra_version = "";
+char *diskfs_extra_version __attribute__ ((weak)) = "";
diff --git a/libdiskfs/file-chflags.c b/libdiskfs/file-chflags.c
index 01dc495..a29ff07 100644
--- a/libdiskfs/file-chflags.c
+++ b/libdiskfs/file-chflags.c
@@ -23,8 +23,15 @@ kern_return_t
 diskfs_S_file_chflags (struct protid *cred,
                      int flags)
 {
+#define HI(X)  ((X) & 0xffff0000u)
   CHANGE_NODE_FIELD (cred,
                   ({
+                     /* Only root is allowed to change the high 16
+                        bits.  */
+                     if ((HI (flags) != HI (np->dn_stat.st_flags))
+                         && ! idvec_contains (cred->user->uids, 0))
+                       return EPERM;
+
                     err = fshelp_isowner (&np->dn_stat, cred->user);
                     if (!err)
                       err = diskfs_validate_flags_change (np, flags);
@@ -37,4 +44,5 @@ diskfs_S_file_chflags (struct protid *cred,
                       diskfs_notice_filechange(np, FILE_CHANGED_META, 
                                                0, 0);
                   }));
+#undef HI
 }
diff --git a/libdiskfs/file-get-children.c b/libdiskfs/file-get-children.c
index 4581e4e..98d5d60 100644
--- a/libdiskfs/file-get-children.c
+++ b/libdiskfs/file-get-children.c
@@ -24,9 +24,9 @@
 
 #include <argz.h>
 
-/* Return any active translators bound to nodes of the receiving
-   filesystem.  CHILDREN is an argz vector containing file names
-   relative to the root of the receiving translator.  */
+/* Return any active translators bound to nodes below CRED.  CHILDREN
+   is an argz vector containing file names relative to the path of
+   CRED.  */
 error_t
 diskfs_S_file_get_children (struct protid *cred,
                            char **children,
@@ -79,7 +79,8 @@ diskfs_S_file_get_children (struct protid *cred,
   char *c = NULL;
   size_t c_len = 0;
 
-  err = fshelp_get_active_translators (&c, &c_len, check_access);
+  err = fshelp_get_active_translators (&c, &c_len, check_access,
+                                      cred->po->path);
   if (err)
     goto errout;
 
diff --git a/libdiskfs/file-statfs.c b/libdiskfs/file-statfs.c
index 817b011..9d97ce6 100644
--- a/libdiskfs/file-statfs.c
+++ b/libdiskfs/file-statfs.c
@@ -41,6 +41,8 @@ diskfs_S_file_statfs (struct protid *file,
     statbuf->f_flag |= ST_NOEXEC;
   if (diskfs_synchronous)
     statbuf->f_flag |= ST_SYNCHRONOUS;
+  if (_diskfs_noatime)
+    statbuf->f_flag |= ST_NOATIME;
 
   diskfs_set_statfs (statbuf);
 
diff --git a/libdiskfs/get-source.c b/libdiskfs/get-source.c
index 4399446..2ef8ebc 100644
--- a/libdiskfs/get-source.c
+++ b/libdiskfs/get-source.c
@@ -21,7 +21,7 @@
 
 #include "priv.h"
 
-error_t
+error_t __attribute__ ((weak))
 diskfs_get_source (struct protid *cred, char *source, size_t source_len)
 {
   if (diskfs_disk_name == NULL)
diff --git a/libdiskfs/init-init.c b/libdiskfs/init-init.c
index 35be7ed..7a7f248 100644
--- a/libdiskfs/init-init.c
+++ b/libdiskfs/init-init.c
@@ -25,6 +25,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 
02139, USA.  */
 #include <stdio.h>
 #include <maptime.h>
 
+/* For safe inlining of diskfs_node_disknode and
+   diskfs_disknode_node.  */
+size_t const _diskfs_sizeof_struct_node = sizeof (struct node);
+
 mach_port_t diskfs_default_pager;
 mach_port_t diskfs_auth_server_port;
 volatile struct mapped_time_value *diskfs_mtime;
diff --git a/libdiskfs/io-duplicate.c b/libdiskfs/io-duplicate.c
index 45c4df5..a2e568b 100644
--- a/libdiskfs/io-duplicate.c
+++ b/libdiskfs/io-duplicate.c
@@ -32,6 +32,7 @@ diskfs_S_io_duplicate (struct protid *cred,
   
   pthread_mutex_lock (&cred->po->np->lock);
 
+  refcount_ref (&cred->po->refcnt);
   err = diskfs_create_protid (cred->po, cred->user, &newpi);
   if (! err)
     {
@@ -39,6 +40,8 @@ diskfs_S_io_duplicate (struct protid *cred,
       *portpoly = MACH_MSG_TYPE_MAKE_SEND;
       ports_port_deref (newpi);
     }
+  else
+    refcount_deref (&cred->po->refcnt);
 
   pthread_mutex_unlock (&cred->po->np->lock);
 
diff --git a/libdiskfs/io-reauthenticate.c b/libdiskfs/io-reauthenticate.c
index 69d78bc..649315f 100644
--- a/libdiskfs/io-reauthenticate.c
+++ b/libdiskfs/io-reauthenticate.c
@@ -35,11 +35,13 @@ diskfs_S_io_reauthenticate (struct protid *cred,
      are a simpleroutine, so callers won't know to restart. */
 
   pthread_mutex_lock (&cred->po->np->lock);
+  refcount_ref (&cred->po->refcnt);
   do
     err = diskfs_start_protid (cred->po, &newcred);
   while (err == EINTR);
   if (err)
     {
+      refcount_deref (&cred->po->refcnt);
       pthread_mutex_unlock (&cred->po->np->lock);
       return err;
     }
diff --git a/libdiskfs/io-restrict-auth.c b/libdiskfs/io-restrict-auth.c
index 011aa19..80c0b20 100644
--- a/libdiskfs/io-restrict-auth.c
+++ b/libdiskfs/io-restrict-auth.c
@@ -41,6 +41,7 @@ diskfs_S_io_restrict_auth (struct protid *cred,
     return err;
 
   pthread_mutex_lock (&cred->po->np->lock);
+  refcount_ref (&cred->po->refcnt);
   err = diskfs_create_protid (cred->po, user, &newpi);
   if (! err)
     {
@@ -48,6 +49,8 @@ diskfs_S_io_restrict_auth (struct protid *cred,
       *newportpoly = MACH_MSG_TYPE_MAKE_SEND;
       ports_port_deref (newpi);
     }
+  else
+    refcount_deref (&cred->po->refcnt);
   pthread_mutex_unlock (&cred->po->np->lock);
 
   iohelp_free_iouser (user);
diff --git a/libdiskfs/lookup.c b/libdiskfs/lookup.c
index bc2ad01..486fedc 100644
--- a/libdiskfs/lookup.c
+++ b/libdiskfs/lookup.c
@@ -21,17 +21,6 @@
 #include "priv.h"
 #include <string.h>
 
-static struct
-{
-  int present;
-  int absent;
-  int errors;
-  int dot;
-  int dotdot;
-} cache_misses;
-static pthread_spinlock_t cm_lock = PTHREAD_SPINLOCK_INITIALIZER;
-
-
 /* Lookup in directory DP (which is locked) the name NAME.  TYPE will
    either be LOOKUP, CREATE, RENAME, or REMOVE.  CRED identifies the
    user making the call.
@@ -175,26 +164,6 @@ diskfs_lookup (struct node *dp, const char *name, enum 
lookup_type type,
   else
     {
       err = diskfs_lookup_hard (dp, name, type, np, ds, cred);
-
-      pthread_spin_lock (&cm_lock);
-      if (type == LOOKUP)
-       {
-         if (err == ENOENT)
-           cache_misses.absent++;
-         else if (err)
-           cache_misses.errors++;
-         else
-           cache_misses.present++;
-         if (name[0] == '.')
-           {
-             if (name[1] == '\0')
-               cache_misses.dot++;
-             else if (name[1] == '.' && name[2] == '\0')
-               cache_misses.dotdot++;
-           }
-       }
-      pthread_spin_unlock (&cm_lock);
-
       if (err && err != ENOENT)
        return err;
 
diff --git a/libdiskfs/name-cache.c b/libdiskfs/name-cache.c
index a212a6d..d8f86b1 100644
--- a/libdiskfs/name-cache.c
+++ b/libdiskfs/name-cache.c
@@ -1,6 +1,6 @@
 /* Directory name lookup caching
 
-   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997, 1998, 2014 Free Software Foundation, Inc.
    Written by Michael I. Bushnell, p/BSG, & Miles Bader.
 
    This file is part of the GNU Hurd.
@@ -20,178 +20,290 @@
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
 
 #include "priv.h"
+#include <assert.h>
 #include <string.h>
-#include <cacheq.h>
 
-/* Maximum number of names to cache at once */
-#define MAXCACHE 200
+/* The name cache is implemented using a hash table.
 
-/* Maximum length of file name we bother caching */
-#define CACHE_NAME_LEN 100
+   We use buckets of a fixed size.  We approximate the
+   least-frequently used cache algorithm by counting the number of
+   lookups using saturating arithmetic in the two lowest bits of the
+   pointer to the name.  Using this strategy we achieve a constant
+   worst-case lookup and insertion time.  */
 
-/* Cache entry */
-struct lookup_cache
-{
-  struct cacheq_hdr hdr;
+/* Number of buckets.  Must be a power of two. */
+#define CACHE_SIZE     256
+
+/* Entries per bucket.  */
+#define BUCKET_SIZE    4
+
+/* A mask for fast binary modulo.  */
+#define CACHE_MASK     (CACHE_SIZE - 1)
 
-  /* Used to indentify nodes to the fs dependent code.  0 for NODE_CACHE_ID
-     means a `negative' entry -- recording that there's definitely no node with
-     this name.  */
-  ino64_t dir_cache_id, node_cache_id;
+/* Cache bucket with BUCKET_SIZE entries.
 
-  /* Name of the node NODE_CACHE_ID in the directory DIR_CACHE_ID.  Entries
-     with names too long to fit in this buffer aren't cached at all.  */
-  char name[CACHE_NAME_LEN];
+   The layout of the bucket is chosen so that it will be straight
+   forward to use vector operations in the future.  */
+struct cache_bucket
+{
+  /* Name of the node NODE_CACHE_ID in the directory DIR_CACHE_ID.  If
+     NULL, the entry is unused.  */
+  unsigned long name[BUCKET_SIZE];
+
+  /* The key.  */
+  unsigned long key[BUCKET_SIZE];
 
-  /* Strlen of NAME.  If this is zero, it's an unused entry. */
-  size_t name_len;
+  /* Used to indentify nodes to the fs dependent code.  */
+  ino64_t dir_cache_id[BUCKET_SIZE];
 
-  /* XXX */
-  int stati;
+  /* 0 for NODE_CACHE_ID means a `negative' entry -- recording that
+     there's definitely no node with this name.  */
+  ino64_t node_cache_id[BUCKET_SIZE];
 };
 
-/* The contents of the cache in no particular order */
-static struct cacheq lookup_cache = { sizeof (struct lookup_cache) };
+/* The cache.  */
+static struct cache_bucket name_cache[CACHE_SIZE];
 
-static pthread_spinlock_t cache_lock = PTHREAD_SPINLOCK_INITIALIZER;
+/* Protected by this lock.  */
+static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Given VALUE, return the char pointer.  */
+static inline char *
+charp (unsigned long value)
+{
+  return (char *) (value & ~3L);
+}
 
-/* Buffer to hold statistics */
-static struct stats
+/* Given VALUE, return the approximation of use frequency.  */
+static inline unsigned long
+frequ (unsigned long value)
+{
+  return value & 3;
+}
+
+/* Add an entry in the Ith slot of the given bucket.  If there is a
+   value there, remove it first.  */
+static inline void
+add_entry (struct cache_bucket *b, int i,
+          const char *name, unsigned long key,
+          ino64_t dir_cache_id, ino64_t node_cache_id)
 {
-  long pos_hits;
-  long neg_hits;
-  long miss;
-  long fetch_errors;
-} statistics;
+  if (b->name[i])
+    free (charp (b->name[i]));
 
-#define PARTIAL_THRESH 100
-#define NPARTIALS MAXCACHE / PARTIAL_THRESH
-struct stats partial_stats [NPARTIALS];
+  b->name[i] = (unsigned long) strdup (name);
+  assert ((b->name[i] & 3) == 0);
+  if (b->name[i] == 0)
+    return;
+
+  b->key[i] = key;
+  b->dir_cache_id[i] = dir_cache_id;
+  b->node_cache_id[i] = node_cache_id;
+}
+
+/* Remove the entry in the Ith slot of the given bucket.  */
+static inline void
+remove_entry (struct cache_bucket *b, int i)
+{
+  if (b->name[i])
+    free (charp (b->name[i]));
+  b->name[i] = 0;
+}
 
+/* Check if the entry in the Ith slot of the given bucket is
+   valid.  */
+static inline int
+valid_entry (struct cache_bucket *b, int i)
+{
+  return b->name[i] != 0;
+}
 
-/* If there's an entry for NAME, of length NAME_LEN, in directory DIR in the
-   cache, return its entry, otherwise 0.  CACHE_LOCK must be held.  */
-static struct lookup_cache *
-find_cache (struct node *dir, const char *name, size_t name_len)
+/* This is the Murmur3 hash algorithm.  */
+
+#define FORCE_INLINE inline __attribute__((always_inline))
+
+inline uint32_t rotl32 ( uint32_t x, int8_t r )
 {
-  struct lookup_cache *c;
-  int i;
+  return (x << r) | (x >> (32 - r));
+}
 
-  /* Search the list.  All unused entries are contiguous at the end of the
-     list, so we can stop searching when we see the first one.  */
-  for (i = 0, c = lookup_cache.mru;
-       c && c->name_len;
-       c = c->hdr.next, i++)
-    if (c->name_len == name_len
-       && c->dir_cache_id == dir->cache_id
-       && c->name[0] == name[0] && strcmp (c->name, name) == 0)
-      {
-       c->stati = i / 100;
-       return c;
-      }
+#define ROTL32(x,y)     rotl32(x,y)
 
-  return 0;
+/* Block read - if your platform needs to do endian-swapping or can
+   only handle aligned reads, do the conversion here.  */
+
+FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i )
+{
+  return p[i];
 }
-
-/* Node NP has just been found in DIR with NAME.  If NP is null, that
-   means that this name has been confirmed as absent in the directory. */
-void
-diskfs_enter_lookup_cache (struct node *dir, struct node *np, const char *name)
+
+/* Finalization mix - force all bits of a hash block to avalanche.  */
+
+FORCE_INLINE uint32_t fmix32 ( uint32_t h )
 {
-  struct lookup_cache *c;
-  size_t name_len = strlen (name);
+  h ^= h >> 16;
+  h *= 0x85ebca6b;
+  h ^= h >> 13;
+  h *= 0xc2b2ae35;
+  h ^= h >> 16;
 
-  if (name_len > CACHE_NAME_LEN - 1)
-    return;
+  return h;
+}
+
+/* The Murmur3 hash function.  */
+void MurmurHash3_x86_32 ( const void * key, int len,
+                          uint32_t seed, void * out )
+{
+  const uint8_t * data = (const uint8_t*)key;
+  const int nblocks = len / 4;
+
+  uint32_t h1 = seed;
+
+  const uint32_t c1 = 0xcc9e2d51;
+  const uint32_t c2 = 0x1b873593;
+
+  /* body */
+
+  const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
+
+  for(int i = -nblocks; i; i++)
+  {
+    uint32_t k1 = getblock32(blocks,i);
+
+    k1 *= c1;
+    k1 = ROTL32(k1,15);
+    k1 *= c2;
+
+    h1 ^= k1;
+    h1 = ROTL32(h1,13);
+    h1 = h1*5+0xe6546b64;
+  }
 
-  pthread_spin_lock (&cache_lock);
+  /* tail */
 
-  if (lookup_cache.length == 0)
-    /* There should always be an lru_cache; this being zero means that the
-       cache hasn't been initialized yet.  Do so.  */
-    cacheq_set_length (&lookup_cache, MAXCACHE);
+  const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
 
-  /* See if there's an old entry for NAME in DIR.  If not, replace the least
-     recently used entry.  */
-  c = find_cache (dir, name, name_len) ?: lookup_cache.lru;
+  uint32_t k1 = 0;
 
-  /* Fill C with the new entry.  */
-  c->dir_cache_id = dir->cache_id;
-  c->node_cache_id = np ? np->cache_id : 0;
-  strcpy (c->name, name);
-  c->name_len = name_len;
+  switch(len & 3)
+  {
+  case 3: k1 ^= tail[2] << 16;
+  case 2: k1 ^= tail[1] << 8;
+  case 1: k1 ^= tail[0];
+          k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
+  };
 
-  /* Now C becomes the MRU entry!  */
-  cacheq_make_mru (&lookup_cache, c);
+  /* finalization */
 
-  pthread_spin_unlock (&cache_lock);
+  h1 ^= len;
+
+  h1 = fmix32(h1);
+
+  *(uint32_t*)out = h1;
 }
 
-/* Purge all references in the cache to NP as a node inside
-   directory DP. */
-void
-diskfs_purge_lookup_cache (struct node *dp, struct node *np)
+/* If there is no best candidate to replace, pick any.  We approximate
+   any by picking the slot depicted by REPLACE, and increment REPLACE
+   then.  */
+static int replace;
+
+/* Lookup (DIR_CACHE_ID, NAME, KEY) in the cache.  If it is found,
+   return 1 and set BUCKET and INDEX to the item.  Otherwise, return 0
+   and set BUCKET and INDEX to the slot where the item should be
+   inserted.  */
+static inline int
+lookup (ino64_t dir_cache_id, const char *name, unsigned long key,
+       struct cache_bucket **bucket, int *index)
 {
-  struct lookup_cache *c, *next;
+  struct cache_bucket *b = *bucket = &name_cache[key & CACHE_MASK];
+  unsigned long best = 3;
+  int i;
 
-  pthread_spin_lock (&cache_lock);
-  for (c = lookup_cache.mru; c; c = next)
+  for (i = 0; i < BUCKET_SIZE; i++)
     {
-      /* Save C->hdr.next, since we may move C from this position. */
-      next = c->hdr.next;
+      unsigned long f = frequ (b->name[i]);
+
+      if (valid_entry (b, i)
+         && b->key[i] == key
+         && b->dir_cache_id[i] == dir_cache_id
+         && strcmp (charp (b->name[i]), name) == 0)
+       {
+         if (f < 3)
+           b->name[i] += 1;
+
+         *index = i;
+         return 1;
+       }
 
-      if (c->name_len
-         && c->dir_cache_id == dp->cache_id
-         && c->node_cache_id == np->cache_id)
+      /* Keep track of the replacement candidate.  */
+      if (f < best)
        {
-         c->name_len = 0;
-         cacheq_make_lru (&lookup_cache, c); /* Use C as the next free
-                                                entry. */
+         best = f;
+         *index = i;
        }
     }
-  pthread_spin_unlock (&cache_lock);
-}
-
-/* Register a negative hit for an entry in the Nth stat class */
-void
-register_neg_hit (int n)
-{
-  int i;
 
-  statistics.neg_hits++;
+  /* If there was no entry with a lower use frequency, just replace
+     any entry.  */
+  if (best == 3)
+    {
+      *index = replace;
+      replace = (replace + 1) & (BUCKET_SIZE - 1);
+    }
 
-  for (i = 0; i < n; i++)
-    partial_stats[i].miss++;
-  for (; i < NPARTIALS; i++)
-    partial_stats[i].neg_hits++;
+  return 0;
 }
 
-/* Register a positive hit for an entry in the Nth stat class */
+/* Hash the directory cache_id and the name.  */
+static inline unsigned long
+hash (ino64_t dir_cache_id, const char *name)
+{
+  unsigned long h;
+  MurmurHash3_x86_32 (&dir_cache_id, sizeof dir_cache_id, 0, &h);
+  MurmurHash3_x86_32 (name, strlen (name), h, &h);
+  return h;
+}
+
+/* Node NP has just been found in DIR with NAME.  If NP is null, that
+   means that this name has been confirmed as absent in the directory. */
 void
-register_pos_hit (int n)
+diskfs_enter_lookup_cache (struct node *dir, struct node *np, const char *name)
 {
-  int i;
-
-  statistics.pos_hits++;
-
-  for (i = 0; i < n; i++)
-    partial_stats[i].miss++;
-  for (; i < NPARTIALS; i++)
-    partial_stats[i].pos_hits++;
+  unsigned long key = hash (dir->cache_id, name);
+  ino64_t value = np ? np->cache_id : 0;
+  struct cache_bucket *bucket;
+  int i = 0, found;
+
+  pthread_mutex_lock (&cache_lock);
+  found = lookup (dir->cache_id, name, key, &bucket, &i);
+  if (! found)
+    add_entry (bucket, i, name, key, dir->cache_id, value);
+  else
+    if (bucket->node_cache_id[i] != value)
+      bucket->node_cache_id[i] = value;
+
+  pthread_mutex_unlock (&cache_lock);
 }
-
-/* Register a miss */
+
+/* Purge all references in the cache to NP as a node inside
+   directory DP. */
 void
-register_miss ()
+diskfs_purge_lookup_cache (struct node *dp, struct node *np)
 {
   int i;
+  struct cache_bucket *b;
 
-  statistics.miss++;
-  for (i = 0; i < NPARTIALS; i++)
-    partial_stats[i].miss++;
-}
+  pthread_mutex_lock (&cache_lock);
 
+  for (b = &name_cache[0]; b < &name_cache[CACHE_SIZE]; b++)
+    for (i = 0; i < BUCKET_SIZE; i++)
+      if (valid_entry (b, i)
+         && b->dir_cache_id[i] == dp->cache_id
+         && b->node_cache_id[i] == np->cache_id)
+       remove_entry (b, i);
 
+  pthread_mutex_unlock (&cache_lock);
+}
 
 /* Scan the cache looking for NAME inside DIR.  If we don't know
    anything entry at all, then return 0.  If the entry is confirmed to
@@ -200,29 +312,28 @@ register_miss ()
 struct node *
 diskfs_check_lookup_cache (struct node *dir, const char *name)
 {
-  struct lookup_cache *c;
-
-  pthread_spin_lock (&cache_lock);
-
-  c = find_cache (dir, name, strlen (name));
-  if (c)
+  unsigned long key = hash (dir->cache_id, name);
+  int lookup_parent = name[0] == '.' && name[1] == '.' && name[2] == '\0';
+  struct cache_bucket *bucket;
+  int i, found;
+
+  if (lookup_parent && dir == diskfs_root_node)
+    /* This is outside our file system, return cache miss.  */
+    return NULL;
+
+  pthread_mutex_lock (&cache_lock);
+  found = lookup (dir->cache_id, name, key, &bucket, &i);
+  if (found)
     {
-      int id = c->node_cache_id;
-
-      cacheq_make_mru (&lookup_cache, c); /* Record C as recently used.  */
+      ino64_t id = bucket->node_cache_id[i];
+      pthread_mutex_unlock (&cache_lock);
 
       if (id == 0)
        /* A negative cache entry.  */
-       {
-         register_neg_hit (c->stati);
-         pthread_spin_unlock (&cache_lock);
-         return (struct node *)-1;
-       }
+       return (struct node *) -1;
       else if (id == dir->cache_id)
        /* The cached node is the same as DIR.  */
        {
-         register_pos_hit (c->stati);
-         pthread_spin_unlock (&cache_lock);
          diskfs_nref (dir);
          return dir;
        }
@@ -232,10 +343,7 @@ diskfs_check_lookup_cache (struct node *dir, const char 
*name)
          struct node *np;
          error_t err;
 
-         register_pos_hit (c->stati);
-         pthread_spin_unlock (&cache_lock);
-
-         if (name[0] == '.' && name[1] == '.' && name[2] == '\0')
+         if (lookup_parent)
            {
              pthread_mutex_unlock (&dir->lock);
              err = diskfs_cached_lookup (id, &np);
@@ -244,14 +352,18 @@ diskfs_check_lookup_cache (struct node *dir, const char 
*name)
              /* In the window where DP was unlocked, we might
                 have lost.  So check the cache again, and see
                 if it's still there; if so, then we win. */
-             c = find_cache (dir, "..", 2);
-             if (!c || c->node_cache_id != id)
+             pthread_mutex_lock (&cache_lock);
+             found = lookup (dir->cache_id, name, key, &bucket, &i);
+             if (! found
+                 || ! bucket->node_cache_id[i] != id)
                {
+                 pthread_mutex_unlock (&cache_lock);
+
                  /* Lose */
-                 pthread_mutex_unlock (&np->lock);
-                 diskfs_nrele (np);
+                 diskfs_nput (np);
                  return 0;
                }
+             pthread_mutex_unlock (&cache_lock);
            }
          else
            err = diskfs_cached_lookup (id, &np);
@@ -259,8 +371,6 @@ diskfs_check_lookup_cache (struct node *dir, const char 
*name)
        }
     }
 
-  register_miss ();
-  pthread_spin_unlock (&cache_lock);
-
+  pthread_mutex_unlock (&cache_lock);
   return 0;
 }
diff --git a/libdiskfs/node-make.c b/libdiskfs/node-make.c
index 2b6ef2a..ff0cc0d 100644
--- a/libdiskfs/node-make.c
+++ b/libdiskfs/node-make.c
@@ -19,16 +19,9 @@
 #include <fcntl.h>
 
 
-/* Create a and return new node structure with DN as its physical disknode.
-   The node will have one hard reference and no light references.  */
-struct node *
-diskfs_make_node (struct disknode *dn)
+static struct node *
+init_node (struct node *np, struct disknode *dn)
 {
-  struct node *np = malloc (sizeof (struct node));
-
-  if (np == 0)
-    return 0;
-
   np->dn = dn;
   np->dn_set_ctime = 0;
   np->dn_set_atime = 0;
@@ -52,3 +45,31 @@ diskfs_make_node (struct disknode *dn)
 
   return np;
 }
+
+/* Create a and return new node structure with DN as its physical disknode.
+   The node will have one hard reference and no light references.  */
+struct node *
+diskfs_make_node (struct disknode *dn)
+{
+  struct node *np = malloc (sizeof (struct node));
+
+  if (np == 0)
+    return 0;
+
+  return init_node (np, dn);
+}
+
+/* Create a new node structure.  Also allocate SIZE bytes for the
+   disknode.  The address of the disknode can be obtained using
+   diskfs_node_disknode.  The new node will have one hard reference
+   and no light references.  */
+struct node *
+diskfs_make_node_alloc (size_t size)
+{
+  struct node *np = malloc (sizeof (struct node) + size);
+
+  if (np == NULL)
+    return NULL;
+
+  return init_node (np, diskfs_node_disknode (np));
+}
diff --git a/libdiskfs/peropen-make.c b/libdiskfs/peropen-make.c
index 6d5ca01..788b9a7 100644
--- a/libdiskfs/peropen-make.c
+++ b/libdiskfs/peropen-make.c
@@ -31,7 +31,7 @@ diskfs_make_peropen (struct node *np, int flags, struct 
peropen *context,
 
   po->filepointer = 0;
   po->lock_status = LOCK_UN;
-  refcount_init (&po->refcnt, 0);
+  refcount_init (&po->refcnt, 1);
   po->openstat = flags;
   po->np = np;
   po->path = NULL;
diff --git a/libdiskfs/protid-make.c b/libdiskfs/protid-make.c
index 22aaa2e..0b09299 100644
--- a/libdiskfs/protid-make.c
+++ b/libdiskfs/protid-make.c
@@ -20,7 +20,7 @@
 #include <assert.h>
 
 /* Build and return in CRED a protid which has no user identification, for
-   peropen PO.  */
+   peropen PO.  On success, consume a reference to PO.  */
 error_t
 diskfs_start_protid (struct peropen *po, struct protid **cred)
 {
@@ -29,7 +29,7 @@ diskfs_start_protid (struct peropen *po, struct protid **cred)
                                 sizeof (struct protid), cred);
   if (! err)
     {
-      refcount_ref (&po->refcnt);
+      /* Consume a reference to po.  */
       (*cred)->po = po;
       (*cred)->shared_object = MACH_PORT_NULL;
       (*cred)->mapped = 0;
@@ -56,7 +56,7 @@ diskfs_finish_protid (struct protid *cred, struct iouser 
*user)
 }
 
 /* Create and return a protid for an existing peropen PO in CRED for
-   USER.  */
+   USER.  On success, consume a reference to PO.  */
 error_t
 diskfs_create_protid (struct peropen *po, struct iouser *user,
                      struct protid **cred)
diff --git a/libdiskfs/readonly-changed.c b/libdiskfs/readonly-changed.c
index 44ee922..7a1c03e 100644
--- a/libdiskfs/readonly-changed.c
+++ b/libdiskfs/readonly-changed.c
@@ -24,7 +24,7 @@
    changed from read-only to read-write mode or vice-versa.  READONLY is the
    new state (which is also reflected in DISKFS_READONLY).  This function is
    also called during initial startup if the filesystem is to be writable.  */
-void
+void __attribute__ ((weak))
 diskfs_readonly_changed (int readonly)
 {
   /* By default do nothing at all.  */
diff --git a/libdiskfs/sync-default.c b/libdiskfs/sync-default.c
index 0d1fd93..9646dd6 100644
--- a/libdiskfs/sync-default.c
+++ b/libdiskfs/sync-default.c
@@ -20,4 +20,4 @@
 
 #include "priv.h"
 
-int diskfs_default_sync_interval = DEFAULT_SYNC_INTERVAL;
+int diskfs_default_sync_interval __attribute__ ((weak)) = 
DEFAULT_SYNC_INTERVAL;
diff --git a/libdiskfs/validate-author.c b/libdiskfs/validate-author.c
index d7dacab..cacfbfa 100644
--- a/libdiskfs/validate-author.c
+++ b/libdiskfs/validate-author.c
@@ -20,7 +20,7 @@
 
 #include "priv.h"
 
-error_t
+error_t __attribute__ ((weak))
 diskfs_validate_author_change (struct node *np, uid_t author)
 {
   return 0;
diff --git a/libdiskfs/validate-flags.c b/libdiskfs/validate-flags.c
index bb54dad..0673ad8 100644
--- a/libdiskfs/validate-flags.c
+++ b/libdiskfs/validate-flags.c
@@ -20,7 +20,7 @@
 
 #include "priv.h"
 
-error_t
+error_t __attribute__ ((weak))
 diskfs_validate_flags_change (struct node *np, int flags)
 {
   return 0;
diff --git a/libdiskfs/validate-group.c b/libdiskfs/validate-group.c
index 556c5aa..ef296ce 100644
--- a/libdiskfs/validate-group.c
+++ b/libdiskfs/validate-group.c
@@ -20,7 +20,7 @@
 
 #include "priv.h"
 
-error_t
+error_t __attribute__ ((weak))
 diskfs_validate_group_change (struct node *np, gid_t group)
 {
   return 0;
diff --git a/libdiskfs/validate-mode.c b/libdiskfs/validate-mode.c
index 1cf150e..bfcc942 100644
--- a/libdiskfs/validate-mode.c
+++ b/libdiskfs/validate-mode.c
@@ -20,7 +20,7 @@
 
 #include "priv.h"
 
-error_t
+error_t __attribute__ ((weak))
 diskfs_validate_mode_change (struct node *np, mode_t mode)
 {
   return 0;
diff --git a/libdiskfs/validate-owner.c b/libdiskfs/validate-owner.c
index 0cbe296..7bb3040 100644
--- a/libdiskfs/validate-owner.c
+++ b/libdiskfs/validate-owner.c
@@ -20,7 +20,7 @@
 
 #include "priv.h"
 
-error_t
+error_t __attribute__ ((weak))
 diskfs_validate_owner_change (struct node *np, uid_t uid)
 {
   return 0;
diff --git a/libdiskfs/validate-rdev.c b/libdiskfs/validate-rdev.c
index 2d76634..0d50667 100644
--- a/libdiskfs/validate-rdev.c
+++ b/libdiskfs/validate-rdev.c
@@ -20,7 +20,7 @@
 
 #include "priv.h"
 
-error_t
+error_t __attribute__ ((weak))
 diskfs_validate_rdev_change (struct node *np, dev_t rdev)
 {
   return 0;
diff --git a/libfshelp/fshelp.h b/libfshelp/fshelp.h
index 5d3a0ce..1c6f04a 100644
--- a/libfshelp/fshelp.h
+++ b/libfshelp/fshelp.h
@@ -61,12 +61,15 @@ fshelp_remove_active_translator (mach_port_t active);
    included in the list.  */
 typedef error_t (*fshelp_filter) (const char *path);
 
-/* Records the list of active translators into the argz vector
-   specified by TRANSLATORS filtered by FILTER.  */
+/* Records the list of active translators below PREFIX into the argz
+   vector specified by TRANSLATORS filtered by FILTER.  If PREFIX is
+   NULL, entries with any prefix are considered.  If FILTER is NULL,
+   no filter is applied.  */
 error_t
 fshelp_get_active_translators (char **translators,
                               size_t *translators_len,
-                              fshelp_filter filter);
+                              fshelp_filter filter,
+                              const char *prefix);
 
 
 /* Passive translator linkage */
diff --git a/libfshelp/translator-list.c b/libfshelp/translator-list.c
index 3ece711..c87bbaa 100644
--- a/libfshelp/translator-list.c
+++ b/libfshelp/translator-list.c
@@ -82,7 +82,10 @@ fshelp_set_active_translator (struct port_info *pi,
 
   t = malloc (sizeof (struct translator));
   if (! t)
-    return ENOMEM;
+    {
+      err = errno;
+      goto out;
+    }
 
   t->active = MACH_PORT_NULL;
   t->pi = NULL;
@@ -110,7 +113,7 @@ fshelp_set_active_translator (struct port_info *pi,
                                                MACH_MSG_TYPE_MAKE_SEND_ONCE,
                                                &old);
          if (err)
-           return err;
+           goto out;
          if (old != MACH_PORT_NULL)
            mach_port_deallocate (mach_task_self (), old);
 
@@ -160,19 +163,32 @@ fshelp_remove_active_translator (mach_port_t active)
   return err;
 }
 
-/* Records the list of active translators into the argz vector
-   specified by TRANSLATORS filtered by FILTER.  */
+/* Records the list of active translators below PREFIX into the argz
+   vector specified by TRANSLATORS filtered by FILTER.  If PREFIX is
+   NULL, entries with any prefix are considered.  If FILTER is NULL,
+   no filter is applied.  */
 error_t
 fshelp_get_active_translators (char **translators,
                               size_t *translators_len,
-                              fshelp_filter filter)
+                              fshelp_filter filter,
+                              const char *prefix)
 {
   error_t err = 0;
   pthread_mutex_lock (&translator_ihash_lock);
 
+  if (prefix && strlen (prefix) == 0)
+    prefix = NULL;
+
   HURD_IHASH_ITERATE (&translator_ihash, value)
     {
       struct translator *t = value;
+
+      if (prefix != NULL
+         && (strncmp (t->name, prefix, strlen (prefix)) != 0
+             || t->name[strlen (prefix)] != '/'))
+       /* Skip this entry, as it is not below PREFIX.  */
+       continue;
+
       if (filter)
        {
          char *dir = strdup (t->name);
@@ -192,7 +208,7 @@ fshelp_get_active_translators (char **translators,
        }
 
       err = argz_add (translators, translators_len,
-                     t->name);
+                     &t->name[prefix? strlen (prefix) + 1: 0]);
       if (err)
        break;
     }
diff --git a/libihash/ihash.h b/libihash/ihash.h
index 345630d..849a55a 100644
--- a/libihash/ihash.h
+++ b/libihash/ihash.h
@@ -241,7 +241,8 @@ hurd_ihash_value_t hurd_ihash_find (hurd_ihash_t ht, 
hurd_ihash_key_t key);
   for (hurd_ihash_value_t val,                                         \
          *_hurd_ihash_valuep = (ht)->size ? &(ht)->items[0].value : 0; \
        (ht)->size                                                      \
-         && ((_hurd_ihash_item_t) _hurd_ihash_valuep) - &(ht)->items[0]        
\
+        && (size_t) ((_hurd_ihash_item_t) _hurd_ihash_valuep           \
+                     - &(ht)->items[0])                                \
             < (ht)->size                                               \
          && (val = *_hurd_ihash_valuep, 1);                            \
        _hurd_ihash_valuep = (hurd_ihash_value_t *)                     \
diff --git a/libmachdev/net.c b/libmachdev/net.c
index 07bb74a..e04b558 100644
--- a/libmachdev/net.c
+++ b/libmachdev/net.c
@@ -243,8 +243,10 @@ netif_rx_handle (char *data, int len, struct net_device 
*dev)
 
   pack_size = len - sizeof (struct ethhdr);
   /* remember message sizes must be rounded up */
-  net_msg->msg_hdr.msgh_size = (((mach_msg_size_t) (sizeof(struct net_rcv_msg)
-                                              - NET_RCV_MAX + pack_size)) + 3) 
& ~3;
+  net_msg->msg_hdr.msgh_size =
+    (((mach_msg_size_t) (sizeof (struct net_rcv_msg)
+                        - sizeof net_msg->sent
+                        - NET_RCV_MAX + pack_size)) + 3) & ~3;
 
   /* Copy packet into message buffer.  */
   eh = (struct ether_header *) (net_msg->header);
diff --git a/libnetfs/file-get-children.c b/libnetfs/file-get-children.c
index bd7e8fc..e8ceddf 100644
--- a/libnetfs/file-get-children.c
+++ b/libnetfs/file-get-children.c
@@ -24,9 +24,9 @@
 
 #include <argz.h>
 
-/* Return any active translators bound to nodes of the receiving
-   filesystem.  CHILDREN is an argz vector containing file names
-   relative to the root of the receiving translator.  */
+/* Return any active translators bound to nodes below CRED.  CHILDREN
+   is an argz vector containing file names relative to the path of
+   CRED.  */
 error_t
 netfs_S_file_get_children (struct protid *cred,
                           char **children,
@@ -93,7 +93,8 @@ netfs_S_file_get_children (struct protid *cred,
   char *c = NULL;
   size_t c_len = 0;
 
-  err = fshelp_get_active_translators (&c, &c_len, check_access);
+  err = fshelp_get_active_translators (&c, &c_len, check_access,
+                                      cred->po->path);
   if (err)
     goto errout;
 
diff --git a/libnetfs/file-get-storage-info-default.c 
b/libnetfs/file-get-storage-info-default.c
index 0ca68c9..4339c7b 100644
--- a/libnetfs/file-get-storage-info-default.c
+++ b/libnetfs/file-get-storage-info-default.c
@@ -23,7 +23,7 @@
 #include <sys/mman.h>
 
 
-error_t
+error_t __attribute__ ((weak))
 netfs_file_get_storage_info (struct iouser *cred,
                             struct node *np,
                             mach_port_t **ports,
diff --git a/libnetfs/get-source.c b/libnetfs/get-source.c
index 73e48be..5a234bc 100644
--- a/libnetfs/get-source.c
+++ b/libnetfs/get-source.c
@@ -21,7 +21,7 @@
 
 #include "priv.h"
 
-error_t
+error_t __attribute__ ((weak))
 netfs_get_source (struct protid *cred, char *source, size_t source_len)
 {
   return EOPNOTSUPP;
diff --git a/libnetfs/init-init.c b/libnetfs/init-init.c
index e98b656..a088ad5 100644
--- a/libnetfs/init-init.c
+++ b/libnetfs/init-init.c
@@ -21,6 +21,9 @@
 
 #include "netfs.h"
 
+/* 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;
diff --git a/libnetfs/make-node.c b/libnetfs/make-node.c
index f20ada1..6bd8109 100644
--- a/libnetfs/make-node.c
+++ b/libnetfs/make-node.c
@@ -21,13 +21,9 @@
 #include "netfs.h"
 #include <hurd/fshelp.h>
 
-struct node *
-netfs_make_node (struct netnode *nn)
+static struct node *
+init_node (struct node *np, struct netnode *nn)
 {
-  struct node *np = malloc (sizeof (struct node));
-  if (! np)
-    return NULL;
-  
   np->nn = nn;
 
   pthread_mutex_init (&np->lock, NULL);
@@ -40,3 +36,24 @@ netfs_make_node (struct netnode *nn)
   
   return np;
 }
+
+struct node *
+netfs_make_node (struct netnode *nn)
+{
+  struct node *np = malloc (sizeof (struct node));
+  if (! np)
+    return NULL;
+
+  return init_node (np, nn);
+}
+
+struct node *
+netfs_make_node_alloc (size_t size)
+{
+  struct node *np = malloc (sizeof (struct node) + size);
+
+  if (np == NULL)
+    return NULL;
+
+  return init_node (np, netfs_node_netnode (np));
+}
diff --git a/libnetfs/netfs.h b/libnetfs/netfs.h
index aef4a3d..fbe2c60 100644
--- a/libnetfs/netfs.h
+++ b/libnetfs/netfs.h
@@ -372,6 +372,33 @@ extern int netfs_maxsymlinks;
    If an error occurs, NULL is returned.  */
 struct node *netfs_make_node (struct netnode *);
 
+/* Create a new node structure.  Also allocate SIZE bytes for the
+   netnode.  The address of the netnode can be obtained using
+   netfs_node_netnode.  The new node will have one hard reference and
+   no light references.  If an error occurs, NULL is returned.  */
+struct node *netfs_make_node_alloc (size_t size);
+
+/* To avoid breaking the ABI whenever sizeof (struct node) changes, we
+   explicitly provide the size.  The following two functions will use
+   this value for offset calculations.  */
+extern const size_t _netfs_sizeof_struct_node;
+
+/* Return the address of the netnode for NODE.  NODE must have been
+   allocated using netfs_make_node_alloc.  */
+static inline struct netnode *
+netfs_node_netnode (struct node *node)
+{
+  return (struct netnode *) ((char *) node + _netfs_sizeof_struct_node);
+}
+
+/* Return the address of the node for NETNODE.  NETNODE must have been
+   allocated using netfs_make_node_alloc.  */
+static inline struct node *
+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;
diff --git a/libnetfs/set-get-trans.c b/libnetfs/set-get-trans.c
index b58668d..c0a4370 100644
--- a/libnetfs/set-get-trans.c
+++ b/libnetfs/set-get-trans.c
@@ -28,7 +28,7 @@
 /* The user may define this function.  Attempt to set the passive
    translator record for FILE to ARGZ (of length ARGZLEN) for user
    CRED. */
-error_t
+error_t __attribute__ ((weak))
 netfs_set_translator (struct iouser *cred, struct node *np,
                      char *argz, size_t argzlen)
 {
@@ -40,7 +40,7 @@ netfs_set_translator (struct iouser *cred, struct node *np,
    mode, look up the name of its translator.  Store the name into newly
    malloced storage, and return it in *ARGZ; set *ARGZ_LEN to the total
    length.  */
-error_t
+error_t __attribute__ ((weak))
 netfs_get_translator (struct node *node, char **argz, size_t *argz_len)
 {
   return EOPNOTSUPP;
diff --git a/libnetfs/trans-callback.c b/libnetfs/trans-callback.c
index 4dec162..f4f0c62 100644
--- a/libnetfs/trans-callback.c
+++ b/libnetfs/trans-callback.c
@@ -74,7 +74,10 @@ _netfs_translator_callback2_fn (void *cookie1, void 
*cookie2, int flags,
       return 0;
     }
   else
-    return errno;
+    {
+      iohelp_free_iouser (user);
+      return errno;
+    }
 }
 
 fshelp_fetch_root_callback1_t _netfs_translator_callback1 =
diff --git a/libpager/Makefile b/libpager/Makefile
index b622295..a15a899 100644
--- a/libpager/Makefile
+++ b/libpager/Makefile
@@ -22,7 +22,7 @@ SRCS = data-request.c data-return.c data-unlock.c 
pager-port.c \
        inhibit-term.c lock-completed.c lock-object.c mark-error.c \
        no-senders.c object-init.c object-terminate.c pagemap.c \
        pager-create.c pager-flush.c pager-shutdown.c pager-sync.c \
-       stubs.c seqnos.c demuxer.c chg-compl.c pager-attr.c clean.c \
+       stubs.c demuxer.c chg-compl.c pager-attr.c clean.c \
        dropweak.c notify-stubs.c get-upi.c pager-memcpy.c pager-return.c \
        offer-page.c
 installhdrs = pager.h
diff --git a/libpager/chg-compl.c b/libpager/chg-compl.c
index d77c46c..89ccfc8 100644
--- a/libpager/chg-compl.c
+++ b/libpager/chg-compl.c
@@ -37,7 +37,6 @@ _pager_seqnos_memory_object_change_completed (struct pager *p,
     }
   
   pthread_mutex_lock (&p->interlock);
-  _pager_wait_for_seqno (p, seq);
 
   for (ar = p->attribute_requests; ar; ar = ar->next)
     if (ar->may_cache == maycache && ar->copy_strategy == strat)
@@ -46,8 +45,7 @@ _pager_seqnos_memory_object_change_completed (struct pager *p,
          pthread_cond_broadcast (&p->wakeup);
        break;
       }
-  
-  _pager_release_seqno (p, seq);
+
   pthread_mutex_unlock (&p->interlock);
   return 0;
 }
diff --git a/libpager/data-request.c b/libpager/data-request.c
index 82ce904..18f3de6 100644
--- a/libpager/data-request.c
+++ b/libpager/data-request.c
@@ -41,7 +41,6 @@ _pager_seqnos_memory_object_data_request (struct pager *p,
 
   /* Acquire the right to meddle with the pagemap */
   pthread_mutex_lock (&p->interlock);
-  _pager_wait_for_seqno (p, seqno);
 
   /* sanity checks -- we don't do multi-page requests yet.  */
   if (control != p->memobjcntl)
@@ -105,7 +104,6 @@ _pager_seqnos_memory_object_data_request (struct pager *p,
     }
 
   /* Let someone else in.  */
-  _pager_release_seqno (p, seqno);
   pthread_mutex_unlock (&p->interlock);
 
   if (!doread)
@@ -139,7 +137,6 @@ _pager_seqnos_memory_object_data_request (struct pager *p,
  allow_release_out:
   _pager_allow_termination (p);
  release_out:
-  _pager_release_seqno (p, seqno);
   pthread_mutex_unlock (&p->interlock);
   return 0;
 }
diff --git a/libpager/data-return.c b/libpager/data-return.c
index ee6c6e8..f16f323 100644
--- a/libpager/data-return.c
+++ b/libpager/data-return.c
@@ -52,7 +52,6 @@ _pager_do_write_request (struct pager *p,
 
   /* Acquire the right to meddle with the pagemap */
   pthread_mutex_lock (&p->interlock);
-  _pager_wait_for_seqno (p, seqno);
 
   /* sanity checks -- we don't do multi-page requests yet.  */
   if (control != p->memobjcntl)
@@ -101,7 +100,6 @@ _pager_do_write_request (struct pager *p,
           notified[i] = (p->notify_on_evict
                          && ! (pm_entries[i] & PM_PAGEINWAIT));
 
-        _pager_release_seqno (p, seqno);
         goto notify;
       }
       else {
@@ -158,7 +156,6 @@ _pager_do_write_request (struct pager *p,
       }
 
   /* Let someone else in. */
-  _pager_release_seqno (p, seqno);
   pthread_mutex_unlock (&p->interlock);
 
   /* This is inefficient; we should send all the pages to the device at once
@@ -251,7 +248,6 @@ _pager_do_write_request (struct pager *p,
   return 0;
 
  release_out:
-  _pager_release_seqno (p, seqno);
   pthread_mutex_unlock (&p->interlock);
   return 0;
 }
diff --git a/libpager/data-unlock.c b/libpager/data-unlock.c
index 599237c..8c7c776 100644
--- a/libpager/data-unlock.c
+++ b/libpager/data-unlock.c
@@ -35,11 +35,6 @@ _pager_seqnos_memory_object_data_unlock (struct pager *p,
       || p->port.class != _pager_class)
     return EOPNOTSUPP;
 
-  pthread_mutex_lock (&p->interlock);
-  _pager_wait_for_seqno (p, seqno);
-  _pager_release_seqno (p, seqno);
-  pthread_mutex_unlock (&p->interlock);
-
   if (p->pager_state != NORMAL)
     {
       printf ("pager in wrong state for unlock\n");
diff --git a/libpager/demuxer.c b/libpager/demuxer.c
index b4d4054..efdf285 100644
--- a/libpager/demuxer.c
+++ b/libpager/demuxer.c
@@ -1,5 +1,5 @@
 /* Demuxer for pager library
-   Copyright (C) 1994, 1995, 2002, 2011 Free Software Foundation
+   Copyright (C) 1993-2014 Free Software Foundation
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -15,26 +15,291 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
 
+#include <error.h>
+#include <mach/mig_errors.h>
+#include <pthread.h>
+#include <string.h>
+
 #include "priv.h"
 #include "memory_object_S.h"
 #include "notify_S.h"
+#include "queue.h"
+
+/*
+  Worker pool for the server functions.
+
+  A single thread receives messages from the port bucket and puts them
+  into a queue.  A fixed number of consumers actually execute the
+  server functions and send the reply.
+
+  The requests to an object O have to be processed in the order they
+  were received.  To this end, each worker has a local queue and a
+  tag.  If a thread processes a request to O, it sets its tag to a
+  unique identifier representing O.  If another thread now dequeues a
+  second request to O, it enqueues it to the first workers queue.
+
+  At least one worker thread is necessary.
+*/
+#define WORKER_COUNT 1
+
+/* An request contains the message received from the port set.  */
+struct request
+{
+  struct item item;
+  mig_routine_t routine;
+  mach_msg_header_t *inp;
+  mach_msg_header_t *outp;
+};
+
+/* A worker.  */
+struct worker
+{
+  struct requests *requests;   /* our pagers request queue */
+  struct queue queue;  /* other workers may delegate requests to us */
+  unsigned long tag;   /* tag of the object we are working on */
+};
+
+/* This is the queue for incoming requests.  A single thread receives
+   messages from the port set, looks the service routine up, and
+   enqueues the request here.  */
+struct requests
+{
+  struct port_bucket *bucket;
+  struct queue queue;
+  int asleep;
+  pthread_cond_t wakeup;
+  pthread_mutex_t lock;
+  struct worker workers[WORKER_COUNT];
+};
 
 /* Demultiplex a single message directed at a pager port; INP is the
    message received; fill OUTP with the reply.  */
-int
-pager_demuxer (mach_msg_header_t *inp,
+static int
+pager_demuxer (struct requests *requests,
+              mach_msg_header_t *inp,
               mach_msg_header_t *outp)
 {
+  error_t err = MIG_NO_REPLY;
+
+  /* The maximum size of the reply is 2048 bytes.  See the MIG source
+     for details.  */
+  const mach_msg_size_t max_size = 2048;
+
   mig_routine_t routine;
-  if ((routine = _pager_seqnos_memory_object_server_routine (inp)) ||
-      (routine = _pager_seqnos_notify_server_routine (inp)))
+  if (! ((routine = _pager_seqnos_memory_object_server_routine (inp)) ||
+        (routine = _pager_seqnos_notify_server_routine (inp))))
+    return FALSE;
+
+#define MASK   (8u - 1u)
+  mach_msg_size_t padded_size = (inp->msgh_size + MASK) & ~MASK;
+#undef MASK
+
+  struct request *r = malloc (sizeof *r + padded_size + max_size);
+  if (r == NULL)
+    {
+      err = ENOMEM;
+      goto out;
+    }
+
+  r->routine = routine;
+  r->inp = (mach_msg_header_t *) ((char *) r + sizeof *r);
+  memcpy (r->inp, inp, inp->msgh_size);
+
+  r->outp = (mach_msg_header_t *) ((char *) r + sizeof *r + padded_size);
+  memcpy (r->outp, outp, sizeof *outp);
+
+  pthread_mutex_lock (&requests->lock);
+
+  queue_enqueue (&requests->queue, &r->item);
+
+  /* Awake worker.  */
+  if (requests->asleep > 0)
+      pthread_cond_signal (&requests->wakeup);
+
+  pthread_mutex_unlock (&requests->lock);
+
+  /* A worker thread will reply.  */
+  err = MIG_NO_REPLY;
+
+ out:
+  ((mig_reply_header_t *) outp)->RetCode = err;
+  return TRUE;
+}
+
+/* Consumes requests from the queue.  */
+static void *
+worker_func (void *arg)
+{
+  struct worker *self = (struct worker *) arg;
+  struct requests *requests = self->requests;
+  struct request *r = NULL;
+
+  while (1)
+    {
+      int i;
+      mach_msg_return_t mr;
+
+      /* Free previous message.  */
+      free (r);
+
+      pthread_mutex_lock (&requests->lock);
+
+      /* First, look in our queue for more requests to the object we
+        have been working on lately.  Some other thread might have
+        delegated them to us.  */
+      r = queue_dequeue (&self->queue);
+      if (r != NULL)
+       goto got_one;
+
+      /* Nope.  Clear our tag and...  */
+      self->tag = 0;
+
+    get_request_locked:
+      /* ... get a request from the global queue instead.  */
+      while ((r = queue_dequeue (&requests->queue)) == NULL)
+       {
+         requests->asleep += 1;
+         pthread_cond_wait (&requests->wakeup, &requests->lock);
+         requests->asleep -= 1;
+       }
+
+      for (i = 0; i < WORKER_COUNT; i++)
+       if (requests->workers[i].tag
+           == (unsigned long) r->inp->msgh_local_port)
+         {
+           /* Some other thread is working on that object.  Delegate
+              the request to that worker.  */
+           queue_enqueue (&requests->workers[i].queue, &r->item);
+           goto get_request_locked;
+         }
+
+      /* Claim responsibility for this object by setting our tag.  */
+      self->tag = (unsigned long) r->inp->msgh_local_port;
+
+    got_one:
+      pthread_mutex_unlock (&requests->lock);
+
+      /* Call the server routine.  */
+      (*r->routine) (r->inp, r->outp);
+
+      /* What follows is basically the second part of
+        mach_msg_server_timeout.  */
+      mig_reply_header_t *request = (mig_reply_header_t *) r->inp;
+      mig_reply_header_t *reply = (mig_reply_header_t *) r->outp;
+
+      switch (reply->RetCode)
+       {
+       case KERN_SUCCESS:
+         /* Hunky dory.  */
+         break;
+
+       case MIG_NO_REPLY:
+         /* The server function wanted no reply sent.
+            Loop for another request.  */
+         continue;
+
+       default:
+         /* Some error; destroy the request message to release any
+            port rights or VM it holds.  Don't destroy the reply port
+            right, so we can send an error message.  */
+         request->Head.msgh_remote_port = MACH_PORT_NULL;
+         mach_msg_destroy (&request->Head);
+         break;
+       }
+
+      if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
+       {
+         /* No reply port, so destroy the reply.  */
+         if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
+           mach_msg_destroy (&reply->Head);
+         continue;
+       }
+
+      /* Send the reply.  */
+      mr = mach_msg (&reply->Head,
+                    MACH_SEND_MSG,
+                    reply->Head.msgh_size,
+                    0,
+                    MACH_PORT_NULL,
+                    0,
+                    MACH_PORT_NULL);
+
+      switch (mr)
+       {
+       case MACH_SEND_INVALID_DEST:
+         /* The reply can't be delivered, so destroy it.  This error
+            indicates only that the requester went away, so we
+            continue and get the next request.  */
+         mach_msg_destroy (&reply->Head);
+         break;
+
+       default:
+         /* Some other form of lossage; there is not much we can
+            do here.  */
+         error (0, mr, "mach_msg");
+       }
+    }
+
+  /* Not reached.  */
+  return NULL;
+}
+
+/* A top-level function for the paging thread that just services paging
+   requests.  */
+static void *
+service_paging_requests (void *arg)
+{
+  struct requests *requests = arg;
+
+  int demuxer (mach_msg_header_t *inp,
+              mach_msg_header_t *outp)
+  {
+    return pager_demuxer (requests, inp, outp);
+  }
+
+  ports_manage_port_operations_one_thread (requests->bucket,
+                                          demuxer,
+                                          0);
+  /* Not reached.  */
+  return NULL;
+}
+
+/* Start the worker threads libpager uses to service requests.  */
+error_t
+pager_start_workers (struct port_bucket *pager_bucket)
+{
+  error_t err;
+  int i;
+  pthread_t t;
+  struct requests *requests;
+
+  requests = malloc (sizeof *requests);
+  if (requests == NULL)
+    return ENOMEM;
+
+  requests->bucket = pager_bucket;
+  requests->asleep = 0;
+  queue_init (&requests->queue);
+  pthread_cond_init (&requests->wakeup, NULL);
+  pthread_mutex_init (&requests->lock, NULL);
+
+  /* Make a thread to service paging requests.  */
+  err = pthread_create (&t, NULL, service_paging_requests, requests);
+  if (err)
+    return err;
+  pthread_detach (t);
+
+  for (i = 0; i < WORKER_COUNT; i++)
     {
-      (*routine) (inp, outp);
-      return TRUE;
+      requests->workers[i].requests = requests;
+      requests->workers[i].tag = 0;
+      queue_init (&requests->workers[i].queue);
+
+      err = pthread_create (&t, NULL, &worker_func, &requests->workers[i]);
+      if (err)
+       return err;
+      pthread_detach (t);
     }
 
-  /* Synchronize our bookkeeping of the port's seqno with the one
-     consumed by this bogus message.  */
-  _pager_update_seqno (inp->msgh_local_port, inp->msgh_seqno);
-  return FALSE;
+  return err;
 }
diff --git a/libpager/lock-completed.c b/libpager/lock-completed.c
index a3f3f16..30b1dd3 100644
--- a/libpager/lock-completed.c
+++ b/libpager/lock-completed.c
@@ -37,7 +37,6 @@ _pager_seqnos_memory_object_lock_completed (struct pager *p,
     return EOPNOTSUPP;
 
   pthread_mutex_lock (&p->interlock);
-  _pager_wait_for_seqno (p, seqno);
 
   if (control != p->memobjcntl)
     {
@@ -59,7 +58,6 @@ _pager_seqnos_memory_object_lock_completed (struct pager *p,
       }
       
  out:
-  _pager_release_seqno (p, seqno);
   pthread_mutex_unlock (&p->interlock);
 
   return err;
diff --git a/libpager/no-senders.c b/libpager/no-senders.c
index c21dfc2..d0bbe27 100644
--- a/libpager/no-senders.c
+++ b/libpager/no-senders.c
@@ -29,7 +29,6 @@ _pager_do_seqnos_mach_notify_no_senders (struct port_info *pi,
       pi->class != _pager_class)
     return EOPNOTSUPP;
 
-  _pager_update_seqno_p ((struct pager *) pi, seqno);
   ports_no_senders (pi, mscount);
 
   return 0;
diff --git a/libpager/notify-stubs.c b/libpager/notify-stubs.c
index ba13882..a826420 100644
--- a/libpager/notify-stubs.c
+++ b/libpager/notify-stubs.c
@@ -28,8 +28,6 @@ _pager_do_seqnos_mach_notify_port_deleted (struct port_info 
*pi,
                                           mach_port_t name
                                           __attribute__ ((unused)))
 {
-  _pager_update_seqno_p ((struct pager *) pi, seqno);
-
   return 0;
 }
 
@@ -39,8 +37,6 @@ _pager_do_seqnos_mach_notify_msg_accepted (struct port_info 
*pi,
                                           mach_port_t name
                                             __attribute__ ((unused)))
 {
-  _pager_update_seqno_p ((struct pager *) pi, seqno);
-
   return 0;
 }
 
@@ -50,8 +46,6 @@ _pager_do_seqnos_mach_notify_port_destroyed (struct port_info 
*pi,
                                             mach_port_t name
                                               __attribute__ ((unused)))
 {
-  _pager_update_seqno_p ((struct pager *) pi, seqno);
-
   return 0;
 }
 
@@ -59,8 +53,6 @@ error_t
 _pager_do_seqnos_mach_notify_send_once (struct port_info *pi,
                                        mach_port_seqno_t seqno)
 {
-  _pager_update_seqno_p ((struct pager *) pi, seqno);
-
   return 0;
 }
 
@@ -70,7 +62,5 @@ _pager_do_seqnos_mach_notify_dead_name (struct port_info *pi,
                                        mach_port_t name
                                          __attribute__ ((unused)))
 {
-  _pager_update_seqno_p ((struct pager *) pi, seqno);
-
   return 0;
 }
diff --git a/libpager/object-init.c b/libpager/object-init.c
index 6683e24..eb62c44 100644
--- a/libpager/object-init.c
+++ b/libpager/object-init.c
@@ -33,7 +33,6 @@ _pager_seqnos_memory_object_init (struct pager *p,
     return EOPNOTSUPP;
 
   pthread_mutex_lock (&p->interlock);
-  _pager_wait_for_seqno (p, seqno);
 
   if (pagesize != __vm_page_size)
     {
@@ -69,7 +68,6 @@ _pager_seqnos_memory_object_init (struct pager *p,
   p->pager_state = NORMAL;
 
  out:
-  _pager_release_seqno (p, seqno);
   pthread_mutex_unlock (&p->interlock);
 
   return 0;
diff --git a/libpager/object-terminate.c b/libpager/object-terminate.c
index 365ba27..e8c6f38 100644
--- a/libpager/object-terminate.c
+++ b/libpager/object-terminate.c
@@ -32,16 +32,15 @@ _pager_seqnos_memory_object_terminate (struct pager *p,
     return EOPNOTSUPP;
 
   pthread_mutex_lock (&p->interlock);
-  _pager_wait_for_seqno (p, seqno);
-  
+
   if (control != p->memobjcntl)
     {
-      printf ("incg terminate: wrong control port");
+      printf ("incg terminate: wrong control port\n");
       goto out;
     }
   if (name != p->memobjname)
     {
-      printf ("incg terminate: wrong name port");
+      printf ("incg terminate: wrong name port\n");
       goto out;
     }
 
@@ -75,7 +74,6 @@ _pager_seqnos_memory_object_terminate (struct pager *p,
 #endif
 
  out:
-  _pager_release_seqno (p, seqno);
   pthread_mutex_unlock (&p->interlock);
 
   return 0;
diff --git a/libpager/pager.h b/libpager/pager.h
index d0572af..29ec833 100644
--- a/libpager/pager.h
+++ b/libpager/pager.h
@@ -25,11 +25,8 @@
    scope.  */
 struct user_pager_info;
 
-/* This de-muxer function is for use within libports_demuxer. */
-/* INP is a message we've received; OUTP will be filled in with
-   a reply message.  */
-int pager_demuxer (mach_msg_header_t *inp,
-                  mach_msg_header_t *outp);
+/* Start the worker threads libpager uses to service requests.  */
+error_t pager_start_workers (struct port_bucket *pager_bucket);
 
 /* Create a new pager.  The pager will have a port created for it
    (using libports, in BUCKET) and will be immediately ready
diff --git a/libpager/priv.h b/libpager/priv.h
index 1f8405a..4576e12 100644
--- a/libpager/priv.h
+++ b/libpager/priv.h
@@ -134,10 +134,6 @@ extern int _pager_page_errors[];
 struct port_class *_pager_class;
 
 
-void _pager_wait_for_seqno (struct pager *, mach_port_seqno_t);
-void _pager_release_seqno (struct pager *, mach_port_seqno_t);
-void _pager_update_seqno (mach_port_t, mach_port_seqno_t);
-void _pager_update_seqno_p (struct pager *, mach_port_seqno_t);
 void _pager_block_termination (struct pager *);
 void _pager_allow_termination (struct pager *);
 error_t _pager_pagemap_resize (struct pager *, vm_address_t);
diff --git a/libpager/queue.h b/libpager/queue.h
new file mode 100644
index 0000000..d3cf738
--- /dev/null
+++ b/libpager/queue.h
@@ -0,0 +1,61 @@
+/* A FIFO queue with constant-time enqueue and dequeue operations.
+
+   Copyright (C) 2014 Free Software Foundation, Inc.
+
+   Written by Justus Winter <address@hidden>
+
+   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/>.  */
+
+/* A FIFO queue with constant-time enqueue and dequeue operations.  */
+struct item {
+  struct item *next;
+};
+
+struct queue {
+  struct item *head;
+  struct item **tail;
+};
+
+static inline void
+queue_init (struct queue *q)
+{
+  q->head = NULL;
+  q->tail = &q->head;
+}
+
+static inline void
+queue_enqueue (struct queue *q, struct item *r)
+{
+  *q->tail = r;
+  q->tail = &r->next;
+  r->next = NULL;
+}
+
+static inline void *
+queue_dequeue (struct queue *q)
+{
+  struct item *r = q->head;
+  if (r == NULL)
+    return NULL;
+
+  /* Pop the first item off.  */
+  if ((q->head = q->head->next) == NULL)
+    /* The queue is empty, fix tail pointer.  */
+    q->tail = &q->head;
+
+  r->next = NULL;
+  return r;
+}
diff --git a/libpager/seqnos.c b/libpager/seqnos.c
deleted file mode 100644
index cab2f33..0000000
--- a/libpager/seqnos.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/* Sequence number synchronization routines for pager library
-   Copyright (C) 1994, 2011 Free Software Foundation
-
-   This program 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.
-
-   This program 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 this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
-#include "priv.h"
-#include <assert.h>
-
-/* The message with seqno SEQNO has just been dequeued for pager P;
-   wait until all preceding messages have had a chance and then
-   return.  */
-void
-_pager_wait_for_seqno (struct pager *p,
-                      mach_port_seqno_t seqno)
-{
-  while (seqno != p->seqno + 1)
-    {
-      p->waitingforseqno = 1;
-      pthread_cond_wait (&p->wakeup, &p->interlock);
-    }
-}
-
-
-/* Allow the next message for pager P (potentially blocked in
-   _pager_wait_for_seqno) to be handled.  */
-void
-_pager_release_seqno (struct pager *p,
-                     mach_port_seqno_t seqno)
-{
-  assert (seqno == p->seqno + 1);
-  p->seqno = seqno;
-  if (p->waitingforseqno)
-    {
-      p->waitingforseqno = 0;
-      pthread_cond_broadcast (&p->wakeup);
-    }
-}
-
-
-/* Just update the seqno.  */
-void
-_pager_update_seqno (mach_port_t object,
-                     mach_port_seqno_t seqno)
-{
-  struct pager *p;
-
-  p = ports_lookup_port (0, object, _pager_class);
-  _pager_update_seqno_p (p, seqno);
-  if (p)
-    ports_port_deref (p);
-}
-
-
-/* Just update the seqno, pointer version.  */
-void
-_pager_update_seqno_p (struct pager *p,
-                       mach_port_seqno_t seqno)
-{
-  if (p
-      && p->port.class == _pager_class)
-    {
-      pthread_mutex_lock (&p->interlock);
-      _pager_wait_for_seqno (p, seqno);
-      _pager_release_seqno (p, seqno);
-      pthread_mutex_unlock (&p->interlock);
-    }
-}
diff --git a/libpager/stubs.c b/libpager/stubs.c
index 411f483..c7f1a5a 100644
--- a/libpager/stubs.c
+++ b/libpager/stubs.c
@@ -29,9 +29,6 @@ _pager_seqnos_memory_object_copy (struct pager *p,
                           mach_port_t new)
 {
   printf ("m_o_copy called\n");
-
-  _pager_update_seqno_p (p, seq);
-
   return EOPNOTSUPP;
 }
 
@@ -44,9 +41,6 @@ _pager_seqnos_memory_object_data_write (struct pager *p,
                                 vm_size_t data_cnt)
 {
   printf ("m_o_data_write called\n");
-
-  _pager_update_seqno_p (p, seq);
-
   return EOPNOTSUPP;
 }
 
@@ -60,8 +54,5 @@ _pager_seqnos_memory_object_supply_completed (struct pager *p,
                                       vm_offset_t err_off)
 {
   printf ("m_o_supply_completed called\n");
-
-  _pager_update_seqno_p (p, seq);
-
   return EOPNOTSUPP;
 }
diff --git a/libpipe/pipe.c b/libpipe/pipe.c
index f9300e7..b7ee851 100644
--- a/libpipe/pipe.c
+++ b/libpipe/pipe.c
@@ -316,7 +316,11 @@ pipe_send (struct pipe *pipe, int noblock, void *source,
           mach_port_t *ports, size_t num_ports,
           size_t *amount)
 {
-  error_t err = 0;
+  error_t err;
+
+  /* Nothing to do.  */
+  if (data_len == 0 && control_len == 0 && num_ports == 0)
+    return 0;
 
   err = pipe_wait_writable (pipe, noblock);
   if (err)
diff --git a/libports/bucket-iterate.c b/libports/bucket-iterate.c
index babc204..b021b99 100644
--- a/libports/bucket-iterate.c
+++ b/libports/bucket-iterate.c
@@ -25,7 +25,7 @@
 /* Internal entrypoint for both ports_bucket_iterate and ports_class_iterate.
    If CLASS is non-null, call FUN only for ports in that class.  */
 error_t
-_ports_bucket_class_iterate (struct port_bucket *bucket,
+_ports_bucket_class_iterate (struct hurd_ihash *ht,
                             struct port_class *class,
                             error_t (*fun)(void *))
 {
@@ -35,37 +35,37 @@ _ports_bucket_class_iterate (struct port_bucket *bucket,
   size_t i, n, nr_items;
   error_t err;
 
-  pthread_mutex_lock (&_ports_lock);
+  pthread_rwlock_rdlock (&_ports_htable_lock);
 
-  if (bucket->htable.nr_items == 0)
+  if (ht->nr_items == 0)
     {
-      pthread_mutex_unlock (&_ports_lock);
+      pthread_rwlock_unlock (&_ports_htable_lock);
       return 0;
     }
 
-  nr_items = bucket->htable.nr_items;
+  nr_items = ht->nr_items;
   p = malloc (nr_items * sizeof *p);
   if (p == NULL)
     {
-      pthread_mutex_unlock (&_ports_lock);
+      pthread_rwlock_unlock (&_ports_htable_lock);
       return ENOMEM;
     }
 
   n = 0;
-  HURD_IHASH_ITERATE (&bucket->htable, arg)
+  HURD_IHASH_ITERATE (ht, arg)
     {
       struct port_info *const pi = arg;
 
       if (class == 0 || pi->class == class)
        {
-         pi->refcnt++;
+         refcounts_ref (&pi->refcounts, NULL);
          p[n] = pi;
          n++;
        }
     }
-  pthread_mutex_unlock (&_ports_lock);
+  pthread_rwlock_unlock (&_ports_htable_lock);
 
-  if (n != nr_items)
+  if (n != 0 && n != nr_items)
     {
       /* We allocated too much.  Release unused memory.  */
       void **new = realloc (p, n * sizeof *p);
@@ -89,5 +89,5 @@ error_t
 ports_bucket_iterate (struct port_bucket *bucket,
                      error_t (*fun)(void *))
 {
-  return _ports_bucket_class_iterate (bucket, 0, fun);
+  return _ports_bucket_class_iterate (&bucket->htable, NULL, fun);
 }
diff --git a/libports/claim-right.c b/libports/claim-right.c
index 4851ea3..85592ff 100644
--- a/libports/claim-right.c
+++ b/libports/claim-right.c
@@ -34,10 +34,13 @@ ports_claim_right (void *portstruct)
   if (ret == MACH_PORT_NULL)
     return ret;
 
-  pthread_mutex_lock (&_ports_lock);
+  pthread_rwlock_wrlock (&_ports_htable_lock);
+  hurd_ihash_locp_remove (&_ports_htable, pi->ports_htable_entry);
   hurd_ihash_locp_remove (&pi->bucket->htable, pi->hentry);
+  pthread_rwlock_unlock (&_ports_htable_lock);
   err = mach_port_move_member (mach_task_self (), ret, MACH_PORT_NULL);
   assert_perror (err);
+  pthread_mutex_lock (&_ports_lock);
   pi->port_right = MACH_PORT_NULL;
   if (pi->flags & PORT_HAS_SENDRIGHTS)
     {
diff --git a/libports/class-iterate.c b/libports/class-iterate.c
index 1f8878a..df33818 100644
--- a/libports/class-iterate.c
+++ b/libports/class-iterate.c
@@ -23,13 +23,5 @@ error_t
 ports_class_iterate (struct port_class *class,
                     error_t (*fun)(void *))
 {
-  pthread_mutex_lock (&_ports_lock);
-  if (class->ports != 0)
-    {
-      struct port_bucket *bucket = class->ports->bucket;
-      pthread_mutex_unlock (&_ports_lock);
-      return _ports_bucket_class_iterate (bucket, class, fun);
-    }
-  pthread_mutex_unlock (&_ports_lock);
-  return 0;
+  return _ports_bucket_class_iterate (&_ports_htable, class, fun);
 }
diff --git a/libports/complete-deallocate.c b/libports/complete-deallocate.c
index 8ce095b..0d852f5 100644
--- a/libports/complete-deallocate.c
+++ b/libports/complete-deallocate.c
@@ -29,15 +29,28 @@ _ports_complete_deallocate (struct port_info *pi)
 
   if (pi->port_right)
     {
+      struct references result;
+
+      pthread_rwlock_wrlock (&_ports_htable_lock);
+      refcounts_references (&pi->refcounts, &result);
+      if (result.hard > 0 || result.weak > 0)
+        {
+          /* A reference was reacquired through a hash table lookup.
+             It's fine, we didn't touch anything yet. */
+          pthread_mutex_unlock (&_ports_htable_lock);
+          return;
+        }
+
+      hurd_ihash_locp_remove (&_ports_htable, pi->ports_htable_entry);
       hurd_ihash_locp_remove (&pi->bucket->htable, pi->hentry);
+      pthread_rwlock_unlock (&_ports_htable_lock);
+
       mach_port_mod_refs (mach_task_self (), pi->port_right,
                          MACH_PORT_RIGHT_RECEIVE, -1);
       pi->port_right = MACH_PORT_NULL;
     }
 
-  *pi->prevp = pi->next;
-  if (pi->next)
-    pi->next->prevp = pi->prevp;
+  pthread_mutex_lock (&_ports_lock);
 
   pi->bucket->count--;
   pi->class->count--;
diff --git a/libports/create-bucket.c b/libports/create-bucket.c
index 52d50c3..2c5f1b6 100644
--- a/libports/create-bucket.c
+++ b/libports/create-bucket.c
@@ -48,11 +48,5 @@ ports_create_bucket ()
 
   hurd_ihash_init (&ret->htable, offsetof (struct port_info, hentry));
   ret->rpcs = ret->flags = ret->count = 0;
-
-  pthread_mutex_lock (&_ports_lock);
-  ret->next = _ports_all_buckets;
-  _ports_all_buckets = ret;
-  pthread_mutex_unlock (&_ports_lock);
-
   return ret;
 }
diff --git a/libports/create-class.c b/libports/create-class.c
index 12c8add..782f52b 100644
--- a/libports/create-class.c
+++ b/libports/create-class.c
@@ -39,7 +39,6 @@ ports_create_class (void (*clean_routine)(void *),
   cl->dropweak_routine = dropweak_routine;
   cl->flags = 0;
   cl->rpcs = 0;
-  cl->ports = NULL;
   cl->count = 0;
   cl->uninhibitable_rpcs = ports_default_uninhibitable_rpcs;
 
diff --git a/libports/create-internal.c b/libports/create-internal.c
index 8551297..2d85931 100644
--- a/libports/create-internal.c
+++ b/libports/create-internal.c
@@ -54,8 +54,7 @@ _ports_create_port_internal (struct port_class *class,
     }
 
   pi->class = class;
-  pi->refcnt = 1;
-  pi->weakrefcnt = 0;
+  refcounts_init (&pi->refcounts, 1, 0);
   pi->cancel_threshold = 0;
   pi->mscount = 0;
   pi->flags = 0;
@@ -81,15 +80,22 @@ _ports_create_port_internal (struct port_class *class,
       goto loop;
     }
 
+  pthread_rwlock_wrlock (&_ports_htable_lock);
+  err = hurd_ihash_add (&_ports_htable, port, pi);
+  if (err)
+    {
+      pthread_rwlock_unlock (&_ports_htable_lock);
+      goto lose;
+    }
   err = hurd_ihash_add (&bucket->htable, port, pi);
   if (err)
-    goto lose;
+    {
+      hurd_ihash_locp_remove (&_ports_htable, pi->ports_htable_entry);
+      pthread_rwlock_unlock (&_ports_htable_lock);
+      goto lose;
+    }
+  pthread_rwlock_unlock (&_ports_htable_lock);
 
-  pi->next = class->ports;
-  pi->prevp = &class->ports;
-  if (class->ports)
-    class->ports->prevp = &pi->next;
-  class->ports = pi;
   bucket->count++;
   class->count++;
   pthread_mutex_unlock (&_ports_lock);
diff --git a/libports/dead-name.c b/libports/dead-name.c
index de89ba6..9363771 100644
--- a/libports/dead-name.c
+++ b/libports/dead-name.c
@@ -21,7 +21,7 @@
 #include "ports.h"
 #include <mach/notify.h>
 
-void
+void __attribute__ ((weak))
 ports_dead_name (void *notify, mach_port_t dead_name)
 {
   ports_interrupt_notified_rpcs (notify, dead_name, MACH_NOTIFY_DEAD_NAME);
diff --git a/libports/destroy-right.c b/libports/destroy-right.c
index 65e19c7..448b379 100644
--- a/libports/destroy-right.c
+++ b/libports/destroy-right.c
@@ -30,12 +30,13 @@ ports_destroy_right (void *portstruct)
 
   if (pi->port_right != MACH_PORT_NULL)
     {
-      pthread_mutex_lock (&_ports_lock);
+      pthread_rwlock_wrlock (&_ports_htable_lock);
+      hurd_ihash_locp_remove (&_ports_htable, pi->ports_htable_entry);
       hurd_ihash_locp_remove (&pi->bucket->htable, pi->hentry);
+      pthread_rwlock_unlock (&_ports_htable_lock);
       err = mach_port_mod_refs (mach_task_self (), pi->port_right,
                                MACH_PORT_RIGHT_RECEIVE, -1);
       assert_perror (err);
-      pthread_mutex_unlock (&_ports_lock);
 
       pi->port_right = MACH_PORT_NULL;
 
diff --git a/libports/get-right.c b/libports/get-right.c
index 89050c6..8681f46 100644
--- a/libports/get-right.c
+++ b/libports/get-right.c
@@ -41,7 +41,7 @@ ports_get_right (void *port)
   if ((pi->flags & PORT_HAS_SENDRIGHTS) == 0)
     {
       pi->flags |= PORT_HAS_SENDRIGHTS;
-      pi->refcnt++;
+      refcounts_ref (&pi->refcounts, NULL);
       err = mach_port_request_notification (mach_task_self (),
                                            pi->port_right,
                                            MACH_NOTIFY_NO_SENDERS,
diff --git a/libports/import-port.c b/libports/import-port.c
index 226f47e..c337c85 100644
--- a/libports/import-port.c
+++ b/libports/import-port.c
@@ -48,8 +48,7 @@ ports_import_port (struct port_class *class, struct 
port_bucket *bucket,
     return ENOMEM;
   
   pi->class = class;
-  pi->refcnt = 1 + !!stat.mps_srights;
-  pi->weakrefcnt = 0;
+  refcounts_init (&pi->refcounts, 1 + !!stat.mps_srights, 0);
   pi->cancel_threshold = 0;
   pi->mscount = stat.mps_mscount;
   pi->flags = stat.mps_srights ? PORT_HAS_SENDRIGHTS : 0;
@@ -75,15 +74,22 @@ ports_import_port (struct port_class *class, struct 
port_bucket *bucket,
       goto loop;
     }
 
+  pthread_rwlock_wrlock (&_ports_htable_lock);
+  err = hurd_ihash_add (&_ports_htable, port, pi);
+  if (err)
+    {
+      pthread_rwlock_unlock (&_ports_htable_lock);
+      goto lose;
+    }
   err = hurd_ihash_add (&bucket->htable, port, pi);
   if (err)
-    goto lose;
+    {
+      hurd_ihash_locp_remove (&_ports_htable, pi->ports_htable_entry);
+      pthread_rwlock_unlock (&_ports_htable_lock);
+      goto lose;
+    }
+  pthread_rwlock_unlock (&_ports_htable_lock);
 
-  pi->next = class->ports;
-  pi->prevp = &class->ports;
-  if (class->ports)
-    class->ports->prevp = &pi->next;
-  class->ports = pi;
   bucket->count++;
   class->count++;
   pthread_mutex_unlock (&_ports_lock);
diff --git a/libports/inhibit-all-rpcs.c b/libports/inhibit-all-rpcs.c
index d4a54ba..27e2ec5 100644
--- a/libports/inhibit-all-rpcs.c
+++ b/libports/inhibit-all-rpcs.c
@@ -36,24 +36,23 @@ ports_inhibit_all_rpcs ()
       struct port_bucket *bucket;
       int this_one = 0;
 
-      for (bucket = _ports_all_buckets; bucket; bucket = bucket->next)
+      pthread_rwlock_rdlock (&_ports_htable_lock);
+      HURD_IHASH_ITERATE (&_ports_htable, portstruct)
        {
-         HURD_IHASH_ITERATE (&bucket->htable, portstruct)
+         struct rpc_info *rpc;
+         struct port_info *pi = portstruct;
+
+         for (rpc = pi->current_rpcs; rpc; rpc = rpc->next)
            {
-             struct rpc_info *rpc;
-             struct port_info *pi = portstruct;
-         
-             for (rpc = pi->current_rpcs; rpc; rpc = rpc->next)
-               {
-                 /* Avoid cancelling the calling thread if it's currently
-                    handling a RPC.  */
-                 if (rpc->thread == hurd_thread_self ())
-                   this_one = 1;
-                 else
-                   hurd_thread_cancel (rpc->thread);
-               }
+             /* Avoid cancelling the calling thread if it's currently
+                handling a RPC.  */
+             if (rpc->thread == hurd_thread_self ())
+               this_one = 1;
+             else
+               hurd_thread_cancel (rpc->thread);
            }
        }
+      pthread_rwlock_unlock (&_ports_htable_lock);
 
       while (_ports_total_rpcs > this_one)
        {
diff --git a/libports/inhibit-bucket-rpcs.c b/libports/inhibit-bucket-rpcs.c
index 965aa03..82efdf5 100644
--- a/libports/inhibit-bucket-rpcs.c
+++ b/libports/inhibit-bucket-rpcs.c
@@ -35,6 +35,7 @@ ports_inhibit_bucket_rpcs (struct port_bucket *bucket)
     {
       int this_one = 0;
 
+      pthread_rwlock_rdlock (&_ports_htable_lock);
       HURD_IHASH_ITERATE (&bucket->htable, portstruct)
        {
          struct rpc_info *rpc;
@@ -49,7 +50,7 @@ ports_inhibit_bucket_rpcs (struct port_bucket *bucket)
                hurd_thread_cancel (rpc->thread);
            }
        }
-
+      pthread_rwlock_unlock (&_ports_htable_lock);
 
       while (bucket->rpcs > this_one)
        {
diff --git a/libports/inhibit-class-rpcs.c b/libports/inhibit-class-rpcs.c
index 7ee8653..9a87a5f 100644
--- a/libports/inhibit-class-rpcs.c
+++ b/libports/inhibit-class-rpcs.c
@@ -36,15 +36,24 @@ ports_inhibit_class_rpcs (struct port_class *class)
       struct rpc_info *rpc;
       int this_one = 0;
 
-      for (pi = class->ports; pi; pi = pi->next)
-       for (rpc = pi->current_rpcs; rpc; rpc = rpc->next)
-         {
-           /* Avoid cancelling the calling thread.  */
-           if (rpc->thread == hurd_thread_self ())
-             this_one = 1;
-           else
-             hurd_thread_cancel (rpc->thread);
-         }
+      pthread_rwlock_rdlock (&_ports_htable_lock);
+      HURD_IHASH_ITERATE (&_ports_htable, portstruct)
+       {
+         struct rpc_info *rpc;
+         struct port_info *pi = portstruct;
+         if (pi->class != class)
+           continue;
+
+         for (rpc = pi->current_rpcs; rpc; rpc = rpc->next)
+           {
+             /* Avoid cancelling the calling thread.  */
+             if (rpc->thread == hurd_thread_self ())
+               this_one = 1;
+             else
+               hurd_thread_cancel (rpc->thread);
+           }
+       }
+      pthread_rwlock_unlock (&_ports_htable_lock);
 
       while (class->rpcs > this_one)
        {
diff --git a/libports/init.c b/libports/init.c
index 3ef5388..4a68cb8 100644
--- a/libports/init.c
+++ b/libports/init.c
@@ -19,9 +19,14 @@
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
 
 #include "ports.h"
+#include <stddef.h>
 
 pthread_mutex_t _ports_lock = PTHREAD_MUTEX_INITIALIZER;
 pthread_cond_t _ports_block = PTHREAD_COND_INITIALIZER;
-struct port_bucket *_ports_all_buckets;
+
+struct hurd_ihash _ports_htable =
+  HURD_IHASH_INITIALIZER (offsetof (struct port_info, ports_htable_entry));
+pthread_rwlock_t _ports_htable_lock = PTHREAD_RWLOCK_INITIALIZER;
+
 int _ports_total_rpcs;
 int _ports_flags;
diff --git a/libports/lookup-port.c b/libports/lookup-port.c
index f79f6f0..2f0eee9 100644
--- a/libports/lookup-port.c
+++ b/libports/lookup-port.c
@@ -26,27 +26,20 @@ ports_lookup_port (struct port_bucket *bucket,
                   mach_port_t port,
                   struct port_class *class)
 {
-  struct port_info *pi = 0;
-  
-  pthread_mutex_lock (&_ports_lock);
-
-  if (bucket)
-    pi = hurd_ihash_find (&bucket->htable, port);
-  else
-    for (bucket = _ports_all_buckets; bucket; bucket = bucket->next)
-      {
-       pi = hurd_ihash_find (&bucket->htable, port);
-       if (pi)
-         break;
-      }
-  
-  if (pi && class && pi->class != class)
+  struct port_info *pi;
+
+  pthread_rwlock_rdlock (&_ports_htable_lock);
+
+  pi = hurd_ihash_find (&_ports_htable, port);
+  if (pi
+      && ((class && pi->class != class)
+          || (bucket && pi->bucket != bucket)))
     pi = 0;
 
   if (pi)
-    pi->refcnt++;
+    refcounts_unsafe_ref (&pi->refcounts, NULL);
+
+  pthread_rwlock_unlock (&_ports_htable_lock);
 
-  pthread_mutex_unlock (&_ports_lock);
-  
   return pi;
 }
diff --git a/libports/port-deref-weak.c b/libports/port-deref-weak.c
index beb4842..8432660 100644
--- a/libports/port-deref-weak.c
+++ b/libports/port-deref-weak.c
@@ -25,12 +25,8 @@ void
 ports_port_deref_weak (void *portstruct)
 {
   struct port_info *pi = portstruct;
-  
-  pthread_mutex_lock (&_ports_lock);
-  assert (pi->weakrefcnt);
-  pi->weakrefcnt--;
-  if (pi->refcnt == 0 && pi->weakrefcnt == 0)
+  struct references result;
+  refcounts_deref_weak (&pi->refcounts, &result);
+  if (result.hard == 0 && result.weak == 0)
     _ports_complete_deallocate (pi);
-  else
-    pthread_mutex_unlock (&_ports_lock);
 }
diff --git a/libports/port-deref.c b/libports/port-deref.c
index cf9b238..b97dd13 100644
--- a/libports/port-deref.c
+++ b/libports/port-deref.c
@@ -25,26 +25,24 @@ void
 ports_port_deref (void *portstruct)
 {
   struct port_info *pi = portstruct;
-  int trieddroppingweakrefs = 0;
-
- retry:
-  
-  pthread_mutex_lock (&_ports_lock);
-  
-  if (pi->refcnt == 1 && pi->weakrefcnt
-      && pi->class->dropweak_routine && !trieddroppingweakrefs)
+  struct references result;
+
+  if (pi->class->dropweak_routine)
     {
-      pthread_mutex_unlock (&_ports_lock);
-      (*pi->class->dropweak_routine) (pi);
-      trieddroppingweakrefs = 1;
-      goto retry;
+      /* If we need to call the dropweak routine, we need to hold one
+         reference while doing so.  We use a weak reference for this
+         purpose, which we acquire by demoting our hard reference to a
+         weak one.  */
+      refcounts_demote (&pi->refcounts, &result);
+
+      if (result.hard == 0 && result.weak > 1)
+        (*pi->class->dropweak_routine) (pi);
+
+      refcounts_deref_weak (&pi->refcounts, &result);
     }
-  
-  assert (pi->refcnt);
+  else
+    refcounts_deref (&pi->refcounts, &result);
 
-  pi->refcnt--;
-  if (pi->refcnt == 0 && pi->weakrefcnt == 0)
+  if (result.hard == 0 && result.weak == 0)
     _ports_complete_deallocate (pi);
-  else
-    pthread_mutex_unlock (&_ports_lock);
 }
diff --git a/libports/port-ref-weak.c b/libports/port-ref-weak.c
index c7d3c69..3f62dfe 100644
--- a/libports/port-ref-weak.c
+++ b/libports/port-ref-weak.c
@@ -25,9 +25,5 @@ void
 ports_port_ref_weak (void *portstruct)
 {
   struct port_info *pi = portstruct;
-  
-  pthread_mutex_lock (&_ports_lock);
-  assert (pi->refcnt || pi->weakrefcnt);
-  pi->weakrefcnt++;
-  pthread_mutex_unlock (&_ports_lock);
+  refcounts_ref_weak (&pi->refcounts, NULL);
 }
diff --git a/libports/port-ref.c b/libports/port-ref.c
index 92b7118..9a1c71e 100644
--- a/libports/port-ref.c
+++ b/libports/port-ref.c
@@ -25,9 +25,5 @@ void
 ports_port_ref (void *portstruct)
 {
   struct port_info *pi = portstruct;
-  
-  pthread_mutex_lock (&_ports_lock);
-  assert (pi->refcnt || pi->weakrefcnt);
-  pi->refcnt++;
-  pthread_mutex_unlock (&_ports_lock);
+  refcounts_ref (&pi->refcounts, NULL);
 }
diff --git a/libports/ports.h b/libports/ports.h
index 7f13124..652edb8 100644
--- a/libports/ports.h
+++ b/libports/ports.h
@@ -27,6 +27,7 @@
 #include <hurd/ihash.h>
 #include <mach/notify.h>
 #include <pthread.h>
+#include <refcount.h>
 
 /* These are global values for common flags used in the various structures.
    Not all of these are meaningful in all flag fields.  */
@@ -39,8 +40,7 @@
 struct port_info
 {
   struct port_class *class;
-  int refcnt;
-  int weakrefcnt;
+  refcounts_t refcounts;
   mach_port_mscount_t mscount;
   mach_msg_seqno_t cancel_threshold;
   int flags;
@@ -48,7 +48,7 @@ struct port_info
   struct rpc_info *current_rpcs;
   struct port_bucket *bucket;
   hurd_ihash_locp_t hentry;
-  struct port_info *next, **prevp; /* links on port_class list */
+  hurd_ihash_locp_t ports_htable_entry;
 };
 typedef struct port_info *port_info_t;
 
@@ -61,11 +61,12 @@ typedef struct port_info *port_info_t;
 struct port_bucket
 {
   mach_port_t portset;
+  /* Per-bucket hash table used for fast iteration.  Access must be
+     serialized using _ports_htable_lock.  */
   struct hurd_ihash htable;
   int rpcs;
   int flags;
   int count;
-  struct port_bucket *next;
 };
 /* FLAGS above are the following: */
 #define PORT_BUCKET_INHIBITED  PORTS_INHIBITED
@@ -78,7 +79,6 @@ struct port_class
 {
   int flags;
   int rpcs;
-  struct port_info *ports;
   int count;
   void (*clean_routine) (void *);
   void (*dropweak_routine) (void *);
@@ -277,7 +277,7 @@ error_t ports_class_iterate (struct port_class *class,
                             error_t (*fun)(void *port));
 
 /* Internal entrypoint for above two.  */
-error_t _ports_bucket_class_iterate (struct port_bucket *bucket,
+error_t _ports_bucket_class_iterate (struct hurd_ihash *ht,
                                     struct port_class *class,
                                     error_t (*fun)(void *port));
 
@@ -402,7 +402,19 @@ extern kern_return_t
 /* Private data */
 extern pthread_mutex_t _ports_lock;
 extern pthread_cond_t _ports_block;
-extern struct port_bucket *_ports_all_buckets;
+
+/* A global hash table mapping port names to port_info objects.  This
+   table is used for port lookups and to iterate over classes.
+
+   A port in this hash table carries an implicit light reference.
+   When the reference counts reach zero, we call
+   _ports_complete_deallocate.  There we reacquire our lock
+   momentarily to check whether someone else reacquired a reference
+   through the hash table.  */
+extern struct hurd_ihash _ports_htable;
+/* Access to all hash tables is protected by this lock.  */
+extern pthread_rwlock_t _ports_htable_lock;
+
 extern int _ports_total_rpcs;
 extern int _ports_flags;
 #define _PORTS_INHIBITED       PORTS_INHIBITED
diff --git a/libports/reallocate-from-external.c 
b/libports/reallocate-from-external.c
index 8cccb2a..7205bd9 100644
--- a/libports/reallocate-from-external.c
+++ b/libports/reallocate-from-external.c
@@ -43,8 +43,11 @@ ports_reallocate_from_external (void *portstruct, 
mach_port_t receive)
                            MACH_PORT_RIGHT_RECEIVE, -1);
   assert_perror (err);
 
+  pthread_rwlock_wrlock (&_ports_htable_lock);
+  hurd_ihash_locp_remove (&_ports_htable, pi->ports_htable_entry);
   hurd_ihash_locp_remove (&pi->bucket->htable, pi->hentry);
-  
+  pthread_rwlock_unlock (&_ports_htable_lock);
+
   if ((pi->flags & PORT_HAS_SENDRIGHTS) && !stat.mps_srights)
     {
       dropref = 1;
@@ -53,17 +56,21 @@ ports_reallocate_from_external (void *portstruct, 
mach_port_t receive)
   else if (((pi->flags & PORT_HAS_SENDRIGHTS) == 0) && stat.mps_srights)
     {
       pi->flags |= PORT_HAS_SENDRIGHTS;
-      pi->refcnt++;
+      refcounts_ref (&pi->refcounts, NULL);
     }
   
   pi->port_right = receive;
   pi->cancel_threshold = 0;
   pi->mscount = stat.mps_mscount;
-  
-  err = hurd_ihash_add (&pi->bucket->htable, receive, pi);
+
+  pthread_rwlock_wrlock (&_ports_htable_lock);
+  err = hurd_ihash_add (&_ports_htable, receive, pi);
   assert_perror (err);
+  err = hurd_ihash_add (&pi->bucket->htable, receive, pi);
+  pthread_rwlock_unlock (&_ports_htable_lock);
   pthread_mutex_unlock (&_ports_lock);
-  
+  assert_perror (err);
+
   mach_port_move_member (mach_task_self (), receive, pi->bucket->portset);
   
   if (stat.mps_srights)
diff --git a/libports/reallocate-port.c b/libports/reallocate-port.c
index d2adaeb..cc534eb 100644
--- a/libports/reallocate-port.c
+++ b/libports/reallocate-port.c
@@ -36,7 +36,10 @@ ports_reallocate_port (void *portstruct)
                            MACH_PORT_RIGHT_RECEIVE, -1);
   assert_perror (err);
 
+  pthread_rwlock_wrlock (&_ports_htable_lock);
+  hurd_ihash_locp_remove (&_ports_htable, pi->ports_htable_entry);
   hurd_ihash_locp_remove (&pi->bucket->htable, pi->hentry);
+  pthread_rwlock_unlock (&_ports_htable_lock);
 
   err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
                            &pi->port_right);
@@ -48,9 +51,13 @@ ports_reallocate_port (void *portstruct)
     }
   pi->cancel_threshold = 0;
   pi->mscount = 0;
-  err = hurd_ihash_add (&pi->bucket->htable, pi->port_right, pi);
+  pthread_rwlock_wrlock (&_ports_htable_lock);
+  err = hurd_ihash_add (&_ports_htable, pi->port_right, pi);
   assert_perror (err);
+  err = hurd_ihash_add (&pi->bucket->htable, pi->port_right, pi);
+  pthread_rwlock_unlock (&_ports_htable_lock);
   pthread_mutex_unlock (&_ports_lock);
+  assert_perror (err);
 
   err = mach_port_move_member (mach_task_self (), pi->port_right, 
                               pi->bucket->portset);
diff --git a/libports/transfer-right.c b/libports/transfer-right.c
index 72488a9..776a8d2 100644
--- a/libports/transfer-right.c
+++ b/libports/transfer-right.c
@@ -41,7 +41,10 @@ ports_transfer_right (void *tostruct,
   port = frompi->port_right;
   if (port != MACH_PORT_NULL)
     {
+      pthread_rwlock_wrlock (&_ports_htable_lock);
+      hurd_ihash_locp_remove (&_ports_htable, frompi->ports_htable_entry);
       hurd_ihash_locp_remove (&frompi->bucket->htable, frompi->hentry);
+      pthread_rwlock_unlock (&_ports_htable_lock);
       frompi->port_right = MACH_PORT_NULL;
       if (frompi->flags & PORT_HAS_SENDRIGHTS)
        {
@@ -54,7 +57,10 @@ ports_transfer_right (void *tostruct,
   /* Destroy the existing right in TOPI. */
   if (topi->port_right != MACH_PORT_NULL)
     {
+      pthread_rwlock_wrlock (&_ports_htable_lock);
+      hurd_ihash_locp_remove (&_ports_htable, topi->ports_htable_entry);
       hurd_ihash_locp_remove (&topi->bucket->htable, topi->hentry);
+      pthread_rwlock_unlock (&_ports_htable_lock);
       err = mach_port_mod_refs (mach_task_self (), topi->port_right,
                                MACH_PORT_RIGHT_RECEIVE, -1);
       assert_perror (err);
@@ -66,7 +72,7 @@ ports_transfer_right (void *tostruct,
       else if (((topi->flags & PORT_HAS_SENDRIGHTS) == 0) && hassendrights)
        {
          topi->flags |= PORT_HAS_SENDRIGHTS;
-         topi->refcnt++;
+         refcounts_ref (&topi->refcounts, NULL);
        }
     }
   
@@ -74,10 +80,16 @@ ports_transfer_right (void *tostruct,
   topi->port_right = port;
   topi->cancel_threshold = frompi->cancel_threshold;
   topi->mscount = frompi->mscount;
-  
+
+  pthread_mutex_unlock (&_ports_lock);
+
   if (port)
     {
+      pthread_rwlock_wrlock (&_ports_htable_lock);
+      err = hurd_ihash_add (&_ports_htable, port, topi);
+      assert_perror (err);
       err = hurd_ihash_add (&topi->bucket->htable, port, topi);
+      pthread_rwlock_unlock (&_ports_htable_lock);
       assert_perror (err);
       if (topi->bucket != frompi->bucket)
         {
@@ -86,9 +98,7 @@ ports_transfer_right (void *tostruct,
          assert_perror (err);
        }
     }
-  
-  pthread_mutex_unlock (&_ports_lock);
-  
+
   /* Take care of any lowered reference counts. */
   if (dereffrompi)
     ports_port_deref (frompi);
diff --git a/libshouldbeinlibc/idvec-impgids.c 
b/libshouldbeinlibc/idvec-impgids.c
index 74d3cc1..d89f487 100644
--- a/libshouldbeinlibc/idvec-impgids.c
+++ b/libshouldbeinlibc/idvec-impgids.c
@@ -1,6 +1,6 @@
 /* Add gids implied by a user
 
-   Copyright (C) 1997, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1997, 2001, 2014 Free Software Foundation, Inc.
 
    Written by Miles Bader <address@hidden>
 
@@ -56,7 +56,6 @@ _merge_implied_gids (struct idvec *implied_gids, uid_t uid)
     else
       {
        struct idvec *cache = make_idvec ();
-#ifdef HAVE_GETGROUPLIST
        gid_t _gids[NUM_STATIC_GIDS], *gids = _gids;
        int maxgids = NUM_STATIC_GIDS;
        int ngids = getgrouplist (pw->pw_name, pw->pw_gid, gids, &maxgids);
@@ -79,17 +78,6 @@ _merge_implied_gids (struct idvec *implied_gids, uid_t uid)
            if (gids != _gids)
              free (gids);
          }
-#else
-#warning "getgrouplist() not available; supplementary group IDs unsupported."
-       if (! cache)
-         err = ENOMEM;
-       else
-         {
-           err = idvec_add_new (cache, pw->pw_gid);
-           if (err)
-             idvec_free (cache);
-         }
-#endif
 
        if (! err)
          {
diff --git a/libshouldbeinlibc/timefmt.c b/libshouldbeinlibc/timefmt.c
index a28f58b..cef72e0 100644
--- a/libshouldbeinlibc/timefmt.c
+++ b/libshouldbeinlibc/timefmt.c
@@ -105,7 +105,7 @@ fmt_named_interval (struct timeval *tv, size_t width,
     {{0, 1},       {0, 1},     {0, 0},    {" microsecond", "us", 0 }},
     {{0, 0} }
   };
-  struct tscale *ts = time_scales;
+  struct tscale *ts;
 
   if (width <= 0 || width >= buf_len)
     width = buf_len - 1;
diff --git a/libstore/part.c b/libstore/part.c
index 56e904e..60ef6c2 100644
--- a/libstore/part.c
+++ b/libstore/part.c
@@ -26,6 +26,17 @@
 
 #include <parted/parted.h>
 /*#include <parted/device_gnu.h>*/
+
+/* XXX Until the Hurd specific header is available, provide the
+   declaration of ped_device_new_from_store here.  */
+#warning "Using local declaration of ped_device_new_from_store."
+
+/* Initialize a PedDevice using SOURCE.  The SOURCE will NOT be destroyed;
+   the caller created it, it is the caller's responsilbility to free it
+   after it calls ped_device_destory.  SOURCE is not registered in Parted's
+   list of devices.  */
+PedDevice* ped_device_new_from_store (struct store *source);
+
 #include <string.h>
 #include <error.h>
 
diff --git a/libthreads/cthreads.c b/libthreads/cthreads.c
index aef20be..1361b8b 100644
--- a/libthreads/cthreads.c
+++ b/libthreads/cthreads.c
@@ -150,10 +150,7 @@
 #include <cthreads.h>
 #include <mach/mig_support.h>
 #include "cthread_internals.h"
-
-#ifdef HAVE_USELOCALE
-# include <locale.h>
-#endif
+#include <locale.h>
 
 /*
  * Thread status bits.
@@ -292,11 +289,11 @@ cthread_body(cproc_t self)
                                /*
                                 * Execute the fork request.
                                 */
-#ifdef HAVE_USELOCALE
+
                                /* A fresh thread needs to be bound to the
                                   global locale.  */
                                uselocale (LC_GLOBAL_LOCALE);
-#endif
+
                                t->result = (*(t->func))(t->arg);
                        }
                        /*
diff --git a/libtrivfs/get-source.c b/libtrivfs/get-source.c
index 2605dac..1b3ce11 100644
--- a/libtrivfs/get-source.c
+++ b/libtrivfs/get-source.c
@@ -21,7 +21,7 @@
 
 #include "priv.h"
 
-error_t
+error_t __attribute__ ((weak))
 trivfs_get_source (struct trivfs_protid *cred, char *source, size_t source_len)
 {
   return EOPNOTSUPP;
diff --git a/libtrivfs/protid-clean.c b/libtrivfs/protid-clean.c
index 86fbc19..ff6cc16 100644
--- a/libtrivfs/protid-clean.c
+++ b/libtrivfs/protid-clean.c
@@ -35,8 +35,8 @@ trivfs_clean_protid (void *arg)
     {
       if (refcount_deref (&cred->po->refcnt) == 0)
         {
-          /* Reaquire a reference while we call the hook.  */
-          refcount_ref (&cred->po->refcnt);
+          /* Reacquire a reference while we call the hook.  */
+          refcount_unsafe_ref (&cred->po->refcnt);
           (*trivfs_peropen_destroy_hook) (cred->po);
           if (refcount_deref (&cred->po->refcnt) == 0)
             {
diff --git a/mach-defpager/default_pager.c b/mach-defpager/default_pager.c
index 380c724..831ed96 100644
--- a/mach-defpager/default_pager.c
+++ b/mach-defpager/default_pager.c
@@ -1803,6 +1803,12 @@ void pager_port_list_insert(port, ds)
                        (hurd_ihash_key_t) port,
                        (hurd_ihash_value_t) ds);
        pthread_mutex_unlock(&all_pagers.lock);
+
+       /* Try to set a protected payload.  This is an optimization,
+          if it fails we degrade gracefully.  */
+       mach_port_set_protected_payload (mach_task_self (),
+                                        port,
+                                        (unsigned long) ds);
 }
 
 void pager_port_list_delete(ds)
@@ -1812,6 +1818,9 @@ void pager_port_list_delete(ds)
        hurd_ihash_locp_remove (&all_pagers.htable,
                                ds->htable_locp);
        pthread_mutex_unlock(&all_pagers.lock);
+
+       mach_port_clear_protected_payload (mach_task_self (),
+                                          ds->pager);
 }
 
 /*
@@ -3160,6 +3169,7 @@ default_pager()
 kern_return_t
 S_default_pager_object_create (mach_port_t pager,
                               mach_port_t *mem_obj,
+                              mach_msg_type_name_t *mem_obj_type,
                               vm_size_t size)
 {
        default_pager_t ds;
@@ -3190,6 +3200,7 @@ S_default_pager_object_create (mach_port_t pager,
        default_pager_add(ds, FALSE);
 
        *mem_obj = port;
+       *mem_obj_type = MACH_MSG_TYPE_MAKE_SEND;
        return (KERN_SUCCESS);
 }
 
diff --git a/mach-defpager/mig-decls.h b/mach-defpager/mig-decls.h
index 8118d61..3357aa2 100644
--- a/mach-defpager/mig-decls.h
+++ b/mach-defpager/mig-decls.h
@@ -31,4 +31,10 @@ begin_using_default_pager (mach_port_t port)
                                             (hurd_ihash_key_t) port);
 }
 
+static inline struct dstruct * __attribute__ ((unused))
+begin_using_default_pager_payload (unsigned long payload)
+{
+  return (default_pager_t) payload;
+}
+
 #endif /* __MACH_DEFPAGER_MIG_DECLS_H__ */
diff --git a/mach-defpager/mig-mutate.h b/mach-defpager/mig-mutate.h
index 54aeeba..31acaa6 100644
--- a/mach-defpager/mig-mutate.h
+++ b/mach-defpager/mig-mutate.h
@@ -18,5 +18,8 @@
    along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #define MEMORY_OBJECT_INTRAN default_pager_t begin_using_default_pager 
(mach_port_t)
+#define MEMORY_OBJECT_INTRAN_PAYLOAD                   \
+  default_pager_t begin_using_default_pager_payload
+
 #define MEMORY_OBJECT_IMPORTS import "mig-decls.h";
 #define DEFAULT_PAGER_IMPORTS import "mig-decls.h";
diff --git a/pfinet/linux-src/net/ipv4/af_inet.c 
b/pfinet/linux-src/net/ipv4/af_inet.c
index 04e0510..0e52a72 100644
--- a/pfinet/linux-src/net/ipv4/af_inet.c
+++ b/pfinet/linux-src/net/ipv4/af_inet.c
@@ -755,6 +755,7 @@ static int inet_getname(struct socket *sock, struct 
sockaddr *uaddr,
                sin->sin_port = sk->sport;
                sin->sin_addr.s_addr = addr;
        }
+       memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
        *uaddr_len = sizeof(*sin);
        return(0);
 }
diff --git a/pfinet/main.c b/pfinet/main.c
index 5b0262f..d52d9a3 100644
--- a/pfinet/main.c
+++ b/pfinet/main.c
@@ -30,7 +30,7 @@
 #include <version.h>
 #include <pids.h>
 
-/* Include Hurd's errno.h file, but don't include glue-include/hurd/errno.h,
+/* Include Hurd's errno.h file, but don't include glue-include/linux/errno.h,
    since it #undef's the errno macro. */
 #define _HACK_ERRNO_H
 #include <errno.h>
diff --git a/procfs/Makefile b/procfs/Makefile
index 2820596..78f20c4 100644
--- a/procfs/Makefile
+++ b/procfs/Makefile
@@ -1,46 +1,31 @@
-TARGET = procfs
-OBJS = procfs.o netfs.o procfs_dir.o \
-       process.o proclist.o rootdir.o dircat.o main.o mach_debugUser.o
-LIBS = -lnetfs -lps -lfshelp -lpthread
-
-CC = gcc
-CFLAGS = -Wall -g
-CPPFLAGS =
-LDFLAGS =
-
-ifdef PROFILE
-CFLAGS= -g -pg
-CPPFLAGS= -DPROFILE
-LDFLAGS= -static
-LIBS= -lnetfs -lfshelp -liohelp -lps -lports -lpthread -lihash -lshouldbeinlibc
-endif
-
-CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
-
-all: $(TARGET)
-
-rootdir.o: rootdir.c mach_debug_U.h
-
-$(TARGET): $(OBJS)
-       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
-
-clean:
-       $(RM) $(TARGET) $(OBJS)
-
-# This is the gist of the MIG user stub handling from Hurd's build
-# system:
-
-# Where to find .defs files.
-vpath %.defs /usr/include/mach_debug
-
-MIG = mig
-MIGCOM = $(MIG) -cc cat - /dev/null
-MIGCOMFLAGS := -subrprefix __
-
-%.udefsi: %.defs
-       $(CPP) -x c $(CPPFLAGS) $(MIGUFLAGS) $($*-MIGUFLAGS) \
-         $< -o $*.udefsi
-
-%_U.h %User.c: %.udefsi
-       $(MIGCOM) $(MIGCOMFLAGS) $(MIGCOMUFLAGS) $($*-MIGCOMUFLAGS) < $< \
-                 -user $*User.c -server /dev/null -header $*_U.h
+#   Makefile - for procfs
+# 
+#   Copyright (C) 2008 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
+#   published by the Free Software Foundation; either version 2, or (at
+#   your option) any later version.
+#
+#   This program 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 this program; if not, write to the Free Software
+#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := procfs
+makemode := server
+
+target = procfs
+
+SRCS = procfs.c netfs.c procfs_dir.c process.c proclist.c rootdir.c dircat.c 
main.c mach_debugUser.c
+LCLHDRS = dircat.h main.h process.h procfs.h procfs_dir.h proclist.h rootdir.h
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = netfs fshelp iohelp ps ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+
+include ../Makeconf
diff --git a/procfs/process.c b/procfs/process.c
index 269a18b..a9b1a59 100644
--- a/procfs/process.c
+++ b/procfs/process.c
@@ -1,5 +1,5 @@
 /* Hurd /proc filesystem, implementation of process directories.
-   Copyright (C) 2010 Free Software Foundation, Inc.
+   Copyright (C) 2010,14 Free Software Foundation, Inc.
 
    This file is part of the GNU Hurd.
 
@@ -109,6 +109,100 @@ process_file_gc_environ (struct proc_stat *ps, char 
**contents)
 }
 
 static ssize_t
+process_file_gc_maps (struct proc_stat *ps, char **contents)
+{
+  error_t err;
+  FILE *s;
+  size_t contents_len;
+  vm_offset_t addr = 0;
+  vm_size_t size;
+  vm_prot_t prot, max_prot;
+  mach_port_t obj;
+  vm_offset_t offs;
+  vm_inherit_t inh;
+  int shared;
+
+  /* Unfortunately we cannot resolve memory objects to their backing
+     file (yet), so we use the port name as identifier.  To avoid the
+     same name from being used again and again, we defer the
+     deallocation until the end of the function.  We use a simple
+     linked list for this purpose.  */
+  struct mem_obj
+    {
+      mach_port_t port;
+      struct mem_obj *next;
+    };
+  struct mem_obj *objects = NULL;
+
+  s = open_memstream (contents, &contents_len);
+  if (s == NULL)
+    {
+      *contents = NULL;
+      return 0;
+    }
+
+  while (1)
+    {
+      err =
+       vm_region (ps->task, &addr, &size, &prot, &max_prot, &inh,
+                  &shared, &obj, &offs);
+      if (err)
+       break;
+
+      fprintf (s, "%0*x-%0*x %c%c%c%c %0*x %s %d ",
+              /* Address range.  */
+              2*sizeof s, addr,
+              2*sizeof s, addr + size,
+              /* Permissions.  */
+              prot & VM_PROT_READ? 'r': '-',
+              prot & VM_PROT_WRITE? 'w': '-',
+              prot & VM_PROT_EXECUTE? 'x': '-',
+              shared? 's': 'p',
+              /* Offset.  */
+              2*sizeof s, offs,
+              /* Device.  */
+              "00:00",
+              /* Inode.  */
+              0);
+
+      /* Pathname.  */
+      if (MACH_PORT_VALID (obj))
+       {
+         struct mem_obj *o = malloc (sizeof *o);
+         if (o)
+           {
+             o->port = obj;
+             o->next = objects;
+             objects = o;
+           }
+         else
+           mach_port_deallocate (mach_task_self (), obj);
+
+         fprintf (s, "[mem_obj=%d]\n", obj);
+       }
+      else
+       fprintf (s, "\n");
+
+      addr += size;
+    }
+
+  while (objects)
+    {
+      struct mem_obj *o = objects;
+      mach_port_deallocate (mach_task_self (), o->port);
+      objects = o->next;
+      free (o);
+    }
+
+  /* This is a bit awkward, fortunately vm_region should not fail.  */
+  if (err != KERN_NO_SPACE)
+    fprintf (s, "%s\n", strerror (err));
+
+  fclose (s);
+  return contents_len;
+}
+
+static ssize_t
 process_file_gc_stat (struct proc_stat *ps, char **contents)
 {
   struct procinfo *pi = proc_stat_proc_info (ps);
@@ -316,7 +410,7 @@ process_file_make_node (void *dir_hook, const void 
*entry_hook)
   return np;
 }
 
-/* Stat needs its own constructor in oreder to set its mode according to
+/* Stat needs its own constructor in order to set its mode according to
    the --stat-mode command-line option.  */
 static struct node *
 process_stat_make_node (void *dir_hook, const void *entry_hook)
@@ -348,6 +442,14 @@ static struct procfs_dir_entry entries[] = {
     },
   },
   {
+    .name = "maps",
+    .hook = & (struct process_file_desc) {
+      .get_contents = process_file_gc_maps,
+      .needs = PSTAT_TASK,
+      .mode = 0400,
+    },
+  },
+  {
     .name = "stat",
     .hook = & (struct process_file_desc) {
       .get_contents = process_file_gc_stat,
diff --git a/procfs/rootdir.c b/procfs/rootdir.c
index 076885c..d68645e 100644
--- a/procfs/rootdir.c
+++ b/procfs/rootdir.c
@@ -1,5 +1,5 @@
 /* Hurd /proc filesystem, permanent files of the root directory.
-   Copyright (C) 2010,13 Free Software Foundation, Inc.
+   Copyright (C) 2010,13,14 Free Software Foundation, Inc.
 
    This file is part of the GNU Hurd.
 
@@ -32,6 +32,7 @@
 #include <sys/stat.h>
 #include <argz.h>
 #include <ps.h>
+#include <glob.h>
 #include "procfs.h"
 #include "procfs_dir.h"
 #include "main.h"
@@ -407,72 +408,7 @@ rootdir_gc_fakeself (void *hook, char **contents, ssize_t 
*contents_len)
   return 0;
 }
 
-/* The mtab translator to use by default for the "mounts" node.  */
-#define MTAB_TRANSLATOR        "/hurd/mtab"
-
 static struct node *rootdir_mounts_node;
-static pthread_spinlock_t rootdir_mounts_node_lock =
-  PTHREAD_SPINLOCK_INITIALIZER;
-
-static struct node *
-rootdir_mounts_make_node (void *dir_hook, const void *entry_hook)
-{
-  struct node *np, *prev;
-
-  pthread_spin_lock (&rootdir_mounts_node_lock);
-  np = rootdir_mounts_node;
-  pthread_spin_unlock (&rootdir_mounts_node_lock);
-
-  if (np != NULL)
-    {
-      netfs_nref (np);
-      return np;
-    }
-
-  np = procfs_make_node (entry_hook, dir_hook);
-  if (np == NULL)
-    return NULL;
-
-  procfs_node_chtype (np, S_IFREG | S_IPTRANS);
-  procfs_node_chmod (np, 0444);
-
-  pthread_spin_lock (&rootdir_mounts_node_lock);
-  prev = rootdir_mounts_node;
-  if (rootdir_mounts_node == NULL)
-    rootdir_mounts_node = np;
-  pthread_spin_unlock (&rootdir_mounts_node_lock);
-
-  if (prev != NULL)
-    {
-      procfs_cleanup (np);
-      np = prev;
-    }
-
-  return np;
-}
-
-static error_t
-rootdir_mounts_get_translator (void *hook, char **argz, size_t *argz_len)
-{
-  static const char const mtab_argz[] = MTAB_TRANSLATOR "\0/";
-
-  *argz = malloc (sizeof mtab_argz);
-  if (! *argz)
-    return ENOMEM;
-
-  memcpy (*argz, mtab_argz, sizeof mtab_argz);
-  *argz_len = sizeof mtab_argz;
-  return 0;
-}
-
-static int
-rootdir_mounts_exists (void *dir_hook, const void *entry_hook)
-{
-  static int translator_exists = -1;
-  if (translator_exists == -1)
-    translator_exists = access (MTAB_TRANSLATOR, F_OK|X_OK) == 0;
-  return translator_exists;
-}
 
 static error_t
 rootdir_gc_slabinfo (void *hook, char **contents, ssize_t *contents_len)
@@ -529,13 +465,63 @@ rootdir_gc_slabinfo (void *hook, char **contents, ssize_t 
*contents_len)
            mem_total, mem_total_reclaimable);
 
   fclose (m);
-  *contents_len += 1;  /* For the terminating 0.  */
 
  out:
   vm_deallocate (mach_task_self (),
                  cache_info, cache_info_count * sizeof *cache_info);
   return err;
 }
+
+static error_t
+rootdir_gc_filesystems (void *hook, char **contents, ssize_t *contents_len)
+{
+  error_t err = 0;
+  size_t i;
+  int glob_ret;
+  glob_t matches;
+  FILE *m;
+
+  m = open_memstream (contents, contents_len);
+  if (m == NULL)
+    return errno;
+
+  glob_ret = glob (_HURD "*fs", 0, NULL, &matches);
+  switch (glob_ret)
+    {
+    case 0:
+      for (i = 0; i < matches.gl_pathc; i++)
+       {
+         /* Get ith entry, shave off the prefix.  */
+         char *name = &matches.gl_pathv[i][sizeof _HURD - 1];
+
+         /* Linux naming convention is a bit inconsistent.  */
+         if (strncmp (name, "ext", 3) == 0
+             || strcmp (name, "procfs") == 0)
+           /* Drop the fs suffix.  */
+           name[strlen (name) - 2] = 0;
+
+         fprintf (m, "\t%s\n", name);
+       }
+
+      globfree (&matches);
+      break;
+
+    case GLOB_NOMATCH:
+      /* Poor fellow.  */
+      break;
+
+    case GLOB_NOSPACE:
+      err = ENOMEM;
+      break;
+
+    default:
+      /* This should not happen.  */
+      err = EGRATUITOUS;
+    }
+
+  fclose (m);
+  return err;
+}
 
 /* Glue logic and entries table */
 
@@ -557,7 +543,84 @@ rootdir_symlink_make_node (void *dir_hook, const void 
*entry_hook)
     procfs_node_chtype (np, S_IFLNK);
   return np;
 }
+
+/* Translator linkage.  */
+
+static pthread_spinlock_t rootdir_translated_node_lock =
+  PTHREAD_SPINLOCK_INITIALIZER;
+
+struct procfs_translated_node_ops
+{
+  struct procfs_node_ops node_ops;
+
+  struct node **npp;
+  char *argz;
+  size_t argz_len;
+};
+
+static struct node *
+rootdir_make_translated_node (void *dir_hook, const void *entry_hook)
+{
+  const struct procfs_translated_node_ops *ops = entry_hook;
+  struct node *np, *prev;
+
+  pthread_spin_lock (&rootdir_translated_node_lock);
+  np = *ops->npp;
+  pthread_spin_unlock (&rootdir_translated_node_lock);
+
+  if (np != NULL)
+    {
+      netfs_nref (np);
+      return np;
+    }
+
+  np = procfs_make_node (entry_hook, entry_hook);
+  if (np == NULL)
+    return NULL;
+
+  procfs_node_chtype (np, S_IFREG | S_IPTRANS);
+  procfs_node_chmod (np, 0444);
+
+  pthread_spin_lock (&rootdir_translated_node_lock);
+  prev = *ops->npp;
+  if (*ops->npp == NULL)
+    *ops->npp = np;
+  pthread_spin_unlock (&rootdir_translated_node_lock);
+
+  if (prev != NULL)
+    {
+      procfs_cleanup (np);
+      np = prev;
+    }
+
+  return np;
+}
+
+static error_t
+rootdir_translated_node_get_translator (void *hook, char **argz,
+                                       size_t *argz_len)
+{
+  const struct procfs_translated_node_ops *ops = hook;
 
+  *argz = malloc (ops->argz_len);
+  if (! *argz)
+    return ENOMEM;
+
+  memcpy (*argz, ops->argz, ops->argz_len);
+  *argz_len = ops->argz_len;
+  return 0;
+}
+
+#define ROOTDIR_DEFINE_TRANSLATED_NODE(NPP, ARGZ)                \
+  &(struct procfs_translated_node_ops) {                         \
+    .node_ops = {                                                \
+      .get_translator = rootdir_translated_node_get_translator,          \
+    },                                                           \
+    .npp = NPP,                                                          \
+    .argz = (ARGZ),                                              \
+    .argz_len = sizeof (ARGZ),                                   \
+  }
+
 static const struct procfs_dir_entry rootdir_entries[] = {
   {
     .name = "self",
@@ -621,12 +684,10 @@ static const struct procfs_dir_entry rootdir_entries[] = {
   },
   {
     .name = "mounts",
-    .hook = & (struct procfs_node_ops) {
-      .get_translator = rootdir_mounts_get_translator,
-    },
+    .hook = ROOTDIR_DEFINE_TRANSLATED_NODE (&rootdir_mounts_node,
+                                           _HURD_MTAB "\0/"),
     .ops = {
-      .make_node = rootdir_mounts_make_node,
-      .exists = rootdir_mounts_exists,
+      .make_node = rootdir_make_translated_node,
     }
   },
   {
@@ -636,6 +697,13 @@ static const struct procfs_dir_entry rootdir_entries[] = {
       .cleanup_contents = procfs_cleanup_contents_with_free,
     },
   },
+  {
+    .name = "filesystems",
+    .hook = & (struct procfs_node_ops) {
+      .get_contents = rootdir_gc_filesystems,
+      .cleanup_contents = procfs_cleanup_contents_with_free,
+    },
+  },
 #ifdef PROFILE
   /* In order to get a usable gmon.out file, we must apparently use exit(). */
   {
diff --git a/random/random.c b/random/random.c
index ca96358..6eea363 100644
--- a/random/random.c
+++ b/random/random.c
@@ -152,6 +152,7 @@ trivfs_S_io_read (struct trivfs_protid *cred,
 
   if (amount > 0)
     {
+      mach_msg_type_number_t new_amount;
       while (readable_pool (amount, level) == 0)
        {
          if (cred->po->openmodes & O_NONBLOCK)
@@ -170,10 +171,22 @@ trivfs_S_io_read (struct trivfs_protid *cred,
 
       /* Possibly allocate a new buffer. */
       if (*data_len < amount)
-       *data = mmap (0, amount, PROT_READ|PROT_WRITE,
-                                    MAP_ANON, 0, 0);
-         
-      amount = read_pool ((byte *) *data, amount, level);
+       {
+         *data = mmap (0, amount, PROT_READ|PROT_WRITE,
+                                      MAP_ANON, 0, 0);
+         if (*data == MAP_FAILED)
+           {
+             pthread_mutex_unlock (&global_lock);
+             return errno;
+           }
+       }
+
+      new_amount = read_pool ((byte *) *data, amount, level);
+
+      if (new_amount < amount)
+       munmap (*data + round_page (new_amount),
+               round_page(amount) - round_page (new_amount));
+      amount = new_amount;
     }
   *data_len = amount;
 
diff --git a/storeio/pager.c b/storeio/pager.c
index 7d78711..c260d73 100644
--- a/storeio/pager.c
+++ b/storeio/pager.c
@@ -24,6 +24,7 @@
 #include <strings.h>
 #include <unistd.h>
 #include <errno.h>
+#include <error.h>
 #include <sys/mman.h>
 #include <stdio.h>
 
@@ -142,21 +143,6 @@ pager_clear_user_data (struct user_pager_info *upi)
 
 static struct port_bucket *pager_port_bucket = 0;
 
-/* A top-level function for the paging thread that just services paging
-   requests.  */
-static void *
-service_paging_requests (void *arg)
-{
-  (void) arg;
-
-  for (;;)
-    ports_manage_port_operations_multithread (pager_port_bucket,
-                                             pager_demuxer,
-                                             1000 * 30, 1000 * 60 * 5, 0);
-
-  return NULL;
-}
-
 /* Initialize paging for this device.  */
 static void
 init_dev_paging ()
@@ -173,14 +159,12 @@ init_dev_paging ()
 
          pager_port_bucket = ports_create_bucket ();
 
-         /* Make a thread to service paging requests.  */
-         err = pthread_create (&thread, NULL, service_paging_requests, NULL);
-         if (!err)
-           pthread_detach (thread);
-         else
+         /* Start libpagers worker threads.  */
+         err = pager_start_workers (pager_port_bucket);
+         if (err)
            {
              errno = err;
-             perror ("pthread_create");
+             error (0, err, "pager_start_workers");
            }
        }
       pthread_mutex_unlock (&pager_global_lock);
diff --git a/sutils/MAKEDEV.sh b/sutils/MAKEDEV.sh
index 0a8f514..1baec15 100644
--- a/sutils/MAKEDEV.sh
+++ b/sutils/MAKEDEV.sh
@@ -100,7 +100,7 @@ mkdev() {
        ;;
 
       std)
-       mkdev console tty null zero full fd time mem klog shm
+       mkdev console tty urandom null zero full fd time mem klog shm
        ;;
       console|com[0-9])
        st $I root 600 /hurd/term ${DEVDIR}/$I device $I;;
@@ -111,6 +111,8 @@ mkdev() {
           ${DEVDIR}/vcs/`echo $I | sed -e s/tty//`/console;;
       lpr[0-9])
         st $I root 660 /hurd/streamio "$I";;
+      urandom)
+       st $I root 644 /hurd/random --fast --seed-file /var/lib/random-seed;;
       null)
        st $I root 666 /hurd/null;;
       full)
diff --git a/term/main.c b/term/main.c
index 9cc32d4..be014e1 100644
--- a/term/main.c
+++ b/term/main.c
@@ -32,6 +32,10 @@
 
 #include <version.h>
 
+#include "term_S.h"
+#include "tioctl_S.h"
+#include "device_reply_S.h"
+
 const char *argp_program_version = STANDARD_HURD_VERSION (term);
 
 int trivfs_fstype = FSTYPE_TERM;
@@ -61,14 +65,18 @@ dev_t rdev;
 int
 demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
 {
-  extern int term_server (mach_msg_header_t *, mach_msg_header_t *);
-  extern int tioctl_server (mach_msg_header_t *, mach_msg_header_t *);
-  extern int device_reply_server (mach_msg_header_t *, mach_msg_header_t *);
-
-  return (trivfs_demuxer (inp, outp)
-         || term_server (inp, outp)
-         || tioctl_server (inp, outp)
-         || device_reply_server (inp, outp));
+  mig_routine_t routine;
+  if ((routine = NULL, trivfs_demuxer (inp, outp)) ||
+      (routine = term_server_routine (inp)) ||
+      (routine = tioctl_server_routine (inp)) ||
+      (routine = device_reply_server_routine (inp)))
+    {
+      if (routine)
+        (*routine) (inp, outp);
+      return TRUE;
+    }
+  else
+    return FALSE;
 }
 
 static struct argp_option options[] =
diff --git a/mach-defpager/mig-decls.h b/term/mig-decls.h
similarity index 65%
copy from mach-defpager/mig-decls.h
copy to term/mig-decls.h
index 8118d61..c91b133 100644
--- a/mach-defpager/mig-decls.h
+++ b/term/mig-decls.h
@@ -17,18 +17,26 @@
    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 __MACH_DEFPAGER_MIG_DECLS_H__
-#define __MACH_DEFPAGER_MIG_DECLS_H__
+#ifndef __TERM_MIG_DECLS_H__
+#define __TERM_MIG_DECLS_H__
 
-#include "priv.h"
+#include <hurd/ports.h>
+
+#include "term.h"
 
 /* Called by server stub functions.  */
 
-static inline struct dstruct * __attribute__ ((unused))
-begin_using_default_pager (mach_port_t port)
+static inline struct port_info * __attribute__ ((unused))
+begin_using_ctty_port (mach_port_t port)
+{
+  return ports_lookup_port (term_bucket, port, cttyid_class);
+}
+
+static inline void __attribute__ ((unused))
+end_using_ctty (struct port_info *p)
 {
-  return (default_pager_t) hurd_ihash_find (&all_pagers.htable,
-                                            (hurd_ihash_key_t) port);
+  if (p)
+    ports_port_deref (p);
 }
 
-#endif /* __MACH_DEFPAGER_MIG_DECLS_H__ */
+#endif /* __TERM_MIG_DECLS_H__ */
diff --git a/term/mig-mutate.h b/term/mig-mutate.h
index a6b99fe..1545719 100644
--- a/term/mig-mutate.h
+++ b/term/mig-mutate.h
@@ -21,5 +21,13 @@
 
 #define IO_INTRAN trivfs_protid_t trivfs_begin_using_protid (io_t)
 #define IO_DESTRUCTOR trivfs_end_using_protid (trivfs_protid_t)
+
+#define CTTY_INTRAN                                    \
+  port_info_t begin_using_ctty_port (mach_port_t)
+#define CTTY_DESTRUCTOR                                        \
+  end_using_ctty (port_info_t)
+
 #define TIOCTL_IMPORTS import "../libtrivfs/mig-decls.h";
-#define TERM_IMPORTS import "../libtrivfs/mig-decls.h";
+#define TERM_IMPORTS                                   \
+  import "../libtrivfs/mig-decls.h";                   \
+  import "mig-decls.h";
diff --git a/term/term.h b/term/term.h
index df82b6c..3067425 100644
--- a/term/term.h
+++ b/term/term.h
@@ -18,6 +18,9 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
 
+#ifndef __HURD_TERM_H__
+#define __HURD_TERM_H__
+
 #include <pthread.h>
 #include <assert.h>
 #include <errno.h>
@@ -391,3 +394,5 @@ error_t pty_io_select (struct trivfs_protid *, mach_port_t,
 error_t pty_open_hook (struct trivfs_control *, struct iouser *, int);
 error_t pty_po_create_hook (struct trivfs_peropen *);
 error_t pty_po_destroy_hook (struct trivfs_peropen *);
+
+#endif /* __HURD_TERM_H__ */
diff --git a/term/users.c b/term/users.c
index 9bd51d0..8151dc7 100644
--- a/term/users.c
+++ b/term/users.c
@@ -379,7 +379,7 @@ S_term_getctty (struct trivfs_protid *cred,
 
 /* Implement termctty_open_terminal as described in <hurd/term.defs>.  */
 kern_return_t
-S_termctty_open_terminal (mach_port_t arg,
+S_termctty_open_terminal (struct port_info *pi,
                          int flags,
                          mach_port_t *result,
                          mach_msg_type_name_t *resulttype)
@@ -388,7 +388,6 @@ S_termctty_open_terminal (mach_port_t arg,
   mach_port_t new_realnode;
   struct iouser *user;
   struct trivfs_protid *newcred;
-  struct port_info *pi = ports_lookup_port (term_bucket, arg, cttyid_class);
   if (!pi)
     return EOPNOTSUPP;
 
@@ -409,7 +408,6 @@ S_termctty_open_terminal (mach_port_t arg,
        }
     }
 
-  ports_port_deref (pi);
   return err;
 }
 
diff --git a/tmpfs/tmpfs.c b/tmpfs/tmpfs.c
index a45d343..718c6d8 100644
--- a/tmpfs/tmpfs.c
+++ b/tmpfs/tmpfs.c
@@ -296,13 +296,14 @@ diskfs_append_args (char **argz, size_t *argz_len)
 static void *
 diskfs_thread_function (void *demuxer)
 {
+  static int thread_timeout = 1000 * 60 * 2; /* two minutes */
   error_t err;
 
   do
     {
       ports_manage_port_operations_multithread (diskfs_port_bucket,
                                                (ports_demuxer_type) demuxer,
-                                               0,
+                                               thread_timeout,
                                                0,
                                                0);
       err = diskfs_shutdown (0);
diff --git a/trans/fakeroot.c b/trans/fakeroot.c
index 4175b55..df2de64 100644
--- a/trans/fakeroot.c
+++ b/trans/fakeroot.c
@@ -47,7 +47,6 @@ static auth_t fakeroot_auth_port;
 
 struct netnode
 {
-  struct node *np;             /* our node */
   hurd_ihash_locp_t idport_locp;/* easy removal pointer in idport ihash */
   mach_port_t idport;          /* port from io_identity */
   int openmodes;               /* O_READ | O_WRITE | O_EXEC */
@@ -64,7 +63,8 @@ struct netnode
 
 pthread_mutex_t idport_ihash_lock = PTHREAD_MUTEX_INITIALIZER;
 struct hurd_ihash idport_ihash
-  = HURD_IHASH_INITIALIZER (offsetof (struct netnode, idport_locp));
+= HURD_IHASH_INITIALIZER (sizeof (struct node)
+                         + offsetof (struct netnode, idport_locp));
 
 
 /* Make a new virtual node.  Always consumes the ports.  If
@@ -74,8 +74,9 @@ new_node (file_t file, mach_port_t idport, int locked, int 
openmodes,
          struct node **np)
 {
   error_t err;
-  struct netnode *nn = calloc (1, sizeof *nn);
-  if (nn == 0)
+  struct netnode *nn;
+  *np = netfs_make_node_alloc (sizeof *nn);
+  if (*np == 0)
     {
       mach_port_deallocate (mach_task_self (), file);
       if (idport != MACH_PORT_NULL)
@@ -84,6 +85,7 @@ new_node (file_t file, mach_port_t idport, int locked, int 
openmodes,
        pthread_mutex_unlock (&idport_ihash_lock);
       return ENOMEM;
     }
+  nn = netfs_node_netnode (*np);
   nn->file = file;
   nn->openmodes = openmodes;
   if (idport != MACH_PORT_NULL)
@@ -97,42 +99,34 @@ new_node (file_t file, mach_port_t idport, int locked, int 
openmodes,
       if (err)
        {
          mach_port_deallocate (mach_task_self (), file);
-         free (nn);
+         free (*np);
          return err;
        }
     }
 
   if (!locked)
     pthread_mutex_lock (&idport_ihash_lock);
-  err = hurd_ihash_add (&idport_ihash, nn->idport, nn);
+  err = hurd_ihash_add (&idport_ihash, nn->idport, *np);
   if (err)
     goto lose;
 
-  *np = nn->np = netfs_make_node (nn);
-  if (*np == 0)
-    {
-      err = ENOMEM;
-      goto lose_hash;
-    }
-
   pthread_mutex_lock (&(*np)->lock);
   pthread_mutex_unlock (&idport_ihash_lock);
   return 0;
 
- lose_hash:
-  hurd_ihash_locp_remove (&idport_ihash, nn->idport_locp);
  lose:
   pthread_mutex_unlock (&idport_ihash_lock);
   mach_port_deallocate (mach_task_self (), nn->idport);
   mach_port_deallocate (mach_task_self (), file);
-  free (nn);
+  free (*np);
+  *np = NULL;
   return err;
 }
 
 static void
 set_default_attributes (struct node *np)
 {
-  np->nn->faked = FAKE_UID | FAKE_GID | FAKE_DEFAULT;
+  netfs_node_netnode (np)->faked = FAKE_UID | FAKE_GID | FAKE_DEFAULT;
   np->nn_stat.st_uid = 0;
   np->nn_stat.st_gid = 0;
 }
@@ -140,9 +134,9 @@ set_default_attributes (struct node *np)
 static void
 set_faked_attribute (struct node *np, unsigned int faked)
 {
-  np->nn->faked |= faked;
+  netfs_node_netnode (np)->faked |= faked;
 
-  if (np->nn->faked & FAKE_DEFAULT)
+  if (netfs_node_netnode (np)->faked & FAKE_DEFAULT)
     {
       /* Now that the node has non-default faked attributes, they have to be
         retained for future accesses.  Account for the hash table reference.
@@ -153,7 +147,7 @@ set_faked_attribute (struct node *np, unsigned int faked)
         easy enough if it's ever needed, although scalability could be
         improved.  */
       netfs_nref (np);
-      np->nn->faked &= ~FAKE_DEFAULT;
+      netfs_node_netnode (np)->faked &= ~FAKE_DEFAULT;
     }
 }
 
@@ -161,18 +155,15 @@ set_faked_attribute (struct node *np, unsigned int faked)
 void
 netfs_node_norefs (struct node *np)
 {
-  assert (np->nn->np == 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, np->nn->idport_locp);
+  hurd_ihash_locp_remove (&idport_ihash, netfs_node_netnode (np)->idport_locp);
   pthread_mutex_unlock (&idport_ihash_lock);
 
-  mach_port_deallocate (mach_task_self (), np->nn->file);
-  mach_port_deallocate (mach_task_self (), np->nn->idport);
-  free (np->nn);
+  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);
@@ -255,7 +246,8 @@ error_t
 netfs_check_open_permissions (struct iouser *user, struct node *np,
                              int flags, int newnode)
 {
-  return check_openmodes (np->nn, flags & (O_RDWR|O_EXEC), MACH_PORT_NULL);
+  return check_openmodes (netfs_node_netnode (np),
+                         flags & (O_RDWR|O_EXEC), MACH_PORT_NULL);
 }
 
 error_t
@@ -281,12 +273,12 @@ netfs_S_dir_lookup (struct protid *diruser,
 
   dnp = diruser->po->np;
 
-  mach_port_t dir = dnp->nn->file;
+  mach_port_t dir = netfs_node_netnode (dnp)->file;
  redo_lookup:
   err = dir_lookup (dir, filename,
                    flags & (O_NOLINK|O_RDWR|O_EXEC|O_CREAT|O_EXCL|O_NONBLOCK),
                    mode, do_retry, retry_name, &file);
-  if (dir != dnp->nn->file)
+  if (dir != netfs_node_netnode (dnp)->file)
     mach_port_deallocate (mach_task_self (), dir);
   if (err)
     return err;
@@ -358,13 +350,12 @@ netfs_S_dir_lookup (struct protid *diruser,
      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);
-  struct netnode *nn = hurd_ihash_find (&idport_ihash, idport);
-  if (nn != NULL)
+  np = hurd_ihash_find (&idport_ihash, idport);
+  if (np != NULL)
     {
-      assert (nn->np->nn == nn);
       /* We already know about this node.  */
 
-      if (nn->np->references == 0)
+      if (np->references == 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
@@ -376,7 +367,6 @@ netfs_S_dir_lookup (struct protid *diruser,
        }
 
       /* Otherwise, reference it right away.  */
-      np = nn->np;
       np->references++;
       pthread_spin_unlock (&netfs_node_refcnt_lock);
 
@@ -392,7 +382,8 @@ netfs_S_dir_lookup (struct protid *diruser,
          pthread_mutex_unlock (&dnp->lock);
        }
 
-      err = check_openmodes (np->nn, (flags & (O_RDWR|O_EXEC)), file);
+      err = check_openmodes (netfs_node_netnode (np),
+                            (flags & (O_RDWR|O_EXEC)), file);
       pthread_mutex_unlock (&idport_ihash_lock);
     }
   else
@@ -460,17 +451,17 @@ error_t
 netfs_validate_stat (struct node *np, struct iouser *cred)
 {
   struct stat st;
-  error_t err = io_stat (np->nn->file, &st);
+  error_t err = io_stat (netfs_node_netnode (np)->file, &st);
   if (err)
     return err;
 
-  if (np->nn->faked & FAKE_UID)
+  if (netfs_node_netnode (np)->faked & FAKE_UID)
     st.st_uid = np->nn_stat.st_uid;
-  if (np->nn->faked & FAKE_GID)
+  if (netfs_node_netnode (np)->faked & FAKE_GID)
     st.st_gid = np->nn_stat.st_gid;
-  if (np->nn->faked & FAKE_AUTHOR)
+  if (netfs_node_netnode (np)->faked & FAKE_AUTHOR)
     st.st_author = np->nn_stat.st_author;
-  if (np->nn->faked & FAKE_MODE)
+  if (netfs_node_netnode (np)->faked & FAKE_MODE)
     st.st_mode = np->nn_stat.st_mode;
 
   np->nn_stat = st;
@@ -540,7 +531,7 @@ netfs_attempt_chmod (struct iouser *cred, struct node *np, 
mode_t mode)
 
   /* We don't bother with error checking since the fake mode change should
      always succeed--worst case a later open will get EACCES.  */
-  (void) file_chmod (np->nn->file, mode);
+  (void) file_chmod (netfs_node_netnode (np)->file, mode);
   set_faked_attribute (np, FAKE_MODE);
   np->nn_stat.st_mode = mode;
   return 0;
@@ -555,7 +546,7 @@ netfs_attempt_mksymlink (struct iouser *cred, struct node 
*np, char *name)
   char trans[sizeof _HURD_SYMLINK + namelen];
   memcpy (trans, _HURD_SYMLINK, sizeof _HURD_SYMLINK);
   memcpy (&trans[sizeof _HURD_SYMLINK], name, namelen);
-  return file_set_translator (np->nn->file,
+  return file_set_translator (netfs_node_netnode (np)->file,
                              FS_TRANS_EXCL|FS_TRANS_SET,
                              FS_TRANS_EXCL|FS_TRANS_SET, 0,
                              trans, sizeof trans,
@@ -574,7 +565,7 @@ netfs_attempt_mkdev (struct iouser *cred, struct node *np,
     return ENOMEM;
   else
     {
-      error_t err = file_set_translator (np->nn->file,
+      error_t err = file_set_translator (netfs_node_netnode (np)->file,
                                         FS_TRANS_EXCL|FS_TRANS_SET,
                                         FS_TRANS_EXCL|FS_TRANS_SET, 0,
                                         trans, translen + 1,
@@ -588,7 +579,7 @@ netfs_attempt_mkdev (struct iouser *cred, struct node *np,
 error_t
 netfs_attempt_chflags (struct iouser *cred, struct node *np, int flags)
 {
-  return file_chflags (np->nn->file, flags);
+  return file_chflags (netfs_node_netnode (np)->file, flags);
 }
 
 error_t
@@ -614,25 +605,25 @@ netfs_attempt_utimes (struct iouser *cred, struct node 
*np,
   else
     m.tv.tv_sec = m.tv.tv_usec = -1;
 
-  return file_utimes (np->nn->file, a.tvt, m.tvt);
+  return file_utimes (netfs_node_netnode (np)->file, a.tvt, m.tvt);
 }
 
 error_t
 netfs_attempt_set_size (struct iouser *cred, struct node *np, off_t size)
 {
-  return file_set_size (np->nn->file, size);
+  return file_set_size (netfs_node_netnode (np)->file, size);
 }
 
 error_t
 netfs_attempt_statfs (struct iouser *cred, struct node *np, struct statfs *st)
 {
-  return file_statfs (np->nn->file, st);
+  return file_statfs (netfs_node_netnode (np)->file, st);
 }
 
 error_t
 netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
 {
-  return file_sync (np->nn->file, wait, 0);
+  return file_sync (netfs_node_netnode (np)->file, wait, 0);
 }
 
 error_t
@@ -645,7 +636,7 @@ error_t
 netfs_attempt_mkdir (struct iouser *user, struct node *dir,
                     char *name, mode_t mode)
 {
-  return dir_mkdir (dir->nn->file, name, mode | S_IRWXU);
+  return dir_mkdir (netfs_node_netnode (dir)->file, name, mode | S_IRWXU);
 }
 
 
@@ -657,7 +648,7 @@ netfs_attempt_mkdir (struct iouser *user, struct node *dir,
 error_t
 netfs_attempt_unlink (struct iouser *user, struct node *dir, char *name)
 {
-  return dir_unlink (dir->nn->file, name);
+  return dir_unlink (netfs_node_netnode (dir)->file, name);
 }
 
 error_t
@@ -665,22 +656,22 @@ netfs_attempt_rename (struct iouser *user, struct node 
*fromdir,
                      char *fromname, struct node *todir,
                      char *toname, int excl)
 {
-  return dir_rename (fromdir->nn->file, fromname,
-                    todir->nn->file, toname, excl);
+  return dir_rename (netfs_node_netnode (fromdir)->file, fromname,
+                    netfs_node_netnode (todir)->file, toname, excl);
 }
 
 error_t
 netfs_attempt_rmdir (struct iouser *user,
                     struct node *dir, char *name)
 {
-  return dir_rmdir (dir->nn->file, name);
+  return dir_rmdir (netfs_node_netnode (dir)->file, name);
 }
 
 error_t
 netfs_attempt_link (struct iouser *user, struct node *dir,
                    struct node *file, char *name, int excl)
 {
-  return dir_link (dir->nn->file, file->nn->file, name, excl);
+  return dir_link (netfs_node_netnode (dir)->file, netfs_node_netnode 
(file)->file, name, excl);
 }
 
 error_t
@@ -688,7 +679,7 @@ netfs_attempt_mkfile (struct iouser *user, struct node *dir,
                      mode_t mode, struct node **np)
 {
   file_t newfile;
-  error_t err = dir_mkfile (dir->nn->file, O_RDWR|O_EXEC,
+  error_t err = dir_mkfile (netfs_node_netnode (dir)->file, O_RDWR|O_EXEC,
                            real_from_fake_mode (mode), &newfile);
   pthread_mutex_unlock (&dir->lock);
   if (err == 0)
@@ -704,7 +695,8 @@ netfs_attempt_readlink (struct iouser *user, struct node 
*np, char *buf)
   char transbuf[sizeof _HURD_SYMLINK + np->nn_stat.st_size + 1];
   char *trans = transbuf;
   size_t translen = sizeof transbuf;
-  error_t err = file_get_translator (np->nn->file, &trans, &translen);
+  error_t err = file_get_translator (netfs_node_netnode (np)->file,
+                                    &trans, &translen);
   if (err == 0)
     {
       if (translen < sizeof _HURD_SYMLINK
@@ -727,7 +719,8 @@ netfs_attempt_read (struct iouser *cred, struct node *np,
                    off_t offset, size_t *len, void *data)
 {
   char *buf = data;
-  error_t err = io_read (np->nn->file, &buf, len, offset, *len);
+  error_t err = io_read (netfs_node_netnode (np)->file,
+                        &buf, len, offset, *len);
   if (err == 0 && buf != data)
     {
       memcpy (data, buf, *len);
@@ -740,7 +733,7 @@ error_t
 netfs_attempt_write (struct iouser *cred, struct node *np,
                     off_t offset, size_t *len, void *data)
 {
-  return io_write (np->nn->file, data, *len, offset, len);
+  return io_write (netfs_node_netnode (np)->file, data, *len, offset, len);
 }
 
 error_t
@@ -756,7 +749,7 @@ netfs_get_dirents (struct iouser *cred, struct node *dir,
                   mach_msg_type_number_t *datacnt,
                   vm_size_t bufsize, int *amt)
 {
-  return dir_readdir (dir->nn->file, data, datacnt,
+  return dir_readdir (netfs_node_netnode (dir)->file, data, datacnt,
                      entry, nentries, bufsize, amt);
 }
 
@@ -774,7 +767,7 @@ netfs_file_get_storage_info (struct iouser *cred,
                             mach_msg_type_number_t *data_len)
 {
   *ports_type = MACH_MSG_TYPE_MOVE_SEND;
-  return file_get_storage_info (np->nn->file,
+  return file_get_storage_info (netfs_node_netnode (np)->file,
                                ports, num_ports,
                                ints, num_ints,
                                offsets, num_offsets,
@@ -807,8 +800,9 @@ netfs_S_file_exec (struct protid *user,
     return EOPNOTSUPP;
 
   pthread_mutex_lock (&user->po->np->lock);
-  err = check_openmodes (user->po->np->nn, O_EXEC, MACH_PORT_NULL);
-  file = user->po->np->nn->file;
+  err = check_openmodes (netfs_node_netnode (user->po->np),
+                        O_EXEC, MACH_PORT_NULL);
+  file = netfs_node_netnode (user->po->np)->file;
   if (!err)
     err = mach_port_mod_refs (mach_task_self (),
                              file, MACH_PORT_RIGHT_SEND, 1);
@@ -818,7 +812,8 @@ netfs_S_file_exec (struct protid *user,
     {
       /* We cannot use MACH_MSG_TYPE_MOVE_SEND because we might need to
         retry an interrupted call that would have consumed the rights.  */
-      err = file_exec (user->po->np->nn->file, task, flags, argv, argvlen,
+      err = file_exec (netfs_node_netnode (user->po->np)->file,
+                      task, flags, argv, argvlen,
                       envp, envplen, fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
                       portarray, MACH_MSG_TYPE_COPY_SEND, portarraylen,
                       intarray, intarraylen, deallocnames, deallocnameslen,
@@ -850,7 +845,7 @@ netfs_S_io_map (struct protid *user,
   *rdobjtype = *wrobjtype = MACH_MSG_TYPE_MOVE_SEND;
 
   pthread_mutex_lock (&user->po->np->lock);
-  err = io_map (user->po->np->nn->file, rdobj, wrobj);
+  err = io_map (netfs_node_netnode (user->po->np)->file, rdobj, wrobj);
   pthread_mutex_unlock (&user->po->np->lock);
   return err;
 }
@@ -867,7 +862,7 @@ netfs_S_io_map_cntl (struct protid *user,
   *objtype = MACH_MSG_TYPE_MOVE_SEND;
 
   pthread_mutex_lock (&user->po->np->lock);
-  err = io_map_cntl (user->po->np->nn->file, obj);
+  err = io_map_cntl (netfs_node_netnode (user->po->np)->file, obj);
   pthread_mutex_unlock (&user->po->np->lock);
   return err;
 }
@@ -888,7 +883,8 @@ netfs_S_io_identity (struct protid *user,
   *idtype = *fsystype = MACH_MSG_TYPE_MOVE_SEND;
 
   pthread_mutex_lock (&user->po->np->lock);
-  err = io_identity (user->po->np->nn->file, id, fsys, fileno);
+  err = io_identity (netfs_node_netnode (user->po->np)->file,
+                    id, fsys, fileno);
   pthread_mutex_unlock (&user->po->np->lock);
   return err;
 }
@@ -903,7 +899,7 @@ netfs_S_##name (struct protid *user)                \
     return EOPNOTSUPP;                         \
                                                \
   pthread_mutex_lock (&user->po->np->lock);    \
-  err = name (user->po->np->nn->file);         \
+  err = name (netfs_node_netnode (user->po->np)->file);                \
   pthread_mutex_unlock (&user->po->np->lock);  \
   return err;                                  \
 }
@@ -925,7 +921,7 @@ netfs_S_io_prenotify (struct protid *user,
     return EOPNOTSUPP;
 
   pthread_mutex_lock (&user->po->np->lock);
-  err = io_prenotify (user->po->np->nn->file, start, stop);
+  err = io_prenotify (netfs_node_netnode (user->po->np)->file, start, stop);
   pthread_mutex_unlock (&user->po->np->lock);
   return err;
 }
@@ -940,7 +936,7 @@ netfs_S_io_postnotify (struct protid *user,
     return EOPNOTSUPP;
 
   pthread_mutex_lock (&user->po->np->lock);
-  err = io_postnotify (user->po->np->nn->file, start, stop);
+  err = io_postnotify (netfs_node_netnode (user->po->np)->file, start, stop);
   pthread_mutex_unlock (&user->po->np->lock);
   return err;
 }
@@ -983,7 +979,7 @@ netfs_demuxer (mach_msg_header_t *inp,
            | MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND,
                              MACH_MSGH_BITS_REMOTE (inp->msgh_bits));
          inp->msgh_local_port = inp->msgh_remote_port; /* reply port */
-         inp->msgh_remote_port = cred->po->np->nn->file;
+         inp->msgh_remote_port = netfs_node_netnode (cred->po->np)->file;
          err = mach_msg (inp, MACH_SEND_MSG, inp->msgh_size, 0,
                          MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
                          MACH_PORT_NULL);
diff --git a/trans/hello-mt.c b/trans/hello-mt.c
index ba9329a..44d54de 100644
--- a/trans/hello-mt.c
+++ b/trans/hello-mt.c
@@ -273,16 +273,32 @@ trivfs_append_args (struct trivfs_control *fsys,
 {
   error_t err;
   char *opt;
+  size_t opt_len;
+  FILE *s;
+  char *c;
+
+  s = open_memstream (&opt, &opt_len);
+  fprintf (s, "--contents='");
 
   pthread_rwlock_rdlock (&contents_lock);
-  err = asprintf (&opt, "--contents=%s", contents) < 0 ? ENOMEM : 0;
+  for (c = contents; *c; c++)
+    switch (*c)
+      {
+      case 0x27: /* Single quote.  */
+       fprintf (s, "'\"'\"'");
+       break;
+
+      default:
+       fprintf (s, "%c", *c);
+      }
   pthread_rwlock_unlock (&contents_lock);
 
-  if (!err)
-    {
-      err = argz_add (argz, argz_len, opt);
-      free (opt);
-    }
+  fprintf (s, "'");
+  fclose (s);
+
+  err = argz_add (argz, argz_len, opt);
+
+  free (opt);
 
   return err;
 }
diff --git a/trans/hello.c b/trans/hello.c
index 4e88c60..d1884df 100644
--- a/trans/hello.c
+++ b/trans/hello.c
@@ -246,9 +246,26 @@ trivfs_append_args (struct trivfs_control *fsys,
 {
   error_t err;
   char *opt;
+  size_t opt_len;
+  FILE *s;
+  char *c;
 
-  if (asprintf (&opt, "--contents=%s", contents) < 0)
-    return ENOMEM;
+  s = open_memstream (&opt, &opt_len);
+  fprintf (s, "--contents='");
+
+  for (c = contents; *c; c++)
+    switch (*c)
+      {
+      case 0x27: /* Single quote.  */
+       fprintf (s, "'\"'\"'");
+       break;
+
+      default:
+       fprintf (s, "%c", *c);
+      }
+
+  fprintf (s, "'");
+  fclose (s);
 
   err = argz_add (argz, argz_len, opt);
 
diff --git a/trans/ifsock.c b/trans/ifsock.c
index 4ed6589..af2376a 100644
--- a/trans/ifsock.c
+++ b/trans/ifsock.c
@@ -143,7 +143,7 @@ S_ifsock_getsockaddr (struct trivfs_protid *cred,
     return EOPNOTSUPP;
 
   err = file_check_access (cred->realnode, &perms);
-  if (!err && !(perms & O_READ))
+  if (!err && !(perms & O_WRITE))
     err = EACCES;
 
   if (!err)
diff --git a/trans/mtab.c b/trans/mtab.c
index df03b1d..5207c1e 100644
--- a/trans/mtab.c
+++ b/trans/mtab.c
@@ -27,6 +27,7 @@
 #include <hurd/trivfs.h>
 #include <inttypes.h>
 #include <mntent.h>
+#include <nullauth.h>
 #include <pthread.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -221,19 +222,7 @@ main (int argc, char *argv[])
   if (bootstrap != MACH_PORT_NULL)
     {
       /* Started as a translator.  */
-
-      auth_t nullauth;
-      err = auth_makeauth (getauth (),
-                          NULL, MACH_MSG_TYPE_COPY_SEND, 0,
-                          NULL, 0,
-                          NULL, 0,
-                          NULL, 0,
-                          NULL, 0,
-                          &nullauth);
-      if (err)
-        error (3, err, "dropping credentials");
-
-      err = setauth (nullauth);
+      err = setnullauth ();
       if (err)
         error (3, err, "dropping credentials");
 
diff --git a/trans/proxy-defpager.c b/trans/proxy-defpager.c
index 9817657..5fe8ffd 100644
--- a/trans/proxy-defpager.c
+++ b/trans/proxy-defpager.c
@@ -45,8 +45,10 @@ allowed (mach_port_t port, int mode)
 kern_return_t
 S_default_pager_object_create (mach_port_t default_pager,
                               memory_object_t *memory_object,
+                              mach_msg_type_name_t *memory_object_type,
                               vm_size_t object_size)
 {
+  *memory_object_type = MACH_MSG_TYPE_COPY_SEND;
   return allowed (default_pager, O_EXEC)
     ?: default_pager_object_create (real_defpager, memory_object, object_size);
 }
diff --git a/utils/login.c b/utils/login.c
index cad3b1e..a3e0563 100644
--- a/utils/login.c
+++ b/utils/login.c
@@ -263,7 +263,7 @@ check_owned (process_t proc_server, pid_t pid, int *owned)
   char *waits = 0;
   mach_msg_type_number_t num_waits = 0;
   struct procinfo _pi, *pi = &_pi;
-  mach_msg_type_number_t pi_size = sizeof pi;
+  mach_msg_type_number_t pi_size = sizeof _pi / sizeof (*(procinfo_t)0);
   error_t err =
     proc_getprocinfo (proc_server, pid, &flags, (procinfo_t *)&pi, &pi_size,
                      &waits, &num_waits);
@@ -272,7 +272,7 @@ check_owned (process_t proc_server, pid_t pid, int *owned)
     {
       *owned = !(pi->state & PI_NOTOWNED);
       if (pi != &_pi)
-       munmap (pi, pi_size);
+       munmap (pi, pi_size * sizeof (*(procinfo_t)0));
     }
 
   return err;
diff --git a/utils/mount.c b/utils/mount.c
index df77c66..c5736ba 100644
--- a/utils/mount.c
+++ b/utils/mount.c
@@ -64,6 +64,8 @@ static const struct argp_option argp_opts[] =
   {"no-mtab", 'n', 0, 0, "Do not update /etc/mtab"},
   {"test-opts", 'O', "OPTIONS", 0,
    "Only mount fstab entries matching the given set of options"},
+  {"bind", 'B', 0, 0, "Bind mount, firmlink"},
+  {"firmlink", 0, 0, OPTION_ALIAS},
   {"fake", 'f', 0, 0, "Do not actually mount, just pretend"},
   {0, 0}
 };
@@ -87,6 +89,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
     case 'r': ARGZ (add (&options, &options_len, "ro"));
     case 'w': ARGZ (add (&options, &options_len, "rw"));
     case 'u': ARGZ (add (&options, &options_len, "update"));
+    case 'B': ARGZ (add (&options, &options_len, "bind"));
     case 'o': ARGZ (add_sep (&options, &options_len, arg, ','));
     case 'v': ++verbose; break;
 #undef ARGZ
@@ -250,12 +253,20 @@ do_mount (struct fs *fs, int remount)
       /* Append the fstab options to any specified on the command line.  */
       ARGZ (create_sep (fs->mntent.mnt_opts, ',', &mntopts, &mntopts_len));
 
-      /* Remove the `noauto' option, since it's for us not the filesystem.  */
+      /* Remove the `noauto' and `bind' options, since they're for us not the
+         filesystem.  */
       for (o = mntopts; o; o = argz_next (mntopts, mntopts_len, o))
-       if (!strcmp (o, MNTOPT_NOAUTO))
-         break;
-      if (o)
-       argz_delete (&mntopts, &mntopts_len, o);
+        {
+          if (strcmp (o, MNTOPT_NOAUTO) == 0)
+            argz_delete (&mntopts, &mntopts_len, o);
+          if (strcmp (o, "bind") == 0)
+            {
+              fs->mntent.mnt_type = strdup ("firmlink");
+              if (!fs->mntent.mnt_type)
+                error (3, ENOMEM, "failed to allocate memory");
+              argz_delete (&mntopts, &mntopts_len, o);
+            }
+        }
 
       ARGZ (append (&mntopts, &mntopts_len, options, options_len));
     }
@@ -273,7 +284,7 @@ do_mount (struct fs *fs, int remount)
       {
        ARGZ (add (&fsopts, &fsopts_len, o));
       }
-    else if (strcmp (o, "defaults") != 0)
+    else if ((strcmp (o, "defaults") != 0) && (strlen (o) != 0))
       {
        /* Prepend `--' to the option to make a long option switch,
           e.g. `--ro' or `--rsize=1024'.  */
@@ -572,7 +583,7 @@ do_query (struct fs *fs)
 int
 main (int argc, char **argv)
 {
-  unsigned int remount;
+  unsigned int remount, firmlink;
   struct fstab *fstab;
   struct fs *fs;
   error_t err;
@@ -598,6 +609,16 @@ main (int argc, char **argv)
   if (err)
     error (3, ENOMEM, "collecting mount options");
 
+  /* Do not pass `bind' option to firmlink translator */
+  char *opt = NULL;
+  firmlink = 0;
+  while ((opt = argz_next (options, options_len, opt)))
+    if (strcmp (opt, "bind") == 0)
+      {
+        firmlink = 1;
+        argz_delete(&options, &options_len, opt);
+      }
+
   if (device)                  /* two-argument form */
     {
       struct mntent m =
@@ -608,6 +629,8 @@ main (int argc, char **argv)
        mnt_opts: 0,
        mnt_freq: 0, mnt_passno: 0
       };
+      if (firmlink)
+        m.mnt_type = strdup ("firmlink");
 
       err = fstab_add_mntent (fstab, &m, &fs);
       if (err)
@@ -625,6 +648,8 @@ main (int argc, char **argv)
        mnt_opts: 0,
        mnt_freq: 0, mnt_passno: 0
       };
+      if (firmlink)
+        m.mnt_type = strdup ("firmlink");
 
       err = fstab_add_mntent (fstab, &m, &fs);
       if (err)
diff --git a/utils/rpctrace.c b/utils/rpctrace.c
index fc913e3..b11fea4 100644
--- a/utils/rpctrace.c
+++ b/utils/rpctrace.c
@@ -431,7 +431,9 @@ destroy_receiver_info (struct receiver_info *info)
   while (send_wrapper)
     {
       struct sender_info *next = send_wrapper->next;
-      assert (TRACED_INFO (send_wrapper)->pi.refcnt == 1);
+      assert (
+       refcounts_hard_references (&TRACED_INFO (send_wrapper)->pi.refcounts)
+       == 1);
       /* Reset the receive_right of the send wrapper in advance to avoid
        * destroy_receiver_info is called when the port info is destroyed. */
       send_wrapper->receive_right = NULL;
@@ -848,7 +850,11 @@ rewrite_right (mach_port_t *right, mach_msg_type_name_t 
*type,
            hurd_ihash_locp_remove (&traced_names, receiver_info->locp);
 
            send_wrapper2 = get_send_wrapper (receiver_info, dest, &rr);
-           assert (TRACED_INFO (send_wrapper2)->pi.refcnt == 1);
+           assert (
+             refcounts_hard_references (
+               &TRACED_INFO (send_wrapper2)->pi.refcounts)
+             == 1);
+
            name = TRACED_INFO (send_wrapper2)->name;
            TRACED_INFO (send_wrapper2)->name = NULL;
            /* send_wrapper2 isn't destroyed normally, so we need to unlink
diff --git a/utils/settrans.c b/utils/settrans.c
index ecc6d75..cd40c56 100644
--- a/utils/settrans.c
+++ b/utils/settrans.c
@@ -1,6 +1,7 @@
 /* Set a file's translator.
 
-   Copyright (C) 1995,96,97,98,2001,02,13 Free Software Foundation, Inc.
+   Copyright (C) 1995,96,97,98,2001,02,13,14
+     Free Software Foundation, Inc.
    Written by Miles Bader <address@hidden>
 
    This program is free software; you can redistribute it and/or
@@ -47,6 +48,7 @@ const char *argp_program_version = STANDARD_HURD_VERSION 
(settrans);
 static struct argp_option options[] =
 {
   {"active",      'a', 0, 0, "Start TRANSLATOR and set it as NODE's active 
translator" },
+  {"start",       's', 0, 0, "Start the translator specified by the NODE's 
passive translator record and set it as NODE's active translator" },
   {"passive",     'p', 0, 0, "Change NODE's passive translator record 
(default)" },
   {"create",      'c', 0, 0, "Create NODE if it doesn't exist" },
   {"dereference", 'L', 0, 0, "If a translator exists, put the new one on top"},
@@ -107,6 +109,7 @@ main(int argc, char *argv[])
   /* Various option flags.  */
   int passive = 0, active = 0, keep_active = 0, pause = 0, kill_active = 0,
       orphan = 0;
+  int start = 0;
   char *pid_file = NULL;
   int excl = 0;
   int timeout = DEFAULT_TIMEOUT * 1000; /* ms */
@@ -122,6 +125,9 @@ main(int argc, char *argv[])
            node_name = arg;
          else                  /* command */
            {
+             if (start)
+               argp_error (state, "both --start and TRANSLATOR given");
+
              error_t err =
                argz_create (state->argv + state->next - 1, &argz, &argz_len);
              if (err)
@@ -135,6 +141,10 @@ main(int argc, char *argv[])
          return EINVAL;
 
        case 'a': active = 1; break;
+       case 's':
+         start = 1;
+         active = 1;   /* start implies active */
+         break;
        case 'p': passive = 1; break;
        case 'k': keep_active = 1; break;
        case 'g': kill_active = 1; break;
@@ -212,6 +222,26 @@ main(int argc, char *argv[])
        active_flags = FS_TRANS_SET | FS_TRANS_EXCL;
     }
 
+  if (start)
+    {
+      /* Retrieve the passive translator record in argz.  */
+      mach_port_t node = file_name_lookup (node_name, lookup_flags, 0);
+      if (node == MACH_PORT_NULL)
+       error (4, errno, "%s", node_name);
+
+      char buf[1024];
+      argz = buf;
+      argz_len = sizeof (buf);
+
+      err = file_get_translator (node, &argz, &argz_len);
+      if (err == EINVAL)
+       error (4, 0, "%s: no passive translator record found", node_name);
+      if (err)
+       error (4, err, "%s", node_name);
+
+      mach_port_deallocate (mach_task_self (), node);
+    }
+
   if ((active || chroot_command) && argz_len > 0)
     {
       /* Error during file lookup; we use this to avoid duplicating error
@@ -280,8 +310,9 @@ main(int argc, char *argv[])
 
   if (chroot_command)
     {
-      pid_t pid;
-      switch ((pid = fork ()))
+      pid_t child;
+      int status;
+      switch ((child = fork ()))
        {
        case -1:
          error (6, errno, "fork");
@@ -318,12 +349,19 @@ main(int argc, char *argv[])
          break;
 
        default: /* Parent.  */
-         if (waitpid (pid, NULL, 0) == -1)
-           error (8, errno, "waitpid");
+         if (waitpid (child, &status, 0) != child)
+           error (8, errno, "waitpid on %d", child);
 
          err = fsys_goaway (active_control, goaway_flags);
          if (err && err != EBUSY)
            error (9, err, "fsys_goaway");
+
+         if (WIFSIGNALED (status))
+           error (WTERMSIG (status) + 128, 0,
+                  "%s for child %d", strsignal (WTERMSIG (status)), child);
+         if (WEXITSTATUS (status) != 0)
+           error (WEXITSTATUS (status), 0,
+                  "Error %d for child %d", WEXITSTATUS (status), child);
        }
     }
 
diff --git a/utils/umount.c b/utils/umount.c
index 7901da6..cf6be22 100644
--- a/utils/umount.c
+++ b/utils/umount.c
@@ -40,7 +40,6 @@ static char *targets;
 static size_t targets_len;
 static int readonly;
 static int verbose;
-static int passive_flags;
 static int active_flags = FS_TRANS_SET;
 static int goaway_flags;
 static int source_goaway;
@@ -200,7 +199,7 @@ do_umount (struct fs *fs)
     }
 
   if (verbose)
-    printf ("settrans -apg%s%s %s\n",
+    printf ("settrans -ag%s%s %s\n",
            goaway_flags & FSYS_GOAWAY_NOSYNC? "S": "",
            goaway_flags & FSYS_GOAWAY_FORCE? "f": "",
            fs->mntent.mnt_dir);
@@ -208,7 +207,7 @@ do_umount (struct fs *fs)
   if (! fake)
     {
       err = file_set_translator (node,
-                                passive_flags, active_flags, goaway_flags,
+                                0, active_flags, goaway_flags,
                                 NULL, 0,
                                 MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
       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]