summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>2000-03-19 20:55:45 +0000
committerRoland McGrath <roland@gnu.org>2000-03-19 20:55:45 +0000
commit402d7b0b9e63a112cf827482a41b3a9c18afbb0f (patch)
treeb551e7f36e6d30cdd1694890c400d566854e5458
parent85f3e87e0238f89ae9a4fa9c57bad675814107fd (diff)
2000-03-19 Roland McGrath <roland@baalperazim.frob.com>
* dev.h (struct dev): New members store_name, readonly, rdev. (dev_is_readonly): New inline function. * dev.c (dev_open): Take just one arg, a struct dev whose store==0. (dev_close): Shut down the store, but leave DEV intact with store==0. * storeio.c (struct storeio_argp_params): New type. (device, device_lock, store_name): Variables removed. (readonly, inhibit_cache, enforce_store, rdev): Likewise. These are all now members in struct storeio_argp_params or struct dev; rdev now uses dev_t instead of int. (parse_opt): Find a struct storeio_argp_params in STATE->input and fill it in accordingly. Use makedev macro to construct rdev. (trivfs_append_args): Find options in struct dev off control hook. Use major, minor macros. (main): Make DEVICE a local here, and point FSYS->hook at it. Don't modify trivfs_allow_open. (getroot_hook): New static function. (trivfs_getroot_hook): New variable, initialized to that. (check_open_hook): Find struct dev in CNTL->hook and use new dev_open interface. Use dev_is_readonly. (open_hook): Find struct dev in PEROPEN->cntl->hook and check DEV->store. (trivfs_modify_stat): Find struct dev in CRED->po->cntl->hook. Use dev_is_readonly. (trivfs_goaway): Find struct dev in FSYS->hook and use its lock. (trivfs_S_fsys_syncfs): Find struct dev in CNTL->hook.
-rw-r--r--storeio/dev.c58
-rw-r--r--storeio/dev.h39
-rw-r--r--storeio/storeio.c164
3 files changed, 153 insertions, 108 deletions
diff --git a/storeio/dev.c b/storeio/dev.c
index 0a713253..ba57f23f 100644
--- a/storeio/dev.c
+++ b/storeio/dev.c
@@ -1,8 +1,7 @@
/* store `device' I/O
- Copyright (C) 1995, 1996, 1998, 1999 Free Software Foundation, Inc.
-
- Written by Miles Bader <miles@gnu.ai.mit.edu>
+ Copyright (C) 1995,96,98,99,2000 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@@ -133,55 +132,49 @@ dev_buf_rw (struct dev *dev, size_t buf_offs, size_t *io_offs, size_t *len,
}
}
-/* Returns a pointer to a new device structure in DEV for the kernel device
- NAME, with the given FLAGS. If BLOCK_SIZE is non-zero, it should be the
- desired block size, and must be a multiple of the device block size.
- If an error occurs, the error code is returned, otherwise 0. */
+/* Called with DEV->lock held. Try to open the store underlying DEV. */
error_t
-dev_open (struct store_parsed *name, int flags, int inhibit_cache,
- struct dev **dev)
+dev_open (struct dev *dev)
{
error_t err;
- struct dev *new = malloc (sizeof (struct dev));
- if (! new)
- return ENOMEM;
+ assert (dev->store == 0);
- err = store_parsed_open (name, flags, &new->store);
+ err = store_parsed_open (dev->store_name,
+ dev->readonly ? STORE_READONLY : 0,
+ &dev->store);
if (err)
- {
- free (new);
- return err;
- }
+ return err;
- new->buf = mmap (0, new->store->block_size, PROT_READ|PROT_WRITE,
+ dev->buf = mmap (0, dev->store->block_size, PROT_READ|PROT_WRITE,
MAP_ANON, 0, 0);
- if (new->buf == (void *) -1)
+ if (dev->buf == MAP_FAILED)
{
- store_free (new->store);
- free (new);
+ store_free (dev->store);
+ dev->store = 0;
return ENOMEM;
}
- new->inhibit_cache = inhibit_cache;
- new->owner = 0;
- if (!inhibit_cache)
+ if (!dev->inhibit_cache)
{
- new->buf_offs = -1;
- rwlock_init (&new->io_lock);
- new->block_mask = (1 << new->store->log2_block_size) - 1;
- new->pager = 0;
- mutex_init (&new->pager_lock);
+ dev->buf_offs = -1;
+ rwlock_init (&dev->io_lock);
+ dev->block_mask = (1 << dev->store->log2_block_size) - 1;
+ dev->pager = 0;
+ mutex_init (&dev->pager_lock);
}
- *dev = new;
return 0;
}
-/* Free DEV and any resources it consumes. */
+/* Shut down the store underlying DEV and free any resources it consumes.
+ DEV itself remains intact so that dev_open can be called again.
+ This should be called with DEV->lock held. */
void
dev_close (struct dev *dev)
{
+ assert (dev->store);
+
if (!dev->inhibit_cache)
{
if (dev->pager != NULL)
@@ -193,8 +186,7 @@ dev_close (struct dev *dev)
}
store_free (dev->store);
-
- free (dev);
+ dev->store = 0;
}
/* Try and write out any pending writes to DEV. If WAIT is true, will wait
diff --git a/storeio/dev.h b/storeio/dev.h
index d6e50102..b223fe7e 100644
--- a/storeio/dev.h
+++ b/storeio/dev.h
@@ -1,8 +1,7 @@
/* store `device' I/O
- Copyright (C) 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
-
- Written by Miles Bader <miles@gnu.ai.mit.edu>
+ Copyright (C) 1995,96,97,99,2000 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@@ -26,12 +25,20 @@
#include <rwlock.h>
#include <hurd/store.h>
-/* Information about a kernel device. */
+/* Information about backend store, which we presumptively call a "device". */
struct dev
{
- /* The device to which we're doing io. */
+ /* 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. */
+ 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
@@ -40,7 +47,9 @@ struct dev
indicates that there is no owner. */
pid_t owner;
- int enforced; /* Nonzero iff --enforced flag was given. */
+ /* This lock protects `store' and `owner'. The other members never
+ change after creation, except for those locked by io_lock (below). */
+ struct mutex lock;
/* Nonzero iff the --no-cache flag was given.
If this is set, the remaining members are not used at all
@@ -68,14 +77,18 @@ struct dev
struct mutex pager_lock;
};
-/* Returns a pointer to a new device structure in DEV for the device
- NAME, with the given FLAGS. If BLOCK_SIZE is non-zero, it should be the
- desired block size, and must be a multiple of the device block size.
- If an error occurs, the error code is returned, otherwise 0. */
-error_t dev_open (struct store_parsed *name, int flags, int inhibit_cache,
- struct dev **dev);
+static inline int
+dev_is_readonly (const struct dev *dev)
+{
+ return dev->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);
-/* Free DEV and any resources it consumes. */
+/* Shut down the store underlying DEV and free any resources it consumes.
+ DEV itself remains intact so that dev_open can be called again.
+ This should be called with DEV->lock held. */
void dev_close (struct dev *dev);
/* Returns in MEMOBJ the port for a memory object backed by the storage on
diff --git a/storeio/storeio.c b/storeio/storeio.c
index 8816f4cd..7c0d4fe4 100644
--- a/storeio/storeio.c
+++ b/storeio/storeio.c
@@ -1,8 +1,7 @@
/* A translator for doing I/O to stores
- Copyright (C) 1995, 96, 97, 98, 99 Free Software Foundation, Inc.
-
- Written by Miles Bader <miles@gnu.ai.mit.edu>
+ Copyright (C) 1995,96,97,98,99,2000 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@@ -48,46 +47,39 @@ static const char doc[] = "Translator for devices and other stores";
const char *argp_program_version = STANDARD_HURD_VERSION (storeio);
-/* The open store. */
-static struct dev *device = NULL;
-/* And a lock to arbitrate changes to it. */
-static struct mutex device_lock;
-
/* Desired store parameters specified by the user. */
-struct store_parsed *store_name;
-static int readonly;
-
-/* Nonzero if user gave --no-cache flag. */
-static int inhibit_cache;
-
-/* Nonzero if user gave --enforced flag. */
-static int enforce_store;
-
-/* A unixy device number to return when the device is stat'd. */
-static int rdev;
+struct storeio_argp_params
+{
+ struct store_argp_params store_params; /* Filled in by store_argp parser. */
+ struct dev *dev; /* We fill in its flag members. */
+};
/* Parse a single option. */
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
+ struct storeio_argp_params *params = state->input;
+
switch (key)
{
- case 'r': readonly = 1; break;
- case 'w': readonly = 0; break;
- case 'c': inhibit_cache = 1; break;
- case 'e': enforce_store = 1; 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 'n':
{
char *start = arg, *end;
+ dev_t rdev;
rdev = strtoul (start, &end, 0);
if (*end == ',')
/* MAJOR,MINOR form */
{
start = end;
- rdev = (rdev << 8) + strtoul (start, &end, 0);
+ rdev = makedev (rdev, strtoul (start, &end, 0));
}
if (end == start || *end != '\0')
@@ -95,11 +87,22 @@ parse_opt (int key, char *arg, struct argp_state *state)
argp_error (state, "%s: Invalid argument to --rdev", arg);
return EINVAL;
}
+
+ params->dev->rdev = rdev;
}
break;
case ARGP_KEY_INIT:
- state->child_inputs[0] = state->input; break;
+ /* Now store_argp's parser will get to initialize its state.
+ The default_type member is our input parameter to it. */
+ bzero (&params->store_params, sizeof params->store_params);
+ params->store_params.default_type = "device";
+ state->child_inputs[0] = &params->store_params;
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ params->dev->store_name = params->store_params.result;
+ break;
default:
return ARGP_ERR_UNKNOWN;
@@ -116,14 +119,14 @@ main (int argc, char *argv[])
error_t err;
mach_port_t bootstrap;
struct trivfs_control *fsys;
- struct store_argp_params store_params = { default_type: "device" };
+ struct dev device;
+ struct storeio_argp_params params;
- argp_parse (&argp, argc, argv, 0, 0, &store_params);
- store_name = store_params.result;
+ bzero (&device, sizeof device);
+ mutex_init (&device.lock);
- if (readonly)
- /* Catch illegal writes at the point of open. */
- trivfs_allow_open &= ~O_WRITE;
+ params.dev = &device;
+ argp_parse (&argp, argc, argv, 0, 0, &params);
task_get_bootstrap_port (mach_task_self (), &bootstrap);
if (bootstrap == MACH_PORT_NULL)
@@ -134,9 +137,7 @@ main (int argc, char *argv[])
if (err)
error (3, err, "trivfs_startup");
- /* Open the device only when necessary. */
- device = NULL;
- mutex_init (&device_lock);
+ fsys->hook = &device;
/* Launch. */
ports_manage_port_operations_multithread (fsys->pi.bucket, trivfs_demuxer,
@@ -149,30 +150,54 @@ error_t
trivfs_append_args (struct trivfs_control *trivfs_control,
char **argz, size_t *argz_len)
{
+ struct dev *const dev = trivfs_control->hook;
error_t err = 0;
- if (rdev)
+ if (dev->rdev != (dev_t) 0)
{
char buf[40];
- snprintf (buf, sizeof buf, "--rdev=%d,%d", (rdev >> 8), rdev & 0xFF);
+ snprintf (buf, sizeof buf, "--rdev=%d,%d",
+ major (dev->rdev), minor (dev->rdev));
err = argz_add (argz, argz_len, buf);
}
- if (!err && inhibit_cache)
+ if (!err && dev->inhibit_cache)
err = argz_add (argz, argz_len, "--no-cache");
- if (!err && enforce_store)
+ if (!err && dev->enforced)
err = argz_add (argz, argz_len, "--enforced");
if (! err)
- err = argz_add (argz, argz_len, readonly ? "--readonly" : "--writable");
+ err = argz_add (argz, argz_len,
+ dev->readonly ? "--readonly" : "--writable");
if (! err)
- err = store_parsed_append_args (store_name, argz, argz_len);
+ err = store_parsed_append_args (dev->store_name, argz, argz_len);
return err;
}
+/* Called whenever a new lookup is done of our node. The only reason we
+ set this hook is to duplicate the check done normally done against
+ trivfs_allow_open in trivfs_S_fsys_getroot, but looking at the
+ per-device state. This gets checked again in check_open_hook, but this
+ hook runs before a little but more overhead gets incurred. In the
+ success case, we just return EAGAIN to have trivfs_S_fsys_getroot
+ continue with its generic processing. */
+static error_t
+getroot_hook (struct trivfs_control *cntl,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_port_type,
+ mach_port_t dotdot,
+ uid_t *uids, u_int nuids, uid_t *gids, u_int 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. */
@@ -181,27 +206,23 @@ check_open_hook (struct trivfs_control *trivfs_control,
struct iouser *user,
int flags)
{
+ struct dev *const dev = trivfs_control->hook;
error_t err = 0;
- if (!err && readonly && (flags & O_WRITE))
+ if (!err && dev_is_readonly (dev) && (flags & O_WRITE))
return EROFS;
- mutex_lock (&device_lock);
- if (device == NULL)
- /* Try and open the device. */
+ mutex_lock (&dev->lock);
+ if (dev->store == NULL)
{
- err = dev_open (store_name, readonly ? STORE_READONLY : 0, inhibit_cache,
- &device);
- if (err)
- device = NULL;
- else
- device->enforced = enforce_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 word correctly. XXX */
err = 0;
}
- mutex_unlock (&device_lock);
+ mutex_unlock (&dev->lock);
return err;
}
@@ -209,8 +230,8 @@ check_open_hook (struct trivfs_control *trivfs_control,
static error_t
open_hook (struct trivfs_peropen *peropen)
{
- struct dev *dev = device;
- if (dev)
+ struct dev *const dev = peropen->cntl->hook;
+ if (dev->store)
return open_create (dev, (struct open **)&peropen->hook);
else
return 0;
@@ -238,6 +259,7 @@ int trivfs_allow_open = O_READ | O_WRITE;
void
trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
{
+ struct dev *const dev = cred->po->cntl->hook;
struct open *open = cred->po->hook;
st->st_mode &= ~S_IFMT;
@@ -254,7 +276,7 @@ trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
st->st_size = size;
st->st_blocks = size / 512;
- st->st_mode |= ((inhibit_cache || store->block_size == 1)
+ st->st_mode |= ((dev->inhibit_cache || store->block_size == 1)
? S_IFCHR : S_IFBLK);
}
else
@@ -264,32 +286,36 @@ trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
st->st_size = 0;
st->st_blocks = 0;
- st->st_mode |= inhibit_cache ? S_IFCHR : S_IFBLK;
+ st->st_mode |= dev->inhibit_cache ? S_IFCHR : S_IFBLK;
}
- st->st_rdev = rdev;
- if (readonly || (open && (open->dev->store->flags & STORE_READONLY)))
+ st->st_rdev = dev->rdev;
+ if (dev_is_readonly (dev))
st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
}
error_t
trivfs_goaway (struct trivfs_control *fsys, int flags)
{
+ struct dev *const device = fsys->hook;
error_t err;
int force = (flags & FSYS_GOAWAY_FORCE);
int nosync = (flags & FSYS_GOAWAY_NOSYNC);
struct port_class *root_port_class = fsys->protid_class;
- mutex_lock (&device_lock);
+ mutex_lock (&device->lock);
- if (device == NULL)
+ if (device->store == NULL)
+ /* The device is not actually open.
+ XXX note that exitting here nukes non-io users, like someone
+ in the middle of a stat who will get SIGLOST or something. */
exit (0);
/* Wait until all pending rpcs are done. */
err = ports_inhibit_class_rpcs (root_port_class);
if (err == EINTR || (err && !force))
{
- mutex_unlock (&device_lock);
+ mutex_unlock (&device->lock);
return err;
}
@@ -322,12 +348,26 @@ trivfs_goaway (struct trivfs_control *fsys, int flags)
/* Allow normal operations to proceed. */
ports_enable_class (root_port_class);
ports_resume_class_rpcs (root_port_class);
- mutex_unlock (&device_lock);
+ 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,
+ uid_t *uids, u_int nuids, uid_t *gids, u_int 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
@@ -355,7 +395,7 @@ 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 = device;
+ struct dev *dev = cntl->hook;
if (dev)
return dev_sync (dev, wait);
else