Hello,

On Tue, 23 Jun 2026 02:03:44 Samuel Thibault wrote:
> Yes, that looks confusing to me. To the user, it would mean that
> partition 1 itself contains a partition table.
>
> Can't we disable the directory facet when there is no actual partition
> table in the store being driven?

How can this be verified? The thing is, even after passing a specific
partition to the translator, no errors occur when reading its partitions.
Libparted simply thinks it's working with a disk with only one partition.
Getting data from struct store_parsed doesn't seem quite right, but I can't
think of another way.

> Well, yes, but I meant that netfs_init could be made to call with
> use_mach_dev=1 first, that would succeed. What I'm surprised is why
> libdiskfs doesn't have the issue.

Yes, that helped. Surely this can't negatively impact the work of other
translators? In any case, the system starts up and works as usual...

> Having a single-chained open_list is however problematic for efficiency.

Agreed, I reworked this place and now there is something like std::vector
from C++.

> But actually, why separating struct open from netfs' struct peropen?
> netfs' peropen already has the filepointer field for the offset. In
> po->np->nn->dev you have the dev. And libnetfs usually uses po->np->lock
> as lock. You need really only nperopens to know whether the store is
> active.

Yes, we were able to remove struct open by using only struct peropen.
Furthermore, this significantly reduced the code in io.c.

> I'm also wondering if we want to plug at the netfs_S_io_read layer, or
> rather at the netfs_attempt_read layer, since the latter would probably
> be simpler? And similar for other operations.

The dev_read and dev_write functions contain code similar to netfs_S_io_read
and netfs_S_io_write. I think we could try removing it and using
netfs_attempt_read and netfs_attempt_write instead, but storeio/pager.c also
handles dev_read and dev_write, so it would need to be modified as well. If
it's worth it, I might give it a try.

Thanks,

--
Mikhail Karpov
From 834e453cedbdb21a6aba69dccabd9fcc0b1f9675 Mon Sep 17 00:00:00 2001
From: Mikhail Karpov <[email protected]>
Date: Fri, 3 Jul 2026 19:06:38 +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              |   67 +-
 storeio/dev.h              |   83 ++-
 storeio/io.c               |  454 +++++--------
 storeio/netfs.h            |   42 ++
 storeio/open.c             |  127 ----
 storeio/open.h             |   68 --
 storeio/pager.c            |    3 +-
 storeio/storeio.c          | 1268 ++++++++++++++++++++++++++++--------
 15 files changed, 1355 insertions(+), 827 deletions(-)
 create mode 100644 libnetfs/priv.c
 create mode 100644 storeio/netfs.h
 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..4557fc83 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,23 @@ 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.  */
+  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 +164,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);
@@ -192,6 +175,32 @@ dev_open (struct dev *dev)
 
   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 +210,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 +231,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 +368,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 +461,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..9a7e12c6 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
@@ -24,46 +24,22 @@
 #include <device/device.h>
 #include <pthread.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'.  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 +60,39 @@ struct dev
   pthread_mutex_t pager_lock;
 };
 
+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 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;
+};
+
+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.
@@ -124,4 +125,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..8047d545 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 "netfs.h"
+#include "libnetfs/fs_S.h"
+#include "libnetfs/io_S.h"
 
-#include "open.h"
-#include "dev.h"
-#include "libtrivfs/trivfs_fs_S.h"
-#include "libtrivfs/trivfs_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,108 @@
    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)
+  if (!cred)
     return EOPNOTSUPP;
-  else if (! (cred->po->openmodes & (O_READ|O_WRITE)))
+
+  if (!(cred->po->openstat & (O_READ | O_WRITE)))
     return EBADF;
-  else
-    {
-      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);
 
-      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;
+  *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 (node->nn->dev, prot, &memobj);
+  if (err)
+    {
+      pthread_mutex_unlock (&node->lock);
       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);
