diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2012-09-17 18:18:44 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2012-09-17 18:18:44 +0000 |
commit | 57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff (patch) | |
tree | 25d07d14e920d31c0b1947c9ca586f2a01fc32d8 /nuttx/fs/nfs | |
download | px4-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/Kconfig | 24 | ||||
-rw-r--r-- | nuttx/fs/nfs/Make.defs | 50 | ||||
-rw-r--r-- | nuttx/fs/nfs/nfs.h | 149 | ||||
-rw-r--r-- | nuttx/fs/nfs/nfs_mount.h | 140 | ||||
-rw-r--r-- | nuttx/fs/nfs/nfs_node.h | 82 | ||||
-rw-r--r-- | nuttx/fs/nfs/nfs_proto.h | 571 | ||||
-rw-r--r-- | nuttx/fs/nfs/nfs_util.c | 608 | ||||
-rw-r--r-- | nuttx/fs/nfs/nfs_vfsops.c | 2531 | ||||
-rw-r--r-- | nuttx/fs/nfs/rpc.h | 491 | ||||
-rw-r--r-- | nuttx/fs/nfs/rpc_clnt.c | 807 | ||||
-rw-r--r-- | nuttx/fs/nfs/xdr_subs.h | 128 |
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 */ |