summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--trans/Makefile8
-rw-r--r--trans/mtab.c800
2 files changed, 805 insertions, 3 deletions
diff --git a/trans/Makefile b/trans/Makefile
index b3210b67..6eb51d0d 100644
--- a/trans/Makefile
+++ b/trans/Makefile
@@ -20,14 +20,15 @@ dir := trans
makemode := servers
targets = symlink firmlink ifsock magic null fifo new-fifo fwd crash \
- password hello hello-mt streamio fakeroot proxy-defpager remap
+ password hello hello-mt streamio fakeroot proxy-defpager remap \
+ mtab
SRCS = ifsock.c symlink.c magic.c null.c fifo.c new-fifo.c fwd.c \
crash.c firmlink.c password.c hello.c hello-mt.c streamio.c \
- fakeroot.c proxy-defpager.c remap.c
+ fakeroot.c proxy-defpager.c remap.c mtab.c
OBJS = $(SRCS:.c=.o) fsysServer.o ifsockServer.o passwordServer.o \
crashServer.o crash_replyUser.o msgServer.o \
default_pagerServer.o default_pagerUser.o \
- device_replyServer.o elfcore.o
+ device_replyServer.o elfcore.o fsysUser.o
HURDLIBS = ports netfs trivfs iohelp fshelp pipe ihash shouldbeinlibc
LDLIBS += -lpthread
password-LDLIBS = $(LIBCRYPT)
@@ -51,6 +52,7 @@ magic: ../libiohelp/libiohelp.a
hello: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a
fakeroot: ../libnetfs/libnetfs.a ../libfshelp/libfshelp.a ../libiohelp/libiohelp.a ../libports/libports.a ../libihash/libihash.a
remap: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a
+mtab: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a fsysUser.o
$(targets): ../libshouldbeinlibc/libshouldbeinlibc.a
$(targets): %: %.o
diff --git a/trans/mtab.c b/trans/mtab.c
new file mode 100644
index 00000000..2973a0b7
--- /dev/null
+++ b/trans/mtab.c
@@ -0,0 +1,800 @@
+/* This is an mtab translator.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <fcntl.h>
+#include <hurd.h>
+#include <hurd/trivfs.h>
+#include <inttypes.h>
+#include <mntent.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <version.h>
+
+#include "fsys_U.h"
+
+static char *path = NULL;
+static int insecure = 0;
+
+/* Our control port. */
+struct trivfs_control *control;
+
+/* These kind of objects are created and populated in the open_hook.
+ They keep track of the content and file position of the client. */
+struct mtab
+{
+ char *contents;
+ size_t contents_len;
+ off_t offs;
+};
+
+const char *argp_program_version = STANDARD_HURD_VERSION (mtab);
+
+static const struct argp_option options[] =
+{
+ {"insecure", 'I', 0, 0,
+ "Follow translators not bound to nodes owned by you or root"},
+ {}
+};
+
+/* Parse a command line option. */
+error_t parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'I':
+ insecure = 1;
+ break;
+
+ case ARGP_KEY_ARG:
+ path = realpath (arg, NULL);
+ if (! path)
+ argp_error (state, "Error while canonicalizing path");
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static struct argp argp =
+ {
+ options,
+ parse_opt,
+ "TARGET\tFile name of a node with an active translator",
+ "A translator providing mtab compatible information about active "
+ "and passive translators below TARGET.",
+ };
+
+/* This will be called from libtrivfs to help construct the answer
+ to an fsys_get_options RPC. */
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ error_t err;
+
+ if (insecure)
+ {
+ err = argz_add (argz, argz_len, path);
+ if (err)
+ return err;
+ }
+
+ err = argz_add (argz, argz_len, path);
+ return err;
+}
+
+/* Setting this variable makes libtrivfs use our argp to
+ parse options passed in an fsys_set_options RPC. */
+struct argp *trivfs_runtime_argp = &argp;
+
+/* Authentication of the current process. */
+uid_t *uids;
+gid_t *gids;
+size_t uids_len, gids_len;
+
+/* Initialize and populate the uids and gids vectors. */
+error_t
+get_credentials (void)
+{
+ /* Fetch uids... */
+ uids_len = geteuids (0, 0);
+ if (uids_len < 0)
+ return errno;
+
+ uids = malloc (uids_len * sizeof (uid_t));
+ if (! uids)
+ return ENOMEM;
+
+ uids_len = geteuids (uids_len, uids);
+ if (uids_len < 0)
+ return errno;
+
+ /* ... and gids. */
+ gids_len = getgroups (0, 0);
+ if (gids_len < 0)
+ return errno;
+
+ gids = malloc (gids_len * sizeof (gid_t));
+ if (! uids)
+ return ENOMEM;
+
+ gids_len = getgroups (gids_len, gids);
+ if (gids_len < 0)
+ return errno;
+
+ return 0;
+}
+
+/* Check if the given struct stat describes a node owned by the
+ current user. */
+int
+is_owner (io_statbuf_t *st)
+{
+ int found = 0;
+ for (size_t i = 0; i < uids_len; i++)
+ if (uids[i] == st->st_uid)
+ {
+ found = 1;
+ break;
+ }
+
+ if (! found)
+ return 0;
+
+ found = 0;
+ for (size_t i = 0; i < gids_len; i++)
+ if (gids[i] == st->st_gid)
+ {
+ found = 1;
+ break;
+ }
+
+ return found;
+}
+
+error_t
+mtab_populate (struct mtab *mtab, const char *path, int insecure);
+
+error_t
+argz_add_device (char **options, size_t *options_len, const char *device);
+
+error_t
+map_device_to_path (const char *device, char **path);
+
+int
+main (int argc, char *argv[])
+{
+ error_t err;
+
+ err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+ if (err)
+ error (1, err, "argument parsing");
+
+ err = get_credentials ();
+ if (err)
+ error (2, err, "getting credentials");
+
+ mach_port_t bootstrap;
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap != MACH_PORT_NULL)
+ {
+ /* Started as a translator. */
+
+ auth_t nullauth;
+ err = auth_makeauth (getauth (),
+ NULL, MACH_MSG_TYPE_COPY_SEND, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ &nullauth);
+ if (err)
+ error (3, err, "dropping credentials");
+
+ err = setauth (nullauth);
+ if (err)
+ error (3, err, "dropping credentials");
+
+ /* Reply to our parent. */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &control);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (4, err, "trivfs_startup");
+
+ /* Launch. */
+ ports_manage_port_operations_one_thread (control->pi.bucket,
+ trivfs_demuxer,
+ 0);
+ }
+ else
+ {
+ /* One-shot mode. */
+ struct mtab mtab = { NULL, 0, 0 };
+ err = mtab_populate (&mtab, path, insecure);
+ if (err)
+ error (5, err, "%s", path);
+
+ if (mtab.contents)
+ printf ("%s", mtab.contents);
+ }
+
+ return 0;
+}
+
+error_t
+mtab_add_entry (struct mtab *mtab, const char *entry, size_t length)
+{
+ char *p = realloc (mtab->contents, mtab->contents_len + length + 1);
+ if (! p)
+ return ENOMEM;
+
+ memcpy (&p[mtab->contents_len], entry, length);
+
+ mtab->contents = p;
+ mtab->contents_len += length;
+
+ /* Zero-terminate contents so that we can also interpret it as
+ string. */
+ mtab->contents[mtab->contents_len] = '\0';
+
+ return 0;
+}
+
+/* Populates the given MTAB object with the information for PATH. If
+ INSECURE is given, also follow translators bound to nodes not owned
+ by root or the current user. */
+/* XXX split up */
+error_t
+mtab_populate (struct mtab *mtab, const char *path, int insecure)
+{
+ error_t err = 0;
+
+ /* These resources are freed in the epilogue. */
+ file_t node = MACH_PORT_NULL;
+ fsys_t fsys = MACH_PORT_NULL;
+ char *argz = NULL;
+ size_t argz_len = 0;
+ char **argv = NULL;
+ char *type = NULL;
+ char *options = NULL;
+ size_t options_len = 0;
+ char *src = NULL;
+ char *entry = NULL;
+ size_t entry_len = 0;
+ char *children = NULL;
+ size_t children_len = 0;
+
+ /* Get the underlying node. */
+ node = file_name_lookup (path, O_NOTRANS, 0666);
+ if (node == MACH_PORT_NULL)
+ {
+ err = errno;
+ goto errout;
+ }
+
+ if (! insecure)
+ {
+ /* Check who owns the node the translator is bound to. */
+ io_statbuf_t st;
+ err = io_stat (node, &st);
+ if (err)
+ goto errout;
+
+ if (st.st_uid != 0 && st.st_gid != 0 && ! is_owner (&st))
+ {
+ err = EPERM;
+ goto errout;
+ }
+ }
+
+ err = file_get_translator_cntl (node, &fsys);
+ if (err == EPERM)
+ /* If we do not have permission to do that, it cannot be a node
+ bound to our control port, so ignore this error. */
+ err = 0;
+
+ if (err == ENXIO && strcmp (path, "/") == 0)
+ /* The root translator fails differently, but this can't be bound
+ to our control port either, so ignore this error. */
+ err = 0;
+
+ if (err)
+ return err;
+
+ if (control && control->pi.port_right == fsys)
+ /* This node is bound to our control port, ignore it. */
+ goto errout;
+
+ /* Re-do the lookup without O_NOTRANS to get the root node. */
+ mach_port_deallocate (mach_task_self (), node);
+ node = file_name_lookup (path, 0, 0666);
+ if (node == MACH_PORT_NULL)
+ {
+ err = errno;
+ goto errout;
+ }
+
+ /* Query its options. */
+ err = file_get_fs_options (node, &argz, &argz_len);
+ if (err)
+ {
+ if (err == EOPNOTSUPP)
+ err = 0; /* There's not much we could do then. */
+
+ goto errout;
+ }
+
+ size_t count = argz_count (argz, argz_len);
+ argv = malloc ((count + 1) * sizeof (char *));
+ if (! argv)
+ {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ argz_extract (argz, argz_len, argv);
+
+ type = strdup (argv[0]);
+ if (! type)
+ {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ for (int i = 1; i < count - 1; i++)
+ {
+ char *v = argv[i];
+
+ if (*v == '-')
+ v++;
+ if (*v == '-')
+ v++;
+
+ err = argz_add (&options, &options_len, v);
+ if (err)
+ goto errout;
+ }
+
+ err = argz_add_device (&options, &options_len, argv[count - 1]);
+ if (err)
+ goto errout;
+
+ argz_stringify (options, options_len, ',');
+
+ string_t source;
+ err = fsys_get_source (node, source);
+ if (err)
+ {
+ if (err == EOPNOTSUPP)
+ {
+ /* Guess based on the last argument. */
+ err = map_device_to_path (argv[count - 1], &src);
+ if (err)
+ goto errout;
+ }
+ else
+ goto errout;
+ }
+ else
+ src = source;
+
+ entry_len = asprintf (&entry, "%s %s %s %s 0 0\n", src, path, type,
+ options? options: MNTOPT_DEFAULTS);
+ if (! entry)
+ {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ err = mtab_add_entry (mtab, entry, entry_len);
+ if (err)
+ goto errout;
+
+ /* path has an active translator, query its children. */
+ err = fsys_get_children (node, &children, &children_len);
+ if (err == EOPNOTSUPP)
+ {
+ err = 0;
+ children_len = 0;
+ }
+
+ if (err)
+ goto errout;
+
+ if (children_len)
+ for (char *c = children; c; c = argz_next (children, children_len, c))
+ {
+ char *p = NULL;
+ asprintf (&p, "%s%s%s",
+ path,
+ path[strlen (path) - 1] == '/'? "": "/",
+ c);
+ if (! p)
+ {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ err = mtab_populate (mtab, p, insecure);
+ if (err)
+ {
+ /* There is really not much we can do about errors here. */
+ error (0, err, "%s", p);
+ err = 0;
+ }
+
+ free (p);
+ }
+
+ errout:
+ if (node != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), node);
+
+ if (fsys != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), fsys);
+
+ if (argz)
+ vm_deallocate (mach_task_self (), (vm_address_t) argz, argz_len);
+
+ free (argv);
+ free (type);
+ free (options);
+
+ if (src != source)
+ free (src);
+
+ free (entry);
+
+ if (children)
+ vm_deallocate (mach_task_self (), (vm_address_t) children, children_len);
+
+ return err;
+}
+
+/* Decodes the DEVICE string into appropriate OPTIONS. Currently only
+ tmpfs-style size declarations are supported. */
+error_t
+argz_add_device (char **options, size_t *options_len, const char *device)
+{
+ error_t err;
+ char *end = NULL;
+ intmax_t size = strtoimax (device, &end, 0);
+ if (end == NULL || end == device)
+ return 0;
+
+ if (size < 0)
+ return 0;
+
+ switch (*end)
+ {
+ case 'g':
+ case 'G':
+ case 'm':
+ case 'M':
+ case 'k':
+ case 'K':
+ break;
+ default:
+ return 0;
+ }
+
+ /* device specifies a size. */
+ char *arg = NULL;
+ asprintf (&arg, "size=%s", device);
+ if (! arg)
+ return ENOMEM;
+
+ err = argz_add (options, options_len, arg);
+
+ free (arg);
+ return err;
+}
+
+/* Matches [hs]d\ds\d\d?. */
+int
+looks_like_block_device (const char *s)
+{
+ size_t len = strlen (s);
+ if (len != 3 && len != 5 && len != 6)
+ return 0;
+
+ return ((s[0] == 'h' || s[0] == 's') && s[1] == 'd' && isdigit (s[2]) &&
+ (len == 3 || (s[3] == 's' && isdigit (s[4]) &&
+ (len == 5 || isdigit (s[5])))));
+}
+
+/* Map a device string to a file name referencing the appropriate
+ device file. */
+error_t
+map_device_to_path (const char *device, char **path)
+{
+ if (strncmp (device, "device:", 7) == 0)
+ asprintf (path, "/dev/%s", &device[7]);
+ else if (strncmp (device, "/dev/", 5) == 0)
+ *path = strdup (device);
+ else if (looks_like_block_device (device))
+ asprintf (path, "/dev/%s", device);
+ else
+ *path = strdup ("none");
+
+ if (! *path)
+ return ENOMEM;
+
+ return 0;
+}
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_allow_open = O_READ;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ /* Mark the node as a read-only plain file. */
+ st->st_mode &= ~(S_IFMT | ALLPERMS);
+ st->st_mode |= (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH);
+ st->st_size = ((struct mtab *) cred->po->hook)->contents_len;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ exit (EXIT_SUCCESS);
+}
+
+static error_t
+open_hook (struct trivfs_peropen *peropen)
+{
+ struct mtab *mtab = malloc (sizeof (struct mtab));
+ if (mtab == NULL)
+ return ENOMEM;
+
+ /* Hook! */
+ peropen->hook = mtab;
+
+ /* Initialize the fields. */
+ mtab->offs = 0;
+ mtab->contents = NULL;
+ mtab->contents_len = 0;
+
+ return mtab_populate (mtab, path, insecure);
+}
+
+static void
+close_hook (struct trivfs_peropen *peropen)
+{
+ free (((struct mtab *) peropen->hook)->contents);
+ free (peropen->hook);
+}
+
+/* Read data from an IO object. If offset is -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMOUNT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, mach_msg_type_number_t amount)
+{
+ struct mtab *op;
+
+ /* Deny access if they have bad credentials. */
+ if (! cred)
+ return EOPNOTSUPP;
+
+ if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+
+ /* Get the offset. */
+ op = cred->po->hook;
+ if (offs == -1)
+ offs = op->offs;
+
+ /* Prune the amount they want to read. */
+ if (offs > op->contents_len)
+ offs = op->contents_len;
+ if (offs + amount > op->contents_len)
+ amount = op->contents_len - offs;
+
+ if (amount > 0)
+ {
+ /* Possibly allocate a new buffer. */
+ if (*data_len < amount)
+ {
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ return ENOMEM;
+ }
+
+ /* Copy the constant data into the buffer. */
+ memcpy ((char *) *data, op->contents + offs, amount);
+
+ /* Update the saved offset. */
+ op->offs += amount;
+ }
+
+ *data_len = amount;
+ return 0;
+}
+
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offs, int whence, off_t *new_offs)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+
+ struct mtab *op = cred->po->hook;
+
+ switch (whence)
+ {
+ case SEEK_CUR:
+ offs += op->offs;
+ goto check;
+ case SEEK_END:
+ offs += op->contents_len;
+ case SEEK_SET:
+ check:
+ if (offs >= 0)
+ {
+ *new_offs = op->offs = offs;
+ break;
+ }
+ default:
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/* If this variable is set, it is called every time a new peropen
+ structure is created and initialized. */
+error_t (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook;
+
+/* If this variable is set, it is called every time a peropen structure
+ is about to be destroyed. */
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+kern_return_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ mach_msg_type_number_t *amount)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(cred->po->openmodes & O_READ))
+ return EINVAL;
+
+ struct mtab *op = cred->po->hook;
+
+ *amount = op->contents_len - op->offs;
+ return 0;
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+kern_return_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (((*type & SELECT_READ) && !(cred->po->openmodes & O_READ))
+ || ((*type & SELECT_WRITE) && !(cred->po->openmodes & O_WRITE)))
+ return EBADF;
+
+ *type &= ~SELECT_URG;
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ struct timespec ts,
+ int *type)
+{
+ return trivfs_S_io_select (cred, reply, replytype, type);
+}
+
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+
+kern_return_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ *bits = cred->po->openmodes;
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ return 0;
+}