+
+  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)
 {
-  error_t err;
-  size_t data_size = *data_len;
-
-  if (! cred)
+  if (!cred)
     return EOPNOTSUPP;
-  else if (! (cred->po->openmodes & O_READ))
+
+  if (!(cred->po->openstat & O_READ))
     return EBADF;
 
-  err = open_read ((struct open *)cred->po->hook,
-		   offs, amount, (void **)data, &data_size);
-  if (err)
-    return err;
-  *data_len = data_size;
-  return 0;
+  struct node *node = cred->po->np;
+  pthread_mutex_lock (&node->lock);
+
+  size_t data_size = *datalen;
+
+  error_t err;
+  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);
+
+  pthread_mutex_unlock (&node->lock);
+  *datalen = data_size;
+
+  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)
+  if (!cred)
     return EOPNOTSUPP;
-  else if (! (cred->po->openmodes & O_READ))
+
+  if (!(cred->po->openstat & O_READ))
     return EBADF;
-  else
-    {
-      struct open *open = (struct open *)cred->po->hook;
-      *amount = open->dev->store->size - open->offs;
-      return 0;
-    }
+
+  struct node *node = cred->po->np;
+  pthread_mutex_lock (&node->lock);
+  error_t err = netfs_validate_stat (node, cred->user);
+  if (!err)
+    *amount = node->nn->dev->store->size - cred->po->filepointer;
+
+  pthread_mutex_unlock (&node->lock);
+
+  return err;
 }
 
 /* Write data to an IO object.  If offset is -1, write at the object
@@ -130,256 +146,102 @@ 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)
+  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;
-}
 
-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))
+    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);
 
-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)
-{
-  if (! cred)
-    return EOPNOTSUPP;
-  else
-    return 0;
-}
-
-/* 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)
-{
-  if (! cred)
-    return EOPNOTSUPP;
-  else
-    {
-      struct open *open = (struct open *)cred->po->hook;
-      *owner = open->dev->owner; /* atomic word fetch */
-      return 0;
-    }
+  return err;
 }
 
-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)
+static inline int
+is_privileged (const struct idvec *uids)
 {
-  if (! cred)
-    return EOPNOTSUPP;
-  else
-    {
-      struct open *open = (struct open *)cred->po->hook;
-      open->dev->owner = owner;	/* atomic word store */
-      return 0;
-    }
+  return idvec_contains (uids, 0) || idvec_contains (uids, getuid ());
 }
-
-/* 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)
+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 dev_sync (((struct open *)cred->po->hook)->dev, wait);
-  else
+  if (!cred)
     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;
+  struct dev *dev = np->nn->dev;
+  struct store *store = dev->store;
+  if (storeio_stat.enforced && !(store->flags & STORE_ENFORCED))
+    {
+      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;
+      return 0;
+    }
 
-  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);
-	    }
-	}
+      struct store *clone;
+      err = store_clone (store, &clone);
+      if (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);
+
+  return err;
 }
diff --git a/storeio/netfs.h b/storeio/netfs.h
new file mode 100644
index 00000000..3b33fce2
--- /dev/null
+++ b/storeio/netfs.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2026 Free Software Foundation
+   Written by Mikhail Karpov.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __NETFS_H__
+#define __NETFS_H__
+
+#include <hurd/netfs.h>
+
+#include "dev.h"
+
+struct opens
+{
+  struct peropen **opens;
+  size_t size;
+  size_t capacity;
+};
+
+struct netnode
+{
+  struct dev *dev;
+  struct opens *opens;
+  char *name;
+  struct node **entries;
+  size_t entries_size;
+};
+
+#endif /* !__NETFS_H__ */
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..1282b31b 100644
--- a/storeio/pager.c
+++ b/storeio/pager.c
@@ -27,6 +27,7 @@
 #include <error.h>
 #include <sys/mman.h>
 #include <stdio.h>
+#include <string.h>
 
 #include "dev.h"
 
