Hello,

On Fri, 31 Jul 2026 02:13:26 Samuel Thibault wrote:
> Uh? So if the disk image has not partition table at all, libparted says
> that it has one partition? That's ugly :/ We'd really need to find a
> way to ask libparted whether there is a partition table or not. Perhaps
> through comparing the PedDiskType name field against "loop"?

While studying the libparted code, I discovered that dev->type is always set
to PED_DEVICE_UNKNOWN in libparted/arch/gnu.c:

dev->type = PED_DEVICE_UNKNOWN;    /* It's deprecated anyway */

So first we need to understand how to determine the type based on the
information the store can provide.

> I don't see the relation with store_parsed?

I mean, we could check store_parsed for 'part:' and, if it detects it, avoid
calling create_partitions, since we're guaranteed to be working with a
partition, not the entire disk. In any case, the libparted approach seems
preferable to me.

> I was rather thinking about all the code about permission checking and
> locking that you have in your netfs_S_io_read/write before calling
> dev_read/write.

We can pass peropen to pager_create instead of dev, and then check
permissions and locking po->np->lock before calling dev_read/write.

> But why do you maintain that opens array actually? I don't think you
> need to iterate on peropens anywhere? You only need to count nperopens
> as the existing code does, to clear/mark STORE_INACTIVE.

Yes, in the end, the opens array really turned out to be unnecessary, and
nperopens is sufficient.

P.S. Bradley Morgan, I've tried mail.com and several other email services,
and they either don't work for me or require a foreign phone number...

Thanks,

--
Mikhail Karpov
From 027cb2534920085e4623ef56eea2bb1a63bb4437 Mon Sep 17 00:00:00 2001
From: Mikhail Karpov <[email protected]>
Date: Wed, 5 Aug 2026 22:30:42 +0700
Subject: [PATCH] Migrating storeio from trivfs to netfs

---
 libnetfs/Makefile          |    2 +-
 libnetfs/init-init.c       |    4 +-
 libnetfs/make-peropen.c    |    9 +
 libnetfs/netfs.h           |    8 +
 libnetfs/priv.c            |   26 +
 libnetfs/release-peropen.c |    8 +
 storeio/Makefile           |   13 +-
 storeio/dev.c              |   69 ++-
 storeio/dev.h              |   99 +--
 storeio/io.c               |  515 +++++++---------
 storeio/open.c             |  127 ----
 storeio/open.h             |   68 ---
 storeio/pager.c            |  101 +++-
 storeio/storeio.c          | 1173 ++++++++++++++++++++++++++++--------
 14 files changed, 1368 insertions(+), 854 deletions(-)
 create mode 100644 libnetfs/priv.c
 delete mode 100644 storeio/open.c
 delete mode 100644 storeio/open.h

diff --git a/libnetfs/Makefile b/libnetfs/Makefile
index 24606ff9..8f947a3c 100644
--- a/libnetfs/Makefile
+++ b/libnetfs/Makefile
@@ -53,7 +53,7 @@ OTHERSRCS= drop-node.c init-init.c make-node.c make-peropen.c make-protid.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 dead-name.c     \
-	get-source.c
+	get-source.c priv.c
 
 SRCS= $(OTHERSRCS) $(FSSRCS) $(IOSRCS) $(FSYSSRCS) $(IFSOCKSRCS)
 
diff --git a/libnetfs/init-init.c b/libnetfs/init-init.c
index 19ed0d3d..f64c7bba 100644
--- a/libnetfs/init-init.c
+++ b/libnetfs/init-init.c
@@ -38,9 +38,9 @@ void
 netfs_init (void)
 {
   error_t err;
-  err = maptime_map (0, 0, &netfs_mtime);
+  err = maptime_map (1, 0, &netfs_mtime);
   if (err)
-    err = maptime_map (1, 0, &netfs_mtime);
+    err = maptime_map (0, 0, &netfs_mtime);
   if (err)
     error (2, err, "mapping time");
 
diff --git a/libnetfs/make-peropen.c b/libnetfs/make-peropen.c
index 3b127881..9bb48923 100644
--- a/libnetfs/make-peropen.c
+++ b/libnetfs/make-peropen.c
@@ -43,6 +43,15 @@ netfs_make_peropen (struct node *np, int flags, struct peropen *context)
   po->np = np;
   po->path = NULL;
 
+  if (netfs_peropen_create_hook)
+    err = (*netfs_peropen_create_hook) (po);
+  if (err)
+    {
+      fshelp_rlock_po_fini (&po->lock_status);
+      free (po);
+      return NULL;
+    }
+
   if (context)
     {
       if (context->path)
diff --git a/libnetfs/netfs.h b/libnetfs/netfs.h
index 6fc53ce4..1f1d5c2c 100644
--- a/libnetfs/netfs.h
+++ b/libnetfs/netfs.h
@@ -113,6 +113,14 @@ extern char *netfs_server_name;
    version number.  */
 extern char *netfs_server_version;
 
+/* If this variable is set, it is called internally every time a new peropen
+   structure is created and initialized.  */
+extern error_t (*netfs_peropen_create_hook) (struct peropen *po);
+
+/* If this variable is set, it is called internally every time a peropen
+   structure is about to be destroyed.  */
+extern void (*netfs_peropen_destroy_hook) (struct peropen *po);
+
 /* The user must define this function.  Make sure that NP->nn_stat is
    filled with the most current information.  CRED identifies the user
    responsible for the operation. NP is locked.  */
diff --git a/libnetfs/priv.c b/libnetfs/priv.c
new file mode 100644
index 00000000..4a46606f
--- /dev/null
+++ b/libnetfs/priv.c
@@ -0,0 +1,26 @@
+/* Default values for weak variables
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */
+
+#include "netfs.h"
+
+error_t (*netfs_peropen_create_hook) (struct peropen *po)
+  __attribute__ ((weak));
+
+void (*netfs_peropen_destroy_hook) (struct peropen *po)
+  __attribute__ ((weak));
diff --git a/libnetfs/release-peropen.c b/libnetfs/release-peropen.c
index 43f4cba7..3dfca368 100644
--- a/libnetfs/release-peropen.c
+++ b/libnetfs/release-peropen.c
@@ -26,6 +26,14 @@ netfs_release_peropen (struct peropen *po)
   if (refcount_deref (&po->refcnt) > 0)
     return;
 
+  if (netfs_peropen_destroy_hook)
+    {
+      refcount_unsafe_ref (&po->refcnt);
+      (*netfs_peropen_destroy_hook) (po);
+      if (refcount_deref (&po->refcnt) > 0)
+        return;
+    }
+
   pthread_mutex_lock (&po->np->lock);
   if (po->root_parent)
     mach_port_deallocate (mach_task_self (), po->root_parent);
diff --git a/storeio/Makefile b/storeio/Makefile
index 5d9aeb4d..b9509504 100644
--- a/storeio/Makefile
+++ b/storeio/Makefile
@@ -1,6 +1,7 @@
 # Makefile for storeio
-# 
-#   Copyright (C) 1995, 1996, 1997, 2000, 2012 Free Software Foundation, Inc.
+#
+#   Copyright (C) 1995, 1996, 1997, 2000, 2012, 2026 Free Software
+#   Foundation, Inc.
 #
 #   This program is free software; you can redistribute it and/or
 #   modify it under the terms of the GNU General Public License as
@@ -19,12 +20,14 @@
 dir := storeio
 makemode := server
 
+#CFLAGS += -DDEBUG
+
 target = storeio storeio.static
-SRCS = dev.c storeio.c open.c pager.c io.c
+SRCS = dev.c storeio.c pager.c io.c
 
 OBJS = $(SRCS:.c=.o)
-HURDLIBS = trivfs pager fshelp iohelp store ports ihash shouldbeinlibc
-LDLIBS = -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz)
+HURDLIBS = netfs pager fshelp iohelp store ports ihash shouldbeinlibc
+LDLIBS = -lparted -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz)
 
 include ../Makeconf
 
diff --git a/storeio/dev.c b/storeio/dev.c
index c87400c0..dadd1e4e 100644
--- a/storeio/dev.c
+++ b/storeio/dev.c
@@ -1,6 +1,6 @@
 /* store `device' I/O
 
-   Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2008
+   Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2008, 2026
      Free Software Foundation, Inc.
    Written by Miles Bader <[email protected]>
 
@@ -140,40 +140,24 @@ dev_buf_rw (struct dev *dev, size_t buf_offs, size_t *io_offs, size_t *len,
       return 0;
     }
 }
-
-/* Called with DEV->lock held.  Try to open the store underlying DEV.  */
+
 error_t
-dev_open (struct dev *dev)
+dev_open_from_store (struct dev *dev, struct store *store)
 {
-  error_t err;
-  const int flags = ((dev->readonly ? STORE_READONLY : 0)
-		     | (dev->no_fileio ? STORE_NO_FILEIO : 0));
-
-  assert_backtrace (dev->store == 0);
-
-  if (dev->store_name == 0)
-    {
-      /* This means we had no store arguments.
-	 We are to operate on our underlying node. */
-      err = store_create (storeio_fsys->underlying, flags, 0, &dev->store);
-    }
-  else
-    /* Open based on the previously parsed store arguments.  */
-    err = store_parsed_open (dev->store_name, flags, &dev->store);
-  if (err)
-    return err;
-
   /* Inactivate the store, it will be activated at first access.
      We ignore possible EINVAL here   .  XXX Pass STORE_INACTIVE to
      store_create/store_parsed_open instead when libstore is fixed
      to support this.  */
+  debug ("dev_init_from_store:\n");
+  dev->store = store;
   store_set_flags (dev->store, STORE_INACTIVE);
 
-  if (! dev->store->block_size)
+  if (!dev->store->block_size)
     dev->buf = NULL;
   else
     dev->buf = mmap (0, dev->store->block_size, PROT_READ|PROT_WRITE,
-		     MAP_ANON, 0, 0);
+                     MAP_ANON, 0, 0);
+
   if (dev->buf == MAP_FAILED)
     {
       store_free (dev->store);
@@ -181,7 +165,7 @@ dev_open (struct dev *dev)
       return ENOMEM;
     }
 
-  if (!dev->inhibit_cache)
+  if (!storeio_stat.inhibit_cache)
     {
       dev->buf_offs = -1;
       pthread_rwlock_init (&dev->io_lock, NULL);
@@ -190,8 +174,35 @@ dev_open (struct dev *dev)
       pthread_mutex_init (&dev->pager_lock, NULL);
     }
 
+  debug ("dev_init_from_store end with 0\n");
   return 0;
 }
+
+/* Called with DEV->lock held.  Try to open the store underlying DEV.  */
+error_t
+dev_open (struct dev *dev, struct store_parsed *store_name)
+{
+  error_t err;
+  const int flags = ((storeio_stat.readonly ? STORE_READONLY : 0)
+                     | (storeio_stat.no_fileio ? STORE_NO_FILEIO : 0));
+
+  assert_backtrace (dev->store == 0);
+
+  struct store *store;
+  if (store_name == 0)
+    {
+      /* This means we had no store arguments.
+	 We are to operate on our underlying node. */
+      err = store_create (underlying_node, flags, 0, &store);
+    }
+  else
+    /* Open based on the previously parsed store arguments.  */
+    err = store_parsed_open (store_name, flags, &store);
+  if (err)
+    return err;
+
+  return dev_open_from_store (dev, store);
+}
 
 /* Shut down the store underlying DEV and free any resources it consumes.
    DEV itself remains intact so that dev_open can be called again.
@@ -201,7 +212,7 @@ dev_close (struct dev *dev)
 {
   assert_backtrace (dev->store);
 
-  if (!dev->inhibit_cache)
+  if (!storeio_stat.inhibit_cache)
     {
       if (dev->pager != NULL)
 	pager_shutdown (dev->pager);
@@ -222,7 +233,7 @@ dev_sync(struct dev *dev, int wait)
 {
   error_t err;
 
-  if (dev->inhibit_cache)
+  if (storeio_stat.inhibit_cache)
     return 0;
 
   /* Sync any paged backing store.  */
