diff options
-rw-r--r-- | nfs/Makefile | 12 | ||||
-rw-r--r-- | nfs/nfs.h | 159 | ||||
-rw-r--r-- | nfs/ops.c | 1225 | ||||
-rw-r--r-- | nfs/rpc.c | 424 |
4 files changed, 1685 insertions, 135 deletions
diff --git a/nfs/Makefile b/nfs/Makefile index 2cf0c117..047bbd9f 100644 --- a/nfs/Makefile +++ b/nfs/Makefile @@ -1,5 +1,5 @@ # -# Copyright (C) 1995 Free Software Foundation +# Copyright (C) 1995, 1996 Free Software Foundation # Written by Michael I. Bushnell. # # This file is part of the GNU Hurd. @@ -19,9 +19,13 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. dir := nfs -makemode := misc +makemode := server -LCLHDRS = mountv1.h nfs.h nfsv2.h rpc.h rpcv2.h -SRCS = ops.c rpc.c +target = nfs +LCLHDRS = nfs.h rpc.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 include ../Makeconf @@ -1,5 +1,5 @@ -/* - Copyright (C) 1994 Free Software Foundation +/* Data structures and global variables for NFS client + Copyright (C) 1994, 1995, 1996 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 @@ -16,13 +16,164 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* Needed for the rpcsvc include files to work. */ +typedef int bool_t; /* Ick. */ + +#include <sys/stat.h> +#include <sys/types.h> +#include <rpcsvc/nfs_prot.h> +#include <hurd/netfs.h> + +/* One of these exists for private data needed by the client for each + node. */ struct netnode { - nfsv2_fhandle_t handle; + char handle[NFS_FHSIZE]; + time_t stat_updated; + struct node *hnext, **hprevp; + + /* These two fields handle translators set internally but + unknown to the server. */ + enum + { + NOT_POSSIBLE, + POSSIBLE, + SYMLINK, + CHRDEV, + BLKDEV, + FIFO, + SOCK, + } dtrans; + union + { + char *name; + dev_t indexes; + } transarg; + +#ifdef notyet + /* 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; +#endif + + /* If this node has been renamed by "deletion" then + this is the directory and 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 { - struct rpc_auth auth; + 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 */ +volatile struct mapped_time_value *mapped_time; + +/* Some tunable parameters */ + +/* How long to keep around stat information */ +extern int stat_timeout; + +/* How long to keep around file contents caches */ +extern int cache_timeout; + +/* How long to wait for replies before re-sending RPC's. */ +extern int initial_transmit_timeout; +extern int max_transmit_timeout; + +/* How many attempts to send before giving up on soft mounts */ +extern int soft_retries; + +/* Whether we are a hard or soft mount */ +extern int mounted_soft; + +/* Maximum amount to read at once */ +extern int read_size; + +/* Maximum amout to write at once */ +extern int write_size; + +/* Service name for portmapper */ +extern char *pmap_service_name; + +/* If pmap_service is null, then this is the port to use for the portmapper; + if the lookup of pmap_service_name fails, use this number. */ +extern short pmap_service_number; + +/* RPC program for the mount agent */ +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 + the portmapper fails or can't be found. */ +extern short mount_port; + +/* If this is nonzero use mount_port without attempting to contact + the portmapper */ +extern int mount_port_override; + +/* RPC program for the NFS server */ +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 the portmapper fails or can't be found */ +extern short nfs_port; + +/* If this is nonzero use nfs_port without attepting to contact the + portmapper. */ +extern int nfs_port_override; + + +/* Count how many four-byte chunks it takss 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 *xdr_encode_data (int *, char *, size_t); +int *xdr_encode_string (int *, char *); +int *xdr_encode_sattr_mode (int *, mode_t); +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_decode_fattr (int *, struct stat *); +int *xdr_decode_string (int *, char *); +int *nfs_initialize_rpc (int, struct netcred *, size_t, void **, + struct node *, uid_t); +error_t nfs_error_trans (int); + +/* mount.c */ +struct node *mount_root (char *, char *); + +/* rpc.c */ +int *initialize_rpc (int, int, int, size_t, void **, uid_t, gid_t, gid_t); +error_t conduct_rpc (void **, int **); +void timeout_service_thread (void); +void rpc_receive_thread (void); + +/* cache.c */ +struct node *lookup_fhandle (void *); +void recache_handle (struct node *, void *); @@ -1,5 +1,5 @@ -/* - Copyright (C) 1994 Free Software Foundation +/* Libnetfs callbacks for node operations in NFS client + Copyright (C) 1994, 1995, 1996 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 @@ -15,62 +15,1203 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "nfs.h" +#include <hurd/netfs.h> +#include <netinet/in.h> +#include <string.h> +#include <fcntl.h> +#include <stdio.h> +#include <dirent.h> +#include <unistd.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. */ +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; + + switch (np->nn->dtrans) + { + 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); + break; + + case BLKDEV: + np->nn_stat.st_rdev = np->nn->transarg.indexes; + np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFBLK); + break; + + case FIFO: + np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFIFO); + break; + + case SOCK: + np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFSOCK); + break; + } + + np->nn_stat.st_fsid = getpid (); + np->nn_stat.st_fstype = FSTYPE_NFS; + np->nn_stat.st_gen = 0; + np->nn_stat.st_author = np->nn_stat.st_uid; + np->nn_stat.st_flags = 0; + + return ret; +} /* Implement the netfs_validate_stat callback as described in <hurd/netfs.h>. */ error_t -netfs_validate_stat (struct node *np, struct protid *cred) +netfs_validate_stat (struct node *np, struct netcred *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 = xdr_encode_fhandle (p, &np->nn->handle); + + err = conduct_rpc (&rpcbuf, &p); + if (!err) + err = nfs_error_trans (ntohl (*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>. */ +error_t +netfs_attempt_chown (struct netcred *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 = xdr_encode_fhandle (p, &np->nn->handle); + p = xdr_encode_sattr_ids (p, uid, gid); + + err = conduct_rpc (&rpcbuf, &p); + + if (!err) + register_fresh_stat (np, p); + + 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, + uid_t author) +{ + return EOPNOTSUPP; +} + +/* Implement the netfs_attempt_chmod callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_chmod (struct netcred *cred, struct node *np, + mode_t mode) +{ + int *p; + void *rpcbuf; + error_t err; + + if ((mode & S_IFMT) != 0) + { + err = netfs_validate_stat (np, cred); + if (err) + return err; + 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; + } + if (f) + free (f); + return 0; + } + } + + p = nfs_initialize_rpc (NFSPROC_SETATTR, cred, 0, &rpcbuf, np, -1); + p = xdr_encode_fhandle (p, &np->nn->handle); + p = xdr_encode_sattr_mode (p, mode); + + err = conduct_rpc (&rpcbuf, &p); + if (!err) + err = nfs_error_trans (ntohl (*p++)); + + if (!err) + register_fresh_stat (np, p); + + 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, + int flags) +{ + return EOPNOTSUPP; +} + +/* Implement the netfs_attempt_utimes callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_utimes (struct netcred *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); + p = xdr_encode_fhandle (p, &np->nn->handle); + p = xdr_encode_sattr_times (p, atime, mtime); + + err = conduct_rpc (&rpcbuf, &p); + if (!err) + err = nfs_error_trans (ntohl (*p++)); + + if (!err) + register_fresh_stat (np, p); + + free (rpcbuf); + return err; +} + +/* Implement the netfs_attempt_set_size callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_set_size (struct netcred *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 = xdr_encode_fhandle (p, &np->nn->handle); + p = xdr_encode_sattr_size (p, size); + + err = conduct_rpc (&rpcbuf, &p); + if (!err) + err = nfs_error_trans (ntohl (*p++)); + + if (!err) + register_fresh_stat (np, p); + + free (rpcbuf); + return err; +} + +/* Implement the netfs_attempt_statfs callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_statfs (struct netcred *cred, struct node *np, + struct fsys_statfsbuf *st) +{ + int *p; + void *rpcbuf; + error_t err; + + p = nfs_initialize_rpc (NFSPROC_SETATTR, cred, 0, &rpcbuf, np, -1); + p = xdr_encode_fhandle (p, &np->nn->handle); + + err = conduct_rpc (&rpcbuf, &p); + if (!err) + err = nfs_error_trans (ntohl (*p++)); + + if (!err) + { + st->fsys_stb_iosize = *p++; + st->fsys_stb_bsize = *p++; + st->fsys_stb_blocks = *p++; + st->fsys_stb_bfree = *p++; + st->fsys_stb_bavail = *p++; + + st->fsys_stb_type = FSTYPE_NFS; + st->fsys_stb_files = 0; + st->fsys_stb_ffree = 0; + st->fsys_stb_fsid = 0; /* XXX wrong */ + } + + free (rpcbuf); + return err; +} + +/* Implement the netfs_attempt_sync callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_sync (struct netcred *cred, struct node *np, int wait) +{ + /* We are already completely synchronous. */ + return 0; +} + +/* Implement the netfs_attempt_syncfs callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_syncfs (struct netcred *cred, int wait) +{ + return 0; +} + +/* Implement the netfs_attempt_read callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_read (struct netcred *cred, struct node *np, + off_t offset, size_t *len, void *data) +{ + int *p; + void *rpcbuf; + size_t trans_len; + error_t err; + size_t amt, thisamt; + + 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 = xdr_encode_fhandle (p, &np->nn->handle); + *p++ = htonl (offset); + *p++ = htonl (thisamt); + *p++ = 0; + + err = conduct_rpc (&rpcbuf, &p); + if (!err) + err = nfs_error_trans (ntohl (*p++)); + + if (err) + { + 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; + } + } + return 0; +} + +/* Implement the netfs_attempt_write callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_write (struct netcred *cred, struct node *np, + off_t offset, size_t *len, void *data) +{ + int *p; + void *rpcbuf; + error_t err; + size_t amt, thisamt; + + for (amt = *len; amt;) + { + thisamt = amt; + if (amt > write_size) + amt = write_size; + + p = nfs_initialize_rpc (NFSPROC_WRITE, cred, 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) + { + *len = 0; + free (rpcbuf); + return err; + } + register_fresh_stat (np, p); + free (rpcbuf); + amt -= thisamt; + data += thisamt; + offset += thisamt; + } + return 0; +} + +/* Implement the netfs_attempt_lookup callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_lookup (struct netcred *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); + p = xdr_encode_fhandle (p, &np->nn->handle); + p = xdr_encode_string (p, name); + + 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); + } + else + *newnp = 0; + + free (rpcbuf); + + return err; +} + +/* Implement the netfs_attempt_mkdir callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_mkdir (struct netcred *cred, struct node *np, + char *name, mode_t mode) { - struct vfsv2_fattr *fattr; - size_t hsize; - char *rpc; - struct nfsv2_attrstat *reply; + int *p; + void *rpcbuf; error_t err; + + p = nfs_initialize_rpc (NFSPROC_MKDIR, 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); + + err = conduct_rpc (&rpcbuf, &p); + if (!err) + err = nfs_error_trans (ntohl (*p++)); + + /* Ignore returned information */ + /* XXX should probably cache it */ + + free (rpcbuf); + return err; +} + +/* Implement the netfs_attempt_rmdir callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_rmdir (struct netcred *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? */ - /* The only arg is the file handle. */ + p = nfs_initialize_rpc (NFSPROC_RMDIR, cred, 0, &rpcbuf, np, -1); + 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++)); + + free (rpcbuf); + return err; +} - hsize = rpc_compute_header_size (cred->nc.auth); - rpc = alloca (hsize + sizeof (nfsv2_fhandle_t)); +/* Implement the netfs_attempt_link callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_link (struct netcred *cred, struct node *dir, + struct node *np, char *name) +{ + int *p; + void *rpcbuf; + error_t err = 0; - /* Fill in request arg */ - bcopy (&np->nn.handle, rpc + hsize, NFSV2_FHSIZE); + /* 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 + doing a normal link. */ + + switch (np->nn->dtrans) + { + case POSSIBLE: + case NOT_POSSIBLE: + mutex_lock (&dir->lock); + p = nfs_initialize_rpc (NFSPROC_LINK, cred, 0, &rpcbuf, dir, -1); + mutex_unlock (&dir->lock); + + mutex_lock (&np->lock); + p = xdr_encode_fhandle (p, &np->nn->handle); + mutex_unlock (&np->lock); - /* Transmit */ - err = nfs_rpc_send (NFSV2_GETATTR, rpc, hsize + NFSV2_FHSIZE, - &reply, sizeof (struct nfsv2_attrstat)); + mutex_lock (&dir->lock); + 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); + + free (rpcbuf); + + break; + + case SYMLINK: + mutex_lock (&dir->lock); + p = nfs_initialize_rpc (NFSPROC_SYMLINK, cred, 0, &rpcbuf, dir, -1); + mutex_unlock (&dir->lock); + + p = xdr_encode_string (p, name); + + mutex_lock (&np->lock); + err = netfs_validate_stat (np, cred); + if (err) + { + mutex_unlock (&np->lock); + free (rpcbuf); + return err; + } + + p = xdr_encode_string (p, np->nn->transarg.name); + p = xdr_encode_sattr_stat (p, &np->nn_stat); + mutex_unlock (&np->lock); + + mutex_lock (&dir->lock); + 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); + + if (err) + err = EGRATUITOUS; /* damn */ + else + { + mutex_lock (&np->lock); + recache_handle (np, p); + p += NFS_FHSIZE / sizeof (int); + register_fresh_stat (np, p); + mutex_unlock (&np->lock); + } + } + else + mutex_unlock (&dir->lock); + 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) + { + 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); + 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); + + break; + } + if (err) return err; - xdr_to_native_attrstat (reply); + mutex_lock (&np->lock); - if (reply->status != NFSV2_OK) - err = nfsv2err_to_hurderr (reply->status); + if (np->nn->dtrans == SYMLINK) + free (np->nn->transarg.name); + np->nn->dtrans = NOT_POSSIBLE; + + /* If there is a dead-dir tag lying around, it's time to delete it now. */ + if (np->nn->dead_dir) + { + struct node *dir = np->nn->dead_dir; + char *name = np->nn->dead_name; + 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); + mutex_unlock (&dir->lock); + } + else + mutex_unlock (&np->lock); + + return 0; +} + +/* Implement the netfs_attempt_mkfile callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_mkfile (struct netcred *cred, struct node *dir, + mode_t mode, struct node **newnp) +{ + error_t err; + char *name; + static int n = 0; + + /* This is the best we can do. */ + + name = malloc (50); + + do + { + sprintf (name, ".nfstmpgnu.%d", n++); + err = netfs_attempt_create_file (cred, dir, name, mode, newnp); + if (err == EEXIST) + mutex_lock (&dir->lock); + } + while (err == EEXIST); + + if (err) + { + free (name); + return err; + } + + assert (!(*newnp)->nn->dead_dir); + assert (!(*newnp)->nn->dead_name); + netfs_nref (dir); + (*newnp)->nn->dead_dir = dir; + (*newnp)->nn->dead_name = name; + if ((*newnp)->nn->dtrans == NOT_POSSIBLE) + (*newnp)->nn->dtrans = POSSIBLE; + return 0; +} + +/* Implement the netfs_attempt_create_file callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_create_file (struct netcred *cred, struct node *np, + char *name, mode_t mode, struct node **newnp) +{ + int *p; + void *rpcbuf; + error_t err; + + 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); + + 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); + } else + *newnp = 0; + + free (rpcbuf); + return err; +} + +/* Implement the netfs_attempt_unlink callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_unlink (struct netcred *cred, struct node *dir, + char *name) +{ + int *p; + void *rpcbuf; + error_t err; + struct node *np; + + /* First lookup the node being removed */ + err = netfs_attempt_lookup (cred, dir, name, &np); + if (err) + { + mutex_lock (&dir->lock); + return err; + } + + /* 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; + int n = 0; + + newname = malloc (50); + mutex_unlock (&np->lock); + do + { + sprintf (newname, ".nfs%xgnu.%d", (int) np, n++); + err = netfs_attempt_link (cred, dir, np, newname); + } + while (err == EEXIST); + + if (err) + { + free (newname); + mutex_lock (&dir->lock); + netfs_nrele (np); + return err; + } + + /* 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); + np->nn->dead_dir = dir; + if (np->nn->dead_name) + free (np->nn->dead_name); + np->nn->dead_name = newname; + if (np->nn->dtrans == NOT_POSSIBLE) + np->nn->dtrans = POSSIBLE; + } + netfs_nput (np); + + 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++)); + + free (rpcbuf); + + return err; +} + +/* Implement the netfs_attempt_rename callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_rename (struct netcred *cred, struct node *fromdir, + char *fromname, struct node *todir, char *toname) +{ + int *p; + void *rpcbuf; + error_t err; + + mutex_lock (&fromdir->lock); + p = nfs_initialize_rpc (NFSPROC_RENAME, cred, 0, &rpcbuf, fromdir, -1); + p = xdr_encode_fhandle (p, &fromdir->nn->handle); + p = xdr_encode_string (p, fromname); + mutex_unlock (&fromdir->lock); + + mutex_lock (&todir->lock); + 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++)); + + free (rpcbuf); + return err; +} + +/* Implement the netfs_attempt_readlink callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_readlink (struct netcred *cred, struct node *np, + char *buf) +{ + int *p; + void *rpcbuf; + error_t err; + + if (np->nn->dtrans == SYMLINK) { - np->nn_stat.st_mode = nfsv2mode_to_hurdmode (reply->attributes.type, - reply->attributes.mode); - np->nn_stat.st_nlink = reply->attributes.nlink; - np->nn_stat.st_uid = reply->attributes.uid; - np->nn_stat.st_gid = reply->attributes.gid; - np->nn_stat.st_size = reply->attributes.size; - np->nn_stat.st_blksize = reply->attributes.blocksize; - np->nn_stat.st_rdev = reply->attributes.rdev; - np->nn_stat.st_blocks = reply->attributes.blocks; - np->nn_stat.st_fstype = FSTYPE_NFS; - np->nn_stat.st_fsid = reply->attributes.fsid; /* surely wrong XXX */ - np->nn_stat.st_fileid = reply->attributes.fileid; - np->nn_stat.st_atime = reply->attributes.atime.seconds; - np->nn_stat.st_atime_usec = reply->attributes.atime.useconds; - np->nn_stat.st_mtime = reply->attributes.mtime.seconds; - np->nn_stat.st_mtime_usec = reply->attributes.mtime.useconds; - np->nn_stat.st_ctime = reply->attributes.ctime.seconds; - np->nn_stat.st_ctime_usec = reply->attributes.ctime.useconds; - /* Deal with other values appropriately? */ - - err = 0; - } - - deallocate_reply_buffer (reply); + strcpy (buf, np->nn->transarg.name); + return 0; + } + + p = nfs_initialize_rpc (NFSPROC_READLINK, cred, 0, &rpcbuf, np, -1); + 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); + + free (rpcbuf); return err; } +/* 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, + 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 + here. So instead what we do is try and do what the user + requested; if we can't, then we fail. Otherwise, we allow the + open, but acknowledge that the server might still give an error + later. (Even with our check, the server can revoke access, thus + violiting Posix semantics; this means that erring on the side of + permitting illegal opens won't harm otherwise correct programs, + because they need to deal with revocation anyway.) We thus here + have the advantage of working correctly with servers that allow + things Unix denies. */ + + if ((flags & O_READ) == 0 + && (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); + if (err) + { + if ((flags & O_READ) || (flags & O_EXEC)) + return err; + else + /* 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); + return err; + } + + /* Try as we might, we couldn't get the server to bump us, so + give (provisional) success. */ + return 0; + + case S_IFDIR: + if (flags & O_READ) + { + void *rpcbuf; + int *p; + + /* Issue a readdir request; if it fails, then we can + 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); + err = conduct_rpc (&rpcbuf, &p); + if (!err) + err = nfs_error_trans (ntohl (*p++)); + free (rpcbuf); + + if (err) + return err; + } + return 0; + } +} + +/* Implement the netfs_report_access callback as described in + <hurd/netfs.h>. */ +void +netfs_report_access (struct netcred *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. */ + + + err = netfs_attempt_read (cred, np, 0, &len, &byte); + if (err) + { + *types = 0; + return; + } + + *types = O_READ | O_EXEC; + + if (len == 1) + { + err = netfs_attempt_write (cred, np, 0, &len, &byte); + if (!err) + *types |= O_WRITE; + } + else + { + /* Oh, ugh. We have to try and write a byte and then truncate + back. God help us if the file becomes unwritable in-between. + But because of the use of this function (by setuid programs + wanting to see if they should write user's files) we must + check this and not just return a presumptive error. */ + byte = 0; + err = netfs_attempt_write (cred, np, 0, &len, &byte); + if (!err) + *types |= O_WRITE; + netfs_attempt_set_size (cred, np, 0); + } +} + +/* Fetch the complete contents of DIR into a buffer of directs. Set + *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. */ +static error_t +fetch_directory (struct netcred *cred, struct node *dir, + void **bufp, size_t *bufsizep, int *totalentries) +{ + void *buf; + int cookie; + int *p; + void *rpcbuf; + struct dirent *entry; + void *bp; + int bufmalloced; + int eof; + error_t err; + int isnext; + + bufmalloced = read_size; + buf = malloc (bufmalloced); + bp = buf; + cookie = 0; + eof = 0; + *totalentries = 0; + + while (!eof) + { + /* Fetch new directory entries */ + p = nfs_initialize_rpc (NFSPROC_READDIR, cred, 0, &rpcbuf, dir, -1); + p = xdr_encode_fhandle (p, &dir->nn->handle); + *p++ = cookie; + *p++ = ntohl (read_size); + err = conduct_rpc (&rpcbuf, &p); + if (!err) + err = nfs_error_trans (ntohl (*p++)); + if (err) + { + free (buf); + return err; + } + + isnext = ntohl (*p++); + + /* Now copy them one at a time. */ + while (isnext) + { + ino_t fileno; + int namlen; + int reclen; + + fileno = ntohl (*p++); + namlen = ntohl (*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); + 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); + entry->d_name[namlen] = '\0'; + + p += INTSIZE (namlen); + bp = bp + entry->d_reclen; + + ++*totalentries; + + cookie = *p++; + isnext = ntohl (*p++); + } + + eof = ntohl (*p++); + free (rpcbuf); + } + + /* Return it all to the user */ + *bufp = buf; + *bufsizep = bufmalloced; + return 0; +} + + +/* Implement the netfs_get_directs callback as described in + <hurd/netfs.h>. */ +error_t +netfs_get_dirents (struct netcred *cred, struct node *np, + int entry, int nentries, char **data, + mach_msg_type_number_t *datacnt, + vm_size_t bufsiz, int *amt) +{ + void *buf; + size_t our_bufsiz, allocsize; + void *bp; + char *userdp; + error_t err; + int totalentries; + int thisentry; + + 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); + + /* Skip ahead to the correct entry. */ + bp = buf; + for (thisentry = 0; thisentry < entry;) + { + struct dirent *entry = (struct dirent *) bp; + bp += entry->d_reclen; + thisentry++; + } + + /* Now copy them one at a time */ + { + int entries_copied; + + 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); + bp += entry->d_reclen; + userdp += entry->d_reclen; + entries_copied++; + thisentry++; + } + *amt = entries_copied; + } + + free (buf); + + /* 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)); + + *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>. */ +error_t +netfs_attempt_mksymlink (struct netcred *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; + np->nn->stat_updated = 0; + return 0; +} + +/* Implement the netfs_attempt_mkdev callback as described in + <hurd/netfs.h>. */ +error_t +netfs_attempt_mkdev (struct netcred *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; + else + np->nn->dtrans = CHRDEV; + np->nn->stat_updated = 0; + return 0; +} + + @@ -1,5 +1,5 @@ -/* - Copyright (C) 1994 Free Software Foundation +/* SunRPC management for NFS client + Copyright (C) 1994, 1995, 1996 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 @@ -15,116 +15,370 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "nfs.h" +/* Needed in order to get the RPC header files to include correctly */ +#undef TRUE +#undef FALSE +#include <rpc/types.h> +#include <rpc/xdr.h> +#include <rpc/auth.h> +#include <rpc/rpc_msg.h> +#include <rpc/auth_unix.h> + +#include <netinet/in.h> +#include <assert.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> + +/* One of these exists for each pending RPC. */ struct rpc_list { struct rpc_list *next, **prevp; - - void *rpc; - size_t rpc_len; void *reply; - size_t reply_buflen; - size_t reply_len; - int reply_received; }; +/* A list of all the pending RPCs. */ static struct rpc_list *outstanding_rpcs; -static struct mutex outstanding_lock = MUTEX_INITIALIZER; + +/* 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. */ static struct condition rpc_wakeup = CONDITION_INITIALIZER; -/* Send an RPC message to target TARGET from buffer BUF of size - LEN, waiting for a reply. The expected reply length is - REPLY_LEN; a buffer that big is provided as REPLY_BUF. - Set REPLY_CONTENTS to the actual protocol-level reply, of - size REPLY_CONTENTS_LEN. AUTH is the authentication used - in constructing BUF.*/ -error_t -rpc_send (struct rpc_target *target, void *buf, size_t len, - void *reply_buf, size_t reply_len, void **reply_contents, - size_t *reply_contents_len, struct rpc_auth *auth) +/* This lock must be held around modifications of the REPLY members of + records on outstanding_rpcs and around uses of rpc_wakeup. */ +static struct mutex outstanding_lock = MUTEX_INITIALIZER; + + + +/* Generate and return a new transaction ID. */ +static int +generate_xid () { - struct rpc_list record; - struct rpcv2_msg_hdr *reply; - struct rpcv2_msg_hdr *request; - struct rpcv2_accepted_reply *accept; - struct rpcv2_rejected_reply *reject; - struct rpcv2_auth *authdata; - struct rpcv2_verf *verf; - error_t error; - - mutex_lock (&outstanding_lock); - - record.rpc = request = buf; - record.len = len; - record.reply = reply_buf; - record.reply_len = reply_len; - record.reply_received = 0; - - record.next = outstanding_rpcs; - if (record.next) - record.next->prevp = &record.next; - outstanding_rpcs = &record; - record.prevp = &outstanding_rpcs; - - rpc_transmit (target, &record); - - while (!record.reply_received) - condition_wait (&rpc_wakeup, &outstanding_lock); + static int nextxid; - /* Examine the reply to see whether any RPC level errors have happened. */ + if (nextxid == 0) + nextxid = mapped_time->seconds; - reply = record.reply; + return nextxid++; +} + +/* 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.) */ +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); + int *p, *lenaddr; + struct rpc_list *hdr; + + /* First the struct rpc_list bit. */ + hdr = buf; + hdr->reply = 0; - assert (reply->xid == request->xid); - assert (reply->mtype == RPCV2_REPLY); + 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); - if (reply->body.reply.reply_state == RPCV2_MSG_ACCEPTED) - { - /* Ignore VERF */ - verf = &reply->body.reply.rest; - accept = (void *)verf + sizeof (struct rpcv2_verf) + verf->nbytes; + assert ((uid == -1) == (gid == -1)); - /* The message was accepted; return the appropriate error - (or success) to the caller. */ - if (accept->stat == RPCV2_SUCCESS) + if (uid != -1) + { + *p++ = htonl (AUTH_UNIX); + lenaddr = p++; + *p++ = htonl (mapped_time->seconds); + p = xdr_encode_string (p, hostname); + *p++ = htonl (uid); + *p++ = htonl (gid); + if (second_gid != -1) { - /* Ah hah! Return to the user */ - *reply_contents = &accept->reply_data.results; - *reply_contents_len = record.reply_len; - *reply_contents_len -= ((void *)&accept->reply_data.result - - (void *) reply); - error = 0; + *p++ = htonl (1); + *p++ = htonl (second_gid); } else - error = EOPNOTSUPP; + *p++ = 0; + *lenaddr = htonl ((p - (lenaddr + 1)) * sizeof (int)); } - else if (reply->body.reply.reply_state == RPCV2_MSG_DENIED) + else { - /* For some reason we didn't win. */ - reject = &reply->body.reply.rest; - if (reject->stat == RPCV2_RPC_MISMATCH) - error = EOPNOTSUPP; - else if (reject->stat == RPCV2_AUTH_ERROR) - error = EACCES; - else - error = EIEIO; + *p++ = htonl (AUTH_NULL); + *p++ = 0; } - else - error = EIEIO; + + /* VERF field */ + *p++ = htonl (AUTH_NULL); + *p++ = 0; + + *bufp = buf; + return p; +} + +/* Remove HDR from the list of pending RPC's. */ +static 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); +} + +/* 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. */ +error_t +conduct_rpc (void **rpcbuf, int **pp) +{ + struct rpc_list *hdr = *rpcbuf; + error_t err; + size_t cc, nc; + int timeout = initial_transmit_timeout; + time_t lasttrans; + int ntransmit = 0; + int *p; + int xid; + 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); + + xid = * (int *) (*rpcbuf + sizeof (struct rpc_list)); + + do + { + /* If we've sent enough, give up */ + if (mounted_soft && ntransmit == soft_retries) + { + unlink_rpc (hdr); + return ETIMEDOUT; + } + + /* Issue the RPC */ + mutex_lock (&outstanding_lock); + 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); + else + assert (cc == nc); + + /* 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); + return EINTR; + } + + if (!hdr->reply) + { + timeout *=2; + if (timeout > max_transmit_timeout) + timeout = max_transmit_timeout; + } + } + while (!hdr->reply); + + /* 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. */ + p = (int *) *rpcbuf; + + assert (*p == xid); + p++; - *record.prevp = record.next; - if (record.next) - record.next->prevp = record.prevp; + switch (ntohl (*p++)) + { + default: + err = EBADRPC; + break; - mutex_unlock (&outstanding_lock); - return error; + case REPLY: + switch (ntohl (*p++)) + { + default: + err = EBADRPC; + break; + + case MSG_DENIED: + switch (ntohl (*p++)) + { + default: + err = EBADRPC; + break; + + case RPC_MISMATCH: + err = ERPCMISMATCH; + break; + + case AUTH_ERROR: + switch (ntohl (*p++)) + { + case AUTH_BADCRED: + case AUTH_REJECTEDCRED: + err = EAUTH; + break; + + case AUTH_TOOWEAK: + err = ENEEDAUTH; + break; + + case AUTH_BADVERF: + case AUTH_REJECTEDVERF: + default: + err = EBADRPC; + break; + } + break; + } + break; + + case MSG_ACCEPTED: + + /* Process VERF field. */ + p++; /* skip verf type */ + n = ntohl (*p++); /* size of verf */ + p += INTSIZE (n); /* skip verf itself */ + + switch (ntohl (*p++)) + { + default: + case GARBAGE_ARGS: + err = EBADRPC; + break; + + case PROG_UNAVAIL: + err = EPROGUNAVAIL; + break; + + case PROG_MISMATCH: + err = EPROGMISMATCH; + break; + + case PROC_UNAVAIL: + err = EPROCUNAVAIL; + break; + + case SUCCESS: + *pp = p; + err = 0; + break; + } + break; + } + break; + } + return err; +} + +/* Dedicated thread to wakeup rpc_wakeup once a second. */ +void +timeout_service_thread () +{ + while (1) + { + sleep (1); + mutex_lock (&outstanding_lock); + condition_broadcast (&rpc_wakeup); + mutex_unlock (&outstanding_lock); + } } +/* Dedicate thread to receive RPC replies, register them on the queue + of pending wakeups, and deal appropriately. */ void -rpc_transmit (struct rpc_target *target, struct rpc_list *record) +rpc_receive_thread () { - static int xid; + int cc; + void *buf; + struct rpc_list *r; + int xid; - /* Assign a unique transaction ID */ - record->rpc + 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; + } + else + { + 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); + } + } + } + while (!r); + } +} + + + + |