Hello everyone!
This is my attempt to port the storeio translator from libtrivfs to libnetfs.
Currently, the system boots using this modified translator, but there are a
number of strange errors and misunderstandings, so I'd like to hear your
opinions on it.
Currently, the storeio translator allows you to work with both the entire disk
and a specific partition. Let's say we have a disk with three partitions:
settrans -c storeio /hurd/storeio -w -T typed file:/root/disk.img
This will create a translator directory, and when accessed, it will open the
disk and create the necessary store. However, when accessing any partition, for example, stat storeio/1 or calling the ls storeio command, the translator will
open each partition and create their nodes starting from 1.
In this case, to enable the ext2fs translator to work with storeio by opening
partition 2, you need to call:
settrans -c ext /hurd/ext2fs -w -T typed part:2:file:/root/storeio
or
settrans -c ext /hurd/ext2fs -w -T typed file:/root/storeio/2
If storeio itself is created with the type part:2:file:/root/disk.img, then it
is sufficient to
settrans -c ext /hurd/ext2fs -w -T typed file:/root/storeio
It should be noted that regardless of whether you need to work with the entire disk or a specific partition, the internal structure of storeio will be created
upon request (in the case of working with a single partition, The directory
will only have node 1). Perhaps in the second case, this behavior is
undesirable, but I haven't found a simple way to prevent it. Most likely, I'll
need to move struct store_parsed out of libstore/argp.c and work with the
user-supplied command.
I also found a strange bug when working with maptime_map. Calling this function
during system boot breaks the boot process, but after boot, it doesn't cause
any problems (the problem was discovered when I replaced the regular trivfs
storeio with this one for the entire system).
There are also several functions that I don't understand how to migrate
correctly: trivfs_goaway, trivfs_modify_stat, close_hook, and
trivfs_S_fsys_syncfs. Am I correct in assuming that these manipulations need to
be performed on each open dev?
Overall, these are the results so far. Any opinions on this? :)

--
Mikhail Karpov
From 800c330ab12f4696710484e304e84ff67bf3e92d Mon Sep 17 00:00:00 2001
From: Mikhail Karpov <[email protected]>
Date: Wed, 3 Jun 2026 18:39:40 +0700
Subject: [PATCH] Migrating storeio from trivfs to netfs

---
 Makefile             |    6 +-
 libnetfs/init-init.c |    4 +-
 storeio/Makefile     |   13 +-
 storeio/dev.c        |   69 +--
 storeio/dev.h        |   67 ++-
 storeio/io.c         |  385 ----------------
 storeio/netfs.c      | 1046 ++++++++++++++++++++++++++++++++++++++++++
 storeio/netfs.h      |   36 ++
 storeio/open.c       |    2 +-
 storeio/pager.c      |    3 +-
 storeio/storeio.c    |  419 +++++------------
 11 files changed, 1295 insertions(+), 755 deletions(-)
 delete mode 100644 storeio/io.c
 create mode 100644 storeio/netfs.c
 create mode 100644 storeio/netfs.h

diff --git a/Makefile b/Makefile
index c51e8c1..9d2968e 100644
--- a/Makefile
+++ b/Makefile
@@ -37,7 +37,7 @@ lib-subdirs = libshouldbeinlibc libihash libiohelp libports \
 # Hurd programs
 prog-subdirs = auth proc exec term \
 	       ext2fs isofs tmpfs fatfs \
-	       storeio pflocal pfinet defpager mach-defpager \
+	       pflocal pfinet defpager mach-defpager \
 	       login daemons boot console \
 	       hostmux usermux ftpfs trans \
 	       console-client utils sutils libfshelp-tests \
@@ -70,6 +70,10 @@ ifeq ($(HAVE_LIBACPICA),yes)
 prog-subdirs += acpi
 endif
 
+ifneq ($(PARTED_LIBS),)
+prog-subdirs += storeio
+endif
+
 # Other directories
 other-subdirs = hurd doc config release include
 