@@ -359,7 +370,7 @@ dev_write (struct dev *dev, off_t offs, const void *buf, size_t len,
 		     buf + io_offs, len, amount);
     }
 
-  if (dev->inhibit_cache)
+  if (storeio_stat.inhibit_cache)
     {
       /* Under --no-cache, we permit only whole-block writes.
 	 Note that in this case we handle non-power-of-two block sizes.  */
@@ -452,7 +463,7 @@ dev_read (struct dev *dev, off_t offs, size_t whole_amount,
       return 0;
     }
 
-  if (dev->inhibit_cache)
+  if (storeio_stat.inhibit_cache)
     {
       /* Under --no-cache, we permit only whole-block reads.
 	 Note that in this case we handle non-power-of-two block sizes.
diff --git a/storeio/dev.h b/storeio/dev.h
index eda7a93d..de7b9481 100644
--- a/storeio/dev.h
+++ b/storeio/dev.h
@@ -1,6 +1,6 @@
 /* store `device' I/O
 
-   Copyright (C) 1995,96,97,99,2000,2001 Free Software Foundation, Inc.
+   Copyright (C) 1995,96,97,99,2000,2001,2026 Free Software Foundation, Inc.
    Written by Miles Bader <[email protected]>
 
    This program is free software; you can redistribute it and/or
@@ -23,47 +23,27 @@
 #include <mach.h>
 #include <device/device.h>
 #include <pthread.h>
+#include <hurd/netfs.h>
 #include <hurd/store.h>
-#include <hurd/trivfs.h>
+#include <stdio.h>
+#include <unistd.h>
 
-extern struct trivfs_control *storeio_fsys;
+extern mach_port_t underlying_node;
 
 /* Information about backend store, which we presumptively call a "device".  */
 struct dev
 {
-  /* The argument specification that we use to open the store.  */
-  struct store_parsed *store_name;
-
   /* The device to which we're doing io.  This is null when the
      device is closed, in which case we will open from `store_name'.  */
   struct store *store;
 
-  int readonly;			/* Nonzero if user gave --readonly flag.  */
-  int enforced;			/* Nonzero if user gave --enforced flag.  */
-  int no_fileio;		/* Nonzero if user gave --no-fileio flag.  */
-  dev_t rdev;			/* A unixy device number for st_rdev.  */
-
-  /* The current owner of the open device.  For terminals, this affects
-     controlling terminal behavior (see term_become_ctty).  For all objects
-     this affects old-style async IO.  Negative values represent pgrps.  This
-     has nothing to do with the owner of a file (as returned by io_stat, and
-     as used for various permission checks by filesystems).  An owner of 0
-     indicates that there is no owner.  */
-  pid_t owner;
-
   /* The number of active opens.  */
   int nperopens;
 
-  /* This lock protects `store', `owner' and `nperopens'.  The other
-     members never change after creation, except for those locked by
-     io_lock (below).  */
+  /* This lock protects `store' and 'nperopens'.  The other members never
+     change after creation, except for those locked by io_lock (below).  */
   pthread_mutex_t lock;
 
-  /* Nonzero iff the --no-cache flag was given.
-     If this is set, the remaining members are not used at all
-     and don't need to be initialized or cleaned up.  */
-  int inhibit_cache;
-
   /* A bitmask corresponding to the part of an offset that lies within a
      device block.  */
   unsigned block_mask;
@@ -84,14 +64,47 @@ struct dev
   pthread_mutex_t pager_lock;
 };
 
+struct netnode
+{
+  struct dev *dev;
+  char *name;
+  struct node **entries;
+  size_t entries_size;
+};
+
+struct storeio_stat
+{
+  /* The argument specification that we use to open the store.  */
+  struct store_parsed *store_name;
+  volatile struct mapped_time_value *current_time;
+  pid_t pid;
+  uid_t uid;
+  gid_t gid;
+  mode_t mode;
+  int readonly; /* Nonzero if user gave --readonly flag.  */
+  int enforced; /* Nonzero if user gave --enforced flag.  */
+  int no_fileio; /* Nonzero if user gave --no-fileio flag.  */
+  dev_t rdev; /* A unixy device number for st_rdev.  */
+
+  /* Nonzero if the --no-cache flag was given.
+     If this is set, the remaining members are not used at all
+     and don't need to be initialized or cleaned up.  */
+  int inhibit_cache;
+};
+
+extern struct storeio_stat storeio_stat;
+
 static inline int
 dev_is_readonly (const struct dev *dev)
 {
-  return dev->readonly || (dev->store && (dev->store->flags & STORE_READONLY));
+  return storeio_stat.readonly || (dev->store && (dev->store->flags
+                                                  & STORE_READONLY));
 }
 
+error_t dev_open_from_store (struct dev *dev, struct store *store);
+
 /* Called with DEV->lock held.  Try to open the store underlying DEV.  */
-error_t dev_open (struct dev *dev);
+error_t dev_open (struct dev *dev, struct store_parsed *store_name);
 
 /* Shut down the store underlying DEV and free any resources it consumes.
    DEV itself remains intact so that dev_open can be called again.
@@ -100,13 +113,13 @@ void dev_close (struct dev *dev);
 
 /* Returns in MEMOBJ the port for a memory object backed by the storage on
    DEV.  Returns 0 or the error code if an error occurred.  */
-error_t dev_get_memory_object(struct dev *dev, vm_prot_t prot,
+error_t dev_get_memory_object(struct peropen *peropen, vm_prot_t prot,
 			      memory_object_t *memobj);
 
-/* Try to stop all paging activity on DEV, returning true if we were
-   successful.  If NOSYNC is true, then we won't write back any (kernel)
-   cached pages to the device.  */
-int dev_stop_paging (struct dev *dev, int nosync);
+/* Try to stop all paging activity, returning true if we were successful.
+   If NOSYNC is true, then we won't write back any (kernel) cached pages to
+   the device.  */
+int dev_stop_paging (int nosync);
 
 /* Try and write out any pending writes to DEV.  If WAIT is true, will wait
    for any paging activity to cease.  */
@@ -124,4 +137,22 @@ error_t dev_write (struct dev *dev, off_t offs, const void *buf, size_t len,
 error_t dev_read (struct dev *dev, off_t offs, size_t amount,
 		  void **buf, size_t *len);
 
+#ifdef DEBUG
+extern FILE *debug_file;
+extern pthread_mutex_t debug_lock;
+# define debug(format, ...)                             \
+  do                                                    \
+    {                                                   \
+      if (debug_file)                                   \
+        {                                               \
+          pthread_mutex_lock (&debug_lock);             \
+          fprintf (debug_file, format, ## __VA_ARGS__); \
+          pthread_mutex_unlock (&debug_lock);           \
+        }                                               \
+    }                                                   \
+  while (0)
+#else
+# define debug(format, ...) do {} while (0)
+#endif
+
 #endif /* !__DEV_H__ */
diff --git a/storeio/io.c b/storeio/io.c
index dca997ef..d14e4902 100644
--- a/storeio/io.c
+++ b/storeio/io.c
@@ -1,6 +1,6 @@
 /* The hurd io interface to storeio
 
-   Copyright (C) 1995,96,97,99,2000,02 Free Software Foundation, Inc.
+   Copyright (C) 1995,96,97,99,2000,02,26 Free Software Foundation, Inc.
    Written by Miles Bader <[email protected]>
 
    This program is free software; you can redistribute it and/or
@@ -17,15 +17,14 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
 
-#include <hurd/trivfs.h>
-#include <stdio.h>
+#include <pthread.h>
 #include <fcntl.h>
+#include <unistd.h>
 
-#include "open.h"
 #include "dev.h"
-#include "libtrivfs/trivfs_fs_S.h"
-#include "libtrivfs/trivfs_io_S.h"
-
+#include "libnetfs/fs_S.h"
+#include "libnetfs/io_S.h"
+
 /* Return objects mapping the data underlying this memory object.  If
    the object can be read then memobjrd will be provided; if the
    object can be written then memobjwr will be provided.  For objects
@@ -35,91 +34,146 @@
    mapping; they will set none of the ports and return an error.  Such
    objects can still be accessed by io_read and io_write.  */
 kern_return_t
-trivfs_S_io_map (struct trivfs_protid *cred,
-		 mach_port_t reply, mach_msg_type_name_t reply_type,
-		 memory_object_t *rd_obj, mach_msg_type_name_t *rd_type,
-		 memory_object_t *wr_obj, mach_msg_type_name_t *wr_type)
+netfs_S_io_map (struct protid *cred,
+                mach_port_t *memobjrd, mach_msg_type_name_t *memobjrdPoly,
+                mach_port_t *memobjwt, mach_msg_type_name_t *memobjwtPoly)
 {
-  if (! cred)
-    return EOPNOTSUPP;
-  else if (! (cred->po->openmodes & (O_READ|O_WRITE)))
-    return EBADF;
-  else
+  debug ("netfs_S_io_map:\n");
+  if (!cred)
     {
-      mach_port_t memobj;
-      int flags = cred->po->openmodes;
-      vm_prot_t prot =
-	((flags & O_READ) ? VM_PROT_READ : 0)
-	| ((flags & O_WRITE) ? VM_PROT_WRITE : 0);
-      struct open *open = (struct open *)cred->po->hook;
-      error_t err = dev_get_memory_object (open->dev, prot, &memobj);
+      debug ("!cred");
+      debug ("netfs_S_io_map return: EOPNOTSUPP");
+      return EOPNOTSUPP;
+    }
 
-      if (!err)
-	{
-	  if (flags & O_READ)
-	    *rd_obj = memobj;
-	  else
-	    *rd_obj = MACH_PORT_NULL;
-	  if (flags & O_WRITE)
-	    *wr_obj = memobj;
-	  else
-	    *wr_obj = MACH_PORT_NULL;
-
-	  if ((flags & (O_READ|O_WRITE)) == (O_READ|O_WRITE)
-	      && memobj != MACH_PORT_NULL)
-	    mach_port_mod_refs (mach_task_self (), memobj,
-				MACH_PORT_RIGHT_SEND, 1);
-	}
-
-      *rd_type = *wr_type = MACH_MSG_TYPE_MOVE_SEND;
+  if (!(cred->po->openstat & (O_READ | O_WRITE)))
+    {
+      debug ("!(cred->po->openstat & (O_READ | O_WRITE))\n");
+      debug ("netfs_S_io_map return: EBADF");
+      return EBADF;
+    }
+
+  *memobjrdPoly = *memobjwtPoly = MACH_MSG_TYPE_MOVE_SEND;
+
+  int flags = cred->po->openstat;
+  vm_prot_t prot = ((flags & O_READ) ? VM_PROT_READ : 0)
+                   | ((flags & O_WRITE) ? VM_PROT_WRITE : 0);
 
+  struct node *node = cred->po->np;
+  mach_port_t memobj;
+  pthread_mutex_lock (&node->lock);
+  error_t err = dev_get_memory_object (cred->po, prot, &memobj);
+  if (err)
+    {
+      debug ("dev_get_memory_object return err: %d\n", err);
+      pthread_mutex_unlock (&node->lock);
+      debug ("netfs_S_io_map return: %d\n", err);
       return err;
     }
+
+  if (flags & O_READ)
+    *memobjrd = memobj;
+  else
+    *memobjrd = MACH_PORT_NULL;
+
+  if (flags & O_WRITE)
+    *memobjwt = memobj;
+  else
+    *memobjwt = MACH_PORT_NULL;
+
+  if ((flags & (O_READ | O_WRITE)) == (O_READ | O_WRITE)
+      && memobj != MACH_PORT_NULL)
+    mach_port_mod_refs (mach_task_self (), memobj, MACH_PORT_RIGHT_SEND, 1);
+  pthread_mutex_unlock (&node->lock);
+
+  debug ("netfs_S_io_map return: 0\n");
+  return 0;
 }
-
+
 /* Read data from an IO object.  If offset if -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.  */
 kern_return_t
-trivfs_S_io_read (struct trivfs_protid *cred,
-		  mach_port_t reply, mach_msg_type_name_t reply_type,
-		  data_t *data, mach_msg_type_name_t *data_len,
-		  off_t offs, vm_size_t amount)
+netfs_S_io_read (struct protid *cred, data_t *data,
+                 mach_msg_type_number_t *datalen, off_t offset,
+                 vm_size_t amount)
 {
+  debug ("netfs_S_io_read:\n");
+
+  if (!cred)
+    {
+      debug ("!cred\n");
+      debug ("netfs_S_io_read return: EOPNOTSUPP\n");
+      return EOPNOTSUPP;
+    }
+
+  if (!(cred->po->openstat & O_READ))
+    {
+      debug ("!(cred->po->openstat & O_READ)\n");
+      debug ("netfs_S_io_read return: EBADF");
+      return EBADF;
+    }
+
+  struct node *node = cred->po->np;
+  pthread_mutex_lock (&node->lock);
+
+  size_t data_size = *datalen;
+
   error_t err;
-  size_t data_size = *data_len;
+  if (offset < 0)
+    {
+      err = dev_read (node->nn->dev, cred->po->filepointer, amount,
+                      (void **) data, &data_size);
+      if (!err)
+        cred->po->filepointer += data_size;
+    }
+  else
+    err = dev_read (node->nn->dev, offset, amount, (void **) data, &data_size);
 
-  if (! cred)
-    return EOPNOTSUPP;
-  else if (! (cred->po->openmodes & O_READ))
-    return EBADF;
+  pthread_mutex_unlock (&node->lock);
+  *datalen = data_size;
 
-  err = open_read ((struct open *)cred->po->hook,
-		   offs, amount, (void **)data, &data_size);
-  if (err)
-    return err;
-  *data_len = data_size;
-  return 0;
+  debug ("netfs_S_io_read return: %d\n", err);
+  return err;
 }
 
 /* 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 reply_type,
-		      vm_size_t *amount)
+netfs_S_io_readable (struct protid *cred, vm_size_t *amount)
 {
-  if (! cred)
-    return EOPNOTSUPP;
-  else if (! (cred->po->openmodes & O_READ))
-    return EBADF;
-  else
+  debug ("netfs_S_io_readable");
+  if (!cred)
     {
-      struct open *open = (struct open *)cred->po->hook;
-      *amount = open->dev->store->size - open->offs;
-      return 0;
+      debug ("!cred\n");
+      debug ("netfs_S_io_readable return: EOPNOTSUPP\n");
+      return EOPNOTSUPP;
+    }
+
+  if (!(cred->po->openstat & O_READ))
+    {
+      debug ("!(cred->po->openstat & O_READ)\n");
+      debug ("netfs_S_io_readable return: EBADF\n");
+      return EBADF;
+    }
+
+  struct node *node = cred->po->np;
+  pthread_mutex_lock (&node->lock);
+  error_t err = netfs_validate_stat (node, cred->user);
+  if (err)
+    {
+      debug ("netfs_validate_stat return err: %d\n", err);
+      pthread_mutex_unlock (&node->lock);
+      debug ("netfs_S_io_readable return: %d\n", err);
+      return err;
     }
+
+  *amount = node->nn->dev->store->size - cred->po->filepointer;
+  pthread_mutex_unlock (&node->lock);
+
+  debug ("netfs_S_io_readable return: 0\n");
+  return 0;
 }
 
 /* Write data to an IO object.  If offset is -1, write at the object
@@ -130,256 +184,125 @@ trivfs_S_io_readable (struct trivfs_protid *cred,
    responses to io_write.  Servers may drop data (returning ENOBUFS)
    if they recevie more than one write when not prepared for it.  */
 kern_return_t
-trivfs_S_io_write (struct trivfs_protid *cred,
-		   mach_port_t reply, mach_msg_type_name_t reply_type,
-		   const_data_t data, mach_msg_type_number_t data_len,
-		   off_t offs, vm_size_t *amount)
-{
-  if (! cred)
-    return EOPNOTSUPP;
-  else if (! (cred->po->openmodes & O_WRITE))
-    return EBADF;
-  else
-    return open_write ((struct open *)cred->po->hook,
-		       offs, data, data_len, amount);
-}
-
-/* Change current read/write offset */
-kern_return_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)
+netfs_S_io_write (struct protid *cred, const_data_t data,
+                  mach_msg_type_number_t datalen, off_t offset,
+                  vm_size_t *amount)
 {
-  if (! cred)
-    return EOPNOTSUPP;
-  else
-    return open_seek ((struct open *)cred->po->hook, offs, whence, new_offs);
-}
-
-/* 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.  */
-kern_return_t
-trivfs_S_io_select (struct trivfs_protid *cred,
-		    mach_port_t reply, mach_msg_type_name_t reply_type,
-		    int *type)
-{
-  if (! cred)
-    return EOPNOTSUPP;
-  *type &= ~SELECT_URG;
-  return 0;
-}
+  debug ("netfs_S_io_write:\n");
+  if (!cred)
+    {
+      debug ("!cred\n");
+      debug ("netfs_S_io_write return: EOPNOTSUPP\n");
+      return EOPNOTSUPP;
+    }
 
-kern_return_t
-trivfs_S_io_select_timeout (struct trivfs_protid *cred,
-			    mach_port_t reply, mach_msg_type_name_t reply_type,
-			    struct timespec ts,
-			    int *type)
-{
-  return trivfs_S_io_select (cred, reply, reply_type, type);
-}
+  if (!(cred->po->openstat & O_WRITE))
+    {
+      debug ("!(user->po->openstat & O_WRITE)\n");
+      debug ("netfs_S_io_write return: EBADF\n");
+      return EBADF;
+    }
 
-/* Truncate file.  */
-kern_return_t
-trivfs_S_file_set_size (struct trivfs_protid *cred,
-			mach_port_t reply, mach_msg_type_name_t reply_type,
-			off_t size)
-{
-  if (! cred)
-    return EOPNOTSUPP;
-  else if (size < 0)
-    return EINVAL;
-  else
-    return 0;
-}
-
-/* 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. */
+  struct node *node = cred->po->np;
+  pthread_mutex_lock (&node->lock);
 
-kern_return_t
-trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
-			   mach_port_t reply, mach_msg_type_name_t reply_type,
-			   int *bits)
-{
-  if (! cred)
-    return EOPNOTSUPP;
-  else
+  error_t err;
+  if (offset < 0)
     {
-      *bits = cred->po->openmodes;
-      return 0;
+      err = dev_write (node->nn->dev, cred->po->filepointer, data, datalen,
+                       amount);
+      if (!err)
+        cred->po->filepointer += *amount;
     }
-}
-
-kern_return_t
-trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred,
-			       mach_port_t reply,
-			       mach_msg_type_name_t reply_type,
-			       int mode)
-{
-  if (! cred)
-    return EOPNOTSUPP;
   else
-    return 0;
-}
+    err = dev_write (node->nn->dev, offset, data, datalen, amount);
 
-kern_return_t
-trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
-				mach_port_t reply,
-				mach_msg_type_name_t reply_type,
-				int bits)
-{
-  if (! cred)
-    return EOPNOTSUPP;
-  else
-    return 0;
+  pthread_mutex_unlock (&node->lock);
+
+  debug ("netfs_S_io_write return: %d\n", err);
+  return err;
 }
 
-kern_return_t
-trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
-				  mach_port_t reply,
-				  mach_msg_type_name_t reply_type,
-				  int bits)
+static inline int
+is_privileged (const struct idvec *uids)
 {
-  if (! cred)
-    return EOPNOTSUPP;
-  else
-    return 0;
+  return idvec_contains (uids, 0) || idvec_contains (uids, getuid ());
 }