@@ -223,7 +224,7 @@ dev_get_memory_object (struct dev *dev, vm_prot_t prot, memory_object_t *memobj)
 {
   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;
 
diff --git a/storeio/storeio.c b/storeio/storeio.c
index 4e8a9628..f93b8cd4 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 "netfs.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,153 @@ 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;
+
+  struct opens *opens;
+  opens = malloc (sizeof (struct opens));
+  if (!opens)
+    {
+      debug ("!opens\n");
+      free (netnode);
+      free (new_node);
+      free (dev);
+      debug ("create_node return: ENOMEM\n");
+      return ENOMEM;
+    }
+
+  opens->opens = NULL;
+  opens->size = 0;
+  opens->capacity = 0;
+  new_node->nn->opens = opens;
+
+  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, name: %s\n", new_node, new_node->nn->name);
+  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 +226,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 +252,939 @@ 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, O_READ);
+  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;
+  if (dev_is_readonly (node->nn->dev) && (flags & O_WRITE))
+    {
+      debug ("check_dev (node: %p, store: %p, flags: %d):\n",
+             node, store, flags);
+      debug ("dev_is_readonly (node->nn->dev) && (flags & O_WRITE)\n");
+      debug ("check_dev return: EROFS\n");
+      return EROFS;
+    }
 
+  struct dev *dev = node->nn->dev;
   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;
+      error_t err;
+
+      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 ("check_dev (node: %p, store: %p, flags: %d):\n",
+                     node, store, flags);
+              debug ("dev open return error, but we are not opening the file"
+                     " for reading or writing, just ignore the error\n");
+            }
+          else
+            {
+              debug ("check_dev (node: %p, store: %p, flags: %d):\n",
+                     node, store, flags);
+              debug ("dev open return err: %d\n", err);
+              pthread_mutex_unlock (&dev->lock);
+              debug ("check_dev return: %d\n", err);
+              return 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);
 
