commit-hurd
[Top][All Lists]
Advanced

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

[hurd] 01/07: New upstream snapshot


From: Samuel Thibault
Subject: [hurd] 01/07: New upstream snapshot
Date: Mon, 16 Sep 2013 07:41:30 +0000

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

sthibault pushed a commit to branch master
in repository hurd.

commit ae796083c6e7862929375c773080f8103905fe82
Author: Samuel Thibault <address@hidden>
Date:   Mon Sep 16 00:17:32 2013 +0000

    New upstream snapshot
---
 TODO                                |    4 -
 config.make.in                      |   13 +
 configure.ac                        |   15 +-
 console-client/Makefile             |    4 +-
 console-client/console.c            |  115 ++-
 console/pager.c                     |   10 +-
 daemons/console-run.c               |    5 +-
 devnode/devnode.c                   |   10 +
 exec/Makefile                       |   12 +-
 exec/exec.c                         |  933 ++-----------------
 exec/main.c                         |    3 +-
 exec/priv.h                         |   43 +-
 ext2fs/balloc.c                     |   31 +-
 ext2fs/ext2fs.c                     |    6 +-
 ext2fs/ext2fs.h                     |  145 ++-
 ext2fs/getblk.c                     |   21 +-
 ext2fs/hyper.c                      |   35 +-
 ext2fs/ialloc.c                     |   37 +-
 ext2fs/inode.c                      |   40 +-
 ext2fs/pager.c                      |  467 +++++++++-
 ext2fs/pokel.c                      |   39 +-
 ext2fs/truncate.c                   |    9 +-
 fatfs/pager.c                       |   11 +-
 hurd/fsys.defs                      |   22 +-
 hurd/fsys_reply.defs                |   12 +-
 hurd/process.defs                   |   32 +-
 hurd/process_reply.defs             |  134 ++-
 hurd/process_request.defs           |   33 +-
 include/pids.h                      |   29 +
 init/init.c                         |   24 +-
 isofs/pager.c                       |   12 +-
 libdiskfs/Makefile                  |    5 +-
 libdiskfs/boot-start.c              |    3 +-
 libdiskfs/dead-name.c               |    4 +-
 libdiskfs/dir-lookup.c              |   26 +-
 libdiskfs/disk-pager.c              |    6 +-
 libdiskfs/diskfs-pager.h            |    3 +-
 libdiskfs/diskfs.h                  |   11 +-
 libdiskfs/file-set-trans.c          |   19 +-
 libdiskfs/fsys-get-children.c       |   99 ++
 libdiskfs/fsys-get-source.c         |   34 +
 libdiskfs/fsys-getroot.c            |    3 +-
 libdiskfs/get-source.c              |   28 +
 libdiskfs/init-startup.c            |   10 +-
 libdiskfs/peropen-make.c            |    8 +
 libdiskfs/peropen-rele.c            |    1 +
 libfshelp/Makefile                  |    3 +-
 libfshelp/fshelp.h                  |   33 +-
 libfshelp/translator-list.c         |  169 ++++
 libihash/ihash.h                    |   17 +
 libnetfs/Makefile                   |    7 +-
 {libdiskfs => libnetfs}/dead-name.c |   12 +-
 libnetfs/dir-lookup.c               |   26 +-
 libnetfs/file-get-transcntl.c       |   52 ++
 libnetfs/file-get-translator.c      |   14 +
 libnetfs/file-set-translator.c      |   17 +-
 libnetfs/fsstubs.c                  |    8 -
 libnetfs/fsys-get-children.c        |  112 +++
 libnetfs/fsys-get-source.c          |   34 +
 libnetfs/fsys-getroot.c             |    6 +-
 libnetfs/get-source.c               |   28 +
 libnetfs/init-startup.c             |   14 +
 libnetfs/make-peropen.c             |    8 +
 libnetfs/netfs.h                    |   10 +-
 libnetfs/release-peropen.c          |    1 +
 libpager/data-request.c             |    3 +-
 libpager/data-return.c              |   79 +-
 libpager/pager-create.c             |    4 +-
 libpager/pager.h                    |   23 +-
 libpager/priv.h                     |    1 +
 libshouldbeinlibc/Makefile          |    4 +-
 libshouldbeinlibc/nullauth.c        |   47 +
 libshouldbeinlibc/nullauth.h        |   31 +
 libstore/Makefile                   |    4 -
 libstore/crypt.h                    |   12 +
 libstore/do-bunzip2.c               | 1745 +++++++++++++++++++++++++++++++++++
 libstore/gzip.h                     |  315 +++++++
 libstore/inflate.c                  |  954 +++++++++++++++++++
 libstore/tailor.h                   |   14 +
 libstore/unzip.c                    |  199 ++++
 libstore/util.c                     |  272 ++++++
 libtrivfs/Makefile                  |    5 +-
 libtrivfs/fsys-get-children.c       |   35 +
 libtrivfs/fsys-get-source.c         |   33 +
 libtrivfs/get-source.c              |   28 +
 libtrivfs/startup.c                 |   19 +
 libtrivfs/trivfs.h                  |    8 +-
 mach-defpager/main.c                |   13 +
 pfinet/main.c                       |    3 +-
 pfinet/tunnel.c                     |   13 +-
 proc/info.c                         |    2 +-
 proc/main.c                         |    3 +-
 proc/mgt.c                          |   77 +-
 proc/pgrp.c                         |    7 +-
 proc/proc.h                         |    6 +-
 procfs/ChangeLog                    |    6 +
 procfs/main.c                       |  165 +++-
 procfs/process.c                    |   24 +-
 procfs/rootdir.c                    |   49 +-
 storeio/pager.c                     |    9 +-
 sutils/fstab.c                      |  115 +--
 tmpfs/pager-stubs.c                 |    8 +
 tmpfs/tmpfs.c                       |    6 +
 trans/Makefile                      |    8 +-
 trans/mtab.c                        |  800 ++++++++++++++++
 trans/null.c                        |    5 +
 trans/symlink.c                     |   28 +
 ufs/pager.c                         |   11 +-
 utils/Makefile                      |   13 +-
 utils/fakeroot.sh                   |   10 +-
 utils/match-options.c               |   68 ++
 utils/match-options.h               |   33 +
 utils/mount.c                       |  122 ++-
 utils/nullauth.c                    |   90 ++
 utils/remap.sh                      |    8 +-
 utils/umount.c                      |  357 +++++++
 116 files changed, 7785 insertions(+), 1246 deletions(-)

diff --git a/TODO b/TODO
index b541f38..bf37fb2 100644
--- a/TODO
+++ b/TODO
@@ -173,10 +173,6 @@ See `tasks', the exported task list.
      3: when a session leader exits, the association has to be torn
         down; bsd does SIGHUP + drain + revoke.)
 
-** exec:
-*** either resurrect or excise BFD exec server
-    (needs stdio magic converted to use libio when libc converts)
-
 ** proc:
 *** Add a version of proc_wait usable by non-parent processes, and use it in
     gdb; maybe just a flag WNOREAP to proc_wait that doesn't reap and allows
diff --git a/config.make.in b/config.make.in
index b8002a1..4d2abcc 100644
--- a/config.make.in
+++ b/config.make.in
@@ -30,6 +30,7 @@ sysconfdir = @sysconfdir@
 localstatedir = @localstatedir@
 sharedstatedir = @sharedstatedir@
 datadir = @datadir@
+datarootdir = @datarootdir@
 
 # All of those directories together:
 installationdirlist = $(hurddir) $(libdir) $(bindir) $(sbindir) \
@@ -48,6 +49,8 @@ MIG = @MIG@
 MIGCOM = $(MIG) -cc cat - /dev/null
 AWK = @AWK@
 SED = @SED@
+LEX = @LEX@
+YACC = @YACC@
 
 # Compilation flags.  Append these to the definitions already made by
 # the specific Makefile.
@@ -77,6 +80,16 @@ X11_LIBS = @X11_LIBS@
 XKB_BASE = @XKB_BASE@
 X11_KEYSYMDEF_H = @X11_KEYSYMDEF_H@
 
+# How to compile and link against libdaemon.
+HAVE_DAEMON = @HAVE_DAEMON@
+libdaemon_CFLAGS = @libdaemon_CFLAGS@
+libdaemon_LIBS = @libdaemon_LIBS@
+
+# How to compile and link against libblkid.
+HAVE_BLKID = @HAVE_BLKID@
+libblkid_CFLAGS = @libblkid_CFLAGS@
+libblkid_LIBS = @libblkid_LIBS@
+
 # Whether Sun RPC support is available.
 HAVE_SUN_RPC = @HAVE_SUN_RPC@
 
diff --git a/configure.ac b/configure.ac
index 31e48ef..5340b50 100644
--- a/configure.ac
+++ b/configure.ac
@@ -246,8 +246,9 @@ PKG_CHECK_MODULES([X11], [x11 xproto],
     AS_IF([test $pkg_failed = no],
       [XKB_BASE="$pkg_cv_XKB_BASE"
        AC_MSG_RESULT([$XKB_BASE])],
-      [XKB_BASE="$datadir/share/X11/xkb"
+      [XKB_BASE="$datadir/X11/xkb"
        AC_MSG_RESULT([(default) $XKB_BASE])])
+    pkg_failed=no
     AC_MSG_CHECKING([for X11 prefix])
     _PKG_CONFIG([X11_PREFIX], [variable=prefix], [x11])
     AS_IF([test $pkg_failed = no],
@@ -296,6 +297,18 @@ else
                            echo ${file}:build.mk.in; done`"
 fi
 
+PKG_CHECK_MODULES([libdaemon], [libdaemon],
+  [AC_DEFINE([HAVE_DAEMON], [1], [Use libdaemon])],
+  [true])
+AC_SUBST([libdaemon_LIBS])
+AC_SUBST([libdaemon_CFLAGS])
+
+PKG_CHECK_MODULES([libblkid], [blkid],
+  [AC_DEFINE([HAVE_BLKID], [1], [Use libblkid])],
+  [true])
+AC_SUBST([libblkid_LIBS])
+AC_SUBST([libblkid_CFLAGS])
+
 AC_CONFIG_FILES([config.make ${makefiles}])
 AC_OUTPUT
 
diff --git a/console-client/Makefile b/console-client/Makefile
index 69a7e37..65a04e0 100644
--- a/console-client/Makefile
+++ b/console-client/Makefile
@@ -37,11 +37,11 @@ SRCS = $(CONSOLE_SRCS) \
 VPATH += $(srcdir)/xkb
 OBJS = $(addsuffix .o,$(basename $(notdir $(SRCS)))) kdioctlServer.o
 HURDLIBS = cons ports netfs fshelp iohelp ihash shouldbeinlibc
-LDLIBS = -ldl -lpthread
+LDLIBS = -ldl -lpthread $(libdaemon_LIBS)
 module-dir = $(libdir)/hurd/console
 console-LDFLAGS = -Wl,-E
 
-CPPFLAGS += -I$(CURDIR)/xkb -I$(srcdir)/xkb
+CPPFLAGS += -I$(CURDIR)/xkb -I$(srcdir)/xkb $(libdaemon_CFLAGS)
 LFLAGS = -i
 YFLAGS = -by
 XKB_DATA_FILES = keymap/hurd types/hurd symbols/hurd
diff --git a/console-client/console.c b/console-client/console.c
index 7c9a880..f995ca2 100644
--- a/console-client/console.c
+++ b/console-client/console.c
@@ -26,6 +26,9 @@
 #include <assert.h>
 
 #include <pthread.h>
+#if HAVE_DAEMON
+#include <libdaemon/daemon.h>
+#endif
 
 #include <hurd/console.h>
 #include <hurd/cons.h>
@@ -61,6 +64,8 @@ static cons_t saved_cons;
    set.  */
 static char *console_node;
 
+/* If set, the client will daemonize.  */
+static int daemonize;
 
 /* Callbacks for input source drivers.  */
 
@@ -516,6 +521,8 @@ cons_vcons_set_mousecursor_status (vcons_t vcons, int 
status)
 }
 
 
+#define DAEMONIZE_KEY 0x80 /* !isascii (DAEMONIZE_KEY), so no short option.  */
+
 /* Console-specific options.  */
 static const struct argp_option
 options[] =
@@ -524,6 +531,9 @@ options[] =
     {"driver", 'd', "NAME", 0, "Add driver NAME to the console" },
     {"console-node", 'c', "FILE", OPTION_ARG_OPTIONAL,
      "Set a translator on the node FILE (default: " DEFAULT_CONSOLE_NODE ")" },
+#if HAVE_DAEMON
+    {"daemonize", DAEMONIZE_KEY, NULL, 0, "daemonize the console client"},
+#endif
     {0}
   };
 
@@ -577,7 +587,11 @@ parse_opt (int key, char *arg, struct argp_state *state)
       if (!console_node)
        return ENOMEM;
       break;
-      
+
+    case DAEMONIZE_KEY:
+      daemonize = 1;
+      break;
+
     case ARGP_KEY_SUCCESS:
       if (!devcount)
        {
@@ -597,6 +611,31 @@ static const struct argp_child startup_children[] =
   { { &cons_startup_argp }, { 0 } };
 static struct argp startup_argp = {options, parse_opt, 0,
                                   0, startup_children};
+#if HAVE_DAEMON
+#define daemon_error(status, errnum, format, args...)                  \
+  do                                                                   \
+    {                                                                  \
+      if (daemonize)                                                   \
+       {                                                               \
+         if (errnum)                                                   \
+           daemon_log (LOG_ERR, format ": %s", ##args,                 \
+                       strerror(errnum));                              \
+         else                                                          \
+           daemon_log (LOG_ERR, format, ##args);                       \
+         if (status)                                                   \
+           {                                                           \
+             /* Signal parent.  */                                     \
+             daemon_retval_send (status);                              \
+             return 0;                                                 \
+           }                                                           \
+       }                                                               \
+      else                                                             \
+       error (status, errnum, format, ##args);                         \
+    }                                                                  \
+  while (0);
+#else
+#define daemon_error   error
+#endif
 
 int
 main (int argc, char *argv[])
@@ -609,9 +648,69 @@ main (int argc, char *argv[])
   /* Parse our command line.  This shouldn't ever return an error.  */
   argp_parse (&startup_argp, argc, argv, ARGP_IN_ORDER, 0, 0);
 
+#if HAVE_DAEMON
+  if (daemonize)
+    {
+      /* Reset signal handlers.         */
+      if (daemon_reset_sigs (-1) < 0)
+       error (1, errno, "Failed to reset all signal handlers");
+
+      /* Unblock signals.  */
+      if (daemon_unblock_sigs (-1) < 0)
+       error (1, errno, "Failed to unblock all signals");
+
+      /* Set indetification string for the daemon for both syslog and
+        PID file.  */
+      daemon_pid_file_ident = daemon_log_ident = \
+       daemon_ident_from_argv0 (argv[0]);
+
+      /* Check that the daemon is not run twice at the same time.  */
+      pid_t pid;
+      if ((pid = daemon_pid_file_is_running ()) >= 0)
+       error (1, errno, "Daemon already running on PID file %u", pid);
+
+      /* Prepare for return value passing from the initialization
+        procedure of the daemon process.  */
+      if (daemon_retval_init () < 0)
+       error (1, errno, "Failed to create pipe.");
+
+      /* Do the fork.  */
+      if ((pid = daemon_fork ()) < 0)
+       {
+         /* Exit on error.  */
+         daemon_retval_done ();
+         error (1, errno, "Failed to fork");
+       }
+      else if (pid)
+       {
+         /* The parent.  */
+         int ret;
+
+         /* Wait for 20 seconds for the return value passed from the
+            daemon process. .  */
+         if ((ret = daemon_retval_wait (20)) < 0)
+           error (1, errno,
+                  "Could not receive return value from daemon process");
+
+         return ret;
+       }
+      else
+       {
+         /* The daemon.  */
+         /* Close FDs.  */
+         if (daemon_close_all (-1) < 0)
+           daemon_error (1, errno, "Failed to close all file descriptors");
+
+         /* Create the PID file.  */
+         if (daemon_pid_file_create () < 0)
+           daemon_error (2, errno, "Could not create PID file");
+       }
+    }
+#endif /* HAVE_DAEMON */
+
   err = driver_start (&errname);
   if (err)
-    error (1, err, "Starting driver %s failed", errname);
+    daemon_error (1, err, "Starting driver %s failed", errname);
     
   pthread_mutex_init (&global_lock, NULL);
 
@@ -619,19 +718,25 @@ main (int argc, char *argv[])
   if (err)
     {
       driver_fini ();
-      error (1, err, "Console library initialization failed");
+      daemon_error (1, err, "Console library initialization failed");
     }
 
   err = timer_init ();
   if (err)
     {
       driver_fini ();
-      error (1, err, "Timer thread initialization failed");
+      daemon_error (1, err, "Timer thread initialization failed");
     }
 
   if (console_node)
     console_setup_node (console_node);
-  
+
+#if HAVE_DAEMON
+  if (daemonize)
+    /* Signal parent that all went well.  */
+    daemon_retval_send(0);
+#endif
+
   cons_server_loop ();
 
   /* Never reached.  */
diff --git a/console/pager.c b/console/pager.c
index 781ba35..4d0c5cd 100644
--- a/console/pager.c
+++ b/console/pager.c
@@ -95,6 +95,14 @@ pager_unlock_page (struct user_pager_info *pager,
 }
 
 
+void
+pager_notify_evict (struct user_pager_info *pager,
+                   vm_offset_t page)
+{
+  assert (!"unrequested notification on eviction");
+}
+
+
 /* Tell how big the file is. */
 error_t
 pager_report_extent (struct user_pager_info *upi,
@@ -170,7 +178,7 @@ user_pager_create (struct user_pager *user_pager, unsigned 
int npages,
 
   /* XXX Are the values 1 and MEMORY_OBJECT_COPY_DELAY correct? */
   user_pager->pager = pager_create (upi, pager_bucket,
-                                   1, MEMORY_OBJECT_COPY_DELAY);
+                                   1, MEMORY_OBJECT_COPY_DELAY, 0);
   if (!user_pager->pager)
     {
       free (upi);
diff --git a/daemons/console-run.c b/daemons/console-run.c
index fb879e5..e1bfe64 100644
--- a/daemons/console-run.c
+++ b/daemons/console-run.c
@@ -217,8 +217,9 @@ open_console (char **namep)
   dup2 (0, 1);
   dup2 (0, 2);
 
-  if (setsid () == -1)
-    error (0, errno, "setsid");
+  if (getsid (0) != getpid ())
+    if (setsid () == -1)
+      error (0, errno, "setsid");
 
   /* Set the console to our pgrp.  */
   tcsetpgrp (0, getpid ());
diff --git a/devnode/devnode.c b/devnode/devnode.c
index 50011aa..218b308 100644
--- a/devnode/devnode.c
+++ b/devnode/devnode.c
@@ -164,6 +164,16 @@ ds_device_open (mach_port_t master_port, mach_port_t 
reply_port,
       || device_name == NULL) 
       return D_NO_SUCH_DEVICE;
 
+  if (master_file != NULL)
+    {
+      if (master_device != MACH_PORT_NULL)
+        mach_port_deallocate (mach_task_self (), master_device);
+
+      master_device = file_name_lookup (master_file, 0, 0);
+      if (master_device == MACH_PORT_NULL)
+        error (1, errno, "file_name_lookup");
+    }
+
   err = device_open (master_device, mode, device_name, device); 
   *devicetype = MACH_MSG_TYPE_MOVE_SEND;
   return err;
diff --git a/exec/Makefile b/exec/Makefile
index 3fc8273..890ee4b 100644
--- a/exec/Makefile
+++ b/exec/Makefile
@@ -20,15 +20,9 @@
 dir := exec
 makemode := server
 
-SRCS = exec.c main.c hashexec.c hostarch.c \
-       $(gzip-sources) $(bzip2-sources)
+SRCS = exec.c main.c hashexec.c hostarch.c
 OBJS = main.o hostarch.o exec.o hashexec.o \
-       execServer.o exec_startupServer.o \
-       $(gzip-objects) $(bzip2-objects)
-gzip-sources = unzip.c util.c inflate.c
-gzip-objects = $(gzip-sources:%.c=%.o)
-bzip2-sources = do-bunzip2.c
-bzip2-objects = $(bzip2-sources:%.c=%.o)
+       execServer.o exec_startupServer.o
 
 target = exec
 #targets = exec exec.static
@@ -40,6 +34,6 @@ exec-MIGSFLAGS = -imacros $(srcdir)/execmutations.h
 
 include ../Makeconf
 
-CPPFLAGS += -DGZIP -DBZIP2 # -DBFD
+CPPFLAGS += # -DBFD
 
 exec.static exec: $(OBJS) $(library_deps)
diff --git a/exec/exec.c b/exec/exec.c
index 30a5e00..fad9492 100644
--- a/exec/exec.c
+++ b/exec/exec.c
@@ -4,16 +4,6 @@
    Written by Roland McGrath.
 
    Can exec ELF format directly.
-   #ifdef GZIP
-   Can gunzip executables into core on the fly.
-   #endif
-   #ifdef BFD
-   Can exec any executable format the BFD library understands
-   to be for this flavor of machine.
-   #endif
-   #ifdef BZIP2
-   Can bunzip2 executables into core on the fly.
-   #endif
 
 This file is part of the GNU Hurd.
 
@@ -50,70 +40,8 @@ size_t std_nports, std_nints;
 pthread_rwlock_t std_lock = PTHREAD_RWLOCK_INITIALIZER;
 
 
-#ifdef BFD
-/* Return a Hurd error code corresponding to the most recent BFD error.  */
-static error_t
-b2he (error_t deflt)
-{
-  switch (bfd_get_error ())
-    {
-    case bfd_error_system_call:
-      return errno;
-
-    case bfd_error_no_memory:
-      return ENOMEM;
-
-    default:
-      return deflt;
-    }
-}
-#else
 #define        b2he()  a2he (errno)
-#endif
 
-#ifdef GZIP
-static void check_gzip (struct execdata *);
-#endif
-
-#ifdef BZIP2
-static void check_bzip2 (struct execdata *);
-#endif
-
-#ifdef BFD
-
-/* Check a section, updating the `locations' vector [BFD].  */
-static void
-check_section (bfd *bfd, asection *sec, void *userdata)
-{
-  struct execdata *u = userdata;
-  static const union
-    {
-      char string[8];
-      unsigned int quadword __attribute__ ((mode (DI)));
-    } interp = { string: ".interp" };
-
-  if (u->error)
-    return;
-
-  /* Fast strcmp for this 8-byte constant string.  */
-  if (*(const __typeof (interp.quadword) *) sec->name == interp.quadword)
-    u->interp.section = sec;
-
-  if (!(sec->flags & (SEC_ALLOC|SEC_LOAD)) ||
-      (sec->flags & SEC_NEVER_LOAD))
-    /* Nothing to do for this section.  */
-    return;
-
-  if (sec->flags & SEC_LOAD)
-    {
-      u->info.bfd_locations[sec->index] = sec->filepos;
-      if ((off_t) sec->filepos < 0 || (off_t) sec->filepos > u->file_size)
-       u->error = ENOEXEC;
-    }
-}
-#endif
-
-
 /* Zero the specified region but don't crash the server if it faults.  */
 
 #include <hurd/sigpreempt.h>
@@ -135,69 +63,45 @@ load_section (void *section, struct execdata *u)
   vm_prot_t vm_prot;
   int anywhere;
   vm_address_t mask = 0;
-#ifdef BFD
-  asection *const sec = section;
-#endif
   const ElfW(Phdr) *const ph = section;
 
   if (u->error)
     return;
 
-#ifdef BFD
-  if (u->bfd && sec->flags & SEC_NEVER_LOAD)
-    /* Nothing to do for this section.  */
-    return;
-#endif
-
   vm_prot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
 
-#ifdef BFD
-  if (u->bfd)
-    {
-      addr = (vm_address_t) sec->vma;
-      filepos = u->info.bfd_locations[sec->index];
-      memsz = sec->_raw_size;
-      filesz = (sec->flags & SEC_LOAD) ? memsz : 0;
-      if (sec->flags & (SEC_READONLY|SEC_ROM))
-       vm_prot &= ~VM_PROT_WRITE;
-      anywhere = 0;
-    }
+  addr = ph->p_vaddr & ~(ph->p_align - 1);
+  memsz = ph->p_vaddr + ph->p_memsz - addr;
+  filepos = ph->p_offset & ~(ph->p_align - 1);
+  filesz = ph->p_offset + ph->p_filesz - filepos;
+  if ((ph->p_flags & PF_R) == 0)
+    vm_prot &= ~VM_PROT_READ;
+  if ((ph->p_flags & PF_W) == 0)
+    vm_prot &= ~VM_PROT_WRITE;
+  if ((ph->p_flags & PF_X) == 0)
+    vm_prot &= ~VM_PROT_EXECUTE;
+  anywhere = u->info.elf.anywhere;
+  if (! anywhere)
+    addr += u->info.elf.loadbase;
   else
-#endif
-    {
-      addr = ph->p_vaddr & ~(ph->p_align - 1);
-      memsz = ph->p_vaddr + ph->p_memsz - addr;
-      filepos = ph->p_offset & ~(ph->p_align - 1);
-      filesz = ph->p_offset + ph->p_filesz - filepos;
-      if ((ph->p_flags & PF_R) == 0)
-       vm_prot &= ~VM_PROT_READ;
-      if ((ph->p_flags & PF_W) == 0)
-       vm_prot &= ~VM_PROT_WRITE;
-      if ((ph->p_flags & PF_X) == 0)
-       vm_prot &= ~VM_PROT_EXECUTE;
-      anywhere = u->info.elf.anywhere;
-      if (! anywhere)
-       addr += u->info.elf.loadbase;
-      else
 #if 0
-       switch (elf_machine)
-         {
-         case EM_386:
-         case EM_486:
-           /* On the i386, programs normally load at 0x08000000, and
-              expect their data segment to be able to grow dynamically
-              upward from its start near that address.  We need to make
-              sure that the dynamic linker is not mapped in a conflicting
-              address.  */
-           /* mask = 0xf8000000UL; */ /* XXX */
-           break;
-         default:
-           break;
-         }
+    switch (elf_machine)
+      {
+      case EM_386:
+      case EM_486:
+       /* On the i386, programs normally load at 0x08000000, and
+          expect their data segment to be able to grow dynamically
+          upward from its start near that address.  We need to make
+          sure that the dynamic linker is not mapped in a conflicting
+          address.  */
+       /* mask = 0xf8000000UL; */ /* XXX */
+       break;
+      default:
+       break;
+      }
 #endif
-      if (anywhere && addr < vm_page_size)
-       addr = vm_page_size;
-    }
+  if (anywhere && addr < vm_page_size)
+    addr = vm_page_size;
 
   if (memsz == 0)
     /* This section is empty; ignore it.  */
@@ -289,6 +193,17 @@ load_section (void *section, struct execdata *u)
            }
        }
 
+      /* If this segment is executable, adjust start_code and end_code
+        so that this mapping is within that range.  */
+      if (vm_prot & VM_PROT_EXECUTE)
+       {
+         if (u->start_code == 0 || u->start_code > addr)
+           u->start_code = addr;
+
+         if (u->end_code < addr + memsz)
+           u->end_code = addr + memsz;
+       }
+
       if (mapstart > addr)
        {
          /* We must read and copy in the space in the section before the
@@ -502,16 +417,6 @@ map (struct execdata *e, off_t posn, size_t len)
   return map_buffer (e) + offset;
 }
 
-
-/* Initialize E's stdio stream.  */
-static void prepare_stream (struct execdata *e);
-
-/* Point the stream at the buffer of file data in E->file_data.  */
-static void prepare_in_memory (struct execdata *e);
-
-
-#ifndef EXECDATA_STREAM
-
 /* We don't have a stdio stream, but we have a mapping window
    we need to initialize.  */
 static void
@@ -521,199 +426,7 @@ prepare_stream (struct execdata *e)
   e->map_vsize = e->map_fsize = 0;
   e->map_filepos = 0;
 }
-
-static void prepare_in_memory (struct execdata *e)
-{
-  prepare_stream(e);
-}
-
-#else
-
-#ifdef _STDIO_USES_IOSTREAM
-
-# error implement me for libio!
-
-#else  /* old GNU stdio */
-
-#if 0
-void *
-map (struct execdata *e, off_t posn, size_t len)
-{
-  FILE *f = &e->stream;
-  const size_t size = e->file_size;
-  size_t offset;
-
-  if ((f->__offset & ~(f->__bufsize - 1)) == (posn & ~(f->__bufsize - 1)) &&
-      f->__buffer + (posn + len - f->__offset) < f->__get_limit)
-    /* The current mapping window covers it.  */
-    offset = posn & (f->__bufsize - 1);
-  else if (e->file_data != NULL)
-    {
-      /* The current "mapping window" is in fact the whole file contents.
-        So if it's not in there, it's not in there.  */
-      f->__eof = 1;
-      return NULL;
-    }
-  else if (e->filemap == MACH_PORT_NULL)
-    {
-      /* No mapping for the file.  Read the data by RPC.  */
-      char *buffer = f->__buffer;
-      mach_msg_type_number_t nread = f->__bufsize;
-      /* Read as much as we can get into the buffer right now.  */
-      e->error = io_read (e->file, &buffer, &nread, posn, round_page (len));
-      if (e->error)
-       {
-         errno = e->error;
-         f->__error = 1;
-         return NULL;
-       }
-      if (buffer != f->__buffer)
-       {
-         /* The data was returned out of line.  Discard the old buffer.  */
-         if (f->__bufsize != 0)
-           munmap (f->__buffer, f->__bufsize);
-         f->__buffer = buffer;
-         f->__bufsize = round_page (nread);
-       }
-
-      f->__offset = posn;
-      f->__get_limit = f->__buffer + nread;
-      offset = 0;
-    }
-  else
-    {
-      /* Deallocate the old mapping area.  */
-      if (f->__buffer != NULL)
-       munmap (f->__buffer, f->__bufsize);
-      f->__buffer = NULL;
-
-      /* Make sure our mapping is page-aligned in the file.  */
-      offset = posn & (vm_page_size - 1);
-      f->__offset = trunc_page (posn);
-      f->__bufsize = round_page (posn + len) - f->__offset;
-
-      /* Map the data from the file.  */
-      if (vm_map (mach_task_self (),
-                 (vm_address_t *) &f->__buffer, f->__bufsize, 0, 1,
-                 e->filemap, f->__offset, 1, VM_PROT_READ, VM_PROT_READ,
-                 VM_INHERIT_NONE))
-       {
-         errno = e->error = EIO;
-         f->__error = 1;
-         return NULL;
-       }
-
-      if (e->cntl)
-       e->cntl->accessed = 1;
-
-      if (f->__offset + f->__bufsize > size)
-       f->__get_limit = f->__buffer + (size - f->__offset);
-      else
-       f->__get_limit = f->__buffer + f->__bufsize;
-    }
-
-  f->__target = f->__offset;
-  f->__bufp = f->__buffer + offset;
-
-  if (f->__bufp + len > f->__get_limit)
-    {
-      f->__eof = 1;
-      return NULL;
-    }
-
-  return f->__bufp;
-}
-#endif
-
-/* stdio input-room function.
-   XXX/fault in the stdio case (or libio replacement), i.e. for bfd
-   (if ever revived), need to check all the mapping fault issues  */
-static int
-input_room (FILE *f)
-{
-  struct execdata *e = f->__cookie;
-  char *p = map (e, f->__target, 1);
-  if (p == NULL)
-    {
-      (e->error ? f->__error : f->__eof) = 1;
-      return EOF;
-    }
-
-  f->__target = f->__offset;
-  f->__bufp = p;
-
-  return (unsigned char) *f->__bufp++;
-}
-
-static int
-close_exec_stream (void *cookie)
-{
-  struct execdata *e = cookie;
-
-  if (e->stream.__buffer != NULL)
-    munmap (e->stream.__buffer, e->stream.__bufsize);
-
-  return 0;
-}
-
-/* stdio seek function. */
-static int
-fake_seek (void *cookie, fpos_t *pos, int whence)
-{
-  struct execdata *e = cookie;
-
-  /* Set __target to match the specifed seek location */
-  switch (whence)
-    {
-    case SEEK_END:
-      e->stream.__target = e->file_size + *pos;
-      break;
-
-    case SEEK_CUR:
-      e->stream.__target += *pos;
-      break;
-
-    case SEEK_SET:
-      e->stream.__target = *pos;
-      break;
-    }
-  *pos = e->stream.__target;
-  return 0;
-}
-
-/* Initialize E's stdio stream.  */
-static void
-prepare_stream (struct execdata *e)
-{
-  memset (&e->stream, 0, sizeof (e->stream));
-  e->stream.__magic = _IOMAGIC;
-  e->stream.__mode.__read = 1;
-  e->stream.__userbuf = 1;
-  e->stream.__room_funcs.__input = input_room;
-  e->stream.__io_funcs.seek = fake_seek;
-  e->stream.__io_funcs.close = close_exec_stream;
-  e->stream.__cookie = e;
-  e->stream.__seen = 1;
-}
-
-/* Point the stream at the buffer of file data.  */
-static void
-prepare_in_memory (struct execdata *e)
-{
-  memset (&e->stream, 0, sizeof (e->stream));
-  e->stream.__magic = _IOMAGIC;
-  e->stream.__mode.__read = 1;
-  e->stream.__buffer = e->file_data;
-  e->stream.__bufsize = e->file_size;
-  e->stream.__get_limit = e->stream.__buffer + e->stream.__bufsize;
-  e->stream.__bufp = e->stream.__buffer;
-  e->stream.__seen = 1;
-}
-#endif
-
-#endif
 
-
 /* Prepare to check and load FILE.  */
 static void
 prepare (file_t file, struct execdata *e)
@@ -722,9 +435,6 @@ prepare (file_t file, struct execdata *e)
 
   e->file = file;
 
-#ifdef BFD
-  e->bfd = NULL;
-#endif
   e->file_data = NULL;
   e->cntl = NULL;
   e->filemap = MACH_PORT_NULL;
@@ -732,6 +442,9 @@ prepare (file_t file, struct execdata *e)
 
   e->interp.section = NULL;
 
+  e->start_code = 0;
+  e->end_code = 0;
+
   /* Initialize E's stdio stream.  */
   prepare_stream (e);
 
@@ -801,42 +514,6 @@ prepare (file_t file, struct execdata *e)
     }
 }
 
-/* Check the magic number, etc. of the file.
-   On successful return, the caller must allocate the
-   E->locations vector, and map check_section over the BFD.  */
-
-#ifdef BFD
-static void
-check_bfd (struct execdata *e)
-{
-  bfd_set_error (bfd_error_no_error);
-
-  e->bfd = bfd_openstreamr (NULL, NULL, &e->stream);
-  if (e->bfd == NULL)
-    {
-      e->error = b2he (ENOEXEC);
-      return;
-    }
-
-  if (!bfd_check_format (e->bfd, bfd_object))
-    {
-      e->error = b2he (ENOEXEC);
-      return;
-    }
-  else if (/* !(e->bfd->flags & EXEC_P) || XXX */
-          (host_bfd.arch_info->compatible = e->bfd->arch_info->compatible,
-           bfd_arch_get_compatible (&host_bfd, e->bfd)) != host_bfd.arch_info)
-    {
-      /* This file is of a recognized binary file format, but it is not
-        executable on this machine.  */
-      e->error = b2he (ENOEXEC);
-      return;
-    }
-
-  e->entry = e->bfd->start_address;
-}
-#endif
-
 #include <endian.h>
 #if BYTE_ORDER == BIG_ENDIAN
 #define host_ELFDATA ELFDATA2MSB
@@ -955,13 +632,6 @@ static void
 check (struct execdata *e)
 {
   check_elf (e);               /* XXX/fault */
-#ifdef BFD
-  if (e->error == ENOEXEC)
-    {
-      e->error = 0;
-      check_bfd (e);
-    }
-#endif
 }
 
 
@@ -1004,18 +674,7 @@ void
 finish (struct execdata *e, int dealloc_file)
 {
   finish_mapping (e);
-#ifdef BFD
-  if (e->bfd != NULL)
     {
-      bfd_close (e->bfd);
-      e->bfd = NULL;
-    }
-  else
-#endif
-    {
-#ifdef EXECDATA_STREAM
-      fclose (&e->stream);
-#else
       if (e->file_data != NULL) {
        free (e->file_data);
        e->file_data = NULL;
@@ -1023,7 +682,6 @@ finish (struct execdata *e, int dealloc_file)
        munmap (map_buffer (e), map_vsize (e));
        map_buffer (e) = NULL;
       }
-#endif
     }
   if (dealloc_file && e->file != MACH_PORT_NULL)
     {
@@ -1041,293 +699,19 @@ load (task_t usertask, struct execdata *e)
 
   if (! e->error)
     {
-#ifdef BFD
-      if (e->bfd)
-       {
-         void load_bfd_section (bfd *bfd, asection *sec, void *userdata)
-           {
-             load_section (sec, userdata);
-           }
-         bfd_map_over_sections (e->bfd, &load_bfd_section, e);
-       }
-      else
-#endif
-       {
-         ElfW(Word) i;
-         for (i = 0; i < e->info.elf.phnum; ++i)
-           if (e->info.elf.phdr[i].p_type == PT_LOAD)
-             load_section (&e->info.elf.phdr[i], e);
-
-         /* The entry point address is relative to wherever we loaded the
-            program text.  */
-         e->entry += e->info.elf.loadbase;
-       }
+      ElfW(Word) i;
+      for (i = 0; i < e->info.elf.phnum; ++i)
+       if (e->info.elf.phdr[i].p_type == PT_LOAD)
+         load_section (&e->info.elf.phdr[i], e);
+
+      /* The entry point address is relative to wherever we loaded the
+        program text.  */
+      e->entry += e->info.elf.loadbase;
     }
 
   /* Release the conch for the file.  */
   finish_mapping (e);
-
-  if (! e->error)
-    {
-      /* Do post-loading processing on the task.  */
-
-#ifdef BFD
-      if (e->bfd)
-       {
-         /* Do post-loading processing for a section.  This consists of
-            peeking the pages of non-demand-paged executables.  */
-
-         void postload_section (bfd *bfd, asection *sec, void *userdata)
-           {
-             struct execdata *u = userdata;
-             vm_address_t addr = 0;
-             vm_size_t secsize = 0;
-
-             addr = (vm_address_t) sec->vma;
-             secsize = sec->_raw_size;
-
-             if ((sec->flags & SEC_LOAD) && !(bfd->flags & D_PAGED))
-               {
-                 /* Pre-load the section by peeking every mapped page.  */
-                 vm_address_t myaddr, a;
-                 vm_size_t mysize;
-                 myaddr = 0;
-
-                 /* We have already mapped the file into the task in
-                    load_section.  Now read from the task's memory into our
-                    own address space so we can peek each page and cause it to
-                    be paged in.  */
-                 u->error = vm_read (u->task, trunc_page (addr),
-                                     round_page (secsize), &myaddr, &mysize);
-                 if (u->error)
-                   return;
-
-                 /* Peek at the first word of each page.  */
-                 for (a = ((myaddr + mysize) & ~(vm_page_size - 1));
-                      a >= myaddr; a -= vm_page_size)
-                   /* Force it to be paged in.  */
-                   (void) *(volatile int *) a;
-
-                 munmap ((caddr_t) myaddr, mysize);
-               }
-           }
-
-         bfd_map_over_sections (e->bfd, postload_section, e);
-       }
-#endif
-    }
-}
-
-#ifdef GZIP
-/* Check the file for being a gzip'd image.  Return with ENOEXEC means not
-   a valid gzip file; return with another error means lossage in decoding;
-   return with zero means the file was uncompressed into memory which E now
-   points to, and `check' can be run again.  */
-
-static void
-check_gzip (struct execdata *earg)
-{
-  struct execdata *e = earg;
-  /* Entry points to unzip engine.  */
-  int get_method (int);
-  void unzip (int, int);
-  extern long int bytes_out;
-  /* Callbacks from unzip for I/O and error interface.  */
-  extern int (*unzip_read) (char *buf, size_t maxread);
-  extern void (*unzip_write) (const char *buf, size_t nwrite);
-  extern void (*unzip_read_error) (void);
-  extern void (*unzip_error) (const char *msg);
-
-  char *zipdata = NULL;
-  size_t zipdatasz = 0;
-  FILE *zipout = NULL;
-  jmp_buf ziperr;
-  off_t zipread_pos = 0;
-  int zipread (char *buf, size_t maxread)
-    {
-      char *contents = map (e, zipread_pos, 1);
-      size_t n;
-      if (contents == NULL)
-       {
-         errno = e->error;
-         return -1;
-       }
-      n = MIN (maxread, map_buffer (e) + map_fsize (e) - contents);
-      errno = hurd_safe_copyin (buf, contents, n); /* XXX/fault */
-      if (errno)
-        longjmp (ziperr, 2);
-        
-      zipread_pos += n;
-      return n;
-    }
-  void zipwrite (const char *buf, size_t nwrite)
-    {
-      if (fwrite (buf, nwrite, 1, zipout) != 1)
-       longjmp (ziperr, 1);
-    }
-  void ziprderr (void)
-    {
-      errno = ENOEXEC;
-      longjmp (ziperr, 2);
-    }
-  void ziperror (const char *msg)
-    {
-      errno = ENOEXEC;
-      longjmp (ziperr, 2);
-    }
-
-  unzip_read = zipread;
-  unzip_write = zipwrite;
-  unzip_read_error = ziprderr;
-  unzip_error = ziperror;
-
-  if (setjmp (ziperr))
-    {
-      /* Error in unzipping jumped out.  */
-      if (zipout)
-       {
-         fclose (zipout);
-         free (zipdata);
-       }
-      e->error = errno;
-      return;
-    }
-
-  if (get_method (0) != 0)
-    {
-      /* Not a happy gzip file.  */
-      e->error = ENOEXEC;
-      return;
-    }
-
-  /* Matched gzip magic number.  Ready to unzip.
-     Set up the output stream and let 'er rip.  */
-
-  zipout = open_memstream (&zipdata, &zipdatasz);
-  if (! zipout)
-    {
-      e->error = errno;
-      return;
-    }
-
-  /* Call the gunzip engine.  */
-  bytes_out = 0;
-  unzip (17, 23);              /* Arguments ignored.  */
-
-  /* The output is complete.  Clean up the stream and store its resultant
-     buffer and size in the execdata as the file contents.  */
-  fclose (zipout);
-
-  /* Clean up the old exec file stream's state.
-     Now that we have the contents all in memory (in E->file_data),
-     nothing will in fact ever try to use E->stream again.  */
-  finish (e, 0);
-
-  /* Prepare the stream state to use the file contents already in memory.  */
-  e->file_data = zipdata;
-  e->file_size = zipdatasz;
-  prepare_in_memory (e);
-}
-#endif
-
-#ifdef BZIP2
-/* Check the file for being a bzip2'd image.  Return with ENOEXEC means not
-   a valid bzip2 file; return with another error means lossage in decoding;
-   return with zero means the file was uncompressed into memory which E now
-   points to, and `check' can be run again.  */
-
-static void
-check_bzip2 (struct execdata *earg)
-{
-  struct execdata *e = earg;
-  /* Entry points to bunzip2 engine.  */
-  void do_bunzip2 (void);
-  /* Callbacks from unzip for I/O and error interface.  */
-  extern int (*unzip_read) (char *buf, size_t maxread);
-  extern void (*unzip_write) (const char *buf, size_t nwrite);
-  extern void (*unzip_read_error) (void);
-  extern void (*unzip_error) (const char *msg);
-
-  char *zipdata = NULL;
-  size_t zipdatasz = 0;
-  FILE *zipout = NULL;
-  jmp_buf ziperr;
-  off_t zipread_pos = 0;
-  int zipread (char *buf, size_t maxread)
-    {
-      char *contents = map (e, zipread_pos, 1);
-      size_t n;
-      if (contents == NULL)
-       {
-         errno = e->error;
-         return -1;
-       }
-      n = MIN (maxread, map_buffer (e) + map_fsize (e) - contents);
-      errno = hurd_safe_copyin (buf, contents, n); /* XXX/fault */
-      if (errno)
-        longjmp (ziperr, 2);
-
-      zipread_pos += n;
-      return n;
-    }
-  void zipwrite (const char *buf, size_t nwrite)
-    {
-      if (fwrite (buf, nwrite, 1, zipout) != 1)
-       longjmp (ziperr, 1);
-    }
-  void ziprderr (void)
-    {
-      errno = ENOEXEC;
-      longjmp (ziperr, 2);
-    }
-  void ziperror (const char *msg)
-    {
-      errno = ENOEXEC;
-      longjmp (ziperr, 2);
-    }
-
-  unzip_read = zipread;
-  unzip_write = zipwrite;
-  unzip_read_error = ziprderr;
-  unzip_error = ziperror;
-
-  if (setjmp (ziperr))
-    {
-      /* Error in unzipping jumped out.  */
-      if (zipout)
-       {
-         fclose (zipout);
-         free (zipdata);
-       }
-      e->error = errno;
-      return;
-    }
-
-  zipout = open_memstream (&zipdata, &zipdatasz);
-  if (! zipout)
-    {
-      e->error = errno;
-      return;
-    }
-
-  /* Call the bunzip2 engine.  */
-  do_bunzip2 ();
-
-  /* The output is complete.  Clean up the stream and store its resultant
-     buffer and size in the execdata as the file contents.  */
-  fclose (zipout);
-
-  /* Clean up the old exec file stream's state.
-     Now that we have the contents all in memory (in E->file_data),
-     nothing will in fact ever try to use E->stream again.  */
-  finish (e, 0);
-
-  /* Prepare the stream state to use the file contents already in memory.  */
-  e->file_data = zipdata;
-  e->file_size = zipdatasz;
-  prepare_in_memory (e);
 }
-#endif
 
 
 static inline void *
@@ -1386,43 +770,6 @@ do_exec (file_t file,
 
       /* Check the file for validity first.  */
       check (e);
-
-#ifdef GZIP
-      if (e->error == ENOEXEC)
-       {
-         /* See if it is a compressed image.  */
-         static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-         /* The gzip code is really cheesy, not even close to thread-safe.
-            So we serialize all uses of it.  */
-         pthread_mutex_lock (&lock);
-         e->error = 0;
-         check_gzip (e);
-         pthread_mutex_unlock (&lock);
-         if (e->error == 0)
-           /* The file was uncompressed into memory, and now E describes the
-              uncompressed image rather than the actual file.  Check it again
-              for a valid magic number.  */
-           check (e);
-       }
-#endif
-#ifdef BZIP2
-      if (e->error == ENOEXEC)
-       {
-         /* See if it is a compressed image.  */
-         static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-         /* The bzip2 code is really cheesy, not even close to thread-safe.
-            So we serialize all uses of it.  */
-         pthread_mutex_lock (&lock);
-         e->error = 0;
-         check_bzip2 (e);
-         pthread_mutex_unlock (&lock);
-         if (e->error == 0)
-           /* The file was uncompressed into memory, and now E describes the
-              uncompressed image rather than the actual file.  Check it again
-              for a valid magic number.  */
-           check (e);
-       }
-#endif
     }
 
 
@@ -1466,20 +813,9 @@ do_exec (file_t file,
     /* The file is not a valid executable.  */
     goto out;
 
-#ifdef BFD
-  if (e.bfd)
-    {
-      e.info.bfd_locations = alloca (e.bfd->section_count *
-                                    sizeof (vm_offset_t));
-      bfd_map_over_sections (e.bfd, check_section, &e);
-    }
-  else
-#endif
-    {
-      const ElfW(Phdr) *phdr = e.info.elf.phdr;
-      e.info.elf.phdr = alloca (e.info.elf.phnum * sizeof (ElfW(Phdr)));
-      check_elf_phdr (&e, phdr);
-    }
+  const ElfW(Phdr) *phdr = e.info.elf.phdr;
+  e.info.elf.phdr = alloca (e.info.elf.phnum * sizeof (ElfW(Phdr)));
+  check_elf_phdr (&e, phdr);
 
   if (oldtask == MACH_PORT_NULL)
     flags |= EXEC_NEWTASK;
@@ -1685,33 +1021,11 @@ do_exec (file_t file,
         along with this executable.  Find the name of the file and open
         it.  */
 
-#ifdef BFD
-      char namebuf[e.bfd ? e.interp.section->_raw_size : 0];
-#endif
-      char *name;
-
-#ifdef BFD
-      if (e.bfd)
-       {
-         if (! bfd_get_section_contents (e.bfd, e.interp.section,
-                                         namebuf, 0,
-                                         e.interp.section->_raw_size))
-           {
-             e.error = b2he (errno);
-             name = NULL;
-           }
-         else
-           name = namebuf;
-       }
-      else
-#endif
-       {
-         name = map (&e, (e.interp.phdr->p_offset
-                          & ~(e.interp.phdr->p_align - 1)),
-                     e.interp.phdr->p_filesz);
-         if (! name && ! e.error)
-           e.error = ENOEXEC;
-       }
+      char *name = map (&e, (e.interp.phdr->p_offset
+                            & ~(e.interp.phdr->p_align - 1)),
+                       e.interp.phdr->p_filesz);
+      if (! name && ! e.error)
+       e.error = ENOEXEC;
 
       if (! name)
        e.interp.section = NULL;
@@ -1749,21 +1063,10 @@ do_exec (file_t file,
       prepare_and_check (interp.file, &interp);
       if (! interp.error)
        {
-#ifdef BFD
-         if (interp.bfd)
-           {
-             interp.info.bfd_locations = alloca (interp.bfd->section_count *
-                                                 sizeof (vm_offset_t));
-             bfd_map_over_sections (interp.bfd, check_section, &e);
-           }
-         else
-#endif
-           {
-             const ElfW(Phdr) *phdr = interp.info.elf.phdr;
-             interp.info.elf.phdr = alloca (interp.info.elf.phnum *
-                                            sizeof (ElfW(Phdr)));
-             check_elf_phdr (&interp, phdr);
-           }
+         const ElfW(Phdr) *phdr = interp.info.elf.phdr;
+         interp.info.elf.phdr = alloca (interp.info.elf.phnum *
+                                        sizeof (ElfW(Phdr)));
+         check_elf_phdr (&interp, phdr);
        }
       e.error = interp.error;
     }
@@ -1842,15 +1145,21 @@ do_exec (file_t file,
      the image so that a load-anywhere image gets the adjusted addresses.  */
     if (e.info.elf.phdr_addr != 0)
       {
-#ifdef BFD
-       if (!e.bfd)
-#endif
-         e.info.elf.phdr_addr += e.info.elf.loadbase;
+       e.info.elf.phdr_addr += e.info.elf.loadbase;
        boot->phdr_addr = e.info.elf.phdr_addr;
        boot->phdr_size = e.info.elf.phnum * sizeof (ElfW(Phdr));
       }
   boot->user_entry = e.entry;  /* already adjusted in `load' */
 
+  /* Set the start_code and end_code values for this process.
+     /hurd/exec is used to start /hurd/proc, so at this point there is
+     no proc server, so we need to be careful here.  */
+  if (boot->portarray[INIT_PORT_PROC] != MACH_PORT_NULL)
+    e.error = proc_set_code (boot->portarray[INIT_PORT_PROC],
+                            e.start_code, e.end_code);
+  if (e.error)
+    goto out;
+
   /* Create the initial thread.  */
   e.error = thread_create (newtask, &thread);
   if (e.error)
@@ -1864,17 +1173,12 @@ do_exec (file_t file,
                               &boot->stack_base, &boot->stack_size);
   if (e.error)
     goto out;
-#ifdef BFD
-  if (!e.bfd)
-#endif
-    {
-      /* It would probably be better to change mach_setup_thread so
-        it does a vm_map with the right permissions to start with.  */
-      if (!e.info.elf.execstack)
-       e.error = vm_protect (newtask, boot->stack_base, boot->stack_size,
-                             0, VM_PROT_READ | VM_PROT_WRITE);
-    }
 
+  /* It would probably be better to change mach_setup_thread so
+     it does a vm_map with the right permissions to start with.  */
+  if (!e.info.elf.execstack)
+    e.error = vm_protect (newtask, boot->stack_base, boot->stack_size,
+                         0, VM_PROT_READ | VM_PROT_WRITE);
 
   if (oldtask != newtask && oldtask != MACH_PORT_NULL)
     {
@@ -2071,87 +1375,6 @@ S_exec_exec (struct trivfs_protid *protid,
   if (! protid)
     return EOPNOTSUPP;
 
-#if 0
-  if (!(flags & EXEC_SECURE))
-    {
-      char *list = envz_get (envp, envplen, "EXECSERVERS");
-
-      if (list)
-       {
-         int tried = 0;
-         list = strdupa (list);
-         while ((p = strsep (&list, ":")))
-           {
-             /* Open the named file using the appropriate directory ports for
-                the user.  */
-             error_t user_port (int which, error_t (*operate) (mach_port_t))
-               {
-                 return (*operate) (nports > which
-                                    ? portarray[which] : MACH_PORT_NULL);
-               }
-             file_t user_fd (int fd)
-               {
-                 if (fd < 0 || fd >= dtablesize ||
-                     dtable[fd] == MACH_PORT_NULL)
-                   {
-                     errno = EBADF;
-                     return MACH_PORT_NULL;
-                   }
-                 return dtable[fd];
-               }
-             file_t server;
-             if (!hurd_file_name_lookup (user_port, user_fd, 0, p, 0,0, 
&server))
-               {
-                 error_t err;
-                 struct trivfs_protid *protid
-                   = ports_lookup_port (port_bucket, server,
-                                        trivfs_protid_portclasses[0]);
-                 if (protid)
-                   {
-                     err = do_exec (file, oldtask, 0,
-                                    argv, argvlen, argv_copy,
-                                    envp, envplen, envp_copy,
-                                    dtable, dtablesize, dtable_copy,
-                                    portarray, nports, portarray_copy,
-                                    intarray, nints, intarray_copy,
-                                    deallocnames, ndeallocnames,
-                                    destroynames, ndestroynames);
-                     ports_port_deref (protid);
-                   }
-                 else
-                   {
-                     int n;
-                     err = exec_exec (server,
-                                      file, MACH_MSG_TYPE_COPY_SEND,
-                                      oldtask, 0,
-                                      argv, argvlen,
-                                      envp, envplen,
-                                      dtable, MACH_MSG_TYPE_COPY_SEND,
-                                      dtablesize,
-                                      portarray, MACH_MSG_TYPE_COPY_SEND,
-                                      nports,
-                                      intarray, nints,
-                                      deallocnames, ndeallocnames,
-                                      destroynames, ndestroynames);
-                     mach_port_deallocate (mach_task_self (), file);
-                     for (n = 0; n < dtablesize; n++)
-                       mach_port_deallocate (mach_task_self (), dtable[n]);
-                     for (n = 0; n < nports; n++)
-                       mach_port_deallocate (mach_task_self (), portarray[n]);
-                   }
-                 mach_port_deallocate (mach_task_self (), server);
-                 if (err != ENOEXEC)
-                   return err;
-                 tried = 1;
-               }
-           }
-         if (tried)
-           /* At least one exec server got a crack at it and gave up.  */
-           return ENOEXEC;
-       }
-    }
-#endif
-
   /* There were no user-specified exec servers,
      or none of them could be found.  */
 
diff --git a/exec/main.c b/exec/main.c
index efad85a..d5d6882 100644
--- a/exec/main.c
+++ b/exec/main.c
@@ -25,6 +25,7 @@
 #include <hurd/startup.h>
 #include <argp.h>
 #include <version.h>
+#include <pids.h>
 
 const char *argp_program_version = STANDARD_HURD_VERSION (exec);
 
@@ -246,7 +247,7 @@ S_exec_init (struct trivfs_protid *protid,
 
   proc_register_version (procserver, host_priv, "exec", "", HURD_VERSION);
 
-  err = proc_getmsgport (procserver, 1, &startup);
+  err = proc_getmsgport (procserver, HURD_PID_STARTUP, &startup);
   assert_perror (err);
   mach_port_deallocate (mach_task_self (), procserver);
 
diff --git a/exec/priv.h b/exec/priv.h
index dbecb7a..85e03ae 100644
--- a/exec/priv.h
+++ b/exec/priv.h
@@ -28,10 +28,6 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 
02139, USA.  */
 #include <hurd/lookup.h>
 #include <pthread.h>
 
-#ifdef BFD
-#include <bfd.h>
-#endif
-
 #include <elf.h>
 #include <link.h>              /* This gives us the ElfW macro.  */
 #include <fcntl.h>
@@ -41,12 +37,6 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 
02139, USA.  */
 #ifndef exec_priv_h
 #define exec_priv_h
 
-#ifdef BFD
-/* A BFD whose architecture and machine type are those of the host system.  */
-extern bfd_arch_info_type host_bfd_arch_info;
-extern bfd host_bfd;
-#endif
-
 /* Information kept around to be given to a new task
    in response to a message on the task's bootstrap port.  */
 struct bootinfo
@@ -70,12 +60,7 @@ struct port_class *execboot_portclass;
 
 extern mach_port_t procserver; /* Our proc port.  */
 
-#ifdef BFD
-#define EXECDATA_STREAM                /* BFD uses stdio to access the 
executable.  */
-#else
 typedef void asection;
-#endif
-
 
 /* Data shared between check, check_section,
    load, load_section, and finish.  */
@@ -88,7 +73,9 @@ struct execdata
     vm_address_t entry;
     file_t file;
 
-#ifndef EXECDATA_STREAM
+    /* Set by load_section.  */
+    vm_address_t start_code;
+    vm_address_t end_code;
 
     /* Note that if `file_data' (below) is set, then these just point
        into that and should not be deallocated (file_data is malloc'd).  */
@@ -102,26 +89,6 @@ struct execdata
 #define map_filepos(e) ((e)->map_filepos)
 #define map_set_fsize(e, fsize) ((e)->map_fsize = (fsize))
 
-#else
-
-#ifdef _STDIO_USES_IOSTREAM
-# error implement me for libio!
-#else
-    FILE stream;
-#define map_buffer(e)  ((e)->stream.__buffer)
-#define map_fsize(e)   ((e)->stream.__get_limit - (e)->stream.__buffer)
-#define map_vsize(e)   ((e)->stream.__bufsize)
-#define map_filepos(e) ((e)->stream.__offset)
-#define map_set_fsize(e, fsize)        \
-  ((e)->stream.__get_limit = (e)->stream.__buffer + (fsize))
-#endif
-
-#endif
-
-#ifdef BFD
-    bfd *bfd;
-#endif
-
     union                      /* Interpreter section giving name of file.  */
       {
        asection *section;
@@ -138,10 +105,6 @@ struct execdata
 
     union
       {
-       /* Vector indexed by section index,
-          information passed from check_section to load_section.
-          Set by caller of check_section and load.  */
-       vm_offset_t *bfd_locations;
        struct
          {
            /* Program header table read from the executable.
diff --git a/ext2fs/balloc.c b/ext2fs/balloc.c
index b2d2eab..efef8ae 100644
--- a/ext2fs/balloc.c
+++ b/ext2fs/balloc.c
@@ -92,7 +92,7 @@ ext2_free_blocks (block_t block, unsigned long count)
                      block, count);
        }
       gdp = group_desc (block_group);
-      bh = bptr (gdp->bg_block_bitmap);
+      bh = disk_cache_block_ref (gdp->bg_block_bitmap);
 
       if (in_range (gdp->bg_block_bitmap, block, gcount) ||
          in_range (gdp->bg_inode_bitmap, block, gcount) ||
@@ -114,6 +114,7 @@ ext2_free_blocks (block_t block, unsigned long count)
        }
 
       record_global_poke (bh);
+      disk_cache_block_ref_ptr (gdp);
       record_global_poke (gdp);
 
       block += gcount;
@@ -139,7 +140,7 @@ ext2_new_block (block_t goal,
                block_t prealloc_goal,
                block_t *prealloc_count, block_t *prealloc_block)
 {
-  char *bh;
+  char *bh = NULL;
   char *p, *r;
   int i, j, k, tmp;
   unsigned long lmap;
@@ -165,6 +166,7 @@ ext2_new_block (block_t goal,
   ext2_debug ("goal=%u", goal);
 
 repeat:
+  assert (bh == NULL);
   /*
      * First, test whether the goal block is free.
    */
@@ -179,7 +181,7 @@ repeat:
       if (j)
        goal_attempts++;
 #endif
-      bh = bptr (gdp->bg_block_bitmap);
+      bh = disk_cache_block_ref (gdp->bg_block_bitmap);
 
       ext2_debug ("goal is at %d:%d", i, j);
 
@@ -245,6 +247,9 @@ repeat:
          j = k;
          goto got_block;
        }
+
+      disk_cache_block_deref (bh);
+      bh = NULL;
     }
 
   ext2_debug ("bit not found in block group %d", i);
@@ -267,7 +272,8 @@ repeat:
       pthread_spin_unlock (&global_lock);
       return 0;
     }
-  bh = bptr (gdp->bg_block_bitmap);
+  assert (bh == NULL);
+  bh = disk_cache_block_ref (gdp->bg_block_bitmap);
   r = memscan (bh, 0, sblock->s_blocks_per_group >> 3);
   j = (r - bh) << 3;
   if (j < sblock->s_blocks_per_group)
@@ -277,12 +283,15 @@ repeat:
                             sblock->s_blocks_per_group);
   if (j >= sblock->s_blocks_per_group)
     {
+      disk_cache_block_deref (bh);
+      bh = NULL;
       ext2_error ("free blocks count corrupted for block group %d", i);
       pthread_spin_unlock (&global_lock);
       return 0;
     }
 
 search_back:
+  assert (bh != NULL);
   /*
      * We have succeeded in finding a free byte in the block
      * bitmap.  Now search backwards up to 7 bits to find the
@@ -291,6 +300,7 @@ search_back:
   for (k = 0; k < 7 && j > 0 && !test_bit (j - 1, bh); k++, j--);
 
 got_block:
+  assert (bh != NULL);
 
   ext2_debug ("using block group %d (%d)", i, gdp->bg_free_blocks_count);
 
@@ -304,6 +314,8 @@ got_block:
   if (set_bit (j, bh))
     {
       ext2_warning ("bit already set for block %d", j);
+      disk_cache_block_deref (bh);
+      bh = NULL;
       goto repeat;
     }
 
@@ -351,6 +363,7 @@ got_block:
   j = tmp;
 
   record_global_poke (bh);
+  bh = NULL;
 
   if (j >= sblock->s_blocks_count)
     {
@@ -363,12 +376,14 @@ got_block:
              j, goal_hits, goal_attempts);
 
   gdp->bg_free_blocks_count--;
+  disk_cache_block_ref_ptr (gdp);
   record_global_poke (gdp);
 
   sblock->s_free_blocks_count--;
   sblock_dirty = 1;
 
  sync_out:
+  assert (bh == NULL);
   pthread_spin_unlock (&global_lock);
   alloc_sync (0);
 
@@ -390,9 +405,12 @@ ext2_count_free_blocks ()
   gdp = NULL;
   for (i = 0; i < groups_count; i++)
     {
+      void *bh;
       gdp = group_desc (i);
       desc_count += gdp->bg_free_blocks_count;
-      x = count_free (bptr (gdp->bg_block_bitmap), block_size);
+      bh = disk_cache_block_ref (gdp->bg_block_bitmap);
+      x = count_free (bh, block_size);
+      disk_cache_block_deref (bh);
       printf ("group %d: stored = %d, counted = %lu",
              i, gdp->bg_free_blocks_count, x);
       bitmap_count += x;
@@ -453,7 +471,7 @@ ext2_check_blocks_bitmap ()
 
       gdp = group_desc (i);
       desc_count += gdp->bg_free_blocks_count;
-      bh = bptr (gdp->bg_block_bitmap);
+      bh = disk_cache_block_ref (gdp->bg_block_bitmap);
 
       if (!EXT2_HAS_RO_COMPAT_FEATURE (sblock,
                                       EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
@@ -479,6 +497,7 @@ ext2_check_blocks_bitmap ()
          ext2_error ("block #%d of the inode table in group %d is marked 
free", j, i);
 
       x = count_free (bh, block_size);
+      disk_cache_block_deref (bh);
       if (gdp->bg_free_blocks_count != x)
        ext2_error ("wrong free blocks count for group %d,"
                    " stored = %d, counted = %lu",
diff --git a/ext2fs/ext2fs.c b/ext2fs/ext2fs.c
index 993f199..128b6ed 100644
--- a/ext2fs/ext2fs.c
+++ b/ext2fs/ext2fs.c
@@ -181,9 +181,9 @@ main (int argc, char **argv)
   /* Map the entire disk. */
   create_disk_pager ();
 
-  pokel_init (&global_pokel, diskfs_disk_pager, disk_image);
+  pokel_init (&global_pokel, diskfs_disk_pager, disk_cache);
 
-  get_hypermetadata();
+  map_hypermetadata ();
 
   inode_init ();
 
@@ -211,6 +211,8 @@ diskfs_reload_global_state ()
 {
   pokel_flush (&global_pokel);
   pager_flush (diskfs_disk_pager, 1);
+  sblock = NULL;
   get_hypermetadata ();
+  map_hypermetadata ();
   return 0;
 }
diff --git a/ext2fs/ext2fs.h b/ext2fs/ext2fs.h
index 52bf2b1..e01d1a5 100644
--- a/ext2fs/ext2fs.h
+++ b/ext2fs/ext2fs.h
@@ -23,7 +23,9 @@
 #include <hurd/pager.h>
 #include <hurd/fshelp.h>
 #include <hurd/iohelp.h>
+#include <hurd/store.h>
 #include <hurd/diskfs.h>
+#include <hurd/ihash.h>
 #include <assert.h>
 #include <pthread.h>
 #include <sys/mman.h>
@@ -195,6 +197,8 @@ struct user_pager_info
 /* ---------------------------------------------------------------- */
 /* pager.c */
 
+#define DISK_CACHE_BLOCKS      65536
+
 #include <hurd/diskfs-pager.h>
 
 /* Set up the disk pager.  */
@@ -218,10 +222,54 @@ extern struct store *store;
 /* What the user specified.  */
 extern struct store_parsed *store_parsed;
 
-/* Mapped image of the disk.  */
-extern void *disk_image;
+/* Mapped image of cached blocks of the disk.  */
+extern void *disk_cache;
+extern store_offset_t disk_cache_size;
+extern int disk_cache_blocks;
+
+#define DC_INCORE      0x01    /* Not in core.  */
+#define DC_UNTOUCHED   0x02    /* Not touched by disk_pager_read_paged
+                                  or disk_cache_block_ref.  */
+#define DC_FIXED       0x04    /* Must not be re-associated.  */
+
+/* Flags that forbid re-association of page.  DC_UNTOUCHED is included
+   because this flag is used only when page is already to be
+   re-associated, so it's not good candidate for another
+   remapping.  */
+#define DC_DONT_REUSE  (DC_INCORE | DC_UNTOUCHED | DC_FIXED)
+
+#define DC_NO_BLOCK    ((block_t) -1L)
+
+#ifndef NDEBUG
+#define DISK_CACHE_LAST_READ_XOR       0xDEADBEEF
+#endif
+
+/* Disk cache blocks' meta info.  */
+struct disk_cache_info
+{
+  block_t block;
+  uint16_t flags;
+  uint16_t ref_count;
+#ifndef NDEBUG
+  block_t last_read, last_read_xor;
+#endif
+};
 
-/* Our in-core copy of the super-block (pointer into the disk_image).  */
+/* block num --> pointer to in-memory block */
+extern hurd_ihash_t disk_cache_bptr;
+/* Metadata about cached block. */
+extern struct disk_cache_info *disk_cache_info;
+/* Lock for these mappings */
+extern pthread_mutex_t disk_cache_lock;
+/* Fired when a re-association is done.  */
+extern pthread_cond_t disk_cache_reassociation;
+
+void *disk_cache_block_ref (block_t block);
+void disk_cache_block_ref_ptr (void *ptr);
+void disk_cache_block_deref (void *ptr);
+int disk_cache_block_is_ref (block_t block);
+
+/* Our in-core copy of the super-block (pointer into the disk_cache).  */
 struct ext2_super_block *sblock;
 /* True if sblock has been modified.  */
 int sblock_dirty;
@@ -251,6 +299,9 @@ vm_address_t zeroblock;
 
 /* Get the superblock from the disk, & setup various global info from it.  */
 void get_hypermetadata ();
+
+/* Map `sblock' and `group_desc_image' pointers to disk cache.  */
+void map_hypermetadata ();
 
 /* ---------------------------------------------------------------- */
 /* Random stuff calculated from the super block.  */
@@ -274,21 +325,51 @@ pthread_spinlock_t generation_lock;
 unsigned long next_generation;
 
 /* ---------------------------------------------------------------- */
-/* Functions for looking inside disk_image */
+/* Functions for looking inside disk_cache */
 
-#define trunc_block(offs) (((offs) >> log2_block_size) << log2_block_size)
+#define trunc_block(offs) \
+  ((off_t) ((offs) >> log2_block_size) << log2_block_size)
 #define round_block(offs) \
-  ((((offs) + block_size - 1) >> log2_block_size) << log2_block_size)
+  ((off_t) (((offs) + block_size - 1) >> log2_block_size) << log2_block_size)
 
 /* block num --> byte offset on disk */
-#define boffs(block) ((block) << log2_block_size)
+#define boffs(block) ((off_t) (block) << log2_block_size)
 /* byte offset on disk --> block num */
 #define boffs_block(offs) ((offs) >> log2_block_size)
 
+/* pointer to in-memory block -> index in disk_cache_info */
+#define bptr_index(ptr) (((char *)ptr - (char *)disk_cache) >> log2_block_size)
+
 /* byte offset on disk --> pointer to in-memory block */
-#define boffs_ptr(offs) (((char *)disk_image) + (offs))
+EXT2FS_EI char *
+boffs_ptr (off_t offset)
+{
+  block_t block = boffs_block (offset);
+  pthread_mutex_lock (&disk_cache_lock);
+  char *ptr = hurd_ihash_find (disk_cache_bptr, block);
+  pthread_mutex_unlock (&disk_cache_lock);
+  assert (ptr);
+  ptr += offset % block_size;
+  ext2_debug ("(%lld) = %p", offset, ptr);
+  return ptr;
+}
+
 /* pointer to in-memory block --> byte offset on disk */
-#define bptr_offs(ptr) ((char *)(ptr) - ((char *)disk_image))
+EXT2FS_EI off_t
+bptr_offs (void *ptr)
+{
+  vm_offset_t mem_offset = (char *)ptr - (char *)disk_cache;
+  off_t offset;
+  assert (mem_offset < disk_cache_size);
+  pthread_mutex_lock (&disk_cache_lock);
+  offset = (off_t) disk_cache_info[boffs_block (mem_offset)].block
+    << log2_block_size;
+  assert (offset || mem_offset < block_size);
+  offset += mem_offset % block_size;
+  pthread_mutex_unlock (&disk_cache_lock);
+  ext2_debug ("(%p) = %lld", ptr, offset);
+  return offset;
+}
 
 /* block num --> pointer to in-memory block */
 #define bptr(block) boffs_ptr(boffs(block))
@@ -308,14 +389,24 @@ extern struct ext2_inode *dino (ino_t inum);
 #if defined(__USE_EXTERN_INLINES) || defined(EXT2FS_DEFINE_EI)
 /* Convert an inode number to the dinode on disk. */
 EXT2FS_EI struct ext2_inode *
-dino (ino_t inum)
+dino_ref (ino_t inum)
 {
   unsigned long inodes_per_group = sblock->s_inodes_per_group;
   unsigned long bg_num = (inum - 1) / inodes_per_group;
   unsigned long group_inum = (inum - 1) % inodes_per_group;
-  struct ext2_group_desc *bg = group_desc(bg_num);
+  struct ext2_group_desc *bg = group_desc (bg_num);
   block_t block = bg->bg_inode_table + (group_inum / inodes_per_block);
-  return ((struct ext2_inode *)bptr(block)) + group_inum % inodes_per_block;
+  struct ext2_inode *inode = disk_cache_block_ref (block);
+  inode += group_inum % inodes_per_block;
+  ext2_debug ("(%llu) = %p", inum, inode);
+  return inode;
+}
+
+EXT2FS_EI void
+dino_deref (struct ext2_inode *inode)
+{
+  ext2_debug ("(%p)", inode);
+  disk_cache_block_deref (inode);
 }
 #endif /* Use extern inlines.  */
 
@@ -377,27 +468,38 @@ global_block_modified (block_t block)
 EXT2FS_EI void
 record_global_poke (void *ptr)
 {
-  int boffs = trunc_block (bptr_offs (ptr));
-  global_block_modified (boffs_block (boffs));
-  pokel_add (&global_pokel, boffs_ptr(boffs), block_size);
+  block_t block = boffs_block (bptr_offs (ptr));
+  void *block_ptr = bptr (block);
+  ext2_debug ("(%p = %p)", ptr, block_ptr);
+  assert (disk_cache_block_is_ref (block));
+  global_block_modified (block);
+  pokel_add (&global_pokel, block_ptr, block_size);
 }
 
 /* This syncs a modification to a non-file block.  */
 EXT2FS_EI void
 sync_global_ptr (void *bptr, int wait)
 {
-  vm_offset_t boffs = trunc_block (bptr_offs (bptr));
-  global_block_modified (boffs_block (boffs));
-  pager_sync_some (diskfs_disk_pager, trunc_page (boffs), vm_page_size, wait);
+  block_t block = boffs_block (bptr_offs (bptr));
+  void *block_ptr = bptr (block);
+  ext2_debug ("(%p -> %u)", bptr, block);
+  global_block_modified (block);
+  disk_cache_block_deref (block_ptr);
+  pager_sync_some (diskfs_disk_pager,
+                  block_ptr - disk_cache, block_size, wait);
+
 }
 
 /* This records a modification to one of a file's indirect blocks.  */
 EXT2FS_EI void
 record_indir_poke (struct node *node, void *ptr)
 {
-  int boffs = trunc_block (bptr_offs (ptr));
-  global_block_modified (boffs_block (boffs));
-  pokel_add (&node->dn->indir_pokel, boffs_ptr(boffs), block_size);
+  block_t block = boffs_block (bptr_offs (ptr));
+  void *block_ptr = bptr (block);
+  ext2_debug ("(%llu, %p)", node->cache_id, ptr);
+  assert (disk_cache_block_is_ref (block));
+  global_block_modified (block);
+  pokel_add (&node->dn->indir_pokel, block_ptr, block_size);
 }
 
 /* ---------------------------------------------------------------- */
@@ -405,6 +507,7 @@ record_indir_poke (struct node *node, void *ptr)
 EXT2FS_EI void
 sync_global (int wait)
 {
+  ext2_debug ("%d", wait);
   pokel_sync (&global_pokel, wait);
 }
 
diff --git a/ext2fs/getblk.c b/ext2fs/getblk.c
index 23ba645..bde66e1 100644
--- a/ext2fs/getblk.c
+++ b/ext2fs/getblk.c
@@ -104,7 +104,7 @@ ext2_alloc_block (struct node *node, block_t goal, int zero)
 
   if (result && zero)
     {
-      char *bh = bptr (result);
+      char *bh = disk_cache_block_ref (result);
       bzero (bh, block_size);
       record_indir_poke (node, bh);
     }
@@ -122,6 +122,8 @@ inode_getblk (struct node *node, int nr, int create, int 
zero,
   block_t hint;
 #endif
 
+  assert (0 <= nr && nr < EXT2_N_BLOCKS);
+
   *result = node->dn->info.i_data[nr];
   if (*result)
     return 0;
@@ -180,14 +182,20 @@ block_getblk (struct node *node, block_t block, int nr, 
int create, int zero,
 {
   int i;
   block_t goal = 0;
-  block_t *bh = (block_t *)bptr (block);
+  block_t *bh = (block_t *)disk_cache_block_ref (block);
 
   *result = bh[nr];
   if (*result)
-    return 0;
+    {
+      disk_cache_block_deref (bh);
+      return 0;
+    }
 
   if (!create)
-    return EINVAL;
+    {
+      disk_cache_block_deref (bh);
+      return EINVAL;
+    }
 
   if (node->dn->info.i_next_alloc_block == new_block)
     goal = node->dn->info.i_next_alloc_goal;
@@ -207,7 +215,10 @@ block_getblk (struct node *node, block_t block, int nr, 
int create, int zero,
 
   *result = ext2_alloc_block (node, goal, zero);
   if (!*result)
-    return ENOSPC;
+    {
+      disk_cache_block_deref (bh);
+      return ENOSPC;
+    }
 
   bh[nr] = *result;
 
diff --git a/ext2fs/hyper.c b/ext2fs/hyper.c
index bee4175..5bcc2ab 100644
--- a/ext2fs/hyper.c
+++ b/ext2fs/hyper.c
@@ -58,11 +58,14 @@ static int ext2fs_clean;    /* fs clean before we started 
writing? */
 void
 get_hypermetadata (void)
 {
-  error_t err = diskfs_catch_exception ();
-  if (err)
-    ext2_panic ("can't read superblock: %s", strerror (err));
+  error_t err;
+  size_t read = 0;
 
-  sblock = (struct ext2_super_block *) boffs_ptr (SBLOCK_OFFS);
+  assert (! sblock);
+  err = store_read (store, SBLOCK_OFFS >> store->log2_block_size,
+                   SBLOCK_SIZE, (void **)&sblock, &read);
+  if (err || read != SBLOCK_SIZE)
+    ext2_panic ("Cannot read hypermetadata");
 
   if (sblock->s_magic != EXT2_SUPER_MAGIC
 #ifdef EXT2FS_PRE_02B_COMPAT
@@ -152,15 +155,25 @@ get_hypermetadata (void)
 
   allocate_mod_map ();
 
-  diskfs_end_catch_exception ();
+  /* A handy source of page-aligned zeros.  */
+  if (zeroblock == 0)
+    {
+      zeroblock = (vm_address_t) mmap (0, block_size, PROT_READ, MAP_ANON, 0, 
0);
+      assert (zeroblock != (vm_address_t) MAP_FAILED);
+    }
+
+  munmap (sblock, SBLOCK_SIZE);
+  sblock = NULL;
+}
+
+void
+map_hypermetadata (void)
+{
+  sblock = (struct ext2_super_block *) boffs_ptr (SBLOCK_OFFS);
 
   /* Cache a convenient pointer to the block group descriptors for allocation.
      These are stored in the filesystem blocks following the superblock.  */
   group_desc_image = (struct ext2_group_desc *) bptr (bptr_block (sblock) + 1);
-
-  /* A handy source of page-aligned zeros.  */
-  if (zeroblock == 0)
-    zeroblock = (vm_address_t) mmap (0, block_size, PROT_READ, MAP_ANON, 0, 0);
 }
 
 error_t
@@ -183,6 +196,7 @@ diskfs_set_hypermetadata (int wait, int clean)
  if (sblock_dirty)
    {
      sblock_dirty = 0;
+     disk_cache_block_ref_ptr (sblock);
      record_global_poke (sblock);
    }
 
@@ -199,7 +213,8 @@ diskfs_readonly_changed (int readonly)
 
   (*(readonly ? store_set_flags : store_clear_flags)) (store, STORE_READONLY);
 
-  mprotect (disk_image, store->size, PROT_READ | (readonly ? 0 : PROT_WRITE));
+  mprotect (disk_cache, disk_cache_size,
+           PROT_READ | (readonly ? 0 : PROT_WRITE));
 
   if (!readonly && !(sblock->s_state & EXT2_VALID_FS))
     ext2_warning ("UNCLEANED FILESYSTEM NOW WRITABLE");
diff --git a/ext2fs/ialloc.c b/ext2fs/ialloc.c
index aa018d9..2d8e51e 100644
--- a/ext2fs/ialloc.c
+++ b/ext2fs/ialloc.c
@@ -75,22 +75,25 @@ diskfs_free_node (struct node *np, mode_t old_mode)
   bit = (inum - 1) % sblock->s_inodes_per_group;
 
   gdp = group_desc (block_group);
-  bh = bptr (gdp->bg_inode_bitmap);
+  bh = disk_cache_block_ref (gdp->bg_inode_bitmap);
 
   if (!clear_bit (bit, bh))
     ext2_warning ("bit already cleared for inode %Ld", inum);
   else
     {
+      disk_cache_block_ref_ptr (bh);
       record_global_poke (bh);
 
       gdp->bg_free_inodes_count++;
       if (S_ISDIR (old_mode))
        gdp->bg_used_dirs_count--;
+      disk_cache_block_ref_ptr (gdp);
       record_global_poke (gdp);
 
       sblock->s_free_inodes_count++;
     }
 
+  disk_cache_block_deref (bh);
   sblock_dirty = 1;
   pthread_spin_unlock (&global_lock);
   alloc_sync(0);
@@ -111,7 +114,7 @@ diskfs_free_node (struct node *np, mode_t old_mode)
 ino_t
 ext2_alloc_inode (ino_t dir_inum, mode_t mode)
 {
-  char *bh;
+  char *bh = NULL;
   int i, j, inum, avefreei;
   struct ext2_group_desc *gdp;
   struct ext2_group_desc *tmp;
@@ -119,6 +122,7 @@ ext2_alloc_inode (ino_t dir_inum, mode_t mode)
   pthread_spin_lock (&global_lock);
 
 repeat:
+  assert (bh == NULL);
   gdp = NULL;
   i = 0;
 
@@ -213,7 +217,7 @@ repeat:
       return 0;
     }
 
-  bh = bptr (gdp->bg_inode_bitmap);
+  bh = disk_cache_block_ref (gdp->bg_inode_bitmap);
   if ((inum =
        find_first_zero_bit ((unsigned long *) bh, sblock->s_inodes_per_group))
       < sblock->s_inodes_per_group)
@@ -221,12 +225,17 @@ repeat:
       if (set_bit (inum, bh))
        {
          ext2_warning ("bit already set for inode %d", inum);
+         disk_cache_block_deref (bh);
+         bh = NULL;
          goto repeat;
        }
       record_global_poke (bh);
+      bh = NULL;
     }
   else
     {
+      disk_cache_block_deref (bh);
+      bh = NULL;
       if (gdp->bg_free_inodes_count != 0)
        {
          ext2_error ("free inodes count corrupted in group %d", i);
@@ -248,15 +257,25 @@ repeat:
   gdp->bg_free_inodes_count--;
   if (S_ISDIR (mode))
     gdp->bg_used_dirs_count++;
+  disk_cache_block_ref_ptr (gdp);
   record_global_poke (gdp);
 
   sblock->s_free_inodes_count--;
   sblock_dirty = 1;
 
  sync_out:
+  assert (bh == NULL);
   pthread_spin_unlock (&global_lock);
   alloc_sync (0);
 
+  /* Make sure the coming read_node won't complain about bad
+     fields.  */
+  {
+    struct ext2_inode *di = dino_ref (inum);
+    memset (di, 0, sizeof *di);
+    dino_deref (di);
+  }
+
   return inum;
 }
 
@@ -353,10 +372,12 @@ ext2_count_free_inodes ()
   gdp = NULL;
   for (i = 0; i < groups_count; i++)
     {
+      void *bh;
       gdp = group_desc (i);
       desc_count += gdp->bg_free_inodes_count;
-      x = count_free (bptr (gdp->bg_inode_bitmap),
-                     sblock->s_inodes_per_group / 8);
+      bh = disk_cache_block_ref (gdp->bg_inode_bitmap);
+      x = count_free (bh, sblock->s_inodes_per_group / 8);
+      disk_cache_block_deref (bh);
       ext2_debug ("group %d: stored = %d, counted = %lu",
                  i, gdp->bg_free_inodes_count, x);
       bitmap_count += x;
@@ -386,10 +407,12 @@ ext2_check_inodes_bitmap ()
   gdp = NULL;
   for (i = 0; i < groups_count; i++)
     {
+      void *bh;
       gdp = group_desc (i);
       desc_count += gdp->bg_free_inodes_count;
-      x = count_free (bptr (gdp->bg_inode_bitmap),
-                     sblock->s_inodes_per_group / 8);
+      bh = disk_cache_block_ref (gdp->bg_inode_bitmap);
+      x = count_free (bh, sblock->s_inodes_per_group / 8);
+      disk_cache_block_deref (bh);
       if (gdp->bg_free_inodes_count != x)
        ext2_error ("wrong free inodes count in group %d, "
                    "stored = %d, counted = %lu",
diff --git a/ext2fs/inode.c b/ext2fs/inode.c
index 2c44279..e75c63f 100644
--- a/ext2fs/inode.c
+++ b/ext2fs/inode.c
@@ -92,7 +92,7 @@ diskfs_cached_lookup (ino_t inum, struct node **npp)
   dn->dir_idx = 0;
   dn->pager = 0;
   pthread_rwlock_init (&dn->alloc_lock, NULL);
-  pokel_init (&dn->indir_pokel, diskfs_disk_pager, disk_image);
+  pokel_init (&dn->indir_pokel, diskfs_disk_pager, disk_cache);
 
   /* Create the new node.  */
   np = diskfs_make_node (dn);
@@ -201,13 +201,17 @@ read_node (struct node *np)
   error_t err;
   struct stat *st = &np->dn_stat;
   struct disknode *dn = np->dn;
-  struct ext2_inode *di = dino (np->cache_id);
+  struct ext2_inode *di;
   struct ext2_inode_info *info = &dn->info;
 
+  ext2_debug ("(%llu)", np->cache_id);
+
   err = diskfs_catch_exception ();
   if (err)
     return err;
 
+  di = dino_ref (np->cache_id);
+
   st->st_fstype = FSTYPE_EXT2FS;
   st->st_fsid = getpid ();     /* This call is very cheap.  */
   st->st_ino = np->cache_id;
@@ -285,7 +289,9 @@ read_node (struct node *np)
       info->i_high_size = di->i_size_high;
       if (info->i_high_size)   /* XXX */
        {
+         dino_deref (di);
          ext2_warning ("cannot handle large file inode %Ld", np->cache_id);
+         diskfs_end_catch_exception ();
          return EFBIG;
        }
     }
@@ -307,6 +313,7 @@ read_node (struct node *np)
     }
   dn->info_i_translator = di->i_translator;
 
+  dino_deref (di);
   diskfs_end_catch_exception ();
 
   if (S_ISREG (st->st_mode) || S_ISDIR (st->st_mode)
@@ -408,7 +415,9 @@ write_node (struct node *np)
 {
   error_t err;
   struct stat *st = &np->dn_stat;
-  struct ext2_inode *di = dino (np->cache_id);
+  struct ext2_inode *di;
+
+  ext2_debug ("(%llu)", np->cache_id);
 
   if (np->dn->info.i_prealloc_count)
     ext2_discard_prealloc (np);
@@ -425,6 +434,8 @@ write_node (struct node *np)
       if (err)
        return NULL;
 
+      di = dino_ref (np->cache_id);
+
       di->i_generation = st->st_gen;
 
       /* We happen to know that the stat mode bits are the same
@@ -505,6 +516,7 @@ write_node (struct node *np)
       diskfs_end_catch_exception ();
       np->dn_stat_dirty = 0;
 
+      /* Leave invoking dino_deref (di) to the caller.  */
       return di;
     }
   else
@@ -674,7 +686,7 @@ diskfs_set_translator (struct node *np, const char *name, 
unsigned namelen,
   if (err)
     return err;
 
-  di = dino (np->cache_id);
+  di = dino_ref (np->cache_id);
   blkno = di->i_translator;
 
   if (namelen && !blkno)
@@ -687,6 +699,7 @@ diskfs_set_translator (struct node *np, const char *name, 
unsigned namelen,
                        0, 0, 0);
       if (blkno == 0)
        {
+         dino_deref (di);
          diskfs_end_catch_exception ();
          return ENOSPC;
        }
@@ -710,15 +723,20 @@ diskfs_set_translator (struct node *np, const char *name, 
unsigned namelen,
       np->dn_stat.st_mode &= ~S_IPTRANS;
       np->dn_set_ctime = 1;
     }
+  else
+    dino_deref (di);
 
   if (namelen)
     {
+      void *blkptr;
+
       buf[0] = namelen & 0xFF;
       buf[1] = (namelen >> 8) & 0xFF;
       bcopy (name, buf + 2, namelen);
 
-      bcopy (buf, bptr (blkno), block_size);
-      record_global_poke (bptr (blkno));
+      blkptr = disk_cache_block_ref (blkno);
+      memcpy (blkptr, buf, block_size);
+      record_global_poke (blkptr);
 
       np->dn_stat.st_mode |= S_IPTRANS;
       np->dn_set_ctime = 1;
@@ -736,7 +754,8 @@ diskfs_get_translator (struct node *np, char **namep, 
unsigned *namelen)
   error_t err = 0;
   daddr_t blkno;
   unsigned datalen;
-  const void *transloc;
+  void *transloc;
+  struct ext2_inode *di;
 
   assert (sblock->s_creator_os == EXT2_OS_HURD);
 
@@ -744,9 +763,11 @@ diskfs_get_translator (struct node *np, char **namep, 
unsigned *namelen)
   if (err)
     return err;
 
-  blkno = (dino (np->cache_id))->i_translator;
+  di = dino_ref (np->cache_id);
+  blkno = di->i_translator;
+  dino_deref (di);
   assert (blkno);
-  transloc = bptr (blkno);
+  transloc = disk_cache_block_ref (blkno);
 
   datalen =
     ((unsigned char *)transloc)[0] + (((unsigned char *)transloc)[1] << 8);
@@ -761,6 +782,7 @@ diskfs_get_translator (struct node *np, char **namep, 
unsigned *namelen)
        memcpy (*namep, transloc + 2, datalen);
     }
 
+  disk_cache_block_deref (transloc);
   diskfs_end_catch_exception ();
 
   *namelen = datalen;
diff --git a/ext2fs/pager.c b/ext2fs/pager.c
index f740434..6e99c83 100644
--- a/ext2fs/pager.c
+++ b/ext2fs/pager.c
@@ -18,17 +18,18 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
 
+#include <unistd.h>
 #include <string.h>
 #include <errno.h>
 #include <hurd/store.h>
 #include "ext2fs.h"
 
+/* XXX */
+#include "../libpager/priv.h"
+
 /* A ports bucket to hold pager ports.  */
 struct port_bucket *pager_bucket;
 
-/* Mapped image of the disk.  */
-void *disk_image;
-
 pthread_spinlock_t node_to_page_lock = PTHREAD_SPINLOCK_INITIALIZER;
 
 
@@ -165,6 +166,9 @@ file_pager_read_page (struct node *node, vm_offset_t page,
   block_t pending_blocks = 0;
   int num_pending_blocks = 0;
 
+  ext2_debug ("reading inode %llu page %lu[%u]",
+             node->cache_id, page, vm_page_size);
+
   /* Read the NUM_PENDING_BLOCKS blocks in PENDING_BLOCKS, into the buffer
      pointed to by BUF (allocating it if necessary) at offset OFFS.  OFFS in
      adjusted by the amount read, and NUM_PENDING_BLOCKS is zeroed.  Any read
@@ -173,7 +177,8 @@ file_pager_read_page (struct node *node, vm_offset_t page,
     {
       if (num_pending_blocks > 0)
        {
-         block_t dev_block = pending_blocks << log2_dev_blocks_per_fs_block;
+         store_offset_t dev_block = (store_offset_t) pending_blocks
+           << log2_dev_blocks_per_fs_block;
          size_t amount = num_pending_blocks << log2_block_size;
          /* The buffer we try to read into; on the first read, we pass in a
             size of zero, so that the read is guaranteed to allocate a new
@@ -297,7 +302,8 @@ pending_blocks_write (struct pending_blocks *pb)
   if (pb->num > 0)
     {
       error_t err;
-      block_t dev_block = pb->block << log2_dev_blocks_per_fs_block;
+      store_offset_t dev_block = (store_offset_t) pb->block
+       << log2_dev_blocks_per_fs_block;
       size_t length = pb->num << log2_block_size, amount;
 
       ext2_debug ("writing block %u[%ld]", pb->block, pb->num);
@@ -359,7 +365,7 @@ pending_blocks_add (struct pending_blocks *pb, block_t 
block)
   return 0;
 }
 
-/* Write one page for the pager backing NODE, at offset PAGE, into BUF.  This
+/* Write one page for the pager backing NODE, at OFFSET, into BUF.  This
    may need to write several filesystem blocks to satisfy one page, and tries
    to consolidate the i/o if possible.  */
 static error_t
@@ -411,12 +417,28 @@ disk_pager_read_page (vm_offset_t page, void **buf, int 
*writelock)
 {
   error_t err;
   size_t length = vm_page_size, read = 0;
-  vm_size_t dev_end = store->size;
+  store_offset_t offset = page, dev_end = store->size;
+  int index = offset >> log2_block_size;
+
+  pthread_mutex_lock (&disk_cache_lock);
+  offset = ((store_offset_t) disk_cache_info[index].block << log2_block_size)
+    + offset % block_size;
+  disk_cache_info[index].flags |= DC_INCORE;
+  disk_cache_info[index].flags &=~ DC_UNTOUCHED;
+#ifndef NDEBUG
+  disk_cache_info[index].last_read = disk_cache_info[index].block;
+  disk_cache_info[index].last_read_xor
+    = disk_cache_info[index].block ^ DISK_CACHE_LAST_READ_XOR;
+#endif
+  pthread_mutex_unlock (&disk_cache_lock);
+
+  ext2_debug ("(%lld)", offset >> log2_block_size);
 
-  if (page + vm_page_size > dev_end)
-    length = dev_end - page;
+  if (offset + vm_page_size > dev_end)
+    length = dev_end - offset;
 
-  err = store_read (store, page >> store->log2_block_size, length, buf, &read);
+  err = store_read (store, offset >> store->log2_block_size, length,
+                   buf, &read);
   if (read != length)
     return EIO;
   if (!err && length != vm_page_size)
@@ -432,26 +454,38 @@ disk_pager_write_page (vm_offset_t page, void *buf)
 {
   error_t err = 0;
   size_t length = vm_page_size, amount;
-  vm_size_t dev_end = store->size;
+  store_offset_t offset = page, dev_end = store->size;
+  int index = offset >> log2_block_size;
+
+  pthread_mutex_lock (&disk_cache_lock);
+  assert (disk_cache_info[index].block != DC_NO_BLOCK);
+  offset = ((store_offset_t) disk_cache_info[index].block << log2_block_size)
+    + offset % block_size;
+#ifndef NDEBUG                 /* Not strictly needed.  */
+  assert ((disk_cache_info[index].last_read ^ DISK_CACHE_LAST_READ_XOR)
+         == disk_cache_info[index].last_read_xor);
+  assert (disk_cache_info[index].last_read
+         == disk_cache_info[index].block);
+#endif
+  pthread_mutex_unlock (&disk_cache_lock);
 
-  if (page + vm_page_size > dev_end)
-    length = dev_end - page;
+  if (offset + vm_page_size > dev_end)
+    length = dev_end - offset;
 
-  ext2_debug ("writing disk page %d[%d]", page, length);
+  ext2_debug ("writing disk page %lld[%zu]", offset, length);
 
   STAT_INC (disk_pageouts);
 
   if (modified_global_blocks)
     /* Be picky about which blocks in a page that we write.  */
     {
-      vm_offset_t offs = page;
       struct pending_blocks pb;
 
       pending_blocks_init (&pb, buf);
 
       while (length > 0 && !err)
        {
-         block_t block = boffs_block (offs);
+         block_t block = boffs_block (offset);
 
          /* We don't clear the block modified bit here because this paging
             write request may not be the same one that actually set the bit,
@@ -469,7 +503,7 @@ disk_pager_write_page (vm_offset_t page, void *buf)
            /* Otherwise just skip it.  */
            err = pending_blocks_skip (&pb);
 
-         offs += block_size;
+         offset += block_size;
          length -= block_size;
        }
 
@@ -478,7 +512,7 @@ disk_pager_write_page (vm_offset_t page, void *buf)
     }
   else
     {
-      err = store_write (store, page >> store->log2_block_size,
+      err = store_write (store, offset >> store->log2_block_size,
                         buf, length, &amount);
       if (!err && length != amount)
        err = EIO;
@@ -486,6 +520,18 @@ disk_pager_write_page (vm_offset_t page, void *buf)
 
   return err;
 }
+
+static void
+disk_pager_notify_evict (vm_offset_t page)
+{
+  unsigned long index = page >> log2_block_size;
+
+  ext2_debug ("(block %lu)", index);
+
+  pthread_mutex_lock (&disk_cache_lock);
+  disk_cache_info[index].flags &= ~DC_INCORE;
+  pthread_mutex_unlock (&disk_cache_lock);
+}
 
 /* Satisfy a pager read request for either the disk pager or file pager
    PAGER, to the page at offset PAGE into BUF.  WRITELOCK should be set if
@@ -511,6 +557,14 @@ pager_write_page (struct user_pager_info *pager, 
vm_offset_t page,
   else
     return file_pager_write_page (pager->node, page, (void *)buf);
 }
+
+void
+pager_notify_evict (struct user_pager_info *pager, vm_offset_t page)
+{
+  if (pager->type == DISK)
+    disk_pager_notify_evict (page);
+}
+
 
 /* Make page PAGE writable, at least up to ALLOCSIZE.  This function and
    diskfs_grow are the only places that blocks are actually added to the
@@ -767,6 +821,373 @@ pager_dropweak (struct user_pager_info *p __attribute__ 
((unused)))
 {
 }
 
+/* Cached blocks from disk.  */
+void *disk_cache;
+
+/* DISK_CACHE size in bytes and blocks.  */
+store_offset_t disk_cache_size;
+int disk_cache_blocks;
+
+/* block num --> pointer to in-memory block */
+hurd_ihash_t disk_cache_bptr;
+/* Cached blocks' info.  */
+struct disk_cache_info *disk_cache_info;
+/* Hint index for which cache block to reuse next.  */
+int disk_cache_hint;
+/* Lock for these structures.  */
+pthread_mutex_t disk_cache_lock;
+/* Fired when a re-association is done.  */
+pthread_cond_t disk_cache_reassociation;
+
+/* Finish mapping initialization. */
+static void
+disk_cache_init (void)
+{
+  if (block_size != vm_page_size)
+    ext2_panic ("Block size %u != vm_page_size %u",
+               block_size, vm_page_size);
+
+  pthread_mutex_init (&disk_cache_lock, NULL);
+  pthread_cond_init (&disk_cache_reassociation, NULL);
+
+  /* Allocate space for block num -> in-memory pointer mapping.  */
+  if (hurd_ihash_create (&disk_cache_bptr, HURD_IHASH_NO_LOCP))
+    ext2_panic ("Can't allocate memory for disk_pager_bptr");
+
+  /* Allocate space for disk cache blocks' info.  */
+  disk_cache_info = malloc ((sizeof *disk_cache_info) * disk_cache_blocks);
+  if (!disk_cache_info)
+    ext2_panic ("Cannot allocate space for disk cache info");
+
+  /* Initialize disk_cache_info.  */
+  for (int i = 0; i < disk_cache_blocks; i++)
+    {
+      disk_cache_info[i].block = DC_NO_BLOCK;
+      disk_cache_info[i].flags = 0;
+      disk_cache_info[i].ref_count = 0;
+#ifndef NDEBUG
+      disk_cache_info[i].last_read = DC_NO_BLOCK;
+      disk_cache_info[i].last_read_xor
+       = DC_NO_BLOCK ^ DISK_CACHE_LAST_READ_XOR;
+#endif
+    }
+  disk_cache_hint = 0;
+
+  /* Map the superblock and the block group descriptors.  */
+  block_t fixed_first = boffs_block (SBLOCK_OFFS);
+  block_t fixed_last = fixed_first
+    + (round_block ((sizeof *group_desc_image) * groups_count)
+       >> log2_block_size);
+  ext2_debug ("%u-%u\n", fixed_first, fixed_last);
+  assert (fixed_last - fixed_first + 1 <= (block_t)disk_cache_blocks + 3);
+  for (block_t i = fixed_first; i <= fixed_last; i++)
+    {
+      disk_cache_block_ref (i);
+      assert (disk_cache_info[i-fixed_first].block == i);
+      disk_cache_info[i-fixed_first].flags |= DC_FIXED;
+    }
+}
+
+static void
+disk_cache_return_unused (void)
+{
+  int index;
+
+  /* XXX: Touch all pages.  It seems that sometimes GNU Mach "forgets"
+     to notify us about evicted pages.  Disk cache must be
+     unlocked.  */
+  for (vm_offset_t i = 0; i < disk_cache_size; i += vm_page_size)
+    *(volatile char *)(disk_cache + i);
+
+  /* Release some references to cached blocks.  */
+  pokel_sync (&global_pokel, 1);
+
+  /* Return unused pages that are in core.  */
+  int pending_begin = -1, pending_end = -1;
+  pthread_mutex_lock (&disk_cache_lock);
+  for (index = 0; index < disk_cache_blocks; index++)
+    if (! (disk_cache_info[index].flags & (DC_DONT_REUSE & ~DC_INCORE))
+       && ! disk_cache_info[index].ref_count)
+      {
+       ext2_debug ("return %u -> %d",
+                   disk_cache_info[index].block, index);
+       if (index != pending_end)
+         {
+           /* Return previous region, if there is such, ... */
+           if (pending_end >= 0)
+             {
+               pthread_mutex_unlock (&disk_cache_lock);
+               pager_return_some (diskfs_disk_pager,
+                                  pending_begin * vm_page_size,
+                                  (pending_end - pending_begin)
+                                  * vm_page_size, 1);
+               pthread_mutex_lock (&disk_cache_lock);
+             }
+           /* ... and start new region.  */
+           pending_begin = index;
+         }
+       pending_end = index + 1;
+      }
+
+  pthread_mutex_unlock (&disk_cache_lock);
+
+  /* Return last region, if there is such.   */
+  if (pending_end >= 0)
+    pager_return_some (diskfs_disk_pager,
+                      pending_begin * vm_page_size,
+                      (pending_end - pending_begin) * vm_page_size,
+                      1);
+  else
+    {
+      printf ("ext2fs: disk cache is starving\n");
+
+      /* Give it some time.  This should happen rarely.  */
+      sleep (1);
+    }
+}
+
+/* Map block and return pointer to it.  */
+void *
+disk_cache_block_ref (block_t block)
+{
+  int index;
+  void *bptr;
+
+  assert (0 <= block && block < store->size >> log2_block_size);
+
+  ext2_debug ("(%u)", block);
+
+retry_ref:
+  pthread_mutex_lock (&disk_cache_lock);
+
+  bptr = hurd_ihash_find (disk_cache_bptr, block);
+  if (bptr)
+    /* Already mapped.  */
+    {
+      index = bptr_index (bptr);
+
+      /* In process of re-associating?  */
+      if (disk_cache_info[index].flags & DC_UNTOUCHED)
+       {
+         /* Wait re-association to finish.  */
+         pthread_cond_wait (&disk_cache_reassociation, &disk_cache_lock);
+         pthread_mutex_unlock (&disk_cache_lock);
+
+#if 0
+         printf ("Re-association -- wait finished.\n");
+#endif
+
+         goto retry_ref;
+       }
+
+      /* Just increment reference and return.  */
+      assert (disk_cache_info[index].ref_count + 1
+             > disk_cache_info[index].ref_count);
+      disk_cache_info[index].ref_count++;
+
+      ext2_debug ("cached %u -> %d (ref_count = %hu, flags = %#hx, ptr = %p)",
+                 disk_cache_info[index].block, index,
+                 disk_cache_info[index].ref_count,
+                 disk_cache_info[index].flags, bptr);
+
+      pthread_mutex_unlock (&disk_cache_lock);
+
+      return bptr;
+    }
+
+  /* Search for a block that is not in core and is not referenced.  */
+  index = disk_cache_hint;
+  while ((disk_cache_info[index].flags & DC_DONT_REUSE)
+        || (disk_cache_info[index].ref_count))
+    {
+      ext2_debug ("reject %u -> %d (ref_count = %hu, flags = %#hx)",
+                 disk_cache_info[index].block, index,
+                 disk_cache_info[index].ref_count,
+                 disk_cache_info[index].flags);
+
+      /* Just move to next block.  */
+      index++;
+      if (index >= disk_cache_blocks)
+       index -= disk_cache_blocks;
+
+      /* If we return to where we started, than there is no suitable
+        block. */
+      if (index == disk_cache_hint)
+       break;
+    }
+
+  /* The next place in the disk cache becomes the current hint.  */
+  disk_cache_hint = index + 1;
+  if (disk_cache_hint >= disk_cache_blocks)
+    disk_cache_hint -= disk_cache_blocks;
+
+  /* Is suitable place found?  */
+  if ((disk_cache_info[index].flags & DC_DONT_REUSE)
+      || disk_cache_info[index].ref_count)
+    /* No place is found.  Try to release some blocks and try
+       again.  */
+    {
+      ext2_debug ("flush %u -> %d", disk_cache_info[index].block, index);
+
+      pthread_mutex_unlock (&disk_cache_lock);
+
+      disk_cache_return_unused ();
+
+      goto retry_ref;
+    }
+
+  /* Suitable place is found.  */
+
+  /* Calculate pointer to data.  */
+  bptr = (char *)disk_cache + (index << log2_block_size);
+  ext2_debug ("map %u -> %d (%p)", block, index, bptr);
+
+  /* This pager_return_some is used only to set PM_FORCEREAD for the
+     page.  DC_UNTOUCHED is set so that we catch if someone has
+     referenced the block while we didn't hold disk_cache_lock.  */
+  disk_cache_info[index].flags |= DC_UNTOUCHED;
+
+#if 0 /* XXX: Let's see if this is needed at all.  */
+
+  pthread_mutex_unlock (&disk_cache_lock);
+  pager_return_some (diskfs_disk_pager, bptr - disk_cache, vm_page_size, 1);
+  pthread_mutex_lock (&disk_cache_lock);
+
+  /* Has someone used our bptr?  Has someone mapped requested block
+     while we have unlocked disk_cache_lock?  If so, environment has
+     changed and we have to restart operation.  */
+  if ((! (disk_cache_info[index].flags & DC_UNTOUCHED))
+      || hurd_ihash_find (disk_cache_bptr, block))
+    {
+      pthread_mutex_unlock (&disk_cache_lock);
+      goto retry_ref;
+    }
+
+#elif 0
+
+  /* XXX: Use libpager internals.  */
+
+  pthread_mutex_lock (&diskfs_disk_pager->interlock);
+  int page = (bptr - disk_cache) / vm_page_size;
+  assert (page >= 0);
+  int is_incore = (page < diskfs_disk_pager->pagemapsize
+                  && (diskfs_disk_pager->pagemap[page] & PM_INCORE));
+  pthread_mutex_unlock (&diskfs_disk_pager->interlock);
+  if (is_incore)
+    {
+      pthread_mutex_unlock (&disk_cache_lock);
+      printf ("INCORE\n");
+      goto retry_ref;
+    }
+
+#endif
+
+  /* Re-associate.  */
+  if (disk_cache_info[index].block != DC_NO_BLOCK)
+    /* Remove old association.  */
+    hurd_ihash_remove (disk_cache_bptr, disk_cache_info[index].block);
+  /* New association.  */
+  if (hurd_ihash_add (disk_cache_bptr, block, bptr))
+    ext2_panic ("Couldn't hurd_ihash_add new disk block");
+  assert (! (disk_cache_info[index].flags & DC_DONT_REUSE & ~DC_UNTOUCHED));
+  disk_cache_info[index].block = block;
+  assert (! disk_cache_info[index].ref_count);
+  disk_cache_info[index].ref_count = 1;
+
+  /* All data structures are set up.  */
+  pthread_mutex_unlock (&disk_cache_lock);
+
+  /* Try to read page.  */
+  *(volatile char *) bptr;
+
+  /* Check if it's actually read.  */
+  pthread_mutex_lock (&disk_cache_lock);
+  if (disk_cache_info[index].flags & DC_UNTOUCHED)
+    /* It's not read.  */
+    {
+      /* Remove newly created association.  */
+      hurd_ihash_remove (disk_cache_bptr, block);
+      disk_cache_info[index].block = DC_NO_BLOCK;
+      disk_cache_info[index].flags &=~ DC_UNTOUCHED;
+      disk_cache_info[index].ref_count = 0;
+      pthread_mutex_unlock (&disk_cache_lock);
+
+      /* Prepare next time association of this page to succeed.  */
+      pager_flush_some (diskfs_disk_pager, bptr - disk_cache,
+                       vm_page_size, 0);
+
+#if 0
+      printf ("Re-association failed.\n");
+#endif
+
+      goto retry_ref;
+    }
+
+  /* Re-association was successful.  */
+  pthread_cond_broadcast (&disk_cache_reassociation);
+
+  pthread_mutex_unlock (&disk_cache_lock);
+
+  ext2_debug ("(%u) = %p", block, bptr);
+  return bptr;
+}
+
+void
+disk_cache_block_ref_ptr (void *ptr)
+{
+  int index;
+
+  pthread_mutex_lock (&disk_cache_lock);
+  index = bptr_index (ptr);
+  assert (disk_cache_info[index].ref_count >= 1);
+  assert (disk_cache_info[index].ref_count + 1
+         > disk_cache_info[index].ref_count);
+  disk_cache_info[index].ref_count++;
+  assert (! (disk_cache_info[index].flags & DC_UNTOUCHED));
+  ext2_debug ("(%p) (ref_count = %hu, flags = %#hx)",
+             ptr,
+             disk_cache_info[index].ref_count,
+             disk_cache_info[index].flags);
+  pthread_mutex_unlock (&disk_cache_lock);
+}
+
+void
+disk_cache_block_deref (void *ptr)
+{
+  int index;
+
+  assert (disk_cache <= ptr && ptr <= disk_cache + disk_cache_size);
+
+  pthread_mutex_lock (&disk_cache_lock);
+  index = bptr_index (ptr);
+  ext2_debug ("(%p) (ref_count = %hu, flags = %#hx)",
+             ptr,
+             disk_cache_info[index].ref_count - 1,
+             disk_cache_info[index].flags);
+  assert (! (disk_cache_info[index].flags & DC_UNTOUCHED));
+  assert (disk_cache_info[index].ref_count >= 1);
+  disk_cache_info[index].ref_count--;
+  pthread_mutex_unlock (&disk_cache_lock);
+}
+
+/* Not used.  */
+int
+disk_cache_block_is_ref (block_t block)
+{
+  int ref;
+  void *ptr;
+
+  pthread_mutex_lock (&disk_cache_lock);
+  ptr = hurd_ihash_find (disk_cache_bptr, block);
+  if (ptr == NULL)
+    ref = 0;
+  else                         /* XXX: Should check for DC_UNTOUCHED too.  */
+    ref = disk_cache_info[bptr_index (ptr)].ref_count;
+  pthread_mutex_unlock (&disk_cache_lock);
+
+  return ref;
+}
+
 /* Create the DISK pager.  */
 void
 create_disk_pager (void)
@@ -776,8 +1197,12 @@ create_disk_pager (void)
     ext2_panic ("can't create disk pager: %s", strerror (errno));
   upi->type = DISK;
   pager_bucket = ports_create_bucket ();
-  diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, store->size,
-                          &disk_image);
+  get_hypermetadata ();
+  disk_cache_blocks = DISK_CACHE_BLOCKS;
+  disk_cache_size = disk_cache_blocks << log2_block_size;
+  diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, 1,
+                          disk_cache_size, &disk_cache);
+  disk_cache_init ();
 }
 
 /* Call this to create a FILE_DATA pager and return a send right.
@@ -817,7 +1242,7 @@ diskfs_get_filemap (struct node *node, vm_prot_t prot)
          diskfs_nref_light (node);
          node->dn->pager =
            pager_create (upi, pager_bucket, MAY_CACHE,
-                         MEMORY_OBJECT_COPY_DELAY);
+                         MEMORY_OBJECT_COPY_DELAY, 0);
          if (node->dn->pager == 0)
            {
              diskfs_nrele_light (node);
diff --git a/ext2fs/pokel.c b/ext2fs/pokel.c
index a8b16c9..3afb32e 100644
--- a/ext2fs/pokel.c
+++ b/ext2fs/pokel.c
@@ -67,12 +67,27 @@ pokel_add (struct pokel *pokel, void *loc, vm_size_t length)
       vm_offset_t p_offs = pl->offset;
       vm_size_t p_end = p_offs + pl->length;
 
-      if (p_offs == offset && p_end == end)
-       break;
+      if (p_offs <= offset && end <= p_end)
+       {
+         if (pokel->image == disk_cache)
+           for (vm_offset_t i = offset; i < end; i += block_size)
+             disk_cache_block_deref (disk_cache + i);
+
+         break;
+       }
       else if (p_end >= offset && end >= p_offs)
        {
          pl->offset = offset < p_offs ? offset : p_offs;
          pl->length = (end > p_end ? end : p_end) - pl->offset;
+
+         if (pokel->image == disk_cache)
+           {
+             vm_offset_t i_begin = p_offs > offset ? p_offs : offset;
+             vm_offset_t i_end = p_end < end ? p_end : end;
+             for (vm_offset_t i = i_begin; i < i_end; i += block_size)
+               disk_cache_block_deref (disk_cache + i);
+           }
+
          ext2_debug ("extended 0x%x[%ul] to 0x%x[%ul]",
                      p_offs, p_end - p_offs, pl->offset, pl->length);
          break;
@@ -113,11 +128,21 @@ _pokel_exec (struct pokel *pokel, int sync, int wait)
   pthread_spin_unlock (&pokel->lock);
 
   for (pl = pokes; pl; last = pl, pl = pl->next)
-    if (sync)
-      {
-       ext2_debug ("syncing 0x%x[%ul]", pl->offset, pl->length);
-       pager_sync_some (pokel->pager, pl->offset, pl->length, wait);
-      }
+    {
+      if (sync)
+       {
+         ext2_debug ("syncing 0x%lx[%ul]", pl->offset, pl->length);
+         pager_sync_some (pokel->pager, pl->offset, pl->length, wait);
+       }
+
+      if (pokel->image == disk_cache)
+       {
+         vm_offset_t begin = trunc_block (pl->offset);
+         vm_offset_t end = round_block (pl->offset + pl->length);
+         for (vm_offset_t i = begin; i != end; i += block_size)
+           disk_cache_block_deref (pokel->image + i);
+       }
+    }
 
   if (last)
     {
diff --git a/ext2fs/truncate.c b/ext2fs/truncate.c
index 37e360b..63d2295 100644
--- a/ext2fs/truncate.c
+++ b/ext2fs/truncate.c
@@ -124,7 +124,7 @@ trunc_indirect (struct node *node, block_t end,
     {
       unsigned index;
       int modified = 0, all_freed = 1;
-      block_t *ind_bh = (block_t *)bptr (*p);
+      block_t *ind_bh = (block_t *) disk_cache_block_ref (*p);
       unsigned first = end < offset ? 0 : end - offset;
 
       for (index = first; index < addr_per_block; index++)
@@ -139,11 +139,16 @@ trunc_indirect (struct node *node, block_t end,
 
       if (first == 0 && all_freed)
        {
-         pager_flush_some (diskfs_disk_pager, boffs (*p), block_size, 1);
+         pager_flush_some (diskfs_disk_pager,
+                           bptr_index (ind_bh) << log2_block_size,
+                           block_size, 1);
          free_block_run_free_ptr (fbr, p);
+         disk_cache_block_deref (ind_bh);
        }
       else if (modified)
        record_indir_poke (node, ind_bh);
+      else
+       disk_cache_block_deref (ind_bh);
     }
 }
 
diff --git a/fatfs/pager.c b/fatfs/pager.c
index f892c88..8146e64 100644
--- a/fatfs/pager.c
+++ b/fatfs/pager.c
@@ -596,6 +596,13 @@ pager_unlock_page (struct user_pager_info *pager,
   return 0;
 }
 
+void
+pager_notify_evict (struct user_pager_info *pager,
+                   vm_offset_t page)
+{
+  assert (!"unrequested notification on eviction");
+}
+
 /* Grow the disk allocated to locked node NODE to be at least SIZE
    bytes, and set NODE->allocsize to the actual allocated size.  (If
    the allocated size is already SIZE bytes, do nothing.)  CRED
@@ -752,7 +759,7 @@ create_fat_pager (void)
   struct user_pager_info *upi = malloc (sizeof (struct user_pager_info));
   upi->type = FAT;
   pager_bucket = ports_create_bucket ();
-  diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE,
+  diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, 0,
                           bytes_per_sector * sectors_per_fat,
                           &fat_image);
 }
@@ -794,7 +801,7 @@ diskfs_get_filemap (struct node *node, vm_prot_t prot)
           diskfs_nref_light (node);
           node->dn->pager =
             pager_create (upi, pager_bucket, MAY_CACHE,
-                          MEMORY_OBJECT_COPY_DELAY);
+                          MEMORY_OBJECT_COPY_DELAY, 0);
           if (node->dn->pager == 0)
             {
               diskfs_nrele_light (node);
diff --git a/hurd/fsys.defs b/hurd/fsys.defs
index 979a6cf..c031da7 100644
--- a/hurd/fsys.defs
+++ b/hurd/fsys.defs
@@ -1,5 +1,5 @@
 /* Definitions for the filesystem control interface
-   Copyright (C) 1992,93,94,95,96,97, 2002 Free Software Foundation
+   Copyright (C) 1992,93,94,95,96,97, 2002,13 Free Software Foundation
 
 This file is part of the GNU Hurd.
 
@@ -127,3 +127,23 @@ routine fsys_get_options (
        server: fsys_t;
        RPT
        out options: data_t, dealloc);
+
+/* 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.  */
+routine fsys_get_children (
+       server: fsys_t;
+       RPT
+       out children: data_t);
+
+/* Return information about the source of the receiving filesystem.
+   If the concept of a source is applicable, SOURCE should refer to
+   the source of the receiving translator and should be a description
+   considered appropriate in the context of the translator.  For
+   example for the case of block device based filesystems, SOURCE
+   should be the file name of the underlying block device.  */
+routine fsys_get_source (
+       server: fsys_t;
+       RPT
+       out source: string_t);
+
diff --git a/hurd/fsys_reply.defs b/hurd/fsys_reply.defs
index e203169..2f5b035 100644
--- a/hurd/fsys_reply.defs
+++ b/hurd/fsys_reply.defs
@@ -1,6 +1,6 @@
 /* Reply half of fsys
 
-   Copyright (C) 1991, 1993, 1994, 1995, 2001, 2007
+   Copyright (C) 1991, 1993, 1994, 1995, 2001, 2007, 2013
      Free Software Foundation, Inc.
 
 This file is part of the GNU Hurd.
@@ -76,3 +76,13 @@ simpleroutine fsys_get_options_reply (
        reply_port: reply_port_t;
        RETURN_CODE_ARG;
        options: data_t);
+
+simpleroutine fsys_get_children (
+       server: fsys_t;
+       RETURN_CODE_ARG;
+       children: data_t);
+
+simpleroutine fsys_get_source (
+       server: fsys_t;
+       RETURN_CODE_ARG;
+       source: string_t);
diff --git a/hurd/process.defs b/hurd/process.defs
index 43cc9f2..bf90556 100644
--- a/hurd/process.defs
+++ b/hurd/process.defs
@@ -1,5 +1,5 @@
 /* Definitions for process server interface
-   Copyright (C) 1992,93,94,95,96,97,2001 Free Software Foundation
+   Copyright (C) 1992,93,94,95,96,97,2001,2013 Free Software Foundation
 
 This file is part of the GNU Hurd.
 
@@ -370,3 +370,33 @@ routine proc_getnports (
        process: process_t;
        which: pid_t;
        out nports: mach_msg_type_number_t);
+
+/*** Routines related to early server bootstrapping ***/
+
+skip;  /* Reserved for proc_set_init_task */
+
+/* Inform the process server that the process is important.  */
+routine proc_mark_important (
+       process: process_t);
+
+/* Query whether the process is important.  */
+routine proc_is_important (
+       process: process_t;
+       out essential: boolean_t);
+
+/* Set the processes start_code and end_code locations.  Any
+   executable segments loaded from the ELF binary are in this
+   range.  */
+routine proc_set_code (
+       process: process_t;
+       start_code: vm_address_t;
+       end_code: vm_address_t);
+
+/* Get the processes start_code and end_code locations.  Any
+   executable segments loaded from the ELF binary are in this range.
+   If zero is returned for these values, the requested information has
+   never been set.  */
+routine proc_get_code (
+       process: process_t;
+       out start_code: vm_address_t;
+       out end_code: vm_address_t);
diff --git a/hurd/process_reply.defs b/hurd/process_reply.defs
index 80d3929..66a7551 100644
--- a/hurd/process_reply.defs
+++ b/hurd/process_reply.defs
@@ -1,5 +1,5 @@
 /* Reply half of wait
-   Copyright (C) 1991,93,94,96,2001 Free Software Foundation, Inc.
+   Copyright (C) 1991,93,94,96,2001,13 Free Software Foundation, Inc.
 
 This file is part of the GNU Hurd.
 
@@ -60,3 +60,135 @@ simpleroutine proc_wait_reply (
        in sigcode: int;
        in rusage: rusage_t;
        in pid_status: pid_t);
+
+skip; /* proc_dostop */
+skip; /* proc_handle_exceptions */
+skip; /* proc_mark_stop */
+skip; /* proc_mark_cont */
+skip; /* proc_mark_exit */
+skip; /* proc_mark_exec */
+skip; /* proc_mark_traced */
+skip; /* proc_mod_stopchild */
+
+simpleroutine proc_pid2task (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       task: task_t);
+
+simpleroutine proc_task2pid (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       pid: pid_t);
+
+simpleroutine proc_task2proc (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       proc: mach_port_make_send_t);
+
+simpleroutine proc_proc2task (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       task: task_t);
+
+simpleroutine proc_pid2proc (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       proc: mach_port_make_send_t);
+
+simpleroutine proc_getprocinfo (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       flags: int;
+       procinfo: procinfo_t, dealloc;
+       threadwaits: data_t, dealloc);
+
+simpleroutine proc_getprocargs (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       procargs: data_t, dealloc);
+
+simpleroutine proc_getprocenv (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       procenv: data_t, dealloc);
+
+skip; /* proc_make_login_coll */
+
+simpleroutine proc_getloginid (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       login_id: pid_t);
+
+simpleroutine proc_getloginpids (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       pids: pidarray_t, dealloc);
+
+skip; /* proc_setlogin */
+
+simpleroutine proc_getlogin (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       logname: string_t);
+
+skip; /* proc_setsid */
+
+simpleroutine proc_getsid (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       sid: pid_t);
+
+simpleroutine proc_getsessionpgids (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       pgidset: pidarray_t, dealloc);
+
+simpleroutine proc_getsessionpids (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       pidset: pidarray_t, dealloc);
+
+simpleroutine proc_getsidport (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       sessport: mach_port_send_t);
+
+skip; /* proc_setpgrp */
+
+simpleroutine proc_getpgrp (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       pgrp: pid_t);
+
+simpleroutine proc_getpgrppids (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       pidset: pidarray_t, dealloc);
+
+simpleroutine proc_get_tty (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       tty: mach_port_send_t);
+
+simpleroutine proc_getnports (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       nports: mach_msg_type_number_t);
+
+/*** Routines related to early server bootstrapping ***/
+
+skip;  /* Reserved for proc_set_init_task */
+skip; /* proc_mark_important */
+
+simpleroutine proc_is_important (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       essential: boolean_t);
+
+skip; /* proc_set_code */
+
+simpleroutine proc_get_code (
+       reply_port: reply_port_t;
+       RETURN_CODE_ARG;
+       start_code: vm_address_t;
+       end_code: vm_address_t);
diff --git a/hurd/process_request.defs b/hurd/process_request.defs
index 80a2828..38e7146 100644
--- a/hurd/process_request.defs
+++ b/hurd/process_request.defs
@@ -1,6 +1,6 @@
 /* Definitions for process server interface (request-only version)
 
-   Copyright (C) 1992, 93, 94, 95, 96, 98 Free Software Foundation, Inc.
+   Copyright (C) 1992, 93, 94, 95, 96, 98, 2013 Free Software Foundation, Inc.
 
 This file is part of the GNU Hurd.
 
@@ -371,3 +371,34 @@ simpleroutine proc_getnports_request (
        process: process_t;
        ureplyport reply: reply_port_t;
        which: pid_t);
+
+/*** Routines related to early server bootstrapping ***/
+
+skip;  /* Reserved for proc_set_init_task */
+
+/* Inform the process server that the process is important.  */
+simpleroutine proc_mark_important_request (
+       process: process_t;
+       ureplyport reply: reply_port_t);
+
+/* Query whether the process is important.  */
+simpleroutine proc_is_important_request (
+       process: process_t;
+       ureplyport reply: reply_port_t);
+
+/* Set the processes start_code and end_code locations.  Any
+   executable segments loaded from the ELF binary are in this
+   range.  */
+simpleroutine proc_set_code_request (
+       process: process_t;
+       ureplyport reply: reply_port_t;
+       start_code: vm_address_t;
+       end_code: vm_address_t);
+
+/* Get the processes start_code and end_code locations.  Any
+   executable segments loaded from the ELF binary are in this range.
+   If zero is returned for these values, the requested information has
+   never been set.  */
+simpleroutine proc_get_code_request (
+       process: process_t;
+       ureplyport reply: reply_port_t);
diff --git a/include/pids.h b/include/pids.h
new file mode 100644
index 0000000..485916b
--- /dev/null
+++ b/include/pids.h
@@ -0,0 +1,29 @@
+/* List of special processes.
+
+   Copyright (C) 2013 Free Software Foundation
+   Written by Justus Winter <address@hidden>
+
+   This file is part of the GNU Hurd.
+
+   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. */
+
+#ifndef _HURD_PROCESSES_H
+#define _HURD_PROCESSES_H
+
+#define HURD_PID_PROC          0
+#define HURD_PID_STARTUP       1
+#define HURD_PID_KERNEL                2
+
+#endif  /* _HURD_PROCESSES_H */
diff --git a/init/init.c b/init/init.c
index 930408e..94f1a9b 100644
--- a/init/init.c
+++ b/init/init.c
@@ -1,7 +1,7 @@
 /* Start and maintain hurd core servers and system run state
 
    Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-     2005, 2008 Free Software Foundation, Inc.
+     2005, 2008, 2013 Free Software Foundation, Inc.
    This file is part of the GNU Hurd.
 
    The GNU Hurd is free software; you can redistribute it and/or modify
@@ -49,6 +49,7 @@
 #include <maptime.h>
 #include <version.h>
 #include <argp.h>
+#include <pids.h>
 
 #include "startup_notify_U.h"
 #include "startup_reply_U.h"
@@ -642,6 +643,8 @@ launch_core_servers (void)
       device_master = 0;
     }
 
+  /* Mark us as important.  */
+  proc_mark_important (procserver);
   proc_mark_exec (procserver);
 
   /* Declare that the filesystem and auth are our children. */
@@ -649,6 +652,7 @@ launch_core_servers (void)
   proc_child (procserver, authtask);
 
   proc_task2proc (procserver, authtask, &authproc);
+  proc_mark_important (authproc);
   proc_mark_exec (authproc);
   startup_authinit_reply (authreply, authreplytype, 0, authproc,
                          MACH_MSG_TYPE_COPY_SEND);
@@ -671,6 +675,7 @@ launch_core_servers (void)
   err = proc_task2proc (procserver, proctask, &procproc);
   if (!err)
     {
+      proc_mark_important (procproc);
       proc_mark_exec (procproc);
       mach_port_deallocate (mach_task_self (), procproc);
     }
@@ -680,6 +685,7 @@ launch_core_servers (void)
   /* Get the bootstrap filesystem's proc server port.
      We must do this before calling proc_setmsgport below.  */
   proc_task2proc (procserver, fstask, &fsproc);
+  proc_mark_important (fsproc);
   proc_mark_exec (fsproc);
 
 #if 0
@@ -770,7 +776,7 @@ frob_kernel_process (void)
   task_t task;
   process_t proc, kbs;
 
-  err = proc_pid2task (procserver, 2, &task);
+  err = proc_pid2task (procserver, HURD_PID_KERNEL, &task);
   if (err)
     {
       error (0, err, "cannot get kernel task port");
@@ -784,8 +790,10 @@ frob_kernel_process (void)
       return;
     }
 
-  /* Mark the kernel task as an essential task so that we never
-     want to task_terminate it.  */
+  /* Mark the kernel task as an essential task so that we or the proc server
+     never want to task_terminate it.  */
+  proc_mark_important (proc);
+
   err = record_essential_task ("kernel", task);
   assert_perror (err);
 
@@ -1066,6 +1074,7 @@ start_child (const char *prog, char **progargs)
                   default_ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
                   default_ints, INIT_INT_MAX,
                   NULL, 0, NULL, 0);
+  proc_mark_important (default_ports[INIT_PORT_PROC]);
   mach_port_deallocate (mach_task_self (), default_ports[INIT_PORT_PROC]);
   mach_port_deallocate (mach_task_self (), file);
   if (err)
@@ -1206,7 +1215,12 @@ S_startup_essential_task (mach_port_t server,
       if (!strcmp (name, "auth"))
        authinit = 1;
       else if (!strcmp (name, "exec"))
-       execinit = 1;
+        {
+          execinit = 1;
+          mach_port_t execproc;
+          proc_task2proc (procserver, task, &execproc);
+          proc_mark_important (execproc);
+        }
       else if (!strcmp (name, "proc"))
        procinit = 1;
 
diff --git a/isofs/pager.c b/isofs/pager.c
index f93e0c8..7a38ba3 100644
--- a/isofs/pager.c
+++ b/isofs/pager.c
@@ -94,6 +94,13 @@ pager_unlock_page (struct user_pager_info *pager,
   return EROFS;
 }
 
+void
+pager_notify_evict (struct user_pager_info *pager,
+                   vm_offset_t page)
+{
+  assert (!"unrequested notification on eviction");
+}
+
 /* Tell how big the file is. */
 error_t
 pager_report_extent (struct user_pager_info *pager,
@@ -137,7 +144,7 @@ create_disk_pager (void)
   upi->type = DISK;
   upi->np = 0;
   pager_bucket = ports_create_bucket ();
-  diskfs_start_disk_pager (upi, pager_bucket, 1, store->size, &disk_image);
+  diskfs_start_disk_pager (upi, pager_bucket, 1, 0, store->size, &disk_image);
   upi->p = diskfs_disk_pager;
 }
 
@@ -168,7 +175,8 @@ diskfs_get_filemap (struct node *np, vm_prot_t prot)
        upi->type = FILE_DATA;
        upi->np = np;
        diskfs_nref_light (np);
-       upi->p = pager_create (upi, pager_bucket, 1, MEMORY_OBJECT_COPY_DELAY);
+       upi->p = pager_create (upi, pager_bucket, 1,
+                              MEMORY_OBJECT_COPY_DELAY, 0);
        if (upi->p == 0)
          {
            diskfs_nrele_light (np);
diff --git a/libdiskfs/Makefile b/libdiskfs/Makefile
index 3c8de4c..03c2e2b 100644
--- a/libdiskfs/Makefile
+++ b/libdiskfs/Makefile
@@ -34,7 +34,8 @@ IOSRCS= io-async-icky.c io-async.c io-duplicate.c 
io-get-conch.c io-revoke.c \
        io-reauthenticate.c io-rel-conch.c io-restrict-auth.c io-seek.c \
        io-select.c io-stat.c io-stubs.c io-write.c io-version.c io-sigio.c
 FSYSSRCS=fsys-getroot.c fsys-goaway.c fsys-startup.c fsys-getfile.c \
-       fsys-options.c fsys-syncfs.c fsys-forward.c
+       fsys-options.c fsys-syncfs.c fsys-forward.c \
+       fsys-get-children.c fsys-get-source.c
 IFSOCKSRCS=ifsock.c
 OTHERSRCS = conch-fetch.c conch-set.c dir-clear.c dir-init.c dir-renamed.c \
        extern-inline.c \
@@ -51,7 +52,7 @@ OTHERSRCS = conch-fetch.c conch-set.c dir-clear.c dir-init.c 
dir-renamed.c \
        remount.c console.c disk-pager.c \
        name-cache.c direnter.c dirrewrite.c dirremove.c lookup.c dead-name.c \
        validate-mode.c validate-group.c validate-author.c validate-flags.c \
-       validate-rdev.c validate-owner.c extra-version.c
+       validate-rdev.c validate-owner.c extra-version.c get-source.c
 SRCS = $(OTHERSRCS) $(FSSRCS) $(IOSRCS) $(FSYSSRCS) $(IFSOCKSRCS)
 installhdrs = diskfs.h diskfs-pager.h
 
diff --git a/libdiskfs/boot-start.c b/libdiskfs/boot-start.c
index b62d5f3..6d9a773 100644
--- a/libdiskfs/boot-start.c
+++ b/libdiskfs/boot-start.c
@@ -33,6 +33,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 
02139, USA.  */
 #include <string.h>
 #include <argz.h>
 #include <error.h>
+#include <pids.h>
 #include "fsys_S.h"
 #include "fsys_reply_U.h"
 
@@ -606,7 +607,7 @@ diskfs_S_fsys_init (mach_port_t port,
   proc_register_version (procserver, host, diskfs_server_name, "",
                         diskfs_server_version);
 
-  err = proc_getmsgport (procserver, 1, &startup);
+  err = proc_getmsgport (procserver, HURD_PID_STARTUP, &startup);
   if (!err)
     {
       startup_essential_task (startup, mach_task_self (), MACH_PORT_NULL,
diff --git a/libdiskfs/dead-name.c b/libdiskfs/dead-name.c
index 760b36f..6ca208e 100644
--- a/libdiskfs/dead-name.c
+++ b/libdiskfs/dead-name.c
@@ -40,6 +40,8 @@ ports_dead_name (void *notify, mach_port_t dead_name)
       else
        pthread_mutex_unlock (&np->lock);
     }
-  
+
+  fshelp_remove_active_translator (dead_name);
+
   ports_interrupt_notified_rpcs (notify, dead_name, MACH_NOTIFY_DEAD_NAME);
 }
diff --git a/libdiskfs/dir-lookup.c b/libdiskfs/dir-lookup.c
index 923be03..15a9b0c 100644
--- a/libdiskfs/dir-lookup.c
+++ b/libdiskfs/dir-lookup.c
@@ -1,6 +1,6 @@
 /* libdiskfs implementation of fs.defs:dir_lookup
    Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
-     2002, 2008 Free Software Foundation, Inc.
+     2002, 2008, 2013 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
@@ -41,6 +41,7 @@ diskfs_S_dir_lookup (struct protid *dircred,
   struct node *np;
   int nsymlink = 0;
   char *nextname;
+  char *relpath;
   int nextnamelen;
   error_t error = 0;
   char *pathbuf = 0;
@@ -68,6 +69,11 @@ diskfs_S_dir_lookup (struct protid *dircred,
   while (path[0] == '/')
     path++;
 
+  /* Preserve the path relative to diruser->po->path.  */
+  relpath = strdup (path);
+  if (! relpath)
+    return ENOMEM;
+
   *returned_port_poly = MACH_MSG_TYPE_MAKE_SEND;
   *retry = FS_RETRY_NORMAL;
   retryname[0] = '\0';
@@ -479,6 +485,22 @@ diskfs_S_dir_lookup (struct protid *dircred,
 
   if (! error)
     {
+      free (newpi->po->path);
+      if (dircred->po->path == NULL)
+       {
+         /* dircred is the root directory.  */
+         newpi->po->path = relpath;
+         relpath = NULL; /* Do not free relpath.  */
+       }
+      else
+       {
+         newpi->po->path = NULL;
+         asprintf (&newpi->po->path, "%s/%s", dircred->po->path, relpath);
+       }
+
+      if (! newpi->po->path)
+       error = errno;
+
       *returned_port = ports_get_right (newpi);
       ports_port_deref (newpi);
       newpi = 0;
@@ -500,5 +522,7 @@ diskfs_S_dir_lookup (struct protid *dircred,
   if (newpo)
     diskfs_release_peropen (newpo);
 
+  free (relpath);
+
   return error;
 }
diff --git a/libdiskfs/disk-pager.c b/libdiskfs/disk-pager.c
index 5795a28..8fe8f80 100644
--- a/libdiskfs/disk-pager.c
+++ b/libdiskfs/disk-pager.c
@@ -49,7 +49,8 @@ service_paging_requests (void *arg)
 
 void
 diskfs_start_disk_pager (struct user_pager_info *upi,
-                        struct port_bucket *pager_bucket, int may_cache,
+                        struct port_bucket *pager_bucket,
+                        int may_cache, int notify_on_evict,
                         size_t size, void **image)
 {
   pthread_t thread;
@@ -68,7 +69,8 @@ diskfs_start_disk_pager (struct user_pager_info *upi,
 
   /* Create the pager.  */
   diskfs_disk_pager = pager_create (upi, pager_bucket,
-                                   may_cache, MEMORY_OBJECT_COPY_NONE);
+                                   may_cache, MEMORY_OBJECT_COPY_NONE,
+                                   notify_on_evict);
   assert (diskfs_disk_pager);
 
   /* Get a port to the disk pager.  */
diff --git a/libdiskfs/diskfs-pager.h b/libdiskfs/diskfs-pager.h
index bd0a050..a253069 100644
--- a/libdiskfs/diskfs-pager.h
+++ b/libdiskfs/diskfs-pager.h
@@ -35,7 +35,8 @@ extern __thread struct disk_image_user *diskfs_exception_diu;
    mapped is returned in IMAGE.  INFO, PAGER_BUCKET, & MAY_CACHE are passed
    to `pager_create'.  */
 extern void diskfs_start_disk_pager (struct user_pager_info *info,
-                                    struct port_bucket *pager_bucket, int 
may_cache,
+                                    struct port_bucket *pager_bucket,
+                                    int may_cache, int notify_on_evict,
                                     size_t size, void **image);
 
 extern struct pager *diskfs_disk_pager;
diff --git a/libdiskfs/diskfs.h b/libdiskfs/diskfs.h
index 0f9c1d3..22262aa 100644
--- a/libdiskfs/diskfs.h
+++ b/libdiskfs/diskfs.h
@@ -1,7 +1,7 @@
 /* Definitions for fileserver helper functions
 
    Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2001, 2002, 2007, 2008,
-   2009 Free Software Foundation, Inc.
+   2009, 2013 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
@@ -69,6 +69,9 @@ struct peropen
   mach_port_t shadow_root_parent;
   /* If in a shadow tree, its root node in this translator.  */
   struct node *shadow_root;
+
+  /* Path relative to the root of the translator. */
+  char *path;
 };
 
 /* A unique one of these exists for each node currently in use (and
@@ -564,6 +567,12 @@ error_t (*diskfs_create_symlink_hook)(struct node *np, 
const char *target);
    isn't set, then the normal method (reading from the file data) is
    used.  If it returns any other error, it is returned to the user. */
 error_t (*diskfs_read_symlink_hook)(struct node *np, char *target);
+
+/* The user may define this function.  The function must set source to
+   the source device of the filesystem. The function may return an
+   EOPNOTSUPP to indicate that the concept of a source device is not
+   applicable. The default function always returns EOPNOTSUPP. */
+error_t diskfs_get_source (char *source);
 
 /* The library exports the following functions for general use */
 
diff --git a/libdiskfs/file-set-trans.c b/libdiskfs/file-set-trans.c
index 49303e7..8de2e64 100644
--- a/libdiskfs/file-set-trans.c
+++ b/libdiskfs/file-set-trans.c
@@ -1,5 +1,6 @@
 /* libdiskfs implementation of fs.defs: file_set_translator
-   Copyright (C) 1992,93,94,95,96,99,2001,02 Free Software Foundation, Inc.
+   Copyright (C) 1992,93,94,95,96,99,2001,02,13
+     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
@@ -206,5 +207,21 @@ diskfs_S_file_set_translator (struct protid *cred,
     }
 
   pthread_mutex_unlock (&np->lock);
+
+  if (! error && cred->po->path)
+    error = fshelp_set_active_translator (cred->po->path, active);
+
+  if (! error && active != MACH_PORT_NULL)
+    {
+      mach_port_t old;
+      error = mach_port_request_notification (mach_task_self (), active,
+                                             MACH_NOTIFY_DEAD_NAME, 0,
+                                             cred->pi.port_right,
+                                             MACH_MSG_TYPE_MAKE_SEND_ONCE,
+                                             &old);
+      if (old != MACH_PORT_NULL)
+       mach_port_deallocate (mach_task_self (), old);
+    }
+
   return error;
 }
diff --git a/libdiskfs/fsys-get-children.c b/libdiskfs/fsys-get-children.c
new file mode 100644
index 0000000..69c9963
--- /dev/null
+++ b/libdiskfs/fsys-get-children.c
@@ -0,0 +1,99 @@
+/* fsys_get_children
+
+   Copyright (C) 2013 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/>.  */
+
+#include "priv.h"
+#include "fsys_S.h"
+
+#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.  */
+error_t
+diskfs_S_fsys_get_children (fsys_t server,
+                           mach_port_t reply,
+                           mach_msg_type_name_t replyPoly,
+                           char **children,
+                           mach_msg_type_number_t *children_len)
+{
+  error_t err;
+
+  struct protid *cred = ports_lookup_port (diskfs_port_bucket,
+                                          server,
+                                          diskfs_protid_class);
+  if (! cred)
+    return EOPNOTSUPP;
+
+  /* check_access performs the same permission check as is normally
+     done, i.e. it checks that all but the last path components are
+     executable by the requesting user and that the last component is
+     readable. */
+  error_t check_access (const char *path)
+  {
+    error_t err;
+    char *elements = NULL;
+    size_t elements_len = 0;
+
+    err = argz_create_sep (path, '/', &elements, &elements_len);
+    if (err)
+      return err;
+
+    struct node *dp = diskfs_root_node;
+
+    for (char *entry = elements;
+        entry;
+        entry = argz_next (elements, elements_len, entry))
+      {
+       struct node *next;
+       err = diskfs_lookup (dp, entry, LOOKUP, &next, NULL, cred);
+
+       if (dp != diskfs_root_node)
+         diskfs_nput (dp);
+
+       if (err)
+         return err;
+
+       dp = next;
+      }
+
+    err = fshelp_access (&dp->dn_stat, S_IRUSR, cred->user);
+    diskfs_nput (dp);
+    return err;
+  }
+
+
+  char *c = NULL;
+  size_t c_len = 0;
+
+  err = fshelp_get_active_translators (&c, &c_len, check_access);
+  if (err)
+    goto errout;
+
+  err = iohelp_return_malloced_buffer (c, c_len, children, children_len);
+  if (err)
+    goto errout;
+
+  c = NULL; /* c was freed by iohelp_return_malloced_buffer. */
+
+ errout:
+  free (c);
+  return err;
+}
diff --git a/libdiskfs/fsys-get-source.c b/libdiskfs/fsys-get-source.c
new file mode 100644
index 0000000..08f227c
--- /dev/null
+++ b/libdiskfs/fsys-get-source.c
@@ -0,0 +1,34 @@
+/* fsys_get_source
+
+   Copyright (C) 2013 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/>.  */
+
+#include "priv.h"
+#include "fsys_S.h"
+
+/* Return information about the source of the receiving
+   filesystem. */
+error_t
+diskfs_S_fsys_get_source (fsys_t server,
+                         mach_port_t reply,
+                         mach_msg_type_name_t replyPoly,
+                         char *source)
+{
+  return diskfs_get_source (source);
+}
diff --git a/libdiskfs/fsys-getroot.c b/libdiskfs/fsys-getroot.c
index 2e11da4..39973a8 100644
--- a/libdiskfs/fsys-getroot.c
+++ b/libdiskfs/fsys-getroot.c
@@ -51,7 +51,8 @@ diskfs_S_fsys_getroot (fsys_t controlport,
   {
     root_parent: dotdot,
     shadow_root_parent: MACH_PORT_NULL,
-    shadow_root: _diskfs_chroot_directory ? diskfs_root_node : NULL /* XXX */
+    shadow_root: _diskfs_chroot_directory ? diskfs_root_node : NULL, /* XXX */
+    path: NULL,
   };
 
   if (!pt)
diff --git a/libdiskfs/get-source.c b/libdiskfs/get-source.c
new file mode 100644
index 0000000..d0c143b
--- /dev/null
+++ b/libdiskfs/get-source.c
@@ -0,0 +1,28 @@
+/* Default version of diskfs_get_source
+
+   Copyright (C) 2013 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/>.  */
+
+#include "priv.h"
+
+error_t
+diskfs_get_source (char *source)
+{
+  return EOPNOTSUPP;
+}
diff --git a/libdiskfs/init-startup.c b/libdiskfs/init-startup.c
index 2c0814f..bf955d2 100644
--- a/libdiskfs/init-startup.c
+++ b/libdiskfs/init-startup.c
@@ -26,6 +26,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 
02139, USA.  */
 #include <error.h>
 #include <hurd/fsys.h>
 #include <hurd/startup.h>
+#include <pids.h>
 
 char *_diskfs_chroot_directory;
 
@@ -190,7 +191,14 @@ _diskfs_init_completed ()
   if (err)
     goto errout;
 
-  err = proc_getmsgport (proc, 1, &init);
+  /* Mark us as important.  */
+  err = proc_mark_important (proc);
+  /* This might fail due to permissions or because the old proc server
+     is still running, ignore any such errors.  */
+  if (err && err != EPERM && err != EMIG_BAD_ID)
+    goto errout;
+
+  err = proc_getmsgport (proc, HURD_PID_STARTUP, &init);
   mach_port_deallocate (mach_task_self (), proc);
   if (err)
     goto errout;
diff --git a/libdiskfs/peropen-make.c b/libdiskfs/peropen-make.c
index d37516c..b11b2ad 100644
--- a/libdiskfs/peropen-make.c
+++ b/libdiskfs/peropen-make.c
@@ -34,6 +34,7 @@ diskfs_make_peropen (struct node *np, int flags, struct 
peropen *context,
   po->refcnt = 0;
   po->openstat = flags;
   po->np = np;
+  po->path = NULL;
 
   if (context)
     {
@@ -50,6 +51,13 @@ diskfs_make_peropen (struct node *np, int flags, struct 
peropen *context,
       if (po->shadow_root_parent != MACH_PORT_NULL)
        mach_port_mod_refs (mach_task_self (), po->shadow_root_parent, 
                            MACH_PORT_RIGHT_SEND, 1);
+
+      if (context->path)
+       {
+         po->path = strdup (context->path);
+         if (! po->path)
+           return ENOMEM;
+       }
     }
   else
     {
diff --git a/libdiskfs/peropen-rele.c b/libdiskfs/peropen-rele.c
index 08276ec..d3f7492 100644
--- a/libdiskfs/peropen-rele.c
+++ b/libdiskfs/peropen-rele.c
@@ -45,5 +45,6 @@ diskfs_release_peropen (struct peropen *po)
 
   diskfs_nput (po->np);
 
+  free (po->path);
   free (po);
 }
diff --git a/libfshelp/Makefile b/libfshelp/Makefile
index 4de3837..6ba6a14 100644
--- a/libfshelp/Makefile
+++ b/libfshelp/Makefile
@@ -20,6 +20,7 @@ makemode := library
 
 libname = libfshelp
 SRCS = lock-acquire.c lock-init.c \
+       translator-list.c \
        start-translator-long.c start-translator.c \
        fetch-root.c transbox-init.c set-active.c fetch-control.c \
        drop-transbox.c translated.c \
@@ -32,7 +33,7 @@ SRCS =        lock-acquire.c lock-init.c \
        touch.c
 installhdrs = fshelp.h
 
-HURDLIBS = shouldbeinlibc iohelp ports
+HURDLIBS = shouldbeinlibc iohelp ports ihash
 LDLIBS += -lpthread
 OBJS = $(subst .c,.o,$(SRCS))
 
diff --git a/libfshelp/fshelp.h b/libfshelp/fshelp.h
index cf39fbc..43fbcd7 100644
--- a/libfshelp/fshelp.h
+++ b/libfshelp/fshelp.h
@@ -1,5 +1,6 @@
 /* FS helper library definitions
-   Copyright (C) 1994,95,96,97,98,99,2000,01,02 Free Software Foundation, Inc.
+   Copyright (C) 1994,95,96,97,98,99,2000,01,02,13
+     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
@@ -32,6 +33,36 @@
 #include <maptime.h>
 
 
+/* Keeping track of active translators */
+/* These routines keep a list of active translators.  They are
+   self-contained and do not require multi threading or the ports
+   library.  */
+
+/* Record an active translator being bound to the given file name
+   NAME.  ACTIVE is the control port of the translator.  */
+error_t
+fshelp_set_active_translator (const char *name, mach_port_t active);
+
+/* Remove the active translator specified by its control port ACTIVE.
+   If there is no active translator with the given control port, this
+   does nothing.  */
+error_t
+fshelp_remove_active_translator (mach_port_t active);
+
+/* This kind of function is used by fshelp_get_active_translators to
+   filter the list of translators to return.  If a filter returns an
+   error for a given PATH, the translator bound to the PATH is not
+   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.  */
+error_t
+fshelp_get_active_translators (char **translators,
+                              size_t *translators_len,
+                              fshelp_filter filter);
+
+
 /* Passive translator linkage */
 /* These routines are self-contained and start passive translators,
    returning the control port.  They do not require multi threading
diff --git a/libfshelp/translator-list.c b/libfshelp/translator-list.c
new file mode 100644
index 0000000..a24d976
--- /dev/null
+++ b/libfshelp/translator-list.c
@@ -0,0 +1,169 @@
+/* A list of active translators.
+
+   Copyright (C) 2013 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/>.  */
+
+#include <argz.h>
+#include <hurd/fsys.h>
+#include <hurd/ihash.h>
+#include <mach.h>
+#include <mach/notify.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fshelp.h"
+
+struct translator
+{
+  char *name;
+  mach_port_t active;
+};
+
+/* The list of active translators.  */
+static struct hurd_ihash translator_ihash
+  = HURD_IHASH_INITIALIZER (HURD_IHASH_NO_LOCP);
+
+/* The lock protecting the translator_ihash.  */
+static pthread_mutex_t translator_ihash_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static void
+translator_ihash_cleanup (void *element, void *arg)
+{
+  /* No need to deallocate port, we only keep the name of the
+     port, not a reference.  */
+  free (element);
+}
+
+/* Record an active translator being bound to the given file name
+   NAME.  ACTIVE is the control port of the translator.  */
+error_t
+fshelp_set_active_translator (const char *name, mach_port_t active)
+{
+  error_t err = 0;
+  pthread_mutex_lock (&translator_ihash_lock);
+
+  if (! translator_ihash.cleanup)
+    hurd_ihash_set_cleanup (&translator_ihash, translator_ihash_cleanup, NULL);
+
+  struct translator *t = NULL;
+  HURD_IHASH_ITERATE (&translator_ihash, value)
+    {
+      t = value;
+      if (strcmp (name, t->name) == 0)
+       goto update; /* Entry exists.  */
+    }
+
+  t = malloc (sizeof (struct translator));
+  if (! t)
+    return ENOMEM;
+
+  t->active = MACH_PORT_NULL;
+  t->name = strdup (name);
+  if (! t->name)
+    {
+      err = errno;
+      free (t);
+      goto out;
+    }
+
+  err = hurd_ihash_add (&translator_ihash, (hurd_ihash_key_t) t, t);
+  if (err)
+    goto out;
+
+ update:
+  if (active)
+    /* No need to increment the reference count, we only keep the
+       name, not a reference.  */
+    t->active = active;
+  else
+    hurd_ihash_remove (&translator_ihash, (hurd_ihash_key_t) t);
+
+ out:
+  pthread_mutex_unlock (&translator_ihash_lock);
+  return err;
+}
+
+/* Remove the active translator specified by its control port ACTIVE.
+   If there is no active translator with the given control port, this
+   does nothing.  */
+error_t
+fshelp_remove_active_translator (mach_port_t active)
+{
+  error_t err = 0;
+  pthread_mutex_lock (&translator_ihash_lock);
+
+  struct translator *t = NULL;
+  HURD_IHASH_ITERATE (&translator_ihash, value)
+    {
+      struct translator *v = value;
+      if (active == v->active)
+       {
+         t = v;
+         break;
+       }
+    }
+
+  if (t)
+    hurd_ihash_remove (&translator_ihash, (hurd_ihash_key_t) t);
+
+  pthread_mutex_unlock (&translator_ihash_lock);
+  return err;
+}
+
+/* Records the list of active translators into the argz vector
+   specified by TRANSLATORS filtered by FILTER.  */
+error_t
+fshelp_get_active_translators (char **translators,
+                              size_t *translators_len,
+                              fshelp_filter filter)
+{
+  error_t err = 0;
+  pthread_mutex_lock (&translator_ihash_lock);
+
+  HURD_IHASH_ITERATE (&translator_ihash, value)
+    {
+      struct translator *t = value;
+      if (filter)
+       {
+         char *dir = strdup (t->name);
+         if (! dir)
+           {
+             err = ENOMEM;
+             break;
+           }
+
+         err = filter (dirname (dir));
+         free (dir);
+         if (err)
+           {
+             err = 0;
+             continue; /* Skip this entry.  */
+           }
+       }
+
+      err = argz_add (translators, translators_len,
+                     t->name);
+      if (err)
+       break;
+    }
+
+  pthread_mutex_unlock (&translator_ihash_lock);
+  return err;
+}
diff --git a/libihash/ihash.h b/libihash/ihash.h
index a4e76dc..3ca5ec3 100644
--- a/libihash/ihash.h
+++ b/libihash/ihash.h
@@ -218,6 +218,23 @@ hurd_ihash_value_t hurd_ihash_find (hurd_ihash_t ht, 
hurd_ihash_key_t key);
         (((_hurd_ihash_item_t) _hurd_ihash_valuep) + 1))               \
     if (val != _HURD_IHASH_EMPTY && val != _HURD_IHASH_DELETED)
 
+/* Iterate over all elements in the hash table making both the key and
+   the value available.  You use this macro with a block, for example
+   like this:
+
+     HURD_IHASH_ITERATE_ITEMS (ht, item)
+       foo (item->key, item->value);
+
+   The block will be run for every element in the hash table HT.  The
+   key and value of the current element is available as ITEM->key and
+   ITEM->value.  */
+#define HURD_IHASH_ITERATE_ITEMS(ht, item)                              \
+  for (_hurd_ihash_item_t item = (ht)->size? &(ht)->items[0]: 0;       \
+       (ht)->size && item - &(ht)->items[0] < (ht)->size;               \
+       item++)                                                          \
+    if (item->value != _HURD_IHASH_EMPTY &&                             \
+        item->value != _HURD_IHASH_DELETED)
+
 /* Remove the entry with the key KEY from the hash table HT.  If such
    an entry was found and removed, 1 is returned, otherwise 0.  */
 int hurd_ihash_remove (hurd_ihash_t ht, hurd_ihash_key_t key);
diff --git a/libnetfs/Makefile b/libnetfs/Makefile
index 24b5aca..1a71b49 100644
--- a/libnetfs/Makefile
+++ b/libnetfs/Makefile
@@ -34,7 +34,8 @@ FSSRCS= dir-link.c dir-lookup.c dir-mkdir.c dir-mkfile.c \
        file-get-translator.c file-getcontrol.c file-getlinknode.c \
        file-lock-stat.c file-lock.c file-set-size.c \
        file-set-translator.c file-statfs.c file-sync.c file-syncfs.c \
-       file-utimes.c file-reparent.c fsstubs.c
+       file-utimes.c file-reparent.c fsstubs.c file-get-transcntl.c \
+       get-source.c
 
 IOSRCS=        io-read.c io-readable.c io-seek.c io-write.c io-stat.c 
io-async.c     \
        io-set-all-openmodes.c io-get-openmodes.c io-set-some-openmodes.c     \
@@ -44,7 +45,7 @@ IOSRCS=       io-read.c io-readable.c io-seek.c io-write.c 
io-stat.c io-async.c     \
        io-version.c
 
 FSYSSRCS= fsys-syncfs.c fsys-getroot.c fsys-get-options.c fsys-set-options.c \
-       fsys-goaway.c fsysstubs.c
+       fsys-goaway.c fsysstubs.c fsys-get-children.c fsys-get-source.c
 
 IFSOCKSRCS=
 OTHERSRCS= drop-node.c init-init.c make-node.c make-peropen.c make-protid.c   \
@@ -52,7 +53,7 @@ OTHERSRCS= drop-node.c init-init.c make-node.c make-peropen.c 
make-protid.c   \
        init-startup.c startup-argp.c set-options.c append-args.c             \
        runtime-argp.c std-runtime-argp.c std-startup-argp.c                  \
        append-std-options.c trans-callback.c set-get-trans.c                 \
-       nref.c nrele.c nput.c file-get-storage-info-default.c
+       nref.c nrele.c nput.c file-get-storage-info-default.c dead-name.c
 
 SRCS= $(OTHERSRCS) $(FSSRCS) $(IOSRCS) $(FSYSSRCS) $(IFSOCKSRCS)
 
diff --git a/libdiskfs/dead-name.c b/libnetfs/dead-name.c
similarity index 88%
copy from libdiskfs/dead-name.c
copy to libnetfs/dead-name.c
index 760b36f..6f2d78d 100644
--- a/libdiskfs/dead-name.c
+++ b/libnetfs/dead-name.c
@@ -23,10 +23,10 @@
 void
 ports_dead_name (void *notify, mach_port_t dead_name)
 {
-  struct protid *pi = ports_lookup_port (diskfs_port_bucket, dead_name,
-                                        diskfs_protid_class);
+  struct protid *pi = ports_lookup_port (netfs_port_bucket, dead_name,
+                                        netfs_protid_class);
   struct node *np;
-  
+
   if (pi)
     {
       np = pi->po->np;
@@ -35,11 +35,13 @@ ports_dead_name (void *notify, mach_port_t dead_name)
        {
          mach_port_deallocate (mach_task_self (), np->sockaddr);
          np->sockaddr = MACH_PORT_NULL;
-         diskfs_nput (np);
+         netfs_nput (np);
        }
       else
        pthread_mutex_unlock (&np->lock);
     }
-  
+
+  fshelp_remove_active_translator (dead_name);
+
   ports_interrupt_notified_rpcs (notify, dead_name, MACH_NOTIFY_DEAD_NAME);
 }
diff --git a/libnetfs/dir-lookup.c b/libnetfs/dir-lookup.c
index f1b6438..10ea2d0 100644
--- a/libnetfs/dir-lookup.c
+++ b/libnetfs/dir-lookup.c
@@ -1,5 +1,6 @@
 /*
-   Copyright (C) 1995,96,97,98,99,2000,01,02 Free Software Foundation, Inc.
+   Copyright (C) 1995,96,97,98,99,2000,01,02,13
+     Free Software Foundation, Inc.
    Written by Michael I. Bushnell, p/BSG.
 
    This file is part of the GNU Hurd.
@@ -46,6 +47,7 @@ netfs_S_dir_lookup (struct protid *diruser,
   int nsymlinks = 0;
   struct node *dnp, *np;
   char *nextname;
+  char *relpath;
   error_t error;
   struct protid *newpi;
   struct iouser *user;
@@ -60,6 +62,11 @@ netfs_S_dir_lookup (struct protid *diruser,
   while (*filename == '/')
     filename++;
 
+  /* Preserve the path relative to diruser->po->path.  */
+  relpath = strdup (filename);
+  if (! relpath)
+    return ENOMEM;
+
   *retry_port_type = MACH_MSG_TYPE_MAKE_SEND;
   *do_retry = FS_RETRY_NORMAL;
   *retry_name = '\0';
@@ -390,6 +397,22 @@ netfs_S_dir_lookup (struct protid *diruser,
       goto out;
     }
 
+  free (newpi->po->path);
+  if (diruser->po->path == NULL)
+    {
+      /* diruser is the root directory.  */
+      newpi->po->path = relpath;
+      relpath = NULL; /* Do not free relpath.  */
+    }
+  else
+    {
+      newpi->po->path = NULL;
+      asprintf (&newpi->po->path, "%s/%s", diruser->po->path, relpath);
+    }
+
+  if (! newpi->po->path)
+    error = errno;
+
   *retry_port = ports_get_right (newpi);
   ports_port_deref (newpi);
 
@@ -398,5 +421,6 @@ netfs_S_dir_lookup (struct protid *diruser,
     netfs_nput (np);
   if (dnp)
     netfs_nrele (dnp);
+  free (relpath);
   return error;
 }
diff --git a/libnetfs/file-get-transcntl.c b/libnetfs/file-get-transcntl.c
new file mode 100644
index 0000000..04596d7
--- /dev/null
+++ b/libnetfs/file-get-transcntl.c
@@ -0,0 +1,52 @@
+/* libnetfs implementation of fs.defs: file_get_translator_cntl
+
+   Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation
+
+   This is a trivially adapted version of
+   libdiskfs/file-get-transcntl.c.
+
+   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 "fs_S.h"
+
+/* Implement file_get_translator_cntl as described in <hurd/fs.defs>. */
+error_t
+netfs_S_file_get_translator_cntl (struct protid *cred,
+                                 mach_port_t *ctl,
+                                 mach_msg_type_name_t *ctltype)
+{
+  struct node *np;
+  error_t err;
+
+  if (!cred)
+    return EOPNOTSUPP;
+
+  np = cred->po->np;
+
+  pthread_mutex_lock (&np->lock);
+
+  err = fshelp_isowner (&np->nn_stat, cred->user);
+  if (!err)
+    err = fshelp_fetch_control (&np->transbox, ctl);
+  if (!err && *ctl == MACH_PORT_NULL)
+    err = ENXIO;
+  if (!err)
+    *ctltype = MACH_MSG_TYPE_MOVE_SEND;
+
+  pthread_mutex_unlock (&np->lock);
+
+  return err;
+}
diff --git a/libnetfs/file-get-translator.c b/libnetfs/file-get-translator.c
index 59e6102..3a54ff1 100644
--- a/libnetfs/file-get-translator.c
+++ b/libnetfs/file-get-translator.c
@@ -109,6 +109,20 @@ netfs_S_file_get_translator (struct protid *user,
       *translen = len;
       err = 0;
     }
+  else if (np->nn_translated & S_IPTRANS)
+    {
+      char *string = NULL;
+      size_t len = 0;
+      err = netfs_get_translator (np, &string, &len);
+      if (!err)
+       {
+         if (len > *translen)
+           *trans = mmap (0, len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+         memcpy (*trans, string, len);
+         *translen = len;
+         free (string);
+       }
+    }
   else
     err = EINVAL;
 
diff --git a/libnetfs/file-set-translator.c b/libnetfs/file-set-translator.c
index b107ccd..a9883a3 100644
--- a/libnetfs/file-set-translator.c
+++ b/libnetfs/file-set-translator.c
@@ -1,5 +1,5 @@
 /*
-   Copyright (C) 1996, 1997, 1999, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997, 1999, 2001, 2013 Free Software Foundation, Inc.
    Written by Michael I. Bushnell, p/BSG.
 
    This file is part of the GNU Hurd.
@@ -175,6 +175,21 @@ netfs_S_file_set_translator (struct protid *user,
        }
     }
 
+  if (! err && user->po->path)
+    err = fshelp_set_active_translator (user->po->path, active);
+
+  if (! err && active != MACH_PORT_NULL)
+    {
+      mach_port_t old;
+      err = mach_port_request_notification (mach_task_self (), active,
+                                           MACH_NOTIFY_DEAD_NAME, 0,
+                                           user->pi.port_right,
+                                           MACH_MSG_TYPE_MAKE_SEND_ONCE,
+                                           &old);
+      if (old != MACH_PORT_NULL)
+       mach_port_deallocate (mach_task_self (), old);
+    }
+
  out:
   pthread_mutex_unlock (&np->lock);
   return err;
diff --git a/libnetfs/fsstubs.c b/libnetfs/fsstubs.c
index 708cbb8..f1b9bf7 100644
--- a/libnetfs/fsstubs.c
+++ b/libnetfs/fsstubs.c
@@ -30,14 +30,6 @@ netfs_S_file_notice_changes (struct protid *user,
 }
 
 error_t
-netfs_S_file_get_translator_cntl (struct protid *user,
-                                 mach_port_t *trans,
-                                 mach_msg_type_name_t *transtype)
-{
-  return EOPNOTSUPP;
-}
-
-error_t
 netfs_S_file_getfh (struct protid *user,
                    char **data, mach_msg_type_number_t *ndata)
 {
diff --git a/libnetfs/fsys-get-children.c b/libnetfs/fsys-get-children.c
new file mode 100644
index 0000000..fb3af91
--- /dev/null
+++ b/libnetfs/fsys-get-children.c
@@ -0,0 +1,112 @@
+/* fsys_get_children
+
+   Copyright (C) 2013 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/>.  */
+
+#include "priv.h"
+
+#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.  */
+error_t
+netfs_S_fsys_get_children (fsys_t server,
+                          mach_port_t reply,
+                          mach_msg_type_name_t replyPoly,
+                          char **children,
+                          mach_msg_type_number_t *children_len)
+{
+  error_t err;
+
+  struct protid *cred = ports_lookup_port (netfs_port_bucket,
+                                          server,
+                                          netfs_protid_class);
+  if (! cred)
+    return EOPNOTSUPP;
+
+  /* check_access performs the same permission check as is normally
+     done, i.e. it checks that all but the last path components are
+     executable by the requesting user and that the last component is
+     readable. */
+  error_t check_access (const char *path)
+  {
+    error_t err;
+    char *elements = NULL;
+    size_t elements_len = 0;
+
+    err = argz_create_sep (path, '/', &elements, &elements_len);
+    if (err)
+      return err;
+
+    struct node *dp = netfs_root_node;
+
+    /* Lock the root node. netfs_attempt_lookup expects the directory to
+       be locked.  */
+    pthread_mutex_lock (&dp->lock);
+
+    /* Increase the reference count, it will be decremented in the loop
+       ahead.  */
+    netfs_nref (dp);
+
+    for (char *entry = elements;
+        entry;
+        entry = argz_next (elements, elements_len, entry))
+      {
+       struct node *next;
+       err = netfs_attempt_lookup (cred->user, dp, entry, &next);
+       /* netfs_attempt_lookup has unlocked dp and returned next
+          locked, so there is no locking to do here.  */
+
+       /* Decrease reference count.  */
+       netfs_nrele (dp);
+
+       if (err)
+         goto errout;
+
+       dp = next;
+      }
+
+    err = fshelp_access (&dp->nn_stat, S_IRUSR, cred->user);
+
+  errout:
+    /* Unlock and unreference the last node.  */
+    netfs_nput (dp);
+
+    free (elements);
+    return err;
+  }
+
+  char *c = NULL;
+  size_t c_len = 0;
+
+  err = fshelp_get_active_translators (&c, &c_len, check_access);
+  if (err)
+    goto errout;
+
+  err = iohelp_return_malloced_buffer (c, c_len, children, children_len);
+  if (err)
+    goto errout;
+
+  c = NULL; /* c was freed by iohelp_return_malloced_buffer. */
+
+ errout:
+  free (c);
+  return err;
+}
diff --git a/libnetfs/fsys-get-source.c b/libnetfs/fsys-get-source.c
new file mode 100644
index 0000000..6143d10
--- /dev/null
+++ b/libnetfs/fsys-get-source.c
@@ -0,0 +1,34 @@
+/* fsys_get_source
+
+   Copyright (C) 2013 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/>.  */
+
+#include "priv.h"
+#include "fsys_S.h"
+
+/* Return information about the source of the receiving
+   filesystem.  */
+error_t
+netfs_S_fsys_get_source (fsys_t server,
+                        mach_port_t reply,
+                        mach_msg_type_name_t replyPoly,
+                        char *source)
+{
+  return netfs_get_source (source);
+}
diff --git a/libnetfs/fsys-getroot.c b/libnetfs/fsys-getroot.c
index a1dd5e5..0d80111 100644
--- a/libnetfs/fsys-getroot.c
+++ b/libnetfs/fsys-getroot.c
@@ -43,7 +43,11 @@ netfs_S_fsys_getroot (mach_port_t cntl,
   error_t err;
   struct protid *newpi;
   mode_t type;
-  struct peropen peropen_context = { root_parent: dotdot };
+  struct peropen peropen_context =
+    {
+      root_parent: dotdot,
+      path: NULL,
+    };
 
   if (!pt)
     return EOPNOTSUPP;
diff --git a/libnetfs/get-source.c b/libnetfs/get-source.c
new file mode 100644
index 0000000..71a9639
--- /dev/null
+++ b/libnetfs/get-source.c
@@ -0,0 +1,28 @@
+/* Default version of netfs_get_source
+
+   Copyright (C) 2013 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/>.  */
+
+#include "priv.h"
+
+error_t
+netfs_get_source (char *source)
+{
+  return EOPNOTSUPP;
+}
diff --git a/libnetfs/init-startup.c b/libnetfs/init-startup.c
index 2e0f43a..e17c4f1 100644
--- a/libnetfs/init-startup.c
+++ b/libnetfs/init-startup.c
@@ -48,5 +48,19 @@ netfs_startup (mach_port_t bootstrap, int flags)
 
   mach_port_deallocate (mach_task_self (), bootstrap);
 
+  /* Mark us as important.  */
+  mach_port_t proc = getproc ();
+  if (proc == MACH_PORT_NULL)
+    error (12, err, "Translator startup failure: getproc");
+
+  err = proc_mark_important (proc);
+
+  /* This might fail due to permissions or because the old proc server
+     is still running, ignore any such errors.  */
+  if (err && err != EPERM && err != EMIG_BAD_ID)
+    error (13, err, "Translator startup failure: proc_mark_important");
+
+  mach_port_deallocate (mach_task_self (), proc);
+
   return realnode;
 }
diff --git a/libnetfs/make-peropen.c b/libnetfs/make-peropen.c
index 92f58da..733bfdc 100644
--- a/libnetfs/make-peropen.c
+++ b/libnetfs/make-peropen.c
@@ -31,6 +31,7 @@ netfs_make_peropen (struct node *np, int flags, struct 
peropen *context)
   po->refcnt = 0;
   po->openstat = flags;
   po->np = np;
+  po->path = NULL;
 
   if (context)
     {
@@ -47,6 +48,13 @@ netfs_make_peropen (struct node *np, int flags, struct 
peropen *context)
       if (po->shadow_root_parent != MACH_PORT_NULL)
        mach_port_mod_refs (mach_task_self (), po->shadow_root_parent,
                            MACH_PORT_RIGHT_SEND, 1);
+
+      if (context->path)
+       {
+         po->path = strdup (context->path);
+         if (! po->path)
+           return ENOMEM;
+       }
     }
 
   netfs_nref (np);
diff --git a/libnetfs/netfs.h b/libnetfs/netfs.h
index d1ebed0..e10ccae 100644
--- a/libnetfs/netfs.h
+++ b/libnetfs/netfs.h
@@ -1,6 +1,6 @@
 /*
 
-   Copyright (C) 1994,95,96,97,99,2000,02 Free Software Foundation, Inc.
+   Copyright (C) 1994,95,96,97,99,2000,02,13 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
@@ -63,6 +63,8 @@ struct peropen
   mach_port_t shadow_root_parent;
   /* If in a shadow tree, its root node in this translator.  */
   struct node *shadow_root;
+
+  char *path;
 };
 
 /* A unique one of these exists for each node currently in use. */
@@ -311,6 +313,12 @@ error_t netfs_file_get_storage_info (struct iouser *cred,
                                     mach_msg_type_number_t *num_offsets,
                                     char **data,
                                     mach_msg_type_number_t *data_len);
+
+/* The user may define this function.  The function must set source to
+   the source device of the filesystem. The function may return an
+   EOPNOTSUPP to indicate that the concept of a source device is not
+   applicable. The default function always returns EOPNOTSUPP. */
+error_t netfs_get_source (char *source);
 
 /* Option parsing */
 
diff --git a/libnetfs/release-peropen.c b/libnetfs/release-peropen.c
index 3e65099..d4d3574 100644
--- a/libnetfs/release-peropen.c
+++ b/libnetfs/release-peropen.c
@@ -41,6 +41,7 @@ netfs_release_peropen (struct peropen *po)
 
       netfs_nput (po->np);
 
+      free (po->path);
       free (po);
     }
 }
diff --git a/libpager/data-request.c b/libpager/data-request.c
index 4454fac..34b8b43 100644
--- a/libpager/data-request.c
+++ b/libpager/data-request.c
@@ -119,7 +119,8 @@ _pager_seqnos_memory_object_data_request (mach_port_t 
object,
     goto error_read;
 
   memory_object_data_supply (p->memobjcntl, offset, page, length, 1,
-                            write_lock ? VM_PROT_WRITE : VM_PROT_NONE, 0,
+                            write_lock ? VM_PROT_WRITE : VM_PROT_NONE,
+                            p->notify_on_evict ? 1 : 0,
                             MACH_PORT_NULL);
   pthread_mutex_lock (&p->interlock);
   _pager_mark_object_error (p, offset, length, 0);
diff --git a/libpager/data-return.c b/libpager/data-return.c
index c70f0e8..6a3b903 100644
--- a/libpager/data-return.c
+++ b/libpager/data-return.c
@@ -39,6 +39,7 @@ _pager_do_write_request (mach_port_t object,
   struct pager *p;
   short *pm_entries;
   int npages, i;
+  char *notified;
   error_t *pagerrs;
   struct lock_request *lr;
   struct lock_list {struct lock_request *lr;
@@ -71,9 +72,6 @@ _pager_do_write_request (mach_port_t object,
       goto release_out;
     }
 
-  if (! dirty)
-    goto release_out;
-
   if (p->pager_state != NORMAL)
     {
       printf ("pager in wrong state for write\n");
@@ -83,6 +81,11 @@ _pager_do_write_request (mach_port_t object,
   npages = length / __vm_page_size;
   pagerrs = alloca (npages * sizeof (error_t));
 
+  notified = alloca (npages * (sizeof *notified));
+#ifndef NDEBUG
+  memset (notified, -1, npages * (sizeof *notified));
+#endif
+
   _pager_block_termination (p);        /* until we are done with the pagemap
                                   when the write completes. */
 
@@ -90,6 +93,24 @@ _pager_do_write_request (mach_port_t object,
 
   pm_entries = &p->pagemap[offset / __vm_page_size];
 
+  if (! dirty)
+    {
+      munmap ((void *) data, length);
+      if (!kcopy) {
+        /* Prepare notified array.  */
+        for (i = 0; i < npages; i++)
+          notified[i] = (p->notify_on_evict
+                         && ! (pm_entries[i] & PM_PAGEINWAIT));
+
+        _pager_release_seqno (p, seqno);
+        goto notify;
+      }
+      else {
+        _pager_allow_termination (p);
+        goto release_out;
+      }
+    }
+
   /* Make sure there are no other in-progress writes for any of these
      pages before we begin.  This imposes a little more serialization
      than we really have to require (because *all* future writes on
@@ -120,10 +141,6 @@ _pager_do_write_request (mach_port_t object,
     for (i = 0; i < npages; i++)
       pm_entries[i] |= PM_PAGINGOUT | PM_INIT;
 
-  if (!kcopy)
-    for (i = 0; i < npages; i++)
-      pm_entries[i] &= ~PM_INCORE;
-
   /* If this write occurs while a lock is pending, record
      it.  We have to keep this list because a lock request
      might come in while we do the I/O; in that case there
@@ -163,7 +180,10 @@ _pager_do_write_request (mach_port_t object,
   for (i = 0; i < npages; i++)
     {
       if (omitdata & (1 << i))
-       continue;
+       {
+         notified[i] = 0;
+         continue;
+       }
 
       if (pm_entries[i] & PM_WRITEWAIT)
        wakeup = 1;
@@ -179,14 +199,22 @@ _pager_do_write_request (mach_port_t object,
        pm_entries[i] |= PM_INVALID;
 
       if (pm_entries[i] & PM_PAGEINWAIT)
-       memory_object_data_supply (p->memobjcntl,
-                                  offset + (vm_page_size * i),
-                                  data + (vm_page_size * i),
-                                  vm_page_size, 1,
-                                  VM_PROT_NONE, 0, MACH_PORT_NULL);
+       {
+         memory_object_data_supply (p->memobjcntl,
+                                    offset + (vm_page_size * i),
+                                    data + (vm_page_size * i),
+                                    vm_page_size, 1,
+                                    VM_PROT_NONE, 0, MACH_PORT_NULL);
+         notified[i] = 0;
+       }
       else
-       munmap ((caddr_t) (data + (vm_page_size * i)),
-               vm_page_size);
+       {
+         munmap ((void *) (data + (vm_page_size * i)),
+                 vm_page_size);
+         notified[i] = (! kcopy && p->notify_on_evict);
+         if (! kcopy)
+           pm_entries[i] &= ~PM_INCORE;
+       }
 
       pm_entries[i] &= ~(PM_PAGINGOUT | PM_PAGEINWAIT | PM_WRITEWAIT);
     }
@@ -198,10 +226,29 @@ _pager_do_write_request (mach_port_t object,
   if (wakeup)
     pthread_cond_broadcast (&p->wakeup);
 
+ notify:
   _pager_allow_termination (p);
-
   pthread_mutex_unlock (&p->interlock);
 
+  for (i = 0; i < npages; i++)
+    {
+      assert (notified[i] == 0 || notified[i] == 1);
+      if (notified[i])
+       {
+         short *pm_entry = &pm_entries[i];
+
+         /* Do notify user.  */
+         pager_notify_evict (p->upi, offset + (i * vm_page_size));
+
+         /* Clear any error that is left.  Notification on eviction
+            is used only to change association of page, so any
+            error may no longer be valid.  */
+         pthread_mutex_lock (&p->interlock);
+         *pm_entry = SET_PM_ERROR (SET_PM_NEXTERROR (*pm_entry, 0), 0);
+         pthread_mutex_unlock (&p->interlock);
+       }
+    }
+
   ports_port_deref (p);
   return 0;
 
diff --git a/libpager/pager-create.c b/libpager/pager-create.c
index 318c9f1..1aea6e9 100644
--- a/libpager/pager-create.c
+++ b/libpager/pager-create.c
@@ -22,7 +22,8 @@ struct pager *
 pager_create (struct user_pager_info *upi,
              struct port_bucket *bucket,
              boolean_t may_cache,
-             memory_object_copy_strategy_t copy_strategy)
+             memory_object_copy_strategy_t copy_strategy,
+             boolean_t notify_on_evict)
 {
   struct pager *p;
 
@@ -38,6 +39,7 @@ pager_create (struct user_pager_info *upi,
   p->attribute_requests = 0;
   p->may_cache = may_cache;
   p->copy_strategy = copy_strategy;
+  p->notify_on_evict = notify_on_evict;
   p->memobjcntl = MACH_PORT_NULL;
   p->memobjname = MACH_PORT_NULL;
   p->seqno = -1;
diff --git a/libpager/pager.h b/libpager/pager.h
index 99fb384..75ff108 100644
--- a/libpager/pager.h
+++ b/libpager/pager.h
@@ -36,14 +36,17 @@ int pager_demuxer (mach_msg_header_t *inp,
    to receive requests.  U_PAGER will be provided to later calls to
    pager_find_address.  The pager will have one user reference
    created.  MAY_CACHE and COPY_STRATEGY are the original values of
-   those attributes as for memory_object_ready.  Users may create
-   references to pagers by use of the relevant ports library
-   functions.  On errors, return null and set errno.  */
+   those attributes as for memory_object_ready.  If NOTIFY_ON_EVICT is
+   non-zero, pager_notify_evict user callback will be called when page
+   is evicted.  Users may create references to pagers by use of the
+   relevant ports library functions.  On errors, return null and set
+   errno.  */
 struct pager *
 pager_create (struct user_pager_info *u_pager,
              struct port_bucket *bucket,
              boolean_t may_cache,
-             memory_object_copy_strategy_t copy_strategy);
+             memory_object_copy_strategy_t copy_strategy,
+             boolean_t notify_on_evict);
 
 /* Return the user_pager_info struct associated with a pager. */
 struct user_pager_info *
@@ -172,6 +175,18 @@ error_t
 pager_unlock_page (struct user_pager_info *pager,
                   vm_offset_t address);
 
+/* The user must define this function.  It is used when you want be
+   able to change association of pages to backing store.  To use it,
+   pass non-zero value in NOTIFY_ON_EVICT when pager is created with
+   pager_create.  You can change association of page only when
+   pager_notify_evict has been called and you haven't touched page
+   content after that.  Note there is a possibility that a page is
+   evicted, but user is not notified about that.  The user should be
+   able to handle this case.  */
+void
+pager_notify_evict (struct user_pager_info *pager,
+                   vm_offset_t page);
+
 /* The user must define this function.  It should report back (in
    *OFFSET and *SIZE the minimum valid address the pager will accept
    and the size of the object.   */
diff --git a/libpager/priv.h b/libpager/priv.h
index e6b2546..7aa0fb4 100644
--- a/libpager/priv.h
+++ b/libpager/priv.h
@@ -49,6 +49,7 @@ struct pager
 
   boolean_t may_cache;
   memory_object_copy_strategy_t copy_strategy;
+  boolean_t notify_on_evict;
 
   /* Interface ports */
   memory_object_control_t memobjcntl;
diff --git a/libshouldbeinlibc/Makefile b/libshouldbeinlibc/Makefile
index 31a940f..14a7939 100644
--- a/libshouldbeinlibc/Makefile
+++ b/libshouldbeinlibc/Makefile
@@ -27,9 +27,9 @@ SRCS = termsize.c timefmt.c exec-reauth.c maptime-funcs.c \
        idvec-impgids.c idvec-verify.c idvec-rep.c \
        ugids.c ugids-argp.c ugids-rep.c ugids-verify.c ugids-subtract.c \
        ugids-auth.c ugids-xinl.c ugids-merge.c ugids-imply.c ugids-posix.c \
-       ugids-verify-auth.c
+       ugids-verify-auth.c nullauth.c
 installhdrs = idvec.h timefmt.h maptime.h \
-             wire.h portinfo.h portxlate.h cacheq.h ugids.h
+             wire.h portinfo.h portxlate.h cacheq.h ugids.h nullauth.h
 installhdrsubdir = .
 
 OBJS = $(SRCS:.c=.o)
diff --git a/libshouldbeinlibc/nullauth.c b/libshouldbeinlibc/nullauth.c
new file mode 100644
index 0000000..4ba10a7
--- /dev/null
+++ b/libshouldbeinlibc/nullauth.c
@@ -0,0 +1,47 @@
+/* Drop all authentication credentials.
+
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+   Written by Justus Winter <address@hidden>
+
+   This file is part of the GNU Hurd.
+
+   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, see <http://www.gnu.org/licenses/>.  */
+
+#include <error.h>
+#include <errno.h>
+#include <hurd.h>
+
+/* Obtain an empty authentication handle and use it for further
+   authentication purposes.  This effectively drops all Unix
+   privileges.  */
+error_t
+setnullauth (void)
+{
+  error_t err;
+
+  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)
+    return err;
+
+  err = setauth (nullauth);
+  return err;
+}
diff --git a/libshouldbeinlibc/nullauth.h b/libshouldbeinlibc/nullauth.h
new file mode 100644
index 0000000..efdb5f3
--- /dev/null
+++ b/libshouldbeinlibc/nullauth.h
@@ -0,0 +1,31 @@
+/* Drop all authentication credentials.
+
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+   Written by Justus Winter <address@hidden>
+
+   This file is part of the GNU Hurd.
+
+   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, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef __NULLAUTH_H__
+#define __NULLAUTH_H__
+
+/* Obtain an empty authentication handle and use it for further
+   authentication purposes.  This effectively drops all Unix
+   privileges.  */
+error_t
+setnullauth (void);
+
+#endif  /* __NULLAUTH_H__ */
diff --git a/libstore/Makefile b/libstore/Makefile
index eafdd0a..607940b 100644
--- a/libstore/Makefile
+++ b/libstore/Makefile
@@ -60,10 +60,6 @@ OBJS = $(SRCS:.c=.o) $(GUNZIP_OBJS) $(BUNZIP2_OBJS)
 
 include ../Makeconf
 
-# Look for zip stuff
-vpath %.c $(srcdir)/../exec
-CPPFLAGS += -I$(srcdir)/../exec
-
 module-CPPFLAGS = -D'STORE_SONAME_SUFFIX=".so.$(hurd-version)"'
 module-DEPS = $(..)config.make
 
diff --git a/libstore/crypt.h b/libstore/crypt.h
new file mode 100644
index 0000000..2a4c203
--- /dev/null
+++ b/libstore/crypt.h
@@ -0,0 +1,12 @@
+/* crypt.h (dummy version) -- do not perform encryption
+ * Hardly worth copyrighting :-)
+ */
+
+#ifdef CRYPT
+#  undef CRYPT      /* dummy version */
+#endif
+
+#define RAND_HEAD_LEN  12  /* length of encryption random header */
+
+#define zencode
+#define zdecode
diff --git a/libstore/do-bunzip2.c b/libstore/do-bunzip2.c
new file mode 100644
index 0000000..716a0cd
--- /dev/null
+++ b/libstore/do-bunzip2.c
@@ -0,0 +1,1745 @@
+/* bunzip2 engine, modified by address@hidden */
+
+/* Stolen from util.c. */
+#include <stdio.h>
+#include <sys/types.h>
+
+/* I/O interface */
+extern int (*unzip_read) (char *buf, size_t maxread);
+extern void (*unzip_write) (const char *buf, size_t nwrite);
+extern void (*unzip_read_error) (void);
+extern void (*unzip_error) (const char *msg);
+
+/* bzip2 doesn't require window sliding. Just for buffering. */
+#define INBUFSIZ       0x1000
+#define OUTBUFSIZ      0x1000
+
+static unsigned char inbuf[INBUFSIZ];
+static unsigned char outbuf[OUTBUFSIZ];
+static unsigned inptr;
+static unsigned insize;
+static unsigned outcnt;
+
+/* ===========================================================================
+ * Fill the input buffer. This is called only when the buffer is empty.
+ */
+static int
+fill_inbuf (int eof_ok)
+{
+  int len;
+  
+  /* Read as much as possible */
+  insize = 0;
+  do
+    {
+      len = (*unzip_read)((char*)inbuf+insize, INBUFSIZ-insize);
+      if (len == 0 || len == EOF)
+       break;
+      insize += len;
+    }
+  while (insize < INBUFSIZ);
+  
+  if (insize == 0)
+    {
+      if (eof_ok)
+       return EOF;
+      unzip_read_error();
+    }
+  
+  inptr = 1;
+  return inbuf[0];
+}
+
+static void
+flush_outbuf (void)
+{
+  if (outcnt == 0)
+    return;
+
+  (*unzip_write) ((char *) outbuf, outcnt);
+  outcnt = 0;
+}
+
+static inline int
+bz2_getc (void *stream)
+{
+  return inptr < insize ? inbuf[inptr++] : fill_inbuf (1);
+}
+
+static inline int
+bz2_putc (int c, void *stream)
+{
+  if (outcnt == OUTBUFSIZ)
+    flush_outbuf ();
+  outbuf[outcnt++] = c;
+  return c;
+}
+
+static inline int
+bz2_ferror (void *stream)
+{
+  return 0;
+}
+
+static inline int
+bz2_fflush (void *stream)
+{
+  flush_outbuf ();
+  return 0;
+}
+
+static inline int
+bz2_fclose (void *stream)
+{
+  flush_outbuf ();
+  return 0;
+}
+
+#define fprintf(s, f...)       /**/
+
+
+/*-----------------------------------------------------------*/
+/*--- A block-sorting, lossless compressor        bzip2.c ---*/
+/*-----------------------------------------------------------*/
+
+/*--
+  This program is bzip2, a lossless, block-sorting data compressor,
+  version 0.1pl2, dated 29-Aug-1997.
+
+  Copyright (C) 1996, 1997 by Julian Seward.
+     Guildford, Surrey, UK
+     email: address@hidden
+
+  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 of the License, 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.
+
+  The GNU General Public License is contained in the file LICENSE.
+
+  This program is based on (at least) the work of:
+     Mike Burrows
+     David Wheeler
+     Peter Fenwick
+     Alistair Moffat
+     Radford Neal
+     Ian H. Witten
+     Robert Sedgewick
+     Jon L. Bentley
+
+  For more information on these sources, see the file ALGORITHMS.
+--*/
+
+/*----------------------------------------------------*/
+/*--- IMPORTANT                                    ---*/
+/*----------------------------------------------------*/
+
+/*--
+   WARNING:
+      This program (attempts to) compress data by performing several
+      non-trivial transformations on it.  Unless you are 100% familiar
+      with *all* the algorithms contained herein, and with the
+      consequences of modifying them, you should NOT meddle with the
+      compression or decompression machinery.  Incorrect changes can
+      and very likely *will* lead to disastrous loss of data.
+
+   DISCLAIMER:
+      I TAKE NO RESPONSIBILITY FOR ANY LOSS OF DATA ARISING FROM THE
+      USE OF THIS PROGRAM, HOWSOEVER CAUSED.
+
+      Every compression of a file implies an assumption that the
+      compressed file can be decompressed to reproduce the original.
+      Great efforts in design, coding and testing have been made to
+      ensure that this program works correctly.  However, the
+      complexity of the algorithms, and, in particular, the presence
+      of various special cases in the code which occur with very low
+      but non-zero probability make it impossible to rule out the
+      possibility of bugs remaining in the program.  DO NOT COMPRESS
+      ANY DATA WITH THIS PROGRAM UNLESS YOU ARE PREPARED TO ACCEPT THE
+      POSSIBILITY, HOWEVER SMALL, THAT THE DATA WILL NOT BE RECOVERABLE.
+
+      That is not to say this program is inherently unreliable.
+      Indeed, I very much hope the opposite is true.  bzip2 has been
+      carefully constructed and extensively tested.
+
+   PATENTS:
+      To the best of my knowledge, bzip2 does not use any patented
+      algorithms.  However, I do not have the resources available to
+      carry out a full patent search.  Therefore I cannot give any
+      guarantee of the above statement.
+--*/
+
+
+
+/*----------------------------------------------------*/
+/*--- and now for something much more pleasant :-) ---*/
+/*----------------------------------------------------*/
+
+/*---------------------------------------------*/
+/*--
+  Place a 1 beside your platform, and 0 elsewhere.
+--*/
+
+/*--
+  Generic 32-bit Unix.
+  Also works on 64-bit Unix boxes.
+--*/
+#define BZ_UNIX      1
+
+/*--
+  Win32, as seen by Jacob Navia's excellent
+  port of (Chris Fraser & David Hanson)'s excellent
+  lcc compiler.
+--*/
+#define BZ_LCCWIN32  0
+
+
+
+/*---------------------------------------------*/
+/*--
+  Some stuff for all platforms.
+--*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#if DEBUG
+  #include <assert.h>
+#endif
+#include <string.h>
+#include <signal.h>
+#include <math.h>
+
+#define ERROR_IF_EOF(i)       { if ((i) == EOF)  ioError(); }
+#define ERROR_IF_NOT_ZERO(i)  { if ((i) != 0)    ioError(); }
+#define ERROR_IF_MINUS_ONE(i) { if ((i) == (-1)) ioError(); }
+
+
+/*---------------------------------------------*/
+/*--
+   Platform-specific stuff.
+--*/
+
+#if BZ_UNIX
+   #include <sys/types.h>
+   #include <utime.h>
+   #include <unistd.h>
+   #include <malloc.h>
+   #include <sys/stat.h>
+   #include <sys/times.h>
+
+   #define Int32   int
+   #define UInt32  unsigned int
+   #define Char    char
+   #define UChar   unsigned char
+   #define Int16   short
+   #define UInt16  unsigned short
+
+   #define PATH_SEP    '/'
+   #define MY_LSTAT    lstat
+   #define MY_S_IFREG  S_ISREG
+   #define MY_STAT     stat
+
+   #define APPEND_FILESPEC(root, name) \
+      root=snocString((root), (name))
+
+   #define SET_BINARY_MODE(fd) /**/
+
+   /*--
+      You should try very hard to persuade your C compiler
+      to inline the bits marked INLINE.  Otherwise bzip2 will
+      run rather slowly.  gcc version 2.x is recommended.
+   --*/
+   #ifdef __GNUC__
+      #define INLINE   inline
+      #define NORETURN __attribute__ ((noreturn))
+   #else
+      #define INLINE   /**/
+      #define NORETURN /**/
+   #endif
+#endif
+
+
+
+#if BZ_LCCWIN32
+   #include <io.h>
+   #include <fcntl.h>
+   #include <sys\stat.h>
+
+   #define Int32   int
+   #define UInt32  unsigned int
+   #define Int16   short
+   #define UInt16  unsigned short
+   #define Char    char
+   #define UChar   unsigned char
+
+   #define INLINE         /**/
+   #define NORETURN       /**/
+   #define PATH_SEP       '\\'
+   #define MY_LSTAT       _stat
+   #define MY_STAT        _stat
+   #define MY_S_IFREG(x)  ((x) & _S_IFREG)
+
+   #if 0
+   /*-- lcc-win32 seems to expand wildcards itself --*/
+   #define APPEND_FILESPEC(root, spec)                \
+      do {                                            \
+         if ((spec)[0] == '-') {                      \
+            root = snocString((root), (spec));        \
+         } else {                                     \
+            struct _finddata_t c_file;                \
+            long hFile;                               \
+            hFile = _findfirst((spec), &c_file);      \
+            if ( hFile == -1L ) {                     \
+               root = snocString ((root), (spec));    \
+            } else {                                  \
+               int anInt = 0;                         \
+               while ( anInt == 0 ) {                 \
+                  root = snocString((root),           \
+                            &c_file.name[0]);         \
+                  anInt = _findnext(hFile, &c_file);  \
+               }                                      \
+            }                                         \
+         }                                            \
+      } while ( 0 )
+   #else
+   #define APPEND_FILESPEC(root, name)                \
+      root = snocString ((root), (name))
+   #endif
+
+   #define SET_BINARY_MODE(fd)                        \
+      do {                                            \
+         int retVal = setmode ( fileno ( fd ),        \
+                               O_BINARY );            \
+         ERROR_IF_MINUS_ONE ( retVal );               \
+      } while ( 0 )
+
+#endif
+
+
+/*---------------------------------------------*/
+/*--
+  Some more stuff for all platforms :-)
+--*/
+
+#define Bool   unsigned char
+#define True   1
+#define False  0
+
+/*--
+  IntNative is your platform's `native' int size.
+  Only here to avoid probs with 64-bit platforms.
+--*/
+#define IntNative int
+
+
+/*--
+   change to 1, or compile with -DDEBUG=1 to debug
+--*/
+#ifndef DEBUG
+#define DEBUG 0
+#endif
+
+
+/*---------------------------------------------------*/
+/*---                                             ---*/
+/*---------------------------------------------------*/
+
+/*--
+   Implementation notes, July 1997
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+   Memory allocation
+   ~~~~~~~~~~~~~~~~~
+   All large data structures are allocated on the C heap,
+   for better or for worse.  That includes the various
+   arrays of pointers, striped words, bytes, frequency
+   tables and buffers for compression and decompression.
+
+   bzip2 can operate at various block-sizes, ranging from
+   100k to 900k in 100k steps, and it allocates only as
+   much as it needs to.  When compressing, we know from the
+   command-line options what the block-size is going to be,
+   so all allocation can be done at start-up; if that
+   succeeds, there can be no further allocation problems.
+
+   Decompression is more complicated.  Each compressed file
+   contains, in its header, a byte indicating the block
+   size used for compression.  This means bzip2 potentially
+   needs to reallocate memory for each file it deals with,
+   which in turn opens the possibility for a memory allocation
+   failure part way through a run of files, by encountering
+   a file requiring a much larger block size than all the
+   ones preceding it.
+
+   The policy is to simply give up if a memory allocation
+   failure occurs.  During decompression, it would be
+   possible to move on to subsequent files in the hope that
+   some might ask for a smaller block size, but the
+   complications for doing this seem more trouble than they
+   are worth.
+
+
+   Compressed file formats
+   ~~~~~~~~~~~~~~~~~~~~~~~
+   [This is now entirely different from both 0.21, and from
+    any previous Huffman-coded variant of bzip.
+    See the associated file bzip2.txt for details.]
+
+
+   Error conditions
+   ~~~~~~~~~~~~~~~~
+   Dealing with error conditions is the least satisfactory
+   aspect of bzip2.  The policy is to try and leave the
+   filesystem in a consistent state, then quit, even if it
+   means not processing some of the files mentioned in the
+   command line.  `A consistent state' means that a file
+   exists either in its compressed or uncompressed form,
+   but not both.  This boils down to the rule `delete the
+   output file if an error condition occurs, leaving the
+   input intact'.  Input files are only deleted when we can
+   be pretty sure the output file has been written and
+   closed successfully.
+
+   Errors are a dog because there's so many things to
+   deal with.  The following can happen mid-file, and
+   require cleaning up.
+
+     internal `panics' -- indicating a bug
+     corrupted or inconsistent compressed file
+     can't allocate enough memory to decompress this file
+     I/O error reading/writing/opening/closing
+     signal catches -- Control-C, SIGTERM, SIGHUP.
+
+   Other conditions, primarily pertaining to file names,
+   can be checked in-between files, which makes dealing
+   with them easier.
+--*/
+
+
+
+/*---------------------------------------------------*/
+/*--- Misc (file handling) data decls             ---*/
+/*---------------------------------------------------*/
+
+static UInt32  bytesIn, bytesOut;
+static Int32   verbosity;
+static Bool    smallMode;
+static UInt32  globalCrc;
+
+
+
+
+static void    panic                 ( Char* );
+static void    ioError               ( void );
+static void    uncompressOutOfMemory ( Int32, Int32 );
+static void    blockOverrun          ( void );
+static void    badBlockHeader        ( void );
+static void    crcError              ( UInt32, UInt32 );
+static void    cleanUpAndFail        ( Int32 );
+static void    compressedStreamEOF   ( void );
+
+
+
+/*---------------------------------------------------*/
+/*--- Data decls for the front end                ---*/
+/*---------------------------------------------------*/
+
+/*--
+   The overshoot bytes allow us to avoid most of
+   the cost of pointer renormalisation during
+   comparison of rotations in sorting.
+   The figure of 20 is derived as follows:
+      qSort3 allows an overshoot of up to 10.
+      It then calls simpleSort, which calls
+      fullGtU, also with max overshoot 10.
+      fullGtU does up to 10 comparisons without
+      renormalising, giving 10+10 == 20.
+--*/
+#define NUM_OVERSHOOT_BYTES 20
+
+/*--
+  These are the main data structures for
+  the Burrows-Wheeler transform.
+--*/
+
+/*--
+  Pointers to compression and decompression
+  structures.  Set by
+     allocateCompressStructures   and
+     setDecompressStructureSizes
+
+  The structures are always set to be suitable
+  for a block of size 100000 * blockSize100k.
+--*/
+static UInt16   *ll16;     /*-- small decompress --*/
+static UChar    *ll4;      /*-- small decompress --*/
+
+static Int32    *tt;       /*-- fast decompress  --*/
+static UChar    *ll8;      /*-- fast decompress  --*/
+
+
+/*--
+  freq table collected to save a pass over the data
+  during decompression.
+--*/
+static Int32   unzftab[256];
+
+
+/*--
+   index of the last char in the block, so
+   the block size == last + 1.
+--*/
+static Int32  last;
+
+
+/*--
+  index in zptr[] of original string after sorting.
+--*/
+static Int32  origPtr;
+
+
+/*--
+  always: in the range 0 .. 9.
+  The current block size is 100000 * this number.
+--*/
+static Int32  blockSize100k;
+
+
+/*--
+  Used when sorting.  If too many long comparisons
+  happen, we stop sorting, randomise the block 
+  slightly, and try again.
+--*/
+
+static Bool   blockRandomised;
+
+
+
+/*---------------------------------------------------*/
+/*--- Data decls for the back end                 ---*/
+/*---------------------------------------------------*/
+
+#define MAX_ALPHA_SIZE 258
+#define MAX_CODE_LEN    23
+
+#define RUNA 0
+#define RUNB 1
+
+#define N_GROUPS 6
+#define G_SIZE   50
+#define N_ITERS  4
+
+#define MAX_SELECTORS (2 + (900000 / G_SIZE))
+
+static Bool  inUse[256];
+static Int32 nInUse;
+
+static UChar seqToUnseq[256];
+static UChar unseqToSeq[256];
+
+static UChar selector   [MAX_SELECTORS];
+static UChar selectorMtf[MAX_SELECTORS];
+
+static UChar len  [N_GROUPS][MAX_ALPHA_SIZE];
+
+/*-- decompress only --*/
+static Int32 limit  [N_GROUPS][MAX_ALPHA_SIZE];
+static Int32 base   [N_GROUPS][MAX_ALPHA_SIZE];
+static Int32 perm   [N_GROUPS][MAX_ALPHA_SIZE];
+static Int32 minLens[N_GROUPS];
+
+
+/*---------------------------------------------------*/
+/*--- 32-bit CRC grunge                           ---*/
+/*---------------------------------------------------*/
+
+/*--
+  I think this is an implementation of the AUTODIN-II,
+  Ethernet & FDDI 32-bit CRC standard.  Vaguely derived
+  from code by Rob Warnock, in Section 51 of the
+  comp.compression FAQ.
+--*/
+
+static UInt32 crc32Table[256] = {
+
+   /*-- Ugly, innit? --*/
+
+   0x00000000UL, 0x04c11db7UL, 0x09823b6eUL, 0x0d4326d9UL,
+   0x130476dcUL, 0x17c56b6bUL, 0x1a864db2UL, 0x1e475005UL,
+   0x2608edb8UL, 0x22c9f00fUL, 0x2f8ad6d6UL, 0x2b4bcb61UL,
+   0x350c9b64UL, 0x31cd86d3UL, 0x3c8ea00aUL, 0x384fbdbdUL,
+   0x4c11db70UL, 0x48d0c6c7UL, 0x4593e01eUL, 0x4152fda9UL,
+   0x5f15adacUL, 0x5bd4b01bUL, 0x569796c2UL, 0x52568b75UL,
+   0x6a1936c8UL, 0x6ed82b7fUL, 0x639b0da6UL, 0x675a1011UL,
+   0x791d4014UL, 0x7ddc5da3UL, 0x709f7b7aUL, 0x745e66cdUL,
+   0x9823b6e0UL, 0x9ce2ab57UL, 0x91a18d8eUL, 0x95609039UL,
+   0x8b27c03cUL, 0x8fe6dd8bUL, 0x82a5fb52UL, 0x8664e6e5UL,
+   0xbe2b5b58UL, 0xbaea46efUL, 0xb7a96036UL, 0xb3687d81UL,
+   0xad2f2d84UL, 0xa9ee3033UL, 0xa4ad16eaUL, 0xa06c0b5dUL,
+   0xd4326d90UL, 0xd0f37027UL, 0xddb056feUL, 0xd9714b49UL,
+   0xc7361b4cUL, 0xc3f706fbUL, 0xceb42022UL, 0xca753d95UL,
+   0xf23a8028UL, 0xf6fb9d9fUL, 0xfbb8bb46UL, 0xff79a6f1UL,
+   0xe13ef6f4UL, 0xe5ffeb43UL, 0xe8bccd9aUL, 0xec7dd02dUL,
+   0x34867077UL, 0x30476dc0UL, 0x3d044b19UL, 0x39c556aeUL,
+   0x278206abUL, 0x23431b1cUL, 0x2e003dc5UL, 0x2ac12072UL,
+   0x128e9dcfUL, 0x164f8078UL, 0x1b0ca6a1UL, 0x1fcdbb16UL,
+   0x018aeb13UL, 0x054bf6a4UL, 0x0808d07dUL, 0x0cc9cdcaUL,
+   0x7897ab07UL, 0x7c56b6b0UL, 0x71159069UL, 0x75d48ddeUL,
+   0x6b93dddbUL, 0x6f52c06cUL, 0x6211e6b5UL, 0x66d0fb02UL,
+   0x5e9f46bfUL, 0x5a5e5b08UL, 0x571d7dd1UL, 0x53dc6066UL,
+   0x4d9b3063UL, 0x495a2dd4UL, 0x44190b0dUL, 0x40d816baUL,
+   0xaca5c697UL, 0xa864db20UL, 0xa527fdf9UL, 0xa1e6e04eUL,
+   0xbfa1b04bUL, 0xbb60adfcUL, 0xb6238b25UL, 0xb2e29692UL,
+   0x8aad2b2fUL, 0x8e6c3698UL, 0x832f1041UL, 0x87ee0df6UL,
+   0x99a95df3UL, 0x9d684044UL, 0x902b669dUL, 0x94ea7b2aUL,
+   0xe0b41de7UL, 0xe4750050UL, 0xe9362689UL, 0xedf73b3eUL,
+   0xf3b06b3bUL, 0xf771768cUL, 0xfa325055UL, 0xfef34de2UL,
+   0xc6bcf05fUL, 0xc27dede8UL, 0xcf3ecb31UL, 0xcbffd686UL,
+   0xd5b88683UL, 0xd1799b34UL, 0xdc3abdedUL, 0xd8fba05aUL,
+   0x690ce0eeUL, 0x6dcdfd59UL, 0x608edb80UL, 0x644fc637UL,
+   0x7a089632UL, 0x7ec98b85UL, 0x738aad5cUL, 0x774bb0ebUL,
+   0x4f040d56UL, 0x4bc510e1UL, 0x46863638UL, 0x42472b8fUL,
+   0x5c007b8aUL, 0x58c1663dUL, 0x558240e4UL, 0x51435d53UL,
+   0x251d3b9eUL, 0x21dc2629UL, 0x2c9f00f0UL, 0x285e1d47UL,
+   0x36194d42UL, 0x32d850f5UL, 0x3f9b762cUL, 0x3b5a6b9bUL,
+   0x0315d626UL, 0x07d4cb91UL, 0x0a97ed48UL, 0x0e56f0ffUL,
+   0x1011a0faUL, 0x14d0bd4dUL, 0x19939b94UL, 0x1d528623UL,
+   0xf12f560eUL, 0xf5ee4bb9UL, 0xf8ad6d60UL, 0xfc6c70d7UL,
+   0xe22b20d2UL, 0xe6ea3d65UL, 0xeba91bbcUL, 0xef68060bUL,
+   0xd727bbb6UL, 0xd3e6a601UL, 0xdea580d8UL, 0xda649d6fUL,
+   0xc423cd6aUL, 0xc0e2d0ddUL, 0xcda1f604UL, 0xc960ebb3UL,
+   0xbd3e8d7eUL, 0xb9ff90c9UL, 0xb4bcb610UL, 0xb07daba7UL,
+   0xae3afba2UL, 0xaafbe615UL, 0xa7b8c0ccUL, 0xa379dd7bUL,
+   0x9b3660c6UL, 0x9ff77d71UL, 0x92b45ba8UL, 0x9675461fUL,
+   0x8832161aUL, 0x8cf30badUL, 0x81b02d74UL, 0x857130c3UL,
+   0x5d8a9099UL, 0x594b8d2eUL, 0x5408abf7UL, 0x50c9b640UL,
+   0x4e8ee645UL, 0x4a4ffbf2UL, 0x470cdd2bUL, 0x43cdc09cUL,
+   0x7b827d21UL, 0x7f436096UL, 0x7200464fUL, 0x76c15bf8UL,
+   0x68860bfdUL, 0x6c47164aUL, 0x61043093UL, 0x65c52d24UL,
+   0x119b4be9UL, 0x155a565eUL, 0x18197087UL, 0x1cd86d30UL,
+   0x029f3d35UL, 0x065e2082UL, 0x0b1d065bUL, 0x0fdc1becUL,
+   0x3793a651UL, 0x3352bbe6UL, 0x3e119d3fUL, 0x3ad08088UL,
+   0x2497d08dUL, 0x2056cd3aUL, 0x2d15ebe3UL, 0x29d4f654UL,
+   0xc5a92679UL, 0xc1683bceUL, 0xcc2b1d17UL, 0xc8ea00a0UL,
+   0xd6ad50a5UL, 0xd26c4d12UL, 0xdf2f6bcbUL, 0xdbee767cUL,
+   0xe3a1cbc1UL, 0xe760d676UL, 0xea23f0afUL, 0xeee2ed18UL,
+   0xf0a5bd1dUL, 0xf464a0aaUL, 0xf9278673UL, 0xfde69bc4UL,
+   0x89b8fd09UL, 0x8d79e0beUL, 0x803ac667UL, 0x84fbdbd0UL,
+   0x9abc8bd5UL, 0x9e7d9662UL, 0x933eb0bbUL, 0x97ffad0cUL,
+   0xafb010b1UL, 0xab710d06UL, 0xa6322bdfUL, 0xa2f33668UL,
+   0xbcb4666dUL, 0xb8757bdaUL, 0xb5365d03UL, 0xb1f740b4UL
+};
+
+
+/*---------------------------------------------*/
+
+static void initialiseCRC ( void )
+{
+   globalCrc = 0xffffffffUL;
+}
+
+
+/*---------------------------------------------*/
+
+static UInt32 getFinalCRC ( void )
+{
+   return ~globalCrc;
+}
+
+
+/*---------------------------------------------*/
+
+static UInt32 getGlobalCRC ( void )
+{
+   return globalCrc;
+}
+
+
+/*---------------------------------------------*/
+
+static void setGlobalCRC ( UInt32 newCrc )
+{
+   globalCrc = newCrc;
+}
+
+
+/*---------------------------------------------*/
+
+#define UPDATE_CRC(crcVar,cha)              \
+{                                           \
+   crcVar = (crcVar << 8) ^                 \
+            crc32Table[(crcVar >> 24) ^     \
+                       ((UChar)cha)];       \
+}
+
+
+/*---------------------------------------------------*/
+/*--- Bit stream I/O                              ---*/
+/*---------------------------------------------------*/
+
+
+static UInt32 bsBuff;
+static Int32  bsLive;
+static void*  bsStream;
+static Bool   bsWriting;
+
+
+/*---------------------------------------------*/
+
+static void bsSetStream ( void* f, Bool wr )
+{
+   if (bsStream != NULL) panic ( "bsSetStream" );
+   bsStream = f;
+   bsLive = 0;
+   bsBuff = 0;
+   bytesOut = 0;
+   bytesIn = 0;
+   bsWriting = wr;
+}
+
+
+/*---------------------------------------------*/
+
+static void bsFinishedWithStream ( void )
+{
+   if (bsWriting)
+      while (bsLive > 0) {
+         bz2_putc ( (UChar)(bsBuff >> 24), bsStream );
+         bsBuff <<= 8;
+         bsLive -= 8;
+         bytesOut++;
+      }
+   bsStream = NULL;
+}
+
+
+/*---------------------------------------------*/
+
+#define bsNEEDR(nz)                           \
+{                                             \
+   while (bsLive < nz) {                      \
+      Int32 zzi = bz2_getc ( bsStream );      \
+      if (zzi == EOF) compressedStreamEOF();  \
+      bsBuff = (bsBuff << 8) | (zzi & 0xffL); \
+      bsLive += 8;                            \
+   }                                          \
+}
+
+
+/*---------------------------------------------*/
+
+#define bsR1(vz)                              \
+{                                             \
+   bsNEEDR(1);                                \
+   vz = (bsBuff >> (bsLive-1)) & 1;           \
+   bsLive--;                                  \
+}
+
+
+/*---------------------------------------------*/
+
+static INLINE UInt32 bsR ( Int32 n )
+{
+   UInt32 v;
+   bsNEEDR ( n );
+   v = (bsBuff >> (bsLive-n)) & ((1 << n)-1);
+   bsLive -= n;
+   return v;
+}
+
+
+/*---------------------------------------------*/
+
+static UChar bsGetUChar ( void )
+{
+   return (UChar)bsR(8);
+}
+
+
+
+/*---------------------------------------------*/
+
+static Int32 bsGetUInt32 ( void )
+{
+   UInt32 u;
+   u = 0;
+   u = (u << 8) | bsR(8);
+   u = (u << 8) | bsR(8);
+   u = (u << 8) | bsR(8);
+   u = (u << 8) | bsR(8);
+   return u;
+}
+
+
+/*---------------------------------------------*/
+
+static UInt32 bsGetIntVS ( UInt32 numBits )
+{
+   return (UInt32)bsR(numBits);
+}
+
+
+
+/*---------------------------------------------------*/
+/*--- Huffman coding low-level stuff              ---*/
+/*---------------------------------------------------*/
+
+
+/*---------------------------------------------*/
+
+static void hbCreateDecodeTables ( Int32 *limit,
+                                  Int32 *base,
+                                  Int32 *perm,
+                                  UChar *length,
+                                  Int32 minLen,
+                                  Int32 maxLen,
+                                  Int32 alphaSize )
+{
+   Int32 pp, i, j, vec;
+
+   pp = 0;
+   for (i = minLen; i <= maxLen; i++)
+      for (j = 0; j < alphaSize; j++)
+         if (length[j] == i) { perm[pp] = j; pp++; };
+
+   for (i = 0; i < MAX_CODE_LEN; i++) base[i] = 0;
+   for (i = 0; i < alphaSize; i++) base[length[i]+1]++;
+
+   for (i = 1; i < MAX_CODE_LEN; i++) base[i] += base[i-1];
+
+   for (i = 0; i < MAX_CODE_LEN; i++) limit[i] = 0;
+   vec = 0;
+
+   for (i = minLen; i <= maxLen; i++) {
+      vec += (base[i+1] - base[i]);
+      limit[i] = vec-1;
+      vec <<= 1;
+   }
+   for (i = minLen + 1; i <= maxLen; i++)
+      base[i] = ((limit[i-1] + 1) << 1) - base[i];
+}
+
+
+
+/*---------------------------------------------------*/
+/*--- Undoing the reversible transformation       ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------*/
+
+#define SET_LL4(i,n)                                          \
+   { if (((i) & 0x1) == 0)                                    \
+        ll4[(i) >> 1] = (ll4[(i) >> 1] & 0xf0) | (n); else    \
+        ll4[(i) >> 1] = (ll4[(i) >> 1] & 0x0f) | ((n) << 4);  \
+   }
+
+
+#define GET_LL4(i)                             \
+    (((UInt32)(ll4[(i) >> 1])) >> (((i) << 2) & 0x4) & 0xF)
+
+
+#define SET_LL(i,n)                       \
+   { ll16[i] = (UInt16)(n & 0x0000ffff);  \
+     SET_LL4(i, n >> 16);                 \
+   }
+
+
+#define GET_LL(i) \
+   (((UInt32)ll16[i]) | (GET_LL4(i) << 16))
+
+
+/*---------------------------------------------*/
+/*--
+  Manage memory for compression/decompression.
+  When compressing, a single block size applies to
+  all files processed, and that's set when the
+  program starts.  But when decompressing, each file
+  processed could have been compressed with a
+  different block size, so we may have to free
+  and reallocate on a per-file basis.
+
+  A call with argument of zero means
+  `free up everything.'  And a value of zero for
+  blockSize100k means no memory is currently allocated.
+--*/
+
+
+/*---------------------------------------------*/
+
+static void setDecompressStructureSizes ( Int32 newSize100k )
+{
+   if (! (0 <= newSize100k   && newSize100k   <= 9 &&
+          0 <= blockSize100k && blockSize100k <= 9))
+      panic ( "setDecompressStructureSizes" );
+
+   if (newSize100k == blockSize100k) return;
+
+   blockSize100k = newSize100k;
+
+   if (ll16  != NULL) free ( ll16  );
+   if (ll4   != NULL) free ( ll4   );
+   if (ll8   != NULL) free ( ll8   );
+   if (tt    != NULL) free ( tt    );
+
+   if (newSize100k == 0) return;
+
+   if (smallMode) {
+
+      Int32 n = 100000 * newSize100k;
+      ll16    = malloc ( n * sizeof(UInt16) );
+      ll4     = malloc ( ((n+1) >> 1) * sizeof(UChar) );
+
+      if (ll4 == NULL || ll16 == NULL) {
+         Int32 totalDraw
+            = n * sizeof(Int16) + ((n+1) >> 1) * sizeof(UChar);
+         uncompressOutOfMemory ( totalDraw, n );
+      }
+
+   } else {
+
+      Int32 n = 100000 * newSize100k;
+      ll8     = malloc ( n * sizeof(UChar) );
+      tt      = malloc ( n * sizeof(Int32) );
+
+      if (ll8 == NULL || tt == NULL) {
+         Int32 totalDraw
+            = n * sizeof(UChar) + n * sizeof(UInt32);
+         uncompressOutOfMemory ( totalDraw, n );
+      }
+
+   }
+}
+
+
+
+/*---------------------------------------------------*/
+/*--- The new back end                            ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------*/
+
+static void makeMaps ( void )
+{
+   Int32 i;
+   nInUse = 0;
+   for (i = 0; i < 256; i++)
+      if (inUse[i]) {
+         seqToUnseq[nInUse] = i;
+         unseqToSeq[i] = nInUse;
+         nInUse++;
+      }
+}
+
+
+
+/*---------------------------------------------*/
+
+static void recvDecodingTables ( void )
+{
+   Int32 i, j, t, nGroups, nSelectors, alphaSize;
+   Int32 minLen, maxLen;
+   Bool inUse16[16];
+
+   /*--- Receive the mapping table ---*/
+   for (i = 0; i < 16; i++)
+      if (bsR(1) == 1) 
+         inUse16[i] = True; else 
+         inUse16[i] = False;
+
+   for (i = 0; i < 256; i++) inUse[i] = False;
+
+   for (i = 0; i < 16; i++)
+      if (inUse16[i])
+         for (j = 0; j < 16; j++)
+            if (bsR(1) == 1) inUse[i * 16 + j] = True;
+
+   makeMaps();
+   alphaSize = nInUse+2;
+
+   /*--- Now the selectors ---*/
+   nGroups = bsR ( 3 );
+   nSelectors = bsR ( 15 );
+   for (i = 0; i < nSelectors; i++) {
+      j = 0;
+      while (bsR(1) == 1) j++;
+      selectorMtf[i] = j;
+   }
+
+   /*--- Undo the MTF values for the selectors. ---*/
+   {
+      UChar pos[N_GROUPS], tmp, v;
+      for (v = 0; v < nGroups; v++) pos[v] = v;
+   
+      for (i = 0; i < nSelectors; i++) {
+         v = selectorMtf[i];
+         tmp = pos[v];
+         while (v > 0) { pos[v] = pos[v-1]; v--; }
+         pos[0] = tmp;
+         selector[i] = tmp;
+      }
+   }
+
+   /*--- Now the coding tables ---*/
+   for (t = 0; t < nGroups; t++) {
+      Int32 curr = bsR ( 5 );
+      for (i = 0; i < alphaSize; i++) {
+         while (bsR(1) == 1) {
+            if (bsR(1) == 0) curr++; else curr--;
+         }
+         len[t][i] = curr;
+      }
+   }
+
+   /*--- Create the Huffman decoding tables ---*/
+   for (t = 0; t < nGroups; t++) {
+      minLen = 32;
+      maxLen = 0;
+      for (i = 0; i < alphaSize; i++) {
+         if (len[t][i] > maxLen) maxLen = len[t][i];
+         if (len[t][i] < minLen) minLen = len[t][i];
+      }
+      hbCreateDecodeTables ( 
+         &limit[t][0], &base[t][0], &perm[t][0], &len[t][0],
+         minLen, maxLen, alphaSize
+      );
+      minLens[t] = minLen;
+   }
+}
+
+
+/*---------------------------------------------*/
+
+#define GET_MTF_VAL(lval)                 \
+{                                         \
+   Int32 zt, zn, zvec, zj;                \
+   if (groupPos == 0) {                   \
+      groupNo++;                          \
+      groupPos = G_SIZE;                  \
+   }                                      \
+   groupPos--;                            \
+   zt = selector[groupNo];                \
+   zn = minLens[zt];                      \
+   zvec = bsR ( zn );                     \
+   while (zvec > limit[zt][zn]) {         \
+      zn++; bsR1(zj);                     \
+      zvec = (zvec << 1) | zj;            \
+   };                                     \
+   lval = perm[zt][zvec - base[zt][zn]];  \
+}
+
+
+/*---------------------------------------------*/
+
+static void getAndMoveToFrontDecode ( void )
+{
+   UChar  yy[256];
+   Int32  i, j, nextSym, limitLast;
+   Int32  EOB, groupNo, groupPos;
+
+   limitLast = 100000 * blockSize100k;
+   origPtr   = bsGetIntVS ( 24 );
+
+   recvDecodingTables();
+   EOB      = nInUse+1;
+   groupNo  = -1;
+   groupPos = 0;
+
+   /*--
+      Setting up the unzftab entries here is not strictly
+      necessary, but it does save having to do it later
+      in a separate pass, and so saves a block's worth of
+      cache misses.
+   --*/
+   for (i = 0; i <= 255; i++) unzftab[i] = 0;
+
+   for (i = 0; i <= 255; i++) yy[i] = (UChar) i;
+
+   last = -1;
+
+   GET_MTF_VAL(nextSym);
+
+   while (True) {
+
+      if (nextSym == EOB) break;
+
+      if (nextSym == RUNA || nextSym == RUNB) {
+         UChar ch;
+         Int32 s = -1;
+         Int32 N = 1;
+         do {
+            if (nextSym == RUNA) s = s + (0+1) * N; else
+            if (nextSym == RUNB) s = s + (1+1) * N;
+            N = N * 2;
+            GET_MTF_VAL(nextSym);
+         }
+            while (nextSym == RUNA || nextSym == RUNB);
+
+         s++;
+         ch = seqToUnseq[yy[0]];
+         unzftab[ch] += s;
+
+         if (smallMode)
+            while (s > 0) {
+               last++; 
+               ll16[last] = ch;
+               s--;
+            }
+         else
+            while (s > 0) {
+               last++;
+               ll8[last] = ch;
+               s--;
+            };
+
+         if (last >= limitLast) blockOverrun();
+         continue;
+
+      } else {
+
+         UChar tmp;
+         last++; if (last >= limitLast) blockOverrun();
+
+         tmp = yy[nextSym-1];
+         unzftab[seqToUnseq[tmp]]++;
+         if (smallMode)
+            ll16[last] = seqToUnseq[tmp]; else
+            ll8[last]  = seqToUnseq[tmp];
+
+         /*--
+            This loop is hammered during decompression,
+            hence the unrolling.
+
+            for (j = nextSym-1; j > 0; j--) yy[j] = yy[j-1];
+         --*/
+
+         j = nextSym-1;
+         for (; j > 3; j -= 4) {
+            yy[j]   = yy[j-1];
+            yy[j-1] = yy[j-2];
+            yy[j-2] = yy[j-3];
+            yy[j-3] = yy[j-4];
+         }
+         for (; j > 0; j--) yy[j] = yy[j-1];
+
+         yy[0] = tmp;
+         GET_MTF_VAL(nextSym);
+         continue;
+      }
+   }
+}
+
+
+/*---------------------------------------------------*/
+/*--- Stuff for randomising repetitive blocks     ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------*/
+static Int32 rNums[512] = { 
+   619, 720, 127, 481, 931, 816, 813, 233, 566, 247, 
+   985, 724, 205, 454, 863, 491, 741, 242, 949, 214, 
+   733, 859, 335, 708, 621, 574, 73, 654, 730, 472, 
+   419, 436, 278, 496, 867, 210, 399, 680, 480, 51, 
+   878, 465, 811, 169, 869, 675, 611, 697, 867, 561, 
+   862, 687, 507, 283, 482, 129, 807, 591, 733, 623, 
+   150, 238, 59, 379, 684, 877, 625, 169, 643, 105, 
+   170, 607, 520, 932, 727, 476, 693, 425, 174, 647, 
+   73, 122, 335, 530, 442, 853, 695, 249, 445, 515, 
+   909, 545, 703, 919, 874, 474, 882, 500, 594, 612, 
+   641, 801, 220, 162, 819, 984, 589, 513, 495, 799, 
+   161, 604, 958, 533, 221, 400, 386, 867, 600, 782, 
+   382, 596, 414, 171, 516, 375, 682, 485, 911, 276, 
+   98, 553, 163, 354, 666, 933, 424, 341, 533, 870, 
+   227, 730, 475, 186, 263, 647, 537, 686, 600, 224, 
+   469, 68, 770, 919, 190, 373, 294, 822, 808, 206, 
+   184, 943, 795, 384, 383, 461, 404, 758, 839, 887, 
+   715, 67, 618, 276, 204, 918, 873, 777, 604, 560, 
+   951, 160, 578, 722, 79, 804, 96, 409, 713, 940, 
+   652, 934, 970, 447, 318, 353, 859, 672, 112, 785, 
+   645, 863, 803, 350, 139, 93, 354, 99, 820, 908, 
+   609, 772, 154, 274, 580, 184, 79, 626, 630, 742, 
+   653, 282, 762, 623, 680, 81, 927, 626, 789, 125, 
+   411, 521, 938, 300, 821, 78, 343, 175, 128, 250, 
+   170, 774, 972, 275, 999, 639, 495, 78, 352, 126, 
+   857, 956, 358, 619, 580, 124, 737, 594, 701, 612, 
+   669, 112, 134, 694, 363, 992, 809, 743, 168, 974, 
+   944, 375, 748, 52, 600, 747, 642, 182, 862, 81, 
+   344, 805, 988, 739, 511, 655, 814, 334, 249, 515, 
+   897, 955, 664, 981, 649, 113, 974, 459, 893, 228, 
+   433, 837, 553, 268, 926, 240, 102, 654, 459, 51, 
+   686, 754, 806, 760, 493, 403, 415, 394, 687, 700, 
+   946, 670, 656, 610, 738, 392, 760, 799, 887, 653, 
+   978, 321, 576, 617, 626, 502, 894, 679, 243, 440, 
+   680, 879, 194, 572, 640, 724, 926, 56, 204, 700, 
+   707, 151, 457, 449, 797, 195, 791, 558, 945, 679, 
+   297, 59, 87, 824, 713, 663, 412, 693, 342, 606, 
+   134, 108, 571, 364, 631, 212, 174, 643, 304, 329, 
+   343, 97, 430, 751, 497, 314, 983, 374, 822, 928, 
+   140, 206, 73, 263, 980, 736, 876, 478, 430, 305, 
+   170, 514, 364, 692, 829, 82, 855, 953, 676, 246, 
+   369, 970, 294, 750, 807, 827, 150, 790, 288, 923, 
+   804, 378, 215, 828, 592, 281, 565, 555, 710, 82, 
+   896, 831, 547, 261, 524, 462, 293, 465, 502, 56, 
+   661, 821, 976, 991, 658, 869, 905, 758, 745, 193, 
+   768, 550, 608, 933, 378, 286, 215, 979, 792, 961, 
+   61, 688, 793, 644, 986, 403, 106, 366, 905, 644, 
+   372, 567, 466, 434, 645, 210, 389, 550, 919, 135, 
+   780, 773, 635, 389, 707, 100, 626, 958, 165, 504, 
+   920, 176, 193, 713, 857, 265, 203, 50, 668, 108, 
+   645, 990, 626, 197, 510, 357, 358, 850, 858, 364, 
+   936, 638
+};
+
+
+#define RAND_DECLS                                \
+   Int32 rNToGo = 0;                              \
+   Int32 rTPos  = 0;                              \
+
+#define RAND_MASK ((rNToGo == 1) ? 1 : 0)
+
+#define RAND_UPD_MASK                             \
+   if (rNToGo == 0) {                             \
+      rNToGo = rNums[rTPos];                      \
+      rTPos++; if (rTPos == 512) rTPos = 0;       \
+   }                                              \
+   rNToGo--;
+
+
+
+/*---------------------------------------------------*/
+/*--- The Reversible Transformation (tm)          ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------*/
+
+
+static INLINE Int32 indexIntoF ( Int32 indx, Int32 *cftab )
+{
+   Int32 nb, na, mid;
+   nb = 0;
+   na = 256;
+   do {
+      mid = (nb + na) >> 1;
+      if (indx >= cftab[mid]) nb = mid; else na = mid;
+   }
+   while (na - nb != 1);
+   return nb;
+}
+
+
+
+#define GET_SMALL(cccc)                     \
+                                            \
+      cccc = indexIntoF ( tPos, cftab );    \
+      tPos = GET_LL(tPos);
+
+
+
+static void undoReversibleTransformation_small ( void* dst )
+{
+   Int32  cftab[257], cftabAlso[257];
+   Int32  i, j, tmp, tPos;
+   UChar  ch;
+
+   /*--
+      We assume here that the global array unzftab will
+      already be holding the frequency counts for
+      ll8[0 .. last].
+   --*/
+
+   /*-- Set up cftab to facilitate generation of indexIntoF --*/
+   cftab[0] = 0;
+   for (i = 1; i <= 256; i++) cftab[i] = unzftab[i-1];
+   for (i = 1; i <= 256; i++) cftab[i] += cftab[i-1];
+
+   /*-- Make a copy of it, used in generation of T --*/
+   for (i = 0; i <= 256; i++) cftabAlso[i] = cftab[i];
+
+   /*-- compute the T vector --*/
+   for (i = 0; i <= last; i++) {
+      ch = (UChar)ll16[i];
+      SET_LL(i, cftabAlso[ch]);
+      cftabAlso[ch]++;
+   }
+
+   /*--
+      Compute T^(-1) by pointer reversal on T.  This is rather
+      subtle, in that, if the original block was two or more
+      (in general, N) concatenated copies of the same thing,
+      the T vector will consist of N cycles, each of length
+      blocksize / N, and decoding will involve traversing one
+      of these cycles N times.  Which particular cycle doesn't
+      matter -- they are all equivalent.  The tricky part is to
+      make sure that the pointer reversal creates a correct
+      reversed cycle for us to traverse.  So, the code below
+      simply reverses whatever cycle origPtr happens to fall into,
+      without regard to the cycle length.  That gives one reversed
+      cycle, which for normal blocks, is the entire block-size long.
+      For repeated blocks, it will be interspersed with the other
+      N-1 non-reversed cycles.  Providing that the F-subscripting
+      phase which follows starts at origPtr, all then works ok.
+   --*/
+   i = origPtr;
+   j = GET_LL(i);
+   do {
+      tmp = GET_LL(j);
+      SET_LL(j, i);
+      i = j;
+      j = tmp;
+   }
+      while (i != origPtr);
+
+   /*--
+      We recreate the original by subscripting F through T^(-1).
+      The run-length-decoder below requires characters incrementally,
+      so tPos is set to a starting value, and is updated by
+      the GET_SMALL macro.
+   --*/
+   tPos   = origPtr;
+
+   /*-------------------------------------------------*/
+   /*--
+      This is pretty much a verbatim copy of the
+      run-length decoder present in the distribution
+      bzip-0.21; it has to be here to avoid creating
+      block[] as an intermediary structure.  As in 0.21,
+      this code derives from some sent to me by
+      Christian von Roques.
+
+      It allows dst==NULL, so as to support the test (-t)
+      option without slowing down the fast decompression
+      code.
+   --*/
+   {
+      IntNative retVal;
+      Int32     i2, count, chPrev, ch2;
+      UInt32    localCrc;
+
+      count    = 0;
+      i2       = 0;
+      ch2      = 256;   /*-- not a char and not EOF --*/
+      localCrc = getGlobalCRC();
+
+      {
+         RAND_DECLS;
+         while ( i2 <= last ) {
+            chPrev = ch2;
+            GET_SMALL(ch2);
+            if (blockRandomised) {
+               RAND_UPD_MASK;
+               ch2 ^= (UInt32)RAND_MASK;
+            }
+            i2++;
+   
+            if (dst)
+               retVal = bz2_putc ( ch2, dst );
+   
+            UPDATE_CRC ( localCrc, (UChar)ch2 );
+   
+            if (ch2 != chPrev) {
+               count = 1;
+            } else {
+               count++;
+               if (count >= 4) {
+                  Int32 j2;
+                  UChar z;
+                  GET_SMALL(z);
+                  if (blockRandomised) {
+                     RAND_UPD_MASK;
+                     z ^= RAND_MASK;
+                  }
+                  for (j2 = 0;  j2 < (Int32)z;  j2++) {
+                     if (dst) retVal = bz2_putc (ch2, dst);
+                     UPDATE_CRC ( localCrc, (UChar)ch2 );
+                  }
+                  i2++;
+                  count = 0;
+               }
+            }
+         }
+      }
+
+      setGlobalCRC ( localCrc );
+   }
+   /*-- end of the in-line run-length-decoder. --*/
+}
+#undef GET_SMALL
+
+
+/*---------------------------------------------*/
+
+#define GET_FAST(cccc)                       \
+                                             \
+      cccc = ll8[tPos];                      \
+      tPos = tt[tPos];
+
+
+
+static void undoReversibleTransformation_fast ( void* dst )
+{
+   Int32  cftab[257];
+   Int32  i, tPos;
+   UChar  ch;
+
+   /*--
+      We assume here that the global array unzftab will
+      already be holding the frequency counts for
+      ll8[0 .. last].
+   --*/
+
+   /*-- Set up cftab to facilitate generation of T^(-1) --*/
+   cftab[0] = 0;
+   for (i = 1; i <= 256; i++) cftab[i] = unzftab[i-1];
+   for (i = 1; i <= 256; i++) cftab[i] += cftab[i-1];
+
+   /*-- compute the T^(-1) vector --*/
+   for (i = 0; i <= last; i++) {
+      ch = (UChar)ll8[i];
+      tt[cftab[ch]] = i;
+      cftab[ch]++;
+   }
+
+   /*--
+      We recreate the original by subscripting L through T^(-1).
+      The run-length-decoder below requires characters incrementally,
+      so tPos is set to a starting value, and is updated by
+      the GET_FAST macro.
+   --*/
+   tPos   = tt[origPtr];
+
+   /*-------------------------------------------------*/
+   /*--
+      This is pretty much a verbatim copy of the
+      run-length decoder present in the distribution
+      bzip-0.21; it has to be here to avoid creating
+      block[] as an intermediary structure.  As in 0.21,
+      this code derives from some sent to me by
+      Christian von Roques.
+   --*/
+   {
+      IntNative retVal;
+      Int32     i2, count, chPrev, ch2;
+      UInt32    localCrc;
+
+      count    = 0;
+      i2       = 0;
+      ch2      = 256;   /*-- not a char and not EOF --*/
+      localCrc = getGlobalCRC();
+
+      if (blockRandomised) {
+         RAND_DECLS;
+         while ( i2 <= last ) {
+            chPrev = ch2;
+            GET_FAST(ch2);
+            RAND_UPD_MASK;
+            ch2 ^= (UInt32)RAND_MASK;
+            i2++;
+   
+            retVal = bz2_putc ( ch2, dst );
+            UPDATE_CRC ( localCrc, (UChar)ch2 );
+   
+            if (ch2 != chPrev) {
+               count = 1;
+            } else {
+               count++;
+               if (count >= 4) {
+                  Int32 j2;
+                  UChar z;
+                  GET_FAST(z);
+                  RAND_UPD_MASK;
+                  z ^= RAND_MASK;
+                  for (j2 = 0;  j2 < (Int32)z;  j2++) {
+                     retVal = bz2_putc (ch2, dst);
+                     UPDATE_CRC ( localCrc, (UChar)ch2 );
+                  }
+                  i2++;
+                  count = 0;
+               }
+            }
+         }
+
+      } else {
+
+         while ( i2 <= last ) {
+            chPrev = ch2;
+            GET_FAST(ch2);
+            i2++;
+   
+            retVal = bz2_putc ( ch2, dst );
+            UPDATE_CRC ( localCrc, (UChar)ch2 );
+   
+            if (ch2 != chPrev) {
+               count = 1;
+            } else {
+               count++;
+               if (count >= 4) {
+                  Int32 j2;
+                  UChar z;
+                  GET_FAST(z);
+                  for (j2 = 0;  j2 < (Int32)z;  j2++) {
+                     retVal = bz2_putc (ch2, dst);
+                     UPDATE_CRC ( localCrc, (UChar)ch2 );
+                  }
+                  i2++;
+                  count = 0;
+               }
+            }
+         }
+
+      }   /*-- if (blockRandomised) --*/
+
+      setGlobalCRC ( localCrc );
+   }
+   /*-- end of the in-line run-length-decoder. --*/
+}
+#undef GET_FAST
+
+
+
+/*---------------------------------------------------*/
+/*--- Processing of complete files and streams    ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------*/
+
+static Bool uncompressStream ( void *zStream, void *stream )
+{
+   UChar      magic1, magic2, magic3, magic4;
+   UChar      magic5, magic6;
+   UInt32     storedBlockCRC, storedCombinedCRC;
+   UInt32     computedBlockCRC, computedCombinedCRC;
+   Int32      currBlockNo;
+   IntNative  retVal;
+
+   SET_BINARY_MODE(stream);
+   SET_BINARY_MODE(zStream);
+
+   ERROR_IF_NOT_ZERO ( bz2_ferror(stream) );
+   ERROR_IF_NOT_ZERO ( bz2_ferror(zStream) );
+
+   bsSetStream ( zStream, False );
+
+   /*--
+      A bad magic number is `recoverable from';
+      return with False so the caller skips the file.
+   --*/
+   magic1 = bsGetUChar ();
+   magic2 = bsGetUChar ();
+   magic3 = bsGetUChar ();
+   magic4 = bsGetUChar ();
+   if (magic1 != 'B' ||
+       magic2 != 'Z' ||
+       magic3 != 'h' ||
+       magic4 < '1'  ||
+       magic4 > '9') {
+     bsFinishedWithStream();
+     retVal = bz2_fclose ( stream );
+     ERROR_IF_EOF ( retVal );
+     return False;
+   }
+
+   setDecompressStructureSizes ( magic4 - '0' );
+   computedCombinedCRC = 0;
+
+   if (verbosity >= 2) fprintf ( stderr, "\n    " );
+   currBlockNo = 0;
+
+   while (True) {
+      magic1 = bsGetUChar ();
+      magic2 = bsGetUChar ();
+      magic3 = bsGetUChar ();
+      magic4 = bsGetUChar ();
+      magic5 = bsGetUChar ();
+      magic6 = bsGetUChar ();
+      if (magic1 == 0x17 && magic2 == 0x72 &&
+          magic3 == 0x45 && magic4 == 0x38 &&
+          magic5 == 0x50 && magic6 == 0x90) break;
+
+      if (magic1 != 0x31 || magic2 != 0x41 ||
+          magic3 != 0x59 || magic4 != 0x26 ||
+          magic5 != 0x53 || magic6 != 0x59) badBlockHeader();
+
+      storedBlockCRC = bsGetUInt32 ();
+
+      if (bsR(1) == 1)
+         blockRandomised = True; else
+         blockRandomised = False;
+
+      currBlockNo++;
+      if (verbosity >= 2)
+         fprintf ( stderr, "[%d: huff+mtf ", currBlockNo );
+      getAndMoveToFrontDecode ();
+      ERROR_IF_NOT_ZERO ( bz2_ferror(zStream) );
+
+      initialiseCRC();
+      if (verbosity >= 2) fprintf ( stderr, "rt+rld" );
+      if (smallMode)
+         undoReversibleTransformation_small ( stream );
+         else
+         undoReversibleTransformation_fast  ( stream );
+
+      ERROR_IF_NOT_ZERO ( bz2_ferror(stream) );
+
+      computedBlockCRC = getFinalCRC();
+      if (verbosity >= 3)
+         fprintf ( stderr, " {0x%x, 0x%x}", storedBlockCRC, computedBlockCRC );
+      if (verbosity >= 2) fprintf ( stderr, "] " );
+
+      /*-- A bad CRC is considered a fatal error. --*/
+      if (storedBlockCRC != computedBlockCRC)
+         crcError ( storedBlockCRC, computedBlockCRC );
+
+      computedCombinedCRC = (computedCombinedCRC << 1) | (computedCombinedCRC 
>> 31);
+      computedCombinedCRC ^= computedBlockCRC;
+   };
+
+   if (verbosity >= 2) fprintf ( stderr, "\n    " );
+
+   storedCombinedCRC  = bsGetUInt32 ();
+   if (verbosity >= 2)
+      fprintf ( stderr,
+                "combined CRCs: stored = 0x%x, computed = 0x%x\n    ",
+                storedCombinedCRC, computedCombinedCRC );
+   if (storedCombinedCRC != computedCombinedCRC)
+      crcError ( storedCombinedCRC, computedCombinedCRC );
+
+
+   bsFinishedWithStream ();
+   ERROR_IF_NOT_ZERO ( bz2_ferror(zStream) );
+   retVal = bz2_fclose ( zStream );
+   ERROR_IF_EOF ( retVal );
+
+   ERROR_IF_NOT_ZERO ( bz2_ferror(stream) );
+   retVal = bz2_fflush ( stream );
+   ERROR_IF_NOT_ZERO ( retVal );
+   return True;
+}
+
+
+#if 0
+
+#endif
+/*---------------------------------------------------*/
+/*--- Error [non-] handling grunge                ---*/
+/*---------------------------------------------------*/
+
+
+
+static void
+myFree (void **p)
+{
+  free (*p);
+  *p = NULL;
+}
+
+/*---------------------------------------------*/
+/* Ugg... Orignal code doesn't free dynamic allocated memories. */
+
+static void cleanUpAndFail ( Int32 ec )
+{
+  myFree ((void **) &ll16);
+  myFree ((void **) &ll4);
+  myFree ((void **) &ll8);
+  myFree ((void **) &tt);
+
+  (*unzip_error) (NULL);
+}
+
+
+/*---------------------------------------------*/
+
+static void panic ( Char* s )
+{
+   cleanUpAndFail( 3 );
+}
+
+
+
+/*---------------------------------------------*/
+
+static void crcError ( UInt32 crcStored, UInt32 crcComputed )
+{
+   cleanUpAndFail( 2 );
+}
+
+
+/*---------------------------------------------*/
+
+static void compressedStreamEOF ( void )
+{
+   cleanUpAndFail( 2 );
+}
+
+
+/*---------------------------------------------*/
+
+static void ioError ( )
+{
+   cleanUpAndFail( 1 );
+}
+
+
+/*---------------------------------------------*/
+
+static void blockOverrun ()
+{
+   cleanUpAndFail( 2 );
+}
+
+
+/*---------------------------------------------*/
+
+static void badBlockHeader ()
+{
+   cleanUpAndFail( 2 );
+}
+
+
+
+/*---------------------------------------------*/
+static void uncompressOutOfMemory ( Int32 draw, Int32 blockSize )
+{
+   cleanUpAndFail(1);
+}
+
+
+
+/*-----------------------------------------------------------*/
+/*--- end                                         bzip2.c ---*/
+/*-----------------------------------------------------------*/
+
+void
+do_bunzip2 (void)
+{
+  Bool ret;
+  
+  /*-- Initialise --*/
+  ll4                     = NULL;
+  ll16                    = NULL;
+  ll8                     = NULL;
+  tt                      = NULL;
+#ifdef SMALL_BZIP2
+  smallMode               = True;
+#else
+  smallMode               = False;
+#endif
+  verbosity               = 0;
+  blockSize100k           = 0;
+  bsStream                = NULL;
+
+  outcnt = 0;
+  inptr = 0;
+  insize = 0;
+  
+  ret = uncompressStream ((void *)1, (void *)2); /* Arguments ignored. */
+  if (ret != True)
+    cleanUpAndFail(1);
+}
diff --git a/libstore/gzip.h b/libstore/gzip.h
new file mode 100644
index 0000000..aedc257
--- /dev/null
+++ b/libstore/gzip.h
@@ -0,0 +1,315 @@
+/* gzip.h -- common declarations for all gzip modules
+ * Copyright (C) 1992-1993, 1996 Jean-loup Gailly.
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+#if defined(__STDC__) || defined(PROTO)
+#  define OF(args)  args
+#else
+#  define OF(args)  ()
+#endif
+
+#ifdef __STDC__
+   typedef void *voidp;
+#else
+   typedef char *voidp;
+#endif
+
+/* I don't like nested includes, but the string and io functions are used
+ * too often
+ */
+#include <stdio.h>
+#if !defined(NO_STRING_H) || defined(STDC_HEADERS)
+#  include <string.h>
+#  if !defined(STDC_HEADERS) && !defined(NO_MEMORY_H) && !defined(__GNUC__)
+#    include <memory.h>
+#  endif
+#  define memzero(s, n)     memset ((voidp)(s), 0, (n))
+#else
+#  include <strings.h>
+#  define strchr            index 
+#  define strrchr           rindex
+#  define memcpy(d, s, n)   bcopy((s), (d), (n)) 
+#  define memcmp(s1, s2, n) bcmp((s1), (s2), (n)) 
+#  define memzero(s, n)     bzero((s), (n))
+#endif
+
+#ifndef RETSIGTYPE
+#  define RETSIGTYPE void
+#endif
+
+#define local static
+
+typedef unsigned char  uch;
+typedef unsigned short ush;
+typedef unsigned long  ulg;
+
+/* Return codes from gzip */
+#define OK      0
+#define ERROR   1
+#define WARNING 2
+
+/* Compression methods (see algorithm.doc) */
+#define STORED      0
+#define COMPRESSED  1
+#define PACKED      2
+#define LZHED       3
+/* methods 4 to 7 reserved */
+#define DEFLATED    8
+#define MAX_METHODS 9
+extern int method;         /* compression method */
+
+/* To save memory for 16 bit systems, some arrays are overlaid between
+ * the various modules:
+ * deflate:  prev+head   window      d_buf  l_buf  outbuf
+ * unlzw:    tab_prefix  tab_suffix  stack  inbuf  outbuf
+ * inflate:              window             inbuf
+ * unpack:               window             inbuf  prefix_len
+ * unlzh:    left+right  window      c_table inbuf c_len
+ * For compression, input is done in window[]. For decompression, output
+ * is done in window except for unlzw.
+ */
+
+#ifndef        INBUFSIZ
+#  ifdef SMALL_MEM
+#    define INBUFSIZ  0x2000  /* input buffer size */
+#  else
+#    define INBUFSIZ  0x8000  /* input buffer size */
+#  endif
+#endif
+#define INBUF_EXTRA  64     /* required by unlzw() */
+
+#ifndef        OUTBUFSIZ
+#  ifdef SMALL_MEM
+#    define OUTBUFSIZ   8192  /* output buffer size */
+#  else
+#    define OUTBUFSIZ  16384  /* output buffer size */
+#  endif
+#endif
+#define OUTBUF_EXTRA 2048   /* required by unlzw() */
+
+#ifndef DIST_BUFSIZE
+#  ifdef SMALL_MEM
+#    define DIST_BUFSIZE 0x2000 /* buffer for distances, see trees.c */
+#  else
+#    define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */
+#  endif
+#endif
+
+#ifdef DYN_ALLOC
+#  define EXTERN(type, array)  extern type * near array
+#  define DECLARE(type, array, size)  type * near array
+#  define ALLOC(type, array, size) { \
+      array = (type*)fcalloc((size_t)(((size)+1L)/2), 2*sizeof(type)); \
+      if (array == NULL) error("insufficient memory"); \
+   }
+#  define FREE(array) {if (array != NULL) fcfree(array), array=NULL;}
+#else
+#  define EXTERN(type, array)  extern type array[]
+#  define DECLARE(type, array, size)  type array[size]
+#  define ALLOC(type, array, size)
+#  define FREE(array)
+#endif
+
+EXTERN(uch, inbuf);          /* input buffer */
+EXTERN(uch, outbuf);         /* output buffer */
+EXTERN(ush, d_buf);          /* buffer for distances, see trees.c */
+EXTERN(uch, window);         /* Sliding window and suffix table (unlzw) */
+#define tab_suffix window
+#ifndef MAXSEG_64K
+#  define tab_prefix prev    /* hash link (see deflate.c) */
+#  define head (prev+WSIZE)  /* hash head (see deflate.c) */
+   EXTERN(ush, tab_prefix);  /* prefix code (see unlzw.c) */
+#else
+#  define tab_prefix0 prev
+#  define head tab_prefix1
+   EXTERN(ush, tab_prefix0); /* prefix for even codes */
+   EXTERN(ush, tab_prefix1); /* prefix for odd  codes */
+#endif
+
+extern unsigned insize; /* valid bytes in inbuf */
+extern unsigned inptr;  /* index of next byte to be processed in inbuf */
+extern unsigned outcnt; /* bytes in output buffer */
+
+extern long bytes_in;   /* number of input bytes */
+extern long bytes_out;  /* number of output bytes */
+extern long header_bytes;/* number of bytes in gzip header */
+
+#define isize bytes_in
+/* for compatibility with old zip sources (to be cleaned) */
+
+extern int  ifd;        /* input file descriptor */
+extern int  ofd;        /* output file descriptor */
+extern char ifname[];   /* input file name or "stdin" */
+extern char ofname[];   /* output file name or "stdout" */
+extern char *progname;  /* program name */
+
+extern long time_stamp; /* original time stamp (modification time) */
+extern long ifile_size; /* input file size, -1 for devices (debug only) */
+
+typedef int file_t;     /* Do not use stdio */
+#define NO_FILE  (-1)   /* in memory compression */
+
+
+#define        PACK_MAGIC     "\037\036" /* Magic header for packed files */
+#define        GZIP_MAGIC     "\037\213" /* Magic header for gzip files, 1F 8B 
*/
+#define        OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 
1.x */
+#define        LZH_MAGIC      "\037\240" /* Magic header for SCO LZH Compress 
files*/
+#define PKZIP_MAGIC    "\120\113\003\004" /* Magic header for pkzip files */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
+#define RESERVED     0xC0 /* bit 6,7:   reserved */
+
+/* internal file attribute */
+#define UNKNOWN 0xffff
+#define BINARY  0
+#define ASCII   1
+
+#ifndef WSIZE
+#  define WSIZE 0x8000     /* window size--must be a power of two, and */
+#endif                     /*  at least 32K for zip's deflate method */
+
+#define MIN_MATCH  3
+#define MAX_MATCH  258
+/* The minimum and maximum match lengths */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST  (WSIZE-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+extern int decrypt;        /* flag to turn on decryption */
+extern int exit_code;      /* program exit code */
+extern int verbose;        /* be verbose (-v) */
+extern int quiet;          /* be quiet (-q) */
+extern int level;          /* compression level */
+extern int test;           /* check .z file integrity */
+extern int to_stdout;      /* output to stdout (-c) */
+extern int save_orig_name; /* set if original name must be saved */
+
+#define get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf(0))
+#define try_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf(1))
+
+/* put_byte is used for the compressed output, put_ubyte for the
+ * uncompressed output. However unlzw() uses window for its
+ * suffix table instead of its output buffer, so it does not use put_ubyte
+ * (to be cleaned up).
+ */
+#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\
+   flush_outbuf();}
+#define put_ubyte(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\
+   flush_window();}
+
+/* Output a 16 bit value, lsb first */
+#define put_short(w) \
+{ if (outcnt < OUTBUFSIZ-2) { \
+    outbuf[outcnt++] = (uch) ((w) & 0xff); \
+    outbuf[outcnt++] = (uch) ((ush)(w) >> 8); \
+  } else { \
+    put_byte((uch)((w) & 0xff)); \
+    put_byte((uch)((ush)(w) >> 8)); \
+  } \
+}
+
+/* Output a 32 bit value to the bit stream, lsb first */
+#define put_long(n) { \
+    put_short((n) & 0xffff); \
+    put_short(((ulg)(n)) >> 16); \
+}
+
+#define seekable()    0  /* force sequential output */
+#define translate_eol 0  /* no option -a yet */
+
+#define tolow(c)  (isupper(c) ? (c)-'A'+'a' : (c))    /* force to lower case */
+
+/* Macros for getting two-byte and four-byte header values */
+#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8))
+#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16))
+
+/* Diagnostic functions */
+#ifdef DEBUG
+#  define Assert(cond,msg) {if(!(cond)) error(msg);}
+#  define Trace(x) fprintf x
+#  define Tracev(x) {if (verbose) fprintf x ;}
+#  define Tracevv(x) {if (verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+#define WARN(msg) {if (!quiet) fprintf msg ; \
+                  if (exit_code == OK) exit_code = WARNING;}
+
+       /* in zip.c: */
+extern int zip        OF((int in, int out));
+extern int file_read  OF((char *buf,  unsigned size));
+
+       /* in unzip.c */
+extern int unzip      OF((int in, int out));
+extern int check_zipfile OF((int in));
+
+       /* in unpack.c */
+extern int unpack     OF((int in, int out));
+
+       /* in unlzh.c */
+extern int unlzh      OF((int in, int out));
+
+       /* in gzip.c */
+RETSIGTYPE abort_gzip OF((void));
+
+        /* in deflate.c */
+void lm_init OF((int pack_level, ush *flags));
+ulg  deflate OF((void));
+
+        /* in trees.c */
+void ct_init     OF((ush *attr, int *method));
+int  ct_tally    OF((int dist, int lc));
+ulg  flush_block OF((char *buf, ulg stored_len, int eof));
+
+        /* in bits.c */
+void     bi_init    OF((file_t zipfile));
+void     send_bits  OF((int value, int length));
+unsigned bi_reverse OF((unsigned value, int length));
+void     bi_windup  OF((void));
+void     copy_block OF((char *buf, unsigned len, int header));
+extern   int (*read_buf) OF((char *buf, unsigned size));
+
+       /* in util.c: */
+extern int copy           OF((int in, int out));
+extern ulg  updcrc        OF((uch *s, unsigned n));
+extern void clear_bufs    OF((void));
+extern int  fill_inbuf    OF((int eof_ok));
+extern void flush_outbuf  OF((void));
+extern void flush_window  OF((void));
+extern void write_buf     OF((int fd, voidp buf, unsigned cnt));
+extern char *strlwr       OF((char *s));
+/* extern char *basename     OF((char *fname));*/
+extern void make_simple_name OF((char *name));
+extern char *add_envopt   OF((int *argcp, char ***argvp, char *env));
+extern void error         OF((char *m));
+extern void warn          OF((char *a, char *b));
+extern void read_error    OF((void));
+extern void write_error   OF((void));
+extern void display_ratio OF((long num, long den, FILE *file));
+extern voidp xmalloc      OF((unsigned int size));
+
+       /* in inflate.c */
+extern int inflate OF((void));
diff --git a/libstore/inflate.c b/libstore/inflate.c
new file mode 100644
index 0000000..c3be438
--- /dev/null
+++ b/libstore/inflate.c
@@ -0,0 +1,954 @@
+/* inflate.c -- Not copyrighted 1992 by Mark Adler
+   version c10p1, 10 January 1993 */
+
+/* You can do whatever you like with this source file, though I would
+   prefer that if you modify it and redistribute it that you include
+   comments to that effect with your name and the date.  Thank you.
+   [The history has been moved to the file ChangeLog.]
+ */
+
+/*
+   Inflate deflated (PKZIP's method 8 compressed) data.  The compression
+   method searches for as much of the current string of bytes (up to a
+   length of 258) in the previous 32K bytes.  If it doesn't find any
+   matches (of at least length 3), it codes the next byte.  Otherwise, it
+   codes the length of the matched string and its distance backwards from
+   the current position.  There is a single Huffman code that codes both
+   single bytes (called "literals") and match lengths.  A second Huffman
+   code codes the distance information, which follows a length code.  Each
+   length or distance code actually represents a base value and a number
+   of "extra" (sometimes zero) bits to get to add to the base value.  At
+   the end of each deflated block is a special end-of-block (EOB) literal/
+   length code.  The decoding process is basically: get a literal/length
+   code; if EOB then done; if a literal, emit the decoded byte; if a
+   length then get the distance and emit the referred-to bytes from the
+   sliding window of previously emitted data.
+
+   There are (currently) three kinds of inflate blocks: stored, fixed, and
+   dynamic.  The compressor deals with some chunk of data at a time, and
+   decides which method to use on a chunk-by-chunk basis.  A chunk might
+   typically be 32K or 64K.  If the chunk is uncompressible, then the
+   "stored" method is used.  In this case, the bytes are simply stored as
+   is, eight bits per byte, with none of the above coding.  The bytes are
+   preceded by a count, since there is no longer an EOB code.
+
+   If the data is compressible, then either the fixed or dynamic methods
+   are used.  In the dynamic method, the compressed data is preceded by
+   an encoding of the literal/length and distance Huffman codes that are
+   to be used to decode this block.  The representation is itself Huffman
+   coded, and so is preceded by a description of that code.  These code
+   descriptions take up a little space, and so for small blocks, there is
+   a predefined set of codes, called the fixed codes.  The fixed method is
+   used if the block codes up smaller that way (usually for quite small
+   chunks), otherwise the dynamic method is used.  In the latter case, the
+   codes are customized to the probabilities in the current block, and so
+   can code it much better than the pre-determined fixed codes.
+ 
+   The Huffman codes themselves are decoded using a mutli-level table
+   lookup, in order to maximize the speed of decoding plus the speed of
+   building the decoding tables.  See the comments below that precede the
+   lbits and dbits tuning parameters.
+ */
+
+
+/*
+   Notes beyond the 1.93a appnote.txt:
+
+   1. Distance pointers never point before the beginning of the output
+      stream.
+   2. Distance pointers can point back across blocks, up to 32k away.
+   3. There is an implied maximum of 7 bits for the bit length table and
+      15 bits for the actual data.
+   4. If only one code exists, then it is encoded using one bit.  (Zero
+      would be more efficient, but perhaps a little confusing.)  If two
+      codes exist, they are coded using one bit each (0 and 1).
+   5. There is no way of sending zero distance codes--a dummy must be
+      sent if there are none.  (History: a pre 2.0 version of PKZIP would
+      store blocks with no distance codes, but this was discovered to be
+      too harsh a criterion.)  Valid only for 1.93a.  2.04c does allow
+      zero distance codes, which is sent as one code of zero bits in
+      length.
+   6. There are up to 286 literal/length codes.  Code 256 represents the
+      end-of-block.  Note however that the static length tree defines
+      288 codes just to fill out the Huffman codes.  Codes 286 and 287
+      cannot be used though, since there is no length base or extra bits
+      defined for them.  Similarly, there are up to 30 distance codes.
+      However, static trees define 32 codes (all 5 bits) to fill out the
+      Huffman codes, but the last two had better not show up in the data.
+   7. Unzip can check dynamic Huffman blocks for complete code sets.
+      The exception is that a single code would not be complete (see #4).
+   8. The five bits following the block type is really the number of
+      literal codes sent minus 257.
+   9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
+      (1+6+6).  Therefore, to output three times the length, you output
+      three codes (1+1+1), whereas to output four times the same length,
+      you only need two codes (1+3).  Hmm.
+  10. In the tree reconstruction algorithm, Code = Code + Increment
+      only if BitLength(i) is not zero.  (Pretty obvious.)
+  11. Correction: 4 Bits: # of Bit Length codes - 4     (4 - 19)
+  12. Note: length code 284 can represent 227-258, but length code 285
+      really is 258.  The last length deserves its own, short code
+      since it gets used a lot in very redundant files.  The length
+      258 is special since 258 - 3 (the min match length) is 255.
+  13. The literal/length and distance code bit lengths are read as a
+      single stream of lengths.  It is possible (and advantageous) for
+      a repeat code (16, 17, or 18) to go across the boundary between
+      the two sets of lengths.
+ */
+
+#ifdef RCSID
+static char rcsid[] = "$Id: inflate.c,v 1.1 1994/12/14 04:31:21 roland Exp $";
+#endif
+
+#include <sys/types.h>
+
+#include "tailor.h"
+
+#if defined(STDC_HEADERS) || !defined(NO_STDLIB_H)
+#  include <stdlib.h>
+#endif
+
+#include "gzip.h"
+#define slide window
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+   that have 16-bit pointers (e.g. PC's in the small or medium model).
+   Valid extra bits are 0..13.  e == 15 is EOB (end of block), e == 16
+   means that v is a literal, 16 < e < 32 means that v is a pointer to
+   the next table, which codes e - 16 bits, and lastly e == 99 indicates
+   an unused code.  If a code with e == 99 is looked up, this implies an
+   error in the data. */
+struct huft {
+  uch e;                /* number of extra bits or operation */
+  uch b;                /* number of bits in this code or subcode */
+  union {
+    ush n;              /* literal, length base, or distance base */
+    struct huft *t;     /* pointer to next level of table */
+  } v;
+};
+
+
+/* Function prototypes */
+int huft_build OF((unsigned *, unsigned, unsigned, ush *, ush *,
+                   struct huft **, int *));
+int huft_free OF((struct huft *));
+int inflate_codes OF((struct huft *, struct huft *, int, int));
+int inflate_stored OF((void));
+int inflate_fixed OF((void));
+int inflate_dynamic OF((void));
+int inflate_block OF((int *));
+int inflate OF((void));
+
+
+/* The inflate algorithm uses a sliding 32K byte window on the uncompressed
+   stream to find repeated byte strings.  This is implemented here as a
+   circular buffer.  The index is updated simply by incrementing and then
+   and'ing with 0x7fff (32K-1). */
+/* It is left to other modules to supply the 32K area.  It is assumed
+   to be usable as if it were declared "uch slide[32768];" or as just
+   "uch *slide;" and then malloc'ed in the latter case.  The definition
+   must be in unzip.h, included above. */
+/* unsigned wp;             current position in slide */
+#define wp outcnt
+#define flush_output(w) (wp=(w),flush_window())
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+static unsigned border[] = {    /* Order of the bit length code lengths */
+        16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+static ush cplens[] = {         /* Copy lengths for literal codes 257..285 */
+        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+        /* note: see note #13 above about the 258 in this list. */
+static ush cplext[] = {         /* Extra bits for literal codes 257..285 */
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+        3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */
+static ush cpdist[] = {         /* Copy offsets for distance codes 0..29 */
+        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+        8193, 12289, 16385, 24577};
+static ush cpdext[] = {         /* Extra bits for distance codes */
+        0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+        7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+        12, 12, 13, 13};
+
+
+
+/* Macros for inflate() bit peeking and grabbing.
+   The usage is:
+   
+        NEEDBITS(j)
+        x = b & mask_bits[j];
+        DUMPBITS(j)
+
+   where NEEDBITS makes sure that b has at least j bits in it, and
+   DUMPBITS removes the bits from b.  The macros use the variable k
+   for the number of bits in b.  Normally, b and k are register
+   variables for speed, and are initialized at the beginning of a
+   routine that uses these macros from a global bit buffer and count.
+
+   If we assume that EOB will be the longest code, then we will never
+   ask for bits with NEEDBITS that are beyond the end of the stream.
+   So, NEEDBITS should not read any more bytes than are needed to
+   meet the request.  Then no bytes need to be "returned" to the buffer
+   at the end of the last block.
+
+   However, this assumption is not true for fixed blocks--the EOB code
+   is 7 bits, but the other literal/length codes can be 8 or 9 bits.
+   (The EOB code is shorter than other codes because fixed blocks are
+   generally short.  So, while a block always has an EOB, many other
+   literal/length codes have a significantly lower probability of
+   showing up at all.)  However, by making the first table have a
+   lookup of seven bits, the EOB code will be found in that first
+   lookup, and so will not require that too many bits be pulled from
+   the stream.
+ */
+
+ulg bb;                         /* bit buffer */
+unsigned bk;                    /* bits in bit buffer */
+
+ush mask_bits[] = {
+    0x0000,
+    0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+    0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+#ifdef CRYPT
+  uch cc;
+#  define NEXTBYTE() \
+     (decrypt ? (cc = get_byte(), zdecode(cc), cc) : get_byte())
+#else
+#  define NEXTBYTE()  (uch)get_byte()
+#endif
+#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<<k;k+=8;}}
+#define DUMPBITS(n) {b>>=(n);k-=(n);}
+
+
+/*
+   Huffman code decoding is performed using a multi-level table lookup.
+   The fastest way to decode is to simply build a lookup table whose
+   size is determined by the longest code.  However, the time it takes
+   to build this table can also be a factor if the data being decoded
+   is not very long.  The most common codes are necessarily the
+   shortest codes, so those codes dominate the decoding time, and hence
+   the speed.  The idea is you can have a shorter table that decodes the
+   shorter, more probable codes, and then point to subsidiary tables for
+   the longer codes.  The time it costs to decode the longer codes is
+   then traded against the time it takes to make longer tables.
+
+   This results of this trade are in the variables lbits and dbits
+   below.  lbits is the number of bits the first level table for literal/
+   length codes can decode in one step, and dbits is the same thing for
+   the distance codes.  Subsequent tables are also less than or equal to
+   those sizes.  These values may be adjusted either when all of the
+   codes are shorter than that, in which case the longest code length in
+   bits is used, or when the shortest code is *longer* than the requested
+   table size, in which case the length of the shortest code in bits is
+   used.
+
+   There are two different values for the two tables, since they code a
+   different number of possibilities each.  The literal/length table
+   codes 286 possible values, or in a flat code, a little over eight
+   bits.  The distance table codes 30 possible values, or a little less
+   than five bits, flat.  The optimum values for speed end up being
+   about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+   The optimum values may differ though from machine to machine, and
+   possibly even between compilers.  Your mileage may vary.
+ */
+
+
+int lbits = 9;          /* bits in base literal/length lookup table */
+int dbits = 6;          /* bits in base distance lookup table */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
+#define BMAX 16         /* maximum bit length of any code (16 for explode) */
+#define N_MAX 288       /* maximum number of codes in any set */
+
+
+unsigned hufts;         /* track memory usage */
+
+
+int huft_build(b, n, s, d, e, t, m)
+unsigned *b;            /* code lengths in bits (all assumed <= BMAX) */
+unsigned n;             /* number of codes (assumed <= N_MAX) */
+unsigned s;             /* number of simple-valued codes (0..s-1) */
+ush *d;                 /* list of base values for non-simple codes */
+ush *e;                 /* list of extra bits for non-simple codes */
+struct huft **t;        /* result: starting table */
+int *m;                 /* maximum lookup bits, returns actual */
+/* Given a list of code lengths and a maximum table size, make a set of
+   tables to decode that set of codes.  Return zero on success, one if
+   the given code set is incomplete (the tables are still built in this
+   case), two if the input is invalid (all zero length codes or an
+   oversubscribed set of lengths), and three if not enough memory. */
+{
+  unsigned a;                   /* counter for codes of length k */
+  unsigned c[BMAX+1];           /* bit length count table */
+  unsigned f;                   /* i repeats in table every f entries */
+  int g;                        /* maximum code length */
+  int h;                        /* table level */
+  register unsigned i;          /* counter, current code */
+  register unsigned j;          /* counter */
+  register int k;               /* number of bits in current code */
+  int l;                        /* bits per table (returned in m) */
+  register unsigned *p;         /* pointer into c[], b[], or v[] */
+  register struct huft *q;      /* points to current table */
+  struct huft r;                /* table entry for structure assignment */
+  struct huft *u[BMAX];         /* table stack */
+  unsigned v[N_MAX];            /* values in order of bit length */
+  register int w;               /* bits before this table == (l * h) */
+  unsigned x[BMAX+1];           /* bit offsets, then code stack */
+  unsigned *xp;                 /* pointer into x */
+  int y;                        /* number of dummy codes added */
+  unsigned z;                   /* number of entries in current table */
+
+
+  /* Generate counts for each bit length */
+  memzero(c, sizeof(c));
+  p = b;  i = n;
+  do {
+    Tracecv(*p, (stderr, (n-i >= ' ' && n-i <= '~' ? "%c %d\n" : "0x%x %d\n"), 
+           n-i, *p));
+    c[*p]++;                    /* assume all entries <= BMAX */
+    p++;                      /* Can't combine with above line (Solaris bug) */
+  } while (--i);
+  if (c[0] == n)                /* null input--all zero length codes */
+  {
+    *t = (struct huft *)NULL;
+    *m = 0;
+    return 0;
+  }
+
+
+  /* Find minimum and maximum length, bound *m by those */
+  l = *m;
+  for (j = 1; j <= BMAX; j++)
+    if (c[j])
+      break;
+  k = j;                        /* minimum code length */
+  if ((unsigned)l < j)
+    l = j;
+  for (i = BMAX; i; i--)
+    if (c[i])
+      break;
+  g = i;                        /* maximum code length */
+  if ((unsigned)l > i)
+    l = i;
+  *m = l;
+
+
+  /* Adjust last length count to fill out codes, if needed */
+  for (y = 1 << j; j < i; j++, y <<= 1)
+    if ((y -= c[j]) < 0)
+      return 2;                 /* bad input: more codes than bits */
+  if ((y -= c[i]) < 0)
+    return 2;
+  c[i] += y;
+
+
+  /* Generate starting offsets into the value table for each length */
+  x[1] = j = 0;
+  p = c + 1;  xp = x + 2;
+  while (--i) {                 /* note that i == g from above */
+    *xp++ = (j += *p++);
+  }
+
+
+  /* Make a table of values in order of bit lengths */
+  p = b;  i = 0;
+  do {
+    if ((j = *p++) != 0)
+      v[x[j]++] = i;
+  } while (++i < n);
+
+
+  /* Generate the Huffman codes and for each, make the table entries */
+  x[0] = i = 0;                 /* first Huffman code is zero */
+  p = v;                        /* grab values in bit order */
+  h = -1;                       /* no tables yet--level -1 */
+  w = -l;                       /* bits decoded == (l * h) */
+  u[0] = (struct huft *)NULL;   /* just to keep compilers happy */
+  q = (struct huft *)NULL;      /* ditto */
+  z = 0;                        /* ditto */
+
+  /* go through the bit lengths (k already is bits in shortest code) */
+  for (; k <= g; k++)
+  {
+    a = c[k];
+    while (a--)
+    {
+      /* here i is the Huffman code of length k bits for value *p */
+      /* make tables up to required level */
+      while (k > w + l)
+      {
+        h++;
+        w += l;                 /* previous table always l bits */
+
+        /* compute minimum size table less than or equal to l bits */
+        z = (z = g - w) > (unsigned)l ? l : z;  /* upper limit on table size */
+        if ((f = 1 << (j = k - w)) > a + 1)     /* try a k-w bit table */
+        {                       /* too few codes for k-w bit table */
+          f -= a + 1;           /* deduct codes from patterns left */
+          xp = c + k;
+          while (++j < z)       /* try smaller tables up to z bits */
+          {
+            if ((f <<= 1) <= *++xp)
+              break;            /* enough codes to use up j bits */
+            f -= *xp;           /* else deduct codes from patterns */
+          }
+        }
+        z = 1 << j;             /* table entries for j-bit table */
+
+        /* allocate and link in new table */
+        if ((q = (struct huft *)malloc((z + 1)*sizeof(struct huft))) ==
+            (struct huft *)NULL)
+        {
+          if (h)
+            huft_free(u[0]);
+          return 3;             /* not enough memory */
+        }
+        hufts += z + 1;         /* track memory usage */
+        *t = q + 1;             /* link to list for huft_free() */
+        *(t = &(q->v.t)) = (struct huft *)NULL;
+        u[h] = ++q;             /* table starts after link */
+
+        /* connect to last table, if there is one */
+        if (h)
+        {
+          x[h] = i;             /* save pattern for backing up */
+          r.b = (uch)l;         /* bits to dump before this table */
+          r.e = (uch)(16 + j);  /* bits in this table */
+          r.v.t = q;            /* pointer to this table */
+          j = i >> (w - l);     /* (get around Turbo C bug) */
+          u[h-1][j] = r;        /* connect to last table */
+        }
+      }
+
+      /* set up table entry in r */
+      r.b = (uch)(k - w);
+      if (p >= v + n)
+        r.e = 99;               /* out of values--invalid code */
+      else if (*p < s)
+      {
+        r.e = (uch)(*p < 256 ? 16 : 15);    /* 256 is end-of-block code */
+        r.v.n = (ush)(*p);             /* simple code is just the value */
+       p++;                           /* one compiler does not like *p++ */
+      }
+      else
+      {
+        r.e = (uch)e[*p - s];   /* non-simple--look up in lists */
+        r.v.n = d[*p++ - s];
+      }
+
+      /* fill code-like entries with r */
+      f = 1 << (k - w);
+      for (j = i >> w; j < z; j += f)
+        q[j] = r;
+
+      /* backwards increment the k-bit code i */
+      for (j = 1 << (k - 1); i & j; j >>= 1)
+        i ^= j;
+      i ^= j;
+
+      /* backup over finished tables */
+      while ((i & ((1 << w) - 1)) != x[h])
+      {
+        h--;                    /* don't need to update q */
+        w -= l;
+      }
+    }
+  }
+
+
+  /* Return true (1) if we were given an incomplete table */
+  return y != 0 && g != 1;
+}
+
+
+
+int huft_free(t)
+struct huft *t;         /* table to free */
+/* Free the malloc'ed tables built by huft_build(), which makes a linked
+   list of the tables it made, with the links in a dummy first entry of
+   each table. */
+{
+  register struct huft *p, *q;
+
+
+  /* Go through linked list, freeing from the malloced (t[-1]) address. */
+  p = t;
+  while (p != (struct huft *)NULL)
+  {
+    q = (--p)->v.t;
+    free((char*)p);
+    p = q;
+  } 
+  return 0;
+}
+
+
+int inflate_codes(tl, td, bl, bd)
+struct huft *tl, *td;   /* literal/length and distance decoder tables */
+int bl, bd;             /* number of bits decoded by tl[] and td[] */
+/* inflate (decompress) the codes in a deflated (compressed) block.
+   Return an error code or zero if it all goes ok. */
+{
+  register unsigned e;  /* table entry flag/number of extra bits */
+  unsigned n, d;        /* length and index for copy */
+  unsigned w;           /* current window position */
+  struct huft *t;       /* pointer to table entry */
+  unsigned ml, md;      /* masks for bl and bd bits */
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+
+  /* make local copies of globals */
+  b = bb;                       /* initialize bit buffer */
+  k = bk;
+  w = wp;                       /* initialize window position */
+
+  /* inflate the coded data */
+  ml = mask_bits[bl];           /* precompute masks for speed */
+  md = mask_bits[bd];
+  for (;;)                      /* do until end of block */
+  {
+    NEEDBITS((unsigned)bl)
+    if ((e = (t = tl + ((unsigned)b & ml))->e) > 16)
+      do {
+        if (e == 99)
+          return 1;
+        DUMPBITS(t->b)
+        e -= 16;
+        NEEDBITS(e)
+      } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
+    DUMPBITS(t->b)
+    if (e == 16)                /* then it's a literal */
+    {
+      slide[w++] = (uch)t->v.n;
+      Tracevv((stderr, "%c", slide[w-1]));
+      if (w == WSIZE)
+      {
+        flush_output(w);
+        w = 0;
+      }
+    }
+    else                        /* it's an EOB or a length */
+    {
+      /* exit if end of block */
+      if (e == 15)
+        break;
+
+      /* get length of block to copy */
+      NEEDBITS(e)
+      n = t->v.n + ((unsigned)b & mask_bits[e]);
+      DUMPBITS(e);
+
+      /* decode distance of block to copy */
+      NEEDBITS((unsigned)bd)
+      if ((e = (t = td + ((unsigned)b & md))->e) > 16)
+        do {
+          if (e == 99)
+            return 1;
+          DUMPBITS(t->b)
+          e -= 16;
+          NEEDBITS(e)
+        } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
+      DUMPBITS(t->b)
+      NEEDBITS(e)
+      d = w - t->v.n - ((unsigned)b & mask_bits[e]);
+      DUMPBITS(e)
+      Tracevv((stderr,"\\[%d,%d]", w-d, n));
+
+      /* do the copy */
+      do {
+        n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e);
+#if !defined(NOMEMCPY) && !defined(DEBUG)
+        if (w - d >= e)         /* (this test assumes unsigned comparison) */
+        {
+          memcpy(slide + w, slide + d, e);
+          w += e;
+          d += e;
+        }
+        else                      /* do it slow to avoid memcpy() overlap */
+#endif /* !NOMEMCPY */
+          do {
+            slide[w++] = slide[d++];
+           Tracevv((stderr, "%c", slide[w-1]));
+          } while (--e);
+        if (w == WSIZE)
+        {
+          flush_output(w);
+          w = 0;
+        }
+      } while (n);
+    }
+  }
+
+
+  /* restore the globals from the locals */
+  wp = w;                       /* restore global window pointer */
+  bb = b;                       /* restore global bit buffer */
+  bk = k;
+
+  /* done */
+  return 0;
+}
+
+
+
+int inflate_stored()
+/* "decompress" an inflated type 0 (stored) block. */
+{
+  unsigned n;           /* number of bytes in block */
+  unsigned w;           /* current window position */
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+
+  /* make local copies of globals */
+  b = bb;                       /* initialize bit buffer */
+  k = bk;
+  w = wp;                       /* initialize window position */
+
+
+  /* go to byte boundary */
+  n = k & 7;
+  DUMPBITS(n);
+
+
+  /* get the length and its complement */
+  NEEDBITS(16)
+  n = ((unsigned)b & 0xffff);
+  DUMPBITS(16)
+  NEEDBITS(16)
+  if (n != (unsigned)((~b) & 0xffff))
+    return 1;                   /* error in compressed data */
+  DUMPBITS(16)
+
+
+  /* read and output the compressed data */
+  while (n--)
+  {
+    NEEDBITS(8)
+    slide[w++] = (uch)b;
+    if (w == WSIZE)
+    {
+      flush_output(w);
+      w = 0;
+    }
+    DUMPBITS(8)
+  }
+
+
+  /* restore the globals from the locals */
+  wp = w;                       /* restore global window pointer */
+  bb = b;                       /* restore global bit buffer */
+  bk = k;
+  return 0;
+}
+
+
+
+int inflate_fixed()
+/* decompress an inflated type 1 (fixed Huffman codes) block.  We should
+   either replace this with a custom decoder, or at least precompute the
+   Huffman tables. */
+{
+  int i;                /* temporary variable */
+  struct huft *tl;      /* literal/length code table */
+  struct huft *td;      /* distance code table */
+  int bl;               /* lookup bits for tl */
+  int bd;               /* lookup bits for td */
+  unsigned l[288];      /* length list for huft_build */
+
+
+  /* set up literal table */
+  for (i = 0; i < 144; i++)
+    l[i] = 8;
+  for (; i < 256; i++)
+    l[i] = 9;
+  for (; i < 280; i++)
+    l[i] = 7;
+  for (; i < 288; i++)          /* make a complete, but wrong code set */
+    l[i] = 8;
+  bl = 7;
+  if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0)
+    return i;
+
+
+  /* set up distance table */
+  for (i = 0; i < 30; i++)      /* make an incomplete code set */
+    l[i] = 5;
+  bd = 5;
+  if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1)
+  {
+    huft_free(tl);
+    return i;
+  }
+
+
+  /* decompress until an end-of-block code */
+  if (inflate_codes(tl, td, bl, bd))
+    return 1;
+
+
+  /* free the decoding tables, return */
+  huft_free(tl);
+  huft_free(td);
+  return 0;
+}
+
+
+
+int inflate_dynamic()
+/* decompress an inflated type 2 (dynamic Huffman codes) block. */
+{
+  int i;                /* temporary variables */
+  unsigned j;
+  unsigned l;           /* last length */
+  unsigned m;           /* mask for bit lengths table */
+  unsigned n;           /* number of lengths to get */
+  struct huft *tl;      /* literal/length code table */
+  struct huft *td;      /* distance code table */
+  int bl;               /* lookup bits for tl */
+  int bd;               /* lookup bits for td */
+  unsigned nb;          /* number of bit length codes */
+  unsigned nl;          /* number of literal/length codes */
+  unsigned nd;          /* number of distance codes */
+#ifdef PKZIP_BUG_WORKAROUND
+  unsigned ll[288+32];  /* literal/length and distance code lengths */
+#else
+  unsigned ll[286+30];  /* literal/length and distance code lengths */
+#endif
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+
+  /* make local bit buffer */
+  b = bb;
+  k = bk;
+
+
+  /* read in table lengths */
+  NEEDBITS(5)
+  nl = 257 + ((unsigned)b & 0x1f);      /* number of literal/length codes */
+  DUMPBITS(5)
+  NEEDBITS(5)
+  nd = 1 + ((unsigned)b & 0x1f);        /* number of distance codes */
+  DUMPBITS(5)
+  NEEDBITS(4)
+  nb = 4 + ((unsigned)b & 0xf);         /* number of bit length codes */
+  DUMPBITS(4)
+#ifdef PKZIP_BUG_WORKAROUND
+  if (nl > 288 || nd > 32)
+#else
+  if (nl > 286 || nd > 30)
+#endif
+    return 1;                   /* bad lengths */
+
+
+  /* read in bit-length-code lengths */
+  for (j = 0; j < nb; j++)
+  {
+    NEEDBITS(3)
+    ll[border[j]] = (unsigned)b & 7;
+    DUMPBITS(3)
+  }
+  for (; j < 19; j++)
+    ll[border[j]] = 0;
+
+
+  /* build decoding table for trees--single level, 7 bit lookup */
+  bl = 7;
+  if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0)
+  {
+    if (i == 1)
+      huft_free(tl);
+    return i;                   /* incomplete code set */
+  }
+
+
+  /* read in literal and distance code lengths */
+  n = nl + nd;
+  m = mask_bits[bl];
+  i = l = 0;
+  while ((unsigned)i < n)
+  {
+    NEEDBITS((unsigned)bl)
+    j = (td = tl + ((unsigned)b & m))->b;
+    DUMPBITS(j)
+    j = td->v.n;
+    if (j < 16)                 /* length of code in bits (0..15) */
+      ll[i++] = l = j;          /* save last length in l */
+    else if (j == 16)           /* repeat last length 3 to 6 times */
+    {
+      NEEDBITS(2)
+      j = 3 + ((unsigned)b & 3);
+      DUMPBITS(2)
+      if ((unsigned)i + j > n)
+        return 1;
+      while (j--)
+        ll[i++] = l;
+    }
+    else if (j == 17)           /* 3 to 10 zero length codes */
+    {
+      NEEDBITS(3)
+      j = 3 + ((unsigned)b & 7);
+      DUMPBITS(3)
+      if ((unsigned)i + j > n)
+        return 1;
+      while (j--)
+        ll[i++] = 0;
+      l = 0;
+    }
+    else                        /* j == 18: 11 to 138 zero length codes */
+    {
+      NEEDBITS(7)
+      j = 11 + ((unsigned)b & 0x7f);
+      DUMPBITS(7)
+      if ((unsigned)i + j > n)
+        return 1;
+      while (j--)
+        ll[i++] = 0;
+      l = 0;
+    }
+  }
+
+
+  /* free decoding table for trees */
+  huft_free(tl);
+
+
+  /* restore the global bit buffer */
+  bb = b;
+  bk = k;
+
+
+  /* build the decoding tables for literal/length and distance codes */
+  bl = lbits;
+  if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0)
+  {
+    if (i == 1) {
+      fprintf(stderr, " incomplete literal tree\n");
+      huft_free(tl);
+    }
+    return i;                   /* incomplete code set */
+  }
+  bd = dbits;
+  if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0)
+  {
+    if (i == 1) {
+      fprintf(stderr, " incomplete distance tree\n");
+#ifdef PKZIP_BUG_WORKAROUND
+      i = 0;
+    }
+#else
+      huft_free(td);
+    }
+    huft_free(tl);
+    return i;                   /* incomplete code set */
+#endif
+  }
+
+
+  /* decompress until an end-of-block code */
+  if (inflate_codes(tl, td, bl, bd))
+    return 1;
+
+
+  /* free the decoding tables, return */
+  huft_free(tl);
+  huft_free(td);
+  return 0;
+}
+
+
+
+int inflate_block(e)
+int *e;                 /* last block flag */
+/* decompress an inflated block */
+{
+  unsigned t;           /* block type */
+  register ulg b;       /* bit buffer */
+  register unsigned k;  /* number of bits in bit buffer */
+
+
+  /* make local bit buffer */
+  b = bb;
+  k = bk;
+
+
+  /* read in last block bit */
+  NEEDBITS(1)
+  *e = (int)b & 1;
+  DUMPBITS(1)
+
+
+  /* read in block type */
+  NEEDBITS(2)
+  t = (unsigned)b & 3;
+  DUMPBITS(2)
+
+
+  /* restore the global bit buffer */
+  bb = b;
+  bk = k;
+
+
+  /* inflate that block type */
+  if (t == 2)
+    return inflate_dynamic();
+  if (t == 0)
+    return inflate_stored();
+  if (t == 1)
+    return inflate_fixed();
+
+
+  /* bad block type */
+  return 2;
+}
+
+
+
+int inflate()
+/* decompress an inflated entry */
+{
+  int e;                /* last block flag */
+  int r;                /* result code */
+  unsigned h;           /* maximum struct huft's malloc'ed */
+
+
+  /* initialize window, bit buffer */
+  wp = 0;
+  bk = 0;
+  bb = 0;
+
+
+  /* decompress until the last block */
+  h = 0;
+  do {
+    hufts = 0;
+    if ((r = inflate_block(&e)) != 0)
+      return r;
+    if (hufts > h)
+      h = hufts;
+  } while (!e);
+
+  /* Undo too much lookahead. The next read will be byte aligned so we
+   * can discard unused bits in the last meaningful byte.
+   */
+  while (bk >= 8) {
+    bk -= 8;
+    inptr--;
+  }
+
+  /* flush out slide */
+  flush_output(wp);
+
+
+  /* return success */
+#ifdef DEBUG
+  fprintf(stderr, "<%u> ", h);
+#endif /* DEBUG */
+  return 0;
+}
diff --git a/libstore/tailor.h b/libstore/tailor.h
new file mode 100644
index 0000000..d207af5
--- /dev/null
+++ b/libstore/tailor.h
@@ -0,0 +1,14 @@
+
+#ifndef get_char
+#  define get_char() get_byte()
+#endif
+
+#ifndef put_char
+#  define put_char(c) put_byte(c)
+#endif
+
+#include <stdio.h>
+#define fprintf(stream, fmt...) /* ignore useless error msgs */ ((void)0)
+
+void (*unzip_error) (const char *msg);
+#define error(msg) (*unzip_error) (msg)
diff --git a/libstore/unzip.c b/libstore/unzip.c
new file mode 100644
index 0000000..d0f5bf1
--- /dev/null
+++ b/libstore/unzip.c
@@ -0,0 +1,199 @@
+/* unzip.c -- decompress files in gzip or pkzip format.
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ *
+ * The code in this file is derived from the file funzip.c written
+ * and put in the public domain by Mark Adler.
+ */
+
+/*
+   This version can extract files in gzip or pkzip format.
+   For the latter, only the first entry is extracted, and it has to be
+   either deflated or stored.
+ */
+
+#ifdef RCSID
+static char rcsid[] = "$Id: unzip.c,v 1.1 1994/12/14 04:30:25 roland Exp $";
+#endif
+
+#include "tailor.h"
+#include "gzip.h"
+#include "crypt.h"
+
+/* PKZIP header definitions */
+#define LOCSIG 0x04034b50L      /* four-byte lead-in (lsb first) */
+#define LOCFLG 6                /* offset of bit flag */
+#define  CRPFLG 1               /*  bit for encrypted entry */
+#define  EXTFLG 8               /*  bit for extended local header */
+#define LOCHOW 8                /* offset of compression method */
+#define LOCTIM 10               /* file mod time (for decryption) */
+#define LOCCRC 14               /* offset of crc */
+#define LOCSIZ 18               /* offset of compressed size */
+#define LOCLEN 22               /* offset of uncompressed length */
+#define LOCFIL 26               /* offset of file name field length */
+#define LOCEXT 28               /* offset of extra field length */
+#define LOCHDR 30               /* size of local header, including sig */
+#define EXTHDR 16               /* size of extended local header, inc sig */
+
+
+/* Globals */
+
+int decrypt;        /* flag to turn on decryption */
+char *key;          /* not used--needed to link crypt.c */
+int pkzip = 0;      /* set for a pkzip file */
+int ext_header = 0; /* set if extended local header */
+
+/* ===========================================================================
+ * Check zip file and advance inptr to the start of the compressed data.
+ * Get ofname from the local header if necessary.
+ */
+int check_zipfile(in)
+    int in;   /* input file descriptors */
+{
+    uch *h = inbuf + inptr; /* first local header */
+
+    ifd = in;
+
+    /* Check validity of local header, and skip name and extra fields */
+    inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT);
+
+    if (inptr > insize || LG(h) != LOCSIG) {
+       fprintf(stderr, "\n%s: %s: not a valid zip file\n",
+               progname, ifname);
+       exit_code = ERROR;
+       return ERROR;
+    }
+    method = h[LOCHOW];
+    if (method != STORED && method != DEFLATED) {
+       fprintf(stderr,
+               "\n%s: %s: first entry not deflated or stored -- use unzip\n",
+               progname, ifname);
+       exit_code = ERROR;
+       return ERROR;
+    }
+
+    /* If entry encrypted, decrypt and validate encryption header */
+    if ((decrypt = h[LOCFLG] & CRPFLG) != 0) {
+       fprintf(stderr, "\n%s: %s: encrypted file -- use unzip\n",
+               progname, ifname);
+       exit_code = ERROR;
+       return ERROR;
+    }
+
+    /* Save flags for unzip() */
+    ext_header = (h[LOCFLG] & EXTFLG) != 0;
+    pkzip = 1;
+
+    /* Get ofname and time stamp from local header (to be done) */
+    return OK;
+}
+
+/* ===========================================================================
+ * Unzip in to out.  This routine works on both gzip and pkzip files.
+ *
+ * IN assertions: the buffer inbuf contains already the beginning of
+ *   the compressed data, from offsets inptr to insize-1 included.
+ *   The magic header has already been checked. The output buffer is cleared.
+ */
+int unzip(in, out)
+    int in, out;   /* input and output file descriptors */
+{
+    ulg orig_crc = 0;       /* original crc */
+    ulg orig_len = 0;       /* original uncompressed length */
+    int n;
+    uch buf[EXTHDR];        /* extended local header */
+
+    ifd = in;
+    ofd = out;
+
+    updcrc(NULL, 0);           /* initialize crc */
+
+    if (pkzip && !ext_header) {  /* crc and length at the end otherwise */
+       orig_crc = LG(inbuf + LOCCRC);
+       orig_len = LG(inbuf + LOCLEN);
+    }
+
+    /* Decompress */
+    if (method == DEFLATED)  {
+
+       int res = inflate();
+
+       if (res == 3) {
+           error("out of memory");
+       } else if (res != 0) {
+           error("invalid compressed data--format violated");
+       }
+
+    } else if (pkzip && method == STORED) {
+
+       register ulg n = LG(inbuf + LOCLEN);
+
+       if (n != LG(inbuf + LOCSIZ) - (decrypt ? RAND_HEAD_LEN : 0)) {
+
+           fprintf(stderr, "len %ld, siz %ld\n", n, LG(inbuf + LOCSIZ));
+           error("invalid compressed data--length mismatch");
+       }
+       while (n--) {
+           uch c = (uch)get_byte();
+#ifdef CRYPT
+           if (decrypt) zdecode(c);
+#endif
+           put_ubyte(c);
+       }
+       flush_window();
+    } else {
+       error("internal error, invalid method");
+    }
+
+    /* Get the crc and original length */
+    if (!pkzip) {
+        /* crc32  (see algorithm.doc)
+        * uncompressed input size modulo 2^32
+         */
+       for (n = 0; n < 8; n++) {
+           buf[n] = (uch)get_byte(); /* may cause an error if EOF */
+       }
+       orig_crc = LG(buf);
+       orig_len = LG(buf+4);
+
+    } else if (ext_header) {  /* If extended header, check it */
+       /* signature - 4bytes: 0x50 0x4b 0x07 0x08
+        * CRC-32 value
+         * compressed size 4-bytes
+         * uncompressed size 4-bytes
+        */
+       for (n = 0; n < EXTHDR; n++) {
+           buf[n] = (uch)get_byte(); /* may cause an error if EOF */
+       }
+       orig_crc = LG(buf+4);
+       orig_len = LG(buf+12);
+    }
+
+    /* Validate decompression */
+    if (orig_crc != updcrc(outbuf, 0)) {
+       error("invalid compressed data--crc error");
+    }
+    if (orig_len != (ulg)bytes_out) {
+       error("invalid compressed data--length error");
+    }
+
+    /* Check if there are more entries in a pkzip file */
+    if (pkzip && inptr + 4 < insize && LG(inbuf+inptr) == LOCSIG) {
+       if (to_stdout) {
+           WARN((stderr,
+                 "%s: %s has more than one entry--rest ignored\n",
+                 progname, ifname));
+       } else {
+           /* Don't destroy the input zip file */
+           fprintf(stderr,
+                   "%s: %s has more than one entry -- unchanged\n",
+                   progname, ifname);
+           exit_code = ERROR;
+           ext_header = pkzip = 0;
+           return ERROR;
+       }
+    }
+    ext_header = pkzip = 0; /* for next file */
+    return OK;
+}
diff --git a/libstore/util.c b/libstore/util.c
new file mode 100644
index 0000000..598586f
--- /dev/null
+++ b/libstore/util.c
@@ -0,0 +1,272 @@
+/* Hacked and slashed by address@hidden for use in Hurd exec server.  */
+
+/* util.c -- utility functions for gzip support
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+#ifdef RCSID
+static char rcsid[] = "$Id: util.c,v 1.1 1994/12/14 04:29:37 roland Exp $";
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include "tailor.h"
+
+#ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+#endif
+#ifndef NO_FCNTL_H
+#  include <fcntl.h>
+#endif
+
+#if defined(STDC_HEADERS) || !defined(NO_STDLIB_H)
+#  include <stdlib.h>
+#else
+   extern int errno;
+#endif
+
+#include "gzip.h"
+#include "crypt.h"
+
+/* cruft */
+int ifd, ofd;
+int to_stdout=1,quiet=1;
+
+/* I/O interface */
+int (*unzip_read) (char *buf, size_t maxread);
+void (*unzip_write) (const char *buf, size_t nwrite);
+#define read_error() (*unzip_read_error) ()
+void (*unzip_read_error) (void);
+
+
+extern ulg crc_32_tab[];   /* crc table, defined below */
+
+int exit_code = OK;   /* program exit code */
+int method = DEFLATED;/* compression method */
+unsigned insize;           /* valid bytes in inbuf */
+unsigned inptr;            /* index of next byte to be processed in inbuf */
+unsigned outcnt;           /* bytes in output buffer */
+DECLARE(uch, inbuf,  INBUFSIZ +INBUF_EXTRA);
+DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
+DECLARE(uch, window, 2L*WSIZE);
+
+
+/* This function stolen from gzip.c. */
+/* ========================================================================
+ * Check the magic number of the input file and update ofname if an
+ * original name was given and to_stdout is not set.
+ * Return the compression method, -1 for error, -2 for warning.
+ * Set inptr to the offset of the next byte to be processed.
+ * Updates time_stamp if there is one and --no-time is not used.
+ * This function may be called repeatedly for an input file consisting
+ * of several contiguous gzip'ed members.
+ * IN assertions: there is at least one remaining compressed member.
+ *   If the member is a zip file, it must be the only one.
+ */
+int get_method(in)
+    int in;        /* input file descriptor */
+{
+    uch flags;     /* compression flags */
+    char magic[2]; /* magic header */
+    ulg stamp;     /* time stamp */
+
+    /* Prime the input buffer.  */
+    fill_inbuf(0);
+    inptr = 0;
+
+    magic[0] = (char)get_byte();
+    magic[1] = (char)get_byte();
+
+    method = -1;                 /* unknown yet */
+
+    if (memcmp(magic, GZIP_MAGIC, 2) == 0
+        || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) {
+
+       method = (int)get_byte();
+       if (method != DEFLATED)
+         return -1;
+
+       flags  = (uch)get_byte();
+       if ((flags & ENCRYPTED) != 0) return -1;
+       if ((flags & CONTINUATION) != 0) return -1;
+       if ((flags & RESERVED) != 0) return -1;
+
+       stamp  = (ulg)get_byte();
+       stamp |= ((ulg)get_byte()) << 8;
+       stamp |= ((ulg)get_byte()) << 16;
+       stamp |= ((ulg)get_byte()) << 24;
+
+       (void)get_byte();  /* Ignore extra flags for the moment */
+       (void)get_byte();  /* Ignore OS type for the moment */
+
+       if ((flags & CONTINUATION) != 0) {
+           unsigned part = (unsigned)get_byte();
+           part |= ((unsigned)get_byte())<<8;
+       }
+       if ((flags & EXTRA_FIELD) != 0) {
+           unsigned len = (unsigned)get_byte();
+           len |= ((unsigned)get_byte())<<8;
+           while (len--) (void)get_byte();
+       }
+
+       /* Get original file name if it was truncated */
+       if ((flags & ORIG_NAME) != 0) {
+         /* Discard the old name */
+         char c; /* dummy used for NeXTstep 3.0 cc optimizer bug */
+         do {c=get_byte();} while (c != 0);
+       } 
+
+       /* Discard file comment if any */
+       if ((flags & COMMENT) != 0) {
+           while (get_char() != 0) /* null */ ;
+       }
+    } else if (memcmp(magic, PKZIP_MAGIC, 2) == 0 && inptr == 2
+           && memcmp((char*)inbuf, PKZIP_MAGIC, 4) == 0) {
+      /* To simplify the code, we support a zip file when alone only.
+       * We are thus guaranteed that the entire local header fits in inbuf.
+       */
+      inptr = 0;
+      if (check_zipfile(in) == OK) return 0;
+    }
+
+    return method != DEFLATED;
+}
+
+
+/* ===========================================================================
+ * Run a set of bytes through the crc shift register.  If s is a NULL
+ * pointer, then initialize the crc shift register contents instead.
+ * Return the current crc in either case.
+ */
+ulg updcrc(s, n)
+    uch *s;                 /* pointer to bytes to pump through */
+    unsigned n;             /* number of bytes in s[] */
+{
+    register ulg c;         /* temporary variable */
+
+    static ulg crc = (ulg)0xffffffffL; /* shift register contents */
+
+    if (s == NULL) {
+       c = 0xffffffffL;
+    } else {
+       c = crc;
+        if (n) do {
+            c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8);
+        } while (--n);
+    }
+    crc = c;
+    return c ^ 0xffffffffL;       /* (instead of ~c for 64-bit machines) */
+}
+
+/* ===========================================================================
+ * Clear input and output buffers
+ */
+void clear_bufs()
+{
+    outcnt = 0;
+    insize = inptr = 0;
+}
+
+/* ===========================================================================
+ * Fill the input buffer. This is called only when the buffer is empty.
+ */
+int fill_inbuf(eof_ok)
+    int eof_ok;          /* set if EOF acceptable as a result */
+{
+    int len;
+
+    /* Read as much as possible */
+    insize = 0;
+    do {
+       len = (*unzip_read)((char*)inbuf+insize, INBUFSIZ-insize);
+        if (len == 0 || len == EOF) break;
+       insize += len;
+    } while (insize < INBUFSIZ);
+
+    if (insize == 0) {
+       if (eof_ok) return EOF;
+       read_error();
+    }
+
+    inptr = 1;
+    return inbuf[0];
+}
+
+long int bytes_out;
+
+/* ===========================================================================
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+void flush_window()
+{
+    if (outcnt == 0) return;
+    updcrc(window, outcnt);
+
+    (*unzip_write) ((char *) window, outcnt);
+    bytes_out += outcnt;
+
+    outcnt = 0;
+}
+
+/* ========================================================================
+ * Table of CRC-32's of all single-byte values (made by makecrc.c)
+ */
+ulg crc_32_tab[] = {
+  0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+  0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+  0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+  0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+  0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+  0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+  0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+  0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+  0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+  0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+  0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+  0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+  0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+  0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+  0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+  0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+  0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+  0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+  0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+  0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+  0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+  0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+  0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+  0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+  0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+  0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+  0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+  0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+  0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+  0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+  0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+  0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+  0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+  0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+  0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+  0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+  0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+  0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+  0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+  0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+  0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+  0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+  0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+  0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+  0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+  0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+  0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+  0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+  0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+  0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+  0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+  0x2d02ef8dL
+};
diff --git a/libtrivfs/Makefile b/libtrivfs/Makefile
index 1c6fd5e..241b76d 100644
--- a/libtrivfs/Makefile
+++ b/libtrivfs/Makefile
@@ -25,7 +25,7 @@ FSSRCS= dir-link.c dir-mkdir.c dir-mkfile.c dir-lookup.c 
dir-readdir.c \
        file-getlinknode.c file-lock.c file-set-trans.c file-statfs.c \
        file-sync.c file-syncfs.c file-set-size.c file-utimes.c file-exec.c \
        file-access.c dir-chg.c file-chg.c file-get-storage-info.c \
-       file-get-fs-options.c file-reparent.c
+       file-get-fs-options.c file-reparent.c get-source.c
 
 IOSRCS=io-async-icky.c io-async.c io-duplicate.c io-map.c io-modes-get.c \
        io-modes-off.c io-modes-on.c io-modes-set.c io-owner-get.c \
@@ -34,7 +34,8 @@ IOSRCS=io-async-icky.c io-async.c io-duplicate.c io-map.c 
io-modes-get.c \
        io-stat.c io-stubs.c io-write.c io-version.c io-identity.c
 
 FSYSSRCS=fsys-getroot.c fsys-goaway.c fsys-stubs.c fsys-syncfs.c \
-       fsys-forward.c fsys-set-options.c fsys-get-options.c
+       fsys-forward.c fsys-set-options.c fsys-get-options.c \
+       fsys-get-children.c fsys-get-source.c
 
 OTHERSRCS=demuxer.c protid-clean.c protid-dup.c cntl-create.c \
        cntl-clean.c migsupport.c times.c startup.c open.c \
diff --git a/libtrivfs/fsys-get-children.c b/libtrivfs/fsys-get-children.c
new file mode 100644
index 0000000..4697cc5
--- /dev/null
+++ b/libtrivfs/fsys-get-children.c
@@ -0,0 +1,35 @@
+/* fsys_get_children
+
+   Copyright (C) 2013 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/>.  */
+
+#include "priv.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.  */
+error_t
+trivfs_S_fsys_get_children (trivfs_control_t server,
+                           mach_port_t reply,
+                           mach_msg_type_name_t replyPoly,
+                           char **children,
+                           mach_msg_type_number_t *children_len)
+{
+  return EOPNOTSUPP;
+}
diff --git a/libtrivfs/fsys-get-source.c b/libtrivfs/fsys-get-source.c
new file mode 100644
index 0000000..64aec2f
--- /dev/null
+++ b/libtrivfs/fsys-get-source.c
@@ -0,0 +1,33 @@
+/* fsys_get_source
+
+   Copyright (C) 2013 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/>.  */
+
+#include "priv.h"
+
+/* Return information about the source of the receiving
+   filesystem. */
+error_t
+trivfs_S_fsys_get_source (trivfs_control_t server,
+                         mach_port_t reply,
+                         mach_msg_type_name_t replyPoly,
+                         char *source)
+{
+  return trivfs_get_source (source);
+}
diff --git a/libtrivfs/get-source.c b/libtrivfs/get-source.c
new file mode 100644
index 0000000..9ea5693
--- /dev/null
+++ b/libtrivfs/get-source.c
@@ -0,0 +1,28 @@
+/* Default version of trivfs_get_source
+
+   Copyright (C) 2013 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/>.  */
+
+#include "priv.h"
+
+error_t
+trivfs_get_source (char *source)
+{
+  return EOPNOTSUPP;
+}
diff --git a/libtrivfs/startup.c b/libtrivfs/startup.c
index 86b0f82..4d76d47 100644
--- a/libtrivfs/startup.c
+++ b/libtrivfs/startup.c
@@ -65,5 +65,24 @@ trivfs_startup(mach_port_t bootstrap, int flags,
   if (!err && control)
     *control = fsys;
 
+  /* Mark us as important.  */
+  if (! err)
+    {
+      mach_port_t proc = getproc ();
+      if (proc == MACH_PORT_NULL)
+       /* /hurd/exec uses libtrivfs.  We have no handle to the proc
+          server in /hurd/exec when it does its handshake with the
+          root filesystem, so fail graciously here.  */
+       return 0;
+
+      err = proc_mark_important (proc);
+      /* This might fail due to permissions or because the old proc
+        server is still running, ignore any such errors.  */
+      if (err == EPERM || err == EMIG_BAD_ID)
+       err = 0;
+
+      mach_port_deallocate (mach_task_self (), proc);
+    }
+
   return err;
 }
diff --git a/libtrivfs/trivfs.h b/libtrivfs/trivfs.h
index 33b0436..cf817b5 100644
--- a/libtrivfs/trivfs.h
+++ b/libtrivfs/trivfs.h
@@ -1,5 +1,5 @@
 /*
-   Copyright (C) 1994,95,96,97,99,2002 Free Software Foundation, Inc.
+   Copyright (C) 1994,95,96,97,99,2002,13 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
@@ -233,6 +233,12 @@ error_t trivfs_set_options (struct trivfs_control *fsys,
    routine simply calls diskfs_append_std_options.  */
 error_t trivfs_append_args (struct trivfs_control *fsys,
                            char **argz, size_t *argz_len);
+
+/* The user may define this function.  The function must set source to
+   the source device of the filesystem. The function may return an
+   EOPNOTSUPP to indicate that the concept of a source device is not
+   applicable. The default function always returns EOPNOTSUPP. */
+error_t trivfs_get_source (char *source);
 
 /* Add the port class *CLASS to the list of control port classes recognized
    by trivfs; if *CLASS is 0, an attempt is made to allocate a new port
diff --git a/mach-defpager/main.c b/mach-defpager/main.c
index 3134d6f..fe6f735 100644
--- a/mach-defpager/main.c
+++ b/mach-defpager/main.c
@@ -125,6 +125,19 @@ main (int argc, char **argv)
        }
     }
 
+  /* Mark us as important.  */
+  mach_port_t proc = getproc ();
+  if (proc == MACH_PORT_NULL)
+    error (3, err, "cannot get a handle to our process");
+
+  err = proc_mark_important (proc);
+  /* This might fail due to permissions or because the old proc server
+     is still running, ignore any such errors.  */
+  if (err && err != EPERM && err != EMIG_BAD_ID)
+    error (3, err, "cannot mark us as important");
+
+  mach_port_deallocate (mach_task_self (), proc);
+
   printf_init(bootstrap_master_device_port);
 
   /*
diff --git a/pfinet/main.c b/pfinet/main.c
index 01b324d..c952719 100644
--- a/pfinet/main.c
+++ b/pfinet/main.c
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <fcntl.h>
 #include <version.h>
+#include <pids.h>
 
 /* Include Hurd's errno.h file, but don't include glue-include/hurd/errno.h,
    since it #undef's the errno macro. */
@@ -153,7 +154,7 @@ arrange_shutdown_notification ()
   if (!procserver)
     return;
 
-  err = proc_getmsgport (procserver, 1, &initport);
+  err = proc_getmsgport (procserver, HURD_PID_STARTUP, &initport);
   mach_port_deallocate (mach_task_self (), procserver);
   if (err)
     return;
diff --git a/pfinet/tunnel.c b/pfinet/tunnel.c
index 4a6f616..495356c 100644
--- a/pfinet/tunnel.c
+++ b/pfinet/tunnel.c
@@ -479,14 +479,23 @@ io_select_common (struct trivfs_protid *cred,
 
   tdev = (struct tunnel_device *) cred->po->cntl->hook;
 
-  /* We only deal with SELECT_READ here.  */
-  *type &= SELECT_READ;
+  /* We only deal with SELECT_READ and SELECT_WRITE here.  */
+  *type &= SELECT_READ | SELECT_WRITE;
 
   if (*type == 0)
     return 0;
 
   pthread_mutex_lock (&tdev->lock);
 
+  if (*type & SELECT_WRITE)
+    {
+      /* We are always writable.  */
+      if (skb_queue_len (&tdev->xq) == 0)
+       *type &= ~SELECT_READ;
+      pthread_mutex_unlock (&tdev->lock);
+      return 0;
+    }
+
   while (1)
     {
       if (skb_queue_len (&tdev->xq) != 0)
diff --git a/proc/info.c b/proc/info.c
index 40f9d21..0d502c6 100644
--- a/proc/info.c
+++ b/proc/info.c
@@ -37,7 +37,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 
02139, USA.  */
 /* Returns true if PROC1 has `owner' privileges over PROC2 (and can thus get
    its task port &c).  If PROC2 has an owner, then PROC1 must have that uid;
    otherwise, both must be in the same login collection.  */
-static inline int
+int
 check_owner (struct proc *proc1, struct proc *proc2)
 {
   return
diff --git a/proc/main.c b/proc/main.c
index 494169e..73abbc0 100644
--- a/proc/main.c
+++ b/proc/main.c
@@ -28,6 +28,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 
02139, USA.  */
 #include <argp.h>
 #include <error.h>
 #include <version.h>
+#include <pids.h>
 
 #include "proc.h"
 
@@ -87,7 +88,7 @@ main (int argc, char **argv, char **envp)
   self_proc = allocate_proc (mach_task_self ());
   assert (self_proc);
 
-  complete_proc (self_proc, 0);
+  complete_proc (self_proc, HURD_PID_PROC);
 
   startup_port = ports_get_send_right (startup_proc);
   err = startup_procinit (boot, startup_port, &startup_proc->p_task,
diff --git a/proc/mgt.c b/proc/mgt.c
index 7af9c1a..c093b8f 100644
--- a/proc/mgt.c
+++ b/proc/mgt.c
@@ -32,6 +32,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 
02139, USA.  */
 #include <sys/resource.h>
 #include <hurd/auth.h>
 #include <assert.h>
+#include <pids.h>
 
 #include "proc.h"
 #include "process_S.h"
@@ -209,6 +210,15 @@ S_proc_child (struct proc *parentp,
                            childp->p_parent->p_pid, childp->p_pgrp->pg_pgid,
                            !childp->p_pgrp->pg_orphcnt);
   childp->p_parentset = 1;
+
+  /* If these are not set in the child, it was probably fork(2)ed.  If
+     so, it inherits the values of its parent.  */
+  if (! childp->start_code && ! childp->end_code)
+    {
+      childp->start_code = parentp->start_code;
+      childp->end_code = parentp->end_code;
+    }
+
   return 0;
 }
 
@@ -582,7 +592,7 @@ create_startup_proc (void)
   p = allocate_proc (MACH_PORT_NULL);
   assert (p);
 
-  p->p_pid = 1;
+  p->p_pid = HURD_PID_STARTUP;
 
   p->p_parent = p;
   p->p_sib = 0;
@@ -592,6 +602,8 @@ create_startup_proc (void)
 
   p->p_deadmsg = 1;            /* Force initial "re-"fetch of msgport.  */
 
+  p->p_important = 1;
+
   p->p_noowner = 0;
   p->p_id = make_ids (&zero, 1);
   assert (p->p_id);
@@ -867,3 +879,66 @@ genpid ()
 
   return nextpid++;
 }
+
+/* Implement proc_mark_important as described in <hurd/process.defs>. */
+kern_return_t
+S_proc_mark_important (struct proc *p)
+{
+  if (!p)
+    return EOPNOTSUPP;
+
+  /* Only root may use this interface.  Any children of startup_proc
+     exempt from this restriction, as startup_proc calls this on their
+     behalf.  The kernel process is a notable example of an process
+     that needs this exemption.  That is not an problem however, since
+     all children of /hurd/init are important and we mark them as such
+     anyway.  */
+  if (! check_uid (p, 0) && ! check_owner (startup_proc, p))
+    return EPERM;
+
+  p->p_important = 1;
+  return 0;
+}
+
+/* Implement proc_is_important as described in <hurd/process.defs>.  */
+error_t
+S_proc_is_important (struct proc *callerp,
+                    boolean_t *essential)
+{
+  if (!callerp)
+    return EOPNOTSUPP;
+
+  *essential = callerp->p_important;
+
+  return 0;
+}
+
+/* Implement proc_set_code as described in <hurd/process.defs>.  */
+error_t
+S_proc_set_code (struct proc *callerp,
+                vm_address_t start_code,
+                vm_address_t end_code)
+{
+  if (!callerp)
+    return EOPNOTSUPP;
+
+  callerp->start_code = start_code;
+  callerp->end_code = end_code;
+
+  return 0;
+}
+
+/* Implement proc_get_code as described in <hurd/process.defs>.  */
+error_t
+S_proc_get_code (struct proc *callerp,
+                vm_address_t *start_code,
+                vm_address_t *end_code)
+{
+  if (!callerp)
+    return EOPNOTSUPP;
+
+  *start_code = callerp->start_code;
+  *end_code = callerp->end_code;
+
+  return 0;
+}
diff --git a/proc/pgrp.c b/proc/pgrp.c
index 2d6ca93..d4ea9ee 100644
--- a/proc/pgrp.c
+++ b/proc/pgrp.c
@@ -1,5 +1,5 @@
 /* Session and process group manipulation
-   Copyright (C) 1992,93,94,95,96,99,2001,02 Free Software Foundation, Inc.
+   Copyright (C) 1992,93,94,95,96,99,2001,02,13 Free Software Foundation, Inc.
 
 This file is part of the GNU Hurd.
 
@@ -265,7 +265,7 @@ S_proc_getpgrppids (struct proc *callerp,
 
   count = 0;
   for (p = pg->pg_plist; p; p = p->p_gnext)
-    if (++count <= npids)
+    if (!p->p_important && ++count <= npids)
       *pp++ = p->p_pid;
 
   if (count > npids)
@@ -278,7 +278,8 @@ S_proc_getpgrppids (struct proc *callerp,
 
       pp = *pids;
       for (p = pg->pg_plist; p; p = p->p_gnext)
-       *pp++ = p->p_pid;
+       if (!p->p_important)
+         *pp++ = p->p_pid;
       /* Dealloc ? XXX */
     }
   *npidsp = count;
diff --git a/proc/proc.h b/proc/proc.h
index 247795d..80f8397 100644
--- a/proc/proc.h
+++ b/proc/proc.h
@@ -64,6 +64,8 @@ struct proc
 
   /* Miscellaneous information */
   vm_address_t p_argv, p_envp;
+  vm_address_t start_code;     /* all executable segments are in this range */
+  vm_address_t end_code;
   int p_status;                        /* to return via wait */
   int p_sigcode;
   struct rusage p_rusage;      /* my usage if I'm dead, to return via wait */
@@ -84,6 +86,7 @@ struct proc
   unsigned int p_noowner:1;    /* has no owner known */
   unsigned int p_loginleader:1;        /* leader of login collection */
   unsigned int p_dead:1;       /* process is dead */
+  unsigned int p_important:1;  /* has called proc_mark_important */
 };
 
 typedef struct proc *pstruct_t;
@@ -129,8 +132,6 @@ struct exc
   natural_t thread_state[0];
 };
 
-struct zombie *zombie_list;
-
 mach_port_t authserver;
 struct proc *self_proc;                /* process 0 (us) */
 struct proc *startup_proc;     /* process 1 (init) */
@@ -157,6 +158,7 @@ process_drop (struct proc *p)
 /* Forward declarations */
 void complete_wait (struct proc *, int);
 int check_uid (struct proc *, uid_t);
+int check_owner (struct proc *, struct proc *);
 void addalltasks (void);
 void prociterate (void (*)(struct proc *, void *), void *);
 void count_up (void *);
diff --git a/procfs/ChangeLog b/procfs/ChangeLog
new file mode 100644
index 0000000..0cd74d0
--- /dev/null
+++ b/procfs/ChangeLog
@@ -0,0 +1,6 @@
+edb4593c38d421b5d538b221a991b50c36fdba15 is the last commit imported from CVS.
+All commits after that one have valid author and committer information.
+
+Use this to examine the change log for earlier changes:
+
+    $ git show edb4593c38d421b5d538b221a991b50c36fdba15:ChangeLog
diff --git a/procfs/main.c b/procfs/main.c
index 90b3e92..54e9682 100644
--- a/procfs/main.c
+++ b/procfs/main.c
@@ -22,6 +22,7 @@
 #include <unistd.h>
 #include <error.h>
 #include <argp.h>
+#include <argz.h>
 #include <hurd/netfs.h>
 #include <ps.h>
 #include "procfs.h"
@@ -37,6 +38,13 @@ pid_t opt_fake_self;
 pid_t opt_kernel_pid;
 uid_t opt_anon_owner;
 
+/* Default values */
+#define OPT_CLK_TCK    sysconf(_SC_CLK_TCK)
+#define OPT_STAT_MODE  0400
+#define OPT_FAKE_SELF  -1
+#define OPT_KERNEL_PID 2
+#define OPT_ANON_OWNER 0
+
 #define NODEV_KEY  -1 /* <= 0, so no short option. */
 #define NOEXEC_KEY -2 /* Likewise. */
 #define NOSUID_KEY -3 /* Likewise. */
@@ -120,50 +128,90 @@ argp_parser (int key, char *arg, struct argp_state *state)
     case NOSUID_KEY:
       /* Ignored for compatibility with Linux' procfs. */
       ;;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
   }
 
   return 0;
 }
 
+struct argp_option common_options[] = {
+  { "clk-tck", 'h', "HZ", 0,
+      "Unit used for the values expressed in system clock ticks "
+      "(default: sysconf(_SC_CLK_TCK))" },
+  { "stat-mode", 's', "MODE", 0,
+      "The [pid]/stat file publishes information which on Hurd is only "
+      "available to the process owner.  "
+      "You can use this option to override its mode to be more permissive "
+      "for compatibility purposes.  "
+      "(default: 0400)" },
+  { "fake-self", 'S', "PID", OPTION_ARG_OPTIONAL,
+      "Provide a fake \"self\" symlink to the given PID, for compatibility "
+      "purposes.  If PID is omitted, \"self\" will point to init.  "
+      "(default: no self link)" },
+  { "kernel-process", 'k', "PID", 0,
+      "Process identifier for the kernel, used to retreive its command "
+      "line, as well as the global up and idle times. "
+      "(default: 2)" },
+  { "compatible", 'c', NULL, 0,
+      "Try to be compatible with the Linux procps utilities.  "
+      "Currently equivalent to -h 100 -s 0444 -S 1." },
+  { "anonymous-owner", 'a', "USER", 0,
+      "Make USER the owner of files related to processes without one.  "
+      "Be aware that USER will be granted access to the environment and "
+      "other sensitive information about the processes in question.  "
+      "(default: use uid 0)" },
+  { "nodev", NODEV_KEY, NULL, 0,
+      "Ignored for compatibility with Linux' procfs." },
+  { "noexec", NOEXEC_KEY, NULL, 0,
+      "Ignored for compatibility with Linux' procfs." },
+  { "nosuid", NOSUID_KEY, NULL, 0,
+      "Ignored for compatibility with Linux' procfs." },
+  {}
+};
+
 struct argp argp = {
-  .options = (struct argp_option []) {
-    { "clk-tck", 'h', "HZ", 0,
-       "Unit used for the values expressed in system clock ticks "
-       "(default: sysconf(_SC_CLK_TCK))" },
-    { "stat-mode", 's', "MODE", 0,
-       "The [pid]/stat file publishes information which on Hurd is only "
-       "available to the process owner.  "
-       "You can use this option to override its mode to be more permissive "
-       "for compatibility purposes.  "
-       "(default: 0400)" },
-    { "fake-self", 'S', "PID", OPTION_ARG_OPTIONAL,
-       "Provide a fake \"self\" symlink to the given PID, for compatibility "
-       "purposes.  If PID is omitted, \"self\" will point to init.  "
-       "(default: no self link)" },
-    { "kernel-process", 'k', "PID", 0,
-       "Process identifier for the kernel, used to retreive its command "
-       "line, as well as the global up and idle times. "
-       "(default: 2)" },
-    { "compatible", 'c', NULL, 0,
-       "Try to be compatible with the Linux procps utilities.  "
-       "Currently equivalent to -h 100 -s 0444 -S 1." },
-    { "anonymous-owner", 'a', "USER", 0,
-       "Make USER the owner of files related to processes without one.  "
-       "Be aware that USER will be granted access to the environment and "
-       "other sensitive information about the processes in question.  "
-       "(default: use uid 0)" },
-    { "nodev", NODEV_KEY, NULL, 0,
-       "Ignored for compatibility with Linux' procfs." },
-    { "noexec", NOEXEC_KEY, NULL, 0,
-       "Ignored for compatibility with Linux' procfs." },
-    { "nosuid", NOSUID_KEY, NULL, 0,
-       "Ignored for compatibility with Linux' procfs." },
+  .options = common_options,
+  .parser = argp_parser,
+  .doc = "A virtual filesystem emulating the Linux procfs.",
+  .children = (struct argp_child []) {
+    { &netfs_std_startup_argp, },
     {}
   },
+};
+
+static error_t
+runtime_argp_parser (int key, char *arg, struct argp_state *state)
+{
+  switch (key)
+  {
+    case 'u':
+      /* do nothing */
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+  }
+
+  return 0;
+}
+
+struct argp runtime_argp = {
+  .options = (struct argp_option []) {
+    { "update", 'u', NULL, 0, "remount; for procfs this does nothing" },
+    {},
+  },
+  .parser = runtime_argp_parser,
+};
+
+struct argp netfs_runtime_argp_ = {
+  .options = common_options,
   .parser = argp_parser,
   .doc = "A virtual filesystem emulating the Linux procfs.",
   .children = (struct argp_child []) {
-    { &netfs_std_startup_argp, },
+    { &runtime_argp, },
+    { &netfs_std_runtime_argp, },
     {}
   },
 };
@@ -171,6 +219,47 @@ struct argp argp = {
 /* Used by netfs_set_options to handle runtime option parsing.  */
 struct argp *netfs_runtime_argp = &argp;
 
+/* Return an argz string describing the current options.  Fill *ARGZ
+   with a pointer to newly malloced storage holding the list and *LEN
+   to the length of that storage.  */
+error_t
+netfs_append_args (char **argz, size_t *argz_len)
+{
+  char buf[80];
+  error_t err = 0;
+
+#define FOPT(opt, default, fmt, args...)             \
+  do { \
+    if (! err && opt != default) \
+      { \
+       snprintf (buf, sizeof buf, fmt, ## args); \
+       err = argz_add (argz, argz_len, buf); \
+      } \
+  } while (0)
+
+  FOPT (opt_clk_tck, OPT_CLK_TCK,
+        "--clk-tck=%d", opt_clk_tck);
+
+  FOPT (opt_stat_mode, OPT_STAT_MODE,
+        "--stat-mode=%o", opt_stat_mode);
+
+  FOPT (opt_fake_self, OPT_FAKE_SELF,
+        "--fake-self=%d", opt_fake_self);
+
+  FOPT (opt_anon_owner, OPT_ANON_OWNER,
+        "--anonymous-owner=%d", opt_anon_owner);
+
+  FOPT (opt_kernel_pid, OPT_KERNEL_PID,
+        "--kernel-process=%d", opt_kernel_pid);
+
+#undef FOPT
+
+  if (! err)
+    err = netfs_append_std_options (argz, argz_len);
+
+  return err;
+}
+
 error_t
 root_make_node (struct ps_context *pc, struct node **np)
 {
@@ -196,11 +285,11 @@ int main (int argc, char **argv)
   mach_port_t bootstrap;
   error_t err;
 
-  opt_clk_tck = sysconf(_SC_CLK_TCK);
-  opt_stat_mode = 0400;
-  opt_fake_self = -1;
-  opt_kernel_pid = 2;
-  opt_anon_owner = 0;
+  opt_clk_tck = OPT_CLK_TCK;
+  opt_stat_mode = OPT_STAT_MODE;
+  opt_fake_self = OPT_FAKE_SELF;
+  opt_kernel_pid = OPT_KERNEL_PID;
+  opt_anon_owner = OPT_ANON_OWNER;
   err = argp_parse (&argp, argc, argv, 0, 0, 0);
   if (err)
     error (1, err, "Could not parse command line");
diff --git a/procfs/process.c b/procfs/process.c
index c5ef7d8..269a18b 100644
--- a/procfs/process.c
+++ b/procfs/process.c
@@ -116,6 +116,26 @@ process_file_gc_stat (struct proc_stat *ps, char 
**contents)
   thread_basic_info_t thbi = proc_stat_thread_basic_info (ps);
   const char *fn = args_filename (proc_stat_args (ps));
 
+  vm_address_t start_code = 1; /* 0 would make killall5.c consider it
+                                 a kernel process, thus use 1 as
+                                 default.  */
+  vm_address_t end_code = 1;
+  process_t p;
+  error_t err = proc_pid2proc (ps->context->server, ps->pid, &p);
+  if (! err)
+    {
+      boolean_t essential = 0;
+      proc_is_important (p, &essential);
+      if (essential)
+       start_code = end_code = 0; /* To make killall5.c consider it a
+                                     kernel process that is to be
+                                     left alone.  */
+      else
+       proc_get_code (p, &start_code, &end_code);
+
+      mach_port_deallocate (mach_task_self (), p);
+    }
+
   /* See proc(5) for more information about the contents of each field for the
      Linux procfs.  */
   return asprintf (contents,
@@ -152,7 +172,9 @@ process_file_gc_stat (struct proc_stat *ps, char **contents)
       timeval_jiffies (thbi->creation_time), /* FIXME: ... since boot */
       (long unsigned) tbi->virtual_size,
       (long unsigned) tbi->resident_size / PAGE_SIZE, 0L,
-      0L, 0L, 0L, 0L, 0L,
+      start_code,
+      end_code,
+      0L, 0L, 0L,
       0L, 0L, 0L, 0L,
       (long unsigned) proc_stat_thread_rpc (ps), /* close enough */
       0L, 0L,
diff --git a/procfs/rootdir.c b/procfs/rootdir.c
index f234dd0..34bf91c 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 Free Software Foundation, Inc.
+   Copyright (C) 2010,13 Free Software Foundation, Inc.
 
    This file is part of the GNU Hurd.
 
@@ -404,6 +404,31 @@ 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 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;
+}
 
 /* Glue logic and entries table */
 
@@ -426,6 +451,18 @@ rootdir_symlink_make_node (void *dir_hook, const void 
*entry_hook)
   return np;
 }
 
+static struct node *
+rootdir_translator_make_node (void *dir_hook, const void *entry_hook)
+{
+  struct node *np = procfs_make_node (entry_hook, dir_hook);
+  if (np)
+    {
+      procfs_node_chtype (np, S_IFREG | S_IPTRANS);
+      procfs_node_chmod (np, 0444);
+    }
+  return np;
+}
+
 static const struct procfs_dir_entry rootdir_entries[] = {
   {
     .name = "self",
@@ -487,6 +524,16 @@ static const struct procfs_dir_entry rootdir_entries[] = {
       .cleanup_contents = procfs_cleanup_contents_with_free,
     },
   },
+  {
+    .name = "mounts",
+    .hook = & (struct procfs_node_ops) {
+      .get_translator = rootdir_mounts_get_translator,
+    },
+    .ops = {
+      .make_node = rootdir_translator_make_node,
+      .exists = rootdir_mounts_exists,
+    }
+  },
 #ifdef PROFILE
   /* In order to get a usable gmon.out file, we must apparently use exit(). */
   {
diff --git a/storeio/pager.c b/storeio/pager.c
index cbae7eb..7d78711 100644
--- a/storeio/pager.c
+++ b/storeio/pager.c
@@ -110,6 +110,13 @@ pager_unlock_page (struct user_pager_info *upi, 
vm_offset_t address)
     return 0;
 }
 
+void
+pager_notify_evict (struct user_pager_info *pager,
+                   vm_offset_t page)
+{
+  assert (!"unrequested notification on eviction");
+}
+
 /* The user must define this function.  It should report back (in
    *OFFSET and *SIZE the minimum valid address the pager will accept
    and the size of the object.   */
@@ -246,7 +253,7 @@ dev_get_memory_object (struct dev *dev, vm_prot_t prot, 
memory_object_t *memobj)
        {
          dev->pager =
            pager_create ((struct user_pager_info *)dev, pager_port_bucket,
-                         1, MEMORY_OBJECT_COPY_DELAY);
+                         1, MEMORY_OBJECT_COPY_DELAY, 0);
          if (dev->pager == NULL)
            {
              pthread_mutex_unlock (&dev->pager_lock);
diff --git a/sutils/fstab.c b/sutils/fstab.c
index 1492ccd..ed59151 100644
--- a/sutils/fstab.c
+++ b/sutils/fstab.c
@@ -457,11 +457,19 @@ fs_remount (struct fs *fs)
   return err;
 }
 
-/* Returns the FS entry in FSTAB with the device field NAME (there can only
-   be one such entry).  */
+/* Returns the FS entry in FSTAB with the device field NAME.
+
+   In general there can only be one such entry. This holds not true
+   for virtual file systems that use "none" as device name.
+
+   If name is "none", NULL is returned. This also makes it possible to
+   add more than one entry for the device "none". */
 inline struct fs *
 fstab_find_device (const struct fstab *fstab, const char *name)
 {
+  if (strcmp (name, "none") == 0)
+    return NULL;
+
   struct fs *fs;
   for (fs = fstab->entries; fs; fs = fs->next)
     if (strcmp (fs->mntent.mnt_fsname, name) == 0)
@@ -880,90 +888,41 @@ fstab_argp_create (struct fstab_argp_params *params,
        check = fstab;
       else
        {
-         struct fs *fs;
-         const char *tn;
-         unsigned int nonexclude_types;
-
          err = fstab_create (types, &check);
          if (err)
            error (105, err, "fstab_create");
 
-         /* For each excluded type (i.e. `-t notype'), clobber the
-            fstype entry's program with an empty string to mark it.  */
-         nonexclude_types = 0;
-         for (tn = params->types; tn;
-              tn = argz_next (params->types, params->types_len, tn))
-           {
-             if (!strncasecmp (tn, "no", 2))
-               {
-                 struct fstype *type;
-                 err = fstypes_get (types, &tn[2], &type);
-                 if (err)
-                   error (106, err, "fstypes_get");
-                 free (type->program);
-                 type->program = strdup ("");
-               }
-             else
-               ++nonexclude_types;
-           }
-
-         if (nonexclude_types != 0)
-           {
-             const char *tn;
-             struct fstypes *wanttypes;
-
-             /* We will copy the types we want to include into a fresh
-                list in WANTTYPES.  Since we specify no search formats,
-                `fstypes_get' applied to WANTTYPES can only create
-                elements with a null `program' field.  */
-             err = fstypes_create (0, 0, &wanttypes);
-             if (err)
-               error (102, err, "fstypes_create");
-
-             for (tn = params->types; tn;
-                  tn = argz_next (params->types, params->types_len, tn))
-               if (strncasecmp (tn, "no", 2))
-                 {
-                   struct fstype *type;
-                   err = fstypes_get (types, tn, &type);
-                   if (err)
-                     error (106, err, "fstypes_get");
-                   if (type->program == 0)
-                     error (0, 0,
-                            "requested filesystem type `%s' unknown", tn);
-                   else
-                     {
-                       struct fstype *newtype = malloc (sizeof *newtype);
-                       newtype->name = strdup (type->name);
-                       newtype->program = strdup (type->program);
-                       newtype->next = wanttypes->entries;
-                       wanttypes->entries = newtype;
-                     }
-                 }
-
-             /* fstypes_free (types); */
-             types = wanttypes;
-           }
+          int blacklist = strncasecmp (params->types, "no", 2) == 0;
+          if (blacklist)
+            params->types += 2; /* Skip no. */
 
+         struct fs *fs;
          for (fs = fstab->entries; fs; fs = fs->next)
            {
-             const char *ptn;
-             struct fstype *type;
-
-             err = fs_type (fs, &type);
-             if (err || nonexclude_types)
-               {
-                 err = fstypes_get (types, fs->mntent.mnt_type, &type);
-                 if (err)
-                   error (106, err, "fstypes_get");
-                 if (params->types != 0)
-                   continue;
-               }
-             if (nonexclude_types && type->program == 0)
-               continue;       /* Freshly created, was not in WANTTYPES.  */
-             if (type->program != 0 && type->program[0] == '\0')
-               continue;       /* This type is marked as excluded.  */
+              if (strcmp (fs->mntent.mnt_type, MNTTYPE_SWAP) == 0)
+                continue; /* Ignore swap entries. */
+
+              const char *tn;
+              int matched = 0;
+              for (tn = params->types; tn;
+                   tn = argz_next (params->types, params->types_len, tn))
+                {
+                  const char *type = fs->mntent.mnt_type;
+                  if (strcmp (type, tn) == 0
+                      /* Skip no for compatibility. */
+                      || ((strncasecmp (type, "no", 2) == 0)
+                          && strcmp (type, tn) == 0))
+                    {
+                      matched = 1;
+                      break;
+                    }
+                }
+
+              if (matched == blacklist)
+                continue; /* Either matched and types is a blacklist
+                             or not matched and types is a whitelist */
 
+             const char *ptn;
              for (ptn = params->exclude; ptn;
                   ptn = argz_next (params->exclude, params->exclude_len, ptn))
                if (fnmatch (ptn, fs->mntent.mnt_dir, 0) == 0)
diff --git a/tmpfs/pager-stubs.c b/tmpfs/pager-stubs.c
index 25d70fe..3cb264b 100644
--- a/tmpfs/pager-stubs.c
+++ b/tmpfs/pager-stubs.c
@@ -57,6 +57,14 @@ pager_unlock_page (struct user_pager_info *pager,
   return EIEIO;
 }
 
+void
+pager_notify_evict (struct user_pager_info *pager,
+                   vm_offset_t page)
+{
+  abort();
+}
+
+
 /* The user must define this function.  It should report back (in
    *OFFSET and *SIZE the minimum valid address the pager will accept
    and the size of the object.   */
diff --git a/tmpfs/tmpfs.c b/tmpfs/tmpfs.c
index 7da3dd5..1872a7d 100644
--- a/tmpfs/tmpfs.c
+++ b/tmpfs/tmpfs.c
@@ -29,6 +29,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 
02139, USA.  */
 #include <fcntl.h>
 #include <hurd.h>
 #include <hurd/paths.h>
+#include <nullauth.h>
 
 char *diskfs_server_name = "tmpfs";
 char *diskfs_server_version = HURD_VERSION;
@@ -437,6 +438,11 @@ main (int argc, char **argv)
   /* We must keep the REALNODE send right to remain the active
      translator for the underlying node.  */
 
+  /* Drop all privileges.  */
+  err = setnullauth();
+  if (err)
+    error (1, err, "Could not drop privileges");
+
   pthread_mutex_unlock (&diskfs_root_node->lock);
 
   /* and so we die, leaving others to do the real work.  */
diff --git a/trans/Makefile b/trans/Makefile
index b3210b6..6eb51d0 100644
--- a/trans/Makefile
+++ b/trans/Makefile
@@ -20,14 +20,15 @@ dir := trans
 makemode := servers
 
 targets = symlink firmlink ifsock magic null fifo new-fifo fwd crash \
-         password hello hello-mt streamio fakeroot proxy-defpager remap
+         password hello hello-mt streamio fakeroot proxy-defpager remap \
+         mtab
 SRCS = ifsock.c symlink.c magic.c null.c fifo.c new-fifo.c fwd.c \
        crash.c firmlink.c password.c hello.c hello-mt.c streamio.c \
-       fakeroot.c proxy-defpager.c remap.c
+       fakeroot.c proxy-defpager.c remap.c mtab.c
 OBJS = $(SRCS:.c=.o) fsysServer.o ifsockServer.o passwordServer.o \
        crashServer.o crash_replyUser.o msgServer.o \
        default_pagerServer.o default_pagerUser.o \
-       device_replyServer.o elfcore.o
+       device_replyServer.o elfcore.o fsysUser.o
 HURDLIBS = ports netfs trivfs iohelp fshelp pipe ihash shouldbeinlibc
 LDLIBS += -lpthread
 password-LDLIBS = $(LIBCRYPT)
@@ -51,6 +52,7 @@ magic: ../libiohelp/libiohelp.a
 hello: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a 
../libports/libports.a ../libihash/libihash.a
 fakeroot: ../libnetfs/libnetfs.a ../libfshelp/libfshelp.a 
../libiohelp/libiohelp.a ../libports/libports.a ../libihash/libihash.a
 remap: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a 
../libports/libports.a ../libihash/libihash.a
+mtab: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a 
../libihash/libihash.a fsysUser.o
 $(targets): ../libshouldbeinlibc/libshouldbeinlibc.a
 
 $(targets): %: %.o
diff --git a/trans/mtab.c b/trans/mtab.c
new file mode 100644
index 0000000..2973a0b
--- /dev/null
+++ b/trans/mtab.c
@@ -0,0 +1,800 @@
+/* This is an mtab translator.
+
+   Copyright (C) 2013 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/>.  */
+
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <fcntl.h>
+#include <hurd.h>
+#include <hurd/trivfs.h>
+#include <inttypes.h>
+#include <mntent.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <version.h>
+
+#include "fsys_U.h"
+
+static char *path = NULL;
+static int insecure = 0;
+
+/* Our control port.  */
+struct trivfs_control *control;
+
+/* These kind of objects are created and populated in the open_hook.
+   They keep track of the content and file position of the client.  */
+struct mtab
+{
+  char *contents;
+  size_t contents_len;
+  off_t offs;
+};
+
+const char *argp_program_version = STANDARD_HURD_VERSION (mtab);
+
+static const struct argp_option options[] =
+{
+  {"insecure", 'I', 0, 0,
+   "Follow translators not bound to nodes owned by you or root"},
+  {}
+};
+
+/* Parse a command line option.         */
+error_t parse_opt (int key, char *arg, struct argp_state *state)
+{
+  switch (key)
+    {
+    case 'I':
+      insecure = 1;
+      break;
+
+    case ARGP_KEY_ARG:
+      path = realpath (arg, NULL);
+      if (! path)
+       argp_error (state, "Error while canonicalizing path");
+      break;
+
+    case ARGP_KEY_NO_ARGS:
+      argp_usage (state);
+      return EINVAL;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+  return 0;
+}
+
+static struct argp argp =
+  {
+    options,
+    parse_opt,
+    "TARGET\tFile name of a node with an active translator",
+    "A translator providing mtab compatible information about active "
+    "and passive translators below TARGET.",
+  };
+
+/* 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;
+
+  if (insecure)
+    {
+      err = argz_add (argz, argz_len, path);
+      if (err)
+       return err;
+    }
+
+  err = argz_add (argz, argz_len, path);
+  return err;
+}
+
+/* Setting this variable makes libtrivfs use our argp to
+   parse options passed in an fsys_set_options RPC.  */
+struct argp *trivfs_runtime_argp = &argp;
+
+/* Authentication of the current process.  */
+uid_t *uids;
+gid_t *gids;
+size_t uids_len, gids_len;
+
+/* Initialize and populate the uids and gids vectors.  */
+error_t
+get_credentials (void)
+{
+  /* Fetch uids...  */
+  uids_len = geteuids (0, 0);
+  if (uids_len < 0)
+    return errno;
+
+  uids = malloc (uids_len * sizeof (uid_t));
+  if (! uids)
+    return ENOMEM;
+
+  uids_len = geteuids (uids_len, uids);
+  if (uids_len < 0)
+    return errno;
+
+  /* ... and gids.  */
+  gids_len = getgroups (0, 0);
+  if (gids_len < 0)
+    return errno;
+
+  gids = malloc (gids_len * sizeof (gid_t));
+  if (! uids)
+    return ENOMEM;
+
+  gids_len = getgroups (gids_len, gids);
+  if (gids_len < 0)
+    return errno;
+
+  return 0;
+}
+
+/* Check if the given struct stat describes a node owned by the
+   current user.  */
+int
+is_owner (io_statbuf_t *st)
+{
+  int found = 0;
+  for (size_t i = 0; i < uids_len; i++)
+    if (uids[i] == st->st_uid)
+      {
+       found = 1;
+       break;
+      }
+
+  if (! found)
+    return 0;
+
+  found = 0;
+  for (size_t i = 0; i < gids_len; i++)
+    if (gids[i] == st->st_gid)
+      {
+       found = 1;
+       break;
+      }
+
+  return found;
+}
+
+error_t
+mtab_populate (struct mtab *mtab, const char *path, int insecure);
+
+error_t
+argz_add_device (char **options, size_t *options_len, const char *device);
+
+error_t
+map_device_to_path (const char *device, char **path);
+
+int
+main (int argc, char *argv[])
+{
+  error_t err;
+
+  err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+  if (err)
+    error (1, err, "argument parsing");
+
+  err = get_credentials ();
+  if (err)
+    error (2, err, "getting credentials");
+
+  mach_port_t bootstrap;
+  task_get_bootstrap_port (mach_task_self (), &bootstrap);
+  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);
+      if (err)
+        error (3, err, "dropping credentials");
+
+      /* Reply to our parent.  */
+      err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &control);
+      mach_port_deallocate (mach_task_self (), bootstrap);
+      if (err)
+       error (4, err, "trivfs_startup");
+
+      /* Launch.  */
+      ports_manage_port_operations_one_thread (control->pi.bucket,
+                                              trivfs_demuxer,
+                                              0);
+    }
+  else
+    {
+      /* One-shot mode.         */
+      struct mtab mtab = { NULL, 0, 0 };
+      err = mtab_populate (&mtab, path, insecure);
+      if (err)
+       error (5, err, "%s", path);
+
+      if (mtab.contents)
+       printf ("%s", mtab.contents);
+    }
+
+  return 0;
+}
+
+error_t
+mtab_add_entry (struct mtab *mtab, const char *entry, size_t length)
+{
+  char *p = realloc (mtab->contents, mtab->contents_len + length + 1);
+  if (! p)
+    return ENOMEM;
+
+  memcpy (&p[mtab->contents_len], entry, length);
+
+  mtab->contents = p;
+  mtab->contents_len += length;
+
+  /* Zero-terminate contents so that we can also interpret it as
+     string.  */
+  mtab->contents[mtab->contents_len] = '\0';
+
+  return 0;
+}
+
+/* Populates the given MTAB object with the information for PATH.  If
+   INSECURE is given, also follow translators bound to nodes not owned
+   by root or the current user.  */
+/* XXX split up */
+error_t
+mtab_populate (struct mtab *mtab, const char *path, int insecure)
+{
+  error_t err = 0;
+
+  /* These resources are freed in the epilogue.         */
+  file_t node = MACH_PORT_NULL;
+  fsys_t fsys = MACH_PORT_NULL;
+  char *argz = NULL;
+  size_t argz_len = 0;
+  char **argv = NULL;
+  char *type = NULL;
+  char *options = NULL;
+  size_t options_len = 0;
+  char *src = NULL;
+  char *entry = NULL;
+  size_t entry_len = 0;
+  char *children = NULL;
+  size_t children_len = 0;
+
+  /* Get the underlying node.  */
+  node = file_name_lookup (path, O_NOTRANS, 0666);
+  if (node == MACH_PORT_NULL)
+    {
+      err = errno;
+      goto errout;
+    }
+
+  if (! insecure)
+    {
+      /* Check who owns the node the translator is bound to.  */
+      io_statbuf_t st;
+      err = io_stat (node, &st);
+      if (err)
+       goto errout;
+
+      if (st.st_uid != 0 && st.st_gid != 0 && ! is_owner (&st))
+       {
+         err = EPERM;
+         goto errout;
+       }
+    }
+
+  err = file_get_translator_cntl (node, &fsys);
+  if (err == EPERM)
+    /* If we do not have permission to do that, it cannot be a node
+       bound to our control port, so ignore this error.         */
+    err = 0;
+
+  if (err == ENXIO && strcmp (path, "/") == 0)
+    /* The root translator fails differently, but this can't be bound
+       to our control port either, so ignore this error.  */
+    err = 0;
+
+  if (err)
+    return err;
+
+  if (control && control->pi.port_right == fsys)
+    /* This node is bound to our control port, ignore it.  */
+    goto errout;
+
+  /* Re-do the lookup without O_NOTRANS to get the root node.  */
+  mach_port_deallocate (mach_task_self (), node);
+  node = file_name_lookup (path, 0, 0666);
+  if (node == MACH_PORT_NULL)
+    {
+      err = errno;
+      goto errout;
+    }
+
+  /* Query its options.         */
+  err = file_get_fs_options (node, &argz, &argz_len);
+  if (err)
+    {
+      if (err == EOPNOTSUPP)
+       err = 0; /* There's not much we could do then.  */
+
+      goto errout;
+    }
+
+  size_t count = argz_count (argz, argz_len);
+  argv = malloc ((count + 1) * sizeof (char *));
+  if (! argv)
+    {
+      err = ENOMEM;
+      goto errout;
+    }
+
+  argz_extract (argz, argz_len, argv);
+
+  type = strdup (argv[0]);
+  if (! type)
+    {
+      err = ENOMEM;
+      goto errout;
+    }
+
+  for (int i = 1; i < count - 1; i++)
+    {
+      char *v = argv[i];
+
+      if (*v == '-')
+       v++;
+      if (*v == '-')
+       v++;
+
+      err = argz_add (&options, &options_len, v);
+      if (err)
+       goto errout;
+    }
+
+  err = argz_add_device (&options, &options_len, argv[count - 1]);
+  if (err)
+    goto errout;
+
+  argz_stringify (options, options_len, ',');
+
+  string_t source;
+  err = fsys_get_source (node, source);
+  if (err)
+    {
+      if (err == EOPNOTSUPP)
+       {
+         /* Guess based on the last argument.  */
+         err = map_device_to_path (argv[count - 1], &src);
+         if (err)
+           goto errout;
+       }
+      else
+       goto errout;
+    }
+  else
+    src = source;
+
+  entry_len = asprintf (&entry, "%s %s %s %s 0 0\n", src, path, type,
+                       options? options: MNTOPT_DEFAULTS);
+  if (! entry)
+    {
+      err = ENOMEM;
+      goto errout;
+    }
+
+  err = mtab_add_entry (mtab, entry, entry_len);
+  if (err)
+    goto errout;
+
+  /* path has an active translator, query its children.         */
+  err = fsys_get_children (node, &children, &children_len);
+  if (err == EOPNOTSUPP)
+    {
+      err = 0;
+      children_len = 0;
+    }
+
+  if (err)
+    goto errout;
+
+  if (children_len)
+    for (char *c = children; c; c = argz_next (children, children_len, c))
+      {
+       char *p = NULL;
+       asprintf (&p, "%s%s%s",
+                 path,
+                 path[strlen (path) - 1] == '/'? "": "/",
+                 c);
+       if (! p)
+         {
+           err = ENOMEM;
+           goto errout;
+         }
+
+       err = mtab_populate (mtab, p, insecure);
+       if (err)
+         {
+           /* There is really not much we can do about errors here.  */
+           error (0, err, "%s", p);
+           err = 0;
+         }
+
+       free (p);
+      }
+
+ errout:
+  if (node != MACH_PORT_NULL)
+    mach_port_deallocate (mach_task_self (), node);
+
+  if (fsys != MACH_PORT_NULL)
+    mach_port_deallocate (mach_task_self (), fsys);
+
+  if (argz)
+    vm_deallocate (mach_task_self (), (vm_address_t) argz, argz_len);
+
+  free (argv);
+  free (type);
+  free (options);
+
+  if (src != source)
+    free (src);
+
+  free (entry);
+
+  if (children)
+    vm_deallocate (mach_task_self (), (vm_address_t) children, children_len);
+
+  return err;
+}
+
+/* Decodes the DEVICE string into appropriate OPTIONS.  Currently only
+   tmpfs-style size declarations are supported.  */
+error_t
+argz_add_device (char **options, size_t *options_len, const char *device)
+{
+  error_t err;
+  char *end = NULL;
+  intmax_t size = strtoimax (device, &end, 0);
+  if (end == NULL || end == device)
+    return 0;
+
+  if (size < 0)
+    return 0;
+
+  switch (*end)
+    {
+      case 'g':
+      case 'G':
+      case 'm':
+      case 'M':
+      case 'k':
+      case 'K':
+       break;
+    default:
+      return 0;
+    }
+
+  /* device specifies a size.  */
+  char *arg = NULL;
+  asprintf (&arg, "size=%s", device);
+  if (! arg)
+    return ENOMEM;
+
+  err = argz_add (options, options_len, arg);
+
+  free (arg);
+  return err;
+}
+
+/* Matches [hs]d\ds\d\d?.  */
+int
+looks_like_block_device (const char *s)
+{
+  size_t len = strlen (s);
+  if (len != 3 && len != 5 && len != 6)
+    return 0;
+
+  return ((s[0] == 'h' || s[0] == 's') && s[1] == 'd' && isdigit (s[2]) &&
+          (len == 3 || (s[3] == 's' && isdigit (s[4]) &&
+                        (len == 5 || isdigit (s[5])))));
+}
+
+/* Map a device string to a file name referencing the appropriate
+   device file.         */
+error_t
+map_device_to_path (const char *device, char **path)
+{
+  if (strncmp (device, "device:", 7) == 0)
+    asprintf (path, "/dev/%s", &device[7]);
+  else if (strncmp (device, "/dev/", 5) == 0)
+    *path = strdup (device);
+  else if (looks_like_block_device (device))
+    asprintf (path, "/dev/%s", device);
+  else
+    *path = strdup ("none");
+
+  if (! *path)
+    return ENOMEM;
+
+  return 0;
+}
+
+/* Trivfs hooks.  */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_allow_open = O_READ;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+  /* Mark the node as a read-only plain file.  */
+  st->st_mode &= ~(S_IFMT | ALLPERMS);
+  st->st_mode |= (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH);
+  st->st_size = ((struct mtab *) cred->po->hook)->contents_len;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+  exit (EXIT_SUCCESS);
+}
+
+static error_t
+open_hook (struct trivfs_peropen *peropen)
+{
+  struct mtab *mtab = malloc (sizeof (struct mtab));
+  if (mtab == NULL)
+    return ENOMEM;
+
+  /* Hook! */
+  peropen->hook = mtab;
+
+  /* Initialize the fields.  */
+  mtab->offs = 0;
+  mtab->contents = NULL;
+  mtab->contents_len = 0;
+
+  return mtab_populate (mtab, path, insecure);
+}
+
+static void
+close_hook (struct trivfs_peropen *peropen)
+{
+  free (((struct mtab *) peropen->hook)->contents);
+  free (peropen->hook);
+}
+
+/* Read data from an IO object.         If offset is -1, read from the object
+   maintained file pointer.  If the object is not seekable, offset is
+   ignored.  The amount desired to be read is in AMOUNT.  */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+                 mach_port_t reply, mach_msg_type_name_t reply_type,
+                 char **data, mach_msg_type_number_t *data_len,
+                 loff_t offs, mach_msg_type_number_t amount)
+{
+  struct mtab *op;
+
+  /* Deny access if they have bad credentials. */
+  if (! cred)
+    return EOPNOTSUPP;
+
+  if (! (cred->po->openmodes & O_READ))
+    return EBADF;
+
+  /* Get the offset.  */
+  op = cred->po->hook;
+  if (offs == -1)
+    offs = op->offs;
+
+  /* Prune the amount they want to read.  */
+  if (offs > op->contents_len)
+    offs = op->contents_len;
+  if (offs + amount > op->contents_len)
+    amount = op->contents_len - offs;
+
+  if (amount > 0)
+    {
+      /* Possibly allocate a new buffer.  */
+      if (*data_len < amount)
+       {
+         *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+         if (*data == MAP_FAILED)
+           return ENOMEM;
+       }
+
+      /* Copy the constant data into the buffer.  */
+      memcpy ((char *) *data, op->contents + offs, amount);
+
+      /* Update the saved offset.  */
+      op->offs += amount;
+    }
+
+  *data_len = amount;
+  return 0;
+}
+
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+                 mach_port_t reply, mach_msg_type_name_t reply_type,
+                 off_t offs, int whence, off_t *new_offs)
+{
+  if (! cred)
+    return EOPNOTSUPP;
+
+  struct mtab *op = cred->po->hook;
+
+  switch (whence)
+    {
+    case SEEK_CUR:
+      offs += op->offs;
+      goto check;
+    case SEEK_END:
+      offs += op->contents_len;
+    case SEEK_SET:
+    check:
+      if (offs >= 0)
+       {
+         *new_offs = op->offs = offs;
+         break;
+       }
+    default:
+      return EINVAL;
+    }
+
+  return 0;
+}
+
+/* If this variable is set, it is called every time a new peropen
+   structure is created and initialized.  */
+error_t (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook;
+
+/* If this variable is set, it is called every time a peropen structure
+   is about to be destroyed.  */
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+/* Tell how much data can be read from the object without blocking for
+   a "long time" (this should be the same meaning of "long time" used
+   by the nonblocking flag.  */
+kern_return_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+                     mach_port_t reply, mach_msg_type_name_t replytype,
+                     mach_msg_type_number_t *amount)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (!(cred->po->openmodes & O_READ))
+    return EINVAL;
+
+  struct mtab *op = cred->po->hook;
+
+  *amount = op->contents_len - op->offs;
+  return 0;
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+   Block until one of the indicated types of i/o can be done "quickly", and
+   return the types that are then available.  ID_TAG is returned as passed; it
+   is just for the convenience of the user in matching up reply messages with
+   specific requests sent.  */
+kern_return_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+                   mach_port_t reply, mach_msg_type_name_t replytype,
+                   int *type)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (((*type & SELECT_READ) && !(cred->po->openmodes & O_READ))
+      || ((*type & SELECT_WRITE) && !(cred->po->openmodes & O_WRITE)))
+    return EBADF;
+
+  *type &= ~SELECT_URG;
+  return 0;
+}
+
+kern_return_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+                           mach_port_t reply, mach_msg_type_name_t replytype,
+                           struct timespec ts,
+                           int *type)
+{
+  return trivfs_S_io_select (cred, reply, replytype, type);
+}
+
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+   O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+   will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+   be used for.         The O_ASYNC bit affects icky async I/O; good async
+   I/O is done through io_async which is orthogonal to these calls. */
+
+kern_return_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+                          mach_port_t reply, mach_msg_type_name_t replytype,
+                          int *bits)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  *bits = cred->po->openmodes;
+  return 0;
+}
+
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+                             mach_port_t reply,
+                             mach_msg_type_name_t replytype,
+                             int mode)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  return 0;
+}
+
+kern_return_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+                               mach_port_t reply,
+                               mach_msg_type_name_t replytype,
+                               int bits)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  return 0;
+}
+
+kern_return_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+                                 mach_port_t reply,
+                                 mach_msg_type_name_t replytype,
+                                 int bits)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  return 0;
+}
diff --git a/trans/null.c b/trans/null.c
index 1f985b3..8b3b4e0 100644
--- a/trans/null.c
+++ b/trans/null.c
@@ -31,6 +31,7 @@
 #include <fcntl.h>
 #include <limits.h>
 #include <argp.h>
+#include <nullauth.h>
 
 const char *argp_program_version = STANDARD_HURD_VERSION (null);
 
@@ -78,6 +79,10 @@ main (int argc, char **argv)
   if (err)
     error(3, err, "Contacting parent");
 
+  err = setnullauth ();
+  if (err)
+    error(4, err, "Dropping privileges");
+
   /* Launch. */
   ports_manage_port_operations_multithread (fsys->pi.bucket, trivfs_demuxer,
                                            2 * 60 * 1000, 0, 0);
diff --git a/trans/symlink.c b/trans/symlink.c
index 03b5100..8562662 100644
--- a/trans/symlink.c
+++ b/trans/symlink.c
@@ -98,6 +98,19 @@ main (int argc, char **argv)
   io_restrict_auth (realnode, &realnodenoauth, 0, 0, 0, 0);
   mach_port_deallocate (mach_task_self (), realnode);
 
+  /* Mark us as important.  */
+  mach_port_t proc = getproc ();
+  if (proc == MACH_PORT_NULL)
+    error (2, err, "cannot get a handle to our process");
+
+  err = proc_mark_important (proc);
+  /* This might fail due to permissions or because the old proc server
+     is still running, ignore any such errors.  */
+  if (err && err != EPERM && err != EMIG_BAD_ID)
+    error (2, err, "Cannot mark us as important");
+
+  mach_port_deallocate (mach_task_self (), proc);
+
   /* Launch */
   while (1)
     {
@@ -221,3 +234,18 @@ S_fsys_forward (mach_port_t server, mach_port_t requestor,
 {
   return EOPNOTSUPP;
 }
+
+error_t
+S_fsys_get_children (mach_port_t server,
+                    char **children,
+                    mach_msg_type_number_t *children_len)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_get_source (mach_port_t server,
+                  char *source)
+{
+  return EOPNOTSUPP;
+}
diff --git a/ufs/pager.c b/ufs/pager.c
index 1e3d140..5d3e44a 100644
--- a/ufs/pager.c
+++ b/ufs/pager.c
@@ -450,6 +450,13 @@ pager_unlock_page (struct user_pager_info *pager,
   return err;
 }
 
+void
+pager_notify_evict (struct user_pager_info *pager,
+                   vm_offset_t page)
+{
+  assert (!"unrequested notification on eviction");
+}
+
 /* Implement the pager_report_extent callback from the pager library.  See
    <hurd/pager.h> for the interface description. */
 inline error_t
@@ -502,7 +509,7 @@ create_disk_pager (void)
   upi->type = DISK;
   upi->np = 0;
   pager_bucket = ports_create_bucket ();
-  diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, store->size,
+  diskfs_start_disk_pager (upi, pager_bucket, MAY_CACHE, 0, store->size,
                           &disk_image);
   upi->p = diskfs_disk_pager;
 }
@@ -595,7 +602,7 @@ diskfs_get_filemap (struct node *np, vm_prot_t prot)
        upi->unlocked_pagein_length = 0;
        diskfs_nref_light (np);
        upi->p = pager_create (upi, pager_bucket,
-                              MAY_CACHE, MEMORY_OBJECT_COPY_DELAY);
+                              MAY_CACHE, MEMORY_OBJECT_COPY_DELAY, 0);
        if (upi->p == 0)
          {
            diskfs_nrele_light (np);
diff --git a/utils/Makefile b/utils/Makefile
index e3bed0b..c079016 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -21,14 +21,17 @@ makemode := utilities
 targets = shd ps settrans showtrans syncfs fsysopts \
        storeinfo login w uptime ids loginpr sush vmstat portinfo \
        devprobe vminfo addauth rmauth unsu setauth ftpcp ftpdir storecat \
-       storeread msgport rpctrace mount gcore fakeauth fakeroot remap
+       storeread msgport rpctrace mount gcore fakeauth fakeroot remap \
+       umount nullauth
+
 special-targets = loginpr sush uptime fakeroot remap
 SRCS = shd.c ps.c settrans.c syncfs.c showtrans.c addauth.c rmauth.c \
        fsysopts.c storeinfo.c login.c loginpr.sh sush.sh w.c \
        uptime.sh psout.c ids.c vmstat.c portinfo.c devprobe.c vminfo.c \
        parse.c frobauth.c frobauth-mod.c setauth.c pids.c nonsugid.c \
        unsu.c ftpcp.c ftpdir.c storeread.c storecat.c msgport.c \
-       rpctrace.c mount.c gcore.c fakeauth.c fakeroot.sh remap.sh
+       rpctrace.c mount.c gcore.c fakeauth.c fakeroot.sh remap.sh \
+       nullauth.c
 
 OBJS = $(filter-out %.sh,$(SRCS:.c=.o))
 HURDLIBS = ps ihash store fshelp ports ftpconn shouldbeinlibc
@@ -36,6 +39,8 @@ LDLIBS += -lpthread
 login-LDLIBS = -lutil $(LIBCRYPT)
 addauth-LDLIBS = $(LIBCRYPT)
 setauth-LDLIBS = $(LIBCRYPT)
+mount-LDLIBS = $(libblkid_LIBS)
+mount-CPPFLAGS = $(libblkid_CFLAGS)
 
 INSTALL-login-ops = -o root -m 4755
 INSTALL-ids-ops = -o root -m 4755
@@ -56,7 +61,7 @@ ftpcp ftpdir: ../libftpconn/libftpconn.a
 settrans: ../libfshelp/libfshelp.a ../libports/libports.a
 ps w ids settrans syncfs showtrans fsysopts storeinfo login vmstat portinfo \
   devprobe vminfo addauth rmauth setauth unsu ftpcp ftpdir storeread \
-  storecat msgport mount: \
+  storecat msgport mount umount nullauth: \
        ../libshouldbeinlibc/libshouldbeinlibc.a
 
 $(filter-out $(special-targets), $(targets)): %: %.o
@@ -72,7 +77,7 @@ fakeauth-CPPFLAGS = -I$(srcdir)/../auth
 authServer-CPPFLAGS = -I$(srcdir)/../auth
 auth_requestUser-CPPFLAGS = -I$(srcdir)/../auth
 
-mount: ../sutils/fstab.o ../sutils/clookup.o \
+mount umount: ../sutils/fstab.o ../sutils/clookup.o match-options.o \
        $(foreach L,fshelp ports,../lib$L/lib$L.a)
 ../sutils/fstab.o ../sutils/clookup.o: FORCE
        $(MAKE) -C $(@D) $(@F)
diff --git a/utils/fakeroot.sh b/utils/fakeroot.sh
index 1ace1cf..a64e963 100644
--- a/utils/fakeroot.sh
+++ b/utils/fakeroot.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 # Execute a command in an environment where it appears to be root.
 #
-# Copyright (C) 2002 Free Software Foundation, Inc.
+# Copyright (C) 2002, 2013 Free Software Foundation, Inc.
 #
 # This file is part of the GNU Hurd.
 #
@@ -54,10 +54,16 @@ if [ $# -eq 0 ]; then
   set -- ${SHELL:-/bin/sh}
 fi
 
+TARGET=
+until [ $# -eq 0 ]; do
+  TARGET="${TARGET} '$(echo "$1" | sed -e "s/'/'\\\\''/g")'"
+  shift
+done
+
 # We exec settrans, which execs the "fakeauth" command in the chroot context.
 # The `pwd` is evaluated here and now, and that result interpreted inside
 # the shell running under fakeauth to chdir there inside the chroot world.
 # That shell then execs our arguments as a command line.
 exec /bin/settrans --chroot \
-     /bin/fakeauth /bin/sh -c "cd `pwd`; $*" \
+     /bin/fakeauth /bin/sh -c "cd `pwd`; exec ${TARGET}" \
      -- / /hurd/fakeroot
diff --git a/utils/match-options.c b/utils/match-options.c
new file mode 100644
index 0000000..11fc9dc
--- /dev/null
+++ b/utils/match-options.c
@@ -0,0 +1,68 @@
+/* Common functionality for the --test-opts flag of mount and umount.
+
+   Copyright (C) 2013 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <argz.h>
+#include <error.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "match-options.h"
+
+char *test_opts;
+size_t test_opts_len;
+
+int
+match_options (struct mntent *mntent)
+{
+  char *opts;
+  size_t opts_len;
+
+  error_t err = argz_create_sep (mntent->mnt_opts, ',', &opts, &opts_len);
+  if (err)
+    error (3, err, "parsing mount options failed");
+
+  for (char *test = test_opts;
+       test; test = argz_next (test_opts, test_opts_len, test))
+    {
+      char *needle = test;
+      int inverse = strncmp("no", needle, 2) == 0;
+      if (inverse)
+        needle += 2;
+
+      int match = 0;
+      for (char *opt = opts; opt; opt = argz_next (opts, opts_len, opt))
+        {
+          if (strcmp (opt, needle) == 0) {
+            if (inverse)
+              return 0; /* foo in opts, nofoo in test_opts. */
+
+            /* foo in opts, foo in test_opts, record match. */
+            match = 1;
+          }
+        }
+
+      if (! inverse && ! match)
+        return 0; /* No foo in opts, but foo in test_opts. */
+    }
+
+  /* If no conflicting test_opt was encountered, return success. */
+  return 1;
+}
diff --git a/utils/match-options.h b/utils/match-options.h
new file mode 100644
index 0000000..ea7ae70
--- /dev/null
+++ b/utils/match-options.h
@@ -0,0 +1,33 @@
+/* Common functionality for the --test-opts flag of mount and umount.
+
+   Copyright (C) 2013 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 this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <mntent.h>
+
+extern char *test_opts;
+extern size_t test_opts_len;
+
+/* Check whether the given mount entry matches the given set of
+   options.
+
+   Returns 0 if foo is in the options vector but nofoo is in test_opts.
+   Returns 0 if foo is in test_opts but foo is not in the options vector. */
+int
+match_options (struct mntent *mntent);
diff --git a/utils/mount.c b/utils/mount.c
index 8b059c2..04519da 100644
--- a/utils/mount.c
+++ b/utils/mount.c
@@ -24,16 +24,23 @@
 #include <error.h>
 #include <stdlib.h>
 #include <fcntl.h>
+#include <unistd.h>
 #include <hurd/fsys.h>
 #include <hurd/fshelp.h>
 #include <hurd/paths.h>
+#ifdef HAVE_BLKID
+#include <blkid/blkid.h>
+#endif
+
+#include "match-options.h"
 
 #define SEARCH_FMTS    _HURD "%sfs\0" _HURD "%s"
-#define DEFAULT_FSTYPE "ext2"
+#define DEFAULT_FSTYPE "auto"
 
 static char *fstype = DEFAULT_FSTYPE;
 static char *device, *mountpoint;
 static int verbose;
+static int fake;
 static char *options;
 static size_t options_len;
 static mach_msg_timeout_t timeout;
@@ -54,6 +61,10 @@ static const struct argp_option argp_opts[] =
   {"update", 'u', 0, 0, "Flush any meta-data cached in core"},
   {"remount", 0, 0, OPTION_ALIAS},
   {"verbose", 'v', 0, 0, "Give more detailed information"},
+  {"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"},
+  {"fake", 'f', 0, 0, "Do not actually mount, just pretend"},
   {0, 0}
 };
 
@@ -110,6 +121,20 @@ parse_opt (int key, char *arg, struct argp_state *state)
        }
       break;
 
+    case 'n':
+      /* do nothing */
+      break;
+
+    case 'f':
+      fake = 1;
+      break;
+
+    case 'O':
+      err = argz_create_sep (arg, ',', &test_opts, &test_opts_len);
+      if (err)
+       argp_failure (state, 100, ENOMEM, "%s", arg);
+      break;
+
     case ARGP_KEY_ARG:
       if (mountpoint == 0)     /* One arg: mountpoint */
        mountpoint = arg;
@@ -307,12 +332,38 @@ do_mount (struct fs *fs, int remount)
          return 0;
        }
 
-      if (mounted != MACH_PORT_NULL)
+      /* Do not fail if there is an active translator if --fake is
+         given. This mimics Linux mount utility more closely which
+         just looks into the mtab file. */
+      if (mounted != MACH_PORT_NULL && !fake)
        {
          error (0, 0, "%s already mounted", fs->mntent.mnt_fsname);
          return EBUSY;
        }
 
+      if (strcmp (fs->mntent.mnt_type, "auto") == 0)
+        {
+#if HAVE_BLKID
+          char *type =
+            blkid_get_tag_value (NULL, "TYPE", fs->mntent.mnt_fsname);
+          if (! type)
+            {
+              error (0, 0, "failed to detect file system type");
+              return EFTYPE;
+            }
+          else
+            {
+              fs->mntent.mnt_type = strdup (type);
+              if (! fs->mntent.mnt_type)
+                error (3, ENOMEM, "failed to allocate memory");
+            }
+#else
+         fs->mntent.mnt_type = strdup ("ext2");
+         if (! fs->mntent.mnt_type)
+           error (3, ENOMEM, "failed to allocate memory");
+#endif
+        }
+
       err = fs_type (fs, &type);
       if (err)
        {
@@ -337,6 +388,23 @@ do_mount (struct fs *fs, int remount)
 
       /* Now we have a translator command line argz in FSOPTS.  */
 
+      if (fake) {
+        /* Fake the translator startup. */
+        mach_port_t underlying;
+        mach_msg_type_name_t underlying_type;
+        err = open_node (O_READ, &underlying, &underlying_type, 0, NULL);
+        if (err)
+          error (1, errno, "cannot mount on %s", fs->mntent.mnt_dir);
+
+        mach_port_deallocate (mach_task_self (), underlying);
+
+        /* See if the translator is at least executable. */
+        if (access(type->program, X_OK) == -1)
+          error (1, errno, "can not execute %s", type->program);
+
+        return 0;
+      }
+
       explain ("settrans -a");
       err = fshelp_start_translator (open_node, NULL, fsopts,
                                     fsopts, fsopts_len, timeout,
@@ -521,6 +589,12 @@ main (int argc, char **argv)
 
   fstab = fstab_argp_create (&fstab_params, SEARCH_FMTS, sizeof SEARCH_FMTS);
 
+  /* This is a convenient way of checking for any `remount' options.  */
+  remount = 0;
+  err = argz_replace (&options, &options_len, "remount", "update", &remount);
+  if (err)
+    error (3, ENOMEM, "collecting mount options");
+
   if (device)                  /* two-argument form */
     {
       struct mntent m =
@@ -531,13 +605,23 @@ main (int argc, char **argv)
        mnt_opts: 0,
        mnt_freq: 0, mnt_passno: 0
       };
-      struct fstype *fst;
 
-      err = fstypes_get (fstab->types, fstype, &fst);
+      err = fstab_add_mntent (fstab, &m, &fs);
       if (err)
-       error (106, err, "cannot initialize type %s", fstype);
-      if (fst->program == 0)
-       error (2, 0, "filesystem type %s not recognized", fstype);
+       error (2, err, "%s", mountpoint);
+    }
+  else if (mountpoint && remount)      /* one-argument remount */
+    {
+      struct mntent m =
+      {
+       mnt_fsname: mountpoint, /* since we cannot know the device,
+                                  using mountpoint here leads to more
+                                  helpful error messages */
+       mnt_dir: mountpoint,
+       mnt_type: fstype,
+       mnt_opts: 0,
+       mnt_freq: 0, mnt_passno: 0
+      };
 
       err = fstab_add_mntent (fstab, &m, &fs);
       if (err)
@@ -552,12 +636,6 @@ main (int argc, char **argv)
   else
     fs = 0;
 
-  /* This is a convenient way of checking for any `remount' options.  */
-  remount = 0;
-  err = argz_replace (&options, &options_len, "remount", "update", &remount);
-  if (err)
-    error (3, ENOMEM, "collecting mount options");
-
   if (fs != 0)
     err = do_mount (fs, remount);
   else
@@ -566,8 +644,22 @@ main (int argc, char **argv)
       case mount:
        for (fs = fstab->entries; fs; fs = fs->next)
          {
-           if (fstab_params.do_all && hasmntopt (&fs->mntent, MNTOPT_NOAUTO))
-             continue;
+           if (fstab_params.do_all) {
+              if (hasmntopt (&fs->mntent, MNTOPT_NOAUTO))
+                continue;
+
+              if (! match_options (&fs->mntent))
+                continue;
+
+              fsys_t mounted;
+              err = fs_fsys (fs, &mounted);
+              if (err)
+                  error (0, err, "cannot determine if %s is already mounted",
+                         fs->mntent.mnt_fsname);
+
+              if (mounted != MACH_PORT_NULL)
+                continue;
+            }
            err |= do_mount (fs, remount);
          }
        break;
diff --git a/utils/nullauth.c b/utils/nullauth.c
new file mode 100644
index 0000000..a0d5d1b
--- /dev/null
+++ b/utils/nullauth.c
@@ -0,0 +1,90 @@
+/* Utility to drop all authentication credentials.
+
+   Copyright (C) 2013 Free Software Foundation, Inc.
+
+   Written by Justus Winter <address@hidden>
+
+   This file is part of the GNU Hurd.
+
+   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, see <http://www.gnu.org/licenses/>.  */
+
+#include <argp.h>
+#include <error.h>
+#include <nullauth.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <version.h>
+
+static char **args;
+
+const char const *argp_program_version = STANDARD_HURD_VERSION (nullauth);
+
+static const struct argp_option const options[] =
+{
+  { 0 }
+};
+
+static const char const doc[] =
+  "Drop all authentication credentials and run the given program.";
+static const char const args_doc[] =
+  "PROGRAM [ARGUMENTS...]\tThe program to run";
+
+error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+  switch (key)
+    {
+    case ARGP_KEY_ARGS:
+      args = state->argv + state->next;
+      break;
+
+    case ARGP_KEY_NO_ARGS:
+      argp_error (state, "expected program to run");
+      return EINVAL;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return 0;
+}
+
+static struct argp argp = {
+  options,
+  parse_opt,
+  args_doc,
+  doc,
+  NULL,
+};
+
+int
+main (int argc, char *argv[])
+{
+  error_t err;
+
+  /* Parse our command line.  This shouldn't ever return an error.  */
+  argp_parse (&argp, argc, argv, 0, 0, NULL);
+
+  /* Drop all privileges.  */
+  err = setnullauth();
+  if (err)
+    error (1, err, "Could not drop privileges");
+
+  execv (args[0], args);
+  error (1, errno, "execv");
+
+  /* Not reached.  */
+  return EXIT_FAILURE;
+}
diff --git a/utils/remap.sh b/utils/remap.sh
index d799759..064f0f5 100644
--- a/utils/remap.sh
+++ b/utils/remap.sh
@@ -57,10 +57,16 @@ if [ $# -eq 0 ]; then
   set -- ${SHELL:-/bin/sh}
 fi
 
+TARGET=
+until [ $# -eq 0 ]; do
+  TARGET="${TARGET} '$(echo "$1" | sed -e "s/'/'\\\\''/g")'"
+  shift
+done
+
 # We exec settrans, which execs the "fakeauth" command in the chroot context.
 # The `pwd` is evaluated here and now, and that result interpreted inside
 # the shell running under fakeauth to chdir there inside the chroot world.
 # That shell then execs our arguments as a command line.
 exec /bin/settrans --chroot \
-     /bin/sh -c "cd `pwd`; $*" \
+     /bin/sh -c "cd `pwd`; exec ${TARGET}" \
      -- / /hurd/remap $MAPPED
diff --git a/utils/umount.c b/utils/umount.c
new file mode 100644
index 0000000..26d2b56
--- /dev/null
+++ b/utils/umount.c
@@ -0,0 +1,357 @@
+/* Roughly Unix/Linux-compatible `umount' frontend for Hurd translators.
+
+   Copyright (C) 2013 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <fcntl.h>
+#include <hurd/fshelp.h>
+#include <hurd/fsys.h>
+#include <hurd/paths.h>
+#include <hurd/process.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "match-options.h"
+#include "../sutils/fstab.h"
+
+/* XXX fix libc */
+#undef _PATH_MOUNTED
+#define _PATH_MOUNTED "/etc/mtab"
+
+static char *targets;
+static size_t targets_len;
+static int readonly;
+static int verbose;
+static int passive_flags = FS_TRANS_SET;
+static int active_flags = FS_TRANS_SET;
+static int goaway_flags;
+static int source_goaway;
+static int fake;
+
+static struct fstab_argp_params fstab_params;
+
+#define FAKE_KEY 0x80 /* !isascii (FAKE_KEY), so no short option.  */
+
+static const struct argp_option argp_opts[] =
+{
+  {NULL, 'd', 0, 0, "Also ask the source translator to go away"},
+  {"fake", FAKE_KEY, 0, 0, "Do not actually umount, just pretend"},
+  {"force", 'f', 0, 0, "Force umount by killing the translator"},
+  {"no-mtab", 'n', 0, 0, "Do not update /etc/mtab"},
+  {"read-only", 'r', 0, 0, "If unmounting fails, try to remount read-only"},
+  {"nosync", 'S', 0, 0, "Don't sync a translator before killing it"},
+  {"test-opts", 'O', "OPTIONS", 0,
+   "Only mount fstab entries matching the given set of options"},
+  {"verbose", 'v', 0, 0, "Give more detailed information"},
+  {},
+};
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+  struct fstab_argp_params *params = state->input;
+  error_t err;
+  switch (key)
+    {
+    case ARGP_KEY_INIT:
+      state->child_inputs[0] = params; /* pass down to fstab_argp parser */
+      break;
+
+    case 'd':
+      source_goaway = 1;
+      break;
+
+    case FAKE_KEY:
+      fake = 1;
+      break;
+
+    case 'f':
+      goaway_flags |= FSYS_GOAWAY_FORCE;
+      break;
+
+    case 'n':
+      /* do nothing */
+      break;
+
+    case 'r':
+      readonly = 1;
+      break;
+
+    case 'S':
+      goaway_flags |= FSYS_GOAWAY_NOSYNC;
+      break;
+
+    case 'O':
+      err = argz_create_sep (arg, ',', &test_opts, &test_opts_len);
+      if (err)
+       argp_failure (state, 100, ENOMEM, "%s", arg);
+      break;
+
+    case 'v':
+      verbose += 1;
+      break;
+
+    case ARGP_KEY_ARG:
+      err = argz_add (&targets, &targets_len, arg);
+      if (err)
+       argp_failure (state, 100, ENOMEM, "%s", arg);
+      break;
+
+    case ARGP_KEY_NO_ARGS:
+      if (! params->do_all)
+       {
+         argp_error (state,
+                     "filesystem argument required if --all is not given");
+         return EINVAL;
+       }
+      break;
+
+    case ARGP_KEY_END:
+      if (params->do_all && targets)
+       {
+         argp_error (state, "filesystem argument not allowed with --all");
+         return EINVAL;
+       }
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return 0;
+}
+
+static const char doc[] = "Stop active and remove passive translators";
+static const char args_doc[] = "DEVICE|DIRECTORY [DEVICE|DIRECTORY ...]";
+
+static struct argp fstab_argp_mtab; /* Slightly modified version.  */
+
+static const struct argp_child argp_kids[] =
+{
+  {&fstab_argp_mtab, 0,
+   "Filesystem selection (if no explicit filesystem arguments given):", 2},
+  {},
+};
+static struct argp argp =
+{
+ options: argp_opts,
+ parser: parse_opt,
+ args_doc: args_doc,
+ doc: doc,
+ children: argp_kids,
+};
+
+/* This is a trimmed and slightly modified version of
+   fstab_argp.options which uses _PATH_MOUNTED instead of _PATH_MNTTAB
+   in the doc strings. */
+static const struct argp_option fstab_argp_mtab_opts[] =
+{
+  {"all",       'a', 0,      0, "Do all filesystems in " _PATH_MOUNTED},
+  {0,           'A', 0,      OPTION_ALIAS },
+  {"fstab",     'F', "FILE", 0, "File to use instead of " _PATH_MOUNTED},
+  {"fstype",    't', "TYPE", 0, "Do only filesystems of given type(s)"},
+  {"exclude-root",'R',0,      0,
+     "Exclude root (/) filesystem from " _PATH_MOUNTED " list"},
+  {"exclude",   'X', "PATTERN", 0, "Exclude directories matching PATTERN"},
+  {}
+};
+
+static error_t
+fstab_argp_mtab_parse_opt (int key, char *arg, struct argp_state *state)
+{
+  return fstab_argp.parser (key, arg, state);
+}
+
+static struct argp fstab_argp_mtab =
+{
+  options: fstab_argp_mtab_opts,
+  parser: fstab_argp_mtab_parse_opt,
+};
+
+/* Unmount one filesystem.  */
+static error_t
+do_umount (struct fs *fs)
+{
+  error_t err = 0;
+
+  file_t node = file_name_lookup (fs->mntent.mnt_dir, O_NOTRANS, 0666);
+  if (node == MACH_PORT_NULL)
+    {
+      error (0, errno, "%s", fs->mntent.mnt_dir);
+      return errno;
+    }
+
+  if (verbose)
+    printf ("settrans -apg%s%s %s\n",
+           goaway_flags & FSYS_GOAWAY_NOSYNC? "S": "",
+           goaway_flags & FSYS_GOAWAY_FORCE? "f": "",
+           fs->mntent.mnt_dir);
+
+  if (! fake)
+    {
+      err = file_set_translator (node,
+                                passive_flags, active_flags, goaway_flags,
+                                NULL, 0,
+                                MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
+      if (! err)
+       {
+         if (strcmp (fs->mntent.mnt_fsname, "") != 0 &&
+             strcmp (fs->mntent.mnt_fsname, "none") != 0)
+           {
+             if (verbose)
+               printf ("settrans -ag%s%s %s\n",
+                       goaway_flags & FSYS_GOAWAY_NOSYNC? "S": "",
+                       goaway_flags & FSYS_GOAWAY_FORCE? "f": "",
+                       fs->mntent.mnt_fsname);
+
+             file_t source = file_name_lookup (fs->mntent.mnt_fsname,
+                                               O_NOTRANS,
+                                               0666);
+             if (source == MACH_PORT_NULL)
+               {
+                 error (0, errno, "%s", fs->mntent.mnt_fsname);
+                 return errno;
+               }
+
+             err = file_set_translator (source,
+                                        0, active_flags, goaway_flags,
+                                        NULL, 0,
+                                        MACH_PORT_NULL,
+                                        MACH_MSG_TYPE_COPY_SEND);
+             if (err)
+               error (0, err, "%s", fs->mntent.mnt_fsname);
+
+             mach_port_deallocate (mach_task_self (), source);
+
+           }
+       }
+      else
+       {
+         error (0, err, "%s", fs->mntent.mnt_dir);
+
+         /* Try remounting readonly instead if requested.  */
+         if (readonly)
+           {
+             if (verbose)
+               printf ("fsysopts %s --readonly\n", fs->mntent.mnt_dir);
+
+             error_t e = fs_set_readonly (fs, TRUE);
+             if (e)
+               error (0, e, "%s", fs->mntent.mnt_dir);
+           }
+       }
+    }
+
+  /* Deallocate the reference so that unmounting nested translators
+     works properly.  */
+  mach_port_deallocate (mach_task_self (), node);
+  return err;
+}
+
+int
+main (int argc, char **argv)
+{
+  error_t err;
+
+  err = argp_parse (&argp, argc, argv, 0, 0, &fstab_params);
+  if (err)
+    error (3, err, "parsing arguments");
+
+  /* Read the mtab file by default.  */
+  if (! fstab_params.fstab_path)
+    fstab_params.fstab_path = _PATH_MOUNTED;
+
+  struct fstab *fstab = fstab_argp_create (&fstab_params, NULL, 0);
+  if (! fstab)
+    error (3, ENOMEM, "fstab creation");
+
+  if (targets)
+    for (char *t = targets; t; t = argz_next (targets, targets_len, t))
+      {
+       /* Figure out if t is the device or the mountpoint.  */
+       struct fs *fs = fstab_find_mount (fstab, t);
+       if (! fs)
+         {
+           fs = fstab_find_device (fstab, t);
+           if (! fs)
+             {
+               error (0, 0, "could not find entry for: %s", t);
+
+               /* As last resort, just assume it is the mountpoint.  */
+               struct mntent m =
+                 {
+                   mnt_fsname: "",
+                   mnt_dir: t,
+                   mnt_type: "",
+                   mnt_opts: 0,
+                   mnt_freq: 0,
+                   mnt_passno: 0,
+                 };
+
+               err = fstab_add_mntent (fstab, &m, &fs);
+               if (err)
+                 error (2, err, "%s", t);
+             }
+         }
+
+       if (fs)
+         err |= do_umount (fs);
+      }
+  else
+    {
+      /* Sort entries in reverse lexicographical order so that the
+        longest mount points are unmounted first.  This makes sure
+        that nested mounts are handled properly.  */
+      size_t count = 0;
+      for (struct fs *fs = fstab->entries; fs; fs = fs->next)
+       count += 1;
+
+      char **entries = malloc (count * sizeof (char *));
+      if (! entries)
+       error (3, ENOMEM, "allocating entries array");
+
+      char **p = entries;
+      for (struct fs *fs = fstab->entries; fs; fs = fs->next)
+       *p++ = fs->mntent.mnt_dir;
+
+      /* Reverse lexicographical order.         */
+      int compare_entries (const void *a, const void *b)
+       {
+         return -strcmp ((char *) a, (char *) b);
+       }
+
+      qsort (entries, count, sizeof (char *), compare_entries);
+
+      for (int i = 0; i < count; i++)
+       {
+         struct fs *fs = fstab_find_mount (fstab, entries[i]);
+         if (! fs)
+           error (4, 0, "could not find entry for: %s", entries[i]);
+
+         if (! match_options (&fs->mntent))
+           continue;
+
+         err |= do_umount (fs);
+       }
+    }
+
+  return err? EXIT_FAILURE: EXIT_SUCCESS;
+}

-- 
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]