diff --git a/libnetfs/init-init.c b/libnetfs/init-init.c
index 19ed0d3..248c1a7 100644
--- a/libnetfs/init-init.c
+++ b/libnetfs/init-init.c
@@ -38,11 +38,11 @@ void
 netfs_init (void)
 {
   error_t err;
-  err = maptime_map (0, 0, &netfs_mtime);
+  /*err = maptime_map (0, 0, &netfs_mtime);
   if (err)
     err = maptime_map (1, 0, &netfs_mtime);
   if (err)
-    error (2, err, "mapping time");
+    error (2, err, "mapping time");*/
 
   netfs_protid_class = ports_create_class (netfs_release_protid, 0);
   netfs_control_class = ports_create_class (0, 0);
diff --git a/storeio/Makefile b/storeio/Makefile
index 5d9aeb4..8e9ed2e 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 open.c pager.c netfs.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 c87400c..84d0108 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_init_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_init_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 eda7a93..7faec42 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,25 +24,18 @@
 #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
@@ -59,11 +52,6 @@ struct dev
      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 +72,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_init_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 +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
deleted file mode 100644
index dca997e..0000000
--- a/storeio/io.c
+++ /dev/null
@@ -1,385 +0,0 @@
-/* The hurd io interface to storeio
-
-   Copyright (C) 1995,96,97,99,2000,02 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/trivfs.h>
-#include <stdio.h>
-#include <fcntl.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
-   where read data and write data are the same, these objects will be
-   equal, otherwise they will be disjoint.  Servers are permitted to
-   implement io_map but not io_map_cntl.  Some objects do not provide
-   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)
-{
-  if (! cred)
-    return EOPNOTSUPP;
-  else if (! (cred->po->openmodes & (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;
-
-      return err;
-    }
-}
-
-/* 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)
-{
-  error_t err;
-  size_t data_size = *data_len;
-
-  if (! cred)
-    return EOPNOTSUPP;
-  else if (! (cred->po->openmodes & 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;
-}
-
-/* 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)
-{
-  if (! cred)
-    return EOPNOTSUPP;
-  else if (! (cred->po->openmodes & O_READ))
-    return EBADF;
-  else
-    {
-      struct open *open = (struct open *)cred->po->hook;
-      *amount = open->dev->store->size - open->offs;
-      return 0;
-    }
-}
-
-/* Write data to an IO object.  If offset is -1, write at the object
-   maintained file pointer.  If the object is not seekable, offset is
-   ignored.  The amount successfully written is returned in amount.  A
-   given user should not have more than one outstanding io_write on an
-   object at a time; servers implement congestion control by delaying
-   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)
-{
-  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);
-}
-
-/* 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. */
-
-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
-    {
-      *bits = cred->po->openmodes;
-      return 0;
-    }
-}
-
-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;
-}
-
-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;
-}
-
-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;
-    }
-}
-
-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
-    {
-      struct open *open = (struct open *)cred->po->hook;
-      open->dev->owner = owner;	/* atomic word store */
-      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;
-      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);
-	    }
-	}
-      else
-	err = store_return (store,
-			    ports, num_ports, ints, num_ints,
-			    offsets, num_offsets, data, data_len);
-
-      return err;
-    }
-}
diff --git a/storeio/netfs.c b/storeio/netfs.c
new file mode 100644
index 0000000..d1b60dd
--- /dev/null
+++ b/storeio/netfs.c
@@ -0,0 +1,1046 @@
+/* storeio interface to libnetfs
+
+   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
+   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/netfs.h>
+#include <dirent.h>
+#include <parted/parted.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "netfs.h"
+#include "libnetfs/fs_S.h"
+#include "libnetfs/io_S.h"
+
+error_t
+create_node (struct node **node, char *name, struct node *dir)
+{
+  debug ("create_node:\n");
+  struct netnode *netnode = malloc (sizeof (struct netnode));
+  if (!netnode)
+    return ENOMEM;
+
+  struct node *new_node = netfs_make_node (netnode);
+  if (!new_node)
+    {
+      free (netnode);
+      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;
+  new_node->nn->name = name;
+  new_node->nn->open = NULL;
+  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 ("create_node end with 0\n");
+  return 0;
+}
+
+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 EOPNOTSUPP;
+}
+
+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)
+{
+  return 0;
+}
+
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+  return 0;
+}
+
+/* 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
+set_last_partition_num (struct store *store, size_t *last_partition_num)
+{
+  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 end with err: 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 end with err: 1\n");
+      return 1;
+    }
+
+  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;
+    }
+
+  ped_disk_destroy (disk);
+  if (!ped_device_close (device))
+    debug ("!ped_device_close (device)\n");
+  ped_exception_leave_all ();
+
+  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
+set_node_open (struct node *node, struct store *store)
+{
+  struct dev *dev = malloc (sizeof (struct dev));
+  if (!dev)
+    return ENOMEM;
+
+  struct open *open;
+  error_t err = open_create (dev, &open);
+  if (err)
+    {
+      free (dev);
+      return err;
+    }
+
+  if (store)
+    err = dev_init_from_store (dev, store);
+  else
+    err = dev_open (dev, storeio_stat.store_name);
+
+  const int flags = ((storeio_stat.readonly ? STORE_READONLY : 0)
+                     | (storeio_stat.no_fileio ? STORE_NO_FILEIO : 0));
+
+  if (err && (flags & (O_READ|O_WRITE)) == 1)
+    {
+      free (dev);
+      free (open);
+      return err;
+    }
+
+  pthread_mutex_lock (&dev->lock);
+  if (dev->nperopens++ == 0)
+    err = store_clear_flags (dev->store, STORE_INACTIVE);
+  pthread_mutex_unlock (&dev->lock);
+
+  node->nn->open = open;
+  node->nn_stat.st_size = open->dev->store->size;
+  pthread_rwlock_init (&open->dev->io_lock, NULL);
+
+  if (node != netfs_root_node)
+    {
+      if (open->dev->store->block_size == 1)
+        node->nn_stat.st_mode |= S_IFCHR;
+      else if (open->dev->store->block_size > 1)
+        {
+          node->nn_stat.st_mode |= S_IFBLK;
+          node->nn_stat.st_blksize = open->dev->store->block_size;
+        }
+    }
+  else
+    if (open->dev->store->block_size > 1)
+      node->nn_stat.st_blksize = open->dev->store->block_size;
+
+  return 0;
+}
+
+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->open->dev->store,
+                                        &last_partition_num);
+  if (err)
+    {
+      debug ("err in set_last_partition_num\n");
+      debug ("create_partitions end with ENOTDIR\n");
+      return ENOTDIR;
+    }
+
+  struct store *source, *store;
+  const int flags = ((storeio_stat.readonly ? STORE_READONLY : 0)
+                     | (storeio_stat.no_fileio ? STORE_NO_FILEIO : 0));
+
+  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 end with ENOMEM\n");
+      return ENOMEM;
+    }
+
+  struct node **part;
+  for (size_t i = 1; i <= last_partition_num; ++i)
+    {
+      part = &dir->nn->entries[i - 1];
+      err = store_parsed_open (storeio_stat.store_name, flags, &source);
+      if (err)
+        {
+          debug ("err in store_parsed_open\n");
+          debug ("create_partitions end with ENOTDIR\n");
+          return ENOTDIR;
+        }
+
+      err = store_part_create (source, i, flags, &store);
+      if (err)
+        {
+          debug ("err in store_part_create\n");
+          debug ("create_partitions end with ENOTDIR\n");
+          return ENOTDIR;
+        }
+
+      err = create_node (part, create_node_name (i), dir);
+      if (err)
+        {
+          debug ("err in create_node\n");
+          debug ("create_partitions end with err: %d\n", err);
+          return err;
+        }
+
+      err = set_node_open (*part, store);
+      if (err)
+        {
+          debug ("err in dev_init_from_store\n");
+          debug ("create_partitions end with err: %d\n", err);
+          return err;
+        }
+    }
+
+  debug ("create_partitions end with 0\n");
+  return 0;
+}
+
+error_t
+netfs_attempt_lookup (struct iouser *user, struct node *dir,
+                      const char *name, struct node **np)
+{
+  debug ("netfs_attempt_lookup (user: %p, dir: %p, name: %s, np: %p):\n",
+         user, dir, name, np);
+  pthread_mutex_unlock (&dir->lock);
+
+  if (!dir->nn->entries)
+    {
+      debug ("!dir->nn->entries\n");
+      if (dir != netfs_root_node)
+        {
+          debug ("dir != netfs_root_node\n");
+          *np = NULL;
+          debug ("netfs_attempt_lookup end with ENOTDIR\n");
+          return ENOTDIR;
+        }
+
+      error_t err = create_partitions ();
+      if (err)
+        {
+          debug ("err in create_partitions\n");
+          *np = NULL;
+          debug ("netfs_attempt_lookup end with err: %d\n", err);
+          return err;
+        }
+    }
+
+  debug ("if (*name ==...\n");
+  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 end with 0\n");
+      return 0;
+    }
+
+  debug ("current_node\n");
+  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 (strcmp (name, iter->nn->name) == 0)
+        {
+          current_node = iter;
+          break;
+        }
+    }
+
+  debug ("current_node 2\n");
+  if (current_node)
+    {
+      pthread_mutex_lock (&dir->lock);
+      *np = current_node;
+      netfs_nref (*np);
+      pthread_mutex_unlock (&dir->lock);
+      debug ("current_node->nn->name: %s\n", current_node->nn->name);
+      debug ("netfs_attempt_lookup end with 0\n");
+      return 0;
+    }
+
+  *np = NULL;
+  debug ("netfs_attempt_lookup end with 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.  */
+error_t
+netfs_attempt_create_file (struct iouser *user, struct node *dir,
+                           const char *name, mode_t mode, struct node **np)
+{
+  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:\n");
+
+  error_t err = 0;
+
+  if (!err && (flags & O_READ))
+    err = fshelp_access (&np->nn_stat, S_IREAD, user);
+  if (!err && (flags & O_WRITE))
+    err = fshelp_access (&np->nn_stat, S_IWRITE, user);
+  if (!err && (flags & O_EXEC))
+    err = fshelp_access (&np->nn_stat, S_IEXEC, user);
+
+  if (err)
+    return err;
+
+  if (!np->nn->open)
+    {
+      err = set_node_open (np, NULL);
+      if (err)
+        return err;
+    }
+
+  if (dev_is_readonly (np->nn->open->dev) && (flags & O_WRITE))
+    return EROFS;
+
+  debug ("netfs_check_open_permissions end with 0\n");
+  return 0;
+}
+
+/* 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 end with ENOTDIR\n");
+          return ENOTDIR;
+        }
+
+      error_t err = create_partitions ();
+      if (err)
+        {
+          debug ("err in create_partitions\n");
+          debug ("netfs_get_dirents end with err: %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 end with 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);
+    }
+
+  *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+  if ((void *) *data == (void *) -1)
+    {
+      debug ("(void *) *data == (void *) -1\n");
+      debug ("netfs_get_dirents end with errno: %d\n", errno);
+      return errno;
+    }
+
+  *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->entries_size > 0)
+        dirent_type = DT_DIR;
+      else
+        if (current_node->nn->open->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 end with 0\n");
+  return 0;
+}
+
+/* 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
+   where read data and write data are the same, these objects will be
+   equal, otherwise they will be disjoint.  Servers are permitted to
+   implement io_map but not io_map_cntl.  Some objects do not provide
+   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
+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;
+
+  if (!(cred->po->openstat & (O_READ | O_WRITE)))
+    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 netnode *node = cred->po->np->nn;
+  mach_port_t memobj;
+
+  error_t err = dev_get_memory_object (node->open->dev, prot, &memobj);
+  if (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);
+
+  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
+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 end with EOPNOTSUPP\n");
+      return EOPNOTSUPP;
+    }
+  else if (!(cred->po->openstat & O_READ))
+    return EBADF;
+
+  size_t data_size = *datalen;
+  error_t err = open_read (cred->po->np->nn->open, offset, amount,
+                           (void **)data, &data_size);
+  if (err)
+    return err;
+
+  *datalen = data_size;
+  return 0;
+}
+
+/* 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
+netfs_S_io_readable (struct protid *cred, vm_size_t *amount)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (!(cred->po->openstat & O_READ))
+    return EBADF;
+
+  struct open *open = cred->po->np->nn->open;
+  *amount = open->dev->store->size - open->offs;
+  return 0;
+}
+
+/* Write data to an IO object.  If offset is -1, write at the object
+   maintained file pointer.  If the object is not seekable, offset is
+   ignored.  The amount successfully written is returned in amount.  A
+   given user should not have more than one outstanding io_write on an
+   object at a time; servers implement congestion control by delaying
+   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
+netfs_S_io_write (struct protid *cred, const_data_t data,
+                  mach_msg_type_number_t datalen, off_t offset,
+                  vm_size_t *amount)
+{
+  debug ("netfs_S_io_write:\n");
+
+  if (!cred)
+    {
+      debug ("!cred\n");
+      debug ("netfs_S_io_write end with EOPNOTSUPP\n");
+      return EOPNOTSUPP;
+    }
+
+  if (!(cred->po->openstat & O_WRITE))
+    {
+      debug ("!(user->po->openstat & O_WRITE)\n");
+      debug ("netfs_S_io_write end with EBADF\n");
+      return EBADF;
+    }
+
+  *amount = datalen;
+
+  error_t err;
+  err = open_write (cred->po->np->nn->open, offset, data, datalen, amount);
+
+  debug ("netfs_S_io_write end with err: %d\n", err);
+  return err;
+}
+
+/* Change current read/write offset */
+kern_return_t
+netfs_S_io_seek (struct protid *cred, loff_t offset, int whence, loff_t *newp)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  error_t err = open_seek (cred->po->np->nn->open, offset, whence, newp);
+  return err;
+}
+
+/* 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
+netfs_S_io_select (struct protid *cred, mach_port_t reply,
+                   mach_msg_type_name_t replyPoly, int *select_type)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  *select_type &= ~SELECT_URG;
+  return 0;
+}
+
+kern_return_t
+netfs_S_io_select_timeout (struct protid *cred,
+                           mach_port_t reply, mach_msg_type_name_t replyPoly,
+                           timespec_t timeout, int *select_type)
+{
+  return netfs_S_io_select (cred, reply, replyPoly, select_type);
+}
+
+/* Truncate file.  */
+kern_return_t
+netfs_S_file_set_size (struct protid *cred, loff_t new_size)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  if (new_size < 0)
+    return EINVAL;
+
+  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. */
+
+kern_return_t
+netfs_S_io_get_openmodes (struct protid *cred, int *bits)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  *bits = cred->po->openstat;
+  return 0;
+}
+
+kern_return_t
+netfs_S_io_set_all_openmodes (struct protid *cred, int newbits)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  return 0;
+}
+
+kern_return_t
+netfs_S_io_set_some_openmodes (struct protid *cred, int bits_to_set)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  return 0;
+}
+
+kern_return_t
+netfs_S_io_clear_some_openmodes (struct protid *cred, int bits_to_clear)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  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
+netfs_S_io_get_owner (struct protid *cred, pid_t *owner)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  *owner = cred->po->np->nn->open->dev->owner;
+  return 0;
+}
+
+kern_return_t
+netfs_S_io_mod_owner (struct protid *cred, pid_t owner)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  cred->po->np->nn->open->dev->owner = owner;
+  return 0;
+}
+
+/* File syncing operations; these all do the same thing, sync the underlying
+   device.  */
+kern_return_t
+netfs_S_file_sync (struct protid *cred, int wait, int omit_metadata)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  return dev_sync (cred->po->np->nn->open->dev, wait);
+}
+
+kern_return_t
+netfs_S_file_syncfs (struct protid *cred, int wait, int do_children)
+{
+  if (!cred)
+    return EOPNOTSUPP;
+
+  return dev_sync (cred->po->np->nn->open->dev, wait);
+}
+
+static inline int
+is_privileged (const struct idvec *uids)
+{
+  return idvec_contains (uids, 0) || idvec_contains (uids, getuid ());
+}
+
+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)
+{
+  debug ("netfs_file_get_storage_info:\n");
+  *ports_type = MACH_MSG_TYPE_COPY_SEND;
+
+  if (!cred)
+    {
+      debug ("!cred\n");
+      debug ("netfs_file_get_storage_info end with EOPNOTSUPP\n");
+      return EOPNOTSUPP;
+    }
+
+  struct dev *dev = np->nn->open->dev;
+  struct store *store = dev->store;
+  if (storeio_stat.enforced && !(store->flags & STORE_ENFORCED))
+    {
+      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 end with 0\n");
+      return 0;
+    }
+
+  error_t err;
+  if (!is_privileged (cred->uids)
+      && !store_is_securely_returnable (store, np->nn_stat.st_mode))
+    {
+      debug ("!is_privileged (cred->uids)...\n");
+      struct store *clone;
+      err = store_clone (store, &clone);
+      if (err)
+        {
+          debug ("err in store_clone\n");
+          debug ("netfs_file_get_storage_info end with err: %d\n", err);
+          return 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);
+    }
+  else
+    err = store_return (store, ports, num_ports, ints, num_ints,
+                        offsets, num_offsets, data, data_len);
+
+  debug ("netfs_file_get_storage_info end with err: %d\n", err);
+  return err;
+}
diff --git a/storeio/netfs.h b/storeio/netfs.h
new file mode 100644
index 0000000..14b1dac
--- /dev/null
+++ b/storeio/netfs.h
@@ -0,0 +1,36 @@
+/* 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 "open.h"
+
+struct netnode
+{
+  struct open *open;
+  char *name;
+  struct node **entries;
+  size_t entries_size;
+};
+
+error_t create_node (struct node **node, char *name, struct node *dir);
+
+#endif /* !__NETFS_H__ */
diff --git a/storeio/open.c b/storeio/open.c
index 7490252..778c5be 100644
--- a/storeio/open.c
+++ b/storeio/open.c
@@ -20,9 +20,9 @@
 
 #include <hurd.h>
 #include <stdio.h>
