aboutsummaryrefslogtreecommitdiff
path: root/nfsd
diff options
context:
space:
mode:
Diffstat (limited to 'nfsd')
-rw-r--r--nfsd/ChangeLog156
-rw-r--r--nfsd/Makefile34
-rw-r--r--nfsd/cache.c534
-rw-r--r--nfsd/fsys.c214
-rw-r--r--nfsd/loop.c218
-rw-r--r--nfsd/main.c94
-rw-r--r--nfsd/nfsd.h129
-rw-r--r--nfsd/ops.c705
-rw-r--r--nfsd/xdr.c218
9 files changed, 2302 insertions, 0 deletions
diff --git a/nfsd/ChangeLog b/nfsd/ChangeLog
new file mode 100644
index 00000000..d4210e8c
--- /dev/null
+++ b/nfsd/ChangeLog
@@ -0,0 +1,156 @@
+1999-07-10 Roland McGrath <roland@baalperazim.frob.com>
+
+ * cache.c: Add #include <sys/mman.h> for munmap decl.
+
+1999-07-03 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * cache.c (create_cached_handle): Use munmap instead of
+ vm_deallocate.
+
+1998-12-20 Roland McGrath <roland@baalperazim.frob.com>
+
+ * loop.c (server_loop): Cosmetic.
+
+ * nfsd.h: Include <rpc/types.h>.
+ (bool_t, enum_t): Remove typedefs.
+
+1998-10-20 Roland McGrath <roland@baalperazim.frob.com>
+
+ * cache.c (scan_creds): Add braces to silence gcc warning.
+ (scan_fhs): Likewise.
+ (scan_replies): Likewise.
+
+Thu Sep 26 14:09:20 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * nfsd.h (enum_t): New type.
+
+ * cache.c: Don't inclide <rpc/auth_unix.h>.
+
+Thu Sep 12 16:52:13 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * Makefile (HURDLIBS): New variable.
+ (nfsd): Delete special depedency.
+
+Tue Sep 3 14:15:50 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * loop.c: Include "../nfs/mount.h" instead of
+ "../nfs/rpcsvc/mount.h".
+ * ops.c: Likewise.
+ * nfsd.h: Include "../nfs/nfs-spec.h" instead of
+ "../nfs/rpcsvc/nfs_prot.h".
+
+Wed Aug 14 13:46:45 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (nfstable): Renamed to nfs2table.
+ * nfsd.h (nfstable): Likewise.
+ * loop.c (server_loop): Reference nfs2table instead of nfstable.
+
+ * nfsd.h, cache.c, ops.c, xdr.c: Use new names for things that
+ have just been changed in ../nfs/rpcsvc/nfs_prot.h.
+ * xdr.c (hurd_mode_to_nfs_type): New parm VERSION; all callers
+ changed.
+ (encode_fattr): New parm VERSION; all callers changed.
+ * nfsd.h (encode_fattr): New parm VERSION.
+
+Tue Aug 13 14:38:36 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (op_create): Sun's NFS client implementation actually
+ bombs if do do what RFC 1094 says to do in section 2.2.10. So
+ don't pass O_EXCL, but do pass O_TRUNC. That's what NetBSD does.
+
+ * ops.c (op_setattr): Fill in an fattr in reply.
+
+Mon Aug 12 11:15:15 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * Makefile (installationdir): Include quotes in variable expansion
+ here.
+ * main.c: Build index_file better.
+
+ * xdr.c (nfs_error_trans): New errors for IO and XDEV don't have
+ prefixed E.
+
+ * main.c (main): Cast second cthread_fork args.
+
+ * nfsd.h (nfs_error_trans): Add new arg to prototype.
+
+Thu Aug 8 19:20:31 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * xdr.c (nfs_error_trans): New parm `version'. All callers
+ changed. If we are version 3, then return one of the new v3 error
+ codes if appropriate.
+
+Wed Aug 7 11:39:31 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * main.c (index_file): New var.
+ (index_file_name): Initialize from index_file.
+ (main): Don't initialize index_file_name here.
+
+ * ops.c (op_null, op_getattr, op_setattr, op_lookup, op_readlink,
+ count_read_buffersize, op_read, op_write, op_create, op_remove,
+ op_rename, op_link, op_symlink, op_mkdir, op_rmdir,
+ count_readdir_buffersize, op_statfs, op_mnt, op_getport): Add new
+ parm `version'.
+ * loop.c (server_loop): Supply VERSION to PROC->alloc_reply and
+ PROC->func.
+ * nfsd.h (struct procedure): Add new parm to `func' and
+ `alloc_reply' members to specify protocol version.
+
+ * ops.c (mounttable): Use op_null for MOUNTPROC_UMNT and
+ MOUNTPROC_UMNTALL.
+
+ * ops.c (op_create): Ignore most of setattr structure given.
+ (op_mkdir): Likewise.
+ (complete_setattr): Clean up and be less active when possible.
+
+ * ops.c (op_readdir): Correctly end list of directory entries.
+
+ * nfsd.h (authserver): New variable.
+n * main.c: Include <hurd.h>.
+ (main): Initialize AUTHSERVER.
+ * cache.c: Include <hurd/io.h> and <hurd/auth.h>.
+ (create_cached_handle): Reauthenticate port before calling
+ file_getfh.
+
+ * fsys.c (init_filesystems): When setting NFSYS, it needs to be at
+ least as big as INDEX + 1, and install the control port into the
+ correct slot.
+ (init_filesystems): When allocating or reallocating FSYSTABLE,
+ make sure the new entries are zeroed.
+ (enter_filesystem): Likewise.
+
+Tue Aug 6 13:18:40 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * cache.c (create_cached_handle): Always call fsys_getfile when
+ creating a new cached handle instead of relying upon the passed in
+ port.
+
+ * xdr.c (hurd_mode_to_nfs_mode): Specify octal number in octal
+ notation.
+ (encode_fattr): Bother filling in all the fields.
+
+ * loop.c (server_loop): Don't attempt to call cache_handle_rele if
+ C is null.
+
+ * cache.c (fh_hash): Mod-ulize HASH before returning it.
+
+ * cache.c (process_cred): Arrange to skip VERF field for
+ non-AUTH_UNIX creds too.
+
+ * loop.c (server_loop): Don't call skip_cred for the VERF here;
+ process_cred does it for us.
+ * xdr.c (skip_cred): Delete function.
+ * nfsd.h (skip_cred): Delete decl.
+
+ * cache.c (process_cred): After processing gid list, set P after
+ it. Start gid processing loop at 0.
+
+ * main.c (main): Detach one thread for pmap_udp_socket,
+ and NTHREADS threads for main_udp_socket.
+ * loop.c (server_loop): New parm `fd'; don't do select.
+ Ignore errors that we get from recvfrom instead of processing the
+ last message again.
+
+ * nfsd.h (server_loop): New parm.
+
+ * main.c (main): Stringify LOCALSTATEDIR; but for now just pound
+ in /var. Call asprintf correctly.
diff --git a/nfsd/Makefile b/nfsd/Makefile
new file mode 100644
index 00000000..2c05b7ed
--- /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)
+HURDLIBS=threads shouldbeinlibc
+
+include ../Makeconf
+
+CPPFLAGS += -DLOCALSTATEDIR=\"$(localstatedir)\"
+
diff --git a/nfsd/cache.c b/nfsd/cache.c
new file mode 100644
index 00000000..deb8ebe3
--- /dev/null
+++ b/nfsd/cache.c
@@ -0,0 +1,534 @@
+/*
+ Copyright (C) 1996, 1998, 1999 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 <sys/mman.h>
+#include <hurd/fsys.h>
+#include <assert.h>
+#include <string.h>
+#include <hurd/io.h>
+#include <hurd/auth.h>
+#include "nfsd.h"
+
+
+#undef TRUE
+#undef FALSE
+#define malloc spoogie_woogie /* ugh^2. */
+#include <rpc/types.h>
+#include <rpc/auth.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);
+ p += INTSIZE (size);
+ }
+ else
+ {
+ 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 = 0; i < ngids; i++)
+ gids[i] = ntohl (gids[i]);
+
+ p += ngids - 1;
+
+ *credp = idspec_lookup (1, ngids, uid, gids);
+ }
+
+ /* Next is the verf field; skip it entirely */
+ p++; /* skip id */
+ len = htonl (*p++);
+ p += INTSIZE (len);
+
+ 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 < NFS2_FHSIZE; n++)
+ hash += fhandle[n];
+ hash += (int) i >> 6;
+ return hash % FHHASH_TABLE_SIZE;
+}
+
+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, NFS2_FHSIZE))
+ {
+ if (c->references == 0)
+ nfreefh--;
+ c->references++;
+ mutex_unlock (&fhhashlock);
+ *cp = c;
+ return p + NFS2_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), NFS2_FHSIZE - sizeof (int), &port))
+ {
+ mutex_unlock (&fhhashlock);
+ *cp = 0;
+ return p + NFS2_FHSIZE / sizeof (int);
+ }
+
+ c = malloc (sizeof (struct cache_handle));
+ bcopy (p, c->handle, NFS2_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 + NFS2_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 userport)
+{
+ char fhandle[NFS2_FHSIZE];
+ error_t err;
+ struct cache_handle *c;
+ int hash;
+ char *bp = fhandle + sizeof (int);
+ size_t handlelen = NFS2_FHSIZE - sizeof (int);
+ mach_port_t newport, ref;
+
+ /* Authenticate USERPORT so that we can call file_getfh on it. */
+ ref = mach_reply_port ();
+ if (io_reauthenticate (userport, ref, MACH_MSG_TYPE_MAKE_SEND)
+ || auth_user_authenticate (authserver, ref, MACH_MSG_TYPE_MAKE_SEND,
+ &newport))
+ {
+ /* Reauthentication has failed, but maybe the filesystem will let
+ us call file_getfh anyway. */
+ newport = userport;
+ }
+ else
+ mach_port_deallocate (mach_task_self (), userport);
+ mach_port_destroy (mach_task_self (), ref);
+
+ /* Fetch the file handle */
+ *(int *)fhandle = fs;
+ err = file_getfh (newport, &bp, &handlelen);
+ mach_port_deallocate (mach_task_self (), newport);
+ if (err || handlelen != NFS2_FHSIZE - sizeof (int))
+ return 0;
+ if (bp != fhandle + sizeof (int))
+ {
+ bcopy (bp, fhandle + sizeof (int), NFS2_FHSIZE - sizeof (int));
+ munmap (bp, handlelen);
+ }
+
+ /* Cache it */
+ 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, NFS2_FHSIZE))
+ {
+ /* Return this one */
+ if (c->references == 0)
+ nfreefh--;
+ c->references++;
+ mutex_unlock (&fhhashlock);
+ return c;
+ }
+
+ /* Always call fsys_getfile so that we don't depend on the
+ particular open modes of the port passed in. */
+
+ err = fsys_getfile (lookup_filesystem (fs),
+ credc->ids->uids, credc->ids->nuids,
+ credc->ids->gids, credc->ids->ngids,
+ fhandle + sizeof (int), NFS2_FHSIZE - sizeof (int),
+ &newport);
+ if (err)
+ {
+ mutex_unlock (&fhhashlock);
+ return 0;
+ }
+
+ /* Create it anew */
+ c = malloc (sizeof (struct cache_handle));
+ bcopy (fhandle, c->handle, NFS2_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..97137455
--- /dev/null
+++ b/nfsd/fsys.c
@@ -0,0 +1,214 @@
+/* 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;
+ int i;
+
+ fsystable = (struct fsys_spec *) malloc ((fsystablesize = 10)
+ * sizeof (struct fsys_spec));
+ for (i = 0; i < fsystablesize; i++)
+ {
+ fsystable[i].fsys = MACH_PORT_NULL;
+ fsystable[i].name = 0;
+ }
+
+ 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, index * 2 * sizeof (struct fsys_spec));
+ for (i = fsystablesize; i < index * 2; i++)
+ {
+ fsystable[i].fsys = MACH_PORT_NULL;
+ fsystable[i].name = 0;
+ }
+ fsystablesize = index * 2;
+ }
+
+ if (index + 1 > nfsys)
+ nfsys = index + 1;
+
+ fsystable[index].name = name;
+ file_getcontrol (root, &fsystable[index].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));
+ for (i = fsystablesize; i < fsystablesize * 2; i++)
+ {
+ fsystable[i].fsys = MACH_PORT_NULL;
+ fsystable[i].name = 0;
+ }
+ fsystablesize *= 2;
+ }
+
+ 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..6af6381c
--- /dev/null
+++ b/nfsd/loop.c
@@ -0,0 +1,218 @@
+/* Main server loop for nfs server.
+ Copyright (C) 1996, 98 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/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 (int fd)
+{
+ 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;
+ int cc;
+
+ bzero (&fakec, sizeof (struct cache_handle));
+
+ for (;;)
+ {
+ p = (int *) buf;
+ proc = 0;
+ addrlen = sizeof (struct sockaddr_in);
+ cc = recvfrom (fd, buf, MAXIOSIZE, 0, &sender, &addrlen);
+ if (cc == -1)
+ continue; /* ignore errors */
+ 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 = &nfs2table;
+ 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);
+
+ 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, version) + 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)
+ /* The function does its own error processing,
+ and we ignore its return value. */
+ (void) (*proc->func) (c, p, &r, version);
+ else
+ {
+ if (c)
+ {
+ /* Assume success for now and patch it later if necessary */
+ int *errloc = r;
+ *r++ = htonl (0);
+ /* Call processing function, its output after error code. */
+ err = (*proc->func) (c, p, &r, version);
+ if (err)
+ {
+ r = errloc; /* Back up, patch error code, discard rest. */
+ *r++ = htonl (nfs_error_trans (err, version));
+ }
+ }
+ else
+ *r++ = htonl (nfs_error_trans (ESTALE, version));
+ }
+
+ cred_rele (cred);
+ if (c && c != &fakec)
+ cache_handle_rele (c);
+
+ send_reply:
+ cr->data = rbuf;
+ cr->len = (char *)r - rbuf;
+
+ repost_reply:
+ sendto (fd, 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..d4d040c6
--- /dev/null
+++ b/nfsd/main.c
@@ -0,0 +1,94 @@
+/* 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>
+#include <hurd.h>
+
+int main_udp_socket, pmap_udp_socket;
+struct sockaddr_in main_address, pmap_address;
+static char index_file[] = LOCALSTATEDIR "/state/misc/nfsd.index";
+char *index_file_name = index_file;
+
+int
+main (int argc, char **argv)
+{
+ int nthreads;
+ int fail;
+
+ 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;
+
+ authserver = getauth ();
+ 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);
+ fail = bind (main_udp_socket, (struct sockaddr *)&main_address,
+ sizeof (struct sockaddr_in));
+ if (fail)
+ {
+ perror ("Binding NFS socket");
+ exit (1);
+ }
+ fail = bind (pmap_udp_socket, (struct sockaddr *)&pmap_address,
+ sizeof (struct sockaddr_in));
+ if (fail)
+ {
+ perror ("Binding PMAP socket");
+ exit (1);
+ }
+
+ init_filesystems ();
+
+ cthread_detach (cthread_fork ((cthread_fn_t) server_loop,
+ (any_t) pmap_udp_socket));
+
+ while (nthreads--)
+ cthread_detach (cthread_fork ((cthread_fn_t) server_loop,
+ (any_t) main_udp_socket));
+
+ 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..ba423f2d
--- /dev/null
+++ b/nfsd/nfsd.h
@@ -0,0 +1,129 @@
+/*
+ Copyright (C) 1996, 98 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/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <cthreads.h>
+#include <rpc/types.h>
+#include "../nfs/nfs-spec.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[NFS2_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 **, int);
+ size_t (*alloc_reply) (int *, 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;
+
+/* Our auth server */
+auth_t authserver;
+
+
+/* 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 (int);
+
+/* ops.c */
+extern struct proctable nfs2table, mounttable, pmaptable;
+
+/* xdr.c */
+int nfs_error_trans (error_t, int);
+int *encode_fattr (int *, struct stat *, int version);
+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..cf86aa12
--- /dev/null
+++ b/nfsd/ops.c
@@ -0,0 +1,705 @@
+/* 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/mount.h" /* XXX */
+#include <rpc/pmap_prot.h>
+
+static error_t
+op_null (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ return 0;
+}
+
+static error_t
+op_getattr (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ struct stat st;
+ error_t err;
+
+ err = io_stat (c->port, &st);
+ if (!err)
+ *reply = encode_fattr (*reply, &st, version);
+ 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;
+ if (gid == -1)
+ gid = st.st_gid;
+ if (uid != st.st_uid || 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)
+ 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;
+
+ if (atime.seconds != st.st_atime
+ || atime.microseconds != st.st_atime_usec
+ || mtime.seconds != st.st_mtime
+ || mtime.microseconds != st.st_mtime_usec)
+ err = file_utimes (port, atime, mtime);
+
+ return err;
+}
+
+static error_t
+op_setattr (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ error_t err = 0;
+ mode_t mode;
+ struct stat st;
+
+ mode = ntohl (*p++);
+ if (mode != -1)
+ err = file_chmod (c->port, mode);
+
+ if (!err)
+ err = complete_setattr (c->port, p);
+ if (!err)
+ err = io_stat (c->port, &st);
+ if (err)
+ return err;
+
+ *reply = encode_fattr (*reply, &st, version);
+ return 0;
+}
+
+static error_t
+op_lookup (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ 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, version);
+ return 0;
+}
+
+static error_t
+op_readlink (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ 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, int version)
+{
+ return ntohl (*++p); /* skip OFFSET, return COUNT */
+}
+
+static error_t
+op_read (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ 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, version);
+ *reply = encode_data (*reply, bp, buflen);
+ return 0;
+}
+
+static error_t
+op_write (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ 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, version);
+ return 0;
+}
+
+static error_t
+op_create (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ 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;
+ int statchanged = 0;
+ off_t size;
+
+ p = decode_name (p, &name);
+ mode = ntohl (*p++);
+
+ err = dir_lookup (c->port, name, O_NOTRANS | O_CREAT | O_TRUNC, mode,
+ &do_retry, retry_name, &newport);
+ if (!err
+ && (do_retry != FS_RETRY_NORMAL
+ || retry_name[0] != '\0'))
+ err = EACCES;
+
+ if (err)
+ return err;
+
+ if (!err)
+ err = io_stat (newport, &st);
+ if (err)
+ goto errout;
+
+ /* NetBSD ignores most of the setattr fields given; that's good enough
+ for me too. */
+
+ p++, p++; /* skip uid and gid */
+
+ size = ntohl (*p++);
+ if (size != -1 && size != st.st_size)
+ {
+ err = file_set_size (newport, size);
+ statchanged = 1;
+ }
+ if (err)
+ goto errout;
+
+ /* ignore times */
+
+ if (statchanged)
+ err = io_stat (newport, &st);
+
+ if (err)
+ {
+ errout:
+ 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, version);
+ return 0;
+}
+
+static error_t
+op_remove (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ 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,
+ int version)
+{
+ 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,
+ int version)
+{
+ 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,
+ int version)
+{
+ 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,
+ int version)
+{
+ 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;
+
+ /* Ignore the rest of the sattr structure */
+
+ 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, version);
+ return 0;
+}
+
+static error_t
+op_rmdir (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ 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 version)
+{
+ int cookie;
+ unsigned count;
+ error_t err;
+ char *buf;
+ struct dirent *dp;
+ size_t bufsize;
+ int nentries;
+ int i;
+ int *replystart;
+ int *r;
+
+ 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;
+
+ r = *reply;
+
+ if (nentries == 0)
+ {
+ *r++ = htonl (0); /* no entry */
+ *r++ = 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))
+ {
+ *r++ = htonl (1); /* entry present */
+ *r++ = htonl (dp->d_ino);
+ r = encode_string (r, dp->d_name);
+ *r++ = htonl (i + cookie + 1); /* next entry */
+ }
+ *r++ = htonl (0); /* no more entries */
+ *r++ = htonl (0); /* not EOF */
+ }
+
+ *reply = r;
+
+ return 0;
+}
+
+static size_t
+count_readdir_buffersize (int *p, int version)
+{
+ return ntohl (*++p); /* skip COOKIE; return COUNT */
+}
+
+static error_t
+op_statfs (struct cache_handle *c,
+ int *p,
+ int **reply,
+ int version)
+{
+ 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,
+ int version)
+{
+ 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 version)
+{
+ 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 nfs2table =
+{
+ NFS2PROC_NULL, /* first proc */
+ NFS2PROC_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 */
+ { op_null, 0, 0, 0}, /* MOUNTPROC_UMNT */
+ { op_null, 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/xdr.c b/nfsd/xdr.c
new file mode 100644
index 00000000..cb954e75
--- /dev/null
+++ b/nfsd/xdr.c
@@ -0,0 +1,218 @@
+/* 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"
+
+/* Any better ideas? */
+static int
+hurd_mode_to_nfs_mode (mode_t m)
+{
+ return m & 0177777;
+}
+
+static int
+hurd_mode_to_nfs_type (mode_t m, int version)
+{
+ 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 (version == 2 ? NF2FIFO : NF3FIFO);
+
+ default:
+ return (version == 2 ? NF2NON : NFREG);
+ }
+}
+
+/* Encode ST into P and return the next thing to come after it. */
+int *
+encode_fattr (int *p, struct stat *st, int version)
+{
+ *p++ = htonl (hurd_mode_to_nfs_type (st->st_mode, version));
+ *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);
+ *p++ = htonl (st->st_fsid);
+ *p++ = htonl (st->st_ino);
+ *p++ = htonl (st->st_atime);
+ *p++ = htonl (st->st_atime_usec);
+ *p++ = htonl (st->st_mtime);
+ *p++ = htonl (st->st_mtime_usec);
+ *p++ = htonl (st->st_ctime);
+ *p++ = htonl (st->st_ctime_usec);
+ 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, NFS2_FHSIZE);
+ return p + INTSIZE (NFS2_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, int version)
+{
+ switch (err)
+ {
+ case 0:
+ return NFS_OK;
+
+ case EPERM:
+ return NFSERR_PERM;
+
+ case ENOENT:
+ return NFSERR_NOENT;
+
+ case EIO:
+ 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;
+
+ default:
+ if (version == 2)
+ return NFSERR_IO;
+ else switch (err)
+ {
+ case EXDEV:
+ return NFSERR_XDEV;
+
+ case EINVAL:
+ return NFSERR_INVAL;
+
+ case EOPNOTSUPP:
+ return NFSERR_NOTSUPP; /* are we sure here? */
+
+ default:
+ return NFSERR_IO;
+ }
+ }
+}
+
+
+