Hello,
It took me some time to get a better understanding of how libnetfs and
storeio
work, and now the current version seems much more correct to me.
On Tue, 9 Jun 2026 00:14:24 Samuel Thibault wrote:
> > 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,
>
>I'm not sure I understand what you mean.
What I meant was that even if I pass -T typed part:1:file:/root/disk.img to
the translator, running ls or cd in the translator directory will create a
single partition file, and storeio and storeio/1 are the same thing. I find
this behavior confusing, since a partition can't be considered a disk, and
therefore the translator shouldn't be a directory.
> Ah, libtrivfs doesn't actually call it, that's possibly why we never saw
> the problem. I guess it gets stuck because maptime_map tries to open
> /dev/time, because netfs_init tries with use_mach_dev=0 first. That
> being said libdiskfs does that too.
Storeio will likely need to be added to the bootstrap chain to solve this
issue. That's the only thing that comes to mind.
> > 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?
>
> For goaway/close/sync, yes. modify_stat is only for trivfs
This caused some complications. Previously, each netnode contained a pointer
to an open struct and called open_create when accessing the translator, but
it wasn't clear where and when to call open_free. Furthermore, it was
unclear
how to work with storeio from different peropens.
I propose the following: add netfs_peropen_create_hook and
netfs_peropen_destroy_hook to libnetfs, which will be called by
netfs_make_peropen and netfs_release_peropen, respectively. Now, a netnode
contains a dev and an open_list, which is a list of peropen and open
pointers.
This way, each open works independently of the others and is created and
freed
as needed.
I have two questions:
1. The code contains numerous conditions whose results are highly likely
to be
unambiguous. Perhaps it's worth using __builtin_expect to help the branch
predictor perform better?
2. During development, I added a debug macro that logs code execution. While
this is very convenient for debugging and doesn't make it into the final
code
without the DEBUG flag, is it too much? Or, conversely, should I add logging
everywhere?
Thanks,
--
Mikhail Karpov
From 350b6f61c0928b637f17e5ee2dfe90798fac5449 Mon Sep 17 00:00:00 2001
From: Mikhail Karpov <[email protected]>
Date: Fri, 19 Jun 2026 22:18:24 +0700
Subject: [PATCH] Migrating storeio from trivfs to netfs
---
Makefile | 6 +-
libnetfs/Makefile | 2 +-
libnetfs/init-init.c | 12 +-
libnetfs/make-peropen.c | 9 +
libnetfs/netfs.h | 8 +
libnetfs/priv.c | 26 +
libnetfs/release-peropen.c | 13 +-
storeio/Makefile | 11 +-
storeio/dev.c | 69 +-
storeio/dev.h | 78 ++-
storeio/io.c | 582 +++++++++--------
storeio/netfs.h | 44 ++
storeio/open.c | 2 +-
storeio/pager.c | 3 +-
storeio/storeio.c | 1228 ++++++++++++++++++++++++++++--------
15 files changed, 1486 insertions(+), 607 deletions(-)
create mode 100644 libnetfs/priv.c
create mode 100644 storeio/netfs.h
diff --git a/Makefile b/Makefile
index c51e8c1c..9d2968e3 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/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..60511cfc 100644
--- a/libnetfs/init-init.c
+++ b/libnetfs/init-init.c
@@ -37,12 +37,12 @@ volatile struct mapped_time_value *netfs_mtime;
void
netfs_init (void)
{
- error_t err;
- err = maptime_map (0, 0, &netfs_mtime);
- if (err)
- err = maptime_map (1, 0, &netfs_mtime);
- if (err)
- error (2, err, "mapping time");
+ //error_t err;
+ //err = maptime_map (0, 0, &netfs_mtime);
+ //if (err)
+ // err = maptime_map (1, 0, &netfs_mtime);
+ //if (err)
+ // 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/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..38e187b4 100644
--- a/libnetfs/release-peropen.c
+++ b/libnetfs/release-peropen.c
@@ -23,10 +23,19 @@
void
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;
+ }
+ else
+ 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..8a3f37f4 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
OBJS = $(SRCS:.c=.o)
-HURDLIBS = trivfs pager fshelp iohelp store ports ihash shouldbeinlibc
-LDLIBS = -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz)
+HURDLIBS = netfs pager fshelp iohelp store ports ihash shouldbeinlibc
+LDLIBS = -lparted -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz)
include ../Makeconf
diff --git a/storeio/dev.c b/storeio/dev.c
index c87400c0..dadd1e4e 100644
--- a/storeio/dev.c
+++ b/storeio/dev.c
@@ -1,6 +1,6 @@
/* store `device' I/O
- Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2008
+ Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2008, 2026
Free Software Foundation, Inc.
Written by Miles Bader <[email protected]>
@@ -140,40 +140,24 @@ dev_buf_rw (struct dev *dev, size_t buf_offs, size_t *io_offs, size_t *len,
return 0;
}
}
-
-/* Called with DEV->lock held. Try to open the store underlying DEV. */
+
error_t
-dev_open (struct dev *dev)
+dev_open_from_store (struct dev *dev, struct store *store)
{
- error_t err;
- const int flags = ((dev->readonly ? STORE_READONLY : 0)
- | (dev->no_fileio ? STORE_NO_FILEIO : 0));
-
- assert_backtrace (dev->store == 0);
-
- if (dev->store_name == 0)
- {
- /* This means we had no store arguments.
- We are to operate on our underlying node. */
- err = store_create (storeio_fsys->underlying, flags, 0, &dev->store);
- }
- else
- /* Open based on the previously parsed store arguments. */
- err = store_parsed_open (dev->store_name, flags, &dev->store);
- if (err)
- return err;
-
/* Inactivate the store, it will be activated at first access.
We ignore possible EINVAL here . XXX Pass STORE_INACTIVE to
store_create/store_parsed_open instead when libstore is fixed
to support this. */
+ debug ("dev_init_from_store:\n");
+ dev->store = store;
store_set_flags (dev->store, STORE_INACTIVE);
- if (! dev->store->block_size)
+ if (!dev->store->block_size)
dev->buf = NULL;
else
dev->buf = mmap (0, dev->store->block_size, PROT_READ|PROT_WRITE,
- MAP_ANON, 0, 0);
+ MAP_ANON, 0, 0);
+
if (dev->buf == MAP_FAILED)
{
store_free (dev->store);
@@ -181,7 +165,7 @@ dev_open (struct dev *dev)
return ENOMEM;
}
- if (!dev->inhibit_cache)
+ if (!storeio_stat.inhibit_cache)
{
dev->buf_offs = -1;
pthread_rwlock_init (&dev->io_lock, NULL);
@@ -190,8 +174,35 @@ dev_open (struct dev *dev)
pthread_mutex_init (&dev->pager_lock, NULL);
}
+ debug ("dev_init_from_store end with 0\n");
return 0;
}
+
+/* Called with DEV->lock held. Try to open the store underlying DEV. */
+error_t
+dev_open (struct dev *dev, struct store_parsed *store_name)
+{
+ error_t err;
+ const int flags = ((storeio_stat.readonly ? STORE_READONLY : 0)
+ | (storeio_stat.no_fileio ? STORE_NO_FILEIO : 0));
+
+ assert_backtrace (dev->store == 0);
+
+ struct store *store;
+ if (store_name == 0)
+ {
+ /* This means we had no store arguments.
+ We are to operate on our underlying node. */
+ err = store_create (underlying_node, flags, 0, &store);
+ }
+ else
+ /* Open based on the previously parsed store arguments. */
+ err = store_parsed_open (store_name, flags, &store);
+ if (err)
+ return err;
+
+ return dev_open_from_store (dev, store);
+}
/* Shut down the store underlying DEV and free any resources it consumes.
DEV itself remains intact so that dev_open can be called again.
@@ -201,7 +212,7 @@ dev_close (struct dev *dev)
{
assert_backtrace (dev->store);
- if (!dev->inhibit_cache)
+ if (!storeio_stat.inhibit_cache)
{
if (dev->pager != NULL)
pager_shutdown (dev->pager);
@@ -222,7 +233,7 @@ dev_sync(struct dev *dev, int wait)
{
error_t err;
- if (dev->inhibit_cache)
+ if (storeio_stat.inhibit_cache)
return 0;
/* Sync any paged backing store. */
@@ -359,7 +370,7 @@ dev_write (struct dev *dev, off_t offs, const void *buf, size_t len,
buf + io_offs, len, amount);
}
- if (dev->inhibit_cache)
+ if (storeio_stat.inhibit_cache)
{
/* Under --no-cache, we permit only whole-block writes.
Note that in this case we handle non-power-of-two block sizes. */
@@ -452,7 +463,7 @@ dev_read (struct dev *dev, off_t offs, size_t whole_amount,
return 0;
}
- if (dev->inhibit_cache)
+ if (storeio_stat.inhibit_cache)
{
/* Under --no-cache, we permit only whole-block reads.
Note that in this case we handle non-power-of-two block sizes.
diff --git a/storeio/dev.h b/storeio/dev.h
index eda7a93d..950bfb7b 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,21 @@
#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;
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+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
@@ -51,19 +47,10 @@ struct dev
indicates that there is no owner. */
pid_t owner;
- /* The number of active opens. */
- int nperopens;
-
- /* This lock protects `store', `owner' and `nperopens'. The other
- members never change after creation, except for those locked by
- io_lock (below). */
+ /* This lock protects `store' and `owner'. 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 +71,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 +136,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..8cf53fae 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,15 @@
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 <hurd/netfs.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 +35,153 @@
mapping; they will set none of the ports and return an error. Such
objects can still be accessed by io_read and io_write. */
kern_return_t
-trivfs_S_io_map (struct trivfs_protid *cred,
- mach_port_t reply, mach_msg_type_name_t reply_type,
- memory_object_t *rd_obj, mach_msg_type_name_t *rd_type,
- memory_object_t *wr_obj, mach_msg_type_name_t *wr_type)
+netfs_S_io_map (struct protid *cred,
+ mach_port_t *memobjrd, mach_msg_type_name_t *memobjrdPoly,
+ mach_port_t *memobjwt, mach_msg_type_name_t *memobjwtPoly)
{
- if (! cred)
- return EOPNOTSUPP;
- else if (! (cred->po->openmodes & (O_READ|O_WRITE)))
- return EBADF;
- else
+ debug ("netfs_S_io_map:\n");
+ if (unlikely (!cred))
+ {
+ debug ("!cred");
+ debug ("netfs_S_io_map return: EOPNOTSUPP");
+ return EOPNOTSUPP;
+ }
+
+ if (!(cred->po->openstat & (O_READ | O_WRITE)))
{
- 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;
+ debug ("!(cred->po->openstat & (O_READ | O_WRITE))\n");
+ debug ("netfs_S_io_map return: EBADF");
+ return EBADF;
+ }
+
+ *memobjrdPoly = *memobjwtPoly = MACH_MSG_TYPE_MOVE_SEND;
+
+ int flags = cred->po->openstat;
+ vm_prot_t prot = ((flags & O_READ) ? VM_PROT_READ : 0)
+ | ((flags & O_WRITE) ? VM_PROT_WRITE : 0);
+ struct node *node = cred->po->np;
+ mach_port_t memobj;
+ pthread_mutex_lock (&node->lock);
+ error_t err = dev_get_memory_object (node->nn->dev, prot, &memobj);
+ if (err)
+ {
+ debug ("dev_get_memory_object return err: %d\n", err);
+ pthread_mutex_unlock (&node->lock);
+ debug ("netfs_S_io_map return: %d\n", err);
return err;
}
+
+ if (flags & O_READ)
+ *memobjrd = memobj;
+ else
+ *memobjrd = MACH_PORT_NULL;
+
+ if (flags & O_WRITE)
+ *memobjwt = memobj;
+ else
+ *memobjwt = MACH_PORT_NULL;
+
+ if ((flags & (O_READ | O_WRITE)) == (O_READ | O_WRITE)
+ && memobj != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (), memobj, MACH_PORT_RIGHT_SEND, 1);
+ pthread_mutex_unlock (&node->lock);
+
+ debug ("netfs_S_io_map return: 0\n");
+ return 0;
}
-
+
/* Read data from an IO object. If offset if -1, read from the object
maintained file pointer. If the object is not seekable, offset is
ignored. The amount desired to be read is in AMOUNT. */
kern_return_t
-trivfs_S_io_read (struct trivfs_protid *cred,
- mach_port_t reply, mach_msg_type_name_t reply_type,
- data_t *data, mach_msg_type_name_t *data_len,
- off_t offs, vm_size_t amount)
+netfs_S_io_read (struct protid *cred, data_t *data,
+ mach_msg_type_number_t *datalen, off_t offset,
+ vm_size_t amount)
{
- error_t err;
- size_t data_size = *data_len;
+ debug ("netfs_S_io_read:\n");
- if (! cred)
- return EOPNOTSUPP;
- else if (! (cred->po->openmodes & O_READ))
- return EBADF;
+ if (unlikely (!cred))
+ {
+ debug ("!cred\n");
+ debug ("netfs_S_io_read return: EOPNOTSUPP\n");
+ return EOPNOTSUPP;
+ }
- err = open_read ((struct open *)cred->po->hook,
- offs, amount, (void **)data, &data_size);
- if (err)
- return err;
- *data_len = data_size;
- return 0;
+ if (!(cred->po->openstat & O_READ))
+ {
+ debug ("!(cred->po->openstat & O_READ)\n");
+ debug ("netfs_S_io_read return: EBADF");
+ return EBADF;
+ }
+
+ struct node *node = cred->po->np;
+ pthread_mutex_lock (&node->lock);
+
+ struct open *open = get_open (node->nn->open_list, cred->po);
+ if (unlikely (!open))
+ {
+ debug ("!open");
+ pthread_mutex_unlock (&node->lock);
+ debug ("netfs_S_io_read return: 1");
+ return 1;
+ }
+
+ size_t data_size = *datalen;
+ error_t err = open_read (open, offset, amount, (void **) data, &data_size);
+ pthread_mutex_unlock (&node->lock);
+ *datalen = data_size;
+
+ debug ("netfs_S_io_read return: %d\n", err);
+ return err;
}
/* Tell how much data can be read from the object without blocking for
a "long time" (this should be the same meaning of "long time" used
by the nonblocking flag. */
kern_return_t
-trivfs_S_io_readable (struct trivfs_protid *cred,
- mach_port_t reply, mach_msg_type_name_t reply_type,
- vm_size_t *amount)
+netfs_S_io_readable (struct protid *cred, vm_size_t *amount)
{
- if (! cred)
- return EOPNOTSUPP;
- else if (! (cred->po->openmodes & O_READ))
- return EBADF;
- else
+ debug ("netfs_S_io_readable");
+ if (unlikely (!cred))
{
- struct open *open = (struct open *)cred->po->hook;
- *amount = open->dev->store->size - open->offs;
- return 0;
+ debug ("!cred\n");
+ debug ("netfs_S_io_readable return: EOPNOTSUPP\n");
+ return EOPNOTSUPP;
+ }
+
+ if (!(cred->po->openstat & O_READ))
+ {
+ debug ("!(cred->po->openstat & O_READ)\n");
+ debug ("netfs_S_io_readable return: EBADF\n");
+ return EBADF;
+ }
+
+ struct node *node = cred->po->np;
+ pthread_mutex_lock (&node->lock);
+ error_t err = netfs_validate_stat (node, cred->user);
+ if (err)
+ {
+ debug ("netfs_validate_stat return err: %d\n", err);
+ pthread_mutex_unlock (&node->lock);
+ debug ("netfs_S_io_readable return: %d\n", err);
+ return err;
+ }
+
+ struct open *open = get_open (node->nn->open_list, cred->po);
+ if (unlikely (!open))
+ {
+ debug ("!open\n");
+ pthread_mutex_unlock (&node->lock);
+ debug ("netfs_S_io_readable return: 1\n");
+ return 1;
}
+
+ *amount = node->nn->dev->store->size - open->offs;
+ pthread_mutex_unlock (&node->lock);
+
+ debug ("netfs_S_io_readable return: 0\n");
+ return 0;
}
/* Write data to an IO object. If offset is -1, write at the object
@@ -130,125 +192,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)
+netfs_S_io_write (struct protid *cred, const_data_t data,
+ mach_msg_type_number_t datalen, off_t offset,
+ vm_size_t *amount)
{
- if (! cred)
- return EOPNOTSUPP;
- else if (! (cred->po->openmodes & O_WRITE))
- return EBADF;
- else
- return open_write ((struct open *)cred->po->hook,
- offs, data, data_len, amount);
+ debug ("netfs_S_io_write:\n");
+ if (unlikely (!cred))
+ {
+ debug ("!cred\n");
+ debug ("netfs_S_io_write return: EOPNOTSUPP\n");
+ return EOPNOTSUPP;
+ }
+
+ if (!(cred->po->openstat & O_WRITE))
+ {
+ debug ("!(user->po->openstat & O_WRITE)\n");
+ debug ("netfs_S_io_write return: EBADF\n");
+ return EBADF;
+ }
+
+ struct node *node = cred->po->np;
+ pthread_mutex_lock (&node->lock);
+ struct open *open = get_open (node->nn->open_list, cred->po);
+ if (unlikely (!open))
+ {
+ debug ("!open\n");
+ pthread_mutex_unlock (&node->lock);
+ debug ("netfs_S_io_write return: 1\n");
+ return 1;
+ }
+
+ error_t err = open_write (open, offset, data, datalen, amount);
+ pthread_mutex_unlock (&node->lock);
+
+ debug ("netfs_S_io_write return: %d\n", err);
+ return err;
}
-
+
/* 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_seek (struct protid *cred, loff_t offset, int whence, loff_t *newp)
{
- if (! cred)
- return EOPNOTSUPP;
- else
- return open_seek ((struct open *)cred->po->hook, offs, whence, new_offs);
-}
+ debug ("netfs_S_io_seek:\n");
+ if (unlikely (!cred))
+ {
+ debug ("!cred\n");
+ debug ("netfs_S_io_seek return: EOPNOTSUPP\n");
+ return EOPNOTSUPP;
+ }
-/* 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;
-}
+ struct node *node = cred->po->np;
+ pthread_mutex_lock (&node->lock);
+ struct open *open = get_open (node->nn->open_list, cred->po);
+ if (unlikely (!open))
+ {
+ debug ("!open\n");
+ pthread_mutex_unlock (&node->lock);
+ debug ("netfs_S_io_seek return: 1\n");
+ return 1;
+ }
-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);
-}
+ error_t err = open_seek (open, offset, whence, newp);
+ pthread_mutex_unlock (&node->lock);
-/* 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;
+ debug ("netfs_S_io_seek return: %d\n", err);
+ return err;
}
-
-/* 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. */
+/* 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_get_openmodes (struct trivfs_protid *cred,
- mach_port_t reply, mach_msg_type_name_t reply_type,
- int *bits)
+netfs_S_io_select (struct protid *cred, mach_port_t reply,
+ mach_msg_type_name_t replyPoly, int *select_type)
{
- if (! cred)
- return EOPNOTSUPP;
- else
+ debug ("netfs_S_io_select:\n");
+ if (unlikely (!cred))
{
- *bits = cred->po->openmodes;
- return 0;
+ debug ("!cred\n");
+ debug ("netfs_S_io_select return: EOPNOTSUPP\n");
+ return EOPNOTSUPP;
}
-}
-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;
-}
+ *select_type &= ~SELECT_URG;
-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;
+ debug ("netfs_S_io_select return: 0");
+ 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)
+netfs_S_io_select_timeout (struct protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replyPoly,
+ timespec_t timeout, int *select_type)
{
- if (! cred)
- return EOPNOTSUPP;
- else
- return 0;
+ return netfs_S_io_select (cred, reply, replyPoly, select_type);
}
-
+
/* 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
@@ -256,130 +295,125 @@ trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
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)
+netfs_S_io_get_owner (struct protid *cred, pid_t *owner)
{
- if (! cred)
- return EOPNOTSUPP;
- else
+ debug ("netfs_S_io_get_owner:\n");
+ if (unlikely (!cred))
{
- struct open *open = (struct open *)cred->po->hook;
- *owner = open->dev->owner; /* atomic word fetch */
- return 0;
+ debug ("!cred\n");
+ debug ("netfs_S_io_get_owner return: EOPNOTSUPP\n");
+ return EOPNOTSUPP;
}
+
+ struct node *node = cred->po->np;
+ pthread_mutex_lock (&node->lock);
+ *owner = node->nn->dev->owner; /* atomic word fetch */
+ pthread_mutex_unlock (&node->lock);
+
+ debug ("netfs_S_io_get_owner return: 0\n");
+ 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)
+netfs_S_io_mod_owner (struct protid *cred, pid_t owner)
{
- if (! cred)
- return EOPNOTSUPP;
- else
+ debug ("netfs_S_io_mod_owner:\n");
+ if (unlikely (!cred))
{
- struct open *open = (struct open *)cred->po->hook;
- open->dev->owner = owner; /* atomic word store */
- return 0;
+ debug ("!cred\n");
+ debug ("netfs_S_io_mod_owner return: EOPNOTSUPP\n");
+ return EOPNOTSUPP;
}
-}
-
-/* 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;
+ struct node *node = cred->po->np;
+ pthread_mutex_lock (&node->lock);
+ node->nn->dev->owner = owner; /* atomic word store */
+ pthread_mutex_unlock (&node->lock);
+
+ debug ("netfs_S_io_mod_owner return: 0\n");
+ return 0;
}
-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)
+static inline int
+is_privileged (const struct idvec *uids)
{
- if (cred)
- return dev_sync (((struct open *)cred->po->hook)->dev, wait);
- else
- return EOPNOTSUPP;
+ return idvec_contains (uids, 0) || idvec_contains (uids, getuid ());
}
-
-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)
+
+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");
+ if (unlikely (!cred))
+ {
+ debug ("!cred\n");
+ debug ("netfs_file_get_storage_info return: EOPNOTSUPP\n");
+ return EOPNOTSUPP;
+ }
+
*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))
+ {
+ debug ("storeio_stat.enforced && !(store->flags & STORE_ENFORCED)\n");
+ size_t name_len = (store->name ? strlen (store->name) + 1 : 0);
+ *num_ports = 0;
+ int i = 0;
+ (*ints)[i++] = STORAGE_OTHER;
+ (*ints)[i++] = store->flags;
+ (*ints)[i++] = store->block_size;
+ (*ints)[i++] = 1;
+ (*ints)[i++] = name_len;
+ (*ints)[i++] = 0;
+ *num_ints = i;
+ i = 0;
+ (*offsets)[i++] = 0;
+ (*offsets)[i++] = store->size;
+ *num_offsets = i;
+ if (store->name)
+ memcpy (*data, store->name, name_len);
+ *data_len = name_len;
+ debug ("netfs_file_get_storage_info return: 0\n");
+ return 0;
+ }
- if (! cred || ! cred->po->hook)
- return EOPNOTSUPP;
- else
+ error_t err;
+ if (!is_privileged (cred->uids)
+ && !store_is_securely_returnable (store, np->nn_stat.st_mode))
{
- error_t err;
- struct dev *dev = ((struct open *)cred->po->hook)->dev;
- struct store *store = dev->store;
-
- if (dev->enforced && !(store->flags & STORE_ENFORCED))
- {
- /* The --enforced switch tells us not to let anyone
- get at the device, no matter how trustable they are. */
- size_t name_len = (store->name ? strlen (store->name) + 1 : 0);
- int i;
- *num_ports = 0;
- i = 0;
- (*ints)[i++] = STORAGE_OTHER;
- (*ints)[i++] = store->flags;
- (*ints)[i++] = store->block_size;
- (*ints)[i++] = 1; /* num_runs */
- (*ints)[i++] = name_len;
- (*ints)[i++] = 0; /* misc_len */
- *num_ints = i;
- i = 0;
- (*offsets)[i++] = 0;
- (*offsets)[i++] = store->size;
- *num_offsets = i;
- if (store->name)
- memcpy (*data, store->name, name_len);
- *data_len = name_len;
- return 0;
- }
-
- if (!cred->isroot
- && !store_is_securely_returnable (store, cred->po->openmodes))
- {
- struct store *clone;
- err = store_clone (store, &clone);
- if (! err)
- {
- err = store_set_flags (clone, STORE_INACTIVE);
- if (err == EINVAL)
- err = EACCES;
- else
- err = store_return (clone,
- ports, num_ports, ints, num_ints,
- offsets, num_offsets, data, data_len);
- store_free (clone);
- }
- }
+ debug ("!is_privileged (cred->uids)...\n");
+ struct store *clone;
+ err = store_clone (store, &clone);
+ if (err)
+ {
+ debug ("store_clone return err: %d\n", err);
+ debug ("netfs_file_get_storage_info return: %d\n", err);
+ return err;
+ }
+
+ err = store_set_flags (clone, STORE_INACTIVE);
+ if (err == EINVAL)
+ err = EACCES;
else
- err = store_return (store,
- ports, num_ports, ints, num_ints,
- offsets, num_offsets, data, data_len);
+ err = store_return (clone, ports, num_ports, ints, num_ints,
+ offsets, num_offsets, data, data_len);
- return err;
+ store_free (clone);
}
+ else
+ err = store_return (store, ports, num_ports, ints, num_ints,
+ offsets, num_offsets, data, data_len);
+
+ debug ("netfs_file_get_storage_info return: %d\n", err);
+ return err;
}
diff --git a/storeio/netfs.h b/storeio/netfs.h
new file mode 100644
index 00000000..965c751c
--- /dev/null
+++ b/storeio/netfs.h
@@ -0,0 +1,44 @@
+/* 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 peropen_item
+{
+ struct peropen *address;
+ struct open *open;
+ struct peropen_item *next_item;
+};
+
+struct netnode
+{
+ struct dev *dev;
+ struct peropen_item *open_list;
+ char *name;
+ struct node **entries;
+ size_t entries_size;
+};
+
+struct open *get_open (struct peropen_item *list, struct peropen *address);
+
+#endif /* !__NETFS_H__ */
diff --git a/storeio/open.c b/storeio/open.c
index 74902520..778c5bef 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 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..6c6ccec2 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,133 @@ 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 (unlikely (!netnode))
+ {
+ debug ("!netnode\n");
+ debug ("create_node return: ENOMEM\n");
+ return ENOMEM;
+ }
+
+ struct node *new_node = netfs_make_node (netnode);
+ if (unlikely (!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 (unlikely (!dev))
+ {
+ debug ("!dev\n");
+ free (netnode);
+ free (new_node);
+ debug ("create_node return: ENOMEM\n");
+ return ENOMEM;
+ }
+
+ memset (dev, 0, sizeof (struct dev));
+ pthread_mutex_init (&dev->lock, NULL);
+ new_node->nn->dev = dev;
+ new_node->nn->open_list = NULL;
+ new_node->nn->name = name;
+ new_node->nn->entries = NULL;
+ new_node->nn->entries_size = 0;
+
+ if (dir)
+ netfs_nref (dir);
+
+ //fshelp_touch (&new_node->nn_stat, TOUCH_ATIME|TOUCH_CTIME|TOUCH_MTIME,
+ // storeio_stat.current_time);
+
+ *node = new_node;
+ debug ("new_node: %p\n", new_node);
+ debug ("create_node return: 0\n");
+ return 0;
+}
+
+static error_t
+create_storeio (void)
+{
+ debug ("create_storeio:\n");
+ error_t err;
+ //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 (unlikely (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 +206,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 (¶ms->store_params, 0, sizeof params->store_params);
- params->store_params.default_type = "device";
- params->store_params.store_optional = 1;
- state->child_inputs[0] = ¶ms->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 +232,923 @@ 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, ¶ms);
+ 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");
- storeio_fsys->hook = &device;
+ err = create_storeio ();
+ if (err)
+ error (1, err, "Cannot creare storeio");
+
+ netfs_root_node->nn_stat = underlying_stat;
+ netfs_root_node->nn_stat.st_mode =
+ S_IFDIR | (underlying_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+ debug ("netfs_root_node->nn_stat.st_mode: %d\n",
+ netfs_root_node->nn_stat.st_mode);
/* Launch. */
- ports_manage_port_operations_multithread (storeio_fsys->pi.bucket,
- trivfs_demuxer,
- 30*1000, 5*60*1000, 0);
+ debug ("netfs_server_loop()...\n");
+ netfs_server_loop ();
return 0;
}
-
+
error_t
-trivfs_append_args (struct trivfs_control *trivfs_control,
- char **argz, size_t *argz_len)
+netfs_append_args (char **argz, size_t *argz_len)
{
- struct dev *const dev = trivfs_control->hook;
error_t err = 0;
- if (dev->rdev != (dev_t) 0)
+ if (storeio_stat.rdev != (dev_t) 0)
{
char buf[40];
- snprintf (buf, sizeof buf, "--rdev=%d,%d",
- gnu_dev_major (dev->rdev), gnu_dev_minor (dev->rdev));
+ snprintf (buf, sizeof (buf), "--rdev=%d,%d",
+ gnu_dev_major (storeio_stat.rdev),
+ gnu_dev_minor (storeio_stat.rdev));
+
err = argz_add (argz, argz_len, buf);
}
- if (!err && dev->inhibit_cache)
+ if (!err && storeio_stat.inhibit_cache)
err = argz_add (argz, argz_len, "--no-cache");
- if (!err && dev->enforced)
+ if (!err && storeio_stat.enforced)
err = argz_add (argz, argz_len, "--enforced");
- if (!err && dev->no_fileio)
+ if (!err && storeio_stat.no_fileio)
err = argz_add (argz, argz_len, "--no-file-io");
- if (! err)
+ if (!err)
err = argz_add (argz, argz_len,
- dev->readonly ? "--readonly" : "--writable");
+ storeio_stat.readonly ? "--readonly" : "--writable");
- if (! err)
- err = store_parsed_append_args (dev->store_name, argz, argz_len);
+ if (!err)
+ err = store_parsed_append_args (storeio_stat.store_name, argz, argz_len);
return err;
}
-
-/* Called whenever a new lookup is done of our node. The only reason we
- set this hook is to duplicate the check done normally done against
- trivfs_allow_open in trivfs_S_fsys_getroot, but looking at the
- per-device state. This gets checked again in check_open_hook, but this
- hook runs before a little but more overhead gets incurred. In the
- success case, we just return EAGAIN to have trivfs_S_fsys_getroot
- continue with its generic processing. */
-static error_t
-getroot_hook (struct trivfs_control *cntl,
- mach_port_t reply_port,
- mach_msg_type_name_t reply_port_type,
- mach_port_t dotdot,
- const uid_t *uids, mach_msg_type_number_t nuids, const uid_t *gids, mach_msg_type_number_t ngids,
- int flags,
- retry_type *do_retry, char *retry_name,
- mach_port_t *node, mach_msg_type_name_t *node_type)
-{
- struct dev *const dev = cntl->hook;
- return (dev_is_readonly (dev) && (flags & O_WRITE)) ? EROFS : EAGAIN;
-}
-
-/* Called whenever someone tries to open our node (even for a stat). We
- delay opening the kernel device until this point, as we can usefully
- return errors from here. */
+
static error_t
-check_open_hook (struct trivfs_control *trivfs_control,
- struct iouser *user,
- int flags)
+check_dev (struct node *node, struct store *store, int flags)
{
- struct dev *const dev = trivfs_control->hook;
- error_t err = 0;
-
- if (!err && dev_is_readonly (dev) && (flags & O_WRITE))
- return EROFS;
+ debug ("check_dev (node: %p, store: %p, flags: %d):\n", node, store, flags);
+ if (unlikely (dev_is_readonly (node->nn->dev) && (flags & O_WRITE)))
+ {
+ debug ("dev_is_readonly (node->nn->dev) && (flags & O_WRITE)\n");
+ debug ("check_dev return: EROFS\n");
+ return EROFS;
+ }
+ error_t err = 0;
+ 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;
+ if (store)
+ err = dev_open_from_store (dev, store);
+ else
+ err = dev_open (dev, storeio_stat.store_name);
+
+ if (unlikely (err))
+ {
+ if ((flags & (O_READ | O_WRITE)) == 0)
+ {
+ debug ("dev open err: %d, but we are not opening the file for"
+ " reading or writing, just ignore the error\n", err);
+ err = 0;
+ }
+ else
+ debug ("dev open err: %d\n", err);
+ }
+
+ if (likely (!err))
+ {
+ node->nn_stat.st_size = dev->store->size;
+
+ if (dev->store->block_size > 1)
+ node->nn_stat.st_blksize = dev->store->block_size;
+
+ if (node != netfs_root_node)
+ {
+ if (dev->store->block_size == 1)
+ node->nn_stat.st_mode |= S_IFCHR;
+ else if (dev->store->block_size > 1)
+ node->nn_stat.st_mode |= S_IFBLK;
+ }
+ }
}
pthread_mutex_unlock (&dev->lock);
+ debug ("check_dev return: %d\n", err);
return err;
}
+struct open *
+get_open (struct peropen_item *list, struct peropen *address)
+{
+ for (struct peropen_item *item = list; item; item = item->next_item)
+ if (item->address == address)
+ return item->open;
+
+ return NULL;
+}
+
static error_t
-open_hook (struct trivfs_peropen *peropen)
+node_open_create (struct peropen *po)
{
- error_t err = 0;
- struct dev *const dev = peropen->cntl->hook;
+ debug ("node_open_create (po: %p):\n", po);
+ debug ("node: %p\n", po->np);
+ struct netnode *netnode = po->np->nn;
- if (dev->store)
+ struct peropen_item *item = malloc (sizeof (struct peropen_item));
+ if (unlikely (!item))
+ {
+ debug ("!item\n");
+ debug ("node_open_create return: ENOMEM\n");
+ return ENOMEM;
+ }
+ item->address = po;
+
+ error_t err;
+ pthread_mutex_lock (&netnode->dev->lock);
+ if (!netnode->open_list)
{
- 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_clear_flags (netnode->dev->store, STORE_INACTIVE);
+ if (unlikely (err))
+ {
+ 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;
+ }
+
+ item->next_item = NULL;
}
+ else
+ item->next_item = netnode->open_list;
+
+ netnode->open_list = item;
+
+ err = open_create (netnode->dev, &item->open);
+ if (unlikely (err))
+ debug ("open_create return err: %d\n", err);
+ pthread_mutex_unlock (&netnode->dev->lock);
+
+ debug ("node_open_create return: %d\n", err);
return err;
}
+error_t (*netfs_peropen_create_hook) (struct peropen *po) = node_open_create;
+
static void
-close_hook (struct trivfs_peropen *peropen)
+node_open_destroy (struct peropen *po)
{
- struct dev *const dev = peropen->cntl->hook;
+ debug ("node_open_destroy (po: %p):\n", po);
+ debug ("node: %p\n", po->np);
+ struct netnode *netnode = po->np->nn;
+ pthread_mutex_lock (&netnode->dev->lock);
+ struct peropen_item *item = netnode->open_list;
+ if (item->address == po)
+ {
+ netnode->open_list = netnode->open_list->next_item;
+ open_free (item->open);
+ free (item);
- if (peropen->hook)
+ if (!netnode->open_list)
+ store_set_flags (netnode->dev->store, STORE_INACTIVE);
+ }
+ else
{
- 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);
+ for (; item->next_item; item = item->next_item)
+ if (item->next_item->address == po)
+ {
+ struct peropen_item *tmp = item->next_item;
+ item->next_item = item->next_item->next_item;
+ open_free (tmp->open);
+ free (tmp);
+ break;
+ }
}
+ pthread_mutex_unlock (&netnode->dev->lock);
+ debug ("node_open_destroy end\n");
}
-
-/* ---------------------------------------------------------------- */
-/* Trivfs hooks */
-int trivfs_fstype = FSTYPE_DEV;
-int trivfs_fsid = 0;
+void (*netfs_peropen_destroy_hook) (struct peropen *po) = node_open_destroy;
-int trivfs_support_read = 1;
-int trivfs_support_write = 1;
-int trivfs_support_exec = 0;
+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);
+}
-int trivfs_allow_open = O_READ | O_WRITE;
+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);
+}
-void
-trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+kern_return_t
+netfs_S_fsys_goaway (struct netfs_control *pt,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int flags)
{
- struct dev *const dev = cred->po->cntl->hook;
- struct open *open = cred->po->hook;
+ debug ("netfs_S_fsys_goaway enter\n");
+ if (unlikely (!pt))
+ {
+ debug ("!pt");
+ debug ("netfs_S_fsys_goaway return: EOPNOTSUPP\n");
+ return EOPNOTSUPP;
+ }
- st->st_mode &= ~S_IFMT;
+ 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 ();
- if (open)
- /* An open device. */
+ int force = flags & FSYS_GOAWAY_FORCE;
+ error_t err = ports_inhibit_class_rpcs (netfs_protid_class);
+ if (err == EINTR || (err && !force))
{
- struct store *store = open->dev->store;
- store_offset_t size = store->size;
+ debug ("err == EINTR || (err && !force)\n");
+ devs_unlock ();
+ debug ("netfs_S_fsys_goaway return: %d\n", err);
+ return err;
+ }
- if (store->block_size > 1)
- st->st_blksize = store->block_size;
+ int nosync = flags & FSYS_GOAWAY_NOSYNC;
+ if (force && nosync)
+ {
+ debug ("force && nosync\n");
+ exit (0);
+ }
- st->st_size = size;
- st->st_mode |= ((dev->inhibit_cache || store->block_size == 1)
- ? S_IFCHR : S_IFBLK);
+ 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;
}
- else
- /* Try and do things without an open device... */
+
+ if (!nosync)
+ {
+ debug ("!nosync\n");
+ err = netfs_attempt_syncfs (0, flags);
+ }
+
+ if (!err && (dev_stop_paging (netfs_root_node->nn->dev, nosync) || force))
{
- st->st_blksize = 0;
- st->st_size = 0;
+ 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);
+ }
+ }
- st->st_mode |= dev->inhibit_cache ? S_IFCHR : S_IFBLK;
+ if (!err)
+ {
+ debug ("!err\n");
+ fsys_goaway_reply (reply, reply_type, 0);
+ debug ("netfs_S_fsys_goaway exit (0)\n");
+ exit (0);
}
- st->st_rdev = dev->rdev;
- if (dev_is_readonly (dev))
- st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ debug ("netfs_S_fsys_goaway return: %d\n", err);
+ return err;
}
error_t
-trivfs_goaway (struct trivfs_control *fsys, int flags)
+netfs_validate_stat (struct node *np, struct iouser *cred)
{
- 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;
+ return 0;
+}
- pthread_mutex_lock (&device->lock);
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *np, uid_t uid,
+ uid_t gid)
+{
+ return EOPNOTSUPP;
+}
- 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);
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *np, uid_t author)
+{
+ return EOPNOTSUPP;
+}
- /* Wait until all pending rpcs are done. */
- err = ports_inhibit_class_rpcs (root_port_class);
- if (err == EINTR || (err && !force))
+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)
+{
+ debug ("netfs_attempt_sync (cred: %p, np: %p, wait: %d):\n",
+ cred, np, wait);
+ error_t err = dev_sync (np->nn->dev, wait);
+ debug ("netfs_attempt_sync return: %d\n", err);
+ return err;
+}
+
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ debug ("netfs_attempt_syncfs (cred: %p, wait: %d):\n", cred, wait);
+ error_t err = dev_sync (netfs_root_node->nn->dev, wait);
+ if (err)
{
- pthread_mutex_unlock (&device->lock);
+ debug ("dev_sync (netfs_root_node->nn->dev) return err: %d\n", err);
+ debug ("netfs_attempt_syncfs return: %d\n", err);
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);
+ 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 ("dev_sync (netfs_root_node->nn->entries[%d]->nn->dev)"
+ " return err: %d\n", i, err);
+ break;
+ }
}
- 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)
+ debug ("netfs_attempt_syncfs return: %d\n", err);
+ return err;
+}
+
+/* Initialize a PedDevice using SOURCE. The SOURCE will NOT be destroyed;
+ the caller created it, it is the caller's responsilbility to free it
+ after it calls ped_device_destroy. SOURCE is not registered in Parted's
+ list of devices. */
+PedDevice* ped_device_new_from_store (struct store *source);
+
+static error_t
+set_last_partition_num (struct store *store, size_t *last_partition_num)
{
- struct dev *dev = cntl->hook;
- if (dev)
- return dev_sync (dev, wait);
- else
- return 0;
+ 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;
+ *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));
+
+ struct store *source, *store;
+ struct node **part;
+ for (size_t i = 1; i <= last_partition_num; ++i)
+ {
+ err = store_parsed_open (storeio_stat.store_name, flags, &source);
+ if (err)
+ {
+ debug ("err in store_parsed_open\n");
+ break;
+ }
+
+ err = store_part_create (source, i, flags, &store);
+ if (err)
+ {
+ debug ("err in store_part_create\n");
+ break;
+ }
+
+ part = &dir->nn->entries[i - 1];
+ err = create_node (part, create_node_name (i), dir);
+ if (err)
+ {
+ debug ("err in create_node\n");
+ break;
+ }
+
+ err = check_dev (*part, store, flags);
+ if (err)
+ {
+ debug ("err in dev_init_from_store\n");
+ break;
+ }
+ }
+
+ debug ("create_partitions return: %d\n", err);
+ return err;
+}
+
+error_t
+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):\n",
+ user, dir, name);
+
+ if (!dir->nn->entries)
+ {
+ debug ("!dir->nn->entries\n");
+ if (dir != netfs_root_node)
+ {
+ debug ("dir != netfs_root_node\n");
+ *np = NULL;
+ pthread_mutex_unlock (&dir->lock);
+ debug ("netfs_attempt_lookup return: ENOTDIR\n");
+ return ENOTDIR;
+ }
+
+ error_t err = create_partitions ();
+ if (err)
+ {
+ debug ("create_partitions return err: %d\n", err);
+ *np = NULL;
+ pthread_mutex_unlock (&dir->lock);
+ debug ("netfs_attempt_lookup return: %d\n", err);
+ return err;
+ }
+ }
+ pthread_mutex_unlock (&dir->lock);
+
+ if (*name == '\0' || strcmp (name, ".") == 0)
+ {
+ debug ("*name == '\\0' || strcmp (name, \".\") == 0\n");
+ *np = dir;
+ pthread_mutex_lock (&dir->lock);
+ netfs_nref (*np);
+ pthread_mutex_unlock (&dir->lock);
+ debug ("netfs_attempt_lookup return: 0\n");
+ return 0;
+ }
+
+ 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;
+ }
+ }
+
+ if (current_node)
+ {
+ debug ("current_node: %p\n", current_node);
+ debug ("current_node->nn->name: %s\n", current_node->nn->name);
+ *np = current_node;
+ pthread_mutex_lock (&dir->lock);
+ netfs_nref (*np);
+ pthread_mutex_unlock (&dir->lock);
+ debug ("netfs_attempt_lookup return: 0\n");
+ return 0;
+ }
+
+ *np = NULL;
+ debug ("netfs_attempt_lookup return: ENOENT\n");
+ return ENOENT;
+}
+
+error_t
+netfs_attempt_unlink (struct iouser *user, struct node *dir, const char *name)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ const char *fromname, struct node *todir,
+ const char *toname, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_mkdir (struct iouser *user, struct node *dir, const char *name,
+ mode_t mode)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_rmdir (struct iouser *user, struct node *dir, const char *name)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_link (struct iouser *user, struct node *dir, struct node *file,
+ const char *name, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+/* We don't use this function, but we need to unlock the dir. */
+error_t
+netfs_attempt_mkfile (struct iouser *user, struct node *dir, mode_t mode,
+ struct node **np)
+{
+ pthread_mutex_unlock (&dir->lock);
+ return EOPNOTSUPP;
+}
+
+/* We don't use this function, but we need to unlock the dir. */
+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 (user: %p, np: %p, flags: %d,"
+ " newnode: %d):\n", user, np, flags, newnode);
+
+ error_t err = 0;
+
+ if (!err && flags & O_READ)
+ {
+ err = fshelp_access (&np->nn_stat, S_IREAD, user);
+ if (err)
+ debug ("fshelp_access with S_IREAD return err: %d\n", err);
+ }
+
+ if (!err && flags & O_WRITE)
+ {
+ err = fshelp_access (&np->nn_stat, S_IWRITE, user);
+ if (err)
+ debug ("fshelp_access with S_IWRITE return err: %d\n", err);
+ }
+
+ if (!err && flags & O_EXEC)
+ {
+ err = fshelp_access (&np->nn_stat, S_IEXEC, user);
+ if (err)
+ debug ("fshelp_access with S_IEXEC return err: %d\n", err);
+ }
+
+ if (!err)
+ {
+ if (!np->nn->dev->store)
+ err = check_dev (np, NULL, flags);
+ if (err)
+ debug ("check_dev return err: %d\n", err);
+ }
+
+ debug ("netfs_check_open_permissions return: %d\n", err);
+ return err;
+}
+
+/* We don't use this function, but it has to be defined. */
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *np, loff_t offset,
+ size_t *len, void *data)
+{
+ return EOPNOTSUPP;
+}
+
+/* We don't use this function, but it has to be defined. */
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *np, loff_t offset,
+ size_t *len, const void *data)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_report_access (struct iouser *cred, struct node *np, int *types)
+{
+ return EOPNOTSUPP;
+}
+
+struct iouser *
+netfs_make_user (uid_t *uids, int nuids, uid_t *gids, int ngids)
+{
+ return NULL;
+}
+
+void
+netfs_node_norefs (struct node *np)
+{
+ return;
+}
+
+/* Returned directory entries are aligned to blocks this many bytes long.
+ Must be a power of two. */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+
+/* Length is structure before the name + the name + '\0', all
+ padded to a four-byte alignment. */
+#define DIRENT_LEN(name_len) \
+ ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \
+ & ~(DIRENT_ALIGN - 1))
+
+static inline int
+bump_size (size_t *size, int *count, const char *name, const int nentries,
+ const vm_size_t buffsize)
+{
+ if (nentries == -1 || *count < nentries)
+ {
+ size_t new_size = *size + DIRENT_LEN (strlen (name));
+ if (buffsize > 0 && new_size > buffsize)
+ return 0;
+
+ *size = new_size;
+ *count += 1;
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int
+add_dir_entry (char **data, const char *name, const ino_t fileno,
+ const int type, int *count, const int nentries, size_t *size)
+{
+ if (nentries == -1 || *count < nentries)
+ {
+ size_t namlen = strlen (name);
+ size_t sz = DIRENT_LEN (namlen);
+
+ if (sz > *size)
+ return 0;
+
+ *size -= sz;
+
+ struct dirent hdr;
+ hdr.d_fileno = fileno;
+ hdr.d_reclen = sz;
+ hdr.d_type = type;
+ hdr.d_namlen = namlen;
+
+ memcpy (*data, &hdr, DIRENT_NAME_OFFS);
+ strcpy (*data + DIRENT_NAME_OFFS, name);
+
+ *data += sz;
+ *count += 1;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *dir, int entry,
+ int nentries, char **data, mach_msg_type_number_t *datacnt,
+ vm_size_t bufsize, int *amt)
+{
+ debug ("netfs_get_dirents (cred: %p, dir: %p, entry: %d, nentries: %d, "
+ "datacnt: %u, bufsize: %u, amt: %d)\n", cred, dir, entry, nentries,
+ *datacnt, bufsize, *amt);
+
+ if (!dir->nn->entries)
+ {
+ debug ("!dir->nn->entries\n");
+ if (dir != netfs_root_node)
+ {
+ debug ("dir != netfs_root_node\n");
+ debug ("netfs_get_dirents return: ENOTDIR\n");
+ return ENOTDIR;
+ }
+
+ error_t err = create_partitions ();
+ if (err)
+ {
+ debug ("err in create_partitions\n");
+ debug ("netfs_get_dirents return: %d\n", err);
+ return err;
+ }
+ }
+
+ if (dir->nn->entries_size + 2 <= entry)
+ {
+ debug ("dir->nn->entries_size + 2 <= entry\n");
+ *datacnt = 0;
+ *amt = 0;
+ *data = NULL;
+ debug ("netfs_get_dirents return: 0\n");
+ return 0;
+ }
+
+ int count = 0;
+ size_t size = 0;
+
+ if (entry == 0)
+ bump_size (&size, &count, ".", nentries, bufsize);
+ if (entry <= 1)
+ bump_size (&size, &count, "..", nentries, bufsize);
+
+ struct node *current_node;
+ for (size_t i = 0; i < dir->nn->entries_size; ++i)
+ {
+ current_node = dir->nn->entries[i];
+ bump_size (&size, &count, current_node->nn->name, nentries, bufsize);
+ }
+
+ void *new_data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (unlikely (new_data == MAP_FAILED))
+ {
+ debug ("new_data == MAP_FAILED\n");
+ debug ("netfs_get_dirents return: %d\n", errno);
+ return errno;
+ }
+
+ *data = (char *) new_data;
+ *datacnt = size;
+ *amt = count;
+
+ count = 0;
+ char *ptr_data = *data;
+
+ if (entry == 0)
+ {
+ debug ("entry == 0\n");
+ add_dir_entry (&ptr_data, ".", dir->nn_stat.st_ino, DT_DIR, &count,
+ nentries, &size);
+ }
+ if (entry <= 1)
+ {
+ debug ("entry <= 1\n");
+ add_dir_entry (&ptr_data, "..", 2, DT_DIR, &count, nentries, &size);
+ }
+
+ debug ("Fill in the real directory entries\n");
+
+ int dirent_type;
+ for (size_t i = 0; i < dir->nn->entries_size; ++i)
+ {
+ current_node = dir->nn->entries[i];
+ if (current_node->nn->dev->store->block_size == 1)
+ dirent_type = DT_CHR;
+ else
+ dirent_type = DT_BLK;
+
+ add_dir_entry (&ptr_data, current_node->nn->name,
+ current_node->nn_stat.st_ino, dirent_type, &count,
+ nentries, &size);
+ }
+
+ debug ("netfs_get_dirents return: 0\n");
+ return 0;
}
--
2.43.0