aboutsummaryrefslogtreecommitdiff
path: root/nuttx/fs/nfs/rpc_clnt.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/fs/nfs/rpc_clnt.c')
-rw-r--r--nuttx/fs/nfs/rpc_clnt.c807
1 files changed, 807 insertions, 0 deletions
diff --git a/nuttx/fs/nfs/rpc_clnt.c b/nuttx/fs/nfs/rpc_clnt.c
new file mode 100644
index 000000000..0e2a394ba
--- /dev/null
+++ b/nuttx/fs/nfs/rpc_clnt.c
@@ -0,0 +1,807 @@
+/****************************************************************************
+ * fs/nfs/rpc_clnt.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2012 Jose Pablo Rojas Vargas. All rights reserved.
+ * Author: Jose Pablo Rojas Vargas <jrojas@nx-engineering.com>
+ * Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Leveraged from OpenBSD:
+ *
+ * Copyright (c) 2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Copyright (c) 2004 Weston Andros Adamson <muzzle@umich.edu>.
+ * Copyright (c) 2004 Marius Aamodt Eriksen <marius@umich.edu>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Copyright (c) 1989, 1991, 1993, 1995 The Regents of the University of
+ * California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by Rick Macklem at
+ * The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution. 3. All advertising
+ * materials mentioning features or use of this software must display the
+ * following acknowledgement: This product includes software developed by the
+ * University of California, Berkeley and its contributors. 4. Neither the
+ * name of the University nor the names of its contributors may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <sys/socket.h>
+#include <queue.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <debug.h>
+#include <nuttx/kmalloc.h>
+
+#include "xdr_subs.h"
+#include "nfs_proto.h"
+#include "rpc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Increment RPC statistics */
+
+#ifdef CONFIG_NFS_STATISTICS
+# define rpc_statistics(n) do { rpcstats.(n)++; } while (0)
+#else
+# define rpc_statistics(n)
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Static data, mostly RPC constants in XDR form */
+
+static uint32_t rpc_reply;
+static uint32_t rpc_call;
+static uint32_t rpc_vers;
+static uint32_t rpc_msgdenied;
+static uint32_t rpc_mismatch;
+static uint32_t rpc_auth_unix;
+static uint32_t rpc_msgaccepted;
+static uint32_t rpc_autherr;
+static uint32_t rpc_auth_null;
+
+/* Global statics for all client instances. Cleared by NuttX on boot-up. */
+
+#ifdef CONFIG_NFS_STATISTICS
+static struct rpcstats rpcstats;
+#endif
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int rpcclnt_send(FAR struct rpcclnt *rpc, int procid, int prog,
+ FAR void *call, int reqlen);
+static int rpcclnt_receive(FAR struct rpcclnt *rpc, struct sockaddr *aname,
+ int proc, int program, void *reply, size_t resplen);
+static int rpcclnt_reply(FAR struct rpcclnt *rpc, int procid, int prog,
+ void *reply, size_t resplen);
+static uint32_t rpcclnt_newxid(void);
+static void rpcclnt_fmtheader(FAR struct rpc_call_header *ch,
+ uint32_t xid, int procid, int prog, int vers);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rpcclnt_send
+ *
+ * Description:
+ * This is the nfs send routine.
+ *
+ * Returned Value:
+ * Returns zero on success or a (positive) errno value on failure.
+ *
+ ****************************************************************************/
+
+static int rpcclnt_send(FAR struct rpcclnt *rpc, int procid, int prog,
+ FAR void *call, int reqlen)
+{
+ ssize_t nbytes;
+ int error = OK;
+
+ /* Send the call message
+ *
+ * On success, psock_sendto returns the number of bytes sent;
+ * On failure, it returns -1 with the specific error in errno.
+ */
+
+ nbytes = psock_sendto(rpc->rc_so, call, reqlen, 0,
+ rpc->rc_name, sizeof(struct sockaddr));
+ if (nbytes < 0)
+ {
+ /* psock_sendto failed */
+
+ error = errno;
+ fdbg("ERROR: psock_sendto failed: %d\n", error);
+ }
+
+ return error;
+}
+
+/****************************************************************************
+ * Name: rpcclnt_receive
+ *
+ * Description:
+ * Receive a Sun RPC Request/Reply. For SOCK_DGRAM, the work is all done
+ * by psock_recvfrom().
+ *
+ ****************************************************************************/
+
+static int rpcclnt_receive(FAR struct rpcclnt *rpc, FAR struct sockaddr *aname,
+ int proc, int program, FAR void *reply,
+ size_t resplen)
+{
+ ssize_t nbytes;
+ int error = 0;
+
+ socklen_t fromlen = sizeof(struct sockaddr);
+ nbytes = psock_recvfrom(rpc->rc_so, reply, resplen, 0, aname, &fromlen);
+ if (nbytes < 0)
+ {
+ error = errno;
+ fdbg("ERROR: psock_recvfrom failed: %d\n", error);
+ }
+
+ return error;
+}
+
+/****************************************************************************
+ * Name: rpcclnt_reply
+ *
+ * Description:
+ * Received the RPC reply on the socket.
+ *
+ ****************************************************************************/
+
+static int rpcclnt_reply(FAR struct rpcclnt *rpc, int procid, int prog,
+ FAR void *reply, size_t resplen)
+{
+ FAR struct rpc_reply_header *replyheader;
+ uint32_t rxid;
+ int error;
+
+ /* Get the next RPC reply from the socket */
+
+ error = rpcclnt_receive(rpc, rpc->rc_name, procid, prog, reply, resplen);
+ if (error != 0)
+ {
+ fdbg("ERROR: rpcclnt_receive returned: %d\n", error);
+
+ /* If we failed because of a timeout, then try sending the CALL
+ * message again.
+ */
+
+ if (error == EAGAIN || error == ETIMEDOUT)
+ {
+ rpc->rc_timeout = true;
+ }
+ }
+
+ /* Get the xid and check that it is an RPC replysvr */
+
+ else
+ {
+ replyheader = (FAR struct rpc_reply_header *)reply;
+ rxid = replyheader->rp_xid;
+
+ if (replyheader->rp_direction != rpc_reply)
+ {
+ fdbg("ERROR: Different RPC REPLY returned\n");
+ rpc_statistics(rpcinvalid);
+ error = EPROTO;
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: rpcclnt_newxid
+ *
+ * Description:
+ * Get a new (non-zero) xid
+ *
+ ****************************************************************************/
+
+static uint32_t rpcclnt_newxid(void)
+{
+ static uint32_t rpcclnt_xid = 0;
+ static uint32_t rpcclnt_xid_touched = 0;
+ int xidp = 0;
+
+ srand(time(NULL));
+ if ((rpcclnt_xid == 0) && (rpcclnt_xid_touched == 0))
+ {
+ rpcclnt_xid = rand();
+ rpcclnt_xid_touched = 1;
+ }
+ else
+ {
+ do
+ {
+ xidp = rand();
+ }
+ while ((xidp % 256) == 0);
+
+ rpcclnt_xid += xidp;
+ }
+
+ return rpcclnt_xid;
+}
+
+/****************************************************************************
+ * Name: rpcclnt_fmtheader
+ *
+ * Description:
+ * Format the common part of the call header
+ *
+ ****************************************************************************/
+
+static void rpcclnt_fmtheader(FAR struct rpc_call_header *ch,
+ uint32_t xid, int prog, int vers, int procid)
+{
+ /* Format the call header */
+
+ ch->rp_xid = txdr_unsigned(xid);
+ ch->rp_direction = rpc_call;
+ ch->rp_rpcvers = rpc_vers;
+ ch->rp_prog = txdr_unsigned(prog);
+ ch->rp_vers = txdr_unsigned(vers);
+ ch->rp_proc = txdr_unsigned(procid);
+
+ /* rpc_auth part (auth_null) */
+
+ ch->rpc_auth.authtype = rpc_auth_null;
+ ch->rpc_auth.authlen = 0;
+
+ /* rpc_verf part (auth_null) */
+
+ ch->rpc_verf.authtype = rpc_auth_null;
+ ch->rpc_verf.authlen = 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rpcclnt_init
+ *
+ * Description:
+ * Initialize the RPC client
+ *
+ ****************************************************************************/
+
+void rpcclnt_init(void)
+{
+ /* RPC constants how about actually using more than one of these! */
+
+ rpc_reply = txdr_unsigned(RPC_REPLY);
+ rpc_vers = txdr_unsigned(RPC_VER2);
+ rpc_call = txdr_unsigned(RPC_CALL);
+ rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
+ rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
+ rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
+ rpc_autherr = txdr_unsigned(RPC_AUTHERR);
+ rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
+ rpc_auth_null = txdr_unsigned(RPCAUTH_NULL);
+
+ fvdbg("RPC initialized\n");
+}
+
+/****************************************************************************
+ * Name: rpcclnt_connect
+ *
+ * Description:
+ * Initialize sockets for a new RPC connection. We do not free the
+ * sockaddr if an error occurs.
+ *
+ ****************************************************************************/
+
+int rpcclnt_connect(struct rpcclnt *rpc)
+{
+ struct socket *so;
+ int error;
+ struct sockaddr *saddr;
+ struct sockaddr_in sin;
+ struct sockaddr_in *sa;
+
+ union
+ {
+ struct rpc_call_pmap sdata;
+ struct rpc_call_mount mountd;
+ } request;
+
+ union
+ {
+ struct rpc_reply_pmap rdata;
+ struct rpc_reply_mount mdata;
+ } response;
+
+ struct timeval tv;
+ uint16_t tport;
+ int errval;
+
+ fvdbg("Connecting\n");
+
+ /* Create the socket */
+
+ saddr = rpc->rc_name;
+
+ /* Create an instance of the socket state structure */
+
+ so = (struct socket *)kzalloc(sizeof(struct socket));
+ if (!so)
+ {
+ fdbg("ERROR: Failed to allocate socket structure\n");
+ return ENOMEM;
+ }
+
+ error = psock_socket(saddr->sa_family, rpc->rc_sotype, IPPROTO_UDP, so);
+ if (error < 0)
+ {
+ errval = errno;
+ fdbg("ERROR: psock_socket failed: %d", errval);
+ return error;
+ }
+
+ so->s_crefs = 1;
+ rpc->rc_so = so;
+
+ /* Always set receive timeout to detect server crash and reconnect.
+ * Otherwise, we can get stuck in psock_receive forever.
+ */
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ error = psock_setsockopt(rpc->rc_so, SOL_SOCKET, SO_RCVTIMEO,
+ (const void *)&tv, sizeof(tv));
+ if (error < 0)
+ {
+ errval = errno;
+ fdbg("ERROR: psock_setsockopt failed: %d\n", errval);
+ goto bad;
+ }
+
+ /* Some servers require that the client port be a reserved port
+ * number. We always allocate a reserved port, as this prevents
+ * filehandle disclosure through UDP port capture.
+ */
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ tport = 1024;
+
+ errval = 0;
+ do
+ {
+ tport--;
+ sin.sin_port = htons(tport);
+ error = psock_bind(rpc->rc_so, (struct sockaddr *)&sin, sizeof(sin));
+ if (error < 0)
+ {
+ errval = errno;
+ fdbg("ERROR: psock_bind failed: %d\n", errval);
+ }
+ }
+ while (errval == EADDRINUSE && tport > 1024 / 2);
+
+ if (error)
+ {
+ fdbg("ERROR: psock_bind failed: %d\n", errval);
+ goto bad;
+ }
+
+ /* Protocols that do not require connections may be optionally left
+ * unconnected for servers that reply from a port other than
+ * NFS_PORT.
+ */
+
+ error = psock_connect(rpc->rc_so, saddr, sizeof(*saddr));
+ if (error < 0)
+ {
+ errval = errno;
+ fdbg("ERROR: psock_connect to PMAP port failed: %d", errval);
+ goto bad;
+ }
+
+ /* Do the RPC to get a dynamic bounding with the server using ppmap.
+ * Get port number for MOUNTD.
+ */
+
+ request.sdata.pmap.prog = txdr_unsigned(RPCPROG_MNT);
+ request.sdata.pmap.vers = txdr_unsigned(RPCMNT_VER1);
+ request.sdata.pmap.proc = txdr_unsigned(IPPROTO_UDP);
+ request.sdata.pmap.port = 0;
+
+ error = rpcclnt_request(rpc, PMAPPROC_GETPORT, PMAPPROG, PMAPVERS,
+ (FAR void *)&request.sdata, sizeof(struct call_args_pmap),
+ (FAR void *)&response.rdata, sizeof(struct rpc_reply_pmap));
+ if (error != 0)
+ {
+ fdbg("ERROR: rpcclnt_request failed: %d\n", error);
+ goto bad;
+ }
+
+ sa = (FAR struct sockaddr_in *)saddr;
+ sa->sin_port = htons(fxdr_unsigned(uint32_t, response.rdata.pmap.port));
+
+ error = psock_connect(rpc->rc_so, saddr, sizeof(*saddr));
+ if (error < 0)
+ {
+ errval = errno;
+ fdbg("ERROR: psock_connect MOUNTD port failed: %d\n", errval);
+ goto bad;
+ }
+
+ /* Do RPC to mountd. */
+
+ strncpy(request.mountd.mount.rpath, rpc->rc_path, 90);
+ request.mountd.mount.len = txdr_unsigned(sizeof(request.mountd.mount.rpath));
+
+ error = rpcclnt_request(rpc, RPCMNT_MOUNT, RPCPROG_MNT, RPCMNT_VER1,
+ (FAR void *)&request.mountd, sizeof(struct call_args_mount),
+ (FAR void *)&response.mdata, sizeof(struct rpc_reply_mount));
+ if (error != 0)
+ {
+ fdbg("ERROR: rpcclnt_request failed: %d\n", error);
+ goto bad;
+ }
+
+ error = fxdr_unsigned(uint32_t, response.mdata.mount.status);
+ if (error != 0)
+ {
+ fdbg("ERROR: Bad mount status: %d\n", error);
+ goto bad;
+ }
+
+ memcpy(&rpc->rc_fh, &response.mdata.mount.fhandle, sizeof(nfsfh_t));
+
+ /* Do the RPC to get a dynamic bounding with the server using PMAP.
+ * NFS port in the socket.
+ */
+
+ sa->sin_port = htons(PMAPPORT);
+
+ error = psock_connect(rpc->rc_so, saddr, sizeof(*saddr));
+ if (error < 0)
+ {
+ errval = errno;
+ fdbg("ERROR: psock_connect PMAP port failed: %d\n", errval);
+ goto bad;
+ }
+
+ request.sdata.pmap.prog = txdr_unsigned(NFS_PROG);
+ request.sdata.pmap.vers = txdr_unsigned(NFS_VER3);
+ request.sdata.pmap.proc = txdr_unsigned(IPPROTO_UDP);
+ request.sdata.pmap.port = 0;
+
+ error = rpcclnt_request(rpc, PMAPPROC_GETPORT, PMAPPROG, PMAPVERS,
+ (FAR void *)&request.sdata, sizeof(struct call_args_pmap),
+ (FAR void *)&response.rdata, sizeof(struct rpc_reply_pmap));
+ if (error != 0)
+ {
+ fdbg("ERROR: rpcclnt_request failed: %d\n", error);
+ goto bad;
+ }
+
+ sa->sin_port = htons(fxdr_unsigned(uint32_t, response.rdata.pmap.port));
+
+ error = psock_connect(rpc->rc_so, saddr, sizeof(*saddr));
+ if (error)
+ {
+ fdbg("psock_connect NFS port returns %d\n", error);
+ goto bad;
+ }
+
+ return OK;
+
+bad:
+ rpcclnt_disconnect(rpc);
+ return error;
+}
+
+/****************************************************************************
+ * Name: rpcclnt_disconnect
+ *
+ * Description:
+ * Disconnect from the NFS server.
+ *
+ ****************************************************************************/
+
+void rpcclnt_disconnect(struct rpcclnt *rpc)
+{
+ if (rpc->rc_so != NULL)
+ {
+ (void)psock_close(rpc->rc_so);
+ }
+}
+
+/****************************************************************************
+ * Name: rpcclnt_umount
+ *
+ * Description:
+ * Un-mount the NFS file system.
+ *
+ ****************************************************************************/
+
+int rpcclnt_umount(struct rpcclnt *rpc)
+{
+ struct sockaddr *saddr;
+ struct sockaddr_in *sa;
+
+ union
+ {
+ struct rpc_call_pmap sdata;
+ struct rpc_call_umount mountd;
+ } request;
+
+ union
+ {
+ struct rpc_reply_pmap rdata;
+ struct rpc_reply_umount mdata;
+ } response;
+
+ int error;
+ int ret;
+
+ saddr = rpc->rc_name;
+ sa = (FAR struct sockaddr_in *)saddr;
+
+ /* Do the RPC to get a dynamic bounding with the server using ppmap.
+ * Get port number for MOUNTD.
+ */
+
+ sa->sin_port = htons(PMAPPORT);
+
+ ret = psock_connect(rpc->rc_so, saddr, sizeof(*saddr));
+ if (ret < 0)
+ {
+ error = errno;
+ fdbg("ERROR: psock_connect failed [port=%d]: %d\n",
+ ntohs(sa->sin_port), error);
+ goto bad;
+ }
+
+ request.sdata.pmap.prog = txdr_unsigned(RPCPROG_MNT);
+ request.sdata.pmap.vers = txdr_unsigned(RPCMNT_VER1);
+ request.sdata.pmap.proc = txdr_unsigned(IPPROTO_UDP);
+ request.sdata.pmap.port = 0;
+
+ error = rpcclnt_request(rpc, PMAPPROC_GETPORT, PMAPPROG, PMAPVERS,
+ (FAR void *)&request.sdata, sizeof(struct call_args_pmap),
+ (FAR void *)&response.rdata, sizeof(struct rpc_reply_pmap));
+ if (error != 0)
+ {
+ fdbg("ERROR: rpcclnt_request failed: %d\n", error);
+ goto bad;
+ }
+
+ sa->sin_port = htons(fxdr_unsigned(uint32_t, response.rdata.pmap.port));
+
+ ret = psock_connect(rpc->rc_so, saddr, sizeof(*saddr));
+ if (ret < 0)
+ {
+ error = errno;
+ fdbg("ERROR: psock_connect failed [port=%d]: %d\n",
+ ntohs(sa->sin_port), error);
+ goto bad;
+ }
+
+ /* Do RPC to umountd. */
+
+ strncpy(request.mountd.umount.rpath, rpc->rc_path, 92);
+ request.mountd.umount.len = txdr_unsigned(sizeof(request.mountd.umount.rpath));
+
+ error = rpcclnt_request(rpc, RPCMNT_UMOUNT, RPCPROG_MNT, RPCMNT_VER1,
+ (FAR void *)&request.mountd, sizeof(struct call_args_umount),
+ (FAR void *)&response.mdata, sizeof(struct rpc_reply_umount));
+ if (error != 0)
+ {
+ fdbg("ERROR: rpcclnt_request failed: %d\n", error);
+ goto bad;
+ }
+
+ return OK;
+
+bad:
+ rpcclnt_disconnect(rpc);
+ return error;
+}
+
+/****************************************************************************
+ * Name: rpcclnt_request
+ *
+ * Description:
+ * Perform the RPC request. Logic formats the RPC CALL message and calls
+ * rpcclnt_send to send the RPC CALL message. It then calls rpcclnt_reply()
+ * to get the response. It may attempt to re-send the CALL message on
+ * certain errors.
+ *
+ * On successful receipt, it verifies the RPC level of the returned values.
+ * (There may still be be NFS layer errors that will be deted by calling
+ * logic).
+ *
+ ****************************************************************************/
+
+int rpcclnt_request(FAR struct rpcclnt *rpc, int procnum, int prog,
+ int version, FAR void *request, size_t reqlen,
+ FAR void *response, size_t resplen)
+{
+ struct rpc_reply_header *replymsg;
+ uint32_t tmp;
+ uint32_t xid;
+ int retries;
+ int error = 0;
+
+ /* Get a new (non-zero) xid */
+
+ xid = rpcclnt_newxid();
+
+ /* Initialize the RPC header fields */
+
+ rpcclnt_fmtheader((FAR struct rpc_call_header *)request,
+ xid, prog, version, procnum);
+
+ /* Get the full size of the message (the size of variable data plus the size of
+ * the messages header).
+ */
+
+ reqlen += sizeof(struct rpc_call_header);
+
+ /* Send the RPC call messsages and receive the RPC response. A limited
+ * number of re-tries will be attempted, but only for the case of response
+ * timeouts.
+ */
+
+ retries = 0;
+ do
+ {
+ /* Do the client side RPC. */
+
+ rpc_statistics(rpcrequests);
+ rpc->rc_timeout = false;
+
+ /* Send the RPC CALL message */
+
+ error = rpcclnt_send(rpc, procnum, prog, request, reqlen);
+ if (error != OK)
+ {
+ fvdbg("ERROR rpcclnt_send failed: %d\n", error);
+ }
+
+ /* Wait for the reply from our send */
+
+ else
+ {
+ error = rpcclnt_reply(rpc, procnum, prog, response, resplen);
+ if (error != OK)
+ {
+ fvdbg("ERROR rpcclnt_reply failed: %d\n", error);
+ }
+ }
+
+ retries++;
+ }
+ while (rpc->rc_timeout && retries <= rpc->rc_retry);
+
+ if (error != OK)
+ {
+ fdbg("ERROR: RPC failed: %d\n", error);
+ return error;
+ }
+
+ /* Break down the RPC header and check if it is OK */
+
+ replymsg = (FAR struct rpc_reply_header *)response;
+
+ tmp = fxdr_unsigned(uint32_t, replymsg->type);
+ if (tmp == RPC_MSGDENIED)
+ {
+ tmp = fxdr_unsigned(uint32_t, replymsg->status);
+ switch (tmp)
+ {
+ case RPC_MISMATCH:
+ fdbg("RPC_MSGDENIED: RPC_MISMATCH error\n");
+ return EOPNOTSUPP;
+
+ case RPC_AUTHERR:
+ fdbg("RPC_MSGDENIED: RPC_AUTHERR error\n");
+ return EACCES;
+
+ default:
+ return EOPNOTSUPP;
+ }
+ }
+ else if (tmp != RPC_MSGACCEPTED)
+ {
+ return EOPNOTSUPP;
+ }
+
+ tmp = fxdr_unsigned(uint32_t, replymsg->status);
+ if (tmp == RPC_SUCCESS)
+ {
+ fvdbg("RPC_SUCCESS\n");
+ }
+ else if (tmp == RPC_PROGMISMATCH)
+ {
+ fdbg("RPC_MSGACCEPTED: RPC_PROGMISMATCH error\n");
+ return EOPNOTSUPP;
+ }
+ else if (tmp > 5)
+ {
+ fdbg("ERROR: Other RPC type: %d\n", tmp);
+ return EOPNOTSUPP;
+ }
+
+ return OK;
+}