diff options
Diffstat (limited to 'nfsd')
-rw-r--r-- | nfsd/ChangeLog | 156 | ||||
-rw-r--r-- | nfsd/Makefile | 34 | ||||
-rw-r--r-- | nfsd/cache.c | 534 | ||||
-rw-r--r-- | nfsd/fsys.c | 214 | ||||
-rw-r--r-- | nfsd/loop.c | 218 | ||||
-rw-r--r-- | nfsd/main.c | 94 | ||||
-rw-r--r-- | nfsd/nfsd.h | 129 | ||||
-rw-r--r-- | nfsd/ops.c | 705 | ||||
-rw-r--r-- | nfsd/xdr.c | 218 |
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; + } + } +} + + + |