+  return 0;
+}
+
+static error_t
+node_open_create (struct peropen *po)
+{
+  struct netnode *netnode = po->np->nn;
+
+  pthread_mutex_lock (&netnode->dev->lock);
+  struct opens *opens = netnode->opens;
+  if (opens->capacity == opens->size)
+    {
+      if (opens->size == 0)
+        {
+          error_t err = store_clear_flags (netnode->dev->store,
+                                           STORE_INACTIVE);
+          if (err)
+            {
+              debug ("node_open_create (po: %p):\n", po);
+              debug ("store_clear_flags return err: %d\n", err);
+              pthread_mutex_unlock (&netnode->dev->lock);
+              debug ("node_open_create return: %d\n", err);
+              return err;
+            }
+        }
+
+      void *new_opens = realloc (opens->opens, (opens->size + 1)
+                                                * sizeof (struct peropen *));
+      if (!new_opens)
+        {
+          debug ("node_open_create (po: %p):\n", po);
+          debug ("!new_opens\n");
+          pthread_mutex_unlock (&netnode->dev->lock);
+          debug ("node_open_create return: ENOMEM\n");
+          return ENOMEM;
+        }
+
+      opens->opens = new_opens;
+      opens->opens[opens->size] = malloc (sizeof (struct peropen));
+      if (!opens->opens[opens->size])
+        {
+          debug ("node_open_create (po: %p):\n", po);
+          debug ("!opens->opens[opens->size: %d]\n", opens->size);
+          pthread_mutex_unlock (&netnode->dev->lock);
+          debug ("node_open_create return: ENOMEM\n");
+          return ENOMEM;
+        }
+
+      ++opens->capacity;
+    }
+
+  opens->opens[opens->size] = po;
+  ++opens->size;
+  pthread_mutex_unlock (&netnode->dev->lock);
+
+  return 0;
+}
+
+error_t (*netfs_peropen_create_hook) (struct peropen *po) = node_open_create;
+
+static void
+node_open_destroy (struct peropen *po)
+{
+  struct netnode *netnode = po->np->nn;
+  pthread_mutex_lock (&netnode->dev->lock);
+  struct opens *opens = netnode->opens;
+  for (size_t i = 0; i < opens->size; ++i)
+    if (opens->opens[i] == po)
+      {
+        opens->opens[i] = opens->opens[opens->size - 1];
+        --opens->size;
+        if (opens->size == 0)
+          if (store_clear_flags (netnode->dev->store, STORE_INACTIVE))
+            {
+              debug ("node_open_destroy (po: %p):\n", po);
+              debug ("store_clear_flags return error, but we can't"
+                     " return it, so we ignore this error'\n");
+            }
+
+        break;
+      }
+
+  pthread_mutex_unlock (&netnode->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);
+}
+
+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)
+    {
+      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 (netfs_root_node->nn->dev, nosync) || force))
+    {
+      debug ("!err && (dev_stop_paging (netfs_root_node, 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;
+}
+
+error_t
+netfs_validate_stat (struct node *np, struct iouser *cred)
+{
+  return 0;
+}
+
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *np, uid_t uid,
+                     uid_t gid)
+{
+  return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *np, uid_t author)
+{
+  return EOPNOTSUPP;
+}
+
+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)
+{
+  error_t err = dev_sync (np->nn->dev, wait);
+  if (err)
+    {
+      debug ("netfs_attempt_sync (cred: %p, np: %p, wait: %d):\n",
+             cred, np, wait);
+      debug ("netfs_attempt_sync return: %d\n", err);
+    }
+
   return err;
 }
 
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+  error_t err = dev_sync (netfs_root_node->nn->dev, wait);
+  if (err)
+    {
+      debug ("netfs_attempt_syncfs (cred: %p, wait: %d):\n", cred, wait);
+      debug ("dev_sync (netfs_root_node->nn->dev) return err: %d\n", err);
+      debug ("netfs_attempt_syncfs return: %d\n", err);
+      return err;
+    }
+
+  for (size_t i = 0; i < netfs_root_node->nn->entries_size; ++i)
+    {
+      err = dev_sync (netfs_root_node->nn->entries[i]->nn->dev, wait);
+      if (err)
+        {
+          debug ("netfs_attempt_syncfs (cred: %p, wait: %d):\n", cred, wait);
+          debug ("dev_sync (netfs_root_node->nn->entries[%d]->nn->dev)"
+                 " return err: %d\n", i, err);
+          debug ("netfs_attempt_syncfs return: %d\n", err);
+          break;
+        }
+    }
+
+  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);
+
 static error_t
-open_hook (struct trivfs_peropen *peropen)
+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;
+    }
+
+  PedDisk *disk = ped_disk_new (device);
+  if (!disk)
+    {
+      debug ("!disk\n");
+      if (!ped_device_close (device))
+        debug ("!ped_device_close (device)\n");
+      debug ("set_last_partition_num return: 1\n");
+      return 1;
+    }
+
   error_t err = 0;
-  struct dev *const dev = peropen->cntl->hook;
+  *last_partition_num = ped_disk_get_last_partition_num (disk);
+  if (*last_partition_num < 0)
+    {
+      debug ("*last_partition_num < 0\n");
+      err = 1;
+    }
+
+  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 ("err in set_last_partition_num\n");
+      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));
 
-  if (dev->store)
+  struct store *source, *store;
+  struct node **part;
+  for (size_t i = 1; i <= last_partition_num; ++i)
     {
-      pthread_mutex_lock (&dev->lock);
-      if (dev->nperopens++ == 0)
-	err = store_clear_flags (dev->store, STORE_INACTIVE);
-      pthread_mutex_unlock (&dev->lock);
-      if (!err)
-	err = open_create (dev, (struct open **)&peropen->hook);
+      err = store_parsed_open (storeio_stat.store_name, flags, &source);
+      if (err)
+        {
+          debug ("store_parsed_open return err: %d\n", err);
+          break;
+        }
+
+      err = store_part_create (source, i, flags, &store);
+      if (err)
+        {
+          debug ("store_part_create return err: %d\n", err);
+          break;
+        }
+
+      part = &dir->nn->entries[i - 1];
+      err = create_node (part, create_node_name (i), dir);
+      if (err)
+        {
+          debug ("create_node return err: %d\n", err);
+          break;
+        }
+
+      err = check_dev (*part, store, flags);
+      if (err)
+        {
+          debug ("dev_init_from_store return err: %d\n", err);
+          break;
+        }
     }