-
-/* Get/set the owner of the IO object.  For terminals, this affects
-   controlling terminal behavior (see term_become_ctty).  For all
-   objects this affects old-style async IO.  Negative values represent
-   pgrps.  This has nothing to do with the owner of a file (as
-   returned by io_stat, and as used for various permission checks by
-   filesystems).  An owner of 0 indicates that there is no owner.  */
-kern_return_t
-trivfs_S_io_get_owner (struct trivfs_protid *cred,
-		       mach_port_t reply,
-		       mach_msg_type_name_t reply_type,
-		       pid_t *owner)
+
+error_t
+netfs_file_get_storage_info (struct iouser *cred, struct node *np,
+                             mach_port_t **ports,
+                             mach_msg_type_name_t *ports_type,
+                             mach_msg_type_number_t *num_ports,
+                             int **ints,
+                             mach_msg_type_number_t *num_ints,
+                             off_t **offsets,
+                             mach_msg_type_number_t *num_offsets,
+                             char **data,
+                             mach_msg_type_number_t *data_len)
 {
-  if (! cred)
-    return EOPNOTSUPP;
-  else
+  debug ("netfs_file_get_storage_info:\n");
+  if (!cred)
     {
-      struct open *open = (struct open *)cred->po->hook;
-      *owner = open->dev->owner; /* atomic word fetch */
-      return 0;
+      debug ("!cred\n");
+      debug ("netfs_file_get_storage_info return: EOPNOTSUPP\n");
+      return EOPNOTSUPP;
     }
-}
 
-kern_return_t
-trivfs_S_io_mod_owner (struct trivfs_protid *cred,
-		       mach_port_t reply, mach_msg_type_name_t reply_type,
-		       pid_t owner)
-{
-  if (! cred)
-    return EOPNOTSUPP;
-  else
+  *ports_type = MACH_MSG_TYPE_COPY_SEND;
+  struct dev *dev = np->nn->dev;
+  struct store *store = dev->store;
+  if (storeio_stat.enforced && !(store->flags & STORE_ENFORCED))
     {
-      struct open *open = (struct open *)cred->po->hook;
-      open->dev->owner = owner;	/* atomic word store */
+      debug ("storeio_stat.enforced && !(store->flags & STORE_ENFORCED)\n");
+      size_t name_len = (store->name ? strlen (store->name) + 1 : 0);
+      *num_ports = 0;
+      int i = 0;
+      (*ints)[i++] = STORAGE_OTHER;
+      (*ints)[i++] = store->flags;
+      (*ints)[i++] = store->block_size;
+      (*ints)[i++] = 1;
+      (*ints)[i++] = name_len;
+      (*ints)[i++] = 0;
+      *num_ints = i;
+      i = 0;
+      (*offsets)[i++] = 0;
+      (*offsets)[i++] = store->size;
+      *num_offsets = i;
+      if (store->name)
+        memcpy (*data, store->name, name_len);
+      *data_len = name_len;
+      debug ("netfs_file_get_storage_info return: 0\n");
       return 0;
     }
-}
-
-/* File syncing operations; these all do the same thing, sync the underlying
-   device.  */
-
-kern_return_t
-trivfs_S_file_sync (struct trivfs_protid *cred,
-		    mach_port_t reply, mach_msg_type_name_t reply_type,
-		    int wait, int omit_metadata)
-{
-  if (cred)
-    return dev_sync (((struct open *)cred->po->hook)->dev, wait);
-  else
-    return EOPNOTSUPP;
-}
 
