diff options
Diffstat (limited to 'nfs')
-rw-r--r-- | nfs/ChangeLog | 170 | ||||
-rw-r--r-- | nfs/Makefile | 16 | ||||
-rw-r--r-- | nfs/cache.c | 129 | ||||
-rw-r--r-- | nfs/cred.c | 116 | ||||
-rw-r--r-- | nfs/main.c | 116 | ||||
-rw-r--r-- | nfs/mount.c | 195 | ||||
-rw-r--r-- | nfs/mount.h | 9 | ||||
-rw-r--r-- | nfs/name-cache.c | 305 | ||||
-rw-r--r-- | nfs/nfs-spec.h | 168 | ||||
-rw-r--r-- | nfs/nfs.c | 555 | ||||
-rw-r--r-- | nfs/nfs.h | 85 | ||||
-rw-r--r-- | nfs/ops.c | 1416 | ||||
-rw-r--r-- | nfs/pager.c | 448 | ||||
-rw-r--r-- | nfs/rpc.c | 282 | ||||
-rw-r--r-- | nfs/rpcsvc/mount.h | 81 | ||||
-rw-r--r-- | nfs/rpcsvc/nfs_prot.h | 343 | ||||
-rw-r--r-- | nfs/storage-info.c | 104 |
17 files changed, 2528 insertions, 2010 deletions
diff --git a/nfs/ChangeLog b/nfs/ChangeLog deleted file mode 100644 index e213507e..00000000 --- a/nfs/ChangeLog +++ /dev/null @@ -1,170 +0,0 @@ -Wed Jul 31 13:25:00 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu> - - * ops.c (netfs_attempt_statfs): Use NFSPROC_STATFS, not SETATTR to - do a statfs. - -Tue Jul 23 19:41:07 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * nfs.c (xdr_encode_sattr_times): `struct timespec' now uses a - field prefix of `tv_'. - -Wed Jul 17 13:12:31 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * main.c (mounted_soft): Initialize to zero. - -Thu Jul 4 17:14:42 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * ops.c (netfs_attempt_link) [case SYMLINK]: Include directory - handle as an RPC arg. - -Wed Jun 26 16:41:00 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * main.c (netfs_get_options): New function. - (netfs_parse_runtime_options, netfs_unparse_runtime_options): - Functions removed. - (runtime_argp_parents, runtime_argp, netfs_runtime_argp): New variables. - (main): Use &NETFS_STD_STARTUP_ARGP insteda of NETFS_STARTUP_ARGP. - -Thu Jun 13 09:24:24 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * Makefile (SRCS): Remove pager.c. - * nfs.h (struct netnode): Add member `fileinfo'. - * nfs.h (register_fresh_stat): Add decl. - -Wed Jun 12 22:37:31 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * Makefile (SRCS): Add pager.c. - -Wed May 22 18:49:16 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * main.c (parse_startup_opt): Use ARGP_ERR_UNKNOWN instead of EINVAL. - -Tue May 14 14:00:21 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * ops.c (netfs_attempt_unlink): Add new arg in call to - netfs_attempt_link. - -Sat May 11 01:10:05 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * main.c (parse_common_opt): Use ARGP_ERR_UNKNOWN instead of EINVAL. - -Fri May 10 18:15:11 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * ops.c (netfs_attempt_rename, netfs_attempt_link): New parm EXCL, - but don't implement the hard case yet. - -Thu May 9 20:24:21 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * ops.c (netfs_attempt_statfs): Expect and fill in new statfs - buffer. - -Fri Apr 19 13:50:25 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * main.c (main): Failure to bind privileged ports is indicated by - EACCES, not EPERM. - -Thu Apr 11 13:51:33 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * ops.c (guess_mode_use): New function. - (netfs_check_open_permissions, netfs_report_access): Replace old - clever versions with less obtrusive one. - -Tue Apr 2 09:12:28 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * ops.c (netfs_report_access): Bother to initialize LEN. - -Fri Mar 29 17:26:14 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * rpc.c: Define malloc to something random around include of rpc/* - header files to avoid bogus definition there. - -Fri Mar 29 17:10:58 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * ops.c (netfs_report_access): Make sure netfs_attempt_read return - a reasonable LEN. - (netfs_attempt_write): Truncate to THISAMT instead of AMT. - -Tue Mar 19 11:00:54 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * Makefile (LCLHDRS): Drop rpc.h. - - * consts.c: Doc fixes. - * cache.c: Likewise. - * cred.c: Likewise. - * main.c: Likewise. - * mount.c: Likewise. - * mount.h: Likewise. - * nfs.c: Likewise. - * ops.c: Likewise. - * rpc.c: Likewise. - - * rpc.c (rpc_receive_thread): Allocate receive buffer big enough - for largest read we expect. - - * cache.c (lookup_fhandle): Correctly install new node in hash - table slot. - - * main.c (parse_startup_opt): Pass STATE, not STATE->argp in call - to argp_error. - - * cache.c (lookup_fhandle): Initialize NN->dead_dir and - NN->dead_name. - - * ops.c: Include <unistd.h>. - (register_fresh_stat): Repair syntax. - -Mon Mar 18 19:49:28 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * main.c (main, netfs_parse_runtime_options): Pass new arg to - argp_parse. - -Mon Mar 18 11:19:27 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * ops.c (register_fresh_stat): Set fs_fsid, st_fstype, st_gen, - st_author, and st_flags here. - * nfs.c (xdr_decode_fattr): Don't set st_fstype, st_gen, - st_author, or st_flags here. - - * ops.c (netfs_attempt_write): Increment OFFSET each time around - the loop. - - * nfs.c (xdr_encode_create_state): Call hurd_mode_to_nfs_mode and - htonl on MODE. - - * nfs.c (xdr_encode_sattr_stat): New function. - -Thu Mar 14 15:11:41 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * nfs.h (struct netnode): New members `dtrans' and `transarg'. - * cache.c (lookup_fhandle): Initialize NN->dtrans. - (netfs_node_norefs): Free transarg if necessary. - (recache_handle): New function. - * ops.c (netfs_attempt_mkfile): Make dtrans possible if it - isn't already. - (netfs_attempt_unlink): Likewise, when doing the rename hack. - (netfs_attempt_mksymlink): Implement using dtrans and transarg. - (netfs_attempt_mkdev): Likewise. - (register_fresh_stat): If NP->nn->dtrans is set, then mutate the - mode here. - (netfs_attempt_readlink): If NP->nn->dtrans is SYMLINK, then DTRT. - (netfs_attempt_link): Only issue NFSPROC_LINK if dtrans is not - operative. Otherwise, DTRT. - (netfs_attempt_chmod): Implement type-changing using dtrans. - -Tue Mar 12 15:23:32 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> - - * ops.c (netfs_set_translator, netfs_attempt_mksymlink, - netfs_attempt_mkdev): New functions. - (netfs_attempt_chmod): Detect attempt to change node type. - (netfs_validate_stat): Clear NP->istranslated. - -Mon Mar 4 16:16:13 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * main.c (main): Use NETFS_STARTUP_ARGP. - (netfs_parse_runtime_options, netfs_unparse_runtime_options): New funs. - -Wed Feb 28 19:24:23 1996 Miles Bader <miles@gnu.ai.mit.edu> - - * main.c (options): New variable. - (main): Parse our arguments. - diff --git a/nfs/Makefile b/nfs/Makefile index 66ecaeac..10ca2587 100644 --- a/nfs/Makefile +++ b/nfs/Makefile @@ -1,5 +1,7 @@ -# -# Copyright (C) 1995, 1996 Free Software Foundation +# +# Copyright (C) 1995, 1996, 1997, 2000, 2001, 2008, 2011 Free Software +# Foundation, Inc. +# # Written by Michael I. Bushnell. # # This file is part of the GNU Hurd. @@ -22,10 +24,10 @@ dir := nfs makemode := server target = nfs -LCLHDRS = nfs.h -SRCS = ops.c rpc.c mount.c cred.c nfs.c cache.c consts.c main.c -OBJS = $(subst .c,.o,$(SRCS)) - -nfs: $(OBJS) ../libports/libports.a ../libnetfs/libnetfs.a ../libfshelp/libfshelp.a ../libthreads/libthreads.a +LCLHDRS = nfs.h mount.h nfs-spec.h +SRCS = ops.c rpc.c mount.c nfs.c cache.c consts.c main.c name-cache.c \ + storage-info.c +OBJS = $(SRCS:.c=.o) +HURDLIBS = netfs fshelp iohelp threads ports ihash shouldbeinlibc include ../Makeconf diff --git a/nfs/cache.c b/nfs/cache.c index 65b46575..8f87f5d0 100644 --- a/nfs/cache.c +++ b/nfs/cache.c @@ -1,5 +1,5 @@ -/* Node cache management for NFS client implementation - Copyright (C) 1995, 1996 Free Software Foundation, Inc. +/* cache.c - Node cache management for NFS client implementation. + Copyright (C) 1995, 1996, 1997, 2002 Free Software Foundation, Inc. Written by Michael I. Bushnell, p/BSG. This file is part of the GNU Hurd. @@ -21,49 +21,64 @@ #include "nfs.h" #include <string.h> +#include <netinet/in.h> -/* Hash table containing all the nodes currently active. */ -#define CACHESIZE 512 +/* Hash table containing all the nodes currently active. XXX Was 512, + however, a prime is much nicer for the hash function. 509 is nice + as not only is it prime, it also keeps the array within a page or + two. */ +#define CACHESIZE 509 static struct node *nodehash [CACHESIZE]; -/* Compute and return a hash key for NFS file handle FHANDLE. */ +/* Compute and return a hash key for NFS file handle DATA of LEN + bytes. */ static inline int -hash (void *fhandle) +hash (int *data, size_t len) { unsigned int h = 0; + char *cp = (char *)data; int i; - for (i = 0; i < NFS_FHSIZE; i++) - h += ((char *)fhandle)[i]; + for (i = 0; i < len; i++) + h += cp[i]; return h % CACHESIZE; } -/* Lookup the specified file handle FHANDLE in the hash table. If it - is not present, initialize a new node structure and insert it into - the hash table. Whichever course, a new reference is generated and - the node is returned. */ -struct node * -lookup_fhandle (void *fhandle) +/* Lookup the file handle P (length LEN) in the hash table. If it is + not present, initialize a new node structure and insert it into the + hash table. Whichever course, a new reference is generated and the + node is returned in *NPP; the lock on the node, (*NPP)->LOCK, is + held. */ +void +lookup_fhandle (void *p, size_t len, struct node **npp) { struct node *np; struct netnode *nn; - int h = hash (fhandle); + int h; + + h = hash (p, len); spin_lock (&netfs_node_refcnt_lock); for (np = nodehash[h]; np; np = np->nn->hnext) { - if (bcmp (np->nn->handle, fhandle, NFS_FHSIZE) != 0) + if (np->nn->handle.size != len + || memcmp (np->nn->handle.data, p, len) != 0) continue; np->references++; spin_unlock (&netfs_node_refcnt_lock); mutex_lock (&np->lock); - return np; + *npp = np; + return; } + /* Could not find it */ nn = malloc (sizeof (struct netnode)); - bcopy (fhandle, nn->handle, NFS_FHSIZE); + assert (nn); + + nn->handle.size = len; + memcpy (nn->handle.data, p, len); nn->stat_updated = 0; nn->dtrans = NOT_POSSIBLE; nn->dead_dir = 0; @@ -79,34 +94,58 @@ lookup_fhandle (void *fhandle) spin_unlock (&netfs_node_refcnt_lock); - return np; + *npp = np; } +/* Package holding args to forked_node_delete. */ +struct fnd +{ + struct node *dir; + char *name; +}; + +/* Worker function to delete nodes that don't have any more local + references or links. */ +any_t +forked_node_delete (any_t arg) +{ + struct fnd *args = arg; + + mutex_lock (&args->dir->lock); + netfs_attempt_unlink ((struct iouser *)-1, args->dir, args->name); + netfs_nput (args->dir); + free (args->name); + free (args); + return 0; +}; + /* Called by libnetfs when node NP has no more references. (See - <hurd/libnetfs.h> for details. Just clear local state and remove - from the hash table. */ + <hurd/libnetfs.h> for details. Just clear its local state and + remove it from the hash table. Called and expected to leave with + NETFS_NODE_REFCNT_LOCK held. */ void netfs_node_norefs (struct node *np) { if (np->nn->dead_dir) { - struct node *dir; - char *name; + struct fnd *args; + + args = malloc (sizeof (struct fnd)); + assert (args); np->references++; spin_unlock (&netfs_node_refcnt_lock); - dir = np->nn->dead_dir; - name = np->nn->dead_name; + args->dir = np->nn->dead_dir; + args->name = np->nn->dead_name; np->nn->dead_dir = 0; np->nn->dead_name = 0; netfs_nput (np); - mutex_lock (&dir->lock); - netfs_attempt_unlink ((struct netcred *)-1, dir, name); - - netfs_nput (dir); - free (name); + /* Do this in a separate thread so that we don't wait for it; it + acquires a lock on the dir, which we are not allowed to + do. */ + cthread_detach (cthread_fork (forked_node_delete, (any_t) args)); /* Caller expects us to leave this locked... */ spin_lock (&netfs_node_refcnt_lock); @@ -123,27 +162,41 @@ netfs_node_norefs (struct node *np) } } -/* Change the file handle used for node NP to be HANDLE. Make sure the - hash table stays up to date. */ -void -recache_handle (struct node *np, void *handle) +/* Change the file handle used for node NP to be the handle at P. + Make sure the hash table stays up to date. Return the address + after the handle. The lock on the node should be held. */ +int * +recache_handle (int *p, struct node *np) { int h; + size_t len; + + if (protocol_version == 2) + len = NFS2_FHSIZE; + else + { + len = ntohl (*p); + p++; + } + /* Unlink it */ spin_lock (&netfs_node_refcnt_lock); *np->nn->hprevp = np->nn->hnext; if (np->nn->hnext) np->nn->hnext->nn->hprevp = np->nn->hprevp; + + /* Change the name */ + np->nn->handle.size = len; + memcpy (np->nn->handle.data, p, len); - bcopy (handle, np->nn->handle, NFS_FHSIZE); - - h = hash (handle); + /* Reinsert it */ + h = hash (p, len); np->nn->hnext = nodehash[h]; if (np->nn->hnext) np->nn->hnext->nn->hprevp = &np->nn->hnext; np->nn->hprevp = &nodehash[h]; spin_unlock (&netfs_node_refcnt_lock); + return p + len / sizeof (int); } - diff --git a/nfs/cred.c b/nfs/cred.c deleted file mode 100644 index b83ebaaf..00000000 --- a/nfs/cred.c +++ /dev/null @@ -1,116 +0,0 @@ -/* Credential manipulation for NFS client - Copyright (C) 1995, 1996 Free Software Foundation, Inc. - Written by Michael I. Bushnell, p/BSG. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ - -#include <hurd/netfs.h> -#include <string.h> - -#include "nfs.h" - -/* This lock must always be held when manipulating the reference count - on credential structures. */ -static spin_lock_t cred_refcnt_lock = SPIN_LOCK_INITIALIZER; - -/* Interpret CRED, returning newly malloced storage describing the - user identification references in UIDS, NUIDS, GIDS, and NGIDS. - See <hurd/libnetfs.h> for details. */ -void -netfs_interpret_credential (struct netcred *cred, uid_t **uids, - int *nuids, uid_t **gids, int *ngids) -{ - /* Who says C isn't APL? */ - bcopy (cred->uids, *uids = malloc ((*nuids = cred->nuids) * sizeof (uid_t)), - cred->nuids * sizeof (uid_t)); - bcopy (cred->gids, *gids = malloc ((*ngids = cred->ngids) * sizeof (uid_t)), - cred->ngids * sizeof (uid_t)); -} - -/* Return a new reference to CRED. See <hurd/libnetfs.h> for details. */ -struct netcred * -netfs_copy_credential (struct netcred *cred) -{ - spin_lock (&cred_refcnt_lock); - cred->refcnt++; - spin_unlock (&cred_refcnt_lock); - return cred; -} - -/* Drop a reference to CRED. See <hurd/libnetfs.h> for details. */ -void -netfs_drop_credential (struct netcred *cred) -{ - spin_lock (&cred_refcnt_lock); - cred->refcnt--; - - if (!cred->refcnt) - { - spin_unlock (&cred_refcnt_lock); - free (cred); - } - else - spin_unlock (&cred_refcnt_lock); -} - -/* Make and return a new credential referring to the user identified - by UIDS, NUIDS, GIDS, and NGIDS. See <hurd/libnetfs.h> for - details. */ -struct netcred * -netfs_make_credential (uid_t *uids, - int nuids, - uid_t *gids, - int ngids) -{ - struct netcred *cred; - - cred = malloc (sizeof (struct netcred) - + nuids * sizeof (uid_t) - + ngids * sizeof (uid_t)); - cred->uids = (void *) cred + sizeof (struct netcred); - cred->gids = (void *) cred->uids + nuids * sizeof (uid_t); - cred->nuids = nuids; - cred->ngids = ngids; - cred->refcnt = 1; - - bcopy (uids, cred->uids, nuids + sizeof (uid_t)); - bcopy (gids, cred->gids, ngids + sizeof (uid_t)); - - return cred; -} - -/* Return nonzero iff CRED contains user id UID. */ -int -cred_has_uid (struct netcred *cred, uid_t uid) -{ - int i; - for (i = 0; i < cred->nuids; i++) - if (cred->uids[i] == uid) - return 1; - return 0; -} - -/* Return nonzero iff CRED contains group id GID. */ -int -cred_has_gid (struct netcred *cred, gid_t gid) -{ - int i; - for (i = 0; i < cred->ngids; i++) - if (cred->gids[i] == gid) - return 1; - return 0; -} @@ -1,5 +1,5 @@ /* - Copyright (C) 1996 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 2002 Free Software Foundation, Inc. Written by Michael I. Bushnell, p/BSG. This file is part of the GNU Hurd. @@ -29,23 +29,34 @@ #include <maptime.h> #include <argp.h> #include <argz.h> +#include <error.h> +#include <version.h> + +char *netfs_server_name = "nfs"; +char *netfs_server_version = HURD_VERSION; extern char *localhost (); /* Default number of times to retry RPCs when mounted soft. */ -#define DEFAULT_SOFT_RETRIES 3 +#define DEFAULT_SOFT_RETRIES 3 /* Default number of seconds to timeout cached stat information. */ #define DEFAULT_STAT_TIMEOUT 3 /* Default number of seconds to timeout cached file contents. */ -#define DEFAULT_CACHE_TIMEOUT 3 +#define DEFAULT_CACHE_TIMEOUT 3 + +/* Default number of seconds to timeout cache positive dir hits. */ +#define DEFAULT_NAME_CACHE_TIMEOUT 3 + +/* Default number of seconds to timeout cache negative dir hits. */ +#define DEFAULT_NAME_CACHE_NEG_TIMEOUT 3 /* Default maximum number of bytes to read at once. */ -#define DEFAULT_READ_SIZE 8192 +#define DEFAULT_READ_SIZE 8192 /* Default maximum number of bytes to write at once. */ -#define DEFAULT_WRITE_SIZE 8192 +#define DEFAULT_WRITE_SIZE 8192 /* Number of seconds to timeout cached stat information. */ @@ -54,6 +65,12 @@ int stat_timeout = DEFAULT_STAT_TIMEOUT; /* Number of seconds to timeout cached file contents. */ int cache_timeout = DEFAULT_CACHE_TIMEOUT; +/* Number of seconds to timeout cached positive dir hits. */ +int name_cache_timeout = DEFAULT_NAME_CACHE_TIMEOUT; + +/* Number of seconds to timeout cached negative dir hits. */ +int name_cache_neg_timeout = DEFAULT_NAME_CACHE_NEG_TIMEOUT; + /* Number of seconds to wait for first retransmission of an RPC. */ int initial_transmit_timeout = 1; @@ -74,8 +91,8 @@ int write_size = DEFAULT_WRITE_SIZE; #define OPT_SOFT 's' #define OPT_HARD 'h' -#define OPT_RSIZE 'r' -#define OPT_WSIZE 'w' +#define OPT_RSIZE 'R' +#define OPT_WSIZE 'W' #define OPT_STAT_TO -2 #define OPT_CACHE_TO -3 #define OPT_INIT_TR_TO -4 @@ -88,19 +105,23 @@ int write_size = DEFAULT_WRITE_SIZE; #define OPT_MNT_PROG -11 #define OPT_NFS_PROG -12 #define OPT_PMAP_PORT -13 +#define OPT_NCACHE_TO -14 +#define OPT_NCACHE_NEG_TO -15 /* Return a string corresponding to the printed rep of DEFAULT_what */ #define ___D(what) #what #define __D(what) ___D(what) #define _D(what) __D(DEFAULT_ ## what) +const char *argp_program_version = STANDARD_HURD_VERSION (nfs); + /* Options usable both at startup and at runtime. */ static const struct argp_option common_options[] = { {0,0,0,0,0,1}, {"soft", OPT_SOFT, "RETRIES", OPTION_ARG_OPTIONAL, - "File system requests will eventually fail, after RETRIES tries if" - " specified, otherwise " _D(SOFT_RETRIES)}, + "File system requests will eventually fail, after RETRIES tries" + " (default " _D(SOFT_RETRIES) ")" }, {"hard", OPT_HARD, 0, 0, "Retry file systems requests until they succeed"}, @@ -117,6 +138,12 @@ static const struct argp_option common_options[] = "Timeout for cached stat information (default " _D(STAT_TIMEOUT) ")"}, {"cache-timeout", OPT_CACHE_TO, "SEC", 0, "Timeout for cached file data (default " _D(CACHE_TIMEOUT) ")"}, + {"name-cache-timeout", OPT_NCACHE_TO, "SEC", 0, + "Timeout for positive directory cache entries (default " + _D(NAME_CACHE_TIMEOUT) ")"}, + {"name-cache-neg-timeout", OPT_NCACHE_NEG_TO, "SEC", 0, + "Timeout for negative directory cache entires (default " + _D(NAME_CACHE_NEG_TIMEOUT) ")"}, {"init-transmit-timeout", OPT_INIT_TR_TO,"SEC", 0}, {"max-transmit-timeout", OPT_MAX_TR_TO, "SEC", 0}, @@ -144,6 +171,8 @@ parse_common_opt (int key, char *arg, struct argp_state *state) case OPT_CACHE_TO: cache_timeout = atoi (arg); break; case OPT_INIT_TR_TO: initial_transmit_timeout = atoi (arg); break; case OPT_MAX_TR_TO: max_transmit_timeout = atoi (arg); break; + case OPT_NCACHE_TO: name_cache_timeout = atoi (arg); break; + case OPT_NCACHE_NEG_TO: name_cache_neg_timeout = atoi (arg); break; default: return ARGP_ERR_UNKNOWN; @@ -172,29 +201,32 @@ static const struct argp_option startup_options[] = { { 0 } }; static char *args_doc = "REMOTE_FS [HOST]"; -static char *doc = "If HOST is not specified, an attempt is made to extract" -" it from REMOTE_FS, using either the `HOST:FS' or `FS@HOST' notations."; +static char *doc = "Hurd nfs translator" +"\vIf HOST is not specified, an attempt is made to extract" +" it from REMOTE_FS using either the `HOST:FS' or `FS@HOST' notations."; -static const struct argp * -runtime_argp_parents[] = { &netfs_std_runtime_argp, 0 }; +static const struct argp_child +runtime_argp_children[] = { {&netfs_std_runtime_argp}, {0} }; static struct argp -runtime_argp = { common_options, parse_common_opt, 0, 0, runtime_argp_parents }; +runtime_argp = { common_options, parse_common_opt, 0, 0, + runtime_argp_children }; -/* Use by netfs_set_options to handle runtime option parsing. */ +/* Used by netfs_set_options to handle runtime option parsing. */ struct argp *netfs_runtime_argp = &runtime_argp; +/* Where to find the remote filesystem. */ +static char *remote_fs; /* = 0; */ +static char *host; /* = 0; */ + /* Return an argz string describing the current options. Fill *ARGZ with a pointer to newly malloced storage holding the list and *LEN to the length of that storage. */ error_t -netfs_get_options (char **argz, size_t *argz_len) +netfs_append_args (char **argz, size_t *argz_len) { char buf[80]; error_t err = 0; - *argz = 0; - *argz_len = 0; - #define FOPT(fmt, arg) \ do { \ if (! err) \ @@ -216,12 +248,23 @@ netfs_get_options (char **argz, size_t *argz_len) FOPT ("--cache-timeout=%d", cache_timeout); FOPT ("--init-transmit-timeout=%d", initial_transmit_timeout); FOPT ("--max-transmit-timeout=%d", max_transmit_timeout); + FOPT ("--name-cache-timeout=%d", name_cache_timeout); + FOPT ("--name-cache-neg-timeout=%d", name_cache_neg_timeout); if (! err) err = netfs_append_std_options (argz, argz_len); - if (err) - free (argz); + if (! err) + { + char *fs; + if (asprintf (&fs, "%s:%s", host, remote_fs)) + { + err = argz_add (argz, argz_len, fs); + free (fs); + } + else + err = ENOMEM; + } return err; } @@ -235,6 +278,8 @@ extract_nfs_args (char *spec, char **remote_fs, char **host) char *sep; spec = strdup (spec); /* So we can trash it. */ + if (! spec) + return NULL; sep = index (spec, ':'); if (sep) @@ -259,13 +304,6 @@ extract_nfs_args (char *spec, char **remote_fs, char **host) return 0; } -/* Where to find the remote filesystem. */ -static char *remote_fs = 0; -static char *host = 0; - -/* For debugging. */ -static volatile int hold = 0; - static error_t parse_startup_opt (int key, char *arg, struct argp_state *state) { @@ -285,8 +323,6 @@ parse_startup_opt (int key, char *arg, struct argp_state *state) nfs_port = atoi (arg); break; - case OPT_HOLD: hold = 1; break; - case ARGP_KEY_ARG: if (state->arg_num == 0) remote_fs = arg; @@ -315,18 +351,17 @@ parse_startup_opt (int key, char *arg, struct argp_state *state) int main (int argc, char **argv) { + error_t err; struct argp common_argp = { common_options, parse_common_opt }; - const struct argp *argp_parents[] = - { &common_argp, &netfs_std_startup_argp, 0 }; + const struct argp_child argp_children[] = + { {&common_argp}, {&netfs_std_startup_argp}, {0} }; struct argp argp = - { startup_options, parse_startup_opt, args_doc, doc, argp_parents }; + { startup_options, parse_startup_opt, args_doc, doc, argp_children }; mach_port_t bootstrap; struct sockaddr_in addr; int ret; argp_parse (&argp, argc, argv, 0, 0, 0); - - while (hold); task_get_bootstrap_port (mach_task_self (), &bootstrap); netfs_init (); @@ -350,14 +385,11 @@ main (int argc, char **argv) } while ((ret == -1) && (errno == EADDRINUSE)); if (ret == -1) - { - perror ("binding main udp socket"); - exit (1); - } + error (1, errno, "binding main udp socket"); - errno = maptime_map (0, 0, &mapped_time); - if (errno) - perror ("mapping time"); + err = maptime_map (0, 0, &mapped_time); + if (err) + error (2, err, "mapping time"); cthread_detach (cthread_fork ((cthread_fn_t) timeout_service_thread, 0)); cthread_detach (cthread_fork ((cthread_fn_t) rpc_receive_thread, 0)); diff --git a/nfs/mount.c b/nfs/mount.c index 59363073..6120f87a 100644 --- a/nfs/mount.c +++ b/nfs/mount.c @@ -1,5 +1,5 @@ -/* - Copyright (C) 1995, 1996 Free Software Foundation, Inc. +/* + Copyright (C) 1995,96,97,98,2001,02 Free Software Foundation, Inc. Written by Michael I. Bushnell, p/BSG. This file is part of the GNU Hurd. @@ -18,22 +18,28 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ -#include "nfs.h" - -#include <rpcsvc/mount.h> +#define malloc a_byte_for_every_bozotic_sun_lossage_and_youd_need_a_lotta_ram +#include <rpc/types.h> +#undef TRUE /* Get rid of sun defs. */ +#undef FALSE +#undef malloc #include <rpc/pmap_prot.h> #include <errno.h> +#include <error.h> #include <sys/socket.h> #include <netdb.h> #include <string.h> #include <netinet/in.h> #include <stdio.h> +#include "nfs.h" +#include "mount.h" + /* Service name for portmapper */ char *pmap_service_name = "sunrpc"; /* Fallback port number for portmapper */ -short pmap_service_number = PMAPPORT; +short pmap_service_number = PMAPPORT; /* RPC program for mount server. */ int mount_program = MOUNTPROG; @@ -59,11 +65,17 @@ short nfs_port = NFS_PORT; /* True iff NFS_PORT should be used even if portmapper present. */ int nfs_port_override = 0; +/* Host name and port number we actually decided to use. */ +const char *mounted_hostname; +uint16_t mounted_nfs_port; /* host order */ + +int protocol_version = 2; + /* Set up an RPC for procedure PROCNUM for talking to the portmapper. Allocate storage with malloc and point *BUF at it; caller must free this when done. Return the address where the args for the procedure should be placed. */ -int * +static int * pmap_initialize_rpc (int procnum, void **buf) { return initialize_rpc (PMAPPROG, PMAPVERS, procnum, 0, buf, 0, 0, -1); @@ -73,29 +85,38 @@ pmap_initialize_rpc (int procnum, void **buf) server. Allocate storage with malloc and point *BUF at it; caller must free this when done. Return the address where the args for the procedure should be placed. */ -int * +static int * mount_initialize_rpc (int procnum, void **buf) { return initialize_rpc (MOUNTPROG, MOUNTVERS, procnum, 0, buf, 0, 0, -1); } /* Using the mount protocol, lookup NAME at host HOST. - Return a node for it or null for an error. */ + Return a node for it or null for an error. If an + error occurs, a message is automatically sent to stderr. */ struct node * mount_root (char *name, char *host) { struct sockaddr_in addr; struct hostent *h; - struct servent *s; int *p; void *rpcbuf; int port; + error_t err; struct node *np; short pmapport; /* Lookup the portmapper port number */ if (pmap_service_name) { + struct servent *s; + + /* XXX This will always fail! pmap_service_name will always be "sunrpc" + What should pmap_service_name really be? By definition the second + argument is either "tcp" or "udp" Thus, is this backwards + (as service_name suggests)? If so, should it read: + s = getservbyname (pmap_service_name, "udp"); + or is there something I am missing here? */ s = getservbyname ("sunrpc", pmap_service_name); if (s) pmapport = s->s_port; @@ -112,103 +133,145 @@ mount_root (char *name, char *host) herror (host); return 0; } - + addr.sin_family = h->h_addrtype; - bcopy (h->h_addr_list[0], &addr.sin_addr, h->h_length); + memcpy (&addr.sin_addr, h->h_addr_list[0], h->h_length); addr.sin_port = pmapport; - - connect (main_udp_socket, - (struct sockaddr *)&addr, sizeof (struct sockaddr_in)); - if (!mount_port_override) + if (mount_port_override) + addr.sin_port = htons (mount_port); + else { /* Formulate and send a PMAPPROC_GETPORT request to lookup the mount program on the server. */ + if (connect (main_udp_socket, (struct sockaddr *)&addr, + sizeof (struct sockaddr_in)) == -1) + { + error (0, errno, "server mount program"); + return 0; + } + p = pmap_initialize_rpc (PMAPPROC_GETPORT, &rpcbuf); - *p++ = htonl (MOUNTPROG); - *p++ = htonl (MOUNTVERS); - *p++ = htonl (IPPROTO_UDP); - *p++ = htonl (0); - errno = conduct_rpc (&rpcbuf, &p); - if (!errno) + if (! p) { - port = ntohl (*p++); + error (0, errno, "creating rpc packet"); + return 0; + } + + *(p++) = htonl (MOUNTPROG); + *(p++) = htonl (MOUNTVERS); + *(p++) = htonl (IPPROTO_UDP); + *(p++) = htonl (0); + err = conduct_rpc (&rpcbuf, &p); + if (!err) + { + port = ntohl (*p); + p++; addr.sin_port = htons (port); } else if (mount_port) addr.sin_port = htons (mount_port); else { - free (rpcbuf); - perror ("portmap of mount"); - return 0; + error (0, err, "portmap of mount"); + goto error_with_rpcbuf; } free (rpcbuf); } - else - addr.sin_port = htons (mount_port); - - /* Now talking to the mount program, fetch the file handle + /* Now, talking to the mount program, fetch a file handle for the root. */ - connect (main_udp_socket, - (struct sockaddr *) &addr, sizeof (struct sockaddr_in)); + if (connect (main_udp_socket, (struct sockaddr *) &addr, + sizeof (struct sockaddr_in)) == -1) + { + error (0, errno, "connect"); + goto error_with_rpcbuf; + } + p = mount_initialize_rpc (MOUNTPROC_MNT, &rpcbuf); + if (! p) + { + error (0, errno, "rpc"); + goto error_with_rpcbuf; + } + p = xdr_encode_string (p, name); - errno = conduct_rpc (&rpcbuf, &p); - if (errno) + err = conduct_rpc (&rpcbuf, &p); + if (err) { - free (rpcbuf); - perror (name); - return 0; + error (0, err, "%s", name); + goto error_with_rpcbuf; } /* XXX Protocol spec says this should be a "unix error code"; we'll - pretend that an NFS error code is what's meant, the numbers match + pretend that an NFS error code is what's meant; the numbers match anyhow. */ - errno = nfs_error_trans (htonl (*p++)); - if (errno) + err = nfs_error_trans (htonl (*p)); + p++; + if (err) { - free (rpcbuf); - perror (name); - return 0; + error (0, err, "%s", name); + goto error_with_rpcbuf; } - + /* Create the node for root */ - np = lookup_fhandle (p); - p += NFS_FHSIZE / sizeof (int); + xdr_decode_fhandle (p, &np); free (rpcbuf); mutex_unlock (&np->lock); - if (!nfs_port_override) + if (nfs_port_override) + port = nfs_port; + else { - /* Now send another PMAPPROC_GETPORT request to lookup the nfs server. */ + /* Send another PMAPPROC_GETPORT request to lookup the nfs server. */ addr.sin_port = pmapport; - connect (main_udp_socket, - (struct sockaddr *) &addr, sizeof (struct sockaddr_in)); + if (connect (main_udp_socket, (struct sockaddr *) &addr, + sizeof (struct sockaddr_in)) == -1) + { + error (0, errno, "connect"); + return 0; + } + p = pmap_initialize_rpc (PMAPPROC_GETPORT, &rpcbuf); - *p++ = htonl (NFS_PROGRAM); - *p++ = htonl (NFS_VERSION); - *p++ = htonl (IPPROTO_UDP); - *p++ = htonl (0); - errno = conduct_rpc (&rpcbuf, &p); - if (!errno) - port = ntohl (*p++); + if (! p) + { + error (0, errno, "rpc"); + goto error_with_rpcbuf; + } + *(p++) = htonl (NFS_PROGRAM); + *(p++) = htonl (NFS_VERSION); + *(p++) = htonl (IPPROTO_UDP); + *(p++) = htonl (0); + err = conduct_rpc (&rpcbuf, &p); + if (!err) + { + port = ntohl (*p); + p++; + } else if (nfs_port) port = nfs_port; else { - free (rpcbuf); - perror ("pmap of nfs server"); - return 0; + error (0, err, "portmap of nfs server"); + goto error_with_rpcbuf; } free (rpcbuf); } - else - port = nfs_port; - + addr.sin_port = htons (port); - connect (main_udp_socket, - (struct sockaddr *) &addr, sizeof (struct sockaddr_in)); - + if (connect (main_udp_socket, (struct sockaddr *) &addr, + sizeof (struct sockaddr_in)) == -1) + { + error (0, errno, "connect"); + return 0; + } + + mounted_hostname = host; + mounted_nfs_port = port; + return np; + +error_with_rpcbuf: + free (rpcbuf); + + return 0; } diff --git a/nfs/mount.h b/nfs/mount.h index b1862442..4cb62a83 100644 --- a/nfs/mount.h +++ b/nfs/mount.h @@ -20,8 +20,11 @@ /* These constants define the RPC mount protocol; see RFC 1094. */ -#define MOUNT_RPC_PROGRAM 100005 -#define MOUNT_RPC_VERSION 1 +#ifndef NFS_MOUNT_H +#define NFS_MOUNT_H + +#define MOUNTPROG 100005 +#define MOUNTVERS 1 /* Obnoxious arbitrary limits */ #define MOUNT_MNTPATHLEN 1024 @@ -33,3 +36,5 @@ #define MOUNTPROC_UMNT 3 #define MOUNTPROC_UMNTALL 4 #define MOUNTPROC_EXPORT 5 + +#endif /* NFS_MOUNT_H */ diff --git a/nfs/name-cache.c b/nfs/name-cache.c new file mode 100644 index 00000000..845cda30 --- /dev/null +++ b/nfs/name-cache.c @@ -0,0 +1,305 @@ +/* Directory name lookup caching + + Copyright (C) 1996, 1997 Free Software Foundation, Inc. + Written by Thomas Bushnell, n/BSG, & Miles Bader. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "nfs.h" +#include <string.h> +#include <cacheq.h> + + +/* Maximum number of names to cache at any given time */ +#define MAXCACHE 200 + +/* Maximum length of file name we bother caching */ +#define CACHE_NAME_LEN 100 + +/* Cache entry */ +struct lookup_cache +{ + struct cacheq_hdr hdr; + + /* File handles and lengths for cache entries. 0 for NODE_CACHE_LEN + means a */ + char dir_cache_fh[NFS3_FHSIZE]; + size_t dir_cache_len; + + /* Zero means a `negative' entry -- recording that there's + definitely no node with this name. */ + struct node *np; + + /* Name of the node NODE_CACHE_ID in the directory DIR_CACHE_ID. Entries + with names too long to fit in this buffer aren't cached at all. */ + char name[CACHE_NAME_LEN]; + + /* Strlen of NAME. If this is zero, it's an unused entry. */ + size_t name_len; + + /* Time that this cache entry was created. */ + time_t cache_stamp; + + /* XXX */ + int stati; +}; + +/* The contents of the cache in no particular order */ +static struct cacheq lookup_cache = { sizeof (struct lookup_cache) }; + +static spin_lock_t cache_lock = SPIN_LOCK_INITIALIZER; + +/* Buffer to hold statistics */ +static struct stats +{ + long pos_hits; + long neg_hits; + long miss; + long fetch_errors; +} statistics; + +#define PARTIAL_THRESH 100 +#define NPARTIALS (MAXCACHE / PARTIAL_THRESH) +struct stats partial_stats [NPARTIALS]; + + +/* If there's an entry for NAME, of length NAME_LEN, in directory DIR in the + cache, return its entry, otherwise 0. CACHE_LOCK must be held. */ +static struct lookup_cache * +find_cache (char *dir, size_t len, const char *name, size_t name_len) +{ + struct lookup_cache *c; + int i; + + /* Search the list. All unused entries are contiguous at the end of the + list, so we can stop searching when we see the first one. */ + for (i = 0, c = lookup_cache.mru; + c && c->name_len; + c = c->hdr.next, i++) + if (c->name_len == name_len + && c->dir_cache_len == len + && c->name[0] == name[0] + && memcmp (c->dir_cache_fh, dir, len) == 0 + && strcmp (c->name, name) == 0) + { + c->stati = i / PARTIAL_THRESH; + return c; + } + + return 0; +} + +/* Node NP has just been found in DIR with NAME. If NP is null, this + name has been confirmed as absent in the directory. DIR is the + fhandle of the directory and LEN is its length. */ +void +enter_lookup_cache (char *dir, size_t len, struct node *np, char *name) +{ + struct lookup_cache *c; + size_t name_len = strlen (name); + + if (name_len > CACHE_NAME_LEN - 1) + return; + + spin_lock (&cache_lock); + + if (lookup_cache.length == 0) + /* There should always be an lru_cache; this being zero means that the + cache hasn't been initialized yet. Do so. */ + cacheq_set_length (&lookup_cache, MAXCACHE); + + /* See if there's an old entry for NAME in DIR. If not, replace the least + recently used entry. */ + c = find_cache (dir, len, name, name_len) ?: lookup_cache.lru; + + /* Fill C with the new entry. */ + memcpy (c->dir_cache_fh, dir, len); + c->dir_cache_len = len; + if (c->np) + netfs_nrele (c->np); + c->np = np; + if (c->np) + netfs_nref (c->np); + strcpy (c->name, name); + c->name_len = name_len; + c->cache_stamp = mapped_time->seconds; + + /* Now C becomes the MRU entry! */ + cacheq_make_mru (&lookup_cache, c); + + spin_unlock (&cache_lock); +} + +/* Purge all references in the cache to NAME within directory DIR. */ +void +purge_lookup_cache (struct node *dp, char *name, size_t namelen) +{ + struct lookup_cache *c, *next; + + spin_lock (&cache_lock); + for (c = lookup_cache.mru; c; c = next) + { + /* Save C->hdr.next, since we may move C from this position. */ + next = c->hdr.next; + + if (c->name_len == namelen + && c->dir_cache_len == dp->nn->handle.size + && memcmp (c->dir_cache_fh, dp->nn->handle.data, + c->dir_cache_len) == 0 + && strcmp (c->name, name) == 0) + { + if (c->np) + netfs_nrele (c->np); + c->name_len = 0; + c->np = 0; + cacheq_make_lru (&lookup_cache, c); /* Use C as the next free + entry. */ + } + } + spin_unlock (&cache_lock); +} + +/* Purge all references in the cache to node NP. */ +void +purge_lookup_cache_node (struct node *np) +{ + struct lookup_cache *c, *next; + + spin_lock (&cache_lock); + for (c = lookup_cache.mru; c; c = next) + { + next = c->hdr.next; + + if (c->np == np) + { + netfs_nrele (c->np); + c->name_len = 0; + c->np = 0; + cacheq_make_lru (&lookup_cache, c); + } + } + spin_unlock (&cache_lock); +} + + + +/* Register a negative hit for an entry in the Nth stat class */ +void +register_neg_hit (int n) +{ + int i; + + statistics.neg_hits++; + + for (i = 0; i < n; i++) + partial_stats[i].miss++; + for (; i < NPARTIALS; i++) + partial_stats[i].neg_hits++; +} + +/* Register a positive hit for an entry in the Nth stat class */ +void +register_pos_hit (int n) +{ + int i; + + statistics.pos_hits++; + + for (i = 0; i < n; i++) + partial_stats[i].miss++; + for (; i < NPARTIALS; i++) + partial_stats[i].pos_hits++; +} + +/* Register a miss */ +void +register_miss () +{ + int i; + + statistics.miss++; + for (i = 0; i < NPARTIALS; i++) + partial_stats[i].miss++; +} + + + +/* Scan the cache looking for NAME inside DIR. If we know nothing + about the entry, then return 0. If the entry is confirmed to not + exist, then return -1. Otherwise, return NP for the entry, with + a newly allocated reference. For all return values other than 0, + unlock DIR->LOCK before returning. For positive hits, lock the + returned node. */ +struct node * +check_lookup_cache (struct node *dir, char *name) +{ + struct lookup_cache *c; + + spin_lock (&cache_lock); + + c = find_cache (dir->nn->handle.data, dir->nn->handle.size, + name, strlen (name)); + if (c) + { + int timeout = c->np + ? name_cache_timeout + : name_cache_neg_timeout; + + /* Make sure the entry is still usable; if not, zap it now. */ + if (mapped_time->seconds - c->cache_stamp >= timeout) + { + register_neg_hit (c->stati); + if (c->np) + netfs_nrele (c->np); + c->name_len = 0; + c->np = 0; + cacheq_make_lru (&lookup_cache, c); + spin_unlock (&cache_lock); + return 0; + } + + cacheq_make_mru (&lookup_cache, c); /* Record C as recently used. */ + + if (c->np == 0) + /* A negative cache entry. */ + { + register_neg_hit (c->stati); + spin_unlock (&cache_lock); + mutex_unlock (&dir->lock); + return (struct node *)-1; + } + else + { + struct node *np; + + np = c->np; + netfs_nref (np); + register_pos_hit (c->stati); + spin_unlock (&cache_lock); + + mutex_unlock (&dir->lock); + mutex_lock (&np->lock); + + return np; + } + } + + register_miss (); + spin_unlock (&cache_lock); + + return 0; +} diff --git a/nfs/nfs-spec.h b/nfs/nfs-spec.h new file mode 100644 index 00000000..bed03874 --- /dev/null +++ b/nfs/nfs-spec.h @@ -0,0 +1,168 @@ +#ifndef NFS_NFS_SPEC_H +#define NFS_NFS_SPEC_H + +#define NFS_PORT 2049 +#define NFS_MAXDATA 8192 +#define NFS_MAXPATHLEN 1024 +#define NFS_MAXNAMLEN 255 +#define NFS2_FHSIZE 32 +#define NFS3_FHSIZE 64 +#define NFS_COOKIESIZE 4 +#define NFS_FIFO_DEV -1 +#define NFS3_COOKIEVERFSIZE 8 +#define NFS3_CREATEVERFSIZE 8 +#define NFS3_WRITEVERFSIZE 8 +#define NFSMODE_FMT 0170000 +#define NFSMODE_DIR 0040000 +#define NFSMODE_CHR 0020000 +#define NFSMODE_BLK 0060000 +#define NFSMODE_REG 0100000 +#define NFSMODE_LNK 0120000 +#define NFSMODE_SOCK 0140000 +#define NFSMODE_FIFO 0010000 + +enum nfsstat { + NFS_OK = 0, + NFSERR_PERM = 1, + NFSERR_NOENT = 2, + NFSERR_IO = 5, + NFSERR_NXIO = 6, + NFSERR_ACCES = 13, + NFSERR_EXIST = 17, + NFSERR_XDEV = 18, /* v3 only */ + NFSERR_NODEV = 19, + NFSERR_NOTDIR = 20, + NFSERR_ISDIR = 21, + NFSERR_INVAL = 22, /* v3 only */ + NFSERR_FBIG = 27, + NFSERR_NOSPC = 28, + NFSERR_ROFS = 30, + NFSERR_MLINK = 31, /* v3 only */ + NFSERR_NAMETOOLONG = 63, + NFSERR_NOTEMPTY = 66, + NFSERR_DQUOT = 69, + NFSERR_STALE = 70, + NFSERR_REMOTE = 71, /* v3 only */ + NFSERR_WFLUSH = 99, /* v2 only */ + NFSERR_BADHANDLE = 10001, /* v3 only */ + NFSERR_NOT_SYNC = 10002, /* v3 only */ + NFSERR_BAD_COOKIE = 10003, /* v3 only */ + NFSERR_NOTSUPP = 10004, /* v3 only */ + NFSERR_TOOSMALL = 10005, /* v3 only */ + NFSERR_SERVERFAULT = 10006, /* v3 only */ + NFSERR_BADTYPE = 10007, /* v3 only */ + NFSERR_JUKEBOX = 10008, /* v3 only */ +#define NFSERR_TRYLATER NFSERR_JUKEBOX +}; + + +enum ftype { + NF2NON = 0, /* v2 only */ + NFREG = 1, + NFDIR = 2, + NFBLK = 3, + NFCHR = 4, + NFLNK = 5, + NFSOCK = 6, + NF3FIFO = 7, /* v3 only */ +#define NF2BAD NF3FIFO /* v2 only */ + NF2FIFO = 8, /* v2 only */ +}; + +/* Ways to set the time in setattr structures */ +enum sattr_time_how +{ + DONT_CHANGE = 0, + SET_TO_SERVER_TIME = 1, + SET_TO_CLIENT_TIME = 2, +}; + +/* Construction of ACCESS arg to NFS3PROC_ACCESS. */ +#define ACCESS3_READ 0x01 +#define ACCESS3_LOOKUP 0x02 +#define ACCESS3_MODIFY 0x04 +#define ACCESS3_EXTEND 0x08 +#define ACCESS3_DELETE 0x10 +#define ACCESS3_EXECUTE 0x20 + +/* STABLE arg to NFS3PROC_READ */ +enum stable_how { + UNSTABLE = 0, + DATA_SYNC = 1, + FILE_SYNC = 2, +}; + +/* MODE arg to NFS3PROC_CREATE */ +enum createmode +{ + UNCHECKED = 0, + GUARDED = 1, + EXCLUSIVE = 2, +}; + +#define NFS_PROGRAM ((u_long)100003) +#define NFS_VERSION ((u_long)2) + +#define NFS_PROTOCOL_FUNC(proc,vers) \ + (vers == 2 ? NFS2PROC_ ## proc : NFS3PROC_ ## proc) + +#define NFSPROC_NULL(v) NFS_PROTOCOL_FUNC (NULL,v) +#define NFSPROC_GETATTR(v) NFS_PROTOCOL_FUNC (GETATTR, v) +#define NFSPROC_SETATTR(v) NFS_PROTOCOL_FUNC (SETATTR, v) +#define NFSPROC_LOOKUP(v) NFS_PROTOCOL_FUNC (LOOKUP, v) +#define NFSPROC_READLINK(v) NFS_PROTOCOL_FUNC (READLINK, v) +#define NFSPROC_READ(v) NFS_PROTOCOL_FUNC (READ, v) +#define NFSPROC_WRITE(v) NFS_PROTOCOL_FUNC (WRITE, v) +#define NFSPROC_CREATE(v) NFS_PROTOCOL_FUNC (CREATE, v) +#define NFSPROC_REMOVE(v) NFS_PROTOCOL_FUNC (REMOVE, v) +#define NFSPROC_RENAME(v) NFS_PROTOCOL_FUNC (RENAME, v) +#define NFSPROC_LINK(v) NFS_PROTOCOL_FUNC (LINK, v) +#define NFSPROC_SYMLINK(v) NFS_PROTOCOL_FUNC (SYMLINK, v) +#define NFSPROC_MKDIR(v) NFS_PROTOCOL_FUNC (MKDIR, v) +#define NFSPROC_RMDIR(v) NFS_PROTOCOL_FUNC (RMDIR, v) +#define NFSPROC_READDIR(v) NFS_PROTOCOL_FUNC (READDIR, v) + +/* Values for each protocol */ +#define NFS2PROC_NULL 0 +#define NFS2PROC_GETATTR 1 +#define NFS2PROC_SETATTR 2 +#define NFS2PROC_ROOT 3 +#define NFS2PROC_LOOKUP 4 +#define NFS2PROC_READLINK 5 +#define NFS2PROC_READ 6 +#define NFS2PROC_WRITECACHE 7 +#define NFS2PROC_WRITE 8 +#define NFS2PROC_CREATE 9 +#define NFS2PROC_REMOVE 10 +#define NFS2PROC_RENAME 11 +#define NFS2PROC_LINK 12 +#define NFS2PROC_SYMLINK 13 +#define NFS2PROC_MKDIR 14 +#define NFS2PROC_RMDIR 15 +#define NFS2PROC_READDIR 16 +#define NFS2PROC_STATFS 17 + +#define NFS3PROC_NULL 0 +#define NFS3PROC_GETATTR 1 +#define NFS3PROC_SETATTR 2 +#define NFS3PROC_LOOKUP 3 +#define NFS3PROC_ACCESS 4 +#define NFS3PROC_READLINK 5 +#define NFS3PROC_READ 6 +#define NFS3PROC_WRITE 7 +#define NFS3PROC_CREATE 8 +#define NFS3PROC_MKDIR 9 +#define NFS3PROC_SYMLINK 10 +#define NFS3PROC_MKNOD 11 +#define NFS3PROC_REMOVE 12 +#define NFS3PROC_RMDIR 13 +#define NFS3PROC_RENAME 14 +#define NFS3PROC_LINK 15 +#define NFS3PROC_READDIR 16 +#define NFS3PROC_READDIRPLUS 17 +#define NFS3PROC_FSSTAT 18 +#define NFS3PROC_FSINFO 19 +#define NFS3PROC_PATHCONF 20 +#define NFS3PROC_COMMIT 21 + +#endif /* NFS_NFS_SPEC_H */ @@ -1,5 +1,8 @@ -/* XDR frobbing and lower level routines for NFS client - Copyright (C) 1995, 1996 Free Software Foundation, Inc. +/* nfs.c - XDR frobbing and lower level routines for NFS client. + + Copyright (C) 1995, 1996, 1997, 1999, 2002, 2007 + Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. This file is part of the GNU Hurd. @@ -24,12 +27,13 @@ #include <netinet/in.h> #include <stdio.h> -/* Convert an NFS mode (TYPE and MODE) to a Hurd mode and return it. */ +/* Convert an NFS mode (TYPE and MODE) to a Hurd mode and return + it. */ mode_t nfs_mode_to_hurd_mode (int type, int mode) { int hurdmode; - + switch (type) { case NFDIR: @@ -45,12 +49,9 @@ nfs_mode_to_hurd_mode (int type, int mode) break; case NFREG: - case NFNON: - case NFBAD: - default: hurdmode = S_IFREG; break; - + case NFLNK: hurdmode = S_IFLNK; break; @@ -59,16 +60,39 @@ nfs_mode_to_hurd_mode (int type, int mode) hurdmode = S_IFSOCK; break; - case NFFIFO: - hurdmode = S_IFIFO; + default: + if (protocol_version == 2) + switch (type) + { + case NF2NON: + case NF2BAD: + default: + hurdmode = S_IFREG; + break; + + case NF2FIFO: + hurdmode = S_IFIFO; + break; + } + else + switch (type) + { + case NF3FIFO: + hurdmode = S_IFIFO; + break; + + default: + hurdmode = S_IFREG; + break; + } break; } - + hurdmode |= mode & ~NFSMODE_FMT; return hurdmode; } -/* Convert a Hurd mode to an NFS mode */ +/* Convert a Hurd mode to an NFS mode. */ int hurd_mode_to_nfs_mode (mode_t mode) { @@ -77,170 +101,384 @@ hurd_mode_to_nfs_mode (mode_t mode) return mode & 07777; } +/* Convert a Hurd mode to an NFS type. */ +int +hurd_mode_to_nfs_type (mode_t mode) +{ + switch (mode & S_IFMT) + { + case S_IFDIR: + return NFDIR; + + case S_IFCHR: + default: + return NFCHR; + + case S_IFBLK: + return NFBLK; + + case S_IFREG: + return NFREG; + + case S_IFLNK: + return NFLNK; + + case S_IFSOCK: + return NFSOCK; + + case S_IFIFO: + return protocol_version == 2 ? NF2FIFO : NF3FIFO; + } +} + + /* Each of the functions on this page copies its second arg to *P, converting it to XDR representation along the way. They then - return the address after the copied value. */ + return the address after the copied value. */ -/* Encode an NFS file handle. */ +/* Encode an NFS file handle. */ int * -xdr_encode_fhandle (int *p, void *fhandle) +xdr_encode_fhandle (int *p, struct fhandle *fhandle) { - bcopy (fhandle, p, NFS_FHSIZE); - return p + INTSIZE (NFS_FHSIZE); + if (protocol_version == 2) + { + memcpy (p, fhandle->data, NFS2_FHSIZE); + return p + INTSIZE (NFS2_FHSIZE); + } + else + return xdr_encode_data (p, fhandle->data, fhandle->size); } -/* Encode uninterpreted bytes. */ +/* Encode uninterpreted bytes. */ int * xdr_encode_data (int *p, char *data, size_t len) { int nints = INTSIZE (len); - + p[nints] = 0; - *p++ = htonl (len); - bcopy (data, p, len); + *(p++) = htonl (len); + memcpy (p, data, len); return p + nints; } -/* Encode a C string. */ +/* Encode a 64 bit integer. */ +int * +xdr_encode_64bit (int *p, long long n) +{ + *(p++) = htonl (n & 0xffffffff00000000LL >> 32); + *(p++) = htonl (n & 0xffffffff); + return p; +} + +/* Encode a C string. */ int * xdr_encode_string (int *p, char *string) { return xdr_encode_data (p, string, strlen (string)); } - -/* Encode a MODE into an otherwise empty sattr. */ + +/* Encode a MODE into an otherwise empty sattr. */ int * xdr_encode_sattr_mode (int *p, mode_t mode) { - *p++ = htonl (hurd_mode_to_nfs_mode (mode)); - *p++ = -1; /* uid */ - *p++ = -1; /* gid */ - *p++ = -1; /* size */ - *p++ = -1; /* atime secs */ - *p++ = -1; /* atime usecs */ - *p++ = -1; /* mtime secs */ - *p++ = -1; /* mtime usecs */ + if (protocol_version == 2) + { + *(p++) = htonl (hurd_mode_to_nfs_mode (mode)); + *(p++) = -1; /* uid */ + *(p++) = -1; /* gid */ + *(p++) = -1; /* size */ + *(p++) = -1; /* atime secs */ + *(p++) = -1; /* atime usecs */ + *(p++) = -1; /* mtime secs */ + *(p++) = -1; /* mtime usecs */ + } + else + { + *(p++) = htonl (1); /* set mode */ + *(p++) = htonl (hurd_mode_to_nfs_mode (mode)); + *(p++) = 0; /* no uid */ + *(p++) = 0; /* no gid */ + *(p++) = 0; /* no size */ + *(p++) = DONT_CHANGE; /* no atime */ + *(p++) = DONT_CHANGE; /* no mtime */ + } return p; } -/* Encode UID and GID into an otherwise empty sattr. */ +/* Encode UID and GID into an otherwise empty sattr. */ int * xdr_encode_sattr_ids (int *p, u_int uid, u_int gid) { - *p++ = -1; /* mode */ - *p++ = htonl (uid); - *p++ = htonl (gid); - *p++ = -1; /* size */ - *p++ = -1; /* atime secs */ - *p++ = -1; /* atime usecs */ - *p++ = -1; /* mtime secs */ - *p++ = -1; /* mtime usecs */ + if (protocol_version == 2) + { + *(p++) = -1; /* mode */ + *(p++) = htonl (uid); + *(p++) = htonl (gid); + *(p++) = -1; /* size */ + *(p++) = -1; /* atime secs */ + *(p++) = -1; /* atime usecs */ + *(p++) = -1; /* mtime secs */ + *(p++) = -1; /* mtime usecs */ + } + else + { + *(p++) = 0; /* no mode */ + *(p++) = htonl (1); /* set uid */ + *(p++) = htonl (uid); + *(p++) = htonl (1); /* set gid */ + *(p++) = htonl (gid); + *(p++) = 0; /* no size */ + *(p++) = DONT_CHANGE; /* no atime */ + *(p++) = DONT_CHANGE; /* no mtime */ + } return p; } -/* Encode a file size into an otherwise empty sattr. */ +/* Encode a file size into an otherwise empty sattr. */ int * xdr_encode_sattr_size (int *p, off_t size) { - *p++ = -1; /* mode */ - *p++ = -1; /* uid */ - *p++ = -1; /* gid */ - *p++ = htonl (size); - *p++ = -1; /* atime secs */ - *p++ = -1; /* atime usecs */ - *p++ = -1; /* mtime secs */ - *p++ = -1; /* mtime secs */ + if (protocol_version == 2) + { + *(p++) = -1; /* mode */ + *(p++) = -1; /* uid */ + *(p++) = -1; /* gid */ + *(p++) = htonl (size); + *(p++) = -1; /* atime secs */ + *(p++) = -1; /* atime usecs */ + *(p++) = -1; /* mtime secs */ + *(p++) = -1; /* mtime secs */ + } + else + { + *(p++) = 0; /* no mode */ + *(p++) = 0; /* no uid */ + *(p++) = 0; /* no gid */ + *(p++) = htonl (1); /* size */ + p = xdr_encode_64bit (p, size); + *(p++) = DONT_CHANGE; /* no atime */ + *(p++) = DONT_CHANGE; /* no mtime */ + } return p; } -/* Encode ATIME and MTIME into an otherwise empty sattr. */ +/* Encode ATIME and MTIME into an otherwise empty sattr. */ int * xdr_encode_sattr_times (int *p, struct timespec *atime, struct timespec *mtime) { - *p++ = -1; /* mode */ - *p++ = -1; /* uid */ - *p++ = -1; /* gid */ - *p++ = -1; /* size */ - *p++ = htonl (atime->tv_sec); - *p++ = htonl (atime->tv_nsec * 1000); - *p++ = htonl (mtime->tv_sec); - *p++ = htonl (mtime->tv_nsec * 1000); + if (protocol_version == 2) + { + *(p++) = -1; /* mode */ + *(p++) = -1; /* uid */ + *(p++) = -1; /* gid */ + *(p++) = -1; /* size */ + *(p++) = htonl (atime->tv_sec); + *(p++) = htonl (atime->tv_nsec / 1000); + *(p++) = htonl (mtime->tv_sec); + *(p++) = htonl (mtime->tv_nsec / 1000); + } + else + { + *(p++) = 0; /* no mode */ + *(p++) = 0; /* no uid */ + *(p++) = 0; /* no gid */ + *(p++) = 0; /* no size */ + *(p++) = htonl (SET_TO_CLIENT_TIME); /* atime */ + *(p++) = htonl (atime->tv_sec); + *(p++) = htonl (atime->tv_nsec); + *(p++) = htonl (SET_TO_CLIENT_TIME); /* mtime */ + *(p++) = htonl (mtime->tv_sec); + *(p++) = htonl (mtime->tv_nsec); + } return p; } -/* Encode MODE and a size of 0 into an otherwise empty sattr. */ +/* Encode MODE, a size of zero, and the specified owner into an + otherwise empty sattr. */ int * -xdr_encode_create_state (int *p, - mode_t mode) +xdr_encode_create_state (int *p, + mode_t mode, + uid_t owner) { - *p++ = htonl (hurd_mode_to_nfs_mode (mode)); - *p++ = -1; /* uid */ - *p++ = -1; /* gid */ - *p++ = 0; /* size */ - *p++ = -1; /* atime sec */ - *p++ = -1; /* atime usec */ - *p++ = -1; /* mtime sec */ - *p++ = -1; /* mtime usec */ + if (protocol_version == 2) + { + *(p++) = htonl (hurd_mode_to_nfs_mode (mode)); + *(p++) = htonl (owner); /* uid */ + *(p++) = -1; /* gid */ + *(p++) = 0; /* size */ + *(p++) = -1; /* atime sec */ + *(p++) = -1; /* atime usec */ + *(p++) = -1; /* mtime sec */ + *(p++) = -1; /* mtime usec */ + } + else + { + *(p++) = htonl (1); /* mode */ + *(p++) = htonl (hurd_mode_to_nfs_mode (mode)); + *(p++) = htonl (1); /* set uid */ + *(p++) = htonl (owner); + *(p++) = 0; /* no gid */ + *(p++) = htonl (1); /* set size */ + p = xdr_encode_64bit (p, 0); + *(p++) = htonl (SET_TO_SERVER_TIME); /* atime */ + *(p++) = htonl (SET_TO_SERVER_TIME); /* mtime */ + } return p; } -/* Encode ST into an sattr. */ +/* Encode ST into an sattr. */ int * xdr_encode_sattr_stat (int *p, struct stat *st) { - *p++ = htonl (st->st_mode); - *p++ = htonl (st->st_uid); - *p++ = htonl (st->st_gid); - *p++ = htonl (st->st_size); - *p++ = htonl (st->st_atime); - *p++ = htonl (st->st_atime_usec); - *p++ = htonl (st->st_mtime); - *p++ = htonl (st->st_mtime_usec); + if (protocol_version == 2) + { + *(p++) = htonl (hurd_mode_to_nfs_mode (st->st_mode)); + *(p++) = htonl (st->st_uid); + *(p++) = htonl (st->st_gid); + *(p++) = htonl (st->st_size); + *(p++) = htonl (st->st_atim.tv_sec); + *(p++) = htonl (st->st_atim.tv_nsec / 1000); + *(p++) = htonl (st->st_mtim.tv_sec); + *(p++) = htonl (st->st_mtim.tv_nsec / 1000); + } + else + { + *(p++) = htonl (1); /* set mode */ + *(p++) = htonl (hurd_mode_to_nfs_mode (st->st_mode)); + *(p++) = htonl (1); /* set uid */ + *(p++) = htonl (st->st_uid); + *(p++) = htonl (1); /* set gid */ + *(p++) = htonl (st->st_gid); + *(p++) = htonl (1); /* set size */ + p = xdr_encode_64bit (p, st->st_size); + *(p++) = htonl (SET_TO_CLIENT_TIME); /* set atime */ + *(p++) = htonl (st->st_atim.tv_sec); + *(p++) = htonl (st->st_atim.tv_nsec); + *(p++) = htonl (SET_TO_CLIENT_TIME); /* set mtime */ + *(p++) = htonl (st->st_mtim.tv_sec); + *(p++) = htonl (st->st_mtim.tv_nsec); + } return p; } +/* Decode *P into a long long; return the address of the following + data. */ +int * +xdr_decode_64bit (int *p, long long *n) +{ + long long high, low; + high = ntohl (*p); + p++; + low = ntohl (*p); + p++; + *n = ((high & 0xffffffff) << 32) | (low & 0xffffffff); + return p; +} + +/* Decode *P into an fhandle and look up the associated node. Return + the address of the following data. */ +int * +xdr_decode_fhandle (int *p, struct node **npp) +{ + size_t len; + + if (protocol_version == 2) + len = NFS2_FHSIZE; + else + { + len = ntohl (*p); + p++; + } + /* Enter into cache. */ + lookup_fhandle (p, len, npp); + return p + len / sizeof (int); +} + /* Decode *P into a stat structure; return the address of the - following data. */ + following data. */ int * xdr_decode_fattr (int *p, struct stat *st) { int type, mode; - - type = ntohl (*p++); - mode = ntohl (*p++); + + type = ntohl (*p); + p++; + mode = ntohl (*p); + p++; st->st_mode = nfs_mode_to_hurd_mode (type, mode); - st->st_nlink = ntohl (*p++); - st->st_uid = ntohl (*p++); - st->st_gid = ntohl (*p++); - st->st_size = ntohl (*p++); - st->st_blksize = ntohl (*p++); - st->st_rdev = ntohl (*p++); - st->st_blocks = ntohl (*p++); - st->st_fsid = ntohl (*p++); - st->st_ino = ntohl (*p++); - st->st_atime = ntohl (*p++); - st->st_atime_usec = ntohl (*p++); - st->st_mtime = ntohl (*p++); - st->st_mtime_usec = ntohl (*p++); - st->st_ctime = ntohl (*p++); - st->st_ctime_usec = ntohl (*p++); + st->st_nlink = ntohl (*p); + p++; + st->st_uid = ntohl (*p); + p++; + st->st_gid = ntohl (*p); + p++; + if (protocol_version == 2) + { + st->st_size = ntohl (*p); + p++; + st->st_blksize = ntohl (*p); + p++; + st->st_rdev = ntohl (*p); + p++; + st->st_blocks = ntohl (*p); + p++; + } + else + { + long long size; + int major, minor; + p = xdr_decode_64bit (p, &size); + st->st_size = size; + p = xdr_decode_64bit (p, &size); + st->st_blocks = size / 512; + st->st_blksize = read_size < write_size ? read_size : write_size; + major = ntohl (*p); + p++; + minor = ntohl (*p); + p++; + st->st_rdev = makedev (major, minor); + } + st->st_fsid = ntohl (*p); + p++; + st->st_ino = ntohl (*p); + p++; + st->st_atim.tv_sec = ntohl (*p); + p++; + st->st_atim.tv_nsec = ntohl (*p); + p++; + st->st_mtim.tv_sec = ntohl (*p); + p++; + st->st_mtim.tv_nsec = ntohl (*p); + p++; + st->st_ctim.tv_sec = ntohl (*p); + p++; + st->st_ctim.tv_nsec = ntohl (*p); + p++; + + if (protocol_version < 3) + { + st->st_atim.tv_nsec *= 1000; + st->st_mtim.tv_nsec *= 1000; + st->st_ctim.tv_nsec *= 1000; + } return p; } /* Decode *P into a string, stored at BUF; return the address - of the following data. */ + of the following data. */ int * xdr_decode_string (int *p, char *buf) { int len; - - len = ntohl (*p++); - bcopy (p, buf, len); + + len = ntohl (*p); + p++; + memcpy (buf, p, len); buf[len] = '\0'; return p + INTSIZE (len); } @@ -253,28 +491,29 @@ xdr_decode_string (int *p, char *buf) means superuser), NP (identifying the node we are operating on), and SECOND_GID (specifying another GID the server might be interested in). Allocate at least LEN bytes of space for bulk data in - addition to the normal amount for an RPC. */ + addition to the normal amount for an RPC. */ int * -nfs_initialize_rpc (int rpc_proc, struct netcred *cred, +nfs_initialize_rpc (int rpc_proc, struct iouser *cred, size_t len, void **bufp, struct node *np, uid_t second_gid) { uid_t uid; uid_t gid; error_t err; - + /* Use heuristics to figure out what ids to present to the server. - Don't lie, but adjust ids as necessary to secure the desired result. */ + Don't lie, but adjust ids as necessary to secure the desired + result. */ - if (cred == (struct netcred *) -1) + if (cred == (struct iouser *) -1) { uid = gid = 0; second_gid = -1; } else if (cred - && (cred->nuids || cred->ngids)) + && (cred->uids->num || cred->gids->num)) { - if (cred_has_uid (cred, 0)) + if (idvec_contains (cred->uids, 0)) { err = netfs_validate_stat (np, 0); uid = 0; @@ -284,35 +523,35 @@ nfs_initialize_rpc (int rpc_proc, struct netcred *cred, } else { - if (cred->nuids == 0) + if (cred->uids->num == 0) uid = -2; - else if (cred->nuids == 1) - uid = cred->uids[0]; + else if (cred->uids->num == 1) + uid = cred->uids->ids[0]; else { err = netfs_validate_stat (np, 0); if (err) { - uid = cred->uids[0]; + uid = cred->uids->ids[0]; printf ("NFS warning, internal stat failure\n"); } else { - if (cred_has_uid (cred, np->nn_stat.st_uid)) + if (idvec_contains (cred->uids, np->nn_stat.st_uid)) uid = np->nn_stat.st_uid; else - uid = cred->uids[0]; + uid = cred->uids->ids[0]; } } - if (cred->ngids == 0) + if (cred->gids->num == 0) { gid = -2; second_gid = -1; } - else if (cred->ngids == 1) + else if (cred->gids->num == 1) { - gid = cred->gids[0]; + gid = cred->gids->ids[0]; second_gid = -1; } else @@ -320,18 +559,18 @@ nfs_initialize_rpc (int rpc_proc, struct netcred *cred, err = netfs_validate_stat (np, 0); if (err) { - gid = cred->gids[0]; + gid = cred->gids->ids[0]; printf ("NFS warning, internal stat failure\n"); } else { - if (cred_has_gid (cred, np->nn_stat.st_gid)) + if (idvec_contains (cred->gids, np->nn_stat.st_gid)) gid = np->nn_stat.st_gid; else - gid = cred->gids[0]; - } + gid = cred->gids->ids[0]; + } if (second_gid != -1 - && !cred_has_gid (cred, second_gid)) + && !idvec_contains (cred->gids, second_gid)) second_gid = -1; } } @@ -343,7 +582,8 @@ nfs_initialize_rpc (int rpc_proc, struct netcred *cred, uid, gid, second_gid); } -/* ERROR is an NFS error code; return the correspending Hurd error. */ +/* ERROR is an NFS error code; return the correspending Hurd + error. */ error_t nfs_error_trans (int error) { @@ -351,58 +591,91 @@ nfs_error_trans (int error) { case NFS_OK: return 0; - + case NFSERR_PERM: return EPERM; case NFSERR_NOENT: return ENOENT; - + case NFSERR_IO: return EIO; - + case NFSERR_NXIO: return ENXIO; - + case NFSERR_ACCES: return EACCES; - + case NFSERR_EXIST: return EEXIST; - + case NFSERR_NODEV: return ENODEV; - + case NFSERR_NOTDIR: return ENOTDIR; - + case NFSERR_ISDIR: return EISDIR; - + case NFSERR_FBIG: return E2BIG; - + case NFSERR_NOSPC: return ENOSPC; case NFSERR_ROFS: return EROFS; - + case NFSERR_NAMETOOLONG: return ENAMETOOLONG; - + case NFSERR_NOTEMPTY: return ENOTEMPTY; - + case NFSERR_DQUOT: return EDQUOT; - + case NFSERR_STALE: return ESTALE; - + case NFSERR_WFLUSH: - default: + /* Not known in v3, but we just give EINVAL for unknown errors + so it's the same. */ return EINVAL; + + default: + if (protocol_version == 2) + return EINVAL; + else + switch (error) + { + case NFSERR_XDEV: + return EXDEV; + + case NFSERR_INVAL: + case NFSERR_REMOTE: /* not sure about this one */ + default: + return EINVAL; + + case NFSERR_MLINK: + return EMLINK; + + case NFSERR_NOTSUPP: + case NFSERR_BADTYPE: + return EOPNOTSUPP; + + case NFSERR_SERVERFAULT: + return EIO; + + case NFSERR_BADHANDLE: + case NFSERR_NOT_SYNC: + case NFSERR_BAD_COOKIE: + case NFSERR_TOOSMALL: + case NFSERR_JUKEBOX: /* ??? */ + /* These indicate bugs in the client, so EGRATUITOUS is right. */ + return EGRATUITOUS; + } } } - @@ -1,5 +1,5 @@ /* Data structures and global variables for NFS client - Copyright (C) 1994, 1995, 1996 Free Software Foundation + Copyright (C) 1994,95,96,97,99,2001 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -15,20 +15,30 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* Needed for the rpcsvc include files to work. */ -typedef int bool_t; /* Ick. */ +#ifndef NFS_NFS_H +#define NFS_NFS_H #include <sys/stat.h> #include <sys/types.h> -#include <rpcsvc/nfs_prot.h> +#include <stdint.h> +#include <sys/mman.h> +#include "nfs-spec.h" #include <hurd/netfs.h> -/* One of these exists for private data needed by the client for each +/* A file handle */ +struct fhandle +{ + size_t size; + + /* Leave enough room for the largest possible fhandle. */ + char data[NFS3_FHSIZE]; +}; + +/* There exists one of there for the private data needed by each client node. */ -struct netnode +struct netnode { - char handle[NFS_FHSIZE]; + struct fhandle handle; time_t stat_updated; struct node *hnext, **hprevp; @@ -49,9 +59,9 @@ struct netnode char *name; dev_t indexes; } transarg; - + #ifdef notyet - /* This indicates that the length of the file must be at + /* This indicates that the length of the file must be at least this big because we've written this much locally, even if the server thinks we haven't gone this far. */ off_t extend_len; @@ -60,27 +70,19 @@ struct netnode struct user_pager_info *fileinfo; /* If this node has been renamed by "deletion" then - this is the directory and name in that directory which - is holding the node */ + this is the directory and the name in that directory + which is holding the node */ struct node *dead_dir; char *dead_name; }; -/* Credential structure to identify a particular user. */ -struct netcred -{ - uid_t *uids, *gids; - int nuids, ngids; - int refcnt; -}; - /* Socket file descriptor for talking to RPC servers. */ int main_udp_socket; /* Our hostname */ char *hostname; -/* The current time */ +/* The current time */ volatile struct mapped_time_value *mapped_time; /* Some tunable parameters */ @@ -91,6 +93,12 @@ extern int stat_timeout; /* How long to keep around file contents caches */ extern int cache_timeout; +/* How long to keep around positive dir cache entries */ +extern int name_cache_timeout; + +/* How long to keep around negative dir cache entries */ +extern int name_cache_neg_timeout; + /* How long to wait for replies before re-sending RPC's. */ extern int initial_transmit_timeout; extern int max_transmit_timeout; @@ -120,7 +128,7 @@ extern int mount_program; /* RPC program version for the mount agent */ extern int mount_version; -/* If this is nonzero, it's the port to use for the mount agent if +/* If this is nonzero, it's the port to use for the mount agent if the portmapper fails or can't be found. */ extern short mount_port; @@ -134,7 +142,7 @@ extern int nfs_program; /* RPC program version for the NFS server */ extern int nfs_version; -/* If this is nonzero, it's the port to be used to find the nfs agent +/* If this is nonzero, it's the port to be used to find the nfs agent if the portmapper fails or can't be found */ extern short nfs_port; @@ -142,17 +150,17 @@ extern short nfs_port; portmapper. */ extern int nfs_port_override; +/* Which NFS protocol version we are using */ +extern int protocol_version; + -/* Count how many four-byte chunks it takss to hold LEN bytes. */ +/* Count how many four-byte chunks it takes to hold LEN bytes. */ #define INTSIZE(len) (((len)+3)>>2) -/* cred.c */ -int cred_has_uid (struct netcred *, uid_t); -int cred_has_gid (struct netcred *, gid_t); - /* nfs.c */ -int *xdr_encode_fhandle (int *, void *); +int hurd_mode_to_nfs_type (mode_t); +int *xdr_encode_fhandle (int *, struct fhandle *); int *xdr_encode_data (int *, char *, size_t); int *xdr_encode_string (int *, char *); int *xdr_encode_sattr_mode (int *, mode_t); @@ -160,15 +168,18 @@ int *xdr_encode_sattr_ids (int *, u_int, u_int); int *xdr_encode_sattr_size (int *, off_t); int *xdr_encode_sattr_times (int *, struct timespec *, struct timespec *); int *xdr_encode_sattr_stat (int *, struct stat *); -int *xdr_encode_create_state (int *, mode_t); +int *xdr_encode_create_state (int *, mode_t, uid_t); int *xdr_decode_fattr (int *, struct stat *); int *xdr_decode_string (int *, char *); -int *nfs_initialize_rpc (int, struct netcred *, size_t, void **, +int *xdr_decode_fhandle (int *, struct node **); +int *nfs_initialize_rpc (int, struct iouser *, size_t, void **, struct node *, uid_t); error_t nfs_error_trans (int); /* mount.c */ struct node *mount_root (char *, char *); +extern const char *mounted_hostname; +extern uint16_t mounted_nfs_port; /* host order */ /* ops.c */ int *register_fresh_stat (struct node *, int *); @@ -180,5 +191,13 @@ void timeout_service_thread (void); void rpc_receive_thread (void); /* cache.c */ -struct node *lookup_fhandle (void *); -void recache_handle (struct node *, void *); +void lookup_fhandle (void *, size_t, struct node **); +int *recache_handle (int *, struct node *); + +/* name-cache.c */ +void enter_lookup_cache (char *, size_t, struct node *, char *); +void purge_lookup_cache (struct node *, char *, size_t); +struct node *check_lookup_cache (struct node *, char *); +void purge_lookup_cache_node (struct node *); + +#endif /* NFS_NFS_H */ @@ -1,5 +1,5 @@ -/* Libnetfs callbacks for node operations in NFS client - Copyright (C) 1994, 1995, 1996 Free Software Foundation +/* ops.c - Libnetfs callbacks for node operations in NFS client. + Copyright (C) 1994,95,96,97,99,2002,2011 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,17 +21,19 @@ #include <string.h> #include <fcntl.h> #include <stdio.h> +#include <stddef.h> #include <dirent.h> #include <unistd.h> +#include <maptime.h> -/* We have fresh stat information for NP; the fattr structure is at - P. Update our entry. Return the address of the next int after - the fattr structure. */ +/* We have fresh stat information for NP; the file attribute (fattr) + structure is at P. Update our entry. Return the address of the next + int after the fattr structure. */ int * register_fresh_stat (struct node *np, int *p) { int *ret; - + ret = xdr_decode_fattr (p, &np->nn_stat); np->nn->stat_updated = mapped_time->seconds; @@ -40,12 +42,12 @@ register_fresh_stat (struct node *np, int *p) case NOT_POSSIBLE: case POSSIBLE: break; - + case SYMLINK: np->nn_stat.st_size = strlen (np->nn->transarg.name); np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFLNK); break; - + case CHRDEV: np->nn_stat.st_rdev = np->nn->transarg.indexes; np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFCHR); @@ -70,75 +72,147 @@ register_fresh_stat (struct node *np, int *p) np->nn_stat.st_gen = 0; np->nn_stat.st_author = np->nn_stat.st_uid; np->nn_stat.st_flags = 0; - + np->nn_translated = np->nn_stat.st_mode & S_IFMT; + return ret; } +/* Handle returned wcc information for various calls. In protocol + version 2, this is just register_fresh_stat. In version 3, it + checks to see if stat information is present too. If this follows + an operation that we expect has modified the attributes, MOD should + be set. (This unpacks the post_op_attr XDR type.) */ +int * +process_returned_stat (struct node *np, int *p, int mod) +{ + if (protocol_version == 2) + return register_fresh_stat (np, p); + else + { + int attrs_exist; + + attrs_exist = ntohl (*p); + p++; + if (attrs_exist) + p = register_fresh_stat (np, p); + else if (mod) + /* We know that our values are now wrong */ + np->nn->stat_updated = 0; + return p; + } +} + + +/* Handle returned wcc information for various calls. In protocol + version 2, this is just register_fresh_stat. In version 3, it does + the wcc_data interpretation too. If this follows an operation that + we expect has modified the attributes, MOD should be set. + (This unpacks the wcc_data XDR type.) */ +int * +process_wcc_stat (struct node *np, int *p, int mod) +{ + if (protocol_version == 2) + return register_fresh_stat (np, p); + else + { + int attrs_exist; + + /* First the pre_op_attr */ + attrs_exist = ntohl (*p); + p++; + if (attrs_exist) + { + /* Just skip them for now */ + p += 2 * sizeof (int); /* size */ + p += 2 * sizeof (int); /* mtime */ + p += 2 * sizeof (int); /* atime */ + } + + /* Now the post_op_attr */ + return process_returned_stat (np, p, mod); + } +} + + /* Implement the netfs_validate_stat callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_validate_stat (struct node *np, struct netcred *cred) +netfs_validate_stat (struct node *np, struct iouser *cred) { int *p; void *rpcbuf; error_t err; - + if (mapped_time->seconds - np->nn->stat_updated < stat_timeout) return 0; - p = nfs_initialize_rpc (NFSPROC_GETATTR, (struct netcred *) -1, - 0, &rpcbuf, np, -1); + p = nfs_initialize_rpc (NFSPROC_GETATTR (protocol_version), + (struct iouser *) -1, 0, &rpcbuf, np, -1); + if (! p) + return errno; + p = xdr_encode_fhandle (p, &np->nn->handle); - + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); + { + err = nfs_error_trans (ntohl (*p)); + p++; + } if (!err) register_fresh_stat (np, p); - np->istranslated = 0; - free (rpcbuf); return err; } /* Implement the netfs_attempt_chown callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_chown (struct netcred *cred, struct node *np, +netfs_attempt_chown (struct iouser *cred, struct node *np, uid_t uid, gid_t gid) { int *p; void *rpcbuf; error_t err; - - p = nfs_initialize_rpc (NFSPROC_SETATTR, cred, 0, &rpcbuf, np, gid); + + p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version), + cred, 0, &rpcbuf, np, gid); + if (! p) + return errno; + p = xdr_encode_fhandle (p, &np->nn->handle); p = xdr_encode_sattr_ids (p, uid, gid); - - err = conduct_rpc (&rpcbuf, &p); + if (protocol_version == 3) + *(p++) = 0; /* guard_check == 0 */ + err = conduct_rpc (&rpcbuf, &p); if (!err) - register_fresh_stat (np, p); + { + err = nfs_error_trans (ntohl (*p)); + p++; + if (!err || protocol_version == 3) + p = process_wcc_stat (np, p, !err); + } free (rpcbuf); - + return err; } /* Implement the netfs_attempt_chauthor callback as described in <hurd/netfs.h>. */ error_t -netfs_attempt_chauthor (struct netcred *cred, struct node *rp, +netfs_attempt_chauthor (struct iouser *cred, struct node *rp, uid_t author) { return EOPNOTSUPP; } /* Implement the netfs_attempt_chmod callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_chmod (struct netcred *cred, struct node *np, +netfs_attempt_chmod (struct iouser *cred, struct node *np, mode_t mode) { int *p; @@ -150,26 +224,29 @@ netfs_attempt_chmod (struct netcred *cred, struct node *np, err = netfs_validate_stat (np, cred); if (err) return err; + + /* Has the file type changed? (e.g. from symlink to + directory). */ if ((mode & S_IFMT) != (np->nn_stat.st_mode & S_IFMT)) { char *f = 0; - + if (np->nn->dtrans == NOT_POSSIBLE) return EOPNOTSUPP; - + if (np->nn->dtrans == SYMLINK) f = np->nn->transarg.name; - + switch (mode & S_IFMT) { default: return EOPNOTSUPP; - + case S_IFIFO: np->nn->dtrans = FIFO; np->nn->stat_updated = 0; break; - + case S_IFSOCK: np->nn->dtrans = SOCK; np->nn->stat_updated = 0; @@ -180,25 +257,33 @@ netfs_attempt_chmod (struct netcred *cred, struct node *np, } } - p = nfs_initialize_rpc (NFSPROC_SETATTR, cred, 0, &rpcbuf, np, -1); + p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version), + cred, 0, &rpcbuf, np, -1); + if (! p) + return errno; + p = xdr_encode_fhandle (p, &np->nn->handle); p = xdr_encode_sattr_mode (p, mode); - + if (protocol_version == 3) + *(p++) = 0; /* guard check == 0 */ + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - - if (!err) - register_fresh_stat (np, p); + { + err = nfs_error_trans (ntohl (*p)); + p++; + if (!err || protocol_version == 3) + p = process_wcc_stat (np, p, !err); + } free (rpcbuf); return err; } /* Implement the netfs_attempt_chflags callback as described in - <hurd/netfs.h>. */ -error_t -netfs_attempt_chflags (struct netcred *cred, struct node *np, + <hurd/netfs.h>. */ +error_t +netfs_attempt_chflags (struct iouser *cred, struct node *np, int flags) { return EOPNOTSUPP; @@ -207,48 +292,91 @@ netfs_attempt_chflags (struct netcred *cred, struct node *np, /* Implement the netfs_attempt_utimes callback as described in <hurd/netfs.h>. */ error_t -netfs_attempt_utimes (struct netcred *cred, struct node *np, +netfs_attempt_utimes (struct iouser *cred, struct node *np, struct timespec *atime, struct timespec *mtime) { int *p; void *rpcbuf; error_t err; - - p = nfs_initialize_rpc (NFSPROC_SETATTR, cred, 0, &rpcbuf, np, -1); + struct timeval tv; + struct timespec current; + + /* XXX For version 3 we can actually do this right, but we don't + just yet. */ + if (!atime || !mtime) + { + maptime_read (mapped_time, &tv); + current.tv_sec = tv.tv_sec; + current.tv_nsec = tv.tv_usec * 1000; + } + + p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version), + cred, 0, &rpcbuf, np, -1); + if (! p) + return errno; + p = xdr_encode_fhandle (p, &np->nn->handle); - p = xdr_encode_sattr_times (p, atime, mtime); - + p = xdr_encode_sattr_times (p, + atime ?: ¤t, + mtime ?: ¤t); + if (protocol_version == 3) + *(p++) = 0; /* guard check == 0 */ + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - - if (!err) - register_fresh_stat (np, p); + { + err = nfs_error_trans (ntohl (*p)); + p++; + if (!err || protocol_version == 3) + p = process_wcc_stat (np, p, !err); + } free (rpcbuf); return err; } /* Implement the netfs_attempt_set_size callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_set_size (struct netcred *cred, struct node *np, +netfs_attempt_set_size (struct iouser *cred, struct node *np, off_t size) { int *p; void *rpcbuf; error_t err; - - p = nfs_initialize_rpc (NFSPROC_SETATTR, cred, 0, &rpcbuf, np, -1); + + p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version), + cred, 0, &rpcbuf, np, -1); + if (! p) + return errno; + p = xdr_encode_fhandle (p, &np->nn->handle); p = xdr_encode_sattr_size (p, size); - + if (protocol_version == 3) + *(p++) = 0; /* guard_check == 0 */ + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - - if (!err) - register_fresh_stat (np, p); + { + err = nfs_error_trans (ntohl (*p)); + p++; + if (!err || protocol_version == 3) + p = process_wcc_stat (np, p, !err); + } + + /* If we got EACCES, but the user has the file open for writing, + then the NFS protocol has screwed us. There's nothing we can do, + except in the important case of opens with + O_TRUNC|O_CREAT|O_WRONLY|O_EXCL where the new mode does not allow + writing. RCS, for example, uses this to create lock files. So permit + cases where the O_TRUNC isn't doing anything to succeed if the user + does have the file open for writing. */ + if (err == EACCES) + { + int error = netfs_validate_stat (np, cred); + if (!error && np->nn_stat.st_size == size) + err = 0; + } free (rpcbuf); return err; @@ -257,45 +385,52 @@ netfs_attempt_set_size (struct netcred *cred, struct node *np, /* Implement the netfs_attempt_statfs callback as described in <hurd/netfs.h>. */ error_t -netfs_attempt_statfs (struct netcred *cred, struct node *np, +netfs_attempt_statfs (struct iouser *cred, struct node *np, struct statfs *st) { int *p; void *rpcbuf; error_t err; - - p = nfs_initialize_rpc (NFSPROC_STATFS, cred, 0, &rpcbuf, np, -1); + + p = nfs_initialize_rpc (NFS2PROC_STATFS, cred, 0, &rpcbuf, np, -1); + if (! p) + return errno; + p = xdr_encode_fhandle (p, &np->nn->handle); - + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - + { + err = nfs_error_trans (ntohl (*p)); + p++; + } + if (!err) { - long iosize, bsize; - iosize = *p++; - bsize = *p++; - st->f_bsize = iosize; - st->f_blocks = ((*p++) * bsize) / iosize; - st->f_bfree = ((*p++) * bsize) / iosize; - st->f_bavail = ((*p++) * bsize) / iosize; - + p++; /* skip IOSIZE field */ + st->f_bsize = ntohl (*p); + p++; + st->f_blocks = ntohl (*p); + p++; + st->f_bfree = ntohl (*p); + p++; + st->f_bavail = ntohl (*p); + p++; st->f_type = FSTYPE_NFS; st->f_files = 0; st->f_ffree = 0; - st->f_fsid = 0; /* XXX wrong */ + st->f_fsid = getpid (); st->f_namelen = 0; } - + free (rpcbuf); return err; } /* Implement the netfs_attempt_sync callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_sync (struct netcred *cred, struct node *np, int wait) +netfs_attempt_sync (struct iouser *cred, struct node *np, int wait) { /* We are already completely synchronous. */ return 0; @@ -304,15 +439,15 @@ netfs_attempt_sync (struct netcred *cred, struct node *np, int wait) /* Implement the netfs_attempt_syncfs callback as described in <hurd/netfs.h>. */ error_t -netfs_attempt_syncfs (struct netcred *cred, int wait) +netfs_attempt_syncfs (struct iouser *cred, int wait) { return 0; } /* Implement the netfs_attempt_read callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_read (struct netcred *cred, struct node *np, +netfs_attempt_read (struct iouser *cred, struct node *np, off_t offset, size_t *len, void *data) { int *p; @@ -320,191 +455,370 @@ netfs_attempt_read (struct netcred *cred, struct node *np, size_t trans_len; error_t err; size_t amt, thisamt; - + int eof; + for (amt = *len; amt;) { thisamt = amt; if (thisamt > read_size) thisamt = read_size; - p = nfs_initialize_rpc (NFSPROC_READ, cred, 0, &rpcbuf, np, -1); + p = nfs_initialize_rpc (NFSPROC_READ (protocol_version), + cred, 0, &rpcbuf, np, -1); + if (! p) + return errno; + p = xdr_encode_fhandle (p, &np->nn->handle); - *p++ = htonl (offset); - *p++ = htonl (thisamt); - *p++ = 0; - + *(p++) = htonl (offset); + *(p++) = htonl (thisamt); + if (protocol_version == 2) + *(p++) = 0; + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - - if (err) { + err = nfs_error_trans (ntohl (*p)); + p++; + + if (!err || protocol_version == 3) + p = process_returned_stat (np, p, !err); + + if (err) + { + free (rpcbuf); + return err; + } + + trans_len = ntohl (*p); + p++; + if (trans_len > thisamt) + trans_len = thisamt; /* ??? */ + + if (protocol_version == 3) + { + eof = ntohl (*p); + p++; + } + else + eof = (trans_len < thisamt); + + memcpy (data, p, trans_len); free (rpcbuf); - return err; - } - - p = register_fresh_stat (np, p); - - trans_len = ntohl (*p++); - if (trans_len > thisamt) - trans_len = thisamt; /* ??? */ - - bcopy (p, data, trans_len); - free (rpcbuf); - data += trans_len; - offset += trans_len; - amt -= trans_len; - - /* If we got a short count, that means we're all done */ - if (trans_len < thisamt) - { - *len -= amt; - return 0; + data += trans_len; + offset += trans_len; + amt -= trans_len; + + if (eof) + { + *len -= amt; + return 0; + } } } return 0; } - + /* Implement the netfs_attempt_write callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_write (struct netcred *cred, struct node *np, +netfs_attempt_write (struct iouser *cred, struct node *np, off_t offset, size_t *len, void *data) { int *p; void *rpcbuf; error_t err; size_t amt, thisamt; - + size_t count; + for (amt = *len; amt;) { thisamt = amt; if (thisamt > write_size) thisamt = write_size; - - p = nfs_initialize_rpc (NFSPROC_WRITE, cred, thisamt, &rpcbuf, np, -1); + + p = nfs_initialize_rpc (NFSPROC_WRITE (protocol_version), + cred, thisamt, &rpcbuf, np, -1); + if (! p) + return errno; + p = xdr_encode_fhandle (p, &np->nn->handle); - *p++ = 0; - *p++ = htonl (offset); - *p++ = 0; + if (protocol_version == 2) + *(p++) = 0; + *(p++) = htonl (offset); + if (protocol_version == 2) + *(p++) = 0; + if (protocol_version == 3) + *(p++) = htonl (FILE_SYNC); p = xdr_encode_data (p, data, thisamt); - + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - + { + err = nfs_error_trans (ntohl (*p)); + p++; + if (!err || protocol_version == 3) + p = process_wcc_stat (np, p, !err); + if (!err) + { + if (protocol_version == 3) + { + count = ntohl (*p); + p++; + p++; /* ignore COMMITTED */ + /* ignore verf for now */ + p += NFS3_WRITEVERFSIZE / sizeof (int); + } + else + /* assume it wrote the whole thing */ + count = thisamt; + + amt -= count; + data += count; + offset += count; + } + } + + free (rpcbuf); + + if (err == EINTR && amt != *len) + { + *len -= amt; + return 0; + } + if (err) { *len = 0; - free (rpcbuf); return err; } - register_fresh_stat (np, p); - free (rpcbuf); - amt -= thisamt; - data += thisamt; - offset += thisamt; } return 0; } +/* See if NAME exists in DIR for CRED. If so, return EEXIST. */ +error_t +verify_nonexistent (struct iouser *cred, struct node *dir, + char *name) +{ + int *p; + void *rpcbuf; + error_t err; + + /* Don't use the lookup cache for this; we want a full sync to + get as close to real exclusive create behavior as possible. */ + + assert (protocol_version == 2); + + p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version), + cred, 0, &rpcbuf, dir, -1); + if (! p) + return errno; + + p = xdr_encode_fhandle (p, &dir->nn->handle); + p = xdr_encode_string (p, name); + + err = conduct_rpc (&rpcbuf, &p); + if (!err) + { + err = nfs_error_trans (ntohl (*p)); + p++; + } + + free (rpcbuf); + + if (!err) + return EEXIST; + else + return 0; +} + /* Implement the netfs_attempt_lookup callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_lookup (struct netcred *cred, struct node *np, +netfs_attempt_lookup (struct iouser *cred, struct node *np, char *name, struct node **newnp) { int *p; void *rpcbuf; error_t err; - - p = nfs_initialize_rpc (NFSPROC_LOOKUP, cred, 0, &rpcbuf, np, -1); + char dirhandle[NFS3_FHSIZE]; + size_t dirlen; + + /* Check the cache first. */ + *newnp = check_lookup_cache (np, name); + if (*newnp) + { + if (*newnp == (struct node *) -1) + { + *newnp = 0; + return ENOENT; + } + else + return 0; + } + + p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version), + cred, 0, &rpcbuf, np, -1); + if (! p) + return errno; + p = xdr_encode_fhandle (p, &np->nn->handle); p = xdr_encode_string (p, name); - + + /* Remember the directory handle for later cache use. */ + + dirlen = np->nn->handle.size; + memcpy (dirhandle, np->nn->handle.data, dirlen); + mutex_unlock (&np->lock); err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - - if (!err) { - *newnp = lookup_fhandle (p); - p += NFS_FHSIZE / sizeof (int); - register_fresh_stat (*newnp, p); + err = nfs_error_trans (ntohl (*p)); + p++; + if (!err) + { + p = xdr_decode_fhandle (p, newnp); + p = process_returned_stat (*newnp, p, 1); + } + if (err) + *newnp = 0; + if (protocol_version == 3) + { + if (*newnp) + mutex_unlock (&(*newnp)->lock); + mutex_lock (&np->lock); + p = process_returned_stat (np, p, 0); /* XXX Do we have to lock np? */ + mutex_unlock (&np->lock); + if (*newnp) + mutex_lock (&(*newnp)->lock); + } } else *newnp = 0; - + + /* Notify the cache of the hit or miss. */ + enter_lookup_cache (dirhandle, dirlen, *newnp, name); + free (rpcbuf); - + return err; } /* Implement the netfs_attempt_mkdir callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_mkdir (struct netcred *cred, struct node *np, +netfs_attempt_mkdir (struct iouser *cred, struct node *np, char *name, mode_t mode) { int *p; void *rpcbuf; error_t err; - - p = nfs_initialize_rpc (NFSPROC_MKDIR, cred, 0, &rpcbuf, np, -1); + uid_t owner; + struct node *newnp; + + if (cred->uids->num) + owner = cred->uids->ids[0]; + else + { + err = netfs_validate_stat (np, cred); + owner = err ? 0 : np->nn_stat.st_uid; + mode &= ~S_ISUID; + } + + purge_lookup_cache (np, name, strlen (name)); + + p = nfs_initialize_rpc (NFSPROC_MKDIR (protocol_version), + cred, 0, &rpcbuf, np, -1); + if (! p) + return errno; + p = xdr_encode_fhandle (p, &np->nn->handle); p = xdr_encode_string (p, name); - p = xdr_encode_create_state (p, mode); - + p = xdr_encode_create_state (p, mode, owner); + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - - /* Ignore returned information */ - /* XXX should probably cache it */ + { + err = nfs_error_trans (ntohl (*p)); + p++; + } + + if (!err) + { + p = xdr_decode_fhandle (p, &newnp); + p = process_returned_stat (newnp, p, 1); + + /* Did we set the owner correctly? If not, try, but ignore failures. */ + if (!netfs_validate_stat (newnp, (struct iouser *) -1) + && newnp->nn_stat.st_uid != owner) + netfs_attempt_chown ((struct iouser *) -1, newnp, owner, + newnp->nn_stat.st_gid); + + /* We don't actually return this. */ + netfs_nput (newnp); + } free (rpcbuf); return err; } /* Implement the netfs_attempt_rmdir callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_rmdir (struct netcred *cred, struct node *np, +netfs_attempt_rmdir (struct iouser *cred, struct node *np, char *name) { int *p; void *rpcbuf; error_t err; - + /* Should we do the same sort of thing here as with attempt_unlink? */ - p = nfs_initialize_rpc (NFSPROC_RMDIR, cred, 0, &rpcbuf, np, -1); + purge_lookup_cache (np, name, strlen (name)); + + p = nfs_initialize_rpc (NFSPROC_RMDIR (protocol_version), + cred, 0, &rpcbuf, np, -1); + if (! p) + return errno; + p = xdr_encode_fhandle (p, &np->nn->handle); p = xdr_encode_string (p, name); - + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - + { + err = nfs_error_trans (ntohl (*p)); + p++; + if (protocol_version == 3) + p = process_wcc_stat (np, p, !err); + } + free (rpcbuf); return err; } /* Implement the netfs_attempt_link callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_link (struct netcred *cred, struct node *dir, +netfs_attempt_link (struct iouser *cred, struct node *dir, struct node *np, char *name, int excl) { int *p; void *rpcbuf; error_t err = 0; - + if (!excl) - return EOPNOTSUPP; /* XXX */ + { + /* We have no RPC available that will do an atomic replacement, + so we settle for second best; just doing an unlink and ignoring + any errors. */ + mutex_lock (&dir->lock); + netfs_attempt_unlink (cred, dir, name); + mutex_unlock (&dir->lock); + } /* If we have postponed a translator setting on an unlinked node, then here's where we set it, by creating the new node instead of @@ -515,32 +829,51 @@ netfs_attempt_link (struct netcred *cred, struct node *dir, case POSSIBLE: case NOT_POSSIBLE: mutex_lock (&dir->lock); - p = nfs_initialize_rpc (NFSPROC_LINK, cred, 0, &rpcbuf, dir, -1); + p = nfs_initialize_rpc (NFSPROC_LINK (protocol_version), + cred, 0, &rpcbuf, dir, -1); + if (! p) + { + mutex_unlock (&dir->lock); + return errno; + } + mutex_unlock (&dir->lock); - + mutex_lock (&np->lock); p = xdr_encode_fhandle (p, &np->nn->handle); mutex_unlock (&np->lock); - + mutex_lock (&dir->lock); + purge_lookup_cache (dir, name, strlen (name)); + p = xdr_encode_fhandle (p, &dir->nn->handle); p = xdr_encode_string (p, name); - + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); + { + err = nfs_error_trans (ntohl (*p)); + p++; + } mutex_unlock (&dir->lock); - + free (rpcbuf); break; case SYMLINK: mutex_lock (&dir->lock); - p = nfs_initialize_rpc (NFSPROC_SYMLINK, cred, 0, &rpcbuf, dir, -1); + p = nfs_initialize_rpc (NFSPROC_SYMLINK (protocol_version), + cred, 0, &rpcbuf, dir, -1); + if (! p) + { + mutex_unlock (&dir->lock); + return errno; + } + p = xdr_encode_fhandle (p, &dir->nn->handle); mutex_unlock (&dir->lock); - + p = xdr_encode_string (p, name); mutex_lock (&np->lock); @@ -551,85 +884,198 @@ netfs_attempt_link (struct netcred *cred, struct node *dir, free (rpcbuf); return err; } - - p = xdr_encode_string (p, np->nn->transarg.name); - p = xdr_encode_sattr_stat (p, &np->nn_stat); + + if (protocol_version == 2) + { + p = xdr_encode_string (p, np->nn->transarg.name); + p = xdr_encode_sattr_stat (p, &np->nn_stat); + } + else + { + p = xdr_encode_sattr_stat (p, &np->nn_stat); + p = xdr_encode_string (p, np->nn->transarg.name); + } mutex_unlock (&np->lock); mutex_lock (&dir->lock); + + purge_lookup_cache (dir, name, strlen (name)); err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - - if (!err) { - /* NFSPROC_SYMLINK stupidly does not pass back an - fhandle, so we have to fetch one now. */ - p = nfs_initialize_rpc (NFSPROC_LOOKUP, cred, 0, &rpcbuf, dir, -1); - p = xdr_encode_fhandle (p, &dir->nn->handle); - p = xdr_encode_string (p, name); - - err = conduct_rpc (&rpcbuf, &p); - if (!err) - err = nfs_error_trans (ntohl (*p++)); - - mutex_unlock (&dir->lock); + err = nfs_error_trans (ntohl (*p)); + p++; - if (err) - err = EGRATUITOUS; /* damn */ - else + if (protocol_version == 2 && !err) { - mutex_lock (&np->lock); - recache_handle (np, p); - p += NFS_FHSIZE / sizeof (int); - register_fresh_stat (np, p); - mutex_unlock (&np->lock); + free (rpcbuf); + + /* NFSPROC_SYMLINK stupidly does not pass back an + fhandle, so we have to fetch one now. */ + p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version), + cred, 0, &rpcbuf, dir, -1); + if (! p) + { + mutex_unlock (&dir->lock); + return errno; + } + p = xdr_encode_fhandle (p, &dir->nn->handle); + p = xdr_encode_string (p, name); + + mutex_unlock (&dir->lock); + + err = conduct_rpc (&rpcbuf, &p); + if (!err) + { + err = nfs_error_trans (ntohl (*p)); + p++; + } + if (!err) + { + mutex_lock (&np->lock); + p = recache_handle (p, np); + p = process_returned_stat (np, p, 1); + mutex_unlock (&np->lock); + } + if (err) + err = EGRATUITOUS; /* damn */ + } + else if (protocol_version == 3) + { + if (!err) + { + mutex_unlock (&dir->lock); + mutex_lock (&np->lock); + p = recache_handle (p, np); + p = process_returned_stat (np, p, 1); + mutex_unlock (&np->lock); + mutex_lock (&dir->lock); + } + p = process_wcc_stat (dir, p, !err); + mutex_unlock (&dir->lock); } + else + mutex_unlock (&dir->lock); } else mutex_unlock (&dir->lock); + + free (rpcbuf); break; - + case CHRDEV: case BLKDEV: case FIFO: case SOCK: - mutex_lock (&dir->lock); - p = nfs_initialize_rpc (NFSPROC_CREATE, cred, 0, &rpcbuf, dir, -1); - p = xdr_encode_fhandle (p, &dir->nn->handle); - p = xdr_encode_string (p, name); - mutex_unlock (&dir->lock); - mutex_lock (&np->lock); - err = netfs_validate_stat (np, cred); - if (err) + if (protocol_version == 2) { + mutex_lock (&dir->lock); + err = verify_nonexistent (cred, dir, name); + if (err) + return err; + + p = nfs_initialize_rpc (NFSPROC_CREATE (protocol_version), + cred, 0, &rpcbuf, dir, -1); + if (! p) + { + mutex_unlock (&dir->lock); + return errno; + } + + p = xdr_encode_fhandle (p, &dir->nn->handle); + p = xdr_encode_string (p, name); + mutex_unlock (&dir->lock); + + mutex_lock (&np->lock); + err = netfs_validate_stat (np, cred); + if (err) + { + mutex_unlock (&np->lock); + free (rpcbuf); + return err; + } + + p = xdr_encode_sattr_stat (p, &np->nn_stat); mutex_unlock (&np->lock); + + mutex_lock (&dir->lock); + purge_lookup_cache (dir, name, strlen (name)); + mutex_unlock (&dir->lock); /* XXX Should this really be after the + _lengthy_ (blocking) conduct_rpc? */ + err = conduct_rpc (&rpcbuf, &p); + if (!err) + { + err = nfs_error_trans (ntohl (*p)); + p++; + } + + if (!err) + { + mutex_lock (&np->lock); + p = recache_handle (p, np); + register_fresh_stat (np, p); + mutex_unlock (&np->lock); + } + free (rpcbuf); - return err; } - - p = xdr_encode_sattr_stat (p, &np->nn_stat); - mutex_unlock (&np->lock); + else /* protocol_version != 2 */ + { + mutex_lock (&dir->lock); + p = nfs_initialize_rpc (NFS3PROC_MKNOD, cred, 0, &rpcbuf, dir, -1); + if (! p) + { + mutex_unlock (&dir->lock); + return errno; + } + p = xdr_encode_fhandle (p, &dir->nn->handle); + p = xdr_encode_string (p, name); + mutex_unlock (&dir->lock); - mutex_lock (&dir->lock); - err = conduct_rpc (&rpcbuf, &p); - if (!err) - err = nfs_error_trans (ntohl (*p++)); - mutex_unlock (&dir->lock); - - mutex_lock (&np->lock); - recache_handle (np, p); - p += NFS_FHSIZE / sizeof (int); - register_fresh_stat (np, p); - mutex_unlock (&np->lock); + mutex_lock (&np->lock); + err = netfs_validate_stat (np, cred); + if (err) + { + mutex_unlock (&np->lock); + free (rpcbuf); + return err; + } + *(p++) = htonl (hurd_mode_to_nfs_type (np->nn_stat.st_mode)); + p = xdr_encode_sattr_stat (p, &np->nn_stat); + if (np->nn->dtrans == BLKDEV || np->nn->dtrans == CHRDEV) + { + *(p++) = htonl (major (np->nn_stat.st_rdev)); + *(p++) = htonl (minor (np->nn_stat.st_rdev)); + } + mutex_unlock (&np->lock); + purge_lookup_cache (dir, name, strlen (name)); + err = conduct_rpc (&rpcbuf, &p); + if (!err) + { + err = nfs_error_trans (ntohl (*p)); + p++; + + if (!err) + { + mutex_lock (&np->lock); + p = recache_handle (p, np); + p = process_returned_stat (np, p, 1); + mutex_unlock (&np->lock); + } + mutex_lock (&dir->lock); + p = process_wcc_stat (dir, p, !err); + mutex_unlock (&dir->lock); + } + free (rpcbuf); + } break; } if (err) return err; - + mutex_lock (&np->lock); if (np->nn->dtrans == SYMLINK) @@ -644,21 +1090,21 @@ netfs_attempt_link (struct netcred *cred, struct node *dir, np->nn->dead_dir = 0; np->nn->dead_name = 0; mutex_unlock (&np->lock); - + mutex_lock (&dir->lock); - netfs_attempt_unlink ((struct netcred *)-1, dir, name); + netfs_attempt_unlink ((struct iouser *)-1, dir, name); mutex_unlock (&dir->lock); } else mutex_unlock (&np->lock); - + return 0; } /* Implement the netfs_attempt_mkfile callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_mkfile (struct netcred *cred, struct node *dir, +netfs_attempt_mkfile (struct iouser *cred, struct node *dir, mode_t mode, struct node **newnp) { error_t err; @@ -668,16 +1114,20 @@ netfs_attempt_mkfile (struct netcred *cred, struct node *dir, /* This is the best we can do. */ name = malloc (50); + if (! name) + return ENOMEM; do { sprintf (name, ".nfstmpgnu.%d", n++); err = netfs_attempt_create_file (cred, dir, name, mode, newnp); if (err == EEXIST) - mutex_lock (&dir->lock); + mutex_lock (&dir->lock); /* XXX is this right? does create need this + and drop this on error? Doesn't look + like it. */ } while (err == EEXIST); - + if (err) { free (name); @@ -695,43 +1145,101 @@ netfs_attempt_mkfile (struct netcred *cred, struct node *dir, } /* Implement the netfs_attempt_create_file callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_create_file (struct netcred *cred, struct node *np, +netfs_attempt_create_file (struct iouser *cred, struct node *np, char *name, mode_t mode, struct node **newnp) { int *p; void *rpcbuf; error_t err; + uid_t owner; + + if (cred->uids->num) + owner = cred->uids->ids[0]; + else + { + err = netfs_validate_stat (np, cred); + owner = err ? 0 : np->nn_stat.st_uid; + mode &= ~S_ISUID; + } + + /* RFC 1094 says that create is always exclusive. But Sun doesn't + actually *implement* the spec. No, of course not. So we have to do + it for them. */ + if (protocol_version == 2) + { + err = verify_nonexistent (cred, np, name); + if (err) + { + mutex_unlock (&np->lock); + return err; + } + } + + purge_lookup_cache (np, name, strlen (name)); + + p = nfs_initialize_rpc (NFSPROC_CREATE (protocol_version), + cred, 0, &rpcbuf, np, -1); + if (! p) + return errno; - p = nfs_initialize_rpc (NFSPROC_CREATE, cred, 0, &rpcbuf, np, -1); p = xdr_encode_fhandle (p, &np->nn->handle); p = xdr_encode_string (p, name); - p = xdr_encode_create_state (p, mode); - + if (protocol_version == 3) + { + /* We happen to know this is where the XID is. */ + int verf = *(int *)rpcbuf; + + *(p++) = ntohl (EXCLUSIVE); + /* 8 byte verf */ + *(p++) = ntohl (verf); + p++; + } + else + p = xdr_encode_create_state (p, mode, owner); + err = conduct_rpc (&rpcbuf, &p); - if (!err) - err = nfs_error_trans (ntohl (*p++)); mutex_unlock (&np->lock); if (!err) { - *newnp = lookup_fhandle (p); - p += NFS_FHSIZE / sizeof (int); - register_fresh_stat (*newnp, p); + err = nfs_error_trans (ntohl (*p)); + p++; + if (!err) + { + p = xdr_decode_fhandle (p, newnp); + p = process_returned_stat (*newnp, p, 1); + } + if (err) + *newnp = 0; + if (protocol_version == 3) + { + if (*newnp) + mutex_unlock (&(*newnp)->lock); + mutex_lock (&np->lock); + p = process_wcc_stat (np, p, 1); + mutex_unlock (&np->lock); + if (*newnp) + mutex_lock (&(*newnp)->lock); + } + + if (*newnp && !netfs_validate_stat (*newnp, (struct iouser *) -1) + && (*newnp)->nn_stat.st_uid != owner) + netfs_attempt_chown ((struct iouser *) -1, *newnp, owner, (*newnp)->nn_stat.st_gid); } else *newnp = 0; - + free (rpcbuf); return err; } /* Implement the netfs_attempt_unlink callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_unlink (struct netcred *cred, struct node *dir, +netfs_attempt_unlink (struct iouser *cred, struct node *dir, char *name) { int *p; @@ -747,20 +1255,36 @@ netfs_attempt_unlink (struct netcred *cred, struct node *dir, return err; } - /* See if there are any other users of this node than the + /* Restore the locks to sanity. */ + mutex_unlock (&np->lock); + mutex_lock (&dir->lock); + + /* Purge the cache of entries for this node, so that we don't + regard cache-held references as live. */ + purge_lookup_cache_node (np); + + /* See if there are any other users of this node than the one we just got; if so, we must give this file another link so that when we delete the one we are asked for it doesn't go away entirely. */ if (np->references > 1) { - char *newname; + char *newname = 0; int n = 0; + mutex_unlock (&dir->lock); + newname = malloc (50); - mutex_unlock (&np->lock); + if (! newname) + { + mutex_lock (&dir->lock); + netfs_nrele (np); /* XXX Is this the correct thing to do? */ + return ENOMEM; + } + do { - sprintf (newname, ".nfs%xgnu.%d", (int) np, n++); + sprintf (newname, ".nfs%txgnu.%d", (ptrdiff_t) np, n++); err = netfs_attempt_link (cred, dir, np, newname, 1); } while (err == EEXIST); @@ -776,6 +1300,7 @@ netfs_attempt_unlink (struct netcred *cred, struct node *dir, /* Write down what name we gave it; we'll delete this when all our uses vanish. */ mutex_lock (&np->lock); + if (np->nn->dead_dir) netfs_nrele (np->nn->dead_dir); netfs_nref (dir); @@ -785,60 +1310,120 @@ netfs_attempt_unlink (struct netcred *cred, struct node *dir, np->nn->dead_name = newname; if (np->nn->dtrans == NOT_POSSIBLE) np->nn->dtrans = POSSIBLE; + + netfs_nput (np); + mutex_lock (&dir->lock); } - netfs_nput (np); + else + netfs_nrele (np); + + p = nfs_initialize_rpc (NFSPROC_REMOVE (protocol_version), + cred, 0, &rpcbuf, dir, -1); + if (! p) + return errno; - mutex_lock (&dir->lock); - p = nfs_initialize_rpc (NFSPROC_REMOVE, cred, 0, &rpcbuf, dir, -1); p = xdr_encode_fhandle (p, &dir->nn->handle); p = xdr_encode_string (p, name); - + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - + { + err = nfs_error_trans (ntohl (*p)); + p++; + if (protocol_version == 3) + p = process_wcc_stat (dir, p, !err); + } + free (rpcbuf); return err; } /* Implement the netfs_attempt_rename callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_rename (struct netcred *cred, struct node *fromdir, - char *fromname, struct node *todir, char *toname, +netfs_attempt_rename (struct iouser *cred, struct node *fromdir, + char *fromname, struct node *todir, char *toname, int excl) { int *p; void *rpcbuf; error_t err; - + if (excl) - return EOPNOTSUPP; /* XXX */ + { + struct node *np; + + /* Just do a lookup/link/unlink sequence. */ + + mutex_lock (&fromdir->lock); + err = netfs_attempt_lookup (cred, fromdir, fromname, &np); + mutex_unlock (&fromdir->lock); + if (err) + return err; + + err = netfs_attempt_link (cred, todir, np, toname, 1); + netfs_nput (np); + if (err) + return err; + + mutex_lock (&fromdir->lock); + err = netfs_attempt_unlink (cred, fromdir, fromname); + mutex_unlock (&fromdir->lock); + + /* If the unlink failed, then back out the link */ + if (err) + { + mutex_lock (&todir->lock); + netfs_attempt_unlink (cred, todir, toname); + mutex_unlock (&todir->lock); + return err; + } + + return 0; + } mutex_lock (&fromdir->lock); - p = nfs_initialize_rpc (NFSPROC_RENAME, cred, 0, &rpcbuf, fromdir, -1); + purge_lookup_cache (fromdir, fromname, strlen (fromname)); + p = nfs_initialize_rpc (NFSPROC_RENAME (protocol_version), + cred, 0, &rpcbuf, fromdir, -1); + if (! p) + { + mutex_unlock (&fromdir->lock); + return errno; + } + p = xdr_encode_fhandle (p, &fromdir->nn->handle); p = xdr_encode_string (p, fromname); mutex_unlock (&fromdir->lock); - + mutex_lock (&todir->lock); + purge_lookup_cache (todir, toname, strlen (toname)); p = xdr_encode_fhandle (p, &todir->nn->handle); p = xdr_encode_string (p, toname); mutex_unlock (&todir->lock); - + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - + { + err = nfs_error_trans (ntohl (*p)); + p++; + if (protocol_version == 3) /* XXX Should we add `&& !err' ? */ + { + mutex_lock (&fromdir->lock); + p = process_wcc_stat (fromdir, p, !err); + p = process_wcc_stat (todir, p, !err); + } + } + free (rpcbuf); return err; } /* Implement the netfs_attempt_readlink callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_readlink (struct netcred *cred, struct node *np, +netfs_attempt_readlink (struct iouser *cred, struct node *np, char *buf) { int *p; @@ -850,105 +1435,132 @@ netfs_attempt_readlink (struct netcred *cred, struct node *np, strcpy (buf, np->nn->transarg.name); return 0; } - - p = nfs_initialize_rpc (NFSPROC_READLINK, cred, 0, &rpcbuf, np, -1); + + p = nfs_initialize_rpc (NFSPROC_READLINK (protocol_version), + cred, 0, &rpcbuf, np, -1); + if (! p) + return errno; + p = xdr_encode_fhandle (p, &np->nn->handle); - + err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); - - if (!err) - p = xdr_decode_string (p, buf); + { + err = nfs_error_trans (ntohl (*p)); + p++; + if (protocol_version == 3) + p = process_returned_stat (np, p, 0); + if (!err) + p = xdr_decode_string (p, buf); + } free (rpcbuf); return err; } -/* For an NFS node NODE, guess whether CRED is able to read or write - it by hoping the server uses permissions bits in the "expected - way". Return the or of O_READ, O_WRITE, and O_EXEC accordingly as - each is possible. */ -static int -guess_mode_use (struct node *np, - struct netcred *cred) -{ - error_t err; - - err = netfs_validate_stat (np, cred); - if (err) - return err; - - if (cred_has_uid (cred, 0)) - return O_READ|O_WRITE|O_EXEC; - else if (cred->nuids == 0 - && (np->nn_stat.st_mode & S_IUSEUNK)) - return - (((np->nn_stat.st_mode & 04000000) ? O_READ : 0) - | ((np->nn_stat.st_mode & 02000000) ? O_WRITE : 0) - | ((np->nn_stat.st_mode & 01000000) ? O_EXEC : 0)); - else if (cred_has_uid (cred, np->nn_stat.st_uid) - || (cred_has_gid (cred, np->nn_stat.st_gid) - && cred_has_uid (cred, np->nn_stat.st_gid))) - /* Owner */ - return - (((np->nn_stat.st_mode & 0400) ? O_READ : 0) - | ((np->nn_stat.st_mode & 0200) ? O_WRITE : 0) - | ((np->nn_stat.st_mode & 0100) ? O_EXEC : 0)); - else if (cred_has_gid (cred, np->nn_stat.st_gid)) - /* Group */ - return - (((np->nn_stat.st_mode & 040) ? O_READ : 0) - | ((np->nn_stat.st_mode & 020) ? O_WRITE : 0) - | ((np->nn_stat.st_mode & 010) ? O_EXEC : 0)); - else - /* Other */ - return - (((np->nn_stat.st_mode & 4) ? O_READ : 0) - | ((np->nn_stat.st_mode & 2) ? O_WRITE : 0) - | ((np->nn_stat.st_mode & 1) ? O_EXEC : 0)); -} - /* Implement the netfs_check_open_permissions callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_check_open_permissions (struct netcred *cred, struct node *np, +netfs_check_open_permissions (struct iouser *cred, struct node *np, int flags, int newnode) { - if ((flags & (O_READ|O_WRITE|O_EXEC)) == 0) + int modes; + + if (newnode || (flags & (O_READ|O_WRITE|O_EXEC)) == 0) return 0; - - if ((flags & (O_READ|O_WRITE|O_EXEC)) - == (flags & guess_mode_use (np, cred))) + + netfs_report_access (cred, np, &modes); + if ((flags & (O_READ|O_WRITE|O_EXEC)) == (flags & modes)) return 0; else return EACCES; } /* Implement the netfs_report_access callback as described in - <hurd/netfs.h>. */ -void -netfs_report_access (struct netcred *cred, + <hurd/netfs.h>. */ +error_t +netfs_report_access (struct iouser *cred, struct node *np, int *types) { - *types = guess_mode_use (np, cred); -} + error_t err; + err = netfs_validate_stat (np, cred); + if (err) + return err; + + if (protocol_version == 2) + { + /* Hope the server means the same thing for the bits as we do. */ + *types = 0; + if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0) + *types |= O_READ; + if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0) + *types |= O_WRITE; + if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0) + *types |= O_EXEC; + return 0; + } + else + { + int *p; + void *rpcbuf; + error_t err; + int ret; + int write_check, execute_check; + + if (S_ISDIR (np->nn_stat.st_mode)) + { + write_check = ACCESS3_MODIFY | ACCESS3_DELETE | ACCESS3_EXTEND; + execute_check = ACCESS3_LOOKUP; + } + else + { + write_check = ACCESS3_MODIFY; + execute_check = ACCESS3_EXECUTE; + } + + p = nfs_initialize_rpc (NFS3PROC_ACCESS, cred, 0, &rpcbuf, np, -1); + if (! p) + return errno; + + p = xdr_encode_fhandle (p, &np->nn->handle); + *(p++) = htonl (ACCESS3_READ | write_check | execute_check); + + err = conduct_rpc (&rpcbuf, &p); + if (!err) + { + err = nfs_error_trans (ntohl (*p)); + p++; + p = process_returned_stat (np, p, 0); /* XXX Should this be + protected by the + if (!err) ? */ + if (!err) + { + ret = ntohl (*p); + p++; + *types = ((ret & ACCESS3_READ ? O_READ : 0) + | (ret & write_check ? O_WRITE : 0) + | (ret & execute_check ? O_EXEC : 0)); + } + } + return err; + } +} /* These definitions have unfortunate side effects, don't use them, - clever though they are. */ + clever though they are. */ #if 0 /* Implement the netfs_check_open_permissions callback as described in <hurd/netfs.h>. */ error_t -netfs_check_open_permissions (struct netcred *cred, struct node *np, +netfs_check_open_permissions (struct iouser *cred, struct node *np, int flags, int newnode) { char byte; error_t err; size_t len; - + /* Sun derived nfs client implementations attempt to reproduce the server's permission restrictions by hoping they look like Unix, and using that to give errors at open time. Sadly, that loses @@ -966,17 +1578,17 @@ netfs_check_open_permissions (struct netcred *cred, struct node *np, && (flags & O_WRITE) == 0 && (flags & O_EXEC) == 0) return 0; - + err = netfs_validate_stat (np, cred); if (err) return err; - + switch (np->nn_stat.st_mode & S_IFMT) { /* Don't know how to check, so return provisional success. */ default: return 0; - + case S_IFREG: len = 1; err = netfs_attempt_read (cred, np, 0, &len, &byte); @@ -985,16 +1597,16 @@ netfs_check_open_permissions (struct netcred *cred, struct node *np, if ((flags & O_READ) || (flags & O_EXEC)) return err; else - /* If we couldn't read a byte, but the user wasn't actually asking + /* If we couldn't read a byte, but the user wasn't actually asking for read, then we shouldn't inhibit the open now. */ return 0; } - + if (len != 1) /* The file is empty; reads are known to be OK, but writes can't be tested, so no matter what, return success. */ return 0; - + if (flags & O_WRITE) { err = netfs_attempt_write (cred, np, 0, &len, &byte); @@ -1015,35 +1627,38 @@ netfs_check_open_permissions (struct netcred *cred, struct node *np, return failure. Otherwise, succeed. */ p = nfs_initialize_rpc (NFSPROC_READDIR, cred, 0, &rpcbuf, np, -1); p = xdr_encode_fhandle (p, &np->nn->handle); - *p++ = 0; - *p++ = htonl (50); + *(p++) = 0; + *(p++) = htonl (50); err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); + { + err = nfs_error_trans (ntohl (*p)); + p++; + } free (rpcbuf); - + if (err) return err; } return 0; } } - + /* Implement the netfs_report_access callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ void -netfs_report_access (struct netcred *cred, +netfs_report_access (struct iouser *cred, struct node *np, int *types) { char byte; error_t err; size_t len; - + /* Much the same logic as netfs_check_open_permissions, except that here we err on the side of denying access, and that we always have to check everything. */ - + *types = 0; len = 1; @@ -1080,9 +1695,9 @@ netfs_report_access (struct netcred *cred, *BUFP to that buffer. *BUFP must be freed by the caller when no longer needed. If an error occurs, don't touch *BUFP and return the error code. Set BUFSIZEP to the amount of data used inside - *BUFP and TOTALENTRIES to the total number of entries copied. */ + *BUFP and TOTALENTRIES to the total number of entries copied. */ static error_t -fetch_directory (struct netcred *cred, struct node *dir, +fetch_directory (struct iouser *cred, struct node *dir, void **bufp, size_t *bufsizep, int *totalentries) { void *buf; @@ -1097,7 +1712,11 @@ fetch_directory (struct netcred *cred, struct node *dir, int isnext; bufmalloced = read_size; + buf = malloc (bufmalloced); + if (! buf) + return ENOMEM; + bp = buf; cookie = 0; eof = 0; @@ -1106,66 +1725,82 @@ fetch_directory (struct netcred *cred, struct node *dir, while (!eof) { /* Fetch new directory entries */ - p = nfs_initialize_rpc (NFSPROC_READDIR, cred, 0, &rpcbuf, dir, -1); + p = nfs_initialize_rpc (NFSPROC_READDIR (protocol_version), + cred, 0, &rpcbuf, dir, -1); + if (! p) + { + free (buf); + return errno; + } + p = xdr_encode_fhandle (p, &dir->nn->handle); - *p++ = cookie; - *p++ = ntohl (read_size); + *(p++) = cookie; + *(p++) = ntohl (read_size); err = conduct_rpc (&rpcbuf, &p); if (!err) - err = nfs_error_trans (ntohl (*p++)); + { + err = nfs_error_trans (ntohl (*p)); + p++; + } if (err) { free (buf); return err; } - isnext = ntohl (*p++); - + isnext = ntohl (*p); + p++; + /* Now copy them one at a time. */ while (isnext) { ino_t fileno; int namlen; int reclen; - - fileno = ntohl (*p++); - namlen = ntohl (*p++); + + fileno = ntohl (*p); + p++; + namlen = ntohl (*p); + p++; /* There's a hidden +1 here for the null byte and -1 because d_name has a size of one already in the sizeof. */ reclen = sizeof (struct dirent) + namlen; reclen = (reclen + 3) & ~3; /* make it a multiple of four */ - + /* Expand buffer if necessary */ if (bp + reclen > buf + bufmalloced) { char *newbuf; - + newbuf = realloc (buf, bufmalloced *= 2); + assert (newbuf); if (newbuf != buf) bp = newbuf + (bp - buf); buf = newbuf; } - + /* Fill in new entry */ entry = (struct dirent *) bp; entry->d_fileno = fileno; entry->d_reclen = reclen; entry->d_type = DT_UNKNOWN; entry->d_namlen = namlen; - bcopy (p, entry->d_name, namlen); + memcpy (entry->d_name, p, namlen); entry->d_name[namlen] = '\0'; p += INTSIZE (namlen); bp = bp + entry->d_reclen; ++*totalentries; - - cookie = *p++; - isnext = ntohl (*p++); + + cookie = *(p++); + isnext = ntohl (*p); + p++; } - eof = ntohl (*p++); + eof = ntohl (*p); + p++; free (rpcbuf); } @@ -1175,11 +1810,11 @@ fetch_directory (struct netcred *cred, struct node *dir, return 0; } - + /* Implement the netfs_get_directs callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_get_dirents (struct netcred *cred, struct node *np, +netfs_get_dirents (struct iouser *cred, struct node *np, int entry, int nentries, char **data, mach_msg_type_number_t *datacnt, vm_size_t bufsiz, int *amt) @@ -1195,15 +1830,15 @@ netfs_get_dirents (struct netcred *cred, struct node *np, err = fetch_directory (cred, np, &buf, &our_bufsiz, &totalentries); if (err) return err; - + /* Allocate enough space to hold the maximum we might return. */ if (!bufsiz || bufsiz > our_bufsiz) allocsize = round_page (our_bufsiz); else allocsize = round_page (bufsiz); if (allocsize > *datacnt) - vm_allocate (mach_task_self (), (vm_address_t *)data, allocsize, 1); - + *data = mmap (0, allocsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + /* Skip ahead to the correct entry. */ bp = buf; for (thisentry = 0; thisentry < entry;) @@ -1212,18 +1847,18 @@ netfs_get_dirents (struct netcred *cred, struct node *np, bp += entry->d_reclen; thisentry++; } - + /* Now copy them one at a time */ { int entries_copied; - - for (entries_copied = 0, userdp = *data; + + for (entries_copied = 0, userdp = *data; (nentries == -1 || entries_copied < nentries) && (!bufsiz || userdp - *data < bufsiz) && thisentry < totalentries;) { struct dirent *entry = (struct dirent *) bp; - bcopy (bp, userdp, entry->d_reclen); + memcpy (userdp, bp, entry->d_reclen); bp += entry->d_reclen; userdp += entry->d_reclen; entries_copied++; @@ -1231,45 +1866,34 @@ netfs_get_dirents (struct netcred *cred, struct node *np, } *amt = entries_copied; } - + free (buf); - /* If we allocated the buffer ourselves, but didn't use + /* If we allocated the buffer ourselves, but didn't use all the pages, free the extra. */ if (allocsize > *datacnt && round_page (userdp - *data) < round_page (allocsize)) - vm_deallocate (mach_task_self (), round_page (userdp), - round_page (allocsize) - round_page (userdp - *data)); + munmap ((caddr_t) round_page (userdp), + round_page (allocsize) - round_page (userdp - *data)); *datacnt = userdp - *data; return 0; } - -/* Implement the netfs_set_translator callback as described in - <hurd/netfs.h>. */ -error_t -netfs_set_translator (struct netcred *cred, - struct node *np, - char *argz, - size_t argzlen) -{ - return EOPNOTSUPP; -} /* Implement the netfs_attempt_mksymlink callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_mksymlink (struct netcred *cred, +netfs_attempt_mksymlink (struct iouser *cred, struct node *np, char *arg) { if (np->nn->dtrans == NOT_POSSIBLE) return EOPNOTSUPP; - + if (np->nn->dtrans == SYMLINK) free (np->nn->transarg.name); - + np->nn->transarg.name = malloc (strlen (arg) + 1); strcpy (np->nn->transarg.name, arg); np->nn->dtrans = SYMLINK; @@ -1278,19 +1902,19 @@ netfs_attempt_mksymlink (struct netcred *cred, } /* Implement the netfs_attempt_mkdev callback as described in - <hurd/netfs.h>. */ + <hurd/netfs.h>. */ error_t -netfs_attempt_mkdev (struct netcred *cred, +netfs_attempt_mkdev (struct iouser *cred, struct node *np, mode_t type, dev_t indexes) { if (np->nn->dtrans == NOT_POSSIBLE) return EOPNOTSUPP; - + if (np->nn->dtrans == SYMLINK) free (np->nn->transarg.name); - + np->nn->transarg.indexes = indexes; if (type == S_IFBLK) np->nn->dtrans = BLKDEV; @@ -1299,5 +1923,3 @@ netfs_attempt_mkdev (struct netcred *cred, np->nn->stat_updated = 0; return 0; } - - diff --git a/nfs/pager.c b/nfs/pager.c deleted file mode 100644 index 687a0e2f..00000000 --- a/nfs/pager.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - Copyright (C) 1995, 1996 Free Software Foundation, Inc. - Written by Michael I. Bushnell, p/BSG. - - This file is part of the GNU Hurd. - - The GNU Hurd is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - The GNU Hurd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ - - -#include "nfs.h" -#include <unistd.h> -#include <hurd/pager.h> -#include <netinet/in.h> -#include <string.h> - -struct user_pager_info -{ - struct node *np; - struct pager *p; - int max_prot; -}; - -struct pager_cache_rec -{ - struct pager_cache_rec *next; - vm_offset_t offset; - struct pager *p; - time_t fetched; -}; - -static struct pager_cache_rec *pager_cache_recs; -static spin_lock_t pager_cache_rec_lock = SPIN_LOCK_INITIALIZER; -static spin_lock_t node2pagelock = SPIN_LOCK_INITIALIZER; -static struct port_bucket *pager_bucket; - -void -register_new_page (struct pager *p, vm_offset_t offset) -{ - struct pager_cache_rec *pc; - - pc = malloc (sizeof (struct pager_cache_rec)); - pc->offset = offset; - pc->p = p; - ports_port_ref (p); - pc->fetched = mapped_time->seconds; - - spin_lock (&pager_cache_rec_lock); - pc->next = pager_cache_recs; - pager_cache_recs = pc; - spin_unlock (&pager_cache_rec_lock); -} - -any_t -flush_pager_cache_thread (any_t foo2) -{ - struct pager_cache_rec *pc, *next, **ppc, *list; - - for (;;) - { - sleep (1); - - /* Dequeue from the main list and queue locally the recs - for expired pages. */ - list = 0; - spin_lock (&pager_cache_rec_lock); - for (pc = pager_cache_recs, ppc = &pager_cache_recs; - pc; - ppc = &pc->next, pc = next) - { - next = pc->next; - if (mapped_time->seconds - pc->fetched > cache_timeout) - { - *ppc = pc->next; - pc->next = list; - list = pc; - } - } - spin_unlock (&pager_cache_rec_lock); - - /* And now, one at a time, expire them */ - for (pc = list; pc; pc = next) - { - pager_return_some (pc->p, pc->offset, vm_page_size, 0); - next = pc->next; - ports_port_deref (pc->p); - free (pc); - } - } -} - -error_t -pager_read_page (struct user_pager_info *pager, - vm_offset_t page, - vm_address_t *buf, - int *writelock) -{ - error_t err; - int *p; - void *rpcbuf; - struct node *np; - size_t amt, thisamt, trans_len; - void *data; - off_t offset; - - np = pager->np; - - mutex_lock (&np->lock); - - vm_allocate (mach_task_self (), buf, vm_page_size, 1); - data = (char *) *buf; - amt = vm_page_size; - offset = page; - - while (amt) - { - thisamt = amt; - if (thisamt > read_size) - thisamt = read_size; - - p = nfs_initialize_rpc (NFSPROC_READ, (struct netcred *)-1, 0, - &rpcbuf, np, -1); - p = xdr_encode_fhandle (p, &np->nn->handle); - *p++ = htonl (offset); - *p++ = htonl (vm_page_size); - *p++ = 0; - - err = conduct_rpc (&rpcbuf, &p); - if (!err) - err = nfs_error_trans (ntohl (*p++)); - if (err) - { - mutex_unlock (&np->lock); - free (rpcbuf); - vm_deallocate (mach_task_self (), *buf, vm_page_size); - return err; - } - - p = register_fresh_stat (np, p); - trans_len = ntohl (*p++); - if (trans_len > thisamt) - trans_len = thisamt; /* ??? */ - - bcopy (p, data, trans_len); - - free (rpcbuf); - - data += trans_len; - offset += trans_len; - amt -= trans_len; - - /* If we got a short count, we're all done. */ - if (trans_len < thisamt) - break; - } - - register_new_page (pager->p, page); - mutex_unlock (&np->lock); - return 0; -} - - -error_t -pager_write_page (struct user_pager_info *pager, - vm_offset_t page, - vm_address_t buf) -{ - int *p; - void *rpcbuf; - error_t err; - size_t amt, thisamt; - off_t offset; - struct node *np; - void *data; - - np = pager->np; - mutex_lock (&np->lock); - - amt = vm_page_size; - offset = page; - data = (void *) buf; - - while (amt) - { - thisamt = amt; - if (amt > write_size) - amt = write_size; - - p = nfs_initialize_rpc (NFSPROC_WRITE, (struct netcred *) -1, - amt, &rpcbuf, np, -1); - p = xdr_encode_fhandle (p, &np->nn->handle); - *p++ = 0; - *p++ = htonl (offset); - *p++ = 0; - p = xdr_encode_data (p, data, thisamt); - - err = conduct_rpc (&rpcbuf, &p); - if (!err) - err = nfs_error_trans (ntohl (*p++)); - if (err) - { - free (rpcbuf); - vm_deallocate (mach_task_self (), buf, vm_page_size); - return err; - } - register_fresh_stat (np, p); - free (rpcbuf); - amt -= thisamt; - data += thisamt; - offset += thisamt; - } - - vm_deallocate (mach_task_self (), buf, vm_page_size); - mutex_unlock (&np->lock); - return 0; -} - -error_t -pager_unlock_page (struct user_pager_info *pager, - vm_offset_t address) -{ - abort (); -} - -error_t -pager_report_extent (struct user_pager_info *pager, - vm_address_t *offset, - vm_size_t *size) -{ - struct node *np; - error_t err; - - np = pager->np; - mutex_lock (&np->lock); - - err = netfs_validate_stat (np, 0); - if (!err) - *size = round_page (np->nn_stat.st_size); - mutex_unlock (&np->lock); - return err; -} - -void -pager_clear_user_data (struct user_pager_info *upi) -{ - spin_lock (&node2pagelock); - if (upi->np->nn->fileinfo == upi) - upi->np->nn->fileinfo = 0; - spin_unlock (&node2pagelock); - netfs_nrele (upi->np); - free (upi); -} - -void -pager_dropweak (struct user_pager_info *upi) -{ - abort (); -} - -mach_port_t -netfs_get_filemap (struct node *np, vm_prot_t prot) -{ - struct user_pager_info *upi; - mach_port_t right; - - spin_lock (&node2pagelock); - do - if (!np->nn->fileinfo) - { - upi = malloc (sizeof (struct user_pager_info)); - upi->np = np; - netfs_nref (np); - upi->max_prot = prot; - upi->p = pager_create (upi, pager_bucket, 1, MEMORY_OBJECT_COPY_NONE); - np->nn->fileinfo = upi; - right = pager_get_port (np->nn->fileinfo->p); - ports_port_deref (np->nn->fileinfo->p); - } - else - { - np->nn->fileinfo->max_prot |= prot; - /* Because NP->dn->fileinfo->p is not a real reference, - this might be nearly deallocated. If that's so, then - the port right will be null. In that case, clear here - and loop. The deallocation will complete separately. */ - right = pager_get_port (np->nn->fileinfo->p); - if (right == MACH_PORT_NULL) - np->nn->fileinfo = 0; - } - while (right == MACH_PORT_NULL); - - spin_unlock (&node2pagelock); - - mach_port_insert_right (mach_task_self (), right, right, - MACH_MSG_TYPE_MAKE_SEND); - return right; -} - -void -drop_pager_softrefs (struct node *np) -{ - struct user_pager_info *upi; - - spin_lock (&node2pagelock); - upi = np->nn->fileinfo; - if (upi) - ports_port_ref (upi->p); - spin_unlock (&node2pagelock); - - if (upi) - { - pager_change_attributes (upi->p, 0, MEMORY_OBJECT_COPY_NONE, 0); - ports_port_deref (upi->p); - } -} - -void -allow_pager_softrefs (struct node *np) -{ - struct user_pager_info *upi; - - spin_lock (&node2pagelock); - upi = np->nn->fileinfo; - if (upi) - ports_port_ref (upi->p); - spin_unlock (&node2pagelock); - - if (upi) - { - pager_change_attributes (upi->p, 1, MEMORY_OBJECT_COPY_NONE, 0); - ports_port_deref (upi->p); - } -} - -void -block_caching () -{ - error_t block_cache (void *arg) - { - struct pager *p = arg; - pager_change_attributes (p, 0, MEMORY_OBJECT_COPY_NONE, 1); - return 0; - } - ports_bucket_iterate (pager_bucket, block_cache); -} - -void -enable_caching () -{ - error_t enable_cache (void *arg) - { - struct pager *p = arg; - struct user_pager_info *upi = pager_get_upi (p); - - pager_change_attributes (p, 1, MEMORY_OBJECT_COPY_NONE, 0); - return 0; - } - - ports_bucket_iterate (pager_bucket, enable_cache); -} - -int -netfs_pager_users () -{ - int npagers = ports_count_bucket (pager_bucket); - - if (!npagers) - return 0; - - block_caching (); - /* Give it a sec; the kernel doesn't issue the shutdown right away */ - sleep (1); - npagers = ports_count_bucket (pager_bucket); - if (!npagers) - return 0; - - enable_caching (); - - ports_enable_bucket (pager_bucket); -} - -vm_prot_t -netfs_max_user_pager_prot () -{ - vm_prot_t max_prot; - int npagers = ports_count_bucket (pager_bucket); - - if (npagers) - { - error_t add_pager_max_prot (void *v_p) - { - struct pager *p = v_p; - struct user_pager_info *upi = pager_get_upi (p); - max_prot |= upi->max_prot; - return max_prot == (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); - } - - block_caching (); - sleep (1); - - ports_bucket_iterate (pager_bucket, add_pager_max_prot); - enable_caching (); - } - - ports_enable_bucket (pager_bucket); - return max_prot; -} - -void -netfs_shutdown_pager () -{ - error_t shutdown_one (void *arg) - { - pager_shutdown ((struct pager *) arg); - return 0; - } - - ports_bucket_iterate (pager_bucket, shutdown_one); -} - -void -netfs_sync_everything (int wait) -{ - error_t sync_one (void *arg) - { - pager_sync ((struct pager *) arg, wait); - return 0; - } - ports_bucket_iterate (pager_bucket, sync_one); -} - -void -pager_initialize (void) -{ - pager_bucket = ports_create_bucket (); - cthread_detach (cthread_fork (flush_pager_cache_thread, 0)); - @@ -1,5 +1,5 @@ -/* SunRPC management for NFS client - Copyright (C) 1994, 1995, 1996 Free Software Foundation +/* rpc.c - SunRPC management for NFS client. + Copyright (C) 1994, 1995, 1996, 1997, 2002 Free Software Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -17,10 +17,10 @@ #include "nfs.h" -/* Needed in order to get the RPC header files to include correctly */ +/* Needed in order to get the RPC header files to include correctly. */ #undef TRUE #undef FALSE -#define malloc spoiufasdf /* Avoid bogus definition in rpc/types.h */ +#define malloc spoiufasdf /* Avoid bogus definition in rpc/types.h. */ #include <rpc/types.h> #include <rpc/xdr.h> @@ -28,40 +28,36 @@ #include <rpc/rpc_msg.h> #include <rpc/auth_unix.h> -#undef malloc /* Get rid protection. */ +#undef malloc /* Get rid of the sun block. */ #include <netinet/in.h> #include <assert.h> #include <errno.h> +#include <error.h> #include <unistd.h> #include <stdio.h> -/* One of these exists for each pending RPC. */ +/* One of these exists for each pending RPC. */ struct rpc_list { struct rpc_list *next, **prevp; void *reply; }; -/* A list of all the pending RPCs. */ +/* A list of all pending RPCs. */ static struct rpc_list *outstanding_rpcs; -/* This lock must be held around any modifications to the list - structure of outstanding_rpcs. */ -static spin_lock_t rpc_list_lock = SPIN_LOCK_INITIALIZER; - /* Wake up this condition when an outstanding RPC has received a reply - or we should check for timeouts. */ + or we should check for timeouts. */ static struct condition rpc_wakeup = CONDITION_INITIALIZER; -/* This lock must be held around modifications of the REPLY members of - records on outstanding_rpcs and around uses of rpc_wakeup. */ +/* Lock the global data and the REPLY fields of outstanding RPC's. */ static struct mutex outstanding_lock = MUTEX_INITIALIZER; -/* Generate and return a new transaction ID. */ -static int +/* Generate and return a new transaction ID. */ +static inline int generate_xid () { static int nextxid; @@ -72,22 +68,31 @@ generate_xid () return nextxid++; } -/* Set up an RPC for procdeure RPC_PROC, for talking to the server +/* Set up an RPC for procdeure RPC_PROC for talking to the server PROGRAM of version VERSION. Allocate storage with malloc and point *BUF at it; caller must free this when done. Allocate at least LEN - bytes more than the usual amount for an RPC. Initialize the RPC - credential structure with UID, GID, and SECOND_GID. (Any of those - may be -1 to indicate that it does not apply; exactly or two of UID - and GID must be -1, however.) */ + bytes more than the usual amount for the RPC. Initialize the RPC + credential structure with UID, GID, and SECOND_GID; any of these + may be -1 to indicate that it does not apply, however, exactly zero + or two of UID and GID must be -1. The returned address is a pointer + to the start of the payload. If NULL is returned, an error occurred + and the code is set in errno. */ int * initialize_rpc (int program, int version, int rpc_proc, size_t len, void **bufp, uid_t uid, gid_t gid, gid_t second_gid) { - void *buf = malloc (len + 1024); + void *buf; int *p, *lenaddr; struct rpc_list *hdr; + buf = malloc (len + 1024); + if (! buf) + { + errno = ENOMEM; + return NULL; + } + /* First the struct rpc_list bit. */ hdr = buf; hdr->reply = 0; @@ -95,63 +100,78 @@ initialize_rpc (int program, int version, int rpc_proc, p = buf + sizeof (struct rpc_list); /* RPC header */ - *p++ = htonl (generate_xid ()); - *p++ = htonl (CALL); - *p++ = htonl (RPC_MSG_VERSION); - *p++ = htonl (program); - *p++ = htonl (version); - *p++ = htonl (rpc_proc); + *(p++) = htonl (generate_xid ()); + *(p++) = htonl (CALL); + *(p++) = htonl (RPC_MSG_VERSION); + *(p++) = htonl (program); + *(p++) = htonl (version); + *(p++) = htonl (rpc_proc); assert ((uid == -1) == (gid == -1)); - if (uid != -1) + if (uid == -1) { - *p++ = htonl (AUTH_UNIX); + /* No authentication */ + *(p++) = htonl (AUTH_NONE); + *(p++) = 0; + } + else + { + /* Unixy authentication */ + *(p++) = htonl (AUTH_UNIX); + /* The length of the message. We do not yet know what this + is, so, just remember where we should put it when we know */ lenaddr = p++; - *p++ = htonl (mapped_time->seconds); + *(p++) = htonl (mapped_time->seconds); p = xdr_encode_string (p, hostname); - *p++ = htonl (uid); - *p++ = htonl (gid); - if (second_gid != -1) + *(p++) = htonl (uid); + *(p++) = htonl (gid); + if (second_gid == -1) + *(p++) = 0; + else { - *p++ = htonl (1); - *p++ = htonl (second_gid); + *(p++) = htonl (1); + *(p++) = htonl (second_gid); } - else - *p++ = 0; *lenaddr = htonl ((p - (lenaddr + 1)) * sizeof (int)); } - else - { - *p++ = htonl (AUTH_NULL); - *p++ = 0; - } /* VERF field */ - *p++ = htonl (AUTH_NULL); - *p++ = 0; + *(p++) = htonl (AUTH_NONE); + *(p++) = 0; *bufp = buf; return p; } -/* Remove HDR from the list of pending RPC's. */ -static void +/* Remove HDR from the list of pending RPC's. The rpc_list's lock + (OUTSTANDING_LOCK) must be held. */ +static inline void unlink_rpc (struct rpc_list *hdr) { - spin_lock (&rpc_list_lock); *hdr->prevp = hdr->next; if (hdr->next) hdr->next->prevp = hdr->prevp; - spin_unlock (&rpc_list_lock); +} + +/* Insert HDR at the head of the LIST. The rpc_list's lock + (OUTSTANDING_LOCK) must be held. */ +static inline void +link_rpc (struct rpc_list **list, struct rpc_list *hdr) +{ + hdr->next = *list; + if (hdr->next) + hdr->next->prevp = &hdr->next; + hdr->prevp = list; + *list = hdr; } /* Send the specified RPC message. *RPCBUF is the initialized buffer - from a previous initialize_rpc call; *PP points past the filled - in args. Set *PP to the address of the reply contents themselves. - The user will be expected to free *RPCBUF (which will have changed) - when done with the reply contents. The old value of *RPCBUF will - be freed by this routine. */ + from a previous initialize_rpc call; *PP, the payload, points past + the filledin args. Set *PP to the address of the reply contents + themselves. The user will be expected to free *RPCBUF (which will + have changed) when done with the reply contents. The old value of + *RPCBUF will be freed by this routine. */ error_t conduct_rpc (void **rpcbuf, int **pp) { @@ -166,86 +186,94 @@ conduct_rpc (void **rpcbuf, int **pp) int n; int cancel; - /* Link it in */ - spin_lock (&rpc_list_lock); - hdr->next = outstanding_rpcs; - if (hdr->next) - hdr->next->prevp = &hdr->next; - hdr->prevp = &outstanding_rpcs; - outstanding_rpcs = hdr; - spin_unlock (&rpc_list_lock); + mutex_lock (&outstanding_lock); + + link_rpc (&outstanding_rpcs, hdr); xid = * (int *) (*rpcbuf + sizeof (struct rpc_list)); do { - /* If we've sent enough, give up */ + /* If we've sent enough, give up. */ if (mounted_soft && ntransmit == soft_retries) { unlink_rpc (hdr); + mutex_unlock (&outstanding_lock); return ETIMEDOUT; } - /* Issue the RPC */ - mutex_lock (&outstanding_lock); + /* Issue the RPC. */ lasttrans = mapped_time->seconds; ntransmit++; nc = (void *) *pp - *rpcbuf - sizeof (struct rpc_list); cc = write (main_udp_socket, *rpcbuf + sizeof (struct rpc_list), nc); if (cc == -1) - assert_perror (errno); + { + unlink_rpc (hdr); + mutex_unlock (&outstanding_lock); + return errno; + } else assert (cc == nc); - /* Wait for reply */ + /* Wait for reply. */ cancel = 0; while (!hdr->reply && (mapped_time->seconds - lasttrans < timeout) && !cancel) cancel = hurd_condition_wait (&rpc_wakeup, &outstanding_lock); - mutex_unlock (&outstanding_lock); if (cancel) { unlink_rpc (hdr); + mutex_unlock (&outstanding_lock); return EINTR; } + /* hdr->reply will have been filled in by rpc_receive_thread, + if it has been filled in, then the rpc has been fulfilled, + otherwise, retransmit and continue to wait. */ if (!hdr->reply) { - timeout *=2; + timeout *= 2; if (timeout > max_transmit_timeout) timeout = max_transmit_timeout; } } while (!hdr->reply); - /* Switch to the reply buffer. */ + mutex_unlock (&outstanding_lock); + + /* Switch to the reply buffer. */ *rpcbuf = hdr->reply; free (hdr); - /* Process the reply, dissecting errors. When we're done, set *PP to - the rpc return contents, if there is no error. */ + /* Process the reply, dissecting errors. When we're done and if + there is no error, set *PP to the rpc return contents. */ p = (int *) *rpcbuf; + /* If the transmition id does not match that in the message, + something strange happened in rpc_receive_thread. */ assert (*p == xid); p++; - switch (ntohl (*p++)) + switch (ntohl (*p)) { default: err = EBADRPC; break; case REPLY: - switch (ntohl (*p++)) + p++; + switch (ntohl (*p)) { default: err = EBADRPC; break; case MSG_DENIED: - switch (ntohl (*p++)) + p++; + switch (ntohl (*p)) { default: err = EBADRPC; @@ -256,7 +284,8 @@ conduct_rpc (void **rpcbuf, int **pp) break; case AUTH_ERROR: - switch (ntohl (*p++)) + p++; + switch (ntohl (*p)) { case AUTH_BADCRED: case AUTH_REJECTEDCRED: @@ -278,13 +307,15 @@ conduct_rpc (void **rpcbuf, int **pp) break; case MSG_ACCEPTED: + p++; - /* Process VERF field. */ - p++; /* skip verf type */ - n = ntohl (*p++); /* size of verf */ - p += INTSIZE (n); /* skip verf itself */ + /* Process VERF field. */ + p++; /* Skip verf type. */ + n = ntohl (*p); /* Size of verf. */ + p++; + p += INTSIZE (n); /* Skip verf itself. */ - switch (ntohl (*p++)) + switch (ntohl (*p)) { default: case GARBAGE_ARGS: @@ -304,6 +335,7 @@ conduct_rpc (void **rpcbuf, int **pp) break; case SUCCESS: + p++; *pp = p; err = 0; break; @@ -315,7 +347,8 @@ conduct_rpc (void **rpcbuf, int **pp) return err; } -/* Dedicated thread to wakeup rpc_wakeup once a second. */ +/* Dedicated thread to signal those waiting on rpc_wakeup + once a second. */ void timeout_service_thread () { @@ -329,59 +362,56 @@ timeout_service_thread () } /* Dedicate thread to receive RPC replies, register them on the queue - of pending wakeups, and deal appropriately. */ + of pending wakeups, and deal appropriately. */ void rpc_receive_thread () { - int cc; void *buf; - struct rpc_list *r; - int xid; + + /* Allocate a receive buffer. */ + buf = malloc (1024 + read_size); + assert (buf); while (1) { - buf = malloc (1024 + read_size); - - do - { - cc = read (main_udp_socket, buf, 1024 + read_size); - if (cc == -1) - { - perror ("nfs read"); - r = 0; + int cc = read (main_udp_socket, buf, 1024 + read_size); + if (cc == -1) + { + error (0, errno, "nfs read"); + continue; + } + else + { + struct rpc_list *r; + int xid = *(int *)buf; + + mutex_lock (&outstanding_lock); + + /* Find the rpc that we just fulfilled. */ + for (r = outstanding_rpcs; r; r = r->next) + { + if (* (int *) &r[1] == xid) + { + unlink_rpc (r); + r->reply = buf; + condition_broadcast (&rpc_wakeup); + break; + } } - else +#if 0 + if (! r) + fprintf (stderr, "NFS dropping reply xid %d\n", xid); +#endif + mutex_unlock (&outstanding_lock); + + /* If r is not null then we had a message from a pending + (i.e. known) rpc. Thus, it was fulfilled and if we want + to get another request, a new buffer is needed. */ + if (r) { - xid = *(int *)buf; - spin_lock (&rpc_list_lock); - for (r = outstanding_rpcs; r; r = r->next) - { - if (* (int *) &r[1] == xid) - { - /* Remove it from the list */ - *r->prevp = r->next; - if (r->next) - r->next->prevp = r->prevp; - spin_unlock (&rpc_list_lock); - - mutex_lock (&outstanding_lock); - r->reply = buf; - condition_broadcast (&rpc_wakeup); - mutex_unlock (&outstanding_lock); - break; - } - } - if (!r) - { - spin_unlock (&rpc_list_lock); - fprintf (stderr, "NFS dropping reply xid %d\n", xid); - } + buf = malloc (1024 + read_size); + assert (buf); } - } - while (!r); + } } } - - - - diff --git a/nfs/rpcsvc/mount.h b/nfs/rpcsvc/mount.h deleted file mode 100644 index 2dc3dc88..00000000 --- a/nfs/rpcsvc/mount.h +++ /dev/null @@ -1,81 +0,0 @@ -#define MNTPATHLEN 1024 -#define MNTNAMLEN 255 -#define FHSIZE 32 - -typedef char fhandle[FHSIZE]; -bool_t xdr_fhandle(); - - -struct fhstatus { - u_int fhs_status; - union { - fhandle fhs_fhandle; - } fhstatus_u; -}; -typedef struct fhstatus fhstatus; -bool_t xdr_fhstatus(); - - -typedef char *dirpath; -bool_t xdr_dirpath(); - - -typedef char *name; -bool_t xdr_name(); - - -typedef struct mountbody *mountlist; -bool_t xdr_mountlist(); - - -struct mountbody { - name ml_hostname; - dirpath ml_directory; - mountlist ml_next; -}; -typedef struct mountbody mountbody; -bool_t xdr_mountbody(); - - -typedef struct groupnode *groups; -bool_t xdr_groups(); - - -struct groupnode { - name gr_name; - groups gr_next; -}; -typedef struct groupnode groupnode; -bool_t xdr_groupnode(); - - -typedef struct exportnode *exports; -bool_t xdr_exports(); - - -struct exportnode { - dirpath ex_dir; - groups ex_groups; - exports ex_next; -}; -typedef struct exportnode exportnode; -bool_t xdr_exportnode(); - - -#define MOUNTPROG ((u_long)100005) -#define MOUNTVERS ((u_long)1) -#define MOUNTPROC_NULL ((u_long)0) -extern void *mountproc_null_1(); -#define MOUNTPROC_MNT ((u_long)1) -extern fhstatus *mountproc_mnt_1(); -#define MOUNTPROC_DUMP ((u_long)2) -extern mountlist *mountproc_dump_1(); -#define MOUNTPROC_UMNT ((u_long)3) -extern void *mountproc_umnt_1(); -#define MOUNTPROC_UMNTALL ((u_long)4) -extern void *mountproc_umntall_1(); -#define MOUNTPROC_EXPORT ((u_long)5) -extern exports *mountproc_export_1(); -#define MOUNTPROC_EXPORTALL ((u_long)6) -extern exports *mountproc_exportall_1(); - diff --git a/nfs/rpcsvc/nfs_prot.h b/nfs/rpcsvc/nfs_prot.h deleted file mode 100644 index 7f974930..00000000 --- a/nfs/rpcsvc/nfs_prot.h +++ /dev/null @@ -1,343 +0,0 @@ -#define NFS_PORT 2049 -#define NFS_MAXDATA 8192 -#define NFS_MAXPATHLEN 1024 -#define NFS_MAXNAMLEN 255 -#define NFS_FHSIZE 32 -#define NFS_COOKIESIZE 4 -#define NFS_FIFO_DEV -1 -#define NFSMODE_FMT 0170000 -#define NFSMODE_DIR 0040000 -#define NFSMODE_CHR 0020000 -#define NFSMODE_BLK 0060000 -#define NFSMODE_REG 0100000 -#define NFSMODE_LNK 0120000 -#define NFSMODE_SOCK 0140000 -#define NFSMODE_FIFO 0010000 - -enum nfsstat { - NFS_OK = 0, - NFSERR_PERM = 1, - NFSERR_NOENT = 2, - NFSERR_IO = 5, - NFSERR_NXIO = 6, - NFSERR_ACCES = 13, - NFSERR_EXIST = 17, - NFSERR_NODEV = 19, - NFSERR_NOTDIR = 20, - NFSERR_ISDIR = 21, - NFSERR_FBIG = 27, - NFSERR_NOSPC = 28, - NFSERR_ROFS = 30, - NFSERR_NAMETOOLONG = 63, - NFSERR_NOTEMPTY = 66, - NFSERR_DQUOT = 69, - NFSERR_STALE = 70, - NFSERR_WFLUSH = 99, -}; -typedef enum nfsstat nfsstat; -bool_t xdr_nfsstat(); - - -enum ftype { - NFNON = 0, - NFREG = 1, - NFDIR = 2, - NFBLK = 3, - NFCHR = 4, - NFLNK = 5, - NFSOCK = 6, - NFBAD = 7, - NFFIFO = 8, -}; -typedef enum ftype ftype; -bool_t xdr_ftype(); - - -struct nfs_fh { - char data[NFS_FHSIZE]; -}; -typedef struct nfs_fh nfs_fh; -bool_t xdr_nfs_fh(); - - -struct nfstime { - u_int seconds; - u_int useconds; -}; -typedef struct nfstime nfstime; -bool_t xdr_nfstime(); - - -struct fattr { - ftype type; - u_int mode; - u_int nlink; - u_int uid; - u_int gid; - u_int size; - u_int blocksize; - u_int rdev; - u_int blocks; - u_int fsid; - u_int fileid; - nfstime atime; - nfstime mtime; - nfstime ctime; -}; -typedef struct fattr fattr; -bool_t xdr_fattr(); - - -struct sattr { - u_int mode; - u_int uid; - u_int gid; - u_int size; - nfstime atime; - nfstime mtime; -}; -typedef struct sattr sattr; -bool_t xdr_sattr(); - - -typedef char *filename; -bool_t xdr_filename(); - - -typedef char *nfspath; -bool_t xdr_nfspath(); - - -struct attrstat { - nfsstat status; - union { - fattr attributes; - } attrstat_u; -}; -typedef struct attrstat attrstat; -bool_t xdr_attrstat(); - - -struct sattrargs { - nfs_fh file; - sattr attributes; -}; -typedef struct sattrargs sattrargs; -bool_t xdr_sattrargs(); - - -struct diropargs { - nfs_fh dir; - filename name; -}; -typedef struct diropargs diropargs; -bool_t xdr_diropargs(); - - -struct diropokres { - nfs_fh file; - fattr attributes; -}; -typedef struct diropokres diropokres; -bool_t xdr_diropokres(); - - -struct diropres { - nfsstat status; - union { - diropokres diropres; - } diropres_u; -}; -typedef struct diropres diropres; -bool_t xdr_diropres(); - - -struct readlinkres { - nfsstat status; - union { - nfspath data; - } readlinkres_u; -}; -typedef struct readlinkres readlinkres; -bool_t xdr_readlinkres(); - - -struct readargs { - nfs_fh file; - u_int offset; - u_int count; - u_int totalcount; -}; -typedef struct readargs readargs; -bool_t xdr_readargs(); - - -struct readokres { - fattr attributes; - struct { - u_int data_len; - char *data_val; - } data; -}; -typedef struct readokres readokres; -bool_t xdr_readokres(); - - -struct readres { - nfsstat status; - union { - readokres reply; - } readres_u; -}; -typedef struct readres readres; -bool_t xdr_readres(); - - -struct writeargs { - nfs_fh file; - u_int beginoffset; - u_int offset; - u_int totalcount; - struct { - u_int data_len; - char *data_val; - } data; -}; -typedef struct writeargs writeargs; -bool_t xdr_writeargs(); - - -struct createargs { - diropargs where; - sattr attributes; -}; -typedef struct createargs createargs; -bool_t xdr_createargs(); - - -struct renameargs { - diropargs from; - diropargs to; -}; -typedef struct renameargs renameargs; -bool_t xdr_renameargs(); - - -struct linkargs { - nfs_fh from; - diropargs to; -}; -typedef struct linkargs linkargs; -bool_t xdr_linkargs(); - - -struct symlinkargs { - diropargs from; - nfspath to; - sattr attributes; -}; -typedef struct symlinkargs symlinkargs; -bool_t xdr_symlinkargs(); - - -typedef char nfscookie[NFS_COOKIESIZE]; -bool_t xdr_nfscookie(); - - -struct readdirargs { - nfs_fh dir; - nfscookie cookie; - u_int count; -}; -typedef struct readdirargs readdirargs; -bool_t xdr_readdirargs(); - - -struct entry { - u_int fileid; - filename name; - nfscookie cookie; - struct entry *nextentry; -}; -typedef struct entry entry; -bool_t xdr_entry(); - - -struct dirlist { - entry *entries; - bool_t eof; -}; -typedef struct dirlist dirlist; -bool_t xdr_dirlist(); - - -struct readdirres { - nfsstat status; - union { - dirlist reply; - } readdirres_u; -}; -typedef struct readdirres readdirres; -bool_t xdr_readdirres(); - - -struct statfsokres { - u_int tsize; - u_int bsize; - u_int blocks; - u_int bfree; - u_int bavail; -}; -typedef struct statfsokres statfsokres; -bool_t xdr_statfsokres(); - - -struct statfsres { - nfsstat status; - union { - statfsokres reply; - } statfsres_u; -}; -typedef struct statfsres statfsres; -bool_t xdr_statfsres(); - - -#define NFS_PROGRAM ((u_long)100003) -#define NFS_VERSION ((u_long)2) -#define NFSPROC_NULL ((u_long)0) -extern void *nfsproc_null_2(); -#define NFSPROC_GETATTR ((u_long)1) -extern attrstat *nfsproc_getattr_2(); -#define NFSPROC_SETATTR ((u_long)2) -extern attrstat *nfsproc_setattr_2(); -#define NFSPROC_ROOT ((u_long)3) -extern void *nfsproc_root_2(); -#define NFSPROC_LOOKUP ((u_long)4) -extern diropres *nfsproc_lookup_2(); -#define NFSPROC_READLINK ((u_long)5) -extern readlinkres *nfsproc_readlink_2(); -#define NFSPROC_READ ((u_long)6) -extern readres *nfsproc_read_2(); -#define NFSPROC_WRITECACHE ((u_long)7) -extern void *nfsproc_writecache_2(); -#define NFSPROC_WRITE ((u_long)8) -extern attrstat *nfsproc_write_2(); -#define NFSPROC_CREATE ((u_long)9) -extern diropres *nfsproc_create_2(); -#define NFSPROC_REMOVE ((u_long)10) -extern nfsstat *nfsproc_remove_2(); -#define NFSPROC_RENAME ((u_long)11) -extern nfsstat *nfsproc_rename_2(); -#define NFSPROC_LINK ((u_long)12) -extern nfsstat *nfsproc_link_2(); -#define NFSPROC_SYMLINK ((u_long)13) -extern nfsstat *nfsproc_symlink_2(); -#define NFSPROC_MKDIR ((u_long)14) -extern diropres *nfsproc_mkdir_2(); -#define NFSPROC_RMDIR ((u_long)15) -extern nfsstat *nfsproc_rmdir_2(); -#define NFSPROC_READDIR ((u_long)16) -extern readdirres *nfsproc_readdir_2(); -#define NFSPROC_STATFS ((u_long)17) -extern statfsres *nfsproc_statfs_2(); - diff --git a/nfs/storage-info.c b/nfs/storage-info.c new file mode 100644 index 00000000..7427b3d8 --- /dev/null +++ b/nfs/storage-info.c @@ -0,0 +1,104 @@ +/* file_get_storage_info RPC for NFS client filesystem + Copyright (C) 2001,02 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "nfs.h" +#include <hurd/netfs.h> +#include <stdio.h> + +error_t +netfs_file_get_storage_info (struct iouser *cred, + struct node *np, + mach_port_t **ports, + mach_msg_type_name_t *ports_type, + mach_msg_type_number_t *num_ports, + int **ints, + mach_msg_type_number_t *num_ints, + off_t **offsets, + mach_msg_type_number_t *num_offsets, + char **data, + mach_msg_type_number_t *data_len) +{ + int name_len, fhpos; + error_t err; + + inline int fmt (size_t buflen) + { + return snprintf (*data, buflen, + "nfsv%u://%s:%u/%n%*c?rsize=%u&wsize=%u", + protocol_version, mounted_hostname, mounted_nfs_port, + &fhpos, (int) (np->nn->handle.size * 2), + 'X', /* filled below */ + read_size, write_size); + } + + /* We return the file size, so make sure we have it up to date now. */ + err = netfs_validate_stat (np, cred); + if (err) + return err; + + /* Format the name, and then do it again if the buffer was too short. */ + name_len = fmt (*data_len); + if (name_len < 0) + return errno; + ++name_len; /* Include the terminating null. */ + if (name_len <= *data_len) + *data_len = name_len; + else + { + *data = mmap (0, name_len, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + if (*data == MAP_FAILED) + return errno; + *data_len = fmt (name_len) + 1; + assert (*data_len == name_len); + } + + /* Now fill in the file handle data in hexadecimal. */ + { + static const char hexdigits[] = "0123456789abcdef"; + size_t i; + for (i = 0; i < np->nn->handle.size; ++i) + { + (*data)[fhpos++] = hexdigits[(uint8_t)np->nn->handle.data[i] >> 4]; + (*data)[fhpos++] = hexdigits[(uint8_t)np->nn->handle.data[i] & 0xf]; + } + } + + /* Now fill in the rest of the canonical-form storage-info data, which + just describes a single run of the file's size, a block-size of one + byte, and our URL as the name for the network store type. */ + + *num_ports = 0; + *ports_type = MACH_MSG_TYPE_COPY_SEND; + + assert (*num_offsets >= 2); /* mig always gives us some */ + *num_offsets = 2; + (*offsets)[0] = 0; + (*offsets)[1] = np->nn_stat.st_size; + + assert (*num_ints >= 6); /* mig always gives us some */ + *num_ints = 1; + (*ints)[0] = STORAGE_NETWORK; + (*ints)[1] = 0; /* XXX readonly if we supported it */ + (*ints)[2] = 1; /* block size */ + (*ints)[3] = 1; /* 1 run in offsets list */ + (*ints)[4] = name_len; + (*ints)[5] = 0; /* misc len */ + + return 0; +} |