summaryrefslogtreecommitdiff
path: root/nfs
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1996-03-19 18:59:34 +0000
committerMichael I. Bushnell <mib@gnu.org>1996-03-19 18:59:34 +0000
commit80c937736e978a94dd3193fa2af9b2c0b2ad9c04 (patch)
treef90060caf10f2f4fa3e828a7bb0297c6e9cd167f /nfs
parent3bd51e2e4bbbe978424d1d4492a54d56cee22bca (diff)
*** empty log message ***
Diffstat (limited to 'nfs')
-rw-r--r--nfs/Makefile12
-rw-r--r--nfs/nfs.h159
-rw-r--r--nfs/ops.c1225
-rw-r--r--nfs/rpc.c424
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
diff --git a/nfs/nfs.h b/nfs/nfs.h
index 80f621c1..0b52f68a 100644
--- a/nfs/nfs.h
+++ b/nfs/nfs.h
@@ -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 *);
diff --git a/nfs/ops.c b/nfs/ops.c
index 824affe7..07963f87 100644
--- a/nfs/ops.c
+++ b/nfs/ops.c
@@ -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;
+}
+
+
diff --git a/nfs/rpc.c b/nfs/rpc.c
index dc1e1b92..348c5c98 100644
--- a/nfs/rpc.c
+++ b/nfs/rpc.c
@@ -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);
+ }
+}
+
+
+
+