-kern_return_t
-trivfs_S_file_syncfs (struct trivfs_protid *cred,
-		      mach_port_t reply, mach_msg_type_name_t reply_type,
-		      int wait, int dochildren)
-{
-  if (cred)
-    return dev_sync (((struct open *)cred->po->hook)->dev, wait);
-  else
-    return EOPNOTSUPP;
-}
-
-kern_return_t
-trivfs_S_file_get_storage_info (struct trivfs_protid *cred,
-				mach_port_t reply,
-				mach_msg_type_name_t reply_type,
-				mach_port_t **ports,
-				mach_msg_type_name_t *ports_type,
-				mach_msg_type_number_t *num_ports,
-				int **ints, mach_msg_type_number_t *num_ints,
-				off_t **offsets,
-				mach_msg_type_number_t *num_offsets,
-				data_t *data, mach_msg_type_number_t *data_len)
-{
-  *ports_type = MACH_MSG_TYPE_COPY_SEND;
-
-  if (! cred || ! cred->po->hook)
-    return EOPNOTSUPP;
-  else
+  error_t err;
+  if (!is_privileged (cred->uids)
+      && !store_is_securely_returnable (store, np->nn_stat.st_mode))
     {
-      error_t err;
-      struct dev *dev = ((struct open *)cred->po->hook)->dev;
-      struct store *store = dev->store;
-
-      if (dev->enforced && !(store->flags & STORE_ENFORCED))
-	{
-	  /* The --enforced switch tells us not to let anyone
-	     get at the device, no matter how trustable they are.  */
-	  size_t name_len = (store->name ? strlen (store->name) + 1 : 0);
-	  int i;
-	  *num_ports = 0;
-	  i = 0;
-	  (*ints)[i++] = STORAGE_OTHER;
-	  (*ints)[i++] = store->flags;
-	  (*ints)[i++] = store->block_size;
-	  (*ints)[i++] = 1;	/* num_runs */
-	  (*ints)[i++] = name_len;
-	  (*ints)[i++] = 0;	/* misc_len */
-	  *num_ints = i;
-	  i = 0;
-	  (*offsets)[i++] = 0;
-	  (*offsets)[i++] = store->size;
-	  *num_offsets = i;
-	  if (store->name)
-	    memcpy (*data, store->name, name_len);
-	  *data_len = name_len;
-	  return 0;
-	}
-
-      if (!cred->isroot
-	  && !store_is_securely_returnable (store, cred->po->openmodes))
-	{
-	  struct store *clone;
-	  err = store_clone (store, &clone);
-	  if (! err)
-	    {
-	      err = store_set_flags (clone, STORE_INACTIVE);
-	      if (err == EINVAL)
-		err = EACCES;
-	      else
-		err = store_return (clone,
-				    ports, num_ports, ints, num_ints,
-				    offsets, num_offsets, data, data_len);
-	      store_free (clone);
-	    }
-	}
+      debug ("!is_privileged (cred->uids)...\n");
+      struct store *clone;
+      err = store_clone (store, &clone);
+      if (err)
+        {
+          debug ("store_clone return err: %d\n", err);
+          debug ("netfs_file_get_storage_info return: %d\n", err);
+          return err;
+        }
+
+      err = store_set_flags (clone, STORE_INACTIVE);
+      if (err == EINVAL)
+        err = EACCES;
       else
-	err = store_return (store,
-			    ports, num_ports, ints, num_ints,
-			    offsets, num_offsets, data, data_len);
+        err = store_return (clone, ports, num_ports, ints, num_ints,
+                            offsets, num_offsets, data, data_len);
 
-      return err;
+      store_free (clone);
     }
+  else
+    err = store_return (store, ports, num_ports, ints, num_ints,
+                        offsets, num_offsets, data, data_len);
+
+  debug ("netfs_file_get_storage_info return: %d\n", err);
+  return err;
 }
diff --git a/storeio/open.c b/storeio/open.c
deleted file mode 100644
index 74902520..00000000
--- a/storeio/open.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/* Per-open information for storeio
-
-   Copyright (C) 1995, 1996, 2006 Free Software Foundation, Inc.
-
-   Written by Miles Bader <[email protected]>
-
-   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 <hurd.h>
-#include <stdio.h>
-
-#include "open.h"
-#include "dev.h"
-
-/* Returns a new per-open structure for the device DEV in OPEN.  If an error
-   occurs, the error-code is returned, otherwise 0.  */
-error_t
-open_create (struct dev *dev, struct open **open)
-{
-  *open = malloc (sizeof (struct open));
-  if (*open == NULL)
-    return ENOMEM;
-
-  (*open)->dev = dev;
-  (*open)->offs = 0;
-  pthread_mutex_init (&(*open)->lock, NULL);
-
-  return 0;
-}
-
-/* Free OPEN and any resources it holds.  */
-void
-open_free (struct open *open)
-{
-  free (open);
-}
-
-/* Writes up to LEN bytes from BUF to OPEN's device at device offset OFFS
-   (which may be ignored if the device doesn't support random access),
-   and returns the number of bytes written in AMOUNT.  If no error occurs,
-   zero is returned, otherwise the error code is returned.  */
-error_t
-open_write (struct open *open, off_t offs, const void *buf, size_t len,
-	    vm_size_t *amount)
-{
-  error_t err;
-  if (offs < 0)
-    /* Use OPEN's offset.  */
-    {
-      pthread_mutex_lock (&open->lock);
-      err = dev_write (open->dev, open->offs, buf, len, amount);
-      if (! err)
-	open->offs += *amount;
-      pthread_mutex_unlock (&open->lock);
-    }
-  else
-    err = dev_write (open->dev, offs, buf, len, amount);
-  return err;
-}    
-
-/* Reads up to AMOUNT bytes from the device into BUF and LEN using the
-   standard mach out-array convention.  If no error occurs, zero is returned,
-   otherwise the error code is returned.  */
-error_t
-open_read (struct open *open, off_t offs, vm_size_t amount,
-	   void **buf, vm_size_t *len)
-{
-  error_t err;
-  if (offs < 0)
-    /* Use OPEN's offset.  */
-    {
-      pthread_mutex_lock (&open->lock);
-      err = dev_read (open->dev, open->offs, amount, buf, len);
-      if (! err)
-	open->offs += *len;
-      pthread_mutex_unlock (&open->lock);
-    }
-  else
-    err = dev_read (open->dev, offs, amount, buf, len);
-  return err;
-}   
-
-/* Set OPEN's location to OFFS, interpreted according to WHENCE as by seek.
-   The new absolute location is returned in NEW_OFFS (and may not be the same
-   as OFFS).  If no error occurs, zero is returned, otherwise the error code
-   is returned.  */
-error_t
-open_seek (struct open *open, off_t offs, int whence, off_t *new_offs)
-{
-  error_t err = 0;
-
-  pthread_mutex_lock (&open->lock);
-
-  switch (whence)
-    {
-    case SEEK_CUR:
-      offs += open->offs;
-      goto check;
-    case SEEK_END:
-      offs += open->dev->store->size;
-    case SEEK_SET:
-    check:
-      if (offs >= 0)
-	{
-	  *new_offs = open->offs = offs;
-	  break;
-	}
-    default:
-      err = EINVAL;
-    }
-
-  pthread_mutex_unlock (&open->lock);
-
-  return err;
-}
diff --git a/storeio/open.h b/storeio/open.h
deleted file mode 100644
index ad2678ff..00000000
--- a/storeio/open.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* Per-open information for storeio
-
-   Copyright (C) 1995, 1996 Free Software Foundation, Inc.
-
-   Written by Miles Bader <[email protected]>
-
-   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 __OPEN_H__
-#define __OPEN_H__
-
-#include "dev.h"
-
-/* ---------------------------------------------------------------- */
-
-/* A structure describing a particular i/o stream on this device.  */
-struct open
-{
-  /* The device that this an open on.  */
-  struct dev *dev;
-
-  /* The per-open offset used for I/O operations that don't specify an
-     explicit offset.  */
-  off_t offs;
-
-  /* A lock used to control write access to OFFS.  */
-  pthread_mutex_t lock;
-};
-
-/* Returns a new per-open structure for the device DEV in OPEN.  If an error
-   occurs, the error-code is returned, otherwise 0.  */
-error_t open_create (struct dev *dev, struct open **open);
-
-/* Free OPEN and any resources it holds.  */
-void open_free (struct open *open);
-
-/* Writes up to LEN bytes from BUF to OPEN's device at device offset OFFS
-   (which may be ignored if the device doesn't support random access),
-   and returns the number of bytes written in AMOUNT.  If no error occurs,
-   zero is returned, otherwise the error code is returned.  */
-error_t open_write (struct open *open, off_t offs, const void *buf, size_t len,
-		    vm_size_t *amount);
-
-/* Reads up to AMOUNT bytes from the device into BUF and BUF_LEN using the
-   standard mach out-array convention.  If no error occurs, zero is returned,
-   otherwise the error code is returned.  */
-error_t open_read (struct open *open, off_t offs, vm_size_t amount,
-		   void **buf, vm_size_t *buf_len);
-
-/* Set OPEN's location to OFFS, interpreted according to WHENCE as by seek.
-   The new absolute location is returned in NEW_OFFS (and may not be the same
-   as OFFS).  If no error occurs, zero is returned, otherwise the error code
-   is returned.  */
-error_t open_seek (struct open *open, off_t offs, int whence, off_t *new_offs);
-
-#endif /* !__OPEN_H__ */
diff --git a/storeio/pager.c b/storeio/pager.c
index 11bf4692..02a598a5 100644
--- a/storeio/pager.c
+++ b/storeio/pager.c
@@ -1,6 +1,6 @@
 /* Paging interface for storeio devices
 
-   Copyright (C) 1995,96,97,99,2002 Free Software Foundation, Inc.
+   Copyright (C) 1995,96,97,99,2002,26 Free Software Foundation, Inc.
 
    Written by Miles Bader <[email protected]>
 
@@ -27,6 +27,7 @@
 #include <error.h>
 #include <sys/mman.h>
 #include <stdio.h>
+#include <string.h>
 
 #include "dev.h"
 
@@ -41,10 +42,21 @@ error_t
 pager_read_page (struct user_pager_info *upi,
 		 vm_offset_t page, vm_address_t *buf, int *writelock)
 {
+  debug ("pager_read_page:\n");
   error_t err;
   size_t read = 0;		/* bytes actually read */
   int want = vm_page_size;	/* bytes we want to read */
-  struct dev *dev = (struct dev *)upi;
+
+  struct peropen *po = (struct peropen *) upi;
+  if (!(po->openstat & O_READ))
+    {
+      debug ("!(po->openstat & O_READ)\n");
+      debug ("pager_read_page return: EIO\n");
+      return EIO;
+    }
+
+  pthread_mutex_lock (&po->np->lock);
+  struct dev *dev = po->np->nn->dev;
   struct store *store = dev->store;
 
   if (page + want > store->size)
@@ -60,10 +72,16 @@ pager_read_page (struct user_pager_info *upi,
 
   *writelock = (store->flags & STORE_READONLY);
 
+  pthread_mutex_unlock (&po->np->lock);
   if (err || read < want)
-    return EIO;
-  else
-    return 0;
+    {
+      debug ("err || read < want\n");
+      debug ("pager_read_page return: EIO\n");
+      return EIO;
+    }
+
+  debug ("pager_read_page return: 0\n");
+  return 0;
 }
 
 /* For pager PAGER, synchronously write one page from BUF to offset PAGE.
@@ -73,35 +91,54 @@ error_t
 pager_write_page (struct user_pager_info *upi,
 		  vm_offset_t page, vm_address_t buf)
 {
-  struct dev *dev = (struct dev *)upi;
+  debug ("pager_write_page:\n");
+  struct peropen *po = (struct peropen *) upi;
+  if (!(po->openstat & O_WRITE))
+    {
+      debug ("!(po->openstat & O_WRITE)\n");
+      debug ("pager_write_page return: EIO\n");
+      return EIO;
+    }
+
+  pthread_mutex_lock (&po->np->lock);
+  struct dev *dev = po->np->nn->dev;
   struct store *store = dev->store;
 
   if (store->flags & STORE_READONLY)
-    return EROFS;
-  else
     {
-      error_t err;
-      size_t written;
-      int want = vm_page_size;
+      debug ("store->flags & STORE_READONLY\n");
+      pthread_mutex_unlock (&po->np->lock);
+      debug ("pager_write_page return: EROFS");
+      return EROFS;
+    }
+
+  error_t err;
+  size_t written;
+  int want = vm_page_size;
 
-      if (page + want > store->size)
-	/* Write a partial page if necessary to avoid reading off the end.  */
-	want = store->size - page;
+  if (page + want > store->size)
+    /* Write a partial page if necessary to avoid reading off the end.  */
+    want = store->size - page;
 
