diff options
author | Miles Bader <miles@gnu.org> | 1997-07-23 13:47:31 +0000 |
---|---|---|
committer | Miles Bader <miles@gnu.org> | 1997-07-23 13:47:31 +0000 |
commit | 6b9fb1d6104459404b5b7a918530071a02af80c7 (patch) | |
tree | a9981acc40ab97b7f9008a31346df4ad07c4684e /usermux | |
parent | fa59e2d6745f93b457f4b984d3d4c331401ec449 (diff) |
Initial checkin
Diffstat (limited to 'usermux')
-rw-r--r-- | usermux/Makefile | 30 | ||||
-rw-r--r-- | usermux/leaf.c | 151 | ||||
-rw-r--r-- | usermux/mux.c | 492 | ||||
-rw-r--r-- | usermux/node.c | 119 | ||||
-rw-r--r-- | usermux/usermux-xinl.c | 24 | ||||
-rw-r--r-- | usermux/usermux.c | 149 | ||||
-rw-r--r-- | usermux/usermux.h | 99 |
7 files changed, 1064 insertions, 0 deletions
diff --git a/usermux/Makefile b/usermux/Makefile new file mode 100644 index 00000000..318adde2 --- /dev/null +++ b/usermux/Makefile @@ -0,0 +1,30 @@ +# Makefile for usermux +# +# Copyright (C) 1997 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2, or (at +# your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +dir := usermux +makemode := server + +target = usermux + +SRCS = usermux.c mux.c leaf.c node.c stubs.c +LCLHDRS = usermux.h + +OBJS = $(SRCS:.c=.o) +HURDLIBS = netfs fshelp iohelp ports threads ihash shouldbeinlibc + +include ../Makeconf diff --git a/usermux/leaf.c b/usermux/leaf.c new file mode 100644 index 00000000..bacaee9d --- /dev/null +++ b/usermux/leaf.c @@ -0,0 +1,151 @@ +/* Usermux leaf node functions + + Copyright (C) 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 <stdio.h> +#include <string.h> +#include <pwd.h> +#include <argz.h> +#include <hurd/paths.h> + +#include "usermux.h" + +/* Read the contents of NODE (a symlink), for USER, into BUF. */ +error_t +netfs_attempt_readlink (struct iouser *user, struct node *node, char *buf) +{ + assert (node->nn->name); + /* For symlink nodes, the translator spec just contains the link target. */ + memcpy (buf, node->nn->trans, node->nn->trans_len); + fshelp_touch (&node->nn_stat, TOUCH_ATIME, usermux_maptime); + return 0; +} + +/* For locked node NODE with S_IPTRANS set in its mode, look up the name of + its translator. Store the name into newly malloced storage, and return it + in *ARGZ; set *ARGZ_LEN to the total length. + + For usermux, this creates a new translator string by instantiating the + global translator template. */ +error_t +netfs_get_translator (struct node *node, char **trans, size_t *trans_len) +{ + if (! node->nn->name) + return EINVAL; + else + { + fshelp_touch (&node->nn_stat, TOUCH_ATIME, usermux_maptime); + *trans = 0; + *trans_len = 0; + if (S_ISLNK (node->nn_stat.st_mode)) + argz_add (trans, trans_len, _HURD_SYMLINK); + return + argz_append (trans, trans_len, node->nn->trans, node->nn->trans_len); + } +} + +/* Create a new leaf node in MUX, with a name NAME, and return the new node + with a single reference in NODE. */ +error_t +create_user_node (struct usermux *mux, struct usermux_name *name, + struct passwd *pw, struct node **node) +{ + error_t err; + struct node *new; + struct netnode *nn = malloc (sizeof (struct netnode)); + + if (! nn) + return ENOMEM; + + nn->mux = mux; + nn->name = name; + + new = netfs_make_node (nn); + if (! new) + { + free (nn); + return ENOMEM; + } + + new->nn_stat = mux->stat_template; + + new->nn_stat.st_ino = pw->pw_uid + USERMUX_FILENO_UID_OFFSET; + + if (strcmp (mux->trans_template, _HURD_SYMLINK) == 0 + && mux->trans_template_len == sizeof _HURD_SYMLINK) + { + err = argz_create_sep (pw->pw_dir, 0, &nn->trans, &nn->trans_len); + new->nn_stat.st_mode = (S_IFLNK | 0666); + new->nn_stat.st_size = nn->trans_len; + } + else + { + unsigned replace_count = 0; + + nn->trans = 0; /* Initialize return value. */ + nn->trans_len = 0; + + err = argz_append (&nn->trans, &nn->trans_len, + mux->trans_template, mux->trans_template_len); + + /* Perform any substitutions. */ + if (!err && mux->user_pat && *mux->user_pat) + err = argz_replace (&nn->trans, &nn->trans_len, + mux->user_pat, pw->pw_name, + &replace_count); + if (!err && mux->home_pat && *mux->home_pat) + err = argz_replace (&nn->trans, &nn->trans_len, + mux->home_pat, pw->pw_dir, + &replace_count); + if (!err && mux->uid_pat && *mux->uid_pat) + { + char uid_buf[10]; + snprintf (uid_buf, sizeof uid_buf, "%d", pw->pw_uid); + err = argz_replace (&nn->trans, &nn->trans_len, + mux->uid_pat, uid_buf, + &replace_count); + } + + if (!err && replace_count == 0) + /* Default, if no instances of any pattern occur, is to append the + user home dir. */ + err = argz_add (&nn->trans, &nn->trans_len, pw->pw_dir); + + if (err && nn->trans_len > 0) + free (nn->trans); + + new->nn_stat.st_mode = (S_IFREG | S_IPTRANS | 0666); + new->nn_stat.st_size = 0; + } + + if (err) + { + free (nn); + free (new); + return err; + } + + fshelp_touch (&new->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME, + usermux_maptime); + + name->node = new; + *node = new; + + return 0; +} diff --git a/usermux/mux.c b/usermux/mux.c new file mode 100644 index 00000000..932361fd --- /dev/null +++ b/usermux/mux.c @@ -0,0 +1,492 @@ +/* Root usermux node + + Copyright (C) 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 <stddef.h> +#include <string.h> +#include <dirent.h> +#include <pwd.h> + +#include "usermux.h" + +/* The granularity with which we allocate space to return our result. */ +#define DIRENTS_CHUNK_SIZE (128*1024)/* Enough for perhaps 8000 names. */ + +/* The number seconds we cache our directory return value, in seconds. */ +#define DIRENTS_CACHE_TIME 90 + +/* Returned directory entries are aligned to blocks this many bytes long. + Must be a power of two. */ +#define DIRENT_ALIGN 4 +#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name) + +/* Length is structure before the name + the name + '\0', all + padded to a four-byte alignment. */ +#define DIRENT_LEN(name_len) \ + ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \ + & ~(DIRENT_ALIGN - 1)) + +static error_t lookup_user (struct usermux *mux, const char *user, + struct node **node); /* fwd decl */ + +/* [root] Directory operations. */ + +/* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If + the name was not found, then return ENOENT. On any error, clear *NODE. + (*NODE, if found, should be locked, this call should unlock DIR no matter + what.) */ +error_t +netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **node) +{ + error_t err; + + if (dir->nn->name) + err = ENOTDIR; + else + err = lookup_user (dir->nn->mux, name, node); + + fshelp_touch (&dir->nn_stat, TOUCH_ATIME, usermux_maptime); + + mutex_unlock (&dir->lock); + + if (! err) + mutex_lock (&(*node)->lock); + + return err; +} + +/* Fetch a directory of user entries, as for netfs_get_dirents (that function + is actually a wrapper that caches the results for a while). */ +static error_t +get_dirents (struct node *dir, + int first_entry, int max_entries, char **data, + mach_msg_type_number_t *data_len, + vm_size_t max_data_len, int *data_entries) +{ + error_t err = 0; + + if (dir->nn->name) + return ENOTDIR; + + /* Start scanning. */ + setpwent (); + + /* Find the first entry. */ + while (first_entry-- > 0) + if (! getpwent ()) + { + max_entries = 0; + break; + } + + if (max_entries != 0) + { + size_t size = (max_data_len == 0 ? DIRENTS_CHUNK_SIZE : max_data_len); + + err = vm_allocate (mach_task_self (), (vm_address_t *) data, size, 1); + + if (! err) + { + struct passwd *pw; + char *p = *data; + int count = 0; + int entry_type = + (S_ISLNK (dir->nn->mux->stat_template.st_mode) ? DT_LNK : DT_REG); + + /* See how much space we need for the result. */ + while ((max_entries == -1 || count < max_entries) + && (pw = getpwent ())) + { + struct dirent hdr; + size_t name_len = strlen (pw->pw_name); + size_t sz = DIRENT_LEN (name_len); + + if ((p - *data) + sz > size) + if (max_data_len > 0) + break; + else + /* Try to grow our return buffer. */ + { + vm_address_t extension = (vm_address_t)(*data + size); + err = vm_allocate (mach_task_self (), &extension, + DIRENTS_CHUNK_SIZE, 0); + if (err) + break; + size += DIRENTS_CHUNK_SIZE; + } + + hdr.d_namlen = name_len; + hdr.d_fileno = pw->pw_uid + USERMUX_FILENO_UID_OFFSET; + hdr.d_reclen = sz; + hdr.d_type = entry_type; + + memcpy (p, &hdr, DIRENT_NAME_OFFS); + strcpy (p + DIRENT_NAME_OFFS, pw->pw_name); + p += sz; + + count++; + } + + if (err) + vm_deallocate (mach_task_self (), (vm_address_t)*data, size); + else + { + vm_address_t alloc_end = (vm_address_t)(*data + size); + vm_address_t real_end = round_page (p); + if (alloc_end > real_end) + vm_deallocate (mach_task_self (), + real_end, alloc_end - real_end); + *data_len = p - *data; + *data_entries = count; + } + } + } + + endpwent (); + + return err; +} + +/* Implement the netfs_get_directs callback as described in + <hurd/netfs.h>. */ +error_t +netfs_get_dirents (struct iouser *cred, struct node *dir, + int first_entry, int max_entries, char **data, + mach_msg_type_number_t *data_len, + vm_size_t max_data_len, int *data_entries) +{ + error_t err; + static time_t cache_timestamp = 0; + static struct rwlock cache_lock = RWLOCK_INITIALIZER; + static char *cached_data = 0; + static mach_msg_type_number_t cached_data_len = 0; + static int cached_data_entries = 0; + struct timeval tv; + char *first; + size_t bytes_left, entries_left; + + maptime_read (usermux_maptime, &tv); + if (tv.tv_sec > cache_timestamp + DIRENTS_CACHE_TIME) + { + rwlock_writer_lock (&cache_lock); + + if (cached_data_len > 0) + /* Free the old cache. */ + { + vm_deallocate (mach_task_self (), + (vm_address_t)cached_data, cached_data_len); + cached_data = 0; + cached_data_len = 0; + } + + err = get_dirents (dir, 0, -1, &cached_data, &cached_data_len, 0, + &cached_data_entries); + + if (! err) + cache_timestamp = tv.tv_sec; + + rwlock_writer_unlock (&cache_lock); + + if (err) + return err; + } + + rwlock_reader_lock (&cache_lock); + + first = cached_data; + bytes_left = cached_data_len; + entries_left = cached_data_entries; + + while (first_entry > 0) + { + struct dirent *e = (struct dirent *)first; + + if (entries_left == 0) + { + rwlock_reader_unlock (&cache_lock); + return EINVAL; + } + + first += e->d_reclen; + bytes_left -= e->d_reclen; + entries_left--; + } + + if ((max_data_len > 0 && max_data_len < bytes_left) + || (max_entries > 0 && max_entries < entries_left)) + /* If there's some limit on the return value, we can't just use our + values representing the whole cache, so we have to explicitly count + how much we're going to return. */ + { + char *lim = first; + int entries = 0; + + while (entries_left > 0 + && max_entries > 0 + && max_data_len > ((struct dirent *)lim)->d_reclen) + { + size_t reclen = ((struct dirent *)lim)->d_reclen; + max_data_len -= reclen; + max_entries--; + entries++; + lim += reclen; + } + + bytes_left = (lim - first); + entries_left = entries; + } + + *data_len = bytes_left; + *data_entries = entries_left; + + err = vm_allocate (mach_task_self (), (vm_address_t *)data, bytes_left, 1); + if (! err) + bcopy (cached_data, *data, bytes_left); + + rwlock_reader_unlock (&cache_lock); + + fshelp_touch (&dir->nn_stat, TOUCH_ATIME, usermux_maptime); + + return err; +} + +/* User lookup. */ + +/* Free storage allocated consumed by the host mux name NM, but not the node + it points to. */ +static void +free_name (struct usermux_name *nm) +{ + free ((char *)nm->name); + free (nm); +} + +/* See if there's an existing entry for the name USER, and if so, return its + node in NODE with an additional references. True is returned iff the + lookup succeeds. If PURGE is true, then any nodes with a null node are + removed. */ +static int +lookup_cached (struct usermux *mux, const char *user, int purge, + struct node **node) +{ + struct usermux_name *nm = mux->names, **prevl = &mux->names; + + while (nm) + { + struct usermux_name *next = nm->next; + + if (strcasecmp (user, nm->name) == 0) + { + spin_lock (&netfs_node_refcnt_lock); + if (nm->node) + nm->node->references++; + spin_unlock (&netfs_node_refcnt_lock); + + if (nm->node) + { + *node = nm->node; + return 1; + } + } + + if (purge && !nm->node) + { + *prevl = nm->next; + free_name (nm); + } + else + prevl = &nm->next; + + nm = next; + } + + return 0; +} + +/* See if there's an existing entry for the name USER, and if so, return its + node in NODE, with an additional reference, otherwise, create a new node + for the user HE as referred to by USER, and return that instead, with a + single reference. The type of node created is either a translator node, + if USER refers to the official name of the user, or a symlink node to the + official name, if it doesn't. */ +static error_t +lookup_pwent (struct usermux *mux, const char *user, struct passwd *pw, + struct node **node) +{ + error_t err; + struct usermux_name *nm = malloc (sizeof (struct usermux_name)); + + if (! nm) + return ENOMEM; + + nm->name = strdup (user); + err = create_user_node (mux, nm, pw, node); + if (err) + { + free_name (nm); + return err; + } + + rwlock_writer_lock (&mux->names_lock); + if (lookup_cached (mux, user, 1, node)) + /* An entry for USER has already been created between the time we last + looked and now (which is possible because we didn't lock MUX). + Just throw away our version and return the one already in the cache. */ + { + rwlock_writer_unlock (&mux->names_lock); + nm->node->nn->name = 0; /* Avoid touching the mux name list. */ + netfs_nrele (nm->node); /* Free the tentative new node. */ + free_name (nm); /* And the name it was under. */ + } + else + /* Enter NM into MUX's list of names, and return the new node. */ + { + nm->next = mux->names; + mux->names = nm; + rwlock_writer_unlock (&mux->names_lock); + } + + return 0; +} + +/* Lookup the user USER in MUX, and return the resulting node in NODE, with + an additional reference, or an error. */ +static error_t +lookup_user (struct usermux *mux, const char *user, struct node **node) +{ + int was_cached; + struct passwd _pw, *pw; + char pwent_data[2048]; /* XXX what size should this be???? */ + + rwlock_reader_lock (&mux->names_lock); + was_cached = lookup_cached (mux, user, 0, node); + rwlock_reader_unlock (&mux->names_lock); + + if (was_cached) + return 0; + else if (getpwnam_r (user, &_pw, pwent_data, sizeof pwent_data, &pw) == 0) + return lookup_pwent (mux, user, pw, node); + else + return ENOENT; +} + +/* This should sync the entire remote filesystem. If WAIT is set, return + only after sync is completely finished. */ +error_t +netfs_attempt_syncfs (struct iouser *cred, int wait) +{ + return 0; +} + +/* This should attempt a chmod call for the user specified by CRED on node + NODE, to change the owner to UID and the group to GID. */ +error_t +netfs_attempt_chown (struct iouser *cred, struct node *node, uid_t uid, uid_t gid) +{ + if (node->nn->name) + return EOPNOTSUPP; + else + { + struct usermux *mux = node->nn->mux; + error_t err = file_chown (mux->underlying, uid, gid); + + if (! err) + { + struct usermux_name *nm; + + /* Change NODE's owner. */ + mux->stat_template.st_uid = uid; + mux->stat_template.st_gid = gid; + node->nn_stat.st_uid = uid; + node->nn_stat.st_gid = gid; + + /* Change the owner of each leaf node. */ + rwlock_reader_lock (&mux->names_lock); + for (nm = mux->names; nm; nm = nm->next) + if (nm->node) + { + nm->node->nn_stat.st_uid = uid; + nm->node->nn_stat.st_gid = gid; + } + rwlock_reader_unlock (&mux->names_lock); + + fshelp_touch (&node->nn_stat, TOUCH_CTIME, usermux_maptime); + } + + return err; + } +} + +/* This should attempt a chauthor call for the user specified by CRED on node + NODE, to change the author to AUTHOR. */ +error_t +netfs_attempt_chauthor (struct iouser *cred, struct node *node, uid_t author) +{ + if (node->nn->name) + return EOPNOTSUPP; + else + { + struct usermux *mux = node->nn->mux; + error_t err = file_chauthor (mux->underlying, author); + + if (! err) + { + struct usermux_name *nm; + + /* Change NODE's owner. */ + mux->stat_template.st_author = author; + node->nn_stat.st_author = author; + + /* Change the owner of each leaf node. */ + rwlock_reader_lock (&mux->names_lock); + for (nm = mux->names; nm; nm = nm->next) + if (nm->node) + nm->node->nn_stat.st_author = author; + rwlock_reader_unlock (&mux->names_lock); + + fshelp_touch (&node->nn_stat, TOUCH_CTIME, usermux_maptime); + } + + return err; + } +} + +/* This should attempt a chmod call for the user specified by CRED on node + NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning + of chmod, this function is also used to attempt to change files into other + types. If such a transition is attempted which is impossible, then return + EOPNOTSUPP. */ +error_t +netfs_attempt_chmod (struct iouser *cred, struct node *node, mode_t mode) +{ + mode &= ~S_ITRANS; + if (node->nn->name || ((mode & S_IFMT) != (node->nn_stat.st_mode & S_IFMT))) + return EOPNOTSUPP; + else + { + error_t err = file_chmod (node->nn->mux->underlying, mode & ~S_IFMT); + if (! err) + { + node->nn_stat.st_mode = mode; + fshelp_touch (&node->nn_stat, TOUCH_CTIME, usermux_maptime); + } + return err; + } +} diff --git a/usermux/node.c b/usermux/node.c new file mode 100644 index 00000000..fa8bfdd7 --- /dev/null +++ b/usermux/node.c @@ -0,0 +1,119 @@ +/* General fs node functions + + Copyright (C) 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 <fcntl.h> + +#include "usermux.h" + +/* Node maintenance. */ + +/* Node NP is all done; free all its associated storage. */ +void +netfs_node_norefs (struct node *node) +{ + if (node->nn->name) + /* Remove our name's pointer to us; the name itself will eventually be + freed by another party. */ + node->nn->name->node = 0; + if (node->nn->trans_len > 0) + free (node->nn->trans); + free (node->nn); + free (node); +} + +/* Attempt to create a file named NAME in DIR for USER with MODE. Set *NODE + to the new node upon return. On any error, clear *NODE. *NODE should be + locked on success; no matter what, unlock DIR before returning. */ +error_t +netfs_attempt_create_file (struct iouser *user, struct node *dir, + char *name, mode_t mode, struct node **node) +{ + *node = 0; + mutex_unlock (&dir->lock); + return EOPNOTSUPP; +} + +/* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero if we + just created this node. Return an error if we should not permit the open + to complete because of a permission restriction. */ +error_t +netfs_check_open_permissions (struct iouser *user, struct node *node, + int flags, int newnode) +{ + error_t err = 0; + if (flags & O_READ) + err = fshelp_access (&node->nn_stat, S_IREAD, user); + if (!err && (flags & O_WRITE)) + err = fshelp_access (&node->nn_stat, S_IWRITE, user); + if (!err && (flags & O_EXEC)) + err = fshelp_access (&node->nn_stat, S_IEXEC, user); + return err; +} + +/* This should attempt a utimes call for the user specified by CRED on node + NODE, to change the atime to ATIME and the mtime to MTIME. */ +error_t +netfs_attempt_utimes (struct iouser *cred, struct node *node, + struct timespec *atime, struct timespec *mtime) +{ + error_t err = fshelp_isowner (&node->nn_stat, cred); + if (! err) + { + node->nn_stat.st_mtime = mtime->tv_sec; + node->nn_stat.st_mtime_usec = mtime->tv_nsec / 1000; + node->nn_stat.st_atime = atime->tv_sec; + node->nn_stat.st_atime_usec = atime->tv_nsec / 1000; + fshelp_touch (&node->nn_stat, TOUCH_CTIME, usermux_maptime); + } + return err; +} + +/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC) + in *TYPES for file NODE and user CRED. */ +error_t +netfs_report_access (struct iouser *cred, struct node *node, int *types) +{ + *types = 0; + if (fshelp_access (&node->nn_stat, S_IREAD, cred) == 0) + *types |= O_READ; + if (fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0) + *types |= O_WRITE; + if (fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0) + *types |= O_EXEC; + return 0; +} + +/* Trivial definitions. */ + +/* Make sure that NP->nn_stat is filled with current information. CRED + identifies the user responsible for the operation. */ +error_t +netfs_validate_stat (struct node *node, struct iouser *cred) +{ + return 0; +} + +/* This should sync the file NODE completely to disk, for the user CRED. If + WAIT is set, return only after sync is completely finished. */ +error_t +netfs_attempt_sync (struct iouser *cred, struct node *node, int wait) +{ + return 0; +} diff --git a/usermux/usermux-xinl.c b/usermux/usermux-xinl.c new file mode 100644 index 00000000..a68b6646 --- /dev/null +++ b/usermux/usermux-xinl.c @@ -0,0 +1,24 @@ +/* Real definitions for extern inline functions in usermux.h + + Copyright (C) 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. */ + +#define USERMUX_EI +#undef __OPTIMIZE__ +#define __OPTIMIZE__ +#include "usermux.h" diff --git a/usermux/usermux.c b/usermux/usermux.c new file mode 100644 index 00000000..060fb411 --- /dev/null +++ b/usermux/usermux.c @@ -0,0 +1,149 @@ +/* Multiplexing filesystems by host + + Copyright (C) 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 <unistd.h> +#include <argp.h> +#include <argz.h> +#include <error.h> +#include <sys/time.h> +#include <hurd/paths.h> + +#include "usermux.h" + +int netfs_maxsymlinks = 25; + +volatile struct mapped_time_value *usermux_mapped_time; + +#define OPT_USER_PAT 1 +#define OPT_HOME_PAT 2 +#define OPT_UID_PAT 3 + +/* Startup options. */ +static const struct argp_option options[] = +{ + { "user-pattern", OPT_USER_PAT, "PAT", 0, + "The string to replace in the translator specification with the user name" + " (default `${user}')" }, + { "home-pattern", OPT_HOME_PAT, "PAT", 0, + "The string to replace in the translator specification with the user's" + " home directory (default `${home}')" }, + { "uid-pattern", OPT_UID_PAT, "PAT", 0, + "The string to replace in the translator specification with the uid" + " (default `${uid}')" }, + { "clear-patterns", 'C', 0, 0, + "Reset all patterns to empty; this option may then be followed by options" + " to set specific patterns" }, + { 0 } +}; +static const char args_doc[] = "[TRANSLATOR [ARG...]]"; +static const char doc[] = + "A translator for invoking user-specific translators" + "\vThis translator appears like a directory in which user names can be" + " looked up, and will start TRANSLATOR to service each resulting node." + " If no pattern occurs in the translator specification, the users's" + " home directory is appended to it instead; TRANSLATOR defaults to" + " " _HURD_SYMLINK "."; + +int +main (int argc, char **argv) +{ + error_t err; + struct stat ul_stat; + mach_port_t bootstrap; + struct usermux mux = + { user_pat: "${user}", home_pat: "${home}", uid_pat: "${uid}" }; + struct netnode root_nn = { mux: &mux }; + + error_t parse_opt (int key, char *arg, struct argp_state *state) + { + switch (key) + { + case OPT_USER_PAT: mux.user_pat = arg; break; + case OPT_HOME_PAT: mux.home_pat = arg; break; + case OPT_UID_PAT: mux.uid_pat = arg; break; + case 'C': bzero (&mux, sizeof mux); break; + + case ARGP_KEY_NO_ARGS: + bzero (&mux, sizeof mux); /* Default doesn't use them; be careful. */ + return argz_create_sep (_HURD_SYMLINK, 0, + &mux.trans_template, &mux.trans_template_len); + case ARGP_KEY_ARGS: + /* Steal the entire tail of arg vector for our own use. */ + return argz_create (state->argv + state->next, + &mux.trans_template, &mux.trans_template_len); + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + struct argp argp = { options, parse_opt, args_doc, doc }; + + /* Parse our command line arguments. */ + argp_parse (&argp, argc, argv, 0, 0, 0); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + netfs_init (); + + /* Create the root node (some attributes initialized below). */ + netfs_root_node = netfs_make_node (&root_nn); + if (! netfs_root_node) + error (5, ENOMEM, "Cannot create root node"); + + err = maptime_map (0, 0, &usermux_maptime); + if (err) + error (6, err, "Cannot map time"); + + /* Handshake with the party trying to start the translator. */ + mux.underlying = netfs_startup (bootstrap, 0); + + /* We inherit various attributes from the node underlying this translator. */ + err = io_stat (mux.underlying, &ul_stat); + if (err) + error (7, err, "Cannot stat underlying node"); + + /* MUX.stat_template contains some fields that are inherited by all nodes + we create. */ + mux.stat_template.st_uid = ul_stat.st_uid; + mux.stat_template.st_gid = ul_stat.st_gid; + mux.stat_template.st_author = ul_stat.st_author; + mux.stat_template.st_fsid = getpid (); + mux.stat_template.st_nlink = 1; + mux.stat_template.st_fstype = FSTYPE_MISC; + + /* We implement symlinks directly, and everything else as a real + translator. */ + if (strcmp (mux.trans_template, _HURD_SYMLINK) == 0) + mux.stat_template.st_mode = S_IFLNK | 0666; + else + mux.stat_template.st_mode = S_IFREG | S_IPTRANS | 0666; + + /* Initialize the root node's stat information. */ + netfs_root_node->nn_stat = mux.stat_template; + netfs_root_node->nn_stat.st_ino = 2; + netfs_root_node->nn_stat.st_mode = + S_IFDIR | (ul_stat.st_mode & ~S_IFMT & ~S_ITRANS); + + fshelp_touch (&netfs_root_node->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME, + usermux_maptime); + + for (;;) /* ?? */ + netfs_server_loop (); +} diff --git a/usermux/usermux.h b/usermux/usermux.h new file mode 100644 index 00000000..0dba4d92 --- /dev/null +++ b/usermux/usermux.h @@ -0,0 +1,99 @@ +/* Multiplexing filesystems by host + + Copyright (C) 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. */ + +#ifndef __USERMUX_H__ +#define __USERMUX_H__ + +#include <hurd/netfs.h> +#include <rwlock.h> +#include <maptime.h> + +struct passwd; + +/* Filenos (aka inode numbers) for user nodes are the uid + this. */ +#define USERMUX_FILENO_UID_OFFSET 10 + +/* Handy source of time. */ +volatile struct mapped_time_value *usermux_maptime; + +/* The state associated with a host multiplexer translator. */ +struct usermux +{ + /* The user nodes in this mux. */ + struct usermux_name *names; + struct rwlock names_lock; + + /* A template argz, which is used to start each user-specific translator + with the user name appropriately added. */ + char *trans_template; + size_t trans_template_len; + + /* What string to replace in TRANS_TEMPLATE with the name of the various + user params; if none occur in the template, the user's home dir is + appended as an additional argument. */ + char *user_pat; /* User name */ + char *home_pat; /* Home directory */ + char *uid_pat; /* Numeric user id */ + + /* Constant fields for user stat entries. */ + struct stat stat_template; + + /* The file that this translator is sitting on top of; we inherit various + characteristics from it. */ + file_t underlying; +}; + +/* The name of a recently looked up user entry. */ +struct usermux_name +{ + const char *name; /* Looked up name. */ + + /* A filesystem node associated with NAME. */ + struct node *node; + + struct usermux_name *next; +}; + +/* The fs specific storage that libnetfs associates with each filesystem + node. */ +struct netnode +{ + /* The mux this node belongs to (the node can either be the mux root, or + one of the users served by it). */ + struct usermux *mux; + + /* For mux nodes, 0, and for leaf nodes, the name under which the node was + looked up. */ + struct usermux_name *name; + + /* The translator associated with node, or if its a symlink, just the link + target. */ + char *trans; + size_t trans_len; +}; + +error_t create_user_node (struct usermux *mux, struct usermux_name *name, + struct passwd *pw, struct node **node); + +#ifndef USERMUX_EI +# define USERMUX_EI extern inline +#endif + +#endif /* __USERMUX_H__ */ |