aboutsummaryrefslogtreecommitdiff
path: root/nfs
diff options
context:
space:
mode:
Diffstat (limited to 'nfs')
-rw-r--r--nfs/ChangeLog170
-rw-r--r--nfs/Makefile16
-rw-r--r--nfs/cache.c129
-rw-r--r--nfs/cred.c116
-rw-r--r--nfs/main.c116
-rw-r--r--nfs/mount.c195
-rw-r--r--nfs/mount.h9
-rw-r--r--nfs/name-cache.c305
-rw-r--r--nfs/nfs-spec.h168
-rw-r--r--nfs/nfs.c555
-rw-r--r--nfs/nfs.h85
-rw-r--r--nfs/ops.c1416
-rw-r--r--nfs/pager.c448
-rw-r--r--nfs/rpc.c282
-rw-r--r--nfs/rpcsvc/mount.h81
-rw-r--r--nfs/rpcsvc/nfs_prot.h343
-rw-r--r--nfs/storage-info.c104
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;
-}
diff --git a/nfs/main.c b/nfs/main.c
index e709fc72..48852062 100644
--- a/nfs/main.c
+++ b/nfs/main.c
@@ -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 */
diff --git a/nfs/nfs.c b/nfs/nfs.c
index 617ca334..4916df65 100644
--- a/nfs/nfs.c
+++ b/nfs/nfs.c
@@ -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;
+ }
}
}
-
diff --git a/nfs/nfs.h b/nfs/nfs.h
index d9d01121..147dc900 100644
--- a/nfs/nfs.h
+++ b/nfs/nfs.h
@@ -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 */
diff --git a/nfs/ops.c b/nfs/ops.c
index efeded10..05cfbe9a 100644
--- a/nfs/ops.c
+++ b/nfs/ops.c
@@ -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 ?: &current,
+ mtime ?: &current);
+ if (protocol_version == 3)
+ *(p++) = 0; /* guard check == 0 */
+
err = conduct_rpc (&rpcbuf, &p);
if (!err)
- err = nfs_error_trans (ntohl (*p++));
-
- 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));
-
diff --git a/nfs/rpc.c b/nfs/rpc.c
index 7df05c36..0b0444d0 100644
--- a/nfs/rpc.c
+++ b/nfs/rpc.c
@@ -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;
+}