-      err = dev_write (dev, page, (char *)buf, want, &written);
+  err = dev_write (dev, page, (char *)buf, want, &written);
 
-      if (err || written < want)
-	return EIO;
-      else
-	return 0;
+  pthread_mutex_unlock (&po->np->lock);
+  if (err || written < want)
+    {
+      debug ("err || written < want\n");
+      debug ("pager_write_page return: EIO\n");
+      return EIO;
     }
+
+  debug ("pager_write_page return: 0\n");
+  return 0;
 }
 
 /* A page should be made writable. */
 error_t
 pager_unlock_page (struct user_pager_info *upi, vm_offset_t address)
 {
-  struct dev *dev = (struct dev *)upi;
+  struct dev *dev = ((struct peropen *) upi)->np->nn->dev;
 
   if (dev->store->flags & STORE_READONLY)
     return EROFS;
@@ -124,7 +161,7 @@ pager_report_extent (struct user_pager_info *upi,
 		    vm_address_t *offset, vm_size_t *size)
 {
   *offset = 0;
-  *size = ((struct dev *)upi)->store->size;
+  *size = ((struct peropen *) upi)->np->nn->dev->store->size;
   return 0;
 }
 
@@ -133,7 +170,7 @@ pager_report_extent (struct user_pager_info *upi,
 void
 pager_clear_user_data (struct user_pager_info *upi)
 {
-  struct dev *dev = (struct dev *)upi;
+  struct dev *dev = ((struct peropen *) upi)->np->nn->dev;
   pthread_mutex_lock (&dev->pager_lock);
   dev->pager = 0;
   pthread_mutex_unlock (&dev->pager_lock);
@@ -174,11 +211,11 @@ pager_dropweak (struct user_pager_info *upi __attribute__ ((unused)))
 {
 }
 
-/* Try to stop all paging activity on DEV, returning true if we were
-   successful.  If NOSYNC is true, then we won't write back any (kernel)
-   cached pages to the device.  */
+/* Try to stop all paging activity, returning true if we were successful.
+   If NOSYNC is true, then we won't write back any (kernel) cached pages to
+   the device.  */
 int
-dev_stop_paging (struct dev *dev, int nosync)
+dev_stop_paging (int nosync)
 {
   size_t num_pagers = (pager_port_bucket ?
 		       ports_count_bucket (pager_port_bucket) : 0);
@@ -219,11 +256,13 @@ dev_stop_paging (struct dev *dev, int nosync)
 /* Returns in MEMOBJ the port for a memory object backed by the storage on
    DEV.  Returns 0 or the error code if an error occurred.  */
 error_t
-dev_get_memory_object (struct dev *dev, vm_prot_t prot, memory_object_t *memobj)
+dev_get_memory_object (struct peropen *peropen, vm_prot_t prot,
+                       memory_object_t *memobj)
 {
+  struct dev *dev = peropen->np->nn->dev;
   error_t err = store_map (dev->store, prot, memobj);
 
-  if (err == EOPNOTSUPP && !dev->inhibit_cache)
+  if (err == EOPNOTSUPP && !storeio_stat.inhibit_cache)
     {
       int created = 0;
 
@@ -234,8 +273,8 @@ dev_get_memory_object (struct dev *dev, vm_prot_t prot, memory_object_t *memobj)
       if (dev->pager == NULL)
 	{
 	  dev->pager =
-	    pager_create ((struct user_pager_info *)dev, pager_port_bucket,
-			  1, MEMORY_OBJECT_COPY_DELAY, 0);
+	    pager_create ((struct user_pager_info *) peropen,
+			  pager_port_bucket, 1, MEMORY_OBJECT_COPY_DELAY, 0);
 	  if (dev->pager == NULL)
 	    {
 	      pthread_mutex_unlock (&dev->pager_lock);
@@ -251,7 +290,7 @@ dev_get_memory_object (struct dev *dev, vm_prot_t prot, memory_object_t *memobj)
 	{
 	  dev->pager = 0;
 	  pthread_mutex_unlock (&dev->pager_lock);
-	  return dev_get_memory_object (dev, prot, memobj);
+	  return dev_get_memory_object (peropen, prot, memobj);
 	}
       else
 	err =
diff --git a/storeio/storeio.c b/storeio/storeio.c
index 4e8a9628..00c15d47 100644
--- a/storeio/storeio.c
+++ b/storeio/storeio.c
@@ -1,6 +1,6 @@
 /* A translator for doing I/O to stores
 
-   Copyright (C) 1995,96,97,98,99,2000,01,02 Free Software Foundation, Inc.
+   Copyright (C) 1995,96,97,98,99,2000,01,02,26 Free Software Foundation, Inc.
    Written by Miles Bader <[email protected]>
 
    This program is free software; you can redistribute it and/or
@@ -18,23 +18,26 @@
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <error.h>
 #include <assert-backtrace.h>
 #include <fcntl.h>
 #include <argp.h>
 #include <argz.h>
 #include <sys/sysmacros.h>
+#include <sys/mman.h>
 #include <stdbool.h>
+#include <parted/parted.h>
+#include <dirent.h>
 
 #include <hurd.h>
 #include <hurd/ports.h>
-#include <hurd/trivfs.h>
 #include <version.h>
 
-#include "open.h"
 #include "dev.h"
-#include "libtrivfs/trivfs_fsys_S.h"
-
+#include "libnetfs/fsys_S.h"
+#include "libnetfs/fsys_reply_U.h"
+
 static struct argp_option options[] =
 {
   {"readonly", 'r', 0,	  0,"Disallow writing"},
@@ -43,8 +46,10 @@ static struct argp_option options[] =
   {"no-file-io", 'F', 0,  0,"Never perform io via plain file io RPCs"},
   {"no-fileio",  0,   0, OPTION_ALIAS | OPTION_HIDDEN},
   {"enforced",  'e', 0,	  0,"Never reveal underlying devices, even to root"},
-  {"debug",	'd', "PATH",	0,
-   "Launch a standalone translator, for debug purposes"},
+#ifdef DEBUG
+  {"debug", 'd', "FILE", 0, "Enable debug and write debug statements to"
+   " FILE.  The FILE must be located outside the translator directory."},
+#endif
   {"rdev",     'n', "ID", 0,
    "The stat rdev number for this node; may be either a"
    " single integer, or of the form MAJOR,MINOR"},
@@ -54,31 +59,135 @@ static const char doc[] = "Translator for devices and other stores";
 
 const char *argp_program_version = STANDARD_HURD_VERSION (storeio);
 
-static bool debug=false;
-static char *debug_fname=NULL;
+char *netfs_server_name = "storeio";
+char *netfs_server_version = HURD_VERSION;
+int netfs_maxsymlinks = 0; /* arbitrary */
+
+char *debug_file_name = NULL;
+FILE *debug_file;
+pthread_mutex_t debug_lock;
 
-/* Desired store parameters specified by the user.  */
-struct storeio_argp_params
+static error_t
+create_node (struct node **node, char *name, struct node *dir)
 {
-  struct store_argp_params store_params; /* Filled in by store_argp parser.  */
-  struct dev *dev;		/* We fill in its flag members.  */
-};
+  debug ("create_node (name: %s, dir: %p):\n", name, dir);
+  struct netnode *netnode = malloc (sizeof (struct netnode));
+  if (!netnode)
+    {
+      debug ("!netnode\n");
+      debug ("create_node return: ENOMEM\n");
+      return ENOMEM;
+    }
+
+  struct node *new_node = netfs_make_node (netnode);
+  if (!new_node)
+    {
+      debug ("!new_node\n");
+      free (netnode);
+      debug ("create_node return: ENOMEM\n");
+      return ENOMEM;
+    }
+
+  static ino_t id = 1;
+  io_statbuf_t statbuf = {
+    .st_fstype = FSTYPE_MISC,
+    .st_fsid = storeio_stat.pid,
+    .st_dev = storeio_stat.pid,
+    .st_rdev = storeio_stat.pid,
+    .st_uid = storeio_stat.uid,
+    .st_author = storeio_stat.uid,
+    .st_gid = storeio_stat.gid,
+    .st_mode = storeio_stat.mode,
+    .st_ino = id++,
+    .st_nlink = 1,
+    .st_blksize = 1,
+    .st_blocks = 1,
+    .st_gen = 0
+  };
+  new_node->nn_stat = statbuf;
+  new_node->next = NULL;
+  new_node->prevp = NULL;
+
+  struct dev *dev = malloc (sizeof (struct dev));
+  if (!dev)
+    {
+      debug ("!dev\n");
+      free (netnode);
+      free (new_node);
+      debug ("create_node return: ENOMEM\n");
+      return ENOMEM;
+    }
+
+  memset (dev, 0, sizeof (struct dev));
+  pthread_mutex_init (&dev->lock, NULL);
+  new_node->nn->dev = dev;
+  new_node->nn->name = name;
+  new_node->nn->entries = NULL;
+  new_node->nn->entries_size = 0;
+
+  if (dir)
+    netfs_nref (dir);
+
+  fshelp_touch (&new_node->nn_stat, TOUCH_ATIME|TOUCH_CTIME|TOUCH_MTIME,
+                storeio_stat.current_time);
+
+  *node = new_node;
+  debug ("new_node: %p\n", new_node);
+  debug ("create_node return: 0\n");
+  return 0;
+}
+
+static error_t
+create_storeio (void)
+{
+  debug ("create_storeio:\n");
+  error_t err = maptime_map (1, 0, &storeio_stat.current_time);
+  if (err)
+    {
+      err = maptime_map (0, 0, &storeio_stat.current_time);
+      if (err)
+        return err;
+    }
+
+  storeio_stat.pid = getpid ();
+  storeio_stat.uid = getuid ();
+  storeio_stat.gid = getgid ();
+
+  storeio_stat.mode = storeio_stat.readonly ? 0444 : 0644;
+
+  err = create_node (&netfs_root_node, NULL, NULL);
+  if (err)
+    {
+      debug ("create_node return err: %d\n", err);
+      debug ("create_storeio return: %d\n", err);
+      return err;
+    }
+
+  netfs_root_node->nn_stat.st_nlink = 2;
+
+  debug ("create_storeio return: 0\n");
+  return 0;
+}
+
+struct storeio_stat storeio_stat;
 
 /* Parse a single option.  */
 static error_t
 parse_opt (int key, char *arg, struct argp_state *state)
 {
-  struct storeio_argp_params *params = state->input;
+  struct store_argp_params *store_params = state->input;
 
   switch (key)
     {
+    case 'r': storeio_stat.readonly = 1; break;
+    case 'w': storeio_stat.readonly = 0; break;
 
-    case 'r': params->dev->readonly = 1; break;
-    case 'w': params->dev->readonly = 0; break;
-
-    case 'c': params->dev->inhibit_cache = 1; break;
-    case 'e': params->dev->enforced = 1; break;
-    case 'F': params->dev->no_fileio = 1; break;
+    case 'c': storeio_stat.inhibit_cache = 1; break;
+    case 'e': storeio_stat.enforced = 1; break;
+    case 'F': storeio_stat.no_fileio = 1; break;
+#ifdef DEBUG
+    case 'd': debug_file_name = arg; break;
+#endif
 
     case 'n':
       {
@@ -99,31 +208,22 @@ parse_opt (int key, char *arg, struct argp_state *state)
 	    return EINVAL;
 	  }
 
-	params->dev->rdev = rdev;
-      }
-      break;
-
-    case 'd':
-      {
-	debug=true;
-	char *new = strdup (arg);
-	if (new == NULL)
-	  return ENOMEM;
-	debug_fname = new;
+	storeio_stat.rdev = rdev;
       }
       break;
 
     case ARGP_KEY_INIT:
       /* Now store_argp's parser will get to initialize its state.
 	 The default_type member is our input parameter to it.  */
-      memset (&params->store_params, 0, sizeof params->store_params);
-      params->store_params.default_type = "device";
-      params->store_params.store_optional = 1;
-      state->child_inputs[0] = &params->store_params;
+      memset (&storeio_stat, 0, sizeof (struct storeio_stat));
+      memset (store_params, 0, sizeof (struct store_argp_params));
+      store_params->default_type = "device";
+      store_params->store_optional = 1;
+      state->child_inputs[0] = store_params;
       break;
 
     case ARGP_KEY_SUCCESS:
-      params->dev->store_name = params->store_params.result;
+      storeio_stat.store_name = store_params->result;
       break;
 
     default:
@@ -134,321 +234,872 @@ parse_opt (int key, char *arg, struct argp_state *state)
 
 static const struct argp_child argp_kids[] = { { &store_argp }, {0} };
 static const struct argp argp = { options, parse_opt, 0, doc, argp_kids };
-
-struct trivfs_control *storeio_fsys;
+
+mach_port_t underlying_node;
 
 int
 main (int argc, char *argv[])
 {
-  error_t err;
+  struct store_argp_params store_params;
+  argp_parse (&argp, argc, argv, 0, 0, &store_params);
+
   mach_port_t bootstrap;
-  struct dev device;
-  struct storeio_argp_params params;
+  task_get_bootstrap_port (mach_task_self (), &bootstrap);
+  if (bootstrap == MACH_PORT_NULL)
+    error (2, 0, "Must be started as a translator");
+
+  netfs_init ();
 
-  memset (&device, 0, sizeof device);
-  pthread_mutex_init (&device.lock, NULL);
+  underlying_node = netfs_startup (bootstrap, 0);
+  io_statbuf_t underlying_stat;
 
-  params.dev = &device;
-  argp_parse (&argp, argc, argv, 0, 0, &params);
+  error_t err = io_stat (underlying_node, &underlying_stat);
+  if (err)
+    error (1, err, "Cannot stat underlying node");
 
-  if (debug)
+#ifdef DEBUG
+  if (debug_file_name)
     {
-      if (!debug_fname)
-	error (3, EINVAL, "missing translated node");
-      err = trivfs_startup_debug (debug_fname, 0, 0, 0, 0, &storeio_fsys);
-      if (err)
-	error (3, err, "trivfs_startup_debug failed");
+      debug_file = fopen (debug_file_name, "a");
+      setbuf (debug_file, NULL);
+      pthread_mutex_init (&debug_lock, NULL);
     }
-  else
-    {
-      task_get_bootstrap_port (mach_task_self (), &bootstrap);
-      if (bootstrap == MACH_PORT_NULL)
-	error (2, 0, "Must be started as a translator");
+#endif
 
-      /* Reply to our parent */
-      err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &storeio_fsys);
-      if (err)
-	error (3, err, "trivfs_startup");
-    }
+  debug ("\n---------------start main---------------\n");
+
+  err = create_storeio ();
+  if (err)
+    error (1, err, "Cannot creare storeio");
 
-  storeio_fsys->hook = &device;
+  netfs_root_node->nn_stat = underlying_stat;
+  netfs_root_node->nn_stat.st_mode =
+    S_IFDIR | (underlying_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+  debug ("netfs_root_node->nn_stat.st_mode: %d\n",
+         netfs_root_node->nn_stat.st_mode);
 
   /* Launch. */
-  ports_manage_port_operations_multithread (storeio_fsys->pi.bucket,
-					    trivfs_demuxer,
-					    30*1000, 5*60*1000, 0);
+  debug ("netfs_server_loop()...\n");
+  netfs_server_loop ();
 
   return 0;
 }
-
+
 error_t
-trivfs_append_args (struct trivfs_control *trivfs_control,
-		    char **argz, size_t *argz_len)
+netfs_append_args (char **argz, size_t *argz_len)
 {
-  struct dev *const dev = trivfs_control->hook;
   error_t err = 0;
 
-  if (dev->rdev != (dev_t) 0)
+  if (storeio_stat.rdev != (dev_t) 0)
     {
       char buf[40];
-      snprintf (buf, sizeof buf, "--rdev=%d,%d",
-		gnu_dev_major (dev->rdev), gnu_dev_minor (dev->rdev));
+      snprintf (buf, sizeof (buf), "--rdev=%d,%d",
+                gnu_dev_major (storeio_stat.rdev),
+                gnu_dev_minor (storeio_stat.rdev));
+
       err = argz_add (argz, argz_len, buf);
     }
 
-  if (!err && dev->inhibit_cache)
+  if (!err && storeio_stat.inhibit_cache)
     err = argz_add (argz, argz_len, "--no-cache");
 
-  if (!err && dev->enforced)
+  if (!err && storeio_stat.enforced)
     err = argz_add (argz, argz_len, "--enforced");
 
-  if (!err && dev->no_fileio)
+  if (!err && storeio_stat.no_fileio)
     err = argz_add (argz, argz_len, "--no-file-io");
 
-  if (! err)
+  if (!err)
     err = argz_add (argz, argz_len,
-		    dev->readonly ? "--readonly" : "--writable");
+                    storeio_stat.readonly ? "--readonly" : "--writable");
 
-  if (! err)
-    err = store_parsed_append_args (dev->store_name, argz, argz_len);
+  if (!err)
+    err = store_parsed_append_args (storeio_stat.store_name, argz, argz_len);
 
   return err;
 }
-
-/* Called whenever a new lookup is done of our node.  The only reason we
-   set this hook is to duplicate the check done normally done against
-   trivfs_allow_open in trivfs_S_fsys_getroot, but looking at the
-   per-device state.  This gets checked again in check_open_hook, but this
-   hook runs before a little but more overhead gets incurred.  In the
-   success case, we just return EAGAIN to have trivfs_S_fsys_getroot
-   continue with its generic processing.  */
-static error_t
-getroot_hook (struct trivfs_control *cntl,
-	      mach_port_t reply_port,
-	      mach_msg_type_name_t reply_port_type,
-	      mach_port_t dotdot,
-	      const uid_t *uids, mach_msg_type_number_t nuids, const uid_t *gids, mach_msg_type_number_t ngids,
-	      int flags,
-	      retry_type *do_retry, char *retry_name,
-	      mach_port_t *node, mach_msg_type_name_t *node_type)
-{
-  struct dev *const dev = cntl->hook;
-  return (dev_is_readonly (dev) && (flags & O_WRITE)) ? EROFS : EAGAIN;
-}
-
-/* Called whenever someone tries to open our node (even for a stat).  We
-   delay opening the kernel device until this point, as we can usefully
-   return errors from here.  */
+
 static error_t
-check_open_hook (struct trivfs_control *trivfs_control,
-		 struct iouser *user,
-		 int flags)
+check_dev (struct node *node, struct store *store, int flags)
 {
-  struct dev *const dev = trivfs_control->hook;
-  error_t err = 0;
-
-  if (!err && dev_is_readonly (dev) && (flags & O_WRITE))
-    return EROFS;
+  debug ("check_dev (node: %p, store: %p, flags: %d):\n", node, store, flags);
+  struct dev *dev = node->nn->dev;
+  if (dev_is_readonly (dev) && (flags & O_WRITE))
+    {
+      debug ("dev_is_readonly (node->nn->dev) && (flags & O_WRITE)\n");
+      debug ("check_dev return: EROFS\n");
+      return EROFS;
+    }
 
+  error_t err = 0;
   pthread_mutex_lock (&dev->lock);
-  if (dev->store == NULL)
+  if (!dev->store)
     {
-      /* Try and open the store.  */
-      err = dev_open (dev);
-      if (err && (flags & (O_READ|O_WRITE)) == 0)
-	/* If we're not opening for read or write, then just ignore the
-	   error, as this allows stat to work correctly.  XXX  */
-	err = 0;
+      if (store)
+        err = dev_open_from_store (dev, store);
+      else
+        err = dev_open (dev, storeio_stat.store_name);
+
+      if (err)
+        {
+          if ((flags & (O_READ | O_WRITE)) == 0)
+            {
+              debug ("dev open err: %d, but we are not opening the file for"
+                     " reading or writing, just ignore the error and exit"
+                     " the function\n", err);
+              pthread_mutex_unlock (&dev->lock);
+              debug ("check_dev return: 0\n");
+              return 0;
+            }
+          else
+            debug ("dev open err: %d\n", err);
+        }
+
+      if (!err)
+        {
+          node->nn_stat.st_size = dev->store->size;
+
+          if (dev->store->block_size > 1)
+            node->nn_stat.st_blksize = dev->store->block_size;
+
+          if (node != netfs_root_node)
+            {
+              if (dev->store->block_size == 1)
+                node->nn_stat.st_mode |= S_IFCHR;
+              else if (dev->store->block_size > 1)
+                node->nn_stat.st_mode |= S_IFBLK;
+            }
+        }
     }
   pthread_mutex_unlock (&dev->lock);
 
+  debug ("check_dev return: %d\n", err);
   return err;
 }
 
 static error_t
-open_hook (struct trivfs_peropen *peropen)
+node_open_create (struct peropen *po)
 {
   error_t err = 0;
-  struct dev *const dev = peropen->cntl->hook;
-
+  struct dev *dev = po->np->nn->dev;
   if (dev->store)
     {
       pthread_mutex_lock (&dev->lock);
       if (dev->nperopens++ == 0)
-	err = store_clear_flags (dev->store, STORE_INACTIVE);
+        err = store_clear_flags (dev->store, STORE_INACTIVE);
       pthread_mutex_unlock (&dev->lock);
-      if (!err)
-	err = open_create (dev, (struct open **)&peropen->hook);
     }
+
   return err;
 }
 
+error_t (*netfs_peropen_create_hook) (struct peropen *po) = node_open_create;
+
 static void
-close_hook (struct trivfs_peropen *peropen)
+node_open_destroy (struct peropen *po)
 {
-  struct dev *const dev = peropen->cntl->hook;
+  struct dev *dev = po->np->nn->dev;
+  pthread_mutex_lock (&dev->lock);
+  if (--dev->nperopens == 0)
+    store_clear_flags (dev->store, STORE_INACTIVE);
+  pthread_mutex_unlock (&dev->lock);
+}
+
+void (*netfs_peropen_destroy_hook) (struct peropen *po) = node_open_destroy;
+
+inline static void
+devs_lock (void)
+{
+  pthread_mutex_lock (&netfs_root_node->nn->dev->lock);
+  for (size_t i = 0; i < netfs_root_node->nn->entries_size; ++i)
+    pthread_mutex_lock (&netfs_root_node->nn->entries[i]->nn->dev->lock);
+}
+
+inline static void
+devs_unlock (void)
+{
+  pthread_mutex_unlock (&netfs_root_node->nn->dev->lock);
+  for (size_t i = 0; i < netfs_root_node->nn->entries_size; ++i)
+    pthread_mutex_unlock (&netfs_root_node->nn->entries[i]->nn->dev->lock);
+}
 
-  if (peropen->hook)
+kern_return_t
+netfs_S_fsys_goaway (struct netfs_control *pt,
+                     mach_port_t reply,
+                     mach_msg_type_name_t reply_type,
+                     int flags)
+{
+  debug ("netfs_S_fsys_goaway enter\n");
+  if (!pt)
     {
-      pthread_mutex_lock (&dev->lock);
-      if (--dev->nperopens == 0)
-	store_set_flags (dev->store, STORE_INACTIVE);
-      pthread_mutex_unlock (&dev->lock);
-      open_free (peropen->hook);
+      debug ("!pt");
+      debug ("netfs_S_fsys_goaway return: EOPNOTSUPP\n");
+      return EOPNOTSUPP;
+    }
+
+  if ((flags & FSYS_GOAWAY_UNLINK)
+      && S_ISDIR (netfs_root_node->nn_stat.st_mode))
+    {
+      debug ("(flags & FSYS_GOAWAY_UNLINK) && S_ISDIR"
+             " (netfs_root_node->nn_stat.st_mode\n");
+      debug ("netfs_S_fsys_goaway return: EBUSY\n");
+      return EBUSY;
     }
+
+  devs_lock ();
+
+  int force = flags & FSYS_GOAWAY_FORCE;
+  error_t err = ports_inhibit_class_rpcs (netfs_protid_class);
+  if (err == EINTR || (err && !force))
+    {
+      debug ("err == EINTR || (err && !force)\n");
+      devs_unlock ();
+      debug ("netfs_S_fsys_goaway return: %d\n", err);
+      return err;
+    }
+
+  int nosync = flags & FSYS_GOAWAY_NOSYNC;
+  if (force && nosync)
+    {
+      debug ("force && nosync\n");
+      exit (0);
+    }
+
+  if (!force && ports_count_class (netfs_protid_class) > 0)
+    {
+      debug ("!force && ports_count_class (netfs_protid_class) > 0\n");
+      ports_enable_class (netfs_protid_class);
+      ports_resume_class_rpcs (netfs_protid_class);
+      devs_unlock ();
+      debug ("netfs_S_fsys_goaway return: EBUSY\n");
+      return EBUSY;
+    }
+
+  if (!nosync)
+    {
+      debug ("!nosync\n");
+      err = netfs_attempt_syncfs (0, flags);
+    }
+
+  if (!err && (dev_stop_paging (nosync) || force))
+    {
+      debug ("!err && (dev_stop_paging (nosync) || force)\n");
+      if (!nosync)
+        {
+          debug ("!nosync\n");
+          dev_close (netfs_root_node->nn->dev);
+          for (size_t i = 0; i < netfs_root_node->nn->entries_size; ++i)
+            dev_close (netfs_root_node->nn->entries[i]->nn->dev);
+        }
+    }
+
+  if (!err)
+    {
+      debug ("!err\n");
+      fsys_goaway_reply (reply, reply_type, 0);
+      debug ("netfs_S_fsys_goaway exit (0)\n");
+      exit (0);
+    }
+
+  debug ("netfs_S_fsys_goaway return: %d\n", err);
+  return err;
 }
-
-/* ---------------------------------------------------------------- */
-/* Trivfs hooks  */
 
-int trivfs_fstype = FSTYPE_DEV;
-int trivfs_fsid = 0;
+error_t
+netfs_validate_stat (struct node *np, struct iouser *cred)
+{
+  return 0;
+}
 
-int trivfs_support_read = 1;
-int trivfs_support_write = 1;
-int trivfs_support_exec = 0;
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *np, uid_t uid,
+                     uid_t gid)
+{
+  return EOPNOTSUPP;
+}
 
-int trivfs_allow_open = O_READ | O_WRITE;
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *np, uid_t author)
+{
+  return EOPNOTSUPP;
+}
 
-void
-trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+error_t
+netfs_attempt_chmod (struct iouser *cred, struct node *np, mode_t mode)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_mksymlink (struct iouser *cred, struct node *np,
+                         const char *name)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_mkdev (struct iouser *cred, struct node *np, mode_t type,
+                     dev_t indexes)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *np, int flags)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *np,
+                      struct timespec *atime, struct timespec *mtime)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *np, loff_t size)
+{
+  return 0;
+}
+
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *np,
+                      fsys_statfsbuf_t *st)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
 {
-  struct dev *const dev = cred->po->cntl->hook;
-  struct open *open = cred->po->hook;
+  debug ("netfs_attempt_sync (cred: %p, np: %p, wait: %d):\n",
+         cred, np, wait);
+  error_t err = dev_sync (np->nn->dev, wait);
+  debug ("netfs_attempt_sync return: %d\n", err);
+  return err;
+}
 
-  st->st_mode &= ~S_IFMT;
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+  debug ("netfs_attempt_syncfs (cred: %p, wait: %d):\n", cred, wait);
+  error_t err = dev_sync (netfs_root_node->nn->dev, wait);
+  if (err)
+    {
+      debug ("dev_sync (netfs_root_node->nn->dev) return err: %d\n", err);
+      debug ("netfs_attempt_syncfs return: %d\n", err);
+      return err;
+    }
 
-  if (open)
-    /* An open device.  */
+  for (size_t i = 0; i < netfs_root_node->nn->entries_size; ++i)
     {
-      struct store *store = open->dev->store;
-      store_offset_t size = store->size;
+      err = dev_sync (netfs_root_node->nn->entries[i]->nn->dev, wait);
+      if (err)
+        {
+          debug ("dev_sync (netfs_root_node->nn->entries[%d]->nn->dev)"
+                 " return err: %d\n", i, err);
+          break;
+        }
+    }
 
-      if (store->block_size > 1)
-	st->st_blksize = store->block_size;
+  debug ("netfs_attempt_syncfs return: %d\n", err);
+  return err;
+}
+
+/* Initialize a PedDevice using SOURCE.  The SOURCE will NOT be destroyed;
+   the caller created it, it is the caller's responsilbility to free it
+   after it calls ped_device_destroy.  SOURCE is not registered in Parted's
+   list of devices.  */
+PedDevice* ped_device_new_from_store (struct store *source);
 