+
+  debug ("create_partitions return: %d\n", err);
   return err;
 }
 
-static void
-close_hook (struct trivfs_peropen *peropen)
+error_t
+netfs_attempt_lookup (struct iouser *user, struct node *dir,
+                      const char *name, struct node **np)
 {
-  struct dev *const dev = peropen->cntl->hook;
+  if (!dir->nn->entries)
+    {
+      if (dir != netfs_root_node)
+        {
+          debug ("netfs_attempt_lookup (user: %p, dir: %p, name: %s):\n",
+                 user, dir, name);
+          debug ("!dir->nn->entries\n");
+          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 ("netfs_attempt_lookup (user: %p, dir: %p, name: %s):\n",
+                 user, dir, name);
+          debug ("!dir->nn->entries\n");
+          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 (*name == '\0' || strcmp (name, ".") == 0)
+    {
+      *np = dir;
+      pthread_mutex_lock (&dir->lock);
+      netfs_nref (*np);
+      pthread_mutex_unlock (&dir->lock);
+      return 0;
+    }
+
+  struct node *current_node = NULL;
+  struct node *iter;
+  for (size_t i = 0; i < dir->nn->entries_size; ++i)
+    {
+      iter = dir->nn->entries[i];
 
-  if (peropen->hook)
+      if (strcmp (name, iter->nn->name) == 0)
+        {
+          current_node = iter;
+          break;
+        }
+    }
+
+  if (current_node)
     {
-      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);
+      *np = current_node;
+      pthread_mutex_lock (&dir->lock);
+      netfs_nref (*np);
+      pthread_mutex_unlock (&dir->lock);
+      return 0;
     }
+
+  *np = NULL;
+  debug ("netfs_attempt_lookup (user: %p, dir: %p, name: %s):\n",
+          user, dir, name);
+  debug ("netfs_attempt_lookup return: ENOENT\n");
+  return ENOENT;
 }
-
-/* ---------------------------------------------------------------- */
-/* Trivfs hooks  */
 
-int trivfs_fstype = FSTYPE_DEV;
-int trivfs_fsid = 0;
+error_t
+netfs_attempt_unlink (struct iouser *user, struct node *dir, const char *name)
+{
+  return EOPNOTSUPP;
+}
 
-int trivfs_support_read = 1;
-int trivfs_support_write = 1;
-int trivfs_support_exec = 0;
+error_t
+netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+                      const char *fromname, struct node *todir,
+                      const char *toname, int excl)
+{
+  return EOPNOTSUPP;
+}
 
-int trivfs_allow_open = O_READ | O_WRITE;
+error_t
+netfs_attempt_mkdir (struct iouser *user, struct node *dir, const char *name,
+                     mode_t mode)
+{
+  return EOPNOTSUPP;
+}
 
-void
-trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+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)
 {
-  struct dev *const dev = cred->po->cntl->hook;
-  struct open *open = cred->po->hook;
+  *np = NULL;
+  pthread_mutex_unlock (&dir->lock);
+  return EOPNOTSUPP;
+}
 
