aboutsummaryrefslogtreecommitdiff
path: root/nfs
diff options
context:
space:
mode:
Diffstat (limited to 'nfs')
-rw-r--r--nfs/Makefile32
-rw-r--r--nfs/cache.c211
-rw-r--r--nfs/consts.c25
-rw-r--r--nfs/main.c438
-rw-r--r--nfs/mount.c277
-rw-r--r--nfs/mount.h40
-rw-r--r--nfs/name-cache.c305
-rw-r--r--nfs/nfs-spec.h168
-rw-r--r--nfs/nfs.c681
-rw-r--r--nfs/nfs.h204
-rw-r--r--nfs/ops.c1925
-rw-r--r--nfs/rpc.c425
-rw-r--r--nfs/storage-info.c104
13 files changed, 4835 insertions, 0 deletions
diff --git a/nfs/Makefile b/nfs/Makefile
new file mode 100644
index 00000000..d814f67d
--- /dev/null
+++ b/nfs/Makefile
@@ -0,0 +1,32 @@
+# Copyright (C) 1995, 1996, 1997, 2000, 2001, 2008, 2011, 2012 Free Software
+# Foundation, Inc.
+#
+# Written by Michael I. Bushnell.
+#
+# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := nfs
+makemode := server
+
+target = nfs
+SRCS = ops.c rpc.c mount.c nfs.c cache.c consts.c main.c name-cache.c \
+ storage-info.c
+OBJS = $(SRCS:.c=.o)
+HURDLIBS = netfs fshelp iohelp ports ihash shouldbeinlibc
+OTHERLIBS = -lpthread
+
+include ../Makeconf
diff --git a/nfs/cache.c b/nfs/cache.c
new file mode 100644
index 00000000..506b90ff
--- /dev/null
+++ b/nfs/cache.c
@@ -0,0 +1,211 @@
+/* cache.c - Node cache management for NFS client implementation.
+ Copyright (C) 1995, 1996, 1997, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ 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 "nfs.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <netinet/in.h>
+
+/* Hash table containing all the nodes currently active. XXX Was 512,
+ however, a prime is much nicer for the hash function. 509 is nice
+ as not only is it prime, it also keeps the array within a page or
+ two. */
+#define CACHESIZE 509
+static struct node *nodehash [CACHESIZE];
+
+/* Compute and return a hash key for NFS file handle DATA of LEN
+ bytes. */
+static inline int
+hash (int *data, size_t len)
+{
+ unsigned int h = 0;
+ char *cp = (char *)data;
+ int i;
+
+ for (i = 0; i < len; i++)
+ h += cp[i];
+
+ return h % CACHESIZE;
+}
+
+/* Lookup the file handle P (length LEN) in the hash table. If it is
+ not present, initialize a new node structure and insert it into the
+ hash table. Whichever course, a new reference is generated and the
+ node is returned in *NPP; the lock on the node, (*NPP)->LOCK, is
+ held. */
+void
+lookup_fhandle (void *p, size_t len, struct node **npp)
+{
+ struct node *np;
+ struct netnode *nn;
+ int h;
+
+ h = hash (p, len);
+
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ for (np = nodehash[h]; np; np = np->nn->hnext)
+ {
+ if (np->nn->handle.size != len
+ || memcmp (np->nn->handle.data, p, len) != 0)
+ continue;
+
+ np->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+ pthread_mutex_lock (&np->lock);
+ *npp = np;
+ return;
+ }
+
+ /* Could not find it */
+ nn = malloc (sizeof (struct netnode));
+ assert (nn);
+
+ nn->handle.size = len;
+ memcpy (nn->handle.data, p, len);
+ nn->stat_updated = 0;
+ nn->dtrans = NOT_POSSIBLE;
+ nn->dead_dir = 0;
+ nn->dead_name = 0;
+
+ np = netfs_make_node (nn);
+ pthread_mutex_lock (&np->lock);
+ nn->hnext = nodehash[h];
+ if (nn->hnext)
+ nn->hnext->nn->hprevp = &nn->hnext;
+ nn->hprevp = &nodehash[h];
+ nodehash[h] = np;
+
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ *npp = np;
+}
+
+/* Package holding args to forked_node_delete. */
+struct fnd
+{
+ struct node *dir;
+ char *name;
+};
+
+/* Worker function to delete nodes that don't have any more local
+ references or links. */
+void *
+forked_node_delete (void *arg)
+{
+ struct fnd *args = arg;
+
+ pthread_mutex_lock (&args->dir->lock);
+ netfs_attempt_unlink ((struct iouser *)-1, args->dir, args->name);
+ netfs_nput (args->dir);
+ free (args->name);
+ free (args);
+ return 0;
+};
+
+/* Called by libnetfs when node NP has no more references. (See
+ <hurd/libnetfs.h> for details. Just clear its local state and
+ remove it from the hash table. Called and expected to leave with
+ NETFS_NODE_REFCNT_LOCK held. */
+void
+netfs_node_norefs (struct node *np)
+{
+ if (np->nn->dead_dir)
+ {
+ struct fnd *args;
+ pthread_t thread;
+ error_t err;
+
+ args = malloc (sizeof (struct fnd));
+ assert (args);
+
+ np->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ args->dir = np->nn->dead_dir;
+ args->name = np->nn->dead_name;
+ np->nn->dead_dir = 0;
+ np->nn->dead_name = 0;
+ netfs_nput (np);
+
+ /* Do this in a separate thread so that we don't wait for it; it
+ acquires a lock on the dir, which we are not allowed to
+ do. */
+ err = pthread_create (&thread, NULL, forked_node_delete, args);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ /* Caller expects us to leave this locked... */
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ }
+ else
+ {
+ *np->nn->hprevp = np->nn->hnext;
+ if (np->nn->hnext)
+ np->nn->hnext->nn->hprevp = np->nn->hprevp;
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+ free (np->nn);
+ free (np);
+ }
+}
+
+/* Change the file handle used for node NP to be the handle at P.
+ Make sure the hash table stays up to date. Return the address
+ after the handle. The lock on the node should be held. */
+int *
+recache_handle (int *p, struct node *np)
+{
+ int h;
+ size_t len;
+
+ if (protocol_version == 2)
+ len = NFS2_FHSIZE;
+ else
+ {
+ len = ntohl (*p);
+ p++;
+ }
+
+ /* Unlink it */
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ *np->nn->hprevp = np->nn->hnext;
+ if (np->nn->hnext)
+ np->nn->hnext->nn->hprevp = np->nn->hprevp;
+
+ /* Change the name */
+ np->nn->handle.size = len;
+ memcpy (np->nn->handle.data, p, len);
+
+ /* Reinsert it */
+ h = hash (p, len);
+ np->nn->hnext = nodehash[h];
+ if (np->nn->hnext)
+ np->nn->hnext->nn->hprevp = &np->nn->hnext;
+ np->nn->hprevp = &nodehash[h];
+
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+ return p + len / sizeof (int);
+}
diff --git a/nfs/consts.c b/nfs/consts.c
new file mode 100644
index 00000000..9146615b
--- /dev/null
+++ b/nfs/consts.c
@@ -0,0 +1,25 @@
+/* Definition of constants required by libnetfs
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ 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 "nfs.h"
+
+/* Maximum number of times to walk through symlinks before returning
+ ELOOP. See <hurd/libnetfs.h> for details. */
+int netfs_maxsymlinks = 8;
diff --git a/nfs/main.c b/nfs/main.c
new file mode 100644
index 00000000..cd1c29a0
--- /dev/null
+++ b/nfs/main.c
@@ -0,0 +1,438 @@
+/*
+ Copyright (C) 1996, 1997, 2002 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ 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>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <device/device.h>
+#include "nfs.h"
+#include <netinet/in.h>
+#include <unistd.h>
+#include <string.h>
+#include <maptime.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <version.h>
+
+char *netfs_server_name = "nfs";
+char *netfs_server_version = HURD_VERSION;
+
+extern char *localhost ();
+
+/* Default number of times to retry RPCs when mounted soft. */
+#define DEFAULT_SOFT_RETRIES 3
+
+/* Default number of seconds to timeout cached stat information. */
+#define DEFAULT_STAT_TIMEOUT 3
+
+/* Default number of seconds to timeout cached file contents. */
+#define DEFAULT_CACHE_TIMEOUT 3
+
+/* Default number of seconds to timeout cache positive dir hits. */
+#define DEFAULT_NAME_CACHE_TIMEOUT 3
+
+/* Default number of seconds to timeout cache negative dir hits. */
+#define DEFAULT_NAME_CACHE_NEG_TIMEOUT 3
+
+/* Default maximum number of bytes to read at once. */
+#define DEFAULT_READ_SIZE 8192
+
+/* Default maximum number of bytes to write at once. */
+#define DEFAULT_WRITE_SIZE 8192
+
+
+/* Number of seconds to timeout cached stat information. */
+int stat_timeout = DEFAULT_STAT_TIMEOUT;
+
+/* Number of seconds to timeout cached file contents. */
+int cache_timeout = DEFAULT_CACHE_TIMEOUT;
+
+/* Number of seconds to timeout cached positive dir hits. */
+int name_cache_timeout = DEFAULT_NAME_CACHE_TIMEOUT;
+
+/* Number of seconds to timeout cached negative dir hits. */
+int name_cache_neg_timeout = DEFAULT_NAME_CACHE_NEG_TIMEOUT;
+
+/* Number of seconds to wait for first retransmission of an RPC. */
+int initial_transmit_timeout = 1;
+
+/* Maximum number of seconds to wait between retransmission of RPCs. */
+int max_transmit_timeout = 30;
+
+/* Maximum number of retries to send when mounted soft. */
+int soft_retries = DEFAULT_SOFT_RETRIES;
+
+/* True iff we are mounted soft. */
+int mounted_soft = 0;
+
+/* Maximum number of bytes to read at once. */
+int read_size = DEFAULT_READ_SIZE;
+
+/* Maximum number of bytes to write at once. */
+int write_size = DEFAULT_WRITE_SIZE;
+
+#define OPT_SOFT 's'
+#define OPT_HARD 'h'
+#define OPT_RSIZE 'R'
+#define OPT_WSIZE 'W'
+#define OPT_STAT_TO -2
+#define OPT_CACHE_TO -3
+#define OPT_INIT_TR_TO -4
+#define OPT_MAX_TR_TO -5
+#define OPT_MNT_PORT -6
+#define OPT_MNT_PORT_D -7
+#define OPT_NFS_PORT -8
+#define OPT_NFS_PORT_D -9
+#define OPT_HOLD -10
+#define OPT_MNT_PROG -11
+#define OPT_NFS_PROG -12
+#define OPT_PMAP_PORT -13
+#define OPT_NCACHE_TO -14
+#define OPT_NCACHE_NEG_TO -15
+
+/* Return a string corresponding to the printed rep of DEFAULT_what */
+#define ___D(what) #what
+#define __D(what) ___D(what)
+#define _D(what) __D(DEFAULT_ ## what)
+
+const char *argp_program_version = STANDARD_HURD_VERSION (nfs);
+
+/* Options usable both at startup and at runtime. */
+static const struct argp_option common_options[] =
+{
+ {0,0,0,0,0,1},
+ {"soft", OPT_SOFT, "RETRIES", OPTION_ARG_OPTIONAL,
+ "File system requests will eventually fail, after RETRIES tries"
+ " (default " _D(SOFT_RETRIES) ")" },
+ {"hard", OPT_HARD, 0, 0,
+ "Retry file systems requests until they succeed"},
+
+ {0,0,0,0,0,2},
+ {"read-size", OPT_RSIZE, "BYTES", 0,
+ "Max packet size for reads (default " _D(READ_SIZE) ")"},
+ {"rsize",0,0,OPTION_ALIAS},
+ {"write-size", OPT_WSIZE, "BYTES", 0,
+ "Max packet size for writes (default " _D(WRITE_SIZE)")"},
+ {"wsize",0,0,OPTION_ALIAS},
+
+ {0,0,0,0,"Timeouts:",3},
+ {"stat-timeout", OPT_STAT_TO, "SEC", 0,
+ "Timeout for cached stat information (default " _D(STAT_TIMEOUT) ")"},
+ {"cache-timeout", OPT_CACHE_TO, "SEC", 0,
+ "Timeout for cached file data (default " _D(CACHE_TIMEOUT) ")"},
+ {"name-cache-timeout", OPT_NCACHE_TO, "SEC", 0,
+ "Timeout for positive directory cache entries (default "
+ _D(NAME_CACHE_TIMEOUT) ")"},
+ {"name-cache-neg-timeout", OPT_NCACHE_NEG_TO, "SEC", 0,
+ "Timeout for negative directory cache entires (default "
+ _D(NAME_CACHE_NEG_TIMEOUT) ")"},
+ {"init-transmit-timeout", OPT_INIT_TR_TO,"SEC", 0},
+ {"max-transmit-timeout", OPT_MAX_TR_TO, "SEC", 0},
+
+ {0}
+};
+
+static error_t
+parse_common_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case OPT_SOFT:
+ mounted_soft = 1;
+ if (arg)
+ soft_retries = atoi (arg);
+ break;
+ case OPT_HARD:
+ mounted_soft = 0;
+ break;
+
+ case OPT_RSIZE: read_size = atoi (arg); break;
+ case OPT_WSIZE: write_size = atoi (arg); break;
+
+ case OPT_STAT_TO: stat_timeout = atoi (arg); break;
+ case OPT_CACHE_TO: cache_timeout = atoi (arg); break;
+ case OPT_INIT_TR_TO: initial_transmit_timeout = atoi (arg); break;
+ case OPT_MAX_TR_TO: max_transmit_timeout = atoi (arg); break;
+ case OPT_NCACHE_TO: name_cache_timeout = atoi (arg); break;
+ case OPT_NCACHE_NEG_TO: name_cache_neg_timeout = atoi (arg); break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Options usable only at startup. */
+static const struct argp_option startup_options[] = {
+ {0,0,0,0,"Server specification:",10},
+ {"mount-port", OPT_MNT_PORT, "PORT", 0,
+ "Port for mount server"},
+ {"default-mount-port", OPT_MNT_PORT_D,"PORT", 0,
+ "Port for mount server, if none can be found automatically"},
+ {"mount-program", OPT_MNT_PROG, "ID[.VERS]"},
+
+ {"nfs-port", OPT_NFS_PORT, "PORT", 0,
+ "Port for nfs operations"},
+ {"default-nfs-port", OPT_NFS_PORT_D,"PORT", 0,
+ "Port for nfs operations, if none can be found automatically"},
+ {"nfs-program", OPT_NFS_PROG, "ID[.VERS]"},
+
+ {"pmap-port", OPT_PMAP_PORT, "SVC|PORT"},
+
+ {"hold", OPT_HOLD, 0, OPTION_HIDDEN}, /* */
+ { 0 }
+};
+static char *args_doc = "REMOTE_FS [HOST]";
+static char *doc = "Hurd nfs translator"
+"\vIf HOST is not specified, an attempt is made to extract"
+" it from REMOTE_FS using either the `HOST:FS' or `FS@HOST' notations.";
+
+static const struct argp_child
+runtime_argp_children[] = { {&netfs_std_runtime_argp}, {0} };
+static struct argp
+runtime_argp = { common_options, parse_common_opt, 0, 0,
+ runtime_argp_children };
+
+/* Used by netfs_set_options to handle runtime option parsing. */
+struct argp *netfs_runtime_argp = &runtime_argp;
+
+/* Where to find the remote filesystem. */
+static char *remote_fs; /* = 0; */
+static char *host; /* = 0; */
+
+/* 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;
+
+#define FOPT(fmt, arg) \
+ do { \
+ if (! err) \
+ { \
+ snprintf (buf, sizeof buf, fmt, arg); \
+ err = argz_add (argz, argz_len, buf); \
+ } \
+ } while (0)
+
+ if (mounted_soft)
+ FOPT ("--soft=%d", soft_retries);
+ else
+ err = argz_add (argz, argz_len, "--hard");
+
+ FOPT ("--read-size=%d", read_size);
+ FOPT ("--write-size=%d", write_size);
+
+ FOPT ("--stat-timeout=%d", stat_timeout);
+ FOPT ("--cache-timeout=%d", cache_timeout);
+ FOPT ("--init-transmit-timeout=%d", initial_transmit_timeout);
+ FOPT ("--max-transmit-timeout=%d", max_transmit_timeout);
+ FOPT ("--name-cache-timeout=%d", name_cache_timeout);
+ FOPT ("--name-cache-neg-timeout=%d", name_cache_neg_timeout);
+
+ if (! err)
+ err = netfs_append_std_options (argz, argz_len);
+
+ if (! err)
+ {
+ char *fs;
+ if (asprintf (&fs, "%s:%s", host, remote_fs))
+ {
+ err = argz_add (argz, argz_len, fs);
+ free (fs);
+ }
+ else
+ err = ENOMEM;
+ }
+
+ return err;
+}
+
+/* The user may define this function. The function must set source to
+ the source of CRED. The function may return an EOPNOTSUPP to
+ indicate that the concept of a source device is not applicable. The
+ default function always returns EOPNOTSUPP. */
+error_t
+netfs_get_source (struct protid *cred, char *source, size_t source_len)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+
+ snprintf (source, source_len, "%s:%s", host, remote_fs);
+ return 0;
+}
+
+/* Extract the host and remote filesystem names from SPEC, which should use
+ either HOST:FS or FS@HOST notation. Returns the malloced storage into
+ which both REMOTE_FS and HOST point, or 0 if SPEC is invalid. */
+static char *
+extract_nfs_args (char *spec, char **remote_fs, char **host)
+{
+ char *sep;
+
+ spec = strdup (spec); /* So we can trash it. */
+ if (! spec)
+ return NULL;
+
+ sep = index (spec, ':');
+ if (sep)
+ {
+ *sep++ = '\0';
+ *host = spec;
+ *remote_fs = sep;
+ return spec;
+ }
+
+ sep = index (spec, '@');
+ if (sep)
+ {
+ *sep++ = '\0';
+ *host = sep;
+ *remote_fs = spec;
+ return spec;
+ }
+
+ free (spec);
+
+ return 0;
+}
+
+static error_t
+parse_startup_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case OPT_MNT_PORT:
+ mount_port_override = 1;
+ /* fall through */
+ case OPT_MNT_PORT_D:
+ mount_port = atoi (arg);
+ break;
+
+ case OPT_NFS_PORT:
+ nfs_port_override = 1;
+ /* fall through */
+ case OPT_NFS_PORT_D:
+ nfs_port = atoi (arg);
+ break;
+
+ case ARGP_KEY_ARG:
+ if (state->arg_num == 0)
+ remote_fs = arg;
+ else if (state->arg_num == 1)
+ host = arg;
+ else
+ return ARGP_ERR_UNKNOWN;
+ break;
+
+ case ARGP_KEY_END:
+ if (!host && !extract_nfs_args (remote_fs, &remote_fs, &host))
+ argp_error (state, "No HOST specified");
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_error (state, "No REMOTE_FS specified");
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+/* NFS client main program */
+int
+main (int argc, char **argv)
+{
+ pthread_t thread;
+ error_t err;
+
+ struct argp common_argp = { common_options, parse_common_opt };
+ const struct argp_child argp_children[] =
+ { {&common_argp}, {&netfs_std_startup_argp}, {0} };
+ struct argp argp =
+ { startup_options, parse_startup_opt, args_doc, doc, argp_children };
+ mach_port_t bootstrap;
+ struct sockaddr_in addr;
+ int ret;
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ netfs_init ();
+
+ main_udp_socket = socket (PF_INET, SOCK_DGRAM, 0);
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = htons (IPPORT_RESERVED);
+ do
+ {
+ addr.sin_port = htons (ntohs (addr.sin_port) - 1);
+ ret = bind (main_udp_socket, (struct sockaddr *)&addr,
+ sizeof (struct sockaddr_in));
+ if (ret == -1 && errno == EACCES)
+ {
+ /* We aren't allowed privileged ports; no matter;
+ let the server deny us later if it wants. */
+ ret = 0;
+ break;
+ }
+ }
+ while ((ret == -1) && (errno == EADDRINUSE));
+ if (ret == -1)
+ error (1, errno, "binding main udp socket");
+
+ err = maptime_map (0, 0, &mapped_time);
+ if (err)
+ error (2, err, "mapping time");
+
+ err = pthread_create (&thread, NULL, timeout_service_thread, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+ err = pthread_create (&thread, NULL, rpc_receive_thread, NULL);
+ if (!err)
+ pthread_detach (thread);
+ else
+ {
+ errno = err;
+ perror ("pthread_create");
+ }
+
+ hostname = localhost ();
+
+ netfs_root_node = mount_root (remote_fs, host);
+
+ if (!netfs_root_node)
+ exit (1);
+
+ netfs_startup (bootstrap, 0);
+
+ for (;;)
+ netfs_server_loop ();
+}
diff --git a/nfs/mount.c b/nfs/mount.c
new file mode 100644
index 00000000..29b203aa
--- /dev/null
+++ b/nfs/mount.c
@@ -0,0 +1,277 @@
+/*
+ Copyright (C) 1995,96,97,98,2001,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ 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 malloc a_byte_for_every_bozotic_sun_lossage_and_youd_need_a_lotta_ram
+#include <rpc/types.h>
+#undef TRUE /* Get rid of sun defs. */
+#undef FALSE
+#undef malloc
+#include <rpc/pmap_prot.h>
+#include <errno.h>
+#include <error.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+#include "nfs.h"
+#include "mount.h"
+
+/* Service name for portmapper */
+char *pmap_service_name = "sunrpc";
+
+/* Fallback port number for portmapper */
+short pmap_service_number = PMAPPORT;
+
+/* RPC program for mount server. */
+int mount_program = MOUNTPROG;
+
+/* RPC version for mount server. */
+int mount_version = MOUNTVERS;
+
+/* Fallback port number for mount server. */
+short mount_port = 0;
+
+/* True iff MOUNT_PORT should be used even if portmapper present. */
+int mount_port_override = 0;
+
+/* RPC program number for NFS server. */
+int nfs_program = NFS_PROGRAM;
+
+/* RPC version number for NFS server. */
+int nfs_version = NFS_VERSION;
+
+/* Fallback port number for NFS server. */
+short nfs_port = NFS_PORT;
+
+/* True iff NFS_PORT should be used even if portmapper present. */
+int nfs_port_override = 0;
+
+/* Host name and port number we actually decided to use. */
+const char *mounted_hostname;
+uint16_t mounted_nfs_port; /* host order */
+
+int protocol_version = 2;
+
+/* Set up an RPC for procedure PROCNUM for talking to the portmapper.
+ Allocate storage with malloc and point *BUF at it; caller must free
+ this when done. Return the address where the args for the
+ procedure should be placed. */
+static int *
+pmap_initialize_rpc (int procnum, void **buf)
+{
+ return initialize_rpc (PMAPPROG, PMAPVERS, procnum, 0, buf, 0, 0, -1);
+}
+
+/* Set up an RPC for procedure PROCNUM for talking to the mount
+ server. Allocate storage with malloc and point *BUF at it; caller
+ must free this when done. Return the address where the args for
+ the procedure should be placed. */
+static int *
+mount_initialize_rpc (int procnum, void **buf)
+{
+ return initialize_rpc (MOUNTPROG, MOUNTVERS, procnum, 0, buf, 0, 0, -1);
+}
+
+/* Using the mount protocol, lookup NAME at host HOST.
+ Return a node for it or null for an error. If an
+ error occurs, a message is automatically sent to stderr. */
+struct node *
+mount_root (char *name, char *host)
+{
+ struct sockaddr_in addr;
+ struct hostent *h;
+ int *p;
+ void *rpcbuf;
+ int port;
+ error_t err;
+ struct node *np;
+ short pmapport;
+
+ /* Lookup the portmapper port number */
+ if (pmap_service_name)
+ {
+ struct servent *s;
+
+ /* XXX This will always fail! pmap_service_name will always be "sunrpc"
+ What should pmap_service_name really be? By definition the second
+ argument is either "tcp" or "udp" Thus, is this backwards
+ (as service_name suggests)? If so, should it read:
+ s = getservbyname (pmap_service_name, "udp");
+ or is there something I am missing here? */
+ s = getservbyname ("sunrpc", pmap_service_name);
+ if (s)
+ pmapport = s->s_port;
+ else
+ pmapport = htons (pmap_service_number);
+ }
+ else
+ pmapport = htons (pmap_service_number);
+
+ /* Lookup the host */
+ h = gethostbyname (host);
+ if (!h)
+ {
+ herror (host);
+ return 0;
+ }
+
+ addr.sin_family = h->h_addrtype;
+ memcpy (&addr.sin_addr, h->h_addr_list[0], h->h_length);
+ addr.sin_port = pmapport;
+
+ if (mount_port_override)
+ addr.sin_port = htons (mount_port);
+ else
+ {
+ /* Formulate and send a PMAPPROC_GETPORT request
+ to lookup the mount program on the server. */
+ if (connect (main_udp_socket, (struct sockaddr *)&addr,
+ sizeof (struct sockaddr_in)) == -1)
+ {
+ error (0, errno, "server mount program");
+ return 0;
+ }
+
+ p = pmap_initialize_rpc (PMAPPROC_GETPORT, &rpcbuf);
+ if (! p)
+ {
+ error (0, errno, "creating rpc packet");
+ return 0;
+ }
+
+ *(p++) = htonl (MOUNTPROG);
+ *(p++) = htonl (MOUNTVERS);
+ *(p++) = htonl (IPPROTO_UDP);
+ *(p++) = htonl (0);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ port = ntohl (*p);
+ p++;
+ addr.sin_port = htons (port);
+ }
+ else if (mount_port)
+ addr.sin_port = htons (mount_port);
+ else
+ {
+ error (0, err, "portmap of mount");
+ goto error_with_rpcbuf;
+ }
+ free (rpcbuf);
+ }
+
+ /* Now, talking to the mount program, fetch a file handle
+ for the root. */
+ if (connect (main_udp_socket, (struct sockaddr *) &addr,
+ sizeof (struct sockaddr_in)) == -1)
+ {
+ error (0, errno, "connect");
+ goto error_with_rpcbuf;
+ }
+
+ p = mount_initialize_rpc (MOUNTPROC_MNT, &rpcbuf);
+ if (! p)
+ {
+ error (0, errno, "rpc");
+ goto error_with_rpcbuf;
+ }
+
+ p = xdr_encode_string (p, name);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (err)
+ {
+ error (0, err, "%s", name);
+ goto error_with_rpcbuf;
+ }
+ /* XXX Protocol spec says this should be a "unix error code"; we'll
+ pretend that an NFS error code is what's meant; the numbers match
+ anyhow. */
+ err = nfs_error_trans (htonl (*p));
+ p++;
+ if (err)
+ {
+ error (0, err, "%s", name);
+ goto error_with_rpcbuf;
+ }
+
+ /* Create the node for root */
+ xdr_decode_fhandle (p, &np);
+ free (rpcbuf);
+ pthread_mutex_unlock (&np->lock);
+
+ if (nfs_port_override)
+ port = nfs_port;
+ else
+ {
+ /* Send another PMAPPROC_GETPORT request to lookup the nfs server. */
+ addr.sin_port = pmapport;
+ if (connect (main_udp_socket, (struct sockaddr *) &addr,
+ sizeof (struct sockaddr_in)) == -1)
+ {
+ error (0, errno, "connect");
+ return 0;
+ }
+
+ p = pmap_initialize_rpc (PMAPPROC_GETPORT, &rpcbuf);
+ if (! p)
+ {
+ error (0, errno, "rpc");
+ goto error_with_rpcbuf;
+ }
+ *(p++) = htonl (NFS_PROGRAM);
+ *(p++) = htonl (NFS_VERSION);
+ *(p++) = htonl (IPPROTO_UDP);
+ *(p++) = htonl (0);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ port = ntohl (*p);
+ p++;
+ }
+ else if (nfs_port)
+ port = nfs_port;
+ else
+ {
+ error (0, err, "portmap of nfs server");
+ goto error_with_rpcbuf;
+ }
+ free (rpcbuf);
+ }
+
+ addr.sin_port = htons (port);
+ if (connect (main_udp_socket, (struct sockaddr *) &addr,
+ sizeof (struct sockaddr_in)) == -1)
+ {
+ error (0, errno, "connect");
+ return 0;
+ }
+
+ mounted_hostname = host;
+ mounted_nfs_port = port;
+
+ return np;
+
+error_with_rpcbuf:
+ free (rpcbuf);
+
+ return 0;
+}
diff --git a/nfs/mount.h b/nfs/mount.h
new file mode 100644
index 00000000..4cb62a83
--- /dev/null
+++ b/nfs/mount.h
@@ -0,0 +1,40 @@
+/* Manifest constants describing the mount protocol
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ 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. */
+
+/* These constants define the RPC mount protocol; see RFC 1094. */
+
+#ifndef NFS_MOUNT_H
+#define NFS_MOUNT_H
+
+#define MOUNTPROG 100005
+#define MOUNTVERS 1
+
+/* Obnoxious arbitrary limits */
+#define MOUNT_MNTPATHLEN 1024
+#define MOUNT_MNTNAMLEN 255
+
+#define MOUNTPROC_NULL 0
+#define MOUNTPROC_MNT 1
+#define MOUNTPROC_DUMP 2
+#define MOUNTPROC_UMNT 3
+#define MOUNTPROC_UMNTALL 4
+#define MOUNTPROC_EXPORT 5
+
+#endif /* NFS_MOUNT_H */
diff --git a/nfs/name-cache.c b/nfs/name-cache.c
new file mode 100644
index 00000000..7553d7c0
--- /dev/null
+++ b/nfs/name-cache.c
@@ -0,0 +1,305 @@
+/* Directory name lookup caching
+
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG, & Miles Bader.
+
+ 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 "nfs.h"
+#include <string.h>
+#include <cacheq.h>
+
+
+/* Maximum number of names to cache at any given time */
+#define MAXCACHE 200
+
+/* Maximum length of file name we bother caching */
+#define CACHE_NAME_LEN 100
+
+/* Cache entry */
+struct lookup_cache
+{
+ struct cacheq_hdr hdr;
+
+ /* File handles and lengths for cache entries. 0 for NODE_CACHE_LEN
+ means a */
+ char dir_cache_fh[NFS3_FHSIZE];
+ size_t dir_cache_len;
+
+ /* Zero means a `negative' entry -- recording that there's
+ definitely no node with this name. */
+ struct node *np;
+
+ /* Name of the node NODE_CACHE_ID in the directory DIR_CACHE_ID. Entries
+ with names too long to fit in this buffer aren't cached at all. */
+ char name[CACHE_NAME_LEN];
+
+ /* Strlen of NAME. If this is zero, it's an unused entry. */
+ size_t name_len;
+
+ /* Time that this cache entry was created. */
+ time_t cache_stamp;
+
+ /* XXX */
+ int stati;
+};
+
+/* The contents of the cache in no particular order */
+static struct cacheq lookup_cache = { sizeof (struct lookup_cache) };
+
+static pthread_spinlock_t cache_lock = PTHREAD_SPINLOCK_INITIALIZER;
+
+/* Buffer to hold statistics */
+static struct stats
+{
+ long pos_hits;
+ long neg_hits;
+ long miss;
+ long fetch_errors;
+} statistics;
+
+#define PARTIAL_THRESH 100
+#define NPARTIALS (MAXCACHE / PARTIAL_THRESH)
+struct stats partial_stats [NPARTIALS];
+
+
+/* If there's an entry for NAME, of length NAME_LEN, in directory DIR in the
+ cache, return its entry, otherwise 0. CACHE_LOCK must be held. */
+static struct lookup_cache *
+find_cache (char *dir, size_t len, const char *name, size_t name_len)
+{
+ struct lookup_cache *c;
+ int i;
+
+ /* Search the list. All unused entries are contiguous at the end of the
+ list, so we can stop searching when we see the first one. */
+ for (i = 0, c = lookup_cache.mru;
+ c && c->name_len;
+ c = c->hdr.next, i++)
+ if (c->name_len == name_len
+ && c->dir_cache_len == len
+ && c->name[0] == name[0]
+ && memcmp (c->dir_cache_fh, dir, len) == 0
+ && strcmp (c->name, name) == 0)
+ {
+ c->stati = i / PARTIAL_THRESH;
+ return c;
+ }
+
+ return 0;
+}
+
+/* Node NP has just been found in DIR with NAME. If NP is null, this
+ name has been confirmed as absent in the directory. DIR is the
+ fhandle of the directory and LEN is its length. */
+void
+enter_lookup_cache (char *dir, size_t len, struct node *np, char *name)
+{
+ struct lookup_cache *c;
+ size_t name_len = strlen (name);
+
+ if (name_len > CACHE_NAME_LEN - 1)
+ return;
+
+ pthread_spin_lock (&cache_lock);
+
+ if (lookup_cache.length == 0)
+ /* There should always be an lru_cache; this being zero means that the
+ cache hasn't been initialized yet. Do so. */
+ cacheq_set_length (&lookup_cache, MAXCACHE);
+
+ /* See if there's an old entry for NAME in DIR. If not, replace the least
+ recently used entry. */
+ c = find_cache (dir, len, name, name_len) ?: lookup_cache.lru;
+
+ /* Fill C with the new entry. */
+ memcpy (c->dir_cache_fh, dir, len);
+ c->dir_cache_len = len;
+ if (c->np)
+ netfs_nrele (c->np);
+ c->np = np;
+ if (c->np)
+ netfs_nref (c->np);
+ strcpy (c->name, name);
+ c->name_len = name_len;
+ c->cache_stamp = mapped_time->seconds;
+
+ /* Now C becomes the MRU entry! */
+ cacheq_make_mru (&lookup_cache, c);
+
+ pthread_spin_unlock (&cache_lock);
+}
+
+/* Purge all references in the cache to NAME within directory DIR. */
+void
+purge_lookup_cache (struct node *dp, char *name, size_t namelen)
+{
+ struct lookup_cache *c, *next;
+
+ pthread_spin_lock (&cache_lock);
+ for (c = lookup_cache.mru; c; c = next)
+ {
+ /* Save C->hdr.next, since we may move C from this position. */
+ next = c->hdr.next;
+
+ if (c->name_len == namelen
+ && c->dir_cache_len == dp->nn->handle.size
+ && memcmp (c->dir_cache_fh, dp->nn->handle.data,
+ c->dir_cache_len) == 0
+ && strcmp (c->name, name) == 0)
+ {
+ if (c->np)
+ netfs_nrele (c->np);
+ c->name_len = 0;
+ c->np = 0;
+ cacheq_make_lru (&lookup_cache, c); /* Use C as the next free
+ entry. */
+ }
+ }
+ pthread_spin_unlock (&cache_lock);
+}
+
+/* Purge all references in the cache to node NP. */
+void
+purge_lookup_cache_node (struct node *np)
+{
+ struct lookup_cache *c, *next;
+
+ pthread_spin_lock (&cache_lock);
+ for (c = lookup_cache.mru; c; c = next)
+ {
+ next = c->hdr.next;
+
+ if (c->np == np)
+ {
+ netfs_nrele (c->np);
+ c->name_len = 0;
+ c->np = 0;
+ cacheq_make_lru (&lookup_cache, c);
+ }
+ }
+ pthread_spin_unlock (&cache_lock);
+}
+
+
+
+/* Register a negative hit for an entry in the Nth stat class */
+void
+register_neg_hit (int n)
+{
+ int i;
+
+ statistics.neg_hits++;
+
+ for (i = 0; i < n; i++)
+ partial_stats[i].miss++;
+ for (; i < NPARTIALS; i++)
+ partial_stats[i].neg_hits++;
+}
+
+/* Register a positive hit for an entry in the Nth stat class */
+void
+register_pos_hit (int n)
+{
+ int i;
+
+ statistics.pos_hits++;
+
+ for (i = 0; i < n; i++)
+ partial_stats[i].miss++;
+ for (; i < NPARTIALS; i++)
+ partial_stats[i].pos_hits++;
+}
+
+/* Register a miss */
+void
+register_miss ()
+{
+ int i;
+
+ statistics.miss++;
+ for (i = 0; i < NPARTIALS; i++)
+ partial_stats[i].miss++;
+}
+
+
+
+/* Scan the cache looking for NAME inside DIR. If we know nothing
+ about the entry, then return 0. If the entry is confirmed to not
+ exist, then return -1. Otherwise, return NP for the entry, with
+ a newly allocated reference. For all return values other than 0,
+ unlock DIR->LOCK before returning. For positive hits, lock the
+ returned node. */
+struct node *
+check_lookup_cache (struct node *dir, char *name)
+{
+ struct lookup_cache *c;
+
+ pthread_spin_lock (&cache_lock);
+
+ c = find_cache (dir->nn->handle.data, dir->nn->handle.size,
+ name, strlen (name));
+ if (c)
+ {
+ int timeout = c->np
+ ? name_cache_timeout
+ : name_cache_neg_timeout;
+
+ /* Make sure the entry is still usable; if not, zap it now. */
+ if (mapped_time->seconds - c->cache_stamp >= timeout)
+ {
+ register_neg_hit (c->stati);
+ if (c->np)
+ netfs_nrele (c->np);
+ c->name_len = 0;
+ c->np = 0;
+ cacheq_make_lru (&lookup_cache, c);
+ pthread_spin_unlock (&cache_lock);
+ return 0;
+ }
+
+ cacheq_make_mru (&lookup_cache, c); /* Record C as recently used. */
+
+ if (c->np == 0)
+ /* A negative cache entry. */
+ {
+ register_neg_hit (c->stati);
+ pthread_spin_unlock (&cache_lock);
+ pthread_mutex_unlock (&dir->lock);
+ return (struct node *)-1;
+ }
+ else
+ {
+ struct node *np;
+
+ np = c->np;
+ netfs_nref (np);
+ register_pos_hit (c->stati);
+ pthread_spin_unlock (&cache_lock);
+
+ pthread_mutex_unlock (&dir->lock);
+ pthread_mutex_lock (&np->lock);
+
+ return np;
+ }
+ }
+
+ register_miss ();
+ pthread_spin_unlock (&cache_lock);
+
+ return 0;
+}
diff --git a/nfs/nfs-spec.h b/nfs/nfs-spec.h
new file mode 100644
index 00000000..bed03874
--- /dev/null
+++ b/nfs/nfs-spec.h
@@ -0,0 +1,168 @@
+#ifndef NFS_NFS_SPEC_H
+#define NFS_NFS_SPEC_H
+
+#define NFS_PORT 2049
+#define NFS_MAXDATA 8192
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS2_FHSIZE 32
+#define NFS3_FHSIZE 64
+#define NFS_COOKIESIZE 4
+#define NFS_FIFO_DEV -1
+#define NFS3_COOKIEVERFSIZE 8
+#define NFS3_CREATEVERFSIZE 8
+#define NFS3_WRITEVERFSIZE 8
+#define NFSMODE_FMT 0170000
+#define NFSMODE_DIR 0040000
+#define NFSMODE_CHR 0020000
+#define NFSMODE_BLK 0060000
+#define NFSMODE_REG 0100000
+#define NFSMODE_LNK 0120000
+#define NFSMODE_SOCK 0140000
+#define NFSMODE_FIFO 0010000
+
+enum nfsstat {
+ NFS_OK = 0,
+ NFSERR_PERM = 1,
+ NFSERR_NOENT = 2,
+ NFSERR_IO = 5,
+ NFSERR_NXIO = 6,
+ NFSERR_ACCES = 13,
+ NFSERR_EXIST = 17,
+ NFSERR_XDEV = 18, /* v3 only */
+ NFSERR_NODEV = 19,
+ NFSERR_NOTDIR = 20,
+ NFSERR_ISDIR = 21,
+ NFSERR_INVAL = 22, /* v3 only */
+ NFSERR_FBIG = 27,
+ NFSERR_NOSPC = 28,
+ NFSERR_ROFS = 30,
+ NFSERR_MLINK = 31, /* v3 only */
+ NFSERR_NAMETOOLONG = 63,
+ NFSERR_NOTEMPTY = 66,
+ NFSERR_DQUOT = 69,
+ NFSERR_STALE = 70,
+ NFSERR_REMOTE = 71, /* v3 only */
+ NFSERR_WFLUSH = 99, /* v2 only */
+ NFSERR_BADHANDLE = 10001, /* v3 only */
+ NFSERR_NOT_SYNC = 10002, /* v3 only */
+ NFSERR_BAD_COOKIE = 10003, /* v3 only */
+ NFSERR_NOTSUPP = 10004, /* v3 only */
+ NFSERR_TOOSMALL = 10005, /* v3 only */
+ NFSERR_SERVERFAULT = 10006, /* v3 only */
+ NFSERR_BADTYPE = 10007, /* v3 only */
+ NFSERR_JUKEBOX = 10008, /* v3 only */
+#define NFSERR_TRYLATER NFSERR_JUKEBOX
+};
+
+
+enum ftype {
+ NF2NON = 0, /* v2 only */
+ NFREG = 1,
+ NFDIR = 2,
+ NFBLK = 3,
+ NFCHR = 4,
+ NFLNK = 5,
+ NFSOCK = 6,
+ NF3FIFO = 7, /* v3 only */
+#define NF2BAD NF3FIFO /* v2 only */
+ NF2FIFO = 8, /* v2 only */
+};
+
+/* Ways to set the time in setattr structures */
+enum sattr_time_how
+{
+ DONT_CHANGE = 0,
+ SET_TO_SERVER_TIME = 1,
+ SET_TO_CLIENT_TIME = 2,
+};
+
+/* Construction of ACCESS arg to NFS3PROC_ACCESS. */
+#define ACCESS3_READ 0x01
+#define ACCESS3_LOOKUP 0x02
+#define ACCESS3_MODIFY 0x04
+#define ACCESS3_EXTEND 0x08
+#define ACCESS3_DELETE 0x10
+#define ACCESS3_EXECUTE 0x20
+
+/* STABLE arg to NFS3PROC_READ */
+enum stable_how {
+ UNSTABLE = 0,
+ DATA_SYNC = 1,
+ FILE_SYNC = 2,
+};
+
+/* MODE arg to NFS3PROC_CREATE */
+enum createmode
+{
+ UNCHECKED = 0,
+ GUARDED = 1,
+ EXCLUSIVE = 2,
+};
+
+#define NFS_PROGRAM ((u_long)100003)
+#define NFS_VERSION ((u_long)2)
+
+#define NFS_PROTOCOL_FUNC(proc,vers) \
+ (vers == 2 ? NFS2PROC_ ## proc : NFS3PROC_ ## proc)
+
+#define NFSPROC_NULL(v) NFS_PROTOCOL_FUNC (NULL,v)
+#define NFSPROC_GETATTR(v) NFS_PROTOCOL_FUNC (GETATTR, v)
+#define NFSPROC_SETATTR(v) NFS_PROTOCOL_FUNC (SETATTR, v)
+#define NFSPROC_LOOKUP(v) NFS_PROTOCOL_FUNC (LOOKUP, v)
+#define NFSPROC_READLINK(v) NFS_PROTOCOL_FUNC (READLINK, v)
+#define NFSPROC_READ(v) NFS_PROTOCOL_FUNC (READ, v)
+#define NFSPROC_WRITE(v) NFS_PROTOCOL_FUNC (WRITE, v)
+#define NFSPROC_CREATE(v) NFS_PROTOCOL_FUNC (CREATE, v)
+#define NFSPROC_REMOVE(v) NFS_PROTOCOL_FUNC (REMOVE, v)
+#define NFSPROC_RENAME(v) NFS_PROTOCOL_FUNC (RENAME, v)
+#define NFSPROC_LINK(v) NFS_PROTOCOL_FUNC (LINK, v)
+#define NFSPROC_SYMLINK(v) NFS_PROTOCOL_FUNC (SYMLINK, v)
+#define NFSPROC_MKDIR(v) NFS_PROTOCOL_FUNC (MKDIR, v)
+#define NFSPROC_RMDIR(v) NFS_PROTOCOL_FUNC (RMDIR, v)
+#define NFSPROC_READDIR(v) NFS_PROTOCOL_FUNC (READDIR, v)
+
+/* Values for each protocol */
+#define NFS2PROC_NULL 0
+#define NFS2PROC_GETATTR 1
+#define NFS2PROC_SETATTR 2
+#define NFS2PROC_ROOT 3
+#define NFS2PROC_LOOKUP 4
+#define NFS2PROC_READLINK 5
+#define NFS2PROC_READ 6
+#define NFS2PROC_WRITECACHE 7
+#define NFS2PROC_WRITE 8
+#define NFS2PROC_CREATE 9
+#define NFS2PROC_REMOVE 10
+#define NFS2PROC_RENAME 11
+#define NFS2PROC_LINK 12
+#define NFS2PROC_SYMLINK 13
+#define NFS2PROC_MKDIR 14
+#define NFS2PROC_RMDIR 15
+#define NFS2PROC_READDIR 16
+#define NFS2PROC_STATFS 17
+
+#define NFS3PROC_NULL 0
+#define NFS3PROC_GETATTR 1
+#define NFS3PROC_SETATTR 2
+#define NFS3PROC_LOOKUP 3
+#define NFS3PROC_ACCESS 4
+#define NFS3PROC_READLINK 5
+#define NFS3PROC_READ 6
+#define NFS3PROC_WRITE 7
+#define NFS3PROC_CREATE 8
+#define NFS3PROC_MKDIR 9
+#define NFS3PROC_SYMLINK 10
+#define NFS3PROC_MKNOD 11
+#define NFS3PROC_REMOVE 12
+#define NFS3PROC_RMDIR 13
+#define NFS3PROC_RENAME 14
+#define NFS3PROC_LINK 15
+#define NFS3PROC_READDIR 16
+#define NFS3PROC_READDIRPLUS 17
+#define NFS3PROC_FSSTAT 18
+#define NFS3PROC_FSINFO 19
+#define NFS3PROC_PATHCONF 20
+#define NFS3PROC_COMMIT 21
+
+#endif /* NFS_NFS_SPEC_H */
diff --git a/nfs/nfs.c b/nfs/nfs.c
new file mode 100644
index 00000000..4916df65
--- /dev/null
+++ b/nfs/nfs.c
@@ -0,0 +1,681 @@
+/* nfs.c - XDR frobbing and lower level routines for NFS client.
+
+ Copyright (C) 1995, 1996, 1997, 1999, 2002, 2007
+ Free Software Foundation, Inc.
+
+ Written by Michael I. Bushnell, p/BSG.
+
+ 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 "nfs.h"
+
+#include <string.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+/* Convert an NFS mode (TYPE and MODE) to a Hurd mode and return
+ it. */
+mode_t
+nfs_mode_to_hurd_mode (int type, int mode)
+{
+ int hurdmode;
+
+ switch (type)
+ {
+ case NFDIR:
+ hurdmode = S_IFDIR;
+ break;
+
+ case NFCHR:
+ hurdmode = S_IFCHR;
+ break;
+
+ case NFBLK:
+ hurdmode = S_IFBLK;
+ break;
+
+ case NFREG:
+ hurdmode = S_IFREG;
+ break;
+
+ case NFLNK:
+ hurdmode = S_IFLNK;
+ break;
+
+ case NFSOCK:
+ hurdmode = S_IFSOCK;
+ break;
+
+ default:
+ if (protocol_version == 2)
+ switch (type)
+ {
+ case NF2NON:
+ case NF2BAD:
+ default:
+ hurdmode = S_IFREG;
+ break;
+
+ case NF2FIFO:
+ hurdmode = S_IFIFO;
+ break;
+ }
+ else
+ switch (type)
+ {
+ case NF3FIFO:
+ hurdmode = S_IFIFO;
+ break;
+
+ default:
+ hurdmode = S_IFREG;
+ break;
+ }
+ break;
+ }
+
+ hurdmode |= mode & ~NFSMODE_FMT;
+ return hurdmode;
+}
+
+/* Convert a Hurd mode to an NFS mode. */
+int
+hurd_mode_to_nfs_mode (mode_t mode)
+{
+ /* This function is used only for chmod; just trim the bits that NFS
+ doesn't support. */
+ return mode & 07777;
+}
+
+/* Convert a Hurd mode to an NFS type. */
+int
+hurd_mode_to_nfs_type (mode_t mode)
+{
+ switch (mode & S_IFMT)
+ {
+ case S_IFDIR:
+ return NFDIR;
+
+ case S_IFCHR:
+ default:
+ return NFCHR;
+
+ case S_IFBLK:
+ return NFBLK;
+
+ case S_IFREG:
+ return NFREG;
+
+ case S_IFLNK:
+ return NFLNK;
+
+ case S_IFSOCK:
+ return NFSOCK;
+
+ case S_IFIFO:
+ return protocol_version == 2 ? NF2FIFO : NF3FIFO;
+ }
+}
+
+
+
+/* Each of the functions on this page copies its second arg to *P,
+ converting it to XDR representation along the way. They then
+ return the address after the copied value. */
+
+/* Encode an NFS file handle. */
+int *
+xdr_encode_fhandle (int *p, struct fhandle *fhandle)
+{
+ if (protocol_version == 2)
+ {
+ memcpy (p, fhandle->data, NFS2_FHSIZE);
+ return p + INTSIZE (NFS2_FHSIZE);
+ }
+ else
+ return xdr_encode_data (p, fhandle->data, fhandle->size);
+}
+
+/* Encode uninterpreted bytes. */
+int *
+xdr_encode_data (int *p, char *data, size_t len)
+{
+ int nints = INTSIZE (len);
+
+ p[nints] = 0;
+ *(p++) = htonl (len);
+ memcpy (p, data, len);
+ return p + nints;
+}
+
+/* Encode a 64 bit integer. */
+int *
+xdr_encode_64bit (int *p, long long n)
+{
+ *(p++) = htonl (n & 0xffffffff00000000LL >> 32);
+ *(p++) = htonl (n & 0xffffffff);
+ return p;
+}
+
+/* Encode a C string. */
+int *
+xdr_encode_string (int *p, char *string)
+{
+ return xdr_encode_data (p, string, strlen (string));
+}
+
+/* Encode a MODE into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_mode (int *p, mode_t mode)
+{
+ if (protocol_version == 2)
+ {
+ *(p++) = htonl (hurd_mode_to_nfs_mode (mode));
+ *(p++) = -1; /* uid */
+ *(p++) = -1; /* gid */
+ *(p++) = -1; /* size */
+ *(p++) = -1; /* atime secs */
+ *(p++) = -1; /* atime usecs */
+ *(p++) = -1; /* mtime secs */
+ *(p++) = -1; /* mtime usecs */
+ }
+ else
+ {
+ *(p++) = htonl (1); /* set mode */
+ *(p++) = htonl (hurd_mode_to_nfs_mode (mode));
+ *(p++) = 0; /* no uid */
+ *(p++) = 0; /* no gid */
+ *(p++) = 0; /* no size */
+ *(p++) = DONT_CHANGE; /* no atime */
+ *(p++) = DONT_CHANGE; /* no mtime */
+ }
+ return p;
+}
+
+/* Encode UID and GID into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_ids (int *p, u_int uid, u_int gid)
+{
+ if (protocol_version == 2)
+ {
+ *(p++) = -1; /* mode */
+ *(p++) = htonl (uid);
+ *(p++) = htonl (gid);
+ *(p++) = -1; /* size */
+ *(p++) = -1; /* atime secs */
+ *(p++) = -1; /* atime usecs */
+ *(p++) = -1; /* mtime secs */
+ *(p++) = -1; /* mtime usecs */
+ }
+ else
+ {
+ *(p++) = 0; /* no mode */
+ *(p++) = htonl (1); /* set uid */
+ *(p++) = htonl (uid);
+ *(p++) = htonl (1); /* set gid */
+ *(p++) = htonl (gid);
+ *(p++) = 0; /* no size */
+ *(p++) = DONT_CHANGE; /* no atime */
+ *(p++) = DONT_CHANGE; /* no mtime */
+ }
+ return p;
+}
+
+/* Encode a file size into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_size (int *p, off_t size)
+{
+ if (protocol_version == 2)
+ {
+ *(p++) = -1; /* mode */
+ *(p++) = -1; /* uid */
+ *(p++) = -1; /* gid */
+ *(p++) = htonl (size);
+ *(p++) = -1; /* atime secs */
+ *(p++) = -1; /* atime usecs */
+ *(p++) = -1; /* mtime secs */
+ *(p++) = -1; /* mtime secs */
+ }
+ else
+ {
+ *(p++) = 0; /* no mode */
+ *(p++) = 0; /* no uid */
+ *(p++) = 0; /* no gid */
+ *(p++) = htonl (1); /* size */
+ p = xdr_encode_64bit (p, size);
+ *(p++) = DONT_CHANGE; /* no atime */
+ *(p++) = DONT_CHANGE; /* no mtime */
+ }
+ return p;
+}
+
+/* Encode ATIME and MTIME into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_times (int *p, struct timespec *atime, struct timespec *mtime)
+{
+ if (protocol_version == 2)
+ {
+ *(p++) = -1; /* mode */
+ *(p++) = -1; /* uid */
+ *(p++) = -1; /* gid */
+ *(p++) = -1; /* size */
+ *(p++) = htonl (atime->tv_sec);
+ *(p++) = htonl (atime->tv_nsec / 1000);
+ *(p++) = htonl (mtime->tv_sec);
+ *(p++) = htonl (mtime->tv_nsec / 1000);
+ }
+ else
+ {
+ *(p++) = 0; /* no mode */
+ *(p++) = 0; /* no uid */
+ *(p++) = 0; /* no gid */
+ *(p++) = 0; /* no size */
+ *(p++) = htonl (SET_TO_CLIENT_TIME); /* atime */
+ *(p++) = htonl (atime->tv_sec);
+ *(p++) = htonl (atime->tv_nsec);
+ *(p++) = htonl (SET_TO_CLIENT_TIME); /* mtime */
+ *(p++) = htonl (mtime->tv_sec);
+ *(p++) = htonl (mtime->tv_nsec);
+ }
+ return p;
+}
+
+/* Encode MODE, a size of zero, and the specified owner into an
+ otherwise empty sattr. */
+int *
+xdr_encode_create_state (int *p,
+ mode_t mode,
+ uid_t owner)
+{
+ if (protocol_version == 2)
+ {
+ *(p++) = htonl (hurd_mode_to_nfs_mode (mode));
+ *(p++) = htonl (owner); /* uid */
+ *(p++) = -1; /* gid */
+ *(p++) = 0; /* size */
+ *(p++) = -1; /* atime sec */
+ *(p++) = -1; /* atime usec */
+ *(p++) = -1; /* mtime sec */
+ *(p++) = -1; /* mtime usec */
+ }
+ else
+ {
+ *(p++) = htonl (1); /* mode */
+ *(p++) = htonl (hurd_mode_to_nfs_mode (mode));
+ *(p++) = htonl (1); /* set uid */
+ *(p++) = htonl (owner);
+ *(p++) = 0; /* no gid */
+ *(p++) = htonl (1); /* set size */
+ p = xdr_encode_64bit (p, 0);
+ *(p++) = htonl (SET_TO_SERVER_TIME); /* atime */
+ *(p++) = htonl (SET_TO_SERVER_TIME); /* mtime */
+ }
+ return p;
+}
+
+/* Encode ST into an sattr. */
+int *
+xdr_encode_sattr_stat (int *p,
+ struct stat *st)
+{
+ if (protocol_version == 2)
+ {
+ *(p++) = htonl (hurd_mode_to_nfs_mode (st->st_mode));
+ *(p++) = htonl (st->st_uid);
+ *(p++) = htonl (st->st_gid);
+ *(p++) = htonl (st->st_size);
+ *(p++) = htonl (st->st_atim.tv_sec);
+ *(p++) = htonl (st->st_atim.tv_nsec / 1000);
+ *(p++) = htonl (st->st_mtim.tv_sec);
+ *(p++) = htonl (st->st_mtim.tv_nsec / 1000);
+ }
+ else
+ {
+ *(p++) = htonl (1); /* set mode */
+ *(p++) = htonl (hurd_mode_to_nfs_mode (st->st_mode));
+ *(p++) = htonl (1); /* set uid */
+ *(p++) = htonl (st->st_uid);
+ *(p++) = htonl (1); /* set gid */
+ *(p++) = htonl (st->st_gid);
+ *(p++) = htonl (1); /* set size */
+ p = xdr_encode_64bit (p, st->st_size);
+ *(p++) = htonl (SET_TO_CLIENT_TIME); /* set atime */
+ *(p++) = htonl (st->st_atim.tv_sec);
+ *(p++) = htonl (st->st_atim.tv_nsec);
+ *(p++) = htonl (SET_TO_CLIENT_TIME); /* set mtime */
+ *(p++) = htonl (st->st_mtim.tv_sec);
+ *(p++) = htonl (st->st_mtim.tv_nsec);
+ }
+ return p;
+}
+
+
+/* Decode *P into a long long; return the address of the following
+ data. */
+int *
+xdr_decode_64bit (int *p, long long *n)
+{
+ long long high, low;
+ high = ntohl (*p);
+ p++;
+ low = ntohl (*p);
+ p++;
+ *n = ((high & 0xffffffff) << 32) | (low & 0xffffffff);
+ return p;
+}
+
+/* Decode *P into an fhandle and look up the associated node. Return
+ the address of the following data. */
+int *
+xdr_decode_fhandle (int *p, struct node **npp)
+{
+ size_t len;
+
+ if (protocol_version == 2)
+ len = NFS2_FHSIZE;
+ else
+ {
+ len = ntohl (*p);
+ p++;
+ }
+ /* Enter into cache. */
+ lookup_fhandle (p, len, npp);
+ return p + len / sizeof (int);
+}
+
+/* Decode *P into a stat structure; return the address of the
+ following data. */
+int *
+xdr_decode_fattr (int *p, struct stat *st)
+{
+ int type, mode;
+
+ type = ntohl (*p);
+ p++;
+ mode = ntohl (*p);
+ p++;
+ st->st_mode = nfs_mode_to_hurd_mode (type, mode);
+ st->st_nlink = ntohl (*p);
+ p++;
+ st->st_uid = ntohl (*p);
+ p++;
+ st->st_gid = ntohl (*p);
+ p++;
+ if (protocol_version == 2)
+ {
+ st->st_size = ntohl (*p);
+ p++;
+ st->st_blksize = ntohl (*p);
+ p++;
+ st->st_rdev = ntohl (*p);
+ p++;
+ st->st_blocks = ntohl (*p);
+ p++;
+ }
+ else
+ {
+ long long size;
+ int major, minor;
+ p = xdr_decode_64bit (p, &size);
+ st->st_size = size;
+ p = xdr_decode_64bit (p, &size);
+ st->st_blocks = size / 512;
+ st->st_blksize = read_size < write_size ? read_size : write_size;
+ major = ntohl (*p);
+ p++;
+ minor = ntohl (*p);
+ p++;
+ st->st_rdev = makedev (major, minor);
+ }
+ st->st_fsid = ntohl (*p);
+ p++;
+ st->st_ino = ntohl (*p);
+ p++;
+ st->st_atim.tv_sec = ntohl (*p);
+ p++;
+ st->st_atim.tv_nsec = ntohl (*p);
+ p++;
+ st->st_mtim.tv_sec = ntohl (*p);
+ p++;
+ st->st_mtim.tv_nsec = ntohl (*p);
+ p++;
+ st->st_ctim.tv_sec = ntohl (*p);
+ p++;
+ st->st_ctim.tv_nsec = ntohl (*p);
+ p++;
+
+ if (protocol_version < 3)
+ {
+ st->st_atim.tv_nsec *= 1000;
+ st->st_mtim.tv_nsec *= 1000;
+ st->st_ctim.tv_nsec *= 1000;
+ }
+
+ return p;
+
+}
+
+/* Decode *P into a string, stored at BUF; return the address
+ of the following data. */
+int *
+xdr_decode_string (int *p, char *buf)
+{
+ int len;
+
+ len = ntohl (*p);
+ p++;
+ memcpy (buf, p, len);
+ buf[len] = '\0';
+ return p + INTSIZE (len);
+}
+
+
+/* Set up an RPC for procedure RPC_PROC for talking to the NFS server.
+ Allocate storage with malloc and point *BUFP at it; caller must
+ free this when done. Initialize RPC credential information with
+ information from CRED (identifying the user making this call; -1
+ means superuser), NP (identifying the node we are operating on), and
+ SECOND_GID (specifying another GID the server might be interested
+ in). Allocate at least LEN bytes of space for bulk data in
+ addition to the normal amount for an RPC. */
+int *
+nfs_initialize_rpc (int rpc_proc, struct iouser *cred,
+ size_t len, void **bufp, struct node *np,
+ uid_t second_gid)
+{
+ uid_t uid;
+ uid_t gid;
+ error_t err;
+
+ /* Use heuristics to figure out what ids to present to the server.
+ Don't lie, but adjust ids as necessary to secure the desired
+ result. */
+
+ if (cred == (struct iouser *) -1)
+ {
+ uid = gid = 0;
+ second_gid = -1;
+ }
+ else if (cred
+ && (cred->uids->num || cred->gids->num))
+ {
+ if (idvec_contains (cred->uids, 0))
+ {
+ err = netfs_validate_stat (np, 0);
+ uid = 0;
+ gid = err ? -2 : 0;
+ if (err)
+ printf ("NFS warning, internal stat failure\n");
+ }
+ else
+ {
+ if (cred->uids->num == 0)
+ uid = -2;
+ else if (cred->uids->num == 1)
+ uid = cred->uids->ids[0];
+ else
+ {
+ err = netfs_validate_stat (np, 0);
+ if (err)
+ {
+ uid = cred->uids->ids[0];
+ printf ("NFS warning, internal stat failure\n");
+ }
+ else
+ {
+ if (idvec_contains (cred->uids, np->nn_stat.st_uid))
+ uid = np->nn_stat.st_uid;
+ else
+ uid = cred->uids->ids[0];
+ }
+ }
+
+ if (cred->gids->num == 0)
+ {
+ gid = -2;
+ second_gid = -1;
+ }
+ else if (cred->gids->num == 1)
+ {
+ gid = cred->gids->ids[0];
+ second_gid = -1;
+ }
+ else
+ {
+ err = netfs_validate_stat (np, 0);
+ if (err)
+ {
+ gid = cred->gids->ids[0];
+ printf ("NFS warning, internal stat failure\n");
+ }
+ else
+ {
+ if (idvec_contains (cred->gids, np->nn_stat.st_gid))
+ gid = np->nn_stat.st_gid;
+ else
+ gid = cred->gids->ids[0];
+ }
+ if (second_gid != -1
+ && !idvec_contains (cred->gids, second_gid))
+ second_gid = -1;
+ }
+ }
+ }
+ else
+ uid = gid = second_gid = -1;
+
+ return initialize_rpc (NFS_PROGRAM, NFS_VERSION, rpc_proc, len, bufp,
+ uid, gid, second_gid);
+}
+
+/* ERROR is an NFS error code; return the correspending Hurd
+ error. */
+error_t
+nfs_error_trans (int error)
+{
+ switch (error)
+ {
+ case NFS_OK:
+ return 0;
+
+ case NFSERR_PERM:
+ return EPERM;
+
+ case NFSERR_NOENT:
+ return ENOENT;
+
+ case NFSERR_IO:
+ return EIO;
+
+ case NFSERR_NXIO:
+ return ENXIO;
+
+ case NFSERR_ACCES:
+ return EACCES;
+
+ case NFSERR_EXIST:
+ return EEXIST;
+
+ case NFSERR_NODEV:
+ return ENODEV;
+
+ case NFSERR_NOTDIR:
+ return ENOTDIR;
+
+ case NFSERR_ISDIR:
+ return EISDIR;
+
+ case NFSERR_FBIG:
+ return E2BIG;
+
+ case NFSERR_NOSPC:
+ return ENOSPC;
+
+ case NFSERR_ROFS:
+ return EROFS;
+
+ case NFSERR_NAMETOOLONG:
+ return ENAMETOOLONG;
+
+ case NFSERR_NOTEMPTY:
+ return ENOTEMPTY;
+
+ case NFSERR_DQUOT:
+ return EDQUOT;
+
+ case NFSERR_STALE:
+ return ESTALE;
+
+ case NFSERR_WFLUSH:
+ /* Not known in v3, but we just give EINVAL for unknown errors
+ so it's the same. */
+ return EINVAL;
+
+ default:
+ if (protocol_version == 2)
+ return EINVAL;
+ else
+ switch (error)
+ {
+ case NFSERR_XDEV:
+ return EXDEV;
+
+ case NFSERR_INVAL:
+ case NFSERR_REMOTE: /* not sure about this one */
+ default:
+ return EINVAL;
+
+ case NFSERR_MLINK:
+ return EMLINK;
+
+ case NFSERR_NOTSUPP:
+ case NFSERR_BADTYPE:
+ return EOPNOTSUPP;
+
+ case NFSERR_SERVERFAULT:
+ return EIO;
+
+ case NFSERR_BADHANDLE:
+ case NFSERR_NOT_SYNC:
+ case NFSERR_BAD_COOKIE:
+ case NFSERR_TOOSMALL:
+ case NFSERR_JUKEBOX: /* ??? */
+ /* These indicate bugs in the client, so EGRATUITOUS is right. */
+ return EGRATUITOUS;
+ }
+ }
+}
diff --git a/nfs/nfs.h b/nfs/nfs.h
new file mode 100644
index 00000000..18dec001
--- /dev/null
+++ b/nfs/nfs.h
@@ -0,0 +1,204 @@
+/* Data structures and global variables for NFS client
+ Copyright (C) 1994,95,96,97,99,2001 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. */
+
+#ifndef NFS_NFS_H
+#define NFS_NFS_H
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include "nfs-spec.h"
+#include <hurd/netfs.h>
+
+/* A file handle */
+struct fhandle
+{
+ size_t size;
+
+ /* Leave enough room for the largest possible fhandle. */
+ char data[NFS3_FHSIZE];
+};
+
+/* There exists one of there for the private data needed by each client
+ node. */
+struct netnode
+{
+ struct fhandle handle;
+ time_t stat_updated;
+ struct node *hnext, **hprevp;
+
+ /* These two fields handle translators set internally but
+ unknown to the server. */
+ enum
+ {
+ NOT_POSSIBLE,
+ POSSIBLE,
+ SYMLINK,
+ CHRDEV,
+ BLKDEV,
+ FIFO,
+ SOCK,
+ } dtrans;
+ union
+ {
+ char *name;
+ dev_t indexes;
+ } transarg;
+
+#ifdef notyet
+ /* This indicates that the length of the file must be at
+ least this big because we've written this much locally,
+ even if the server thinks we haven't gone this far. */
+ off_t extend_len;
+#endif
+
+ struct user_pager_info *fileinfo;
+
+ /* If this node has been renamed by "deletion" then
+ this is the directory and the name in that directory
+ which is holding the node */
+ struct node *dead_dir;
+ char *dead_name;
+};
+
+/* Socket file descriptor for talking to RPC servers. */
+int main_udp_socket;
+
+/* Our hostname */
+char *hostname;
+
+/* The current time */
+volatile struct mapped_time_value *mapped_time;
+
+/* Some tunable parameters */
+
+/* How long to keep around stat information */
+extern int stat_timeout;
+
+/* How long to keep around file contents caches */
+extern int cache_timeout;
+
+/* How long to keep around positive dir cache entries */
+extern int name_cache_timeout;
+
+/* How long to keep around negative dir cache entries */
+extern int name_cache_neg_timeout;
+
+/* How long to wait for replies before re-sending RPC's. */
+extern int initial_transmit_timeout;
+extern int max_transmit_timeout;
+
+/* How many attempts to send before giving up on soft mounts */
+extern int soft_retries;
+
+/* Whether we are a hard or soft mount */
+extern int mounted_soft;
+
+/* Maximum amount to read at once */
+extern int read_size;
+
+/* Maximum amout to write at once */
+extern int write_size;
+
+/* Service name for portmapper */
+extern char *pmap_service_name;
+
+/* If pmap_service is null, then this is the port to use for the portmapper;
+ if the lookup of pmap_service_name fails, use this number. */
+extern short pmap_service_number;
+
+/* RPC program for the mount agent */
+extern int mount_program;
+
+/* RPC program version for the mount agent */
+extern int mount_version;
+
+/* If this is nonzero, it's the port to use for the mount agent if
+ the portmapper fails or can't be found. */
+extern short mount_port;
+
+/* If this is nonzero use mount_port without attempting to contact
+ the portmapper */
+extern int mount_port_override;
+
+/* RPC program for the NFS server */
+extern int nfs_program;
+
+/* RPC program version for the NFS server */
+extern int nfs_version;
+
+/* If this is nonzero, it's the port to be used to find the nfs agent
+ if the portmapper fails or can't be found */
+extern short nfs_port;
+
+/* If this is nonzero use nfs_port without attepting to contact the
+ portmapper. */
+extern int nfs_port_override;
+
+/* Which NFS protocol version we are using */
+extern int protocol_version;
+
+
+/* Count how many four-byte chunks it takes to hold LEN bytes. */
+#define INTSIZE(len) (((len)+3)>>2)
+
+
+/* nfs.c */
+int hurd_mode_to_nfs_type (mode_t);
+int *xdr_encode_fhandle (int *, struct fhandle *);
+int *xdr_encode_data (int *, char *, size_t);
+int *xdr_encode_string (int *, char *);
+int *xdr_encode_sattr_mode (int *, mode_t);
+int *xdr_encode_sattr_ids (int *, u_int, u_int);
+int *xdr_encode_sattr_size (int *, off_t);
+int *xdr_encode_sattr_times (int *, struct timespec *, struct timespec *);
+int *xdr_encode_sattr_stat (int *, struct stat *);
+int *xdr_encode_create_state (int *, mode_t, uid_t);
+int *xdr_decode_fattr (int *, struct stat *);
+int *xdr_decode_string (int *, char *);
+int *xdr_decode_fhandle (int *, struct node **);
+int *nfs_initialize_rpc (int, struct iouser *, size_t, void **,
+ struct node *, uid_t);
+error_t nfs_error_trans (int);
+
+/* mount.c */
+struct node *mount_root (char *, char *);
+extern const char *mounted_hostname;
+extern uint16_t mounted_nfs_port; /* host order */
+
+/* ops.c */
+int *register_fresh_stat (struct node *, int *);
+
+/* rpc.c */
+int *initialize_rpc (int, int, int, size_t, void **, uid_t, gid_t, gid_t);
+error_t conduct_rpc (void **, int **);
+void *timeout_service_thread (void *);
+void *rpc_receive_thread (void *);
+
+/* cache.c */
+void lookup_fhandle (void *, size_t, struct node **);
+int *recache_handle (int *, struct node *);
+
+/* name-cache.c */
+void enter_lookup_cache (char *, size_t, struct node *, char *);
+void purge_lookup_cache (struct node *, char *, size_t);
+struct node *check_lookup_cache (struct node *, char *);
+void purge_lookup_cache_node (struct node *);
+
+#endif /* NFS_NFS_H */
diff --git a/nfs/ops.c b/nfs/ops.c
new file mode 100644
index 00000000..a4d6ac77
--- /dev/null
+++ b/nfs/ops.c
@@ -0,0 +1,1925 @@
+/* ops.c - Libnetfs callbacks for node operations in NFS client.
+ Copyright (C) 1994,95,96,97,99,2002,2011 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. */
+
+#include "nfs.h"
+#include <hurd/netfs.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <maptime.h>
+
+/* We have fresh stat information for NP; the file attribute (fattr)
+ structure is at P. Update our entry. Return the address of the next
+ int after the fattr structure. */
+int *
+register_fresh_stat (struct node *np, int *p)
+{
+ int *ret;
+
+ ret = xdr_decode_fattr (p, &np->nn_stat);
+ np->nn->stat_updated = mapped_time->seconds;
+
+ switch (np->nn->dtrans)
+ {
+ case NOT_POSSIBLE:
+ case POSSIBLE:
+ break;
+
+ case SYMLINK:
+ np->nn_stat.st_size = strlen (np->nn->transarg.name);
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFLNK);
+ break;
+
+ case CHRDEV:
+ np->nn_stat.st_rdev = np->nn->transarg.indexes;
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFCHR);
+ break;
+
+ case BLKDEV:
+ np->nn_stat.st_rdev = np->nn->transarg.indexes;
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFBLK);
+ break;
+
+ case FIFO:
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFIFO);
+ break;
+
+ case SOCK:
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFSOCK);
+ break;
+ }
+
+ np->nn_stat.st_fsid = getpid ();
+ np->nn_stat.st_fstype = FSTYPE_NFS;
+ np->nn_stat.st_gen = 0;
+ np->nn_stat.st_author = np->nn_stat.st_uid;
+ np->nn_stat.st_flags = 0;
+ np->nn_translated = np->nn_stat.st_mode & S_IFMT;
+
+ return ret;
+}
+
+/* Handle returned wcc information for various calls. In protocol
+ version 2, this is just register_fresh_stat. In version 3, it
+ checks to see if stat information is present too. If this follows
+ an operation that we expect has modified the attributes, MOD should
+ be set. (This unpacks the post_op_attr XDR type.) */
+int *
+process_returned_stat (struct node *np, int *p, int mod)
+{
+ if (protocol_version == 2)
+ return register_fresh_stat (np, p);
+ else
+ {
+ int attrs_exist;
+
+ attrs_exist = ntohl (*p);
+ p++;
+ if (attrs_exist)
+ p = register_fresh_stat (np, p);
+ else if (mod)
+ /* We know that our values are now wrong */
+ np->nn->stat_updated = 0;
+ return p;
+ }
+}
+
+
+/* Handle returned wcc information for various calls. In protocol
+ version 2, this is just register_fresh_stat. In version 3, it does
+ the wcc_data interpretation too. If this follows an operation that
+ we expect has modified the attributes, MOD should be set.
+ (This unpacks the wcc_data XDR type.) */
+int *
+process_wcc_stat (struct node *np, int *p, int mod)
+{
+ if (protocol_version == 2)
+ return register_fresh_stat (np, p);
+ else
+ {
+ int attrs_exist;
+
+ /* First the pre_op_attr */
+ attrs_exist = ntohl (*p);
+ p++;
+ if (attrs_exist)
+ {
+ /* Just skip them for now */
+ p += 2 * sizeof (int); /* size */
+ p += 2 * sizeof (int); /* mtime */
+ p += 2 * sizeof (int); /* atime */
+ }
+
+ /* Now the post_op_attr */
+ return process_returned_stat (np, p, mod);
+ }
+}
+
+
+/* Implement the netfs_validate_stat callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_validate_stat (struct node *np, struct iouser *cred)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if (mapped_time->seconds - np->nn->stat_updated < stat_timeout)
+ return 0;
+
+ p = nfs_initialize_rpc (NFSPROC_GETATTR (protocol_version),
+ (struct iouser *) -1, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+ if (!err)
+ register_fresh_stat (np, p);
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_chown callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *np,
+ uid_t uid, gid_t gid)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, gid);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_ids (p, uid, gid);
+ if (protocol_version == 3)
+ *(p++) = 0; /* guard_check == 0 */
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ free (rpcbuf);
+
+ return err;
+}
+
+/* Implement the netfs_attempt_chauthor callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *rp,
+ uid_t author)
+{
+ return EOPNOTSUPP;
+}
+
+/* Implement the netfs_attempt_chmod callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chmod (struct iouser *cred, struct node *np,
+ mode_t mode)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if ((mode & S_IFMT) != 0)
+ {
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+
+ /* Has the file type changed? (e.g. from symlink to
+ directory). */
+ if ((mode & S_IFMT) != (np->nn_stat.st_mode & S_IFMT))
+ {
+ char *f = 0;
+
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ return EOPNOTSUPP;
+
+ if (np->nn->dtrans == SYMLINK)
+ f = np->nn->transarg.name;
+
+ switch (mode & S_IFMT)
+ {
+ default:
+ return EOPNOTSUPP;
+
+ case S_IFIFO:
+ np->nn->dtrans = FIFO;
+ np->nn->stat_updated = 0;
+ break;
+
+ case S_IFSOCK:
+ np->nn->dtrans = SOCK;
+ np->nn->stat_updated = 0;
+ }
+ if (f)
+ free (f);
+ return 0;
+ }
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_mode (p, mode);
+ if (protocol_version == 3)
+ *(p++) = 0; /* guard check == 0 */
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_chflags callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *np,
+ int flags)
+{
+ return EOPNOTSUPP;
+}
+
+/* Implement the netfs_attempt_utimes callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *np,
+ struct timespec *atime, struct timespec *mtime)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ struct timeval tv;
+ struct timespec current;
+
+ /* XXX For version 3 we can actually do this right, but we don't
+ just yet. */
+ if (!atime || !mtime)
+ {
+ maptime_read (mapped_time, &tv);
+ current.tv_sec = tv.tv_sec;
+ current.tv_nsec = tv.tv_usec * 1000;
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_times (p,
+ atime ?: &current,
+ mtime ?: &current);
+ if (protocol_version == 3)
+ *(p++) = 0; /* guard check == 0 */
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_set_size callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *np,
+ off_t size)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_size (p, size);
+ if (protocol_version == 3)
+ *(p++) = 0; /* guard_check == 0 */
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ /* If we got EACCES, but the user has the file open for writing,
+ then the NFS protocol has screwed us. There's nothing we can do,
+ except in the important case of opens with
+ O_TRUNC|O_CREAT|O_WRONLY|O_EXCL where the new mode does not allow
+ writing. RCS, for example, uses this to create lock files. So permit
+ cases where the O_TRUNC isn't doing anything to succeed if the user
+ does have the file open for writing. */
+ if (err == EACCES)
+ {
+ int error = netfs_validate_stat (np, cred);
+ if (!error && np->nn_stat.st_size == size)
+ err = 0;
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_statfs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *np,
+ struct statfs *st)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ p = nfs_initialize_rpc (NFS2PROC_STATFS, cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+
+ if (!err)
+ {
+ p++; /* skip IOSIZE field */
+ st->f_bsize = ntohl (*p);
+ p++;
+ st->f_blocks = ntohl (*p);
+ p++;
+ st->f_bfree = ntohl (*p);
+ p++;
+ st->f_bavail = ntohl (*p);
+ p++;
+ st->f_type = FSTYPE_NFS;
+ st->f_files = 0;
+ st->f_ffree = 0;
+ st->f_fsid = getpid ();
+ st->f_namelen = 0;
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_sync callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
+{
+ /* We are already completely synchronous. */
+ return 0;
+}
+
+/* Implement the netfs_attempt_syncfs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+/* Implement the netfs_attempt_read callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ int *p;
+ void *rpcbuf;
+ size_t trans_len;
+ error_t err;
+ size_t amt, thisamt;
+ int eof;
+
+ for (amt = *len; amt;)
+ {
+ thisamt = amt;
+ if (thisamt > read_size)
+ thisamt = read_size;
+
+ p = nfs_initialize_rpc (NFSPROC_READ (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *(p++) = htonl (offset);
+ *(p++) = htonl (thisamt);
+ if (protocol_version == 2)
+ *(p++) = 0;
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+
+ if (!err || protocol_version == 3)
+ p = process_returned_stat (np, p, !err);
+
+ if (err)
+ {
+ free (rpcbuf);
+ return err;
+ }
+
+ trans_len = ntohl (*p);
+ p++;
+ if (trans_len > thisamt)
+ trans_len = thisamt; /* ??? */
+
+ if (protocol_version == 3)
+ {
+ eof = ntohl (*p);
+ p++;
+ }
+ else
+ eof = (trans_len < thisamt);
+
+ memcpy (data, p, trans_len);
+ free (rpcbuf);
+
+ data += trans_len;
+ offset += trans_len;
+ amt -= trans_len;
+
+ if (eof)
+ {
+ *len -= amt;
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Implement the netfs_attempt_write callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ size_t amt, thisamt;
+ size_t count;
+
+ for (amt = *len; amt;)
+ {
+ thisamt = amt;
+ if (thisamt > write_size)
+ thisamt = write_size;
+
+ p = nfs_initialize_rpc (NFSPROC_WRITE (protocol_version),
+ cred, thisamt, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ if (protocol_version == 2)
+ *(p++) = 0;
+ *(p++) = htonl (offset);
+ if (protocol_version == 2)
+ *(p++) = 0;
+ if (protocol_version == 3)
+ *(p++) = htonl (FILE_SYNC);
+ p = xdr_encode_data (p, data, thisamt);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ if (!err)
+ {
+ if (protocol_version == 3)
+ {
+ count = ntohl (*p);
+ p++;
+ p++; /* ignore COMMITTED */
+ /* ignore verf for now */
+ p += NFS3_WRITEVERFSIZE / sizeof (int);
+ }
+ else
+ /* assume it wrote the whole thing */
+ count = thisamt;
+
+ amt -= count;
+ data += count;
+ offset += count;
+ }
+ }
+
+ free (rpcbuf);
+
+ if (err == EINTR && amt != *len)
+ {
+ *len -= amt;
+ return 0;
+ }
+
+ if (err)
+ {
+ *len = 0;
+ return err;
+ }
+ }
+ return 0;
+}
+
+/* See if NAME exists in DIR for CRED. If so, return EEXIST. */
+error_t
+verify_nonexistent (struct iouser *cred, struct node *dir,
+ char *name)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ /* Don't use the lookup cache for this; we want a full sync to
+ get as close to real exclusive create behavior as possible. */
+
+ assert (protocol_version == 2);
+
+ p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+
+ free (rpcbuf);
+
+ if (!err)
+ return EEXIST;
+ else
+ return 0;
+}
+
+/* Implement the netfs_attempt_lookup callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_lookup (struct iouser *cred, struct node *np,
+ char *name, struct node **newnp)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ char dirhandle[NFS3_FHSIZE];
+ size_t dirlen;
+
+ /* Check the cache first. */
+ *newnp = check_lookup_cache (np, name);
+ if (*newnp)
+ {
+ if (*newnp == (struct node *) -1)
+ {
+ *newnp = 0;
+ return ENOENT;
+ }
+ else
+ return 0;
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ /* Remember the directory handle for later cache use. */
+
+ dirlen = np->nn->handle.size;
+ memcpy (dirhandle, np->nn->handle.data, dirlen);
+
+ pthread_mutex_unlock (&np->lock);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err)
+ {
+ p = xdr_decode_fhandle (p, newnp);
+ p = process_returned_stat (*newnp, p, 1);
+ }
+ if (err)
+ *newnp = 0;
+ if (protocol_version == 3)
+ {
+ if (*newnp)
+ pthread_mutex_unlock (&(*newnp)->lock);
+ pthread_mutex_lock (&np->lock);
+ p = process_returned_stat (np, p, 0); /* XXX Do we have to lock np? */
+ pthread_mutex_unlock (&np->lock);
+ if (*newnp)
+ pthread_mutex_lock (&(*newnp)->lock);
+ }
+ }
+ else
+ *newnp = 0;
+
+ /* Notify the cache of the hit or miss. */
+ enter_lookup_cache (dirhandle, dirlen, *newnp, name);
+
+ free (rpcbuf);
+
+ return err;
+}
+
+/* Implement the netfs_attempt_mkdir callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mkdir (struct iouser *cred, struct node *np,
+ char *name, mode_t mode)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ uid_t owner;
+ struct node *newnp;
+
+ if (cred->uids->num)
+ owner = cred->uids->ids[0];
+ else
+ {
+ err = netfs_validate_stat (np, cred);
+ owner = err ? 0 : np->nn_stat.st_uid;
+ mode &= ~S_ISUID;
+ }
+
+ purge_lookup_cache (np, name, strlen (name));
+
+ p = nfs_initialize_rpc (NFSPROC_MKDIR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+ p = xdr_encode_create_state (p, mode, owner);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+
+ if (!err)
+ {
+ p = xdr_decode_fhandle (p, &newnp);
+ p = process_returned_stat (newnp, p, 1);
+
+ /* Did we set the owner correctly? If not, try, but ignore failures. */
+ if (!netfs_validate_stat (newnp, (struct iouser *) -1)
+ && newnp->nn_stat.st_uid != owner)
+ netfs_attempt_chown ((struct iouser *) -1, newnp, owner,
+ newnp->nn_stat.st_gid);
+
+ /* We don't actually return this. */
+ netfs_nput (newnp);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_rmdir callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_rmdir (struct iouser *cred, struct node *np,
+ char *name)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ /* Should we do the same sort of thing here as with attempt_unlink? */
+
+ purge_lookup_cache (np, name, strlen (name));
+
+ p = nfs_initialize_rpc (NFSPROC_RMDIR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_link callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_link (struct iouser *cred, struct node *dir,
+ struct node *np, char *name, int excl)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err = 0;
+
+ if (!excl)
+ {
+ /* We have no RPC available that will do an atomic replacement,
+ so we settle for second best; just doing an unlink and ignoring
+ any errors. */
+ pthread_mutex_lock (&dir->lock);
+ netfs_attempt_unlink (cred, dir, name);
+ pthread_mutex_unlock (&dir->lock);
+ }
+
+ /* If we have postponed a translator setting on an unlinked node,
+ then here's where we set it, by creating the new node instead of
+ doing a normal link. */
+
+ switch (np->nn->dtrans)
+ {
+ case POSSIBLE:
+ case NOT_POSSIBLE:
+ pthread_mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFSPROC_LINK (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ return errno;
+ }
+
+ pthread_mutex_unlock (&dir->lock);
+
+ pthread_mutex_lock (&np->lock);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ pthread_mutex_unlock (&np->lock);
+
+ pthread_mutex_lock (&dir->lock);
+ purge_lookup_cache (dir, name, strlen (name));
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+ pthread_mutex_unlock (&dir->lock);
+
+ free (rpcbuf);
+
+ break;
+
+ case SYMLINK:
+ pthread_mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFSPROC_SYMLINK (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ return errno;
+ }
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ pthread_mutex_unlock (&dir->lock);
+
+ p = xdr_encode_string (p, name);
+
+ pthread_mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ free (rpcbuf);
+ return err;
+ }
+
+ if (protocol_version == 2)
+ {
+ p = xdr_encode_string (p, np->nn->transarg.name);
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ }
+ else
+ {
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ p = xdr_encode_string (p, np->nn->transarg.name);
+ }
+ pthread_mutex_unlock (&np->lock);
+
+ pthread_mutex_lock (&dir->lock);
+
+ purge_lookup_cache (dir, name, strlen (name));
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+
+ if (protocol_version == 2 && !err)
+ {
+ free (rpcbuf);
+
+ /* NFSPROC_SYMLINK stupidly does not pass back an
+ fhandle, so we have to fetch one now. */
+ p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ return errno;
+ }
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ pthread_mutex_unlock (&dir->lock);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+ if (!err)
+ {
+ pthread_mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ p = process_returned_stat (np, p, 1);
+ pthread_mutex_unlock (&np->lock);
+ }
+ if (err)
+ err = EGRATUITOUS; /* damn */
+ }
+ else if (protocol_version == 3)
+ {
+ if (!err)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ pthread_mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ p = process_returned_stat (np, p, 1);
+ pthread_mutex_unlock (&np->lock);
+ pthread_mutex_lock (&dir->lock);
+ }
+ p = process_wcc_stat (dir, p, !err);
+ pthread_mutex_unlock (&dir->lock);
+ }
+ else
+ pthread_mutex_unlock (&dir->lock);
+ }
+ else
+ pthread_mutex_unlock (&dir->lock);
+
+ free (rpcbuf);
+ break;
+
+ case CHRDEV:
+ case BLKDEV:
+ case FIFO:
+ case SOCK:
+
+ if (protocol_version == 2)
+ {
+ pthread_mutex_lock (&dir->lock);
+ err = verify_nonexistent (cred, dir, name);
+ if (err)
+ return err;
+
+ p = nfs_initialize_rpc (NFSPROC_CREATE (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ return errno;
+ }
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+ pthread_mutex_unlock (&dir->lock);
+
+ pthread_mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ free (rpcbuf);
+ return err;
+ }
+
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ pthread_mutex_unlock (&np->lock);
+
+ pthread_mutex_lock (&dir->lock);
+ purge_lookup_cache (dir, name, strlen (name));
+ pthread_mutex_unlock (&dir->lock); /* XXX Should this really be after the
+ _lengthy_ (blocking) conduct_rpc? */
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+
+ if (!err)
+ {
+ pthread_mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ register_fresh_stat (np, p);
+ pthread_mutex_unlock (&np->lock);
+ }
+
+ free (rpcbuf);
+ }
+ else /* protocol_version != 2 */
+ {
+ pthread_mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFS3PROC_MKNOD, cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ pthread_mutex_unlock (&dir->lock);
+ return errno;
+ }
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+ pthread_mutex_unlock (&dir->lock);
+
+ pthread_mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ free (rpcbuf);
+ return err;
+ }
+ *(p++) = htonl (hurd_mode_to_nfs_type (np->nn_stat.st_mode));
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ if (np->nn->dtrans == BLKDEV || np->nn->dtrans == CHRDEV)
+ {
+ *(p++) = htonl (major (np->nn_stat.st_rdev));
+ *(p++) = htonl (minor (np->nn_stat.st_rdev));
+ }
+ pthread_mutex_unlock (&np->lock);
+
+ purge_lookup_cache (dir, name, strlen (name));
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+
+ if (!err)
+ {
+ pthread_mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ p = process_returned_stat (np, p, 1);
+ pthread_mutex_unlock (&np->lock);
+ }
+ pthread_mutex_lock (&dir->lock);
+ p = process_wcc_stat (dir, p, !err);
+ pthread_mutex_unlock (&dir->lock);
+ }
+ free (rpcbuf);
+ }
+ break;
+ }
+
+ if (err)
+ return err;
+
+ pthread_mutex_lock (&np->lock);
+
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+ np->nn->dtrans = NOT_POSSIBLE;
+
+ /* If there is a dead-dir tag lying around, it's time to delete it now. */
+ if (np->nn->dead_dir)
+ {
+ struct node *dir = np->nn->dead_dir;
+ char *name = np->nn->dead_name;
+ np->nn->dead_dir = 0;
+ np->nn->dead_name = 0;
+ pthread_mutex_unlock (&np->lock);
+
+ pthread_mutex_lock (&dir->lock);
+ netfs_attempt_unlink ((struct iouser *)-1, dir, name);
+ pthread_mutex_unlock (&dir->lock);
+ }
+ else
+ pthread_mutex_unlock (&np->lock);
+
+ return 0;
+}
+
+/* Implement the netfs_attempt_mkfile callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mkfile (struct iouser *cred, struct node *dir,
+ mode_t mode, struct node **newnp)
+{
+ error_t err;
+ char *name;
+ static int n = 0;
+
+ /* This is the best we can do. */
+
+ name = malloc (50);
+ if (! name)
+ return ENOMEM;
+
+ do
+ {
+ sprintf (name, ".nfstmpgnu.%d", n++);
+ err = netfs_attempt_create_file (cred, dir, name, mode, newnp);
+ if (err == EEXIST)
+ pthread_mutex_lock (&dir->lock); /* XXX is this right? does create need this
+ and drop this on error? Doesn't look
+ like it. */
+ }
+ while (err == EEXIST);
+
+ if (err)
+ {
+ free (name);
+ return err;
+ }
+
+ assert (!(*newnp)->nn->dead_dir);
+ assert (!(*newnp)->nn->dead_name);
+ netfs_nref (dir);
+ (*newnp)->nn->dead_dir = dir;
+ (*newnp)->nn->dead_name = name;
+ if ((*newnp)->nn->dtrans == NOT_POSSIBLE)
+ (*newnp)->nn->dtrans = POSSIBLE;
+ return 0;
+}
+
+/* Implement the netfs_attempt_create_file callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_create_file (struct iouser *cred, struct node *np,
+ char *name, mode_t mode, struct node **newnp)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ uid_t owner;
+
+ if (cred->uids->num)
+ owner = cred->uids->ids[0];
+ else
+ {
+ err = netfs_validate_stat (np, cred);
+ owner = err ? 0 : np->nn_stat.st_uid;
+ mode &= ~S_ISUID;
+ }
+
+ /* RFC 1094 says that create is always exclusive. But Sun doesn't
+ actually *implement* the spec. No, of course not. So we have to do
+ it for them. */
+ if (protocol_version == 2)
+ {
+ err = verify_nonexistent (cred, np, name);
+ if (err)
+ {
+ pthread_mutex_unlock (&np->lock);
+ return err;
+ }
+ }
+
+ purge_lookup_cache (np, name, strlen (name));
+
+ p = nfs_initialize_rpc (NFSPROC_CREATE (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+ if (protocol_version == 3)
+ {
+ /* We happen to know this is where the XID is. */
+ int verf = *(int *)rpcbuf;
+
+ *(p++) = ntohl (EXCLUSIVE);
+ /* 8 byte verf */
+ *(p++) = ntohl (verf);
+ p++;
+ }
+ else
+ p = xdr_encode_create_state (p, mode, owner);
+
+ err = conduct_rpc (&rpcbuf, &p);
+
+ pthread_mutex_unlock (&np->lock);
+
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (!err)
+ {
+ p = xdr_decode_fhandle (p, newnp);
+ p = process_returned_stat (*newnp, p, 1);
+ }
+ if (err)
+ *newnp = 0;
+ if (protocol_version == 3)
+ {
+ if (*newnp)
+ pthread_mutex_unlock (&(*newnp)->lock);
+ pthread_mutex_lock (&np->lock);
+ p = process_wcc_stat (np, p, 1);
+ pthread_mutex_unlock (&np->lock);
+ if (*newnp)
+ pthread_mutex_lock (&(*newnp)->lock);
+ }
+
+ if (*newnp && !netfs_validate_stat (*newnp, (struct iouser *) -1)
+ && (*newnp)->nn_stat.st_uid != owner)
+ netfs_attempt_chown ((struct iouser *) -1, *newnp, owner, (*newnp)->nn_stat.st_gid);
+ }
+ else
+ *newnp = 0;
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_unlink callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_unlink (struct iouser *cred, struct node *dir,
+ char *name)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ struct node *np;
+
+ /* First lookup the node being removed */
+ err = netfs_attempt_lookup (cred, dir, name, &np);
+ if (err)
+ {
+ pthread_mutex_lock (&dir->lock);
+ return err;
+ }
+
+ /* Restore the locks to sanity. */
+ pthread_mutex_unlock (&np->lock);
+ pthread_mutex_lock (&dir->lock);
+
+ /* Purge the cache of entries for this node, so that we don't
+ regard cache-held references as live. */
+ purge_lookup_cache_node (np);
+
+ /* See if there are any other users of this node than the
+ one we just got; if so, we must give this file another link
+ so that when we delete the one we are asked for it doesn't go
+ away entirely. */
+ if (np->references > 1)
+ {
+ char *newname = 0;
+ int n = 0;
+
+ pthread_mutex_unlock (&dir->lock);
+
+ newname = malloc (50);
+ if (! newname)
+ {
+ pthread_mutex_lock (&dir->lock);
+ netfs_nrele (np); /* XXX Is this the correct thing to do? */
+ return ENOMEM;
+ }
+
+ do
+ {
+ sprintf (newname, ".nfs%txgnu.%d", (ptrdiff_t) np, n++);
+ err = netfs_attempt_link (cred, dir, np, newname, 1);
+ }
+ while (err == EEXIST);
+
+ if (err)
+ {
+ free (newname);
+ pthread_mutex_lock (&dir->lock);
+ netfs_nrele (np);
+ return err;
+ }
+
+ /* Write down what name we gave it; we'll delete this when all
+ our uses vanish. */
+ pthread_mutex_lock (&np->lock);
+
+ if (np->nn->dead_dir)
+ netfs_nrele (np->nn->dead_dir);
+ netfs_nref (dir);
+ np->nn->dead_dir = dir;
+ if (np->nn->dead_name)
+ free (np->nn->dead_name);
+ np->nn->dead_name = newname;
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ np->nn->dtrans = POSSIBLE;
+
+ netfs_nput (np);
+ pthread_mutex_lock (&dir->lock);
+ }
+ else
+ netfs_nrele (np);
+
+ p = nfs_initialize_rpc (NFSPROC_REMOVE (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (protocol_version == 3)
+ p = process_wcc_stat (dir, p, !err);
+ }
+
+ free (rpcbuf);
+
+ return err;
+}
+
+/* Implement the netfs_attempt_rename callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_rename (struct iouser *cred, struct node *fromdir,
+ char *fromname, struct node *todir, char *toname,
+ int excl)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if (excl)
+ {
+ struct node *np;
+
+ /* Just do a lookup/link/unlink sequence. */
+
+ pthread_mutex_lock (&fromdir->lock);
+ err = netfs_attempt_lookup (cred, fromdir, fromname, &np);
+ pthread_mutex_unlock (&fromdir->lock);
+ if (err)
+ return err;
+
+ err = netfs_attempt_link (cred, todir, np, toname, 1);
+ netfs_nput (np);
+ if (err)
+ return err;
+
+ pthread_mutex_lock (&fromdir->lock);
+ err = netfs_attempt_unlink (cred, fromdir, fromname);
+ pthread_mutex_unlock (&fromdir->lock);
+
+ /* If the unlink failed, then back out the link */
+ if (err)
+ {
+ pthread_mutex_lock (&todir->lock);
+ netfs_attempt_unlink (cred, todir, toname);
+ pthread_mutex_unlock (&todir->lock);
+ return err;
+ }
+
+ return 0;
+ }
+
+ pthread_mutex_lock (&fromdir->lock);
+ purge_lookup_cache (fromdir, fromname, strlen (fromname));
+ p = nfs_initialize_rpc (NFSPROC_RENAME (protocol_version),
+ cred, 0, &rpcbuf, fromdir, -1);
+ if (! p)
+ {
+ pthread_mutex_unlock (&fromdir->lock);
+ return errno;
+ }
+
+ p = xdr_encode_fhandle (p, &fromdir->nn->handle);
+ p = xdr_encode_string (p, fromname);
+ pthread_mutex_unlock (&fromdir->lock);
+
+ pthread_mutex_lock (&todir->lock);
+ purge_lookup_cache (todir, toname, strlen (toname));
+ p = xdr_encode_fhandle (p, &todir->nn->handle);
+ p = xdr_encode_string (p, toname);
+ pthread_mutex_unlock (&todir->lock);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (protocol_version == 3) /* XXX Should we add `&& !err' ? */
+ {
+ pthread_mutex_lock (&fromdir->lock);
+ p = process_wcc_stat (fromdir, p, !err);
+ p = process_wcc_stat (todir, p, !err);
+ }
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_readlink callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_readlink (struct iouser *cred, struct node *np,
+ char *buf)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if (np->nn->dtrans == SYMLINK)
+ {
+ strcpy (buf, np->nn->transarg.name);
+ return 0;
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_READLINK (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ if (protocol_version == 3)
+ p = process_returned_stat (np, p, 0);
+ if (!err)
+ p = xdr_decode_string (p, buf);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_check_open_permissions callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_check_open_permissions (struct iouser *cred, struct node *np,
+ int flags, int newnode)
+{
+ int modes;
+
+ if (newnode || (flags & (O_READ|O_WRITE|O_EXEC)) == 0)
+ return 0;
+
+ netfs_report_access (cred, np, &modes);
+ if ((flags & (O_READ|O_WRITE|O_EXEC)) == (flags & modes))
+ return 0;
+ else
+ return EACCES;
+}
+
+/* Implement the netfs_report_access callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_report_access (struct iouser *cred,
+ struct node *np,
+ int *types)
+{
+ error_t err;
+
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+
+ if (protocol_version == 2)
+ {
+ /* Hope the server means the same thing for the bits as we do. */
+ *types = 0;
+ if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ return 0;
+ }
+ else
+ {
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ int ret;
+ int write_check, execute_check;
+
+ if (S_ISDIR (np->nn_stat.st_mode))
+ {
+ write_check = ACCESS3_MODIFY | ACCESS3_DELETE | ACCESS3_EXTEND;
+ execute_check = ACCESS3_LOOKUP;
+ }
+ else
+ {
+ write_check = ACCESS3_MODIFY;
+ execute_check = ACCESS3_EXECUTE;
+ }
+
+ p = nfs_initialize_rpc (NFS3PROC_ACCESS, cred, 0, &rpcbuf, np, -1);
+ if (! p)
+ return errno;
+
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *(p++) = htonl (ACCESS3_READ | write_check | execute_check);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ p = process_returned_stat (np, p, 0); /* XXX Should this be
+ protected by the
+ if (!err) ? */
+ if (!err)
+ {
+ ret = ntohl (*p);
+ p++;
+ *types = ((ret & ACCESS3_READ ? O_READ : 0)
+ | (ret & write_check ? O_WRITE : 0)
+ | (ret & execute_check ? O_EXEC : 0));
+ }
+ }
+ return err;
+ }
+}
+
+/* These definitions have unfortunate side effects, don't use them,
+ clever though they are. */
+#if 0
+/* Implement the netfs_check_open_permissions callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_check_open_permissions (struct iouser *cred, struct node *np,
+ int flags, int newnode)
+{
+ char byte;
+ error_t err;
+ size_t len;
+
+ /* Sun derived nfs client implementations attempt to reproduce the
+ server's permission restrictions by hoping they look like Unix,
+ and using that to give errors at open time. Sadly, that loses
+ here. So instead what we do is try and do what the user
+ requested; if we can't, then we fail. Otherwise, we allow the
+ open, but acknowledge that the server might still give an error
+ later. (Even with our check, the server can revoke access, thus
+ violiting Posix semantics; this means that erring on the side of
+ permitting illegal opens won't harm otherwise correct programs,
+ because they need to deal with revocation anyway.) We thus here
+ have the advantage of working correctly with servers that allow
+ things Unix denies. */
+
+ if ((flags & O_READ) == 0
+ && (flags & O_WRITE) == 0
+ && (flags & O_EXEC) == 0)
+ return 0;
+
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+
+ switch (np->nn_stat.st_mode & S_IFMT)
+ {
+ /* Don't know how to check, so return provisional success. */
+ default:
+ return 0;
+
+ case S_IFREG:
+ len = 1;
+ err = netfs_attempt_read (cred, np, 0, &len, &byte);
+ if (err)
+ {
+ if ((flags & O_READ) || (flags & O_EXEC))
+ return err;
+ else
+ /* If we couldn't read a byte, but the user wasn't actually asking
+ for read, then we shouldn't inhibit the open now. */
+ return 0;
+ }
+
+ if (len != 1)
+ /* The file is empty; reads are known to be OK, but writes can't be
+ tested, so no matter what, return success. */
+ return 0;
+
+ if (flags & O_WRITE)
+ {
+ err = netfs_attempt_write (cred, np, 0, &len, &byte);
+ return err;
+ }
+
+ /* Try as we might, we couldn't get the server to bump us, so
+ give (provisional) success. */
+ return 0;
+
+ case S_IFDIR:
+ if (flags & O_READ)
+ {
+ void *rpcbuf;
+ int *p;
+
+ /* Issue a readdir request; if it fails, then we can
+ return failure. Otherwise, succeed. */
+ p = nfs_initialize_rpc (NFSPROC_READDIR, cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *(p++) = 0;
+ *(p++) = htonl (50);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+ free (rpcbuf);
+
+ if (err)
+ return err;
+ }
+ return 0;
+ }
+}
+
+/* Implement the netfs_report_access callback as described in
+ <hurd/netfs.h>. */
+void
+netfs_report_access (struct iouser *cred,
+ struct node *np,
+ int *types)
+{
+ char byte;
+ error_t err;
+ size_t len;
+
+ /* Much the same logic as netfs_check_open_permissions, except that
+ here we err on the side of denying access, and that we always
+ have to check everything. */
+
+ *types = 0;
+
+ len = 1;
+ err = netfs_attempt_read (cred, np, 0, &len, &byte);
+ if (err)
+ return;
+ assert (len == 1 || len == 0);
+
+ *types |= O_READ | O_EXEC;
+
+ if (len == 1)
+ {
+ err = netfs_attempt_write (cred, np, 0, &len, &byte);
+ if (!err)
+ *types |= O_WRITE;
+ }
+ else
+ {
+ /* Oh, ugh. We have to try and write a byte and then truncate
+ back. God help us if the file becomes unwritable in-between.
+ But because of the use of this function (by setuid programs
+ wanting to see if they should write user's files) we must
+ check this and not just return a presumptive error. */
+ byte = 0;
+ err = netfs_attempt_write (cred, np, 0, &len, &byte);
+ if (!err)
+ *types |= O_WRITE;
+ netfs_attempt_set_size (cred, np, 0);
+ }
+}
+#endif
+
+/* Fetch the complete contents of DIR into a buffer of directs. Set
+ *BUFP to that buffer. *BUFP must be freed by the caller when no
+ longer needed. If an error occurs, don't touch *BUFP and return
+ the error code. Set BUFSIZEP to the amount of data used inside
+ *BUFP and TOTALENTRIES to the total number of entries copied. */
+static error_t
+fetch_directory (struct iouser *cred, struct node *dir,
+ void **bufp, size_t *bufsizep, int *totalentries)
+{
+ void *buf;
+ int cookie;
+ int *p;
+ void *rpcbuf;
+ struct dirent *entry;
+ void *bp;
+ int bufmalloced;
+ int eof;
+ error_t err;
+ int isnext;
+
+ bufmalloced = read_size;
+
+ buf = malloc (bufmalloced);
+ if (! buf)
+ return ENOMEM;
+
+ bp = buf;
+ cookie = 0;
+ eof = 0;
+ *totalentries = 0;
+
+ while (!eof)
+ {
+ /* Fetch new directory entries */
+ p = nfs_initialize_rpc (NFSPROC_READDIR (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ if (! p)
+ {
+ free (buf);
+ return errno;
+ }
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ *(p++) = cookie;
+ *(p++) = ntohl (read_size);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p));
+ p++;
+ }
+ if (err)
+ {
+ free (buf);
+ return err;
+ }
+
+ isnext = ntohl (*p);
+ p++;
+
+ /* Now copy them one at a time. */
+ while (isnext)
+ {
+ ino_t fileno;
+ int namlen;
+ int reclen;
+
+ fileno = ntohl (*p);
+ p++;
+ namlen = ntohl (*p);
+ p++;
+
+ /* There's a hidden +1 here for the null byte and -1 because d_name
+ has a size of one already in the sizeof. */
+ reclen = sizeof (struct dirent) + namlen;
+ reclen = (reclen + 3) & ~3; /* make it a multiple of four */
+
+ /* Expand buffer if necessary */
+ if (bp + reclen > buf + bufmalloced)
+ {
+ char *newbuf;
+
+ newbuf = realloc (buf, bufmalloced *= 2);
+ assert (newbuf);
+ if (newbuf != buf)
+ bp = newbuf + (bp - buf);
+ buf = newbuf;
+ }
+
+ /* Fill in new entry */
+ entry = (struct dirent *) bp;
+ entry->d_fileno = fileno;
+ entry->d_reclen = reclen;
+ entry->d_type = DT_UNKNOWN;
+ entry->d_namlen = namlen;
+ memcpy (entry->d_name, p, namlen);
+ entry->d_name[namlen] = '\0';
+
+ p += INTSIZE (namlen);
+ bp = bp + entry->d_reclen;
+
+ ++*totalentries;
+
+ cookie = *(p++);
+ isnext = ntohl (*p);
+ p++;
+ }
+
+ eof = ntohl (*p);
+ p++;
+ free (rpcbuf);
+ }
+
+ /* Return it all to the user */
+ *bufp = buf;
+ *bufsizep = bufmalloced;
+ return 0;
+}
+
+
+/* Implement the netfs_get_directs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *np,
+ int entry, int nentries, char **data,
+ mach_msg_type_number_t *datacnt,
+ vm_size_t bufsiz, int *amt)
+{
+ void *buf;
+ size_t our_bufsiz, allocsize;
+ void *bp;
+ char *userdp;
+ error_t err;
+ int totalentries;
+ int thisentry;
+
+ err = fetch_directory (cred, np, &buf, &our_bufsiz, &totalentries);
+ if (err)
+ return err;
+
+ /* Allocate enough space to hold the maximum we might return. */
+ if (!bufsiz || bufsiz > our_bufsiz)
+ allocsize = round_page (our_bufsiz);
+ else
+ allocsize = round_page (bufsiz);
+ if (allocsize > *datacnt)
+ *data = mmap (0, allocsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+ /* Skip ahead to the correct entry. */
+ bp = buf;
+ for (thisentry = 0; thisentry < entry;)
+ {
+ struct dirent *entry = (struct dirent *) bp;
+ bp += entry->d_reclen;
+ thisentry++;
+ }
+
+ /* Now copy them one at a time */
+ {
+ int entries_copied;
+
+ for (entries_copied = 0, userdp = *data;
+ (nentries == -1 || entries_copied < nentries)
+ && (!bufsiz || userdp - *data < bufsiz)
+ && thisentry < totalentries;)
+ {
+ struct dirent *entry = (struct dirent *) bp;
+ memcpy (userdp, bp, entry->d_reclen);
+ bp += entry->d_reclen;
+ userdp += entry->d_reclen;
+ entries_copied++;
+ thisentry++;
+ }
+ *amt = entries_copied;
+ }
+
+ free (buf);
+
+ /* If we allocated the buffer ourselves, but didn't use
+ all the pages, free the extra. */
+ if (allocsize > *datacnt
+ && round_page (userdp - *data) < round_page (allocsize))
+ munmap ((caddr_t) round_page (userdp),
+ round_page (allocsize) - round_page (userdp - *data));
+
+ *datacnt = userdp - *data;
+ return 0;
+}
+
+
+/* Implement the netfs_attempt_mksymlink callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mksymlink (struct iouser *cred,
+ struct node *np,
+ char *arg)
+{
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ return EOPNOTSUPP;
+
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+
+ np->nn->transarg.name = malloc (strlen (arg) + 1);
+ strcpy (np->nn->transarg.name, arg);
+ np->nn->dtrans = SYMLINK;
+ np->nn->stat_updated = 0;
+ return 0;
+}
+
+/* Implement the netfs_attempt_mkdev callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mkdev (struct iouser *cred,
+ struct node *np,
+ mode_t type,
+ dev_t indexes)
+{
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ return EOPNOTSUPP;
+
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+
+ np->nn->transarg.indexes = indexes;
+ if (type == S_IFBLK)
+ np->nn->dtrans = BLKDEV;
+ else
+ np->nn->dtrans = CHRDEV;
+ np->nn->stat_updated = 0;
+ return 0;
+}
diff --git a/nfs/rpc.c b/nfs/rpc.c
new file mode 100644
index 00000000..c0d0290e
--- /dev/null
+++ b/nfs/rpc.c
@@ -0,0 +1,425 @@
+/* rpc.c - SunRPC management for NFS client.
+ Copyright (C) 1994, 1995, 1996, 1997, 2002 Free Software Foundation
+
+ 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. */
+
+#include "nfs.h"
+
+/* Needed in order to get the RPC header files to include correctly. */
+#undef TRUE
+#undef FALSE
+#define malloc spoiufasdf /* Avoid bogus definition in rpc/types.h. */
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/rpc_msg.h>
+#include <rpc/auth_unix.h>
+
+#undef malloc /* Get rid of the sun block. */
+
+#include <netinet/in.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/* One of these exists for each pending RPC. */
+struct rpc_list
+{
+ struct rpc_list *next, **prevp;
+ void *reply;
+};
+
+/* A list of all pending RPCs. */
+static struct rpc_list *outstanding_rpcs;
+
+/* Wake up this condition when an outstanding RPC has received a reply
+ or we should check for timeouts. */
+static pthread_cond_t rpc_wakeup = PTHREAD_COND_INITIALIZER;
+
+/* Lock the global data and the REPLY fields of outstanding RPC's. */
+static pthread_mutex_t outstanding_lock = PTHREAD_MUTEX_INITIALIZER;
+
+
+
+/* Generate and return a new transaction ID. */
+static inline int
+generate_xid ()
+{
+ static int nextxid;
+
+ if (nextxid == 0)
+ nextxid = mapped_time->seconds;
+
+ return nextxid++;
+}
+
+/* Set up an RPC for procdeure RPC_PROC for talking to the server
+ PROGRAM of version VERSION. Allocate storage with malloc and point
+ *BUF at it; caller must free this when done. Allocate at least LEN
+ bytes more than the usual amount for the RPC. Initialize the RPC
+ credential structure with UID, GID, and SECOND_GID; any of these
+ may be -1 to indicate that it does not apply, however, exactly zero
+ or two of UID and GID must be -1. The returned address is a pointer
+ to the start of the payload. If NULL is returned, an error occurred
+ and the code is set in errno. */
+int *
+initialize_rpc (int program, int version, int rpc_proc,
+ size_t len, void **bufp,
+ uid_t uid, gid_t gid, gid_t second_gid)
+{
+ void *buf;
+ int *p, *lenaddr;
+ struct rpc_list *hdr;
+
+ buf = malloc (len + 1024);
+ if (! buf)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* First the struct rpc_list bit. */
+ hdr = buf;
+ hdr->reply = 0;
+
+ p = buf + sizeof (struct rpc_list);
+
+ /* RPC header */
+ *(p++) = htonl (generate_xid ());
+ *(p++) = htonl (CALL);
+ *(p++) = htonl (RPC_MSG_VERSION);
+ *(p++) = htonl (program);
+ *(p++) = htonl (version);
+ *(p++) = htonl (rpc_proc);
+
+ assert ((uid == -1) == (gid == -1));
+
+ if (uid == -1)
+ {
+ /* No authentication */
+ *(p++) = htonl (AUTH_NONE);
+ *(p++) = 0;
+ }
+ else
+ {
+ /* Unixy authentication */
+ *(p++) = htonl (AUTH_UNIX);
+ /* The length of the message. We do not yet know what this
+ is, so, just remember where we should put it when we know */
+ lenaddr = p++;
+ *(p++) = htonl (mapped_time->seconds);
+ p = xdr_encode_string (p, hostname);
+ *(p++) = htonl (uid);
+ *(p++) = htonl (gid);
+ if (second_gid == -1)
+ *(p++) = 0;
+ else
+ {
+ *(p++) = htonl (1);
+ *(p++) = htonl (second_gid);
+ }
+ *lenaddr = htonl ((p - (lenaddr + 1)) * sizeof (int));
+ }
+
+ /* VERF field */
+ *(p++) = htonl (AUTH_NONE);
+ *(p++) = 0;
+
+ *bufp = buf;
+ return p;
+}
+
+/* Remove HDR from the list of pending RPC's. The rpc_list's lock
+ (OUTSTANDING_LOCK) must be held. */
+static inline void
+unlink_rpc (struct rpc_list *hdr)
+{
+ *hdr->prevp = hdr->next;
+ if (hdr->next)
+ hdr->next->prevp = hdr->prevp;
+}
+
+/* Insert HDR at the head of the LIST. The rpc_list's lock
+ (OUTSTANDING_LOCK) must be held. */
+static inline void
+link_rpc (struct rpc_list **list, struct rpc_list *hdr)
+{
+ hdr->next = *list;
+ if (hdr->next)
+ hdr->next->prevp = &hdr->next;
+ hdr->prevp = list;
+ *list = hdr;
+}
+
+/* Send the specified RPC message. *RPCBUF is the initialized buffer
+ from a previous initialize_rpc call; *PP, the payload, points past
+ the filledin args. Set *PP to the address of the reply contents
+ themselves. The user will be expected to free *RPCBUF (which will
+ have changed) when done with the reply contents. The old value of
+ *RPCBUF will be freed by this routine. */
+error_t
+conduct_rpc (void **rpcbuf, int **pp)
+{
+ struct rpc_list *hdr = *rpcbuf;
+ error_t err;
+ size_t cc, nc;
+ int timeout = initial_transmit_timeout;
+ time_t lasttrans;
+ int ntransmit = 0;
+ int *p;
+ int xid;
+ int n;
+ int cancel;
+
+ pthread_mutex_lock (&outstanding_lock);
+
+ link_rpc (&outstanding_rpcs, hdr);
+
+ xid = * (int *) (*rpcbuf + sizeof (struct rpc_list));
+
+ do
+ {
+ /* If we've sent enough, give up. */
+ if (mounted_soft && ntransmit == soft_retries)
+ {
+ unlink_rpc (hdr);
+ pthread_mutex_unlock (&outstanding_lock);
+ return ETIMEDOUT;
+ }
+
+ /* Issue the RPC. */
+ lasttrans = mapped_time->seconds;
+ ntransmit++;
+ nc = (void *) *pp - *rpcbuf - sizeof (struct rpc_list);
+ cc = write (main_udp_socket, *rpcbuf + sizeof (struct rpc_list), nc);
+ if (cc == -1)
+ {
+ unlink_rpc (hdr);
+ pthread_mutex_unlock (&outstanding_lock);
+ return errno;
+ }
+ else
+ assert (cc == nc);
+
+ /* Wait for reply. */
+ cancel = 0;
+ while (!hdr->reply
+ && (mapped_time->seconds - lasttrans < timeout)
+ && !cancel)
+ cancel = pthread_hurd_cond_wait_np (&rpc_wakeup, &outstanding_lock);
+
+ if (cancel)
+ {
+ unlink_rpc (hdr);
+ pthread_mutex_unlock (&outstanding_lock);
+ return EINTR;
+ }
+
+ /* hdr->reply will have been filled in by rpc_receive_thread,
+ if it has been filled in, then the rpc has been fulfilled,
+ otherwise, retransmit and continue to wait. */
+ if (!hdr->reply)
+ {
+ timeout *= 2;
+ if (timeout > max_transmit_timeout)
+ timeout = max_transmit_timeout;
+ }
+ }
+ while (!hdr->reply);
+
+ pthread_mutex_unlock (&outstanding_lock);
+
+ /* Switch to the reply buffer. */
+ *rpcbuf = hdr->reply;
+ free (hdr);
+
+ /* Process the reply, dissecting errors. When we're done and if
+ there is no error, set *PP to the rpc return contents. */
+ p = (int *) *rpcbuf;
+
+ /* If the transmition id does not match that in the message,
+ something strange happened in rpc_receive_thread. */
+ assert (*p == xid);
+ p++;
+
+ switch (ntohl (*p))
+ {
+ default:
+ err = EBADRPC;
+ break;
+
+ case REPLY:
+ p++;
+ switch (ntohl (*p))
+ {
+ default:
+ err = EBADRPC;
+ break;
+
+ case MSG_DENIED:
+ p++;
+ switch (ntohl (*p))
+ {
+ default:
+ err = EBADRPC;
+ break;
+
+ case RPC_MISMATCH:
+ err = ERPCMISMATCH;
+ break;
+
+ case AUTH_ERROR:
+ p++;
+ switch (ntohl (*p))
+ {
+ case AUTH_BADCRED:
+ case AUTH_REJECTEDCRED:
+ err = EAUTH;
+ break;
+
+ case AUTH_TOOWEAK:
+ err = ENEEDAUTH;
+ break;
+
+ case AUTH_BADVERF:
+ case AUTH_REJECTEDVERF:
+ default:
+ err = EBADRPC;
+ break;
+ }
+ break;
+ }
+ break;
+
+ case MSG_ACCEPTED:
+ p++;
+
+ /* Process VERF field. */
+ p++; /* Skip verf type. */
+ n = ntohl (*p); /* Size of verf. */
+ p++;
+ p += INTSIZE (n); /* Skip verf itself. */
+
+ switch (ntohl (*p))
+ {
+ default:
+ case GARBAGE_ARGS:
+ err = EBADRPC;
+ break;
+
+ case PROG_UNAVAIL:
+ err = EPROGUNAVAIL;
+ break;
+
+ case PROG_MISMATCH:
+ err = EPROGMISMATCH;
+ break;
+
+ case PROC_UNAVAIL:
+ err = EPROCUNAVAIL;
+ break;
+
+ case SUCCESS:
+ p++;
+ *pp = p;
+ err = 0;
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ return err;
+}
+
+/* Dedicated thread to signal those waiting on rpc_wakeup
+ once a second. */
+void *
+timeout_service_thread (void *arg)
+{
+ (void) arg;
+
+ while (1)
+ {
+ sleep (1);
+ pthread_mutex_lock (&outstanding_lock);
+ pthread_cond_broadcast (&rpc_wakeup);
+ pthread_mutex_unlock (&outstanding_lock);
+ }
+
+ return NULL;
+}
+
+/* Dedicate thread to receive RPC replies, register them on the queue
+ of pending wakeups, and deal appropriately. */
+void *
+rpc_receive_thread (void *arg)
+{
+ void *buf;
+
+ (void) arg;
+
+ /* Allocate a receive buffer. */
+ buf = malloc (1024 + read_size);
+ assert (buf);
+
+ while (1)
+ {
+ int cc = read (main_udp_socket, buf, 1024 + read_size);
+ if (cc == -1)
+ {
+ error (0, errno, "nfs read");
+ continue;
+ }
+ else
+ {
+ struct rpc_list *r;
+ int xid = *(int *)buf;
+
+ pthread_mutex_lock (&outstanding_lock);
+
+ /* Find the rpc that we just fulfilled. */
+ for (r = outstanding_rpcs; r; r = r->next)
+ {
+ if (* (int *) &r[1] == xid)
+ {
+ unlink_rpc (r);
+ r->reply = buf;
+ pthread_cond_broadcast (&rpc_wakeup);
+ break;
+ }
+ }
+#if 0
+ if (! r)
+ fprintf (stderr, "NFS dropping reply xid %d\n", xid);
+#endif
+ pthread_mutex_unlock (&outstanding_lock);
+
+ /* If r is not null then we had a message from a pending
+ (i.e. known) rpc. Thus, it was fulfilled and if we want
+ to get another request, a new buffer is needed. */
+ if (r)
+ {
+ buf = malloc (1024 + read_size);
+ assert (buf);
+ }
+ }
+ }
+
+ return NULL;
+}
diff --git a/nfs/storage-info.c b/nfs/storage-info.c
new file mode 100644
index 00000000..7427b3d8
--- /dev/null
+++ b/nfs/storage-info.c
@@ -0,0 +1,104 @@
+/* file_get_storage_info RPC for NFS client filesystem
+ 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 "nfs.h"
+#include <hurd/netfs.h>
+#include <stdio.h>
+
+error_t
+netfs_file_get_storage_info (struct iouser *cred,
+ struct node *np,
+ mach_port_t **ports,
+ mach_msg_type_name_t *ports_type,
+ 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)
+{
+ int name_len, fhpos;
+ error_t err;
+
+ inline int fmt (size_t buflen)
+ {
+ return snprintf (*data, buflen,
+ "nfsv%u://%s:%u/%n%*c?rsize=%u&wsize=%u",
+ protocol_version, mounted_hostname, mounted_nfs_port,
+ &fhpos, (int) (np->nn->handle.size * 2),
+ 'X', /* filled below */
+ read_size, write_size);
+ }
+
+ /* We return the file size, so make sure we have it up to date now. */
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+
+ /* Format the name, and then do it again if the buffer was too short. */
+ name_len = fmt (*data_len);
+ if (name_len < 0)
+ return errno;
+ ++name_len; /* Include the terminating null. */
+ if (name_len <= *data_len)
+ *data_len = name_len;
+ else
+ {
+ *data = mmap (0, name_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ return errno;
+ *data_len = fmt (name_len) + 1;
+ assert (*data_len == name_len);
+ }
+
+ /* Now fill in the file handle data in hexadecimal. */
+ {
+ static const char hexdigits[] = "0123456789abcdef";
+ size_t i;
+ for (i = 0; i < np->nn->handle.size; ++i)
+ {
+ (*data)[fhpos++] = hexdigits[(uint8_t)np->nn->handle.data[i] >> 4];
+ (*data)[fhpos++] = hexdigits[(uint8_t)np->nn->handle.data[i] & 0xf];
+ }
+ }
+
+ /* Now fill in the rest of the canonical-form storage-info data, which
+ just describes a single run of the file's size, a block-size of one
+ byte, and our URL as the name for the network store type. */
+
+ *num_ports = 0;
+ *ports_type = MACH_MSG_TYPE_COPY_SEND;
+
+ assert (*num_offsets >= 2); /* mig always gives us some */
+ *num_offsets = 2;
+ (*offsets)[0] = 0;
+ (*offsets)[1] = np->nn_stat.st_size;
+
+ assert (*num_ints >= 6); /* mig always gives us some */
+ *num_ints = 1;
+ (*ints)[0] = STORAGE_NETWORK;
+ (*ints)[1] = 0; /* XXX readonly if we supported it */
+ (*ints)[2] = 1; /* block size */
+ (*ints)[3] = 1; /* 1 run in offsets list */
+ (*ints)[4] = name_len;
+ (*ints)[5] = 0; /* misc len */
+
+ return 0;
+}