-      st->st_size = size;
-      st->st_mode |= ((dev->inhibit_cache || store->block_size == 1)
-		      ? S_IFCHR : S_IFBLK);
+static error_t
+set_last_partition_num (struct store *store, size_t *last_partition_num)
+{
+  debug ("last_partition_num (store: %p):\n", store);
+  ped_exception_fetch_all ();
+  PedDevice *device = ped_device_new_from_store (store);
+  if (!device || !ped_device_open (device))
+    {
+      debug ("!device || !ped_device_open (device)\n");
+      debug ("set_last_partition_num return: 1\n");
+      return 1;
     }
-  else
-    /* Try and do things without an open device...  */
+
+  PedDisk *disk = ped_disk_new (device);
+  if (!disk)
     {
-      st->st_blksize = 0;
-      st->st_size = 0;
+      debug ("!disk\n");
+      if (!ped_device_close (device))
+        debug ("!ped_device_close (device)\n");
+      debug ("set_last_partition_num return: 1\n");
+      return 1;
+    }
 
-      st->st_mode |= dev->inhibit_cache ? S_IFCHR : S_IFBLK;
+  error_t err = 0;
+  *last_partition_num = ped_disk_get_last_partition_num (disk);
+  if (*last_partition_num < 0)
+    {
+      debug ("*last_partition_num < 0\n");
+      err = 1;
     }
 
-  st->st_rdev = dev->rdev;
-  if (dev_is_readonly (dev))
-    st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+  ped_disk_destroy (disk);
+  if (!ped_device_close (device))
+    debug ("!ped_device_close (device)\n");
+  ped_exception_leave_all ();
+
+  debug ("set_last_partition_num return: %d\n", err);
+  return err;
+}
+
+static inline char *
+create_node_name (const size_t num)
+{
+  char buffer[20];
+  snprintf (buffer, sizeof (buffer), "%zu", num);
+
+  return strdup (buffer);
+}
+
+static error_t
+create_partitions (void)
+{
+  debug ("create_partitions:\n");
+  struct node *dir = netfs_root_node;
+  size_t last_partition_num;
+  error_t err = set_last_partition_num (dir->nn->dev->store,
+                                        &last_partition_num);
+  if (err)
+    {
+      debug ("set_last_partition_num return err: %d\n", err);
+      debug ("create_partitions return: ENOTDIR\n");
+      return ENOTDIR;
+    }
+
+  dir->nn->entries_size = last_partition_num;
+  dir->nn->entries = malloc (last_partition_num * sizeof (struct node *));
+  if (!dir->nn->entries)
+    {
+      debug ("!dir->nn->entries\n");
+      debug ("create_partitions return: ENOMEM\n");
+      return ENOMEM;
+    }
+
+  const int flags = ((storeio_stat.readonly ? STORE_READONLY : 0)
+                     | (storeio_stat.no_fileio ? STORE_NO_FILEIO : 0));
+
+  struct store *source, *store;
+  struct node **part;
+  for (size_t i = 1; i <= last_partition_num; ++i)
+    {
+      err = store_parsed_open (storeio_stat.store_name, flags, &source);
+      if (err)
+        {
+          debug ("err in store_parsed_open\n");
+          break;
+        }
+
+      err = store_part_create (source, i, flags, &store);
+      if (err)
+        {
+          debug ("err in store_part_create\n");
+          break;
+        }
+
+      part = &dir->nn->entries[i - 1];
+      char *node_name = create_node_name (i);
+      if (!node_name)
+        {
+          debug ("!node_name\n");
+          err = ENOMEM;
+          break;
+        }
+
+      err = create_node (part, node_name, dir);
+      if (err)
+        {
+          debug ("err in create_node\n");
+          break;
+        }
+
+      err = check_dev (*part, store, flags);
+      if (err)
+        {
+          debug ("err in dev_init_from_store\n");
+          break;
+        }
+    }
+
+  debug ("create_partitions return: %d\n", err);
+  return err;
 }
 
 error_t