-  st->st_mode &= ~S_IFMT;
+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)
+{
+  error_t err = 0;
+
+  if (!err && flags & O_READ)
+    {
+      err = fshelp_access (&np->nn_stat, S_IREAD, user);
+      if (err)
+        {
+          debug ("netfs_check_open_permissions (user: %p, np: %p, flags: %d,"
+                 " newnode: %d):\n", user, np, flags, newnode);
+          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 ("netfs_check_open_permissions (user: %p, np: %p, flags: %d,"
+                 " newnode: %d):\n", user, np, flags, newnode);
+          debug ("fshelp_access with S_IWRITE return err: %d\n", err);
+        }
+    }
 
-  if (open)
-    /* An open device.  */
+  if (!err && flags & O_EXEC)
     {
-      struct store *store = open->dev->store;
-      store_offset_t size = store->size;
+      err = fshelp_access (&np->nn_stat, S_IEXEC, user);
+      if (err)
+        {
+          debug ("netfs_check_open_permissions (user: %p, np: %p, flags: %d,"
+                 " newnode: %d):\n", user, np, flags, newnode);
+          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 ("netfs_check_open_permissions (user: %p, np: %p,"
+                   " flags: %d, newnode: %d):\n", user, np, flags, newnode);
+            debug ("check_dev return err: %d\n", err);
+          }
+      }
+
+  if (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;
+}
 
-      if (store->block_size > 1)
-	st->st_blksize = store->block_size;
+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;
 
-      st->st_size = size;
-      st->st_mode |= ((dev->inhibit_cache || store->block_size == 1)
-		      ? S_IFCHR : S_IFBLK);
+      *size = new_size;
+      *count += 1;
+      return 1;
     }
-  else
-    /* Try and do things without an open device...  */
+
+  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)
     {
-      st->st_blksize = 0;
-      st->st_size = 0;
+      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;
 
-      st->st_mode |= dev->inhibit_cache ? S_IFCHR : S_IFBLK;
+      return 1;
     }
 
-  st->st_rdev = dev->rdev;
-  if (dev_is_readonly (dev))
-    st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+  return 0;
 }
 
 error_t
-trivfs_goaway (struct trivfs_control *fsys, int flags)
+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)
 {
-  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;
+  if (!dir->nn->entries)
+    {
+      if (dir != netfs_root_node)
+        {
+          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);
+          debug ("!dir->nn->entries\n");
+          debug ("dir != netfs_root_node\n");
+          debug ("netfs_get_dirents return: ENOTDIR\n");
+          return ENOTDIR;
+        }
+
+      error_t err = create_partitions ();
+      if (err)
+        {
+          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);
+          debug ("!dir->nn->entries\n");
+          debug ("create_partitions return err: %d\n", err);
+          debug ("netfs_get_dirents return: %d\n", err);
+          return err;
+        }
+    }
 
-  pthread_mutex_lock (&device->lock);
+  if (dir->nn->entries_size + 2 <= entry)
+    {
+      *datacnt = 0;
+      *amt = 0;
+      *data = NULL;
+      return 0;
+    }
 
-  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);
+  int count = 0;
+  size_t size = 0;
 
-  /* Wait until all pending rpcs are done.  */
-  err = ports_inhibit_class_rpcs (root_port_class);
-  if (err == EINTR || (err && !force))
+  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)
     {
-      pthread_mutex_unlock (&device->lock);
-      return err;
+      current_node = dir->nn->entries[i];
+      if (!bump_size (&size, &count, current_node->nn->name, nentries,
+                      bufsize))
+        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);
+  void *new_data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+  if (new_data == MAP_FAILED)
+    {
+      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);
+      debug ("new_data == MAP_FAILED\n");
+      debug ("netfs_get_dirents return: %d\n", errno);
+      return errno;
     }
 
- 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;
+  *data = (char *) new_data;
+  *datacnt = size;
+  *amt = count;
+
+  count = 0;
+  char *ptr_data = *data;
+
+  if (entry == 0)
+    add_dir_entry (&ptr_data, ".", dir->nn_stat.st_ino, DT_DIR, &count,
+                   nentries, &size);
+
+  if (entry <= 1)
+    add_dir_entry (&ptr_data, "..", 2, DT_DIR, &count, nentries, &size);
+
+  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;
+
+      if (!add_dir_entry (&ptr_data, current_node->nn->name,
+                          current_node->nn_stat.st_ino, dirent_type, &count,
+                          nentries, &size))
+        break;
+    }
+
+  return 0;
 }
-- 
2.43.0

Reply via email to