aboutsummaryrefslogtreecommitdiff
path: root/nuttx/fs/nfs
diff options
context:
space:
mode:
authorpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2012-09-17 18:18:44 +0000
committerpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2012-09-17 18:18:44 +0000
commit57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff (patch)
tree25d07d14e920d31c0b1947c9ca586f2a01fc32d8 /nuttx/fs/nfs
downloadpx4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.tar.gz
px4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.tar.bz2
px4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.zip
Resync new repository with old repo r5166
git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5153 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'nuttx/fs/nfs')
-rw-r--r--nuttx/fs/nfs/Kconfig24
-rw-r--r--nuttx/fs/nfs/Make.defs50
-rw-r--r--nuttx/fs/nfs/nfs.h149
-rw-r--r--nuttx/fs/nfs/nfs_mount.h140
-rw-r--r--nuttx/fs/nfs/nfs_node.h82
-rw-r--r--nuttx/fs/nfs/nfs_proto.h571
-rw-r--r--nuttx/fs/nfs/nfs_util.c608
-rw-r--r--nuttx/fs/nfs/nfs_vfsops.c2531
-rw-r--r--nuttx/fs/nfs/rpc.h491
-rw-r--r--nuttx/fs/nfs/rpc_clnt.c807
-rw-r--r--nuttx/fs/nfs/xdr_subs.h128
11 files changed, 5581 insertions, 0 deletions
diff --git a/nuttx/fs/nfs/Kconfig b/nuttx/fs/nfs/Kconfig
new file mode 100644
index 000000000..3838efffa
--- /dev/null
+++ b/nuttx/fs/nfs/Kconfig
@@ -0,0 +1,24 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+config NFS
+ bool "NFS client file system"
+ default n
+ depends on NET && !DISABLE_MOUNTPOINT
+ ---help---
+ Enable network file system (NFS) client file system
+
+#if NFS
+
+config NFS_STATISTICS
+ bool "NFS Stastics"
+ default n
+ depends on NFS
+ ---help---
+ Collect support for NFS statistics. There is no user interface to
+ obtain these statistics, however. So they would only be of value
+ if you add debug instrumentation or use a debugger.
+
+#endif
diff --git a/nuttx/fs/nfs/Make.defs b/nuttx/fs/nfs/Make.defs
new file mode 100644
index 000000000..fc4682f85
--- /dev/null
+++ b/nuttx/fs/nfs/Make.defs
@@ -0,0 +1,50 @@
+############################################################################
+# Make.defs
+#
+# Copyright (C) 2012 Gregory Nutt. All rights reserved.
+# Author: Gregory Nutt <gnutt@nuttx.org>
+#
+# 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 Nuttx 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 COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+############################################################################
+
+ifeq ($(CONFIG_NFS),y)
+# Files required for NFS file system support
+
+ASRCS +=
+CSRCS +=
+
+# Files required for NFS RPC
+
+ASRCS +=
+CSRCS += rpc_clnt.c nfs_util.c nfs_vfsops.c
+
+# Argument for dependency checking
+
+NFSDEPPATH = --dep-path nfs
+endif
diff --git a/nuttx/fs/nfs/nfs.h b/nuttx/fs/nfs/nfs.h
new file mode 100644
index 000000000..af24357a4
--- /dev/null
+++ b/nuttx/fs/nfs/nfs.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+ * fs/nfs/nfs.h
+ *
+ * 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) 1989, 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. 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_NFS_NFS_H
+#define __FS_NFS_NFS_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "nfs_mount.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define NFS_TICKINTVL MSEC_PER_TICK /* Smallest that we can get */
+#define NFS_TICKS 1 /* Number of system ticks */
+#define NFS_HZ CLOCKS_PER_SEC /* Ticks/sec */
+#define NFS_TIMEO (1 * NFS_HZ) /* Default timeout = 1 second */
+#define NFS_MINTIMEO (1 * NFS_HZ) /* Min timeout to use */
+#define NFS_MAXTIMEO (60 * NFS_HZ) /* Max timeout to backoff to */
+#define NFS_TIMEOUTMUL 2 /* Timeout/Delay multiplier */
+#define NFS_MAXREXMIT 100 /* Stop counting after this many */
+#define NFS_RETRANS 10 /* Num of retrans for soft mounts */
+#define NFS_WSIZE 8192 /* Def. write data size <= 8192 */
+#define NFS_RSIZE 8192 /* Def. read data size <= 8192 */
+#define NFS_READDIRSIZE 8192 /* Def. readdir size */
+#define NFS_NPROCS 23
+
+/* Ideally, NFS_DIRBLKSIZ should be bigger, but I've seen servers with
+ * broken NFS/ethernet drivers that won't work with anything bigger (Linux..)
+ */
+
+#define NFS_DIRBLKSIZ 1024 /* Must be a multiple of DIRBLKSIZ */
+
+/* Increment NFS statistics */
+
+#ifdef CONFIG_NFS_STATISTICS
+# define nfs_statistics(n) do { nfsstats.rpccnt[n]++; } while (0)
+#else
+# define nfs_statistics(n)
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+extern uint32_t nfs_true;
+extern uint32_t nfs_false;
+extern uint32_t nfs_xdrneg1;
+#ifdef CONFIG_NFS_STATISTICS
+extern struct nfsstats nfsstats;
+#endif
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* NFS statistics structure */
+
+struct nfsstats
+{
+ uint64_t rpccnt[NFS_NPROCS];
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+EXTERN void nfs_semtake(FAR struct nfsmount *nmp);
+EXTERN void nfs_semgive(FAR struct nfsmount *nmp);
+EXTERN int nfs_checkmount(FAR struct nfsmount *nmp);
+EXTERN int nfs_fsinfo(FAR struct nfsmount *nmp);
+EXTERN int nfs_request(struct nfsmount *nmp, int procnum,
+ FAR void *request, size_t reqlen,
+ FAR void *response, size_t resplen);
+EXTERN int nfs_lookup(FAR struct nfsmount *nmp, FAR const char *filename,
+ FAR struct file_handle *fhandle,
+ FAR struct nfs_fattr *obj_attributes,
+ FAR struct nfs_fattr *dir_attributes);
+EXTERN int nfs_findnode(FAR struct nfsmount *nmp, FAR const char *relpath,
+ FAR struct file_handle *fhandle,
+ FAR struct nfs_fattr *obj_attributes,
+ FAR struct nfs_fattr *dir_attributes);
+EXTERN int nfs_finddir(FAR struct nfsmount *nmp, FAR const char *relpath,
+ FAR struct file_handle *fhandle,
+ FAR struct nfs_fattr *attributes, FAR char *filename);
+EXTERN void nfs_attrupdate(FAR struct nfsnode *np,
+ FAR struct nfs_fattr *attributes);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _NFS_NFS_H */
diff --git a/nuttx/fs/nfs/nfs_mount.h b/nuttx/fs/nfs/nfs_mount.h
new file mode 100644
index 000000000..8f1f7be53
--- /dev/null
+++ b/nuttx/fs/nfs/nfs_mount.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+ * fs/nfs/nfs_mount.h
+ *
+ * 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) 1989, 1993
+ * 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.
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_NFS_NFS_MOUNT_H
+#define __FS_NFS_NFS_MOUNT_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <sys/socket.h>
+
+#include "rpc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Mount structure. One mount structure is allocated for each NFS mount. This
+ * structure holds NFS specific information for mount.
+ */
+
+struct nfsmount
+{
+ struct nfsnode *nm_head; /* A list of all files opened on this mountpoint */
+ sem_t nm_sem; /* Used to assure thread-safe access */
+ nfsfh_t nm_fh; /* File handle of root dir */
+ char nm_path[90]; /* server's path of the directory being mounted */
+ struct nfs_fattr nm_fattr; /* nfs file attribute cache */
+ struct rpcclnt *nm_rpcclnt; /* RPC state */
+ struct socket *nm_so; /* RPC socket */
+ struct sockaddr nm_nam; /* Addr of server */
+ bool nm_mounted; /* true: The file system is ready */
+ uint8_t nm_fhsize; /* Size of root file handle (host order) */
+ uint8_t nm_sotype; /* Type of socket */
+ uint8_t nm_retry; /* Max retries */
+ uint16_t nm_timeo; /* Timeout value (in system clock ticks) */
+ uint16_t nm_rsize; /* Max size of read RPC */
+ uint16_t nm_wsize; /* Max size of write RPC */
+ uint16_t nm_readdirsize; /* Size of a readdir RPC */
+ uint16_t nm_buflen; /* Size of I/O buffer */
+
+ /* Set aside memory on the stack to hold the largest call message. NOTE
+ * that for the case of the write call message, it is the reply message that
+ * is in this union.
+ */
+
+ union
+ {
+ struct rpc_call_pmap pmap;
+ struct rpc_call_mount mountd;
+ struct rpc_call_create create;
+ struct rpc_call_lookup lookup;
+ struct rpc_call_read read;
+ struct rpc_call_remove removef;
+ struct rpc_call_rename renamef;
+ struct rpc_call_mkdir mkdir;
+ struct rpc_call_rmdir rmdir;
+ struct rpc_call_readdir readdir;
+ struct rpc_call_fs fsstat;
+ struct rpc_call_setattr setattr;
+ struct rpc_call_fs fs;
+ struct rpc_reply_write write;
+ } nm_msgbuffer;
+
+ /* I/O buffer (must be a aligned to 32-bit boundaries). This buffer used for all
+ * reply messages EXCEPT for the WRITE RPC. In that case it is used for the WRITE
+ * call message that contains the data to be written. This buffer must be
+ * dynamically sized based on the characteristics of the server and upon the
+ * configuration of the NuttX network. It must be sized to hold the largest
+ * possible WRITE call message or READ response message.
+ */
+
+ uint32_t nm_iobuffer[1]; /* Actual size is given by nm_buflen */
+};
+
+/* The size of the nfsmount structure will debug on the size of the allocated I/O
+ * buffer.
+ */
+
+#define SIZEOF_nfsmount(n) (sizeof(struct nfsmount) + ((n + 3) & ~3) - sizeof(uint32_t))
+
+/* Mount parameters structure. This structure is use in nfs_decode_args funtion before one
+ * mount structure is allocated in each NFS mount.
+ */
+
+struct nfs_mount_parameters
+{
+ uint8_t timeo; /* Timeout value (in deciseconds) */
+ uint8_t retry; /* Max retries */
+ uint16_t rsize; /* Max size of read RPC */
+ uint16_t wsize; /* Max size of write RPC */
+ uint16_t readdirsize; /* Size of a readdir RPC */
+};
+
+#endif
diff --git a/nuttx/fs/nfs/nfs_node.h b/nuttx/fs/nfs/nfs_node.h
new file mode 100644
index 000000000..4ae9e162c
--- /dev/null
+++ b/nuttx/fs/nfs/nfs_node.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+ * fs/nfs/nfs_node.h
+ *
+ * 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) 1989, 1993
+ * 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. 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_NFS_NFS_NODE_H
+#define __FS_NFS_NFS_NODE_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "nfs_proto.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Flags for struct nfsnode n_flag */
+
+#define NFSNODE_OPEN (1 << 0) /* File is still open */
+#define NFSNODE_MODIFIED (1 << 1) /* Might have a modified buffer */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* There is a unique nfsnode allocated for each active file. An nfsnode is
+ * 'named' by its file handle.
+ */
+
+struct nfsnode
+{
+ struct nfsnode *n_next; /* Retained in a singly linked list. */
+ uint8_t n_type; /* File type */
+ uint8_t n_fhsize; /* Size in bytes of the file handle */
+ uint8_t n_flags; /* Node flags */
+ struct timespec n_mtime; /* File modification time (see NOTE) */
+ time_t n_ctime; /* File creation time (see NOTE) */
+ nfsfh_t n_fhandle; /* NFS File Handle */
+ uint64_t n_size; /* Current size of file (see NOTE) */
+};
+
+#endif /* __FS_NFS_NFS_NODE_H */
diff --git a/nuttx/fs/nfs/nfs_proto.h b/nuttx/fs/nfs/nfs_proto.h
new file mode 100644
index 000000000..676ee6232
--- /dev/null
+++ b/nuttx/fs/nfs/nfs_proto.h
@@ -0,0 +1,571 @@
+/****************************************************************************
+ * fs/nfs/nfs_proto.h
+ *
+ * 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) 1989, 1993
+ * 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. 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_NFS_NFS_PROTO_H
+#define __FS_NFS_NFS_PROTO_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Constants as defined in the Sun NFS Version 2 and 3 specs.
+ * "NFS: Network File System Protocol Specification" RFC1094
+ * and in the "NFS: Network File System Version 3 Protocol
+ * Specification"
+ */
+
+#define NFS_PORT 2049
+#define NFS_PROG 100003
+#define NFS_VER2 2
+#define NFS_VER3 3
+#define NFS_VER4 4
+#define NFS_MAXDGRAMDATA 32768
+#define MAXBSIZE 64000
+#define NFS_MAXDATA MAXBSIZE
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS_MAXPKTHDR 404
+#define NFS_MAXPACKET (NFS_MAXPKTHDR + NFS_MAXDATA)
+#define NFS_MINPACKET 20
+#define NFS_FABLKSIZE 512 /* Size in bytes of a block wrt fa_blocks */
+
+/* Stat numbers for rpc returns (version 2 and 3) */
+
+#define NFS_OK 0
+#define NFSERR_PERM 1
+#define NFSERR_NOENT 2
+#define NFSERR_IO 5
+#define NFSERR_NXIO 6
+#define NFSERR_ACCES 13
+#define NFSERR_EXIST 17
+#define NFSERR_XDEV 18 /* Version 3 only */
+#define NFSERR_NODEV 19
+#define NFSERR_NOTDIR 20
+#define NFSERR_ISDIR 21
+#define NFSERR_INVAL 22 /* Version 3 only */
+#define NFSERR_FBIG 27
+#define NFSERR_NOSPC 28
+#define NFSERR_ROFS 30
+#define NFSERR_MLINK 31 /* Version 3 only */
+#define NFSERR_NAMETOL 63
+#define NFSERR_NOTEMPTY 66
+#define NFSERR_DQUOT 69
+#define NFSERR_STALE 70
+#define NFSERR_REMOTE 71 /* Version 3 only */
+#define NFSERR_WFLUSH 99 /* Version 2 only */
+#define NFSERR_BADHANDLE 10001 /* The rest Version 3 only */
+#define NFSERR_NOT_SYNC 10002
+#define NFSERR_BAD_COOKIE 10003
+#define NFSERR_NOTSUPP 10004
+#define NFSERR_TOOSMALL 10005
+#define NFSERR_SERVERFAULT 10006
+#define NFSERR_BADTYPE 10007
+#define NFSERR_JUKEBOX 10008
+#define NFSERR_TRYLATER NFSERR_JUKEBOX
+#define NFSERR_STALEWRITEVERF 30001 /* Fake return for nfs_commit() */
+
+#define NFSERR_RETVOID 0x20000000 /* Return void, not error */
+#define NFSERR_AUTHERR 0x40000000 /* Mark an authentication error */
+#define NFSERR_RETERR 0x80000000 /* Mark an error return for V3 */
+
+/* Sizes in bytes of various nfs rpc components */
+
+#define NFSX_UNSIGNED 4
+
+/* Specific to NFS Version 3 */
+
+#define NFSX_V3FH (sizeof (fhandle_t)) /* size this server uses */
+#define NFSX_V3FHMAX 64 /* max. allowed by protocol */
+#define NFSX_V3FATTR 84
+#define NFSX_V3SATTR 60 /* max. all fields filled in */
+#define NFSX_V3SRVSATTR (sizeof (struct nfsv3_sattr))
+#define NFSX_V3POSTOPATTR (NFSX_V3FATTR + NFSX_UNSIGNED)
+#define NFSX_V3WCCDATA (NFSX_V3POSTOPATTR + 8 * NFSX_UNSIGNED)
+#define NFSX_V3COOKIEVERF 8
+#define NFSX_V3WRITEVERF 8
+#define NFSX_V3CREATEVERF 8
+#define NFSX_V3STATFS 52
+#define NFSX_V3FSINFO 48
+#define NFSX_V3PATHCONF 24
+
+/* NFS RPC procedure numbers (before version mapping) */
+
+#define NFSPROC_NULL 0
+#define NFSPROC_GETATTR 1
+#define NFSPROC_SETATTR 2
+#define NFSPROC_LOOKUP 3
+#define NFSPROC_ACCESS 4
+#define NFSPROC_READLINK 5
+#define NFSPROC_READ 6
+#define NFSPROC_WRITE 7
+#define NFSPROC_CREATE 8
+#define NFSPROC_MKDIR 9
+#define NFSPROC_SYMLINK 10
+#define NFSPROC_MKNOD 11
+#define NFSPROC_REMOVE 12
+#define NFSPROC_RMDIR 13
+#define NFSPROC_RENAME 14
+#define NFSPROC_LINK 15
+#define NFSPROC_READDIR 16
+#define NFSPROC_READDIRPLUS 17
+#define NFSPROC_FSSTAT 18
+#define NFSPROC_FSINFO 19
+#define NFSPROC_PATHCONF 20
+#define NFSPROC_COMMIT 21
+#define NFSPROC_NOOP 22
+#define NFS_NPROCS 23
+
+
+/* Constants used by the Version 3 protocol for various RPCs */
+
+#define NFSV3SATTRTIME_DONTCHANGE 0
+#define NFSV3SATTRTIME_TOSERVER 1
+#define NFSV3SATTRTIME_TOCLIENT 2
+
+#define NFSV3ACCESS_READ 0x01
+#define NFSV3ACCESS_LOOKUP 0x02
+#define NFSV3ACCESS_MODIFY 0x04
+#define NFSV3ACCESS_EXTEND 0x08
+#define NFSV3ACCESS_DELETE 0x10
+#define NFSV3ACCESS_EXECUTE 0x20
+
+#define NFSV3WRITE_UNSTABLE 0
+#define NFSV3WRITE_DATASYNC 1
+#define NFSV3WRITE_FILESYNC 2
+
+#define NFSV3CREATE_UNCHECKED 0
+#define NFSV3CREATE_GUARDED 1
+#define NFSV3CREATE_EXCLUSIVE 2
+
+#define NFSV3FSINFO_LINK 0x01
+#define NFSV3FSINFO_SYMLINK 0x02
+#define NFSV3FSINFO_HOMOGENEOUS 0x08
+#define NFSV3FSINFO_CANSETTIME 0x10
+
+/* Conversion macros */
+
+#define vtonfsv3_mode(m) txdr_unsigned((m) & 07777)
+#define nfstov_mode(a) (fxdr_unsigned(u_int16_t, (a))&07777)
+#define vtonfsv3_type(a) txdr_unsigned(nfsv3_type[((int32_t)(a))])
+#define nfsv3tov_type(a) nv3tov_type[fxdr_unsigned(uint32_t,(a))&0x7]
+
+/* Mode bit values */
+
+#define NFSMODE_IXOTH (1 << 0) /* Execute permission for others on a file */
+#define NFSMODE_IWOTH (1 << 1) /* Write permission for others */
+#define NFSMODE_IROTH (1 << 2) /* Read permission for others */
+#define NFSMODE_IXGRP (1 << 3) /* Execute permission for group on a file */
+#define NFSMODE_IWGRP (1 << 4) /* Write permission for group */
+#define NFSMODE_IRGRP (1 << 5) /* Read permission for group */
+#define NFSMODE_IXUSR (1 << 6) /* Execute permission for owner on a file */
+#define NFSMODE_IWUSR (1 << 7) /* Write permission for owner */
+#define NFSMODE_IRUSR (1 << 8) /* Read permission for owner */
+#define NFSMODE_SAVETEXT (1 << 9) /* Save swapped text */
+#define NFSMODE_ISGID (1 << 10) /* Set group ID on execution */
+#define NFSMODE_ISUID (1 << 11) /* Set user ID on execution */
+
+/* File identifier */
+
+#define MAXFIDSZ 16
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* File types */
+
+typedef enum
+{
+ NFNON = 0, /* Unknown type */
+ NFREG = 1, /* Regular file */
+ NFDIR = 2, /* Directory */
+ NFBLK = 3, /* Block special device file */
+ NFCHR = 4, /* Character special device file */
+ NFLNK = 5, /* Symbolic link */
+ NFSOCK = 6, /* Socket */
+ NFFIFO = 7 /* Named FIFO */
+} nfstype;
+
+/* File Handle variable is up to 64 bytes for version 3. This structures a
+ * ariable sized and are provided only for setting aside maximum memory
+ * allocations for a file handle.
+ */
+
+struct nfsfh
+{
+ uint8_t fh_bytes[NFSX_V3FHMAX];
+};
+typedef struct nfsfh nfsfh_t;
+#define SIZEOF_nfsfh_t(n) (n)
+
+struct nfsv3_time
+{
+ uint32_t nfsv3_sec;
+ uint32_t nfsv3_nsec;
+};
+typedef struct nfsv3_time nfstime3;
+
+/* Quads are defined as arrays of 2 longs to ensure dense packing for the
+ * protocol and to facilitate xdr conversion.
+ */
+
+struct nfs_uquad
+{
+ uint32_t nfsuquad[2];
+};
+typedef struct nfs_uquad nfsuint64;
+
+/* NFS Version 3 special file number. */
+
+struct nfsv3_spec
+{
+ uint32_t specdata1;
+ uint32_t specdata2;
+};
+typedef struct nfsv3_spec nfsv3spec;
+
+/* File attributes and setable attributes. These structures cover both
+ * NFS version 2 and the version 3 protocol. Note that the union is only
+ * used so that one pointer can refer to both variants. These structures
+ * go out on the wire and must be densely packed, so no quad data types
+ * are used. (all fields are longs or u_longs or structures of same)
+ */
+
+struct nfs_fattr
+{
+ uint32_t fa_type;
+ uint32_t fa_mode;
+ uint32_t fa_nlink;
+ uint32_t fa_uid;
+ uint32_t fa_gid;
+ nfsuint64 fa_size;
+ nfsuint64 fa_used;
+ nfsv3spec fa_rdev;
+ nfsuint64 fa_fsid;
+ nfsuint64 fa_fileid;
+ nfstime3 fa_atime;
+ nfstime3 fa_mtime;
+ nfstime3 fa_ctime;
+};
+
+/* NFS Version 3 sattr structure for the new node creation case. This is the
+ * maximum size of the attributes; the actual size may vary if values are not
+ * include.
+ */
+
+struct nfsv3_sattr
+{
+ uint32_t sa_modefollows; /* TRUE: Mode value follows */
+ uint32_t sa_mode; /* Mode value */
+ uint32_t sa_uidfollows; /* TRUE: Uid value follows */
+ uint32_t sa_uid; /* Uid value */
+ uint32_t sa_gidfollows; /* TRUE: Mode value follows */
+ uint32_t sa_gid; /* Mode value */
+ uint32_t sa_sizefollows; /* TRUE: Size value follows */
+ uint32_t sa_size; /* Size value */
+ uint32_t sa_atimetype; /* Don't change, use server timer, or use client time */
+ nfstime3 sa_atime; /* Client time */
+ uint32_t sa_mtimetype; /* Don't change, use server timer, or use client time */
+ nfstime3 sa_mtime; /* Client time */
+};
+
+struct nfs_statfs
+{
+ struct nfs_fattr obj_attributes;
+ nfsuint64 sf_tbytes;
+ nfsuint64 sf_fbytes;
+ nfsuint64 sf_abytes;
+ nfsuint64 sf_tfiles;
+ nfsuint64 sf_ffiles;
+ nfsuint64 sf_afiles;
+ uint32_t sf_invarsec;
+};
+
+struct post_attr
+{
+ uint32_t obj_attributesfalse;
+ struct nfs_fattr attributes;
+};
+
+struct nfsv3_fsinfo
+{
+//struct post_attr obj_attributes;
+ uint32_t obj_attributesfalse;
+ uint32_t fs_rtmax;
+ uint32_t fs_rtpref;
+ uint32_t fs_rtmult;
+ uint32_t fs_wtmax;
+ uint32_t fs_wtpref;
+ uint32_t fs_wtmult;
+ uint32_t fs_dtpref;
+ nfsuint64 fs_maxfilesize;
+ nfstime3 fs_timedelta;
+ uint32_t fs_properties;
+};
+
+/* NFS procedures args */
+
+struct wcc_attr
+{
+ nfsuint64 size;
+ nfstime3 mtime;
+ nfstime3 ctime;
+};
+
+struct wcc_data
+{
+ uint32_t wcc_attr_follows; /* True if data follows */
+ struct wcc_attr before;
+ uint32_t nfs_attr_follow; /* True if attributes present */
+ struct nfs_fattr after;
+};
+
+struct file_handle
+{
+ uint32_t length;
+ nfsfh_t handle;
+};
+#define SIZEOF_file_handle(n) (sizeof(uint32_t) + SIZEOF_nfsfh_t(n))
+
+struct diropargs3
+{
+ struct file_handle fhandle; /* Variable length */
+ uint32_t length; /* Size of name[] */
+ uint32_t name[(NAME_MAX+3) >> 2]; /* Variable length */
+};
+
+struct CREATE3args
+{
+ struct diropargs3 where;
+ uint32_t create_mode;
+ struct nfsv3_sattr how;
+};
+
+struct CREATE3resok
+{
+ uint32_t handle_follows; /* True, handle follows */
+ struct file_handle fhandle; /* Variable length */
+ uint32_t attributes_follows; /* True, attributes follows */
+ struct nfs_fattr attributes; /* File attributes */
+ struct wcc_data dir_wcc;
+};
+
+/* The actual size of the lookup argument is variable. These structures are, therefore,
+ * only useful in setting aside maximum memory usage for the LOOKUP arguments.
+ */
+
+struct LOOKUP3filename
+{
+ uint32_t namelen; /* Size of name[] */
+ uint32_t name[(NAME_MAX+3) >> 2]; /* Variable length */
+};
+
+struct LOOKUP3args
+{
+ struct file_handle dirhandle; /* Variable length */
+ struct LOOKUP3filename name; /* Variable length */
+};
+
+struct SETATTR3args
+{
+ struct file_handle fhandle; /* Variable length */
+ struct nfsv3_sattr new_attributes; /* Variable length */
+ uint32_t guard; /* Guard value */
+};
+
+struct SETATTR3resok
+{
+ struct wcc_data wcc_data;
+};
+
+/* Actual size of LOOKUP3args */
+
+#define SIZEOF_LOOKUP3filename(b) (sizeof(uint32_t) + (((b)+3) & ~3))
+#define SIZEOF_LOOKUP3args(a,b) (SIZEOF_file_handle(a) + SIZEOF_LOOKUP3filename(b))
+
+struct LOOKUP3resok
+{
+ struct file_handle fhandle;
+ uint32_t obj_attributes_follow;
+ struct nfs_fattr obj_attributes;
+ uint32_t dir_attributes_follow;
+ struct nfs_fattr dir_attributes;
+};
+
+struct READ3args
+{
+ struct file_handle fhandle; /* Variable length */
+ uint64_t offset;
+ uint32_t count;
+};
+
+struct nfs_rdhdr_s
+{
+ uint32_t attributes_follow;
+ struct nfs_fattr attributes; /* Will not be present if attributes_follow == 0 */
+ uint32_t count; /* Number of bytes read */
+ uint32_t eof; /* Non-zero if at the end of file */
+ uint32_t length; /* Length of data (same as count?) */
+};
+
+struct READ3resok
+{
+ struct nfs_rdhdr_s hdr;
+ uint8_t data[1]; /* Actual data size depends on count */
+};
+#define SIZEOF_READ3resok(n) (sizeof(struct nfs_rdhdr_s) + (n))
+
+struct nfs_wrhdr_s
+{
+ struct file_handle fhandle; /* Variable length */
+ uint64_t offset;
+ uint32_t count;
+ uint32_t stable;
+};
+
+struct WRITE3args
+{
+ struct nfs_wrhdr_s hdr;
+ uint8_t data[1]; /* Actual data size depends on count */
+};
+#define SIZEOF_WRITE3args(n) (sizeof(struct nfs_wrhdr_s) + (n))
+
+struct WRITE3resok
+{
+ struct wcc_data file_wcc;
+ uint32_t count;
+ uint32_t committed;
+ uint8_t verf[NFSX_V3WRITEVERF];
+};
+
+struct REMOVE3args
+{
+ struct diropargs3 object;
+};
+
+struct REMOVE3resok
+{
+ struct wcc_data dir_wcc;
+};
+
+struct RENAME3args
+{
+ struct diropargs3 from;
+ struct diropargs3 to;
+};
+
+struct RENAME3resok
+{
+ struct wcc_data fromdir_wcc;
+ struct wcc_data todir_wcc;
+};
+
+struct MKDIR3args
+{
+ struct diropargs3 where;
+ struct nfsv3_sattr how;
+};
+
+struct MKDIR3resok
+{
+ uint32_t handle_follows; /* True, handle follows */
+ struct file_handle fhandle; /* Variable length */
+ uint32_t attributes_follows; /* True, attributes follows */
+ struct nfs_fattr attributes; /* Directory attributes */
+ struct wcc_data dir_wcc;
+};
+
+struct RMDIR3args
+{
+ struct diropargs3 object;
+};
+
+struct RMDIR3resok
+{
+ struct wcc_data dir_wcc;
+};
+
+/* The actual size of the lookup argument is variable. This structures is, therefore,
+ * only useful in setting aside maximum memory usage for the LOOKUP arguments.
+ */
+
+struct READDIR3args
+{
+ struct file_handle dir; /* Variable length */
+ nfsuint64 cookie;
+ uint8_t cookieverf[NFSX_V3COOKIEVERF];
+ uint32_t count;
+};
+
+/* The READDIR reply is variable length and consists of multiple entries, each
+ * of form:
+ *
+ * EOF - OR -
+ *
+ * File ID (8 bytes)
+ * Name length (4 bytes)
+ * Name string (varaiable size but in multiples of 4 bytes)
+ * Cookie (8 bytes)
+ * next entry (4 bytes)
+ */
+
+struct READDIR3resok
+{
+ uint32_t attributes_follow;
+ struct nfs_fattr dir_attributes;
+ uint8_t cookieverf[NFSX_V3COOKIEVERF];
+ uint32_t value_follows;
+ uint32_t reply[1]; /* Variable length reply begins here */
+};
+
+struct FS3args
+{
+ struct file_handle fsroot;
+};
+
+#endif /* __FS_NFS_NFS_PROTO_H */
+
diff --git a/nuttx/fs/nfs/nfs_util.c b/nuttx/fs/nfs/nfs_util.c
new file mode 100644
index 000000000..73fda72a7
--- /dev/null
+++ b/nuttx/fs/nfs/nfs_util.c
@@ -0,0 +1,608 @@
+/****************************************************************************
+ * fs/nfs/nfs_util.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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 NuttX 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <nuttx/config.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <stdint.h>
+#include <queue.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/fs/dirent.h>
+
+#include "rpc.h"
+#include "nfs.h"
+#include "nfs_proto.h"
+#include "nfs_mount.h"
+#include "nfs_node.h"
+#include "xdr_subs.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static inline int nfs_pathsegment(FAR const char **path, FAR char *buffer,
+ FAR char *terminator)
+{
+ FAR const char *src = *path;
+ FAR char *dest = buffer;
+ int nbytes = 0;
+ char ch;
+
+ /* Loop until the name is successfully parsed or an error occurs */
+
+ for (;;)
+ {
+ /* Get the next byte from the path */
+
+ ch = *src++;
+
+ /* Check if this the last byte in this segment name */
+
+ if (ch == '\0' || ch == '/')
+ {
+ /* This logic just suppors "//" sequences in the path name */
+
+ if (ch == '\0' || nbytes > 0 )
+ {
+ /* NULL terminate the parsed path segment */
+
+ *dest = '\0';
+
+ /* Return next path and the terminating character */
+
+ *terminator = ch;
+ *path = src;
+ return OK;
+ }
+
+ /* Just skip over any leading '/' characters */
+ }
+ else if (nbytes >= NAME_MAX)
+ {
+ fdbg("File name segment is too long: %d\n", *path);
+ return EFBIG;
+ }
+ else
+ {
+ /* Save next character in the accumulated name */
+
+ *dest++ = ch;
+ nbytes++;
+ }
+ }
+}
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nfs_semtake
+ ****************************************************************************/
+
+void nfs_semtake(struct nfsmount *nmp)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&nmp->nm_sem) != 0)
+ {
+ /* The only case that an error should occur here is if
+ * the wait was awakened by a signal.
+ */
+
+ ASSERT(*get_errno_ptr() == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Name: nfs_semgive
+ ****************************************************************************/
+
+void nfs_semgive(struct nfsmount *nmp)
+{
+ sem_post(&nmp->nm_sem);
+}
+
+/****************************************************************************
+ * Name: nfs_checkmount
+ *
+ * Desciption: Check if the mountpoint is still valid.
+ *
+ * The caller should hold the mountpoint semaphore
+ *
+ ****************************************************************************/
+
+int nfs_checkmount(struct nfsmount *nmp)
+{
+ struct nfsnode *file;
+
+ /* If the nm_mounted flag is false, then we have already handled the loss
+ * of the mount.
+ */
+
+ DEBUGASSERT(nmp);
+ if (!nmp->nm_mounted)
+ {
+ /* Make sure that this is flagged in every opened file */
+
+ for (file = nmp->nm_head; file; file = file->n_next)
+ {
+ file->n_flags &= ~NFSNODE_OPEN;
+ }
+
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Name: nfs_request
+ *
+ * Desciption:
+ * Perform the NFS request. On successful receipt, it verifies the NFS level of the
+ * returned values.
+ *
+ * Return Value:
+ * Zero on success; a positive errno value on failure.
+ *
+ ****************************************************************************/
+
+int nfs_request(struct nfsmount *nmp, int procnum,
+ FAR void *request, size_t reqlen,
+ FAR void *response, size_t resplen)
+{
+ struct rpcclnt *clnt = nmp->nm_rpcclnt;
+ struct nfs_reply_header replyh;
+ int trylater_delay;
+ int error;
+
+tryagain:
+ error = rpcclnt_request(clnt, procnum, NFS_PROG, NFS_VER3,
+ request, reqlen, response, resplen);
+ if (error != 0)
+ {
+ fdbg("ERROR: rpcclnt_request failed: %d\n", error);
+ return error;
+ }
+
+ memcpy(&replyh, response, sizeof(struct nfs_reply_header));
+
+ if (replyh.nfs_status != 0)
+ {
+ if (fxdr_unsigned(uint32_t, replyh.nfs_status) > 32)
+ {
+ error = EOPNOTSUPP;
+ }
+ else
+ {
+ /* NFS_ERRORS are the same as NuttX errno values */
+
+ error = fxdr_unsigned(uint32_t, replyh.nfs_status);
+ }
+
+ return error;
+ }
+
+ if (replyh.rpc_verfi.authtype != 0)
+ {
+ error = fxdr_unsigned(int, replyh.rpc_verfi.authtype);
+
+ if (error == EAGAIN)
+ {
+ error = 0;
+ trylater_delay *= NFS_TIMEOUTMUL;
+ if (trylater_delay > NFS_MAXTIMEO)
+ {
+ trylater_delay = NFS_MAXTIMEO;
+ }
+
+ goto tryagain;
+ }
+
+ fdbg("ERROR: NFS error %d from server\n", error);
+ return error;
+ }
+
+ fvdbg("NFS_SUCCESS\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nfs_lookup
+ *
+ * Desciption:
+ * Given a directory file handle, and the path to file in the directory,
+ * return the file handle of the path and attributes of both the file and
+ * the directory containing the file.
+ *
+ * NOTE: The LOOKUP call differs from other RPC messages in that the
+ * call message is variable length, depending upon the size of the path
+ * name.
+ *
+ ****************************************************************************/
+
+int nfs_lookup(struct nfsmount *nmp, FAR const char *filename,
+ FAR struct file_handle *fhandle,
+ FAR struct nfs_fattr *obj_attributes,
+ FAR struct nfs_fattr *dir_attributes)
+{
+ FAR uint32_t *ptr;
+ uint32_t value;
+ int reqlen;
+ int namelen;
+ int error = 0;
+
+ DEBUGASSERT(nmp && filename && fhandle);
+
+ /* Get the length of the string to be sent */
+
+ namelen = strlen(filename);
+ if (namelen > NAME_MAX)
+ {
+ fdbg("Length of the string is too big: %d\n", namelen);
+ return E2BIG;
+ }
+
+ /* Initialize the request */
+
+ ptr = (FAR uint32_t *)&nmp->nm_msgbuffer.lookup.lookup;
+ reqlen = 0;
+
+ /* Copy the variable length, directory file handle */
+
+ *ptr++ = txdr_unsigned(fhandle->length);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, &fhandle->handle, fhandle->length);
+ reqlen += fhandle->length;
+ ptr += uint32_increment(fhandle->length);
+
+ /* Copy the variable-length file name */
+
+ *ptr++ = txdr_unsigned(namelen);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, filename, namelen);
+ reqlen += uint32_alignup(namelen);
+
+ /* Request LOOKUP from the server */
+
+ nfs_statistics(NFSPROC_LOOKUP);
+ error = nfs_request(nmp, NFSPROC_LOOKUP,
+ (FAR void *)&nmp->nm_msgbuffer.lookup, reqlen,
+ (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
+
+ if (error)
+ {
+ fdbg("ERROR: nfs_request failed: %d\n", error);
+ return error;
+ }
+
+ /* Return the data to the caller's buffers. NOTE: Here we ignore the
+ * the exact layout of the rpc_reply_lookup structure. File handles
+ * may differ in size whereas struct rpc_reply_lookup uses a fixed size.
+ */
+
+ ptr = (FAR uint32_t *)&((FAR struct rpc_reply_lookup *)nmp->nm_iobuffer)->lookup;
+
+ /* Get the length of the file handle */
+
+ value = *ptr++;
+ value = fxdr_unsigned(uint32_t, value);
+ if (value > NFSX_V3FHMAX)
+ {
+ fdbg("ERROR: Bad file handle length: %d\n", value);
+ return EIO;
+ }
+
+ /* Return the file handle */
+
+ fhandle->length = value;
+ memcpy(&fhandle->handle, ptr, value);
+ ptr += uint32_increment(value);
+
+ /* Check if there are object attributes and, if so, copy them to the user
+ * buffer
+ */
+
+ value = *ptr++;
+ if (value)
+ {
+ if (obj_attributes)
+ {
+ memcpy(obj_attributes, ptr, sizeof(struct nfs_fattr));
+ }
+ ptr += uint32_increment(sizeof(struct nfs_fattr));
+ }
+
+ /* Check if there are directory attributes and, if so, copy them to the
+ * user buffer
+ */
+
+ value = *ptr++;
+ if (value && dir_attributes)
+ {
+ memcpy(dir_attributes, ptr, sizeof(struct nfs_fattr));
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nfs_findnode
+ *
+ * Desciption:
+ * Given a path to something that may or may not be in the file system,
+ * return the handle of the directory entry of the requested object.
+ *
+ * Return Value:
+ * Zero on success; a positive errno value on failure.
+ *
+ ****************************************************************************/
+
+int nfs_findnode(struct nfsmount *nmp, FAR const char *relpath,
+ FAR struct file_handle *fhandle, FAR struct nfs_fattr *obj_attributes,
+ FAR struct nfs_fattr *dir_attributes)
+{
+ FAR const char *path = relpath;
+ char buffer[NAME_MAX+1];
+ char terminator;
+ uint32_t tmp;
+ int error;
+
+ /* Start with the file handle of the root directory. */
+
+ fhandle->length = nmp->nm_fhsize;
+ memcpy(&fhandle->handle, &nmp->nm_fh, nmp->nm_fhsize);
+
+ /* If no path was provided, then the root directory must be exactly what
+ * the caller is looking for.
+ */
+
+ if (*path == '\0' || strlen(path) == 0)
+ {
+ /* Return the root directory attributes */
+
+ if (obj_attributes)
+ {
+ memcpy(obj_attributes, &nmp->nm_fattr, sizeof(struct nfs_fattr));
+ }
+
+ if (dir_attributes)
+ {
+ memcpy(dir_attributes, &nmp->nm_fattr, sizeof(struct nfs_fattr));
+ }
+
+ return OK;
+ }
+
+ /* This is not the root directory. Loop until the directory entry corresponding
+ * to the path is found.
+ */
+
+ for (;;)
+ {
+ /* Extract the next path segment name. */
+
+ error = nfs_pathsegment(&path, buffer, &terminator);
+ if (error != OK)
+ {
+ /* The filename segment contains is too long. */
+
+ fdbg("nfs_pathsegment of \"%s\" failed after \"%s\": %d\n",
+ relpath, buffer, error);
+ return error;
+ }
+
+ /* Look-up this path segment */
+
+ error = nfs_lookup(nmp, buffer, fhandle, obj_attributes, dir_attributes);
+ if (error != OK)
+ {
+ fdbg("nfs_lookup of \"%s\" failed at \"%s\": %d\n",
+ relpath, buffer, error);
+ return error;
+ }
+
+ /* If the terminator character in the path was the end of the string
+ * then we have successfully found the directory entry that describes
+ * the path.
+ */
+
+ if (!terminator)
+ {
+ /* Return success meaning that the description the matching
+ * directory entry is in fhandle, obj_attributes, and dir_attributes.
+ */
+
+ return OK;
+ }
+
+ /* No.. then we have found one of the intermediate directories on
+ * the way to the final path target. In this case, make sure
+ * the thing that we found is, indeed, a directory.
+ */
+
+ tmp = fxdr_unsigned(uint32_t, obj_attributes->fa_type);
+ if (tmp != NFDIR)
+ {
+ /* Ooops.. we found something else */
+
+ fdbg("ERROR: Intermediate segment \"%s\" of \'%s\" is not a directory\n",
+ buffer, path);
+ return ENOTDIR;
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: nfs_finddir
+ *
+ * Desciption:
+ * Given a path to something that may or may not be in the file system,
+ * return the handle of the entry of the directory containing the requested
+* object.
+ *
+ * Return Value:
+ * Zero on success; a positive errno value on failure.
+ *
+ ****************************************************************************/
+
+int nfs_finddir(struct nfsmount *nmp, FAR const char *relpath,
+ FAR struct file_handle *fhandle,
+ FAR struct nfs_fattr *attributes, FAR char *filename)
+{
+ FAR const char *path = relpath;
+ uint32_t tmp;
+ char terminator;
+ int error;
+
+ /* Verify that a path was provided */
+
+ if (*path == '\0' || strlen(path) == 0)
+ {
+ /* Return the root directory attributes */
+
+ return ENOENT;
+ }
+
+ /* Start with the file handle of the root directory. */
+
+ fhandle->length = nmp->nm_fhsize;
+ memcpy(&fhandle->handle, &nmp->nm_fh, nmp->nm_fhsize);
+ memcpy(attributes, &nmp->nm_fattr, sizeof(struct nfs_fattr));
+
+ /* Loop until the directory entry containing the path is found. */
+
+ for (;;)
+ {
+ /* Extract the next path segment name. */
+
+ error = nfs_pathsegment(&path, filename, &terminator);
+ if (error != OK)
+ {
+ /* The filename segment contains is too long. */
+
+ fdbg("nfs_pathsegment of \"%s\" failed after \"%s\": %d\n",
+ relpath, filename, error);
+ return error;
+ }
+
+ /* If the terminator character in the path was the end of the string
+ * then we have successfully found the directory that contains the name
+ * of interest.
+ */
+
+ if (!terminator)
+ {
+ /* Return success meaning that the description of the directory
+ * containing the object is in fhandle and attributes.
+ */
+
+ return OK;
+ }
+
+ /* Look-up the next path segment */
+
+ error = nfs_lookup(nmp, filename, fhandle, attributes, NULL);
+ if (error != OK)
+ {
+ fdbg("nfs_lookup of \"%s\" failed at \"%s\": %d\n",
+ relpath, filename, error);
+ return error;
+ }
+
+ /* Make sure the thing that we found is, indeed, a directory. */
+
+ tmp = fxdr_unsigned(uint32_t, attributes->fa_type);
+ if (tmp != NFDIR)
+ {
+ /* Ooops.. we found something else */
+
+ fdbg("ERROR: Intermediate segment \"%s\" of \'%s\" is not a directory\n",
+ filename, path);
+ return ENOTDIR;
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: nfs_attrupdate
+ *
+ * Desciption:
+ * Update file attributes on write or after the file is modified.
+ *
+ * Return Value:
+ * None.
+ *
+ ****************************************************************************/
+
+void nfs_attrupdate(FAR struct nfsnode *np, FAR struct nfs_fattr *attributes)
+{
+ /* Save a few of the files attribute values in file structur (host order) */
+
+ np->n_type = fxdr_unsigned(uint32_t, attributes->fa_type);
+ np->n_size = fxdr_hyper(&attributes->fa_size);
+ fxdr_nfsv3time(&attributes->fa_mtime, &np->n_mtime)
+ np->n_ctime = fxdr_hyper(&attributes->fa_ctime);
+}
diff --git a/nuttx/fs/nfs/nfs_vfsops.c b/nuttx/fs/nfs/nfs_vfsops.c
new file mode 100644
index 000000000..3cd5a47dc
--- /dev/null
+++ b/nuttx/fs/nfs/nfs_vfsops.c
@@ -0,0 +1,2531 @@
+/****************************************************************************
+ * fs/nfs/nfs_vfsops.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) 1989, 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. 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 <nuttx/config.h>
+
+#include <sys/socket.h>
+#include <sys/statfs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <queue.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <semaphore.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/dirent.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/nfs.h>
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-udp.h>
+#include <nuttx/net/uip/uipopt.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include "nfs.h"
+#include "rpc.h"
+#include "nfs_proto.h"
+#include "nfs_node.h"
+#include "nfs_mount.h"
+#include "xdr_subs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* The V3 EXCLUSIVE file creation logic is not fully supported. */
+
+#define USE_GUARDED_CREATE 1
+
+/* include/nuttx/fs/dirent.h has its own version of these lengths. They must
+ * match the NFS versions.
+ */
+
+#if NFSX_V3FHMAX != DIRENT_NFS_MAXHANDLE
+# error "Length of file handle in fs_dirent_s is incorrect"
+#endif
+
+#if NFSX_V3COOKIEVERF != DIRENT_NFS_VERFLEN
+# error "Length of cookie verify in fs_dirent_s is incorrect"
+#endif
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+uint32_t nfs_true;
+uint32_t nfs_false;
+uint32_t nfs_xdrneg1;
+
+#ifdef CONFIG_NFS_STATISTICS
+struct nfsstats nfsstats;
+#endif
+
+/****************************************************************************
+ * Private Type Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int nfs_filecreate(FAR struct nfsmount *nmp, struct nfsnode *np,
+ FAR const char *relpath, mode_t mode);
+static int nfs_filetruncate(FAR struct nfsmount *nmp, struct nfsnode *np);
+static int nfs_fileopen(FAR struct nfsmount *nmp, struct nfsnode *np,
+ FAR const char *relpath, int oflags, mode_t mode);
+static int nfs_open(FAR struct file *filep, const char *relpath,
+ int oflags, mode_t mode);
+static int nfs_close(FAR struct file *filep);
+static ssize_t nfs_read(FAR struct file *filep, char *buffer, size_t buflen);
+static ssize_t nfs_write(FAR struct file *filep, const char *buffer,
+ size_t buflen);
+static int nfs_opendir(struct inode *mountpt, const char *relpath,
+ struct fs_dirent_s *dir);
+static int nfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir);
+static int nfs_rewinddir(FAR struct inode *mountpt,
+ FAR struct fs_dirent_s *dir);
+static void nfs_decode_args(FAR struct nfs_mount_parameters *nprmt,
+ FAR struct nfs_args *argp);
+static int nfs_bind(FAR struct inode *blkdriver, const void *data,
+ void **handle);
+static int nfs_unbind(void *handle, FAR struct inode **blkdriver);
+static int nfs_statfs(struct inode *mountpt, struct statfs *buf);
+static int nfs_remove(struct inode *mountpt, const char *relpath);
+static int nfs_mkdir(struct inode *mountpt, const char *relpath,
+ mode_t mode);
+static int nfs_rmdir(struct inode *mountpt, const char *relpath);
+static int nfs_rename(struct inode *mountpt, const char *oldrelpath,
+ const char *newrelpath);
+static int nfs_stat(struct inode *mountpt, const char *relpath,
+ struct stat *buf);
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+/* nfs vfs operations. */
+
+const struct mountpt_operations nfs_operations =
+{
+ nfs_open, /* open */
+ nfs_close, /* close */
+ nfs_read, /* read */
+ nfs_write, /* write */
+ NULL, /* seek */
+ NULL, /* ioctl */
+ NULL, /* sync */
+
+ nfs_opendir, /* opendir */
+ NULL, /* closedir */
+ nfs_readdir, /* readdir */
+ nfs_rewinddir, /* rewinddir */
+
+ nfs_bind, /* bind */
+ nfs_unbind, /* unbind */
+ nfs_statfs, /* statfs */
+
+ nfs_remove, /* unlink */
+ nfs_mkdir, /* mkdir */
+ nfs_rmdir, /* rmdir */
+ nfs_rename, /* rename */
+ nfs_stat /* stat */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nfs_filecreate
+ *
+ * Description:
+ * Create a file. This is part of the file open logic that is executed if
+ * the user asks to create a file.
+ *
+ * Returned Value:
+ * 0 on success; a positive errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_filecreate(FAR struct nfsmount *nmp, struct nfsnode *np,
+ FAR const char *relpath, mode_t mode)
+{
+ struct file_handle fhandle;
+ struct nfs_fattr fattr;
+ char filename[NAME_MAX + 1];
+ FAR uint32_t *ptr;
+ uint32_t tmp;
+ int namelen;
+ int reqlen;
+ int error;
+
+ /* Find the NFS node of the directory containing the file to be created */
+
+ error = nfs_finddir(nmp, relpath, &fhandle, &fattr, filename);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_finddir returned: %d\n", error);
+ return error;
+ }
+
+ /* Create the CREATE RPC call arguments */
+
+ ptr = (FAR uint32_t *)&nmp->nm_msgbuffer.create.create;
+ reqlen = 0;
+
+ /* Copy the variable length, directory file handle */
+
+ *ptr++ = txdr_unsigned(fhandle.length);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, &fhandle.handle, fhandle.length);
+ reqlen += (int)fhandle.length;
+ ptr += uint32_increment(fhandle.length);
+
+ /* Copy the variable-length file name */
+
+ namelen = strlen(filename);
+
+ *ptr++ = txdr_unsigned(namelen);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, filename, namelen);
+ ptr += uint32_increment(namelen);
+ reqlen += uint32_alignup(namelen);
+
+ /* Set the creation mode */
+
+ if ((mode & O_CREAT) != 0)
+ {
+#ifdef USE_GUARDED_CREATE
+ *ptr++ = HTONL(NFSV3CREATE_GUARDED);
+#else
+ *ptr++ = HTONL(NFSV3CREATE_EXCLUSIVE);
+#endif
+ }
+ else
+ {
+ *ptr++ = HTONL(NFSV3CREATE_UNCHECKED);
+ }
+ reqlen += sizeof(uint32_t);
+
+ /* Mode information is not provided if EXCLUSIVE creation is used.
+ * in this case, we must call SETATTR after successfully creating
+ * the file.
+ */
+
+#ifndef USE_GUARDED_CREATE
+ if ((mode & O_CREAT) == 0)
+#endif
+ {
+ /* Set the mode. NOTE: Here we depend on the fact that the NuttX and NFS
+ * bit settings are the same (at least for the bits of interest).
+ */
+
+ *ptr++ = nfs_true; /* True: mode value follows */
+ reqlen += sizeof(uint32_t);
+
+ tmp = mode & (NFSMODE_IWOTH | NFSMODE_IROTH | NFSMODE_IWGRP |
+ NFSMODE_IRGRP | NFSMODE_IWUSR | NFSMODE_IRUSR);
+ *ptr++ = txdr_unsigned(tmp);
+ reqlen += sizeof(uint32_t);
+
+ /* Set the user ID to zero */
+
+ *ptr++ = nfs_true; /* True: Uid value follows */
+ *ptr++ = 0; /* UID = 0 (nobody) */
+ reqlen += 2*sizeof(uint32_t);
+
+ /* Set the group ID to one */
+
+ *ptr++ = nfs_true; /* True: Gid value follows */
+ *ptr++ = HTONL(1); /* GID = 1 (nogroup) */
+ reqlen += 2*sizeof(uint32_t);
+
+ /* Set the size to zero */
+
+ *ptr++ = nfs_true; /* True: Size value follows */
+ *ptr++ = 0; /* Size = 0 */
+ *ptr++ = 0;
+ reqlen += 3*sizeof(uint32_t);
+
+ /* Don't change times */
+
+ *ptr++ = HTONL(NFSV3SATTRTIME_DONTCHANGE); /* Don't change atime */
+ *ptr++ = HTONL(NFSV3SATTRTIME_DONTCHANGE); /* Don't change mtime */
+ reqlen += 2*sizeof(uint32_t);
+ }
+
+ /* Send the NFS request. Note there is special logic here to handle version 3
+ * exclusive open semantics.
+ */
+
+ do
+ {
+ nfs_statistics(NFSPROC_CREATE);
+ error = nfs_request(nmp, NFSPROC_CREATE,
+ (FAR void *)&nmp->nm_msgbuffer.create, reqlen,
+ (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
+ }
+#ifdef USE_GUARDED_CREATE
+ while (0);
+#else
+ while (((mode & O_CREAT) != 0) && error == EOPNOTSUPP);
+#endif
+
+ /* Check for success */
+
+ if (error == OK)
+ {
+ /* Parse the returned data */
+
+ ptr = (FAR uint32_t *)&((FAR struct rpc_reply_create *)nmp->nm_iobuffer)->create;
+
+ /* Save the file handle in the file data structure */
+
+ tmp = *ptr++; /* handle_follows */
+ if (!tmp)
+ {
+ fdbg("ERROR: no file handle follows\n");
+ return EINVAL;
+ }
+
+ tmp = *ptr++;
+ tmp = fxdr_unsigned(uint32_t, tmp);
+ DEBUGASSERT(tmp <= NFSX_V3FHMAX);
+
+ np->n_fhsize = (uint8_t)tmp;
+ memcpy(&np->n_fhandle, ptr, tmp);
+ ptr += uint32_increment(tmp);
+
+ /* Save the attributes in the file data structure */
+
+ tmp = *ptr++; /* handle_follows */
+ if (!tmp)
+ {
+ fdbg("WARNING: no file attributes\n");
+ }
+ else
+ {
+ /* Initialize the file attributes */
+
+ nfs_attrupdate(np, (FAR struct nfs_fattr *)ptr);
+ ptr += uint32_increment(sizeof(struct nfs_fattr));
+ }
+
+ /* Any following dir_wcc data is ignored for now */
+ }
+
+ return error;
+}
+
+/****************************************************************************
+ * Name: nfs_fileopen
+ *
+ * Description:
+ * Truncate an open file to zero length. This is part of the file open
+ * logic.
+ *
+ * Returned Value:
+ * 0 on success; a positive errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_filetruncate(FAR struct nfsmount *nmp, struct nfsnode *np)
+{
+ FAR uint32_t *ptr;
+ int reqlen;
+ int error;
+
+ fvdbg("Truncating file\n");
+
+ /* Create the SETATTR RPC call arguments */
+
+ ptr = (FAR uint32_t *)&nmp->nm_msgbuffer.setattr.setattr;
+ reqlen = 0;
+
+ /* Copy the variable length, directory file handle */
+
+ *ptr++ = txdr_unsigned(np->n_fhsize);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, &np->n_fhandle, np->n_fhsize);
+ reqlen += (int)np->n_fhsize;
+ ptr += uint32_increment(np->n_fhsize);
+
+ /* Copy the variable-length attribtes */
+
+ *ptr++ = nfs_false; /* Don't change mode */
+ *ptr++ = nfs_false; /* Don't change uid */
+ *ptr++ = nfs_false; /* Don't change gid */
+ *ptr++ = nfs_true; /* Use the following size */
+ *ptr++ = 0; /* Truncate to zero length */
+ *ptr++ = 0;
+ *ptr++ = HTONL(NFSV3SATTRTIME_TOSERVER); /* Use the server's time */
+ *ptr++ = HTONL(NFSV3SATTRTIME_TOSERVER); /* Use the server's time */
+ *ptr++ = nfs_false; /* No guard value */
+ reqlen += 9*sizeof(uint32_t)
+
+ /* Perform the SETATTR RPC */
+
+ nfs_statistics(NFSPROC_SETATTR);
+ error = nfs_request(nmp, NFSPROC_SETATTR,
+ (FAR void *)&nmp->nm_msgbuffer.setattr, reqlen,
+ (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_request failed: %d\n", error);
+ return error;
+ }
+
+ /* Indicate that the file now has zero length */
+
+ np->n_size = 0;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nfs_fileopen
+ *
+ * Description:
+ * Open a file. This is part of the file open logic that attempts to open
+ * an existing file.
+ *
+ * Returned Value:
+ * 0 on success; a positive errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_fileopen(FAR struct nfsmount *nmp, struct nfsnode *np,
+ FAR const char *relpath, int oflags, mode_t mode)
+{
+ struct file_handle fhandle;
+ struct nfs_fattr fattr;
+ uint32_t tmp;
+ int error = 0;
+
+ /* Find the NFS node associate with the path */
+
+ error = nfs_findnode(nmp, relpath, &fhandle, &fattr, NULL);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_findnode returned: %d\n", error);
+ return error;
+ }
+
+ /* Check if the object is a directory */
+
+ tmp = fxdr_unsigned(uint32_t, fattr.fa_type);
+ if (tmp == NFDIR)
+ {
+ /* Exit with EISDIR if we attempt to open a directory */
+
+ fdbg("ERROR: Path is a directory\n");
+ return EISDIR;
+ }
+
+ /* Check if the caller has sufficient privileges to open the file */
+
+ if ((oflags & O_WRONLY) != 0)
+ {
+ /* Check if anyone has priveleges to write to the file -- owner,
+ * group, or other (we are probably "other" and may still not be
+ * able to write).
+ */
+
+ tmp = fxdr_unsigned(uint32_t, fattr.fa_mode);
+ if ((tmp & (NFSMODE_IWOTH|NFSMODE_IWGRP|NFSMODE_IWUSR)) == 0)
+ {
+ fdbg("ERROR: File is read-only: %08x\n", tmp);
+ return EACCES;
+ }
+ }
+
+ /* It would be an error if we are asked to create the file exclusively */
+
+ if ((oflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
+ {
+ /* Already exists -- can't create it exclusively */
+
+ fdbg("ERROR: File exists\n");
+ return EEXIST;
+ }
+
+ /* Initialize the file private data */
+ /* Copy the file handle */
+
+ np->n_fhsize = (uint8_t)fhandle.length;
+ memcpy(&np->n_fhandle, &fhandle.handle, fhandle.length);
+
+ /* Save the file attributes */
+
+ nfs_attrupdate(np, &fattr);
+
+ /* If O_TRUNC is specified and the file is opened for writing,
+ * then truncate the file. This operation requires that the file is
+ * writable, but we have already checked that. O_TRUNC without write
+ * access is ignored.
+ */
+
+ if ((oflags & (O_TRUNC|O_WRONLY)) == (O_TRUNC|O_WRONLY))
+ {
+ /* Truncate the file to zero length. I think we can do this with
+ * the SETATTR call by setting the length to zero.
+ */
+
+ return nfs_filetruncate(nmp, np);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nfs_open
+ *
+ * Description:
+ * If oflags == O_CREAT it creates a file, if not it check to see if the
+ * type is ok and that deletion is not in progress.
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_open(FAR struct file *filep, FAR const char *relpath,
+ int oflags, mode_t mode)
+{
+ struct nfsmount *nmp;
+ struct nfsnode *np = NULL;
+ int error;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_inode != NULL);
+
+ /* Get the mountpoint inode reference from the file structure and the
+ * mountpoint private data from the inode structure
+ */
+
+ nmp = (struct nfsmount*)filep->f_inode->i_private;
+ DEBUGASSERT(nmp != NULL);
+
+ /* Pre-allocate the file private data to describe the opened file. */
+
+ np = (struct nfsnode *)kzalloc(sizeof(struct nfsnode));
+ if (!np)
+ {
+ fdbg("ERROR: Failed to allocate private data\n");
+ return -ENOMEM;
+ }
+
+ /* Check if the mount is still healthy */
+
+ nfs_semtake(nmp);
+ error = nfs_checkmount(nmp);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_checkmount failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Try to open an existing file at that path */
+
+ error = nfs_fileopen(nmp, np, relpath, oflags, mode);
+ if (error != OK)
+ {
+ /* An error occurred while trying to open the existing file. Check if
+ * the open failed because the file does not exist. That is not
+ * necessarily an error; that may only mean that we have to create the
+ * file.
+ */
+
+ if (error != ENOENT)
+ {
+ fdbg("ERROR: nfs_findnode failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* The file does not exist. Check if we were asked to create the file. If
+ * the O_CREAT bit is set in the oflags then we should create the file if it
+ * does not exist.
+ */
+
+ if ((oflags & O_CREAT) == 0)
+ {
+ /* Return ENOENT if the file does not exist and we were not asked
+ * to create it.
+ */
+
+ fdbg("ERROR: File does not exist\n");
+ error = ENOENT;
+ goto errout_with_semaphore;
+ }
+
+ /* Create the file */
+
+ error = nfs_filecreate(nmp, np, relpath, mode);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_filecreate failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+ }
+
+ /* Initialize the file private data (only need to initialize
+ * non-zero elements)
+ */
+
+ /* Attach the private data to the struct file instance */
+
+ filep->f_priv = np;
+
+ /* Then insert the new instance at the head of the list in the mountpoint
+ * tructure. It needs to be there (1) to handle error conditions that effect
+ * all files, and (2) to inform the umount logic that we are busy. We
+ * cannot unmount the file system if this list is not empty!
+ */
+
+ np->n_next = nmp->nm_head;
+ nmp->nm_head = np;
+
+ np->n_flags |= (NFSNODE_OPEN | NFSNODE_MODIFIED);
+ nfs_semgive(nmp);
+ return OK;
+
+errout_with_semaphore:
+ if (np)
+ {
+ kfree(np);
+ }
+ nfs_semgive(nmp);
+ return -error;
+}
+
+/****************************************************************************
+ * Name: nfs_close
+ *
+ * Description:
+ * Close a file.
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_close(FAR struct file *filep)
+{
+ FAR struct nfsmount *nmp;
+ FAR struct nfsnode *np;
+ FAR struct nfsnode *prev;
+ FAR struct nfsnode *curr;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ nmp = (struct nfsmount*) filep->f_inode->i_private;
+ np = (struct nfsnode*) filep->f_priv;
+
+ DEBUGASSERT(nmp != NULL);
+
+ /* Get exclusive access to the mount structure. */
+
+ nfs_semtake(nmp);
+
+ /* Find our file structure in the list of file structures containted in the
+ * mount structure.
+ */
+
+ for (prev = NULL, curr = nmp->nm_head; curr; prev = curr, curr = curr->n_next)
+ {
+ /* Check if this node is ours */
+
+ if (np == curr)
+ {
+ /* Yes.. remove it from the list of file structures */
+
+ if (prev)
+ {
+ /* Remove from mid-list */
+
+ prev->n_next = np->n_next;
+ }
+ else
+ {
+ /* Remove from the head of the list */
+
+ nmp->nm_head = np->n_next;
+ }
+
+ /* Then deallocate the file structure and return success */
+
+ kfree(np);
+ nfs_semgive(nmp);
+ return OK;
+ }
+ }
+
+ fdbg("ERROR: file structure not found in list: %p\n", np);
+ nfs_semgive(nmp);
+ return EINVAL;
+}
+
+/****************************************************************************
+ * Name: nfs_read
+ *
+ * Returned Value:
+ * The (non-negative) number of bytes read on success; a negated errno
+ * value on failure.
+ *
+ ****************************************************************************/
+
+static ssize_t nfs_read(FAR struct file *filep, char *buffer, size_t buflen)
+{
+ FAR struct nfsmount *nmp;
+ FAR struct nfsnode *np;
+ ssize_t readsize;
+ ssize_t tmp;
+ ssize_t bytesread;
+ size_t reqlen;
+ FAR uint32_t *ptr;
+ int error = 0;
+
+ fvdbg("Read %d bytes from offset %d\n", buflen, filep->f_pos);
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ nmp = (struct nfsmount*) filep->f_inode->i_private;
+ np = (struct nfsnode*) filep->f_priv;
+
+ DEBUGASSERT(nmp != NULL);
+
+ /* Make sure that the mount is still healthy */
+
+ nfs_semtake(nmp);
+ error = nfs_checkmount(nmp);
+ if (error != OK)
+ {
+ fdbg("nfs_checkmount failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Get the number of bytes left in the file and truncate read count so that
+ * it does not exceed the number of bytes left in the file.
+ */
+
+ tmp = np->n_size - filep->f_pos;
+ if (buflen > tmp)
+ {
+ buflen = tmp;
+ fvdbg("Read size truncated to %d\n", buflen);
+ }
+
+ /* Now loop until we fill the user buffer (or hit the end of the file) */
+
+ for (bytesread = 0; bytesread < buflen; )
+ {
+ /* Make sure that the attempted read size does not exceed the RPC maximum */
+
+ readsize = buflen;
+ if (readsize > nmp->nm_rsize)
+ {
+ readsize = nmp->nm_rsize;
+ }
+
+ /* Make sure that the attempted read size does not exceed the IO buffer size */
+
+ tmp = SIZEOF_rpc_reply_read(readsize);
+ if (tmp > nmp->nm_buflen)
+ {
+ readsize -= (tmp - nmp->nm_buflen);
+ }
+
+ /* Initialize the request */
+
+ ptr = (FAR uint32_t*)&nmp->nm_msgbuffer.read.read;
+ reqlen = 0;
+
+ /* Copy the variable length, file handle */
+
+ *ptr++ = txdr_unsigned((uint32_t)np->n_fhsize);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, &np->n_fhandle, np->n_fhsize);
+ reqlen += (int)np->n_fhsize;
+ ptr += uint32_increment((int)np->n_fhsize);
+
+ /* Copy the file offset */
+
+ txdr_hyper((uint64_t)filep->f_pos, ptr);
+ ptr += 2;
+ reqlen += 2*sizeof(uint32_t);
+
+ /* Set the readsize */
+
+ *ptr = txdr_unsigned(readsize);
+ reqlen += sizeof(uint32_t);
+
+ /* Perform the read */
+
+ fvdbg("Reading %d bytes\n", readsize);
+ nfs_statistics(NFSPROC_READ);
+ error = nfs_request(nmp, NFSPROC_READ,
+ (FAR void *)&nmp->nm_msgbuffer.read, reqlen,
+ (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
+ if (error)
+ {
+ fdbg("ERROR: nfs_request failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* The read was successful. Get a pointer to the beginning of the NFS
+ * response data.
+ */
+
+ ptr = (FAR uint32_t *)&((FAR struct rpc_reply_read *)nmp->nm_iobuffer)->read;
+
+ /* Check if attributes are included in the responses */
+
+ tmp = *ptr++;
+ if (*ptr != 0)
+ {
+ /* Yes... just skip over the attributes for now */
+
+ ptr += uint32_increment(sizeof(struct nfs_fattr));
+ }
+
+ /* This is followed by the count of data read. Isn't this
+ * the same as the length that is included in the read data?
+ *
+ * Just skip over if for now.
+ */
+
+ ptr++;
+
+ /* Next comes an EOF indication. Save that in tmp for now. */
+
+ tmp = *ptr++;
+
+ /* Then the length of the read data followed by the read data itself */
+
+ readsize = fxdr_unsigned(uint32_t, *ptr);
+ ptr++;
+
+ /* Copy the read data into the user buffer */
+
+ memcpy(buffer, ptr, readsize);
+
+ /* Update the read state data */
+
+ filep->f_pos += readsize;
+ bytesread += readsize;
+ buffer += readsize;
+
+ /* Check if we hit the end of file */
+
+ if (tmp != 0)
+ {
+ break;
+ }
+ }
+
+ fvdbg("Read %d bytes\n", bytesread);
+ nfs_semgive(nmp);
+ return bytesread;
+
+errout_with_semaphore:
+ nfs_semgive(nmp);
+ return -error;
+}
+
+/****************************************************************************
+ * Name: nfs_write
+ *
+ * Returned Value:
+ * The (non-negative) number of bytes written on success; a negated errno
+ * value on failure.
+ *
+ ****************************************************************************/
+
+static ssize_t nfs_write(FAR struct file *filep, const char *buffer,
+ size_t buflen)
+{
+ struct nfsmount *nmp;
+ struct nfsnode *np;
+ ssize_t writesize;
+ ssize_t bufsize;
+ ssize_t byteswritten;
+ size_t reqlen;
+ FAR uint32_t *ptr;
+ uint32_t tmp;
+ int commit = 0;
+ int committed = NFSV3WRITE_FILESYNC;
+ int error;
+
+ fvdbg("Write %d bytes to offset %d\n", buflen, filep->f_pos);
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ nmp = (struct nfsmount*)filep->f_inode->i_private;
+ np = (struct nfsnode*)filep->f_priv;
+
+ DEBUGASSERT(nmp != NULL);
+
+ /* Make sure that the mount is still healthy */
+
+ nfs_semtake(nmp);
+ error = nfs_checkmount(nmp);
+ if (error != OK)
+ {
+ fdbg("nfs_checkmount failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Check if the file size would exceed the range of off_t */
+
+ if (np->n_size + buflen < np->n_size)
+ {
+ error = EFBIG;
+ goto errout_with_semaphore;
+ }
+
+ /* Now loop until we send the entire user buffer */
+
+ writesize = 0;
+ for (byteswritten = 0; byteswritten < buflen; )
+ {
+ /* Make sure that the attempted write size does not exceed the RPC maximum */
+
+ writesize = buflen;
+ if (writesize > nmp->nm_wsize)
+ {
+ writesize = nmp->nm_wsize;
+ }
+
+ /* Make sure that the attempted read size does not exceed the IO buffer size */
+
+ bufsize = SIZEOF_rpc_call_write(writesize);
+ if (bufsize > nmp->nm_buflen)
+ {
+ writesize -= (bufsize - nmp->nm_buflen);
+ }
+
+ /* Initialize the request. Here we need an offset pointer to the write
+ * arguments, skipping over the RPC header. Write is unique among the
+ * RPC calls in that the entry RPC calls messasge lies in the I/O buffer
+ */
+
+ ptr = (FAR uint32_t *)&((FAR struct rpc_call_write *)nmp->nm_iobuffer)->write;
+ reqlen = 0;
+
+ /* Copy the variable length, file handle */
+
+ *ptr++ = txdr_unsigned((uint32_t)np->n_fhsize);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, &np->n_fhandle, np->n_fhsize);
+ reqlen += (int)np->n_fhsize;
+ ptr += uint32_increment((int)np->n_fhsize);
+
+ /* Copy the file offset */
+
+ txdr_hyper((uint64_t)filep->f_pos, ptr);
+ ptr += 2;
+ reqlen += 2*sizeof(uint32_t);
+
+ /* Copy the count and stable values */
+
+ *ptr++ = txdr_unsigned(buflen);
+ *ptr++ = txdr_unsigned(committed);
+ reqlen += 2*sizeof(uint32_t);
+
+ /* Copy a chunk of the user data into the I/O buffer */
+
+ *ptr++ = txdr_unsigned(buflen);
+ reqlen += sizeof(uint32_t);
+ memcpy(ptr, buffer, writesize);
+ reqlen += uint32_alignup(writesize);
+
+ /* Perform the write */
+
+ nfs_statistics(NFSPROC_WRITE);
+ error = nfs_request(nmp, NFSPROC_WRITE,
+ (FAR void *)nmp->nm_iobuffer, reqlen,
+ (FAR void *)&nmp->nm_msgbuffer.write, sizeof(struct rpc_reply_write));
+ if (error)
+ {
+ fdbg("ERROR: nfs_request failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Get a pointer to the WRITE reply data */
+
+ ptr = (FAR uint32_t *)&nmp->nm_msgbuffer.write.write;
+
+ /* Parse file_wcc. First, check if WCC attributes follow. */
+
+ tmp = *ptr++;
+ if (tmp != 0)
+ {
+ /* Yes.. WCC attributes follow. But we just skip over them. */
+
+ ptr += uint32_increment(sizeof(struct wcc_attr));
+ }
+
+ /* Check if normal file attributes follow */
+
+ tmp = *ptr++;
+ if (tmp != 0)
+ {
+ /* Yes.. Update the cached file status in the file structure. */
+
+ nfs_attrupdate(np, (FAR struct nfs_fattr *)ptr);
+ ptr += uint32_increment(sizeof(struct nfs_fattr));
+ }
+
+ /* Get the count of bytes actually written */
+
+ tmp = fxdr_unsigned(uint32_t, *ptr);
+ ptr++;
+
+ if (tmp < 1 || tmp > writesize)
+ {
+ error = EIO;
+ goto errout_with_semaphore;
+ }
+
+ writesize = tmp;
+
+ /* Determine the lowest committment level obtained by any of the RPCs. */
+
+ commit = *ptr++;
+ if (committed == NFSV3WRITE_FILESYNC)
+ {
+ committed = commit;
+ }
+ else if (committed == NFSV3WRITE_DATASYNC &&
+ commit == NFSV3WRITE_UNSTABLE)
+ {
+ committed = commit;
+ }
+
+ /* Update the read state data */
+
+ filep->f_pos += writesize;
+ byteswritten += writesize;
+ buffer += writesize;
+ }
+
+ nfs_semgive(nmp);
+ return writesize;
+
+errout_with_semaphore:
+ nfs_semgive(nmp);
+ return -error;
+}
+
+/****************************************************************************
+ * Name: nfs_opendir
+ *
+ * Description:
+ * Open a directory for read access
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_opendir(struct inode *mountpt, const char *relpath,
+ struct fs_dirent_s *dir)
+{
+ struct nfsmount *nmp;
+ struct file_handle fhandle;
+ struct nfs_fattr obj_attributes;
+ uint32_t objtype;
+ int error;
+
+ fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && dir);
+
+ /* Recover our private data from the inode instance */
+
+ nmp = mountpt->i_private;
+
+ /* Initialize the NFS-specific portions of dirent structure to zero */
+
+ memset(&dir->u.nfs, 0, sizeof(struct nfsdir_s));
+
+ /* Make sure that the mount is still healthy */
+
+ nfs_semtake(nmp);
+ error = nfs_checkmount(nmp);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_checkmount failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Find the NFS node associate with the path */
+
+ error = nfs_findnode(nmp, relpath, &fhandle, &obj_attributes, NULL);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_findnode failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* The entry is a directory */
+
+ objtype = fxdr_unsigned(uint32_t, obj_attributes.fa_type);
+ if (objtype != NFDIR)
+ {
+ fdbg("ERROR: Not a directory, type=%d\n", objtype);
+ error = ENOTDIR;
+ goto errout_with_semaphore;
+ }
+
+ /* Save the directory information in struct fs_dirent_s so that it can be
+ * used later when readdir() is called.
+ */
+
+ dir->u.nfs.nfs_fhsize = (uint8_t)fhandle.length;
+ DEBUGASSERT(fhandle.length <= DIRENT_NFS_MAXHANDLE);
+
+ memcpy(dir->u.nfs.nfs_fhandle, &fhandle.handle, DIRENT_NFS_MAXHANDLE);
+ error = OK;
+
+errout_with_semaphore:
+ nfs_semgive(nmp);
+ return -error;
+}
+
+/****************************************************************************
+ * Name: nfs_readdir
+ *
+ * Description: Read from directory
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
+{
+ struct nfsmount *nmp;
+ struct file_handle fhandle;
+ struct nfs_fattr obj_attributes;
+ uint32_t tmp;
+ uint32_t *ptr;
+ uint8_t *name;
+ unsigned int length;
+ int reqlen;
+ int error = 0;
+
+ fvdbg("Entry\n");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Recover our private data from the inode instance */
+
+ nmp = mountpt->i_private;
+
+ /* Make sure that the mount is still healthy */
+
+ nfs_semtake(nmp);
+ error = nfs_checkmount(nmp);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_checkmount failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Request a block directory entries, copying directory information from
+ * the dirent structure.
+ */
+
+ ptr = (FAR uint32_t*)&nmp->nm_msgbuffer.readdir.readdir;
+ reqlen = 0;
+
+ /* Copy the variable length, directory file handle */
+
+ *ptr++ = txdr_unsigned((uint32_t)dir->u.nfs.nfs_fhsize);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, dir->u.nfs.nfs_fhandle, dir->u.nfs.nfs_fhsize);
+ reqlen += (int)dir->u.nfs.nfs_fhsize;
+ ptr += uint32_increment((int)dir->u.nfs.nfs_fhsize);
+
+ /* Cookie and cookie verifier */
+
+ ptr[0] = dir->u.nfs.nfs_cookie[0];
+ ptr[1] = dir->u.nfs.nfs_cookie[1];
+ ptr += 2;
+ reqlen += 2*sizeof(uint32_t);
+
+ memcpy(ptr, dir->u.nfs.nfs_verifier, DIRENT_NFS_VERFLEN);
+ ptr += uint32_increment(DIRENT_NFS_VERFLEN);
+ reqlen += DIRENT_NFS_VERFLEN;
+
+ /* Number of directory entries (We currently only process one entry at a time) */
+
+ *ptr = txdr_unsigned(nmp->nm_readdirsize);
+ reqlen += sizeof(uint32_t);
+
+ /* And read the directory */
+
+ nfs_statistics(NFSPROC_READDIR);
+ error = nfs_request(nmp, NFSPROC_READDIR,
+ (FAR void *)&nmp->nm_msgbuffer.readdir, reqlen,
+ (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_request failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* A new group of entries was successfully read. Process the
+ * information contained in the response header. This information
+ * includes:
+ *
+ * 1) Attributes follow indication - 4 bytes
+ * 2) Directory attributes - sizeof(struct nfs_fattr)
+ * 3) Cookie verifier - NFSX_V3COOKIEVERF bytes
+ * 4) Values follows indication - 4 bytes
+ */
+
+ ptr = (uint32_t *)&((FAR struct rpc_reply_readdir *)nmp->nm_iobuffer)->readdir;
+
+ /* Check if attributes follow, if 0 so Skip over the attributes */
+
+ tmp = *ptr++;
+ if (tmp != 0)
+ {
+ /* Attributes are not currently used */
+
+ ptr += uint32_increment(sizeof(struct nfs_fattr));
+ }
+
+ /* Save the verification cookie */
+
+ memcpy(dir->u.nfs.nfs_verifier, ptr, DIRENT_NFS_VERFLEN);
+ ptr += uint32_increment(DIRENT_NFS_VERFLEN);
+
+ /* Check if values follow. If no values follow, then the EOF indication
+ * will appear next.
+ */
+
+ tmp = *ptr++;
+ if (tmp == 0)
+ {
+ /* No values follow, then the reply should consist only of a 4-byte
+ * end-of-directory indication.
+ */
+
+ tmp = *ptr++;
+ if (tmp != 0)
+ {
+ fvdbg("End of directory\n");
+ error = ENOENT;
+ }
+
+ /* What would it mean if there were not data and we not at the end of
+ * file?
+ */
+
+ else
+ {
+ fvdbg("No data but not end of directory???\n");
+ error = EAGAIN;
+ }
+
+ goto errout_with_semaphore;
+ }
+
+ /* If we are not at the end of the directory listing, then a set of entries
+ * will follow the header. Each entry is of the form:
+ *
+ * File ID (8 bytes)
+ * Name length (4 bytes)
+ * Name string (varaiable size but in multiples of 4 bytes)
+ * Cookie (8 bytes)
+ * next entry (4 bytes)
+ */
+
+ /* There is an entry. Skip over the file ID and point to the length */
+
+ ptr += 2;
+
+ /* Get the length and point to the name */
+
+ tmp = *ptr++;
+ length = fxdr_unsigned(uint32_t, tmp);
+ name = (uint8_t*)ptr;
+
+ /* Increment the pointer past the name (allowing for padding). ptr
+ * now points to the cookie.
+ */
+
+ ptr += uint32_increment(length);
+
+ /* Save the cookie and increment the pointer to the next entry */
+
+ dir->u.nfs.nfs_cookie[0] = *ptr++;
+ dir->u.nfs.nfs_cookie[1] = *ptr++;
+
+ ptr++; /* Just skip over the nextentry for now */
+
+ /* Return the name of the node to the caller */
+
+ if (length > NAME_MAX)
+ {
+ length = NAME_MAX;
+ }
+
+ memcpy(dir->fd_dir.d_name, name, length);
+ dir->fd_dir.d_name[length] = '\0';
+ fvdbg("name: \"%s\"\n", dir->fd_dir.d_name);
+
+ /* Get the file attributes associated with this name and return
+ * the file type.
+ */
+
+ fhandle.length = (uint32_t)dir->u.nfs.nfs_fhsize;
+ memcpy(&fhandle.handle, dir->u.nfs.nfs_fhandle, DIRENT_NFS_MAXHANDLE);
+
+ error = nfs_lookup(nmp, dir->fd_dir.d_name, &fhandle, &obj_attributes, NULL);
+ if (error != OK)
+ {
+ fdbg("nfs_lookup failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Set the dirent file type */
+
+ tmp = fxdr_unsigned(uint32_t, obj_attributes.fa_type);
+ switch (tmp)
+ {
+ default:
+ case NFNON: /* Unknown type */
+ case NFSOCK: /* Socket */
+ case NFLNK: /* Symbolic link */
+ break;
+
+ case NFREG: /* Regular file */
+ dir->fd_dir.d_type = DTYPE_FILE;
+ break;
+
+ case NFDIR: /* Directory */
+ dir->fd_dir.d_type = DTYPE_DIRECTORY;
+ break;
+
+ case NFBLK: /* Block special device file */
+ dir->fd_dir.d_type = DTYPE_BLK;
+ break;
+
+ case NFFIFO: /* Named FIFO */
+ case NFCHR: /* Character special device file */
+ dir->fd_dir.d_type = DTYPE_CHR;
+ break;
+ }
+ fvdbg("type: %d->%d\n", (int)tmp, dir->fd_dir.d_type);
+
+errout_with_semaphore:
+ nfs_semgive(nmp);
+ return -error;
+}
+
+/****************************************************************************
+ * Name: nfs_rewinddir
+ *
+ * Description:
+ * Reset the directory traveral logic to the first entry in the open
+ * directory.
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir)
+{
+ fvdbg("Entry\n");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && dir != NULL);
+
+ /* Reset the NFS-specific portions of dirent structure, retaining only the
+ * file handle.
+ */
+
+ memset(&dir->u.nfs.nfs_verifier, 0, DIRENT_NFS_VERFLEN);
+ dir->u.nfs.nfs_cookie[0] = 0;
+ dir->u.nfs.nfs_cookie[1] = 0;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nfs_decode_args
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void nfs_decode_args(FAR struct nfs_mount_parameters *nprmt,
+ FAR struct nfs_args *argp)
+{
+ int maxio;
+
+ /* Get the selected timeout value */
+
+ if ((argp->flags & NFSMNT_TIMEO) != 0 && argp->timeo > 0)
+ {
+ uint32_t tmp = ((uint32_t)argp->timeo * NFS_HZ + 5) / 10;
+ if (tmp < NFS_MINTIMEO)
+ {
+ tmp = NFS_MINTIMEO;
+ }
+ else if (tmp > NFS_MAXTIMEO)
+ {
+ tmp = NFS_MAXTIMEO;
+ }
+ nprmt->timeo = tmp;
+ }
+
+ /* Get the selected retransmission count */
+
+ if ((argp->flags & NFSMNT_RETRANS) != 0 && argp->retrans > 1)
+ {
+ if (argp->retrans < NFS_MAXREXMIT)
+ {
+ nprmt->retry = argp->retrans;
+ }
+ else
+ {
+ nprmt->retry = NFS_MAXREXMIT;
+ }
+ }
+
+ if ((argp->flags & NFSMNT_SOFT) == 0)
+ {
+ nprmt->retry = NFS_MAXREXMIT + 1; /* Past clip limit */
+ }
+
+ /* Get the maximum amount of data that can be transferred in one packet */
+
+ if ((argp->sotype == SOCK_DGRAM) != 0)
+ {
+ maxio = NFS_MAXDGRAMDATA;
+ }
+ else
+ {
+ fdbg("ERROR: Only SOCK_DRAM is supported\n");
+ maxio = NFS_MAXDATA;
+ }
+
+ /* Get the maximum amount of data that can be transferred in one write transfer */
+
+ if ((argp->flags & NFSMNT_WSIZE) != 0 && argp->wsize > 0)
+ {
+ nprmt->wsize = argp->wsize;
+
+ /* Round down to multiple of blocksize */
+
+ nprmt->wsize &= ~(NFS_FABLKSIZE - 1);
+ if (nprmt->wsize <= 0)
+ {
+ nprmt->wsize = NFS_FABLKSIZE;
+ }
+ }
+
+ if (nprmt->wsize > maxio)
+ {
+ nprmt->wsize = maxio;
+ }
+
+ if (nprmt->wsize > MAXBSIZE)
+ {
+ nprmt->wsize = MAXBSIZE;
+ }
+
+ /* Get the maximum amount of data that can be transferred in one read transfer */
+
+ if ((argp->flags & NFSMNT_RSIZE) != 0 && argp->rsize > 0)
+ {
+ nprmt->rsize = argp->rsize;
+
+ /* Round down to multiple of blocksize */
+
+ nprmt->rsize &= ~(NFS_FABLKSIZE - 1);
+ if (nprmt->rsize <= 0)
+ {
+ nprmt->rsize = NFS_FABLKSIZE;
+ }
+ }
+
+ if (nprmt->rsize > maxio)
+ {
+ nprmt->rsize = maxio;
+ }
+
+ if (nprmt->rsize > MAXBSIZE)
+ {
+ nprmt->rsize = MAXBSIZE;
+ }
+
+ /* Get the maximum amount of data that can be transferred in directory transfer */
+
+ if ((argp->flags & NFSMNT_READDIRSIZE) != 0 && argp->readdirsize > 0)
+ {
+ nprmt->readdirsize = argp->readdirsize;
+
+ /* Round down to multiple of blocksize */
+
+ nprmt->readdirsize &= ~(NFS_DIRBLKSIZ - 1);
+ if (nprmt->readdirsize < NFS_DIRBLKSIZ)
+ {
+ nprmt->readdirsize = NFS_DIRBLKSIZ;
+ }
+ }
+ else if (argp->flags & NFSMNT_RSIZE)
+ {
+ nprmt->readdirsize = nprmt->rsize;
+ }
+
+ if (nprmt->readdirsize > maxio)
+ {
+ nprmt->readdirsize = maxio;
+ }
+}
+
+/****************************************************************************
+ * Name: nfs_bind
+ *
+ * Description:
+ * This implements a portion of the mount operation. This function allocates
+ * and initializes the mountpoint private data and gets mount information
+ * from the NFS server. The final binding of the private data (containing
+ * NFS server mount information) to the mountpoint is performed by mount().
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_bind(FAR struct inode *blkdriver, FAR const void *data,
+ FAR void **handle)
+{
+ FAR struct nfs_args *argp = (FAR struct nfs_args *)data;
+ FAR struct nfsmount *nmp;
+ struct rpcclnt *rpc;
+ struct rpc_call_fs getattr;
+ struct rpc_reply_getattr resok;
+ struct nfs_mount_parameters nprmt;
+ uint32_t buflen;
+ uint32_t tmp;
+ int error = 0;
+
+ DEBUGASSERT(data && handle);
+
+ /* Set default values of the parameters. These may be overridden by
+ * settings in the argp->flags.
+ */
+
+ nprmt.timeo = NFS_TIMEO;
+ nprmt.retry = NFS_RETRANS;
+ nprmt.wsize = NFS_WSIZE;
+ nprmt.rsize = NFS_RSIZE;
+ nprmt.readdirsize = NFS_READDIRSIZE;
+
+ nfs_decode_args(&nprmt, argp);
+
+ /* Determine the size of a buffer that will hold one RPC data transfer.
+ * First, get the maximum size of a read and a write transfer */
+
+ buflen = SIZEOF_rpc_call_write(nprmt.wsize);
+ tmp = SIZEOF_rpc_reply_read(nprmt.rsize);
+
+ /* The buffer size will be the maximum of those two sizes */
+
+ if (tmp > buflen)
+ {
+ buflen = tmp;
+ }
+
+ /* But don't let the buffer size exceed the MSS of the socket type */
+
+ if (buflen > UIP_UDP_MSS)
+ {
+ buflen = UIP_UDP_MSS;
+ }
+
+ /* Create an instance of the mountpt state structure */
+
+ nmp = (FAR struct nfsmount *)kzalloc(SIZEOF_nfsmount(buflen));
+ if (!nmp)
+ {
+ fdbg("ERROR: Failed to allocate mountpoint structure\n");
+ return ENOMEM;
+ }
+
+ /* Save the allocated I/O buffer size */
+
+ nmp->nm_buflen = (uint16_t)buflen;
+
+ /* Initialize the allocated mountpt state structure. */
+
+ /* Initialize the semaphore that controls access. The initial count
+ * is zero, but nfs_semgive() is called at the completion of initialization,
+ * incrementing the count to one.
+ */
+
+ sem_init(&nmp->nm_sem, 0, 0); /* Initialize the semaphore that controls access */
+
+ /* Initialize NFS */
+
+ nfs_true = txdr_unsigned(TRUE);
+ nfs_false = txdr_unsigned(FALSE);
+ nfs_xdrneg1 = txdr_unsigned(-1);
+
+ rpcclnt_init();
+
+ /* Set initial values of other fields */
+
+ nmp->nm_timeo = nprmt.timeo;
+ nmp->nm_retry = nprmt.retry;
+ nmp->nm_wsize = nprmt.wsize;
+ nmp->nm_rsize = nprmt.rsize;
+ nmp->nm_readdirsize = nprmt.readdirsize;
+ nmp->nm_fhsize = NFSX_V3FHMAX;
+
+ strncpy(nmp->nm_path, argp->path, 90);
+ memcpy(&nmp->nm_nam, &argp->addr, argp->addrlen);
+
+ /* Set up the sockets and per-host congestion */
+
+ nmp->nm_sotype = argp->sotype;
+
+ if (nmp->nm_sotype == SOCK_DGRAM)
+ {
+ /* Connection-less... connect now */
+
+ /* Create an instance of the rpc state structure */
+
+ rpc = (struct rpcclnt *)kzalloc(sizeof(struct rpcclnt));
+ if (!rpc)
+ {
+ fdbg("ERROR: Failed to allocate rpc structure\n");
+ return ENOMEM;
+ }
+
+ fvdbg("Connecting\n");
+
+ /* Translate nfsmnt flags -> rpcclnt flags */
+
+ rpc->rc_path = nmp->nm_path;
+ rpc->rc_name = &nmp->nm_nam;
+ rpc->rc_sotype = nmp->nm_sotype;
+ rpc->rc_retry = nmp->nm_retry;
+
+ nmp->nm_rpcclnt = rpc;
+
+ error = rpcclnt_connect(nmp->nm_rpcclnt);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_connect failed: %d\n", error);
+ goto bad;
+ }
+ }
+
+ nmp->nm_mounted = true;
+ nmp->nm_so = nmp->nm_rpcclnt->rc_so;
+ memcpy(&nmp->nm_fh, &nmp->nm_rpcclnt->rc_fh, sizeof(nfsfh_t));
+
+ /* Get the file attributes */
+
+ getattr.fs.fsroot.length = txdr_unsigned(nmp->nm_fhsize);
+ memcpy(&getattr.fs.fsroot.handle, &nmp->nm_fh, sizeof(nfsfh_t));
+
+ error = nfs_request(nmp, NFSPROC_GETATTR,
+ (FAR void *)&getattr, sizeof(struct FS3args),
+ (FAR void*)&resok, sizeof(struct rpc_reply_getattr));
+ if (error)
+ {
+ fdbg("ERROR: nfs_request failed: %d\n", error);
+ goto bad;
+ }
+
+ /* Save the file attributes */
+
+ memcpy(&nmp->nm_fattr, &resok.attr, sizeof(struct nfs_fattr));
+
+ /* Mounted! */
+
+ *handle = (void*)nmp;
+ nfs_semgive(nmp);
+
+ fvdbg("Successfully mounted\n");
+ return OK;
+
+bad:
+ if (nmp)
+ {
+ /* Disconnect from the server */
+
+ rpcclnt_disconnect(nmp->nm_rpcclnt);
+
+ /* Free connection-related resources */
+
+ sem_destroy(&nmp->nm_sem);
+ if (nmp->nm_so)
+ {
+ kfree(nmp->nm_so);
+ }
+ if (nmp->nm_rpcclnt)
+ {
+ kfree(nmp->nm_rpcclnt);
+ }
+ kfree(nmp);
+ }
+ return error;
+}
+
+/****************************************************************************
+ * Name: nfs_unbind
+ *
+ * Description: This implements the filesystem portion of the umount
+ * operation.
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int nfs_unbind(FAR void *handle, FAR struct inode **blkdriver)
+{
+ FAR struct nfsmount *nmp = (FAR struct nfsmount *)handle;
+ int error;
+
+ fvdbg("Entry\n");
+ DEBUGASSERT(nmp);
+
+ /* Get exclusive access to the mount structure */
+
+ nfs_semtake(nmp);
+
+ /* Are there any open files? We can tell if there are open files by looking
+ * at the list of file structures in the mount structure. If this list
+ * not empty, then there are open files and we cannot unmount now (or a
+ * crash is sure to follow).
+ */
+
+ if (nmp->nm_head != NULL)
+ {
+ fdbg("ERROR; There are open files: %p\n", nmp->nm_head);
+ error = EBUSY;
+ goto errout_with_semaphore;
+ }
+
+ /* No open file... Umount the file system. */
+
+ error = rpcclnt_umount(nmp->nm_rpcclnt);
+ if (error)
+ {
+ fdbg("ERROR: rpcclnt_umount failed: %d\n", error);
+ }
+
+ /* Disconnect from the server */
+
+ rpcclnt_disconnect(nmp->nm_rpcclnt);
+
+ /* And free any allocated resources */
+
+ sem_destroy(&nmp->nm_sem);
+ kfree(nmp->nm_so);
+ kfree(nmp->nm_rpcclnt);
+ kfree(nmp);
+
+ return -error;
+
+errout_with_semaphore:
+ nfs_semgive(nmp);
+ return -error;
+}
+
+/****************************************************************************
+ * Name: nfs_fsinfo
+ *
+ * Description:
+ * Return information about root directory.
+ *
+ * Returned Value:
+ * 0 on success; positive errno value on failure
+ *
+ * Assumptions:
+ * The caller has exclusive access to the NFS mount structure
+ *
+ ****************************************************************************/
+
+int nfs_fsinfo(FAR struct nfsmount *nmp)
+{
+ struct rpc_call_fs fsinfo;
+ struct rpc_reply_fsinfo fsp;
+ uint32_t pref;
+ uint32_t max;
+ int error = 0;
+
+ fsinfo.fs.fsroot.length = txdr_unsigned(nmp->nm_fhsize);
+ fsinfo.fs.fsroot.handle = nmp->nm_fh;
+
+ /* Request FSINFO from the server */
+
+ nfs_statistics(NFSPROC_FSINFO);
+ error = nfs_request(nmp, NFSPROC_FSINFO,
+ (FAR void *)&fsinfo, sizeof(struct FS3args),
+ (FAR void *)&fsp, sizeof(struct rpc_reply_fsinfo));
+ if (error)
+ {
+ return error;
+ }
+
+ /* Save the root file system attributes */
+
+//memcpy(&nmp->nm_fattr. &fsp.obj_attributes, sizeof(struct nfs_fattr));
+
+ pref = fxdr_unsigned(uint32_t, fsp.fsinfo.fs_wtpref);
+ if (pref < nmp->nm_wsize)
+ {
+ nmp->nm_wsize = (pref + NFS_FABLKSIZE - 1) & ~(NFS_FABLKSIZE - 1);
+ }
+
+ max = fxdr_unsigned(uint32_t, fsp.fsinfo.fs_wtmax);
+ if (max < nmp->nm_wsize)
+ {
+ nmp->nm_wsize = max & ~(NFS_FABLKSIZE - 1);
+ if (nmp->nm_wsize == 0)
+ {
+ nmp->nm_wsize = max;
+ }
+ }
+
+ pref = fxdr_unsigned(uint32_t, fsp.fsinfo.fs_rtpref);
+ if (pref < nmp->nm_rsize)
+ {
+ nmp->nm_rsize = (pref + NFS_FABLKSIZE - 1) & ~(NFS_FABLKSIZE - 1);
+ }
+
+ max = fxdr_unsigned(uint32_t, fsp.fsinfo.fs_rtmax);
+ if (max < nmp->nm_rsize)
+ {
+ nmp->nm_rsize = max & ~(NFS_FABLKSIZE - 1);
+ if (nmp->nm_rsize == 0)
+ {
+ nmp->nm_rsize = max;
+ }
+ }
+
+ pref = fxdr_unsigned(uint32_t, fsp.fsinfo.fs_dtpref);
+ if (pref < nmp->nm_readdirsize)
+ {
+ nmp->nm_readdirsize = (pref + NFS_DIRBLKSIZ - 1) & ~(NFS_DIRBLKSIZ - 1);
+ }
+
+ if (max < nmp->nm_readdirsize)
+ {
+ nmp->nm_readdirsize = max & ~(NFS_DIRBLKSIZ - 1);
+ if (nmp->nm_readdirsize == 0)
+ {
+ nmp->nm_readdirsize = max;
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nfs_statfs
+ *
+ * Description:
+ * Return filesystem statistics
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_statfs(FAR struct inode *mountpt, FAR struct statfs *sbp)
+{
+ FAR struct nfsmount *nmp;
+ FAR struct rpc_call_fs *fsstat;
+ FAR struct rpc_reply_fsstat *sfp;
+ int error = 0;
+ uint64_t tquad;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ nmp = (struct nfsmount*)mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ nfs_semtake(nmp);
+ error = nfs_checkmount(nmp);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_checkmount failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Fill in the statfs info */
+
+ sbp->f_type = NFS_SUPER_MAGIC;
+
+ (void)nfs_fsinfo(nmp);
+
+ fsstat = &nmp->nm_msgbuffer.fsstat;
+ fsstat->fs.fsroot.length = txdr_unsigned(nmp->nm_fhsize);
+ memcpy(&fsstat->fs.fsroot.handle, &nmp->nm_fh, sizeof(nfsfh_t));
+
+ nfs_statistics(NFSPROC_FSSTAT);
+ error = nfs_request(nmp, NFSPROC_FSSTAT,
+ (FAR void *)fsstat, sizeof(struct FS3args),
+ (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
+ if (error)
+ {
+ goto errout_with_semaphore;
+ }
+
+ sfp = (FAR struct rpc_reply_fsstat *)nmp->nm_iobuffer;
+ sbp->f_bsize = NFS_FABLKSIZE;
+ tquad = fxdr_hyper(&sfp->fsstat.sf_tbytes);
+ sbp->f_blocks = tquad / (uint64_t) NFS_FABLKSIZE;
+ tquad = fxdr_hyper(&sfp->fsstat.sf_fbytes);
+ sbp->f_bfree = tquad / (uint64_t) NFS_FABLKSIZE;
+ tquad = fxdr_hyper(&sfp->fsstat.sf_abytes);
+ sbp->f_bavail = tquad / (uint64_t) NFS_FABLKSIZE;
+ tquad = fxdr_hyper(&sfp->fsstat.sf_tfiles);
+ sbp->f_files = tquad;
+ tquad = fxdr_hyper(&sfp->fsstat.sf_ffiles);
+ sbp->f_ffree = tquad;
+ sbp->f_namelen = NAME_MAX;
+
+errout_with_semaphore:
+ nfs_semgive(nmp);
+ return -error;
+}
+
+/****************************************************************************
+ * Name: nfs_remove
+ *
+ * Description:
+ * Remove a file
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_remove(struct inode *mountpt, const char *relpath)
+{
+ FAR struct nfsmount *nmp;
+ struct file_handle fhandle;
+ struct nfs_fattr fattr;
+ char filename[NAME_MAX + 1];
+ FAR uint32_t *ptr;
+ int namelen;
+ int reqlen;
+ int error;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ nmp = (struct nfsmount*)mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ nfs_semtake(nmp);
+ error = nfs_checkmount(nmp);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_checkmount failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Find the NFS node of the directory containing the file to be deleted */
+
+ error = nfs_finddir(nmp, relpath, &fhandle, &fattr, filename);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_finddir returned: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Create the REMOVE RPC call arguments */
+
+ ptr = (FAR uint32_t *)&nmp->nm_msgbuffer.removef.remove;
+ reqlen = 0;
+
+ /* Copy the variable length, directory file handle */
+
+ *ptr++ = txdr_unsigned(fhandle.length);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, &fhandle.handle, fhandle.length);
+ reqlen += (int)fhandle.length;
+ ptr += uint32_increment(fhandle.length);
+
+ /* Copy the variable-length file name */
+
+ namelen = strlen(filename);
+
+ *ptr++ = txdr_unsigned(namelen);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, filename, namelen);
+ reqlen += uint32_alignup(namelen);
+
+ /* Perform the REMOVE RPC call */
+
+ nfs_statistics(NFSPROC_REMOVE);
+ error = nfs_request(nmp, NFSPROC_REMOVE,
+ (FAR void *)&nmp->nm_msgbuffer.removef, reqlen,
+ (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
+
+errout_with_semaphore:
+ nfs_semgive(nmp);
+ return -error;
+}
+
+/****************************************************************************
+ * Name: nfs_mkdir
+ *
+ * Description:
+ * Create a directory
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_mkdir(struct inode *mountpt, const char *relpath, mode_t mode)
+{
+ struct nfsmount *nmp;
+ struct file_handle fhandle;
+ struct nfs_fattr fattr;
+ char dirname[NAME_MAX + 1];
+ FAR uint32_t *ptr;
+ uint32_t tmp;
+ int namelen;
+ int reqlen;
+ int error;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ nmp = (struct nfsmount*) mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ nfs_semtake(nmp);
+ error = nfs_checkmount(nmp);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_checkmount: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Find the NFS node of the directory containing the directory to be created */
+
+ error = nfs_finddir(nmp, relpath, &fhandle, &fattr, dirname);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_finddir returned: %d\n", error);
+ return error;
+ }
+
+ /* Format the MKDIR call message arguments */
+
+ ptr = (FAR uint32_t *)&nmp->nm_msgbuffer.mkdir.mkdir;
+ reqlen = 0;
+
+ /* Copy the variable length, directory file handle */
+
+ *ptr++ = txdr_unsigned(fhandle.length);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, &fhandle.handle, fhandle.length);
+ ptr += uint32_increment(fhandle.length);
+ reqlen += (int)fhandle.length;
+
+ /* Copy the variable-length directory name */
+
+ namelen = strlen(dirname);
+
+ *ptr++ = txdr_unsigned(namelen);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, dirname, namelen);
+ ptr += uint32_increment(namelen);
+ reqlen += uint32_alignup(namelen);
+
+ /* Set the mode. NOTE: Here we depend on the fact that the NuttX and NFS
+ * bit settings are the same (at least for the bits of interest).
+ */
+
+ *ptr++ = nfs_true; /* True: mode value follows */
+ reqlen += sizeof(uint32_t);
+
+ tmp = mode & (NFSMODE_IXOTH | NFSMODE_IWOTH | NFSMODE_IROTH |
+ NFSMODE_IXGRP | NFSMODE_IWGRP | NFSMODE_IRGRP |
+ NFSMODE_IXUSR | NFSMODE_IWUSR | NFSMODE_IRUSR);
+ *ptr++ = txdr_unsigned(tmp);
+ reqlen += sizeof(uint32_t);
+
+ /* Set the user ID to zero */
+
+ *ptr++ = nfs_true; /* True: Uid value follows */
+ *ptr++ = 0; /* UID = 0 (nobody) */
+ reqlen += 2*sizeof(uint32_t);
+
+ /* Set the group ID to one */
+
+ *ptr++ = nfs_true; /* True: Gid value follows */
+ *ptr++ = HTONL(1); /* GID = 1 (nogroup) */
+ reqlen += 2*sizeof(uint32_t);
+
+ /* No size */
+
+ *ptr++ = nfs_false; /* False: No size value follows */
+ reqlen += sizeof(uint32_t);
+
+ /* Don't change times */
+
+ *ptr++ = HTONL(NFSV3SATTRTIME_DONTCHANGE); /* Don't change atime */
+ *ptr++ = HTONL(NFSV3SATTRTIME_DONTCHANGE); /* Don't change mtime */
+ reqlen += 2*sizeof(uint32_t);
+
+ /* Perform the MKDIR RPC */
+
+ nfs_statistics(NFSPROC_MKDIR);
+ error = nfs_request(nmp, NFSPROC_MKDIR,
+ (FAR void *)&nmp->nm_msgbuffer.mkdir, reqlen,
+ (FAR void *)&nmp->nm_iobuffer, nmp->nm_buflen);
+ if (error)
+ {
+ fdbg("ERROR: nfs_request failed: %d\n", error);
+ }
+
+errout_with_semaphore:
+ nfs_semgive(nmp);
+ return -error;
+}
+
+/****************************************************************************
+ * Name: nfs_rmdir
+ *
+ * Description:
+ * Remove a directory
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_rmdir(struct inode *mountpt, const char *relpath)
+{
+ struct nfsmount *nmp;
+ struct file_handle fhandle;
+ struct nfs_fattr fattr;
+ char dirname[NAME_MAX + 1];
+ FAR uint32_t *ptr;
+ int namelen;
+ int reqlen;
+ int error;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ nmp = (struct nfsmount *)mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ nfs_semtake(nmp);
+ error = nfs_checkmount(nmp);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_checkmount failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Find the NFS node of the directory containing the directory to be removed */
+
+ error = nfs_finddir(nmp, relpath, &fhandle, &fattr, dirname);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_finddir returned: %d\n", error);
+ return error;
+ }
+
+ /* Set up the RMDIR call message arguments */
+
+ ptr = (FAR uint32_t *)&nmp->nm_msgbuffer.rmdir.rmdir;
+ reqlen = 0;
+
+ /* Copy the variable length, directory file handle */
+
+ *ptr++ = txdr_unsigned(fhandle.length);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, &fhandle.handle, fhandle.length);
+ reqlen += (int)fhandle.length;
+ ptr += uint32_increment(fhandle.length);
+
+ /* Copy the variable-length directory name */
+
+ namelen = strlen(dirname);
+
+ *ptr++ = txdr_unsigned(namelen);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, dirname, namelen);
+ reqlen += uint32_alignup(namelen);
+
+ /* Perform the RMDIR RPC */
+
+ nfs_statistics(NFSPROC_RMDIR);
+ error = nfs_request(nmp, NFSPROC_RMDIR,
+ (FAR void *)&nmp->nm_msgbuffer.rmdir, reqlen,
+ (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
+
+errout_with_semaphore:
+ nfs_semgive(nmp);
+ return -error;
+}
+
+/****************************************************************************
+ * Name: nfs_rename
+ *
+ * Description:
+ * Rename a file or directory
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_rename(struct inode *mountpt, const char *oldrelpath,
+ const char *newrelpath)
+{
+ struct nfsmount *nmp;
+ struct file_handle from_handle;
+ struct file_handle to_handle;
+ char from_name[NAME_MAX+1];
+ char to_name[NAME_MAX+1];
+ struct nfs_fattr fattr;
+ FAR uint32_t *ptr;
+ int namelen;
+ int reqlen;
+ int error;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ nmp = (struct nfsmount *)mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ nfs_semtake(nmp);
+ error = nfs_checkmount(nmp);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_checkmount returned: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Find the NFS node of the directory containing the 'from' object */
+
+ error = nfs_finddir(nmp, oldrelpath, &from_handle, &fattr, from_name);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_finddir returned: %d\n", error);
+ return error;
+ }
+
+ /* Find the NFS node of the directory containing the 'from' object */
+
+ error = nfs_finddir(nmp, newrelpath, &to_handle, &fattr, to_name);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_finddir returned: %d\n", error);
+ return error;
+ }
+
+ /* Format the RENAME RPC arguments */
+
+ ptr = (FAR uint32_t *)&nmp->nm_msgbuffer.renamef.rename;
+ reqlen = 0;
+
+ /* Copy the variable length, 'from' directory file handle */
+
+ *ptr++ = txdr_unsigned(from_handle.length);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, &from_handle.handle, from_handle.length);
+ reqlen += (int)from_handle.length;
+ ptr += uint32_increment(from_handle.length);
+
+ /* Copy the variable-length 'from' object name */
+
+ namelen = strlen(from_name);
+
+ *ptr++ = txdr_unsigned(namelen);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, from_name, namelen);
+ reqlen += uint32_alignup(namelen);
+ ptr += uint32_increment(namelen);
+
+ /* Copy the variable length, 'to' directory file handle */
+
+ *ptr++ = txdr_unsigned(to_handle.length);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, &to_handle.handle, to_handle.length);
+ ptr += uint32_increment(to_handle.length);
+ reqlen += (int)to_handle.length;
+
+ /* Copy the variable-length 'to' object name */
+
+ namelen = strlen(to_name);
+
+ *ptr++ = txdr_unsigned(namelen);
+ reqlen += sizeof(uint32_t);
+
+ memcpy(ptr, to_name, namelen);
+ reqlen += uint32_alignup(namelen);
+
+ /* Perform the RENAME RPC */
+
+ nfs_statistics(NFSPROC_RENAME);
+ error = nfs_request(nmp, NFSPROC_RENAME,
+ (FAR void *)&nmp->nm_msgbuffer.renamef, reqlen,
+ (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
+
+errout_with_semaphore:
+ nfs_semgive(nmp);
+ return -error;
+}
+
+/****************************************************************************
+ * Name: nfs_stat
+ *
+ * Description:
+ * Return information about the file system object at 'relpath'
+ *
+ * Returned Value:
+ * 0 on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int nfs_stat(struct inode *mountpt, const char *relpath,
+ struct stat *buf)
+{
+ struct nfsmount *nmp;
+ struct file_handle fhandle;
+ struct nfs_fattr obj_attributes;
+ uint32_t tmp;
+ uint32_t mode;
+ int error;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ nmp = (struct nfsmount*)mountpt->i_private;
+ DEBUGASSERT(nmp && buf);
+
+ /* Check if the mount is still healthy */
+
+ nfs_semtake(nmp);
+ error = nfs_checkmount(nmp);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_checkmount failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Get the file handle attributes of the requested node */
+
+ error = nfs_findnode(nmp, relpath, &fhandle, &obj_attributes, NULL);
+ if (error != OK)
+ {
+ fdbg("ERROR: nfs_findnode failed: %d\n", error);
+ goto errout_with_semaphore;
+ }
+
+ /* Construct the file mode. This is a 32-bit, encoded value containing
+ * both the access mode and the file type.
+ */
+
+ tmp = fxdr_unsigned(uint32_t, obj_attributes.fa_mode);
+
+ /* Here we exploit the fact that most mode bits are the same in NuttX
+ * as in the NFSv3 spec.
+ */
+
+ mode = tmp & (NFSMODE_IXOTH|NFSMODE_IWOTH|NFSMODE_IROTH|
+ NFSMODE_IXGRP|NFSMODE_IWGRP|NFSMODE_IRGRP|
+ NFSMODE_IXUSR|NFSMODE_IWUSR|NFSMODE_IRUSR);
+
+ /* Handle the cases that are not the same */
+
+ if ((mode & NFSMODE_ISGID) != 0)
+ {
+ mode |= S_ISGID;
+ }
+
+ if ((mode & NFSMODE_ISUID) != 0)
+ {
+ mode |= S_ISUID;
+ }
+
+ /* Now OR in the file type */
+
+ tmp = fxdr_unsigned(uint32_t, obj_attributes.fa_type);
+ switch (tmp)
+ {
+ default:
+ case NFNON: /* Unknown type */
+ break;
+
+ case NFREG: /* Regular file */
+ mode |= S_IFREG;
+ break;
+
+ case NFDIR: /* Directory */
+ mode |= S_IFDIR;
+ break;
+
+ case NFBLK: /* Block special device file */
+ mode |= S_IFBLK;
+ break;
+
+ case NFCHR: /* Character special device file */
+ mode |= S_IFCHR;
+ break;
+
+ case NFLNK: /* Symbolic link */
+ mode |= S_IFLNK;
+ break;
+
+ case NFSOCK: /* Socket */
+ mode |= S_IFSOCK;
+ break;
+
+ case NFFIFO: /* Named pipe */
+ mode |= S_IFMT;
+ break;
+ }
+
+ buf->st_mode = mode;
+ buf->st_size = fxdr_hyper(&obj_attributes.fa_size);
+ buf->st_blksize = 0;
+ buf->st_blocks = 0;
+ buf->st_mtime = fxdr_hyper(&obj_attributes.fa_mtime);
+ buf->st_atime = fxdr_hyper(&obj_attributes.fa_atime);
+ buf->st_ctime = fxdr_hyper(&obj_attributes.fa_ctime);
+
+errout_with_semaphore:
+ nfs_semgive(nmp);
+ return -error;
+}
diff --git a/nuttx/fs/nfs/rpc.h b/nuttx/fs/nfs/rpc.h
new file mode 100644
index 000000000..27366557d
--- /dev/null
+++ b/nuttx/fs/nfs/rpc.h
@@ -0,0 +1,491 @@
+/****************************************************************************
+ * fs/nfs/rpc.h
+ *
+ * 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) 2003
+ * the regents of the university of michigan
+ * all rights reserved
+ *
+ * permission is granted to use, copy, create derivative works and redistribute
+ * this software and such derivative works for any purpose, so long as the name
+ * of the university of michigan is not used in any advertising or publicity
+ * pertaining to the use or distribution of this software without specific,
+ * written prior authorization. if the above copyright notice or any other
+ * identification of the university of michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must also be included.
+ *
+ * this software is provided as is, without representation from the university
+ * of michigan as to its fitness for any purpose, and without warranty by the
+ * university of michigan of any kind, either express or implied, including
+ * without limitation the implied warranties of merchantability and fitness for
+ * a particular purpose. the regents of the university of michigan shall not be
+ * liable for any damages, including special, indirect, incidental, or
+ * consequential damages, with respect to any claim arising out of or in
+ * connection with the use of the software, even if it has been or is hereafter
+ * advised of the possibility of such damages.
+ *
+ * Copyright (c) 1989, 1993
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_NFS_RPC_H
+#define __FS_NFS_RPC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <sys/types.h>
+#include "nfs_proto.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Version # */
+
+#define RPC_VER2 2
+
+/* Authentication */
+
+#define RPCAUTH_NULL 0
+#define RPCAUTH_UNIX 1
+#define RPCAUTH_SHORT 2
+#define RPCAUTH_KERB4 4
+#define RPCAUTH_MAXSIZ 400
+#define RPCVERF_MAXSIZ 12
+ /* For Kerb, can actually be 400 */
+#define RPCAUTH_UNIXGIDS 16
+
+/* Constants associated with authentication flavours. */
+
+#define RPCAKN_FULLNAME 0
+#define RPCAKN_NICKNAME 1
+
+/* RPC Constants */
+
+#define RPC_CALL 0
+#define RPC_REPLY 1
+#define RPC_MSGACCEPTED 0
+#define RPC_MSGDENIED 1
+#define RPC_PROGUNAVAIL 1
+#define RPC_PROGMISMATCH 2
+#define RPC_PROCUNAVAIL 3
+#define RPC_GARBAGE 4
+
+#define RPC_MISMATCH 0
+#define RPC_AUTHERR 1
+
+/* Authentication failures */
+
+#define AUTH_BADCRED 1
+#define AUTH_REJECTCRED 2
+#define AUTH_BADVERF 3
+#define AUTH_REJECTVERF 4
+#define AUTH_TOOWEAK 5
+
+/* Sizes of RPC header parts */
+
+#define RPC_SIZ 24
+#define RPC_REPLYSIZ 28
+
+/* RPC Prog definitions */
+
+#define RPCPROG_MNT 100005
+#define RPCMNT_VER1 1
+#define RPCMNT_VER3 3
+#define RPCMNT_MOUNT 1
+#define RPCMNT_DUMP 2
+#define RPCMNT_UMOUNT 3
+#define RPCMNT_UMNTALL 4
+#define RPCMNT_EXPORT 5
+#define RPCMNT_NAMELEN 255
+#define RPCMNT_PATHLEN 1024
+#define RPCPROG_NFS 100003
+
+/* RPC definitions for the portmapper. */
+
+#define PMAPPORT 111
+#define PMAPPROG 100000
+#define PMAPVERS 2
+
+#define PMAPPROC_NULL 0
+#define PMAPPROC_SET 1
+#define PMAPPROC_UNSET 2
+#define PMAPPROC_GETPORT 3
+#define PMAPPROC_DUMP 4
+#define PMAPPROC_CALLIT 5
+
+#define RPC_SUCCESS 0
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Global RPC statistics */
+
+#ifdef CONFIG_NFS_STATISTICS
+struct rpcstats
+{
+ int rpcretries;
+ int rpcrequests;
+ int rpctimeouts;
+ int rpcinvalid;
+};
+#endif
+
+/* PMAP headers */
+
+struct call_args_pmap
+{
+ uint32_t prog;
+ uint32_t vers;
+ uint32_t proc;
+ uint32_t port;
+};
+
+struct call_result_pmap
+{
+ uint32_t port;
+};
+
+/* MOUNTD headers */
+
+struct call_args_mount
+{
+ uint32_t len;
+ char rpath[90];
+};
+
+struct call_args_umount
+{
+ uint32_t len;
+ char rpath[90];
+};
+
+struct call_result_mount
+{
+ uint32_t status;
+ nfsfh_t fhandle;
+};
+
+/* Generic RPC call headers */
+
+enum auth_flavor
+{
+ AUTH_NONE = 0,
+ AUTH_SYS = 1,
+ AUTH_SHORT = 2
+ /* and more to be defined */
+};
+
+struct rpc_auth_info
+{
+ uint32_t authtype; /* auth type */
+ uint32_t authlen; /* auth length */
+};
+
+struct auth_unix
+{
+ int32_t stamp;
+ uint8_t hostname; /* null */
+ int32_t uid;
+ int32_t gid;
+ int32_t gidlist; /* null */
+};
+
+struct rpc_call_header
+{
+ uint32_t rp_xid; /* request transaction id */
+ int32_t rp_direction; /* call direction (0) */
+ uint32_t rp_rpcvers; /* RPC version (2) */
+ uint32_t rp_prog; /* program */
+ uint32_t rp_vers; /* version */
+ uint32_t rp_proc; /* procedure */
+ struct rpc_auth_info rpc_auth;
+ struct rpc_auth_info rpc_verf;
+};
+
+struct rpc_call_pmap
+{
+ struct rpc_call_header ch;
+ struct call_args_pmap pmap;
+};
+
+struct rpc_call_mount
+{
+ struct rpc_call_header ch;
+ struct call_args_mount mount;
+};
+
+struct rpc_call_umount
+{
+ struct rpc_call_header ch;
+ struct call_args_umount umount;
+};
+
+struct rpc_call_create
+{
+ struct rpc_call_header ch;
+ struct CREATE3args create;
+};
+
+struct rpc_call_lookup
+{
+ struct rpc_call_header ch;
+ struct LOOKUP3args lookup;
+};
+#define SIZEOF_rpc_call_lookup(n) (sizeof(struct rpc_call_header) + SIZEOF_LOOKUP3args(n))
+
+struct rpc_call_read
+{
+ struct rpc_call_header ch;
+ struct READ3args read;
+};
+
+struct rpc_call_write
+{
+ struct rpc_call_header ch;
+ struct WRITE3args write; /* Variable length */
+};
+#define SIZEOF_rpc_call_write(n) (sizeof(struct rpc_call_header) + SIZEOF_WRITE3args(n))
+
+struct rpc_call_remove
+{
+ struct rpc_call_header ch;
+ struct REMOVE3args remove;
+};
+
+struct rpc_call_rename
+{
+ struct rpc_call_header ch;
+ struct RENAME3args rename;
+};
+
+struct rpc_call_mkdir
+{
+ struct rpc_call_header ch;
+ struct MKDIR3args mkdir;
+};
+
+struct rpc_call_rmdir
+{
+ struct rpc_call_header ch;
+ struct RMDIR3args rmdir;
+};
+
+struct rpc_call_readdir
+{
+ struct rpc_call_header ch;
+ struct READDIR3args readdir;
+};
+
+struct rpc_call_setattr
+{
+ struct rpc_call_header ch;
+ struct SETATTR3args setattr;
+};
+
+struct rpc_call_fs
+{
+ struct rpc_call_header ch;
+ struct FS3args fs;
+};
+
+/* Generic RPC reply headers */
+
+struct rpc_reply_header
+{
+ uint32_t rp_xid; /* Request transaction id */
+ uint32_t rp_direction; /* Call direction (1) */
+ uint32_t type;
+ struct rpc_auth_info rpc_verfi;
+ uint32_t status;
+};
+
+struct nfs_reply_header
+{
+ uint32_t rp_xid; /* Request transaction id */
+ uint32_t rp_direction; /* Call direction (1) */
+ uint32_t type;
+ struct rpc_auth_info rpc_verfi;
+ uint32_t status;
+ uint32_t nfs_status;
+};
+
+struct rpc_reply_pmap
+{
+ struct rpc_reply_header rh;
+ struct call_result_pmap pmap;
+};
+
+struct rpc_reply_mount
+{
+ struct rpc_reply_header rh;
+ struct call_result_mount mount;
+};
+
+struct rpc_reply_umount
+{
+ struct rpc_reply_header rh;
+};
+
+struct rpc_reply_create
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct CREATE3resok create;
+};
+
+struct rpc_reply_lookup
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct LOOKUP3resok lookup;
+};
+
+struct rpc_reply_write
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct WRITE3resok write; /* Variable length */
+};
+
+struct rpc_reply_read
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct READ3resok read; /* Variable length */
+};
+#define SIZEOF_rpc_reply_read(n) (sizeof(struct rpc_reply_header) + sizeof(uint32_t) + SIZEOF_READ3resok(n))
+
+struct rpc_reply_remove
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct REMOVE3resok remove;
+};
+
+struct rpc_reply_rename
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct RENAME3resok rename;
+};
+
+struct rpc_reply_mkdir
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct MKDIR3resok mkdir;
+};
+
+struct rpc_reply_rmdir
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct RMDIR3resok rmdir;
+};
+
+struct rpc_reply_readdir
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct READDIR3resok readdir;
+};
+
+struct rpc_reply_fsinfo
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct nfsv3_fsinfo fsinfo;
+};
+
+struct rpc_reply_fsstat
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct nfs_statfs fsstat;
+};
+
+struct rpc_reply_getattr
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct nfs_fattr attr;
+};
+
+struct rpc_reply_setattr
+{
+ struct rpc_reply_header rh;
+ uint32_t status;
+ struct SETATTR3resok setattr;
+};
+
+struct rpcclnt
+{
+ nfsfh_t rc_fh; /* File handle of the root directory */
+ char *rc_path; /* Server's path of the mounted directory */
+
+ struct sockaddr *rc_name;
+ struct socket *rc_so; /* RPC socket */
+
+ bool rc_timeout; /* Receipt of reply timed out */
+ uint8_t rc_sotype; /* Type of socket */
+ uint8_t rc_retry; /* Max retries */
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+void rpcclnt_init(void);
+int rpcclnt_connect(FAR struct rpcclnt *rpc);
+int rpcclnt_reconnect(FAR struct rpcclnt *rpc);
+void rpcclnt_disconnect(FAR struct rpcclnt *rpc);
+int rpcclnt_umount(FAR struct rpcclnt *rpc);
+void rpcclnt_safedisconnect(FAR struct rpcclnt *rpc);
+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);
+
+#endif /* __FS_NFS_RPC_H */
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;
+}
diff --git a/nuttx/fs/nfs/xdr_subs.h b/nuttx/fs/nfs/xdr_subs.h
new file mode 100644
index 000000000..891af004c
--- /dev/null
+++ b/nuttx/fs/nfs/xdr_subs.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+ * fs/nfs/xdr_subs.h
+ * Definitions for Sun RPC Version 2, from
+ * "RPC: Remote Procedure Call Protocol Specification" RFC1057
+ *
+ * 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) 1989, 1993
+ * 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. 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_NFS_XDR_SUBS_H
+#define __FS_NFS_XDR_SUBS_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <arpa/inet.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Macros used for conversion to/from xdr representation by nfs...
+ * These use the MACHINE DEPENDENT routines ntohl, htonl
+ * As defined by "XDR: External Data Representation Standard" RFC1014
+ *
+ * To simplify the implementation, we use ntohl/htonl even on big-endian
+ * machines, and count on them being `#define'd away. Some of these
+ * might be slightly more efficient as int64_t copies on a big-endian,
+ * but we cannot count on their alignment anyway.
+ */
+
+#define fxdr_unsigned(t, v) ((t)ntohl(v))
+#define txdr_unsigned(v) (htonl(v))
+
+#define fxdr_nfsv2time(f, t) \
+{ \
+ (t)->tv_sec = ntohl(((struct nfsv2_time *)(f))->nfsv2_sec); \
+ if (((struct nfsv2_time *)(f))->nfsv2_usec != 0xffffffff) \
+ (t)->tv_nsec = 1000 * ntohl(((struct nfsv2_time *)(f))->nfsv2_usec); \
+ else \
+ (t)->tv_nsec = 0; \
+}
+
+#define txdr_nfsv2time(f, t) \
+{ \
+ ((struct nfsv2_time *)(t))->nfsv2_sec = htonl((f)->tv_sec); \
+ if ((f)->tv_nsec != -1) \
+ ((struct nfsv2_time *)(t))->nfsv2_usec = htonl((f)->tv_nsec / 1000); \
+ else \
+ ((struct nfsv2_time *)(t))->nfsv2_usec = 0xffffffff; \
+}
+
+#define fxdr_nfsv3time(f, t) \
+{ \
+ (t)->tv_sec = ntohl(((struct nfsv3_time *)(f))->nfsv3_sec); \
+ (t)->tv_nsec = ntohl(((struct nfsv3_time *)(f))->nfsv3_nsec); \
+}
+
+#define fxdr_nfsv3time2(f, t) { \
+ (t)->nfsv3_sec = ntohl(((struct nfsv3_time *)(f))->nfsv3_sec); \
+ (t)->nfsv3_nsec = ntohl(((struct nfsv3_time *)(f))->nfsv3_nsec); \
+}
+
+#define txdr_nfsv3time(f, t) \
+{ \
+ ((struct nfsv3_time *)(t))->nfsv3_sec = htonl((f)->tv_sec); \
+ ((struct nfsv3_time *)(t))->nfsv3_nsec = htonl((f)->tv_nsec); \
+}
+
+#define txdr_nfsv3time2(f, t) \
+{ \
+ ((struct nfsv3_time *)(t))->nfsv3_sec = htonl((f)->nfsv3_sec); \
+ ((struct nfsv3_time *)(t))->nfsv3_nsec = htonl((f)->nfsv3_nsec); \
+}
+
+#define fxdr_hyper(f) \
+ ((((uint64_t)ntohl(((uint32_t *)(f))[0])) << 32) | \
+ (uint64_t)(ntohl(((uint32_t *)(f))[1])))
+
+#define txdr_hyper(f, t) \
+{ \
+ ((uint32_t *)(t))[0] = htonl((uint32_t)((f) >> 32)); \
+ ((uint32_t *)(t))[1] = htonl((uint32_t)((f) & 0xffffffff)); \
+}
+
+/* Macros for dealing with byte data saved in uint32_t aligned memory */
+
+#define uint32_aligndown(b) ((b) & ~3)
+#define uint32_alignup(b) (((b) + 3) & ~3)
+#define uint32_increment(b) (((b) + 3) >> 2)
+
+#endif /* __FS_NFS_XDR_SUBS_H */