+#include <stdlib.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.  */
diff --git a/storeio/pager.c b/storeio/pager.c
index 11bf469..1282b31 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 4e8a962..3cc3ace 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,6 +18,7 @@
    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>
@@ -28,13 +29,22 @@
 
 #include <hurd.h>
 #include <hurd/ports.h>
-#include <hurd/trivfs.h>
+#include <hurd/netfs.h>
 #include <version.h>
 
-#include "open.h"
-#include "dev.h"
-#include "libtrivfs/trivfs_fsys_S.h"
-
+#include "netfs.h"
+#include "libnetfs/fsys_S.h"
+
+char *netfs_server_name = "storeio";
+char *netfs_server_version = HURD_VERSION;
+int netfs_maxsymlinks = 0; /* arbitrary */
+
+const char *argp_program_version = STANDARD_HURD_VERSION (storeio);
+
+char *debug_file_name = NULL;
+FILE *debug_file;
+pthread_mutex_t debug_lock;
+
 static struct argp_option options[] =
 {
   {"readonly", 'r', 0,	  0,"Disallow writing"},
@@ -43,8 +53,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"},
@@ -52,18 +64,38 @@ static struct argp_option options[] =
 };
 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;
-
 /* Desired store parameters specified by the user.  */
 struct storeio_argp_params
 {
   struct store_argp_params store_params; /* Filled in by store_argp parser.  */
-  struct dev *dev;		/* We fill in its flag members.  */
+  struct storeio_stat *storeio_stat; /* We fill in its flag members.  */
 };
 
