aboutsummaryrefslogtreecommitdiff
path: root/libstore/device.c
diff options
context:
space:
mode:
Diffstat (limited to 'libstore/device.c')
-rw-r--r--libstore/device.c326
1 files changed, 270 insertions, 56 deletions
diff --git a/libstore/device.c b/libstore/device.c
index 9036699f..3a72df48 100644
--- a/libstore/device.c
+++ b/libstore/device.c
@@ -1,9 +1,7 @@
/* Mach device store backend
- Copyright (C) 1995, 1996 Free Software Foundation, Inc.
-
- Written by Miles Bader <miles@gnu.ai.mit.edu>
-
+ Copyright (C) 1995,96,97,99,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
This file is part of the GNU Hurd.
The GNU Hurd is free software; you can redistribute it and/or
@@ -18,7 +16,7 @@
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. */
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
#include <assert.h>
#include <stdio.h>
@@ -28,47 +26,229 @@
#include "store.h"
+static inline error_t
+dev_error (error_t err)
+{
+ /* Give the canonical POSIX error codes,
+ rather than letting the Mach code propagate up. */
+ switch (err)
+ {
+ case D_IO_ERROR: return EIO;
+ case D_WOULD_BLOCK: return EAGAIN;
+ case D_NO_SUCH_DEVICE: return ENXIO;
+ case D_ALREADY_OPEN: return EBUSY;
+ case D_DEVICE_DOWN: return ENXIO; /* ? */
+ case D_INVALID_OPERATION: return EBADF; /* ? */
+ case D_NO_MEMORY: return ENOMEM;
+ default:
+ break;
+ }
+ /* Anything unexpected propagates up where weirdness will get noticed. */
+ return err;
+}
+
static error_t
dev_read (struct store *store,
- off_t addr, size_t index, mach_msg_type_number_t amount,
- char **buf, mach_msg_type_number_t *len)
-{
- error_t err = device_read (store->port, 0, addr, amount, (io_buf_ptr_t *)buf, len);
- char rep_buf[20];
- if (err)
- strcpy (rep_buf, "-");
- else if (*len > sizeof rep_buf - 3)
- sprintf (rep_buf, "\"%.*s\"...", (int)(sizeof rep_buf - 6), *buf);
- else
- sprintf (rep_buf, "\"%.*s\"", (int)(sizeof rep_buf - 3), *buf);
- fprintf (stderr, "; dev_read (%ld, %d, %d) [%d] => %s, %s, %d\n",
- addr, index, amount, store->block_size, err ? strerror (err) : "-",
- rep_buf, err ? 0 : *len);
- return err;
+ store_offset_t addr, size_t index, mach_msg_type_number_t amount,
+ void **buf, mach_msg_type_number_t *len)
+{
+ return dev_error (device_read (store->port, 0, addr, amount,
+ (io_buf_ptr_t *)buf, len));
}
static error_t
dev_write (struct store *store,
- off_t addr, size_t index, char *buf, mach_msg_type_number_t len,
+ store_offset_t addr, size_t index,
+ const void *buf, mach_msg_type_number_t len,
mach_msg_type_number_t *amount)
{
- return device_write (store->port, 0, addr, (io_buf_ptr_t)buf, len, amount);
+ error_t err = dev_error (device_write (store->port, 0, addr,
+ (io_buf_ptr_t)buf, len,
+ (int *) amount));
+ *amount = *(int *) amount; /* stupid device.defs uses int */
+ return err;
+}
+
+static error_t
+dev_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
}
static error_t
-dev_decode (struct store_enc *enc, struct store_class *classes,
+dev_decode (struct store_enc *enc, const struct store_class *const *classes,
struct store **store)
{
return store_std_leaf_decode (enc, _store_device_create, store);
}
-static struct store_class
-dev_class =
+static error_t
+dev_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
{
- STORAGE_DEVICE, "device", dev_read, dev_write,
- store_std_leaf_allocate_encoding, store_std_leaf_encode, dev_decode
+ return dev_error (store_device_open (name, flags, store));
+}
+
+static error_t
+dopen (const char *name, device_t *device, int *mod_flags)
+{
+ device_t dev_master;
+ error_t err = get_privileged_ports (0, &dev_master);
+ if (! err)
+ {
+ if (*mod_flags & STORE_HARD_READONLY)
+ err = device_open (dev_master, D_READ, (char *)name, device);
+ else
+ {
+ err = device_open (dev_master, D_WRITE | D_READ, (char *)name, device);
+ if (err == ED_READ_ONLY)
+ {
+ err = device_open (dev_master, D_READ, (char *)name, device);
+ if (! err)
+ *mod_flags |= STORE_HARD_READONLY;
+ }
+ else if (! err)
+ *mod_flags &= ~STORE_HARD_READONLY;
+ }
+ mach_port_deallocate (mach_task_self (), dev_master);
+ }
+ return err;
+}
+
+static void
+dclose (struct store *store)
+{
+ mach_port_deallocate (mach_task_self (), store->port);
+ store->port = MACH_PORT_NULL;
+}
+
+/* Return 0 if STORE's range is enforce by the kernel, otherwise an error. */
+static error_t
+enforced (struct store *store)
+{
+ error_t err;
+ dev_status_data_t sizes;
+ size_t sizes_len = DEV_STATUS_MAX;
+
+ if (store->num_runs != 1 || store->runs[0].start != 0)
+ /* Can't enforce non-contiguous ranges, or one not starting at 0. */
+ return EINVAL;
+ else
+ /* See if the the current (one) range is that the kernel is enforcing. */
+ {
+#ifdef DEV_GET_RECORDS
+ err =
+ device_get_status (store->port, DEV_GET_RECORDS, sizes, &sizes_len);
+
+ if (err && err != D_INVALID_OPERATION)
+ return EINVAL;
+
+ if (!err)
+ {
+ assert (sizes_len == DEV_GET_RECORDS_COUNT);
+
+ if (sizes[DEV_GET_RECORDS_RECORD_SIZE] != store->block_size
+ || (store->runs[0].length !=
+ sizes[DEV_GET_RECORDS_DEVICE_RECORDS]))
+ return EINVAL;
+
+ return 0;
+ }
+ else
+#endif
+ {
+ sizes_len = DEV_STATUS_MAX;
+ err =
+ device_get_status (store->port, DEV_GET_SIZE, sizes, &sizes_len);
+
+ if (err)
+ return EINVAL;
+
+ assert (sizes_len == DEV_GET_SIZE_COUNT);
+
+ if (sizes[DEV_GET_SIZE_RECORD_SIZE] != store->block_size
+ || (store->runs[0].length !=
+ sizes[DEV_GET_SIZE_DEVICE_SIZE] >> store->log2_block_size))
+ return EINVAL;
+
+ return 0;
+ }
+ }
+}
+
+static error_t
+dev_set_flags (struct store *store, int flags)
+{
+ if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
+ /* Trying to set flags we don't support. */
+ return EINVAL;
+
+ if (! ((store->flags | flags) & STORE_INACTIVE))
+ /* Currently active and staying that way, so we must be trying to set the
+ STORE_ENFORCED flag. */
+ {
+ error_t err = enforced (store);
+ if (err)
+ return err;
+ }
+
+ if (flags & STORE_INACTIVE)
+ dclose (store);
+
+ store->flags |= flags; /* When inactive, anything goes. */
+
+ return 0;
+}
+
+static error_t
+dev_clear_flags (struct store *store, int flags)
+{
+ error_t err = 0;
+ if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
+ err = EINVAL;
+ if (!err && (flags & STORE_INACTIVE))
+ err = store->name ? dopen (store->name, &store->port, &store->flags) : ENODEV;
+ if (! err)
+ store->flags &= ~flags;
+ return err;
+}
+
+static error_t
+dev_map (const struct store *store, vm_prot_t prot, mach_port_t *memobj)
+{
+ size_t nruns = store->num_runs;
+
+ if (nruns > 1 || (nruns == 1 && store->runs[0].start != 0))
+ return EOPNOTSUPP;
+ else
+ {
+ /* Note that older Mach drivers (through GNU Mach 1.x) ignore
+ the OFFSET and SIZE parameters. The OSKit-Mach drivers obey
+ them, and so the size we pass must be large enough (or zero
+ only if the size is indeterminable). If using only the newer
+ drivers, we could remove the `start != 0' condition above and
+ support kernel mapping of partial devices. However, since
+ the older drivers silently ignore the OFFSET argument, that
+ would produce scrambled results on old kernels. */
+ error_t err = device_map (store->port, prot,
+ store->runs[0].start,
+ store->runs[0].length,
+ memobj, 0);
+ if (err == ED_INVALID_OPERATION)
+ err = EOPNOTSUPP; /* This device doesn't support paging. */
+ return err;
+ }
+}
+
+const struct store_class
+store_device_class =
+{
+ STORAGE_DEVICE, "device", dev_read, dev_write, dev_set_size,
+ store_std_leaf_allocate_encoding, store_std_leaf_encode, dev_decode,
+ dev_set_flags, dev_clear_flags, 0, 0, 0, dev_open, 0, dev_map
};
-_STORE_STD_CLASS (dev_class);
+STORE_STD_CLASS (device);
/* Return a new store in STORE referring to the mach device DEVICE. Consumes
the send right DEVICE. */
@@ -76,22 +256,57 @@ error_t
store_device_create (device_t device, int flags, struct store **store)
{
struct store_run run;
- size_t sizes[DEV_GET_SIZE_COUNT], block_size;
- size_t sizes_len = DEV_GET_SIZE_COUNT;
- error_t err = device_get_status (device, DEV_GET_SIZE, sizes, &sizes_len);
+ size_t block_size = 0;
+ dev_status_data_t sizes;
+ size_t sizes_len = DEV_STATUS_MAX;
+ error_t err;
- if (err)
- return err;
+#ifdef DEV_GET_RECORDS
+ err = device_get_status (device, DEV_GET_RECORDS, sizes, &sizes_len);
+ if (! err && sizes_len == DEV_GET_RECORDS_COUNT)
+ {
+ block_size = sizes[DEV_GET_RECORDS_RECORD_SIZE];
- assert (sizes_len == DEV_GET_SIZE_COUNT);
+ if (block_size)
+ {
+ run.start = 0;
+ run.length = sizes[DEV_GET_RECORDS_DEVICE_RECORDS];
+ }
+ }
+ else
+#endif
+ {
+ /* Some Mach devices do not implement device_get_status, but do not
+ return an error. To detect these devices we set the size of the
+ input buffer to something larger than DEV_GET_SIZE_COUNT. If the
+ size of the returned device status is not equal to
+ DEV_GET_SIZE_COUNT, we know that something is wrong. */
+ sizes_len = DEV_STATUS_MAX;
+ err = device_get_status (device, DEV_GET_SIZE, sizes, &sizes_len);
+ if (! err && sizes_len == DEV_GET_SIZE_COUNT)
+ {
+ block_size = sizes[DEV_GET_SIZE_RECORD_SIZE];
- block_size = sizes[DEV_GET_SIZE_RECORD_SIZE];
- run.start = 0;
- run.length = sizes[DEV_GET_SIZE_DEVICE_SIZE] / block_size;
+ if (block_size)
+ {
+ run.start = 0;
+ run.length = sizes[DEV_GET_SIZE_DEVICE_SIZE] / block_size;
+
+ if (run.length * block_size != sizes[DEV_GET_SIZE_DEVICE_SIZE])
+ /* Bogus results (which some mach devices return). */
+ block_size = 0;
+ }
+ }
+ }
flags |= STORE_ENFORCED; /* 'cause it's the whole device. */
- return _store_device_create (device, flags, block_size, &run, 1, store);
+ if (block_size == 0)
+ /* Treat devices that can't do device_get_status as zero-length. */
+ return _store_device_create (device, flags, 0, &run, 0, store);
+ else
+ /* Make a store with one run covering the whole device. */
+ return _store_device_create (device, flags, block_size, &run, 1, store);
}
/* Like store_device_create, but doesn't query the device for information. */
@@ -100,29 +315,28 @@ _store_device_create (device_t device, int flags, size_t block_size,
const struct store_run *runs, size_t num_runs,
struct store **store)
{
- *store =
- _make_store (&dev_class, device, flags, block_size, runs, num_runs, 0);
- return *store ? 0 : ENOMEM;
+ return
+ _store_create (&store_device_class, device, flags, block_size,
+ runs, num_runs, 0, store);
}
/* Open the device NAME, and return the corresponding store in STORE. */
error_t
store_device_open (const char *name, int flags, struct store **store)
{
- device_t dev_master, device;
- int open_flags = ((flags & STORE_HARD_READONLY) ? 0 : D_WRITE) | D_READ;
- error_t err = get_privileged_ports (0, &dev_master);
-
- if (err)
- return err;
-
- err = device_open (dev_master, open_flags, (char *)name, &device);
-
- mach_port_deallocate (mach_task_self (), dev_master);
-
- err = store_device_create (device, flags, store);
- if (err)
- mach_port_deallocate (mach_task_self (), device);
-
+ device_t device;
+ error_t err = dopen (name, &device, &flags);
+ if (! err)
+ {
+ err = store_device_create (device, flags, store);
+ if (! err)
+ {
+ err = store_set_name (*store, name);
+ if (err)
+ store_free (*store);
+ }
+ if (err)
+ mach_port_deallocate (mach_task_self (), device);
+ }
return err;
}