diff options
Diffstat (limited to 'nfs/rpc.c')
-rw-r--r-- | nfs/rpc.c | 251 |
1 files changed, 145 insertions, 106 deletions
@@ -1,5 +1,5 @@ -/* SunRPC management for NFS client - Copyright (C) 1994, 1995, 1996 Free Software Foundation +/* rpc.c - SunRPC management for NFS client. + Copyright (C) 1994, 1995, 1996, 1997, 2002 Free Software Foundation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -17,10 +17,10 @@ #include "nfs.h" -/* Needed in order to get the RPC header files to include correctly */ +/* Needed in order to get the RPC header files to include correctly. */ #undef TRUE #undef FALSE -#define malloc spoiufasdf /* Avoid bogus definition in rpc/types.h */ +#define malloc spoiufasdf /* Avoid bogus definition in rpc/types.h. */ #include <rpc/types.h> #include <rpc/xdr.h> @@ -28,35 +28,36 @@ #include <rpc/rpc_msg.h> #include <rpc/auth_unix.h> -#undef malloc /* Get rid protection. */ +#undef malloc /* Get rid of the sun block. */ #include <netinet/in.h> #include <assert.h> #include <errno.h> +#include <error.h> #include <unistd.h> #include <stdio.h> -/* One of these exists for each pending RPC. */ +/* One of these exists for each pending RPC. */ struct rpc_list { struct rpc_list *next, **prevp; void *reply; }; -/* A list of all the pending RPCs. */ +/* A list of all pending RPCs. */ static struct rpc_list *outstanding_rpcs; /* Wake up this condition when an outstanding RPC has received a reply - or we should check for timeouts. */ + or we should check for timeouts. */ static struct condition rpc_wakeup = CONDITION_INITIALIZER; -/* Lock the global data and the REPLY fields of outstanding RPC's. */ +/* Lock the global data and the REPLY fields of outstanding RPC's. */ static struct mutex outstanding_lock = MUTEX_INITIALIZER; -/* Generate and return a new transaction ID. */ -static int +/* Generate and return a new transaction ID. */ +static inline int generate_xid () { static int nextxid; @@ -67,22 +68,31 @@ generate_xid () return nextxid++; } -/* Set up an RPC for procdeure RPC_PROC, for talking to the server +/* Set up an RPC for procdeure RPC_PROC for talking to the server PROGRAM of version VERSION. Allocate storage with malloc and point *BUF at it; caller must free this when done. Allocate at least LEN - bytes more than the usual amount for an RPC. Initialize the RPC - credential structure with UID, GID, and SECOND_GID. (Any of those - may be -1 to indicate that it does not apply; exactly or two of UID - and GID must be -1, however.) */ + bytes more than the usual amount for the RPC. Initialize the RPC + credential structure with UID, GID, and SECOND_GID; any of these + may be -1 to indicate that it does not apply, however, exactly zero + or two of UID and GID must be -1. The returned address is a pointer + to the start of the payload. If NULL is returned, an error occurred + and the code is set in errno. */ int * initialize_rpc (int program, int version, int rpc_proc, size_t len, void **bufp, uid_t uid, gid_t gid, gid_t second_gid) { - void *buf = malloc (len + 1024); + void *buf; int *p, *lenaddr; struct rpc_list *hdr; + buf = malloc (len + 1024); + if (! buf) + { + errno = ENOMEM; + return NULL; + } + /* First the struct rpc_list bit. */ hdr = buf; hdr->reply = 0; @@ -90,49 +100,53 @@ initialize_rpc (int program, int version, int rpc_proc, p = buf + sizeof (struct rpc_list); /* RPC header */ - *p++ = htonl (generate_xid ()); - *p++ = htonl (CALL); - *p++ = htonl (RPC_MSG_VERSION); - *p++ = htonl (program); - *p++ = htonl (version); - *p++ = htonl (rpc_proc); + *(p++) = htonl (generate_xid ()); + *(p++) = htonl (CALL); + *(p++) = htonl (RPC_MSG_VERSION); + *(p++) = htonl (program); + *(p++) = htonl (version); + *(p++) = htonl (rpc_proc); assert ((uid == -1) == (gid == -1)); - if (uid != -1) + if (uid == -1) { - *p++ = htonl (AUTH_UNIX); + /* No authentication */ + *(p++) = htonl (AUTH_NONE); + *(p++) = 0; + } + else + { + /* Unixy authentication */ + *(p++) = htonl (AUTH_UNIX); + /* The length of the message. We do not yet know what this + is, so, just remember where we should put it when we know */ lenaddr = p++; - *p++ = htonl (mapped_time->seconds); + *(p++) = htonl (mapped_time->seconds); p = xdr_encode_string (p, hostname); - *p++ = htonl (uid); - *p++ = htonl (gid); - if (second_gid != -1) + *(p++) = htonl (uid); + *(p++) = htonl (gid); + if (second_gid == -1) + *(p++) = 0; + else { - *p++ = htonl (1); - *p++ = htonl (second_gid); + *(p++) = htonl (1); + *(p++) = htonl (second_gid); } - else - *p++ = 0; *lenaddr = htonl ((p - (lenaddr + 1)) * sizeof (int)); } - else - { - *p++ = htonl (AUTH_NULL); - *p++ = 0; - } /* VERF field */ - *p++ = htonl (AUTH_NULL); - *p++ = 0; + *(p++) = htonl (AUTH_NONE); + *(p++) = 0; *bufp = buf; return p; } -/* Remove HDR from the list of pending RPC's. rpc_list_lock must be - held */ -static void +/* Remove HDR from the list of pending RPC's. The rpc_list's lock + (OUTSTANDING_LOCK) must be held. */ +static inline void unlink_rpc (struct rpc_list *hdr) { *hdr->prevp = hdr->next; @@ -140,12 +154,24 @@ unlink_rpc (struct rpc_list *hdr) hdr->next->prevp = hdr->prevp; } +/* Insert HDR at the head of the LIST. The rpc_list's lock + (OUTSTANDING_LOCK) must be held. */ +static inline void +link_rpc (struct rpc_list **list, struct rpc_list *hdr) +{ + hdr->next = *list; + if (hdr->next) + hdr->next->prevp = &hdr->next; + hdr->prevp = list; + *list = hdr; +} + /* Send the specified RPC message. *RPCBUF is the initialized buffer - from a previous initialize_rpc call; *PP points past the filled - in args. Set *PP to the address of the reply contents themselves. - The user will be expected to free *RPCBUF (which will have changed) - when done with the reply contents. The old value of *RPCBUF will - be freed by this routine. */ + from a previous initialize_rpc call; *PP, the payload, points past + the filledin args. Set *PP to the address of the reply contents + themselves. The user will be expected to free *RPCBUF (which will + have changed) when done with the reply contents. The old value of + *RPCBUF will be freed by this routine. */ error_t conduct_rpc (void **rpcbuf, int **pp) { @@ -162,18 +188,13 @@ conduct_rpc (void **rpcbuf, int **pp) mutex_lock (&outstanding_lock); - /* Link it in */ - hdr->next = outstanding_rpcs; - if (hdr->next) - hdr->next->prevp = &hdr->next; - hdr->prevp = &outstanding_rpcs; - outstanding_rpcs = hdr; + link_rpc (&outstanding_rpcs, hdr); xid = * (int *) (*rpcbuf + sizeof (struct rpc_list)); do { - /* If we've sent enough, give up */ + /* If we've sent enough, give up. */ if (mounted_soft && ntransmit == soft_retries) { unlink_rpc (hdr); @@ -181,7 +202,7 @@ conduct_rpc (void **rpcbuf, int **pp) return ETIMEDOUT; } - /* Issue the RPC */ + /* Issue the RPC. */ lasttrans = mapped_time->seconds; ntransmit++; nc = (void *) *pp - *rpcbuf - sizeof (struct rpc_list); @@ -195,7 +216,7 @@ conduct_rpc (void **rpcbuf, int **pp) else assert (cc == nc); - /* Wait for reply */ + /* Wait for reply. */ cancel = 0; while (!hdr->reply && (mapped_time->seconds - lasttrans < timeout) @@ -209,9 +230,12 @@ conduct_rpc (void **rpcbuf, int **pp) return EINTR; } + /* hdr->reply will have been filled in by rpc_receive_thread, + if it has been filled in, then the rpc has been fulfilled, + otherwise, retransmit and continue to wait. */ if (!hdr->reply) { - timeout *=2; + timeout *= 2; if (timeout > max_transmit_timeout) timeout = max_transmit_timeout; } @@ -220,32 +244,36 @@ conduct_rpc (void **rpcbuf, int **pp) mutex_unlock (&outstanding_lock); - /* Switch to the reply buffer. */ + /* Switch to the reply buffer. */ *rpcbuf = hdr->reply; free (hdr); - /* Process the reply, dissecting errors. When we're done, set *PP to - the rpc return contents, if there is no error. */ + /* Process the reply, dissecting errors. When we're done and if + there is no error, set *PP to the rpc return contents. */ p = (int *) *rpcbuf; + /* If the transmition id does not match that in the message, + something strange happened in rpc_receive_thread. */ assert (*p == xid); p++; - switch (ntohl (*p++)) + switch (ntohl (*p)) { default: err = EBADRPC; break; case REPLY: - switch (ntohl (*p++)) + p++; + switch (ntohl (*p)) { default: err = EBADRPC; break; case MSG_DENIED: - switch (ntohl (*p++)) + p++; + switch (ntohl (*p)) { default: err = EBADRPC; @@ -256,7 +284,8 @@ conduct_rpc (void **rpcbuf, int **pp) break; case AUTH_ERROR: - switch (ntohl (*p++)) + p++; + switch (ntohl (*p)) { case AUTH_BADCRED: case AUTH_REJECTEDCRED: @@ -278,13 +307,15 @@ conduct_rpc (void **rpcbuf, int **pp) break; case MSG_ACCEPTED: + p++; - /* Process VERF field. */ - p++; /* skip verf type */ - n = ntohl (*p++); /* size of verf */ - p += INTSIZE (n); /* skip verf itself */ + /* Process VERF field. */ + p++; /* Skip verf type. */ + n = ntohl (*p); /* Size of verf. */ + p++; + p += INTSIZE (n); /* Skip verf itself. */ - switch (ntohl (*p++)) + switch (ntohl (*p)) { default: case GARBAGE_ARGS: @@ -304,6 +335,7 @@ conduct_rpc (void **rpcbuf, int **pp) break; case SUCCESS: + p++; *pp = p; err = 0; break; @@ -315,7 +347,8 @@ conduct_rpc (void **rpcbuf, int **pp) return err; } -/* Dedicated thread to wakeup rpc_wakeup once a second. */ +/* Dedicated thread to signal those waiting on rpc_wakeup + once a second. */ void timeout_service_thread () { @@ -329,50 +362,56 @@ timeout_service_thread () } /* Dedicate thread to receive RPC replies, register them on the queue - of pending wakeups, and deal appropriately. */ + of pending wakeups, and deal appropriately. */ void rpc_receive_thread () { - int cc; void *buf; - struct rpc_list *r; - int xid; + + /* Allocate a receive buffer. */ + buf = malloc (1024 + read_size); + assert (buf); while (1) { - buf = malloc (1024 + read_size); - - do - { - cc = read (main_udp_socket, buf, 1024 + read_size); - if (cc == -1) - { - perror ("nfs read"); - r = 0; + int cc = read (main_udp_socket, buf, 1024 + read_size); + if (cc == -1) + { + error (0, errno, "nfs read"); + continue; + } + else + { + struct rpc_list *r; + int xid = *(int *)buf; + + mutex_lock (&outstanding_lock); + + /* Find the rpc that we just fulfilled. */ + for (r = outstanding_rpcs; r; r = r->next) + { + if (* (int *) &r[1] == xid) + { + unlink_rpc (r); + r->reply = buf; + condition_broadcast (&rpc_wakeup); + break; + } } - else +#if 0 + if (! r) + fprintf (stderr, "NFS dropping reply xid %d\n", xid); +#endif + mutex_unlock (&outstanding_lock); + + /* If r is not null then we had a message from a pending + (i.e. known) rpc. Thus, it was fulfilled and if we want + to get another request, a new buffer is needed. */ + if (r) { - xid = *(int *)buf; - mutex_lock (&outstanding_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; - - r->reply = buf; - condition_broadcast (&rpc_wakeup); - break; - } - } - if (!r) - fprintf (stderr, "NFS dropping reply xid %d\n", xid); - mutex_unlock (&outstanding_lock); + buf = malloc (1024 + read_size); + assert (buf); } - } - while (!r); + } } } |