-trivfs_goaway (struct trivfs_control *fsys, int flags)
+netfs_attempt_lookup (struct iouser *user, struct node *dir,
+                      const char *name, struct node **np)
 {
-  struct dev *const device = fsys->hook;
-  error_t err;
-  int force = (flags & FSYS_GOAWAY_FORCE);
-  int nosync = (flags & FSYS_GOAWAY_NOSYNC);
-  struct port_class *root_port_class = fsys->protid_class;
+  debug ("netfs_attempt_lookup (user: %p, dir: %p, name: %s):\n",
+         user, dir, name);
 
-  pthread_mutex_lock (&device->lock);
+  if (!dir->nn->entries)
+    {
+      debug ("!dir->nn->entries\n");
+      if (dir != netfs_root_node)
+        {
+          debug ("dir != netfs_root_node\n");
+          *np = NULL;
+          pthread_mutex_unlock (&dir->lock);
+          debug ("netfs_attempt_lookup return: ENOTDIR\n");
+          return ENOTDIR;
+        }
+
+      error_t err = create_partitions ();
+      if (err)
+        {
+          debug ("create_partitions return err: %d\n", err);
+          *np = NULL;
+          pthread_mutex_unlock (&dir->lock);
+          debug ("netfs_attempt_lookup return: %d\n", err);
+          return err;
+        }
+    }
+  pthread_mutex_unlock (&dir->lock);
 
-  if (device->store == NULL)
-    /* The device is not actually open.
-       XXX note that exitting here nukes non-io users, like someone
-       in the middle of a stat who will get SIGLOST or something.  */
-    exit (0);
+  if (*name == '\0' || strcmp (name, ".") == 0)
+    {
+      debug ("*name == '\\0' || strcmp (name, \".\") == 0\n");
+      *np = dir;
+      pthread_mutex_lock (&dir->lock);
+      netfs_nref (*np);
+      pthread_mutex_unlock (&dir->lock);
+      debug ("netfs_attempt_lookup return: 0\n");
+      return 0;
+    }
 
-  /* Wait until all pending rpcs are done.  */
-  err = ports_inhibit_class_rpcs (root_port_class);
-  if (err == EINTR || (err && !force))
+  debug ("current_node\n");
+  struct node *current_node = NULL;
+  struct node *iter;
+  for (size_t i = 0; i < dir->nn->entries_size; ++i)
     {
-      pthread_mutex_unlock (&device->lock);
-      return err;
+      iter = dir->nn->entries[i];
+
+      if (strcmp (name, iter->nn->name) == 0)
+        {
+          current_node = iter;
+          break;
+        }
     }
 
-  if (force && nosync)
-    /* Exit with extreme prejudice.  */
-    exit (0);
-
-  if (!force && ports_count_class (root_port_class) > 0)
-    /* Still users, so don't exit.  */
-    goto busy;
-
-  if (! nosync)
-    /* Sync the device here, if necessary, so that closing it won't result in
-       any I/O (which could get hung up trying to use one of our pagers).  */
-    dev_sync (device, 1);
-
-  /* devpager_shutdown may sync the pagers as side-effect (if NOSYNC is 0),
-     so we put that first in this test.  */
-  if (dev_stop_paging (device, nosync) || force)
-    /* Bye-bye.  */
-    {
-      if (! nosync)
-	/* If NOSYNC is true, we don't close DEV, as that could cause data to
-	   be written back.  */
-	dev_close (device);
-      exit (0);
+  if (current_node)
+    {
+      debug ("current_node: %p\n", current_node);
+      debug ("current_node->nn->name: %s\n", current_node->nn->name);
+      *np = current_node;
+      pthread_mutex_lock (&dir->lock);
+      netfs_nref (*np);
+      pthread_mutex_unlock (&dir->lock);
+      debug ("netfs_attempt_lookup return: 0\n");
+      return 0;
     }
 
- busy:
-  /* Allow normal operations to proceed.  */
-  ports_enable_class (root_port_class);
-  ports_resume_class_rpcs (root_port_class);
-  pthread_mutex_unlock (&device->lock);
-
-  /* Complain that there are still users.  */
-  return EBUSY;
-}
-
-/* If this variable is set, it is called by trivfs_S_fsys_getroot before any
-   other processing takes place; if the return value is EAGAIN, normal trivfs
-   getroot processing continues, otherwise the rpc returns with that return
-   value.  */
-error_t (*trivfs_getroot_hook) (struct trivfs_control *cntl,
-				mach_port_t reply_port,
-				mach_msg_type_name_t reply_port_type,
-				mach_port_t dotdot,
-				const uid_t *uids, mach_msg_type_number_t nuids, const uid_t *gids, mach_msg_type_number_t ngids,
-				int flags,
-				retry_type *do_retry, char *retry_name,
-				mach_port_t *node, mach_msg_type_name_t *node_type)
-     = getroot_hook;
-
-/* If this variable is set, it is called every time an open happens.
-   USER and FLAGS are from the open; CNTL identifies the
-   node being opened.  This call need not check permissions on the underlying
-   node.  If the open call should block, then return EWOULDBLOCK.  Other
-   errors are immediately reflected to the user.  If O_NONBLOCK
-   is not set in FLAGS and EWOULDBLOCK is returned, then call
-   trivfs_complete_open when all pending open requests for this
-   file can complete. */
-error_t (*trivfs_check_open_hook)(struct trivfs_control *trivfs_control,
-				  struct iouser *user,
-				  int flags)
-     = check_open_hook;
-
-/* 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;
-
-/* Sync this filesystem.  */
-kern_return_t
-trivfs_S_fsys_syncfs (struct trivfs_control *cntl,
-		      mach_port_t reply, mach_msg_type_name_t replytype,
-		      int wait, int dochildren)
-{
-  struct dev *dev = cntl->hook;
-  if (dev)
-    return dev_sync (dev, wait);
-  else
-    return 0;
+  *np = NULL;
+  debug ("netfs_attempt_lookup return: ENOENT\n");
+  return ENOENT;
+}
+
+error_t
+netfs_attempt_unlink (struct iouser *user, struct node *dir, const char *name)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+                      const char *fromname, struct node *todir,
+                      const char *toname, int excl)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_mkdir (struct iouser *user, struct node *dir, const char *name,
+                     mode_t mode)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_rmdir (struct iouser *user, struct node *dir, const char *name)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_link (struct iouser *user, struct node *dir, struct node *file,
+                    const char *name, int excl)
+{
+  return EOPNOTSUPP;
+}
+
+/* We don't use this function, but we need to unlock the dir.  */
+error_t
+netfs_attempt_mkfile (struct iouser *user, struct node *dir, mode_t mode,
+                      struct node **np)
+{
+  pthread_mutex_unlock (&dir->lock);
+  return EOPNOTSUPP;
+}
+
+/* We don't use this function, but we need to unlock the dir and clear np.  */
+error_t
+netfs_attempt_create_file (struct iouser *user, struct node *dir,
+                           const char *name, mode_t mode, struct node **np)
+{
+  *np = NULL;
+  pthread_mutex_unlock (&dir->lock);
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *np,
+                              int flags, int newnode)
+{
+  debug ("netfs_check_open_permissions (user: %p, np: %p, flags: %d,"
+         " newnode: %d):\n", user, np, flags, newnode);
+
+  error_t err = 0;
+
+  if (!err && flags & O_READ)
+    {
+      err = fshelp_access (&np->nn_stat, S_IREAD, user);
+      if (err)
+        debug ("fshelp_access with S_IREAD return err: %d\n", err);
+    }
+
+  if (!err && flags & O_WRITE)
+    {
+      err = fshelp_access (&np->nn_stat, S_IWRITE, user);
+      if (err)
+        debug ("fshelp_access with S_IWRITE return err: %d\n", err);
+    }
+
+  if (!err && flags & O_EXEC)
+    {
+      err = fshelp_access (&np->nn_stat, S_IEXEC, user);
+      if (err)
+        debug ("fshelp_access with S_IEXEC return err: %d\n", err);
+    }
+
+  if (!err)
+    {
+      if (!np->nn->dev->store)
+        err = check_dev (np, NULL, flags);
+      if (err)
+        debug ("check_dev return err: %d\n", err);
+    }
+
+  debug ("netfs_check_open_permissions return: %d\n", err);
+  return err;
+}
+
+/* We don't use this function, but it has to be defined.  */
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *np, loff_t offset,
+                    size_t *len, void *data)
+{
+  return EOPNOTSUPP;
+}
+
+/* We don't use this function, but it has to be defined.  */
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *np, loff_t offset,
+                     size_t *len, const void *data)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_report_access (struct iouser *cred, struct node *np, int *types)
+{
+  return EOPNOTSUPP;
+}
+
+struct iouser *
+netfs_make_user (uid_t *uids, int nuids, uid_t *gids, int ngids)
+{
+  return NULL;
+}
+
+void
+netfs_node_norefs (struct node *np)
+{
+  return;
+}
+
+/* Returned directory entries are aligned to blocks this many bytes long.
+   Must be a power of two.  */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+
+/* Length is structure before the name + the name + '\0', all
+   padded to a four-byte alignment.  */
+#define DIRENT_LEN(name_len) \
+  ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \
+   & ~(DIRENT_ALIGN - 1))
+
+static inline int
+bump_size (size_t *size, int *count, const char *name, const int nentries,
+           const vm_size_t buffsize)
+{
+  if (nentries == -1 || *count < nentries)
+    {
+      size_t new_size = *size + DIRENT_LEN (strlen (name));
+      if (buffsize > 0 && new_size > buffsize)
+        return 0;
+
+      *size = new_size;
+      *count += 1;
+      return 1;
+    }
+
+  return 0;
+}
+
+static inline int
+add_dir_entry (char **data, const char *name, const ino_t fileno,
+               const int type, int *count, const int nentries, size_t *size)
+{
+  if (nentries == -1 || *count < nentries)
+    {
+      size_t namlen = strlen (name);
+      size_t sz = DIRENT_LEN (namlen);
+
+      if (sz > *size)
+        return 0;
+
+      *size -= sz;
+
+      struct dirent hdr;
+      hdr.d_fileno = fileno;
+      hdr.d_reclen = sz;
+      hdr.d_type = type;
+      hdr.d_namlen = namlen;
+
+      memcpy (*data, &hdr, DIRENT_NAME_OFFS);
+      strcpy (*data + DIRENT_NAME_OFFS, name);
+
+      *data += sz;
+      *count += 1;
+
+      return 1;
+    }
+
+  return 0;
+}
+
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *dir, int entry,
+                   int nentries, char **data, mach_msg_type_number_t *datacnt,
+                   vm_size_t bufsize, int *amt)
+{
+  debug ("netfs_get_dirents (cred: %p, dir: %p, entry: %d, nentries: %d, "
+         "datacnt: %u, bufsize: %u, amt: %d)\n", cred, dir, entry, nentries,
+         *datacnt, bufsize, *amt);
+
+  if (!dir->nn->entries)
+    {
+      debug ("!dir->nn->entries\n");
+      if (dir != netfs_root_node)
+        {
+          debug ("dir != netfs_root_node\n");
+          debug ("netfs_get_dirents return: ENOTDIR\n");
+          return ENOTDIR;
+        }
+
+      error_t err = create_partitions ();
+      if (err)
+        {
+          debug ("err in create_partitions\n");
+          debug ("netfs_get_dirents return: %d\n", err);
+          return err;
+        }
+    }
+
+  if (dir->nn->entries_size + 2 <= entry)
+    {
+      debug ("dir->nn->entries_size + 2 <= entry\n");
+      *datacnt = 0;
+      *amt = 0;
+      *data = NULL;
+      debug ("netfs_get_dirents return: 0\n");
+      return 0;
+    }
+
+  int count = 0;
+  size_t size = 0;
+
+  if (entry == 0)
+    bump_size (&size, &count, ".", nentries, bufsize);
+  if (entry <= 1)
+    bump_size (&size, &count, "..", nentries, bufsize);
+
+  struct node *current_node;
+  for (size_t i = 0; i < dir->nn->entries_size; ++i)
+    {
+      current_node = dir->nn->entries[i];
+      bump_size (&size, &count, current_node->nn->name, nentries, bufsize);
+    }
+
+  void *new_data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+  if (new_data == MAP_FAILED)
+    {
+      debug ("new_data == MAP_FAILED\n");
+      debug ("netfs_get_dirents return: %d\n", errno);
+      return errno;
+    }
+
+  *data = (char *) new_data;
+  *datacnt = size;
+  *amt = count;
+
+  count = 0;
+  char *ptr_data = *data;
+
+  if (entry == 0)
+    {
+      debug ("entry == 0\n");
+      add_dir_entry (&ptr_data, ".", dir->nn_stat.st_ino, DT_DIR, &count,
+                     nentries, &size);
+    }
+  if (entry <= 1)
+    {
+      debug ("entry <= 1\n");
+      add_dir_entry (&ptr_data, "..", 2, DT_DIR, &count, nentries, &size);
+    }
+
+  debug ("Fill in the real directory entries\n");
+
+  int dirent_type;
+  for (size_t i = 0; i < dir->nn->entries_size; ++i)
+    {
+      current_node = dir->nn->entries[i];
+      if (current_node->nn->dev->store->block_size == 1)
+        dirent_type = DT_CHR;
+      else
+        dirent_type = DT_BLK;
+
+      add_dir_entry (&ptr_data, current_node->nn->name,
+                     current_node->nn_stat.st_ino, dirent_type, &count,
+                     nentries, &size);
+    }
+
+  debug ("netfs_get_dirents return: 0\n");
+  return 0;
 }
-- 
2.43.0

Reply via email to