summaryrefslogtreecommitdiff
path: root/hostmux
diff options
context:
space:
mode:
Diffstat (limited to 'hostmux')
-rw-r--r--hostmux/Makefile30
-rw-r--r--hostmux/hostmux-xinl.c22
-rw-r--r--hostmux/hostmux.c169
-rw-r--r--hostmux/hostmux.h95
-rw-r--r--hostmux/leaf.c124
-rw-r--r--hostmux/mux.c457
-rw-r--r--hostmux/node.c127
-rw-r--r--hostmux/stubs.c145
8 files changed, 1169 insertions, 0 deletions
diff --git a/hostmux/Makefile b/hostmux/Makefile
new file mode 100644
index 00000000..8e100995
--- /dev/null
+++ b/hostmux/Makefile
@@ -0,0 +1,30 @@
+# Makefile for hostmux
+#
+# Copyright (C) 1997, 1999, 2000 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 := hostmux
+makemode := server
+
+target = hostmux
+
+SRCS = hostmux.c mux.c leaf.c node.c stubs.c
+LCLHDRS = hostmux.h
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = netfs fshelp iohelp threads ports ihash shouldbeinlibc
+
+include ../Makeconf
diff --git a/hostmux/hostmux-xinl.c b/hostmux/hostmux-xinl.c
new file mode 100644
index 00000000..4e11968e
--- /dev/null
+++ b/hostmux/hostmux-xinl.c
@@ -0,0 +1,22 @@
+/* Real definitions for extern inline functions in hostmux.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 HOSTMUX_DEFINE_EI
+#include "hostmux.h"
diff --git a/hostmux/hostmux.c b/hostmux/hostmux.c
new file mode 100644
index 00000000..3778613c
--- /dev/null
+++ b/hostmux/hostmux.c
@@ -0,0 +1,169 @@
+/* Multiplexing filesystems by host
+
+ Copyright (C) 1997, 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 <unistd.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <sys/time.h>
+
+#include <version.h>
+
+#include "hostmux.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (hostmux);
+
+char *netfs_server_name = "hostmux";
+char *netfs_server_version = HURD_VERSION;
+int netfs_maxsymlinks = 25;
+
+volatile struct mapped_time_value *hostmux_mapped_time;
+
+#define DEFAULT_HOST_PAT "${host}"
+
+/* Startup options. */
+static const struct argp_option options[] =
+{
+ { "host-pattern", 'H', "PAT", 0,
+ "The string to replace in the translator specification with the hostname;"
+ " if empty, or doesn't occur, the hostname is appended as additional"
+ " argument instead (default `" DEFAULT_HOST_PAT "')" },
+ { 0 }
+};
+static const char args_doc[] = "TRANSLATOR [ARG...]";
+static const char doc[] =
+ "A translator for invoking host-specific translators."
+ "\vThis translator appears like a directory in which hostnames can be"
+ " looked up, and will start TRANSLATOR to service each resulting node.";
+
+/* Return an argz string describing the current options. Fill *ARGZ
+ with a pointer to newly malloced storage holding the list and *LEN
+ to the length of that storage. */
+error_t
+netfs_append_args (char **argz, size_t *argz_len)
+{
+ char buf[80];
+ error_t err = 0;
+ struct hostmux *mux = netfs_root_node->nn->mux;
+
+#define FOPT(fmt, arg) \
+ do { \
+ if (! err) \
+ { \
+ snprintf (buf, sizeof buf, fmt, arg); \
+ err = argz_add (argz, argz_len, buf); \
+ } \
+ } while (0)
+
+ if (strcmp (mux->host_pat, DEFAULT_HOST_PAT) != 0)
+ FOPT ("--host-pattern=%s", mux->host_pat);
+
+ if (! err)
+ err = argz_append (argz, argz_len,
+ mux->trans_template, mux->trans_template_len);
+
+ return err;
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ struct stat ul_stat;
+ mach_port_t bootstrap;
+ struct hostmux mux = { host_pat: DEFAULT_HOST_PAT, next_fileno: 10 };
+ struct netnode root_nn = { mux: &mux };
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'H':
+ mux.host_pat = arg; break;
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ 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, ARGP_IN_ORDER, 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, &hostmux_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;
+
+ /* 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);
+ netfs_root_node->nn_translated = 0;
+
+ /* If the underlying node isn't a directory, propagate read permission to
+ execute permission since we need that for lookups. */
+ if (! S_ISDIR (ul_stat.st_mode))
+ {
+ if (ul_stat.st_mode & S_IRUSR)
+ netfs_root_node->nn_stat.st_mode |= S_IXUSR;
+ if (ul_stat.st_mode & S_IRGRP)
+ netfs_root_node->nn_stat.st_mode |= S_IXGRP;
+ if (ul_stat.st_mode & S_IROTH)
+ netfs_root_node->nn_stat.st_mode |= S_IXOTH;
+ }
+
+ fshelp_touch (&netfs_root_node->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ hostmux_maptime);
+
+ for (;;) /* ?? */
+ netfs_server_loop ();
+}
diff --git a/hostmux/hostmux.h b/hostmux/hostmux.h
new file mode 100644
index 00000000..98ef04ac
--- /dev/null
+++ b/hostmux/hostmux.h
@@ -0,0 +1,95 @@
+/* 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 __HOSTMUX_H__
+#define __HOSTMUX_H__
+
+#include <hurd/netfs.h>
+#include <rwlock.h>
+#include <maptime.h>
+#include <features.h>
+
+#ifdef HOSTMUX_DEFINE_EI
+#define HOSTMUX_EI
+#else
+#define HOSTMUX_EI __extern_inline
+#endif
+
+/* Handy source of time. */
+volatile struct mapped_time_value *hostmux_maptime;
+
+/* The state associated with a host multiplexer translator. */
+struct hostmux
+{
+ /* The host hodes in this mux. */
+ struct hostmux_name *names;
+ struct rwlock names_lock;
+
+ /* The next inode number we'll use; protected by NAMES_LOCK. */
+ ino_t next_fileno;
+
+ /* A template argz, which is used to start each host-specific translator
+ with the host name appropriately added. */
+ char *trans_template;
+ size_t trans_template_len;
+
+ /* What string to replace in TRANS_TEMPLATE with the name of the host; if
+ 0, or it doesn't occur, the host name is appended as an additional
+ argument. */
+ char *host_pat;
+
+ /* Constant fields for host 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 host entry. */
+struct hostmux_name
+{
+ const char *name; /* Looked up name (may be a number). */
+ const char *canon; /* The canonical (fq) host name. */
+
+ /* A filesystem node associated with NAME. If NAME = CANON, then this will
+ refer to a node with a translator for that host, otherwise, the node
+ will be a symbolic link to the canonical name. */
+ struct node *node;
+
+ ino_t fileno; /* The inode number for this entry. */
+
+ struct hostmux_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 hosts served by it). */
+ struct hostmux *mux;
+
+ /* For mux nodes, 0, and for leaf nodes, the name under which the node was
+ looked up. */
+ struct hostmux_name *name;
+};
+
+#endif /* __HOSTMUX_H__ */
diff --git a/hostmux/leaf.c b/hostmux/leaf.c
new file mode 100644
index 00000000..fb53622f
--- /dev/null
+++ b/hostmux/leaf.c
@@ -0,0 +1,124 @@
+/* Hostmux leaf node functions
+
+ Copyright (C) 1997,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 <string.h>
+#include <argz.h>
+
+#include "hostmux.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);
+ memcpy (buf, node->nn->name->canon, node->nn_stat.st_size);
+ fshelp_touch (&node->nn_stat, TOUCH_ATIME, hostmux_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 hostmux, this creates a new translator string by instantiating the
+ global translator template. */
+error_t
+netfs_get_translator (struct node *node, char **argz, size_t *argz_len)
+{
+ if (! node->nn->name)
+ return EINVAL;
+ else
+ {
+ error_t err = 0;
+ unsigned replace_count = 0;
+ struct hostmux *mux = node->nn->mux;
+
+ *argz = 0; /* Initialize return value. */
+ *argz_len = 0;
+
+ /* Return a copy of MUX's translator template, with occurrences of
+ HOST_PAT replaced by the canonical hostname. */
+ err = argz_append (argz, argz_len,
+ mux->trans_template, mux->trans_template_len);
+ if (! err)
+ err = argz_replace (argz, argz_len,
+ mux->host_pat, node->nn->name->canon,
+ &replace_count);
+
+ if (!err && replace_count == 0)
+ /* Default, if no instances of HOST_PAT occur, is to append the
+ hostname. */
+ err = argz_add (argz, argz_len, node->nn->name->canon);
+
+ if (err && *argz_len > 0)
+ free (*argz);
+
+ return err;
+ }
+}
+
+/* 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_host_node (struct hostmux *mux, struct hostmux_name *name,
+ struct node **node)
+{
+ 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 = name->fileno;
+
+ if (strcmp (name->name, name->canon) == 0)
+ /* The real name of the host, make a real node. */
+ {
+ new->nn_stat.st_mode = (S_IFREG | S_IPTRANS | 0666);
+ new->nn_stat.st_size = 0;
+ }
+ else
+ /* An alias for this host, make a symlink instead. */
+ {
+ new->nn_stat.st_mode = (S_IFLNK | 0666);
+ new->nn_stat.st_size = strlen (name->canon);
+ }
+ new->nn_translated = new->nn_stat.st_mode;
+
+ fshelp_touch (&new->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ hostmux_maptime);
+
+ name->node = new;
+
+ *node = new;
+
+ return 0;
+}
diff --git a/hostmux/mux.c b/hostmux/mux.c
new file mode 100644
index 00000000..dd976c57
--- /dev/null
+++ b/hostmux/mux.c
@@ -0,0 +1,457 @@
+/* Root hostmux node
+
+ Copyright (C) 1997,99,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 <stddef.h>
+#include <string.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/mman.h>
+
+#include "hostmux.h"
+
+error_t create_host_node (struct hostmux *mux, struct hostmux_name *name,
+ struct node **node);
+
+/* 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_host (struct hostmux *mux, const char *host,
+ 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 = fshelp_access (&dir->nn_stat, S_IEXEC, user);
+
+ if (! err)
+ {
+ if (strcmp (name, ".") == 0)
+ /* Current directory -- just add an additional reference to DIR and
+ return it. */
+ {
+ netfs_nref (dir);
+ *node = dir;
+ err = 0;
+ }
+ else if (strcmp (name, "..") == 0)
+ err = EAGAIN;
+ else
+ err = lookup_host (dir->nn->mux, name, node);
+
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, hostmux_maptime);
+ }
+
+ mutex_unlock (&dir->lock);
+ if (err)
+ *node = 0;
+ else
+ mutex_lock (&(*node)->lock);
+
+ 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 num_entries, char **data,
+ mach_msg_type_number_t *data_len,
+ vm_size_t max_data_len, int *data_entries)
+{
+ error_t err;
+ int count;
+ size_t size = 0; /* Total size of our return block. */
+ struct hostmux_name *first_name, *nm;
+
+ /* Add the length of a directory entry for NAME to SIZE and return true,
+ unless it would overflow MAX_DATA_LEN or NUM_ENTRIES, in which case
+ return false. */
+ int bump_size (const char *name)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ size_t new_size = size + DIRENT_LEN (strlen (name));
+ if (max_data_len > 0 && new_size > max_data_len)
+ return 0;
+ size = new_size;
+ count++;
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ if (dir->nn->name)
+ return ENOTDIR;
+
+ rwlock_reader_lock (&dir->nn->mux->names_lock);
+
+ /* Find the first entry. */
+ for (first_name = dir->nn->mux->names, count = 2;
+ first_name && first_entry > count;
+ first_name = first_name->next)
+ if (first_name->node)
+ count++;
+
+ count = 0;
+
+ /* Make space for the `.' and `..' entries. */
+ if (first_entry == 0)
+ bump_size (".");
+ if (first_entry <= 1)
+ bump_size ("..");
+
+ /* See how much space we need for the result. */
+ for (nm = first_name; nm; nm = nm->next)
+ if (nm->node && !bump_size (nm->name))
+ break;
+
+ /* Allocate it. */
+ *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = ((void *) *data == (void *) -1) ? errno : 0;
+
+ if (! err)
+ /* Copy out the result. */
+ {
+ char *p = *data;
+
+ int add_dir_entry (const char *name, ino_t fileno, int type)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ struct dirent hdr;
+ size_t name_len = strlen (name);
+ size_t sz = DIRENT_LEN (name_len);
+
+ if (sz > size)
+ return 0;
+ else
+ size -= sz;
+
+ hdr.d_fileno = fileno;
+ hdr.d_reclen = sz;
+ hdr.d_type = type;
+ hdr.d_namlen = name_len;
+
+ memcpy (p, &hdr, DIRENT_NAME_OFFS);
+ strcpy (p + DIRENT_NAME_OFFS, name);
+ p += sz;
+
+ count++;
+
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ *data_len = size;
+ *data_entries = count;
+
+ count = 0;
+
+ /* Add `.' and `..' entries. */
+ if (first_entry == 0)
+ add_dir_entry (".", 2, DT_DIR);
+ if (first_entry <= 1)
+ add_dir_entry ("..", 2, DT_DIR);
+
+ /* Fill in the real directory entries. */
+ for (nm = first_name; nm; nm = nm->next)
+ if (nm->node
+ && !add_dir_entry (nm->name, nm->fileno,
+ strcmp (nm->canon, nm->name) == 0
+ ? DT_REG : DT_LNK))
+ break;
+ }
+
+ rwlock_reader_unlock (&dir->nn->mux->names_lock);
+
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, hostmux_maptime);
+
+ return err;
+}
+
+/* Host lookup. */
+
+/* Free storage allocated consumed by the host mux name NM, but not the node
+ it points to. */
+static void
+free_name (struct hostmux_name *nm)
+{
+ if (nm->name != nm->canon)
+ free ((char *)nm->canon);
+ free ((char *)nm->name);
+ free (nm);
+}
+
+/* See if there's an existing entry for the name HOST, 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 hostmux *mux, const char *host, int purge,
+ struct node **node)
+{
+ struct hostmux_name *nm = mux->names, **prevl = &mux->names;
+
+ while (nm)
+ {
+ struct hostmux_name *next = nm->next;
+
+ if (strcasecmp (host, 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 HOST, and if so, return its
+ node in NODE, with an additional reference, otherwise, create a new node
+ for the host HE as referred to by HOST, and return that instead, with a
+ single reference. The type of node created is either a translator node,
+ if HOST refers to the official name of the host, or a symlink node to the
+ official name, if it doesn't. */
+static error_t
+lookup_addrinfo (struct hostmux *mux, const char *host, struct addrinfo *he,
+ struct node **node)
+{
+ error_t err;
+ struct hostmux_name *nm = malloc (sizeof (struct hostmux_name));
+
+ if (! nm)
+ return ENOMEM;
+
+ nm->name = strdup (host);
+ if (strcmp (host, he->ai_canonname) == 0)
+ nm->canon = nm->name;
+ else
+ nm->canon = strdup (he->ai_canonname);
+
+ err = create_host_node (mux, nm, node);
+ if (err)
+ {
+ free_name (nm);
+ return err;
+ }
+
+ rwlock_writer_lock (&mux->names_lock);
+ if (lookup_cached (mux, host, 1, node))
+ /* An entry for HOST 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->fileno = mux->next_fileno++; /* Now that we hold the lock... */
+ nm->next = mux->names;
+ mux->names = nm;
+ rwlock_writer_unlock (&mux->names_lock);
+ }
+
+ return 0;
+}
+
+/* Lookup the host HOST in MUX, and return the resulting node in NODE, with
+ an additional reference, or an error. */
+static error_t
+lookup_host (struct hostmux *mux, const char *host, struct node **node)
+{
+ int was_cached;
+ int h_err;
+ struct addrinfo *ai;
+ struct addrinfo hints;
+
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = PF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_IP;
+
+ rwlock_reader_lock (&mux->names_lock);
+ was_cached = lookup_cached (mux, host, 0, node);
+ rwlock_reader_unlock (&mux->names_lock);
+
+ if (was_cached)
+ return 0;
+
+ h_err = getaddrinfo (host, NULL, &hints, &ai);
+ if (! h_err)
+ {
+ h_err = lookup_addrinfo (mux, host, ai, node);
+ freeaddrinfo (ai);
+ }
+
+ return h_err;
+}
+
+/* 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 hostmux *mux = node->nn->mux;
+ error_t err = file_chown (mux->underlying, uid, gid);
+
+ if (! err)
+ {
+ struct hostmux_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, hostmux_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 hostmux *mux = node->nn->mux;
+ error_t err = file_chauthor (mux->underlying, author);
+
+ if (! err)
+ {
+ struct hostmux_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, hostmux_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 ((mode & S_IFMT) == 0)
+ mode |= (node->nn_stat.st_mode & S_IFMT);
+ 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, hostmux_maptime);
+ }
+ return err;
+ }
+}
diff --git a/hostmux/node.c b/hostmux/node.c
new file mode 100644
index 00000000..d6edc53e
--- /dev/null
+++ b/hostmux/node.c
@@ -0,0 +1,127 @@
+/* General fs node functions
+
+ Copyright (C) 1997, 1999, 2007 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 "hostmux.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;
+ 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);
+ int flags = TOUCH_CTIME;
+
+ if (! err)
+ {
+ if (mtime)
+ node->nn_stat.st_mtim = *mtime;
+ else
+ flags |= TOUCH_MTIME;
+
+ if (atime)
+ node->nn_stat.st_atim = *atime;
+ else
+ flags |= TOUCH_ATIME;
+
+ fshelp_touch (&node->nn_stat, flags, hostmux_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/hostmux/stubs.c b/hostmux/stubs.c
new file mode 100644
index 00000000..f5dd9b67
--- /dev/null
+++ b/hostmux/stubs.c
@@ -0,0 +1,145 @@
+/* Stub routines for hostmux
+
+ 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 <hurd/netfs.h>
+
+/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */
+error_t
+netfs_attempt_mksymlink (struct iouser *cred, struct node *node, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or
+ S_IFCHR. */
+error_t
+netfs_attempt_mkdev (struct iouser *cred, struct node *node,
+ mode_t type, dev_t indexes)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to set the passive translator record for FILE to ARGZ (of length
+ ARGZLEN) for user CRED. */
+error_t
+netfs_set_translator (struct iouser *cred, struct node *node,
+ char *argz, size_t argzlen)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt a chflags call for the user specified by CRED on node
+ NODE, to change the flags to FLAGS. */
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *node, int flags)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt to set the size of the file NODE (for user CRED) to
+ SIZE bytes long. */
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *node, off_t size)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt to fetch filesystem status information for the remote
+ filesystem, for the user CRED. */
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *node,
+ struct statfs *st)
+{
+ return EOPNOTSUPP;
+}
+
+/* Delete NAME in DIR for USER. */
+error_t
+netfs_attempt_unlink (struct iouser *user, struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+/* Note that in this one call, neither of the specific nodes are locked. */
+error_t
+netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ char *fromname, struct node *todir,
+ char *toname, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to create a new directory named NAME in DIR for USER with mode
+ MODE. */
+error_t
+netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to remove directory named NAME in DIR for USER. */
+error_t
+netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+/* Create a link in DIR with name NAME to FILE for USER. Note that neither
+ DIR nor FILE are locked. If EXCL is set, do not delete the target, but
+ return EEXIST if NAME is already found in DIR. */
+error_t
+netfs_attempt_link (struct iouser *user, struct node *dir,
+ struct node *file, char *name, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to create an anonymous file related to DIR for USER with MODE.
+ Set *NODE to the returned file upon success. No matter what, unlock DIR. */
+error_t
+netfs_attempt_mkfile (struct iouser *user, struct node *dir,
+ mode_t mode, struct node **node)
+{
+ *node = 0;
+ mutex_unlock (&dir->lock);
+ return EOPNOTSUPP;
+}
+
+/* Read from the file NODE for user CRED starting at OFFSET and continuing for
+ up to *LEN bytes. Put the data at DATA. Set *LEN to the amount
+ successfully read upon return. */
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *node,
+ off_t offset, size_t *len, void *data)
+{
+ return EOPNOTSUPP;
+}
+
+/* Write to the file NODE for user CRED starting at OFSET and continuing for up
+ to *LEN bytes from DATA. Set *LEN to the amount seccessfully written upon
+ return. */
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *node,
+ off_t offset, size_t *len, void *data)
+{
+ return EOPNOTSUPP;
+}