summaryrefslogtreecommitdiff
path: root/libstore
diff options
context:
space:
mode:
Diffstat (limited to 'libstore')
-rw-r--r--libstore/Makefile89
-rw-r--r--libstore/argp.c394
-rw-r--r--libstore/bunzip2.c13
-rw-r--r--libstore/clone.c91
-rw-r--r--libstore/copy.c267
-rw-r--r--libstore/create.c79
-rw-r--r--libstore/crypt.h12
-rw-r--r--libstore/decode.c203
-rw-r--r--libstore/derive.c87
-rw-r--r--libstore/device.c342
-rw-r--r--libstore/do-bunzip2.c87
-rw-r--r--libstore/do-gunzip.c80
-rw-r--r--libstore/enc.c98
-rw-r--r--libstore/encode.c178
-rw-r--r--libstore/file.c303
-rw-r--r--libstore/flags.c66
-rw-r--r--libstore/gunzip.c33
-rw-r--r--libstore/kids.c312
-rw-r--r--libstore/make.c100
-rw-r--r--libstore/map.c79
-rw-r--r--libstore/memobj.c197
-rw-r--r--libstore/module.c178
-rw-r--r--libstore/mvol.c158
-rw-r--r--libstore/nbd.c529
-rw-r--r--libstore/open.c65
-rw-r--r--libstore/part.c209
-rw-r--r--libstore/rdwr.c298
-rw-r--r--libstore/remap.c345
-rw-r--r--libstore/set.c78
-rw-r--r--libstore/std.c44
-rw-r--r--libstore/store.h800
-rw-r--r--libstore/stripe.c293
-rw-r--r--libstore/task.c205
-rw-r--r--libstore/typed.c177
-rw-r--r--libstore/unknown.c231
-rw-r--r--libstore/unzipstore.c267
-rw-r--r--libstore/url.c92
-rw-r--r--libstore/util.c19
-rw-r--r--libstore/xinl.c2
-rw-r--r--libstore/zero.c196
40 files changed, 7296 insertions, 0 deletions
diff --git a/libstore/Makefile b/libstore/Makefile
new file mode 100644
index 00000000..28f56609
--- /dev/null
+++ b/libstore/Makefile
@@ -0,0 +1,89 @@
+# Makefile for libstore
+#
+# Copyright (C) 1995,96,97,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
+# 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.
+
+dir := libstore
+makemode := library
+
+libname = libstore
+SRCS = create.c derive.c make.c rdwr.c set.c \
+ enc.c encode.c decode.c clone.c argp.c kids.c flags.c \
+ open.c xinl.c typed.c map.c url.c unknown.c \
+ stripe.c $(filter-out ileave.c concat.c,$(store-types:=.c))
+
+store-types = \
+ concat \
+ copy \
+ device \
+ file \
+ ileave \
+ memobj \
+ module \
+ mvol \
+ nbd \
+ remap \
+ task \
+ zero
+
+# This has to be evaluated after config.make has been included;
+# as a consequence, using 'ifneq' or similar is not an option.
+store-types += \
+ $(and $(PARTED_LIBS),part) \
+ $(and $(HAVE_LIBBZ2),bunzip2) \
+ $(and $(HAVE_LIBZ),gunzip) \
+
+libstore.so-LDLIBS += $(PARTED_LIBS) -ldl
+installhdrs=store.h
+
+HURDLIBS = shouldbeinlibc
+LDLIBS += -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz)
+GUNZIP_OBJS = do-gunzip.o util.o
+BUNZIP2_OBJS = do-bunzip2.o
+OBJS = $(SRCS:.c=.o) \
+ $(and $(HAVE_LIBZ),$(GUNZIP_OBJS)) \
+ $(and $(HAVE_LIBBZ2),$(BUNZIP2_OBJS))
+
+include ../Makeconf
+
+module-CPPFLAGS = -D'STORE_SONAME_SUFFIX=".so.$(hurd-version)"'
+module-DEPS = $(..)config.make
+
+libstore_gunzip.so.$(hurd-version): $(GUNZIP_OBJS:.o=_pic.o)
+libstore_bunzip2.so.$(hurd-version): $(BUNZIP2_OBJS:.o=_pic.o)
+
+# You can use this rule to make a dynamically-loadable version of any
+# of the modules. We don't make any of these by default, since we
+# just include all the standard store types in libstore.so itself.
+libstore_%.so.$(hurd-version): %_pic.o libstore.so
+ $(CC) -shared -Wl,-soname=$@ -o $@ \
+ $(rpath) $(CFLAGS) $(LDFLAGS) $(libstore_$*.so-LDFLAGS) $^
+
+# Each libstore_TYPE.a is in fact an object file script so that `-lstore_TYPE'
+# just has the same effect as `-u store_TYPE_class'.
+$(store-types:%=libstore_%.a): libstore_%.a: $(srcdir)/Makefile
+ $(CC) -r -nostdlib -nostartfiles -x c /dev/null \
+ -o $@ -u store_$*_class
+cleantarg += $(store-types:%=libstore_%.a)
+
+all: $(store-types:%=libstore_%.a)
+
+install: $(store-types:%=$(libdir)/libstore_%.a)
+$(store-types:%=$(libdir)/libstore_%.a): $(libdir)/%: %
+ $(INSTALL_DATA) $< $@
diff --git a/libstore/argp.c b/libstore/argp.c
new file mode 100644
index 00000000..6ed79964
--- /dev/null
+++ b/libstore/argp.c
@@ -0,0 +1,394 @@
+/* Store argument parsing
+
+ Copyright (C) 1996,97,98,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
+ 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 <string.h>
+#include <assert.h>
+#include <hurd.h>
+#include <argp.h>
+#include <argz.h>
+
+#include "store.h"
+
+/* We use this class variable instead of just the name so that we ensure
+ linking in store_open to define it. */
+#define DEFAULT_STORE_CLASS store_query_class
+
+static const struct argp_option options[] = {
+ {"store-type",'T', "TYPE", 0, "Each DEVICE names a store of type TYPE"},
+ {"machdev", 'm', 0, OPTION_HIDDEN}, /* deprecated */
+ {"interleave",'I', "BLOCKS", 0, "Interleave in runs of length BLOCKS"},
+ {"layer", 'L', 0, 0, "Layer multiple devices for redundancy"},
+ {0}
+};
+
+static const char args_doc[] = "DEVICE...";
+static const char doc[] = "\vIf neither --interleave or --layer is specified,"
+" multiple DEVICEs are concatenated.";
+
+struct store_parsed
+{
+ /* Names of devices parsed. */
+ char *names;
+ size_t names_len;
+
+ /* Prefix that should be applied to each member of NAMES. */
+ char *name_prefix;
+
+ /* --store-type specified. This defaults to the `query' type. */
+ const struct store_class *type;
+
+ /* A vector of class pointers used to lookup class names. Defaults to
+ STORE_STD_CLASSES. */
+ const struct store_class *const *classes;
+
+ /* DEFAULT_TYPE field passed to parser. */
+ const struct store_class *default_type;
+
+ store_offset_t interleave; /* --interleave value */
+ int layer : 1; /* --layer specified */
+};
+
+void
+store_parsed_free (struct store_parsed *parsed)
+{
+ if (parsed->names_len > 0)
+ free (parsed->names);
+ if (parsed->name_prefix)
+ free (parsed->name_prefix);
+ free (parsed);
+}
+
+/* Add the arguments PARSED, and return the corresponding store in STORE. */
+error_t
+store_parsed_append_args (const struct store_parsed *parsed,
+ char **args, size_t *args_len)
+{
+ char buf[40];
+ error_t err = 0;
+ size_t num_names = argz_count (parsed->names, parsed->names_len);
+
+ if (!err && num_names > 1 && (parsed->interleave || parsed->layer))
+ {
+ if (parsed->interleave)
+ snprintf (buf, sizeof buf, "--interleave=%Ld", parsed->interleave);
+ else
+ snprintf (buf, sizeof buf, "--layer=%d", parsed->layer);
+ err = argz_add (args, args_len, buf);
+ }
+
+ if (!err && parsed->type != parsed->default_type)
+ {
+ if (parsed->name_prefix)
+ /* A name prefix of "PFX" is equivalent to appending ":PFX" to the
+ type name. */
+ {
+ size_t npfx_len = strlen (parsed->name_prefix);
+ char tname[strlen ("--store-type=")
+ + strlen (parsed->type->name) + 1 + npfx_len + 1];
+ snprintf (tname, sizeof tname, "--store-type=%s:%.*s",
+ parsed->type->name, (int) npfx_len, parsed->name_prefix);
+ err = argz_add (args, args_len, tname);
+ }
+ else
+ /* A simple type name. */
+ {
+ snprintf (buf, sizeof buf, "--store-type=%s", parsed->type->name);
+ err = argz_add (args, args_len, buf);
+ }
+ }
+
+ if (! err)
+ err = argz_append (args, args_len, parsed->names, parsed->names_len);
+
+ return err;
+}
+
+error_t
+store_parsed_name (const struct store_parsed *parsed, char **name)
+{
+ char buf[40];
+ char *pfx = 0;
+
+ if (argz_count (parsed->names, parsed->names_len) > 1)
+ {
+ if (parsed->interleave)
+ {
+ snprintf (buf, sizeof buf, "interleave(%Ld,", parsed->interleave);
+ pfx = buf;
+ }
+ else if (parsed->layer)
+ pfx = "layer(";
+ }
+
+ if (pfx)
+ *name = malloc (strlen (pfx) + parsed->names_len + 1);
+ else
+ *name = malloc (parsed->names_len);
+
+ if (! *name)
+ return ENOMEM;
+
+ if (pfx)
+ {
+ char *end = stpcpy (*name, pfx);
+ bcopy (parsed->names, end, parsed->names_len);
+ argz_stringify (end, parsed->names_len, ',');
+ strcpy (end + parsed->names_len, ")");
+ }
+ else
+ {
+ bcopy (parsed->names, *name, parsed->names_len);
+ argz_stringify (*name, parsed->names_len, ',');
+ }
+
+ return 0;
+}
+
+/* Open PARSED, and return the corresponding store in STORE. */
+error_t
+store_parsed_open (const struct store_parsed *parsed, int flags,
+ struct store **store)
+{
+ size_t pfx_len = parsed->name_prefix ? strlen (parsed->name_prefix) : 0;
+ size_t num = argz_count (parsed->names, parsed->names_len);
+
+ error_t open (char *name, struct store **store)
+ {
+ const struct store_class *type = parsed->type;
+ if (type->open)
+ {
+ if (parsed->name_prefix)
+ /* If there's a name prefix, we prefix any names we open with that
+ and a colon. */
+ {
+ char pfxed_name[pfx_len + 1 + strlen (name) + 1];
+ stpcpy (stpcpy (stpcpy (pfxed_name, parsed->name_prefix),
+ ":"),
+ name);
+ return (*type->open) (pfxed_name, flags, parsed->classes, store);
+ }
+ else
+ return (*type->open) (name, flags, parsed->classes, store);
+ }
+ else
+ return EOPNOTSUPP;
+ }
+
+ if (num == 1)
+ return open (parsed->names, store);
+ else if (num == 0)
+ return open (0, store);
+ else
+ {
+ int i;
+ char *name;
+ error_t err = 0;
+ struct store **stores = malloc (sizeof (struct store *) * num);
+
+ if (! stores)
+ return ENOMEM;
+
+ for (i = 0, name = parsed->names;
+ !err && i < num;
+ i++, name = argz_next (parsed->names, parsed->names_len, name))
+ err = open (name, &stores[i]);
+
+ if (! err)
+ {
+ if (parsed->interleave)
+ err =
+ store_ileave_create (stores, num, parsed->interleave,
+ flags, store);
+ else if (parsed->layer)
+ assert (! parsed->layer);
+ else
+ err = store_concat_create (stores, num, flags, store);
+ }
+
+ if (err)
+ {
+ while (i > 0)
+ store_free (stores[i--]);
+ free (stores);
+ }
+
+ return err;
+ }
+}
+
+static const struct store_class *
+find_class (const char *name, const struct store_class *const *const classes)
+{
+ const struct store_class *const *cl;
+ for (cl = classes ?: __start_store_std_classes;
+ classes ? *cl != 0 : cl < __stop_store_std_classes;
+ ++cl)
+ if ((*cl)->name && strcmp (name, (*cl)->name) == 0)
+ return *cl;
+
+# pragma weak store_module_find_class
+ if (! classes && store_module_find_class)
+ {
+ const struct store_class *cl;
+ if (store_module_find_class (name, strchr (name, '\0'), &cl) == 0)
+ return cl;
+ }
+
+ return 0;
+}
+
+/* Print a parsing error message and (if exiting is turned off) return the
+ error code ERR. Requires a variable called STATE to be in scope. */
+#define PERR(err, fmt, args...) \
+ do { argp_error (state, fmt , ##args); return err; } while (0)
+
+/* Parse a --store-type/-T option. */
+static error_t
+parse_type (char *arg, struct argp_state *state, struct store_parsed *parsed)
+{
+ char *name_prefix = 0;
+ char *type_name = arg;
+ const struct store_class *type;
+ char *class_sep = strchr (arg, ':');
+
+ if (class_sep)
+ /* A `:'-separated class name "T1:T2" is equivalent to prepending "T2:"
+ to the device name passed to T1, and is useful for the case where T1
+ takes typed names of the form "T:NAME". A trailing `:', like "T1:" is
+ equivalent to prefixing `:' to the device name, which causes NAME to
+ be opened with store_open, as a file. */
+ {
+ type_name = strndupa (arg, class_sep - arg);
+ name_prefix = class_sep + 1;
+ }
+
+ type = find_class (type_name, parsed->classes);
+ if (!type || !type->open)
+ PERR (EINVAL, "%s: Invalid argument to --store-type", arg);
+ else if (type != parsed->type && parsed->type != parsed->default_type)
+ PERR (EINVAL, "--store-type specified multiple times");
+
+ parsed->type = type;
+ parsed->name_prefix = name_prefix;
+
+ return 0;
+}
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ error_t err;
+ struct store_parsed *parsed = state->hook;
+
+ switch (opt)
+ {
+ case 'm':
+ arg = "device";
+ /* fall through */
+ case 'T':
+ return parse_type (arg, state, parsed);
+
+ case 'I':
+ if (parsed->layer)
+ PERR (EINVAL, "--layer and --interleave are exclusive");
+ if (parsed->interleave)
+ /* Actually no reason why we couldn't support this.... */
+ PERR (EINVAL, "--interleave specified multiple times");
+
+ parsed->interleave = atoi (arg);
+ if (! parsed->interleave)
+ PERR (EINVAL, "%s: Bad value for --interleave", arg);
+ break;
+
+ case 'L':
+#if 1
+ argp_failure (state, 5, 0, "--layer not implemented");
+ return EINVAL;
+#else
+ if (parsed->interleave)
+ PERR (EINVAL, "--layer and --interleave are exclusive");
+ parsed->layer = 1;
+#endif
+ break;
+
+ case ARGP_KEY_ARG:
+ /* A store device to use! */
+ if (parsed->type->validate_name)
+ err = (*parsed->type->validate_name) (arg, parsed->classes);
+ else
+ err = 0;
+ if (! err)
+ err = argz_add (&parsed->names, &parsed->names_len, arg);
+ if (err)
+ argp_failure (state, 1, err, "%s", arg);
+ return err;
+ break;
+
+ case ARGP_KEY_INIT:
+ /* Initialize our parsing state. */
+ {
+ struct store_argp_params *params = state->input;
+ if (! params)
+ return EINVAL; /* Need at least a way to return a result. */
+ parsed = state->hook = malloc (sizeof (struct store_parsed));
+ if (! parsed)
+ return ENOMEM;
+ bzero (parsed, sizeof (struct store_parsed));
+ parsed->classes = params->classes;
+ parsed->default_type =
+ find_class (params->default_type ?: DEFAULT_STORE_CLASS.name,
+ parsed->classes);
+ if (! parsed->default_type)
+ {
+ free (parsed);
+ return EINVAL;
+ }
+ parsed->type = parsed->default_type;
+ }
+ break;
+
+ case ARGP_KEY_ERROR:
+ /* Parsing error occurred, free everything. */
+ store_parsed_free (parsed); break;
+
+ case ARGP_KEY_SUCCESS:
+ /* Successfully finished parsing, return a result. */
+ if (parsed->names == 0
+ && (!parsed->type->validate_name
+ || (*parsed->type->validate_name) (0, parsed->classes) != 0))
+ {
+ struct store_argp_params *params = state->input;
+ store_parsed_free (parsed);
+ if (!params->store_optional)
+ PERR (EINVAL, "No store specified");
+ parsed = 0;
+ }
+ ((struct store_argp_params *)state->input)->result = parsed;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+struct argp
+store_argp = { options, parse_opt, args_doc, doc };
diff --git a/libstore/bunzip2.c b/libstore/bunzip2.c
new file mode 100644
index 00000000..ec0630ec
--- /dev/null
+++ b/libstore/bunzip2.c
@@ -0,0 +1,13 @@
+#include <errno.h>
+
+extern void do_bunzip2 (void); /* Entry point to bunzip2 engine. */
+
+static error_t
+DO_UNZIP (void)
+{
+ do_bunzip2 ();
+ return 0;
+}
+
+#define UNZIP bunzip2
+#include "unzipstore.c"
diff --git a/libstore/clone.c b/libstore/clone.c
new file mode 100644
index 00000000..cdbd5747
--- /dev/null
+++ b/libstore/clone.c
@@ -0,0 +1,91 @@
+/* Store cloning
+
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ 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 <string.h>
+#include <stdlib.h>
+
+#include "store.h"
+
+/* Return in TO a copy of FROM. */
+error_t
+store_clone (struct store *from, struct store **to)
+{
+ struct store *c;
+ error_t err =
+ _store_create (from->class, from->port, from->flags, from->block_size,
+ from->runs, from->num_runs, from->end, &c);
+
+ if (err)
+ return err;
+
+ if (from->name)
+ {
+ c->name = strdup (from->name);
+ if (! c->name)
+ err = ENOMEM;
+ }
+
+ if (from->misc_len)
+ {
+ c->misc = malloc (from->misc_len);
+ if (! c->misc)
+ err = ENOMEM;
+ }
+
+ if (!err && c->port != MACH_PORT_NULL)
+ {
+ err = mach_port_mod_refs (mach_task_self (),
+ c->port, MACH_PORT_RIGHT_SEND, 1);
+ if (err)
+ c->port = MACH_PORT_NULL; /* Don't deallocate it. */
+ }
+ if (!err && from->source != MACH_PORT_NULL)
+ {
+ err = mach_port_mod_refs (mach_task_self (),
+ from->source, MACH_PORT_RIGHT_SEND, 1);
+ if (! err)
+ c->source = from->source;
+ }
+ if (!err && from->num_children > 0)
+ {
+ int k;
+
+ c->children = malloc (from->num_children * sizeof (struct store *));
+ if (! c->children)
+ err = ENOMEM;
+
+ for (k = 0; !err && k < from->num_children; k++)
+ {
+ err = store_clone (from->children[k], &c->children[k]);
+ if (! err)
+ c->num_children++;
+ }
+ }
+
+ if (!err && from->class->clone)
+ err = (*from->class->clone)(from, c);
+
+ if (err)
+ store_free (c);
+ else
+ *to = c;
+
+ return err;
+}
diff --git a/libstore/copy.c b/libstore/copy.c
new file mode 100644
index 00000000..c670ebf3
--- /dev/null
+++ b/libstore/copy.c
@@ -0,0 +1,267 @@
+/* Copy store backend
+
+ Copyright (C) 1995,96,97,99,2000,01,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
+ 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 <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <sys/mman.h>
+#include <mach.h>
+
+#define page_aligned(addr) (((size_t) addr & (vm_page_size - 1)) == 0)
+
+#include "store.h"
+
+static error_t
+copy_read (struct store *store, store_offset_t addr, size_t index,
+ size_t amount, void **buf, size_t *len)
+{
+ char *data = store->hook + (addr * store->block_size);
+
+ if (page_aligned (data) && page_aligned (amount))
+ /* When reading whole pages, we can avoid any real copying. */
+ return vm_read (mach_task_self (),
+ (vm_address_t) data, amount,
+ (pointer_t *) buf, len);
+
+ if (*len < amount)
+ /* Have to allocate memory for the return value. */
+ {
+ *buf = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*buf == MAP_FAILED)
+ return errno;
+ }
+
+ memcpy (*buf, data, amount);
+ *len = amount;
+ return 0;
+}
+
+static error_t
+copy_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len, size_t *amount)
+{
+ char *data = store->hook + (addr * store->block_size);
+
+ if (page_aligned (data) && page_aligned (len) && page_aligned (buf))
+ {
+ /* When writing whole pages, we can avoid any real copying. */
+ error_t err = vm_write (mach_task_self (),
+ (vm_address_t) data, (vm_address_t) buf, len);
+ *amount = len;
+ return err;
+ }
+
+ memcpy (data, buf, len);
+ *amount = len;
+ return 0;
+}
+
+static error_t
+copy_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+copy_allocate_encoding (const struct store *store, struct store_enc *enc)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+copy_encode (const struct store *store, struct store_enc *enc)
+{
+ return EOPNOTSUPP;
+}
+
+static error_t
+copy_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ return EOPNOTSUPP;
+}
+
+static error_t
+copy_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_copy_open (name, flags, classes, store);
+}
+
+static error_t
+copy_set_flags (struct store *store, int flags)
+{
+ if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
+ /* Trying to set flags we don't support. */
+ return EINVAL;
+
+ /* ... */
+
+ store->flags |= flags; /* When inactive, anything goes. */
+
+ return 0;
+}
+
+static error_t
+copy_clear_flags (struct store *store, int flags)
+{
+ error_t err = 0;
+ if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
+ err = EINVAL;
+ /* ... */
+ if (! err)
+ store->flags &= ~flags;
+ return err;
+}
+
+/* Called just before deallocating STORE. */
+void
+copy_cleanup (struct store *store)
+{
+ if (store->size > 0)
+ munmap (store->hook, store->size);
+}
+
+/* Copy any format-dependent fields in FROM to TO; if there's some reason
+ why the copy can't be made, an error should be returned. This call is
+ made after all format-indendependent fields have been cloned. */
+error_t
+copy_clone (const struct store *from, struct store *to)
+{
+ void *buf;
+ buf = mmap (0, to->size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (buf != (void *) -1)
+ {
+ to->hook = buf;
+ memcpy (to->hook, from->hook, from->size);
+ return 0;
+ }
+ return errno;
+}
+
+const struct store_class
+store_copy_class =
+{
+ STORAGE_COPY, "copy", copy_read, copy_write, copy_set_size,
+ copy_allocate_encoding, copy_encode, copy_decode,
+ copy_set_flags, copy_clear_flags, copy_cleanup, copy_clone, 0, copy_open
+};
+STORE_STD_CLASS (copy);
+
+/* Return a new store in STORE which contains a snapshot of the contents of
+ the store FROM; FROM is consumed. */
+error_t
+store_copy_create (struct store *from, int flags, struct store **store)
+{
+ error_t err;
+ struct store_run run;
+
+ run.start = 0;
+ run.length = from->size;
+
+ flags |= STORE_ENFORCED; /* Only uses local resources. */
+
+ err =
+ _store_create (&store_copy_class,
+ MACH_PORT_NULL, flags, from->block_size, &run, 1, 0,
+ store);
+ if (! err)
+ {
+ size_t buf_len = 0;
+
+ /* Copy the input store. */
+ err = store_read (from, 0, from->size, &(*store)->hook, &buf_len);
+
+ if (! err)
+ /* Set the store name. */
+ {
+ if (from->name)
+ {
+ size_t len =
+ strlen (from->class->name) + 1 + strlen (from->name) + 1;
+ (*store)->name = malloc (len);
+ if ((*store)->name)
+ snprintf ((*store)->name, len,
+ "%s:%s", from->class->name, from->name);
+ }
+ else
+ (*store)->name = strdup (from->class->name);
+
+ if (! (*store)->name)
+ err = ENOMEM;
+ }
+
+ if (err)
+ store_free (*store);
+ }
+
+ return err;
+}
+
+/* Return a new store in STORE which contains the memory buffer BUF, of
+ length BUF_LEN, and uses the block size BLOCK_SIZE. BUF must be
+ vm_allocated, and will be consumed, and BUF_LEN must be a multiple of
+ BLOCK_SIZE. */
+error_t
+store_buffer_create (void *buf, size_t buf_len, int flags,
+ struct store **store)
+{
+ error_t err;
+ struct store_run run;
+
+ run.start = 0;
+ run.length = buf_len;
+
+ flags |= STORE_ENFORCED; /* Only uses local resources. */
+
+ err =
+ _store_create (&store_copy_class,
+ MACH_PORT_NULL, flags, 1, &run, 1, 0, store);
+ if (! err)
+ (*store)->hook = buf;
+
+ return err;
+}
+
+/* Open the copy store NAME -- which consists of another store-class name, a
+ ':', and a name for that store class to open -- and return the
+ corresponding store in STORE. CLASSES is used to select classes specified
+ by the type name; if it is 0, STORE_STD_CLASSES is used. */
+error_t
+store_copy_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ struct store *from;
+ error_t err =
+ store_typed_open (name, flags | STORE_HARD_READONLY, classes, &from);
+
+ if (! err)
+ {
+ err = store_copy_create (from, flags, store);
+ if (err)
+ store_free (from);
+ }
+
+ return err;
+}
diff --git a/libstore/create.c b/libstore/create.c
new file mode 100644
index 00000000..010a053e
--- /dev/null
+++ b/libstore/create.c
@@ -0,0 +1,79 @@
+/* Store creation
+
+ Copyright (C) 1995,96,97,2001 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
+ 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 <hurd/fs.h>
+
+#include "store.h"
+
+/* Return a new store in STORE, which refers to the storage underlying SOURCE.
+ CLASSES is used to select classes specified by the provider; if it is 0,
+ STORE_STD_CLASSES is used. FLAGS is set with store_set_flags, with the
+ exception of STORE_INACTIVE, which merely indicates that no attempt should
+ be made to activate an inactive store; if STORE_INACTIVE is not specified,
+ and the store returned for SOURCE is inactive, an attempt is made to
+ activate it (failure of which causes an error to be returned). A reference
+ to SOURCE is created (but may be destroyed with store_close_source). */
+error_t
+store_create (file_t source, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ error_t err;
+ struct store_enc enc;
+ mach_port_t inline_ports[10];
+ int inline_ints[60];
+ off_t inline_offsets[60];
+ char inline_data[100];
+
+ store_enc_init (&enc, inline_ports, 10, inline_ints, 60,
+ inline_offsets, 60, inline_data, 100);
+
+ err = file_get_storage_info (source,
+ &enc.ports, &enc.num_ports,
+ &enc.ints, &enc.num_ints,
+ &enc.offsets, &enc.num_offsets,
+ &enc.data, &enc.data_len);
+ if (err)
+ return err;
+
+ err = store_decode (&enc, classes, store);
+ if (! err)
+ {
+ if (flags & STORE_INACTIVE)
+ flags &= ~STORE_INACTIVE; /* Don't actually make store inactive. */
+ else if ((*store)->flags & STORE_INACTIVE)
+ err = store_clear_flags (*store, STORE_INACTIVE);
+ if (!err && flags)
+ err = store_set_flags (*store, flags);
+ if (err)
+ store_free (*store);
+ }
+ else if (err == EINVAL && (flags &~ STORE_INACTIVE) == STORE_NO_FILEIO)
+ /* Open a generic "unknown" store that can regurgitate this encoding. */
+ err = store_unknown_decode (&enc, classes, store);
+
+ store_enc_dealloc (&enc);
+
+ if (! err)
+ /* Keep a reference to SOURCE around. */
+ (*store)->source = source;
+
+ return err;
+}
diff --git a/libstore/crypt.h b/libstore/crypt.h
new file mode 100644
index 00000000..2a4c203c
--- /dev/null
+++ b/libstore/crypt.h
@@ -0,0 +1,12 @@
+/* crypt.h (dummy version) -- do not perform encryption
+ * Hardly worth copyrighting :-)
+ */
+
+#ifdef CRYPT
+# undef CRYPT /* dummy version */
+#endif
+
+#define RAND_HEAD_LEN 12 /* length of encryption random header */
+
+#define zencode
+#define zdecode
diff --git a/libstore/decode.c b/libstore/decode.c
new file mode 100644
index 00000000..64405ecd
--- /dev/null
+++ b/libstore/decode.c
@@ -0,0 +1,203 @@
+/* Store wire decoding
+
+ Copyright (C) 1996,97,98,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
+ 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 <string.h>
+#include <malloc.h>
+
+#include "store.h"
+
+/* The maximum number of runs for which we allocate run vectors on the stack. */
+#define MAX_STACK_RUNS (16*1024 / sizeof (struct store_run))
+
+/* Decodes the standard leaf encoding that's common to various builtin
+ formats, and calls CREATE to actually create the store. */
+error_t
+store_std_leaf_decode (struct store_enc *enc,
+ store_std_leaf_create_t create,
+ struct store **store)
+{
+ char *misc, *name;
+ error_t err;
+ int type, flags;
+ mach_port_t port;
+ size_t block_size, num_runs, name_len, misc_len;
+ /* Call CREATE appriately from within store_with_decoded_runs. */
+ error_t call_create (const struct store_run *runs, size_t num_runs)
+ {
+ return (*create)(port, flags, block_size, runs, num_runs, store);
+ }
+
+ /* Make sure there are enough encoded ints and ports. */
+ if (enc->cur_int + 6 > enc->num_ints || enc->cur_port + 1 > enc->num_ports)
+ return EINVAL;
+
+ /* Read encoded ints. */
+ type = enc->ints[enc->cur_int++];
+ flags = enc->ints[enc->cur_int++];
+ block_size = enc->ints[enc->cur_int++];
+ num_runs = enc->ints[enc->cur_int++];
+ name_len = enc->ints[enc->cur_int++];
+ misc_len = enc->ints[enc->cur_int++];
+
+ /* Make sure there are enough encoded offsets and data. */
+ if (enc->cur_offset + num_runs * 2 > enc->num_offsets
+ || enc->cur_data + name_len + misc_len > enc->data_len)
+ return EINVAL;
+
+ if (name_len > 0 && enc->data[enc->cur_data + name_len - 1] != '\0')
+ return EINVAL; /* Name not terminated. */
+
+ if (name_len > 0)
+ {
+ name = strdup (enc->data + enc->cur_data);
+ if (! name)
+ return ENOMEM;
+ enc->cur_data += name_len;
+ }
+ else
+ name = 0;
+
+ if (misc_len > 0)
+ {
+ misc = malloc (misc_len);
+ if (! misc)
+ {
+ if (name)
+ free (name);
+ return ENOMEM;
+ }
+ memcpy (misc, enc->data + enc->cur_data + name_len, misc_len);
+ enc->cur_data += misc_len;
+ }
+ else
+ misc = 0;
+
+ /* Read encoded ports (be careful to deallocate this if we barf). */
+ port = enc->ports[enc->cur_port++];
+
+ err = store_with_decoded_runs (enc, num_runs, call_create);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), port);
+ if (misc)
+ free (misc);
+ if (name)
+ free (name);
+ }
+ else
+ {
+ (*store)->flags = flags;
+ (*store)->name = name;
+ (*store)->misc = misc;
+ (*store)->misc_len = misc_len;
+ }
+
+ return err;
+}
+
+/* Call FUN with the vector RUNS of length RUNS_LEN extracted from ENC. */
+error_t
+store_with_decoded_runs (struct store_enc *enc, size_t num_runs,
+ error_t (*fun) (const struct store_run *runs,
+ size_t num_runs))
+{
+ int i;
+ error_t err;
+
+ /* Since the runs are passed in an array of off_t pairs, and we use struct
+ store_run, we have to make a temporary array to hold the (probably
+ bitwise identical) converted representation to pass to CREATE. */
+ if (num_runs <= MAX_STACK_RUNS)
+ {
+ struct store_run runs[num_runs];
+ off_t *e = enc->offsets + enc->cur_offset;
+ for (i = 0; i < num_runs; i++)
+ {
+ runs[i].start = *e++;
+ runs[i].length = *e++;
+ }
+ enc->cur_offset = e - enc->offsets;
+ err = (*fun)(runs, num_runs);
+ }
+ else
+ /* Ack. Too many runs to allocate the temporary RUNS array on the stack.
+ This will probably never happen. */
+ {
+ struct store_run *runs = malloc (num_runs * sizeof (struct store_run));
+ if (runs)
+ {
+ off_t *e = enc->offsets + enc->cur_offset;
+ for (i = 0; i < num_runs; i++)
+ {
+ runs[i].start = *e++;
+ runs[i].length = *e++;
+ }
+ enc->cur_offset = e - enc->offsets;
+ err = (*fun) (runs, num_runs);
+ free (runs);
+ }
+ else
+ err = ENOMEM;
+ }
+
+ return err;
+}
+
+/* Decode ENC, either returning a new store in STORE, or an error. CLASSES
+ defines the mapping from hurd storage class ids to store classes; if it is
+ 0, STORE_STD_CLASSES is used. If nothing else is to be done with ENC, its
+ contents may then be freed using store_enc_dealloc. */
+error_t
+store_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ const struct store_class *const *cl;
+
+ if (enc->cur_int >= enc->num_ints)
+ /* The first int should always be the type. */
+ return EINVAL;
+
+ if (enc->ints[enc->cur_int] == STORAGE_NETWORK)
+ /* This is a special case because store classes supporting
+ individual URL types will also use STORAGE_NETWORK,
+ and we want the generic dispatcher to come first. */
+ return store_url_decode (enc, classes, store);
+
+ for (cl = classes ?: __start_store_std_classes;
+ classes ? *cl != 0 : cl < __stop_store_std_classes;
+ ++cl)
+ if ((*cl)->id == enc->ints[enc->cur_int])
+ {
+ if ((*cl)->decode)
+ return (*(*cl)->decode) (enc, classes, store);
+ else
+ return EOPNOTSUPP;
+ }
+
+# pragma weak store_module_decode
+ if (! classes && store_module_decode)
+ {
+ error_t err = store_module_decode (enc, classes, store);
+ if (err != ENOENT)
+ return err;
+ }
+
+ return EINVAL;
+}
diff --git a/libstore/derive.c b/libstore/derive.c
new file mode 100644
index 00000000..a76fbe1c
--- /dev/null
+++ b/libstore/derive.c
@@ -0,0 +1,87 @@
+/* Calculation of various derived store fields
+
+ Copyright (C) 1995-97,2001 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
+ 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 <assert.h>
+#include <sys/types.h>
+#include <mach.h>
+
+#include "store.h"
+
+/* Fills in the values of the various fields in STORE that are derivable from
+ the set of runs & the block size. */
+void
+_store_derive (struct store *store)
+{
+ unsigned i;
+ struct store_run *runs = store->runs;
+ unsigned num_runs = store->num_runs;
+ size_t bsize = store->block_size;
+
+ /* BLOCK & SIZE */
+ store->blocks = 0;
+ store->wrap_src = 0;
+
+ for (i = 0; i < num_runs; i++)
+ {
+ store->wrap_src += runs[i].length;
+ if (runs[i].start >= 0) /* Not a hole */
+ store->blocks += runs[i].length;
+ }
+
+ if (store->end == 0)
+ /* END not set; set it using the info from RUNS. */
+ store->end = store->wrap_src;
+ else if (store->wrap_src < store->end)
+ /* A wrapped disk! RUNS is repeated N times to reach END. Adjust BLOCKS
+ to include all iterations. */
+ {
+ size_t num_iters = store->end / store->wrap_src;
+ store_offset_t last_part_base = num_iters * store->wrap_src;
+
+ store->blocks *= num_iters;
+
+ for (i = 0; i < num_runs; i++)
+ if (last_part_base + runs[i].length < store->end)
+ {
+ store->blocks += store->end - (last_part_base + runs[i].length);
+ break;
+ }
+ else if (runs[i].start >= 0)
+ store->blocks += runs[i].length;
+
+ /* WRAP_DST must be set by the caller. */
+ }
+
+ store->size = store->end * bsize;
+
+ store->log2_block_size = 0;
+ store->log2_blocks_per_page = 0;
+
+ if (bsize != 0)
+ {
+ while ((1 << store->log2_block_size) < bsize)
+ store->log2_block_size++;
+ assert ((1 << store->log2_block_size) == bsize);
+
+ while ((bsize << store->log2_blocks_per_page) < vm_page_size)
+ store->log2_blocks_per_page++;
+ assert ((bsize << store->log2_blocks_per_page) == vm_page_size);
+ }
+}
diff --git a/libstore/device.c b/libstore/device.c
new file mode 100644
index 00000000..3a72df48
--- /dev/null
+++ b/libstore/device.c
@@ -0,0 +1,342 @@
+/* Mach device store backend
+
+ 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
+ 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <hurd.h>
+
+#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,
+ 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,
+ store_offset_t addr, size_t index,
+ const void *buf, mach_msg_type_number_t len,
+ mach_msg_type_number_t *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, const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_std_leaf_decode (enc, _store_device_create, store);
+}
+
+static error_t
+dev_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ 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 (device);
+
+/* Return a new store in STORE referring to the mach device DEVICE. Consumes
+ the send right DEVICE. */
+error_t
+store_device_create (device_t device, int flags, struct store **store)
+{
+ struct store_run run;
+ size_t block_size = 0;
+ dev_status_data_t sizes;
+ size_t sizes_len = DEV_STATUS_MAX;
+ error_t 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];
+
+ 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];
+
+ 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. */
+
+ 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. */
+error_t
+_store_device_create (device_t device, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ 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 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;
+}
diff --git a/libstore/do-bunzip2.c b/libstore/do-bunzip2.c
new file mode 100644
index 00000000..d2bc9da1
--- /dev/null
+++ b/libstore/do-bunzip2.c
@@ -0,0 +1,87 @@
+/* bunzip2 decompression
+
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Ignazio Sgalmuzzo <ignaker@gmail.com>
+
+ 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 <bzlib.h>
+
+/* I/O interface */
+extern int (*unzip_read) (char *buf, size_t maxread);
+extern void (*unzip_write) (const char *buf, size_t nwrite);
+extern void (*unzip_read_error) (void);
+extern void (*unzip_error) (const char *msg);
+
+/* bzip2 doesn't require window sliding. Just for buffering. */
+#define INBUFSIZ 0x1000
+#define OUTBUFSIZ 0x1000
+
+static char inbuf[INBUFSIZ];
+static char outbuf[OUTBUFSIZ];
+
+#ifdef SMALL_BZIP2
+#define SMALL_MODE 1
+#else
+#define SMALL_MODE 0
+#endif
+
+void
+do_bunzip2 (void)
+{
+ int result;
+ bz_stream strm;
+
+ strm.bzalloc = NULL;
+ strm.bzfree = NULL;
+ strm.opaque = NULL;
+
+ strm.avail_in = 0;
+ strm.next_out = outbuf;
+ strm.avail_out = OUTBUFSIZ;
+
+ result = BZ2_bzDecompressInit (&strm, 0, SMALL_MODE);
+
+ while (result == BZ_OK)
+ {
+ if (strm.avail_in == 0)
+ {
+ strm.next_in = inbuf;
+ strm.avail_in = (*unzip_read)(strm.next_in, INBUFSIZ);
+
+ if (strm.avail_in == 0)
+ break;
+ }
+
+ result = BZ2_bzDecompress (&strm);
+
+ if ((result != BZ_OK) && (result != BZ_STREAM_END))
+ break;
+
+ if ((strm.avail_out == 0) || (result == BZ_STREAM_END))
+ {
+ (*unzip_write) (outbuf, OUTBUFSIZ - strm.avail_out);
+ strm.next_out = outbuf;
+ strm.avail_out = OUTBUFSIZ;
+ }
+ }
+
+ BZ2_bzDecompressEnd (&strm);
+
+ if (result != BZ_STREAM_END)
+ (*unzip_error) (NULL);
+}
diff --git a/libstore/do-gunzip.c b/libstore/do-gunzip.c
new file mode 100644
index 00000000..a9d019e7
--- /dev/null
+++ b/libstore/do-gunzip.c
@@ -0,0 +1,80 @@
+/* gzip decompression
+
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Ignazio Sgalmuzzo <ignaker@gmail.com>
+
+ 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 <zlib.h>
+
+/* I/O interface */
+extern int (*unzip_read) (char *buf, size_t maxread);
+extern void (*unzip_write) (const char *buf, size_t nwrite);
+extern void (*unzip_read_error) (void);
+extern void (*unzip_error) (const char *msg);
+
+#define INBUFSIZ 0x1000
+#define OUTBUFSIZ 0x1000
+
+static unsigned char inbuf[INBUFSIZ];
+static unsigned char outbuf[OUTBUFSIZ];
+
+void
+do_gunzip (void)
+{
+ int result;
+ z_stream strm;
+
+ strm.zalloc = NULL;
+ strm.zfree = NULL;
+ strm.opaque = NULL;
+
+ strm.avail_in = 0;
+ strm.next_out = outbuf;
+ strm.avail_out = OUTBUFSIZ;
+
+ result = inflateInit2 (&strm, 32 + MAX_WBITS);
+
+ while (result == Z_OK)
+ {
+ if (strm.avail_in == 0)
+ {
+ strm.next_in = inbuf;
+ strm.avail_in = (*unzip_read)((char*) strm.next_in, INBUFSIZ);
+
+ if (strm.avail_in == 0)
+ break;
+ }
+
+ result = inflate (&strm, Z_NO_FLUSH);
+
+ if ((result != Z_OK) && (result != Z_STREAM_END))
+ break;
+
+ if ((strm.avail_out == 0) || (result == Z_STREAM_END))
+ {
+ (*unzip_write) ((char*) outbuf, OUTBUFSIZ - strm.avail_out);
+ strm.next_out = outbuf;
+ strm.avail_out = OUTBUFSIZ;
+ }
+ }
+
+ inflateEnd (&strm);
+
+ if (result != Z_STREAM_END)
+ (*unzip_error) (NULL);
+}
diff --git a/libstore/enc.c b/libstore/enc.c
new file mode 100644
index 00000000..d5002a0e
--- /dev/null
+++ b/libstore/enc.c
@@ -0,0 +1,98 @@
+/* Store wire encoding/decoding
+
+ Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ 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 <string.h>
+#include <sys/mman.h>
+
+#include "store.h"
+
+/* Initialize ENC. The given vector and sizes will be used for the encoding
+ if they are big enough (otherwise new ones will be automatically
+ allocated). */
+void
+store_enc_init (struct store_enc *enc,
+ mach_port_t *ports, 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)
+{
+ bzero (enc, sizeof (*enc));
+
+ enc->ports = enc->init_ports = ports;
+ enc->num_ports = num_ports;
+ enc->ints = enc->init_ints = ints;
+ enc->num_ints = num_ints;
+ enc->offsets = enc->init_offsets = offsets;
+ enc->num_offsets = num_offsets;
+ enc->data = enc->init_data = data;
+ enc->data_len = data_len;
+}
+
+/* Deallocate storage used by the fields in ENC (but nothing is done with ENC
+ itself). */
+void
+store_enc_dealloc (struct store_enc *enc)
+{
+ if (enc->ports && enc->num_ports > 0)
+ /* For ports, we must deallocate each port as well. */
+ {
+ while (enc->cur_port < enc->num_ports)
+ {
+ mach_port_t port = enc->ports[enc->cur_port++];
+ if (port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), port);
+ }
+
+ if (enc->ports != enc->init_ports)
+ munmap ((caddr_t) enc->ports, enc->num_ports * sizeof (*enc->ports));
+ }
+
+ if (enc->ints && enc->num_ints > 0 && enc->ints != enc->init_ints)
+ munmap ((caddr_t) enc->ints, enc->num_ints * sizeof (*enc->ints));
+
+ if (enc->offsets && enc->num_offsets > 0
+ && enc->offsets != enc->init_offsets)
+ munmap ((caddr_t) enc->offsets, enc->num_offsets * sizeof (*enc->offsets));
+
+ if (enc->data && enc->data_len > 0 && enc->data != enc->init_data)
+ munmap (enc->data, enc->data_len);
+
+ /* For good measure... */
+ bzero (enc, sizeof (*enc));
+}
+
+/* Copy out the parameters from ENC into the given variables suitably for
+ returning from a file_get_storage_info rpc, and deallocate ENC. */
+void
+store_enc_return (struct store_enc *enc,
+ mach_port_t **ports, 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)
+{
+ *ports = enc->ports;
+ *num_ports = enc->num_ports;
+ *ints = enc->ints;
+ *num_ints = enc->num_ints;
+ *offsets = enc->offsets;
+ *num_offsets = enc->num_offsets;
+ *data = enc->data;
+ *data_len = enc->data_len;
+}
diff --git a/libstore/encode.c b/libstore/encode.c
new file mode 100644
index 00000000..df27250d
--- /dev/null
+++ b/libstore/encode.c
@@ -0,0 +1,178 @@
+/* Store wire encoding
+
+ Copyright (C) 1996,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
+ 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 <string.h>
+#include <sys/mman.h>
+
+#include "store.h"
+
+/* Standard encoding used for most leaf store types. */
+
+error_t
+store_std_leaf_allocate_encoding (const struct store *store,
+ struct store_enc *enc)
+{
+ enc->num_ports++;
+ enc->num_ints += 6;
+ enc->num_offsets += store->num_runs * 2;
+ if (store->name)
+ enc->data_len += strlen (store->name) + 1;
+ enc->data_len += store->misc_len;
+ return 0;
+}
+
+/* The RPC protocol uses 32-bit off_t's, but store_offset_t is now 64 bits. */
+static inline int too_big (store_offset_t ofs)
+{
+ off_t o = (off_t) ofs;
+ return o < 0 || ((store_offset_t) o != ofs);
+}
+
+error_t
+store_std_leaf_encode (const struct store *store, struct store_enc *enc)
+{
+ int i;
+ size_t name_len = (store->name ? strlen (store->name) + 1 : 0);
+
+ enc->ports[enc->cur_port++] = store->port;
+
+ enc->ints[enc->cur_int++] = store->class->id;
+ enc->ints[enc->cur_int++] = store->flags;
+ enc->ints[enc->cur_int++] = store->block_size;
+ enc->ints[enc->cur_int++] = store->num_runs;
+ enc->ints[enc->cur_int++] = name_len;
+ enc->ints[enc->cur_int++] = store->misc_len;
+
+ for (i = 0; i < store->num_runs; i++)
+ {
+ if (sizeof (*enc->offsets) != sizeof (store->runs[i].start)
+ && (too_big (store->runs[i].start)
+ || too_big (store->runs[i].start + store->runs[i].length)))
+ return EOVERFLOW;
+ enc->offsets[enc->cur_offset++] = store->runs[i].start;
+ enc->offsets[enc->cur_offset++] = store->runs[i].length;
+ }
+
+ if (store->name)
+ {
+ bcopy (store->name, enc->data + enc->cur_data, name_len);
+ enc->cur_data += name_len;
+ }
+ if (store->misc_len)
+ {
+ bcopy (store->misc, enc->data + enc->cur_data, store->misc_len);
+ enc->cur_data += store->misc_len;
+ }
+
+ return 0;
+}
+
+/* Encode STORE into ENC, which should have been prepared with
+ store_enc_init, or return an error. The contents of ENC may then be
+ return as the value of file_get_storage_info; if for some reason this
+ can't be done, store_enc_dealloc may be used to deallocate the mmemory
+ used by the unsent vectors. */
+error_t
+store_encode (const struct store *store, struct store_enc *enc)
+{
+ void *buf;
+ error_t err;
+ const struct store_class *class = store->class;
+ /* We zero each vector length for the allocate_encoding method to work, so
+ save the old values. */
+ mach_msg_type_number_t init_num_ports = enc->num_ports;
+ mach_msg_type_number_t init_num_ints = enc->num_ints;
+ mach_msg_type_number_t init_num_offsets = enc->num_offsets;
+ mach_msg_type_number_t init_data_len = enc->data_len;
+
+ if (!class->allocate_encoding || !class->encode)
+ return EOPNOTSUPP;
+
+ enc->num_ports = 0;
+ enc->num_ints = 0;
+ enc->num_offsets = 0;
+ enc->data_len = 0;
+ err = (*class->allocate_encoding) (store, enc);
+ if (err)
+ return err;
+
+ errno = 0;
+ if (enc->num_ports > init_num_ports)
+ {
+ buf = mmap (0, enc->num_ports * sizeof *enc->ports,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (buf != (void *) -1)
+ enc->ports = buf;
+ }
+ if (!errno && enc->num_ints > init_num_ints)
+ {
+ buf = mmap (0, enc->num_ints * sizeof *enc->ints,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (buf != (void *) -1)
+ enc->ints = buf;
+ }
+ if (!errno && enc->num_offsets > init_num_offsets)
+ {
+ buf = mmap (0, enc->num_offsets * sizeof *enc->offsets,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (buf != (void *) -1)
+ enc->offsets = buf;
+
+ }
+ if (!errno && enc->data_len > init_data_len)
+ {
+ buf = mmap (0, enc->data_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (buf != (void *) -1)
+ enc->data = buf;
+ }
+ err = errno;
+ if (! err)
+ err = (*class->encode) (store, enc);
+
+ enc->cur_port = enc->cur_int = enc->cur_offset = enc->cur_data = 0;
+
+ if (err)
+ store_enc_dealloc (enc);
+
+ return err;
+}
+
+/* Encode STORE into the given return variables, suitably for returning from a
+ file_get_storage_info rpc. */
+error_t
+store_return (const struct store *store,
+ mach_port_t **ports, 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)
+{
+ error_t err;
+ struct store_enc enc;
+
+ store_enc_init (&enc, *ports, *num_ports, *ints, *num_ints,
+ *offsets, *num_offsets, *data, *data_len);
+ err = store_encode (store, &enc);
+ if (err)
+ store_enc_dealloc (&enc);
+ else
+ store_enc_return (&enc, ports, num_ports, ints, num_ints,
+ offsets, num_offsets, data, data_len);
+ return err;
+}
diff --git a/libstore/file.c b/libstore/file.c
new file mode 100644
index 00000000..49f1c3fb
--- /dev/null
+++ b/libstore/file.c
@@ -0,0 +1,303 @@
+/* File store backend
+
+ Copyright (C) 1995,96,97,98,2001, 2002 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
+ 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 <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <hurd.h>
+
+#include <hurd/io.h>
+
+#include "store.h"
+
+/* Return 0 if STORE's range is enforced by the filesystem, otherwise an
+ error. */
+static error_t
+enforced (struct store *store)
+{
+ 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. */
+ struct stat st;
+ error_t err = io_stat (store->port, &st);
+
+ if (!err
+ && store->runs[0].length != (st.st_size >> store->log2_block_size))
+ /* The single run is not the whole file. */
+ err = EINVAL;
+
+ return err;
+ }
+}
+
+static error_t
+file_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount, void **buf,
+ size_t *len)
+{
+ size_t bsize = store->block_size;
+ return io_read (store->port, (char **)buf, len, addr * bsize, amount);
+}
+
+static error_t
+file_write (struct store *store,
+ store_offset_t addr, size_t index, const void *buf, size_t len,
+ size_t *amount)
+{
+ size_t bsize = store->block_size;
+ return io_write (store->port, buf, len, addr * bsize, amount);
+}
+
+static error_t
+file_store_set_size (struct store *store, size_t newsize)
+{
+ error_t err;
+
+ if (enforced (store) != 0)
+ /* Bail out if there is more than a single run. */
+ return EOPNOTSUPP;
+
+ err = file_set_size (store->port, newsize);
+
+ if (!err)
+ {
+ /* Update STORE's size and run. */
+ store->size = newsize;
+ store->runs[0].length = newsize >> store->log2_block_size;
+ }
+
+ return err;
+}
+
+static error_t
+file_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_std_leaf_decode (enc, _store_file_create, store);
+}
+
+static error_t
+file_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_file_open (name, flags, store);
+}
+
+static error_t
+fiopen (const char *name, file_t *file, int *mod_flags)
+{
+ if (*mod_flags & STORE_HARD_READONLY)
+ *file = file_name_lookup (name, O_RDONLY, 0);
+ else
+ {
+ *file = file_name_lookup (name, O_RDWR, 0);
+ if (*file == MACH_PORT_NULL
+ && (errno == EACCES || errno == EROFS))
+ {
+ *file = file_name_lookup (name, O_RDONLY, 0);
+ if (*file != MACH_PORT_NULL)
+ *mod_flags |= STORE_HARD_READONLY;
+ }
+ else if (*file != MACH_PORT_NULL)
+ *mod_flags &= ~STORE_HARD_READONLY;
+ }
+ return *file == MACH_PORT_NULL ? errno : 0;
+}
+
+static void
+ficlose (struct store *store)
+{
+ mach_port_deallocate (mach_task_self (), store->port);
+ store->port = MACH_PORT_NULL;
+}
+
+
+
+static error_t
+file_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)
+ ficlose (store);
+
+ store->flags |= flags; /* When inactive, anything goes. */
+
+ return 0;
+}
+
+static error_t
+file_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
+ ? fiopen (store->name, &store->port, &store->flags)
+ : ENOENT;
+ if (! err)
+ store->flags &= ~flags;
+ return err;
+}
+
+static error_t
+file_map (const struct store *store, vm_prot_t prot, mach_port_t *memobj)
+{
+ error_t err;
+ mach_port_t rd_memobj, wr_memobj;
+ int ro = (store->flags & STORE_HARD_READONLY);
+
+ if (store->num_runs != 1 || store->runs[0].start != 0)
+ return EOPNOTSUPP;
+
+ if ((prot & VM_PROT_WRITE) && ro)
+ return EACCES;
+
+ err = io_map (store->port, &rd_memobj, &wr_memobj);
+ if (err)
+ return err;
+
+ *memobj = rd_memobj;
+
+ if (ro && wr_memobj == MACH_PORT_NULL)
+ return 0;
+ else if (rd_memobj == wr_memobj)
+ {
+ if (rd_memobj != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (), rd_memobj,
+ MACH_PORT_RIGHT_SEND, -1);
+ }
+ else
+ {
+ if (rd_memobj != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), rd_memobj);
+ if (wr_memobj != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), wr_memobj);
+ err = EOPNOTSUPP;
+ }
+
+ return err;
+}
+
+const struct store_class
+store_file_class =
+{
+ STORAGE_HURD_FILE, "file", file_read, file_write, file_store_set_size,
+ store_std_leaf_allocate_encoding, store_std_leaf_encode, file_decode,
+ file_set_flags, file_clear_flags, 0, 0, 0, file_open, 0, file_map
+};
+STORE_STD_CLASS (file);
+
+static error_t
+file_byte_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount,
+ void **buf, size_t *len)
+{
+ return io_read (store->port, (char **)buf, len, addr, amount);
+}
+
+static error_t
+file_byte_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len,
+ size_t *amount)
+{
+ return io_write (store->port, buf, len, addr, amount);
+}
+
+struct store_class
+store_file_byte_class =
+{
+ STORAGE_HURD_FILE, "file", file_byte_read, file_byte_write,
+ file_store_set_size,
+ store_std_leaf_allocate_encoding, store_std_leaf_encode, file_decode,
+ file_set_flags, file_clear_flags, 0, 0, 0, file_open, 0, file_map
+};
+
+/* Return a new store in STORE referring to the mach file FILE. Consumes
+ the send right FILE. */
+error_t
+store_file_create (file_t file, int flags, struct store **store)
+{
+ struct store_run run;
+ struct stat stat;
+ error_t err = io_stat (file, &stat);
+
+ if (err)
+ return err;
+
+ run.start = 0;
+ run.length = stat.st_size;
+
+ flags |= STORE_ENFORCED; /* 'cause it's the whole file. */
+
+ return _store_file_create (file, flags, 1, &run, 1, store);
+}
+
+/* Like store_file_create, but doesn't query the file for information. */
+error_t
+_store_file_create (file_t file, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ if (block_size == 1)
+ return _store_create (&store_file_byte_class,
+ file, flags, 1, runs, num_runs, 0, store);
+ else
+ return _store_create (&store_file_class,
+ file, flags, block_size, runs, num_runs, 0, store);
+}
+
+/* Open the file NAME, and return the corresponding store in STORE. */
+error_t
+store_file_open (const char *name, int flags, struct store **store)
+{
+ file_t file;
+ error_t err = fiopen (name, &file, &flags);
+ if (! err)
+ {
+ err = store_file_create (file, flags, store);
+ if (! err)
+ {
+ err = store_set_name (*store, name);
+ if (err)
+ store_free (*store);
+ }
+ if (err)
+ mach_port_deallocate (mach_task_self (), file);
+ }
+ return err;
+}
diff --git a/libstore/flags.c b/libstore/flags.c
new file mode 100644
index 00000000..d80181ac
--- /dev/null
+++ b/libstore/flags.c
@@ -0,0 +1,66 @@
+/* Setting various store flags
+
+ Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ 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 <malloc.h>
+#include <string.h>
+
+#include "store.h"
+
+/* Add FLAGS to STORE's currently set flags. */
+error_t
+store_set_flags (struct store *store, int flags)
+{
+ error_t err = 0;
+ int orig = store->flags, new = flags & ~orig;
+
+ if (new & STORE_BACKEND_FLAGS)
+ {
+ if (store->class->set_flags)
+ err = (*store->class->set_flags) (store, new);
+ else
+ err = EINVAL;
+ }
+
+ if (! err)
+ store->flags |= (new & ~STORE_BACKEND_FLAGS);
+
+ return err;
+}
+
+/* Remove FLAGS from STORE's currently set flags. */
+error_t
+store_clear_flags (struct store *store, int flags)
+{
+ error_t err = 0;
+ int orig = store->flags, kill = flags & orig;
+
+ if (kill & STORE_BACKEND_FLAGS)
+ {
+ if (store->class->clear_flags)
+ err = (*store->class->clear_flags) (store, kill);
+ else
+ err = EINVAL;
+ }
+
+ if (! err)
+ store->flags &= ~(kill & ~STORE_BACKEND_FLAGS);
+
+ return err;
+}
diff --git a/libstore/gunzip.c b/libstore/gunzip.c
new file mode 100644
index 00000000..f47c0b2b
--- /dev/null
+++ b/libstore/gunzip.c
@@ -0,0 +1,33 @@
+/* Decompressing store backend
+
+ Copyright (C) 1997, 1999, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ 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 <errno.h>
+
+extern void do_gunzip (void); /* Entry point to gunzip engine. */
+
+static error_t
+DO_UNZIP (void)
+{
+ do_gunzip ();
+ return 0;
+}
+
+#define UNZIP gunzip
+#include "unzipstore.c"
diff --git a/libstore/kids.c b/libstore/kids.c
new file mode 100644
index 00000000..901a7f85
--- /dev/null
+++ b/libstore/kids.c
@@ -0,0 +1,312 @@
+/* Managing sub-stores
+
+ Copyright (C) 1995,96,97,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
+ 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "store.h"
+
+/* Set STORE's current children list to (a copy of) CHILDREN and NUM_CHILDREN. */
+error_t
+store_set_children (struct store *store,
+ struct store *const *children, size_t num_children)
+{
+ unsigned size = num_children * sizeof (struct store *);
+ struct store **copy = malloc (size);
+
+ if (!copy)
+ return ENOMEM;
+
+ if (store->children)
+ free (store->children);
+
+ memcpy (copy, children, size);
+ store->children = copy;
+ store->num_children = num_children;
+
+ return 0;
+}
+
+/* Calls the allocate_encoding method in each child store of STORE,
+ propagating any errors. If any child does not hae such a method,
+ EOPNOTSUPP is returned. */
+error_t
+store_allocate_child_encodings (const struct store *store,
+ struct store_enc *enc)
+{
+ int i;
+ error_t err = 0;
+ for (i = 0; i < store->num_children && !err; i++)
+ {
+ struct store *k = store->children[i];
+ if (k->class->allocate_encoding)
+ (*k->class->allocate_encoding) (k, enc);
+ else
+ err = EOPNOTSUPP;
+ }
+ return err;
+}
+
+/* Calls the encode method in each child store of STORE, propagating any
+ errors. If any child does not hae such a method, EOPNOTSUPP is returned. */
+error_t
+store_encode_children (const struct store *store, struct store_enc *enc)
+{
+ int i;
+ error_t err = 0;
+ for (i = 0; i < store->num_children && !err; i++)
+ {
+ struct store *k = store->children[i];
+ if (k->class->encode)
+ (*k->class->encode) (k, enc);
+ else
+ err = EOPNOTSUPP;
+ }
+ return err;
+}
+
+/* Decodes NUM_CHILDREN from ENC, storing the results into successive
+ positions in CHILDREN. */
+error_t
+store_decode_children (struct store_enc *enc, int num_children,
+ const struct store_class *const *classes,
+ struct store **children)
+{
+ int i;
+ error_t err = 0;
+ for (i = 0; i < num_children && !err; i++)
+ err = store_decode (enc, classes, &children[i]);
+ if (err)
+ /* Deallocate anything we've already created. */
+ while (--i >= 0)
+ store_free (children[i]);
+ return err;
+}
+
+/* Set FLAGS in all children of STORE, and if successful, add FLAGS to
+ STORE's flags. */
+error_t
+store_set_child_flags (struct store *store, int flags)
+{
+ int i;
+ error_t err = 0;
+ int old_child_flags[store->num_children];
+
+ for (i = 0; i < store->num_children && !err; i++)
+ {
+ old_child_flags[i] = store->children[i]->flags;
+ err = store_set_flags (store->children[i], flags);
+ }
+
+ if (err)
+ while (i-- > 0)
+ store_clear_flags (store->children[i], flags & ~old_child_flags[i]);
+ else
+ store->flags |= flags;
+
+ return err;
+}
+
+/* Clear FLAGS in all children of STORE, and if successful, remove FLAGS from
+ STORE's flags. */
+error_t
+store_clear_child_flags (struct store *store, int flags)
+{
+ int i;
+ error_t err = 0;
+ int old_child_flags[store->num_children];
+
+ for (i = 0; i < store->num_children && !err; i++)
+ {
+ old_child_flags[i] = store->children[i]->flags;
+ err = store_clear_flags (store->children[i], flags);
+ }
+
+ if (err)
+ while (i-- > 0)
+ store_set_flags (store->children[i], flags & ~old_child_flags[i]);
+ else
+ store->flags &= ~flags;
+
+ return err;
+}
+
+/* Parse multiple store names in NAME, and open each individually, returning
+ all in the vector STORES, and the number in NUM_STORES. The syntax of
+ NAME is a single non-alpha-numeric separator character, followed by each
+ child store name separated by the same separator; each child name is
+ TYPE:NAME notation as parsed by store_typed_open. If every child uses the
+ same TYPE: prefix, then it may be factored out and put before the child
+ list instead (the two types of notation are differentiated by whether the
+ first character of name is alpha-numeric or not). */
+error_t
+store_open_children (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store ***stores, size_t *num_stores)
+{
+ char *pfx = 0; /* Prefix applied to each part name. */
+ size_t pfx_len = 0; /* Space PFX + separator takes up. */
+ char sep = *name; /* Character separating individual names. */
+
+ if (sep && isalnum (sep))
+ /* If the first character is a `name' character, it's likely to be either
+ a type prefix (e.g, TYPE:@NAME1@NAME2@), so we distribute the type
+ prefix among the elements (@TYPE:NAME1@TYPE:NAME2@). */
+ {
+ const char *pfx_end = name;
+
+ while (isalnum (*pfx_end))
+ pfx_end++;
+
+ if (*pfx_end++ != ':')
+ return EINVAL;
+
+ /* Make a copy of the prefix. */
+ pfx = strndupa (name, pfx_end - name);
+ pfx_len = pfx_end - name;
+
+ sep = *pfx_end;
+ }
+
+ if (sep)
+ /* Parse a list of store specs separated by SEP. */
+ {
+ int k;
+ const char *p, *end;
+ error_t err = 0;
+ size_t count = 0;
+
+ /* First, see how many there are. */
+ for (p = name; p && p[1]; p = strchr (p + 1, sep))
+ count++;
+
+ /* Make a vector to hold them. */
+ *stores = malloc (count * sizeof (struct store *));
+ *num_stores = count;
+ if (! *stores)
+ return ENOMEM;
+
+ bzero (*stores, count * sizeof (struct store *));
+
+ /* Open each child store. */
+ for (p = name, k = 0; !err && p && p[1]; p = end, k++)
+ {
+ size_t kname_len;
+
+ end = strchr (p + 1, sep);
+ kname_len = (end ? end - p - 1 : strlen (p + 1));
+
+ {
+ /* Allocate temporary child name on the stack. */
+ char kname[pfx_len + kname_len + 1];
+
+ if (pfx)
+ /* Add type prefix to child name. */
+ memcpy (kname, pfx, pfx_len);
+
+ memcpy (kname + pfx_len, p + 1, kname_len);
+ kname[pfx_len + kname_len] = '\0';
+
+ err = store_typed_open (kname, flags, classes, &(*stores)[k]);
+ }
+ }
+
+ if (err)
+ /* Failure opening some child, deallocate what we've done so far. */
+ {
+ while (--k >= 0)
+ store_free ((*stores)[k]);
+ free (*stores);
+ }
+
+ return err;
+ }
+ else
+ /* Empty list. */
+ {
+ *stores = 0;
+ *num_stores = 0;
+ return 0;
+ }
+}
+
+/* Try to come up with a name for the children in STORE, combining the names
+ of each child in a way that could be used to parse them with
+ store_open_children. This is done heuristically, and so may not succeed.
+ If a child doesn't have a name, EINVAL is returned. */
+error_t
+store_children_name (const struct store *store, char **name)
+{
+ static char try_seps[] = "@+=,._%|;^!~'&";
+ struct store **kids = store->children;
+ size_t num_kids = store->num_children;
+
+ if (num_kids == 0)
+ {
+ *name = strdup ("");
+ return *name ? 0 : ENOMEM;
+ }
+ else
+ {
+ int k;
+ char *s; /* Current separator in search for one. */
+ int fail; /* If we couldn't use *S as as sep. */
+ size_t total_len = 0; /* Length of name we will return. */
+
+ /* Detect children without names, and calculate the total length of the
+ name we will return (which is the sum of the lengths of the child
+ names plus room for the types and separator characters. */
+ for (k = 0; k < num_kids; k++)
+ if (!kids[k] || !kids[k]->name)
+ return EINVAL;
+ else
+ total_len +=
+ /* separator + type name + type separator + child name */
+ 1 + strlen (kids[k]->class->name) + 1 + strlen (kids[k]->name);
+
+ /* Look for a separator character from those in TRY_SEPS that doesn't
+ occur in any of the the child names. */
+ for (s = try_seps, fail = 1; *s && fail; s++)
+ for (k = 0, fail = 0; k < num_kids && !fail; k++)
+ if (strchr (kids[k]->name, *s))
+ fail = 1;
+
+ if (*s)
+ /* We found a usable separator! */
+ {
+ char *p = malloc (total_len + 1);
+
+ if (! p)
+ return ENOMEM;
+ *name = p;
+
+ for (k = 0; k < num_kids; k++)
+ p +=
+ sprintf (p, "%c%s:%s", *s, kids[k]->class->name, kids[k]->name);
+
+ return 0;
+ }
+ else
+ return EGRATUITOUS;
+ }
+}
diff --git a/libstore/make.c b/libstore/make.c
new file mode 100644
index 00000000..8f289533
--- /dev/null
+++ b/libstore/make.c
@@ -0,0 +1,100 @@
+/* Store allocation/deallocation
+
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+ 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 <malloc.h>
+
+#include "store.h"
+
+/* Allocate a new store structure with meths METHS, and the various other
+ fields initialized to the given parameters. */
+error_t
+_store_create (const struct store_class *class,
+ mach_port_t port, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ store_offset_t end, struct store **store)
+{
+ if ((block_size & (block_size - 1)) || (block_size == 0 && num_runs > 0))
+ return EINVAL; /* block size not a power of two. */
+ else
+ {
+ struct store *new = malloc (sizeof (struct store));
+ if (new)
+ {
+ error_t err;
+
+ new->name = 0;
+ new->port = port;
+ new->runs = 0;
+ new->num_runs = 0;
+ new->wrap_src = 0;
+ new->wrap_dst = 0;
+ new->flags = flags;
+ new->end = end;
+ new->block_size = block_size;
+ new->source = MACH_PORT_NULL;
+ new->blocks = 0;
+ new->size = 0;
+ new->log2_block_size = 0;
+ new->log2_blocks_per_page = 0;
+ new->misc = 0;
+ new->misc_len = 0;
+ new->hook = 0;
+ new->children = 0;
+ new->num_children = 0;
+
+ new->class = class;
+
+ /* store_set_runs calls _store_derive to derive other fields. */
+ err = store_set_runs (new, runs, num_runs);
+ if (err)
+ free (new);
+ else
+ *store = new;
+
+ return err;
+ }
+ else
+ return ENOMEM;
+ }
+}
+
+void
+store_free (struct store *store)
+{
+ int k;
+
+ if (store->class->cleanup)
+ (*store->class->cleanup) (store);
+
+ for (k = 0; k < store->num_children; k++)
+ store_free (store->children[k]);
+
+ if (store->port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), store->port);
+ if (store->source != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), store->source);
+
+ if (store->name)
+ free (store->name);
+ if (store->runs)
+ free (store->runs);
+
+ free (store);
+}
diff --git a/libstore/map.c b/libstore/map.c
new file mode 100644
index 00000000..b7533cbb
--- /dev/null
+++ b/libstore/map.c
@@ -0,0 +1,79 @@
+/* Direct store mapping
+
+ Copyright (C) 1997 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
+ 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 <hurd.h>
+#include <hurd/io.h>
+
+#include "store.h"
+
+/* Return a memory object paging on STORE. [among other reasons,] this may
+ fail because store contains non-contiguous regions on the underlying
+ object. In such a case you can try calling some of the routines below to
+ get a pager. */
+error_t
+store_map (const struct store *store, vm_prot_t prot,
+ mach_port_t *memobj)
+{
+ error_t (*map) (const struct store *store, vm_prot_t prot,
+ mach_port_t *memobj) =
+ store->class->map;
+ error_t err = map ? (*map) (store, prot, memobj) : EOPNOTSUPP;
+
+ if (err == EOPNOTSUPP && store->source != MACH_PORT_NULL)
+ /* Can't map the store directly, but we know it represents the file
+ STORE->source, so we can try mapping that instead. */
+ {
+ mach_port_t rd_memobj, wr_memobj;
+ int ro = (store->flags & STORE_HARD_READONLY);
+
+ if ((prot & VM_PROT_WRITE) && ro)
+ return EACCES;
+
+ err = io_map (store->port, &rd_memobj, &wr_memobj);
+ if (! err)
+ {
+ *memobj = rd_memobj;
+
+ if (!ro || wr_memobj != MACH_PORT_NULL)
+ /* If either we or the server think this object is writable, then
+ the write-memory-object must be the same as the read one (if
+ we only care about reading, then it can be null too). */
+ {
+ if (rd_memobj == wr_memobj)
+ {
+ if (rd_memobj != MACH_PORT_NULL)
+ mach_port_mod_refs (mach_task_self (), rd_memobj,
+ MACH_PORT_RIGHT_SEND, -1);
+ }
+ else
+ {
+ if (rd_memobj != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), rd_memobj);
+ if (wr_memobj != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), wr_memobj);
+ err = EOPNOTSUPP;
+ }
+ }
+ }
+ }
+
+ return err;
+}
diff --git a/libstore/memobj.c b/libstore/memobj.c
new file mode 100644
index 00000000..0d5c816e
--- /dev/null
+++ b/libstore/memobj.c
@@ -0,0 +1,197 @@
+/* Store backend using a Mach memory object
+ Copyright (C) 2001, 2002 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 "store.h"
+#include <hurd.h>
+#include <hurd/sigpreempt.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <assert.h>
+
+
+/* Return a new store in STORE referring to the memory object MEMOBJ.
+ Consumes the send right MEMOBJ. */
+error_t
+store_memobj_create (memory_object_t memobj, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ return _store_create (&store_memobj_class,
+ memobj, flags, block_size, runs, num_runs, 0, store);
+}
+
+
+/* This one is pretty easy. */
+static error_t
+memobj_map (const struct store *store, vm_prot_t prot, mach_port_t *memobj)
+{
+ *memobj = store->port;
+ return mach_port_mod_refs (mach_task_self (), *memobj,
+ MACH_PORT_RIGHT_SEND, +1);
+}
+
+
+static error_t /* swiped from libpager's pager_memcpy */
+memobj_memcpy (memory_object_t memobj,
+ vm_offset_t offset, void *other, size_t *size,
+ vm_prot_t prot)
+{
+ vm_address_t window = 0;
+ vm_size_t windowsize = 8 * vm_page_size;
+ size_t to_copy = *size;
+ error_t err;
+
+ error_t copy (struct hurd_signal_preemptor *preemptor)
+ {
+ while (to_copy > 0)
+ {
+ size_t pageoff = offset & (vm_page_size - 1);
+
+ if (window)
+ /* Deallocate the old window. */
+ munmap ((caddr_t) window, windowsize);
+
+ /* Map in and copy a standard-sized window, unless that is
+ more than the total left to be copied. */
+
+ if (windowsize > pageoff + to_copy)
+ windowsize = pageoff + to_copy;
+
+ window = 0;
+ err = vm_map (mach_task_self (), &window, windowsize, 0, 1,
+ memobj, offset - pageoff, 0,
+ prot, prot, VM_INHERIT_NONE);
+ if (err)
+ return 0;
+
+ /* Realign the fault preemptor for the new mapping window. */
+ preemptor->first = window;
+ preemptor->last = window + windowsize;
+
+ if (prot == VM_PROT_READ)
+ memcpy (other, (const void *) window + pageoff,
+ windowsize - pageoff);
+ else
+ memcpy ((void *) window + pageoff, other, windowsize - pageoff);
+
+ offset += windowsize - pageoff;
+ other += windowsize - pageoff;
+ to_copy -= windowsize - pageoff;
+ }
+ return 0;
+ }
+
+ jmp_buf buf;
+ void fault (int signo, long int sigcode, struct sigcontext *scp)
+ {
+ assert (scp->sc_error == EKERN_MEMORY_ERROR);
+ err = EIO;
+ to_copy -= sigcode - window;
+ longjmp (buf, 1);
+ }
+
+ if (to_copy == 0)
+ /* Short-circuit return if nothing to do.
+ ERR would not be initialized by the copy loop in this case. */
+ return 0;
+
+ if (setjmp (buf) == 0)
+ hurd_catch_signal (sigmask (SIGSEGV) | sigmask (SIGBUS),
+ window, window + windowsize,
+ &copy, (sighandler_t) &fault);
+
+ if (window)
+ munmap ((caddr_t) window, windowsize);
+
+ *size -= to_copy;
+
+ return err;
+}
+
+static error_t
+memobj_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount,
+ void **buf, size_t *len)
+{
+ addr <<= store->log2_block_size;
+ if (((size_t) addr & (vm_page_size - 1)) == 0)
+ {
+ *len = amount;
+ return vm_map (mach_task_self (), (vm_address_t *) buf, amount,
+ 0, 1, store->port, addr << store->log2_block_size, 0,
+ VM_PROT_READ, VM_PROT_ALL, VM_INHERIT_NONE);
+ }
+ else
+ {
+ error_t err;
+ int alloced = 0;
+ if (*len < amount)
+ {
+ *buf = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*buf == MAP_FAILED)
+ return errno;
+ alloced = 1;
+ }
+ *len = amount;
+ err = memobj_memcpy (store->port, addr, *buf, len, VM_PROT_READ);
+ if (err && alloced)
+ munmap (*buf, amount);
+ else if (alloced && round_page (*len) < round_page (amount))
+ munmap (*buf + round_page (*len),
+ round_page (amount) - round_page (*len));
+ return err;
+ }
+}
+
+static error_t
+memobj_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len, size_t *amount)
+{
+ *amount = len;
+ return memobj_memcpy (store->port, addr << store->log2_block_size,
+ (void *) buf, amount, VM_PROT_WRITE);
+}
+
+static error_t
+memobj_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+static error_t
+memobj_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_std_leaf_decode (enc, store_memobj_create, store);
+}
+
+const struct store_class
+store_memobj_class =
+{
+ STORAGE_MEMORY, "memobj",
+ map: memobj_map,
+ read: memobj_read,
+ set_size: memobj_set_size,
+ write: memobj_write,
+ allocate_encoding: store_std_leaf_allocate_encoding,
+ encode: store_std_leaf_encode,
+ decode: memobj_decode,
+};
+STORE_STD_CLASS (memobj);
diff --git a/libstore/module.c b/libstore/module.c
new file mode 100644
index 00000000..6e75099a
--- /dev/null
+++ b/libstore/module.c
@@ -0,0 +1,178 @@
+/* Dynamic loading of store class modules
+ Copyright (C) 2002, 2003 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 "store.h"
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h> /* XXX */
+
+static error_t
+open_class (int need_open,
+ const char *name, const char *clname_end,
+ const struct store_class **classp)
+{
+ char *modname, *clsym;
+ void *mod;
+
+ /* Construct the name of the shared object for this module. */
+ if (asprintf (&modname,
+ "libstore_%.*s%s", (int) (clname_end - name), name,
+ STORE_SONAME_SUFFIX) < 0)
+ return ENOMEM;
+
+ /* Now try to load the module.
+
+ Note we never dlclose the module, and add a ref every time we open it
+ anew. We can't dlclose it until no stores of this class exist, so
+ we'd need a creation/deletion hook for that. */
+
+ errno = 0;
+ mod = dlopen (modname, RTLD_LAZY);
+ if (mod == NULL)
+ {
+ const char *errstring = dlerror (); /* Must always call or it leaks! */
+ if (errno != ENOENT)
+ /* XXX not good, but how else to report the error? */
+ error (0, 0, "cannot load %s: %s", modname, errstring);
+ }
+ free (modname);
+ if (mod == NULL)
+ return errno ?: ENOENT;
+
+ if (asprintf (&clsym, "store_%.*s_class",
+ (int) (clname_end - name), name) < 0)
+ {
+ dlclose (mod);
+ return ENOMEM;
+ }
+
+ *classp = dlsym (mod, clsym);
+ free (clsym);
+ if (*classp == NULL)
+ {
+ error (0, 0, "invalid store module %.*s: %s",
+ (int) (clname_end - name), name, dlerror ());
+ dlclose (mod);
+ return EGRATUITOUS;
+ }
+
+ if (need_open && ! (*classp)->open)
+ {
+ /* This class cannot be opened as needed. */
+ dlclose (mod);
+ return EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+error_t
+store_module_find_class (const char *name, const char *clname_end,
+ const struct store_class **classp)
+{
+ return open_class (0, name, clname_end, classp);
+}
+
+error_t
+store_module_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ const struct store_class *cl;
+ const char *clname_end = strchrnul (name, ':');
+ error_t err;
+
+ err = open_class (1, name, clname_end, &cl);
+ if (err)
+ return err;
+
+ if (*clname_end)
+ /* Skip the ':' separating the class-name from the device name. */
+ clname_end++;
+
+ if (! *clname_end)
+ /* The class-specific portion of the name is empty, so make it *really*
+ empty. */
+ clname_end = 0;
+
+ return (*cl->open) (clname_end, flags, classes, store);
+}
+
+const struct store_class store_module_open_class =
+{ -1, "module", open: store_module_open };
+STORE_STD_CLASS (module_open);
+
+error_t
+store_module_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ char *modname;
+ void *mod;
+ const struct store_class *const *cl, *const *clend;
+ enum file_storage_class id;
+
+ if (enc->cur_int >= enc->num_ints)
+ /* The first int should always be the type. */
+ return EINVAL;
+
+ id = enc->ints[enc->cur_int];
+
+ /* Construct the name of the shared object for this module. */
+ if (asprintf (&modname, "libstore_type-%d%s", id, STORE_SONAME_SUFFIX) < 0)
+ return ENOMEM;
+
+ /* Try to open the module. */
+ mod = dlopen (modname, RTLD_LAZY);
+ free (modname);
+ if (mod == NULL)
+ {
+ (void) dlerror (); /* otherwise it leaks */
+ return ENOENT;
+ }
+
+ /* Now find its "store_std_classes" section, which points to each
+ `struct store_class' defined in this module. */
+ cl = dlsym (mod, "__start_store_std_classes");
+ if (cl == NULL)
+ {
+ error (0, 0, "invalid store module type-%d: %s", id, dlerror ());
+ dlclose (mod);
+ return EGRATUITOUS;
+ }
+ clend = dlsym (mod, "__stop_store_std_classes");
+ if (clend == NULL)
+ {
+ error (0, 0, "invalid store module type-%d: %s", id, dlerror ());
+ dlclose (mod);
+ return EGRATUITOUS;
+ }
+
+ while (cl < clend)
+ if ((*cl)->decode && (*cl)->id == id)
+ return (*(*cl)->decode) (enc, classes, store);
+ else
+ ++cl;
+
+ /* This class cannot be opened via store_decode. */
+ dlclose (mod);
+ return EOPNOTSUPP;
+}
diff --git a/libstore/mvol.c b/libstore/mvol.c
new file mode 100644
index 00000000..d243cc8a
--- /dev/null
+++ b/libstore/mvol.c
@@ -0,0 +1,158 @@
+/* Multiple-volume store backend
+
+ Copyright (C) 1996,97,2001, 2002 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
+ 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "store.h"
+
+struct mvol_state
+{
+ /* The current `volume'. */
+ ssize_t cur_vol;
+
+ /* A function to change volumes, making NEW_VOL readable on the store
+ instead of OLD_VOL. OLD_VOL is initially -1, */
+ error_t (*swap_vols) (struct store *store, size_t new_vol, ssize_t old_vol);
+};
+
+static error_t
+ensure_vol (struct store *store, size_t vol)
+{
+ error_t err = 0;
+ struct mvol_state *mv = store->hook;
+ if (vol != mv->cur_vol)
+ {
+ err = (*mv->swap_vols) (store, vol, mv->cur_vol);
+ if (! err)
+ mv->cur_vol = vol;
+ }
+ return err;
+}
+
+static error_t
+mvol_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount,
+ void **buf, size_t *len)
+{
+ error_t err = ensure_vol (store, index);
+ if (! err)
+ err = store_read (store->children[0], addr, amount, buf, len);
+ return err;
+}
+
+static error_t
+mvol_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len, size_t *amount)
+{
+ error_t err = ensure_vol (store, index);
+ if (! err)
+ err = store_write (store->children[0], addr, buf, len, amount);
+ return err;
+}
+
+static error_t
+mvol_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+mvol_remap (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ return store_remap_create (source, runs, num_runs, 0, store);
+}
+
+const struct store_class
+store_mvol_class =
+{
+ -1, "mvol", mvol_read, mvol_write, mvol_set_size,
+ 0, 0, 0,
+ store_set_child_flags, store_clear_child_flags, 0, 0, mvol_remap
+};
+STORE_STD_CLASS (mvol);
+
+/* Return a new store in STORE that multiplexes multiple physical volumes
+ from PHYS as one larger virtual volume. SWAP_VOLS is a function that will
+ be called whenever the volume currently active isn't correct. PHYS is
+ consumed. */
+error_t
+store_mvol_create (struct store *phys,
+ error_t (*swap_vols) (struct store *store, size_t new_vol,
+ ssize_t old_vol),
+ int flags,
+ struct store **store)
+{
+ error_t err;
+ struct store_run run;
+
+ run.start = 0;
+ run.length = phys->end;
+
+ err = _store_create (&store_mvol_class, MACH_PORT_NULL,
+ flags | phys->flags, phys->block_size,
+ &run, 1, 0, store);
+ if (! err)
+ {
+ struct mvol_state *mv = malloc (sizeof (struct mvol_state));
+ if (mv)
+ {
+ mv->swap_vols = swap_vols;
+ mv->cur_vol = -1;
+ (*store)->hook = mv;
+ }
+ else
+ err = ENOMEM;
+
+ if (! err)
+ err = store_set_children (*store, &phys, 1);
+
+ if (! err)
+ {
+ if (phys->name)
+ {
+ size_t nlen =
+ strlen (phys->class->name) + 1 + strlen (phys->name) + 1;
+ char *name = malloc (nlen);
+
+ if (name)
+ {
+ snprintf (name, nlen, "%s:%s", phys->class->name, phys->name);
+ (*store)->name = name;
+ }
+ else
+ err = ENOMEM;
+ }
+ }
+
+ if (err)
+ {
+ if (mv)
+ free (mv);
+ store_free (*store);
+ }
+ }
+
+ return err;
+}
diff --git a/libstore/nbd.c b/libstore/nbd.c
new file mode 100644
index 00000000..3138af01
--- /dev/null
+++ b/libstore/nbd.c
@@ -0,0 +1,529 @@
+/* "Network Block Device" store backend compatible with Linux `nbd' driver
+ Copyright (C) 2001, 2002, 2008 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 "store.h"
+#include <hurd.h>
+#include <hurd/io.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+
+// Avoid dragging in the resolver when linking statically.
+#pragma weak gethostbyname
+
+
+/* The nbd protocol, such as it is, is not really specified anywhere.
+ These message layouts and constants were culled from the nbd-server and
+ Linux kernel nbd module sources. */
+
+#define NBD_INIT_MAGIC "NBDMAGIC\x00\x00\x42\x02\x81\x86\x12\x53"
+
+#define NBD_REQUEST_MAGIC (htonl (0x25609513))
+#define NBD_REPLY_MAGIC (htonl (0x67446698))
+
+#define NBD_IO_MAX 10240
+
+struct nbd_startup
+{
+ char magic[16]; /* NBD_INIT_MAGIC */
+ uint64_t size; /* size in bytes, 64 bits in net order */
+ char reserved[128]; /* zeros, we don't check it */
+};
+
+struct nbd_request
+{
+ uint32_t magic; /* NBD_REQUEST_MAGIC */
+ uint32_t type; /* 0 read, 1 write, 2 disconnect */
+ uint64_t handle; /* returned in reply */
+ uint64_t from;
+ uint32_t len;
+} __attribute__ ((packed));
+
+struct nbd_reply
+{
+ uint32_t magic; /* NBD_REPLY_MAGIC */
+ uint32_t error;
+ uint64_t handle; /* value from request */
+} __attribute__ ((packed));
+
+
+/* i/o functions. */
+
+#if BYTE_ORDER == BIG_ENDIAN
+# define htonll(x) (x)
+#elif BYTE_ORDER == LITTLE_ENDIAN
+# include <byteswap.h>
+# define htonll(x) (bswap_64 (x))
+#else
+# error what endian?
+#endif
+#define ntohll htonll
+
+
+static inline error_t
+read_reply (struct store *store, uint64_t handle)
+{
+ struct nbd_reply reply;
+ char *buf = (void *) &reply;
+ mach_msg_type_number_t cc = sizeof reply;
+ error_t err = io_read (store->port, &buf, &cc, -1, cc);
+ if (err)
+ return err;
+ if (buf != (void *) &reply)
+ {
+ memcpy (&reply, buf, sizeof reply);
+ munmap (buf, cc);
+ }
+ if (cc != sizeof reply)
+ return EIO;
+ if (reply.magic != NBD_REPLY_MAGIC)
+ return EIO;
+ if (reply.handle != handle)
+ return EIO;
+ if (reply.error != 0)
+ return EIO;
+ return 0;
+}
+
+static error_t
+nbd_write (struct store *store,
+ store_offset_t addr, size_t index, const void *buf, size_t len,
+ size_t *amount)
+{
+ struct nbd_request req =
+ {
+ magic: NBD_REQUEST_MAGIC,
+ type: htonl (1), /* WRITE */
+ };
+ error_t err;
+ mach_msg_type_number_t cc;
+
+ addr <<= store->log2_block_size;
+ *amount = 0;
+
+ do
+ {
+ size_t chunk = len < NBD_IO_MAX ? len : NBD_IO_MAX, nwrote;
+ req.from = htonll (addr);
+ req.len = htonl (chunk);
+
+ err = io_write (store->port, (char *) &req, sizeof req, -1, &cc);
+ if (err)
+ return err;
+ if (cc != sizeof req)
+ return EIO;
+
+ nwrote = 0;
+ do
+ {
+ err = io_write (store->port, (char *) buf, chunk - nwrote, -1, &cc);
+ if (err)
+ return err;
+ buf += cc;
+ nwrote += cc;
+ } while (nwrote < chunk);
+
+ err = read_reply (store, req.handle);
+ if (err)
+ return err;
+
+ addr += chunk;
+ *amount += chunk;
+ len -= chunk;
+ } while (len > 0);
+
+ return 0;
+}
+
+static error_t
+nbd_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount,
+ void **buf, size_t *len)
+{
+ struct nbd_request req =
+ {
+ magic: NBD_REQUEST_MAGIC,
+ type: htonl (0), /* READ */
+ };
+ error_t err;
+ size_t ofs, chunk;
+ char *databuf, *piecebuf;
+ size_t databuflen, piecelen;
+
+ /* Send a request for the largest possible piece of remaining data and
+ read the first piece of its reply into PIECEBUF, PIECELEN. The amount
+ requested can be found in CHUNK. */
+ inline error_t request_chunk (char **buf, size_t *len)
+ {
+ mach_msg_type_number_t cc;
+
+ chunk = (amount - ofs) < NBD_IO_MAX ? (amount - ofs) : NBD_IO_MAX;
+
+ req.from = htonll (addr);
+ req.len = htonl (chunk);
+
+ /* Advance ADDR immediately, so it always points past what we've
+ already requested. */
+ addr += chunk;
+
+ return (io_write (store->port, (char *) &req, sizeof req, -1, &cc) ?
+ : cc != sizeof req ? EIO
+ : read_reply (store, req.handle) ?
+ : io_read (store->port, buf, len, (off_t) -1, chunk));
+ }
+
+ addr <<= store->log2_block_size;
+
+ /* Read the first piece, which can go directly into the caller's buffer. */
+ databuf = *buf;
+ piecelen = databuflen = *len;
+ err = request_chunk (&databuf, &piecelen);
+ if (err)
+ return err;
+ if (databuflen >= amount)
+ {
+ /* That got it all. We're done. */
+ *buf = databuf;
+ *len = piecelen;
+ return 0;
+ }
+
+ /* We haven't read the entire amount yet. */
+ ofs = 0;
+ do
+ {
+ /* Account for what we just read. */
+ ofs += piecelen;
+ chunk -= piecelen;
+ if (ofs == amount)
+ {
+ /* That got it all. We're done. */
+ *buf = databuf;
+ *len = ofs;
+ return 0;
+ }
+
+ /* Now we'll read another piece of the data, hopefully
+ into the latter part of the existing buffer. */
+ piecebuf = databuf + ofs;
+ piecelen = databuflen - ofs;
+
+ if (chunk > 0)
+ /* We haven't finishing reading the last chunk we requested. */
+ err = io_read (store->port, &piecebuf, &piecelen,
+ (off_t) -1, chunk);
+ else
+ /* Request the next chunk from the server. */
+ err = request_chunk (&piecebuf, &piecelen);
+
+ if (!err && piecebuf != databuf + ofs)
+ {
+ /* Now we have two discontiguous pieces of the buffer. */
+ size_t newbuflen = round_page (databuflen + piecelen);
+ char *newbuf = mmap (0, newbuflen,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (newbuf == MAP_FAILED)
+ {
+ err = errno;
+ break;
+ }
+ memcpy (newbuf, databuf, ofs);
+ memcpy (newbuf + ofs, piecebuf, piecelen);
+ if (databuf != *buf)
+ munmap (databuf, databuflen);
+ databuf = newbuf;
+ databuflen = newbuflen;
+ }
+ } while (! err);
+
+ if (databuf != *buf)
+ munmap (databuf, databuflen);
+ return err;
+}
+
+static error_t
+nbd_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+
+
+/* Setup hooks. */
+
+static error_t
+nbd_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_std_leaf_decode (enc, _store_nbd_create, store);
+}
+
+static error_t
+nbd_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_nbd_open (name, flags, store);
+}
+
+static const char url_prefix[] = "nbd://";
+
+/* Valid name syntax is [nbd://]HOSTNAME:PORT[/BLOCKSIZE].
+ If "/BLOCKSIZE" is omitted, the block size is 1. */
+static error_t
+nbd_validate_name (const char *name,
+ const struct store_class *const *classes)
+{
+ char *p, *endp;
+
+ if (!strncmp (name, url_prefix, sizeof url_prefix - 1))
+ name += sizeof url_prefix - 1;
+
+ p = strchr (name, ':');
+ if (p == 0)
+ return EINVAL;
+ endp = 0;
+ strtoul (++p, &endp, 0);
+ if (endp == 0 || endp == p)
+ return EINVAL;
+ switch (*endp)
+ {
+ default:
+ return EINVAL;
+ case '\0':
+ break;
+ case '/':
+ p = endp + 1;
+ strtoul (p, &endp, 0);
+ if (endp == 0 || endp == p)
+ return EINVAL;
+ if (*endp != '\0')
+ return EINVAL;
+ }
+ return 0;
+}
+
+static error_t
+nbdopen (const char *name, int *mod_flags,
+ socket_t *sockport, size_t *blocksize, store_offset_t *size)
+{
+ int sock;
+ struct sockaddr_in sin;
+ const struct hostent *he;
+ char **ap;
+ struct nbd_startup ns;
+ ssize_t cc;
+ size_t ofs;
+ unsigned long int port;
+ char *hostname, *p, *endp;
+
+ if (!strncmp (name, url_prefix, sizeof url_prefix - 1))
+ name += sizeof url_prefix - 1;
+
+ /* First we have to parse the store name to get the host name and TCP
+ port number to connect to and the block size to use. */
+
+ hostname = strdupa (name);
+ p = strchr (hostname, ':');
+
+ if (p == 0)
+ return EINVAL;
+ *p++ = '\0';
+ port = strtoul (p, &endp, 0);
+ if (endp == 0 || endp == p || port > 0xffffUL)
+ return EINVAL;
+ switch (*endp)
+ {
+ default:
+ return EINVAL;
+ case '\0':
+ *blocksize = 1;
+ break;
+ case '/':
+ p = endp + 1;
+ *blocksize = strtoul (p, &endp, 0);
+ if (endp == 0 || endp == p)
+ return EINVAL;
+ if (*endp != '\0')
+ return EINVAL;
+ }
+
+ /* Now look up the host name and get a TCP connection. */
+
+ he = gethostbyname (hostname);
+ if (he == 0) /* XXX emit an error message? */
+ return errno; /* XXX what value will this have? */
+
+ sock = socket (PF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ return errno;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons (port);
+ for (ap = he->h_addr_list; *ap != 0; ++ap)
+ {
+ sin.sin_addr = *(const struct in_addr *) *ap;
+ errno = 0;
+ if (connect (sock, &sin, sizeof sin) == 0 || errno == ECONNREFUSED)
+ break;
+ }
+ if (errno != 0) /* last connect failed */
+ {
+ error_t err = errno;
+ close (sock);
+ return err;
+ }
+
+ /* Read the startup packet, which tells us the size of the store. */
+ ofs = 0;
+ do {
+ cc = read (sock, (char *) &ns + ofs, sizeof ns - ofs);
+ if (cc < 0)
+ {
+ error_t err = errno;
+ close (sock);
+ return err;
+ }
+ ofs += cc;
+ } while (cc > 0 && ofs < sizeof ns);
+
+ if (ofs != sizeof ns
+ || memcmp (ns.magic, NBD_INIT_MAGIC, sizeof ns.magic) != 0)
+ {
+ close (sock);
+ return EGRATUITOUS; /* ? */
+ }
+
+ *size = ntohll (ns.size);
+ *sockport = getdport (sock);
+ close (sock);
+
+ return 0;
+}
+
+static void
+nbdclose (struct store *store)
+{
+ if (store->port != MACH_PORT_NULL)
+ {
+ /* Send a disconnect message, but don't wait for a reply. */
+ struct nbd_request req =
+ {
+ magic: NBD_REQUEST_MAGIC,
+ type: htonl (2), /* disconnect */
+ };
+ mach_msg_type_number_t cc;
+ (void) io_write (store->port, (char *) &req, sizeof req, -1, &cc);
+
+ /* Close the socket. */
+ mach_port_deallocate (mach_task_self (), store->port);
+ store->port = MACH_PORT_NULL;
+ }
+}
+
+static error_t
+nbd_set_flags (struct store *store, int flags)
+{
+ if ((flags & ~STORE_INACTIVE) != 0)
+ /* Trying to set flags we don't support. */
+ return EINVAL;
+
+ nbdclose (store);
+ store->flags |= STORE_INACTIVE;
+
+ return 0;
+}
+
+static error_t
+nbd_clear_flags (struct store *store, int flags)
+{
+ error_t err = 0;
+ if ((flags & ~STORE_INACTIVE) != 0)
+ err = EINVAL;
+ err = store->name
+ ? nbdopen (store->name, &store->flags,
+ &store->port, &store->block_size, &store->size)
+ : ENOENT;
+ if (! err)
+ store->flags &= ~STORE_INACTIVE;
+ return err;
+}
+
+const struct store_class store_nbd_class =
+{
+ STORAGE_NETWORK, "nbd",
+ open: nbd_open,
+ validate_name: nbd_validate_name,
+ read: nbd_read,
+ write: nbd_write,
+ set_size: nbd_set_size,
+ allocate_encoding: store_std_leaf_allocate_encoding,
+ encode: store_std_leaf_encode,
+ decode: nbd_decode,
+ set_flags: nbd_set_flags, clear_flags: nbd_clear_flags,
+};
+STORE_STD_CLASS (nbd);
+
+/* Create a store from an existing socket to an nbd server.
+ The initial handshake has already been done. */
+error_t
+_store_nbd_create (mach_port_t port, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ return _store_create (&store_nbd_class,
+ port, flags, block_size, runs, num_runs, 0, store);
+}
+
+/* Open a new store backed by the named nbd server. */
+error_t
+store_nbd_open (const char *name, int flags, struct store **store)
+{
+ error_t err;
+ socket_t sock;
+ struct store_run run;
+ size_t blocksize;
+
+ run.start = 0;
+ err = nbdopen (name, &flags, &sock, &blocksize, &run.length);
+ if (!err)
+ {
+ run.length /= blocksize;
+ err = _store_nbd_create (sock, flags, blocksize, &run, 1, store);
+ if (! err)
+ {
+ if (!strncmp (name, url_prefix, sizeof url_prefix - 1))
+ err = store_set_name (*store, name);
+ else
+ asprintf (&(*store)->name, "%s%s", url_prefix, name);
+ if (err)
+ store_free (*store);
+ }
+ if (err)
+ mach_port_deallocate (mach_task_self (), sock);
+ }
+ return err;
+}
diff --git a/libstore/open.c b/libstore/open.c
new file mode 100644
index 00000000..5c00e107
--- /dev/null
+++ b/libstore/open.c
@@ -0,0 +1,65 @@
+/* Store creation from a file name
+
+ Copyright (C) 1996,97,98,2001, 2002 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
+ 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 <fcntl.h>
+#include <hurd.h>
+
+#include "store.h"
+
+/* Open the file NAME, and return a new store in STORE, which refers to the
+ storage underlying it. CLASSES is used to select classes specified by the
+ provider; if it is 0, STORE_STD_CLASSES is used. FLAGS is set with
+ store_set_flags. A reference to the open file is created (but may be
+ destroyed with store_close_source). */
+error_t
+store_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ error_t err;
+ int open_flags = (flags & STORE_HARD_READONLY) ? O_RDONLY : O_RDWR;
+ file_t node = file_name_lookup (name, open_flags, 0);
+
+ if (node == MACH_PORT_NULL && !(flags & STORE_HARD_READONLY)
+ && (errno == EACCES || errno == EROFS))
+ {
+ flags |= STORE_HARD_READONLY;
+ node = file_name_lookup (name, O_RDONLY, 0);
+ }
+
+ if (node == MACH_PORT_NULL)
+ return errno;
+
+ err = store_create (node, flags, classes, store);
+ if (err)
+ {
+ if (! (flags & STORE_NO_FILEIO))
+ /* Try making a store that does file io to NODE. */
+ err = store_file_create (node, flags, store);
+ if (err)
+ mach_port_deallocate (mach_task_self (), node);
+ }
+
+ return err;
+}
+
+const struct store_class
+store_query_class = { -1, "query", open: store_open };
+STORE_STD_CLASS (query);
diff --git a/libstore/part.c b/libstore/part.c
new file mode 100644
index 00000000..60ef6c21
--- /dev/null
+++ b/libstore/part.c
@@ -0,0 +1,209 @@
+/* Partition store backend
+ Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ Written by Neal H Walfield <neal@cs.uml.edu>
+
+ 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 "store.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include <parted/parted.h>
+/*#include <parted/device_gnu.h>*/
+
+/* XXX Until the Hurd specific header is available, provide the
+ declaration of ped_device_new_from_store here. */
+#warning "Using local declaration of ped_device_new_from_store."
+
+/* 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_destory. SOURCE is not registered in Parted's
+ list of devices. */
+PedDevice* ped_device_new_from_store (struct store *source);
+
+#include <string.h>
+#include <error.h>
+
+#define NEED_PARTED_VERSION "1.5.4"
+#ifndef PED_SECTOR_SIZE
+#define PED_SECTOR_SIZE PED_SECTOR_SIZE_DEFAULT
+#endif
+
+/* Return a new store in STORE which contains a remap store of partition
+ PART from the contents of SOURCE; SOURCE is consumed. */
+error_t
+store_part_create (struct store *source, int index, int flags,
+ struct store **store)
+{
+ static pthread_mutex_t parted_lock = PTHREAD_MUTEX_INITIALIZER;
+ static int version_check;
+
+ error_t err = 0;
+ PedDevice *dev;
+ PedDisk *disk;
+ PedPartition *part;
+ struct store_run run;
+
+ if ((source->block_size < PED_SECTOR_SIZE
+ && PED_SECTOR_SIZE % source->block_size != 0)
+ || (source->block_size > PED_SECTOR_SIZE
+ && source->block_size % PED_SECTOR_SIZE != 0))
+ return EINVAL;
+
+ pthread_mutex_lock (&parted_lock);
+
+ /* Since Parted provides no source-level information about
+ version compatibility, we have to check at run time. */
+ if (version_check == 0)
+ {
+ const char *version = ped_get_version ();
+ version_check = -1;
+ if (version == 0)
+ error (0, 0, "cannot get version of Parted library!");
+ else if (strverscmp (version, NEED_PARTED_VERSION) < 0)
+ error (0, 0, "Parted library version %s older than needed %s",
+ version, NEED_PARTED_VERSION);
+ else
+ version_check = 1;
+ }
+ if (version_check <= 0)
+ {
+ error (0, 0, "the `part' store type is not available");
+ pthread_mutex_unlock (&parted_lock);
+ return ENOTSUP;
+ }
+
+ ped_exception_fetch_all ();
+
+ dev = ped_device_new_from_store (source);
+ if (! dev)
+ {
+ ped_exception_catch ();
+ err = EIO;
+ goto out;
+ }
+
+ assert (ped_device_open (dev) != 0);
+
+ disk = ped_disk_new (dev);
+ if (! disk)
+ {
+ ped_exception_catch ();
+ err = EIO;
+ goto out_with_dev;
+ }
+
+ for (part = ped_disk_next_partition (disk, NULL); part;
+ part = ped_disk_next_partition (disk, part))
+ {
+ if (part->type != PED_PARTITION_LOGICAL
+ && part->type != 0 /* PED_PARTITION_PRIMARY */)
+ continue;
+
+ assert (part->num);
+ if (part->num == index)
+ break;
+ }
+
+ if (! part)
+ {
+ err = EIO;
+ goto out_with_disk;
+ }
+
+ if (source->block_size == PED_SECTOR_SIZE)
+ {
+ run.start = part->geom.start;
+ run.length = part->geom.length;
+ }
+ else if (source->block_size < PED_SECTOR_SIZE)
+ {
+ run.start = part->geom.start * (PED_SECTOR_SIZE / source->block_size);
+ run.length = part->geom.length * (PED_SECTOR_SIZE / source->block_size);
+ }
+ else
+ /* source->block_size > PED_SECTOR_SIZE */
+ {
+ run.start = part->geom.start * PED_SECTOR_SIZE;
+ if (run.start % source->block_size != 0)
+ err = EIO;
+ else
+ {
+ run.start /= source->block_size;
+ run.length = part->geom.length * PED_SECTOR_SIZE;
+ if (run.length % source->block_size != 0)
+ err = EIO;
+ else
+ run.length /= source->block_size;
+ }
+ }
+
+out_with_disk:
+ assert (ped_device_close (dev) != 0);
+ ped_disk_destroy (disk);
+out_with_dev:
+ ped_device_destroy (dev);
+out:
+ ped_exception_leave_all ();
+ pthread_mutex_unlock (&parted_lock);
+
+ if (! err)
+ err = store_remap (source, &run, 1, store);
+
+ return err;
+}
+
+/* Open the part NAME. NAME consists of a partition number, a ':', a another
+ store class name, a ':' and a name for to by passed to the store class.
+ E.g. "2:device:hd0" would open the second partition on a DEVICE store
+ named "hd0". FLAGS indicate how to open the store. CLASSES is used to
+ select classes specified by the type NAME; if it is 0, STORE_STD_CLASSES
+ is used. The new store is returned in *STORE. */
+error_t
+store_part_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ int part;
+ char *endp;
+ struct store *source;
+ error_t err;
+
+ part = strtol (name, &endp, 0);
+ if (endp == name || *endp != ':')
+ return EINVAL;
+
+ name = endp + 1;
+ if (*name == '\0')
+ return EINVAL;
+
+ err = store_typed_open (name, flags, classes, &source);
+ if (! err)
+ {
+ err = store_part_create (source, part, flags, store);
+ if (err)
+ store_free (source);
+ }
+
+ return err;
+}
+
+const struct store_class
+store_part_class = { -1, "part", open: store_part_open };
+STORE_STD_CLASS (part);
diff --git a/libstore/rdwr.c b/libstore/rdwr.c
new file mode 100644
index 00000000..9737c515
--- /dev/null
+++ b/libstore/rdwr.c
@@ -0,0 +1,298 @@
+/* Store I/O
+
+ Copyright (C) 1995-1999,2001,2002,2003 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
+ 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 <string.h>
+#include <sys/mman.h>
+
+#include "store.h"
+
+/* Returns in RUN the tail of STORE's run list, who's first run contains
+ ADDR, and is not a hole, and in RUNS_END a pointer pointing at the end of
+ the run list. Returns the offset within it at which ADDR occurs. Also
+ returns BASE, which should be added to offsets from RUNS. */
+static inline store_offset_t
+store_find_first_run (struct store *store, store_offset_t addr,
+ struct store_run **run, struct store_run **runs_end,
+ store_offset_t *base, size_t *index)
+{
+ struct store_run *tail = store->runs, *tail_end = tail + store->num_runs;
+ store_offset_t wrap_src = store->wrap_src;
+
+ if (addr >= wrap_src && addr < store->end)
+ /* Locate the correct position within a repeating pattern of runs. */
+ {
+ *base = addr / store->wrap_dst;
+ addr %= wrap_src;
+ }
+ else
+ *base = 0;
+
+ /* XXX: this isn't going to be very efficient if RUNS is very complex...
+ But it should do dandy if it's short. For long run lists, we could do a
+ binary search or something. */
+ while (tail < tail_end)
+ {
+ store_offset_t run_blocks = tail->length;
+
+ if (run_blocks > addr)
+ {
+ *run = tail;
+ *runs_end = tail_end;
+ *index = tail - store->runs;
+ return addr;
+ }
+
+ /* Not to the right place yet, move on... */
+ addr -= run_blocks;
+ tail++;
+ }
+
+ return -1;
+}
+
+/* Update RUN, BASE, & INDEX to point to the next elemement in the runs
+ array. RUNS_END is the point where RUNS will wrap. Returns true if
+ things are still kosher. */
+static inline int
+store_next_run (struct store *store, struct store_run *runs_end,
+ struct store_run **run, store_offset_t *base, size_t *index)
+{
+ (*run)++;
+ (*index)++;
+
+ if (*run == runs_end)
+ /* Wrap around in a repeating RUNS. */
+ {
+ *run = store->runs;
+ *base += store->wrap_dst;
+ *index = 0;
+ return (*base < store->end);
+ }
+ else
+ return 1;
+}
+
+/* Write LEN bytes from BUF to STORE at ADDR. Returns the amount written
+ in AMOUNT. ADDR is in BLOCKS (as defined by STORE->block_size). */
+error_t
+store_write (struct store *store,
+ store_offset_t addr, const void *buf, size_t len, size_t *amount)
+{
+ error_t err;
+ size_t index;
+ store_offset_t base;
+ struct store_run *run, *runs_end;
+ int block_shift = store->log2_block_size;
+ store_write_meth_t write = store->class->write;
+
+ if (store->flags & STORE_READONLY)
+ return EROFS; /* XXX */
+
+ if ((addr << block_shift) + len > store->size)
+ return EIO;
+
+ if (store->block_size != 0 && (len & (store->block_size - 1)) != 0)
+ return EINVAL;
+
+ addr = store_find_first_run (store, addr, &run, &runs_end, &base, &index);
+ if (addr < 0)
+ err = EIO;
+ else if ((len >> block_shift) <= run->length - addr)
+ /* The first run has it all... */
+ err = (*write)(store, base + run->start + addr, index, buf, len, amount);
+ else
+ /* ARGH, we've got to split up the write ... */
+ {
+ mach_msg_type_number_t try, written;
+
+ /* Write the initial bit in the first run. Errors here are returned. */
+ try = (run->length - addr) << block_shift;
+ err = (*write) (store, base + run->start + addr, index, buf, try,
+ &written);
+
+ if (!err && written == try)
+ /* Wrote the first bit successfully, now do the rest. Any errors
+ will just result in a short write. */
+ {
+ buf += written;
+ len -= written;
+
+ while (store_next_run (store, runs_end, &run, &base, &index)
+ && run->start >= 0) /* Check for holes. */
+ /* Ok, we can write in this run, at least a bit. */
+ {
+ mach_msg_type_number_t seg_written;
+
+ if ((len >> block_shift) <= run->length)
+ try = len;
+ else
+ try = run->length << block_shift;
+
+ err = (*write)(store, base + run->start, index, buf, try,
+ &seg_written);
+ if (err)
+ break; /* Ack */
+ written += seg_written;
+
+ if (seg_written < try)
+ break; /* Didn't use up the run, we're done. */
+
+ len -= seg_written;
+ if (len == 0)
+ break; /* Nothing left to write! */
+
+ buf += seg_written;
+ }
+ }
+
+ *amount = written;
+ }
+
+ return err;
+}
+
+/* Read AMOUNT bytes from STORE at ADDR into BUF & LEN (which follows the
+ usual mach buffer-return semantics) to STORE at ADDR. ADDR is in BLOCKS
+ (as defined by STORE->block_size). */
+error_t
+store_read (struct store *store,
+ store_offset_t addr, size_t amount, void **buf, size_t *len)
+{
+ size_t index;
+ store_offset_t base;
+ struct store_run *run, *runs_end;
+ int block_shift = store->log2_block_size;
+ store_read_meth_t read = store->class->read;
+
+ addr = store_find_first_run (store, addr, &run, &runs_end, &base, &index);
+ if (addr < 0 || run->start < 0)
+ return EIO; /* Reading from a hole. */
+
+ if ((addr << block_shift) + amount > store->size)
+ amount = store->size - (addr << block_shift);
+
+ if (store->block_size != 0 && (amount & (store->block_size - 1)) != 0)
+ return EINVAL;
+
+ if ((amount >> block_shift) <= run->length - addr)
+ /* The first run has it all... */
+ return (*read) (store, base + run->start + addr, index, amount, buf, len);
+ else
+ /* ARGH, we've got to split up the read ... This isn't fun. */
+ {
+ error_t err;
+ int all;
+ /* WHOLE_BUF and WHOLE_BUF_LEN will point to a buff that's large enough
+ to hold the entire request. This is initially whatever the user
+ passed in, but we'll change it as necessary. */
+ void *whole_buf = *buf, *buf_end;
+ size_t whole_buf_len = *len;
+
+ /* Read LEN bytes from the store address ADDR into BUF_END. BUF_END
+ and AMOUNT are adjusted by the amount actually read. Whether or not
+ the amount read is the same as what was request is returned in ALL. */
+ inline error_t seg_read (store_offset_t addr, size_t len, int *all)
+ {
+ /* SEG_BUF and SEG_LEN are the buffer for a particular bit of the
+ whole (within one run). */
+ void *seg_buf = buf_end;
+ size_t seg_buf_len = len;
+ error_t err =
+ (*read)(store, addr, index, len, &seg_buf, &seg_buf_len);
+ if (!err)
+ {
+ /* If for some bizarre reason, the underlying storage chose not
+ to use the buffer space we so kindly gave it, copy it to
+ that space. */
+ if (seg_buf != buf_end)
+ {
+ memcpy (buf_end, seg_buf, seg_buf_len);
+ munmap (seg_buf, seg_buf_len);
+ }
+ buf_end += seg_buf_len;
+ amount -= seg_buf_len;
+ *all = (seg_buf_len == len);
+ }
+ return err;
+ }
+
+ if (whole_buf_len < amount)
+ /* Not enough room in the user's buffer to hold everything, better
+ make room. */
+ {
+ whole_buf_len = amount;
+ whole_buf = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (whole_buf == (void *) -1)
+ return errno; /* Punt early, there's nothing to clean up. */
+ }
+
+ buf_end = whole_buf;
+
+ err = seg_read (base + run->start + addr,
+ (run->length - addr) << block_shift, &all);
+ while (!err && all && amount > 0
+ && store_next_run (store, runs_end, &run, &base, &index))
+ {
+ if (run->start < 0)
+ /* A hole! Can't read here. Must stop. */
+ break;
+ else
+ err = seg_read (base + run->start,
+ (amount >> block_shift) <= run->length
+ ? amount /* This run has the rest. */
+ : (run->length << block_shift), /* Whole run. */
+ &all);
+ }
+
+ /* The actual amount read. */
+ *len = buf_end - whole_buf;
+ if (*len > 0)
+ err = 0; /* Return a short read instead of an error. */
+
+ /* Deallocate any amount of WHOLE_BUF we didn't use. */
+ if (whole_buf != *buf)
+ {
+ if (err)
+ munmap (whole_buf, whole_buf_len);
+ else
+ {
+ vm_size_t unused = whole_buf_len - round_page (*len);
+ if (unused)
+ munmap (whole_buf + whole_buf_len - unused, unused);
+ *buf = whole_buf;
+ }
+ }
+
+ return err;
+ }
+}
+
+/* Set STORE's size to NEWSIZE (in bytes). */
+error_t
+store_set_size (struct store *store, size_t newsize)
+{
+ error_t err;
+ store_set_size_meth_t set_size = store->class->set_size;
+
+ /* Updating the runs list is up to the class set_size method. */
+ err = (* set_size) (store, newsize);
+
+ return err;
+}
diff --git a/libstore/remap.c b/libstore/remap.c
new file mode 100644
index 00000000..55ab51ac
--- /dev/null
+++ b/libstore/remap.c
@@ -0,0 +1,345 @@
+/* Block address translation
+
+ Copyright (C) 1996,97,99,2001, 2002 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
+ 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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <hurd/fs.h>
+
+#include "store.h"
+
+static error_t
+remap_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount,
+ void **buf, size_t *len)
+{
+ return store_read (store->children[0], addr, amount, buf, len);
+}
+
+static error_t
+remap_write (struct store *store,
+ store_offset_t addr, size_t index, const void *buf, size_t len,
+ size_t *amount)
+{
+ return store_write (store->children[0], addr, buf, len, amount);
+}
+
+static error_t
+remap_set_size (struct store *store, size_t newsize)
+{
+ return store_set_size (store->children[0], newsize);
+}
+
+error_t
+remap_allocate_encoding (const struct store *store, struct store_enc *enc)
+{
+ enc->num_ints += 3;
+ enc->num_offsets += store->num_runs * 2;
+ return store_allocate_child_encodings (store, enc);
+}
+
+error_t
+remap_encode (const struct store *store, struct store_enc *enc)
+{
+ int i;
+ enc->ints[enc->cur_int++] = store->class->id;
+ enc->ints[enc->cur_int++] = store->flags;
+ enc->ints[enc->cur_int++] = store->num_runs;
+ for (i = 0; i < store->num_runs; i++)
+ {
+ enc->offsets[enc->cur_offset++] = store->runs[i].start;
+ enc->offsets[enc->cur_offset++] = store->runs[i].length;
+ }
+ return store_encode_children (store, enc);
+}
+
+error_t
+remap_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ if (enc->cur_int + 3 > enc->num_ints)
+ return EINVAL;
+ else
+ {
+ int type __attribute__((unused)) = enc->ints[enc->cur_int++];
+ int flags = enc->ints[enc->cur_int++];
+ int num_runs = enc->ints[enc->cur_int++];
+ error_t create_remap (const struct store_run *runs, size_t num_runs)
+ {
+ struct store *source;
+ error_t err = store_decode_children (enc, 1, classes, &source);
+ if (! err)
+ err = store_remap_create (source, runs, num_runs, flags, store);
+ return err;
+ }
+ return store_with_decoded_runs (enc, num_runs, create_remap);
+ }
+}
+
+error_t
+remap_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ error_t err;
+ struct store *from;
+ const char *end, *p;
+ struct store_run *runs;
+ size_t nruns;
+
+ end = strchr (name, ':');
+ if (!end)
+ return EINVAL;
+
+ runs = alloca ((end - name) * sizeof runs[0]);
+
+ nruns = 0;
+ p = name;
+ do
+ {
+ char *endp;
+ runs[nruns].start = strtoul (p, &endp, 0);
+ if (*endp == '+')
+ {
+ if (endp == p) /* Syntax "+5,+7" means "0+5,0+7". */
+ runs[nruns].start = 0;
+ p = endp + 1;
+ if (p == end || *p == ',')
+ {
+ /* Syntax "100+" means block 100 to the end of the store.
+ Since we don't know the size yet, we use -1 as a marker
+ for the code below. */
+ runs[nruns++].length = (store_offset_t) -1;
+ break;
+ }
+ runs[nruns].length = strtoul (p, &endp, 0);
+ if (endp == p)
+ return EINVAL;
+ }
+ else if (endp == p) /* Must have a number unless starts with +. */
+ return EINVAL;
+ else
+ runs[nruns].length = 1;
+ ++nruns;
+ p = endp;
+ if (*p == ',')
+ ++p;
+ } while (p < end);
+
+ err = store_typed_open (end + 1, flags, classes, &from);
+ if (!err)
+ {
+ /* Check for any runs marked as "through the end of the store"
+ and update them to use the actual size of the store. */
+ size_t i;
+ for (i = 0; i < nruns; ++i)
+ if (runs[i].length == (store_offset_t) -1)
+ runs[i].length = from->blocks - runs[i].start;
+
+ /* Now do the remapping according to RUNS. */
+ err = store_remap (from, runs, nruns, store);
+ if (err)
+ store_free (from);
+ }
+ return err;
+}
+
+error_t
+remap_validate_name (const char *name,
+ const struct store_class *const *classes)
+{
+ const char *end = strchr (name, ':');
+ const char *p;
+
+ if (!end)
+ return EINVAL;
+
+ p = name;
+ do
+ {
+ if (*p != '+')
+ {
+ if (!isdigit (*p))
+ return EINVAL;
+ do
+ ++p;
+ while (isdigit (*p));
+ }
+
+ if (*p == '+')
+ {
+ ++p;
+ if (!isdigit (*p))
+ return EINVAL;
+ do
+ ++p;
+ while (isdigit (*p));
+ }
+
+ if (*p == ',')
+ ++p;
+ else if (*p == ':')
+ return 0;
+ } while (*p != '\0');
+
+ return EINVAL;
+}
+
+
+const struct store_class
+store_remap_class =
+{
+ STORAGE_REMAP, "remap", remap_read, remap_write, remap_set_size,
+ remap_allocate_encoding, remap_encode, remap_decode,
+ store_set_child_flags, store_clear_child_flags,
+ NULL, NULL, NULL, /* cleanup, clone, remap */
+ remap_open, remap_validate_name
+};
+STORE_STD_CLASS (remap);
+
+/* Return a new store in STORE that reflects the blocks in RUNS & RUNS_LEN
+ from SOURCE; SOURCE is consumed, but RUNS is not. Unlike the
+ store_remap function, this function always operates by creating a new
+ store of type `remap' which has SOURCE as a child, and so may be less
+ efficient than store_remap for some types of stores. */
+error_t
+store_remap_create (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ int flags, struct store **store)
+{
+ error_t err =
+ _store_create (&store_remap_class, MACH_PORT_NULL, flags | source->flags,
+ source->block_size, runs, num_runs, 0, store);
+
+ if (! err)
+ {
+ err = store_set_children (*store, &source, 1);
+ if (err)
+ store_free (*store);
+ }
+
+ return err;
+}
+
+/* For each run in RUNS, of length NUM_RUNS, translate the */
+error_t
+store_remap_runs (const struct store_run *runs, size_t num_runs,
+ const struct store_run *base_runs, size_t num_base_runs,
+ struct store_run **xruns, size_t *num_xruns)
+{
+ int i, j;
+ size_t xruns_alloced = num_runs + num_base_runs;
+
+ /* Add the single run [ADDR, LEN) to *XRUNS, returning true if successful. */
+ int add_run (store_offset_t addr, store_offset_t len)
+ {
+ if (*num_xruns == xruns_alloced)
+ /* Make some more space in *XRUNS. */
+ {
+ struct store_run *new;
+ xruns_alloced *= 2;
+ new = realloc (*xruns, xruns_alloced * sizeof (struct store_run));
+ if (! new)
+ return 0;
+ *xruns = new;
+ }
+ (*xruns)[(*num_xruns)++] = (struct store_run){ addr, len };
+ return 1;
+ }
+
+ *xruns = malloc (xruns_alloced * sizeof (struct store_run));
+ if (! *xruns)
+ return ENOMEM;
+
+ /* Clean up and return error code CODE. */
+#define ERR(code) do { free (*xruns); return (code); } while (0)
+
+ for (i = 0; i < num_runs; i++)
+ {
+ store_offset_t addr = runs[i].start, left = runs[i].length;
+
+ if (addr >= 0)
+ for (j = 0; j < num_base_runs && left > 0; j++)
+ {
+ store_offset_t baddr = base_runs[j].start;
+ store_offset_t blen = base_runs[j].length;
+
+ if (addr >= blen)
+ addr -= blen;
+ else if (baddr < 0)
+ /* A hole, which is invalid. */
+ ERR (EINVAL);
+ else
+ /* Add another output run. */
+ {
+ store_offset_t len = blen - addr; /* Size of next output run. */
+ if (! add_run (baddr + addr, len > left ? left : len))
+ ERR (ENOMEM);
+ addr = 0;
+ left -= len;
+ }
+ }
+ else
+ /* a hole */
+ if (! add_run (-1, left))
+ ERR (ENOMEM);
+ }
+
+ if (xruns_alloced > *num_xruns)
+ *xruns = realloc (*xruns, *num_xruns * sizeof (struct store_run));
+
+ return 0;
+}
+
+/* Return a store in STORE that reflects the blocks in RUNS & RUNS_LEN from
+ SOURCE; SOURCE is consumed, but not RUNS. Unlike the store_remap_create
+ function, this may simply modify SOURCE and return it. */
+error_t
+store_remap (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ if (source->class->remap)
+ /* Use the class-specific remaping function. */
+ return (* source->class->remap) (source, runs, num_runs, store);
+ else
+ /* Just replace SOURCE's runs-list by an appropiately translated RUNS. */
+ {
+ struct store_run *xruns = 0;
+ size_t num_xruns = 0;
+ error_t err =
+ store_remap_runs (runs, num_runs, source->runs, source->num_runs,
+ &xruns, &num_xruns);
+ if (! err)
+ {
+ /* Don't use store_set_runs -- we've already allocated the
+ storage. */
+ free (source->runs);
+ source->runs = xruns;
+ source->num_runs = num_xruns;
+ source->flags &= ~STORE_ENFORCED;
+ source->end = 0; /* Needed to make _store_derive work. */
+ store_close_source (source);
+ _store_derive (source);
+ *store = source;
+ }
+ return err;
+ }
+}
diff --git a/libstore/set.c b/libstore/set.c
new file mode 100644
index 00000000..b9ff4f41
--- /dev/null
+++ b/libstore/set.c
@@ -0,0 +1,78 @@
+/* Setting various store fields
+
+ Copyright (C) 1995,96,97,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
+ 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 <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <mach.h>
+
+#include "store.h"
+
+/* Set STORE's current runs list to (a copy of) RUNS and NUM_RUNS. */
+error_t
+store_set_runs (struct store *store,
+ const struct store_run *runs, size_t num_runs)
+{
+ unsigned size = num_runs * sizeof (struct store_run);
+ struct store_run *copy = malloc (size);
+
+ if (!copy)
+ return ENOMEM;
+
+ if (store->runs)
+ free (store->runs);
+
+ memcpy (copy, runs, size);
+ store->runs = copy;
+ store->num_runs = num_runs;
+
+ if (store->block_size > 0)
+ _store_derive (store);
+
+ return 0;
+}
+
+/* Sets the name associated with STORE to a copy of NAME. */
+error_t
+store_set_name (struct store *store, const char *name)
+{
+ char *copy = strdup (name);
+
+ if (!copy)
+ return ENOMEM;
+
+ if (store->name)
+ free (store->name);
+
+ store->name = copy;
+
+ return 0;
+}
+
+/* If STORE was created using store_create, remove the reference to the
+ source from which it was created. */
+void store_close_source (struct store *store)
+{
+ if (store->source != MACH_PORT_NULL)
+ {
+ mach_port_deallocate (mach_task_self (), store->source);
+ store->source = MACH_PORT_NULL;
+ }
+}
diff --git a/libstore/std.c b/libstore/std.c
new file mode 100644
index 00000000..4784a8a5
--- /dev/null
+++ b/libstore/std.c
@@ -0,0 +1,44 @@
+/* List of standard store classes
+
+ Copyright (C) 1996,97,2001 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
+ 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 "store.h"
+
+const struct store_class *const __attribute__ ((section ("store_std_classes")))
+store_std_classes[] =
+{
+ &store_device_class,
+#if HAVE_PARTED_PARTED_H
+ &store_part_class,
+#endif
+ &store_file_class,
+ &store_zero_class,
+ &store_task_class,
+ &store_ileave_class, &store_concat_class, &store_remap_class,
+ &store_query_class,
+ &store_copy_class, &store_gunzip_class, &store_bunzip2_class,
+
+ /* This pseudo-class must appear before any real STORAGE_NETWORK class,
+ to parse STORAGE_NETWORK file_get_storage_info results properly. */
+ &store_url_open_class,
+ &store_nbd_class,
+
+ &store_typed_open_class,
+ 0
+};
diff --git a/libstore/store.h b/libstore/store.h
new file mode 100644
index 00000000..ae334a1d
--- /dev/null
+++ b/libstore/store.h
@@ -0,0 +1,800 @@
+/* Store I/O
+
+ Copyright (C) 1995,96,97,98,99,2001,02,04,05 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
+ 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. */
+
+/* A `store' is a fixed-size block of storage, which can be read and perhaps
+ written to. This library implements many different backends which allow
+ the abstract store interface to be used with common types of storage --
+ devices, files, memory, tasks, etc. It also allows stores to be combined
+ and filtered in various ways. */
+
+#ifndef __STORE_H__
+#define __STORE_H__
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <mach.h>
+#include <device/device.h>
+#include <hurd/hurd_types.h>
+#include <features.h>
+
+#ifdef STORE_DEFINE_EI
+#define STORE_EI
+#else
+#define STORE_EI __extern_inline
+#endif
+
+/* Type for addresses inside the store. */
+typedef off64_t store_offset_t;
+
+/* A portion of a store. If START == -1, it's a hole. */
+struct store_run
+{
+ store_offset_t start, length;
+};
+
+struct store
+{
+ /* If this store was created using store_create, the file from which we got
+ our store. */
+ file_t source;
+
+ /* Address ranges in the underlying storage which make up our contiguous
+ address space. In units of BLOCK_SIZE, below. */
+ struct store_run *runs; /* Malloced */
+ size_t num_runs; /* Length of RUNS. */
+
+ /* Maximum valid offset. This is the same as SIZE, but in blocks. */
+ store_offset_t end;
+
+ /* WRAP_SRC is the sum of the run lengths in RUNS. If this is less than
+ END, then RUNS describes a repeating pattern, of length WRAP_SRC -- each
+ successive iteration having an additional offset of WRAP_DST. */
+ store_offset_t wrap_src;
+ store_offset_t wrap_dst; /* Only meaningful if WRAP_SRC < END */
+
+ /* Handles for the underlying storage. */
+ char *name; /* Malloced */
+ mach_port_t port; /* Send right */
+
+ /* The size of a `block' on this storage. */
+ size_t block_size;
+
+ /* The number of blocks (of size BLOCK_SIZE) in this storage. */
+ store_offset_t blocks;
+ /* The number of bytes in this storage, including holes. */
+ store_offset_t size;
+
+ /* Log_2 (BLOCK_SIZE) or 0 if not a power of 2. */
+ unsigned log2_block_size;
+ /* Log_2 (VM_PAGE_SIZE / BLOCK_SIZE); only valid if LOG2_BLOCK_SIZE is. */
+ unsigned log2_blocks_per_page;
+
+ /* Random flags. */
+ int flags;
+
+ void *misc; /* malloced */
+ size_t misc_len;
+
+ const struct store_class *class;
+
+ /* A list of sub-stores. The interpretation of this is type-specific. */
+ struct store **children;
+ size_t num_children;
+
+ void *hook; /* Type specific noise. */
+};
+
+/* Store flags. These are in addition to the STORAGE_ flags defined in
+ <hurd/hurd_types.h>. XXX synchronize these values. */
+
+/* Flags that reflect something immutable about the object. */
+#define STORE_IMMUTABLE_FLAGS 0x00FF
+
+/* Flags implemented by generic store code. */
+#define STORE_READONLY 0x0100 /* No writing allowed. */
+#define STORE_NO_FILEIO 0x0200 /* If store_create can't fetch store
+ information, don't create a store
+ using file io instead. */
+#define STORE_GENERIC_FLAGS (STORE_READONLY | STORE_NO_FILEIO)
+
+/* Flags implemented by each backend. */
+#define STORE_HARD_READONLY 0x1000 /* Can't be made writable. */
+#define STORE_ENFORCED 0x2000 /* Range is enforced by device. */
+#define STORE_INACTIVE 0x4000 /* Not in a usable state. */
+#define STORE_INNOCUOUS 0x8000 /* Cannot modify anything dangerous. */
+#define STORE_BACKEND_SPEC_BASE 0x10000 /* Here up are backend-specific */
+#define STORE_BACKEND_FLAGS (STORE_HARD_READONLY | STORE_ENFORCED \
+ | STORE_INACTIVE \
+ | ~(STORE_BACKEND_SPEC_BASE - 1))
+
+typedef error_t (*store_write_meth_t)(struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf,
+ mach_msg_type_number_t len,
+ mach_msg_type_number_t *amount);
+typedef error_t (*store_read_meth_t)(struct store *store,
+ store_offset_t addr, size_t index,
+ mach_msg_type_number_t amount,
+ void **buf, mach_msg_type_number_t *len);
+typedef error_t (*store_set_size_meth_t)(struct store *store,
+ size_t newsize);
+
+struct store_enc; /* fwd decl */
+
+struct store_class
+{
+ /* The type of storage this is (see STORAGE_ in hurd/hurd_types.h). */
+ enum file_storage_class id;
+
+ /* Name of the class. */
+ const char *name;
+
+ /* Read up to AMOUNT bytes at the underlying address ADDR from the storage
+ into BUF and LEN. INDEX varies from 0 to the number of runs in STORE. */
+ store_read_meth_t read;
+ /* Write up to LEN bytes from BUF to the storage at the underlying address
+ ADDR. INDEX varies from 0 to the number of runs in STORE. */
+ store_write_meth_t write;
+ /* Set store's size to NEWSIZE (in bytes). */
+ store_set_size_meth_t set_size;
+
+ /* To the lengths of each for the four arrays in ENC, add how much STORE
+ would need to be encoded. */
+ error_t (*allocate_encoding)(const struct store *store,
+ struct store_enc *enc);
+ /* Append the encoding for STORE to ENC. */
+ error_t (*encode) (const struct store *store, struct store_enc *enc);
+
+ /* Decode from ENC a new store, which return in STORE. CLASSES is used to
+ lookup child classes. */
+ error_t (*decode) (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store);
+
+ /* Modify flags that reflect backend state, such as STORE_HARD_READONLY and
+ STORE_ENFORCED. */
+ error_t (*set_flags) (struct store *store, int flags);
+ error_t (*clear_flags) (struct store *store, int flags);
+
+ /* Called just before deallocating STORE. */
+ void (*cleanup) (struct store *store);
+
+ /* Copy any format-dependent fields in FROM to TO; if there's some reason
+ why the copy can't be made, an error should be returned. This call is
+ made after all format-indendependent fields have been cloned. */
+ error_t (*clone) (const struct store *from, struct store *to);
+
+ /* Return in STORE a store that only contains the parts of SOURCE as
+ enumerated in RUNS & RUNS_LEN, consuming SOURCE in the process. The
+ default behavior, if REMAP is 0, is to replace SOURCE's run list with
+ the subset selected by RUNS, and return SOURCE. */
+ error_t (*remap) (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+ /* Open a new store called NAME in this class. CLASSES is supplied in case
+ it's desirable to open a sub-store in some manner. */
+ error_t (*open) (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+ /* Given a user argument ARG, this function should check it for syntactic
+ validity, or print a syntax error, using ARGP_STATE in the normal
+ manner; if zero is returned, then this argument is assumed valid, and
+ can be passed to the open function. If ARG is 0, then there were *no*
+ arguments specified; in this case, returning EINVAL means that this is
+ not kosher. If PARSE is 0, then it is assumed that if this class has an
+ OPEN function, then validity can't be syntactically determined. */
+ error_t (*validate_name) (const char *name,
+ const struct store_class *const *classes);
+
+ /* Return a memory object paging on STORE. */
+ error_t (*map) (const struct store *store, vm_prot_t prot, mach_port_t *memobj);
+};
+
+/* Return a new store in STORE, which refers to the storage underlying
+ SOURCE. CLASSES is as if passed to store_find_class, which see. FLAGS
+ is set with store_set_flags, with the exception of STORE_INACTIVE, which
+ merely indicates that no attempt should be made to activate an inactive
+ store; if STORE_INACTIVE is not specified, and the store returned for
+ SOURCE is inactive, an attempt is made to activate it (failure of which
+ causes an error to be returned). A reference to SOURCE is created (but
+ may be destroyed with store_close_source). */
+error_t store_create (file_t source, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+void store_free (struct store *store);
+
+/* Open the file NAME, and return a new store in STORE, which refers to the
+ storage underlying it. CLASSES is as if passed to store_find_class,
+ which see. FLAGS is set with store_set_flags. A reference to the open
+ file is created (but may be destroyed with store_close_source). */
+error_t store_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Allocate a new store structure, returned in STORE, with class CLASS and
+ the various other fields initialized to the given parameters. */
+error_t
+_store_create (const struct store_class *class, mach_port_t port,
+ int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ store_offset_t end, struct store **store);
+
+/* Set STORE's current runs list to (a copy of) RUNS and NUM_RUNS. */
+error_t store_set_runs (struct store *store,
+ const struct store_run *runs, size_t num_runs);
+
+/* Set STORE's current children to (a copy of) CHILDREN and NUM_CHILDREN
+ (note that just the vector CHILDREN is copied, not the actual children). */
+error_t store_set_children (struct store *store,
+ struct store *const *children, size_t num_children);
+
+/* Try to come up with a name for the children in STORE, combining the names
+ of each child in a way that could be used to parse them with
+ store_open_children. This is done heuristically, and so may not succeed.
+ If a child doesn't have a name, EINVAL is returned. */
+error_t store_children_name (const struct store *store, char **name);
+
+/* Sets the name associated with STORE to a copy of NAME. */
+error_t store_set_name (struct store *store, const char *name);
+
+/* Add FLAGS to STORE's currently set flags. */
+error_t store_set_flags (struct store *store, int flags);
+
+/* Remove FLAGS from STORE's currently set flags. */
+error_t store_clear_flags (struct store *store, int flags);
+
+/* Set FLAGS in all children of STORE, and if successful, add FLAGS to
+ STORE's flags. */
+error_t store_set_child_flags (struct store *store, int flags);
+
+/* Clear FLAGS in all children of STORE, and if successful, remove FLAGS from
+ STORE's flags. */
+error_t store_clear_child_flags (struct store *store, int flags);
+
+extern int store_is_securely_returnable (struct store *store, int open_flags);
+
+#if defined(__USE_EXTERN_INLINES) || defined(STORE_DEFINE_EI)
+
+/* Returns true if STORE can safely be returned to a user who has accessed it
+ via a node using OPEN_FLAGS, without compromising security. */
+STORE_EI int
+store_is_securely_returnable (struct store *store, int open_flags)
+{
+ int flags = store->flags;
+ return
+ (flags & (STORE_INNOCUOUS | STORE_INACTIVE))
+ || ((flags & STORE_ENFORCED)
+ && (((open_flags & O_ACCMODE) == O_RDWR)
+ || (flags & STORE_HARD_READONLY)));
+}
+
+#endif /* Use extern inlines. */
+
+/* Fills in the values of the various fields in STORE that are derivable from
+ the set of runs & the block size. */
+void _store_derive (struct store *store);
+
+/* Return in TO a copy of FROM. */
+error_t store_clone (struct store *from, struct store **to);
+
+/* Return a store in STORE that reflects the blocks in RUNS & RUNS_LEN from
+ source; SOURCE is consumed, but not RUNS. Unlike the store_remap_create
+ function, this may simply modify SOURCE and return it. */
+error_t store_remap (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+/* Write LEN bytes from BUF to STORE at ADDR. Returns the amount written in
+ AMOUNT (in bytes). ADDR is in BLOCKS (as defined by STORE->block_size). */
+error_t store_write (struct store *store,
+ store_offset_t addr, const void *buf, size_t len,
+ size_t *amount);
+
+/* Read AMOUNT bytes from STORE at ADDR into BUF & LEN (which following the
+ usual mach buffer-return semantics) to STORE at ADDR. ADDR is in BLOCKS
+ (as defined by STORE->block_size). Note that LEN is in bytes. */
+error_t store_read (struct store *store,
+ store_offset_t addr, size_t amount, void **buf, size_t *len);
+
+/* Set STORE's size to NEWSIZE (in bytes). */
+error_t store_set_size (struct store *store, size_t newsize);
+
+/* If STORE was created using store_create, remove the reference to the
+ source from which it was created. */
+void store_close_source (struct store *store);
+
+/* Return a memory object paging on STORE. If this call fails with
+ EOPNOTSUPP, you can try calling some of the routines below to get a pager. */
+error_t store_map (const struct store *store, vm_prot_t prot,
+ mach_port_t *memobj);
+
+#if 0
+
+/* Create a new pager and paging threads paging on STORE, and return the
+ resulting memory object in PAGER. */
+error_t store_create_pager (struct store *store, vm_prot_t prot, ...,
+ mach_port_t *memobj)
+
+#endif
+
+/* Creating specific types of stores. */
+
+/* Return a new zero store SIZE bytes long in STORE. */
+error_t store_zero_create (store_offset_t size, int flags, struct store **store);
+
+/* Return a new store in STORE referring to the mach device DEVICE. Consumes
+ the send right DEVICE. */
+error_t store_device_create (device_t device, int flags, struct store **store);
+
+/* Like store_device_create, but doesn't query the device for information. */
+error_t _store_device_create (device_t device, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **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);
+
+/* Return a new store in STORE which contains a remap store of partition
+ PART from the contents of SOURCE; SOURCE is consumed. */
+error_t store_part_create (struct store *source, int index, int flags,
+ struct store **store);
+
+/* Open the part NAME. NAME consists of a partition number, a ':', a
+ another store class name, a ':' and a name for to by passed to the
+ store class. E.g. "2:device:hd0" would open the second partition
+ on a DEVICE store named "hd0". FLAGS indicate how to open the
+ store. CLASSES is as if passed to store_find_class, which see.
+ The new store is returned in *STORE. */
+error_t store_part_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Return a new store in STORE referring to the file FILE. Unlike
+ store_create, this will always use file i/o, even it would be possible to
+ be more direct. This may work in more cases, for instance if the file has
+ holes. Consumes the send right FILE. */
+error_t store_file_create (file_t file, int flags, struct store **store);
+
+/* Like store_file_create, but doesn't query the file for information. */
+error_t _store_file_create (file_t file, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+/* Open the file NAME, and return the corresponding store in STORE. */
+error_t store_file_open (const char *name, int flags, struct store **store);
+
+/* Return a new store in STORE referring to the task TASK, consuming TASK. */
+error_t store_task_create (task_t task, int flags, struct store **store);
+
+/* Like store_task_create, but doesn't query the task for information. */
+error_t _store_task_create (task_t task, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+/* Open the task NAME (NAME should be the task's pid), and return the
+ corresponding store in STORE. */
+error_t store_task_open (const char *name, int flags, struct store **store);
+
+/* Return a new store in STORE referring to the memory object MEMOBJ.
+ Consumes the send right MEMOBJ. */
+error_t store_memobj_create (memory_object_t memobj, int flags,
+ size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+/* Open the network block device NAME (parsed as "HOSTNAME:PORT[/BLOCKSIZE]"),
+ and return the corresponding store in STORE. This opens a socket and
+ initial connection handshake, which determine the size of the device,
+ and then uses _store_nbd_create with the open socket port. */
+error_t store_nbd_open (const char *name, int flags, struct store **store);
+
+/* Create a store that works by talking to an nbd server on an existing
+ socket port. */
+error_t _store_nbd_create (mach_port_t port, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store);
+
+/* Return a new store of type "unknown" that holds a copy of the
+ given encoding. The name of the store is taken from ENC->data.
+ Future calls to store_encode/store_return will produce exactly
+ the encoding supplied here. All i/o operations fail with EFTYPE. */
+error_t store_unknown_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Return a new store in STORE that interleaves all the stores in STRIPES
+ (NUM_STRIPES of them) every INTERLEAVE bytes; INTERLEAVE must be an
+ integer multiple of each stripe's block size. The stores in STRIPES are
+ consumed -- that is, will be freed when this store is (however, the
+ *array* STRIPES is copied, and so should be freed by the caller). */
+error_t store_ileave_create (struct store * const *stripes, size_t num_stripes,
+ store_offset_t interleave, int flags,
+ struct store **store);
+
+/* Return a new store in STORE that concatenates all the stores in STORES
+ (NUM_STORES of them). The stores in STRIPES are consumed -- that is, will
+ be freed when this store is (however, the *array* STRIPES is copied, and
+ so should be freed by the caller). */
+error_t store_concat_create (struct store * const *stores, size_t num_stores,
+ int flags, struct store **store);
+
+/* Return a new store that concatenates the stores created by opening all the
+ individual stores described in NAME; for the syntax of NAME, see
+ store_open_children. */
+error_t store_concat_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Return a new store in STORE that reflects the blocks in RUNS & RUNS_LEN
+ from SOURCE; SOURCE is consumed, but RUNS is not. Unlike the store_remap
+ function, this function always operates by creating a new store of type
+ `remap' which has SOURCE as a child, and so may be less efficient than
+ store_remap for some types of stores. */
+error_t store_remap_create (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ int flags, struct store **store);
+
+/* Return a new store in STORE which contains a snapshot of the contents of
+ the store FROM; FROM is consumed. */
+error_t store_copy_create (struct store *from, int flags, struct store **store);
+
+/* Open the copy store NAME -- which consists of another store-class
+ name, a ':', and a name for that store class to open -- and return
+ the corresponding store in STORE. CLASSES is as if passed to
+ store_find_class, which see. */
+error_t store_copy_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Return a new store in STORE which contains the memory buffer BUF, of
+ length BUF_LEN. BUF must be vm_allocated, and will be consumed. */
+error_t store_buffer_create (void *buf, size_t buf_len, int flags,
+ struct store **store);
+
+/* Return a new store in STORE which contains a snapshot of the uncompressed
+ contents of the store FROM; FROM is consumed. BLOCK_SIZE is the desired
+ block size of the result. */
+error_t store_gunzip_create (struct store *from, int flags,
+ struct store **store);
+
+/* Open the gunzip NAME -- which consists of another store-class name, a
+ ':', and a name for that store class to open -- and return the
+ corresponding store in STORE. CLASSES is as if passed to
+ store_find_class, which see. */
+error_t store_gunzip_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Return a new store in STORE which contains a snapshot of the uncompressed
+ contents of the store FROM; FROM is consumed. BLOCK_SIZE is the desired
+ block size of the result. */
+error_t store_bunzip2_create (struct store *from, int flags,
+ struct store **store);
+
+/* Open the bunzip2 NAME -- which consists of another store-class name, a ':',
+ and a name for that store class to open -- and return the corresponding
+ store in STORE. CLASSES is as if passed to store_find_class, which see. */
+error_t store_bunzip2_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Return a new store in STORE that multiplexes multiple physical volumes
+ from PHYS as one larger virtual volume. SWAP_VOLS is a function that will
+ be called whenever the volume currently active isn't correct. PHYS is
+ consumed. */
+error_t store_mvol_create (struct store *phys,
+ error_t (*swap_vols) (struct store *store, size_t new_vol,
+ ssize_t old_vol),
+ int flags,
+ struct store **store);
+
+/* Opening stores from a standard set of store classes.
+
+ These first two functions underlie the following functions, and
+ other functions such as store_open taking a CLASSES argument that
+ can be null. The standard set of classes to be searched when that
+ argument is null includes all the `const struct store_class *'
+ pointers found in the `store_std_classes' section of the executable
+ and all loaded shared objects; store_find_class searches that set
+ for the named class. The store_typed_open and store_url_open
+ functions also try store_module_find_class, but only if the
+ function has already been linked in; it's always available in the
+ shared library, and available for static linking with
+ -lstore_module -ldl.
+
+ The macro STORE_STD_CLASS produces a reference in the `store_std_classes'
+ section, so that linking in a module containing that definition will add
+ the referenced class to the standard search list. In the shared library,
+ the various standard classes are included this way. In the static
+ library, only the pseudo classes like `query' and `typed' will normally
+ be linked in (via referenced to store_open and so forth); to force
+ specific store type modules to be linked in, you must specify an
+ `-lstore_CLASS' option for each individual class to be statically linked.
+*/
+
+/* Find a store class by name. CLNAME_END points to the character
+ after the class name NAME points to; if null, then NAME is just the
+ null-terminated class name. */
+const struct store_class *
+store_find_class (const char *name,
+ const char *clname_end,
+ const struct store_class *const *classes);
+
+/* This is the underlying function that tries to load a module to
+ define the store type called NAME. On success, returns zero
+ and sets *CLASSP to the descriptor found. Returns ENOENT if
+ there is no such module, or other error codes if there is a
+ module but it does not load correctly. */
+error_t store_module_find_class (const char *name,
+ const char *clname_end,
+ const struct store_class **classp);
+
+
+/* Open the store indicated by NAME, which should consist of a store
+ type name followed by a ':' and any type-specific name, returning the
+ new store in STORE. CLASSES is as if passed to store_find_class,
+ which see. */
+error_t store_typed_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Similar to store_typed_open, but NAME must be in URL format, i.e. a
+ class name followed by a ':' and any type-specific name. A leading ':'
+ or no ':' at all is invalid syntax. (See store_module_open, below.) */
+error_t store_url_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* This attempts to decode a standard-form STORAGE_NETWORK encoding whose
+ encoded name is in URL format, by finding the store type indicated in
+ the URL (as for store_url_open) and that type's decode function. */
+error_t store_url_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store);
+
+
+/* Similar to store_typed_open, but the store type's code is found
+ dynamically rather than statically in CLASSES. A shared object name
+ for `dlopen' and symbol names for `dlsym' are derived from the type
+ name and used to find the `struct store_class' for the named type.
+ (CLASSES is used only by the type's own open function, e.g. if that
+ type accepts a child-store syntax in its name.)
+
+ In fact, when this code is linked in (always in the shared library,
+ only with `-lstore_module -ldl -lstore' for static linking), all
+ the functions documented as using STORE_STD_CLASSES will also
+ check for loadable modules if the type name is not found statically. */
+error_t store_module_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store);
+
+
+/* This attempts to find a module that can decode ENC. If no such
+ module can be found it returns ENOENT. Otherwise it returns
+ the result of the loaded store type's `decode' function. */
+error_t store_module_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Parse multiple store names in NAME, and open each individually, returning
+ all in the vector STORES, and the number in NUM_STORES. The syntax of
+ NAME is a single non-alpha-numeric separator character, followed by each
+ child store name separated by the same separator; each child name is
+ TYPE:NAME notation as parsed by store_typed_open. If every child uses the
+ same TYPE: prefix, then it may be factored out and put before the child
+ list instead (the two types of notation are differentiated by whether the
+ first character of name is alpha-numeric or not). */
+error_t store_open_children (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store ***stores, size_t *num_stores);
+
+
+/* Standard store classes implemented by libstore. */
+extern const struct store_class store_device_class;
+extern const struct store_class store_part_class;
+extern const struct store_class store_file_class;
+extern const struct store_class store_task_class;
+extern const struct store_class store_nbd_class;
+extern const struct store_class store_memobj_class;
+extern const struct store_class store_zero_class;
+extern const struct store_class store_ileave_class;
+extern const struct store_class store_concat_class;
+extern const struct store_class store_remap_class;
+extern const struct store_class store_query_class;
+extern const struct store_class store_copy_class;
+extern const struct store_class store_gunzip_class;
+extern const struct store_class store_bunzip2_class;
+extern const struct store_class store_typed_open_class;
+extern const struct store_class store_url_open_class;
+extern const struct store_class store_module_open_class;
+extern const struct store_class store_unknown_class;
+
+/* The following are not included in STORE_STD_CLASSES. */
+extern const struct store_class store_mvol_class;
+
+#define STORE_STD_CLASS(name) \
+ static const struct store_class *const store_std_classes_##name[] \
+ __attribute_used__ __attribute__ ((section ("store_std_classes"))) \
+ = { &store_##name##_class }
+
+
+extern const struct store_class *const __start_store_std_classes[] __attribute__ ((weak));
+extern const struct store_class *const __stop_store_std_classes[] __attribute__ ((weak));
+
+/* Used to hold the various bits that make up the representation of a store
+ for transmission via rpc. See <hurd/hurd_types.h> for an explanation of
+ the encodings for the various storage types. */
+struct store_enc
+{
+ /* Each of the four vectors used. All are vm_allocated. */
+ mach_port_t *ports;
+ int *ints;
+ loff_t *offsets;
+ char *data;
+
+ /* The sizes of the vectors. */
+ mach_msg_type_number_t num_ports, num_ints, num_offsets, data_len;
+
+ /* Offsets into the above vectors, for an encoding/decoding in progress. */
+ size_t cur_port, cur_int, cur_offset, cur_data;
+
+ /* Each of these is an `initial' version of the associated vector. When
+ store_enc_dealloc is called, any vector that is the same as its `init_'
+ version won't be deallocated. */
+ mach_port_t *init_ports;
+ int *init_ints;
+ loff_t *init_offsets;
+ char *init_data;
+};
+
+/* Initialize ENC. The given vector and sizes will be used for the encoding
+ if they are big enough (otherwise new ones will be automatically
+ allocated). */
+void store_enc_init (struct store_enc *enc,
+ mach_port_t *ports, mach_msg_type_number_t num_ports,
+ int *ints, mach_msg_type_number_t num_ints,
+ loff_t *offsets, mach_msg_type_number_t num_offsets,
+ char *data, mach_msg_type_number_t data_len);
+
+/* Deallocate storage used by the fields in ENC (but nothing is done with ENC
+ itself). */
+void store_enc_dealloc (struct store_enc *enc);
+
+/* Copy out the parameters from ENC into the given variables suitably for
+ returning from a file_get_storage_info rpc, and deallocate ENC. */
+void store_enc_return (struct store_enc *enc,
+ mach_port_t **ports, mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ loff_t **offsets, mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len);
+
+/* Encode STORE into the given return variables, suitably for returning from a
+ file_get_storage_info rpc. */
+error_t store_return (const struct store *store,
+ mach_port_t **ports, mach_msg_type_number_t *num_ports,
+ int **ints, mach_msg_type_number_t *num_ints,
+ loff_t **offsets, mach_msg_type_number_t *num_offsets,
+ char **data, mach_msg_type_number_t *data_len);
+
+/* Encode STORE into ENC, which should have been prepared with
+ store_enc_init, or return an error. The contents of ENC may then be
+ return as the value of file_get_storage_info; if for some reason this
+ can't be done, store_enc_dealloc may be used to deallocate the mmemory
+ used by the unsent vectors. */
+error_t store_encode (const struct store *store, struct store_enc *enc);
+
+/* Decode ENC, either returning a new store in STORE, or an error.
+ CLASSES is as if passed to store_find_class, which see. If nothing
+ else is to be done with ENC, its contents may then be freed using
+ store_enc_dealloc. */
+error_t store_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store);
+
+/* Calls the allocate_encoding method in each child store of STORE,
+ propagating any errors. If any child does not hae such a method,
+ EOPNOTSUPP is returned. */
+error_t store_allocate_child_encodings (const struct store *store,
+ struct store_enc *enc);
+
+/* Calls the encode method in each child store of STORE, propagating any
+ errors. If any child does not hae such a method, EOPNOTSUPP is returned. */
+error_t store_encode_children (const struct store *store,
+ struct store_enc *enc);
+
+/* Decodes NUM_CHILDREN from ENC, storing the results into successive
+ positions in CHILDREN. */
+error_t store_decode_children (struct store_enc *enc, int num_children,
+ const struct store_class *const *classes,
+ struct store **children);
+
+/* Call FUN with the vector RUNS of length NUM_RUNS extracted from ENC. */
+error_t store_with_decoded_runs (struct store_enc *enc, size_t num_runs,
+ error_t (*fun) (const struct store_run *runs,
+ size_t num_runs));
+
+/* Standard encoding used for most leaf store types. */
+error_t store_std_leaf_allocate_encoding (const struct store *store,
+ struct store_enc *enc);
+error_t store_std_leaf_encode (const struct store *store,
+ struct store_enc *enc);
+
+/* Creation function signature used by store_std_leaf_decode. */
+typedef error_t (*store_std_leaf_create_t)(mach_port_t port,
+ int flags,
+ size_t block_size,
+ const struct store_run *runs,
+ size_t num_runs,
+ struct store **store);
+
+/* Decodes the standard leaf encoding that's common to various builtin
+ formats, and calls CREATE to actually create the store. */
+error_t store_std_leaf_decode (struct store_enc *enc,
+ store_std_leaf_create_t create,
+ struct store **store);
+
+/* An argument parser that may be used for parsing a simple command line
+ specification for stores. The accompanying input parameter must be a
+ pointer to a struct store_argp_params. */
+extern struct argp store_argp;
+
+/* The structure used to pass args back and forth from STORE_ARGP. */
+struct store_argp_params
+{
+ /* The resulting parsed result. */
+ struct store_parsed *result;
+
+ /* If --store-type isn't specified use this; 0 is equivalent to "query". */
+ const char *default_type;
+
+ /* The set of classes used to validate store-types and argument syntax. */
+ const struct store_class *const *classes;
+
+ /* This controls the behavior when no store arguments are specified.
+ If zero, the parser fails with the error message "No store specified".
+ If nonzero, the parser succeeds and sets `result' to null. */
+ int store_optional;
+};
+
+/* The result of parsing a store, which should be enough information to open
+ it, or return the arguments. */
+struct store_parsed;
+
+/* Free all resources used by PARSED. */
+void store_parsed_free (struct store_parsed *parsed);
+
+/* Open PARSED, and return the corresponding store in STORE. */
+error_t store_parsed_open (const struct store_parsed *parsed, int flags,
+ struct store **store);
+
+/* Add the arguments used to create PARSED to ARGZ & ARGZ_LEN. */
+error_t store_parsed_append_args (const struct store_parsed *parsed,
+ char **argz, size_t *argz_len);
+
+/* Make a string describing PARSED, and return it in malloced storage in
+ NAME. */
+error_t store_parsed_name (const struct store_parsed *parsed, char **name);
+
+
+#endif /* __STORE_H__ */
diff --git a/libstore/stripe.c b/libstore/stripe.c
new file mode 100644
index 00000000..e9c58466
--- /dev/null
+++ b/libstore/stripe.c
@@ -0,0 +1,293 @@
+/* Striped store backend
+
+ Copyright (C) 1996,97,99,2001, 2002 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
+ 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 <stdlib.h>
+#include <string.h>
+
+#include "store.h"
+
+extern long lcm (long p, long q);
+
+/* Return ADDR adjust for any block size difference between STORE and
+ STRIPE. We assume that STORE's block size is no less than STRIPE's. */
+static inline store_offset_t
+addr_adj (store_offset_t addr, struct store *store, struct store *stripe)
+{
+ unsigned common_bs = store->log2_block_size;
+ unsigned stripe_bs = stripe->log2_block_size;
+ if (common_bs == stripe_bs)
+ return addr;
+ else
+ return addr << (common_bs - stripe_bs);
+}
+
+static error_t
+stripe_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount,
+ void **buf, size_t *len)
+{
+ struct store *stripe = store->children[index];
+ return store_read (stripe, addr_adj (addr, store, stripe), amount, buf, len);
+}
+
+static error_t
+stripe_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len, size_t *amount)
+{
+ struct store *stripe = store->children[index];
+ return
+ store_write (stripe, addr_adj (addr, store, stripe), buf, len, amount);
+}
+
+error_t
+stripe_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+stripe_remap (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ return store_remap_create (source, runs, num_runs, 0, store);
+}
+
+error_t
+ileave_allocate_encoding (const struct store *store, struct store_enc *enc)
+{
+ enc->num_ints += 4;
+ return store_allocate_child_encodings (store, enc);
+}
+
+error_t
+ileave_encode (const struct store *store, struct store_enc *enc)
+{
+ enc->ints[enc->cur_int++] = store->class->id;
+ enc->ints[enc->cur_int++] = store->flags;
+ enc->ints[enc->cur_int++] = store->wrap_dst; /* interleave factor */
+ enc->ints[enc->cur_int++] = store->num_children;
+ return store_encode_children (store, enc);
+}
+
+error_t
+ileave_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ if (enc->cur_int + 4 > enc->num_ints)
+ return EINVAL;
+ else
+ {
+ int type __attribute__((unused)) = enc->ints[enc->cur_int++];
+ int flags = enc->ints[enc->cur_int++];
+ int interleave = enc->ints[enc->cur_int++];
+ int nkids = enc->ints[enc->cur_int++];
+ struct store *kids[nkids];
+ error_t err = store_decode_children (enc, nkids, classes, kids);
+ if (! err)
+ err = store_ileave_create (kids, nkids, interleave, flags, store);
+ return err;
+ }
+}
+
+const struct store_class
+store_ileave_class =
+{
+ STORAGE_INTERLEAVE, "interleave", stripe_read, stripe_write, stripe_set_size,
+ ileave_allocate_encoding, ileave_encode, ileave_decode,
+ store_set_child_flags, store_clear_child_flags, 0, 0, stripe_remap
+};
+STORE_STD_CLASS (ileave);
+
+error_t
+concat_allocate_encoding (const struct store *store, struct store_enc *enc)
+{
+ enc->num_ints += 3;
+ return store_allocate_child_encodings (store, enc);
+}
+
+error_t
+concat_encode (const struct store *store, struct store_enc *enc)
+{
+ enc->ints[enc->cur_int++] = store->class->id;
+ enc->ints[enc->cur_int++] = store->flags;
+ enc->ints[enc->cur_int++] = store->num_children;
+ return store_encode_children (store, enc);
+}
+
+error_t
+concat_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ if (enc->cur_int + 3 > enc->num_ints)
+ return EINVAL;
+ else
+ {
+ int type __attribute__((unused)) = enc->ints[enc->cur_int++];
+ int flags = enc->ints[enc->cur_int++];
+ int nkids = enc->ints[enc->cur_int++];
+ struct store *kids[nkids];
+ error_t err = store_decode_children (enc, nkids, classes, kids);
+ if (! err)
+ err = store_concat_create (kids, nkids, flags, store);
+ return err;
+ }
+}
+
+const struct store_class
+store_concat_class =
+{
+ STORAGE_CONCAT, "concat", stripe_read, stripe_write, stripe_set_size,
+ concat_allocate_encoding, concat_encode, concat_decode,
+ store_set_child_flags, store_clear_child_flags, 0, 0, stripe_remap,
+ store_concat_open
+};
+STORE_STD_CLASS (concat);
+
+/* Return a new store in STORE that interleaves all the stores in STRIPES
+ (NUM_STRIPES of them) every INTERLEAVE bytes; INTERLEAVE must be an
+ integer multiple of each stripe's block size. The stores in STRIPES are
+ consumed -- that is, will be freed when this store is (however, the
+ *array* STRIPES is copied, and so should be freed by the caller). */
+error_t
+store_ileave_create (struct store *const *stripes, size_t num_stripes,
+ store_offset_t interleave, int flags,
+ struct store **store)
+{
+ size_t i;
+ error_t err;
+ size_t block_size = 1;
+ store_offset_t min_end = 0;
+ struct store_run runs[num_stripes];
+ int common_flags = STORE_BACKEND_FLAGS;
+
+ /* Find a common block size. */
+ for (i = 0; i < num_stripes; i++)
+ block_size = lcm (block_size, stripes[i]->block_size);
+
+ if (interleave < block_size || (interleave % block_size) != 0)
+ return EINVAL;
+
+ interleave /= block_size;
+
+ for (i = 0; i < num_stripes; i++)
+ {
+ /* The stripe's end adjusted to the common block size. */
+ store_offset_t end = stripes[i]->end;
+
+ runs[i].start = 0;
+ runs[i].length = interleave;
+
+ if (stripes[i]->block_size != block_size)
+ end /= (block_size / stripes[i]->block_size);
+
+ if (min_end < 0)
+ min_end = end;
+ else if (min_end > end)
+ /* Only use as much space as the smallest stripe has. */
+ min_end = end;
+
+ common_flags &= stripes[i]->flags;
+ }
+
+ err = _store_create (&store_ileave_class, MACH_PORT_NULL,
+ common_flags | flags, block_size,
+ runs, num_stripes, min_end, store);
+ if (! err)
+ {
+ (*store)->wrap_dst = interleave;
+
+ err = store_set_children (*store, stripes, num_stripes);
+ if (err)
+ store_free (*store);
+ }
+
+ return err;
+}
+
+/* Return a new store in STORE that concatenates all the stores in STORES
+ (NUM_STORES of them). The stores in STRIPES are consumed -- that is, will
+ be freed when this store is (however, the *array* STRIPES is copied, and
+ so should be freed by the caller). */
+error_t
+store_concat_create (struct store * const *stores, size_t num_stores,
+ int flags, struct store **store)
+{
+ size_t i;
+ error_t err;
+ size_t block_size = 1;
+ int common_flags = STORE_BACKEND_FLAGS;
+ struct store_run runs[num_stores];
+
+ /* Find a common block size. */
+ for (i = 0; i < num_stores; i++)
+ block_size = lcm (block_size, stores[i]->block_size);
+
+ for (i = 0; i < num_stores; i++)
+ {
+ runs[i].start = 0;
+ runs[i].length = stores[i]->end;
+ common_flags &= stores[i]->flags;
+ }
+
+ err = _store_create (&store_concat_class, MACH_PORT_NULL,
+ flags | common_flags, block_size,
+ runs, num_stores, 0, store);
+ if (! err)
+ {
+ err = store_set_children (*store, stores, num_stores);
+ if (! err)
+ {
+ err = store_children_name (*store, &(*store)->name);
+ if (err == EINVAL)
+ err = 0; /* Can't find a name; deal. */
+ }
+ if (err)
+ store_free (*store);
+ }
+
+ return err;
+}
+
+/* Return a new store that concatenates the stores created by opening all the
+ individual stores described in NAME; for the syntax of NAME, see
+ store_open_children. */
+error_t
+store_concat_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ struct store **stores;
+ size_t num_stores;
+ error_t err =
+ store_open_children (name, flags, classes, &stores, &num_stores);
+ if (! err)
+ {
+ err = store_concat_create (stores, num_stores, flags, store);
+ if (err)
+ {
+ size_t k;
+ for (k = 0; k < (*store)->num_children; k++)
+ store_free ((*store)->children[k]);
+ }
+ }
+ return err;
+}
diff --git a/libstore/task.c b/libstore/task.c
new file mode 100644
index 00000000..ea1475c8
--- /dev/null
+++ b/libstore/task.c
@@ -0,0 +1,205 @@
+/* Mach task store backend
+
+ Copyright (C) 1995,96,97,2001, 2002 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
+ 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 <stdio.h>
+#include <string.h>
+#include <hurd.h>
+
+#include <mach/machine/vm_param.h>
+
+#include "store.h"
+
+static process_t
+proc_server ()
+{
+ static process_t proc = MACH_PORT_NULL;
+ if (proc == MACH_PORT_NULL)
+ proc = getproc ();
+ return proc;
+}
+
+static error_t
+topen (const char *name, task_t *task)
+{
+ char *name_end;
+ pid_t pid = strtoul (name, &name_end, 0);
+
+ if (*name == '\0' || *name_end != '\0')
+ return EINVAL;
+
+ return proc_pid2task (proc_server (), pid, task);
+}
+
+static void
+tclose (struct store *store)
+{
+ mach_port_deallocate (mach_task_self (), store->port);
+ store->port = MACH_PORT_NULL;
+}
+
+static error_t
+task_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount, void **buf, size_t *len)
+{
+ size_t bsize = store->block_size;
+ return vm_read (store->port, addr * bsize, amount, (vm_address_t *)buf, len);
+}
+
+static error_t
+task_write (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len, size_t *amount)
+{
+ size_t bsize = store->block_size;
+ error_t err = vm_write (store->port, addr * bsize, (vm_address_t)buf, len);
+ if (! err)
+ *amount = len;
+ return err;
+}
+
+static error_t
+task_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+static error_t
+task_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_std_leaf_decode (enc, _store_task_create, store);
+}
+
+static error_t
+task_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ return store_task_open (name, flags, store);
+}
+
+static error_t
+task_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 ((flags & STORE_ENFORCED)
+ && (store->num_runs > 0
+ || store->runs[0].start != 0
+ || store->runs[0].length != (VM_MAX_ADDRESS >> store->log2_block_size)))
+ /* Kernel only enforces the whole thing... */
+ return EINVAL;
+
+ if (flags & STORE_INACTIVE)
+ tclose (store);
+
+ store->flags |= flags; /* When inactive, anything goes. */
+
+ return 0;
+}
+
+static error_t
+task_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 ? topen (store->name, &store->port) : ESRCH;
+ if (! err)
+ store->flags &= ~flags;
+ return err;
+}
+
+const struct store_class
+store_task_class =
+{
+ STORAGE_TASK, "task", task_read, task_write, task_set_size,
+ store_std_leaf_allocate_encoding, store_std_leaf_encode, task_decode,
+ task_set_flags, task_clear_flags, 0, 0, 0, task_open
+};
+STORE_STD_CLASS (task);
+
+/* Return a new store in STORE referring to the mach task TASK. Consumes
+ the send right TASK. */
+error_t
+store_task_create (task_t task, int flags, struct store **store)
+{
+ struct store_run run;
+
+ run.start = 0;
+ run.length = VM_MAX_ADDRESS / vm_page_size;
+
+ flags |= STORE_ENFORCED; /* 'cause it's the whole task. */
+
+ return _store_task_create (task, flags, vm_page_size, &run, 1, store);
+}
+
+/* Like store_task_create, but doesn't query the task for information. */
+error_t
+_store_task_create (task_t task, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ error_t err = 0;
+
+ if (block_size >= vm_page_size)
+ err = _store_create (&store_task_class,
+ task, flags, block_size, runs, num_runs, 0, store);
+ else
+ err = EINVAL; /* block size less than page size. */
+
+ if (! err)
+ {
+ pid_t pid;
+
+ err = proc_task2pid (proc_server (), task, &pid);
+ if (! err)
+ {
+ char buf[20];
+ snprintf (buf, sizeof buf, "%u", pid);
+ err = store_set_name (*store, buf);
+ }
+
+ if (err)
+ store_free (*store);
+ }
+
+ return err;
+}
+
+/* Open the task NAME, and return the corresponding store in STORE. */
+error_t
+store_task_open (const char *name, int flags, struct store **store)
+{
+ task_t task;
+ error_t err = topen (name, &task);
+
+ if (! err)
+ {
+ err = store_task_create (task, flags, store);
+ if (err)
+ mach_port_deallocate (mach_task_self (), task);
+ }
+
+ return err;
+}
diff --git a/libstore/typed.c b/libstore/typed.c
new file mode 100644
index 00000000..b1be747d
--- /dev/null
+++ b/libstore/typed.c
@@ -0,0 +1,177 @@
+/* Support for opening `typed' stores
+
+ Copyright (C) 1997,1998,2001,2002,2003,2004 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
+ 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 "store.h"
+#include <string.h>
+#include <dlfcn.h>
+#include <link.h>
+
+
+const struct store_class *
+store_find_class (const char *name, const char *clname_end,
+ const struct store_class *const *classes)
+{
+ const struct store_class *const *cl;
+
+ if (! clname_end)
+ clname_end = strchr (name, '\0');
+
+ if (classes != 0)
+ {
+ /* The caller gave a class list, so that's is all we'll use. */
+ for (cl = classes; *cl != 0; ++cl)
+ if (strlen ((*cl)->name) == (clname_end - name)
+ && !memcmp (name, (*cl)->name, (clname_end - name)))
+ break;
+ return *cl;
+ }
+
+ /* Check the statically-linked set of classes found in the
+ "store_std_classes" section. For static linking, this is the section
+ in the program executable itself and it has been populated by the set
+ of -lstore_TYPE pseudo-libraries included in the link. For dynamic
+ linking with just -lstore, these symbols will be found in libstore.so
+ and have the set statically included when the shared object was built.
+ If a dynamically-linked program has its own "store_std_classes"
+ section, e.g. by -lstore_TYPE objects included in the link, this will
+ be just that section and libstore.so itself is covered below. */
+ for (cl = __start_store_std_classes; cl < __stop_store_std_classes; ++cl)
+ if (strlen ((*cl)->name) == (clname_end - name)
+ && strncmp (name, (*cl)->name, (clname_end - name)) == 0)
+ return *cl;
+
+ /* Now we will iterate through all of the dynamic objects loaded
+ and examine each one's "store_std_classes" section. */
+# pragma weak _r_debug
+# pragma weak dlsym
+# pragma weak dlopen
+# pragma weak dlclose
+# pragma weak dlerror
+ if (dlsym)
+ {
+ struct link_map *map;
+ for (map = _r_debug.r_map; map != 0; map = map->l_next)
+ {
+ const struct store_class *const *start, *const *stop;
+
+ /* We cannot just use MAP directly because it may not have been
+ opened by dlopen such that its data structures are fully set
+ up for dlsym. */
+ void *module = dlopen (map->l_name, RTLD_NOLOAD);
+ if (module == 0)
+ {
+ (void) dlerror (); /* Required to avoid a leak! */
+ continue;
+ }
+
+ start = dlsym (map, "__start_store_std_classes");
+ if (start == 0)
+ (void) dlerror (); /* Required to avoid a leak! */
+ else if (start != __start_store_std_classes) /* */
+ {
+ stop = dlsym (map, "__stop_store_std_classes");
+ if (stop == 0)
+ (void) dlerror (); /* Required to avoid a leak! */
+ else
+ for (cl = start; cl < stop; ++cl)
+ if (strlen ((*cl)->name) == (clname_end - name)
+ && strncmp (name, (*cl)->name, (clname_end - name)) == 0)
+ {
+ dlclose (module);
+ return *cl;
+ }
+ }
+ dlclose (module);
+ }
+ }
+
+ return 0;
+}
+
+
+/* Open the store indicated by NAME, which should consist of a store type
+ name followed by a ':' and any type-specific name, returning the new store
+ in STORE. If NAME doesn't contain a `:', then it will be interpreted as
+ either a class name, if such a class occurs in CLASSES, or a filename,
+ which is opened by calling store_open on NAME; a `:' at the end or the
+ beginning of NAME unambiguously causes the remainder to be treated as a
+ class-name or a filename, respectively. CLASSES is used to select classes
+ specified by the type name; if it is 0, STORE_STD_CLASSES is used. */
+error_t
+store_typed_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ const struct store_class *cl;
+ const char *clname_end = strchrnul (name, ':');
+
+ if (clname_end == name && *clname_end)
+ /* Open NAME with store_open. */
+ return store_open (name + 1, flags, classes, store);
+
+ /* Try to find an existing class by the given name. */
+ cl = store_find_class (name, clname_end, classes);
+ if (cl != 0)
+ {
+ if (! cl->open)
+ /* CL cannot be opened. */
+ return EOPNOTSUPP;
+
+ if (*clname_end)
+ /* Skip the ':' separating the class-name from the device name. */
+ clname_end++;
+
+ if (! *clname_end)
+ /* The class-specific portion of the name is empty, so make it *really*
+ empty. */
+ clname_end = 0;
+
+ return (*cl->open) (clname_end, flags, classes, store);
+ }
+
+ /* Try to open a store by loading a module to define the class, if we
+ have the module-loading support linked in. We don't just use
+ store_module_find_class, because store_module_open will unload the new
+ module if the open doesn't succeed and we have no other way to unload
+ it. We always leave modules loaded once a store from the module has
+ been successfully opened and so can leave unbounded numbers of old
+ modules loaded after closing all the stores using them. But at least
+ we can avoid having modules loaded for stores we never even opened. */
+# pragma weak store_module_open
+ if (store_module_open)
+ {
+ error_t err = store_module_open (name, flags, classes, store);
+ if (err != ENOENT)
+ return err;
+ }
+
+ /* No class with the given name found. */
+ if (*clname_end)
+ /* NAME really should be a class name, which doesn't exist. */
+ return EINVAL;
+ else
+ /* Try opening NAME by querying it as a file instead. */
+ return store_open (name, flags, classes, store);
+}
+
+const struct store_class
+store_typed_open_class = { -1, "typed", open: store_typed_open };
+STORE_STD_CLASS (typed_open);
diff --git a/libstore/unknown.c b/libstore/unknown.c
new file mode 100644
index 00000000..8b7f4268
--- /dev/null
+++ b/libstore/unknown.c
@@ -0,0 +1,231 @@
+/* Store backend for unknown encodings
+
+ Copyright (C) 2001,02 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 "store.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+
+/* You can't do anything with an unknown store but encode it. */
+
+static error_t
+noread (struct store *store, store_offset_t addr, size_t index,
+ size_t amount, void **buf, size_t *len)
+{
+ return EFTYPE;
+}
+
+static error_t
+nowrite (struct store *store,
+ store_offset_t addr, size_t index,
+ const void *buf, size_t len, size_t *amount)
+{
+ return EFTYPE;
+}
+
+static error_t
+noset_size (struct store *store, size_t newsize)
+{
+ return EFTYPE;
+}
+
+static error_t
+noflags (struct store *store, int flags)
+{
+ return EINVAL;
+}
+
+/* This is the only way that stores of the "unknown" class get created.
+ We save the store encoding verbatim and regurgitate it as our own. */
+
+static struct store_enc *
+duplicate_encoding (struct store_enc *enc)
+{
+ struct store_enc *us;
+ size_t i;
+
+ us = calloc (1, sizeof *us);
+ if (us == NULL)
+ return NULL;
+
+ us->ports = mmap (0, enc->num_ports * sizeof *enc->ports,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (us->ports == MAP_FAILED)
+ {
+ no_memory:
+ store_enc_dealloc (us);
+ free (us);
+ return NULL;
+ }
+ us->ints = mmap (0, enc->num_ints * sizeof *enc->ints,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (us->ints == MAP_FAILED)
+ goto no_memory;
+ us->offsets = mmap (0, enc->num_offsets * sizeof *enc->offsets,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (us->offsets == MAP_FAILED)
+ goto no_memory;
+ us->data = mmap (0, enc->data_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (us->data == MAP_FAILED)
+ goto no_memory;
+
+ memcpy (us->ports, enc->ports, enc->num_ports * sizeof *enc->ports);
+ memcpy (us->ints, enc->ints, enc->num_ints * sizeof *enc->ints);
+ memcpy (us->offsets, enc->offsets, enc->num_offsets * sizeof *enc->offsets);
+ memcpy (us->data, enc->data, enc->data_len);
+
+ us->num_ports = enc->num_ports;
+ us->num_ints = enc->num_ints;
+ us->num_offsets = enc->num_offsets;
+ us->data_len = enc->data_len;
+
+ for (i = 0; i < us->num_ports; ++i)
+ mach_port_mod_refs (mach_task_self (), us->ports[i],
+ MACH_PORT_RIGHT_SEND, +1);
+
+ return us;
+}
+
+error_t
+store_unknown_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ struct store_enc *us;
+ error_t err = _store_create (&store_unknown_class,
+ MACH_PORT_NULL, STORE_ENFORCED, 0, NULL, 0, 0,
+ store);
+ if (err)
+ return err;
+
+ us = duplicate_encoding (enc);
+ if (us == NULL)
+ {
+ store_free (*store);
+ return ENOMEM;
+ }
+ (*store)->hook = us;
+
+ /* Derive a name for this unknown store from its encoded type field
+ (or lack thereof) and the leading string of its encoded data bytes. */
+ if (enc->cur_int == enc->num_ints)
+ asprintf (&(*store)->name, "notype:%.*s",
+ (int) (us->data_len - us->cur_data), us->data + us->cur_data);
+ else
+ asprintf (&(*store)->name, "type-%d:%.*s", enc->ints[enc->cur_int],
+ (int) ( us->data_len - us->cur_data), us->data + us->cur_data);
+
+ return 0;
+}
+
+/* Re-encode just the way we got it. */
+error_t
+unknown_allocate_encoding (const struct store *store, struct store_enc *enc)
+{
+ const struct store_enc *us = store->hook;
+ if (us == NULL)
+ return EOPNOTSUPP;
+ enc->num_ports += us->num_ports;
+ enc->num_ints += us->num_ints;
+ enc->num_offsets += us->num_offsets;
+ enc->data_len += us->data_len;
+ return 0;
+}
+
+error_t
+unknown_encode (const struct store *store, struct store_enc *enc)
+{
+ const struct store_enc *us = store->hook;
+ if (us == NULL)
+ return EOPNOTSUPP;
+
+ memcpy (enc->ports, us->ports, us->num_ports * sizeof enc->ports[0]);
+ enc->ports += us->num_ports;
+ memcpy (enc->ints, us->ints, us->num_ints * sizeof enc->ints[0]);
+ enc->ints += us->num_ints;
+ memcpy (enc->offsets, us->offsets, us->num_offsets * sizeof enc->offsets[0]);
+ enc->offsets += us->num_offsets;
+ memcpy (enc->data + enc->cur_data, us->data, us->data_len);
+ enc->cur_data += us->data_len;
+
+ return 0;
+}
+
+
+/* Called just before deallocating STORE. */
+static void
+unknown_cleanup (struct store *store)
+{
+ if (store->hook != NULL)
+ {
+ store_enc_dealloc (store->hook);
+ free (store->hook);
+ }
+}
+
+/* Copy any format-dependent fields in FROM to TO; if there's some reason
+ why the copy can't be made, an error should be returned. This call is
+ made after all format-independent fields have been cloned. */
+static error_t
+unknown_clone (const struct store *from, struct store *to)
+{
+ if (from->hook == NULL)
+ return 0;
+ to->hook = duplicate_encoding (from->hook);
+ return to->hook ? 0 : ENOMEM;
+}
+
+/* Unknown stores cannot be opened with a name. */
+static error_t
+unknown_validate_name (const char *name,
+ const struct store_class *const *classes)
+{
+ return name == NULL ? 0 : EINVAL;
+}
+
+static error_t
+unknown_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ return (name == NULL
+ ? _store_create (&store_unknown_class, MACH_PORT_NULL,
+ STORE_ENFORCED, 0, NULL, 0, 0, store)
+ : EINVAL);
+}
+
+const struct store_class store_unknown_class =
+{
+ -1, "unknown",
+ read: noread,
+ write: nowrite,
+ set_size: noset_size,
+ allocate_encoding: unknown_allocate_encoding,
+ encode: unknown_encode,
+ decode: store_unknown_decode,
+ set_flags: noflags,
+ clear_flags: noflags,
+ cleanup: unknown_cleanup,
+ clone: unknown_clone,
+ open: unknown_open,
+ validate_name: unknown_validate_name,
+};
+STORE_STD_CLASS (unknown);
diff --git a/libstore/unzipstore.c b/libstore/unzipstore.c
new file mode 100644
index 00000000..8d500c1b
--- /dev/null
+++ b/libstore/unzipstore.c
@@ -0,0 +1,267 @@
+/* Decompressing store backend (common code for gunzip and bunzip2)
+
+ Copyright (C) 1998, 1999, 2002 Free Software Foundation, Inc.
+ Written by okuji@kuicr.kyoto-u.ac.jp <okuji@kuicr.kyoto-u.ac.jp>
+ 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 <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+#include <pthread.h>
+#include <sys/mman.h>
+
+#include "store.h"
+
+#define IN_BUFFERING (256*1024)
+#define OUT_BUFFERING (512*1024)
+
+static pthread_mutex_t unzip_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#define STORE_UNZIP(name) STORE_UNZIP_1 (UNZIP, name)
+#define STORE_UNZIP_1(unzip,name) STORE_UNZIP_2 (unzip, name)
+#define STORE_UNZIP_2(unzip,name) store_##unzip##_##name
+#define STORE_STD_CLASS_1(name) STORE_STD_CLASS(name)
+#define STRINGIFY(name) STRINGIFY_1(name)
+#define STRINGIFY_1(name) #name
+
+
+/* Uncompress the contents of FROM, which should contain a valid bzip2 file,
+ into memory, returning the result buffer in BUF & BUF_LEN. */
+static error_t
+unzip_store (struct store *from, void **buf, size_t *buf_len)
+{
+ /* Callbacks from decompression engine for I/O and error interface. */
+ extern int (*unzip_read) (char *buf, size_t maxread);
+ extern void (*unzip_write) (const char *buf, size_t nwrite);
+ extern void (*unzip_read_error) (void);
+ extern void (*unzip_error) (const char *msg);
+
+ /* How we return errors from our hook functions. */
+ jmp_buf zerr_jmp_buf;
+ error_t zerr;
+
+ /* vm_alloced buffer for the input store. */
+ void *in_buf = 0;
+ size_t in_buf_len = 0; /* Amount of valid data in IN_BUF. */
+ size_t in_buf_size = 0; /* Allocated space for IN_BUF. */
+ size_t in_buf_offs = 0; /* Offset of read point in IN_BUF. */
+ off_t in_buf_addr = 0; /* Address in FROM of *next* IN_BUF. */
+
+ /* Buffer input in units that are least IN_BUFFERING bytes, but are also a
+ multiple of FROM's block size. */
+ size_t in_addr_mask = ((1 << from->log2_block_size) - 1);
+ size_t in_buffering = ((IN_BUFFERING + in_addr_mask) & ~in_addr_mask);
+
+ /* Read at most MAXREAD (or 0 if eof) bytes into BUF from our current
+ position in FROM. */
+ int zread (char *buf, size_t maxread)
+ {
+ size_t did_read = 0;
+
+ while (maxread > 0)
+ {
+ size_t left = in_buf_len - in_buf_offs;
+
+ if (left > 0)
+ /* Fill BUF with what we can from IN_BUF. */
+ {
+ if (left > maxread)
+ left = maxread;
+ bcopy (in_buf + in_buf_offs, buf, left);
+ in_buf_offs += left;
+ buf += left;
+ maxread -= left;
+ did_read += left;
+ }
+
+ /* Limit MAXREAD to the number of bytes left in the input. */
+ if (maxread > (from->size - (in_buf_addr << from->log2_block_size)))
+ maxread = from->size - (in_buf_addr << from->log2_block_size);
+
+ if (maxread > 0)
+ /* Have to fill IN_BUF again. */
+ {
+ void *new_in_buf = in_buf;
+ size_t new_in_buf_len = in_buf_len;
+
+ zerr = store_read (from, in_buf_addr, in_buffering,
+ &new_in_buf, &new_in_buf_len);
+ if (zerr)
+ longjmp (zerr_jmp_buf, 1);
+
+ in_buf_addr += (new_in_buf_len >> from->log2_block_size);
+
+ if (new_in_buf != in_buf)
+ {
+ if (in_buf_size > 0)
+ munmap (in_buf, in_buf_size);
+ in_buf = new_in_buf;
+ in_buf_size = new_in_buf_len;
+ }
+
+ in_buf_len = new_in_buf_len;
+ in_buf_offs = 0;
+ }
+ }
+ return did_read;
+ }
+
+ size_t out_buf_offs = 0; /* Position in the output buffer. */
+
+ /* Write compress data to our output buffer. */
+ void zwrite (const char *wbuf, size_t nwrite)
+ {
+ size_t old_buf_len = *buf_len;
+
+ if (out_buf_offs + nwrite > old_buf_len)
+ /* Have to grow the output buffer. */
+ {
+ void *old_buf = *buf;
+ void *new_buf = old_buf + old_buf_len; /* First try. */
+ size_t new_buf_len = round_page (old_buf_len + old_buf_len + nwrite);
+
+ /* Try to grow the buffer. */
+ zerr =
+ vm_allocate (mach_task_self (),
+ (vm_address_t *)&new_buf, new_buf_len - old_buf_len,
+ 0);
+ if (zerr)
+ /* Can't do that, try to make a bigger buffer elsewhere. */
+ {
+ new_buf = mmap (0, new_buf_len, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ zerr = (new_buf == (void *) -1) ? errno : 0;
+ if (zerr)
+ longjmp (zerr_jmp_buf, 1);
+
+ if (out_buf_offs > 0)
+ /* Copy the old buffer into the start of the new & free it. */
+ bcopy (old_buf, new_buf, out_buf_offs);
+
+ munmap (old_buf, old_buf_len);
+
+ *buf = new_buf;
+ }
+
+ *buf_len = new_buf_len;
+ }
+
+ bcopy (wbuf, *buf + out_buf_offs, nwrite);
+ out_buf_offs += nwrite;
+ }
+
+ void zreaderr (void)
+ {
+ zerr = EIO;
+ longjmp (zerr_jmp_buf, 1);
+ }
+ void zerror (const char *msg)
+ {
+ zerr = EINVAL;
+ longjmp (zerr_jmp_buf, 2);
+ }
+
+ /* Try to guess a reasonable output buffer size. */
+ *buf_len = round_page (from->size * 2);
+ *buf = mmap (0, *buf_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ zerr = (*buf == (void *) -1) ? errno : 0;
+ if (zerr)
+ return zerr;
+
+ pthread_mutex_lock (&unzip_lock);
+
+ unzip_read = zread;
+ unzip_write = zwrite;
+ unzip_read_error = zreaderr;
+ unzip_error = zerror;
+
+ if (! setjmp (zerr_jmp_buf))
+ {
+ /* Call the decompression engine. */
+ zerr = DO_UNZIP ();
+ }
+
+ pthread_mutex_unlock (&unzip_lock);
+
+ if (in_buf_size > 0)
+ munmap (in_buf, in_buf_size);
+
+ if (zerr)
+ {
+ if (*buf_len > 0)
+ munmap (*buf, *buf_len);
+ }
+ else if (out_buf_offs < *buf_len)
+ /* Trim the output buffer to be the right length. */
+ {
+ size_t end = round_page (out_buf_offs);
+ if (end < *buf_len)
+ munmap (*buf + end, *buf_len - end);
+ *buf_len = out_buf_offs;
+ }
+
+ return zerr;
+}
+
+
+/* Return a new store in STORE which contains a snapshot of the uncompressed
+ contents of the store FROM; FROM is consumed. */
+error_t
+STORE_UNZIP(create) (struct store *from, int flags, struct store **store)
+{
+ void *buf;
+ size_t buf_len;
+ error_t err = unzip_store (from, &buf, &buf_len);
+
+ if (! err)
+ {
+ err = store_buffer_create (buf, buf_len, flags, store);
+ if (err)
+ munmap (buf, buf_len);
+ else
+ store_free (from);
+ }
+
+ return err;
+}
+
+/* Open the compressed store NAME -- which consists of another store-class
+ name, a ':', and a name for that store class to open -- and return the
+ corresponding store in STORE. CLASSES is used to select classes
+ specified by the type name; if it is 0, STORE_STD_CLASSES is used. */
+error_t
+STORE_UNZIP(open) (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ struct store *from;
+ error_t err =
+ store_typed_open (name, flags | STORE_HARD_READONLY, classes, &from);
+
+ if (! err)
+ {
+ err = STORE_UNZIP(create) (from, flags, store);
+ if (err)
+ store_free (from);
+ }
+
+ return err;
+}
+
+const struct store_class STORE_UNZIP(class) =
+{ -1, STRINGIFY(UNZIP), open: STORE_UNZIP(open) };
+STORE_STD_CLASS_1 (UNZIP);
diff --git a/libstore/url.c b/libstore/url.c
new file mode 100644
index 00000000..9b5f524e
--- /dev/null
+++ b/libstore/url.c
@@ -0,0 +1,92 @@
+/* Support for opening stores named in URL syntax.
+
+ Copyright (C) 2001,02 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 "store.h"
+#include <string.h>
+#include <stdlib.h>
+
+
+/* Similar to store_typed_open, but NAME must be in URL format,
+ i.e. a class name followed by a ':' and any type-specific name.
+ Store classes opened this way must strip off the "class:" prefix.
+ A leading ':' or no ':' at all is invalid syntax. */
+
+error_t
+store_url_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ if (name == 0 || name[0] == ':' || strchr (name, ':') == 0)
+ return EINVAL;
+
+ return store_typed_open (name, flags, classes, store);
+}
+
+error_t
+store_url_decode (struct store_enc *enc,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ const struct store_class *cl;
+
+ /* This is pretty bogus. We use decode.c's code just to validate
+ the generic format and extract the name from the data. */
+ struct store dummy, *dummyptr;
+ error_t dummy_create (mach_port_t port, int flags, size_t block_size,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+ {
+ *store = &dummy;
+ return 0;
+ }
+ struct store_enc dummy_enc = *enc;
+ error_t err = store_std_leaf_decode (&dummy_enc, &dummy_create, &dummyptr);
+ if (err)
+ return err;
+
+ /* Find the class matching this name. */
+ cl = store_find_class (dummy.name, strchr (dummy.name, ':'), classes);
+# pragma weak store_module_find_class
+ if (cl == 0 && store_module_find_class)
+ err = store_module_find_class (dummy.name, strchr (dummy.name, ':'),
+ &cl);
+ free (dummy.name);
+ free (dummy.misc);
+
+ if (cl == 0)
+ return EINVAL;
+
+ /* Now that we have the class, we just punt to its own decode hook. */
+
+ return (!cl->decode ? EOPNOTSUPP : (*cl->decode) (enc, classes, store));
+}
+
+/* This class is only trivially different from the "typed" class when used
+ by name. Its real purpose is to decode file_get_storage_info results
+ that use the STORAGE_NETWORK type, for which the convention is that the
+ name be in URL format (i.e. "type:something"). */
+
+const struct store_class store_url_open_class =
+{
+ STORAGE_NETWORK, "url",
+ open: store_url_open,
+ decode: store_url_decode
+};
+STORE_STD_CLASS (url_open);
diff --git a/libstore/util.c b/libstore/util.c
new file mode 100644
index 00000000..59085241
--- /dev/null
+++ b/libstore/util.c
@@ -0,0 +1,19 @@
+/* Hacked and slashed by roland@gnu.ai.mit.edu for use in Hurd exec server. */
+
+/* util.c -- utility functions for gzip support
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+#ifdef RCSID
+static char rcsid[] = "$Id: util.c,v 1.1 1994/12/14 04:29:37 roland Exp $";
+#endif
+
+#include <stddef.h>
+
+/* I/O interface */
+int (*unzip_read) (char *buf, size_t maxread);
+void (*unzip_write) (const char *buf, size_t nwrite);
+void (*unzip_read_error) (void);
+void (*unzip_error) (const char *msg);
diff --git a/libstore/xinl.c b/libstore/xinl.c
new file mode 100644
index 00000000..90242212
--- /dev/null
+++ b/libstore/xinl.c
@@ -0,0 +1,2 @@
+#define STORE_DEFINE_EI
+#include "store.h"
diff --git a/libstore/zero.c b/libstore/zero.c
new file mode 100644
index 00000000..2fba72cc
--- /dev/null
+++ b/libstore/zero.c
@@ -0,0 +1,196 @@
+/* Zero store backend
+
+ Copyright (C) 1995,96,97,99,2000,01, 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
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/mman.h>
+
+#include "store.h"
+
+static error_t
+zero_read (struct store *store,
+ store_offset_t addr, size_t index, size_t amount, void **buf,
+ size_t *len)
+{
+ if (*len < amount)
+ {
+ *buf = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*buf == MAP_FAILED)
+ return errno;
+ *len = amount;
+ return 0;
+ }
+ else
+ memset (*buf, 0, amount);
+
+ *len = amount;
+ return 0;
+}
+
+static error_t
+zero_write (struct store *store,
+ store_offset_t addr, size_t index, const void *buf, size_t len,
+ size_t *amount)
+{
+ return 0;
+}
+
+static error_t
+zero_set_size (struct store *store, size_t newsize)
+{
+ return EOPNOTSUPP;
+}
+
+/* Modify SOURCE to reflect those runs in RUNS, and return it in STORE. */
+error_t
+zero_remap (struct store *source,
+ const struct store_run *runs, size_t num_runs,
+ struct store **store)
+{
+ /* Because all blocks are the same, a zero store always contains just one
+ run; here we simply count up the number of blocks specified by RUNS, and
+ modify SOURCE's one run to reflect that. */
+ int i;
+ store_offset_t length = 0, old_length = source->runs[0].length;
+ for (i = 0; i < num_runs; i++)
+ if (runs[i].start < 0 || runs[i].start + runs[i].length >= old_length)
+ return EINVAL;
+ else
+ length += runs[i].length;
+ source->runs[0].length = length;
+ *store = source;
+ return 0;
+}
+
+error_t
+zero_allocate_encoding (const struct store *store, struct store_enc *enc)
+{
+ enc->num_ints += 2;
+ enc->num_offsets += 1;
+ return 0;
+}
+
+error_t
+zero_encode (const struct store *store, struct store_enc *enc)
+{
+ enc->ints[enc->cur_int++] = store->class->id;
+ enc->ints[enc->cur_int++] = store->flags;
+ enc->offsets[enc->cur_offset++] = store->size;
+ return 0;
+}
+
+error_t
+zero_decode (struct store_enc *enc, const struct store_class *const *classes,
+ struct store **store)
+{
+ store_offset_t size;
+ int type, flags;
+
+ if (enc->cur_int + 2 > enc->num_ints
+ || enc->cur_offset + 1 > enc->num_offsets)
+ return EINVAL;
+
+ type = enc->ints[enc->cur_int++];
+ flags = enc->ints[enc->cur_int++];
+ size = enc->offsets[enc->cur_offset++];
+
+ return store_zero_create (size, flags, store);
+}
+
+static error_t
+zero_open (const char *name, int flags,
+ const struct store_class *const *classes,
+ struct store **store)
+{
+ if (name)
+ {
+ char *end;
+ store_offset_t size = strtoull (name, &end, 0);
+ if (end == name || end == NULL)
+ return EINVAL;
+ switch (*end)
+ {
+ case 'b':
+ size *= 512;
+ break;
+ case 'k':
+ case 'K':
+ size *= 1024;
+ break;
+ case 'm':
+ case 'M':
+ size *= 1024 * 1024;
+ break;
+ case 'g':
+ case 'G':
+ size *= 1024 * 1024 * 1024;
+ break;
+ }
+ return store_zero_create (size, flags, store);
+ }
+ else
+ {
+ store_offset_t max_offs = ~((store_offset_t)1
+ << (CHAR_BIT * sizeof (store_offset_t) - 1));
+ return store_zero_create (max_offs, flags, store);
+ }
+}
+
+static error_t
+zero_validate_name (const char *name, const struct store_class *const *classes)
+{
+ if (name)
+ {
+ char *end;
+ strtoul (name, &end, 0);
+ return end == name ? EINVAL : 0;
+ }
+ else
+ return 0; /* `maximum size' */
+}
+
+static error_t
+zero_map (const struct store *store, vm_prot_t prot, mach_port_t *memobj)
+{
+ *memobj = MACH_PORT_NULL;
+ return 0;
+}
+
+const struct store_class
+store_zero_class =
+{
+ STORAGE_ZERO, "zero", zero_read, zero_write, zero_set_size,
+ zero_allocate_encoding, zero_encode, zero_decode,
+ 0, 0, 0, 0, zero_remap, zero_open, zero_validate_name,
+ zero_map
+};
+STORE_STD_CLASS (zero);
+
+/* Return a new zero store SIZE bytes long in STORE. */
+error_t
+store_zero_create (store_offset_t size, int flags, struct store **store)
+{
+ struct store_run run = { 0, size };
+ return
+ _store_create (&store_zero_class, MACH_PORT_NULL,
+ flags | STORE_INNOCUOUS, 1, &run, 1, 0, store);
+}