summaryrefslogtreecommitdiff
path: root/nfs/rpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'nfs/rpc.c')
-rw-r--r--nfs/rpc.c424
1 files changed, 339 insertions, 85 deletions
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);
+ }
+}
+
+
+
+