aboutsummaryrefslogtreecommitdiff
path: root/nuttx/fs/nfs/nfs_vfsops.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/fs/nfs/nfs_vfsops.c')
-rw-r--r--nuttx/fs/nfs/nfs_vfsops.c2531
1 files changed, 2531 insertions, 0 deletions
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;
+}