summaryrefslogtreecommitdiff
path: root/nfsd
diff options
context:
space:
mode:
Diffstat (limited to 'nfsd')
-rw-r--r--nfsd/Makefile34
-rw-r--r--nfsd/cache.c496
-rw-r--r--nfsd/fsys.c192
-rw-r--r--nfsd/loop.c233
-rw-r--r--nfsd/main.c78
-rw-r--r--nfsd/nfsd.h128
-rw-r--r--nfsd/ops.c652
-rw-r--r--nfsd/proctables.c57
-rw-r--r--nfsd/xdr.c204
9 files changed, 2074 insertions, 0 deletions
diff --git a/nfsd/Makefile b/nfsd/Makefile
new file mode 100644
index 00000000..4460b4ba
--- /dev/null
+++ b/nfsd/Makefile
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 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.
+
+dir := nfsd
+makemode := utility
+
+SRCS = cache.c loop.c main.c ops.c fsys.c xdr.c
+OBJS = $(subst .c,.o,$(SRCS))
+LCLHDRS = nfsd.h
+target = nfsd
+installationdir = $(sbindir)
+
+include ../Makeconf
+
+CPPFLAGS += -DLOCALSTATEDIR=$(localstatedir)
+
+nfsd: ../libthreads/libthreads.a ../libshouldbeinlibc/libshouldbeinlibc.a \ No newline at end of file
diff --git a/nfsd/cache.c b/nfsd/cache.c
new file mode 100644
index 00000000..745a7c8e
--- /dev/null
+++ b/nfsd/cache.c
@@ -0,0 +1,496 @@
+/*
+ Copyright (C) 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 <string.h>
+#include <hurd/fsys.h>
+#include <assert.h>
+#include <string.h>
+#include "nfsd.h"
+
+
+#undef TRUE
+#undef FALSE
+#define malloc spoogie_woogie /* ugh^2. */
+#include <rpc/types.h>
+#include <rpc/auth.h>
+#include <rpc/auth_unix.h>
+#undef malloc
+
+#define IDHASH_TABLE_SIZE 1024
+#define FHHASH_TABLE_SIZE 1024
+#define REPLYHASH_TABLE_SIZE 1024
+
+
+static struct idspec *idhashtable[IDHASH_TABLE_SIZE];
+spin_lock_t idhashlock = SPIN_LOCK_INITIALIZER;
+static int nfreeids;
+static int leastidlastuse;
+
+/* Compare I against the specified set of users/groups. */
+/* Use of int in decl of UIDS and GIDS is correct here; that's
+ the NFS type because they come in in known 32 bit slots. */
+static int
+idspec_compare (struct idspec *i, int nuids, int ngids,
+ int *uids, int *gids)
+{
+ if (i->nuids != nuids
+ || i->ngids != ngids)
+ return 0;
+
+ assert (sizeof (int) == sizeof (uid_t));
+
+ if (bcmp (i->uids, uids, nuids * sizeof (uid_t))
+ || bcmp (i->gids, gids, ngids * sizeof (gid_t)))
+ return 0;
+
+ return 1;
+}
+
+/* Compute a hash value for a given user spec */
+static int
+idspec_hash (int nuids, int ngids, int *uids, int *gids)
+{
+ int hash, n;
+
+ hash = nuids + ngids;
+ for (n = 0; n < ngids; n++)
+ hash += gids[n];
+ for (n = 0; n < nuids; n++)
+ hash += uids[n];
+ hash %= IDHASH_TABLE_SIZE;
+ return hash;
+}
+
+/* Lookup a user spec in the hash table and allocate a reference */
+static struct idspec *
+idspec_lookup (int nuids, int ngids, int *uids, int *gids)
+{
+ int hash;
+ struct idspec *i;
+
+ hash = idspec_hash (nuids, ngids, uids, gids);
+
+ spin_lock (&idhashlock);
+ for (i = idhashtable[hash]; i; i = i->next)
+ if (idspec_compare (i, nuids, ngids, uids, gids))
+ {
+ i->references++;
+ if (i->references == 1)
+ nfreeids--;
+ spin_unlock (&idhashlock);
+ return i;
+ }
+
+ assert (sizeof (uid_t) == sizeof (int));
+ i = malloc (sizeof (struct idspec));
+ i->nuids = nuids;
+ i->ngids = ngids;
+ i->uids = malloc (nuids * sizeof (uid_t));
+ i->gids = malloc (ngids * sizeof (gid_t));
+ bcopy (uids, i->uids, nuids * sizeof (uid_t));
+ bcopy (gids, i->gids, ngids * sizeof (gid_t));
+ i->references = 1;
+
+ i->next = idhashtable[hash];
+ if (idhashtable[hash])
+ idhashtable[hash]->prevp = &i->next;
+ i->prevp = &idhashtable[hash];
+ idhashtable[hash] = i;
+
+ spin_unlock (&idhashlock);
+ return i;
+}
+
+int *
+process_cred (int *p, struct idspec **credp)
+{
+ int type;
+ int len;
+ int *uid;
+ int *gids;
+ int ngids;
+ int firstgid;
+ int i;
+
+ type = ntohl (*p++);
+
+ if (type != AUTH_UNIX)
+ {
+ int size = ntohl (*p++);
+ *credp = idspec_lookup (0, 0, 0, 0);
+ return p + INTSIZE (size);
+ }
+
+ p++; /* skip size */
+ p++; /* skip seconds */
+ len = ntohl (*p++);
+ p += INTSIZE (len); /* skip hostname */
+
+ uid = p++; /* remember loc of uid */
+ *uid = ntohl (*uid);
+
+ firstgid = *p++; /* remember first gid */
+ gids = p; /* here's where the array will start */
+ ngids = ntohl (*p++);
+
+ /* Now swap the first gid to be the first element of the array */
+ *gids = firstgid;
+ ngids++; /* and count it */
+
+ /* And byteswap the gids */
+ for (i = 1; i < ngids; i++)
+ gids[i] = ntohl (gids[i]);
+
+ /* Next is the verf field; skip it entirely */
+ p++; /* skip id */
+ len = htonl (*p++);
+ p += INTSIZE (len);
+
+ *credp = idspec_lookup (1, ngids, uid, gids);
+ return p;
+}
+
+void
+cred_rele (struct idspec *i)
+{
+ spin_lock (&idhashlock);
+ i->references--;
+ if (i->references == 0)
+ {
+ i->lastuse = mapped_time->seconds;
+ if (i->lastuse < leastidlastuse || nfreeids == 0)
+ leastidlastuse = i->lastuse;
+ nfreeids++;
+ }
+ spin_unlock (&idhashlock);
+}
+
+void
+cred_ref (struct idspec *i)
+{
+ spin_lock (&idhashlock);
+ assert (i->references);
+ i->references++;
+ spin_unlock (&idhashlock);
+}
+
+void
+scan_creds ()
+{
+ struct idspec *i;
+ int n;
+ int newleast = mapped_time->seconds;
+
+ spin_lock (&idhashlock);
+ if (mapped_time->seconds - leastidlastuse > ID_KEEP_TIMEOUT)
+ for (n = 0; n < IDHASH_TABLE_SIZE && nfreeids; n++)
+ for (i = idhashtable[n]; i && nfreeids; i = i->next)
+ if (!i->references
+ && mapped_time->seconds - i->lastuse > ID_KEEP_TIMEOUT)
+ {
+ nfreeids--;
+ *i->prevp = i->next;
+ if (i->next)
+ i->next->prevp = i->prevp;
+ free (i->uids);
+ free (i->gids);
+ free (i);
+ }
+ else
+ if (!i->references && newleast > i->lastuse)
+ newleast = i->lastuse;
+
+ /* If we didn't bail early, then this is valid */
+ if (nfreeids)
+ leastidlastuse = newleast;
+ spin_unlock (&idhashlock);
+}
+
+
+
+static struct cache_handle *fhhashtable[FHHASH_TABLE_SIZE];
+struct mutex fhhashlock = MUTEX_INITIALIZER;
+static int nfreefh;
+static int leastfhlastuse;
+
+static int
+fh_hash (char *fhandle, struct idspec *i)
+{
+ int hash = 0, n;
+
+ for (n = 0; n < NFS_FHSIZE; n++)
+ hash += fhandle[n];
+ hash += (int) i >> 6;
+ return hash;
+}
+
+int *
+lookup_cache_handle (int *p, struct cache_handle **cp, struct idspec *i)
+{
+ int hash;
+ struct cache_handle *c;
+ fsys_t fsys;
+ file_t port;
+
+ hash = fh_hash ((char *)p, i);
+ mutex_lock (&fhhashlock);
+ for (c = fhhashtable[hash]; c; c = c->next)
+ if (c->ids == i && ! bcmp (c->handle, p, NFS_FHSIZE))
+ {
+ if (c->references == 0)
+ nfreefh--;
+ c->references++;
+ mutex_unlock (&fhhashlock);
+ *cp = c;
+ return p + NFS_FHSIZE / sizeof (int);
+ }
+
+ /* Not found */
+
+ /* First four bytes are our internal table of filesystems */
+ fsys = lookup_filesystem (*p);
+ if (fsys == MACH_PORT_NULL
+ || fsys_getfile (fsys, i->uids, i->nuids, i->gids, i->ngids,
+ (char *)(p + 1), NFS_FHSIZE - sizeof (int), &port))
+ {
+ mutex_unlock (&fhhashlock);
+ *cp = 0;
+ return p + NFS_FHSIZE / sizeof (int);
+ }
+
+ c = malloc (sizeof (struct cache_handle));
+ bcopy (p, c->handle, NFS_FHSIZE);
+ cred_ref (i);
+ c->ids = i;
+ c->port = port;
+ c->references = 1;
+
+ c->next = fhhashtable[hash];
+ if (c->next)
+ c->next->prevp = &c->next;
+ c->prevp = &fhhashtable[hash];
+ fhhashtable[hash] = c;
+
+ mutex_unlock (&fhhashlock);
+ *cp = c;
+ return p + NFS_FHSIZE / sizeof (int);
+}
+
+void
+cache_handle_rele (struct cache_handle *c)
+{
+ mutex_lock (&fhhashlock);
+ c->references--;
+ if (c->references == 0)
+ {
+ c->lastuse = mapped_time->seconds;
+ if (c->lastuse < leastfhlastuse || nfreefh == 0)
+ leastfhlastuse = c->lastuse;
+ nfreefh++;
+ }
+ mutex_unlock (&fhhashlock);
+}
+
+void
+scan_fhs ()
+{
+ struct cache_handle *c;
+ int n;
+ int newleast = mapped_time->seconds;
+
+ mutex_lock (&fhhashlock);
+ if (mapped_time->seconds - leastfhlastuse > FH_KEEP_TIMEOUT)
+ for (n = 0; n < FHHASH_TABLE_SIZE && nfreefh; n++)
+ for (c = fhhashtable[n]; c && nfreefh; c = c->next)
+ if (!c->references
+ && mapped_time->seconds - c->lastuse > FH_KEEP_TIMEOUT)
+ {
+ nfreefh--;
+ *c->prevp = c->next;
+ if (c->next)
+ c->next->prevp = c->prevp;
+ cred_rele (c->ids);
+ mach_port_deallocate (mach_task_self (), c->port);
+ free (c);
+ }
+ else
+ if (!c->references && newleast > c->lastuse)
+ newleast = c->lastuse;
+
+ /* If we didn't bail early, then this is valid. */
+ if (nfreefh)
+ leastfhlastuse = newleast;
+ mutex_unlock (&fhhashlock);
+}
+
+struct cache_handle *
+create_cached_handle (int fs, struct cache_handle *credc, file_t newport)
+{
+ char fhandle[NFS_FHSIZE];
+ error_t err;
+ struct cache_handle *c;
+ int hash;
+ char *bp = fhandle + sizeof (int);
+ size_t handlelen = NFS_FHSIZE - sizeof (int);
+
+ *(int *)fhandle = fs;
+ err = file_getfh (newport, &bp, &handlelen);
+ if (err || handlelen != NFS_FHSIZE - sizeof (int))
+ {
+ mach_port_deallocate (mach_task_self (), newport);
+ return 0;
+ }
+ if (bp != fhandle + sizeof (int))
+ {
+ bcopy (bp, fhandle + sizeof (int), NFS_FHSIZE - sizeof (int));
+ vm_deallocate (mach_task_self (), (vm_address_t) bp, handlelen);
+ }
+
+ hash = fh_hash (fhandle, credc->ids);
+ mutex_lock (&fhhashlock);
+ for (c = fhhashtable[hash]; c; c = c->next)
+ if (c->ids == credc->ids && ! bcmp (fhandle, c->handle, NFS_FHSIZE))
+ {
+ /* Return this one */
+ if (c->references == 0)
+ nfreefh--;
+ c->references++;
+ mutex_unlock (&fhhashlock);
+ mach_port_deallocate (mach_task_self (), newport);
+ return c;
+ }
+
+ /* Create it anew */
+ c = malloc (sizeof (struct cache_handle));
+ bcopy (fhandle, c->handle, NFS_FHSIZE);
+ cred_ref (credc->ids);
+ c->ids = credc->ids;
+ c->port = newport;
+ c->references = 1;
+
+ /* And add it to the hash table */
+ c->next = fhhashtable[hash];
+ if (c->next)
+ c->next->prevp = &c->next;
+ c->prevp = &fhhashtable[hash];
+ fhhashtable[hash] = c;
+ mutex_unlock (&fhhashlock);
+
+ return c;
+}
+
+
+
+static struct cached_reply *replyhashtable [REPLYHASH_TABLE_SIZE];
+static spin_lock_t replycachelock = SPIN_LOCK_INITIALIZER;
+static int nfreereplies;
+static int leastreplylastuse;
+
+/* Check the list of cached replies to see if this is a replay of a
+ previous transaction; if so, return the cache record. Otherwise,
+ create a new cache record. */
+struct cached_reply *
+check_cached_replies (int xid,
+ struct sockaddr_in *sender)
+{
+ struct cached_reply *cr;
+ int hash;
+
+ hash = xid % REPLYHASH_TABLE_SIZE;
+
+ spin_lock (&replycachelock);
+ for (cr = replyhashtable[hash]; cr; cr = cr->next)
+ if (cr->xid == xid
+ && !bcmp (sender, &cr->source, sizeof (struct sockaddr_in)))
+ {
+ cr->references++;
+ if (cr->references == 1)
+ nfreereplies--;
+ spin_unlock (&replycachelock);
+ mutex_lock (&cr->lock);
+ return cr;
+ }
+
+ cr = malloc (sizeof (struct cached_reply));
+ mutex_init (&cr->lock);
+ mutex_lock (&cr->lock);
+ bcopy (sender, &cr->source, sizeof (struct sockaddr_in));
+ cr->xid = xid;
+ cr->data = 0;
+
+ cr->next = replyhashtable[hash];
+ if (replyhashtable[hash])
+ replyhashtable[hash]->prevp = &cr->next;
+ cr->prevp = &replyhashtable[hash];
+ replyhashtable[hash] = cr;
+
+ spin_unlock (&replycachelock);
+ return cr;
+}
+
+/* A cached reply returned by check_cached_replies is now no longer
+ needed by its caller. */
+void
+release_cached_reply (struct cached_reply *cr)
+{
+ mutex_unlock (&cr->lock);
+ spin_lock (&replycachelock);
+ cr->references--;
+ if (cr->references == 0)
+ {
+ cr->lastuse = mapped_time->seconds;
+ if (cr->lastuse < leastreplylastuse || nfreereplies == 0)
+ leastreplylastuse = cr->lastuse;
+ nfreereplies++;
+ }
+ spin_unlock (&replycachelock);
+}
+
+void
+scan_replies ()
+{
+ struct cached_reply *cr;
+ int n;
+ int newleast = mapped_time->seconds;
+
+ spin_lock (&replycachelock);
+ if (mapped_time->seconds - leastreplylastuse > REPLY_KEEP_TIMEOUT)
+ for (n = 0; n < REPLYHASH_TABLE_SIZE && nfreereplies; n++)
+ for (cr = replyhashtable[n]; cr && nfreereplies; cr = cr->next)
+ if (!cr->references
+ && mapped_time->seconds - cr->lastuse > REPLY_KEEP_TIMEOUT)
+ {
+ nfreereplies--;
+ *cr->prevp = cr->next;
+ if (cr->next)
+ cr->next->prevp = cr->prevp;
+ if (cr->data)
+ free (cr->data);
+ }
+ else
+ if (!cr->references && newleast > cr->lastuse)
+ newleast = cr->lastuse;
+
+ /* If we didn't bail early, then this is valid */
+ if (nfreereplies)
+ leastreplylastuse = newleast;
+ spin_unlock (&replycachelock);
+}
diff --git a/nfsd/fsys.c b/nfsd/fsys.c
new file mode 100644
index 00000000..d3c50220
--- /dev/null
+++ b/nfsd/fsys.c
@@ -0,0 +1,192 @@
+/* Filesystem management for NFS server
+ Copyright (C) 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 <stdio.h>
+#include <errno.h>
+#include <hurd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "nfsd.h"
+
+struct fsys_spec
+{
+ fsys_t fsys;
+ char *name;
+};
+
+static struct fsys_spec *fsystable;
+static int nfsys = 0;
+static int fsystablesize = 0;
+
+file_t index_file_dir;
+char *index_file_compname;
+
+/* Read the filesystem table in from disk */
+void
+init_filesystems (void)
+{
+ int nitems;
+ char *name;
+ int index;
+ int line;
+ file_t root;
+ static FILE *index_file;
+
+ fsystable = (struct fsys_spec *) malloc ((fsystablesize = 10)
+ * sizeof (struct fsys_spec));
+
+ if (!index_file_name)
+ return;
+
+ index_file = fopen (index_file_name, "r");
+ if (!index_file)
+ {
+ fprintf (stderr, "%s: Cannot open `%s': %s\n",
+ program_invocation_name, index_file_name, strerror (errno));
+ return;
+ }
+
+ for (line = 1; ; line++)
+ {
+ nitems = fscanf (index_file, "%d %as\n", &index, &name);
+ if (nitems == EOF)
+ {
+ fclose (index_file);
+ return;
+ }
+
+ if (nitems != 2)
+ {
+ fprintf (stderr, "%s:%s:%d Bad syntax\n",
+ program_invocation_name, index_file_name, line);
+ continue;
+ }
+
+ root = file_name_lookup (name, 0, 0);
+ if (!root)
+ {
+ fprintf (stderr, "%s:%s:%d Filesystem `%s': %s\n",
+ program_invocation_name, index_file_name, line,
+ name, strerror (errno));
+ free (name);
+ continue;
+ }
+
+ if (index >= fsystablesize)
+ fsystable = (struct fsys_spec *) realloc (fsystable,
+ (fsystablesize = index * 2)
+ * sizeof (struct fsys_spec));
+ if (index > nfsys)
+ nfsys = index;
+
+ fsystable[index].name = name;
+ file_getcontrol (root, &fsystable[nfsys].fsys);
+ mach_port_deallocate (mach_task_self (), root);
+ }
+}
+
+/* Write the current filesystem table to disk synchronously. */
+static void
+write_filesystems (void)
+{
+ file_t newindex;
+ FILE *f;
+ int i;
+
+ if (!index_file_name)
+ return;
+
+ if (index_file_dir == MACH_PORT_NULL)
+ {
+ index_file_dir = file_name_split (index_file_name, &index_file_compname);
+ if (index_file_dir == MACH_PORT_NULL)
+ {
+ fprintf (stderr, "%s: `%s': %s\n",
+ program_invocation_name, index_file_name, strerror (errno));
+ index_file_name = 0;
+ return;
+ }
+ }
+
+ /* Create an anonymous file in the same directory */
+ errno = dir_mkfile (index_file_dir, O_WRONLY, 0666, &newindex);
+ if (errno)
+ {
+ fprintf (stderr, "%s: `%s': %s\n",
+ program_invocation_name, index_file_name, strerror (errno));
+ index_file_name = 0;
+ mach_port_deallocate (mach_task_self (), index_file_dir);
+ index_file_dir = MACH_PORT_NULL;
+ return;
+ }
+
+ f = fopenport (newindex, "w");
+
+ for (i = 0; i < nfsys; i++)
+ if (fsystable[i].name)
+ fprintf (f, "%d %s\n", i, fsystable[i].name);
+
+ /* Link it in */
+ errno = dir_link (index_file_dir, newindex, index_file_compname, 0);
+ if (errno)
+ fprintf (stderr, "%s: `%s': %s\n",
+ program_invocation_name, index_file_name, strerror (errno));
+ fflush (f);
+ file_sync (newindex, 1, 0);
+ fclose (f);
+}
+
+/* From a filesystem ID number, return the fsys_t for talking to that
+ filesystem; MACH_PORT_NULL if it isn't in our list. */
+fsys_t
+lookup_filesystem (int id)
+{
+ if (id >= nfsys)
+ return MACH_PORT_NULL;
+ return fsystable[id].fsys;
+}
+
+/* Enter a name in the table of filesystems; return its ID number.
+ ROOT refers to the root of this filesystem. */
+int
+enter_filesystem (char *name, file_t root)
+{
+ int i;
+
+ for (i = 0; i < nfsys; i++)
+ if (fsystable[i].name && !strcmp (fsystable[i].name, name))
+ return i;
+
+ if (nfsys == fsystablesize)
+ fsystable = (struct fsys_spec *) realloc (fsystable,
+ (fsystablesize *= 2)
+ * sizeof (struct fsys_spec));
+
+ fsystable[nfsys].name = malloc (strlen (name) + 1);
+ strcpy (fsystable[nfsys].name, name);
+ file_getcontrol (root, &fsystable[nfsys].fsys);
+ nfsys++;
+
+ write_filesystems ();
+
+ return nfsys - 1;
+}
+
diff --git a/nfsd/loop.c b/nfsd/loop.c
new file mode 100644
index 00000000..3520e1da
--- /dev/null
+++ b/nfsd/loop.c
@@ -0,0 +1,233 @@
+/*
+ Copyright (C) 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 <string.h>
+#include <fcntl.h>
+
+#include "nfsd.h"
+
+#include <rpc/pmap_prot.h>
+#include "../nfs/rpcsvc/mount.h"
+
+#undef TRUE
+#undef FALSE
+#define malloc spoogie_woogie /* barf */
+#include <rpc/xdr.h>
+#include <rpc/types.h>
+#include <rpc/auth.h>
+#include <rpc/rpc_msg.h>
+#undef malloc
+
+void
+server_loop ()
+{
+ char buf[MAXIOSIZE];
+ int xid;
+ int *p, *r;
+ char *rbuf;
+ struct cached_reply *cr;
+ int program;
+ struct sockaddr_in sender;
+ int version;
+ int procedure;
+ struct proctable *table = 0;
+ struct procedure *proc;
+ struct idspec *cred;
+ struct cache_handle *c, fakec;
+ error_t err;
+ size_t addrlen;
+ fd_set readfds;
+ int maxfd;
+ int i;
+ int *errloc;
+
+ bzero (&fakec, sizeof (struct cache_handle));
+
+ if (main_udp_socket > pmap_udp_socket)
+ maxfd = main_udp_socket;
+ else
+ maxfd = pmap_udp_socket;
+
+ for (;;)
+ {
+ FD_ZERO (&readfds);
+ FD_SET (main_udp_socket, &readfds);
+ FD_SET (pmap_udp_socket, &readfds);
+ select (maxfd, &readfds, 0, 0, 0);
+
+ for (i = main_udp_socket;
+ i != -1;
+ i = (i == main_udp_socket ? pmap_udp_socket : -1))
+ {
+ if (!FD_ISSET (i, &readfds))
+ continue;
+
+ p = (int *) buf;
+ proc = 0;
+ addrlen = sizeof (struct sockaddr_in);
+ recvfrom (i, buf, MAXIOSIZE, 0, &sender, &addrlen);
+ xid = *p++;
+
+ /* Ignore things that aren't proper RPCs. */
+ if (ntohl (*p++) != CALL)
+ continue;
+
+ cr = check_cached_replies (xid, &sender);
+ if (cr->data)
+ /* This transacation has already completed */
+ goto repost_reply;
+
+ r = (int *) rbuf = malloc (MAXIOSIZE);
+
+ if (ntohl (*p++) != RPC_MSG_VERSION)
+ {
+ /* Reject RPC */
+ *r++ = xid;
+ *r++ = htonl (REPLY);
+ *r++ = htonl (MSG_DENIED);
+ *r++ = htonl (RPC_MISMATCH);
+ *r++ = htonl (RPC_MSG_VERSION);
+ *r++ = htonl (RPC_MSG_VERSION);
+ goto send_reply;
+ }
+
+ program = ntohl (*p++);
+ switch (program)
+ {
+ case MOUNTPROG:
+ version = MOUNTVERS;
+ table = &mounttable;
+ break;
+
+ case NFS_PROGRAM:
+ version = NFS_VERSION;
+ table = &nfstable;
+ break;
+
+ case PMAPPROG:
+ version = PMAPVERS;
+ table = &pmaptable;
+ break;
+
+ default:
+ /* Program unavailable */
+ *r++ = xid;
+ *r++ = htonl (REPLY);
+ *r++ = htonl (MSG_ACCEPTED);
+ *r++ = htonl (AUTH_NULL);
+ *r++ = htonl (0);
+ *r++ = htonl (PROG_UNAVAIL);
+ goto send_reply;
+ }
+
+ if (ntohl (*p++) != version)
+ {
+ /* Program mismatch */
+ *r++ = xid;
+ *r++ = htonl (REPLY);
+ *r++ = htonl (MSG_ACCEPTED);
+ *r++ = htonl (AUTH_NULL);
+ *r++ = htonl (0);
+ *r++ = htonl (PROG_MISMATCH);
+ *r++ = htonl (version);
+ *r++ = htonl (version);
+ goto send_reply;
+ }
+
+ procedure = htonl (*p++);
+ if (procedure < table->min
+ || procedure > table->max
+ || table->procs[procedure - table->min].func == 0)
+ {
+ /* Procedure unavailable */
+ *r++ = xid;
+ *r++ = htonl (REPLY);
+ *r++ = htonl (MSG_ACCEPTED);
+ *r++ = htonl (AUTH_NULL);
+ *r++ = htonl (0);
+ *r++ = htonl (PROC_UNAVAIL);
+ *r++ = htonl (table->min);
+ *r++ = htonl (table->max);
+ goto send_reply;
+ }
+ proc = &table->procs[procedure - table->min];
+
+ p = process_cred (p, &cred); /* auth */
+ p = skip_cred (p); /* verf */
+
+ if (proc->need_handle)
+ p = lookup_cache_handle (p, &c, cred);
+ else
+ {
+ fakec.ids = cred;
+ c = &fakec;
+ }
+
+ if (proc->alloc_reply)
+ {
+ size_t amt;
+ amt = (*proc->alloc_reply) (p) + 256;
+ if (amt > MAXIOSIZE)
+ {
+ free (rbuf);
+ r = (int *) rbuf = malloc (amt);
+ }
+ }
+
+ /* Fill in beginning of reply */
+ *r++ = xid;
+ *r++ = htonl (REPLY);
+ *r++ = htonl (MSG_ACCEPTED);
+ *r++ = htonl (AUTH_NULL);
+ *r++ = htonl (0);
+ *r++ = htonl (SUCCESS);
+ if (proc->process_error)
+ {
+ /* Assume success for now and patch it later if necessary */
+ errloc = r;
+ *r++ = htonl (0);
+ }
+
+ if (c)
+ err = (*proc->func) (c, p, &r);
+ else
+ err = ESTALE;
+
+ if (proc->process_error && err)
+ {
+ r = errloc;
+ *r++ = htonl (nfs_error_trans (err));
+ }
+
+ cred_rele (cred);
+ if (c != &fakec)
+ cache_handle_rele (c);
+
+ send_reply:
+ cr->data = rbuf;
+ cr->len = (char *)r - rbuf;
+
+ repost_reply:
+ sendto (i, cr->data, cr->len, 0,
+ (struct sockaddr *)&sender, addrlen);
+ release_cached_reply (cr);
+ }
+ }
+}
diff --git a/nfsd/main.c b/nfsd/main.c
new file mode 100644
index 00000000..69099361
--- /dev/null
+++ b/nfsd/main.c
@@ -0,0 +1,78 @@
+/* Main NFS server program
+ Copyright (C) 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 "nfsd.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <rpc/pmap_prot.h>
+#include <maptime.h>
+
+int main_udp_socket, pmap_udp_socket;
+struct sockaddr_in main_address, pmap_address;
+char *index_file_name;
+
+int
+main (int argc, char **argv)
+{
+ int nthreads;
+
+ if (argc > 2)
+ {
+ fprintf (stderr, "%s [num-threads]\n", argv[0]);
+ exit (1);
+ }
+ if (argc == 1)
+ nthreads = 4;
+ else
+ nthreads = atoi (argv[1]);
+ if (!nthreads)
+ nthreads = 4;
+
+ index_file_name = asprintf ("%s/state/misc/nfsd.index", LOCALSTATEDIR);
+
+ maptime_map (0, 0, &mapped_time);
+
+ main_address.sin_family = AF_INET;
+ main_address.sin_port = htons (NFS_PORT);
+ main_address.sin_addr.s_addr = INADDR_ANY;
+ pmap_address.sin_family = AF_INET;
+ pmap_address.sin_port = htons (PMAPPORT);
+ pmap_address.sin_addr.s_addr = INADDR_ANY;
+
+ main_udp_socket = socket (PF_INET, SOCK_DGRAM, 0);
+ pmap_udp_socket = socket (PF_INET, SOCK_DGRAM, 0);
+ bind (main_udp_socket, (struct sockaddr *)&main_address,
+ sizeof (struct sockaddr_in));
+ bind (pmap_udp_socket, (struct sockaddr *)&pmap_address,
+ sizeof (struct sockaddr_in));
+
+ init_filesystems ();
+
+ while (nthreads--)
+ cthread_detach (cthread_fork ((cthread_fn_t) server_loop, 0));
+
+ for (;;)
+ {
+ sleep (1);
+ scan_fhs ();
+ scan_creds ();
+ scan_replies ();
+ }
+}
diff --git a/nfsd/nfsd.h b/nfsd/nfsd.h
new file mode 100644
index 00000000..7b6f530d
--- /dev/null
+++ b/nfsd/nfsd.h
@@ -0,0 +1,128 @@
+/*
+ Copyright (C) 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. */
+
+typedef int bool_t;
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <cthreads.h>
+#include "../nfs/rpcsvc/nfs_prot.h" /* XXX */
+#include <hurd/fs.h>
+
+/* These should be configuration options */
+#define ID_KEEP_TIMEOUT 3600 /* one hour */
+#define FH_KEEP_TIMEOUT 600 /* ten minutes */
+#define REPLY_KEEP_TIMEOUT 120 /* two minutes */
+#define MAXIOSIZE 10240
+
+struct idspec
+{
+ struct idspec *next, **prevp;
+ int nuids, ngids;
+ uid_t *uids, *gids;
+ time_t lastuse;
+ int references;
+};
+
+struct cache_handle
+{
+ struct cache_handle *next, **prevp;
+ char handle[NFS_FHSIZE];
+ struct idspec *ids;
+ file_t port;
+ time_t lastuse;
+ int references;
+};
+
+struct cached_reply
+{
+ struct cached_reply *next, **prevp;
+ struct mutex lock;
+ struct sockaddr_in source;
+ int xid;
+ time_t lastuse;
+ int references;
+ size_t len;
+ char *data;
+};
+
+struct procedure
+{
+ error_t (*func) (struct cache_handle *, int *, int **);
+ size_t (*alloc_reply) (int *);
+ int need_handle;
+ int process_error;
+};
+
+struct proctable
+{
+ int min;
+ int max;
+ struct procedure procs[0];
+};
+
+volatile struct mapped_time_value *mapped_time;
+
+#define INTSIZE(n) (((n) + 3) >> 2)
+
+/* We don't actually distinguish between these two sockets, but
+ we have to listen on two different ports, so that's why they're here. */
+extern int main_udp_socket, pmap_udp_socket;
+extern struct sockaddr_in main_address, pmap_address;
+
+/* Name of the file on disk containing the filesystem index table */
+extern char *index_file_name;
+
+
+/* cache.c */
+int *process_cred (int *, struct idspec **);
+void cred_rele (struct idspec *);
+void cred_ref (struct idspec *);
+void scan_creds (void);
+int *lookup_cache_handle (int *, struct cache_handle **, struct idspec *);
+void cache_handle_rele (struct cache_handle *);
+void scan_fhs (void);
+struct cache_handle *create_cached_handle (int, struct cache_handle *, file_t);
+struct cached_reply *check_cached_replies (int, struct sockaddr_in *);
+void release_cached_reply (struct cached_reply *cr);
+void scan_replies (void);
+
+/* loop.c */
+void server_loop (void);
+
+/* ops.c */
+extern struct proctable nfstable, mounttable, pmaptable;
+
+/* xdr.c */
+int *skip_cred (int *);
+int nfs_error_trans (error_t);
+int *encode_fattr (int *, struct stat *);
+int *decode_name (int *, char **);
+int *encode_fhandle (int *, char *);
+int *encode_string (int *, char *);
+int *encode_data (int *, char *, size_t);
+int *encode_statfs (int *, struct statfs *);
+
+/* fsys.c */
+fsys_t lookup_filesystem (int);
+int enter_filesystem (char *, file_t);
+void init_filesystems (void);
diff --git a/nfsd/ops.c b/nfsd/ops.c
new file mode 100644
index 00000000..93c8373c
--- /dev/null
+++ b/nfsd/ops.c
@@ -0,0 +1,652 @@
+/* NFS daemon protocol operations
+ Copyright (C) 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 <hurd/io.h>
+#include <hurd/fs.h>
+#include <fcntl.h>
+#include <hurd/paths.h>
+#include <hurd.h>
+#include <dirent.h>
+
+#include "nfsd.h"
+#include "../nfs/rpcsvc/mount.h" /* XXX */
+#include <rpc/pmap_prot.h>
+
+static error_t
+op_null (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ return 0;
+}
+
+static error_t
+op_getattr (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ struct stat st;
+ error_t err;
+
+ err = io_stat (c->port, &st);
+ if (!err)
+ *reply = encode_fattr (*reply, &st);
+ return err;
+}
+
+static error_t
+complete_setattr (mach_port_t port,
+ int *p)
+{
+ uid_t uid, gid;
+ off_t size;
+ time_value_t atime, mtime;
+ struct stat st;
+ error_t err;
+
+ err = io_stat (port, &st);
+ if (err)
+ return err;
+
+ uid = ntohl (*p++);
+ gid = ntohl (*p++);
+ if ((uid != -1 && uid != st.st_uid)
+ || (gid != -1 && gid != st.st_gid))
+ {
+ if (uid == -1)
+ uid = st.st_uid;
+ if (gid == -1)
+ gid = st.st_gid;
+ err = file_chown (port, uid, gid);
+ if (err)
+ return err;
+ }
+
+ size = ntohl (*p++);
+ if (size != -1 && size != st.st_size)
+ err = file_set_size (port, size);
+ if (err)
+ return err;
+
+ atime.seconds = ntohl (*p++);
+ atime.microseconds = ntohl (*p++);
+ mtime.seconds = ntohl (*p++);
+ mtime.microseconds = ntohl (*p++);
+ if (atime.seconds != -1 && atime.microseconds == -1)
+ atime.microseconds = 0;
+ if (mtime.seconds != -1 && mtime.microseconds == -1)
+ mtime.microseconds = 0;
+ if (atime.seconds != -1 || mtime.seconds != -1
+ || atime.microseconds != -1 || mtime.microseconds != -1)
+ {
+ if (atime.seconds == -1)
+ atime.seconds = st.st_atime;
+ if (atime.microseconds == -1)
+ atime.microseconds = st.st_atime_usec;
+ if (mtime.seconds == -1)
+ mtime.seconds = st.st_mtime;
+ if (mtime.microseconds == -1)
+ mtime.microseconds = st.st_mtime_usec;
+ err = file_utimes (port, atime, mtime);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static error_t
+op_setattr (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ error_t err = 0;
+ mode_t mode;
+
+ mode = ntohl (*p++);
+ if (mode != -1)
+ err = file_chmod (c->port, mode);
+ if (err)
+ return err;
+
+ return complete_setattr (c->port, p);
+}
+
+static error_t
+op_lookup (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ error_t err;
+ char *name;
+ retry_type do_retry;
+ char retry_name [1024];
+ mach_port_t newport;
+ struct cache_handle *newc;
+ struct stat st;
+
+ decode_name (p, &name);
+
+ err = dir_lookup (c->port, name, O_NOTRANS, 0, &do_retry, retry_name,
+ &newport);
+ free (name);
+
+ /* Block attempts to bounce out of this filesystem by any technique */
+ if (!err
+ && (do_retry != FS_RETRY_NORMAL
+ || retry_name[0] != '\0'))
+ err = EACCES;
+
+ if (!err)
+ err = io_stat (newport, &st);
+
+ if (err)
+ return err;
+
+ newc = create_cached_handle (*(int *)c->handle, c, newport);
+ if (!newc)
+ return ESTALE;
+ *reply = encode_fhandle (*reply, newc->handle);
+ *reply = encode_fattr (*reply, &st);
+ return 0;
+}
+
+static error_t
+op_readlink (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ char buf[2048], *transp = buf;
+ mach_msg_type_number_t len = sizeof (buf);
+ error_t err;
+
+ /* Shamelessly copied from the libc readlink */
+ err = file_get_translator (c->port, &transp, &len);
+ if (err)
+ return err;
+
+ if (len < sizeof (_HURD_SYMLINK)
+ || memcmp (transp, _HURD_SYMLINK, sizeof (_HURD_SYMLINK)))
+ return EINVAL;
+
+ transp += sizeof (_HURD_SYMLINK);
+
+ *reply = encode_string (*reply, transp);
+ return 0;
+}
+
+static size_t
+count_read_buffersize (int *p)
+{
+ return ntohl (*++p); /* skip OFFSET, return COUNT */
+}
+
+static error_t
+op_read (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ off_t offset;
+ size_t count;
+ char buf[2048], *bp = buf;
+ mach_msg_type_number_t buflen = sizeof (buf);
+ struct stat st;
+ error_t err;
+
+ offset = ntohl (*p++);
+ count = ntohl (*p++);
+
+ err = io_read (c->port, &bp, &buflen, offset, count);
+ if (err)
+ return err;
+
+ err = io_stat (c->port, &st);
+ if (err)
+ return err;
+
+ *reply = encode_fattr (*reply, &st);
+ *reply = encode_data (*reply, bp, buflen);
+ return 0;
+}
+
+static error_t
+op_write (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ off_t offset;
+ size_t count;
+ error_t err;
+ mach_msg_type_number_t amt;
+ char *bp;
+ struct stat st;
+
+ p++;
+ offset = ntohl (*p++);
+ p++;
+ count = ntohl (*p++);
+ bp = (char *) *reply;
+
+ while (count)
+ {
+ err = io_write (c->port, bp, count, offset, &amt);
+ if (err)
+ return err;
+ if (amt == 0)
+ return EIO;
+ count -= amt;
+ bp += amt;
+ offset += amt;
+ }
+
+ file_sync (c->port, 1, 0);
+
+ err = io_stat (c->port, &st);
+ if (err)
+ return err;
+ *reply = encode_fattr (*reply, &st);
+ return 0;
+}
+
+static error_t
+op_create (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ error_t err;
+ char *name;
+ retry_type do_retry;
+ char retry_name [1024];
+ mach_port_t newport;
+ struct cache_handle *newc;
+ struct stat st;
+ mode_t mode;
+
+ p = decode_name (p, &name);
+ mode = ntohl (*p++);
+
+ err = dir_lookup (c->port, name, O_NOTRANS | O_CREAT | O_EXCL, mode,
+ &do_retry, retry_name, &newport);
+ if (!err
+ && (do_retry != FS_RETRY_NORMAL
+ || retry_name[0] != '\0'))
+ err = EACCES;
+
+ if (err)
+ return err;
+
+ err = complete_setattr (newport, p);
+ if (!err)
+ err = io_stat (newport, &st);
+
+ if (err)
+ {
+ dir_unlink (c->port, name);
+ free (name);
+ return err;
+ }
+ free (name);
+
+ newc = create_cached_handle (*(int *)c->handle, c, newport);
+ if (!newc)
+ return ESTALE;
+
+ *reply = encode_fhandle (*reply, newc->handle);
+ *reply = encode_fattr (*reply, &st);
+ return 0;
+}
+
+static error_t
+op_remove (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ error_t err;
+ char *name;
+
+ decode_name (p, &name);
+
+ err = dir_unlink (c->port, name);
+ free (name);
+
+ return 0;
+}
+
+static error_t
+op_rename (struct cache_handle *fromc,
+ int *p,
+ int **reply)
+{
+ struct cache_handle *toc;
+ char *fromname, *toname;
+ error_t err = 0;
+
+ p = decode_name (p, &fromname);
+ p = lookup_cache_handle (p, &toc, fromc->ids);
+ decode_name (p, &toname);
+
+ if (!toc)
+ err = ESTALE;
+ if (!err)
+ err = dir_rename (fromc->port, fromname, toc->port, toname, 0);
+ free (fromname);
+ free (toname);
+ return err;
+}
+
+static error_t
+op_link (struct cache_handle *filec,
+ int *p,
+ int **reply)
+{
+ struct cache_handle *dirc;
+ char *name;
+ error_t err = 0;
+
+ p = lookup_cache_handle (p, &dirc, filec->ids);
+ decode_name (p, &name);
+
+ if (!dirc)
+ err = ESTALE;
+ if (!err)
+ err = dir_link (dirc->port, filec->port, name, 1);
+
+ free (name);
+ return err;
+}
+
+static error_t
+op_symlink (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ char *name, *target;
+ error_t err;
+ mode_t mode;
+ file_t newport = MACH_PORT_NULL;
+ size_t len;
+ char *buf;
+
+ p = decode_name (p, &name);
+ p = decode_name (p, &target);
+ mode = ntohl (*p++);
+ if (mode == -1)
+ mode = 0777;
+
+ len = strlen (target) + 1;
+ buf = alloca (sizeof (_HURD_SYMLINK) + len);
+ memcpy (buf, _HURD_SYMLINK, sizeof (_HURD_SYMLINK));
+ memcpy (buf + sizeof (_HURD_SYMLINK), target, len);
+
+ err = dir_mkfile (c->port, O_WRITE, mode, &newport);
+ if (!err)
+ err = file_set_translator (newport,
+ FS_TRANS_EXCL|FS_TRANS_SET,
+ FS_TRANS_EXCL|FS_TRANS_SET, 0,
+ buf, sizeof (_HURD_SYMLINK) + len,
+ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
+ if (!err)
+ err = dir_link (c->port, newport, name, 1);
+
+ free (name);
+ free (target);
+
+ if (newport != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), newport);
+ return err;
+}
+
+static error_t
+op_mkdir (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ char *name;
+ mode_t mode;
+ retry_type do_retry;
+ char retry_name [1024];
+ mach_port_t newport;
+ struct stat st;
+ struct cache_handle *newc;
+ error_t err;
+
+ p = decode_name (p, &name);
+ mode = ntohl (*p++);
+
+ err = dir_mkdir (c->port, name, mode);
+
+ if (err)
+ {
+ free (name);
+ return err;
+ }
+
+ err = dir_lookup (c->port, name, O_NOTRANS, 0, &do_retry,
+ retry_name, &newport);
+ free (name);
+ if (!err
+ && (do_retry != FS_RETRY_NORMAL
+ || retry_name[0] != '\0'))
+ err = EACCES;
+ if (err)
+ return err;
+
+ err = complete_setattr (newport, p);
+ if (!err)
+ err = io_stat (newport, &st);
+ if (err)
+ return err;
+
+ newc = create_cached_handle (*(int *)c->handle, c, newport);
+ if (!newc)
+ return ESTALE;
+ *reply = encode_fhandle (*reply, newc->handle);
+ *reply = encode_fattr (*reply, &st);
+ return 0;
+}
+
+static error_t
+op_rmdir (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ char *name;
+ error_t err;
+
+ decode_name (p, &name);
+
+ err = dir_rmdir (c->port, name);
+ free (name);
+ return err;
+}
+
+static error_t
+op_readdir (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ int cookie;
+ unsigned count;
+ error_t err;
+ char *buf;
+ struct dirent *dp;
+ size_t bufsize;
+ int nentries;
+ int i;
+ int *replystart;
+
+ cookie = ntohl (*p++);
+ count = ntohl (*p++);
+
+ buf = alloca (count);
+ bufsize = count;
+ err = dir_readdir (c->port, &buf, &bufsize, cookie, -1, count, &nentries);
+ if (err)
+ return err;
+
+ if (nentries == 0)
+ {
+ *(*reply)++ = htonl (0); /* no entry */
+ *(*reply)++ = htonl (1); /* EOF */
+ }
+ else
+ {
+ for (i = 0, dp = (struct dirent *) buf, replystart = *reply;
+ ((char *)dp < buf + bufsize
+ && i < nentries
+ && (char *)reply < (char *)replystart + count);
+ i++, dp = (struct dirent *) ((char *)dp + dp->d_reclen))
+ {
+ *(*reply)++ = htonl (1); /* entry present */
+ *(*reply)++ = htonl (dp->d_ino);
+ *reply = encode_string (*reply, dp->d_name);
+ *(*reply)++ = htonl (i + cookie + 1); /* next entry */
+ }
+ *(*reply)++ = htonl (0); /* not EOF */
+ }
+
+ return 0;
+}
+
+static size_t
+count_readdir_buffersize (int *p)
+{
+ return ntohl (*++p); /* skip COOKIE; return COUNT */
+}
+
+static error_t
+op_statfs (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ struct statfs st;
+ error_t err;
+
+ err = file_statfs (c->port, &st);
+ if (!err)
+ *reply = encode_statfs (*reply, &st);
+ return err;
+}
+
+static error_t
+op_mnt (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ file_t root;
+ struct cache_handle *newc;
+ char *name;
+
+ decode_name (p, &name);
+
+ root = file_name_lookup (name, 0, 0);
+ if (!root)
+ {
+ free (name);
+ return errno;
+ }
+
+ newc = create_cached_handle (enter_filesystem (name, root), c, root);
+ free (name);
+ if (!newc)
+ return ESTALE;
+ *reply = encode_fhandle (*reply, newc->handle);
+ return 0;
+}
+
+static error_t
+op_getport (struct cache_handle *c,
+ int *p,
+ int **reply)
+{
+ int prog, vers, prot;
+
+ prog = ntohl (*p++);
+ vers = ntohl (*p++);
+ prot = ntohl (*p++);
+
+ if (prot != IPPROTO_UDP)
+ *(*reply)++ = htonl (0);
+ else if ((prog == MOUNTPROG && vers == MOUNTVERS)
+ || (prog == NFS_PROGRAM && vers == NFS_VERSION))
+ *(*reply)++ = htonl (NFS_PORT);
+ else if (prog == PMAPPROG && vers == PMAPVERS)
+ *(*reply)++ = htonl (PMAPPORT);
+ else
+ *(*reply)++ = 0;
+
+ return 0;
+}
+
+
+struct proctable nfstable =
+{
+ NFSPROC_NULL, /* first proc */
+ NFSPROC_STATFS, /* last proc */
+ {
+ { op_null, 0, 0, 0},
+ { op_getattr, 0, 1, 1},
+ { op_setattr, 0, 1, 1},
+ { 0, 0, 0, 0 }, /* deprecated NFSPROC_ROOT */
+ { op_lookup, 0, 1, 1},
+ { op_readlink, 0, 1, 1},
+ { op_read, count_read_buffersize, 1, 1},
+ { 0, 0, 0, 0 }, /* nonexistent NFSPROC_WRITECACHE */
+ { op_write, 0, 1, 1},
+ { op_create, 0, 1, 1},
+ { op_remove, 0, 1, 1},
+ { op_rename, 0, 1, 1},
+ { op_link, 0, 1, 1},
+ { op_symlink, 0, 1, 1},
+ { op_mkdir, 0, 1, 1},
+ { op_rmdir, 0, 1, 1},
+ { op_readdir, count_readdir_buffersize, 1, 1},
+ { op_statfs, 0, 1, 1},
+ }
+};
+
+
+struct proctable mounttable =
+{
+ MOUNTPROC_NULL, /* first proc */
+ MOUNTPROC_EXPORT, /* last proc */
+ {
+ { op_null, 0, 0, 0},
+ { op_mnt, 0, 0, 1},
+ { 0, 0, 0, 0}, /* MOUNTPROC_DUMP */
+ { 0, 0, 0, 0}, /* MOUNTPROC_UMNT */
+ { 0, 0, 0, 0}, /* MOUNTPROC_UMNTALL */
+ { 0, 0, 0, 0}, /* MOUNTPROC_EXPORT */
+ }
+};
+
+struct proctable pmaptable =
+{
+ PMAPPROC_NULL, /* first proc */
+ PMAPPROC_CALLIT, /* last proc */
+ {
+ { op_null, 0, 0, 0},
+ { 0, 0, 0, 0}, /* PMAPPROC_SET */
+ { 0, 0, 0, 0}, /* PMAPPROC_UNSET */
+ { op_getport, 0, 0, 0},
+ { 0, 0, 0, 0}, /* PMAPPROC_DUMP */
+ { 0, 0, 0, 0}, /* PMAPPROC_CALLIT */
+ }
+};
diff --git a/nfsd/proctables.c b/nfsd/proctables.c
new file mode 100644
index 00000000..1bc76a38
--- /dev/null
+++ b/nfsd/proctables.c
@@ -0,0 +1,57 @@
+/*
+ Copyright (C) 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. */
+
+
+struct proctable nfstable =
+{
+ NFSPROC_NULL, /* first proc */
+ NFSPROC_STATFS, /* last proc */
+ { op_null, 0, 0},
+ { op_getattr, 0, 1},
+ { op_setattr, 0, 1},
+ { 0, 0, 0 }, /* deprecated NFSPROC_ROOT */
+ { op_lookup, 0, 1},
+ { op_readlink, 0, 1},
+ { op_read, count_read_buffersize, 1},
+ { 0, 0, 0 }, /* nonexistent NFSPROC_WRITECACHE */
+ { op_write, 0, 1},
+ { op_create, 0, 1},
+ { op_remove, 0, 1},
+ { op_rename, 0, 1},
+ { op_link, 0, 1},
+ { op_symlink, 0, 1},
+ { op_mkdir, 0, 1},
+ { op_rmdir, 0, 1},
+ { op_readdir, count_readdir_buffersize, 1},
+ { op_statfs, 0, 1},
+};
+
+
+struct proctable mounttable =
+{
+ MOUNTPROC_NULL, /* first proc */
+ MOUNTPROC_EXPORT, /* last proc */
+ { op_null, 0, 0},
+ { op_mnt, 0, 0},
+ { 0, 0, 0}, /* MOUNTPROC_DUMP */
+ { 0, 0, 0}, /* MOUNTPROC_UMNT */
+ { 0, 0, 0}, /* MOUNTPROC_UMNTALL */
+ { 0, 0, 0}, /* MOUNTPROC_EXPORT */
+};
diff --git a/nfsd/xdr.c b/nfsd/xdr.c
new file mode 100644
index 00000000..d5bea0bd
--- /dev/null
+++ b/nfsd/xdr.c
@@ -0,0 +1,204 @@
+/* XDR packing and unpacking in nfsd
+ Copyright (C) 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 <sys/stat.h>
+#include <sys/statfs.h>
+#include <string.h>
+#include "nfsd.h"
+
+/* Return the address of the next thing after the credential at P. */
+int *
+skip_cred (int *p)
+{
+ int size;
+
+ p++; /* TYPE */
+ size = ntohl (*p++);
+ return p + INTSIZE (size);
+}
+
+/* Any better ideas? */
+static int
+hurd_mode_to_nfs_mode (mode_t m)
+{
+ return m & 0x177777;
+}
+
+static int
+hurd_mode_to_nfs_type (mode_t m)
+{
+ switch (m & S_IFMT)
+ {
+ case S_IFDIR:
+ return NFDIR;
+
+ case S_IFCHR:
+ 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 NFFIFO;
+
+ default:
+ return NFNON;
+ }
+}
+
+/* Encode ST into P and return the next thing to come after it. */
+int *
+encode_fattr (int *p, struct stat *st)
+{
+ *p++ = htonl (hurd_mode_to_nfs_type (st->st_mode));
+ *p++ = htonl (hurd_mode_to_nfs_mode (st->st_mode));
+ *p++ = htonl (st->st_nlink);
+ *p++ = htonl (st->st_uid);
+ *p++ = htonl (st->st_gid);
+ *p++ = htonl (st->st_size);
+ *p++ = htonl (st->st_blksize);
+ *p++ = htonl (st->st_rdev);
+ *p++ = htonl (st->st_blocks);
+ return p;
+}
+
+/* Decode P into NAME and return the next thing to come after it. */
+int *
+decode_name (int *p, char **name)
+{
+ int len;
+
+ len = ntohl (*p++);
+ *name = malloc (len + 1);
+ bcopy (p, *name, len);
+ (*name)[len] = '\0';
+ return p + INTSIZE (len);
+}
+
+/* Encode HANDLE into P and return the next thing to come after it. */
+int *
+encode_fhandle (int *p, char *handle)
+{
+ bcopy (handle, p, NFS_FHSIZE);
+ return p + INTSIZE (NFS_FHSIZE);
+}
+
+/* Encode STRING into P and return the next thing to come after it. */
+int *
+encode_string (int *p, char *string)
+{
+ return encode_data (p, string, strlen (string));
+}
+
+/* Encode DATA into P and return the next thing to come after it. */
+int *
+encode_data (int *p, char *data, size_t len)
+{
+ int nints = INTSIZE (len);
+
+ p[nints] = 0;
+ *p++ = htonl (len);
+ bcopy (data, p, len);
+ return p + nints;
+}
+
+/* Encode ST into P and return the next thing to come after it. */
+int *
+encode_statfs (int *p, struct statfs *st)
+{
+ *p++ = st->f_bsize;
+ *p++ = st->f_bsize;
+ *p++ = st->f_blocks;
+ *p++ = st->f_bfree;
+ *p++ = st->f_bavail;
+ return p;
+}
+
+/* Return an NFS error corresponding to Hurd error ERR. */
+int
+nfs_error_trans (error_t err)
+{
+ switch (err)
+ {
+ case 0:
+ return NFS_OK;
+
+ case EPERM:
+ return NFSERR_PERM;
+
+ case ENOENT:
+ return NFSERR_NOENT;
+
+ case EIO:
+ default:
+ return NFSERR_IO;
+
+ case ENXIO:
+ return NFSERR_NXIO;
+
+ case EACCES:
+ return NFSERR_ACCES;
+
+ case EEXIST:
+ return NFSERR_EXIST;
+
+ case ENODEV:
+ return NFSERR_NODEV;
+
+ case ENOTDIR:
+ return NFSERR_NOTDIR;
+
+ case EISDIR:
+ return NFSERR_ISDIR;
+
+ case E2BIG:
+ return NFSERR_FBIG;
+
+ case ENOSPC:
+ return NFSERR_NOSPC;
+
+ case EROFS:
+ return NFSERR_ROFS;
+
+ case ENAMETOOLONG:
+ return NFSERR_NAMETOOLONG;
+
+ case ENOTEMPTY:
+ return NFSERR_NOTEMPTY;
+
+ case EDQUOT:
+ return NFSERR_DQUOT;
+
+ case ESTALE:
+ return NFSERR_STALE;
+ }
+}
+
+
+