+static error_t
+create_storeio (void)
+{
+  debug ("create_storeio:\n");
+  error_t 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)
+    return err;
+
+  netfs_root_node->nn_stat.st_nlink = 2;
+
+  debug ("create_storeio end with 0\n");
+  return 0;
+}
+
 /* Parse a single option.  */
 static error_t
 parse_opt (int key, char *arg, struct argp_state *state)
@@ -73,12 +105,15 @@ parse_opt (int key, char *arg, struct argp_state *state)
   switch (key)
     {
 
-    case 'r': params->dev->readonly = 1; break;
-    case 'w': params->dev->readonly = 0; break;
+    case 'r': params->storeio_stat->readonly = 1; break;
+    case 'w': params->storeio_stat->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': params->storeio_stat->inhibit_cache = 1; break;
+    case 'e': params->storeio_stat->enforced = 1; break;
+    case 'F': params->storeio_stat->no_fileio = 1; break;
+#ifdef DEBUG
+    case 'd': debug_file_name = arg; break;
+#endif
 
     case 'n':
       {
@@ -99,17 +134,7 @@ 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;
+	params->storeio_stat->rdev = rdev;
       }
       break;
 
@@ -123,7 +148,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
       break;
 
     case ARGP_KEY_SUCCESS:
-      params->dev->store_name = params->store_params.result;
+      params->storeio_stat->store_name = params->store_params.result;
       break;
 
     default:
@@ -134,321 +159,89 @@ 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;
+
+struct storeio_stat storeio_stat;
+mach_port_t underlying_node;
 
 int
 main (int argc, char *argv[])
 {
-  error_t err;
-  mach_port_t bootstrap;
-  struct dev device;
+  memset (&storeio_stat, 0, sizeof (struct storeio_stat));
+
   struct storeio_argp_params params;
+  params.storeio_stat = &storeio_stat;
+  argp_parse (&argp, argc, argv, 0, 0, &params);
 
-  memset (&device, 0, sizeof device);
-  pthread_mutex_init (&device.lock, NULL);
+  mach_port_t bootstrap;
+  task_get_bootstrap_port (mach_task_self (), &bootstrap);
+  if (bootstrap == MACH_PORT_NULL)
+    error (2, 0, "Must be started as a translator");
 
-  params.dev = &device;
-  argp_parse (&argp, argc, argv, 0, 0, &params);
+  netfs_init ();
 
-  if (debug)
-    {
-      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");
-    }
-  else
+  underlying_node = netfs_startup (bootstrap, O_READ);
+  io_statbuf_t underlying_stat;
+
+  error_t err = io_stat (underlying_node, &underlying_stat);
+  if (err)
+    error (1, err, "Cannot stat underlying node");
+
+#ifdef DEBUG
+  if (debug_file_name)
     {
-      task_get_bootstrap_port (mach_task_self (), &bootstrap);
-      if (bootstrap == MACH_PORT_NULL)
-	error (2, 0, "Must be started as a translator");
-
-      /* Reply to our parent */
-      err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &storeio_fsys);
-      if (err)
-	error (3, err, "trivfs_startup");
+      debug_file = fopen (debug_file_name, "a");
+      setbuf (debug_file, NULL);
+      pthread_mutex_init (&debug_lock, NULL);
     }
+#endif
+
+  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);
 
   /* 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");
-
-  if (! err)
-    err = store_parsed_append_args (dev->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)
-{
-  struct dev *const dev = trivfs_control->hook;
-  error_t err = 0;
-
-  if (!err && dev_is_readonly (dev) && (flags & O_WRITE))
-    return EROFS;
-
-  pthread_mutex_lock (&dev->lock);
-  if (dev->store == NULL)
-    {
-      /* 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;
-    }
-  pthread_mutex_unlock (&dev->lock);
-
-  return err;
-}
+                    storeio_stat.readonly ? "--readonly" : "--writable");
 
-static error_t
-open_hook (struct trivfs_peropen *peropen)
-{
-  error_t err = 0;
-  struct dev *const dev = peropen->cntl->hook;
+  if (!err)
+    err = store_parsed_append_args (storeio_stat.store_name, argz, argz_len);
 
-  if (dev->store)
-    {
-      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);
-    }
   return err;
 }
-
-static void
-close_hook (struct trivfs_peropen *peropen)
-{
-  struct dev *const dev = peropen->cntl->hook;
-
-  if (peropen->hook)
-    {
-      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);
-    }
-}
-
-/* ---------------------------------------------------------------- */
-/* Trivfs hooks  */
-
-int trivfs_fstype = FSTYPE_DEV;
-int trivfs_fsid = 0;
-
-int trivfs_support_read = 1;
-int trivfs_support_write = 1;
-int trivfs_support_exec = 0;
-
-int trivfs_allow_open = O_READ | O_WRITE;
-
-void
-trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
-{
-  struct dev *const dev = cred->po->cntl->hook;
-  struct open *open = cred->po->hook;
-
-  st->st_mode &= ~S_IFMT;
-
-  if (open)
-    /* An open device.  */
-    {
-      struct store *store = open->dev->store;
-      store_offset_t size = store->size;
-
-      if (store->block_size > 1)
-	st->st_blksize = store->block_size;
-
-      st->st_size = size;
-      st->st_mode |= ((dev->inhibit_cache || store->block_size == 1)
-		      ? S_IFCHR : S_IFBLK);
-    }
-  else
-    /* Try and do things without an open device...  */
-    {
-      st->st_blksize = 0;
-      st->st_size = 0;
-
-      st->st_mode |= dev->inhibit_cache ? S_IFCHR : S_IFBLK;
-    }
-
-  st->st_rdev = dev->rdev;
-  if (dev_is_readonly (dev))
-    st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
-}
-
-error_t
-trivfs_goaway (struct trivfs_control *fsys, int flags)
-{
-  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;
-
-  pthread_mutex_lock (&device->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);
-
-  /* Wait until all pending rpcs are done.  */
-  err = ports_inhibit_class_rpcs (root_port_class);
-  if (err == EINTR || (err && !force))
-    {
-      pthread_mutex_unlock (&device->lock);
-      return err;
-    }
-
-  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);
-    }
-
- 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;
-}
-- 
2.43.0

Reply via email to