Re: [PATCH] Migrating storeio from trivfs to netfs.
Mikhail Karpov <[email protected]>
| Newsgroups | gmane.os.hurd.bugs |
|---|---|
| Message-ID | <[email protected]> |
Hello,
On Mon, 24 Aug 2026 23:28:06 Samuel Thibault wrote:
> > @@ -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
> > +
>
> Ah, no, we do not want to have to depend on parted for such a low-level
> thing as storeio. Better automatically disable the part of the code that
> adds partitions, it shouldn't be very hard, it'd essentially mean making
> create_partitions just return ENOTDIR, and #ifdef-out the parted-using
> functions.
I think we can do this in a similar way to part.c in libstore.
> > diff --git a/libnetfs/init-init.c b/libnetfs/init-init.c
> > index 19ed0d3..f64c7bb 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");
> >
>
> Better make this a separate patch.
I agree, it can be divided into three separate patches.
> > diff --git a/storeio/dev.c b/storeio/dev.c
> > index c87400c..97b36cd 100644
> > --- a/storeio/dev.c
> > +++ b/storeio/dev.c
> > @@ -22,7 +22,6 @@
> > #include <assert-backtrace.h>
> > #include <string.h>
> > #include <hurd/pager.h>
> > -#include <hurd/store.h>
>
> dev.c is still calling store_create etc. so should include store.h
Is this necessary? dev.h already includes store.h.
> > diff --git a/storeio/pager.c b/storeio/pager.c
> > index 11bf469..7df1a79 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"
> >
> > @@ -51,6 +52,11 @@ pager_read_page (struct user_pager_info *upi,
> > /* Read a partial page if necessary to avoid reading off the
end. */
> > want = store->size - page;
> >
> > + void *new_buf = mmap (0, want, PROT_READ|PROT_WRITE, MAP_ANON,
0, 0);
> > + if (new_buf == MAP_FAILED)
> > + return EIO;
> > +
> > + *((void **) buf) = new_buf;
> > err = dev_read (dev, page, want, (void **)buf, &read);
>
> Mmm, why always allocating? It's better to allocate lazily in case the
> read can be directly be fed by raw_read i.e. store_read, and avoid
> a copy. That means keeping ensure_buf() etc., did you have a reason
> to remove it? You can make pager_read_page set buf to NULL, and let
> dev_read cope with that case, and otherwise try to use the provided
> buffer (netfs_attempt_read case).
I think we can allocate memory in the pager_read_page function if it hasn't
already been allocated. This way, dev_read will behave consistently. To
implement this, we can add the pager_buf and pager_buf_size fields to the
dev structure.
Thanks,
--
Mikhail Karpov
0001-Change-the-order-of-calling-the-maptime_map-function.patch
(text/x-patch, 773 B)
From 0aba87af10bb923fa8e09188c5bb84625a06076e Mon Sep 17 00:00:00 2001 From: Mikhail Karpov <[email protected]> Date: Wed, 26 Aug 2026 16:06:46 +0700 Subject: [PATCH 1/3] Change the order of calling the maptime_map function --- libnetfs/init-init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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"); -- 2.43.0
0002-Adding-hooks-for-create-and-destroy-peropen.patch
(text/x-patch, 4.1 KB)
From f9f844c98c0d6eea72905846d84989b11247b82f Mon Sep 17 00:00:00 2001 From: Mikhail Karpov <[email protected]> Date: Wed, 26 Aug 2026 16:52:50 +0700 Subject: [PATCH 2/3] Adding hooks for create and destroy peropen --- libnetfs/Makefile | 2 +- libnetfs/make-peropen.c | 9 +++++++++ libnetfs/netfs.h | 8 ++++++++ libnetfs/priv.c | 26 ++++++++++++++++++++++++++ libnetfs/release-peropen.c | 8 ++++++++ 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 libnetfs/priv.c 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/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); -- 2.43.0
0003-Migrating-storeio-from-trivfs-to-netfs.patch
(text/x-patch, 78.6 KB)
From 7a6a12f862a21aa7f69b7e80bedbfa6e4ceaafe6 Mon Sep 17 00:00:00 2001 From: Mikhail Karpov <[email protected]> Date: Wed, 26 Aug 2026 17:01:51 +0700 Subject: [PATCH 3/3] Migrating storeio from trivfs to netfs --- storeio/Makefile | 13 +- storeio/dev.c | 123 ++--- storeio/dev.h | 108 ++-- storeio/io.c | 385 -------------- storeio/open.c | 127 ----- storeio/open.h | 68 --- storeio/pager.c | 50 +- storeio/part.c | 160 ++++++ storeio/storeio.c | 1216 +++++++++++++++++++++++++++++++++++---------- 9 files changed, 1291 insertions(+), 959 deletions(-) delete mode 100644 storeio/io.c delete mode 100644 storeio/open.c delete mode 100644 storeio/open.h create mode 100644 storeio/part.c diff --git a/storeio/Makefile b/storeio/Makefile index 5d9aeb4d..616dbc67 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 $(and $(PARTED_LIBS),part.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 = -lpthread $(PARTED_LIBS) $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz) include ../Makeconf diff --git a/storeio/dev.c b/storeio/dev.c index c87400c0..4350b499 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]> @@ -22,7 +22,6 @@ #include <assert-backtrace.h> #include <string.h> #include <hurd/pager.h> -#include <hurd/store.h> #include <sys/mman.h> #include "dev.h" @@ -140,48 +139,33 @@ 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. */ + +/* Called with DEV->lock held. Initialize the DEV struct.*/ 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) { + error_t err = errno; store_free (dev->store); dev->store = 0; - return ENOMEM; + return err; } - if (!dev->inhibit_cache) + if (!storeio_stat.inhibit_cache) { dev->buf_offs = -1; pthread_rwlock_init (&dev->io_lock, NULL); @@ -190,8 +174,37 @@ dev_open (struct dev *dev) pthread_mutex_init (&dev->pager_lock, NULL); } + dev->pager_buf = NULL; + dev->pager_buf_size = 0; + return 0; } + +/* 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 +214,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 +235,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 +372,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. */ @@ -390,27 +403,10 @@ error_t dev_read (struct dev *dev, off_t offs, size_t whole_amount, void **buf, size_t *len) { - error_t err; - int allocated_buf = 0; - error_t ensure_buf (void) - { - if (*len < whole_amount) - { - void *new = mmap (0, whole_amount, PROT_READ|PROT_WRITE, - MAP_ANON, 0, 0); - if (new == (void *) -1) - return errno; - *buf = new; - allocated_buf = 1; - } - return 0; - } error_t buf_read (size_t buf_offs, size_t io_offs, size_t len) { - error_t err = ensure_buf (); - if (! err) - memcpy (*buf + io_offs, dev->buf + buf_offs, len); - return err; + memcpy (*buf + io_offs, dev->buf + buf_offs, len); + return 0; } error_t raw_read (off_t offs, size_t io_offs, size_t len, size_t *amount) { @@ -424,22 +420,15 @@ dev_read (struct dev *dev, off_t offs, size_t whole_amount, a buffer big enough to hold everything, in case we have to coalesce multiple reads into a single return buffer. */ { - error_t err = ensure_buf (); + void *_req_buf = *buf + io_offs, *req_buf = _req_buf; + size_t req_len = len; + error_t err = store_read (store, addr, len, &req_buf, &req_len); if (! err) { - void *_req_buf = *buf + io_offs, *req_buf = _req_buf; - size_t req_len = len; - err = store_read (store, addr, len, &req_buf, &req_len); - if (! err) - { - if (req_buf != _req_buf) - /* Copy from wherever the read put it. */ - { - memcpy (_req_buf, req_buf, req_len); - munmap (req_buf, req_len); - } - *amount = req_len; - } + if (req_buf != _req_buf) + /* Copy from wherever the read put it. */ + memcpy (_req_buf, req_buf, req_len); + *amount = req_len; } return err; } @@ -452,7 +441,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. @@ -476,9 +465,5 @@ dev_read (struct dev *dev, off_t offs, size_t whole_amount, whole_amount, buf, len); } - err = dev_rw (dev, offs, whole_amount, len, buf_read, raw_read); - if (err && allocated_buf) - munmap (*buf, whole_amount); - - return err; + return dev_rw (dev, offs, whole_amount, len, buf_read, raw_read); } diff --git a/storeio/dev.h b/storeio/dev.h index eda7a93d..de7b5542 100644 --- a/storeio/dev.h +++ b/storeio/dev.h @@ -1,6 +1,6 @@ /* store `device' I/O - Copyright (C) 1995,96,97,99,2000,2001 Free Software Foundation, Inc. + Copyright (C) 1995,96,97,99,2000,2001,2026 Free Software Foundation, Inc. Written by Miles Bader <[email protected]> This program is free software; you can redistribute it and/or @@ -23,47 +23,27 @@ #include <mach.h> #include <device/device.h> #include <pthread.h> +#include <hurd/netfs.h> #include <hurd/store.h> -#include <hurd/trivfs.h> +#include <stdio.h> +#include <unistd.h> -extern struct trivfs_control *storeio_fsys; +extern mach_port_t underlying_node; /* Information about backend store, which we presumptively call a "device". */ struct dev { - /* The argument specification that we use to open the store. */ - struct store_parsed *store_name; - /* The device to which we're doing io. This is null when the device is closed, in which case we will open from `store_name'. */ struct store *store; - int readonly; /* Nonzero if user gave --readonly flag. */ - int enforced; /* Nonzero if user gave --enforced flag. */ - int no_fileio; /* Nonzero if user gave --no-fileio flag. */ - dev_t rdev; /* A unixy device number for st_rdev. */ - - /* The current owner of the open device. For terminals, this affects - controlling terminal behavior (see term_become_ctty). For all objects - this affects old-style async IO. Negative values represent pgrps. This - has nothing to do with the owner of a file (as returned by io_stat, and - as used for various permission checks by filesystems). An owner of 0 - indicates that there is no owner. */ - pid_t owner; - /* The number of active opens. */ int nperopens; - /* This lock protects `store', `owner' and `nperopens'. The other - members never change after creation, except for those locked by - io_lock (below). */ + /* This lock protects `store' and 'nperopens'. The other members never + change after creation, except for those locked by io_lock (below). */ pthread_mutex_t lock; - /* Nonzero iff the --no-cache flag was given. - If this is set, the remaining members are not used at all - and don't need to be initialized or cleaned up. */ - int inhibit_cache; - /* A bitmask corresponding to the part of an offset that lies within a device block. */ unsigned block_mask; @@ -81,17 +61,59 @@ struct dev int buf_dirty; struct pager *pager; + void **pager_buf; + size_t pager_buf_size; pthread_mutex_t pager_lock; }; +struct netnode +{ + struct dev *dev; + char *name; + struct node **entries; + + /* If the storage is a disk, this is the number of partitions on it. */ + size_t entries_num; +}; + +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; + +error_t create_node (struct node **node, char *name, struct node *dir); +error_t check_dev (struct node *node, struct store *store, int flags); +error_t create_partitions (void); + 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)); } -/* Called with DEV->lock held. Try to open the store underlying DEV. */ -error_t dev_open (struct dev *dev); +/* Called with DEV->lock held. Initialize the DEV with data from the STORE.*/ +error_t dev_open_from_store (struct dev *dev, struct store *store); + +/* Try to open the store underlying 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. @@ -103,10 +125,10 @@ void dev_close (struct dev *dev); error_t dev_get_memory_object(struct dev *dev, vm_prot_t prot, memory_object_t *memobj); -/* Try to stop all paging activity on DEV, returning true if we were - successful. If NOSYNC is true, then we won't write back any (kernel) - cached pages to the device. */ -int dev_stop_paging (struct dev *dev, int nosync); +/* Try to stop all paging activity, returning true if we were successful. + If NOSYNC is true, then we won't write back any (kernel) cached pages to + the device. */ +int dev_stop_paging (int nosync); /* Try and write out any pending writes to DEV. If WAIT is true, will wait for any paging activity to cease. */ @@ -124,4 +146,22 @@ error_t dev_write (struct dev *dev, off_t offs, const void *buf, size_t len, error_t dev_read (struct dev *dev, off_t offs, size_t amount, void **buf, size_t *len); +#ifdef DEBUG +extern FILE *debug_file; +extern pthread_mutex_t debug_lock; +# define debug(format, ...) \ + do \ + { \ + if (debug_file) \ + { \ + pthread_mutex_lock (&debug_lock); \ + fprintf (debug_file, format, ## __VA_ARGS__); \ + pthread_mutex_unlock (&debug_lock); \ + } \ + } \ + while (0) +#else +# define debug(format, ...) do {} while (0) +#endif + #endif /* !__DEV_H__ */ diff --git a/storeio/io.c b/storeio/io.c deleted file mode 100644 index dca997ef..00000000 --- a/storeio/io.c +++ /dev/null @@ -1,385 +0,0 @@ -/* The hurd io interface to storeio - - Copyright (C) 1995,96,97,99,2000,02 Free Software Foundation, Inc. - Written by Miles Bader <[email protected]> - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#include <hurd/trivfs.h> -#include <stdio.h> -#include <fcntl.h> - -#include "open.h" -#include "dev.h" -#include "libtrivfs/trivfs_fs_S.h" -#include "libtrivfs/trivfs_io_S.h" - -/* Return objects mapping the data underlying this memory object. If - the object can be read then memobjrd will be provided; if the - object can be written then memobjwr will be provided. For objects - where read data and write data are the same, these objects will be - equal, otherwise they will be disjoint. Servers are permitted to - implement io_map but not io_map_cntl. Some objects do not provide - mapping; they will set none of the ports and return an error. Such - objects can still be accessed by io_read and io_write. */ -kern_return_t -trivfs_S_io_map (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - memory_object_t *rd_obj, mach_msg_type_name_t *rd_type, - memory_object_t *wr_obj, mach_msg_type_name_t *wr_type) -{ - if (! cred) - return EOPNOTSUPP; - else if (! (cred->po->openmodes & (O_READ|O_WRITE))) - return EBADF; - else - { - mach_port_t memobj; - int flags = cred->po->openmodes; - vm_prot_t prot = - ((flags & O_READ) ? VM_PROT_READ : 0) - | ((flags & O_WRITE) ? VM_PROT_WRITE : 0); - struct open *open = (struct open *)cred->po->hook; - error_t err = dev_get_memory_object (open->dev, prot, &memobj); - - if (!err) - { - if (flags & O_READ) - *rd_obj = memobj; - else - *rd_obj = MACH_PORT_NULL; - if (flags & O_WRITE) - *wr_obj = memobj; - else - *wr_obj = MACH_PORT_NULL; - - if ((flags & (O_READ|O_WRITE)) == (O_READ|O_WRITE) - && memobj != MACH_PORT_NULL) - mach_port_mod_refs (mach_task_self (), memobj, - MACH_PORT_RIGHT_SEND, 1); - } - - *rd_type = *wr_type = MACH_MSG_TYPE_MOVE_SEND; - - return err; - } -} - -/* Read data from an IO object. If offset if -1, read from the object - maintained file pointer. If the object is not seekable, offset is - ignored. The amount desired to be read is in AMOUNT. */ -kern_return_t -trivfs_S_io_read (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - data_t *data, mach_msg_type_name_t *data_len, - off_t offs, vm_size_t amount) -{ - error_t err; - size_t data_size = *data_len; - - if (! cred) - return EOPNOTSUPP; - else if (! (cred->po->openmodes & O_READ)) - return EBADF; - - err = open_read ((struct open *)cred->po->hook, - offs, amount, (void **)data, &data_size); - if (err) - return err; - *data_len = data_size; - return 0; -} - -/* Tell how much data can be read from the object without blocking for - a "long time" (this should be the same meaning of "long time" used - by the nonblocking flag. */ -kern_return_t -trivfs_S_io_readable (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - vm_size_t *amount) -{ - if (! cred) - return EOPNOTSUPP; - else if (! (cred->po->openmodes & O_READ)) - return EBADF; - else - { - struct open *open = (struct open *)cred->po->hook; - *amount = open->dev->store->size - open->offs; - return 0; - } -} - -/* Write data to an IO object. If offset is -1, write at the object - maintained file pointer. If the object is not seekable, offset is - ignored. The amount successfully written is returned in amount. A - given user should not have more than one outstanding io_write on an - object at a time; servers implement congestion control by delaying - responses to io_write. Servers may drop data (returning ENOBUFS) - if they recevie more than one write when not prepared for it. */ -kern_return_t -trivfs_S_io_write (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - const_data_t data, mach_msg_type_number_t data_len, - off_t offs, vm_size_t *amount) -{ - if (! cred) - return EOPNOTSUPP; - else if (! (cred->po->openmodes & O_WRITE)) - return EBADF; - else - return open_write ((struct open *)cred->po->hook, - offs, data, data_len, amount); -} - -/* Change current read/write offset */ -kern_return_t -trivfs_S_io_seek (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - off_t offs, int whence, off_t *new_offs) -{ - if (! cred) - return EOPNOTSUPP; - else - return open_seek ((struct open *)cred->po->hook, offs, whence, new_offs); -} - -/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG. - Block until one of the indicated types of i/o can be done "quickly", and - return the types that are then available. */ -kern_return_t -trivfs_S_io_select (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - int *type) -{ - if (! cred) - return EOPNOTSUPP; - *type &= ~SELECT_URG; - return 0; -} - -kern_return_t -trivfs_S_io_select_timeout (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - struct timespec ts, - int *type) -{ - return trivfs_S_io_select (cred, reply, reply_type, type); -} - -/* Truncate file. */ -kern_return_t -trivfs_S_file_set_size (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - off_t size) -{ - if (! cred) - return EOPNOTSUPP; - else if (size < 0) - return EINVAL; - else - return 0; -} - -/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and - O_NONBLOCK bits for the IO object. In addition, io_get_openmodes - will tell you which of O_READ, O_WRITE, and O_EXEC the object can - be used for. The O_ASYNC bit affects icky async I/O; good async - I/O is done through io_async which is orthogonal to these calls. */ - -kern_return_t -trivfs_S_io_get_openmodes (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - int *bits) -{ - if (! cred) - return EOPNOTSUPP; - else - { - *bits = cred->po->openmodes; - return 0; - } -} - -kern_return_t -trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred, - mach_port_t reply, - mach_msg_type_name_t reply_type, - int mode) -{ - if (! cred) - return EOPNOTSUPP; - else - return 0; -} - -kern_return_t -trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred, - mach_port_t reply, - mach_msg_type_name_t reply_type, - int bits) -{ - if (! cred) - return EOPNOTSUPP; - else - return 0; -} - -kern_return_t -trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred, - mach_port_t reply, - mach_msg_type_name_t reply_type, - int bits) -{ - if (! cred) - return EOPNOTSUPP; - else - return 0; -} - -/* Get/set the owner of the IO object. For terminals, this affects - controlling terminal behavior (see term_become_ctty). For all - objects this affects old-style async IO. Negative values represent - pgrps. This has nothing to do with the owner of a file (as - returned by io_stat, and as used for various permission checks by - filesystems). An owner of 0 indicates that there is no owner. */ -kern_return_t -trivfs_S_io_get_owner (struct trivfs_protid *cred, - mach_port_t reply, - mach_msg_type_name_t reply_type, - pid_t *owner) -{ - if (! cred) - return EOPNOTSUPP; - else - { - struct open *open = (struct open *)cred->po->hook; - *owner = open->dev->owner; /* atomic word fetch */ - return 0; - } -} - -kern_return_t -trivfs_S_io_mod_owner (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - pid_t owner) -{ - if (! cred) - return EOPNOTSUPP; - else - { - struct open *open = (struct open *)cred->po->hook; - open->dev->owner = owner; /* atomic word store */ - return 0; - } -} - -/* File syncing operations; these all do the same thing, sync the underlying - device. */ - -kern_return_t -trivfs_S_file_sync (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - int wait, int omit_metadata) -{ - if (cred) - return dev_sync (((struct open *)cred->po->hook)->dev, wait); - else - return EOPNOTSUPP; -} - -kern_return_t -trivfs_S_file_syncfs (struct trivfs_protid *cred, - mach_port_t reply, mach_msg_type_name_t reply_type, - int wait, int dochildren) -{ - if (cred) - return dev_sync (((struct open *)cred->po->hook)->dev, wait); - else - return EOPNOTSUPP; -} - -kern_return_t -trivfs_S_file_get_storage_info (struct trivfs_protid *cred, - mach_port_t reply, - mach_msg_type_name_t reply_type, - mach_port_t **ports, - mach_msg_type_name_t *ports_type, - mach_msg_type_number_t *num_ports, - int **ints, mach_msg_type_number_t *num_ints, - off_t **offsets, - mach_msg_type_number_t *num_offsets, - data_t *data, mach_msg_type_number_t *data_len) -{ - *ports_type = MACH_MSG_TYPE_COPY_SEND; - - if (! cred || ! cred->po->hook) - return EOPNOTSUPP; - else - { - error_t err; - struct dev *dev = ((struct open *)cred->po->hook)->dev; - struct store *store = dev->store; - - if (dev->enforced && !(store->flags & STORE_ENFORCED)) - { - /* The --enforced switch tells us not to let anyone - get at the device, no matter how trustable they are. */ - size_t name_len = (store->name ? strlen (store->name) + 1 : 0); - int i; - *num_ports = 0; - i = 0; - (*ints)[i++] = STORAGE_OTHER; - (*ints)[i++] = store->flags; - (*ints)[i++] = store->block_size; - (*ints)[i++] = 1; /* num_runs */ - (*ints)[i++] = name_len; - (*ints)[i++] = 0; /* misc_len */ - *num_ints = i; - i = 0; - (*offsets)[i++] = 0; - (*offsets)[i++] = store->size; - *num_offsets = i; - if (store->name) - memcpy (*data, store->name, name_len); - *data_len = name_len; - return 0; - } - - if (!cred->isroot - && !store_is_securely_returnable (store, cred->po->openmodes)) - { - struct store *clone; - err = store_clone (store, &clone); - if (! err) - { - err = store_set_flags (clone, STORE_INACTIVE); - if (err == EINVAL) - err = EACCES; - else - err = store_return (clone, - ports, num_ports, ints, num_ints, - offsets, num_offsets, data, data_len); - store_free (clone); - } - } - else - err = store_return (store, - ports, num_ports, ints, num_ints, - offsets, num_offsets, data, data_len); - - return err; - } -} diff --git a/storeio/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..b3b569c4 100644 --- a/storeio/pager.c +++ b/storeio/pager.c @@ -27,12 +27,27 @@ #include <error.h> #include <sys/mman.h> #include <stdio.h> +#include <string.h> #include "dev.h" /* ---------------------------------------------------------------- */ /* Pager library callbacks; see <hurd/pager.h> for more info. */ +static error_t +mmap_pager_buf (struct dev *dev, size_t size) +{ + dev->pager_buf = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + if (dev->pager_buf == MAP_FAILED) + { + dev->pager_buf = NULL; + return EIO; + } + + dev->pager_buf_size = size; + return 0; +} + /* For pager PAGER, read one page from offset PAGE. Set *BUF to be the address of the page, and set *WRITE_LOCK if the page must be provided read-only. The only permissible error returns are EIO, EDQUOT, and @@ -51,6 +66,21 @@ pager_read_page (struct user_pager_info *upi, /* Read a partial page if necessary to avoid reading off the end. */ want = store->size - page; + if (!dev->pager_buf) + { + err = mmap_pager_buf (dev, want); + if (err) + return err; + } + else if (dev->pager_buf_size != want) + { + munmap (*dev->pager_buf, dev->pager_buf_size); + err = mmap_pager_buf (dev, want); + if (err) + return err; + } + + *((void **) buf) = dev->pager_buf; err = dev_read (dev, page, want, (void **)buf, &read); if (!err && want < vm_page_size) @@ -61,9 +91,13 @@ pager_read_page (struct user_pager_info *upi, *writelock = (store->flags & STORE_READONLY); if (err || read < want) - return EIO; - else - return 0; + { + munmap (*((void **) buf), want); + dev->pager_buf = NULL; + return EIO; + } + + return 0; } /* For pager PAGER, synchronously write one page from BUF to offset PAGE. @@ -174,11 +208,11 @@ pager_dropweak (struct user_pager_info *upi __attribute__ ((unused))) { } -/* Try to stop all paging activity on DEV, returning true if we were - successful. If NOSYNC is true, then we won't write back any (kernel) - cached pages to the device. */ +/* Try to stop all paging activity, returning true if we were successful. + If NOSYNC is true, then we won't write back any (kernel) cached pages to + the device. */ int -dev_stop_paging (struct dev *dev, int nosync) +dev_stop_paging (int nosync) { size_t num_pagers = (pager_port_bucket ? ports_count_bucket (pager_port_bucket) : 0); @@ -223,7 +257,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/part.c b/storeio/part.c new file mode 100644 index 00000000..768d4824 --- /dev/null +++ b/storeio/part.c @@ -0,0 +1,160 @@ +/* Partition storeio backend + 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 <parted/parted.h> + +#include "dev.h" + +/* 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 int +set_last_partition_num (struct store *store) +{ + debug ("last_partition_num (store: %p):\n", store); + int last_partition_num = 0; + 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"); + goto err_ped_exception_leave_all; + } + + PedDisk *disk = ped_disk_new (device); + if (!disk) + { + debug ("!disk\n"); + goto err_ped_device_close; + } + + if (strcmp (disk->type->name, "loop") == 0) + { + debug ("strcmp (disk->type->name, \"loop\") == 0\n"); + goto err_ped_disk_destroy; + } + + last_partition_num = ped_disk_get_last_partition_num (disk); + if (last_partition_num < 0) + { + debug ("last_partition_num < 0\n"); + last_partition_num = 0; + } + +err_ped_disk_destroy: + ped_disk_destroy (disk); + +err_ped_device_close: + if (!ped_device_close (device)) + debug ("!ped_device_close (device)\n"); + +err_ped_exception_leave_all: + ped_exception_leave_all (); + + debug ("set_last_partition_num return: %d\n", last_partition_num); + return last_partition_num; +} + +static inline char * +create_node_name (const size_t num) +{ + char *buffer; + error_t err = asprintf (&buffer, "%zu", num); + if (err == -1) + return NULL; + + return buffer; +} + +error_t +create_partitions (void) +{ + debug ("create_partitions:\n"); + struct node *dir = netfs_root_node; + size_t last_partition_num = + (size_t) set_last_partition_num (dir->nn->dev->store); + if (last_partition_num == 0) + { + debug ("last_partition_num == 0\n"); + debug ("create_partitions return: ENOTDIR\n"); + return ENOTDIR; + } + + dir->nn->entries_num = 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; + char *node_name; + error_t err; + for (size_t i = 1; i <= last_partition_num; ++i) + { + 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]; + node_name = create_node_name (i); + if (!node_name) + { + debug ("!node_name\n"); + err = ENOMEM; + break; + } + + err = create_node (part, node_name, dir); + if (err) + { + debug ("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; +} diff --git a/storeio/storeio.c b/storeio/storeio.c index 4e8a9628..3076a655 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,27 @@ 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 <dirent.h> #include <hurd.h> #include <hurd/ports.h> -#include <hurd/trivfs.h> #include <version.h> -#include "open.h" #include "dev.h" -#include "libtrivfs/trivfs_fsys_S.h" - +#include "libnetfs/fs_S.h" +#include "libnetfs/io_S.h" +#include "libnetfs/fsys_S.h" +#include "libnetfs/fsys_reply_U.h" + static struct argp_option options[] = { {"readonly", 'r', 0, 0,"Disallow writing"}, @@ -43,8 +47,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 +60,217 @@ 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 +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. */ -}; + error_t err; + struct netnode *netnode = malloc (sizeof (struct netnode)); + if (!netnode) + { + err = errno; + goto return_error; + } + + struct node *new_node = netfs_make_node (netnode); + if (!new_node) + { + err = errno; + goto free_netnode; + } + + 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 = 0, + .st_size = 0, + .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) + { + err = errno; + goto free_new_node; + } + + memset (dev, 0, sizeof (struct dev)); + pthread_mutex_init (&dev->lock, NULL); + new_node->nn->dev = dev; + new_node->nn->name = name; + new_node->nn->entries = NULL; + new_node->nn->entries_num = 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; + return 0; + + free_new_node: + free (new_node); + + free_netnode: + free (netnode); + + return_error: + *node = NULL; + return err; +} + +static inline void +change_node_mode (struct node *node) +{ + node->nn_stat.st_mode &= S_IFMT; + + if (storeio_stat.inhibit_cache) + { + node->nn_stat.st_mode |= S_IFCHR; + return; + } + + if (node->nn->dev->store && node->nn->dev->store->block_size == 1) + node->nn_stat.st_mode |= S_IFCHR; + else + node->nn_stat.st_mode |= S_IFBLK; +} + +error_t +check_dev (struct node *node, struct store *store, int flags) +{ + 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) + { + error_t err; + + if (store) + err = dev_open_from_store (dev, store); + else + err = dev_open (dev, storeio_stat.store_name); + + if (err) + { + pthread_mutex_unlock (&dev->lock); + + debug ("check_dev (node: %p, store: %p, flags: %d):\n", node, + store, flags); + if ((flags & (O_READ | O_WRITE)) == 0) + { + debug ("dev open err: %d, but we are not opening the file for" + " reading or writing, just ignore the error and exit" + " the function. XXX\n", err); + debug ("check_dev return: 0\n"); + return 0; + } + + debug ("dev open return err: %d\n", err); + 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) + change_node_mode (node); + } + pthread_mutex_unlock (&dev->lock); + + return 0; +} + +error_t __attribute__ ((weak)) +create_partitions (void) +{ + return ENOTDIR; +} + +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 +291,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 +317,828 @@ 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) +node_open_create (struct peropen *po) { - struct dev *const dev = trivfs_control->hook; error_t err = 0; + struct dev *dev = po->np->nn->dev; + if (dev->store) + { + pthread_mutex_lock (&dev->lock); + if (dev->nperopens++ == 0) + err = store_clear_flags (dev->store, STORE_INACTIVE); + pthread_mutex_unlock (&dev->lock); + } + + return err; +} - if (!err && dev_is_readonly (dev) && (flags & O_WRITE)) - return EROFS; +error_t (*netfs_peropen_create_hook) (struct peropen *po) = node_open_create; +static void +node_open_destroy (struct peropen *po) +{ + struct dev *dev = po->np->nn->dev; pthread_mutex_lock (&dev->lock); - if (dev->store == NULL) + if (--dev->nperopens == 0) + store_clear_flags (dev->store, STORE_INACTIVE); + pthread_mutex_unlock (&dev->lock); +} + +void (*netfs_peropen_destroy_hook) (struct peropen *po) = node_open_destroy; + +inline static void +devs_lock (void) +{ + pthread_mutex_lock (&netfs_root_node->nn->dev->lock); + for (size_t i = 0; i < netfs_root_node->nn->entries_num; ++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_num; ++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) { - /* 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; + 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; } - pthread_mutex_unlock (&dev->lock); + devs_lock (); + + int force = flags & FSYS_GOAWAY_FORCE; + error_t err = ports_inhibit_class_rpcs (netfs_protid_class); + if (err == EINTR || (err && !force)) + { + debug ("err == EINTR || (err && !force)\n"); + devs_unlock (); + debug ("netfs_S_fsys_goaway return: %d\n", err); + return err; + } + + int nosync = flags & FSYS_GOAWAY_NOSYNC; + if (force && nosync) + { + debug ("force && nosync\n"); + exit (0); + } + + if (!force && ports_count_class (netfs_protid_class) > 0) + { + debug ("!force && ports_count_class (netfs_protid_class) > 0\n"); + ports_enable_class (netfs_protid_class); + ports_resume_class_rpcs (netfs_protid_class); + devs_unlock (); + debug ("netfs_S_fsys_goaway return: EBUSY\n"); + return EBUSY; + } + + if (!nosync) + { + debug ("!nosync\n"); + err = netfs_attempt_syncfs (0, flags); + } + + if (!err && (dev_stop_paging (nosync) || force)) + { + debug ("!err && (dev_stop_paging (nosync) || force)\n"); + if (!nosync) + { + debug ("!nosync\n"); + dev_close (netfs_root_node->nn->dev); + for (size_t i = 0; i < netfs_root_node->nn->entries_num; ++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; } -static error_t -open_hook (struct trivfs_peropen *peropen) +error_t +netfs_validate_stat (struct node *np, struct iouser *cred) +{ + if (np->nn->dev->store) + { + np->nn_stat.st_size = np->nn->dev->store->size; + np->nn_stat.st_blksize = np->nn->dev->store->block_size; + } + else + { + np->nn_stat.st_size = 0; + np->nn_stat.st_blksize = 0; + } + + 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_num; ++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; +} + +error_t +netfs_attempt_lookup (struct iouser *user, struct node *dir, + const char *name, struct node **np) +{ + 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); + pthread_mutex_lock (&(*np)->lock); + return 0; + } + + struct node *current_node = NULL; + struct node *iter; + for (size_t i = 0; i < dir->nn->entries_num; ++i) + { + iter = dir->nn->entries[i]; + + if (strcmp (name, iter->nn->name) == 0) + { + current_node = iter; + break; + } + } + + if (current_node) + { + *np = current_node; + pthread_mutex_lock (&dir->lock); + netfs_nref (*np); + pthread_mutex_unlock (&dir->lock); + pthread_mutex_lock (&(*np)->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; +} + +error_t +netfs_attempt_unlink (struct iouser *user, struct node *dir, const char *name) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_rename (struct iouser *user, struct node *fromdir, + const char *fromname, struct node *todir, + const char *toname, int excl) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_mkdir (struct iouser *user, struct node *dir, const char *name, + mode_t mode) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_rmdir (struct iouser *user, struct node *dir, const char *name) +{ + return EOPNOTSUPP; +} + +error_t +netfs_attempt_link (struct iouser *user, struct node *dir, struct node *file, + const char *name, int excl) +{ + return EOPNOTSUPP; +} + +/* We don't use this function, but we need to unlock the dir. */ +error_t +netfs_attempt_mkfile (struct iouser *user, struct node *dir, mode_t mode, + struct node **np) +{ + pthread_mutex_unlock (&dir->lock); + return EOPNOTSUPP; +} + +/* We don't use this function, but we need to unlock the dir and clear np. */ +error_t +netfs_attempt_create_file (struct iouser *user, struct node *dir, + const char *name, mode_t mode, struct node **np) +{ + *np = NULL; + pthread_mutex_unlock (&dir->lock); + return EOPNOTSUPP; +} + +error_t +netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf) +{ + return EOPNOTSUPP; +} + +error_t +netfs_check_open_permissions (struct iouser *user, struct node *np, + int flags, int newnode) { error_t err = 0; - struct dev *const dev = peropen->cntl->hook; - if (dev->store) + if (!err && flags & O_READ) { - 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 = 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 (!err && flags & O_EXEC) + { + 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; } -static void -close_hook (struct trivfs_peropen *peropen) +error_t +netfs_attempt_read (struct iouser *cred, struct node *np, loff_t offset, + size_t *len, void *data) { - struct dev *const dev = peropen->cntl->hook; + void *our_data = data; + error_t err = dev_read (np->nn->dev, offset, *len, &our_data, len); + if (err) + { + debug ("netfs_attempt_read (np: %p, offset: %lld, *len: %zu," + " data: %p):\n", np, offset, *len, data); + debug ("dev_read return err: %d\n", err); + debug ("netfs_attempt_read return: %d\n", err); + return err; + } - if (peropen->hook) + if (our_data != data) { - 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); + memcpy (data, our_data, *len); + munmap (our_data, *len); } + + return 0; } - -/* ---------------------------------------------------------------- */ -/* Trivfs hooks */ -int trivfs_fstype = FSTYPE_DEV; -int trivfs_fsid = 0; +error_t +netfs_attempt_write (struct iouser *cred, struct node *np, loff_t offset, + size_t *len, const void *data) +{ + error_t err = dev_write (np->nn->dev, offset, data, *len, len); + if (err) + { + debug ("netfs_attempt_write (np: %p, offset: %lld, *len: %zu," + " data: %p):\n", np, offset, *len, data); + debug ("dev_write return err: %d\n", err); + debug ("netfs_attempt_write return: %d\n", err); + } -int trivfs_support_read = 1; -int trivfs_support_write = 1; -int trivfs_support_exec = 0; + return err; +} -int trivfs_allow_open = O_READ | O_WRITE; +error_t +netfs_report_access (struct iouser *cred, struct node *np, int *types) +{ + return EOPNOTSUPP; +} + +mach_port_t +netfs_get_filemap (struct node *np, vm_prot_t prot) +{ + mach_port_t memobj; + errno = dev_get_memory_object (np->nn->dev, prot, &memobj); + if (errno) + { + debug ("netfs_get_filemap (np: %p, vm_prot_t: %d):\n", np, prot); + memobj = MACH_PORT_NULL; + debug ("dev_get_memory_object return err: %d\n", errno); + } + + return memobj; +} + +struct iouser * +netfs_make_user (uid_t *uids, int nuids, uid_t *gids, int ngids) +{ + return NULL; +} void -trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st) +netfs_node_norefs (struct node *np) { - struct dev *const dev = cred->po->cntl->hook; - struct open *open = cred->po->hook; + return; +} - st->st_mode &= ~S_IFMT; +/* 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) - if (open) - /* An open device. */ - { - struct store *store = open->dev->store; - store_offset_t size = store->size; +/* 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)) - if (store->block_size > 1) - st->st_blksize = store->block_size; +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; - st->st_mode |= dev->inhibit_cache ? S_IFCHR : S_IFBLK; + *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; } - 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; + } + } + + if (dir->nn->entries_num + 2 <= entry) + { + *datacnt = 0; + *amt = 0; + *data = NULL; + return 0; + } - pthread_mutex_lock (&device->lock); + int count = 0; + size_t size = 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); + if (entry == 0) + bump_size (&size, &count, ".", nentries, bufsize); + if (entry <= 1) + bump_size (&size, &count, "..", nentries, bufsize); - /* Wait until all pending rpcs are done. */ - err = ports_inhibit_class_rpcs (root_port_class); - if (err == EINTR || (err && !force)) + struct node *current_node; + for (size_t i = 0; i < dir->nn->entries_num; ++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) + *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_num; ++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; +} + +static inline int +is_privileged (const struct idvec *uids) { - struct dev *dev = cntl->hook; - if (dev) - return dev_sync (dev, wait); + return idvec_contains (uids, 0) || idvec_contains (uids, getuid ()); +} + +error_t +netfs_file_get_storage_info (struct iouser *cred, struct node *np, + mach_port_t **ports, + mach_msg_type_name_t *ports_type, + mach_msg_type_number_t *num_ports, + int **ints, + mach_msg_type_number_t *num_ints, + off_t **offsets, + mach_msg_type_number_t *num_offsets, + char **data, + mach_msg_type_number_t *data_len) +{ + if (!cred) + 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)) + { + /* 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); + *num_ports = 0; + int 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; + } + + error_t err; + if (!is_privileged (cred->uids) + && !store_is_securely_returnable (store, np->nn_stat.st_mode)) + { + 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 (clone, ports, num_ports, ints, num_ints, + offsets, num_offsets, data, data_len); + + store_free (clone); + } else - return 0; + err = store_return (store, ports, num_ports, ints, num_ints, + offsets, num_offsets, data, data_len); + + return err; } -- 2.43.0