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