aboutsummaryrefslogtreecommitdiff
path: root/nuttx/fs
diff options
context:
space:
mode:
authorpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2012-09-17 18:18:44 +0000
committerpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2012-09-17 18:18:44 +0000
commit57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff (patch)
tree25d07d14e920d31c0b1947c9ca586f2a01fc32d8 /nuttx/fs
downloadpx4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.tar.gz
px4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.tar.bz2
px4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.zip
Resync new repository with old repo r5166
git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5153 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'nuttx/fs')
-rw-r--r--nuttx/fs/Kconfig41
-rw-r--r--nuttx/fs/Makefile144
-rw-r--r--nuttx/fs/fat/Kconfig66
-rw-r--r--nuttx/fs/fat/Make.defs50
-rw-r--r--nuttx/fs/fat/fs_configfat.c963
-rw-r--r--nuttx/fs/fat/fs_fat32.c2286
-rw-r--r--nuttx/fs/fat/fs_fat32.h938
-rw-r--r--nuttx/fs/fat/fs_fat32attrib.c189
-rw-r--r--nuttx/fs/fat/fs_fat32dirent.c2947
-rw-r--r--nuttx/fs/fat/fs_fat32util.c1856
-rw-r--r--nuttx/fs/fat/fs_mkfatfs.c312
-rw-r--r--nuttx/fs/fat/fs_mkfatfs.h170
-rw-r--r--nuttx/fs/fat/fs_writefat.c544
-rw-r--r--nuttx/fs/fs_close.c133
-rw-r--r--nuttx/fs/fs_closeblockdriver.c112
-rw-r--r--nuttx/fs/fs_closedir.c148
-rw-r--r--nuttx/fs/fs_dup.c107
-rw-r--r--nuttx/fs/fs_dup2.c109
-rw-r--r--nuttx/fs/fs_fcntl.c264
-rw-r--r--nuttx/fs/fs_fdopen.c261
-rw-r--r--nuttx/fs/fs_filedup.c122
-rw-r--r--nuttx/fs/fs_filedup2.c121
-rw-r--r--nuttx/fs/fs_files.c479
-rw-r--r--nuttx/fs/fs_findblockdriver.c131
-rw-r--r--nuttx/fs/fs_foreachinode.c233
-rw-r--r--nuttx/fs/fs_foreachmountpoint.c177
-rw-r--r--nuttx/fs/fs_fsync.c138
-rw-r--r--nuttx/fs/fs_inode.c377
-rw-r--r--nuttx/fs/fs_inodeaddref.c83
-rw-r--r--nuttx/fs/fs_inodefind.c99
-rw-r--r--nuttx/fs/fs_inoderelease.c106
-rw-r--r--nuttx/fs/fs_inoderemove.c161
-rw-r--r--nuttx/fs/fs_inodereserve.c237
-rw-r--r--nuttx/fs/fs_internal.h327
-rw-r--r--nuttx/fs/fs_ioctl.c150
-rw-r--r--nuttx/fs/fs_lseek.c176
-rw-r--r--nuttx/fs/fs_mkdir.c130
-rw-r--r--nuttx/fs/fs_mount.c397
-rw-r--r--nuttx/fs/fs_open.c206
-rw-r--r--nuttx/fs/fs_openblockdriver.c128
-rw-r--r--nuttx/fs/fs_opendir.c312
-rw-r--r--nuttx/fs/fs_poll.c327
-rw-r--r--nuttx/fs/fs_read.c147
-rw-r--r--nuttx/fs/fs_readdir.c230
-rw-r--r--nuttx/fs/fs_registerblockdriver.c124
-rw-r--r--nuttx/fs/fs_registerdriver.c120
-rw-r--r--nuttx/fs/fs_rename.c153
-rw-r--r--nuttx/fs/fs_rewinddir.c145
-rw-r--r--nuttx/fs/fs_rmdir.c130
-rw-r--r--nuttx/fs/fs_seekdir.c230
-rw-r--r--nuttx/fs/fs_select.c239
-rw-r--r--nuttx/fs/fs_stat.c223
-rw-r--r--nuttx/fs/fs_statfs.c165
-rw-r--r--nuttx/fs/fs_syslog.c515
-rw-r--r--nuttx/fs/fs_umount.c206
-rw-r--r--nuttx/fs/fs_unlink.c130
-rw-r--r--nuttx/fs/fs_unregisterblockdriver.c86
-rw-r--r--nuttx/fs/fs_unregisterdriver.c86
-rw-r--r--nuttx/fs/fs_write.c188
-rw-r--r--nuttx/fs/mmap/Kconfig27
-rw-r--r--nuttx/fs/mmap/Make.defs43
-rw-r--r--nuttx/fs/mmap/README.txt78
-rw-r--r--nuttx/fs/mmap/fs_mmap.c174
-rw-r--r--nuttx/fs/mmap/fs_munmap.c212
-rw-r--r--nuttx/fs/mmap/fs_rammap.c245
-rw-r--r--nuttx/fs/mmap/fs_rammap.h150
-rw-r--r--nuttx/fs/nfs/Kconfig24
-rw-r--r--nuttx/fs/nfs/Make.defs50
-rw-r--r--nuttx/fs/nfs/nfs.h149
-rw-r--r--nuttx/fs/nfs/nfs_mount.h140
-rw-r--r--nuttx/fs/nfs/nfs_node.h82
-rw-r--r--nuttx/fs/nfs/nfs_proto.h571
-rw-r--r--nuttx/fs/nfs/nfs_util.c608
-rw-r--r--nuttx/fs/nfs/nfs_vfsops.c2531
-rw-r--r--nuttx/fs/nfs/rpc.h491
-rw-r--r--nuttx/fs/nfs/rpc_clnt.c807
-rw-r--r--nuttx/fs/nfs/xdr_subs.h128
-rw-r--r--nuttx/fs/nxffs/Kconfig51
-rw-r--r--nuttx/fs/nxffs/Make.defs46
-rw-r--r--nuttx/fs/nxffs/README.txt180
-rw-r--r--nuttx/fs/nxffs/nxffs.h1083
-rw-r--r--nuttx/fs/nxffs/nxffs_block.c186
-rw-r--r--nuttx/fs/nxffs/nxffs_blockstats.c152
-rw-r--r--nuttx/fs/nxffs/nxffs_cache.c261
-rw-r--r--nuttx/fs/nxffs/nxffs_dirent.c217
-rw-r--r--nuttx/fs/nxffs/nxffs_dump.c479
-rw-r--r--nuttx/fs/nxffs/nxffs_initialize.c520
-rw-r--r--nuttx/fs/nxffs/nxffs_inode.c502
-rw-r--r--nuttx/fs/nxffs/nxffs_ioctl.c150
-rw-r--r--nuttx/fs/nxffs/nxffs_open.c1263
-rw-r--r--nuttx/fs/nxffs/nxffs_pack.c1572
-rw-r--r--nuttx/fs/nxffs/nxffs_read.c473
-rw-r--r--nuttx/fs/nxffs/nxffs_reformat.c267
-rw-r--r--nuttx/fs/nxffs/nxffs_stat.c186
-rw-r--r--nuttx/fs/nxffs/nxffs_unlink.c187
-rw-r--r--nuttx/fs/nxffs/nxffs_util.c181
-rw-r--r--nuttx/fs/nxffs/nxffs_write.c853
-rw-r--r--nuttx/fs/romfs/Kconfig14
-rw-r--r--nuttx/fs/romfs/Make.defs45
-rw-r--r--nuttx/fs/romfs/fs_romfs.c1090
-rw-r--r--nuttx/fs/romfs/fs_romfs.h231
-rw-r--r--nuttx/fs/romfs/fs_romfsutil.c979
102 files changed, 37052 insertions, 0 deletions
diff --git a/nuttx/fs/Kconfig b/nuttx/fs/Kconfig
new file mode 100644
index 000000000..1d1046735
--- /dev/null
+++ b/nuttx/fs/Kconfig
@@ -0,0 +1,41 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+comment "File system configuration"
+
+source fs/fat/Kconfig
+source fs/mmap/Kconfig
+source fs/nfs/Kconfig
+source fs/nxffs/Kconfig
+source fs/romfs/Kconfig
+
+comment "System Logging"
+
+config SYSLOG
+ bool "System logging"
+ default n
+ ---help---
+ Enables generic system logging features.
+
+config SYSLOG_DEVPATH
+ string "System log device"
+ default "/dev/syslog"
+ depends on SYSLOG
+ ---help---
+ The full path to the system logging device. For the RAMLOG SYSLOG device,
+ this is normally "/dev/ramlog". For character SYSLOG devices, it should be
+ some other existing character device (or file) supported by the configuration
+ (such as "/dev/ttyS1")/
+
+config SYSLOG_CHAR
+ bool "System log character device support"
+ default y
+ depends on SYSLOG
+ ---help---
+ Enable the generic character device for the SYSLOG. The full path to the
+ SYSLOG device is provided by SYSLOG_DEVPATH. A valid character device (or
+ file) must exist at this path. It will by opened by syslog_initialize.
+
+ Do not enable more than one SYSLOG device.
diff --git a/nuttx/fs/Makefile b/nuttx/fs/Makefile
new file mode 100644
index 000000000..ce952e06f
--- /dev/null
+++ b/nuttx/fs/Makefile
@@ -0,0 +1,144 @@
+############################################################################
+# fs/Makefile
+#
+# Copyright (C) 2007, 2008, 2011-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.
+#
+############################################################################
+
+-include $(TOPDIR)/Make.defs
+
+ASRCS =
+AOBJS = $(ASRCS:.S=$(OBJEXT))
+
+CSRCS =
+
+# If there are no file descriptors configured, then a small part of the
+# logic in this directory may still apply to socket descriptors
+
+ifeq ($(CONFIG_NFILE_DESCRIPTORS),0)
+ifneq ($(CONFIG_NSOCKET_DESCRIPTORS),0)
+
+# Socket descriptor support
+
+CSRCS += fs_close.c fs_read.c fs_write.c fs_ioctl.c fs_poll.c fs_select.c
+endif
+
+# Support for network access using streams
+
+ifneq ($(CONFIG_NFILE_STREAMS),0)
+CSRCS += fs_fdopen.c
+endif
+
+else
+
+# Common file/socket descriptor support
+
+CSRCS += fs_close.c fs_closedir.c fs_dup.c fs_dup2.c fs_fcntl.c \
+ fs_filedup.c fs_filedup2.c fs_ioctl.c fs_lseek.c fs_open.c \
+ fs_opendir.c fs_poll.c fs_read.c fs_readdir.c fs_rewinddir.c \
+ fs_seekdir.c fs_stat.c fs_statfs.c fs_select.c fs_write.c
+CSRCS += fs_files.c fs_foreachinode.c fs_inode.c fs_inodeaddref.c \
+ fs_inodefind.c fs_inoderelease.c fs_inoderemove.c \
+ fs_inodereserve.c
+CSRCS += fs_registerdriver.c fs_unregisterdriver.c
+CSRCS += fs_registerblockdriver.c fs_unregisterblockdriver.c \
+ fs_findblockdriver.c fs_openblockdriver.c fs_closeblockdriver.c
+
+include mmap/Make.defs
+
+# Stream support
+
+ifneq ($(CONFIG_NFILE_STREAMS),0)
+CSRCS += fs_fdopen.c
+endif
+
+# System logging to a character device (or file)
+
+ifeq ($(CONFIG_SYSLOG),y)
+ifeq ($(CONFIG_SYSLOG_CHAR),y)
+CSRCS += fs_syslog.c
+endif
+endif
+
+# Additional files required is mount-able file systems are supported
+
+ifneq ($(CONFIG_DISABLE_MOUNTPOINT),y)
+CSRCS += fs_fsync.c fs_mkdir.c fs_mount.c fs_rename.c fs_rmdir.c \
+ fs_umount.c fs_unlink.c
+CSRCS += fs_foreachmountpoint.c
+include fat/Make.defs
+include romfs/Make.defs
+include nxffs/Make.defs
+include nfs/Make.defs
+endif
+endif
+
+COBJS = $(CSRCS:.c=$(OBJEXT))
+
+SRCS = $(ASRCS) $(CSRCS)
+OBJS = $(AOBJS) $(COBJS)
+
+BIN = libfs$(LIBEXT)
+
+SUBDIRS = mmap fat romfs nxffs:nfs
+VPATH = mmap:fat:romfs:nxffs:nfs
+
+all: $(BIN)
+
+$(AOBJS): %$(OBJEXT): %.S
+ $(call ASSEMBLE, $<, $@)
+
+$(COBJS): %$(OBJEXT): %.c
+ $(call COMPILE, $<, $@)
+
+$(BIN): $(OBJS)
+ @( for obj in $(OBJS) ; do \
+ $(call ARCHIVE, $@, $${obj}); \
+ done ; )
+
+.depend: Makefile $(SRCS)
+ @$(MKDEP) --dep-path . $(MMAPDEPPATH) $(FATDEPPATH) $(ROMFSDEPPATH) $(NXFFSDEPPATH) $(NFSDEPPATH) \
+ $(CC) -- $(CFLAGS) -- $(SRCS) >Make.dep
+ @touch $@
+
+depend: .depend
+
+clean:
+ @rm -f $(BIN) *~ .*.swp
+ $(call CLEAN)
+ @( for dir in $(SUBDIRS); do \
+ rm -f $${dir}/*~ $${dir}/.*.swp; \
+ done ; )
+
+distclean: clean
+ @rm -f Make.dep .depend
+
+-include Make.dep
diff --git a/nuttx/fs/fat/Kconfig b/nuttx/fs/fat/Kconfig
new file mode 100644
index 000000000..1de613ce4
--- /dev/null
+++ b/nuttx/fs/fat/Kconfig
@@ -0,0 +1,66 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+config FS_FAT
+ bool "FAT file system"
+ default n
+ depends on !DISABLE_MOUNTPOINT
+ ---help---
+ Enable FAT filesystem support
+
+if FS_FAT
+config FAT_LCNAMES
+ bool "FAT upper/lower names"
+ default n
+ ---help---
+ Enable use of the NT-style upper/lower case 8.3
+ file name support.
+
+config FAT_LFN
+ bool "FAT long file names"
+ default n
+ ---help---
+ Enable FAT long file names. NOTE: Microsoft claims
+ patents on FAT long file name technology. Please read the
+ disclaimer in the top-level COPYING file and only enable this
+ feature if you understand these issues.
+
+config FAT_MAXFNAME
+ int "FAT maximum file name size"
+ depends on FAT_LFN
+ ---help---
+ If CONFIG_FAT_LFN is defined, then the default, maximum long file
+ name is 255 bytes. This can eat up a lot of memory (especially stack
+ space). If you are willing to live with some non-standard, short long
+ file names, then define this value to be something more reasonable. A
+ good choice would be the same value as selected for NAME_MAX which will
+ limit the visibility of longer file names anyway.
+
+config FS_FATTIME
+ bool "FAT timestamps"
+ default n
+ ---help---
+ Support FAT date and time. NOTE: There is not
+ much sense in supporting FAT date and time unless you have a
+ hardware RTC or other way to get the time and date.
+
+config FAT_DMAMEMORY
+ bool "DMA memory allocator"
+ default n
+ ---help---
+ The FAT file system allocates two I/O buffers for data transfer, each
+ are the size of one device sector. One of the buffers is allocated
+ once for each FAT volume that is mounted; the other buffers are
+ allocated each time a FAT file is opened.
+
+ Some hardware, however, may require special DMA-capable memory in
+ order to perform the the transfers. If FAT_DMAMEMORY is defined
+ then the architecture-specific hardware must provide the funtions
+ fat_dma_alloc() and fat_dma_free(): fat_dmalloc() will allocate
+ DMA-capable memory of the specified size; fat_dmafree() is the
+ corresponding function that will be called to free the DMA-capable
+ memory.
+
+endif
diff --git a/nuttx/fs/fat/Make.defs b/nuttx/fs/fat/Make.defs
new file mode 100644
index 000000000..136302b86
--- /dev/null
+++ b/nuttx/fs/fat/Make.defs
@@ -0,0 +1,50 @@
+############################################################################
+# Make.defs
+#
+# Copyright (C) 2008, 2011 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_FS_FAT),y)
+# Files required for FAT file system support
+
+ASRCS +=
+CSRCS += fs_fat32.c fs_fat32dirent.c fs_fat32attrib.c fs_fat32util.c
+
+# Files required for mkfatfs utility function
+
+ASRCS +=
+CSRCS += fs_mkfatfs.c fs_configfat.c fs_writefat.c
+
+# Argument for dependency checking
+
+FATDEPPATH = --dep-path fat
+endif
diff --git a/nuttx/fs/fat/fs_configfat.c b/nuttx/fs/fat/fs_configfat.c
new file mode 100644
index 000000000..2075caa9f
--- /dev/null
+++ b/nuttx/fs/fat/fs_configfat.c
@@ -0,0 +1,963 @@
+/****************************************************************************
+ * fs/fat/fs_configfat.c
+ *
+ * Copyright (C) 2008-2009 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 <stdint.h>
+#include <string.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/fat.h>
+#include <nuttx/fs/mkfatfs.h>
+
+#include "fs_internal.h"
+#include "fs_fat32.h"
+#include "fs_mkfatfs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define NDX12 0
+#define NDX16 1
+#define NDX32 2
+
+#define fatconfig12 fatconfig[NDX12]
+#define fatconfig16 fatconfig[NDX16]
+#define fatconfig32 fatconfig[NDX32]
+
+/* JMP rel8 and NOP opcodes */
+
+#define OPCODE_JMP_REL8 0xeb
+#define OPCODE_NOP 0x90
+
+#define BOOTCODE_MSGOFFSET 29
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct fat_config_s
+{
+ uint32_t fc_navailsects; /* The number of available sectors */
+ uint32_t fc_nfatsects; /* The number of sectors in one FAT */
+ uint32_t fc_nclusters; /* The number of clusters in the filesystem */
+ uint32_t fc_rsvdseccount; /* The number of reserved sectors */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Reverse engineered, generic boot message logic for non-bootable disk.
+ * Message begins at offset 29; Sector relative offset must be poked into
+ * offset 3.
+ */
+
+static uint8_t g_bootcodeblob[] =
+{
+ 0x0e, 0x1f, 0xbe, 0x00, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0x0b, 0x56,
+ 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10, 0x5e, 0xeb, 0xf0, 0x32,
+ 0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73,
+ 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x62,
+ 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x64, 0x69, 0x73,
+ 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
+ 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f,
+ 0x6f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70,
+ 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72, 0x65,
+ 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20,
+ 0x74, 0x6f, 0x20, 0x74, 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69,
+ 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x0d, 0x0a, 0x00
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mkfatfs_nfatsect12
+ *
+ * Description:
+ * Calculate the number of sectors need for one fat in a FAT12 file system.
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Other format parameters that are not caller specifiable. (Most
+ * set by mkfatfs_configfatfs()).
+ * navailsects - The number of sectors available for both FAT and data.
+ * This is a precalculated value equal to the total number of sectors
+ * minus the number of root directory sectors and minus the number of
+ * reserved sectors.
+ *
+ * Return:
+ * 0: That calculation would have overflowed
+ * >0: The size of one FAT in sectors.
+ *
+ ****************************************************************************/
+static inline uint32_t
+mkfatfs_nfatsect12(FAR struct fat_format_s *fmt, FAR struct fat_var_s *var,
+ uint32_t navailsects)
+{
+#ifdef CONFIG_HAVE_LONG_LONG
+ uint64_t denom;
+ uint64_t numer;
+#else
+ uint32_t denom;
+ uint32_t numer;
+#endif
+
+ /* For FAT12, the cluster number is held in a 12-bit number or 1.5 bytes per
+ * cluster reference. So each FAT sector will hold sectorsize/1.5 cluster
+ * references (except for the first sector of each FAT which has two reserved
+ * 12-bit values). And the total number of FAT sectors needed is:
+ *
+ * nfatsects = (1.5 * (ndataclust + 2) / sectorsize)
+ *
+ * where:
+ *
+ * ndataclust = ndatasect / clustsize
+ * nvailsects = nfatsects + ndatasect
+ *
+ * The solution to this set of linear equations is:
+ *
+ * nfatsects = (3 * navailsects + 6 * clustersize) /
+ * (3 * nfats + 2 * sectorsize * clustersize)
+ *
+ * The numerator would overflow uint32_t if:
+ *
+ * 3 * navailsects + 6 * clustersize > 0xffffffff
+ *
+ * Or
+ *
+ * navailsects > 0x55555555 - 2 * clustersize
+ */
+
+#ifndef CONFIG_HAVE_LONG_LONG
+ if (navailsects <= (0x55555555 - (1 << (fmt->ff_clustshift + 1))))
+ {
+#endif
+
+ denom = (fmt->ff_nfats << 1) + fmt->ff_nfats
+ + (var->fv_sectorsize << (fmt->ff_clustshift + 1));
+ numer = (navailsects << 1) + navailsects
+ + (1 << (fmt->ff_clustshift + 2)) + (1 << (fmt->ff_clustshift + 1));
+ return (uint32_t)((numer + denom - 1) / denom);
+
+#ifndef CONFIG_HAVE_LONG_LONG
+ }
+ else
+ {
+ return 0;
+ }
+#endif
+}
+
+/****************************************************************************
+ * Name: mkfatfs_nfatsect16
+ *
+ * Description:
+ * Calculate the number of sectors need for one fat in a FAT16 file system.
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Other format parameters that are not caller specifiable. (Most
+ * set by mkfatfs_configfatfs()).
+ * navailsects - The number of sectors available for both FAT and data.
+ * This is a precalculated value equal to the total number of sectors
+ * minus the number of root directory sectors and minus the number of
+ * reserved sectors.
+ *
+ * Return:
+ * The size of one FAT in sectors.
+ *
+ ****************************************************************************/
+static inline uint32_t
+mkfatfs_nfatsect16(FAR struct fat_format_s *fmt, FAR struct fat_var_s *var,
+ uint32_t navailsects)
+{
+#ifdef CONFIG_HAVE_LONG_LONG
+ uint64_t denom;
+ uint64_t numer;
+#else
+ uint32_t denom;
+ uint32_t numer;
+#endif
+
+ /* For FAT16, the cluster number is held in a 16-bit number or 2 bytes per
+ * cluster reference. So each FAT sector will hold sectorsize/2 cluster
+ * references (except for the first sector of each FAT which has two reserved
+ * 16-bit values). And the total number of FAT sectors needed is:
+ *
+ * nfatsects = (2 * (ndataclust + 2) / sectorsize)
+ *
+ * where:
+ *
+ * ndataclust = ndatasect / clustsize
+ * nvailsects = nfatsects + ndatasect
+ *
+ * The solution to this set of linear equations is:
+ *
+ * nfatsects = (navailsects + 2 * clustersize) /
+ * (nfats + sectorsize * clustersize / 2)
+ *
+ * Overflow in the calculation of the numerator could occur if:
+ *
+ * navailsects > 0xffffffff - 2 * clustersize
+ */
+
+ if (fmt->ff_clustshift == 0)
+ {
+ denom = fmt->ff_nfats + (var->fv_sectorsize >> 1);
+ numer = navailsects + 2;
+ }
+ else
+ {
+ denom = fmt->ff_nfats + (var->fv_sectorsize << (fmt->ff_clustshift - 1));
+ numer = navailsects + (1 << (fmt->ff_clustshift + 1));
+ }
+ return (uint32_t)((numer + denom - 1) / denom);
+}
+
+/****************************************************************************
+ * Name: mkfatfs_nfatsect32
+ *
+ * Description:
+ * Calculate the number of sectors need for one fat in a FAT32 file system.
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Other format parameters that are not caller specifiable. (Most
+ * set by mkfatfs_configfatfs()).
+ * navailsects - The number of sectors available for both FAT and data.
+ * This is a precalculated value equal to the total number of sectors
+ * minus the number of root directory sectors and minus the number of
+ * reserved sectors.
+ *
+ * Return:
+ * The size of one FAT in sectors.
+ *
+ ****************************************************************************/
+static inline uint32_t
+mkfatfs_nfatsect32(FAR struct fat_format_s *fmt, FAR struct fat_var_s *var,
+ uint32_t navailsects)
+{
+#ifdef CONFIG_HAVE_LONG_LONG
+ uint64_t denom;
+ uint64_t numer;
+#else
+ uint32_t denom;
+ uint32_t numer;
+#endif
+
+ /* For FAT32, the cluster number is held in a 32-bit number or 4 bytes per
+ * cluster reference. So each FAT sector will hold sectorsize/4 cluster
+ * references (except for the first sector of each FAT which has three reserved
+ * 32-bit values). And the total number of FAT sectors needed is:
+ *
+ * nfatsects = (4 * (ndataclust + 3) / sectorsize)
+ *
+ * where:
+ *
+ * ndataclust = ndatasect / clustsize
+ * nvailsects = nfatsects + ndatasect
+ *
+ * The solution to this set of linear equations is:
+ *
+ * nfatsects = (navailsects + 3 * clustersize) /
+ * (nfats + sectorsize * clustersize / 4)
+ *
+ * Overflow in the 32-bit calculation of the numerator could occur if:
+ *
+ * navailsects > 0xffffffff - 3 * clustersize
+ */
+
+ if (fmt->ff_clustshift == 0)
+ {
+ denom = fmt->ff_nfats + (var->fv_sectorsize >> 2);
+ numer = navailsects + 3;
+ }
+ else if (fmt->ff_clustshift == 1)
+ {
+ denom = fmt->ff_nfats + (var->fv_sectorsize >> 1);
+ numer = navailsects + 6;
+ }
+ else
+ {
+ denom = fmt->ff_nfats + (var->fv_sectorsize << (fmt->ff_clustshift - 2));
+ numer = navailsects + (1 << (fmt->ff_clustshift + 1)) + (1 << fmt->ff_clustshift);
+ }
+ return (uint32_t)((numer + denom - 1) / denom);
+}
+
+/****************************************************************************
+ * Name: mkfatfs_clustersearchlimits
+ *
+ * Description:
+ * Pick the starting and ending cluster size to use in the search for the
+ * the optimal cluster size.
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Other format parameters that are not caller specifiable. (Most
+ * set by mkfatfs_configfatfs()).
+ *
+ * Return:
+ * Starting cluster size is set in fmt->ff_clustshift; Final cluster
+ * size is the returned value.
+ *
+ ****************************************************************************/
+static inline uint8_t
+mkfatfs_clustersearchlimits(FAR struct fat_format_s *fmt, FAR struct fat_var_s *var)
+{
+ uint8_t mxclustshift;
+
+ /* Did the caller already pick the cluster size? If not, the clustshift value
+ * will be 0xff
+ */
+
+ if (fmt->ff_clustshift == 0xff)
+ {
+ /* Pick a starting size based on the number of sectors on the device */
+
+ if (fmt->ff_nsectors < 2048)
+ {
+ /* 2k sectors, start wit 1 sector/cluster. */
+ fmt->ff_clustshift = 0;
+ }
+ else if (fmt->ff_nsectors < 4096)
+ {
+ /* 4k sectors, start with 2 sector/cluster. */
+ fmt->ff_clustshift = 1;
+ }
+ else if (fmt->ff_nsectors < 8192)
+ {
+ /* 8k sectors, start with 4 sector/cluster. */
+ fmt->ff_clustshift = 2;
+ }
+ else if (fmt->ff_nsectors < 16384)
+ {
+ /* 16k sectors, start with 8 sector/cluster. */
+ fmt->ff_clustshift = 3;
+ }
+ else if (fmt->ff_nsectors < 32768)
+ {
+ /* 32k sectors, start with 16 sector/cluster. */
+ fmt->ff_clustshift = 4;
+ }
+ else
+ {
+ /* Otherwise, 32 sector/cluster. */
+ fmt->ff_clustshift = 5;
+ }
+
+ /* Wherever the search starts, it will end with the maximum of
+ * 128 sectors per cluster
+ */
+
+ mxclustshift = 7;
+ }
+ else
+ {
+ /* The caller has selected a cluster size. There will be no search!
+ * Just set the maximum to the caller specificed value.
+ */
+
+ mxclustshift = fmt->ff_clustshift;
+ }
+ return mxclustshift;
+}
+
+/****************************************************************************
+ * Name: mkfatfs_try12
+ *
+ * Description:
+ * Try to define a FAT12 filesystem on the device using the candidate
+ * sectors per cluster
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Other format parameters that are not caller specifiable. (Most
+ * set by mkfatfs_configfatfs()).
+ * fatconfig - FAT12-specific configuration
+ *
+ * Return:
+ * Zero on success configuration of a FAT12 file system; negated errno
+ * on failure
+ *
+ ****************************************************************************/
+static inline int
+mkfatfs_tryfat12(FAR struct fat_format_s *fmt, FAR struct fat_var_s *var,
+ FAR struct fat_config_s *config)
+{
+ uint32_t maxnclusters;
+
+ /* Calculate the number sectors in one FAT required to access all of the
+ * available sectors.
+ */
+
+ config->fc_nfatsects = mkfatfs_nfatsect12(fmt, var, config->fc_navailsects);
+ if (config->fc_nfatsects > 0)
+ {
+ /* Calculate the number of clusters available given the number of available
+ * sectors and the number of those that will be used for FAT:
+ */
+
+ config->fc_nclusters =
+ (config->fc_navailsects -
+ fmt->ff_nfats * config->fc_nfatsects) >> fmt->ff_clustshift;
+
+ /* Calculate the maximum number of clusters that could be supported by a
+ * FAT of this size.
+ *
+ * maxnclusters = nfatsects * sectorsize / 1.5 - 2
+ */
+
+ maxnclusters = (config->fc_nfatsects >> (var->fv_sectshift + 1)) / 3;
+ if (maxnclusters > FAT_MAXCLUST12)
+ {
+ maxnclusters = FAT_MAXCLUST12;
+ }
+ fvdbg("nfatsects=%u nclusters=%u (max=%u)\n",
+ config->fc_nfatsects, config->fc_nclusters, maxnclusters);
+
+ /* Check if this number of clusters would overflow the maximum for
+ * FAT12 (remembering that two FAT cluster slots are reserved).
+ */
+
+ if (config->fc_nclusters > maxnclusters - 2)
+ {
+ fvdbg("Too many clusters for FAT12\n");
+ return -ENFILE;
+ }
+ }
+ return 0;
+}
+
+/****************************************************************************
+ * Name: mkfatfs_try16
+ *
+ * Description:
+ * Try to define a FAT16 filesystem on the device using the candidate
+ * sectors per cluster
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Other format parameters that are not caller specifiable. (Most
+ * set by mkfatfs_configfatfs()).
+ * fatconfig - FAT16-specific configuration
+ *
+ * Return:
+ * Zero on success configuration of a FAT16 file system; negated errno
+ * on failure
+ *
+ ****************************************************************************/
+static inline int
+mkfatfs_tryfat16(FAR struct fat_format_s *fmt, FAR struct fat_var_s *var,
+ FAR struct fat_config_s *config)
+{
+ uint32_t maxnclusters;
+
+ /* Calculate the number sectors in one FAT required to access all of the
+ * available sectors.
+ */
+
+ config->fc_nfatsects = mkfatfs_nfatsect16(fmt, var, config->fc_navailsects);
+ if (config->fc_nfatsects > 0)
+ {
+ /* Calculate the number of clusters available given the number of available
+ * sectors and the number of those that will be used for FAT:
+ */
+
+ config->fc_nclusters =
+ (config->fc_navailsects -
+ fmt->ff_nfats * config->fc_nfatsects) >> fmt->ff_clustshift;
+
+ /* Calculate the maximum number of clusters that could be supported by a
+ * FAT of this size.
+ *
+ * maxnclusters = nfatsects * sectorsize / 2 - 2
+ */
+
+ maxnclusters = config->fc_nfatsects << (var->fv_sectorsize - 1);
+ if (maxnclusters > FAT_MAXCLUST16)
+ {
+ maxnclusters = FAT_MAXCLUST16;
+ }
+ fvdbg("nfatsects=%u nclusters=%u (min=%u max=%u)\n",
+ config->fc_nfatsects, config->fc_nclusters, FAT_MINCLUST16, maxnclusters);
+
+ /* Check if this number of clusters would overflow the maximum for
+ * FAT16 (remembering that two FAT cluster slots are reserved).
+ * Check the lower limit as well. The FAT12 is distinguished from FAT16
+ * by comparing the number of clusters on the device agains a known
+ * threshold. If a small FAT16 file system were created, then it would
+ * be confused as a FAT12 at mount time.
+ */
+
+ if ((config->fc_nclusters > maxnclusters - 2) ||
+ (config->fc_nclusters < FAT_MINCLUST16))
+ {
+ fvdbg("Too few or too many clusters for FAT16\n");
+ return -ENFILE;
+ }
+ }
+ return 0;
+}
+
+/****************************************************************************
+ * Name: mkfatfs_try32
+ *
+ * Description:
+ * Try to define a FAT32 filesystem on the device using the candidate
+ * sectors per cluster
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Other format parameters that are not caller specifiable. (Most
+ * set by mkfatfs_configfatfs()).
+ * fatconfig - FAT32-specific configuration
+ *
+ * Return:
+ * Zero on success configuration of a FAT32 file system; negated errno
+ * on failure
+ *
+ ****************************************************************************/
+static inline int
+mkfatfs_tryfat32(FAR struct fat_format_s *fmt, FAR struct fat_var_s *var,
+ FAR struct fat_config_s *config)
+{
+ uint32_t maxnclusters;
+
+ /* Calculate the number sectors in one FAT required to access all of the
+ * available sectors.
+ */
+
+ config->fc_nfatsects = mkfatfs_nfatsect32(fmt, var, config->fc_navailsects);
+ if (config->fc_nfatsects > 0)
+ {
+ /* Calculate the number of clusters available given the number of available
+ * sectors and the number of those that will be used for FAT:
+ */
+
+ config->fc_nclusters =
+ (config->fc_navailsects -
+ fmt->ff_nfats * config->fc_nfatsects) >> fmt->ff_clustshift;
+
+ /* Calculate the maximum number of clusters that could be supported by a
+ * FAT of this size.
+ *
+ * maxnclusters = nfatsects * sectorsize / 4 - 2
+ */
+
+ maxnclusters = (config->fc_nfatsects >> (var->fv_sectshift - 2));
+ if (maxnclusters > FAT_MAXCLUST32)
+ {
+ maxnclusters = FAT_MAXCLUST32;
+ }
+ fvdbg("nfatsects=%u nclusters=%u (max=%u)\n",
+ config->fc_nfatsects, config->fc_nclusters, maxnclusters);
+
+ /* Check if this number of clusters would overflow the maximum for
+ * FAT32 (remembering that two FAT cluster slots are reserved).
+ */
+
+ if ((config->fc_nclusters > maxnclusters - 3) ||
+ (config->fc_nclusters < FAT_MINCLUST32 && fmt->ff_fattype != 32))
+ {
+ fvdbg("Too few or too many clusters for FAT32\n");
+ return -ENFILE;
+ }
+ }
+ return 0;
+}
+
+/****************************************************************************
+ * Name: mkfatfs_selectfat
+ *
+ * Description:
+ * The cluster search has succeeded, select the specified FAT FS
+ *
+ * Input:
+ * fattype - The FAT size selected
+ * fmt - Caller specified format parameters
+ * var - Format parameters that are not caller specifiable.
+ *
+ * Return:
+ * None
+ *
+ ****************************************************************************/
+
+static inline void
+mkfatfs_selectfat(int fattype, FAR struct fat_format_s *fmt,
+ FAR struct fat_var_s *var, FAR struct fat_config_s *config)
+{
+ /* Return the appropriate information about the selected file system. */
+
+ fdbg("Selected FAT%d\n", fattype);
+ var->fv_fattype = fattype;
+ var->fv_nclusters = config->fc_nclusters;
+ var->fv_nfatsects = config->fc_nfatsects;
+ fmt->ff_rsvdseccount = config->fc_rsvdseccount;
+}
+
+/****************************************************************************
+ * Name: mkfatfs_clustersearch
+ *
+ * Description:
+ * Search to find the smallest (reasonable) cluster size for the FAT file
+ * system.
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Other format parameters that are not caller specifiable. (Most
+ * set by mkfatfs_configfatfs()).
+ *
+ * Return:
+ * Zero on success; negated errno on failure
+ *
+ ****************************************************************************/
+
+static inline int
+mkfatfs_clustersearch(FAR struct fat_format_s *fmt, FAR struct fat_var_s *var)
+{
+ struct fat_config_s fatconfig[3];
+ uint8_t mxclustshift;
+
+ memset(fatconfig, 0, 3*sizeof(struct fat_config_s));
+
+ /* Select the reserved sector count for each FAT size */
+
+ if (fmt->ff_rsvdseccount)
+ {
+ fatconfig12.fc_rsvdseccount = fmt->ff_rsvdseccount;
+ fatconfig16.fc_rsvdseccount = fmt->ff_rsvdseccount;
+
+ if (fmt->ff_rsvdseccount < 2)
+ {
+ fvdbg("At least 2 reserved sectors needed by FAT32\n");
+ fatconfig32.fc_rsvdseccount = 2;
+ }
+ else
+ {
+ fatconfig32.fc_rsvdseccount = fmt->ff_rsvdseccount;
+ }
+ }
+ else
+ {
+ fatconfig12.fc_rsvdseccount = 1;
+ fatconfig16.fc_rsvdseccount = 1;
+ fatconfig32.fc_rsvdseccount = 32;
+ }
+
+ /* Determine the number of sectors needed by the root directory.
+ * This is a constant value, independent of cluster size for FAT12/16
+ */
+
+ if (var->fv_fattype != 32)
+ {
+ /* Calculate the number of sectors reqired to contain the selected
+ * number of root directory entries. This value is save in the var
+ * structure but will be overwritten if FAT32 is selected. FAT32 uses
+ * a cluster chain for the root directory, so the concept of the number
+ * of root directory entries does not apply to FAT32
+ */
+
+ var->fv_nrootdirsects =
+ ((fmt->ff_rootdirentries << DIR_SHIFT) + var->fv_sectorsize - 1) >> var->fv_sectshift;
+
+ /* The number of data sectors available (includes the fat itself)
+ * This value is a constant for FAT12/16, but not FAT32 because the
+ * size of the root directory cluster changes
+ */
+
+ fatconfig12.fc_navailsects =
+ fatconfig16.fc_navailsects =
+ fmt->ff_nsectors - var->fv_nrootdirsects - fatconfig12.fc_rsvdseccount;
+ }
+
+ /* Select an initial and terminal clustersize to use in the search (if these
+ * values were not provided by the caller)
+ */
+
+ mxclustshift = mkfatfs_clustersearchlimits(fmt, var);
+
+ do
+ {
+ fvdbg("Configuring with %d sectors/cluster...\n", 1 << fmt->ff_clustshift);
+
+ /* Check if FAT12 has not been excluded */
+
+ if (var->fv_fattype == 0 || var->fv_fattype == 12)
+ {
+ /* Try to configure a FAT12 filesystem with this cluster size */
+
+ if (mkfatfs_tryfat12(fmt, var, &fatconfig12) != 0)
+ {
+ {
+ fvdbg("Cannot format FAT12 at %u sectors/cluster\n", 1 << fmt->ff_clustshift);
+ fatconfig12.fc_nfatsects = 0;
+ fatconfig12.fc_nclusters = 0;
+ }
+ }
+ }
+
+ /* Check if FAT16 has not been excluded */
+
+ if (var->fv_fattype == 0 || var->fv_fattype == 16)
+ {
+ /* Try to configure a FAT16 filesystem with this cluster size */
+
+ if (mkfatfs_tryfat16(fmt, var, &fatconfig16) != 0)
+ {
+ {
+ fvdbg("Cannot format FAT16 at %u sectors/cluster\n", 1 << fmt->ff_clustshift);
+ fatconfig16.fc_nfatsects = 0;
+ fatconfig16.fc_nclusters = 0;
+ }
+ }
+ }
+
+ /* If either FAT12 or 16 was configured at this sector/cluster setting,
+ * then finish the configuration and break out now
+ */
+
+ if (fatconfig12.fc_nclusters || fatconfig16.fc_nclusters)
+ {
+ if ((!var->fv_fattype && fatconfig16.fc_nclusters > fatconfig12.fc_nclusters) ||
+ (var ->fv_fattype == 16))
+ {
+ /* The caller has selected FAT16 -OR- no FAT type has been selected, but
+ * the FAT16 selection has more clusters. Select FAT16.
+ */
+
+ mkfatfs_selectfat(16, fmt, var, &fatconfig16);
+ }
+ else
+ {
+ /* The caller has selected FAT12 -OR- no FAT type has been selected, but
+ * the FAT12 selected has more clusters. Selected FAT12
+ */
+
+ mkfatfs_selectfat(12, fmt, var, &fatconfig12);
+ }
+ return OK;
+ }
+
+ /* Check if FAT32 has not been excluded */
+
+ if (var->fv_fattype == 0 || var->fv_fattype == 32)
+ {
+ /* The number of data sectors available (includes the fat itself)
+ * This value is a constant with respect to cluster sizefor FAT12/16, but not FAT32
+ * because the size of the root directory cluster changes with cluster size.
+ */
+
+ fatconfig32.fc_navailsects = fmt->ff_nsectors - (1 << fmt->ff_clustshift) - fatconfig32.fc_rsvdseccount;
+
+ /* Try to configure a FAT32 filesystem with this cluster size */
+
+ if (mkfatfs_tryfat32(fmt, var, &fatconfig32) != 0)
+ {
+ {
+ fvdbg("Cannot format FAT32 at %u sectors/cluster\n", 1 << fmt->ff_clustshift);
+ fatconfig32.fc_nfatsects = 0;
+ fatconfig32.fc_nclusters = 0;
+ }
+ }
+ else
+ {
+ /* Select FAT32 if we have not already done so */
+
+ mkfatfs_selectfat(32, fmt, var, &fatconfig32);
+ return OK;
+ }
+ }
+
+ /* Otherwise, bump up the sectors/cluster for the next time around the loop. */
+
+ fmt->ff_clustshift++;
+ }
+ while (fmt->ff_clustshift <= mxclustshift);
+ return -ENFILE;
+}
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mkfatfs_configfatfs
+ *
+ * Description:
+ * Based on the geometry of the block device and upon the caller-selected
+ * values, configure the FAT filesystem for the device.
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Holds disk geomtry data. Also, the location to return FAT
+ * configuration data
+ *
+ * Return:
+ * Zero on success; negated errno on failure
+ *
+ ****************************************************************************/
+int mkfatfs_configfatfs(FAR struct fat_format_s *fmt,
+ FAR struct fat_var_s *var)
+{
+ int ret;
+
+ /* Select the number of root directory entries (FAT12/16 only). If FAT32 is selected,
+ * this value will be cleared later
+ */
+
+ if (!fmt->ff_rootdirentries)
+ {
+ /* The caller did not specify the number of root directory entries; use a default of 512. */
+
+ fmt->ff_rootdirentries = 512;
+ }
+
+ /* Search to determine the smallest (reasonable) cluster size. A by-product
+ * of this search will be the selection of the FAT size (12/16/32) if the
+ * caller has not specified the FAT size
+ */
+
+ ret = mkfatfs_clustersearch(fmt, var);
+ if (ret < 0)
+ {
+ fdbg("Failed to set cluster size\n");
+ return ret;
+ }
+
+ /* Perform FAT specific initialization */
+
+ /* Set up boot jump assuming FAT 12/16 offset to bootcode */
+
+ var->fv_jump[0] = OPCODE_JMP_REL8;
+ var->fv_jump[2] = OPCODE_NOP;
+ var->fv_bootcode = g_bootcodeblob;
+ var->fv_bootcodesize = sizeof(g_bootcodeblob);
+
+ if (var->fv_fattype != 32)
+ {
+ /* Set up additional, non-zero FAT12/16 fields */
+
+ /* Patch in the correct offset to the boot code */
+
+ var->fv_jump[1] = BS16_BOOTCODE - 2;
+ g_bootcodeblob[3] = BS16_BOOTCODE + BOOTCODE_MSGOFFSET;
+ }
+ else
+ {
+ /* Patch in the correct offset to the boot code */
+
+ var->fv_jump[1] = BS32_BOOTCODE - 2;
+ g_bootcodeblob[3] = BS32_BOOTCODE + BOOTCODE_MSGOFFSET;
+
+ /* The root directory is a cluster chain... its is initialize size is one cluster */
+
+ var->fv_nrootdirsects = 1 << fmt->ff_clustshift;
+
+ /* The number of reported root directory entries should should be zero for
+ * FAT32 because the root directory is a cluster chain.
+ */
+
+ fmt->ff_rootdirentries = 0;
+
+ /* Verify the caller's backupboot selection */
+
+ if (fmt->ff_backupboot <= 1 || fmt->ff_backupboot >= fmt->ff_rsvdseccount)
+ {
+ fdbg("Invalid backup boot sector: %d\n", fmt->ff_backupboot);
+ fmt->ff_backupboot = 0;
+ }
+
+ /* Check if the caller has selected a location for the backup boot record */
+
+ if (!fmt->ff_backupboot)
+ {
+ /* There must be reserved sectors in order to have a backup boot sector */
+
+ if (fmt->ff_rsvdseccount > 0 && fmt->ff_rsvdseccount >= 2)
+ {
+ /* Sector 0 is the MBR; 1... ff_rsvdseccount are reserved. Try the next
+ * the last reserved sector.
+ */
+
+ fmt->ff_backupboot = fmt->ff_rsvdseccount - 1;
+ if (fmt->ff_backupboot > 6)
+ {
+ /* Limit the location to within the first 7 */
+
+ fmt->ff_backupboot = 6;
+ }
+ }
+ }
+ }
+
+ /* Report the selected fat type */
+
+ fmt->ff_fattype = var->fv_fattype;
+
+ /* Describe the configured filesystem */
+
+#ifdef CONFIG_DEBUG
+ fdbg("Sector size: %d bytes\n", var->fv_sectorsize);
+ fdbg("Number of sectors: %d sectors\n", fmt->ff_nsectors);
+ fdbg("FAT size: %d bits\n", var->fv_fattype);
+ fdbg("Number FATs: %d\n", fmt->ff_nfats);
+ fdbg("Sectors per cluster: %d sectors\n", 1 << fmt->ff_clustshift);
+ fdbg("FS size: %d sectors\n", var->fv_nfatsects);
+ fdbg(" %d clusters\n", var->fv_nclusters);
+ if (var->fv_fattype != 32)
+ {
+ fdbg("Root directory slots: %d\n", fmt->ff_rootdirentries);
+ }
+ fdbg("Volume ID: %08x\n", fmt->ff_volumeid);
+ fdbg("Volume Label: \"%c%c%c%c%c%c%c%c%c%c%c\"\n",
+ fmt->ff_volumelabel[0], fmt->ff_volumelabel[1], fmt->ff_volumelabel[2],
+ fmt->ff_volumelabel[3], fmt->ff_volumelabel[4], fmt->ff_volumelabel[5],
+ fmt->ff_volumelabel[6], fmt->ff_volumelabel[7], fmt->ff_volumelabel[8],
+ fmt->ff_volumelabel[9], fmt->ff_volumelabel[10]);
+#endif
+ return OK;
+}
+
diff --git a/nuttx/fs/fat/fs_fat32.c b/nuttx/fs/fat/fs_fat32.c
new file mode 100644
index 000000000..0c28cea67
--- /dev/null
+++ b/nuttx/fs/fat/fs_fat32.c
@@ -0,0 +1,2286 @@
+/****************************************************************************
+ * fs/fat/fs_fat32.c
+ *
+ * Copyright (C) 2007-2009, 2011-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * Microsoft FAT documentation
+ * Some good ideas were leveraged from the FAT implementation:
+ * 'Copyright (C) 2007, ChaN, all right reserved.'
+ * which has an unrestricted license.
+ *
+ * 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/stat.h>
+#include <sys/statfs.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <semaphore.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/fat.h>
+#include <nuttx/fs/dirent.h>
+
+#include "fs_internal.h"
+#include "fs_fat32.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int fat_open(FAR struct file *filep, const char *relpath,
+ int oflags, mode_t mode);
+static int fat_close(FAR struct file *filep);
+static ssize_t fat_read(FAR struct file *filep, char *buffer, size_t buflen);
+static ssize_t fat_write(FAR struct file *filep, const char *buffer,
+ size_t buflen);
+static off_t fat_seek(FAR struct file *filep, off_t offset, int whence);
+static int fat_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+static int fat_sync(FAR struct file *filep);
+
+static int fat_opendir(struct inode *mountpt, const char *relpath,
+ struct fs_dirent_s *dir);
+static int fat_readdir(struct inode *mountpt, struct fs_dirent_s *dir);
+static int fat_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir);
+
+static int fat_bind(FAR struct inode *blkdriver, const void *data,
+ void **handle);
+static int fat_unbind(void *handle, FAR struct inode **blkdriver);
+static int fat_statfs(struct inode *mountpt, struct statfs *buf);
+
+static int fat_unlink(struct inode *mountpt, const char *relpath);
+static int fat_mkdir(struct inode *mountpt, const char *relpath,
+ mode_t mode);
+static int fat_rmdir(struct inode *mountpt, const char *relpath);
+static int fat_rename(struct inode *mountpt, const char *oldrelpath,
+ const char *newrelpath);
+static int fat_stat(struct inode *mountpt, const char *relpath, struct stat *buf);
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/* See fs_mount.c -- this structure is explicitly externed there.
+ * We use the old-fashioned kind of initializers so that this will compile
+ * with any compiler.
+ */
+
+const struct mountpt_operations fat_operations =
+{
+ fat_open,
+ fat_close,
+ fat_read,
+ fat_write,
+ fat_seek,
+ fat_ioctl,
+ fat_sync,
+
+ fat_opendir,
+ NULL,
+ fat_readdir,
+ fat_rewinddir,
+
+ fat_bind,
+ fat_unbind,
+ fat_statfs,
+
+ fat_unlink,
+ fat_mkdir,
+ fat_rmdir,
+ fat_rename,
+ fat_stat
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fat_open
+ ****************************************************************************/
+
+static int fat_open(FAR struct file *filep, const char *relpath,
+ int oflags, mode_t mode)
+{
+ struct fat_dirinfo_s dirinfo;
+ struct inode *inode;
+ struct fat_mountpt_s *fs;
+ struct fat_file_s *ff;
+ uint8_t *direntry;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv == NULL && filep->f_inode != NULL);
+
+ /* Get the mountpoint inode reference from the file structure and the
+ * mountpoint private data from the inode structure
+ */
+
+ inode = filep->f_inode;
+ fs = inode->i_private;
+
+ DEBUGASSERT(fs != NULL);
+
+ /* Check if the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Initialize the directory info structure */
+
+ memset(&dirinfo, 0, sizeof(struct fat_dirinfo_s));
+
+ /* Locate the directory entry for this path */
+
+ ret = fat_finddirentry(fs, &dirinfo, relpath);
+
+ /* Three possibililities: (1) a node exists for the relpath and
+ * dirinfo describes the directory entry of the entity, (2) the
+ * node does not exist, or (3) some error occurred.
+ */
+
+ if (ret == OK)
+ {
+ bool readonly;
+
+ /* The name exists -- but is it a file or a directory? */
+
+ direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset];
+ if (dirinfo.fd_root ||
+ (DIR_GETATTRIBUTES(direntry) & FATATTR_DIRECTORY))
+ {
+ /* It is a directory */
+
+ ret = -EISDIR;
+ goto errout_with_semaphore;
+ }
+
+ /* It would be an error if we are asked to create it exclusively */
+
+ if ((oflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
+ {
+ /* Already exists -- can't create it exclusively */
+
+ ret = -EEXIST;
+ goto errout_with_semaphore;
+ }
+
+#ifdef CONFIG_FILE_MODE
+# warning "Missing check for privileges based on inode->i_mode"
+#endif
+
+ /* Check if the caller has sufficient privileges to open the file */
+
+ readonly = ((DIR_GETATTRIBUTES(direntry) & FATATTR_READONLY) != 0);
+ if (((oflags & O_WRONLY) != 0) && readonly)
+ {
+ ret = -EACCES;
+ goto errout_with_semaphore;
+ }
+
+ /* 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 */
+
+ ret = fat_dirtruncate(fs, &dirinfo);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+ }
+
+ /* fall through to finish the file open operations */
+ }
+ else if (ret == -ENOENT)
+ {
+ /* The file does not exist. Were we asked to create it? */
+
+ if ((oflags & O_CREAT) == 0)
+ {
+ /* No.. then we fail with -ENOENT */
+
+ ret = -ENOENT;
+ goto errout_with_semaphore;
+ }
+
+ /* Yes.. create the file */
+
+ ret = fat_dircreate(fs, &dirinfo);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Fall through to finish the file open operation */
+
+ direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset];
+ }
+ else
+ {
+ /* An error occurred while checking for file existence --
+ * such as if an invalid path were provided.
+ */
+
+ goto errout_with_semaphore;
+ }
+
+ /* Create an instance of the file private date to describe the opened
+ * file.
+ */
+
+ ff = (struct fat_file_s *)kzalloc(sizeof(struct fat_file_s));
+ if (!ff)
+ {
+ ret = -ENOMEM;
+ goto errout_with_semaphore;
+ }
+
+ /* Create a file buffer to support partial sector accesses */
+
+ ff->ff_buffer = (uint8_t*)fat_io_alloc(fs->fs_hwsectorsize);
+ if (!ff->ff_buffer)
+ {
+ ret = -ENOMEM;
+ goto errout_with_struct;
+ }
+
+ /* Initialize the file private data (only need to initialize non-zero elements) */
+
+ ff->ff_open = true;
+ ff->ff_oflags = oflags;
+
+ /* Save information that can be used later to recover the directory entry */
+
+ ff->ff_dirsector = fs->fs_currentsector;
+ ff->ff_dirindex = dirinfo.dir.fd_index;
+
+ /* File cluster/size info */
+
+ ff->ff_startcluster =
+ ((uint32_t)DIR_GETFSTCLUSTHI(direntry) << 16) |
+ DIR_GETFSTCLUSTLO(direntry);
+
+ ff->ff_currentcluster = ff->ff_startcluster;
+ ff->ff_sectorsincluster = fs->fs_fatsecperclus;
+ ff->ff_size = DIR_GETFILESIZE(direntry);
+
+ /* Attach the private date to the struct file instance */
+
+ filep->f_priv = ff;
+
+ /* Then insert the new instance into the mountpoint structure.
+ * 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
+ * (but a simple reference count could have done that).
+ */
+
+ ff->ff_next = fs->fs_head;
+ fs->fs_head = ff->ff_next;
+
+ fat_semgive(fs);
+
+ /* In write/append mode, we need to set the file pointer to the end of the file */
+
+ if ((oflags & (O_APPEND|O_WRONLY)) == (O_APPEND|O_WRONLY))
+ {
+ off_t offset = fat_seek(filep, ff->ff_size, SEEK_SET);
+ if (offset < 0)
+ {
+ kfree(ff);
+ return (int)offset;
+ }
+ }
+
+ return OK;
+
+ /* Error exits -- goto's are nasty things, but they sure can make error
+ * handling a lot simpler.
+ */
+
+errout_with_struct:
+ kfree(ff);
+
+errout_with_semaphore:
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_close
+ ****************************************************************************/
+
+static int fat_close(FAR struct file *filep)
+{
+ struct inode *inode;
+ struct fat_file_s *ff;
+ int ret = OK;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ ff = filep->f_priv;
+ inode = filep->f_inode;
+
+ /* Do not check if the mount is healthy. We must support closing of
+ * the file even when there is healthy mount.
+ */
+
+ /* Synchronize the file buffers and disk content; update times */
+
+ ret = fat_sync(filep);
+
+ /* Then deallocate the memory structures created when the open method
+ * was called.
+ *
+ * Free the sector buffer that was used to manage partial sector accesses.
+ */
+
+ if (ff->ff_buffer)
+ {
+ fat_io_free(ff->ff_buffer, fs->fs_hwsectorsize);
+ }
+
+ /* Then free the file structure itself. */
+
+ kfree(ff);
+ filep->f_priv = NULL;
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_read
+ ****************************************************************************/
+
+static ssize_t fat_read(FAR struct file *filep, char *buffer, size_t buflen)
+{
+ struct inode *inode;
+ struct fat_mountpt_s *fs;
+ struct fat_file_s *ff;
+ unsigned int bytesread;
+ unsigned int readsize;
+ unsigned int nsectors;
+ size_t bytesleft;
+ int32_t cluster;
+ uint8_t *userbuffer = (uint8_t*)buffer;
+ int sectorindex;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ ff = filep->f_priv;
+ inode = filep->f_inode;
+ fs = inode->i_private;
+
+ DEBUGASSERT(fs != NULL);
+
+ /* Make sure that the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Check if the file was opened with read access */
+
+ if ((ff->ff_oflags & O_RDOK) == 0)
+ {
+ ret = -EACCES;
+ goto errout_with_semaphore;
+ }
+
+ /* Get the number of bytes left in the file */
+
+ bytesleft = ff->ff_size - filep->f_pos;
+
+ /* Truncate read count so that it does not exceed the number
+ * of bytes left in the file.
+ */
+
+ if (buflen > bytesleft)
+ {
+ buflen = bytesleft;
+ }
+
+ /* Get the first sector to read from. */
+
+ if (!ff->ff_currentsector)
+ {
+ /* The current sector can be determined from the current cluster
+ * and the file offset.
+ */
+
+ ret = fat_currentsector(fs, ff, filep->f_pos);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+
+ /* Loop until either (1) all data has been transferred, or (2) an
+ * error occurs. We assume we start with the current sector
+ * (ff_currentsector) which may be uninitialized.
+ */
+
+ readsize = 0;
+ sectorindex = filep->f_pos & SEC_NDXMASK(fs);
+
+ while (buflen > 0)
+ {
+ bytesread = 0;
+
+ /* Check if the user has provided a buffer large enough to
+ * hold one or more complete sectors -AND- the read is
+ * aligned to a sector boundary.
+ */
+
+ nsectors = buflen / fs->fs_hwsectorsize;
+ if (nsectors > 0 && sectorindex == 0)
+ {
+ /* Read maximum contiguous sectors directly to the user's
+ * buffer without using our tiny read buffer.
+ *
+ * Limit the number of sectors that we read on this time
+ * through the loop to the remaining contiguous sectors
+ * in this cluster
+ */
+
+ if (nsectors > ff->ff_sectorsincluster)
+ {
+ nsectors = ff->ff_sectorsincluster;
+ }
+
+ /* We are not sure of the state of the file buffer so
+ * the safest thing to do is just invalidate it
+ */
+
+ (void)fat_ffcacheinvalidate(fs, ff);
+
+ /* Read all of the sectors directly into user memory */
+
+ ret = fat_hwread(fs, userbuffer, ff->ff_currentsector, nsectors);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ ff->ff_sectorsincluster -= nsectors;
+ ff->ff_currentsector += nsectors;
+ bytesread = nsectors * fs->fs_hwsectorsize;
+ }
+ else
+ {
+ /* We are reading a partial sector. First, read the whole sector
+ * into the file data buffer. This is a caching buffer so if
+ * it is already there then all is well.
+ */
+
+ ret = fat_ffcacheread(fs, ff, ff->ff_currentsector);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Copy the partial sector into the user buffer */
+
+ bytesread = fs->fs_hwsectorsize - sectorindex;
+ if (bytesread > buflen)
+ {
+ /* We will not read to the end of the buffer */
+
+ bytesread = buflen;
+ }
+ else
+ {
+ /* We will read to the end of the buffer (or beyond) */
+
+ ff->ff_sectorsincluster--;
+ ff->ff_currentsector++;
+ }
+
+ memcpy(userbuffer, &ff->ff_buffer[sectorindex], bytesread);
+ }
+
+ /* Set up for the next sector read */
+
+ userbuffer += bytesread;
+ filep->f_pos += bytesread;
+ readsize += bytesread;
+ buflen -= bytesread;
+ sectorindex = filep->f_pos & SEC_NDXMASK(fs);
+
+ /* Check if the current read stream has incremented to the next
+ * cluster boundary
+ */
+
+ if (ff->ff_sectorsincluster < 1)
+ {
+ /* Find the next cluster in the FAT. */
+
+ cluster = fat_getcluster(fs, ff->ff_currentcluster);
+ if (cluster < 2 || cluster >= fs->fs_nclusters)
+ {
+ ret = -EINVAL; /* Not the right error */
+ goto errout_with_semaphore;
+ }
+
+ /* Setup to read the first sector from the new cluster */
+
+ ff->ff_currentcluster = cluster;
+ ff->ff_currentsector = fat_cluster2sector(fs, cluster);
+ ff->ff_sectorsincluster = fs->fs_fatsecperclus;
+ }
+ }
+
+ fat_semgive(fs);
+ return readsize;
+
+errout_with_semaphore:
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_write
+ ****************************************************************************/
+
+static ssize_t fat_write(FAR struct file *filep, const char *buffer,
+ size_t buflen)
+{
+ struct inode *inode;
+ struct fat_mountpt_s *fs;
+ struct fat_file_s *ff;
+ int32_t cluster;
+ unsigned int byteswritten;
+ unsigned int writesize;
+ unsigned int nsectors;
+ uint8_t *userbuffer = (uint8_t*)buffer;
+ int sectorindex;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ ff = filep->f_priv;
+ inode = filep->f_inode;
+ fs = inode->i_private;
+
+ DEBUGASSERT(fs != NULL);
+
+ /* Make sure that the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Check if the file was opened for write access */
+
+ if ((ff->ff_oflags & O_WROK) == 0)
+ {
+ ret = -EACCES;
+ goto errout_with_semaphore;
+ }
+
+ /* Check if the file size would exceed the range of off_t */
+
+ if (ff->ff_size + buflen < ff->ff_size)
+ {
+ ret = -EFBIG;
+ goto errout_with_semaphore;
+ }
+
+ /* Get the first sector to write to. */
+
+ if (!ff->ff_currentsector)
+ {
+ /* Has the starting cluster been defined? */
+
+ if (ff->ff_startcluster == 0)
+ {
+ /* No.. we have to create a new cluster chain */
+
+ ff->ff_startcluster = fat_createchain(fs);
+ ff->ff_currentcluster = ff->ff_startcluster;
+ ff->ff_sectorsincluster = fs->fs_fatsecperclus;
+ }
+
+ /* The current sector can then be determined from the currentcluster
+ * and the file offset.
+ */
+
+ ret = fat_currentsector(fs, ff, filep->f_pos);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+
+ /* Loop until either (1) all data has been transferred, or (2) an
+ * error occurs. We assume we start with the current sector in
+ * cache (ff_currentsector)
+ */
+
+ byteswritten = 0;
+ sectorindex = filep->f_pos & SEC_NDXMASK(fs);
+
+ while (buflen > 0)
+ {
+ /* Check if the user has provided a buffer large enough to
+ * hold one or more complete sectors.
+ */
+
+ nsectors = buflen / fs->fs_hwsectorsize;
+ if (nsectors > 0 && sectorindex == 0)
+ {
+ /* Write maximum contiguous sectors directly from the user's
+ * buffer without using our tiny read buffer.
+ *
+ * Limit the number of sectors that we write on this time
+ * through the loop to the remaining contiguous sectors
+ * in this cluster
+ */
+
+ if (nsectors > ff->ff_sectorsincluster)
+ {
+ nsectors = ff->ff_sectorsincluster;
+ }
+
+ /* We are not sure of the state of the sector cache so the
+ * safest thing to do is write back any dirty, cached sector
+ * and invalidate the current cache content.
+ */
+
+ (void)fat_ffcacheinvalidate(fs, ff);
+
+ /* Write all of the sectors directly from user memory */
+
+ ret = fat_hwwrite(fs, userbuffer, ff->ff_currentsector, nsectors);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ ff->ff_sectorsincluster -= nsectors;
+ ff->ff_currentsector += nsectors;
+ writesize = nsectors * fs->fs_hwsectorsize;
+ ff->ff_bflags |= FFBUFF_MODIFIED;
+ }
+ else
+ {
+ /* We are writing a partial sector -OR- the current sector
+ * has not yet been filled.
+ *
+ * We will first have to read the full sector in memory as
+ * part of a read-modify-write operation. NOTE we don't
+ * have to read the data on a rare case: When we are extending
+ * the file (filep->f_pos == ff->ff_size) -AND- the new data
+ * happens to be aligned at the beginning of the sector
+ * (sectorindex == 0).
+ */
+
+ if (filep->f_pos < ff->ff_size || sectorindex != 0)
+ {
+ /* Read the current sector into memory (perhaps first flushing
+ * the old, dirty sector to disk).
+ */
+
+ ret = fat_ffcacheread(fs, ff, ff->ff_currentsector);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+ }
+ else
+ {
+ /* Flush unwritten data in the sector cache. */
+
+ ret = fat_ffcacheflush(fs, ff);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Now mark the clean cache buffer as the current sector. */
+
+ ff->ff_cachesector = ff->ff_currentsector;
+ }
+
+ /* Copy the partial sector from the user buffer */
+
+ writesize = fs->fs_hwsectorsize - sectorindex;
+ if (writesize > buflen)
+ {
+ /* We will not write to the end of the buffer. Set
+ * write size to the size of the user buffer.
+ */
+
+ writesize = buflen;
+ }
+ else
+ {
+ /* We will write to the end of the buffer (or beyond). Bump
+ * up the current sector number (actually the next sector number).
+ */
+
+ ff->ff_sectorsincluster--;
+ ff->ff_currentsector++;
+ }
+
+ /* Copy the data into the cached sector and make sure that the
+ * cached sector is marked "dirty"
+ */
+
+ memcpy(&ff->ff_buffer[sectorindex], userbuffer, writesize);
+ ff->ff_bflags |= (FFBUFF_DIRTY|FFBUFF_VALID|FFBUFF_MODIFIED);
+ }
+
+ /* Set up for the next write */
+
+ userbuffer += writesize;
+ filep->f_pos += writesize;
+ byteswritten += writesize;
+ buflen -= writesize;
+ sectorindex = filep->f_pos & SEC_NDXMASK(fs);
+
+ /* Check if the current read stream has incremented to the next
+ * cluster boundary
+ */
+
+ if (ff->ff_sectorsincluster < 1)
+ {
+ /* Extend the current cluster by one (unless lseek was used to
+ * move the file position back from the end of the file)
+ */
+
+ cluster = fat_extendchain(fs, ff->ff_currentcluster);
+
+ /* Verify the cluster number */
+
+ if (cluster < 0)
+ {
+ ret = cluster;
+ goto errout_with_semaphore;
+ }
+ else if (cluster < 2 || cluster >= fs->fs_nclusters)
+ {
+ ret = -ENOSPC;
+ goto errout_with_semaphore;
+ }
+
+ /* Setup to write the first sector from the new cluster */
+
+ ff->ff_currentcluster = cluster;
+ ff->ff_sectorsincluster = fs->fs_fatsecperclus;
+ ff->ff_currentsector = fat_cluster2sector(fs, cluster);
+ }
+ }
+
+ /* The transfer has completed without error. Update the file size */
+
+ if (filep->f_pos > ff->ff_size)
+ {
+ ff->ff_size = filep->f_pos;
+ }
+
+ fat_semgive(fs);
+ return byteswritten;
+
+errout_with_semaphore:
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_seek
+ ****************************************************************************/
+
+static off_t fat_seek(FAR struct file *filep, off_t offset, int whence)
+{
+ struct inode *inode;
+ struct fat_mountpt_s *fs;
+ struct fat_file_s *ff;
+ int32_t cluster;
+ off_t position;
+ unsigned int clustersize;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ ff = filep->f_priv;
+ inode = filep->f_inode;
+ fs = inode->i_private;
+
+ DEBUGASSERT(fs != NULL);
+
+ /* Map the offset according to the whence option */
+ switch (whence)
+ {
+ case SEEK_SET: /* The offset is set to offset bytes. */
+ position = offset;
+ break;
+
+ case SEEK_CUR: /* The offset is set to its current location plus
+ * offset bytes. */
+
+ position = offset + filep->f_pos;
+ break;
+
+ case SEEK_END: /* The offset is set to the size of the file plus
+ * offset bytes. */
+
+ position = offset + ff->ff_size;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Make sure that the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Check if there is unwritten data in the file buffer */
+
+ ret = fat_ffcacheflush(fs, ff);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Attempts to set the position beyound the end of file will
+ * work if the file is open for write access.
+ */
+
+ if (position > ff->ff_size && (ff->ff_oflags & O_WROK) == 0)
+ {
+ /* Otherwise, the position is limited to the file size */
+
+ position = ff->ff_size;
+ }
+
+ /* Set file position to the beginning of the file (first cluster,
+ * first sector in cluster)
+ */
+
+ filep->f_pos = 0;
+ ff->ff_sectorsincluster = fs->fs_fatsecperclus;
+
+ /* Get the start cluster of the file */
+
+ cluster = ff->ff_startcluster;
+
+ /* Create a new cluster chain if the file does not have one (and
+ * if we are seeking beyond zero
+ */
+
+ if (!cluster && position > 0)
+ {
+ cluster = fat_createchain(fs);
+ if (cluster < 0)
+ {
+ ret = cluster;
+ goto errout_with_semaphore;
+ }
+ ff->ff_startcluster = cluster;
+ }
+
+ /* Move file position if necessary */
+
+ if (cluster)
+ {
+ /* If the file has a cluster chain, follow it to the
+ * requested position.
+ */
+
+ clustersize = fs->fs_fatsecperclus * fs->fs_hwsectorsize;
+ for (;;)
+ {
+ /* Skip over clusters prior to the one containing
+ * the requested position.
+ */
+
+ ff->ff_currentcluster = cluster;
+ if (position < clustersize)
+ {
+ break;
+ }
+
+ /* Extend the cluster chain if write in enabled. NOTE:
+ * this is not consistent with the lseek description:
+ * "The lseek() function allows the file offset to be
+ * set beyond the end of the file (but this does not
+ * change the size of the file). If data is later written
+ * at this point, subsequent reads of the data in the
+ * gap (a "hole") return null bytes ('\0') until data
+ * is actually written into the gap."
+ */
+
+ if ((ff->ff_oflags & O_WROK) != 0)
+ {
+ /* Extend the cluster chain (fat_extendchain
+ * will follow the existing chain or add new
+ * clusters as needed.
+ */
+
+ cluster = fat_extendchain(fs, cluster);
+ }
+ else
+ {
+ /* Otherwise we can only follong the existing chain */
+
+ cluster = fat_getcluster(fs, cluster);
+ }
+
+ if (cluster < 0)
+ {
+ /* An error occurred getting the cluster */
+
+ ret = cluster;
+ goto errout_with_semaphore;
+ }
+
+ /* Zero means that there is no further clusters available
+ * in the chain.
+ */
+
+ if (cluster == 0)
+ {
+ /* At the position to the current locaiton and
+ * break out.
+ */
+
+ position = clustersize;
+ break;
+ }
+
+ if (cluster >= fs->fs_nclusters)
+ {
+ ret = -ENOSPC;
+ goto errout_with_semaphore;
+ }
+
+ /* Otherwise, update the position and continue looking */
+
+ filep->f_pos += clustersize;
+ position -= clustersize;
+ }
+
+ /* We get here after we have found the sector containing
+ * the requested position.
+ *
+ * Save the new file position
+ */
+
+ filep->f_pos += position;
+
+ /* Then get the current sector from the cluster and the offset
+ * into the cluster from the position
+ */
+
+ (void)fat_currentsector(fs, ff, filep->f_pos);
+
+ /* Load the sector corresponding to the position */
+
+ if ((position & SEC_NDXMASK(fs)) != 0)
+ {
+ ret = fat_ffcacheread(fs, ff, ff->ff_currentsector);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+ }
+ }
+
+ /* If we extended the size of the file, then mark the file as modified. */
+
+ if ((ff->ff_oflags & O_WROK) != 0 && filep->f_pos > ff->ff_size)
+ {
+ ff->ff_size = filep->f_pos;
+ ff->ff_bflags |= FFBUFF_MODIFIED;
+ }
+
+ fat_semgive(fs);
+ return OK;
+
+errout_with_semaphore:
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_ioctl
+ ****************************************************************************/
+
+static int fat_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ struct inode *inode;
+ struct fat_mountpt_s *fs;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ inode = filep->f_inode;
+ fs = inode->i_private;
+
+ DEBUGASSERT(fs != NULL);
+
+ /* Make sure that the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ fat_semgive(fs);
+ return ret;
+ }
+
+ /* ioctl calls are just passed through to the contained block driver */
+
+ fat_semgive(fs);
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: fat_sync
+ *
+ * Description: Synchronize the file state on disk to match internal, in-
+ * memory state.
+ *
+ ****************************************************************************/
+
+static int fat_sync(FAR struct file *filep)
+{
+ struct inode *inode;
+ struct fat_mountpt_s *fs;
+ struct fat_file_s *ff;
+ uint32_t wrttime;
+ uint8_t *direntry;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ ff = filep->f_priv;
+ inode = filep->f_inode;
+ fs = inode->i_private;
+
+ DEBUGASSERT(fs != NULL);
+
+ /* Make sure that the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Check if the has been modified in any way */
+
+ if ((ff->ff_bflags & FFBUFF_MODIFIED) != 0)
+ {
+ /* Flush any unwritten data in the file buffer */
+
+ ret = fat_ffcacheflush(fs, ff);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Update the directory entry. First read the directory
+ * entry into the fs_buffer (preserving the ff_buffer)
+ */
+
+ ret = fat_fscacheread(fs, ff->ff_dirsector);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Recover a pointer to the specific directory entry
+ * in the sector using the saved directory index.
+ */
+
+ direntry = &fs->fs_buffer[(ff->ff_dirindex & DIRSEC_NDXMASK(fs)) * DIR_SIZE];
+
+ /* Set the archive bit, set the write time, and update
+ * anything that may have* changed in the directory
+ * entry: the file size, and the start cluster
+ */
+
+ direntry[DIR_ATTRIBUTES] |= FATATTR_ARCHIVE;
+
+ DIR_PUTFILESIZE(direntry, ff->ff_size);
+ DIR_PUTFSTCLUSTLO(direntry, ff->ff_startcluster);
+ DIR_PUTFSTCLUSTHI(direntry, ff->ff_startcluster >> 16);
+
+ wrttime = fat_systime2fattime();
+ DIR_PUTWRTTIME(direntry, wrttime & 0xffff);
+ DIR_PUTWRTDATE(direntry, wrttime >> 16);
+
+ /* Clear the modified bit in the flags */
+
+ ff->ff_bflags &= ~FFBUFF_MODIFIED;
+
+ /* Flush these change to disk and update FSINFO (if
+ * appropriate.
+ */
+
+ fs->fs_dirty = true;
+ ret = fat_updatefsinfo(fs);
+ }
+
+errout_with_semaphore:
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_opendir
+ *
+ * Description: Open a directory for read access
+ *
+ ****************************************************************************/
+
+static int fat_opendir(struct inode *mountpt, const char *relpath, struct fs_dirent_s *dir)
+{
+ struct fat_mountpt_s *fs;
+ struct fat_dirinfo_s dirinfo;
+ uint8_t *direntry;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Recover our private data from the inode instance */
+
+ fs = mountpt->i_private;
+
+ /* Make sure that the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Find the requested directory */
+
+ ret = fat_finddirentry(fs, &dirinfo, relpath);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+ direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset];
+
+ /* Check if this is the root directory */
+
+ if (dirinfo.fd_root)
+ {
+ /* Handle the FAT12/16/32 root directory using the values setup by
+ * fat_finddirentry() above.
+ */
+
+ dir->u.fat.fd_startcluster = dirinfo.dir.fd_startcluster;
+ dir->u.fat.fd_currcluster = dirinfo.dir.fd_currcluster;
+ dir->u.fat.fd_currsector = dirinfo.dir.fd_currsector;
+ dir->u.fat.fd_index = dirinfo.dir.fd_index;
+ }
+
+ /* This is not the root directory. Verify that it is some kind of directory */
+
+ else if ((DIR_GETATTRIBUTES(direntry) & FATATTR_DIRECTORY) == 0)
+ {
+ /* The entry is not a directory */
+
+ ret = -ENOTDIR;
+ goto errout_with_semaphore;
+ }
+ else
+ {
+ /* The entry is a directory (but not the root directory) */
+
+ dir->u.fat.fd_startcluster =
+ ((uint32_t)DIR_GETFSTCLUSTHI(direntry) << 16) |
+ DIR_GETFSTCLUSTLO(direntry);
+ dir->u.fat.fd_currcluster = dir->u.fat.fd_startcluster;
+ dir->u.fat.fd_currsector = fat_cluster2sector(fs, dir->u.fat.fd_currcluster);
+ dir->u.fat.fd_index = 2;
+ }
+
+ fat_semgive(fs);
+ return OK;
+
+errout_with_semaphore:
+ fat_semgive(fs);
+ return ERROR;
+}
+
+/****************************************************************************
+ * Name: fat_readdir
+ *
+ * Description: Read the next directory entry
+ *
+ ****************************************************************************/
+
+static int fat_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
+{
+ struct fat_mountpt_s *fs;
+ unsigned int dirindex;
+ uint8_t *direntry;
+ uint8_t ch;
+ uint8_t attribute;
+ bool found;
+ int ret = OK;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Recover our private data from the inode instance */
+
+ fs = mountpt->i_private;
+
+ /* Make sure that the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Read the next directory entry */
+
+ dir->fd_dir.d_name[0] = '\0';
+ found = false;
+
+ while (dir->u.fat.fd_currsector && !found)
+ {
+ ret = fat_fscacheread(fs, dir->u.fat.fd_currsector);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Get a reference to the current directory entry */
+
+ dirindex = (dir->u.fat.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
+ direntry = &fs->fs_buffer[dirindex];
+
+ /* Has it reached to end of the directory */
+
+ ch = *direntry;
+ if (ch == DIR0_ALLEMPTY)
+ {
+ /* We signal the end of the directory by returning the
+ * special error -ENOENT
+ */
+
+ ret = -ENOENT;
+ goto errout_with_semaphore;
+ }
+
+ /* No, is the current entry a valid entry? */
+
+ attribute = DIR_GETATTRIBUTES(direntry);
+
+#ifdef CONFIG_FAT_LFN
+ if (ch != DIR0_EMPTY &&
+ ((attribute & FATATTR_VOLUMEID) == 0 ||
+ ((ch & LDIR0_LAST) != 0 && attribute == LDDIR_LFNATTR)))
+#else
+ if (ch != DIR0_EMPTY && (attribute & FATATTR_VOLUMEID) == 0)
+#endif
+ {
+ /* Yes.. get the name from the directory entry. NOTE: For the case
+ * of the long file name entry, this will advance the several
+ * several directory entries.
+ */
+
+ ret = fat_dirname2path(fs, dir);
+ if (ret == OK)
+ {
+ /* The name was successfully extracted. Re-read the
+ * attributes: If this is long directory entry, then the
+ * attributes that we need will be the the final, short file
+ * name entry and not in the directory entry where we started
+ * looking for the file name. We can be assured that, on
+ * success, fat_dirname2path() will leave the short file name
+ * entry in the cache regardless of the kind of directory
+ * entry. We simply have to re-read it to cover the the long
+ * file name case.
+ */
+
+#ifdef CONFIG_FAT_LFN
+
+ /* Get a reference to the current, short file name directory
+ * entry.
+ */
+
+ dirindex = (dir->u.fat.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
+ direntry = &fs->fs_buffer[dirindex];
+
+ /* Then re-read the attributes from the short file name entry */
+
+ attribute = DIR_GETATTRIBUTES(direntry);
+#endif
+ /* Now get the file type from the directory attributes. */
+
+ if ((attribute & FATATTR_DIRECTORY) == 0)
+ {
+ dir->fd_dir.d_type = DTYPE_FILE;
+ }
+ else
+ {
+ dir->fd_dir.d_type = DTYPE_DIRECTORY;
+ }
+
+ /* Mark the entry found. We will set up the next directory index,
+ * and then exit with success.
+ */
+
+ found = true;
+ }
+ }
+
+ /* Set up the next directory index */
+
+ if (fat_nextdirentry(fs, &dir->u.fat) != OK)
+ {
+ ret = -ENOENT;
+ goto errout_with_semaphore;
+ }
+ }
+
+ fat_semgive(fs);
+ return OK;
+
+errout_with_semaphore:
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_rewindir
+ *
+ * Description: Reset directory read to the first entry
+ *
+ ****************************************************************************/
+
+static int fat_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir)
+{
+ struct fat_mountpt_s *fs;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Recover our private data from the inode instance */
+
+ fs = mountpt->i_private;
+
+ /* Make sure that the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Check if this is the root directory. If it is the root directory, we
+ * reset the fd_index to 0, starting with the initial, entry.
+ */
+
+ if (fs->fs_type != FSTYPE_FAT32 &&
+ dir->u.fat.fd_startcluster == 0)
+ {
+ /* Handle the FAT12/16 root directory */
+
+ dir->u.fat.fd_currcluster = 0;
+ dir->u.fat.fd_currsector = fs->fs_rootbase;
+ dir->u.fat.fd_index = 0;
+ }
+ else if (fs->fs_type == FSTYPE_FAT32 &&
+ dir->u.fat.fd_startcluster == fs->fs_rootbase)
+ {
+ /* Handle the FAT32 root directory */
+
+ dir->u.fat.fd_currcluster = dir->u.fat.fd_startcluster;
+ dir->u.fat.fd_currsector = fat_cluster2sector(fs, fs->fs_rootbase);
+ dir->u.fat.fd_index = 0;
+ }
+
+ /* This is not the root directory. Here the fd_index is set to 2, skipping over
+ * both the "." and ".." entries.
+ */
+
+ else
+ {
+ dir->u.fat.fd_currcluster = dir->u.fat.fd_startcluster;
+ dir->u.fat.fd_currsector = fat_cluster2sector(fs, dir->u.fat.fd_currcluster);
+ dir->u.fat.fd_index = 2;
+ }
+
+ fat_semgive(fs);
+ return OK;
+
+errout_with_semaphore:
+ fat_semgive(fs);
+ return ERROR;
+}
+
+/****************************************************************************
+ * Name: fat_bind
+ *
+ * Description: This implements a portion of the mount operation. This
+ * function allocates and initializes the mountpoint private data and
+ * binds the blockdriver inode to the filesystem private data. The final
+ * binding of the private data (containing the blockdriver) to the
+ * mountpoint is performed by mount().
+ *
+ ****************************************************************************/
+
+static int fat_bind(FAR struct inode *blkdriver, const void *data,
+ void **handle)
+{
+ struct fat_mountpt_s *fs;
+ int ret;
+
+ /* Open the block driver */
+
+ if (!blkdriver || !blkdriver->u.i_bops)
+ {
+ return -ENODEV;
+ }
+
+ if (blkdriver->u.i_bops->open &&
+ blkdriver->u.i_bops->open(blkdriver) != OK)
+ {
+ return -ENODEV;
+ }
+
+ /* Create an instance of the mountpt state structure */
+
+ fs = (struct fat_mountpt_s *)kzalloc(sizeof(struct fat_mountpt_s));
+ if (!fs)
+ {
+ return -ENOMEM;
+ }
+
+ /* Initialize the allocated mountpt state structure. The filesystem is
+ * responsible for one reference ont the blkdriver inode and does not
+ * have to addref() here (but does have to release in ubind().
+ */
+
+ fs->fs_blkdriver = blkdriver; /* Save the block driver reference */
+ sem_init(&fs->fs_sem, 0, 0); /* Initialize the semaphore that controls access */
+
+ /* Then get information about the FAT32 filesystem on the devices managed
+ * by this block driver.
+ */
+
+ ret = fat_mount(fs, true);
+ if (ret != 0)
+ {
+ sem_destroy(&fs->fs_sem);
+ kfree(fs);
+ return ret;
+ }
+
+ *handle = (void*)fs;
+ fat_semgive(fs);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_unbind
+ *
+ * Description: This implements the filesystem portion of the umount
+ * operation.
+ *
+ ****************************************************************************/
+
+static int fat_unbind(void *handle, FAR struct inode **blkdriver)
+{
+ struct fat_mountpt_s *fs = (struct fat_mountpt_s*)handle;
+ int ret;
+
+ if (!fs)
+ {
+ return -EINVAL;
+ }
+
+ /* Check if there are sill any files opened on the filesystem. */
+
+ ret = OK; /* Assume success */
+ fat_semtake(fs);
+ if (fs->fs_head)
+ {
+ /* We cannot unmount now.. there are open files */
+
+ ret = -EBUSY;
+ }
+ else
+ {
+ /* Unmount ... close the block driver */
+
+ if (fs->fs_blkdriver)
+ {
+ struct inode *inode = fs->fs_blkdriver;
+ if (inode)
+ {
+ if (inode->u.i_bops && inode->u.i_bops->close)
+ {
+ (void)inode->u.i_bops->close(inode);
+ }
+
+ /* We hold a reference to the block driver but should
+ * not but mucking with inodes in this context. So, we will just return
+ * our contained reference to the block driver inode and let the umount
+ * logic dispose of it.
+ */
+
+ if (blkdriver)
+ {
+ *blkdriver = inode;
+ }
+ }
+ }
+
+ /* Release the mountpoint private data */
+
+ if (fs->fs_buffer)
+ {
+ fat_io_free(fs->fs_buffer, fs->fs_hwsectorsize);
+ }
+
+ kfree(fs);
+ }
+
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_statfs
+ *
+ * Description: Return filesystem statistics
+ *
+ ****************************************************************************/
+
+static int fat_statfs(struct inode *mountpt, struct statfs *buf)
+{
+ struct fat_mountpt_s *fs;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Fill in the statfs info */
+
+ memset(buf, 0, sizeof(struct statfs));
+ buf->f_type = MSDOS_SUPER_MAGIC;
+
+ /* We will claim that the optimal transfer size is the size of a cluster in bytes */
+
+ buf->f_bsize = fs->fs_fatsecperclus * fs->fs_hwsectorsize;
+
+ /* Everything else follows in units of clusters */
+
+ ret = fat_nfreeclusters(fs, &buf->f_bfree); /* Free blocks in the file system */
+ if (ret >= 0)
+ {
+ buf->f_blocks = fs->fs_nclusters; /* Total data blocks in the file system */
+ buf->f_bavail = buf->f_bfree; /* Free blocks avail to non-superuser */
+#ifdef CONFIG_FAT_LFN
+ buf->f_namelen = LDIR_MAXFNAME; /* Maximum length of filenames */
+#else
+ buf->f_namelen = (8+1+3); /* Maximum length of filenames */
+#endif
+ }
+
+errout_with_semaphore:
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_unlink
+ *
+ * Description: Remove a file
+ *
+ ****************************************************************************/
+
+static int fat_unlink(struct inode *mountpt, const char *relpath)
+{
+ struct fat_mountpt_s *fs;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret == OK)
+ {
+ /* If the file is open, the correct behavior is to remove the file
+ * name, but to keep the file cluster chain in place until the last
+ * open reference to the file is closed.
+ */
+
+#ifdef CONFIG_CPP_HAVE_WARNING
+# warning "Need to defer deleting cluster chain if the file is open"
+#endif
+
+ /* Remove the file */
+
+ ret = fat_remove(fs, relpath, false);
+ }
+
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_mkdir
+ *
+ * Description: Create a directory
+ *
+ ****************************************************************************/
+
+static int fat_mkdir(struct inode *mountpt, const char *relpath, mode_t mode)
+{
+ struct fat_mountpt_s *fs;
+ struct fat_dirinfo_s dirinfo;
+ uint8_t *direntry;
+ uint8_t *direntry2;
+ off_t parentsector;
+ off_t dirsector;
+ int32_t dircluster;
+ uint32_t parentcluster;
+ uint32_t crtime;
+ unsigned int i;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Find the directory where the new directory should be created. */
+
+ ret = fat_finddirentry(fs, &dirinfo, relpath);
+
+ /* If anything exists at this location, then we fail with EEXIST */
+
+ if (ret == OK)
+ {
+ ret = -EEXIST;
+ goto errout_with_semaphore;
+ }
+
+ /* What we want to see is for fat_finddirentry to fail with -ENOENT.
+ * This error means that no failure occurred but that nothing exists
+ * with this name. NOTE: The name has already been set in dirinfo
+ * structure.
+ */
+
+ if (ret != -ENOENT)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* NOTE: There is no check that dirinfo.fd_name contains the final
+ * directory name. We could be creating an intermediate directory
+ * in the full relpath.
+ */
+
+ /* Allocate a directory entry for the new directory in this directory */
+
+ ret = fat_allocatedirentry(fs, &dirinfo);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+ parentsector = fs->fs_currentsector;
+
+ /* Allocate a cluster for new directory */
+
+ dircluster = fat_createchain(fs);
+ if (dircluster < 0)
+ {
+ ret = dircluster;
+ goto errout_with_semaphore;
+ }
+ else if (dircluster < 2)
+ {
+ ret = -ENOSPC;
+ goto errout_with_semaphore;
+ }
+
+ dirsector = fat_cluster2sector(fs, dircluster);
+ if (dirsector < 0)
+ {
+ ret = dirsector;
+ goto errout_with_semaphore;
+ }
+
+ /* Flush any existing, dirty data in fs_buffer (because we need
+ * it to create the directory entries.
+ */
+
+ ret = fat_fscacheflush(fs);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Get a pointer to the first directory entry in the sector */
+
+ direntry = fs->fs_buffer;
+
+ /* Now erase the contents of fs_buffer */
+
+ fs->fs_currentsector = dirsector;
+ memset(direntry, 0, fs->fs_hwsectorsize);
+
+ /* Now clear all sectors in the new directory cluster (except for the first) */
+
+ for (i = 1; i < fs->fs_fatsecperclus; i++)
+ {
+ ret = fat_hwwrite(fs, direntry, ++dirsector, 1);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+ }
+
+ /* Now create the "." directory entry in the first directory slot. These
+ * are special directory entries and are not handled by the normal directory
+ * management routines.
+ */
+
+ memset(&direntry[DIR_NAME], ' ', DIR_MAXFNAME);
+ direntry[DIR_NAME] = '.';
+ DIR_PUTATTRIBUTES(direntry, FATATTR_DIRECTORY);
+
+ crtime = fat_systime2fattime();
+ DIR_PUTCRTIME(direntry, crtime & 0xffff);
+ DIR_PUTWRTTIME(direntry, crtime & 0xffff);
+ DIR_PUTCRDATE(direntry, crtime >> 16);
+ DIR_PUTWRTDATE(direntry, crtime >> 16);
+
+ /* Create ".." directory entry in the second directory slot */
+
+ direntry2 = direntry + DIR_SIZE;
+
+ /* So far, the two entries are nearly the same */
+
+ memcpy(direntry2, direntry, DIR_SIZE);
+ direntry2[DIR_NAME+1] = '.';
+
+ /* Now add the cluster information to both directory entries */
+
+ DIR_PUTFSTCLUSTHI(direntry, dircluster >> 16);
+ DIR_PUTFSTCLUSTLO(direntry, dircluster);
+
+ parentcluster = dirinfo.dir.fd_startcluster;
+ if (fs->fs_type != FSTYPE_FAT32 && parentcluster == fs->fs_rootbase)
+ {
+ parentcluster = 0;
+ }
+
+ DIR_PUTFSTCLUSTHI(direntry2, parentcluster >> 16);
+ DIR_PUTFSTCLUSTLO(direntry2, parentcluster);
+
+ /* Save the first sector of the directory cluster and re-read
+ * the parentsector
+ */
+
+ fs->fs_dirty = true;
+ ret = fat_fscacheread(fs, parentsector);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Write the new entry directory entry in the parent directory */
+
+ ret = fat_dirwrite(fs, &dirinfo, FATATTR_DIRECTORY, crtime);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Set subdirectory start cluster. We assume that fat_dirwrite() did not
+ * change the sector in the cache.
+ */
+
+ direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset];
+ DIR_PUTFSTCLUSTLO(direntry, dircluster);
+ DIR_PUTFSTCLUSTHI(direntry, dircluster >> 16);
+ fs->fs_dirty = true;
+
+ /* Now update the FAT32 FSINFO sector */
+
+ ret = fat_updatefsinfo(fs);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ fat_semgive(fs);
+ return OK;
+
+errout_with_semaphore:
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_rmdir
+ *
+ * Description: Remove a directory
+ *
+ ****************************************************************************/
+
+int fat_rmdir(struct inode *mountpt, const char *relpath)
+{
+ struct fat_mountpt_s *fs;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret == OK)
+ {
+ /* If the directory is open, the correct behavior is to remove the directory
+ * name, but to keep the directory cluster chain in place until the last
+ * open reference to the directory is closed.
+ */
+
+#ifdef CONFIG_CPP_HAVE_WARNING
+# warning "Need to defer deleting cluster chain if the directory is open"
+#endif
+
+ /* Remove the directory */
+
+ ret = fat_remove(fs, relpath, true);
+ }
+
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_rename
+ *
+ * Description: Rename a file or directory
+ *
+ ****************************************************************************/
+
+int fat_rename(struct inode *mountpt, const char *oldrelpath,
+ const char *newrelpath)
+{
+ struct fat_mountpt_s *fs;
+ struct fat_dirinfo_s dirinfo;
+ struct fat_dirseq_s dirseq;
+ uint8_t *direntry;
+ uint8_t dirstate[DIR_SIZE-DIR_ATTRIBUTES];
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Find the directory entry for the oldrelpath (there may be multiple
+ * directory entries if long file name support is enabled).
+ */
+
+ ret = fat_finddirentry(fs, &dirinfo, oldrelpath);
+ if (ret != OK)
+ {
+ /* Some error occurred -- probably -ENOENT */
+
+ goto errout_with_semaphore;
+ }
+
+ /* One more check: Make sure that the oldrelpath does not refer to the
+ * root directory. We can't rename the root directory.
+ */
+
+ if (dirinfo.fd_root)
+ {
+ ret = -EXDEV;
+ goto errout_with_semaphore;
+ }
+
+ /* Save the information that will need to recover the directory sector and
+ * directory entry offset to the old directory.
+ *
+ * Save the positional information of the old directory entry.
+ */
+
+ memcpy(&dirseq, &dirinfo.fd_seq, sizeof(struct fat_dirseq_s));
+
+ /* Save the non-name-related portion of the directory entry intact */
+
+ direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset];
+ memcpy(dirstate, &direntry[DIR_ATTRIBUTES], DIR_SIZE-DIR_ATTRIBUTES);
+
+ /* Now find the directory where we should create the newpath object */
+
+ ret = fat_finddirentry(fs, &dirinfo, newrelpath);
+ if (ret == OK)
+ {
+ /* It is an error if the object at newrelpath already exists */
+
+ ret = -EEXIST;
+ goto errout_with_semaphore;
+ }
+
+ /* What we expect is -ENOENT mean that the full directory path was
+ * followed but that the object does not exists in the terminal directory.
+ */
+
+ if (ret != -ENOENT)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Reserve a directory entry. If long file name support is enabled, then
+ * this might, in fact, allocate a sequence of directory entries. A side
+ * effect of fat_allocatedirentry() in either case is that it leaves the
+ * short file name entry in the sector cache.
+ */
+
+ ret = fat_allocatedirentry(fs, &dirinfo);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Then write the new file name into the directory entry. This, of course,
+ * may involve writing multiple directory entries if long file name
+ * support is enabled. A side effect of fat_allocatedirentry() in either
+ * case is that it leaves the short file name entry in the sector cache.
+ */
+
+ ret = fat_dirnamewrite(fs, &dirinfo);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Copy the unchanged information into the new short file name entry. */
+
+ direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset];
+ memcpy(&direntry[DIR_ATTRIBUTES], dirstate, DIR_SIZE-DIR_ATTRIBUTES);
+ fs->fs_dirty = true;
+
+ /* Remove the old entry, flushing the new directory entry to disk. If
+ * the old file name was a long file name, then multiple directory
+ * entries may be freed.
+ */
+
+ ret = fat_freedirentry(fs, &dirseq);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Write the old entry to disk and update FSINFO if necessary */
+
+ ret = fat_updatefsinfo(fs);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ fat_semgive(fs);
+ return OK;
+
+errout_with_semaphore:
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_stat
+ *
+ * Description: Return information about a file or directory
+ *
+ ****************************************************************************/
+
+static int fat_stat(struct inode *mountpt, const char *relpath, struct stat *buf)
+{
+ struct fat_mountpt_s *fs;
+ struct fat_dirinfo_s dirinfo;
+ uint16_t fatdate;
+ uint16_t date2;
+ uint16_t fattime;
+ uint8_t *direntry;
+ uint8_t attribute;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Find the directory entry corresponding to relpath. */
+
+ ret = fat_finddirentry(fs, &dirinfo, relpath);
+
+ /* If nothing was found, then we fail with the reported error */
+
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ memset(buf, 0, sizeof(struct stat));
+ if (dirinfo.fd_root)
+ {
+ /* It's directory name of the mount point */
+
+ buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR|S_IWOTH|S_IWGRP|S_IWUSR;
+ ret = OK;
+ goto errout_with_semaphore;
+ }
+
+ /* Get the FAT attribute and map it so some meaningful mode_t values */
+
+ direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset];
+ attribute = DIR_GETATTRIBUTES(direntry);
+ if ((attribute & FATATTR_VOLUMEID) != 0)
+ {
+ ret = -ENOENT;
+ goto errout_with_semaphore;
+ }
+
+ /* Set the access permissions. The file/directory is always readable
+ * by everyone but may be writeable by no-one.
+ */
+
+ buf->st_mode = S_IROTH|S_IRGRP|S_IRUSR;
+ if ((attribute & FATATTR_READONLY) == 0)
+ {
+ buf->st_mode |= S_IWOTH|S_IWGRP|S_IWUSR;
+ }
+
+ /* We will report only types file or directory */
+
+ if ((attribute & FATATTR_DIRECTORY) != 0)
+ {
+ buf->st_mode |= S_IFDIR;
+ }
+ else
+ {
+ buf->st_mode |= S_IFREG;
+ }
+
+ /* File/directory size, access block size */
+
+ buf->st_size = DIR_GETFILESIZE(direntry);
+ buf->st_blksize = fs->fs_fatsecperclus * fs->fs_hwsectorsize;
+ buf->st_blocks = (buf->st_size + buf->st_blksize - 1) / buf->st_blksize;
+
+ /* Times */
+
+ fatdate = DIR_GETWRTDATE(direntry);
+ fattime = DIR_GETWRTTIME(direntry);
+ buf->st_mtime = fat_fattime2systime(fattime, fatdate);
+
+ date2 = DIR_GETLASTACCDATE(direntry);
+ if (fatdate == date2)
+ {
+ buf->st_atime = buf->st_mtime;
+ }
+ else
+ {
+ buf->st_atime = fat_fattime2systime(0, date2);
+ }
+
+ fatdate = DIR_GETCRDATE(direntry);
+ fattime = DIR_GETCRTIME(direntry);
+ buf->st_ctime = fat_fattime2systime(fattime, fatdate);
+
+ ret = OK;
+
+errout_with_semaphore:
+ fat_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
diff --git a/nuttx/fs/fat/fs_fat32.h b/nuttx/fs/fat/fs_fat32.h
new file mode 100644
index 000000000..71a21333b
--- /dev/null
+++ b/nuttx/fs/fat/fs_fat32.h
@@ -0,0 +1,938 @@
+/****************************************************************************
+ * fs/fat/fs_fat32.h
+ *
+ * Copyright (C) 2007-2009, 2011 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_FAT_FS_FAT32_H
+#define __FS_FAT_FS_FAT32_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <semaphore.h>
+#include <time.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/dirent.h>
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * These offsets describes the master boot record.
+ *
+ * The folowing fields are common to FAT12/16/32 (but all value descriptions
+ * refer to the interpretation under FAT32.
+ */
+
+#define BS_JUMP 0 /* 3@0: Jump instruction to boot code (ignored) */
+#define BS_OEMNAME 3 /* 8@3: Usually "MSWIN4.1" */
+#define BS_BYTESPERSEC 11 /* 2@11: Bytes per sector: 512, 1024, 2048, 4096 */
+#define BS_SECPERCLUS 13 /* 1@13: Sectors per allocation unit: 2**n, n=0..7 */
+#define BS_RESVDSECCOUNT 14 /* 2@14: Reserved sector count: Usually 32 */
+#define BS_NUMFATS 16 /* 1@16: Number of FAT data structures: always 2 */
+#define BS_ROOTENTCNT 17 /* 2@17: FAT12/16: Must be 0 for FAT32 */
+#define BS_TOTSEC16 19 /* 2@19: FAT12/16: Must be 0, see BS_TOTSEC32 */
+#define BS_MEDIA 21 /* 1@21: Media code: f0, f8, f9-fa, fc-ff */
+#define BS_FATSZ16 22 /* 2@22: FAT12/16: Must be 0, see BS_FATSZ32 */
+#define BS_SECPERTRK 24 /* 2@24: Sectors per track geometry value */
+#define BS_NUMHEADS 26 /* 2@26: Number of heads geometry value */
+#define BS_HIDSEC 28 /* 4@28: Count of hidden sectors preceding FAT */
+#define BS_TOTSEC32 32 /* 4@32: Total count of sectors on the volume */
+
+/* The following fields are only valid for FAT12/16 */
+
+#define BS16_DRVNUM 36 /* 1@36: Drive number for MSDOS bootstrap */
+ /* 1@37: Reserved (zero) */
+#define BS16_BOOTSIG 38 /* 1@38: Extended boot signature: 0x29 if following valid */
+#define BS16_VOLID 39 /* 4@39: Volume serial number */
+#define BS16_VOLLAB 43 /* 11@43: Volume label */
+#define BS16_FILESYSTYPE 54 /* 8@54: "FAT12 ", "FAT16 ", or "FAT " */
+
+#define BS16_BOOTCODE 62 /* Boot code may be placed in the remainder of the sector */
+#define BS16_BOOTCODESIZE 448
+
+/* The following fields are only valid for FAT32 */
+
+#define BS32_FATSZ32 36 /* 4@36: Count of sectors occupied by one FAT */
+#define BS32_EXTFLAGS 40 /* 2@40: 0-3:Active FAT, 7=0 both FATS, 7=1 one FAT */
+#define BS32_FSVER 42 /* 2@42: MSB:Major LSB:Minor revision number (0.0) */
+#define BS32_ROOTCLUS 44 /* 4@44: Cluster no. of 1st cluster of root dir */
+#define BS32_FSINFO 48 /* 2@48: Sector number of fsinfo structure. Usually 1. */
+#define BS32_BKBOOTSEC 50 /* 2@50: Sector number of boot record. Usually 6 */
+ /* 12@52: Reserved (zero) */
+#define BS32_DRVNUM 64 /* 1@64: Drive number for MSDOS bootstrap */
+ /* 1@65: Reserved (zero) */
+#define BS32_BOOTSIG 66 /* 1@66: Extended boot signature: 0x29 if following valid */
+#define BS32_VOLID 67 /* 4@67: Volume serial number */
+#define BS32_VOLLAB 71 /* 11@71: Volume label */
+#define BS32_FILESYSTYPE 82 /* 8@82: "FAT12 ", "FAT16 ", or "FAT " */
+
+#define BS32_BOOTCODE 90 /* Boot code may be placed in the remainder of the sector */
+#define BS32_BOOTCODESIZE 420
+
+/* If the sector is not an MBR, then it could have a partition table at
+ * this offset.
+ */
+
+#define MBR_TABLE 446
+
+/* The magic bytes at the end of the MBR are common to FAT12/16/32 */
+
+#define BS_SIGNATURE 510 /* 2@510: Valid MBRs have 0x55aa here */
+
+#define BOOT_SIGNATURE16 0xaa55
+#define BOOT_SIGNATURE32 0xaa550000
+
+/* The extended boot signature (BS16/32_BOOTSIG) */
+
+#define EXTBOOT_SIGNATURE 0x29
+
+/****************************************************************************
+ * These offsets describes the partition table.
+ */
+ /* 446@0: Generally unused and zero; but may
+ * include IDM Boot Manager menu entry at 8@394 */
+#define PART_ENTRY(n) (446+((n) << 4)) /* n = 0,1,2,3 */
+#define PART_ENTRY1 446 /* 16@446: Partition table, first entry */
+#define PART_ENTRY2 462 /* 16@462: Partition table, second entry */
+#define PART_ENTRY3 478 /* 16@478: Partition table, third entry */
+#define PART_ENTRY4 494 /* 16@494: Partition table, fourth entry */
+#define PART_SIGNATURE 510 /* 2@510: Valid partitions have 0x55aa here */
+
+/****************************************************************************
+ * These offsets describes one partition table entry. NOTE that ent entries
+ * are aligned to 16-bit offsets so that the STARTSECTOR and SIZE values are
+ * not properly aligned.
+ */
+
+#define PART_BOOTINDICATOR 0 /* 1@0: Boot indicator (0x80: active;0x00:otherwise) */
+#define PART_STARTCHS 1 /* 3@1: Starting Cylinder/Head/Sector values */
+#define PART_TYPE 4 /* 1@4: Partition type description */
+#define PART_ENDCHS 5 /* 3@5: Ending Cylinder/Head/Sector values */
+#define PART_STARTSECTOR 8 /* 4@8: Starting sector */
+#define PART_SIZE 12 /* 4@12: Partition size (in sectors) */
+
+/****************************************************************************
+ * Partition table types.
+ */
+
+#define PART_TYPE_NONE 0 /* No partition */
+#define PART_TYPE_FAT12 1 /* FAT12 */
+#define PART_TYPE_FAT16A 4 /* FAT16 (Partition smaller than 32MB) */
+#define PART_TYPE_EXT 5 /* Extended MS-DOS Partition */
+#define PART_TYPE_FAT16B 6 /* FAT16 (Partition larger than 32MB) */
+#define PART_TYPE_FAT32 11 /* FAT32 (Partition up to 2048Gb) */
+#define PART_TYPE_FAT32X 12 /* Same as 11, but uses LBA1 0x13 extensions */
+#define PART_TYPE_FAT16X 14 /* Same as 6, but uses LBA1 0x13 extensions */
+#define PART_TYPE_EXTX 15 /* Same as 5, but uses LBA1 0x13 extensions */
+
+/****************************************************************************
+ * Each FAT "short" 8.3 file name directory entry is 32-bytes long.
+ *
+ * Sizes and limits
+ */
+
+/****************************************************************************
+ * Each FAT "short" 8.3 file name directory entry is 32-bytes long.
+ *
+ * Sizes and limits
+ */
+
+#define DIR_MAXFNAME 11 /* Max short name size is 8+3 = 11 */
+
+/* The following define offsets relative to the beginning of a directory
+ * entry.
+ */
+
+#define DIR_NAME 0 /* 11@ 0: NAME: 8 bytes + 3 byte extension */
+#define DIR_ATTRIBUTES 11 /* 1@11: File attibutes (see below) */
+#define DIR_NTRES 12 /* 1@12: Reserved for use by NT */
+#define DIR_CRTTIMETENTH 13 /* 1@13: Tenth sec creation timestamp */
+#define DIR_CRTIME 14 /* 2@14: Time file created */
+#define DIR_CRDATE 16 /* 2@16: Date file created */
+#define DIR_LASTACCDATE 18 /* 2@19: Last access date */
+#define DIR_FSTCLUSTHI 20 /* 2@20: MS first cluster number */
+#define DIR_WRTTIME 22 /* 2@22: Time of last write */
+#define DIR_WRTDATE 24 /* 2@24: Date of last write */
+#define DIR_FSTCLUSTLO 26 /* 2@26: LS first cluster number */
+#define DIR_FILESIZE 28 /* 4@28: File size in bytes */
+#define DIR_SIZE 32 /* The size of one directory entry */
+#define DIR_SHIFT 5 /* log2 of DIR_SIZE */
+
+/* First byte of the directory name has special meanings: */
+
+#define DIR0_EMPTY 0xe5 /* The directory entry is empty */
+#define DIR0_ALLEMPTY 0x00 /* This entry and all following are empty */
+#define DIR0_E5 0x05 /* The actual value is 0xe5 */
+
+/* NTRES flags in the FAT directory */
+
+#define FATNTRES_LCNAME 0x08 /* Lower case in name */
+#define FATNTRES_LCEXT 0x10 /* Lower case in extension */
+
+/* Directory indexing helper. Each directory entry is 32-bytes in length.
+ * The number of directory entries in a sector then varies with the size
+ * of the sector supported in hardware.
+ */
+
+#define DIRSEC_NDXMASK(f) (((f)->fs_hwsectorsize - 1) >> 5)
+#define DIRSEC_NDIRS(f) (((f)->fs_hwsectorsize) >> 5)
+#define DIRSEC_BYTENDX(f,i) (((i) & DIRSEC_NDXMASK(fs)) << 5)
+
+#define SEC_NDXMASK(f) ((f)->fs_hwsectorsize - 1)
+#define SEC_NSECTORS(f,n) ((n) / (f)->fs_hwsectorsize)
+
+#define CLUS_NDXMASK(f) ((f)->fs_fatsecperclus - 1)
+
+/****************************************************************************
+ * The FAT "long" file name (LFN) directory entry */
+
+#ifdef CONFIG_FAT_LFN
+
+/* Sizes and limits */
+
+# ifndef CONFIG_FAT_MAXFNAME /* The maximum support filename can be limited */
+# define LDIR_MAXFNAME 255 /* Max unicode characters in file name */
+# elif CONFIG_FAT_MAXFNAME <= 255
+# define LDIR_MAXFNAME CONFIG_FAT_MAXFNAME
+# else
+# error "Illegal value for CONFIG_FAT_MAXFNAME"
+# endif
+
+# define LDIR_MAXLFNCHARS 13 /* Max unicode characters in one LFN entry */
+# define LDIR_MAXLFNS 20 /* Max number of LFN entries */
+
+/* LFN directory entry offsets */
+
+# define LDIR_SEQ 0 /* 1@ 0: Sequence number */
+# define LDIR_WCHAR1_5 1 /* 10@ 1: File name characters 1-5 (5 Unicode characters) */
+# define LDIR_ATTRIBUTES 11 /* 1@11: File attributes (always 0x0f) */
+# define LDIR_NTRES 12 /* 1@12: Reserved for use by NT (always 0x00) */
+# define LDIR_CHECKSUM 13 /* 1@13: Checksum of the DOS filename */
+# define LDIR_WCHAR6_11 14 /* 12@14: File name characters 6-11 (6 Unicode characters) */
+# define LDIR_FSTCLUSTLO 26 /* 2@26: First cluster (always 0x0000) */
+# define LDIR_WCHAR12_13 28 /* 4@28: File name characters 12-13 (2 Unicode characters) */
+
+/* LFN sequence number and allocation status */
+
+# define LDIR0_EMPTY DIR0_EMPTY /* The directory entry is empty */
+# define LDIR0_ALLEMPTY DIR0_ALLEMPTY /* This entry and all following are empty */
+# define LDIR0_E5 DIR0_E5 /* The actual value is 0xe5 */
+# define LDIR0_LAST 0x40 /* Last LFN in file name (appears first) */
+# define LDIR0_SEQ_MASK 0x1f /* Mask for sequence number (1-20) */
+
+/* The LFN entry attribute */
+
+# define LDDIR_LFNATTR 0x0f
+#endif
+
+/****************************************************************************
+ * File system types */
+
+#define FSTYPE_FAT12 0
+#define FSTYPE_FAT16 1
+#define FSTYPE_FAT32 2
+
+/* File buffer flags */
+
+#define FFBUFF_VALID 1
+#define FFBUFF_DIRTY 2
+#define FFBUFF_MODIFIED 4
+
+/****************************************************************************
+ * These offset describe the FSINFO sector
+ */
+
+#define FSI_LEADSIG 0 /* 4@0: 0x41615252 = "RRaA" */
+ /* 480@4: Reserved (zero) */
+#define FSI_STRUCTSIG 484 /* 4@484: 0x61417272 = "rrAa" */
+#define FSI_FREECOUNT 488 /* 4@488: Last free cluster count on volume */
+#define FSI_NXTFREE 492 /* 4@492: Cluster number of 1st free cluster */
+ /* 12@496: Reserved (zero) */
+#define FSI_TRAILSIG 508 /* 4@508: 0xaa550000 */
+
+/****************************************************************************
+ * FAT values
+ */
+
+#define FAT_EOF 0x0ffffff8
+#define FAT_BAD 0x0ffffff7
+
+/****************************************************************************
+ * Maximum cluster by FAT type. This is the key value used to distinquish
+ * between FAT12, 16, and 32.
+ */
+
+/* FAT12: For M$, the calculation is ((1 << 12) - 19). But we will follow the
+ * Linux tradition of allowing slightly more clusters for FAT12.
+ */
+
+#define FAT_MAXCLUST12 ((1 << 12) - 16)
+
+/* FAT16: For M$, the calculation is ((1 << 16) - 19). (The uint32_t cast is
+ * needed for architectures where int is only 16 bits).
+ */
+
+#define FAT_MINCLUST16 (FAT_MAXCLUST12 + 1)
+#define FAT_MAXCLUST16 (((uint32_t)1 << 16) - 16)
+
+/* FAT32: M$ reserves the MS 4 bits of a FAT32 FAT entry so only 18 bits are
+ * available. For M$, the calculation is ((1 << 28) - 19). (The uint32_t cast
+ * is needed for architectures where int is only 16 bits).
+ */
+
+#define FAT_MINCLUST32 65524
+/* #define FAT_MINCLUST32 (FAT_MAXCLUST16 + 1) */
+#define FAT_MAXCLUST32 (((uint32_t)1 << 28) - 16)
+
+/****************************************************************************
+ * Access to data in raw sector data */
+
+#define UBYTE_VAL(p,o) (((uint8_t*)(p))[o])
+#define UBYTE_PTR(p,o) &UBYTE_VAL(p,o)
+#define UBYTE_PUT(p,o,v) (UBYTE_VAL(p,o)=(uint8_t)(v))
+
+#define UINT16_PTR(p,o) ((uint16_t*)UBYTE_PTR(p,o))
+#define UINT16_VAL(p,o) (*UINT16_PTR(p,o))
+#define UINT16_PUT(p,o,v) (UINT16_VAL(p,o)=(uint16_t)(v))
+
+#define UINT32_PTR(p,o) ((uint32_t*)UBYTE_PTR(p,o))
+#define UINT32_VAL(p,o) (*UINT32_PTR(p,o))
+#define UINT32_PUT(p,o,v) (UINT32_VAL(p,o)=(uint32_t)(v))
+
+/* Regardless of the endian-ness of the target or alignment of the data, no
+ * special operations are required for byte, string or byte array accesses.
+ * The FAT data stream is little endian so multiple byte values must be
+ * accessed byte-by-byte for big-endian targets.
+ */
+
+#define MBR_GETSECPERCLUS(p) UBYTE_VAL(p,BS_SECPERCLUS)
+#define MBR_GETNUMFATS(p) UBYTE_VAL(p,BS_NUMFATS)
+#define MBR_GETMEDIA(p) UBYTE_VAL(p,BS_MEDIA)
+#define MBR_GETDRVNUM16(p) UBYTE_VAL(p,BS16_DRVNUM)
+#define MBR_GETDRVNUM32(p) UBYTE_VAL(p,BS32_DRVNUM)
+#define MBR_GETBOOTSIG16(p) UBYTE_VAL(p,BS16_BOOTSIG)
+#define MBR_GETBOOTSIG32(p) UBYTE_VAL(p,BS32_BOOTSIG)
+
+#define PART_GETTYPE(n,p) UBYTE_VAL(p,PART_ENTRY(n)+PART_TYPE)
+#define PART1_GETTYPE(p) UBYTE_VAL(p,PART_ENTRY1+PART_TYPE)
+#define PART2_GETTYPE(p) UBYTE_VAL(p,PART_ENTRY2+PART_TYPE)
+#define PART3_GETTYPE(p) UBYTE_VAL(p,PART_ENTRY3+PART_TYPE)
+#define PART4_GETTYPE(p) UBYTE_VAL(p,PART_ENTRY4+PART_TYPE)
+
+#define DIR_GETATTRIBUTES(p) UBYTE_VAL(p,DIR_ATTRIBUTES)
+#define DIR_GETNTRES(p) UBYTE_VAL(p,DIR_NTRES)
+#define DIR_GETCRTTIMETENTH(p) UBYTE_VAL(p,DIR_CRTTIMETENTH)
+
+#ifdef CONFIG_FAT_LFN
+# define LDIR_GETSEQ(p) UBYTE_VAL(p,LDIR_SEQ)
+# define LDIR_GETATTRIBUTES(p) UBYTE_VAL(p,LDIR_ATTRIBUTES)
+# define LDIR_GETNTRES(p) UBYTE_VAL(p,LDIR_NTRES)
+# define LDIR_GETCHECKSUM(p) UBYTE_VAL(p,LDIR_CHECKSUM)
+#endif
+
+#define MBR_PUTSECPERCLUS(p,v) UBYTE_PUT(p,BS_SECPERCLUS,v)
+#define MBR_PUTNUMFATS(p,v) UBYTE_PUT(p,BS_NUMFATS,v)
+#define MBR_PUTMEDIA(p,v) UBYTE_PUT(p,BS_MEDIA,v)
+#define MBR_PUTDRVNUM16(p,v) UBYTE_PUT(p,BS16_DRVNUM,v)
+#define MBR_PUTDRVNUM32(p,v) UBYTE_PUT(p,BS32_DRVNUM,v)
+#define MBR_PUTBOOTSIG16(p,v) UBYTE_PUT(p,BS16_BOOTSIG,v)
+#define MBR_PUTBOOTSIG32(p,v) UBYTE_PUT(p,BS32_BOOTSIG,v)
+
+#define PART_PUTTYPE(n,p,v) UBYTE_PUT(p,PART_ENTRY(n)+PART_TYPE,v)
+#define PART1_PUTTYPE(p,v) UBYTE_PUT(p,PART_ENTRY1+PART_TYPE,v)
+#define PART2_PUTTYPE(p,v) UBYTE_PUT(p,PART_ENTRY2+PART_TYPE,v)
+#define PART3_PUTTYPE(p,v) UBYTE_PUT(p,PART_ENTRY3+PART_TYPE,v)
+#define PART4_PUTTYPE(p,v) UBYTE_PUT(p,PART_ENTRY4+PART_TYPE,v)
+
+#define DIR_PUTATTRIBUTES(p,v) UBYTE_PUT(p,DIR_ATTRIBUTES,v)
+#define DIR_PUTNTRES(p,v) UBYTE_PUT(p,DIR_NTRES,v)
+#define DIR_PUTCRTTIMETENTH(p,v) UBYTE_PUT(p,DIR_CRTTIMETENTH,v)
+
+#ifdef CONFIG_FAT_LFN
+# define LDIR_PUTSEQ(p,v) UBYTE_PUT(p,LDIR_SEQ,v)
+# define LDIR_PUTATTRIBUTES(p,v) UBYTE_PUT(p,LDIR_ATTRIBUTES,v)
+# define LDIR_PUTNTRES(p,v) UBYTE_PUT(p,LDIR_NTRES,v)
+# define LDIR_PUTCHECKSUM(p,v) UBYTE_PUT(p,LDIR_CHECKSUM,v)
+#endif
+
+/* For the all targets, unaligned values need to be accessed byte-by-byte.
+ * Some architectures may handle unaligned accesses with special interrupt
+ * handlers. But even in that case, it is more efficient to avoid the traps.
+ */
+
+/* Unaligned multi-byte access macros */
+
+#define MBR_GETBYTESPERSEC(p) fat_getuint16(UBYTE_PTR(p,BS_BYTESPERSEC))
+#define MBR_GETROOTENTCNT(p) fat_getuint16(UBYTE_PTR(p,BS_ROOTENTCNT))
+#define MBR_GETTOTSEC16(p) fat_getuint16(UBYTE_PTR(p,BS_TOTSEC16))
+#define MBR_GETVOLID16(p) fat_getuint32(UBYTE_PTR(p,BS16_VOLID))
+#define MBR_GETVOLID32(p) fat_getuint32(UBYTE_PTR(p,BS32_VOLID))
+
+#define PART_GETSTARTSECTOR(n,p) fat_getuint32(UBYTE_PTR(p,PART_ENTRY(n)+PART_STARTSECTOR))
+#define PART_GETSIZE(n,p) fat_getuint32(UBYTE_PTR(p,PART_ENTRY(n)+PART_SIZE))
+#define PART1_GETSTARTSECTOR(p) fat_getuint32(UBYTE_PTR(p,PART_ENTRY1+PART_STARTSECTOR))
+#define PART1_GETSIZE(p) fat_getuint32(UBYTE_PTR(p,PART_ENTRY1+PART_SIZE))
+#define PART2_GETSTARTSECTOR(p) fat_getuint32(UBYTE_PTR(p,PART_ENTRY2+PART_STARTSECTOR))
+#define PART2_GETSIZE(p) fat_getuint32(UBYTE_PTR(p,PART_ENTRY2+PART_SIZE))
+#define PART3_GETSTARTSECTOR(p) fat_getuint32(UBYTE_PTR(p,PART_ENTRY3+PART_STARTSECTOR))
+#define PART3_GETSIZE(p) fat_getuint32(UBYTE_PTR(p,PART_ENTRY3+PART_SIZE))
+#define PART4_GETSTARTSECTOR(p) fat_getuint32(UBYTE_PTR(p,PART_ENTRY4+PART_STARTSECTOR))
+#define PART4_GETSIZE(p) fat_getuint32(UBYTE_PTR(p,PART_ENTRY4+PART_SIZE))
+
+#define MBR_PUTBYTESPERSEC(p,v) fat_putuint16(UBYTE_PTR(p,BS_BYTESPERSEC),v)
+#define MBR_PUTROOTENTCNT(p,v) fat_putuint16(UBYTE_PTR(p,BS_ROOTENTCNT),v)
+#define MBR_PUTTOTSEC16(p,v) fat_putuint16(UBYTE_PTR(p,BS_TOTSEC16),v)
+#define MBR_PUTVOLID16(p,v) fat_putuint32(UBYTE_PTR(p,BS16_VOLID),v)
+#define MBR_PUTVOLID32(p,v) fat_putuint32(UBYTE_PTR(p,BS32_VOLID),v)
+
+#define PART_PUTSTARTSECTOR(n,p,v) fat_putuint32(UBYTE_PTR(p,PART_ENTRY(n)+PART_STARTSECTOR),v)
+#define PART_PUTSIZE(n,p,v) fat_putuint32(UBYTE_PTR(p,PART_ENTRY(n)+PART_SIZE),v)
+#define PART1_PUTSTARTSECTOR(p,v) fat_putuint32(UBYTE_PTR(p,PART_ENTRY1+PART_STARTSECTOR),v)
+#define PART1_PUTSIZE(p,v) fat_putuint32(UBYTE_PTR(p,PART_ENTRY1+PART_SIZE),v)
+#define PART2_PUTSTARTSECTOR(p,v) fat_putuint32(UBYTE_PTR(p,PART_ENTRY2+PART_STARTSECTOR),v)
+#define PART2_PUTSIZE(p,v) fat_putuint32(UBYTE_PTR(p,PART_ENTRY2+PART_SIZE),v)
+#define PART3_PUTSTARTSECTOR(p,v) fat_putuint32(UBYTE_PTR(p,PART_ENTRY3+PART_STARTSECTOR),v)
+#define PART3_PUTSIZE(p,v) fat_putuint32(UBYTE_PTR(p,PART_ENTRY3+PART_SIZE),v)
+#define PART4_PUTSTARTSECTOR(p,v) fat_putuint32(UBYTE_PTR(p,PART_ENTRY4+PART_STARTSECTOR),v)
+#define PART4_PUTSIZE(p,v) fat_putuint32(UBYTE_PTR(p,PART_ENTRY4+PART_SIZE),v)
+
+#ifdef CONFIG_FAT_LFN
+# define LDIR_PTRWCHAR1_5(p) UBYTE_PTR(p,LDIR_WCHAR1_5)
+# define LDIR_PTRWCHAR6_11(p) UBYTE_PTR(p,LDIR_WCHAR6_11)
+# define LDIR_PTRWCHAR12_13(p) UBYTE_PTR(p,LDIR_WCHAR12_13)
+#endif
+
+/* But for multi-byte values, the endian-ness of the target vs. the little
+ * endian order of the byte stream or alignment of the data within the byte
+ * stream can force special, byte-by-byte accesses.
+ */
+
+#ifdef CONFIG_ENDIAN_BIG
+
+/* If the target is big-endian, then even aligned multi-byte values must be
+ * accessed byte-by-byte.
+ */
+
+# define MBR_GETRESVDSECCOUNT(p) fat_getuint16(UBYTE_PTR(p,BS_RESVDSECCOUNT))
+# define MBR_GETFATSZ16(p) fat_getuint16(UBYTE_PTR(p,BS_FATSZ16))
+# define MBR_GETSECPERTRK(p) fat_getuint16(UBYTE_PTR(p,BS_SECPERTRK))
+# define MBR_GETNUMHEADS(p) fat_getuint16(UBYTE_PTR(p,BS_NUMHEADS))
+# define MBR_GETHIDSEC(p) fat_getuint32(UBYTE_PTR(p,BS_HIDSEC))
+# define MBR_GETTOTSEC32(p) fat_getuint32(UBYTE_PTR(p,BS_TOTSEC32))
+# define MBR_GETFATSZ32(p) fat_getuint32(UBYTE_PTR(p,BS32_FATSZ32))
+# define MBR_GETEXTFLAGS(p) fat_getuint16(UBYTE_PTR(p,BS32_EXTFLAGS))
+# define MBR_GETFSVER(p) fat_getuint16(UBYTE_PTR(p,BS32_FSVER))
+# define MBR_GETROOTCLUS(p) fat_getuint32(UBYTE_PTR(p,BS32_ROOTCLUS))
+# define MBR_GETFSINFO(p) fat_getuint16(UBYTE_PTR(p,BS32_FSINFO))
+# define MBR_GETBKBOOTSEC(p) fat_getuint16(UBYTE_PTR(p,BS32_BKBOOTSEC))
+# define MBR_GETSIGNATURE(p) fat_getuint16(UBYTE_PTR(p,BS_SIGNATURE))
+
+# define FSI_GETLEADSIG(p) fat_getuint32(UBYTE_PTR(p,FSI_LEADSIG))
+# define FSI_GETSTRUCTSIG(p) fat_getuint32(UBYTE_PTR(p,FSI_STRUCTSIG))
+# define FSI_GETFREECOUNT(p) fat_getuint32(UBYTE_PTR(p,FSI_FREECOUNT))
+# define FSI_GETNXTFREE(p) fat_getuint32(UBYTE_PTR(p,FSI_NXTFREE))
+# define FSI_GETTRAILSIG(p) fat_getuint32(UBYTE_PTR(p,FSI_TRAILSIG))
+
+# define DIR_GETCRTIME(p) fat_getuint16(UBYTE_PTR(p,DIR_CRTIME))
+# define DIR_GETCRDATE(p) fat_getuint16(UBYTE_PTR(p,DIR_CRDATE))
+# define DIR_GETLASTACCDATE(p) fat_getuint16(UBYTE_PTR(p,DIR_LASTACCDATE))
+# define DIR_GETFSTCLUSTHI(p) fat_getuint16(UBYTE_PTR(p,DIR_FSTCLUSTHI))
+# define DIR_GETWRTTIME(p) fat_getuint16(UBYTE_PTR(p,DIR_WRTTIME))
+# define DIR_GETWRTDATE(p) fat_getuint16(UBYTE_PTR(p,DIR_WRTDATE))
+# define DIR_GETFSTCLUSTLO(p) fat_getuint16(UBYTE_PTR(p,DIR_FSTCLUSTLO))
+# define DIR_GETFILESIZE(p) fat_getuint32(UBYTE_PTR(p,DIR_FILESIZE))
+
+# ifdef CONFIG_FAT_LFN
+# define LDIR_GETWCHAR1(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR1_5))
+# define LDIR_GETWCHAR2(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR1_5+2))
+# define LDIR_GETWCHAR3(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR1_5+4))
+# define LDIR_GETWCHAR4(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR1_5+6))
+# define LDIR_GETWCHAR5(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR1_5+8))
+# define LDIR_GETWCHAR6(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR6_11))
+# define LDIR_GETWCHAR7(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR6_11+2))
+# define LDIR_GETWCHAR8(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR6_11+4))
+# define LDIR_GETWCHAR8(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR6_11+6))
+# define LDIR_GETWCHAR10(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR6_11+8))
+# define LDIR_GETWCHAR11(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR6_11+10))
+# define LDIR_GETWCHAR12(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR12_13))
+# define LDIR_GETWCHAR13(p) fat_getuint16(UBYTE_PTR(p,LDIR_WCHAR12_13+2))
+# define LDIR_GETFSTCLUSTLO(p) fat_getuint16(UBYTE_PTR(p,LDIR_FSTCLUSTLO))
+# endif
+
+# define FSI_GETLEADSIG(p) fat_getuint32(UBYTE_PTR(p,FSI_LEADSIG))
+# define FSI_GETSTRUCTSIG(p) fat_getuint32(UBYTE_PTR(p,FSI_STRUCTSIG))
+# define FSI_GETFREECOUNT(p) fat_getuint32(UBYTE_PTR(p,FSI_FREECOUNT))
+# define FSI_GETNXTFREE(p) fat_getuint32(UBYTE_PTR(p,FSI_NXTFREE))
+# define FSI_GETTRAILSIG(p) fat_getuint32(UBYTE_PTR(p,FSI_TRAILSIG))
+
+# define FAT_GETFAT16(p,i) fat_getuint16(UBYTE_PTR(p,i))
+# define FAT_GETFAT32(p,i) fat_getuint32(UBYTE_PTR(p,i))
+
+# define MBR_PUTRESVDSECCOUNT(p,v) fat_putuint16(UBYTE_PTR(p,BS_RESVDSECCOUNT),v)
+# define MBR_PUTFATSZ16(p,v) fat_putuint16(UBYTE_PTR(p,BS_FATSZ16),v)
+# define MBR_PUTSECPERTRK(p,v) fat_putuint16(UBYTE_PTR(p,BS_SECPERTRK),v)
+# define MBR_PUTNUMHEADS(p,v) fat_putuint16(UBYTE_PTR(p,BS_NUMHEADS),v)
+# define MBR_PUTHIDSEC(p,v) fat_putuint32(UBYTE_PTR(p,BS_HIDSEC),v)
+# define MBR_PUTTOTSEC32(p,v) fat_putuint32(UBYTE_PTR(p,BS_TOTSEC32),v)
+# define MBR_PUTFATSZ32(p,v) fat_putuint32(UBYTE_PTR(p,BS32_FATSZ32),v)
+# define MBR_PUTEXTFLAGS(p,v) fat_putuint16(UBYTE_PTR(p,BS32_EXTFLAGS),v)
+# define MBR_PUTFSVER(p,v) fat_putuint16(UBYTE_PTR(p,BS32_FSVER),v)
+# define MBR_PUTROOTCLUS(p,v) fat_putuint32(UBYTE_PTR(p,BS32_ROOTCLUS),v)
+# define MBR_PUTFSINFO(p,v) fat_putuint16(UBYTE_PTR(p,BS32_FSINFO),v)
+# define MBR_PUTBKBOOTSEC(p,v) fat_putuint16(UBYTE_PTR(p,BS32_BKBOOTSEC),v)
+# define MBR_PUTSIGNATURE(p,v) fat_putuint16(UBYTE_PTR(p,BS_SIGNATURE),v)
+
+# define FSI_PUTLEADSIG(p,v) fat_putuint32(UBYTE_PTR(p,FSI_LEADSIG),v)
+# define FSI_PUTSTRUCTSIG(p,v) fat_putuint32(UBYTE_PTR(p,FSI_STRUCTSIG),v)
+# define FSI_PUTFREECOUNT(p,v) fat_putuint32(UBYTE_PTR(p,FSI_FREECOUNT),v)
+# define FSI_PUTNXTFREE(p,v) fat_putuint32(UBYTE_PTR(p,FSI_NXTFREE),v)
+# define FSI_PUTTRAILSIG(p,v) fat_putuint32(UBYTE_PTR(p,FSI_TRAILSIG),v)
+
+# define DIR_PUTCRTIME(p,v) fat_putuint16(UBYTE_PTR(p,DIR_CRTIME),v)
+# define DIR_PUTCRDATE(p,v) fat_putuint16(UBYTE_PTR(p,DIR_CRDATE),v)
+# define DIR_PUTLASTACCDATE(p,v) fat_putuint16(UBYTE_PTR(p,DIR_LASTACCDATE),v)
+# define DIR_PUTFSTCLUSTHI(p,v) fat_putuint16(UBYTE_PTR(p,DIR_FSTCLUSTHI),v)
+# define DIR_PUTWRTTIME(p,v) fat_putuint16(UBYTE_PTR(p,DIR_WRTTIME),v)
+# define DIR_PUTWRTDATE(p,v) fat_putuint16(UBYTE_PTR(p,DIR_WRTDATE),v)
+# define DIR_PUTFSTCLUSTLO(p,v) fat_putuint16(UBYTE_PTR(p,DIR_FSTCLUSTLO),v)
+# define DIR_PUTFILESIZE(p,v) fat_putuint32(UBYTE_PTR(p,DIR_FILESIZE),v)
+
+# ifdef CONFIG_FAT_LFN
+# define LDIR_PUTWCHAR1(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR1_5),v)
+# define LDIR_PUTWCHAR2(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR1_5+2),v)
+# define LDIR_PUTWCHAR3(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR1_5+4),v)
+# define LDIR_PUTWCHAR4(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR1_5+6),v)
+# define LDIR_PUTWCHAR5(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR1_5+8),v)
+# define LDIR_PUTWCHAR6(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR6_11),v)
+# define LDIR_PUTWCHAR7(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR6_11+2),v)
+# define LDIR_PUTWCHAR8(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR6_11+4),v)
+# define LDIR_PUTWCHAR8(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR6_11+6),v)
+# define LDIR_PUTWCHAR10(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR6_11+8),v)
+# define LDIR_PUTWCHAR11(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR6_11+10),v)
+# define LDIR_PUTWCHAR12(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR12_13),v)
+# define LDIR_PUTWCHAR13(p) fat_putuint16(UBYTE_PTR(p,LDIR_WCHAR12_13+2),v)
+# endif
+
+# define FSI_PUTLEADSIG(p,v) fat_putuint32(UBYTE_PTR(p,FSI_LEADSIG),v)
+# define FSI_PUTSTRUCTSIG(p,v) fat_putuint32(UBYTE_PTR(p,FSI_STRUCTSIG),v)
+# define FSI_PUTFREECOUNT(p,v) fat_putuint32(UBYTE_PTR(p,FSI_FREECOUNT),v)
+# define FSI_PUTNXTFREE(p,v) fat_putuint32(UBYTE_PTR(p,FSI_NXTFREE),v)
+# define FSI_PUTTRAILSIG(p,v) fat_putuint32(UBYTE_PTR(p,FSI_TRAILSIG),v)
+
+# define FAT_PUTFAT16(p,i,v) fat_putuint16(UBYTE_PTR(p,i),v)
+# define FAT_PUTFAT32(p,i,v) fat_putuint32(UBYTE_PTR(p,i),v)
+
+#else
+
+/* But nothing special has to be done for the little endian-case for access
+ * to aligned mulitbyte values.
+ */
+
+# define MBR_GETRESVDSECCOUNT(p) UINT16_VAL(p,BS_RESVDSECCOUNT)
+# define MBR_GETFATSZ16(p) UINT16_VAL(p,BS_FATSZ16)
+# define MBR_GETSECPERTRK(p) UINT16_VAL(p,BS_SECPERTRK)
+# define MBR_GETNUMHEADS(p) UINT16_VAL(p,BS_NUMHEADS)
+# define MBR_GETHIDSEC(p) UINT32_VAL(p,BS_HIDSEC)
+# define MBR_GETTOTSEC32(p) UINT32_VAL(p,BS_TOTSEC32)
+# define MBR_GETFATSZ32(p) UINT32_VAL(p,BS32_FATSZ32)
+# define MBR_GETEXTFLAGS(p) UINT16_VAL(p,BS32_EXTFLAGS)
+# define MBR_GETFSVER(p) UINT16_VAL(p,BS32_FSVER)
+# define MBR_GETROOTCLUS(p) UINT32_VAL(p,BS32_ROOTCLUS)
+# define MBR_GETFSINFO(p) UINT16_VAL(p,BS32_FSINFO)
+# define MBR_GETBKBOOTSEC(p) UINT16_VAL(p,BS32_BKBOOTSEC)
+# define MBR_GETSIGNATURE(p) UINT16_VAL(p,BS_SIGNATURE)
+
+# define FSI_GETLEADSIG(p) UINT32_VAL(p,FSI_LEADSIG)
+# define FSI_GETSTRUCTSIG(p) UINT32_VAL(p,FSI_STRUCTSIG)
+# define FSI_GETFREECOUNT(p) UINT32_VAL(p,FSI_FREECOUNT)
+# define FSI_GETNXTFREE(p) UINT32_VAL(p,FSI_NXTFREE)
+# define FSI_GETTRAILSIG(p) UINT32_VAL(p,FSI_TRAILSIG)
+
+# define DIR_GETCRTIME(p) UINT16_VAL(p,DIR_CRTIME)
+# define DIR_GETCRDATE(p) UINT16_VAL(p,DIR_CRDATE)
+# define DIR_GETLASTACCDATE(p) UINT16_VAL(p,DIR_LASTACCDATE)
+# define DIR_GETFSTCLUSTHI(p) UINT16_VAL(p,DIR_FSTCLUSTHI)
+# define DIR_GETWRTTIME(p) UINT16_VAL(p,DIR_WRTTIME)
+# define DIR_GETWRTDATE(p) UINT16_VAL(p,DIR_WRTDATE)
+# define DIR_GETFSTCLUSTLO(p) UINT16_VAL(p,DIR_FSTCLUSTLO)
+# define DIR_GETFILESIZE(p) UINT32_VAL(p,DIR_FILESIZE)
+
+# ifdef CONFIG_FAT_LFN
+# define LDIR_GETWCHAR1(p) UINT16_VAL(p,LDIR_WCHAR1_5)
+# define LDIR_GETWCHAR2(p) UINT16_VAL(p,LDIR_WCHAR1_5+2)
+# define LDIR_GETWCHAR3(p) UINT16_VAL(p,LDIR_WCHAR1_5+4)
+# define LDIR_GETWCHAR4(p) UINT16_VAL(p,LDIR_WCHAR1_5+6)
+# define LDIR_GETWCHAR5(p) UINT16_VAL(p,LDIR_WCHAR1_5+8)
+# define LDIR_GETWCHAR6(p) UINT16_VAL(p,LDIR_WCHAR6_11)
+# define LDIR_GETWCHAR7(p) UINT16_VAL(p,LDIR_WCHAR6_11+2)
+# define LDIR_GETWCHAR8(p) UINT16_VAL(p,LDIR_WCHAR6_11+4)
+# define LDIR_GETWCHAR9(p) UINT16_VAL(p,LDIR_WCHAR6_11+6)
+# define LDIR_GETWCHAR10(p) UINT16_VAL(p,LDIR_WCHAR6_11+8)
+# define LDIR_GETWCHAR11(p) UINT16_VAL(p,LDIR_WCHAR6_11+10)
+# define LDIR_GETWCHAR12(p) UINT16_VAL(p,LDIR_WCHAR12_13)
+# define LDIR_GETWCHAR13(p) UINT16_VAL(p,LDIR_WCHAR12_13+2)
+# endif
+
+# define FSI_GETLEADSIG(p) UINT32_VAL(p,FSI_LEADSIG)
+# define FSI_GETSTRUCTSIG(p) UINT32_VAL(p,FSI_STRUCTSIG)
+# define FSI_GETFREECOUNT(p) UINT32_VAL(p,FSI_FREECOUNT)
+# define FSI_GETNXTFREE(p) UINT32_VAL(p,FSI_NXTFREE)
+# define FSI_GETTRAILSIG(p) UINT32_VAL(p,FSI_TRAILSIG)
+
+# define FAT_GETFAT16(p,i) UINT16_VAL(p,i)
+# define FAT_GETFAT32(p,i) UINT32_VAL(p,i)
+
+# define MBR_PUTRESVDSECCOUNT(p,v) UINT16_PUT(p,BS_RESVDSECCOUNT,v)
+# define MBR_PUTFATSZ16(p,v) UINT16_PUT(p,BS_FATSZ16,v)
+# define MBR_PUTSECPERTRK(p,v) UINT16_PUT(p,BS_SECPERTRK,v)
+# define MBR_PUTNUMHEADS(p,v) UINT16_PUT(p,BS_NUMHEADS,v)
+# define MBR_PUTHIDSEC(p,v) UINT32_PUT(p,BS_HIDSEC,v)
+# define MBR_PUTTOTSEC32(p,v) UINT32_PUT(p,BS_TOTSEC32,v)
+# define MBR_PUTFATSZ32(p,v) UINT32_PUT(p,BS32_FATSZ32,v)
+# define MBR_PUTEXTFLAGS(p,v) UINT16_PUT(p,BS32_EXTFLAGS,v)
+# define MBR_PUTFSVER(p,v) UINT16_PUT(p,BS32_FSVER,v)
+# define MBR_PUTROOTCLUS(p,v) UINT32_PUT(p,BS32_ROOTCLUS,v)
+# define MBR_PUTFSINFO(p,v) UINT16_PUT(p,BS32_FSINFO,v)
+# define MBR_PUTBKBOOTSEC(p,v) UINT16_PUT(p,BS32_BKBOOTSEC,v)
+# define MBR_PUTSIGNATURE(p,v) UINT16_PUT(p,BS_SIGNATURE,v)
+
+# define FSI_PUTLEADSIG(p,v) UINT32_PUT(p,FSI_LEADSIG,v)
+# define FSI_PUTSTRUCTSIG(p,v) UINT32_PUT(p,FSI_STRUCTSIG,v)
+# define FSI_PUTFREECOUNT(p,v) UINT32_PUT(p,FSI_FREECOUNT,v)
+# define FSI_PUTNXTFREE(p,v) UINT32_PUT(p,FSI_NXTFREE,v)
+# define FSI_PUTTRAILSIG(p,v) UINT32_PUT(p,FSI_TRAILSIG,v)
+
+# define DIR_PUTCRTIME(p,v) UINT16_PUT(p,DIR_CRTIME,v)
+# define DIR_PUTCRDATE(p,v) UINT16_PUT(p,DIR_CRDATE,v)
+# define DIR_PUTLASTACCDATE(p,v) UINT16_PUT(p,DIR_LASTACCDATE,v)
+# define DIR_PUTFSTCLUSTHI(p,v) UINT16_PUT(p,DIR_FSTCLUSTHI,v)
+# define DIR_PUTWRTTIME(p,v) UINT16_PUT(p,DIR_WRTTIME,v)
+# define DIR_PUTWRTDATE(p,v) UINT16_PUT(p,DIR_WRTDATE,v)
+# define DIR_PUTFSTCLUSTLO(p,v) UINT16_PUT(p,DIR_FSTCLUSTLO,v)
+# define DIR_PUTFILESIZE(p,v) UINT32_PUT(p,DIR_FILESIZE,v)
+
+# ifdef CONFIG_FAT_LFN
+# define LDIR_PUTWCHAR1(p,v) UINT16_PUT(p,LDIR_WCHAR1_5,v)
+# define LDIR_PUTWCHAR2(p,v) UINT16_PUT(p,LDIR_WCHAR1_5+2,v)
+# define LDIR_PUTWCHAR3(p,v) UINT16_PUT(p,LDIR_WCHAR1_5+4,v)
+# define LDIR_PUTWCHAR4(p,v) UINT16_PUT(p,LDIR_WCHAR1_5+6,v)
+# define LDIR_PUTWCHAR5(p,v) UINT16_PUT(p,LDIR_WCHAR1_5+8,v)
+# define LDIR_PUTWCHAR6(p,v) UINT16_PUT(p,LDIR_WCHAR6_11,v)
+# define LDIR_PUTWCHAR7(p,v) UINT16_PUT(p,LDIR_WCHAR6_11+2,v)
+# define LDIR_PUTWCHAR8(p,v) UINT16_PUT(p,LDIR_WCHAR6_11+4,v)
+# define LDIR_PUTWCHAR9(p,v) UINT16_PUT(p,LDIR_WCHAR6_11+6,v)
+# define LDIR_PUTWCHAR10(p,v) UINT16_PUT(p,LDIR_WCHAR6_11+8,v)
+# define LDIR_PUTWCHAR11(p,v) UINT16_PUT(p,LDIR_WCHAR6_11+10,v)
+# define LDIR_PUTWCHAR12(p,v) UINT16_PUT(p,LDIR_WCHAR12_13,v)
+# define LDIR_PUTWCHAR13(p,v) UINT16_PUT(p,LDIR_WCHAR12_13+2,v)
+# endif
+
+# define FSI_PUTLEADSIG(p,v) UINT32_PUT(p,FSI_LEADSIG,v)
+# define FSI_PUTSTRUCTSIG(p,v) UINT32_PUT(p,FSI_STRUCTSIG,v)
+# define FSI_PUTFREECOUNT(p,v) UINT32_PUT(p,FSI_FREECOUNT,v)
+# define FSI_PUTNXTFREE(p,v) UINT32_PUT(p,FSI_NXTFREE,v)
+# define FSI_PUTTRAILSIG(p,v) UINT32_PUT(p,FSI_TRAILSIG,v)
+
+# define FAT_PUTFAT16(p,i,v) UINT16_PUT(p,i,v)
+# define FAT_PUTFAT32(p,i,v) UINT32_PUT(p,i,v)
+
+#endif
+
+/****************************************************************************
+ * Name: fat_io_alloc and fat_io_free
+ *
+ * Description:
+ * The FAT file system allocates two I/O buffers for data transfer, each
+ * are the size of one device sector. One of the buffers is allocated
+ * once for each FAT volume that is mounted; the other buffers are
+ * allocated each time a FAT file is opened.
+ *
+ * Some hardware, however, may require special DMA-capable memory in
+ * order to perform the the transfers. If CONFIG_FAT_DMAMEMORY is defined
+ * then the architecture-specific hardware must provide the funtions
+ * fat_dma_alloc() and fat_dma_free() as prototyped below: fat_dmalloc()
+ * will allocate DMA-capable memory of the specified size; fat_dmafree()
+ * is the corresponding function that will be called to free the DMA-
+ * capable memory.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_DMAMEMORY
+# define fat_io_alloc(s) fat_dma_alloc(s)
+# define fat_io_free(m,s) fat_dma_free(m,s)
+#else
+# define fat_io_alloc(s) kmalloc(s)
+# define fat_io_free(m,s) kfree(m)
+#endif
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* This structure represents the overall mountpoint state. An instance of this
+ * structure is retained as inode private data on each mountpoint that is
+ * mounted with a fat32 filesystem.
+ */
+
+struct fat_file_s;
+struct fat_mountpt_s
+{
+ struct inode *fs_blkdriver; /* The block driver inode that hosts the FAT32 fs */
+ struct fat_file_s *fs_head; /* A list to all files opened on this mountpoint */
+
+ sem_t fs_sem; /* Used to assume thread-safe access */
+ off_t fs_hwsectorsize; /* HW: Sector size reported by block driver*/
+ off_t fs_hwnsectors; /* HW: The number of sectors reported by the hardware */
+ off_t fs_fatbase; /* Logical block of start of filesystem (past resd sectors) */
+ off_t fs_rootbase; /* MBR: Cluster no. of 1st cluster of root dir */
+ off_t fs_database; /* Logical block of start data sectors */
+ off_t fs_fsinfo; /* MBR: Sector number of FSINFO sector */
+ off_t fs_currentsector; /* The sector number buffered in fs_buffer */
+ uint32_t fs_nclusters; /* Maximum number of data clusters */
+ uint32_t fs_nfatsects; /* MBR: Count of sectors occupied by one fat */
+ uint32_t fs_fattotsec; /* MBR: Total count of sectors on the volume */
+ uint32_t fs_fsifreecount; /* FSI: Last free cluster count on volume */
+ uint32_t fs_fsinextfree; /* FSI: Cluster number of 1st free cluster */
+ uint16_t fs_fatresvdseccount; /* MBR: The total number of reserved sectors */
+ uint16_t fs_rootentcnt; /* MBR: Count of 32-bit root directory entries */
+ bool fs_mounted; /* true: The file system is ready */
+ bool fs_dirty; /* true: fs_buffer is dirty */
+ bool fs_fsidirty; /* true: FSINFO sector must be written to disk */
+ uint8_t fs_type; /* FSTYPE_FAT12, FSTYPE_FAT16, or FSTYPE_FAT32 */
+ uint8_t fs_fatnumfats; /* MBR: Number of FATs (probably 2) */
+ uint8_t fs_fatsecperclus; /* MBR: Sectors per allocation unit: 2**n, n=0..7 */
+ uint8_t *fs_buffer; /* This is an allocated buffer to hold one sector
+ * from the device */
+};
+
+/* This structure represents on open file under the mountpoint. An instance
+ * of this structure is retained as struct file specific information on each
+ * opened file.
+ */
+
+struct fat_file_s
+{
+ struct fat_file_s *ff_next; /* Retained in a singly linked list */
+ bool ff_open; /* true: The file is (still) open */
+ uint8_t ff_bflags; /* The file buffer flags */
+ uint8_t ff_oflags; /* Flags provided when file was opened */
+ uint8_t ff_sectorsincluster; /* Sectors remaining in cluster */
+ uint16_t ff_dirindex; /* Index into ff_dirsector to directory entry */
+ uint32_t ff_currentcluster; /* Current cluster being accessed */
+ off_t ff_dirsector; /* Sector containing the directory entry */
+ off_t ff_size; /* Size of the file in bytes */
+ off_t ff_startcluster; /* Start cluster of file on media */
+ off_t ff_currentsector; /* Current sector being operated on */
+ off_t ff_cachesector; /* Current sector in the file buffer */
+ uint8_t *ff_buffer; /* File buffer (for partial sector accesses) */
+};
+
+/* This structure holds the sequency of directory entries used by one
+ * file element (directory or file). For short file names, this is
+ * single diretory entry. But for long file names, the is a sequence
+ * of directory entries. Long directory name entries appear in reverse
+ * order: Last, next-to-last, ..., first. The "first" long file name
+ * directory is then following by the short directory name entry. The
+ * short file name entry contains the real meat of the file data.
+ *
+ * So it takes the sector number and entry offset of the last long
+ * file name entry and of the short file name entry to define the
+ * sequence. In the case of short file names, the sector number and
+ * offset will be the same.
+ */
+
+struct fat_dirseq_s
+{
+ /* Sector offsets */
+
+ uint16_t ds_offset; /* Sector offset to short file name entry */
+#ifdef CONFIG_FAT_LFN
+ uint16_t ds_lfnoffset; /* Sector offset to last long file name entry */
+#endif
+
+ /* Sector and cluster numbers */
+
+ off_t ds_sector; /* Sector of the short file name entry */
+#ifdef CONFIG_FAT_LFN
+ off_t ds_cluster; /* Cluster containing the short file name entry */
+ off_t ds_lfnsector; /* Sector of the last long name entry */
+ off_t ds_lfncluster; /* Cluster containing the long file name entry */
+ off_t ds_startsector; /* Starting sector of the directory */
+#endif
+};
+
+/* This structure is used internally for describing directory entries */
+
+struct fat_dirinfo_s
+{
+ /* The file/directory name */
+
+#ifdef CONFIG_FAT_LFN
+ uint8_t fd_lfname[LDIR_MAXFNAME+1]; /* Long filename with terminator */
+#endif
+ uint8_t fd_name[DIR_MAXFNAME]; /* Short 8.3 alias filename (no terminator) */
+
+ /* NT flags are not used */
+
+#ifdef CONFIG_FAT_LCNAMES
+ uint8_t fd_ntflags; /* NTRes lower case flags */
+#endif
+
+ /* TRUE if this is the root directory */
+
+ bool fd_root;
+
+ /* The following provides the sequence of directory entries used by the
+ * file or directory.
+ */
+
+ struct fat_dirseq_s fd_seq; /* Directory sequence */
+
+ /* This is part of the opendir, readdir, ... logic */
+
+ struct fs_fatdir_s dir; /* Used with opendir, readdir, etc. */
+};
+
+/* Generic helper macros ****************************************************/
+
+#ifndef MIN
+# define MIN(a,b) (a < b ? a : b)
+#endif
+
+#ifndef MAX
+# define MAX(a,b) (a > b ? a : b)
+#endif
+
+/****************************************************************************
+ * Global Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/* Utitilies to handle unaligned or byte swapped accesses */
+
+EXTERN uint16_t fat_getuint16(uint8_t *ptr);
+EXTERN uint32_t fat_getuint32(uint8_t *ptr);
+EXTERN void fat_putuint16(uint8_t *ptr, uint16_t value16);
+EXTERN void fat_putuint32(uint8_t *ptr, uint32_t value32);
+
+/* Manage the per-mount semaphore that protects access to shared resources */
+
+EXTERN void fat_semtake(struct fat_mountpt_s *fs);
+EXTERN void fat_semgive(struct fat_mountpt_s *fs);
+
+/* Get the current time for FAT creation and write times */
+
+EXTERN uint32_t fat_systime2fattime(void);
+EXTERN time_t fat_fattime2systime(uint16_t fattime, uint16_t fatdate);
+
+/* Handle hardware interactions for mounting */
+
+EXTERN int fat_mount(struct fat_mountpt_s *fs, bool writeable);
+EXTERN int fat_checkmount(struct fat_mountpt_s *fs);
+
+/* low-level hardware access */
+
+EXTERN int fat_hwread(struct fat_mountpt_s *fs, uint8_t *buffer,
+ off_t sector, unsigned int nsectors);
+EXTERN int fat_hwwrite(struct fat_mountpt_s *fs, uint8_t *buffer,
+ off_t sector, unsigned int nsectors);
+
+/* Cluster / cluster chain access helpers */
+
+EXTERN off_t fat_cluster2sector(struct fat_mountpt_s *fs, uint32_t cluster);
+EXTERN off_t fat_getcluster(struct fat_mountpt_s *fs, uint32_t clusterno);
+EXTERN int fat_putcluster(struct fat_mountpt_s *fs, uint32_t clusterno,
+ off_t startsector);
+EXTERN int fat_removechain(struct fat_mountpt_s *fs, uint32_t cluster);
+EXTERN int32_t fat_extendchain(struct fat_mountpt_s *fs, uint32_t cluster);
+
+#define fat_createchain(fs) fat_extendchain(fs, 0)
+
+/* Help for traversing directory trees and accessing directory entries */
+
+EXTERN int fat_nextdirentry(struct fat_mountpt_s *fs, struct fs_fatdir_s *dir);
+EXTERN int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo,
+ const char *path);
+EXTERN int fat_dirnamewrite(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo);
+EXTERN int fat_dirwrite(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo,
+ uint8_t attributes, uint32_t fattime);
+EXTERN int fat_allocatedirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo);
+EXTERN int fat_freedirentry(struct fat_mountpt_s *fs, struct fat_dirseq_s *seq);
+EXTERN int fat_dirname2path(struct fat_mountpt_s *fs, struct fs_dirent_s *dir);
+
+/* File creation and removal helpers */
+
+EXTERN int fat_dirtruncate(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo);
+EXTERN int fat_dircreate(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo);
+EXTERN int fat_remove(struct fat_mountpt_s *fs, const char *relpath, bool directory);
+
+/* Mountpoint and file buffer cache (for partial sector accesses) */
+
+EXTERN int fat_fscacheflush(struct fat_mountpt_s *fs);
+EXTERN int fat_fscacheread(struct fat_mountpt_s *fs, off_t sector);
+EXTERN int fat_ffcacheflush(struct fat_mountpt_s *fs, struct fat_file_s *ff);
+EXTERN int fat_ffcacheread(struct fat_mountpt_s *fs, struct fat_file_s *ff, off_t sector);
+EXTERN int fat_ffcacheinvalidate(struct fat_mountpt_s *fs, struct fat_file_s *ff);
+
+/* FSINFO sector support */
+
+EXTERN int fat_updatefsinfo(struct fat_mountpt_s *fs);
+EXTERN int fat_nfreeclusters(struct fat_mountpt_s *fs, off_t *pfreeclusters);
+EXTERN int fat_currentsector(struct fat_mountpt_s *fs, struct fat_file_s *ff, off_t position);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __FS_FAT_FS_FAT32_H */
diff --git a/nuttx/fs/fat/fs_fat32attrib.c b/nuttx/fs/fat/fs_fat32attrib.c
new file mode 100644
index 000000000..f58ed81bb
--- /dev/null
+++ b/nuttx/fs/fat/fs_fat32attrib.c
@@ -0,0 +1,189 @@
+/****************************************************************************
+ * fs/fat/fs_fat32attrib.c
+ *
+ * Copyright (C) 2007-2009, 2011 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 <stdint.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/fat.h>
+
+#include "fs_internal.h"
+#include "fs_fat32.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fat_attrib
+ ****************************************************************************/
+
+static int fat_attrib(const char *path, fat_attrib_t *retattrib,
+ fat_attrib_t setbits, fat_attrib_t clearbits)
+{
+ struct fat_mountpt_s *fs;
+ struct fat_dirinfo_s dirinfo;
+ FAR struct inode *inode;
+ const char *relpath = NULL;
+ uint8_t *direntry;
+ uint8_t oldattributes;
+ uint8_t newattributes;
+ int ret;
+
+ /* Get an inode for this file */
+
+ inode = inode_find(path, &relpath);
+ if (!inode)
+ {
+ /* There is no mountpoint that includes in this path */
+
+ ret = ENOENT;
+ goto errout;
+ }
+
+ /* Verify that the inode is a valid mountpoint. */
+
+ if (!INODE_IS_MOUNTPT(inode) || !inode->u.i_mops || !inode->i_private)
+ {
+ ret = ENXIO;
+ goto errout_with_inode;
+ }
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = inode->i_private;
+
+ /* Check if the mount is still healthy */
+
+ fat_semtake(fs);
+ ret = fat_checkmount(fs);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Find the file/directory entry for the oldrelpath */
+
+ ret = fat_finddirentry(fs, &dirinfo, relpath);
+ if (ret != OK)
+ {
+ /* Some error occurred -- probably -ENOENT */
+
+ goto errout_with_semaphore;
+ }
+
+ /* Make sure that we found some valid file or directory */
+
+ if (dirinfo.fd_root)
+ {
+ /* Ooops.. we found the root directory */
+
+ ret = EACCES;
+ goto errout_with_semaphore;
+ }
+
+ /* Get the current attributes */
+
+ direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset];
+ oldattributes = DIR_GETATTRIBUTES(direntry);
+ newattributes = oldattributes;
+
+ /* Set or clear any bits as requested */
+
+ newattributes &= ~(clearbits & (FATATTR_READONLY|FATATTR_HIDDEN|FATATTR_SYSTEM|FATATTR_ARCHIVE));
+ newattributes |= (setbits & (FATATTR_READONLY|FATATTR_HIDDEN|FATATTR_SYSTEM|FATATTR_ARCHIVE));
+
+ /* Did any thingchange? */
+
+ if (newattributes != oldattributes)
+ {
+ DIR_PUTATTRIBUTES(direntry, newattributes);
+ fs->fs_dirty = true;
+ ret = fat_updatefsinfo(fs);
+ if (ret != OK)
+ {
+ ret = -ret;
+ goto errout_with_semaphore;
+ }
+ }
+
+ /* Success */
+
+ if (retattrib)
+ {
+ *retattrib = newattributes;
+ }
+
+ fat_semgive(fs);
+ inode_release(inode);
+ return OK;
+
+errout_with_semaphore:
+ fat_semgive(fs);
+errout_with_inode:
+ inode_release(inode);
+errout:
+ *get_errno_ptr() = ret;
+ return ERROR;
+}
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fat_getattrib
+ ****************************************************************************/
+
+int fat_getattrib(const char *path, fat_attrib_t *attrib)
+{
+ return fat_attrib(path, attrib, 0, 0);
+}
+
+/****************************************************************************
+ * Name: fat_setattrib
+ ****************************************************************************/
+
+int fat_setattrib(const char *path, fat_attrib_t setbits, fat_attrib_t clearbits)
+{
+ return fat_attrib(path, NULL, setbits, clearbits);
+}
+
diff --git a/nuttx/fs/fat/fs_fat32dirent.c b/nuttx/fs/fat/fs_fat32dirent.c
new file mode 100644
index 000000000..18cf67847
--- /dev/null
+++ b/nuttx/fs/fat/fs_fat32dirent.c
@@ -0,0 +1,2947 @@
+/****************************************************************************
+ * fs/fat/fs_fat32dirent.c
+ *
+ * Copyright (C) 2011 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * NOTE: If CONFIG_FAT_LFN is defined, then there may be some legal, patent
+ * issues. The following was extracted from the entry "File Allocation Table
+ * from Wikipedia, the free encyclopedia:
+ *
+ * "On December 3, 2003 Microsoft announced it would be offering licenses
+ * for use of its FAT specification and 'associated intellectual property',
+ * at the cost of a US$0.25 royalty per unit sold, with a $250,000 maximum
+ * royalty per license agreement.
+ *
+ * o "U.S. Patent 5,745,902 (http://www.google.com/patents?vid=5745902) -
+ * Method and system for accessing a file using file names having
+ * different file name formats. ...
+ * o "U.S. Patent 5,579,517 (http://www.google.com/patents?vid=5579517) -
+ * Common name space for long and short filenames. ...
+ * o "U.S. Patent 5,758,352 (http://www.google.com/patents?vid=5758352) -
+ * Common name space for long and short filenames. ...
+ * o "U.S. Patent 6,286,013 (http://www.google.com/patents?vid=6286013) -
+ * Method and system for providing a common name space for long and
+ * short file names in an operating system. ...
+ *
+ * "Many technical commentators have concluded that these patents only cover
+ * FAT implementations that include support for long filenames, and that
+ * removable solid state media and consumer devices only using short names
+ * would be unaffected. ..."
+ *
+ * So you have been forewarned: Use the long filename at your own risk!
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/fat.h>
+
+#include "fs_internal.h"
+#include "fs_fat32.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+enum fat_case_e
+{
+ FATCASE_UNKNOWN = 0,
+ FATCASE_UPPER,
+ FATCASE_LOWER
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static uint8_t fat_lfnchecksum(const uint8_t *sfname);
+#endif
+static inline int fat_parsesfname(const char **path,
+ struct fat_dirinfo_s *dirinfo,
+ char *terminator);
+#ifdef CONFIG_FAT_LFN
+static inline int fat_parselfname(const char **path,
+ struct fat_dirinfo_s *dirinfo,
+ char *terminator);
+static inline int fat_createalias(struct fat_dirinfo_s *dirinfo);
+static inline int fat_findalias(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo);
+static inline int fat_uniquealias(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo);
+#endif
+static int fat_path2dirname(const char **path, struct fat_dirinfo_s *dirinfo,
+ char *terminator);
+static int fat_findsfnentry(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo);
+#ifdef CONFIG_FAT_LFN
+static bool fat_cmplfnchunk(uint8_t *chunk, const uint8_t *substr, int nchunk);
+static bool fat_cmplfname(const uint8_t *direntry, const uint8_t *substr);
+static inline int fat_findlfnentry(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo);
+
+#endif
+static inline int fat_allocatesfnentry(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo);
+#ifdef CONFIG_FAT_LFN
+static inline int fat_allocatelfnentry(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo);
+#endif
+static inline int fat_getsfname(uint8_t *direntry, char *buffer,
+ unsigned int buflen);
+#ifdef CONFIG_FAT_LFN
+static void fat_getlfnchunk(uint8_t *chunk, uint8_t *dest, int nchunk);
+static inline int fat_getlfname(struct fat_mountpt_s *fs, struct fs_dirent_s *dir);
+#endif
+static int fat_putsfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo);
+#ifdef CONFIG_FAT_LFN
+static void fat_initlfname(uint8_t *chunk, int nchunk);
+static void fat_putlfnchunk(uint8_t *chunk, const uint8_t *src, int nchunk);
+static int fat_putlfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo);
+#endif
+static int fat_putsfdirentry(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo,
+ uint8_t attributes, uint32_t fattime);
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fat_lfnchecksum
+ *
+ * Desciption: Caculate the checksum of .
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static uint8_t fat_lfnchecksum(const uint8_t *sfname)
+{
+ uint8_t sum = 0;
+ int i;
+
+ for (i = DIR_MAXFNAME; i; i--)
+ {
+ sum = ((sum & 1) << 7) + (sum >> 1) + *sfname++;
+ }
+
+ return sum;
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_parsesfname
+ *
+ * Desciption: Convert a user filename into a properly formatted FAT
+ * (short 8.3) filename as it would appear in a directory entry. Here are
+ * the rules for the 8+3 short file name in the directory:
+ *
+ * The first byte:
+ *
+ * 0xe5 = The directory is free
+ * 0x00 = This directory and all following directories are free
+ * 0x05 = Really 0xe5
+ * 0x20 = May NOT be ' '
+ *
+ * Other characters may be any characters except for the following:
+ *
+ * 0x00-0x1f = (except for 0x00 and 0x05 in the first byte)
+ * 0x22 = '"'
+ * 0x2a-0x2c = '*', '+', ','
+ * 0x2e-0x2f = '.', '/'
+ * 0x3a-0x3f = ':', ';', '<', '=', '>', '?'
+ * 0x5b-0x5d = '[', '\\', ;]'
+ * 0x7c = '|'
+ *
+ * '.' May only occur once within string and only within the first 9
+ * bytes. The '.' is not save in the directory, but is implicit in
+ * 8+3 format.
+ *
+ * Lower case characters are not allowed in directory names (without some
+ * poorly documented operations on the NTRes directory byte). Lower case
+ * codes may represent different characters in other character sets ("DOS
+ * code pages". The logic below does not, at present, support any other
+ * character sets.
+ *
+ * Returned value:
+ * OK - The path refers to a valid 8.3 FAT file name and has been properly
+ * converted and stored in dirinfo.
+ * <0 - Otherwise an negated error is returned meaning that the string is
+ * not a valid 8+3 because:
+ *
+ * 1. Contains characters not in the printable character set,
+ * 2. Contains forbidden characters or multiple '.' characters
+ * 3. File name or extension is too long.
+ *
+ * If CONFIG_FAT_LFN is defined and CONFIG_FAT_LCNAMES is NOT
+ * defined, then:
+ *
+ * 4a. File name or extension contains lower case characters.
+ *
+ * If CONFIG_FAT_LFN is defined and CONFIG_FAT_LCNAMES is defined,
+ * then:
+ *
+ * 4b. File name or extension is not all the same case.
+ *
+ ****************************************************************************/
+
+static inline int fat_parsesfname(const char **path,
+ struct fat_dirinfo_s *dirinfo,
+ char *terminator)
+{
+#ifdef CONFIG_FAT_LCNAMES
+ unsigned int ntlcenable = FATNTRES_LCNAME | FATNTRES_LCEXT;
+ unsigned int ntlcfound = 0;
+#ifdef CONFIG_FAT_LFN
+ enum fat_case_e namecase = FATCASE_UNKNOWN;
+ enum fat_case_e extcase = FATCASE_UNKNOWN;
+#endif
+#endif
+ const char *node = *path;
+ int endndx;
+ uint8_t ch;
+ int ndx = 0;
+
+ /* Initialized the name with all spaces */
+
+ memset(dirinfo->fd_name, ' ', DIR_MAXFNAME);
+
+ /* Loop until the name is successfully parsed or an error occurs */
+
+ endndx = 8;
+ for (;;)
+ {
+ /* Get the next byte from the path */
+
+ ch = *node++;
+
+ /* Check if this the last byte in this node of the name */
+
+ if ((ch == '\0' || ch == '/') && ndx != 0 )
+ {
+ /* Return the accumulated NT flags and the terminating character */
+
+#ifdef CONFIG_FAT_LCNAMES
+ dirinfo->fd_ntflags = ntlcfound & ntlcenable;
+#endif
+ *terminator = ch;
+ *path = node;
+ return OK;
+ }
+
+ /* Accept only the printable character set (excluding space). Note
+ * that the first byte of the name could be 0x05 meaning that is it
+ * 0xe5, but this is not a printable character in this character in
+ * either case.
+ */
+
+ else if (!isgraph(ch))
+ {
+ goto errout;
+ }
+
+ /* Check for transition from name to extension. Only one '.' is
+ * permitted and it must be within first 9 characters
+ */
+
+ else if (ch == '.' && endndx == 8)
+ {
+ /* Starting the extension */
+
+ ndx = 8;
+ endndx = 11;
+ continue;
+ }
+
+ /* Reject printable characters forbidden by FAT */
+
+ else if (ch == '"' || (ch >= '*' && ch <= ',') ||
+ ch == '.' || ch == '/' ||
+ (ch >= ':' && ch <= '?') ||
+ (ch >= '[' && ch <= ']') ||
+ (ch == '|'))
+ {
+ goto errout;
+ }
+
+ /* Check for upper case characters */
+
+#ifdef CONFIG_FAT_LCNAMES
+ else if (isupper(ch))
+ {
+ /* Some or all of the characters in the name or extension
+ * are upper case. Force all of the characters to be interpreted
+ * as upper case.
+ */
+
+ if (endndx == 8)
+ {
+ /* Is there mixed case in the name? */
+
+#ifdef CONFIG_FAT_LFN
+ if (namecase == FATCASE_LOWER)
+ {
+ /* Mixed case in the name -- use the long file name */
+
+ goto errout;
+ }
+
+ /* So far, only upper case in the name*/
+
+ namecase = FATCASE_UPPER;
+#endif
+
+ /* Clear lower case name bit in mask*/
+
+ ntlcenable &= ~FATNTRES_LCNAME;
+ }
+ else
+ {
+ /* Is there mixed case in the extension? */
+
+#ifdef CONFIG_FAT_LFN
+ if (extcase == FATCASE_LOWER)
+ {
+ /* Mixed case in the extension -- use the long file name */
+
+ goto errout;
+ }
+
+ /* So far, only upper case in the extension*/
+
+ extcase = FATCASE_UPPER;
+#endif
+
+ /* Clear lower case extension in mask */
+
+ ntlcenable &= ~FATNTRES_LCEXT;
+ }
+ }
+#endif
+
+ /* Check for lower case characters */
+
+ else if (islower(ch))
+ {
+#if defined(CONFIG_FAT_LFN) && !defined(CONFIG_FAT_LCNAMES)
+ /* If lower case characters are present, then a long file
+ * name will be constructed.
+ */
+
+ goto errout;
+#else
+ /* Convert the character to upper case */
+
+ ch = toupper(ch);
+
+ /* Some or all of the characters in the name or extension
+ * are lower case. They can be interpreted as lower case if
+ * only if all of the characters in the name or extension are
+ * lower case.
+ */
+
+#ifdef CONFIG_FAT_LCNAMES
+ if (endndx == 8)
+ {
+ /* Is there mixed case in the name? */
+
+#ifdef CONFIG_FAT_LFN
+ if (namecase == FATCASE_UPPER)
+ {
+ /* Mixed case in the name -- use the long file name */
+
+ goto errout;
+ }
+
+ /* So far, only lower case in the name*/
+
+ namecase = FATCASE_LOWER;
+#endif
+
+ /* Set lower case name bit */
+
+ ntlcfound |= FATNTRES_LCNAME;
+ }
+ else
+ {
+ /* Is there mixed case in the extension? */
+
+#ifdef CONFIG_FAT_LFN
+ if (extcase == FATCASE_UPPER)
+ {
+ /* Mixed case in the extension -- use the long file name */
+
+ goto errout;
+ }
+
+ /* So far, only lower case in the extension*/
+
+ extcase = FATCASE_LOWER;
+#endif
+
+ /* Set lower case extension bit */
+
+ ntlcfound |= FATNTRES_LCEXT;
+ }
+#endif
+#endif /* CONFIG_FAT_LFN && !CONFIG_FAT_LCNAMES */
+ }
+
+ /* Check if the file name exceeds the size permitted (without
+ * long file name support).
+ */
+
+ if (ndx >= endndx)
+ {
+ goto errout;
+ }
+
+ /* Save next character in the accumulated name */
+
+ dirinfo->fd_name[ndx++] = ch;
+ }
+
+ errout:
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Name: fat_parselfname
+ *
+ * Desciption: Convert a user filename into a properly formatted FAT
+ * long filename as it would appear in a directory entry. Here are
+ * the rules for the long file name in the directory:
+ *
+ * Valid characters are the same as for short file names EXCEPT:
+ *
+ * 1. '+', ',', ';', '=', '[', and ']' are accepted in the file name
+ * 2. '.' (dot) can occur more than once in a filename. Extension is
+ * the substring after the last dot.
+ *
+ * Returned value:
+ * OK - The path refers to a valid long file name and has been properly
+ * stored in dirinfo.
+ * <0 - Otherwise an negated error is returned meaning that the string is
+ * not a valid long file name:
+ *
+ * 1. Contains characters not in the printable character set,
+ * 2. Contains forbidden characters
+ * 3. File name is too long.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static inline int fat_parselfname(const char **path,
+ struct fat_dirinfo_s *dirinfo,
+ char *terminator)
+{
+ const char *node = *path;
+ uint8_t ch;
+ int ndx = 0;
+
+ /* Loop until the name is successfully parsed or an error occurs */
+
+ for (;;)
+ {
+ /* Get the next byte from the path */
+
+ ch = *node++;
+
+ /* Check if this the last byte in this node of the name */
+
+ if ((ch == '\0' || ch == '/') && ndx != 0 )
+ {
+ /* Null terminate the string */
+
+ dirinfo->fd_lfname[ndx] = '\0';
+
+ /* Return the remaining sub-string and the terminating character. */
+
+ *terminator = ch;
+ *path = node;
+ return OK;
+ }
+
+ /* Accept only the printable character set (including space) */
+
+ else if (!isprint(ch))
+ {
+ goto errout;
+ }
+
+ /* Reject printable characters forbidden by FAT */
+
+ else if (ch == '"' || ch == '*' || ch == '/' || ch == ':' ||
+ ch == '<' || ch == '>' || ch == '?' || ch == '\\' ||
+ ch == '|')
+ {
+ goto errout;
+ }
+
+ /* Check if the file name exceeds the size permitted. */
+
+ if (ndx >= LDIR_MAXFNAME)
+ {
+ goto errout;
+ }
+
+ /* Save next character in the accumulated name */
+
+ dirinfo->fd_lfname[ndx++] = ch;
+ }
+
+ errout:
+ dirinfo->fd_lfname[0] = '\0';
+ return -EINVAL;
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_createalias
+ *
+ * Desciption: Given a valid long file name, create a short filename alias.
+ * Here are the rules for creation of the alias:
+ *
+ * 1. All uppercase
+ * 2. All dots except the last deleted
+ * 3. First 6 (uppercase) characters used as a base
+ * 4. Then ~1. The number is increased if the file already exists in the
+ * directory. If the number exeeds >10, then character stripped off the
+ * base.
+ * 5. The extension is the first 3 uppercase chars of extension.
+ *
+ * This function is called only from fat_putlfname()
+ *
+ * Returned value:
+ * OK - The alias was created correctly.
+ * <0 - Otherwise an negated error is returned.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static inline int fat_createalias(struct fat_dirinfo_s *dirinfo)
+{
+ uint8_t ch; /* Current character being processed */
+ char *ext; /* Pointer to the extension substring */
+ char *src; /* Pointer to the long file name source */
+ int len; /* Total length of the long file name */
+ int namechars; /* Number of characters available in long name */
+ int extchars; /* Number of characters available in long name extension */
+ int endndx; /* Maximum index into the short name array */
+ int ndx; /* Index to store next character */
+
+ /* First, let's decide what is name and what is extension */
+
+ len = strlen((char*)dirinfo->fd_lfname);
+ ext = strrchr((char*)dirinfo->fd_lfname, '.');
+ if (ext)
+ {
+ ptrdiff_t tmp;
+
+ /* ext points to the final '.'. The difference in bytes from the
+ * beginning of the string is then the name length.
+ */
+
+ tmp = ext - (char*)dirinfo->fd_lfname;
+ namechars = tmp;
+
+ /* And the rest, exluding the '.' is the extension. */
+
+ extchars = len - namechars - 1;
+ ext++;
+ }
+ else
+ {
+ /* No '.' found. It is all name and no extension. */
+
+ namechars = len;
+ extchars = 0;
+ }
+
+ /* Alias are always all upper case */
+
+#ifdef CONFIG_FAT_LCNAMES
+ dirinfo->fd_ntflags = 0;
+#endif
+
+ /* Initialized the short name with all spaces */
+
+ memset(dirinfo->fd_name, ' ', DIR_MAXFNAME);
+
+ /* Handle a special case where there is no name. Windows seems to use
+ * the extension plus random stuff then ~1 to pat to 8 bytes. Some
+ * examples:
+ *
+ * a.b -> a.b No long name
+ * a., -> A26BE~1._ Padded name to make unique, _ replaces ,
+ * .b -> B1DD2~1 Extension used as name
+ * .bbbbbbb -> BBBBBB~1 Extension used as name
+ * a.bbbbbbb -> AAD39~1.BBB Padded name to make unique.
+ * aaa.bbbbbbb -> AAA~1.BBBB Not padded, already unique?
+ * ,.bbbbbbb -> _82AF~1.BBB _ replaces ,
+ * +[],.bbbbbbb -> ____~1.BBB _ replaces +[],
+ */
+
+ if (namechars < 1)
+ {
+ /* Use the extension as the name */
+
+ DEBUGASSERT(ext && extchars > 0);
+ src = ext;
+ ext = NULL;
+ namechars = extchars;
+ extchars = 0;
+ }
+ else
+ {
+ src = (char*)dirinfo->fd_lfname;
+ }
+
+ /* Then copy the name and extension, handling upper case conversions and
+ * excluding forbidden characters.
+ */
+
+ ndx = 0; /* Position to write the next name character */
+ endndx = 6; /* Maximum index before we write ~! and switch to the extension */
+
+ for (;;)
+ {
+ /* Get the next byte from the path. Break out of the loop if we
+ * encounter the end of null-terminated the long file name string.
+ */
+
+ ch = *src++;
+ if (ch == '\0')
+ {
+ /* This is the end of the source string. Do we need to add ~1. We
+ * will do that if we were parsing the name part when the endo of
+ * string was encountered.
+ */
+
+ if (endndx == 6)
+ {
+ /* Write the ~1 at the end of the name */
+
+ dirinfo->fd_name[ndx++] = '~';
+ dirinfo->fd_name[ndx] = '1';
+ }
+
+ /* In any event, we are done */
+
+ return OK;
+ }
+
+ /* Exclude those few characters included in long file names, but
+ * excluded in short file name: '+', ',', ';', '=', '[', ']', and '.'
+ */
+
+ if (ch == '+' || ch == ',' || ch == '.' || ch == ';' ||
+ ch == '=' || ch == '[' || ch == ']' || ch == '|')
+ {
+ /* Use the underbar character instead */
+
+ ch = '_';
+ }
+
+ /* Handle lower case characters */
+
+ ch = toupper(ch);
+
+ /* We now have a valid character to add to the name or extension. */
+
+ dirinfo->fd_name[ndx++] = ch;
+
+ /* Did we just add a character to the name? */
+
+ if (endndx == 6)
+ {
+ /* Decrement the number of characters available in the name
+ * portion of the long name.
+ */
+
+ namechars--;
+
+ /* Is it time to add ~1 to the string? We will do that if
+ * either (1) we have already added the maximum number of
+ * characters to the short name, or (2) if there are no further
+ * characters available in the name portion of the long name.
+ */
+
+ if (namechars < 1 || ndx == 6)
+ {
+ /* Write the ~1 at the end of the name */
+
+ dirinfo->fd_name[ndx++] = '~';
+ dirinfo->fd_name[ndx] = '1';
+
+ /* Then switch to the extension (if there is one) */
+
+ if (!ext || extchars < 1)
+ {
+ return OK;
+ }
+
+ ndx = 8;
+ endndx = 11;
+ src = ext;
+ }
+ }
+
+ /* No.. we just added a character to the extension */
+
+ else
+ {
+ /* Decrement the number of characters available in the name
+ * portion of the long name
+ */
+
+ extchars--;
+
+ /* Is the extension complete? */
+
+ if (extchars < 1 || ndx == 11)
+ {
+ return OK;
+ }
+ }
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_findalias
+ *
+ * Desciption: Make sure that the short alias for the long file name is
+ * unique, ie., that there is no other
+ *
+ * NOTE: This function does not restore the directory entry that was in the
+ * sector cache
+ *
+ * Returned value:
+ * OK - The alias is unique.
+ * <0 - Otherwise an negated error is returned.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static inline int fat_findalias(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo)
+{
+ struct fat_dirinfo_s tmpinfo;
+
+ /* Save the current directory info. */
+
+ memcpy(&tmpinfo, dirinfo, sizeof(struct fat_dirinfo_s));
+
+ /* Then re-initialize to the beginning of the current directory, starting
+ * with the first entry.
+ */
+
+ tmpinfo.dir.fd_startcluster = tmpinfo.dir.fd_currcluster;
+ tmpinfo.dir.fd_currsector = tmpinfo.fd_seq.ds_startsector;
+ tmpinfo.dir.fd_index = 0;
+
+ /* Search for the single short file name directory entry in this directory */
+
+ return fat_findsfnentry(fs, &tmpinfo);
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_uniquealias
+ *
+ * Desciption: Make sure that the short alias for the long file name is
+ * unique, modifying the alias as necessary to assure uniqueness.
+ *
+ * NOTE: This function does not restore the directory entry that was in the
+ * sector cache
+ *
+ * information upon return.
+ * Returned value:
+ * OK - The alias is unique.
+ * <0 - Otherwise an negated error is returned.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static inline int fat_uniquealias(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo)
+{
+ int tilde;
+ int lsdigit;
+ int ret;
+ int i;
+
+ /* Find the position of the tilde character in the short name. The tilde
+ * can not occur in positions 0 or 7:
+ */
+
+ for (tilde = 1; tilde < 7 && dirinfo->fd_name[tilde] != '~'; tilde++);
+ if (tilde >= 7)
+ {
+ return -EINVAL;
+ }
+
+ /* The least significant number follows the digit (and must be '1') */
+
+ lsdigit = tilde + 1;
+ DEBUGASSERT(dirinfo->fd_name[lsdigit] == '1');
+
+ /* Search for the single short file name directory entry in this directory */
+
+ while ((ret = fat_findalias(fs, dirinfo)) == OK)
+ {
+ /* Adjust the numeric value after the '~' to make the file name unique */
+
+ for (i = lsdigit; i > 0; i--)
+ {
+ /* If we have backed up to the tilde position, then we have to move
+ * the tilde back one position.
+ */
+
+ if (i == tilde)
+ {
+ /* Is there space to back up the tilde? */
+
+ if (tilde <= 1)
+ {
+ /* No.. then we cannot add the name to the directory.
+ * What is the likelihood of that happening?
+ */
+
+ return -ENOSPC;
+ }
+
+ /* Back up the tilde and break out of the inner loop */
+
+ tilde--;
+ dirinfo->fd_name[tilde] = '~';
+ dirinfo->fd_name[tilde+1] = '1';
+ break;
+ }
+
+ /* We are not yet at the tilde,. Check if this digit has already
+ * reached its maximum value.
+ */
+
+ else if (dirinfo->fd_name[i] < '9')
+ {
+ /* No, it has not.. just increment the LS digit and break out of
+ * the inner loop.
+ */
+
+ dirinfo->fd_name[i]++;
+ break;
+ }
+
+ /* Yes.. Reset the digit to '0' and loop to adjust the digit before
+ * this one.
+ */
+
+ else
+ {
+ dirinfo->fd_name[i] = '0';
+ }
+ }
+ }
+
+ /* The while loop terminated because of an error; fat_findalias()
+ * returned something other than OK. The only acceptable error is
+ * -ENOENT, meaning that the short file name directory does not
+ * exist in this directory.
+ */
+
+ if (ret == -ENOENT)
+ {
+ ret = OK;
+ }
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_path2dirname
+ *
+ * Desciption: Convert a user filename into a properly formatted FAT
+ * (short 8.3) filename as it would appear in a directory entry.
+ *
+ ****************************************************************************/
+
+static int fat_path2dirname(const char **path, struct fat_dirinfo_s *dirinfo,
+ char *terminator)
+{
+#ifdef CONFIG_FAT_LFN
+ int ret;
+
+ /* Assume no long file name */
+
+ dirinfo->fd_lfname[0] = '\0';
+
+ /* Then parse the (assumed) 8+3 short file name */
+
+ ret = fat_parsesfname(path, dirinfo, terminator);
+ if (ret < 0)
+ {
+ /* No, the name is not a valid short 8+3 file name. Try parsing
+ * the long file name.
+ */
+
+ ret = fat_parselfname(path, dirinfo, terminator);
+ }
+
+ return ret;
+#else
+ /* Only short, 8+3 filenames supported */
+
+ return fat_parsesfname(path, dirinfo, terminator);
+#endif
+}
+
+/****************************************************************************
+ * Name: fat_findsfnentry
+ *
+ * Desciption: Find a short file name directory entry. Returns OK if the
+ * directory exists; -ENOENT if it does not.
+ *
+ ****************************************************************************/
+
+static int fat_findsfnentry(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo)
+{
+ uint16_t diroffset;
+ uint8_t *direntry;
+#ifdef CONFIG_FAT_LFN
+ off_t startsector;
+#endif
+ int ret;
+
+ /* Save the starting sector of the directory. This is not really needed
+ * for short name entries, but this keeps things consistent with long
+ * file name entries..
+ */
+
+#ifdef CONFIG_FAT_LFN
+ startsector = dirinfo->dir.fd_currsector;
+#endif
+
+ /* Search, beginning with the current sector, for a directory entry with
+ * the matching short name
+ */
+
+ for (;;)
+ {
+ /* Read the next sector into memory */
+
+ ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Get a pointer to the directory entry */
+
+ diroffset = DIRSEC_BYTENDX(fs, dirinfo->dir.fd_index);
+ direntry = &fs->fs_buffer[diroffset];
+
+ /* Check if we are at the end of the directory */
+
+ if (direntry[DIR_NAME] == DIR0_ALLEMPTY)
+ {
+ return -ENOENT;
+ }
+
+ /* Check if we have found the directory entry that we are looking for */
+
+ if (direntry[DIR_NAME] != DIR0_EMPTY &&
+ !(DIR_GETATTRIBUTES(direntry) & FATATTR_VOLUMEID) &&
+ !memcmp(&direntry[DIR_NAME], dirinfo->fd_name, DIR_MAXFNAME) )
+ {
+ /* Yes.. Return success */
+
+ dirinfo->fd_seq.ds_sector = fs->fs_currentsector;
+ dirinfo->fd_seq.ds_offset = diroffset;
+#ifdef CONFIG_FAT_LFN
+ dirinfo->fd_seq.ds_cluster = dirinfo->dir.fd_currcluster;
+ dirinfo->fd_seq.ds_startsector = startsector;
+
+ /* Position the last long file name directory entry at the same
+ * position.
+ */
+
+ dirinfo->fd_seq.ds_lfnsector = dirinfo->fd_seq.ds_sector;
+ dirinfo->fd_seq.ds_lfnoffset = dirinfo->fd_seq.ds_offset;
+ dirinfo->fd_seq.ds_lfncluster = dirinfo->fd_seq.ds_cluster;
+#endif
+ return OK;
+ }
+
+ /* No... get the next directory index and try again */
+
+ if (fat_nextdirentry(fs, &dirinfo->dir) != OK)
+ {
+ return -ENOENT;
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: fat_cmplfnchunk
+ *
+ * Desciption: There are 13 characters per LFN entry, broken up into three
+ * chunks for characts 1-5, 6-11, and 12-13. This function will perform
+ * the comparison of a single chunk.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static bool fat_cmplfnchunk(uint8_t *chunk, const uint8_t *substr, int nchunk)
+{
+ wchar_t wch;
+ uint8_t ch;
+ int i;
+
+ /* Check bytes 1-nchunk */
+
+ for (i = 0; i < nchunk; i++)
+ {
+ /* Get the next character from the name string (which might be the NUL
+ * terminating character).
+ */
+
+ if (*substr == '\0')
+ {
+ ch = '\0';
+ }
+ else
+ {
+ ch = *substr++;
+ }
+
+ /* Get the next unicode character from the chunk. We only handle
+ * ASCII. For ASCII, the upper byte should be zero and the lower
+ * should match the ASCII code.
+ */
+
+ wch = (wchar_t)fat_getuint16((uint8_t*)chunk);
+ if ((wch & 0xff) != (wchar_t)ch)
+ {
+ return false;
+ }
+
+ /* The characters match. If we just matched the NUL terminating
+ * character, then the strings match and we are finished.
+ */
+
+ if (ch == '\0')
+ {
+ return true;
+ }
+
+ /* Try the next character from the directory entry. */
+
+ chunk += sizeof(wchar_t);
+ }
+
+ /* All of the characters in the chunk match.. Return success */
+
+ return true;
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_cmplfname
+ *
+ * Desciption: Given an LFN directory entry, compare a substring of the name
+ * to a portion in the directory entry.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static bool fat_cmplfname(const uint8_t *direntry, const uint8_t *substr)
+{
+ uint8_t *chunk;
+ int len;
+ bool match;
+
+ /* How much of string do we have to compare? (including the NUL
+ * terminator).
+ */
+
+ len = strlen((char*)substr) + 1;
+
+ /* Check bytes 1-5 */
+
+ chunk = LDIR_PTRWCHAR1_5(direntry);
+ match = fat_cmplfnchunk(chunk, substr, 5);
+ if (match && len > 5)
+ {
+ /* Check bytes 6-11 */
+
+ chunk = LDIR_PTRWCHAR6_11(direntry);
+ match = fat_cmplfnchunk(chunk, &substr[5], 6);
+ if (match && len > 11)
+ {
+ /* Check bytes 12-13 */
+
+ chunk = LDIR_PTRWCHAR12_13(direntry);
+ match = fat_cmplfnchunk(chunk, &substr[11], 2);
+ }
+ }
+
+ return match;
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_findlfnentry
+ *
+ * Desciption: Find a sequence of long file name directory entries.
+ *
+ * NOTE: As a side effect, this function returns with the sector containing
+ * the short file name directory entry in the cache.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static inline int fat_findlfnentry(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo)
+{
+ uint16_t diroffset;
+ uint8_t *direntry;
+ uint8_t lastseq;
+ uint8_t seqno;
+ uint8_t nfullentries;
+ uint8_t nentries;
+ uint8_t remainder;
+ uint8_t checksum = 0;
+ off_t startsector;
+ int offset;
+ int namelen;
+ int ret;
+
+ /* Get the length of the long file name (size of the fd_lfname array is
+ * LDIR_MAXFNAME+1 we do not have to check the length of the string).
+ */
+
+ namelen = strlen((char*)dirinfo->fd_lfname);
+ DEBUGASSERT(namelen <= LDIR_MAXFNAME+1);
+
+ /* How many LFN directory entries are we expecting? */
+
+ nfullentries = namelen / LDIR_MAXLFNCHARS;
+ remainder = namelen - nfullentries * LDIR_MAXLFNCHARS;
+ nentries = nfullentries;
+ if (remainder > 0)
+ {
+ nentries++;
+ }
+ DEBUGASSERT(nentries > 0 && nentries <= LDIR_MAXLFNS);
+
+ /* This is the first sequency number we are looking for, the sequence
+ * number of the last LFN entry (remember that they appear in reverse
+ * order.. from last to first).
+ */
+
+ lastseq = LDIR0_LAST | nentries;
+ seqno = lastseq;
+
+ /* Save the starting sector of the directory. This is needed later to
+ * re-scan the directory, looking duplicate short alias names.
+ */
+
+ startsector = dirinfo->dir.fd_currsector;
+
+ /* Search, beginning with the current sector, for a directory entry this
+ * the match shore name
+ */
+
+ for (;;)
+ {
+ /* Read the next sector into memory */
+
+ ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Get a pointer to the directory entry */
+
+ diroffset = DIRSEC_BYTENDX(fs, dirinfo->dir.fd_index);
+ direntry = &fs->fs_buffer[diroffset];
+
+ /* Check if we are at the end of the directory */
+
+ if (direntry[DIR_NAME] == DIR0_ALLEMPTY)
+ {
+ return -ENOENT;
+ }
+
+ /* Is this an LFN entry? Does it have the sequence number we are
+ * looking for?
+ */
+
+ if (LDIR_GETATTRIBUTES(direntry) != LDDIR_LFNATTR ||
+ LDIR_GETSEQ(direntry) != seqno)
+ {
+ /* No, restart the search at the next entry */
+
+ seqno = lastseq;
+ goto next_entry;
+ }
+
+ /* Yes.. If this is not the "last" LFN entry, then the checksum must
+ * also be the same.
+ */
+
+ if (seqno == lastseq)
+ {
+ /* Just save the checksum for subsequent checks */
+
+ checksum = LDIR_GETCHECKSUM(direntry);
+ }
+
+ /* Not the first entry in the sequence. Does the checksum match the
+ * previous sequences?
+ */
+
+ else if (checksum != LDIR_GETCHECKSUM(direntry))
+ {
+ /* No, restart the search at the next entry */
+
+ seqno = lastseq;
+ goto next_entry;
+ }
+
+ /* Check if the name substring in this LFN matches the corresponding
+ * substring of the name we are looking for.
+ */
+
+ offset = ((seqno & LDIR0_SEQ_MASK) - 1) * LDIR_MAXLFNCHARS;
+ if (fat_cmplfname(direntry, &dirinfo->fd_lfname[offset]))
+ {
+ /* Yes.. it matches. Check the sequence number. Is this the
+ * "last" LFN entry (i.e., the one that appears first)?
+ */
+
+ if (seqno == lastseq)
+ {
+ /* Yes.. Save information about this LFN entry position */
+
+ dirinfo->fd_seq.ds_lfnsector = fs->fs_currentsector;
+ dirinfo->fd_seq.ds_lfnoffset = diroffset;
+ dirinfo->fd_seq.ds_lfncluster = dirinfo->dir.fd_currcluster;
+ dirinfo->fd_seq.ds_startsector = startsector;
+ seqno &= LDIR0_SEQ_MASK;
+ }
+
+ /* Is this the first sequence number (i.e., the LFN entry that
+ * will appear last)?
+ */
+
+ if (seqno == 1)
+ {
+ /* We have found all of the LFN entries. The next directory
+ * entry should be the one containing the short file name
+ * alias and all of the meat about the file or directory.
+ */
+
+ if (fat_nextdirentry(fs, &dirinfo->dir) != OK)
+ {
+ return -ENOENT;
+ }
+
+ /* Make sure that the directory entry is in the sector cache */
+
+ ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Get a pointer to the directory entry */
+
+ diroffset = DIRSEC_BYTENDX(fs, dirinfo->dir.fd_index);
+ direntry = &fs->fs_buffer[diroffset];
+
+ /* Verify the checksum */
+
+ if (fat_lfnchecksum(&direntry[DIR_NAME]) == checksum)
+ {
+ /* Success! Save the position of the directory entry and
+ * return success.
+ */
+
+ dirinfo->fd_seq.ds_sector = fs->fs_currentsector;
+ dirinfo->fd_seq.ds_offset = diroffset;
+ dirinfo->fd_seq.ds_cluster = dirinfo->dir.fd_currcluster;
+ return OK;
+ }
+
+ /* Bad news.. reset and continue with this entry (which is
+ * probably not an LFN entry unless the file systen is
+ * seriously corrupted.
+ */
+
+ seqno = lastseq;
+ continue;
+ }
+
+ /* No.. there are more LFN entries to go. Decrement the sequence
+ * number and check the next directory entry.
+ */
+
+ seqno--;
+ }
+ else
+ {
+ /* No.. the names do not match. Restart the search at the next
+ * entry.
+ */
+
+ seqno = lastseq;
+ }
+
+ /* Continue at the next directory entry */
+
+next_entry:
+ if (fat_nextdirentry(fs, &dirinfo->dir) != OK)
+ {
+ return -ENOENT;
+ }
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_allocatesfnentry
+ *
+ * Desciption: Find a free directory entry for a short file name entry.
+ *
+ ****************************************************************************/
+
+static inline int fat_allocatesfnentry(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo)
+{
+ uint16_t diroffset;
+ uint8_t *direntry;
+#ifdef CONFIG_FAT_LFN
+ off_t startsector;
+#endif
+ uint8_t ch;
+ int ret;
+
+ /* Save the sector number of the first sector of the directory. We don't
+ * really need this for short file name entries; this is just done for
+ * consistency with the long file name logic.
+ */
+
+#ifdef CONFIG_FAT_LFN
+ startsector = dirinfo->dir.fd_currsector;
+#endif
+
+ /* Then search for a free short file name directory entry */
+
+ for (;;)
+ {
+ /* Read the directory sector into fs_buffer */
+
+ ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector);
+ if (ret < 0)
+ {
+ /* Make sure that the return value is NOT -ENOSPC */
+
+ return -EIO;
+ }
+
+ /* Get a pointer to the entry at fd_index */
+
+ diroffset = (dirinfo->dir.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
+ direntry = &fs->fs_buffer[diroffset];
+
+ /* Check if this directory entry is empty */
+
+ ch = direntry[DIR_NAME];
+ if (ch == DIR0_ALLEMPTY || ch == DIR0_EMPTY)
+ {
+ /* It is empty -- we have found a directory entry */
+
+ dirinfo->fd_seq.ds_sector = fs->fs_currentsector;
+ dirinfo->fd_seq.ds_offset = diroffset;
+#ifdef CONFIG_FAT_LFN
+ dirinfo->fd_seq.ds_cluster = dirinfo->dir.fd_currcluster;
+ dirinfo->fd_seq.ds_startsector = startsector;
+
+ /* Set the "last" long file name offset to the same entry */
+
+ dirinfo->fd_seq.ds_lfnsector = dirinfo->fd_seq.ds_sector;
+ dirinfo->fd_seq.ds_lfnoffset = dirinfo->fd_seq.ds_offset;
+ dirinfo->fd_seq.ds_lfncluster = dirinfo->fd_seq.ds_cluster;
+#endif
+ return OK;
+ }
+
+ /* It is not empty try the next one */
+
+ ret = fat_nextdirentry(fs, &dirinfo->dir);
+ if (ret < 0)
+ {
+ /* This will return -ENOSPC if we have examined all of the
+ * directory entries without finding a free entry.
+ */
+
+ return ret;
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: fat_allocatelfnentry
+ *
+ * Desciption: Find a sequence of free directory entries for a several long
+ * and one short file name entry.
+ *
+ * On entry, dirinfo.dir refers to the first interesting entry the directory.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static inline int fat_allocatelfnentry(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo)
+{
+ uint16_t diroffset;
+ uint8_t *direntry;
+ off_t startsector;
+ uint8_t nentries;
+ uint8_t remainder;
+ uint8_t needed;
+ uint8_t ch;
+ int namelen;
+ int ret;
+
+ /* Get the length of the long file name (size of the fd_lfname array is
+ * LDIR_MAXFNAME+1 we do not have to check the length of the string).
+ */
+
+ namelen = strlen((char *)dirinfo->fd_lfname);
+ DEBUGASSERT(namelen <= LDIR_MAXFNAME+1);
+
+ /* How many LFN directory entries are we expecting? */
+
+ nentries = namelen / LDIR_MAXLFNCHARS;
+ remainder = namelen - nentries * LDIR_MAXLFNCHARS;
+ if (remainder > 0)
+ {
+ nentries++;
+ }
+ DEBUGASSERT(nentries > 0 && nentries <= LDIR_MAXLFNS);
+
+ /* Plus another for short file name entry that follows the sequence of LFN
+ * entries.
+ */
+
+ nentries++;
+
+ /* Save the sector number of the first sector of the directory. We will
+ * need this later for re-scanning the directory to verify that a FAT file
+ * name is unique.
+ */
+
+ startsector = dirinfo->dir.fd_currsector;
+
+ /* Now, search the directory looking for a sequence for free entries that
+ * long.
+ */
+
+ needed = nentries;
+ for (;;)
+ {
+ /* Read the directory sector into fs_buffer */
+
+ ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector);
+ if (ret < 0)
+ {
+ /* Make sure that the return value is NOT -ENOSPC */
+
+ return -EIO;
+ }
+
+ /* Get a pointer to the entry at fd_index */
+
+ diroffset = (dirinfo->dir.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
+ direntry = &fs->fs_buffer[diroffset];
+
+ /* Check if this directory entry is empty */
+
+ ch = LDIR_GETSEQ(direntry);
+ if (ch == DIR0_ALLEMPTY || ch == DIR0_EMPTY)
+ {
+ /* It is empty -- we have found a directory entry. Is this the
+ * "last" LFN entry (i.e., the one that occurs first)?
+ */
+
+ if (needed == nentries)
+ {
+ /* Yes.. remember the position of this entry */
+
+ dirinfo->fd_seq.ds_lfnsector = fs->fs_currentsector;
+ dirinfo->fd_seq.ds_lfnoffset = diroffset;
+ dirinfo->fd_seq.ds_lfncluster = dirinfo->dir.fd_currcluster;
+ dirinfo->fd_seq.ds_startsector = startsector;
+ }
+
+ /* Is this last entry we need (i.e., the entry for the short
+ * file name entry)?
+ */
+
+ if (needed <= 1)
+ {
+ /* Yes.. remember the position of this entry and return
+ * success.
+ */
+
+ dirinfo->fd_seq.ds_sector = fs->fs_currentsector;
+ dirinfo->fd_seq.ds_offset = diroffset;
+ dirinfo->fd_seq.ds_cluster = dirinfo->dir.fd_currcluster;
+ return OK;
+ }
+
+ /* Otherwise, just decrement the number of directory entries
+ * needed and continue looking.
+ */
+
+ needed--;
+ }
+
+ /* The directory entry is not available */
+
+ else
+ {
+ /* Reset the search and continue looking */
+
+ needed = nentries;
+ }
+
+ /* Try the next directory entry */
+
+ ret = fat_nextdirentry(fs, &dirinfo->dir);
+ if (ret < 0)
+ {
+ /* This will return -ENOSPC if we have examined all of the
+ * directory entries without finding a free entry.
+ */
+
+ return ret;
+ }
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_getsfname
+ *
+ * Desciption: Get the 8.3 filename from a directory entry. On entry, the
+ * short file name entry is already in the cache.
+ *
+ ****************************************************************************/
+
+static inline int fat_getsfname(uint8_t *direntry, char *buffer,
+ unsigned int buflen)
+{
+#ifdef CONFIG_FAT_LCNAMES
+ uint8_t ntflags;
+#endif
+ int ch;
+ int ndx;
+
+ /* Check if we will be doing upper to lower case conversions */
+
+#ifdef CONFIG_FAT_LCNAMES
+ ntflags = DIR_GETNTRES(direntry);
+#endif
+
+ /* Reserve a byte for the NUL terminator */
+
+ buflen--;
+
+ /* Get the 8-byte filename */
+
+ for (ndx = 0; ndx < 8 && buflen > 0; ndx++)
+ {
+ /* Get the next filename character from the directory entry */
+
+ ch = direntry[ndx];
+
+ /* Any space (or ndx==8) terminates the filename */
+
+ if (ch == ' ')
+ {
+ break;
+ }
+
+ /* In this version, we never write 0xe5 in the directory filenames
+ * (because we do not handle any character sets where 0xe5 is valid
+ * in a filaname), but we could encounted this in a filesystem
+ * written by some other system
+ */
+
+ if (ndx == 0 && ch == DIR0_E5)
+ {
+ ch = 0xe5;
+ }
+
+ /* Check if we should perform upper to lower case conversion
+ * of the (whole) filename.
+ */
+
+#ifdef CONFIG_FAT_LCNAMES
+ if (ntflags & FATNTRES_LCNAME && isupper(ch))
+ {
+ ch = tolower(ch);
+ }
+#endif
+ /* Copy the next character into the filename */
+
+ *buffer++ = ch;
+ buflen--;
+ }
+
+ /* Check if there is an extension */
+
+ if (direntry[8] != ' ' && buflen > 0)
+ {
+ /* Yes, output the dot before the extension */
+
+ *buffer++ = '.';
+ buflen--;
+
+ /* Then output the (up to) 3 character extension */
+
+ for (ndx = 8; ndx < 11 && buflen > 0; ndx++)
+ {
+ /* Get the next extensions character from the directory entry */
+
+ ch = direntry[DIR_NAME + ndx];
+
+ /* Any space (or ndx==11) terminates the extension */
+
+ if (ch == ' ')
+ {
+ break;
+ }
+
+ /* Check if we should perform upper to lower case conversion
+ * of the (whole) filename.
+ */
+
+#ifdef CONFIG_FAT_LCNAMES
+ if (ntflags & FATNTRES_LCEXT && isupper(ch))
+ {
+ ch = tolower(ch);
+ }
+#endif
+ /* Copy the next character into the filename */
+
+ *buffer++ = ch;
+ buflen--;
+ }
+ }
+
+ /* Put a null terminator at the end of the filename. We don't have to
+ * check if there is room because we reserved a byte for the NUL
+ * terminator at the beginning of this function.
+ */
+
+ *buffer = '\0';
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_getlfnchunk
+ *
+ * Desciption: There are 13 characters per LFN entry, broken up into three
+ * chunks for characts 1-5, 6-11, and 12-13. This function will get the
+ * file name characters from one chunk.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static void fat_getlfnchunk(uint8_t *chunk, uint8_t *dest, int nchunk)
+{
+ wchar_t wch;
+ int i;
+
+ /* Copy bytes 1-nchunk */
+
+ for (i = 0; i < nchunk; i++)
+ {
+ /* Get the next unicode character from the chunk. We only handle ASCII.
+ * For ASCII, the upper byte should be zero and the lower should match
+ * the ASCII code.
+ */
+
+ wch = (wchar_t)fat_getuint16(chunk);
+ *dest++ = (uint8_t)(wch & 0xff);
+ chunk += sizeof(wchar_t);
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_getlfname
+ *
+ * Desciption: Get the long filename from a sequence of directory entries.
+ * On entry, the "last" long file name entry is in the cache. Returns with
+ * the short file name entry in the cache.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static inline int fat_getlfname(struct fat_mountpt_s *fs, struct fs_dirent_s *dir)
+{
+ uint8_t lfname[LDIR_MAXLFNCHARS];
+ uint16_t diroffset;
+ uint8_t *direntry;
+ uint8_t seqno;
+ uint8_t rawseq;
+ uint8_t offset;
+ uint8_t checksum;
+ int nsrc;
+ int ret;
+ int i;
+
+ /* Get a reference to the current directory entry */
+
+ diroffset = (dir->u.fat.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
+ direntry = &fs->fs_buffer[diroffset];
+
+ /* Get the starting sequence number */
+
+ seqno = LDIR_GETSEQ(direntry);
+ DEBUGASSERT((seqno & LDIR0_LAST) != 0);
+
+ /* Sanity check */
+
+ rawseq = (seqno & LDIR0_SEQ_MASK);
+ if (rawseq < 1 || rawseq > LDIR_MAXLFNS)
+ {
+ return -EINVAL;
+ }
+
+ /* Save the checksum value */
+
+ checksum = LDIR_GETCHECKSUM(direntry);
+
+ /* Loop until the whole file name has been transferred */
+
+ for (;;)
+ {
+ /* Get the string offset associated with the "last" entry. */
+
+ offset = (rawseq - 1) * LDIR_MAXLFNCHARS;
+
+ /* Will any of this file name fit into the destination buffer? */
+
+ if (offset < NAME_MAX)
+ {
+ /* Yes.. extract and convert the unicode name */
+
+ fat_getlfnchunk(LDIR_PTRWCHAR1_5(direntry), lfname, 5);
+ fat_getlfnchunk(LDIR_PTRWCHAR6_11(direntry), &lfname[5], 6);
+ fat_getlfnchunk(LDIR_PTRWCHAR12_13(direntry), &lfname[11], 2);
+
+ /* Ignore trailing spaces on the "last" directory entry. The
+ * number of characters avaiable is LDIR_MAXLFNCHARS or that
+ * minus the number of trailing spaces on the "last" directory
+ * entry.
+ */
+
+ nsrc = LDIR_MAXLFNCHARS;
+ if ((seqno & LDIR0_LAST) != 0)
+ {
+ /* Reduce the number of characters by the number of trailing
+ * spaces.
+ */
+
+ for (; nsrc > 0 && lfname[nsrc-1] == ' '; nsrc--);
+
+ /* Further reduce the length so that it fits in the destination
+ * buffer.
+ */
+
+ if (offset + nsrc > NAME_MAX)
+ {
+ nsrc = NAME_MAX - offset;
+ }
+
+ /* Add a null terminator to the destination string (the actual
+ * length of the destination buffer is NAME_MAX+1, so the NUL
+ * terminator will fit).
+ */
+
+ dir->fd_dir.d_name[offset+nsrc] = '\0';
+ }
+
+ /* Then transfer the characters */
+
+ for (i = 0; i < nsrc && offset+i < NAME_MAX; i++)
+ {
+ dir->fd_dir.d_name[offset+i] = lfname[i];
+ }
+ }
+
+ /* Read next directory entry */
+
+ if (fat_nextdirentry(fs, &dir->u.fat) != OK)
+ {
+ return -ENOENT;
+ }
+
+ /* Make sure that the directory sector into the sector cache */
+
+ ret = fat_fscacheread(fs, dir->u.fat.fd_currsector);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Get a reference to the current directory entry */
+
+ diroffset = (dir->u.fat.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
+ direntry = &fs->fs_buffer[diroffset];
+
+ /* Get the next expected sequence number. */
+
+ seqno = --rawseq;
+ if (seqno < 1)
+ {
+ /* We just completed processing the "first" long file name entry
+ * and we just read the short file name entry. Verify that the
+ * checksum of the short file name matches the checksum that we
+ * found in the long file name entries.
+ */
+
+ if (fat_lfnchecksum(direntry) == checksum)
+ {
+ /* Yes.. return success! */
+
+ return OK;
+ }
+
+ /* No, the checksum is bad. */
+
+ return -EINVAL;
+ }
+
+ /* Verify the next long file name entry. Is this an LFN entry? Does it
+ * have the sequence number we are looking for? Does the checksum
+ * match the previous entries?
+ */
+
+ if (LDIR_GETATTRIBUTES(direntry) != LDDIR_LFNATTR ||
+ LDIR_GETSEQ(direntry) != seqno ||
+ LDIR_GETCHECKSUM(direntry) != checksum)
+ {
+ return -EINVAL;
+ }
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_putsfname
+ *
+ * Desciption: Write the short directory entry name.
+ *
+ * Assumption: The directory sector is in the cache.
+ *
+ ****************************************************************************/
+
+static int fat_putsfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
+{
+ uint8_t *direntry = &fs->fs_buffer[dirinfo->fd_seq.ds_offset];
+
+ /* Write the short directory entry */
+
+ memcpy(&direntry[DIR_NAME], dirinfo->fd_name, DIR_MAXFNAME);
+#ifdef CONFIG_FAT_LCNAMES
+ DIR_PUTNTRES(direntry, dirinfo->fd_ntflags);
+#else
+ DIR_PUTNTRES(direntry, 0);
+#endif
+ fs->fs_dirty = true;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_initlfname
+ *
+ * Desciption: There are 13 characters per LFN entry, broken up into three
+ * chunks for characts 1-5, 6-11, and 12-13. This function will put the
+ * 0xffff characters into one chunk.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static void fat_initlfname(uint8_t *chunk, int nchunk)
+{
+ int i;
+
+ /* Initialize unicode characters 1-nchunk */
+
+ for (i = 0; i < nchunk; i++)
+ {
+ /* The write the 16-bit 0xffff character into the directory entry. */
+
+ fat_putuint16((uint8_t *)chunk, (uint16_t)0xffff);
+ chunk += sizeof(wchar_t);
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_putlfnchunk
+ *
+ * Desciption: There are 13 characters per LFN entry, broken up into three
+ * chunks for characts 1-5, 6-11, and 12-13. This function will put the
+ * file name characters into one chunk.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static void fat_putlfnchunk(uint8_t *chunk, const uint8_t *src, int nchunk)
+{
+ uint16_t wch;
+ int i;
+
+ /* Write bytes 1-nchunk */
+
+ for (i = 0; i < nchunk; i++)
+ {
+ /* Get the next ascii character from the name substring and convert it
+ * to unicode. The upper byte should be zero and the lower should be
+ * the ASCII code. The write the unicode character into the directory
+ * entry.
+ */
+
+ wch = (uint16_t)*src++;
+ fat_putuint16(chunk, wch);
+ chunk += sizeof(wchar_t);
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_putlfname
+ *
+ * Desciption: Write the long filename into a sequence of directory entries.
+ * On entry, the "last" long file name entry is in the cache. Returns with
+ * the short file name entry in the cache.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_FAT_LFN
+static int fat_putlfname(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
+{
+ uint16_t diroffset;
+ uint8_t *direntry;
+ uint8_t nfullentries;
+ uint8_t nentries;
+ uint8_t remainder;
+ uint8_t offset;
+ uint8_t seqno;
+ uint8_t checksum;
+ int namelen;
+ int ret;
+
+ /* Get the length of the long file name (size of the fd_lfname array is
+ * LDIR_MAXFNAME+1 we do not have to check the length of the string).
+ * NOTE that remainder is conditionally incremented to include the NUL
+ * terminating character that may also need be written to the directory
+ * entry. NUL terminating is not required if length is multiple of
+ * LDIR_MAXLFNCHARS (13).
+ */
+
+ namelen = strlen((char*)dirinfo->fd_lfname);
+ DEBUGASSERT(namelen <= LDIR_MAXFNAME+1);
+
+ /* How many LFN directory entries do we need to write? */
+
+ nfullentries = namelen / LDIR_MAXLFNCHARS;
+ remainder = namelen - nfullentries * LDIR_MAXLFNCHARS;
+ nentries = nfullentries;
+ if (remainder > 0)
+ {
+ nentries++;
+ remainder++;
+ }
+ DEBUGASSERT(nentries > 0 && nentries <= LDIR_MAXLFNS);
+
+ /* Create the short file name alias */
+
+ ret = fat_createalias(dirinfo);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Set up the initial positional data */
+
+ dirinfo->dir.fd_currcluster = dirinfo->fd_seq.ds_lfncluster;
+ dirinfo->dir.fd_currsector = dirinfo->fd_seq.ds_lfnsector;
+ dirinfo->dir.fd_index = dirinfo->fd_seq.ds_lfnoffset / DIR_SIZE;
+
+ /* Make sure that the alias is unique in this directory*/
+
+ ret = fat_uniquealias(fs, dirinfo);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Get the short file name checksum */
+
+ checksum = fat_lfnchecksum(dirinfo->fd_name);
+
+ /* Setup the starting sequence number */
+
+ seqno = LDIR0_LAST | nentries;
+
+ /* Make sure that the sector containing the "last" long file name entry
+ * is in the sector cache (it probably is not).
+ */
+
+ ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Now loop, writing each long file name entry */
+
+ for (;;)
+ {
+ /* Get the string offset associated with the directory entry. */
+
+ offset = (nentries - 1) * LDIR_MAXLFNCHARS;
+
+ /* Get a reference to the current directory entry */
+
+ diroffset = (dirinfo->dir.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
+ direntry = &fs->fs_buffer[diroffset];
+
+ /* Is this the "last" LFN directory entry? */
+
+ if ((seqno & LDIR0_LAST) != 0 && remainder != 0)
+ {
+ int nbytes;
+
+ /* Initialize the "last" directory entry name to all 0xffff */
+
+ fat_initlfname(LDIR_PTRWCHAR1_5(direntry), 5);
+ fat_initlfname(LDIR_PTRWCHAR6_11(direntry), 6);
+ fat_initlfname(LDIR_PTRWCHAR12_13(direntry), 2);
+
+ /* Store the tail portion of the long file name in directory entry */
+
+ nbytes = MIN(5, remainder);
+ fat_putlfnchunk(LDIR_PTRWCHAR1_5(direntry),
+ &dirinfo->fd_lfname[offset], nbytes);
+ remainder -= nbytes;
+
+ if (remainder > 0)
+ {
+ nbytes = MIN(6, remainder);
+ fat_putlfnchunk(LDIR_PTRWCHAR6_11(direntry),
+ &dirinfo->fd_lfname[offset+5], nbytes);
+ remainder -= nbytes;
+ }
+
+ if (remainder > 0)
+ {
+ nbytes = MIN(2, remainder);
+ fat_putlfnchunk(LDIR_PTRWCHAR12_13(direntry),
+ &dirinfo->fd_lfname[offset+11], nbytes);
+ remainder -= nbytes;
+ }
+
+ /* The remainder should now be zero */
+
+ DEBUGASSERT(remainder == 0);
+ }
+ else
+ {
+ /* Store a portion long file name in this directory entry */
+
+ fat_putlfnchunk(LDIR_PTRWCHAR1_5(direntry),
+ &dirinfo->fd_lfname[offset], 5);
+ fat_putlfnchunk(LDIR_PTRWCHAR6_11(direntry),
+ &dirinfo->fd_lfname[offset+5], 6);
+ fat_putlfnchunk(LDIR_PTRWCHAR12_13(direntry),
+ &dirinfo->fd_lfname[offset+11], 2);
+ }
+
+ /* Write the remaining directory entries */
+
+ LDIR_PUTSEQ(direntry, seqno);
+ LDIR_PUTATTRIBUTES(direntry, LDDIR_LFNATTR);
+ LDIR_PUTNTRES(direntry, 0);
+ LDIR_PUTCHECKSUM(direntry, checksum);
+ fs->fs_dirty = true;
+
+ /* Read next directory entry */
+
+ if (fat_nextdirentry(fs, &dirinfo->dir) != OK)
+ {
+ return -ENOENT;
+ }
+
+ /* Make sure that the sector containing the directory entry is in the
+ * sector cache
+ */
+
+ ret = fat_fscacheread(fs, dirinfo->dir.fd_currsector);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Decrement the number of entries and get the next sequence number. */
+
+ if (--nentries <= 0)
+ {
+ /* We have written all of the long file name entries to the media
+ * and we have the short file name entry in the cache. We can
+ * just return success.
+ */
+
+ return OK;
+ }
+
+ /* The sequence number is just the number of entries left to be
+ * written.
+ */
+
+ seqno = nentries;
+ }
+}
+#endif
+
+/****************************************************************************
+ * Name: fat_putsfdirentry
+ *
+ * Desciption: Write a short file name directory entry
+ *
+ * Assumption: The directory sector is in the cache. The caller will write
+ * sector information.
+ *
+ ****************************************************************************/
+
+static int fat_putsfdirentry(struct fat_mountpt_s *fs,
+ struct fat_dirinfo_s *dirinfo,
+ uint8_t attributes, uint32_t fattime)
+{
+ uint8_t *direntry;
+
+ /* Initialize the 32-byte directory entry */
+
+ direntry = &fs->fs_buffer[dirinfo->fd_seq.ds_offset];
+ memset(direntry, 0, DIR_SIZE);
+
+ /* Directory name info */
+
+ (void)fat_putsfname(fs, dirinfo);
+
+ /* Set the attribute attribute, write time, creation time */
+
+ DIR_PUTATTRIBUTES(direntry, attributes);
+
+ /* Set the time information */
+
+ DIR_PUTWRTTIME(direntry, fattime & 0xffff);
+ DIR_PUTCRTIME(direntry, fattime & 0xffff);
+ DIR_PUTWRTDATE(direntry, fattime >> 16);
+ DIR_PUTCRDATE(direntry, fattime >> 16);
+
+ fs->fs_dirty = true;
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fat_finddirentry
+ *
+ * Desciption: Given a path to something that may or may not be in the file
+ * system, return the description of the directory entry of the requested
+ * item.
+ *
+ * NOTE: As a side effect, this function returns with the sector containing
+ * the short file name directory entry in the cache.
+ *
+ ****************************************************************************/
+
+int fat_finddirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo,
+ const char *path)
+{
+ off_t cluster;
+ uint8_t *direntry;
+ char terminator;
+ int ret;
+
+ /* Initialize to traverse the chain. Set it to the cluster of the root
+ * directory
+ */
+
+ cluster = fs->fs_rootbase;
+ if (fs->fs_type == FSTYPE_FAT32)
+ {
+ /* For FAT32, the root directory is variable sized and is a cluster
+ * chain like any other directory. fs_rootbase holds the first
+ * cluster of the root directory.
+ */
+
+ dirinfo->dir.fd_startcluster = cluster;
+ dirinfo->dir.fd_currcluster = cluster;
+ dirinfo->dir.fd_currsector = fat_cluster2sector(fs, cluster);
+ }
+ else
+ {
+ /* For FAT12/16, the first sector of the root directory is a sector
+ * relative to the first sector of the fat volume.
+ */
+
+ dirinfo->dir.fd_startcluster = 0;
+ dirinfo->dir.fd_currcluster = 0;
+ dirinfo->dir.fd_currsector = cluster;
+ }
+
+ /* fd_index is the index into the current directory table. It is set to the
+ * the first, entry in the root directory.
+ */
+
+ dirinfo->dir.fd_index = 0;
+
+ /* If no path was provided, then the root directory must be exactly what
+ * the caller is looking for.
+ */
+
+ if (*path == '\0')
+ {
+ dirinfo->fd_root = true;
+ return OK;
+ }
+
+ /* This is not the root directory */
+
+ dirinfo->fd_root = false;
+
+ /* Now loop until the directory entry corresponding to the path is found */
+
+ for (;;)
+ {
+ /* Convert the next the path segment name into the kind of name that
+ * we would see in the directory entry.
+ */
+
+ ret = fat_path2dirname(&path, dirinfo, &terminator);
+ if (ret < 0)
+ {
+ /* ERROR: The filename contains invalid characters or is
+ * too long.
+ */
+
+ return ret;
+ }
+
+ /* Is this a path segment a long or a short file. Was a long file
+ * name parsed?
+ */
+
+#ifdef CONFIG_FAT_LFN
+ if (dirinfo->fd_lfname[0] != '\0')
+ {
+ /* Yes.. Search for the sequence of long file name directory
+ * entries. NOTE: As a side effect, this function returns with
+ * the sector containing the short file name directory entry
+ * in the cache.
+ */
+
+ ret = fat_findlfnentry(fs, dirinfo);
+ }
+ else
+#endif
+ {
+ /* No.. Search for the single short file name directory entry */
+
+ ret = fat_findsfnentry(fs, dirinfo);
+ }
+
+ /* Did we find the directory entries? */
+
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* 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 dirinfo.
+ */
+
+ 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.
+ */
+
+ direntry = &fs->fs_buffer[dirinfo->fd_seq.ds_offset];
+ if (!(DIR_GETATTRIBUTES(direntry) & FATATTR_DIRECTORY))
+ {
+ /* Ooops.. we found something else */
+
+ return -ENOTDIR;
+ }
+
+ /* Get the cluster number of this directory */
+
+ cluster =
+ ((uint32_t)DIR_GETFSTCLUSTHI(direntry) << 16) |
+ DIR_GETFSTCLUSTLO(direntry);
+
+ /* Then restart scanning at the new directory, skipping over both the
+ * '.' and '..' entries that exist in all directories EXCEPT the root
+ * directory.
+ */
+
+ dirinfo->dir.fd_startcluster = cluster;
+ dirinfo->dir.fd_currcluster = cluster;
+ dirinfo->dir.fd_currsector = fat_cluster2sector(fs, cluster);
+ dirinfo->dir.fd_index = 2;
+ }
+}
+
+/****************************************************************************
+ * Name: fat_allocatedirentry
+ *
+ * Desciption: Find a free directory entry
+ *
+ ****************************************************************************/
+
+int fat_allocatedirentry(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
+{
+ int32_t cluster;
+ off_t sector;
+ int ret;
+ int i;
+
+ /* Re-initialize directory object */
+
+ cluster = dirinfo->dir.fd_startcluster;
+
+ /* Loop until we successfully allocate the sequence of directory entries
+ * or until to fail to extend the directory cluster chain.
+ */
+
+ for (;;)
+ {
+ /* Can this cluster chain be extended */
+
+ if (cluster)
+ {
+ /* Cluster chain can be extended */
+
+ dirinfo->dir.fd_currcluster = cluster;
+ dirinfo->dir.fd_currsector = fat_cluster2sector(fs, cluster);
+ }
+ else
+ {
+ /* Fixed size FAT12/16 root directory is at fixed offset/size */
+
+ dirinfo->dir.fd_currsector = fs->fs_rootbase;
+ }
+
+ /* Start at the first entry in the root directory. */
+
+ dirinfo->dir.fd_index = 0;
+
+ /* Is this a path segment a long or a short file. Was a long file
+ * name parsed?
+ */
+
+#ifdef CONFIG_FAT_LFN
+ if (dirinfo->fd_lfname[0] != '\0')
+ {
+ /* Yes.. Allocate for the sequence of long file name directory
+ * entries plus a short file name directory entry.
+ */
+
+ ret = fat_allocatelfnentry(fs, dirinfo);
+ }
+
+ /* No.. Allocate only a short file name directory entry */
+
+ else
+#endif
+ {
+ ret = fat_allocatesfnentry(fs, dirinfo);
+ }
+
+ /* Did we successfully allocate the directory entries? If the error
+ * value is -ENOSPC, then we can try to extend the directory cluster
+ * (we can't handle other return values)
+ */
+
+ if (ret == OK || ret != -ENOSPC)
+ {
+ return ret;
+ }
+
+ /* If we get here, then we have reached the end of the directory table
+ * in this sector without finding a free directory entry.
+ *
+ * It this is a fixed size directory entry, then this is an error.
+ * Otherwise, we can try to extend the directory cluster chain to
+ * make space for the new directory entry.
+ */
+
+ if (!cluster)
+ {
+ /* The size is fixed */
+
+ return -ENOSPC;
+ }
+
+ /* Try to extend the cluster chain for this directory */
+
+ cluster = fat_extendchain(fs, dirinfo->dir.fd_currcluster);
+ if (cluster < 0)
+ {
+ return cluster;
+ }
+
+ /* Flush out any cached data in fs_buffer.. we are going to use
+ * it to initialize the new directory cluster.
+ */
+
+ ret = fat_fscacheflush(fs);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Clear all sectors comprising the new directory cluster */
+
+ fs->fs_currentsector = fat_cluster2sector(fs, cluster);
+ memset(fs->fs_buffer, 0, fs->fs_hwsectorsize);
+
+ sector = fs->fs_currentsector;
+ for (i = fs->fs_fatsecperclus; i; i--)
+ {
+ ret = fat_hwwrite(fs, fs->fs_buffer, sector, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ sector++;
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: fat_freedirentry
+ *
+ * Desciption: Free the directory entry.
+ *
+ * NOTE: As a side effect, this function returns with the sector containing
+ * the deleted short file name directory entry in the cache.
+ *
+ ****************************************************************************/
+
+int fat_freedirentry(struct fat_mountpt_s *fs, struct fat_dirseq_s *seq)
+{
+#ifdef CONFIG_FAT_LFN
+ struct fs_fatdir_s dir;
+ uint16_t diroffset;
+ uint8_t *direntry;
+ int ret;
+
+ /* Set it to the cluster containing the "last" LFN entry (that appears
+ * first on the media).
+ */
+
+ dir.fd_currcluster = seq->ds_lfncluster;
+ dir.fd_currsector = seq->ds_lfnsector;
+ dir.fd_index = seq->ds_lfnoffset / DIR_SIZE;
+
+ /* Free all of the directory entries used for the sequence of long file name
+ * and for the single short file name entry.
+ */
+
+ for (;;)
+ {
+ /* Read the directory sector into the sector cache */
+
+ ret = fat_fscacheread(fs, dir.fd_currsector);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Get a pointer to the directory entry */
+
+ diroffset = (dir.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
+ direntry = &fs->fs_buffer[diroffset];
+
+ /* Then mark the entry as deleted */
+
+ direntry[DIR_NAME] = DIR0_EMPTY;
+ fs->fs_dirty = true;
+
+ /* Did we just free the single short file name entry? */
+
+ if (dir.fd_currsector == seq->ds_sector &&
+ diroffset == seq->ds_offset)
+ {
+ /* Yes.. then we are finished. flush anything remaining in the
+ * cache and return, probably successfully.
+ */
+
+ return fat_fscacheflush(fs);
+ }
+
+ /* There are more entries to go.. Try the next directory entry */
+
+ ret = fat_nextdirentry(fs, &dir);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+
+#else
+ uint8_t *direntry;
+ int ret;
+
+ /* Free the single short file name entry.
+ *
+ * Make sure that the sector containing the directory entry is in the
+ * cache.
+ */
+
+ ret = fat_fscacheread(fs, seq->ds_sector);
+ if (ret == OK)
+ {
+ /* Then mark the entry as deleted */
+
+ direntry = &fs->fs_buffer[seq->ds_offset];
+ direntry[DIR_NAME] = DIR0_EMPTY;
+ fs->fs_dirty = true;
+ }
+
+ return ret;
+#endif
+}
+
+/****************************************************************************
+ * Name: fat_dirname2path
+ *
+ * Desciption: Convert a filename in a raw directory entry into a user
+ * filename. This is essentially the inverse operation of that performed
+ * by fat_path2dirname. See that function for more details.
+ *
+ ****************************************************************************/
+
+int fat_dirname2path(struct fat_mountpt_s *fs, struct fs_dirent_s *dir)
+{
+ uint16_t diroffset;
+ uint8_t *direntry;
+#ifdef CONFIG_FAT_LFN
+ uint8_t attribute;
+#endif
+
+ /* Get a reference to the current directory entry */
+
+ diroffset = (dir->u.fat.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
+ direntry = &fs->fs_buffer[diroffset];
+
+ /* Does this entry refer to the last entry of a long file name? */
+
+#ifdef CONFIG_FAT_LFN
+ attribute = DIR_GETATTRIBUTES(direntry);
+ if (((*direntry & LDIR0_LAST) != 0 && attribute == LDDIR_LFNATTR))
+ {
+ /* Yes.. Get the name from a sequence of long file name directory
+ * entries.
+ */
+
+ return fat_getlfname(fs, dir);
+ }
+ else
+#endif
+ {
+ /* No.. Get the name from a short file name directory entries */
+
+ return fat_getsfname(direntry, dir->fd_dir.d_name, NAME_MAX+1);
+ }
+}
+
+/****************************************************************************
+ * Name: fat_dirnamewrite
+ *
+ * Desciption: Write the (possibly long) directory entry name. This function
+ * is called only from fat_rename to write the new file name.
+ *
+ * Assumption: The directory sector containing the short file name entry
+ * is in the cache. *NOT* the sector containing the last long file name
+ * entry!
+ *
+ ****************************************************************************/
+
+int fat_dirnamewrite(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
+{
+#ifdef CONFIG_FAT_LFN
+ int ret;
+
+ /* Is this a long file name? */
+
+ if (dirinfo->fd_lfname[0] != '\0')
+ {
+ /* Write the sequence of long file name directory entries (this function
+ * also creates the short file name alias).
+ */
+
+ ret = fat_putlfname(fs, dirinfo);
+ if (ret != OK)
+ {
+ return ret;
+ }
+ }
+
+ /* On return, fat_lfsfname() will leave the short file name entry in the
+ * cache. So we can just fall throught to write that directory entry, perhaps
+ * using the short file name alias for the long file name.
+ */
+#endif
+
+ return fat_putsfname(fs, dirinfo);
+}
+
+/****************************************************************************
+ * Name: fat_dirwrite
+ *
+ * Desciption: Write a directory entry, possibly with a long file name.
+ * Called from:
+ *
+ * fat_mkdir() to write the new FAT directory entry.
+ * fat_dircreate() to create any new directory entry.
+ *
+ * Assumption: The directory sector is in the cache. The caller will write
+ * sector information.
+ *
+ ****************************************************************************/
+
+int fat_dirwrite(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo,
+ uint8_t attributes, uint32_t fattime)
+{
+#ifdef CONFIG_FAT_LFN
+ int ret;
+
+ /* Does this directory entry have a long file name? */
+
+ if (dirinfo->fd_lfname[0] != '\0')
+ {
+ /* Write the sequence of long file name directory entries (this function
+ * also creates the short file name alias).
+ */
+
+ ret = fat_putlfname(fs, dirinfo);
+ if (ret != OK)
+ {
+ return ret;
+ }
+ }
+
+ /* On return, fat_lfsfname() will leave the short file name entry in the
+ * cache. So we can just fall throught to write that directory entry, perhaps
+ * using the short file name alias for the long file name.
+ */
+#endif
+
+ /* Put the short file name entry data */
+
+ return fat_putsfdirentry(fs, dirinfo, attributes, fattime);
+}
+
+/****************************************************************************
+ * Name: fat_dircreate
+ *
+ * Desciption: Create a directory entry for a new file
+ *
+ ****************************************************************************/
+
+int fat_dircreate(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
+{
+ uint32_t fattime;
+ int ret;
+
+ /* Allocate a directory entry. If long file name support is enabled, then
+ * this might, in fact, allocate a sequence of directory entries.
+ */
+
+ ret = fat_allocatedirentry(fs, dirinfo);
+ if (ret != OK)
+ {
+ /* Failed to allocate the required directory entry or entries. */
+
+ return ret;
+ }
+
+ /* Write the directory entry (or entries) with the current time and the
+ * ARCHIVE attribute.
+ */
+
+ fattime = fat_systime2fattime();
+ return fat_dirwrite(fs, dirinfo, FATATTR_ARCHIVE, fattime);
+}
+
+/****************************************************************************
+ * Name: fat_remove
+ *
+ * Desciption: Remove a directory or file from the file system. This
+ * implements both rmdir() and unlink().
+ *
+ ****************************************************************************/
+
+int fat_remove(struct fat_mountpt_s *fs, const char *relpath, bool directory)
+{
+ struct fat_dirinfo_s dirinfo;
+ uint32_t dircluster;
+ uint8_t *direntry;
+ int ret;
+
+ /* Find the directory entry referring to the entry to be deleted */
+
+ ret = fat_finddirentry(fs, &dirinfo, relpath);
+ if (ret != OK)
+ {
+ /* No such path */
+
+ return -ENOENT;
+ }
+
+ /* Check if this is a FAT12/16 root directory */
+
+ if (dirinfo.fd_root)
+ {
+ /* The root directory cannot be removed */
+
+ return -EPERM;
+ }
+
+ /* The object has to have write access to be deleted */
+
+ direntry = &fs->fs_buffer[dirinfo.fd_seq.ds_offset];
+ if ((DIR_GETATTRIBUTES(direntry) & FATATTR_READONLY) != 0)
+ {
+ /* It is a read-only entry */
+
+ return -EACCES;
+ }
+
+ /* Get the directory sector and cluster containing the entry to be deleted. */
+
+ dircluster =
+ ((uint32_t)DIR_GETFSTCLUSTHI(direntry) << 16) |
+ DIR_GETFSTCLUSTLO(direntry);
+
+ /* Is this entry a directory? */
+
+ if (DIR_GETATTRIBUTES(direntry) & FATATTR_DIRECTORY)
+ {
+ /* It is a sub-directory. Check if we are be asked to remove
+ * a directory or a file.
+ */
+
+ if (!directory)
+ {
+ /* We are asked to delete a file */
+
+ return -EISDIR;
+ }
+
+ /* We are asked to delete a directory. Check if this sub-directory is
+ * empty (i.e., that there are no valid entries other than the initial
+ * '.' and '..' entries).
+ */
+
+ dirinfo.dir.fd_currcluster = dircluster;
+ dirinfo.dir.fd_currsector = fat_cluster2sector(fs, dircluster);
+ dirinfo.dir.fd_index = 2;
+
+ /* Loop until either (1) an entry is found in the directory (error),
+ * (2) the directory is found to be empty, or (3) some error occurs.
+ */
+
+ for (;;)
+ {
+ unsigned int subdirindex;
+ uint8_t *subdirentry;
+
+ /* Make sure that the sector containing the of the subdirectory
+ * sector is in the cache
+ */
+
+ ret = fat_fscacheread(fs, dirinfo.dir.fd_currsector);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Get a reference to the next entry in the directory */
+
+ subdirindex = (dirinfo.dir.fd_index & DIRSEC_NDXMASK(fs)) * DIR_SIZE;
+ subdirentry = &fs->fs_buffer[subdirindex];
+
+ /* Is this the last entry in the direcory? */
+
+ if (subdirentry[DIR_NAME] == DIR0_ALLEMPTY)
+ {
+ /* Yes then the directory is empty. Break out of the
+ * loop and delete the directory.
+ */
+
+ break;
+ }
+
+ /* Check if the next entry refers to a file or directory */
+
+ if (subdirentry[DIR_NAME] != DIR0_EMPTY &&
+ !(DIR_GETATTRIBUTES(subdirentry) & FATATTR_VOLUMEID))
+ {
+ /* The directory is not empty */
+
+ return -ENOTEMPTY;
+ }
+
+ /* Get the next directory entry */
+
+ ret = fat_nextdirentry(fs, &dirinfo.dir);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+ }
+ else
+ {
+ /* It is a file. Check if we are be asked to remove a directory
+ * or a file.
+ */
+
+ if (directory)
+ {
+ /* We are asked to remove a directory */
+
+ return -ENOTDIR;
+ }
+ }
+
+ /* Mark the directory entry 'deleted'. If long file name support is
+ * enabled, then multiple directory entries may be freed.
+ */
+
+ ret = fat_freedirentry(fs, &dirinfo.fd_seq);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* And remove the cluster chain making up the subdirectory */
+
+ ret = fat_removechain(fs, dircluster);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Update the FSINFO sector (FAT32) */
+
+ ret = fat_updatefsinfo(fs);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ return OK;
+}
diff --git a/nuttx/fs/fat/fs_fat32util.c b/nuttx/fs/fat/fs_fat32util.c
new file mode 100644
index 000000000..7231456d7
--- /dev/null
+++ b/nuttx/fs/fat/fs_fat32util.c
@@ -0,0 +1,1856 @@
+/****************************************************************************
+ * fs/fat/fs_fat32util.c
+ *
+ * Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References:
+ * Microsoft FAT documentation
+ * Some good ideas were leveraged from the FAT implementation:
+ * 'Copyright (C) 2007, ChaN, all right reserved.'
+ * which has an unrestricted license.
+ *
+ * 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 <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <semaphore.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/fat.h>
+
+#include "fs_internal.h"
+#include "fs_fat32.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fat_checkfsinfo
+ *
+ * Desciption: Read the FAT32 FSINFO sector
+ *
+ ****************************************************************************/
+
+static int fat_checkfsinfo(struct fat_mountpt_s *fs)
+{
+ /* Make sure that the fsinfo sector is in the cache */
+
+ if (fat_fscacheread(fs, fs->fs_fsinfo) == OK)
+ {
+ /* Verify that this is, indeed, an FSINFO sector */
+
+ if (FSI_GETLEADSIG(fs->fs_buffer) == 0x41615252 &&
+ FSI_GETSTRUCTSIG(fs->fs_buffer) == 0x61417272 &&
+ FSI_GETTRAILSIG(fs->fs_buffer) == BOOT_SIGNATURE32)
+ {
+ fs->fs_fsinextfree = FSI_GETFREECOUNT(fs->fs_buffer);
+ fs->fs_fsifreecount = FSI_GETNXTFREE(fs->fs_buffer);
+ return OK;
+ }
+ }
+
+ return -ENODEV;
+}
+
+/****************************************************************************
+ * Name: fat_checkbootrecord
+ *
+ * Desciption: Read a sector and verify that it is a a FAT boot record.
+ *
+ ****************************************************************************/
+
+static int fat_checkbootrecord(struct fat_mountpt_s *fs)
+{
+ uint32_t ndatasectors;
+ uint32_t ntotalfatsects;
+ uint16_t rootdirsectors = 0;
+ bool notfat32 = false;
+
+ /* Verify the MBR signature at offset 510 in the sector (true even
+ * if the sector size is greater than 512. All FAT file systems have
+ * this signature. On a FAT32 volume, the RootEntCount , FatSz16, and
+ * FatSz32 values should always be zero. The FAT sector size should
+ * match the reported hardware sector size.
+ */
+
+ if (MBR_GETSIGNATURE(fs->fs_buffer) != BOOT_SIGNATURE16 ||
+ MBR_GETBYTESPERSEC(fs->fs_buffer) != fs->fs_hwsectorsize)
+ {
+ fdbg("ERROR: Signature: %04x FS sectorsize: %d HW sectorsize: %d\n",
+ MBR_GETSIGNATURE(fs->fs_buffer), MBR_GETBYTESPERSEC(fs->fs_buffer),
+ fs->fs_hwsectorsize);
+
+ return -EINVAL;
+ }
+
+ /* Verify the FAT32 file system type. The determination of the file
+ * system type is based on the number of clusters on the volume: FAT12
+ * volume has <= FAT_MAXCLUST12 (4084) clusters, a FAT16 volume has <=
+ * FAT_MINCLUST16 (microsfoft says < 65,525) clusters, and any larger
+ * is FAT32.
+ *
+ * Get the number of 32-bit directory entries in root directory (zero
+ * for FAT32).
+ */
+
+ fs->fs_rootentcnt = MBR_GETROOTENTCNT(fs->fs_buffer);
+ if (fs->fs_rootentcnt != 0)
+ {
+ notfat32 = true; /* Must be zero for FAT32 */
+ rootdirsectors = (32 * fs->fs_rootentcnt + fs->fs_hwsectorsize - 1) / fs->fs_hwsectorsize;
+ }
+
+ /* Determine the number of sectors in a FAT. */
+
+ fs->fs_nfatsects = MBR_GETFATSZ16(fs->fs_buffer); /* Should be zero */
+ if (fs->fs_nfatsects)
+ {
+ notfat32 = true; /* Must be zero for FAT32 */
+ }
+ else
+ {
+ fs->fs_nfatsects = MBR_GETFATSZ32(fs->fs_buffer);
+ }
+
+ if (!fs->fs_nfatsects || fs->fs_nfatsects >= fs->fs_hwnsectors)
+ {
+ fdbg("ERROR: fs_nfatsects %d fs_hwnsectors: %d\n",
+ fs->fs_nfatsects, fs->fs_hwnsectors);
+
+ return -EINVAL;
+ }
+
+ /* Get the total number of sectors on the volume. */
+
+ fs->fs_fattotsec = MBR_GETTOTSEC16(fs->fs_buffer); /* Should be zero */
+ if (fs->fs_fattotsec)
+ {
+ notfat32 = true; /* Must be zero for FAT32 */
+ }
+ else
+ {
+ fs->fs_fattotsec = MBR_GETTOTSEC32(fs->fs_buffer);
+ }
+
+ if (!fs->fs_fattotsec || fs->fs_fattotsec > fs->fs_hwnsectors)
+ {
+ fdbg("ERROR: fs_fattotsec %d fs_hwnsectors: %d\n",
+ fs->fs_fattotsec, fs->fs_hwnsectors);
+
+ return -EINVAL;
+ }
+
+ /* Get the total number of reserved sectors */
+
+ fs->fs_fatresvdseccount = MBR_GETRESVDSECCOUNT(fs->fs_buffer);
+ if (fs->fs_fatresvdseccount > fs->fs_hwnsectors)
+ {
+ fdbg("ERROR: fs_fatresvdseccount %d fs_hwnsectors: %d\n",
+ fs->fs_fatresvdseccount, fs->fs_hwnsectors);
+
+ return -EINVAL;
+ }
+
+ /* Get the number of FATs. This is probably two but could have other values */
+
+ fs->fs_fatnumfats = MBR_GETNUMFATS(fs->fs_buffer);
+ ntotalfatsects = fs->fs_fatnumfats * fs->fs_nfatsects;
+
+ /* Get the total number of data sectors */
+
+ ndatasectors = fs->fs_fattotsec - fs->fs_fatresvdseccount - ntotalfatsects - rootdirsectors;
+ if (ndatasectors > fs->fs_hwnsectors)
+ {
+ fdbg("ERROR: ndatasectors %d fs_hwnsectors: %d\n",
+ ndatasectors, fs->fs_hwnsectors);
+
+ return -EINVAL;
+ }
+
+ /* Get the sectors per cluster */
+
+ fs->fs_fatsecperclus = MBR_GETSECPERCLUS(fs->fs_buffer);
+
+ /* Calculate the number of clusters */
+
+ fs->fs_nclusters = ndatasectors / fs->fs_fatsecperclus;
+
+ /* Finally, the test: */
+
+ if (fs->fs_nclusters <= FAT_MAXCLUST12)
+ {
+ fs->fs_fsinfo = 0;
+ fs->fs_type = FSTYPE_FAT12;
+ }
+ else if (fs->fs_nclusters <= FAT_MAXCLUST16)
+ {
+ fs->fs_fsinfo = 0;
+ fs->fs_type = FSTYPE_FAT16;
+ }
+ else if (!notfat32)
+ {
+ fs->fs_fsinfo = fs->fs_fatbase + MBR_GETFSINFO(fs->fs_buffer);
+ fs->fs_type = FSTYPE_FAT32;
+ }
+ else
+ {
+ fdbg("ERROR: notfat32: %d fs_nclusters: %d\n",
+ notfat32, fs->fs_nclusters);
+
+ return -EINVAL;
+ }
+
+ /* We have what appears to be a valid FAT filesystem! Save a few more things
+ * from the boot record that we will need later.
+ */
+
+ fs->fs_fatbase += fs->fs_fatresvdseccount;
+
+ if (fs->fs_type == FSTYPE_FAT32)
+ {
+ fs->fs_rootbase = MBR_GETROOTCLUS(fs->fs_buffer);
+ }
+ else
+ {
+ fs->fs_rootbase = fs->fs_fatbase + ntotalfatsects;
+ }
+
+ fs->fs_database = fs->fs_fatbase + ntotalfatsects + fs->fs_rootentcnt / DIRSEC_NDIRS(fs);
+ fs->fs_fsifreecount = 0xffffffff;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fat_getuint16
+ ****************************************************************************/
+
+uint16_t fat_getuint16(uint8_t *ptr)
+{
+#ifdef CONFIG_ENDIAN_BIG
+ /* The bytes always have to be swapped if the target is big-endian */
+
+ return ((uint16_t)ptr[0] << 8) | ptr[1];
+#else
+ /* Byte-by-byte transfer is still necessary if the address is un-aligned */
+
+ return ((uint16_t)ptr[1] << 8) | ptr[0];
+#endif
+}
+
+/****************************************************************************
+ * Name: fat_getuint32
+ ****************************************************************************/
+
+uint32_t fat_getuint32(uint8_t *ptr)
+{
+#ifdef CONFIG_ENDIAN_BIG
+ /* The bytes always have to be swapped if the target is big-endian */
+
+ return ((uint32_t)fat_getuint16(&ptr[0]) << 16) | fat_getuint16(&ptr[2]);
+#else
+ /* Byte-by-byte transfer is still necessary if the address is un-aligned */
+
+ return ((uint32_t)fat_getuint16(&ptr[2]) << 16) | fat_getuint16(&ptr[0]);
+#endif
+}
+
+/****************************************************************************
+ * Name: fat_putuint16
+ ****************************************************************************/
+
+void fat_putuint16(uint8_t *ptr, uint16_t value16)
+{
+ uint8_t *val = (uint8_t*)&value16;
+#ifdef CONFIG_ENDIAN_BIG
+ /* The bytes always have to be swapped if the target is big-endian */
+
+ ptr[0] = val[1];
+ ptr[1] = val[0];
+#else
+ /* Byte-by-byte transfer is still necessary if the address is un-aligned */
+
+ ptr[0] = val[0];
+ ptr[1] = val[1];
+#endif
+}
+
+/****************************************************************************
+ * Name: fat_putuint32
+ ****************************************************************************/
+
+void fat_putuint32(uint8_t *ptr, uint32_t value32)
+{
+ uint16_t *val = (uint16_t*)&value32;
+#ifdef CONFIG_ENDIAN_BIG
+ /* The bytes always have to be swapped if the target is big-endian */
+
+ fat_putuint16(&ptr[0], val[2]);
+ fat_putuint16(&ptr[2], val[0]);
+#else
+ /* Byte-by-byte transfer is still necessary if the address is un-aligned */
+
+ fat_putuint16(&ptr[0], val[0]);
+ fat_putuint16(&ptr[2], val[2]);
+#endif
+}
+
+/****************************************************************************
+ * Name: fat_semtake
+ ****************************************************************************/
+
+void fat_semtake(struct fat_mountpt_s *fs)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&fs->fs_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: fat_semgive
+ ****************************************************************************/
+
+void fat_semgive(struct fat_mountpt_s *fs)
+{
+ sem_post(&fs->fs_sem);
+}
+
+/****************************************************************************
+ * Name: fat_systime2fattime
+ *
+ * Desciption: Get the system time convert to a time and and date suitble
+ * for writing into the FAT FS.
+ *
+ * TIME in LS 16-bits:
+ * Bits 0:4 = 2 second count (0-29 representing 0-58 seconds)
+ * Bits 5-10 = minutes (0-59)
+ * Bits 11-15 = hours (0-23)
+ * DATE in MS 16-bits
+ * Bits 0:4 = Day of month (1-31)
+ * Bits 5:8 = Month of year (1-12)
+ * Bits 9:15 = Year from 1980 (0-127 representing 1980-2107)
+ *
+ ****************************************************************************/
+
+uint32_t fat_systime2fattime(void)
+{
+ /* Unless you have a hardware RTC or some other to get accurate time, then
+ * there is no reason to support FAT time.
+ */
+
+#ifdef CONFIG_FS_FATTIME
+ struct timespec ts;
+ struct tm tm;
+ int ret;
+
+ /* Get the current time in seconds and nanoseconds */
+
+ ret = clock_gettime(CLOCK_REALTIME, &ts);
+ if (ret == OK)
+ {
+ /* Break done the seconds in date and time units */
+
+ if (gmtime_r((FAR const time_t *)&ts.tv_sec, &tm) != NULL)
+ {
+ /* FAT can only represent dates since 1980. struct tm can
+ * represent dates since 1900.
+ */
+
+ if (tm.tm_year >= 80)
+ {
+ uint16_t fattime;
+ uint16_t fatdate;
+
+ fattime = (tm.tm_sec >> 1) & 0x001f; /* Bits 0-4: 2 second count (0-29) */
+ fattime |= (tm.tm_min << 5) & 0x07e0; /* Bits 5-10: minutes (0-59) */
+ fattime |= (tm.tm_hour << 11) & 0xf800; /* Bits 11-15: hours (0-23) */
+
+ fatdate = tm.tm_mday & 0x001f; /* Bits 0-4: Day of month (1-31) */
+ fatdate |= ((tm.tm_mon+1) << 5) & 0x01e0; /* Bits 5-8: Month of year (1-12) */
+ fatdate |= ((tm.tm_year-80) << 9) & 0xfe00; /* Bits 9-15: Year from 1980 */
+
+ return (uint32_t)fatdate << 16 | (uint32_t)fattime;
+ }
+ }
+ }
+#endif
+ return 0;
+}
+
+/****************************************************************************
+ * Name: fat_fattime2systime
+ *
+ * Desciption: Convert FAT data and time to a system time_t
+ *
+ * 16-bit FAT time:
+ * Bits 0:4 = 2 second count (0-29 representing 0-58 seconds)
+ * Bits 5-10 = minutes (0-59)
+ * Bits 11-15 = hours (0-23)
+ * 16-bit FAT date:
+ * Bits 0:4 = Day of month (1-31)
+ * Bits 5:8 = Month of year (1-12)
+ * Bits 9:15 = Year from 1980 (0-127 representing 1980-2107)
+ *
+ ****************************************************************************/
+
+time_t fat_fattime2systime(uint16_t fattime, uint16_t fatdate)
+{
+ /* Unless you have a hardware RTC or some other to get accurate time, then
+ * there is no reason to support FAT time.
+ */
+
+#ifdef CONFIG_FS_FATTIME
+ struct tm tm;
+ unsigned int tmp;
+
+ /* Break out the date and time */
+
+ tm.tm_sec = (fattime & 0x001f) << 1; /* Bits 0-4: 2 second count (0-29) */
+ tm.tm_min = (fattime & 0x07e0) >> 5; /* Bits 5-10: minutes (0-59) */
+ tm.tm_hour = (fattime & 0xf800) >> 11; /* Bits 11-15: hours (0-23) */
+
+ tm.tm_mday = (fatdate & 0x001f); /* Bits 0-4: Day of month (1-31) */
+ tmp = ((fatdate & 0x01e0) >> 5); /* Bits 5-8: Month of year (1-12) */
+ tm.tm_mon = tmp > 0 ? tmp-1 : 0;
+ tm.tm_year = ((fatdate & 0xfe00) >> 9) + 80; /* Bits 9-15: Year from 1980 */
+
+ /* Then convert the broken out time into seconds since the epoch */
+
+ return mktime(&tm);
+#else
+ return 0;
+#endif
+}
+
+/****************************************************************************
+ * Name: fat_mount
+ *
+ * Desciption: This function is called only when the mountpoint is first
+ * established. It initializes the mountpoint structure and verifies
+ * that a valid FAT32 filesystem is provided by the block driver.
+ *
+ * The caller should hold the mountpoint semaphore
+ *
+ ****************************************************************************/
+
+int fat_mount(struct fat_mountpt_s *fs, bool writeable)
+{
+ FAR struct inode *inode;
+ struct geometry geo;
+ int ret;
+
+ /* Assume that the mount is successful */
+
+ fs->fs_mounted = true;
+
+ /* Check if there is media available */
+
+ inode = fs->fs_blkdriver;
+ if (!inode || !inode->u.i_bops || !inode->u.i_bops->geometry ||
+ inode->u.i_bops->geometry(inode, &geo) != OK || !geo.geo_available)
+ {
+ ret = -ENODEV;
+ goto errout;
+ }
+
+ /* Make sure that that the media is write-able (if write access is needed) */
+
+ if (writeable && !geo.geo_writeenabled)
+ {
+ ret = -EACCES;
+ goto errout;
+ }
+
+ /* Save the hardware geometry */
+
+ fs->fs_hwsectorsize = geo.geo_sectorsize;
+ fs->fs_hwnsectors = geo.geo_nsectors;
+
+ /* Allocate a buffer to hold one hardware sector */
+
+ fs->fs_buffer = (uint8_t*)fat_io_alloc(fs->fs_hwsectorsize);
+ if (!fs->fs_buffer)
+ {
+ ret = -ENOMEM;
+ goto errout;
+ }
+
+ /* Search FAT boot record on the drive. First check at sector zero. This
+ * could be either the boot record or a partition that refers to the boot
+ * record.
+ *
+ * First read sector zero. This will be the first access to the drive and a
+ * likely failure point.
+ */
+
+ fs->fs_fatbase = 0;
+ ret = fat_hwread(fs, fs->fs_buffer, 0, 1);
+ if (ret < 0)
+ {
+ goto errout_with_buffer;
+ }
+
+ ret = fat_checkbootrecord(fs);
+ if (ret != OK)
+ {
+ /* The contents of sector 0 is not a boot record. It could be a
+ * partition, however. Assume it is a partition and get the offset
+ * into the partition table. This table is at offset MBR_TABLE and is
+ * indexed by 16x the partition number.
+ */
+
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ /* Check if the partition exists and, if so, get the bootsector for that
+ * partition and see if we can find the boot record there.
+ */
+
+ uint8_t part = PART_GETTYPE(i, fs->fs_buffer);
+ fvdbg("Partition %d, offset %d, type %d\n", i, PART_ENTRY(i), part);
+
+ if (part == 0)
+ {
+ fvdbg("No partition %d\n", i);
+ continue;
+ }
+
+ /* There appears to be a partition, get the sector number of the
+ * partition (LBA)
+ */
+
+ fs->fs_fatbase = PART_GETSTARTSECTOR(i, fs->fs_buffer);
+
+ /* Read the new candidate boot sector */
+
+ ret = fat_hwread(fs, fs->fs_buffer, fs->fs_fatbase, 1);
+ if (ret < 0)
+ {
+ /* Failed to read the sector */
+
+ goto errout_with_buffer;
+ }
+
+ /* Check if this is a boot record */
+
+ ret = fat_checkbootrecord(fs);
+ if (ret == OK)
+ {
+ /* Break out of the loop if a valid boot record is found */
+
+ fvdbg("MBR found in partition %d\n", i);
+ break;
+ }
+
+ /* Re-read sector 0 so that we can check the next partition */
+
+ fvdbg("Partition %d is not an MBR\n", i);
+ ret = fat_hwread(fs, fs->fs_buffer, 0, 1);
+ if (ret < 0)
+ {
+ goto errout_with_buffer;
+ }
+ }
+
+ if (i > 3)
+ {
+ fdbg("No valid MBR\n");
+ goto errout_with_buffer;
+ }
+ }
+
+ /* We have what appears to be a valid FAT filesystem! Now read the
+ * FSINFO sector (FAT32 only)
+ */
+
+ if (fs->fs_type == FSTYPE_FAT32)
+ {
+ ret = fat_checkfsinfo(fs);
+ if (ret != OK)
+ {
+ goto errout_with_buffer;
+ }
+ }
+
+ /* We did it! */
+
+ fdbg("FAT%d:\n", fs->fs_type == 0 ? 12 : fs->fs_type == 1 ? 16 : 32);
+ fdbg("\tHW sector size: %d\n", fs->fs_hwsectorsize);
+ fdbg("\t sectors: %d\n", fs->fs_hwnsectors);
+ fdbg("\tFAT reserved: %d\n", fs->fs_fatresvdseccount);
+ fdbg("\t sectors: %d\n", fs->fs_fattotsec);
+ fdbg("\t start sector: %d\n", fs->fs_fatbase);
+ fdbg("\t root sector: %d\n", fs->fs_rootbase);
+ fdbg("\t root entries: %d\n", fs->fs_rootentcnt);
+ fdbg("\t data sector: %d\n", fs->fs_database);
+ fdbg("\t FSINFO sector: %d\n", fs->fs_fsinfo);
+ fdbg("\t Num FATs: %d\n", fs->fs_fatnumfats);
+ fdbg("\t FAT sectors: %d\n", fs->fs_nfatsects);
+ fdbg("\t sectors/cluster: %d\n", fs->fs_fatsecperclus);
+ fdbg("\t max clusters: %d\n", fs->fs_nclusters);
+ fdbg("\tFSI free count %d\n", fs->fs_fsifreecount);
+ fdbg("\t next free %d\n", fs->fs_fsinextfree);
+
+ return OK;
+
+ errout_with_buffer:
+ fat_io_free(fs->fs_buffer, fs->fs_hwsectorsize);
+ fs->fs_buffer = 0;
+ errout:
+ fs->fs_mounted = false;
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_checkmount
+ *
+ * Desciption: Check if the mountpoint is still valid.
+ *
+ * The caller should hold the mountpoint semaphore
+ *
+ ****************************************************************************/
+
+int fat_checkmount(struct fat_mountpt_s *fs)
+{
+ /* If the fs_mounted flag is false, then we have already handled the loss
+ * of the mount.
+ */
+
+ if (fs && fs->fs_mounted)
+ {
+ struct fat_file_s *file;
+
+ /* We still think the mount is healthy. Check an see if this is
+ * still the case
+ */
+
+ if (fs->fs_blkdriver)
+ {
+ struct inode *inode = fs->fs_blkdriver;
+ if (inode && inode->u.i_bops && inode->u.i_bops->geometry)
+ {
+ struct geometry geo;
+ int errcode = inode->u.i_bops->geometry(inode, &geo);
+ if (errcode == OK && geo.geo_available && !geo.geo_mediachanged)
+ {
+ return OK;
+ }
+ }
+ }
+
+ /* If we get here, the mount is NOT healthy */
+
+ fs->fs_mounted = false;
+
+ /* Make sure that this is flagged in every opened file */
+
+ for (file = fs->fs_head; file; file = file->ff_next)
+ {
+ file->ff_open = false;
+ }
+ }
+ return -ENODEV;
+}
+
+/****************************************************************************
+ * Name: fat_hwread
+ *
+ * Desciption: Read the specified sector into the sector buffer
+ *
+ ****************************************************************************/
+
+int fat_hwread(struct fat_mountpt_s *fs, uint8_t *buffer, off_t sector,
+ unsigned int nsectors)
+{
+ int ret = -ENODEV;
+ if (fs && fs->fs_blkdriver )
+ {
+ struct inode *inode = fs->fs_blkdriver;
+ if (inode && inode->u.i_bops && inode->u.i_bops->read)
+ {
+ ssize_t nSectorsRead = inode->u.i_bops->read(inode, buffer,
+ sector, nsectors);
+ if (nSectorsRead == nsectors)
+ {
+ ret = OK;
+ }
+ else if (nSectorsRead < 0)
+ {
+ ret = nSectorsRead;
+ }
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_hwwrite
+ *
+ * Desciption: Write the sector buffer to the specified sector
+ *
+ ****************************************************************************/
+
+int fat_hwwrite(struct fat_mountpt_s *fs, uint8_t *buffer, off_t sector,
+ unsigned int nsectors)
+{
+ int ret = -ENODEV;
+ if (fs && fs->fs_blkdriver )
+ {
+ struct inode *inode = fs->fs_blkdriver;
+ if (inode && inode->u.i_bops && inode->u.i_bops->write)
+ {
+ ssize_t nSectorsWritten =
+ inode->u.i_bops->write(inode, buffer, sector, nsectors);
+
+ if (nSectorsWritten == nsectors)
+ {
+ ret = OK;
+ }
+ else if (nSectorsWritten < 0)
+ {
+ ret = nSectorsWritten;
+ }
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_cluster2sector
+ *
+ * Desciption: Convert a cluster number to a start sector number
+ *
+ ****************************************************************************/
+
+off_t fat_cluster2sector(struct fat_mountpt_s *fs, uint32_t cluster )
+{
+ cluster -= 2;
+ if (cluster >= fs->fs_nclusters - 2)
+ {
+ return -EINVAL;
+ }
+ return cluster * fs->fs_fatsecperclus + fs->fs_database;
+}
+
+/****************************************************************************
+ * Name: fat_getcluster
+ *
+ * Desciption: Get the next cluster start from the FAT.
+ *
+ * Return: <0: error, 0:cluster unassigned, >=0: start sector of cluster
+ *
+ ****************************************************************************/
+
+off_t fat_getcluster(struct fat_mountpt_s *fs, uint32_t clusterno)
+{
+ /* Verify that the cluster number is within range */
+
+ if (clusterno >= 2 && clusterno < fs->fs_nclusters)
+ {
+ /* Okay.. Read the next cluster from the FAT. The way we will do
+ * this depends on the type of FAT filesystm we are dealing with.
+ */
+
+ switch (fs->fs_type)
+ {
+ case FSTYPE_FAT12 :
+ {
+ off_t fatsector;
+ unsigned int fatoffset;
+ unsigned int cluster;
+ unsigned int fatindex;
+
+ /* FAT12 is more complex because it has 12-bits (1.5 bytes)
+ * per FAT entry. Get the offset to the first byte:
+ */
+
+ fatoffset = (clusterno * 3) / 2;
+ fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
+
+ /* Read the sector at this offset */
+
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+
+ break;
+ }
+
+ /* Get the first, LS byte of the cluster from the FAT */
+
+ fatindex = fatoffset & SEC_NDXMASK(fs);
+ cluster = fs->fs_buffer[fatindex];
+
+ /* With FAT12, the second byte of the cluster number may lie in
+ * a different sector than the first byte.
+ */
+
+ fatindex++;
+ if (fatindex >= fs->fs_hwsectorsize)
+ {
+ fatsector++;
+ fatindex = 0;
+
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+
+ break;
+ }
+ }
+
+ /* Get the second, MS byte of the cluster for 16-bits. The
+ * does not depend on the endian-ness of the target, but only
+ * on the fact that the byte stream is little-endian.
+ */
+
+ cluster |= (unsigned int)fs->fs_buffer[fatindex] << 8;
+
+ /* Now, pick out the correct 12 bit cluster start sector value */
+
+ if ((clusterno & 1) != 0)
+ {
+ /* Odd.. take the MS 12-bits */
+
+ cluster >>= 4;
+ }
+ else
+ {
+ /* Even.. take the LS 12-bits */
+
+ cluster &= 0x0fff;
+ }
+ return cluster;
+ }
+
+ case FSTYPE_FAT16 :
+ {
+ unsigned int fatoffset = 2 * clusterno;
+ off_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
+ unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
+
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+ break;
+ }
+ return FAT_GETFAT16(fs->fs_buffer, fatindex);
+ }
+
+ case FSTYPE_FAT32 :
+ {
+ unsigned int fatoffset = 4 * clusterno;
+ off_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
+ unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
+
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+ break;
+ }
+ return FAT_GETFAT32(fs->fs_buffer, fatindex) & 0x0fffffff;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* There is no cluster information, or an error occured */
+
+ return (off_t)-EINVAL;
+}
+
+/****************************************************************************
+ * Name: fat_putcluster
+ *
+ * Desciption: Write a new cluster into the FAT
+ *
+ ****************************************************************************/
+
+int fat_putcluster(struct fat_mountpt_s *fs, uint32_t clusterno, off_t nextcluster)
+{
+ /* Verify that the cluster number is within range. Zero erases the cluster. */
+
+ if (clusterno == 0 || (clusterno >= 2 && clusterno < fs->fs_nclusters))
+ {
+ /* Okay.. Write the next cluster into the FAT. The way we will do
+ * this depends on the type of FAT filesystm we are dealing with.
+ */
+
+ switch (fs->fs_type)
+ {
+ case FSTYPE_FAT12 :
+ {
+ off_t fatsector;
+ unsigned int fatoffset;
+ unsigned int fatindex;
+ uint8_t value;
+
+ /* FAT12 is more complex because it has 12-bits (1.5 bytes)
+ * per FAT entry. Get the offset to the first byte:
+ */
+
+ fatoffset = (clusterno * 3) / 2;
+ fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
+
+ /* Make sure that the sector at this offset is in the cache */
+
+ if (fat_fscacheread(fs, fatsector)< 0)
+ {
+ /* Read error */
+
+ break;
+ }
+
+ /* Get the LS byte first handling the 12-bit alignment within
+ * the 16-bits
+ */
+
+ fatindex = fatoffset & SEC_NDXMASK(fs);
+ if ((clusterno & 1) != 0)
+ {
+ /* Save the LS four bits of the next cluster */
+
+ value = (fs->fs_buffer[fatindex] & 0x0f) | nextcluster << 4;
+ }
+ else
+ {
+ /* Save the LS eight bits of the next cluster */
+
+ value = (uint8_t)nextcluster;
+ }
+
+ fs->fs_buffer[fatindex] = value;
+
+ /* With FAT12, the second byte of the cluster number may lie in
+ * a different sector than the first byte.
+ */
+
+ fatindex++;
+ if (fatindex >= fs->fs_hwsectorsize)
+ {
+ /* Read the next sector */
+
+ fatsector++;
+ fatindex = 0;
+
+ /* Set the dirty flag to make sure the sector that we
+ * just modified is written out.
+ */
+
+ fs->fs_dirty = true;
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+
+ break;
+ }
+ }
+
+ /* Output the MS byte first handling the 12-bit alignment within
+ * the 16-bits
+ */
+
+ if ((clusterno & 1) != 0)
+ {
+ /* Save the MS eight bits of the next cluster */
+
+ value = (uint8_t)(nextcluster >> 4);
+ }
+ else
+ {
+ /* Save the MS four bits of the next cluster */
+
+ value = (fs->fs_buffer[fatindex] & 0xf0) | ((nextcluster >> 8) & 0x0f);
+ }
+
+ fs->fs_buffer[fatindex] = value;
+ }
+ break;
+
+ case FSTYPE_FAT16 :
+ {
+ unsigned int fatoffset = 2 * clusterno;
+ off_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
+ unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
+
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+
+ break;
+ }
+ FAT_PUTFAT16(fs->fs_buffer, fatindex, nextcluster & 0xffff);
+ }
+ break;
+
+ case FSTYPE_FAT32 :
+ {
+ unsigned int fatoffset = 4 * clusterno;
+ off_t fatsector = fs->fs_fatbase + SEC_NSECTORS(fs, fatoffset);
+ unsigned int fatindex = fatoffset & SEC_NDXMASK(fs);
+
+ if (fat_fscacheread(fs, fatsector) < 0)
+ {
+ /* Read error */
+
+ break;
+ }
+ FAT_PUTFAT32(fs->fs_buffer, fatindex, nextcluster & 0x0fffffff);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Mark the modified sector as "dirty" and return success */
+
+ fs->fs_dirty = true;
+ return OK;
+ }
+
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Name: fat_removechain
+ *
+ * Desciption: Remove an entire chain of clusters, starting with 'cluster'
+ *
+ ****************************************************************************/
+
+int fat_removechain(struct fat_mountpt_s *fs, uint32_t cluster)
+{
+ int32_t nextcluster;
+ int ret;
+
+ /* Loop while there are clusters in the chain */
+
+ while (cluster >= 2 && cluster < fs->fs_nclusters)
+ {
+ /* Get the next cluster after the current one */
+
+ nextcluster = fat_getcluster(fs, cluster);
+ if (nextcluster < 0)
+ {
+ /* Error! */
+ return nextcluster;
+ }
+
+ /* Then nullify current cluster -- removing it from the chain */
+
+ ret = fat_putcluster(fs, cluster, 0);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Update FSINFINFO data */
+
+ if (fs->fs_fsifreecount != 0xffffffff)
+ {
+ fs->fs_fsifreecount++;
+ fs->fs_fsidirty = 1;
+ }
+
+ /* Then set up to remove the next cluster */
+
+ cluster = nextcluster;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_extendchain
+ *
+ * Desciption: Add a new cluster to the chain following cluster (if cluster
+ * is non-NULL). if cluster is zero, then a new chain is created.
+ *
+ * Return: <0:error, 0: no free cluster, >=2: new cluster number
+ *
+ ****************************************************************************/
+
+int32_t fat_extendchain(struct fat_mountpt_s *fs, uint32_t cluster)
+{
+ off_t startsector;
+ uint32_t newcluster;
+ uint32_t startcluster;
+ int ret;
+
+ /* The special value 0 is used when the new chain should start */
+
+ if (cluster == 0)
+ {
+ /* The FSINFO NextFree entry should be a good starting point
+ * in the search for a new cluster
+ */
+
+ startcluster = fs->fs_fsinextfree;
+ if (startcluster == 0 || startcluster >= fs->fs_nclusters)
+ {
+ /* But it is bad.. we have to start at the beginning */
+ startcluster = 1;
+ }
+ }
+ else
+ {
+ /* We are extending an existing chain. Verify that this
+ * is a valid cluster by examining its start sector.
+ */
+
+ startsector = fat_getcluster(fs, cluster);
+ if (startsector < 0)
+ {
+ /* An error occurred, return the error value */
+
+ return startsector;
+ }
+ else if (startsector < 2)
+ {
+ /* Oops.. this cluster does not exist. */
+
+ return 0;
+ }
+ else if (startsector < fs->fs_nclusters)
+ {
+ /* It is already followed by next cluster */
+
+ return startsector;
+ }
+
+ /* Okay.. it checks out */
+
+ startcluster = cluster;
+ }
+
+ /* Loop until (1) we discover that there are not free clusters
+ * (return 0), an errors occurs (return -errno), or (3) we find
+ * the next cluster (return the new cluster number).
+ */
+
+ newcluster = startcluster;
+ for (;;)
+ {
+ /* Examine the next cluster in the FAT */
+
+ newcluster++;
+ if (newcluster >= fs->fs_nclusters)
+ {
+ /* If we hit the end of the available clusters, then
+ * wrap back to the beginning because we might have
+ * started at a non-optimal place. But don't continue
+ * past the start cluster.
+ */
+
+ newcluster = 2;
+ if (newcluster > startcluster)
+ {
+ /* We are back past the starting cluster, then there
+ * is no free cluster.
+ */
+
+ return 0;
+ }
+ }
+
+ /* We have a candidate cluster. Check if the cluster number is
+ * mapped to a group of sectors.
+ */
+
+ startsector = fat_getcluster(fs, newcluster);
+ if (startsector == 0)
+ {
+ /* Found have found a free cluster break out*/
+ break;
+ }
+ else if (startsector < 0)
+ {
+ /* Some error occurred, return the error number */
+ return startsector;
+ }
+
+ /* We wrap all the back to the starting cluster? If so, then
+ * there are no free clusters.
+ */
+
+ if (newcluster == startcluster)
+ {
+ return 0;
+ }
+ }
+
+ /* We get here only if we break out with an available cluster
+ * number in 'newcluster' Now mark that cluster as in-use.
+ */
+
+ ret = fat_putcluster(fs, newcluster, 0x0fffffff);
+ if (ret < 0)
+ {
+ /* An error occurred */
+ return ret;
+ }
+
+ /* And link if to the start cluster (if any)*/
+
+ if (cluster)
+ {
+ /* There is a start cluster -- link it */
+
+ ret = fat_putcluster(fs, cluster, newcluster);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+
+ /* And update the FINSINFO for the next time we have to search */
+
+ fs->fs_fsinextfree = newcluster;
+ if (fs->fs_fsifreecount != 0xffffffff)
+ {
+ fs->fs_fsifreecount--;
+ fs->fs_fsidirty = 1;
+ }
+
+ /* Return then number of the new cluster that was added to the chain */
+
+ return newcluster;
+}
+
+/****************************************************************************
+ * Name: fat_nextdirentry
+ *
+ * Desciption: Read the next directory entry from the sector in cache,
+ * reading the next sector(s) in the cluster as necessary. This function
+ * must return -ENOSPC if if fails because there are no further entries
+ * available in the directory.
+ *
+ ****************************************************************************/
+
+int fat_nextdirentry(struct fat_mountpt_s *fs, struct fs_fatdir_s *dir)
+{
+ unsigned int cluster;
+ unsigned int ndx;
+
+ /* Increment the index to the next 32-byte directory entry */
+
+ ndx = dir->fd_index + 1;
+
+ /* Check if all of the directory entries in this sectory have
+ * been examined.
+ */
+
+ if ((ndx & (DIRSEC_NDIRS(fs)-1)) == 0)
+ {
+ /* Yes, then we will have to read the next sector */
+
+ dir->fd_currsector++;
+
+ /* For FAT12/16, the root directory is a group of sectors relative
+ * to the first sector of the fat volume.
+ */
+
+ if (!dir->fd_currcluster)
+ {
+ /* For FAT12/16, the boot record tells us number of 32-bit directories
+ * that are contained in the root directory. This should correspond to
+ * an even number of sectors.
+ */
+
+ if (ndx >= fs->fs_rootentcnt)
+ {
+ /* When we index past this count, we have examined all of the entries in
+ * the root directory.
+ */
+
+ return -ENOSPC;
+ }
+ }
+ else
+ {
+ /* Not a FAT12/16 root directory, check if we have examined the entire
+ * cluster comprising the directory.
+ *
+ * The current sector within the cluster is the entry number divided
+ * byte the number of entries per sector
+ */
+
+ int sector = ndx / DIRSEC_NDIRS(fs);
+
+ /* We are finished with the cluster when the last sector of the cluster
+ * has been examined.
+ */
+
+ if ((sector & (fs->fs_fatsecperclus-1)) == 0)
+ {
+ /* Get next cluster */
+
+ cluster = fat_getcluster(fs, dir->fd_currcluster);
+
+ /* Check if a valid cluster was obtained. */
+
+ if (cluster < 2 || cluster >= fs->fs_nclusters)
+ {
+ /* No, we have probably reached the end of the cluster list */
+
+ return -ENOSPC;
+ }
+
+ /* Initialize for new cluster */
+
+ dir->fd_currcluster = cluster;
+ dir->fd_currsector = fat_cluster2sector(fs, cluster);
+ }
+ }
+ }
+
+ /* Save the new index into dir->fd_currsector */
+
+ dir->fd_index = ndx;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_dirtruncate
+ *
+ * Desciption: Truncate an existing file to zero length
+ *
+ * Assumptions: The caller holds mountpoint semaphore, fs_buffer holds
+ * the directory entry, the directory entry sector (fd_sector) is
+ * currently in the sector cache.
+ *
+ ****************************************************************************/
+
+int fat_dirtruncate(struct fat_mountpt_s *fs, struct fat_dirinfo_s *dirinfo)
+{
+ unsigned int startcluster;
+ uint32_t writetime;
+ uint8_t *direntry;
+ off_t savesector;
+ int ret;
+
+ /* Get start cluster of the file to truncate */
+
+ direntry = &fs->fs_buffer[dirinfo->fd_seq.ds_offset];
+ startcluster =
+ ((uint32_t)DIR_GETFSTCLUSTHI(direntry) << 16) |
+ DIR_GETFSTCLUSTLO(direntry);
+
+ /* Clear the cluster start value in the directory and set the file size
+ * to zero. This makes the file look empty but also have to dispose of
+ * all of the clusters in the chain.
+ */
+
+ DIR_PUTFSTCLUSTHI(direntry, 0);
+ DIR_PUTFSTCLUSTLO(direntry, 0);
+ DIR_PUTFILESIZE(direntry, 0);
+
+ /* Set the ARCHIVE attribute and update the write time */
+
+ DIR_PUTATTRIBUTES(direntry, FATATTR_ARCHIVE);
+
+ writetime = fat_systime2fattime();
+ DIR_PUTWRTTIME(direntry, writetime & 0xffff);
+ DIR_PUTWRTDATE(direntry, writetime > 16);
+
+ /* This sector needs to be written back to disk eventually */
+
+ fs->fs_dirty = true;
+
+ /* Now remove the entire cluster chain comprising the file */
+
+ savesector = fs->fs_currentsector;
+ ret = fat_removechain(fs, startcluster);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Setup FSINFO to resuse this cluster next */
+
+ fs->fs_fsinextfree = startcluster - 1;
+
+ /* Make sure that the directory is still in the cache */
+
+ return fat_fscacheread(fs, savesector);
+}
+
+/****************************************************************************
+ * Name: fat_fscacheflush
+ *
+ * Desciption: Flush any dirty sector if fs_buffer as necessary
+ *
+ ****************************************************************************/
+
+int fat_fscacheflush(struct fat_mountpt_s *fs)
+{
+ int ret;
+
+ /* Check if the fs_buffer is dirty. In this case, we will write back the
+ * contents of fs_buffer.
+ */
+
+ if (fs->fs_dirty)
+ {
+ /* Write the dirty sector */
+
+ ret = fat_hwwrite(fs, fs->fs_buffer, fs->fs_currentsector, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Does the sector lie in the FAT region? */
+
+ if (fs->fs_currentsector >= fs->fs_fatbase &&
+ fs->fs_currentsector < fs->fs_fatbase + fs->fs_nfatsects)
+ {
+ /* Yes, then make the change in the FAT copy as well */
+ int i;
+
+ for (i = fs->fs_fatnumfats; i >= 2; i--)
+ {
+ fs->fs_currentsector += fs->fs_nfatsects;
+ ret = fat_hwwrite(fs, fs->fs_buffer, fs->fs_currentsector, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+ }
+
+ /* No longer dirty */
+
+ fs->fs_dirty = false;
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_fscacheread
+ *
+ * Desciption: Read the specified sector into the sector cache, flushing any
+ * existing dirty sectors as necessary.
+ *
+ ****************************************************************************/
+
+int fat_fscacheread(struct fat_mountpt_s *fs, off_t sector)
+{
+ int ret;
+
+ /* fs->fs_currentsector holds the current sector that is buffered in
+ * fs->fs_buffer. If the requested sector is the same as this sector, then
+ * we do nothing. Otherwise, we will have to read the new sector.
+ */
+
+ if (fs->fs_currentsector != sector)
+ {
+ /* We will need to read the new sector. First, flush the cached
+ * sector if it is dirty.
+ */
+
+ ret = fat_fscacheflush(fs);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Then read the specified sector into the cache */
+
+ ret = fat_hwread(fs, fs->fs_buffer, sector, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Update the cached sector number */
+
+ fs->fs_currentsector = sector;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_ffcacheflush
+ *
+ * Desciption: Flush any dirty sectors as necessary
+ *
+ ****************************************************************************/
+
+int fat_ffcacheflush(struct fat_mountpt_s *fs, struct fat_file_s *ff)
+{
+ int ret;
+
+ /* Check if the ff_buffer is dirty. In this case, we will write back the
+ * contents of ff_buffer.
+ */
+
+ if (ff->ff_cachesector &&
+ (ff->ff_bflags & (FFBUFF_DIRTY|FFBUFF_VALID)) == (FFBUFF_DIRTY|FFBUFF_VALID))
+ {
+ /* Write the dirty sector */
+
+ ret = fat_hwwrite(fs, ff->ff_buffer, ff->ff_cachesector, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* No longer dirty, but still valid */
+
+ ff->ff_bflags &= ~FFBUFF_DIRTY;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_ffcacheread
+ *
+ * Desciption: Read the specified sector into the sector cache, flushing any
+ * existing dirty sectors as necessary.
+ *
+ ****************************************************************************/
+
+int fat_ffcacheread(struct fat_mountpt_s *fs, struct fat_file_s *ff, off_t sector)
+{
+ int ret;
+
+ /* ff->ff_cachesector holds the current sector that is buffered in
+ * ff->ff_buffer. If the requested sector is the same as this sector, then
+ * we do nothing. Otherwise, we will have to read the new sector.
+ */
+
+ if (ff->ff_cachesector != sector || (ff->ff_bflags & FFBUFF_VALID) == 0)
+ {
+ /* We will need to read the new sector. First, flush the cached
+ * sector if it is dirty.
+ */
+
+ ret = fat_ffcacheflush(fs, ff);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Then read the specified sector into the cache */
+
+ ret = fat_hwread(fs, ff->ff_buffer, sector, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Update the cached sector number */
+
+ ff->ff_cachesector = sector;
+ ff->ff_bflags |= FFBUFF_VALID;
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_ffcacheread
+ *
+ * Desciption: Invalidate the current file buffer contents
+ *
+ ****************************************************************************/
+
+int fat_ffcacheinvalidate(struct fat_mountpt_s *fs, struct fat_file_s *ff)
+{
+ int ret;
+
+ /* Is there anything valid in the buffer now? */
+
+ if ((ff->ff_bflags & FFBUFF_VALID) != 0)
+ {
+ /* We will invalidate the buffered sector */
+
+ ret = fat_ffcacheflush(fs, ff);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Then discard the current cache contents */
+
+ ff->ff_bflags &= ~FFBUFF_VALID;
+ ff->ff_cachesector = 0;
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_updatefsinfo
+ *
+ * Desciption: Flush evertyhing buffered for the mountpoint and update
+ * the FSINFO sector, if appropriate
+ *
+ ****************************************************************************/
+
+int fat_updatefsinfo(struct fat_mountpt_s *fs)
+{
+ int ret;
+
+ /* Flush the fs_buffer if it is dirty */
+
+ ret = fat_fscacheflush(fs);
+ if (ret == OK)
+ {
+ /* The FSINFO sector only has to be update for the case of a FAT32 file
+ * system. Check if the file system type.. If this is a FAT32 file
+ * system then the fs_fsidirty flag will indicate if the FSINFO sector
+ * needs to be re-written.
+ */
+
+ if (fs->fs_type == FSTYPE_FAT32 && fs->fs_fsidirty)
+ {
+ /* Create an image of the FSINFO sector in the fs_buffer */
+
+ memset(fs->fs_buffer, 0, fs->fs_hwsectorsize);
+ FSI_PUTLEADSIG(fs->fs_buffer, 0x41615252);
+ FSI_PUTSTRUCTSIG(fs->fs_buffer, 0x61417272);
+ FSI_PUTFREECOUNT(fs->fs_buffer, fs->fs_fsifreecount);
+ FSI_PUTNXTFREE(fs->fs_buffer, fs->fs_fsinextfree);
+ FSI_PUTTRAILSIG(fs->fs_buffer, BOOT_SIGNATURE32);
+
+ /* Then flush this to disk */
+
+ fs->fs_currentsector = fs->fs_fsinfo;
+ fs->fs_dirty = true;
+ ret = fat_fscacheflush(fs);
+
+ /* No longer dirty */
+
+ fs->fs_fsidirty = false;
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: fat_nfreeclusters
+ *
+ * Desciption: Get the number of free clusters
+ *
+ ****************************************************************************/
+
+int fat_nfreeclusters(struct fat_mountpt_s *fs, off_t *pfreeclusters)
+{
+ uint32_t nfreeclusters;
+
+ /* If number of the first free cluster is valid, then just return that value. */
+
+ if (fs->fs_fsifreecount <= fs->fs_nclusters - 2)
+ {
+ *pfreeclusters = fs->fs_fsifreecount;
+ return OK;
+ }
+
+ /* Otherwise, we will have to count the number of free clusters */
+
+ nfreeclusters = 0;
+ if (fs->fs_type == FSTYPE_FAT12)
+ {
+ off_t sector;
+
+ /* Examine every cluster in the fat */
+
+ for (sector = 2; sector < fs->fs_nclusters; sector++)
+ {
+
+ /* If the cluster is unassigned, then increment the count of free clusters */
+
+ if ((uint16_t)fat_getcluster(fs, sector) == 0)
+ {
+ nfreeclusters++;
+ }
+ }
+ }
+ else
+ {
+ unsigned int cluster;
+ off_t fatsector;
+ unsigned int offset;
+ int ret;
+
+ fatsector = fs->fs_fatbase;
+ offset = fs->fs_hwsectorsize;
+
+ /* Examine each cluster in the fat */
+
+ for (cluster = fs->fs_nclusters; cluster > 0; cluster--)
+ {
+ /* If we are starting a new sector, then read the new sector in fs_buffer */
+
+ if (offset >= fs->fs_hwsectorsize)
+ {
+ ret = fat_fscacheread(fs, fatsector++);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Reset the offset to the next FAT entry.
+ * Increment the sector number to read next time around.
+ */
+
+ offset = 0;
+ fatsector++;
+ }
+
+ /* FAT16 and FAT32 differ only on the size of each cluster start
+ * sector number in the FAT.
+ */
+
+ if (fs->fs_type == FSTYPE_FAT16)
+ {
+ if (FAT_GETFAT16(fs->fs_buffer, offset) == 0)
+ {
+ nfreeclusters++;
+ }
+ offset += 2;
+ }
+ else
+ {
+ if (FAT_GETFAT32(fs->fs_buffer, offset) == 0)
+ {
+ nfreeclusters++;
+ }
+
+ offset += 4;
+ }
+ }
+ }
+
+ fs->fs_fsifreecount = nfreeclusters;
+ if (fs->fs_type == FSTYPE_FAT32)
+ {
+ fs->fs_fsidirty = true;
+ }
+
+ *pfreeclusters = nfreeclusters;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: fat_nfreeclusters
+ *
+ * Desciption:
+ * Given the file position, set the correct current sector to access.
+ *
+ ****************************************************************************/
+
+int fat_currentsector(struct fat_mountpt_s *fs, struct fat_file_s *ff,
+ off_t position)
+{
+ int sectoroffset;
+
+ if (position <= ff->ff_size )
+ {
+ /* sectoroffset is the sector number offset into the current cluster */
+
+ sectoroffset = SEC_NSECTORS(fs, position) & CLUS_NDXMASK(fs);
+
+ /* The current cluster is the first sector of the cluster plus
+ * the sector offset
+ */
+
+ ff->ff_currentsector = fat_cluster2sector(fs, ff->ff_currentcluster)
+ + sectoroffset;
+
+ /* The remainder is the number of sectors left in the cluster to be
+ * read/written
+ */
+
+ ff->ff_sectorsincluster = fs->fs_fatsecperclus - sectoroffset;
+
+ fvdbg("position=%d currentsector=%d sectorsincluster=%d\n",
+ position, ff->ff_currentsector, ff->ff_sectorsincluster);
+
+ return OK;
+ }
+
+ /* The position does not lie within the file */
+
+ return -ENOSPC;
+}
+
+
diff --git a/nuttx/fs/fat/fs_mkfatfs.c b/nuttx/fs/fat/fs_mkfatfs.c
new file mode 100644
index 000000000..384aa9356
--- /dev/null
+++ b/nuttx/fs/fat/fs_mkfatfs.c
@@ -0,0 +1,312 @@
+/****************************************************************************
+ * fs/fat/fs_writefat.c
+ *
+ * Copyright (C) 2008-2009 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/fat.h>
+#include <nuttx/fs/mkfatfs.h>
+
+#include "fs_internal.h"
+#include "fs_fat32.h"
+#include "fs_mkfatfs.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mkfatfs_getgeometry
+ *
+ * Description:
+ * Get the sector size and number of sectors of the underlying block
+ * device.
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Other format parameters that are not caller specifiable. (Most
+ * set by mkfatfs_configfatfs()).
+ *
+ * Return:
+ * Zero on success; negated errno on failure
+ *
+ ****************************************************************************/
+
+static inline int mkfatfs_getgeometry(FAR struct fat_format_s *fmt,
+ FAR struct fat_var_s *var)
+{
+ struct geometry geometry;
+ int ret;
+
+ /* Get the device geometry */
+
+ ret = DEV_GEOMETRY(geometry);
+ if (ret < 0)
+ {
+ fdbg("geometry() returned %d\n", ret);
+ return ret;
+ }
+
+ if (!geometry.geo_available || !geometry.geo_writeenabled)
+ {
+ fdbg("Media is not available\n", ret);
+ return -ENODEV;
+ }
+
+ /* Check if the user provided maxblocks was provided and, if so, that is it less than
+ * the actual number of blocks on the device.
+ */
+
+ if (fmt->ff_nsectors != 0)
+ {
+ if (fmt->ff_nsectors > geometry.geo_nsectors)
+ {
+ fdbg("User maxblocks (%d) exceeds blocks on device (%d)\n",
+ fmt->ff_nsectors, geometry.geo_nsectors);
+ return -EINVAL;
+ }
+ }
+ else
+ {
+ /* Use the actual number of blocks on the device */
+
+ fmt->ff_nsectors = geometry.geo_nsectors;
+ }
+
+ /* Verify that we can handle this sector size */
+
+ var->fv_sectorsize = geometry.geo_sectorsize;
+ switch (var->fv_sectorsize)
+ {
+ case 512:
+ var->fv_sectshift = 9;
+ break;
+
+ case 1024:
+ var->fv_sectshift = 10;
+ break;
+
+ case 2048:
+ var->fv_sectshift = 11;
+ break;
+
+ case 4096:
+ var->fv_sectshift = 12;
+ break;
+
+ default:
+ fdbg("Unsupported sector size: %d\n", var->fv_sectorsize);
+ return -EPERM;
+ }
+ return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mkfatfs
+ *
+ * Description:
+ * Make a FAT file system image on the specified block device
+ *
+ * Inputs:
+ * pathname - the full path to a registered block driver
+ * fmt - Describes characteristics of the desired filesystem
+ *
+ * Return:
+ * Zero (OK) on success; -1 (ERROR) on failure with errno set appropriately:
+ *
+ * EINVAL - NULL block driver string, bad number of FATS in 'fmt', bad FAT
+ * size in 'fmt', bad cluster size in 'fmt'
+ * ENOENT - 'pathname' does not refer to anything in the filesystem.
+ * ENOTBLK - 'pathname' does not refer to a block driver
+ * EACCES - block driver does not support wrie or geometry methods
+ *
+ * Assumptions:
+ * - The caller must assure that the block driver is not mounted and not in
+ * use when this function is called. The result of formatting a mounted
+ * device is indeterminate (but likely not good).
+ *
+ ****************************************************************************/
+int mkfatfs(FAR const char *pathname, FAR struct fat_format_s *fmt)
+{
+ struct fat_var_s var;
+ int ret;
+
+ /* Initialize */
+
+ memset(&var, 0, sizeof(struct fat_var_s));
+
+ /* Get the filesystem creation time */
+
+ var.fv_createtime = fat_systime2fattime();
+
+ /* Verify format options (only when DEBUG enabled) */
+
+#ifdef CONFIG_DEBUG
+ if (!pathname)
+ {
+ fdbg("No block driver path\n");
+ ret = -EINVAL;
+ goto errout;
+ }
+
+ if (fmt->ff_nfats < 1 || fmt->ff_nfats > 4)
+ {
+ fdbg("Invalid number of fats: %d\n", fmt->ff_nfats);
+ ret = -EINVAL;
+ goto errout;
+ }
+
+ if (fmt->ff_fattype != 0 && fmt->ff_fattype != 12 &&
+ fmt->ff_fattype != 16 && fmt->ff_fattype != 32)
+ {
+ fdbg("Invalid FAT size: %d\n", fmt->ff_fattype);
+ ret = -EINVAL;
+ goto errout;
+ }
+#endif
+ var.fv_fattype = fmt->ff_fattype;
+
+ /* The valid range off ff_clustshift is {0,1,..7} corresponding to
+ * cluster sizes of {1,2,..128} sectors. The special value of 0xff
+ * means that we should autoselect the cluster sizel.
+ */
+#ifdef CONFIG_DEBUG
+ if (fmt->ff_clustshift > 7 && fmt->ff_clustshift != 0xff)
+ {
+ fdbg("Invalid cluster shift value: %d\n", fmt->ff_clustshift);
+ ret = -EINVAL;
+ goto errout;
+ }
+
+ if (fmt->ff_rootdirentries != 0 && (fmt->ff_rootdirentries < 16 || fmt->ff_rootdirentries > 32767))
+ {
+ fdbg("Invalid number of root dir entries: %d\n", fmt->ff_rootdirentries);
+ ret = -EINVAL;
+ goto errout;
+ }
+
+ if (fmt->ff_rsvdseccount != 0 && (fmt->ff_rsvdseccount < 1 || fmt->ff_rsvdseccount > 32767))
+ {
+ fdbg("Invalid number of reserved sectors: %d\n", fmt->ff_rsvdseccount);
+ ret = -EINVAL;
+ goto errout;
+ }
+#endif
+
+ /* Find the inode of the block driver indentified by 'source' */
+
+ ret = open_blockdriver(pathname, 0, &var.fv_inode);
+ if (ret < 0)
+ {
+ fdbg("Failed to open %s\n", pathname);
+ goto errout;
+ }
+
+ /* Make sure that the inode supports the write and geometry methods at a minimum */
+
+ if (!var.fv_inode->u.i_bops->write || !var.fv_inode->u.i_bops->geometry)
+ {
+ fdbg("%s does not support write or geometry methods\n", pathname);
+ ret = -EACCES;
+ goto errout_with_driver;
+ }
+
+ /* Determine the volume configuration based upon the input values and upon the
+ * reported device geometry.
+ */
+
+ ret = mkfatfs_getgeometry(fmt, &var);
+ if (ret < 0)
+ {
+ goto errout_with_driver;
+ }
+
+ /* Configure the file system */
+
+ ret = mkfatfs_configfatfs(fmt, &var);
+ if (ret < 0)
+ {
+ goto errout_with_driver;
+ }
+
+ /* Allocate a buffer that will be working sector memory */
+
+ var.fv_sect = (uint8_t*)malloc(var.fv_sectorsize);
+ if (!var.fv_sect)
+ {
+ fdbg("Failed to allocate working buffers\n");
+ goto errout_with_driver;
+ }
+
+ /* Write the filesystem to media */
+
+ ret = mkfatfs_writefatfs(fmt, &var);
+
+errout_with_driver:
+ /* Close the driver */
+
+ (void)close_blockdriver(var.fv_inode);
+
+errout:
+ /* Release all allocated memory */
+
+ if (var.fv_sect)
+ {
+ free(var.fv_sect);
+ }
+
+ /* Return any reported errors */
+
+ if (ret < 0)
+ {
+ errno = -ret;
+ return ERROR;
+ }
+ return OK;
+}
diff --git a/nuttx/fs/fat/fs_mkfatfs.h b/nuttx/fs/fat/fs_mkfatfs.h
new file mode 100644
index 000000000..05801c92d
--- /dev/null
+++ b/nuttx/fs/fat/fs_mkfatfs.h
@@ -0,0 +1,170 @@
+/****************************************************************************
+ * fs/fat/fs_mkfat.h
+ *
+ * Copyright (C) 2008-2009 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_FAT_FS_MKATFS_H
+#define __FS_FAT_FS_MKATFS_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only the "hard drive" media type is used */
+
+#define FAT_DEFAULT_MEDIA_TYPE 0xf8
+
+/* Default hard driver geometry */
+
+#define FAT_DEFAULT_SECPERTRK 63
+#define FAT_DEFAULT_NUMHEADS 255
+
+/* FSINFO is always at this sector */
+
+#define FAT_DEFAULT_FSINFO_SECTOR 1
+
+/* FAT32 foot cluster number */
+
+#define FAT32_DEFAULT_ROOT_CLUSTER 2
+
+/* Macros to simplify direct block driver access */
+
+#define DEV_OPEN() \
+ var->fb_inode->u.i_bops->open ? \
+ var->fv_inode->u.i_bops->open(var->fv_inode) : \
+ 0
+#define DEV_CLOSE() \
+ var->fb_inode->u.i_bops->close ? \
+ var->fv_inode->u.i_bops->close(var->fv_inode) : \
+ 0
+#define DEV_READ(buf, sect, nsect) \
+ var->fv_inode->u.i_bops->read(var->fv_inode, buf, sect, nsect)
+#define DEV_WRITE(buf, sect, nsect) \
+ var->fv_inode->u.i_bops->write(var->fv_inode, buf, sect, nsect)
+#define DEV_GEOMETRY(geo) \
+ var->fv_inode->u.i_bops->geometry(var->fv_inode, &geo)
+#define DEV_IOCTL(cmd, arg) \
+ var->fv_inode->u.i_bops->ioctl(var->fv_inode, cmd, arg)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* This structure (plus the user-provided struct fat_format_s) describes
+ * the format FAT file system. All "global" variables used in the format
+ * logic are contained in this structure so that is possible to format two
+ * block devices concurrently.
+ */
+
+struct fat_var_s
+{
+ struct inode *fv_inode; /* The block driver "handle" */
+ uint8_t fv_jump[3]; /* 3-byte boot jump instruction */
+ uint8_t fv_sectshift; /* Log2 of fv_sectorsize */
+ uint8_t fv_nrootdirsects; /* Number of root directory sectors */
+ uint8_t fv_fattype; /* FAT size: 0 (not determined), 12, 16, or 32 */
+ uint16_t fv_bootcodesize; /* Size of array at fv_bootcode */
+ uint32_t fv_createtime; /* Creation time */
+ uint32_t fv_sectorsize; /* Size of one hardware sector */
+ uint32_t fv_nfatsects; /* Number of sectors in each FAT */
+ uint32_t fv_nclusters; /* Number of clusters */
+ uint8_t *fv_sect; /* Allocated working sector buffer */
+ const uint8_t *fv_bootcode; /* Points to boot code to put into MBR */
+};
+
+/****************************************************************************
+ * Global Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: mkfatfs_configfatfs
+ *
+ * Description:
+ * Based on the geometry of the block device and upon the caller-selected
+ * values, configure the FAT filesystem for the device.
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Holds disk geomtry data. Also, the location to return FAT
+ * configuration data
+ *
+ * Return:
+ * Zero on success; negated errno on failure
+ *
+ ****************************************************************************/
+EXTERN int mkfatfs_configfatfs(FAR struct fat_format_s *fmt,
+ FAR struct fat_var_s *var);
+
+/****************************************************************************
+ * Name: mkfatfs_writefat
+ *
+ * Description:
+ * Write the configured fat filesystem to the block device
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Other format parameters that are not caller specifiable. (Most
+ * set by mkfatfs_configfatfs()).
+ *
+ * Return:
+ * Zero on success; negated errno on failure
+ *
+ ****************************************************************************/
+EXTERN int mkfatfs_writefatfs(FAR struct fat_format_s *fmt,
+ FAR struct fat_var_s *var);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __FS_FAT_FS_MKATFS_H */
diff --git a/nuttx/fs/fat/fs_writefat.c b/nuttx/fs/fat/fs_writefat.c
new file mode 100644
index 000000000..564be5b50
--- /dev/null
+++ b/nuttx/fs/fat/fs_writefat.c
@@ -0,0 +1,544 @@
+/****************************************************************************
+ * fs/fat/fs_writefat.c
+ *
+ * Copyright (C) 2008-2009, 2011 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 <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/fat.h>
+#include <nuttx/fs/mkfatfs.h>
+
+#include "fs_internal.h"
+#include "fs_fat32.h"
+#include "fs_mkfatfs.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mkfatfs_initmbr
+ *
+ * Description:
+ * Initialize the sector image of a masterbood record
+ *
+ * Input:
+ * fmt - User specified format parameters
+ * var - Other format parameters that are not user specifiable
+ *
+ * Return:
+ * None; caller is responsible for providing valid parameters.
+ *
+ ****************************************************************************/
+static inline void mkfatfs_initmbr(FAR struct fat_format_s *fmt,
+ FAR struct fat_var_s *var)
+{
+ memset(var->fv_sect, 0, var->fv_sectorsize);
+
+ /* 3@0: Jump instruction to boot code */
+
+ memcpy(&var->fv_sect[BS_JUMP], var->fv_jump, 3);
+
+ /* 8@3: Usually "MSWIN4.1" */
+
+ strcpy((char*)&var->fv_sect[BS_OEMNAME], "NUTTX ");
+
+ /* 2@11: Bytes per sector: 512, 1024, 2048, 4096 */
+
+ MBR_PUTBYTESPERSEC(var->fv_sect, var->fv_sectorsize);
+
+ /* 1@13: Sectors per allocation unit: 2**n, n=0..7 */
+
+ MBR_PUTSECPERCLUS(var->fv_sect, (1 << fmt->ff_clustshift));
+
+ /* 2@14: Reserved sector count: Usually 32 */
+
+ MBR_PUTRESVDSECCOUNT(var->fv_sect, fmt->ff_rsvdseccount);
+
+ /* 1@16: Number of FAT data structures: always 2 */
+
+ MBR_PUTNUMFATS(var->fv_sect, fmt->ff_nfats);
+
+ /* 2@17: FAT12/16: Must be 0 for FAT32 */
+
+ MBR_PUTROOTENTCNT(var->fv_sect, fmt->ff_rootdirentries);
+
+ /* 2@19: FAT12/16: Must be 0, see BS_TOTSEC32.
+ * Handled with 4@32: Total count of sectors on the volume */
+
+ if (fmt->ff_nsectors >= 65536)
+ {
+ MBR_PUTTOTSEC32(var->fv_sect, fmt->ff_nsectors);
+ }
+ else
+ {
+ MBR_PUTTOTSEC16(var->fv_sect, (uint16_t)fmt->ff_nsectors);
+ }
+
+ /* 1@21: Media code: f0, f8, f9-fa, fc-ff */
+
+ MBR_PUTMEDIA(var->fv_sect, FAT_DEFAULT_MEDIA_TYPE); /* Only "hard drive" supported */
+
+ /* 2@22: FAT12/16: Must be 0, see BS32_FATSZ32 -- handled in FAT specific logic */
+
+ /* 2@24: Sectors per track geometry value and 2@26: Number of heads geometry value */
+
+ MBR_PUTSECPERTRK(var->fv_sect, FAT_DEFAULT_SECPERTRK);
+ MBR_PUTNUMHEADS(var->fv_sect, FAT_DEFAULT_NUMHEADS);
+
+ /* 4@28: Count of hidden sectors preceding FAT */
+
+ MBR_PUTHIDSEC(var->fv_sect, fmt->ff_hidsec);
+
+ /* 4@32: Total count of sectors on the volume -- handled above */
+
+ /* Most of the rest of the sector depends on the FAT size */
+
+ if (fmt->ff_fattype != 32)
+ {
+ /* 2@22: FAT12/16: Must be 0, see BS32_FATSZ32 */
+
+ MBR_PUTFATSZ16(var->fv_sect, (uint16_t)var->fv_nfatsects);
+
+ /* The following fields are only valid for FAT12/16 */
+ /* 1@36: Drive number for MSDOS bootstrap -- left zero */
+ /* 1@37: Reserved (zero) */
+ /* 1@38: Extended boot signature: 0x29 if following valid */
+
+ MBR_PUTBOOTSIG16(var->fv_sect, EXTBOOT_SIGNATURE);
+
+ /* 4@39: Volume serial number */
+
+ MBR_PUTVOLID16(var->fv_sect, fmt->ff_volumeid);
+
+ /* 11@43: Volume label */
+
+ memcpy(&var->fv_sect[BS16_VOLLAB], fmt->ff_volumelabel, 11);
+
+ /* 8@54: "FAT12 ", "FAT16 ", or "FAT " */
+
+ if (fmt->ff_fattype == 12)
+ {
+ memcpy(&var->fv_sect[BS16_FILESYSTYPE], "FAT12 ", 8);
+ }
+ else /* if (fmt->ff_fattype == 16) */
+ {
+ memcpy(&var->fv_sect[BS16_FILESYSTYPE], "FAT16 ", 8);
+ }
+
+ /* Boot code may be placed in the remainder of the sector */
+
+ memcpy(&var->fv_sect[BS16_BOOTCODE], var->fv_bootcode, var->fv_bootcodesize);
+ }
+ else
+ {
+ /* The following fields are only valid for FAT32 */
+ /* 4@36: Count of sectors occupied by one FAT */
+
+ MBR_PUTFATSZ32(var->fv_sect, var->fv_nfatsects);
+
+ /* 2@40: 0-3:Active FAT, 7=0 both FATS, 7=1 one FAT -- left zero*/
+ /* 2@42: MSB:Major LSB:Minor revision number (0.0) -- left zero */
+ /* 4@44: Cluster no. of 1st cluster of root dir */
+
+ MBR_PUTROOTCLUS(var->fv_sect, FAT32_DEFAULT_ROOT_CLUSTER);
+
+ /* 2@48: Sector number of fsinfo structure. Usually 1. */
+
+ MBR_PUTFSINFO(var->fv_sect, FAT_DEFAULT_FSINFO_SECTOR);
+
+ /* 2@50: Sector number of boot record. Usually 6 */
+
+ MBR_PUTBKBOOTSEC(var->fv_sect, fmt->ff_backupboot);
+
+ /* 12@52: Reserved (zero) */
+ /* 1@64: Drive number for MSDOS bootstrap -- left zero */
+ /* 1@65: Reserved (zero) */
+ /* 1@66: Extended boot signature: 0x29 if following valid */
+
+ MBR_PUTBOOTSIG32(var->fv_sect, EXTBOOT_SIGNATURE);
+
+ /* 4@67: Volume serial number */
+
+ MBR_PUTVOLID32(var->fv_sect, fmt->ff_volumeid);
+
+ /* 11@71: Volume label */
+
+ memcpy(&var->fv_sect[BS32_VOLLAB], fmt->ff_volumelabel, 11);
+
+ /* 8@82: "FAT12 ", "FAT16 ", or "FAT " */
+
+ memcpy(&var->fv_sect[BS32_FILESYSTYPE], "FAT32 ", 8);
+
+ /* Boot code may be placed in the remainder of the sector */
+
+ memcpy(&var->fv_sect[BS16_BOOTCODE], var->fv_bootcode, var->fv_bootcodesize);
+ }
+
+ /* The magic bytes at the end of the MBR are common to FAT12/16/32 */
+ /* 2@510: Valid MBRs have 0x55aa here */
+
+ MBR_PUTSIGNATURE(var->fv_sect, BOOT_SIGNATURE16);
+}
+
+/****************************************************************************
+ * Name: mkfatfs_initfsinfo
+ *
+ * Description:
+ * Initialize the FAT32 FSINFO sector image
+ *
+ * Input:
+ * fmt - User specified format parameters
+ * var - Other format parameters that are not user specifiable
+ *
+ * Return:
+ * None; caller is responsible for providing valid parameters.
+ *
+ ****************************************************************************/
+static inline void mkfatfs_initfsinfo(FAR struct fat_format_s *fmt,
+ FAR struct fat_var_s *var)
+{
+ memset(var->fv_sect, 0, var->fv_sectorsize);
+
+ /* 4@0: 0x41615252 = "RRaA" */
+
+ FSI_PUTLEADSIG(var->fv_sect, 0x41615252);
+
+ /* 480@4: Reserved (zero) */
+ /* 4@484: 0x61417272 = "rrAa" */
+
+ FSI_PUTSTRUCTSIG(var->fv_sect, 0x61417272);
+
+ /* 4@488: Last free cluster count on volume */
+
+ FSI_PUTFREECOUNT(var->fv_sect, var->fv_nclusters - 1);
+
+ /* 4@492: Cluster number of 1st free cluster */
+
+ FSI_PUTNXTFREE(var->fv_sect, FAT32_DEFAULT_ROOT_CLUSTER);
+
+ /* 12@496: Reserved (zero) */
+ /* 4@508: 0xaa550000 */
+
+ FSI_PUTTRAILSIG(var->fv_sect, BOOT_SIGNATURE32);
+}
+
+/****************************************************************************
+ * Name: mkfatfs_initrootdir
+ *
+ * Description:
+ * Initialize one root directory sector image
+ *
+ * Input:
+ * fmt - User specified format parameters
+ * var - Other format parameters that are not user specifiable
+ * sectno - On FAT32, the root directory is a cluster chain.
+ * This value indicates which sector of the cluster should be produced.
+ *
+ * Return:
+ * None; caller is responsible for providing valid parameters.
+ *
+ ****************************************************************************/
+static inline void mkfatfs_initrootdir(FAR struct fat_format_s *fmt,
+ FAR struct fat_var_s *var, int sectno)
+{
+ memset(var->fv_sect, 0, var->fv_sectorsize);
+ if (sectno == 0)
+ {
+ /* It is only necessary to set data in the first sector of the directory */
+
+ if (memcmp(fmt->ff_volumelabel, " ", 11))
+ {
+ memcpy(&var->fv_sect[DIR_NAME], fmt->ff_volumelabel, 11);
+ }
+
+ DIR_PUTATTRIBUTES(var->fv_sect, FATATTR_VOLUMEID);
+ DIR_PUTCRTIME(var->fv_sect, var->fv_createtime & 0xffff);
+ DIR_PUTWRTTIME(var->fv_sect, var->fv_createtime & 0xffff);
+ DIR_PUTCRDATE(var->fv_sect, var->fv_createtime >> 16);
+ DIR_PUTWRTDATE(var->fv_sect, var->fv_createtime >> 16);
+ }
+}
+
+/****************************************************************************
+ * Name: mkfatfs_writembr
+ *
+ * Description:
+ * Write the master boot record and, for FAT32, the backup boot record and
+ * the fsinfo sector.
+ *
+ * Input:
+ * fmt - User specified format parameters
+ * var - Other format parameters that are not user specifiable
+ *
+ * Return:
+ * Zero on success; negated errno on failure
+ *
+ ****************************************************************************/
+
+static inline int mkfatfs_writembr(FAR struct fat_format_s *fmt,
+ FAR struct fat_var_s *var)
+{
+ int sectno;
+ int ret;
+
+ /* Create an image of the configured master boot record */
+
+ mkfatfs_initmbr(fmt, var);
+
+ /* Write the master boot record as sector zero */
+
+ ret = DEV_WRITE(var->fv_sect, 0, 1);
+
+ /* Write all of the reserved sectors */
+
+ memset(var->fv_sect, 0, var->fv_sectorsize);
+ for (sectno = 1; sectno < fmt->ff_rsvdseccount && ret >= 0; sectno++)
+ {
+ ret = DEV_WRITE(var->fv_sect, sectno, 1);
+ }
+
+ /* Write FAT32-specific sectors */
+
+ if (ret >= 0 && fmt->ff_fattype == 32)
+ {
+ /* Write the backup master boot record */
+
+ if (fmt->ff_backupboot != 0)
+ {
+ /* Create another copy of the configured master boot record */
+
+ mkfatfs_initmbr(fmt, var);
+
+ /* Write it to the backup location */
+
+ ret = DEV_WRITE(var->fv_sect, fmt->ff_backupboot, 1);
+ }
+
+ if (ret >= 0)
+ {
+ /* Create an image of the fsinfo sector*/
+
+ mkfatfs_initfsinfo(fmt, var);
+
+ /* Write the fsinfo sector */
+
+ ret = DEV_WRITE(var->fv_sect, FAT_DEFAULT_FSINFO_SECTOR, 1);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: mkfatfs_writefat
+ *
+ * Description:
+ * Write the FAT sectors
+ *
+ * Input:
+ * fmt - User specified format parameters
+ * var - Other format parameters that are not user specifiable
+ *
+ * Return:
+ * Zero on success; negated errno on failure
+ *
+ ****************************************************************************/
+
+static inline int mkfatfs_writefat(FAR struct fat_format_s *fmt,
+ FAR struct fat_var_s *var)
+{
+ off_t offset = fmt->ff_rsvdseccount;
+ int fatno;
+ int sectno;
+ int ret;
+
+ /* Loop for each FAT copy */
+
+ for (fatno = 0; fatno < fmt->ff_nfats; fatno++)
+ {
+ /* Loop for each sector in the FAT */
+
+ for (sectno = 0; sectno < var->fv_nfatsects; sectno++)
+ {
+ memset(var->fv_sect, 0, var->fv_sectorsize);
+
+ /* Mark cluster allocations in sector one of each FAT */
+
+ if (sectno == 0)
+ {
+ memset(var->fv_sect, 0, var->fv_sectorsize);
+ switch(fmt->ff_fattype)
+ {
+ case 12:
+ /* Mark the first two full FAT entries -- 24 bits, 3 bytes total */
+
+ memset(var->fv_sect, 0xff, 3);
+ break;
+
+ case 16:
+ /* Mark the first two full FAT entries -- 32 bits, 4 bytes total */
+
+ memset(var->fv_sect, 0xff, 4);
+ break;
+
+ case 32:
+ default: /* Shouldn't happen */
+ /* Mark the first two full FAT entries -- 64 bits, 8 bytes total */
+
+ memset(var->fv_sect, 0xff, 8);
+
+ /* Cluster 2 is used as the root directory. Mark as EOF */
+
+ var->fv_sect[8] = 0xf8;
+ memset(&var->fv_sect[9], 0xff, 3);
+ break;
+ }
+
+ /* Save the media type in the first byte of the FAT */
+
+ var->fv_sect[0] = FAT_DEFAULT_MEDIA_TYPE;
+ }
+
+ /* Write the FAT sector */
+
+ ret = DEV_WRITE(var->fv_sect, offset, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ offset++;
+ }
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: mkfatfs_writerootdir
+ *
+ * Description:
+ * Write the root directory sectors
+ *
+ * Input:
+ * fmt - User specified format parameters
+ * var - Other format parameters that are not user specifiable
+ *
+ * Return:
+ * Zero on success; negated errno on failure
+ *
+ ****************************************************************************/
+
+static inline int mkfatfs_writerootdir(FAR struct fat_format_s *fmt,
+ FAR struct fat_var_s *var)
+{
+ off_t offset = fmt->ff_rsvdseccount + fmt->ff_nfats * var->fv_nfatsects;
+ int ret;
+ int i;
+
+ /* Write the root directory after the last FAT. This is the root directory
+ * area for FAT12/16, and the first cluster on FAT32.
+ */
+
+ for (i = 0; i < var->fv_nrootdirsects; i++)
+ {
+ /* Format the next sector of the root directory */
+
+ mkfatfs_initrootdir(fmt, var, i);
+
+ /* Write the next sector of the root directory */
+
+ ret = DEV_WRITE(var->fv_sect, offset, 1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ offset++;
+ }
+ return 0;
+}
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mkfatfs_writefat
+ *
+ * Description:
+ * Write the configured fat filesystem to the block device
+ *
+ * Input:
+ * fmt - Caller specified format parameters
+ * var - Other format parameters that are not caller specifiable. (Most
+ * set by mkfatfs_configfatfs()).
+ *
+ * Return:
+ * Zero on success; negated errno on failure
+ *
+ ****************************************************************************/
+
+int mkfatfs_writefatfs(FAR struct fat_format_s *fmt,
+ FAR struct fat_var_s *var)
+{
+ int ret;
+
+ /* Write the master boot record (also the backup and fsinfo sectors) */
+
+ ret = mkfatfs_writembr(fmt, var);
+
+ /* Write FATs */
+
+ if (ret >= 0)
+ {
+ ret = mkfatfs_writefat(fmt, var);
+ }
+
+ /* Write the root directory after the last FAT. */
+
+ if (ret >= 0)
+ {
+ ret = mkfatfs_writerootdir(fmt, var);
+ }
+ return ret;
+}
+
diff --git a/nuttx/fs/fs_close.c b/nuttx/fs/fs_close.c
new file mode 100644
index 000000000..7f08995de
--- /dev/null
+++ b/nuttx/fs/fs_close.c
@@ -0,0 +1,133 @@
+/****************************************************************************
+ * fs/fs_close.c
+ *
+ * Copyright (C) 2007-2009, 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 <unistd.h>
+#include <sched.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+
+#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0
+# include <nuttx/net/net.h>
+#endif
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: close
+ *
+ * Description:
+ * close() closes a file descriptor, so that it no longer refers to any
+ * file and may be reused. Any record locks (see fcntl(2)) held on the file
+ * it was associated with, and owned by the process, are removed (regardless
+ * of the file descriptor that was used to obtain the lock).
+ *
+ * If fd is the last copy of a particular file descriptor the resources
+ * associated with it are freed; if the descriptor was the last reference
+ * to a file which has been removed using unlink(2) the file is deleted.
+ *
+ * Parameters:
+ * fd file descriptor to close
+ *
+ * Returned Value:
+ * 0 on success; -1 on error with errno set appropriately.
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+int close(int fd)
+{
+ int err;
+#if CONFIG_NFILE_DESCRIPTORS > 0
+ int ret;
+
+ /* Did we get a valid file descriptor? */
+
+ if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS)
+#endif
+ {
+ /* Close a socket descriptor */
+
+#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0
+ if ((unsigned int)fd < (CONFIG_NFILE_DESCRIPTORS+CONFIG_NSOCKET_DESCRIPTORS))
+ {
+ return net_close(fd);
+ }
+ else
+#endif
+ {
+ err = EBADF;
+ goto errout;
+ }
+ }
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+ /* Close the driver or mountpoint. NOTES: (1) there is no
+ * exclusion mechanism here , the driver or mountpoint must be
+ * able to handle concurrent operations internally, (2) The driver
+ * may have been opened numerous times (for different file
+ * descriptors) and must also handle being closed numerous times.
+ * (3) for the case of the mountpoint, we depend on the close
+ * methods bing identical in signature and position in the operations
+ * vtable.
+ */
+
+ ret = files_close(fd);
+ if (ret < 0)
+ {
+ /* An error occurred while closing the driver */
+
+ err = -ret;
+ goto errout;
+ }
+ return OK;
+
+#endif
+
+errout:
+ set_errno(err);
+ return ERROR;
+}
+
diff --git a/nuttx/fs/fs_closeblockdriver.c b/nuttx/fs/fs_closeblockdriver.c
new file mode 100644
index 000000000..2151b04df
--- /dev/null
+++ b/nuttx/fs/fs_closeblockdriver.c
@@ -0,0 +1,112 @@
+/****************************************************************************
+ * fs/fs_closeblockdriver.c
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in pathname and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of pathname 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 <debug.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: close_blockdriver
+ *
+ * Description:
+ * Call the close method and release the inode
+ *
+ * Inputs:
+ * inode - reference to the inode of a block driver opened by open_blockdriver
+ *
+ * Return:
+ * Returns zero on success or a negated errno on failure:
+ *
+ * EINVAL - inode is NULL
+ * ENOTBLK - The inode is not a block driver
+ *
+ ****************************************************************************/
+
+int close_blockdriver(FAR struct inode *inode)
+{
+ int ret = 0; /* Assume success */
+
+ /* Sanity checks */
+
+#ifdef CONFIG_DEBUG
+ if (!inode || !inode->u.i_bops)
+ {
+ ret = -EINVAL;
+ goto errout;
+ }
+#endif
+
+ /* Verify that the inode is a block driver. */
+
+ if (!INODE_IS_BLOCK(inode))
+ {
+ fdbg("inode is not a block driver\n");
+ ret = -ENOTBLK;
+ goto errout;
+ }
+
+ /* Close the block driver. Not that no mutually exclusive access
+ * to the driver is enforced here. That must be done in the driver
+ * if needed.
+ */
+
+ if (inode->u.i_bops->close)
+ {
+ ret = inode->u.i_bops->close(inode);
+ }
+
+ /* Then release the reference on the inode */
+
+ inode_release(inode);
+
+errout:
+ return ret;
+}
diff --git a/nuttx/fs/fs_closedir.c b/nuttx/fs/fs_closedir.c
new file mode 100644
index 000000000..ba4f12961
--- /dev/null
+++ b/nuttx/fs/fs_closedir.c
@@ -0,0 +1,148 @@
+/****************************************************************************
+ * fs/fs_closedir.c
+ *
+ * Copyright (C) 2007-2009, 2011 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 <dirent.h>
+#include <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/dirent.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: closedir
+ *
+ * Description:
+ * The closedir() function closes the directory stream associated with
+ * 'dirp'. The directory stream descriptor 'dirp' is not available after
+ * this call.
+ *
+ * Inputs:
+ * dirp -- An instance of type DIR created by a previous call to opendir();
+ *
+ * Return:
+ * The closedir() function returns 0 on success. On error, -1 is
+ * returned, and errno is set appropriately.
+ *
+ ****************************************************************************/
+
+int closedir(FAR DIR *dirp)
+{
+ struct fs_dirent_s *idir = (struct fs_dirent_s *)dirp;
+ struct inode *inode;
+ int ret;
+
+ if (!idir || !idir->fd_root)
+ {
+ ret = EBADF;
+ goto errout;
+ }
+
+ /* This is the 'root' inode of the directory. This means different
+ * things wih different filesystems.
+ */
+
+ inode = idir->fd_root;
+
+ /* The way that we handle the close operation depends on what kind of root
+ * inode we have open.
+ */
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ if (INODE_IS_MOUNTPT(inode) && !DIRENT_ISPSEUDONODE(idir->fd_flags))
+ {
+ /* The node is a file system mointpoint. Verify that the mountpoint
+ * supports the closedir() method (not an error if it does not)
+ */
+
+ if (inode->u.i_mops && inode->u.i_mops->closedir)
+ {
+ /* Perform the closedir() operation */
+
+ ret = inode->u.i_mops->closedir(inode, idir);
+ if (ret < 0)
+ {
+ ret = -ret;
+ goto errout_with_inode;
+ }
+ }
+ }
+ else
+#endif
+ {
+ /* The node is part of the root pseudo file system, release
+ * our contained reference to the 'next' inode.
+ */
+
+ if (idir->u.pseudo.fd_next)
+ {
+ inode_release(idir->u.pseudo.fd_next);
+ }
+ }
+
+ /* Release our references on the contained 'root' inode */
+
+ inode_release(idir->fd_root);
+
+ /* Then release the container */
+
+ kfree(idir);
+ return OK;
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+errout_with_inode:
+ inode_release(inode);
+ kfree(idir);
+#endif
+
+errout:
+ set_errno(ret);
+ return ERROR;
+}
diff --git a/nuttx/fs/fs_dup.c b/nuttx/fs/fs_dup.c
new file mode 100644
index 000000000..890da039b
--- /dev/null
+++ b/nuttx/fs/fs_dup.c
@@ -0,0 +1,107 @@
+/****************************************************************************
+ * fs/fs_dup.c
+ *
+ * Copyright (C) 2007-2009 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 <unistd.h>
+#include <sched.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: dup
+ *
+ * Description:
+ * Clone a file or socket descriptor to an arbitray descriptor number
+ *
+ ****************************************************************************/
+
+int dup(int fildes)
+{
+ int ret = OK;
+
+ /* Check the range of the descriptor to see if we got a file or a socket
+ * descriptor. */
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+ if ((unsigned int)fildes < CONFIG_NFILE_DESCRIPTORS)
+ {
+ /* Its a valid file descriptor.. dup the file descriptor using any
+ * other file descriptor*/
+
+ ret = file_dup(fildes, 0);
+ }
+ else
+#endif
+ {
+ /* Not a vailid file descriptor. Did we get a valid socket descriptor? */
+
+#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0
+ if ((unsigned int)fildes < (CONFIG_NFILE_DESCRIPTORS+CONFIG_NSOCKET_DESCRIPTORS))
+ {
+ /* Yes.. dup the socket descriptor */
+
+ ret = net_dup(fildes, CONFIG_NFILE_DESCRIPTORS);
+ }
+ else
+#endif
+ {
+ /* No.. then it is a bad descriptor number */
+
+ set_errno(EBADF);
+ ret = ERROR;
+ }
+ }
+
+ return ret;
+}
diff --git a/nuttx/fs/fs_dup2.c b/nuttx/fs/fs_dup2.c
new file mode 100644
index 000000000..45ff2bb73
--- /dev/null
+++ b/nuttx/fs/fs_dup2.c
@@ -0,0 +1,109 @@
+/****************************************************************************
+ * fs/fs_dup2.c
+ *
+ * Copyright (C) 2007-2009, 2011 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 <unistd.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "fs_internal.h"
+
+/* This logic in this applies only when both socket and file descriptors are
+ * in that case, this function descriminates which type of dup2 is being
+ * performed.
+ */
+
+#if CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: dup2
+ *
+ * Description:
+ * Clone a file descriptor or socket descriptor to a specific descriptor
+ * number
+ *
+ ****************************************************************************/
+
+int dup2(int fildes1, int fildes2)
+{
+ /* Check the range of the descriptor to see if we got a file or a socket
+ * descriptor.
+ */
+
+ if ((unsigned int)fildes1 >= CONFIG_NFILE_DESCRIPTORS)
+ {
+ /* Not a valid file descriptor. Did we get a valid socket descriptor? */
+
+ if ((unsigned int)fildes1 < (CONFIG_NFILE_DESCRIPTORS+CONFIG_NSOCKET_DESCRIPTORS))
+ {
+ /* Yes.. dup the socket descriptor */
+
+ return net_dup2(fildes1, fildes2);
+ }
+ else
+ {
+ /* No.. then it is a bad descriptor number */
+
+ set_errno(EBADF);
+ return ERROR;
+ }
+ }
+ else
+ {
+ /* Its a valid file descriptor.. dup the file descriptor */
+
+ return file_dup2(fildes1, fildes2);
+ }
+}
+
+#endif /* CONFIG_NFILE_DESCRIPTORS > 0 ... */
+
diff --git a/nuttx/fs/fs_fcntl.c b/nuttx/fs/fs_fcntl.c
new file mode 100644
index 000000000..ff53b0f20
--- /dev/null
+++ b/nuttx/fs/fs_fcntl.c
@@ -0,0 +1,264 @@
+/****************************************************************************
+ * fs/fs_fcntl.c
+ *
+ * Copyright (C) 2009, 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 <stdarg.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/net/net.h>
+#include <nuttx/sched.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: file_vfcntl
+ ****************************************************************************/
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+static inline int file_vfcntl(int fildes, int cmd, va_list ap)
+{
+ FAR struct filelist *list;
+ FAR struct file *this_file;
+ int err = 0;
+ int ret = OK;
+
+ /* Get the thread-specific file list */
+
+ list = sched_getfiles();
+ if (!list)
+ {
+ err = EMFILE;
+ goto errout;
+ }
+
+ /* Was this file opened ? */
+
+ this_file = &list->fl_files[fildes];
+ if (!this_file->f_inode)
+ {
+ err = EBADF;
+ goto errout;
+ }
+
+ switch (cmd)
+ {
+ case F_DUPFD:
+ /* Return a new file descriptor which shall be the lowest numbered
+ * available (that is, not already open) file descriptor greater than
+ * or equal to the third argument, arg, taken as an integer of type
+ * int. The new file descriptor shall refer to the same open file
+ * description as the original file descriptor, and shall share any
+ * locks. The FD_CLOEXEC flag associated with the new file descriptor
+ * shall be cleared to keep the file open across calls to one of the
+ * exec functions.
+ */
+
+ {
+ ret = file_dup(fildes, va_arg(ap, int));
+ }
+ break;
+
+ case F_GETFD:
+ /* Get the file descriptor flags defined in <fcntl.h> that are associated
+ * with the file descriptor fildes. File descriptor flags are associated
+ * with a single file descriptor and do not affect other file descriptors
+ * that refer to the same file.
+ */
+
+ case F_SETFD:
+ /* Set the file descriptor flags defined in <fcntl.h>, that are associated
+ * with fildes, to the third argument, arg, taken as type int. If the
+ * FD_CLOEXEC flag in the third argument is 0, the file shall remain open
+ * across the exec functions; otherwise, the file shall be closed upon
+ * successful execution of one of the exec functions.
+ */
+
+ err = ENOSYS;
+ break;
+
+ case F_GETFL:
+ /* Get the file status flags and file access modes, defined in <fcntl.h>,
+ * for the file description associated with fildes. The file access modes
+ * can be extracted from the return value using the mask O_ACCMODE, which is
+ * defined in <fcntl.h>. File status flags and file access modes are associated
+ * with the file description and do not affect other file descriptors that
+ * refer to the same file with different open file descriptions.
+ */
+
+ {
+ ret = this_file->f_oflags;
+ }
+ break;
+
+ case F_SETFL:
+ /* Set the file status flags, defined in <fcntl.h>, for the file description
+ * associated with fildes from the corresponding bits in the third argument,
+ * arg, taken as type int. Bits corresponding to the file access mode and
+ * the file creation flags, as defined in <fcntl.h>, that are set in arg shall
+ * be ignored. If any bits in arg other than those mentioned here are changed
+ * by the application, the result is unspecified.
+ */
+
+ {
+ this_file->f_oflags = va_arg(ap, int);
+ }
+ break;
+
+ case F_GETOWN:
+ /* If fildes refers to a socket, get the process or process group ID specified
+ * to receive SIGURG signals when out-of-band data is available. Positive values
+ * indicate a process ID; negative values, other than -1, indicate a process group
+ * ID. If fildes does not refer to a socket, the results are unspecified.
+ */
+
+ case F_SETOWN:
+ /* If fildes refers to a socket, set the process or process group ID specified
+ * to receive SIGURG signals when out-of-band data is available, using the value
+ * of the third argument, arg, taken as type int. Positive values indicate a
+ * process ID; negative values, other than -1, indicate a process group ID. If
+ * fildes does not refer to a socket, the results are unspecified.
+ */
+
+ err = EBADF; /* Only valid on socket descriptors */
+ break;
+
+ case F_GETLK:
+ /* Get the first lock which blocks the lock description pointed to by the third
+ * argument, arg, taken as a pointer to type struct flock, defined in <fcntl.h>.
+ * The information retrieved shall overwrite the information passed to fcntl() in
+ * the structure flock. If no lock is found that would prevent this lock from being
+ * created, then the structure shall be left unchanged except for the lock type
+ * which shall be set to F_UNLCK.
+ */
+
+ case F_SETLK:
+ /* Set or clear a file segment lock according to the lock description pointed to
+ * by the third argument, arg, taken as a pointer to type struct flock, defined in
+ * <fcntl.h>. F_SETLK can establish shared (or read) locks (F_RDLCK) or exclusive
+ * (or write) locks (F_WRLCK), as well as to remove either type of lock (F_UNLCK).
+ * F_RDLCK, F_WRLCK, and F_UNLCK are defined in <fcntl.h>. If a shared or exclusive
+ * lock cannot be set, fcntl() shall return immediately with a return value of -1.
+ */
+
+ case F_SETLKW:
+ /* This command shall be equivalent to F_SETLK except that if a shared or exclusive
+ * lock is blocked by other locks, the thread shall wait until the request can be
+ * satisfied. If a signal that is to be caught is received while fcntl() is waiting
+ * for a region, fcntl() shall be interrupted. Upon return from the signal handler,
+ * fcntl() shall return -1 with errno set to [EINTR], and the lock operation shall
+ * not be done.
+ */
+
+ err = ENOSYS; /* Not implemented */
+ break;
+
+ default:
+ err = EINVAL;
+ break;
+ }
+
+errout:
+ if (err != 0)
+ {
+ set_errno(err);
+ return ERROR;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_NFILE_DESCRIPTORS > 0 */
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fcntl
+ ****************************************************************************/
+
+int fcntl(int fildes, int cmd, ...)
+{
+ va_list ap;
+ int ret;
+
+ /* Setup to access the variable argument list */
+
+ va_start(ap, cmd);
+
+ /* Did we get a valid file descriptor? */
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+ if ((unsigned int)fildes < CONFIG_NFILE_DESCRIPTORS)
+ {
+ /* Yes.. defer file operations to file_vfcntl() */
+
+ ret = file_vfcntl(fildes, cmd, ap);
+ }
+ else
+#endif
+ {
+ /* No... check for operations on a socket descriptor */
+
+#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0
+ if ((unsigned int)fildes < (CONFIG_NFILE_DESCRIPTORS+CONFIG_NSOCKET_DESCRIPTORS))
+ {
+ /* Yes.. defer socket descriptor operations to net_vfcntl() */
+
+ ret = net_vfcntl(fildes, cmd, ap);
+ }
+ else
+#endif
+ {
+ /* No.. this descriptor number is out of range */
+
+ ret = EBADF;
+ }
+ }
+
+ va_end(ap);
+ return ret;
+}
+
diff --git a/nuttx/fs/fs_fdopen.c b/nuttx/fs/fs_fdopen.c
new file mode 100644
index 000000000..fd6aa88a8
--- /dev/null
+++ b/nuttx/fs/fs_fdopen.c
@@ -0,0 +1,261 @@
+/****************************************************************************
+ * fs/fs_fdopen.c
+ *
+ * Copyright (C) 2007-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 <stdio.h>
+#include <string.h>
+#include <semaphore.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/net/net.h>
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fs_checkfd
+ *
+ * Description:
+ * Check if the file descriptor is valid for the provided TCB and if it
+ * supports the requested access.
+ *
+ ****************************************************************************/
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+static inline int fs_checkfd(FAR _TCB *tcb, int fd, int oflags)
+{
+ FAR struct filelist *flist;
+ FAR struct inode *inode;
+
+ /* Get the file list from the TCB */
+
+ flist = tcb->filelist;
+
+ /* Get the inode associated with the file descriptor. This should
+ * normally be the case if fd >= 0. But not in the case where the
+ * called attempts to explictly stdin with fdopen(0) but stdin has
+ * been closed.
+ */
+
+ inode = flist->fl_files[fd].f_inode;
+ if (!inode)
+ {
+ /* No inode -- descriptor does not correspond to an open file */
+
+ return -ENOENT;
+ }
+
+ /* Make sure that the inode supports the requested access. In
+ * the case of fdopen, we are not actually creating the file -- in
+ * particular w and w+ do not truncate the file and any files have
+ * already been created.
+ */
+
+ if (inode_checkflags(inode, oflags) != OK)
+ {
+ /* Cannot support the requested access */
+
+ return -EACCES;
+ }
+
+ /* Looks good to me */
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fs_fdopen
+ *
+ * Description:
+ * This function does the core operations for fopen and fdopen.
+ *
+ ****************************************************************************/
+
+FAR struct file_struct *fs_fdopen(int fd, int oflags, FAR _TCB *tcb)
+{
+ FAR struct streamlist *slist;
+ FAR FILE *stream;
+ int err = OK;
+ int ret;
+ int i;
+
+ /* Check input parameters */
+
+ if (fd < 0)
+ {
+ err = EBADF;
+ goto errout;
+ }
+
+ /* A NULL TCB pointer means to use this threads TCB. This is a little
+ * hack the let's this function be called from user-space (via a syscall)
+ * without having access to the TCB.
+ */
+
+ if (!tcb)
+ {
+ tcb = sched_self();
+ }
+
+ /* Verify that this is a valid file/socket descriptor and that the
+ * requested access can be support.
+ *
+ * Is this fd in the range of valid file descriptors? Socket descriptors
+ * lie in a different range.
+ */
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+ if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS)
+#endif
+ {
+ /* No.. If networking is enabled then this might be a socket
+ * descriptor.
+ */
+
+#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0
+ ret = net_checksd(fd, oflags);
+#else
+ /* No networking... it is just a bad descriptor */
+
+ err = EBADF;
+ goto errout;
+#endif
+ }
+
+ /* The descriptor is in a valid range to file descriptor... perform some more checks */
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+ else
+ {
+ ret = fs_checkfd(tcb, fd, oflags);
+ }
+#endif
+
+ /* Do we have a good descriptor of some sort? */
+
+ if (ret < 0)
+ {
+ /* No... return the reported error */
+
+ err = -ret;
+ goto errout;
+ }
+
+ /* Get the stream list from the TCB */
+
+ slist = tcb->streams;
+
+ /* Find an unallocated FILE structure in the stream list */
+
+ ret = sem_wait(&slist->sl_sem);
+ if (ret != OK)
+ {
+ goto errout_with_errno;
+ }
+
+ for (i = 0 ; i < CONFIG_NFILE_STREAMS; i++)
+ {
+ stream = &slist->sl_streams[i];
+ if (stream->fs_filedes < 0)
+ {
+ /* Zero the structure */
+
+#if CONFIG_STDIO_BUFFER_SIZE > 0
+ memset(stream, 0, sizeof(FILE));
+#elif CONFIG_NUNGET_CHARS > 0
+ stream->fs_nungotten = 0;
+#endif
+
+#if CONFIG_STDIO_BUFFER_SIZE > 0
+ /* Initialize the semaphore the manages access to the buffer */
+
+ (void)sem_init(&stream->fs_sem, 0, 1);
+
+ /* Allocate the IO buffer */
+
+ stream->fs_bufstart = kmalloc(CONFIG_STDIO_BUFFER_SIZE);
+ if (!stream->fs_bufstart)
+ {
+ err = ENOMEM;
+ goto errout_with_sem;
+ }
+
+ /* Set up pointers */
+
+ stream->fs_bufend = &stream->fs_bufstart[CONFIG_STDIO_BUFFER_SIZE];
+ stream->fs_bufpos = stream->fs_bufstart;
+ stream->fs_bufpos = stream->fs_bufstart;
+ stream->fs_bufread = stream->fs_bufstart;
+#endif
+ /* Save the file description and open flags. Setting the
+ * file descriptor locks this stream.
+ */
+
+ stream->fs_filedes = fd;
+ stream->fs_oflags = (uint16_t)oflags;
+
+ sem_post(&slist->sl_sem);
+ return stream;
+ }
+ }
+
+ /* No free stream available.. report ENFILE */
+
+ err = ENFILE;
+
+#if CONFIG_STDIO_BUFFER_SIZE > 0
+errout_with_sem:
+#endif
+ sem_post(&slist->sl_sem);
+
+errout:
+ set_errno(err);
+errout_with_errno:
+ return NULL;
+}
diff --git a/nuttx/fs/fs_filedup.c b/nuttx/fs/fs_filedup.c
new file mode 100644
index 000000000..b7eeb7add
--- /dev/null
+++ b/nuttx/fs/fs_filedup.c
@@ -0,0 +1,122 @@
+/****************************************************************************
+ * fs/fs_filedup.c
+ *
+ * Copyright (C) 2007-2009, 2011-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 <sched.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DUP_ISOPEN(fd, list) \
+ ((unsigned int)fd < CONFIG_NFILE_DESCRIPTORS && \
+ list->fl_files[fd].f_inode != NULL)
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: file_dup OR dup
+ *
+ * Description:
+ * Clone a file descriptor 'fd' to an arbitray descriptor number (any value
+ * greater than or equal to 'minfd'). If socket descriptors are
+ * implemented, then this is called by dup() for the case of file
+ * descriptors. If socket descriptors are not implemented, then this
+ * function IS dup().
+ *
+ ****************************************************************************/
+
+int file_dup(int fildes, int minfd)
+{
+ FAR struct filelist *list;
+ int fildes2;
+
+ /* Get the thread-specific file list */
+
+ list = sched_getfiles();
+ if (!list)
+ {
+ set_errno(EMFILE);
+ return ERROR;
+ }
+
+ /* Verify that fildes is a valid, open file descriptor */
+
+ if (!DUP_ISOPEN(fildes, list))
+ {
+ set_errno(EBADF);
+ return ERROR;
+ }
+
+ /* Increment the reference count on the contained inode */
+
+ inode_addref(list->fl_files[fildes].f_inode);
+
+ /* Then allocate a new file descriptor for the inode */
+
+ fildes2 = files_allocate(list->fl_files[fildes].f_inode,
+ list->fl_files[fildes].f_oflags,
+ list->fl_files[fildes].f_pos,
+ minfd);
+ if (fildes2 < 0)
+ {
+ set_errno(EMFILE);
+ inode_release(list->fl_files[fildes].f_inode);
+ return ERROR;
+ }
+
+ return fildes2;
+}
+
+#endif /* CONFIG_NFILE_DESCRIPTORS > 0 */
+
diff --git a/nuttx/fs/fs_filedup2.c b/nuttx/fs/fs_filedup2.c
new file mode 100644
index 000000000..d064aa08b
--- /dev/null
+++ b/nuttx/fs/fs_filedup2.c
@@ -0,0 +1,121 @@
+/****************************************************************************
+ * fs/fs_filedup2.c
+ *
+ * Copyright (C) 2007-2009, 2011-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 <unistd.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "fs_internal.h"
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DUP_ISOPEN(fd, list) \
+ ((unsigned int)fd < CONFIG_NFILE_DESCRIPTORS && \
+ list->fl_files[fd].f_inode != NULL)
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: file_dup2 OR dup2
+ *
+ * Description:
+ * Clone a file descriptor to a specific descriptor number. If socket
+ * descriptors are implemented, then this is called by dup2() for the
+ * case of file descriptors. If socket descriptors are not implemented,
+ * then this function IS dup2().
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0
+int file_dup2(int fildes1, int fildes2)
+#else
+int dup2(int fildes1, int fildes2)
+#endif
+{
+ FAR struct filelist *list;
+
+ /* Get the thread-specific file list */
+
+ list = sched_getfiles();
+ if (!list)
+ {
+ set_errno(EMFILE);
+ return ERROR;
+ }
+
+ /* Verify that fildes is a valid, open file descriptor */
+
+ if (!DUP_ISOPEN(fildes1, list))
+ {
+ set_errno(EBADF);
+ return ERROR;
+ }
+
+ /* Handle a special case */
+
+ if (fildes1 == fildes2)
+ {
+ return fildes1;
+ }
+
+ /* Verify fildes2 */
+
+ if ((unsigned int)fildes2 >= CONFIG_NFILE_DESCRIPTORS)
+ {
+ set_errno(EBADF);
+ return ERROR;
+ }
+
+ return files_dup(&list->fl_files[fildes1], &list->fl_files[fildes2]);
+}
+
+#endif /* CONFIG_NFILE_DESCRIPTORS > 0 */
+
diff --git a/nuttx/fs/fs_files.c b/nuttx/fs/fs_files.c
new file mode 100644
index 000000000..425e7c73f
--- /dev/null
+++ b/nuttx/fs/fs_files.c
@@ -0,0 +1,479 @@
+/****************************************************************************
+ * fs/fs_files.c
+ *
+ * Copyright (C) 2007-2009, 2011-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 <string.h>
+#include <semaphore.h>
+#include <assert.h>
+#include <sched.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/kmalloc.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: _files_semtake
+ ****************************************************************************/
+
+static void _files_semtake(FAR struct filelist *list)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&list->fl_sem) != 0)
+ {
+ /* The only case that an error should occr here is if
+ * the wait was awakened by a signal.
+ */
+
+ ASSERT(get_errno() == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Name: _files_semgive
+ ****************************************************************************/
+
+#define _files_semgive(list) sem_post(&list->fl_sem)
+
+/****************************************************************************
+ * Name: _files_close
+ *
+ * Description:
+ * Close an inode (if open)
+ *
+ * Assumuptions:
+ * Caller holds the list semaphore because the file descriptor will be freed.
+ *
+ ****************************************************************************/
+
+static int _files_close(FAR struct file *filep)
+{
+ struct inode *inode = filep->f_inode;
+ int ret = OK;
+
+ /* Check if the struct file is open (i.e., assigned an inode) */
+
+ if (inode)
+ {
+ /* Close the file, driver, or mountpoint. */
+
+ if (inode->u.i_ops && inode->u.i_ops->close)
+ {
+ /* Perform the close operation */
+
+ ret = inode->u.i_ops->close(filep);
+ }
+
+ /* And release the inode */
+
+ inode_release(inode);
+
+ /* Release the file descriptor */
+
+ filep->f_oflags = 0;
+ filep->f_pos = 0;
+ filep->f_inode = NULL;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: files_initialize
+ *
+ * Description:
+ * This is called from the FS initialization logic to configure the files.
+ *
+ ****************************************************************************/
+
+void files_initialize(void)
+{
+}
+
+/****************************************************************************
+ * Name: files_alloclist
+ *
+ * Description: Allocate a list of files for a new task
+ *
+ ****************************************************************************/
+
+FAR struct filelist *files_alloclist(void)
+{
+ FAR struct filelist *list;
+ list = (FAR struct filelist*)kzalloc(sizeof(struct filelist));
+ if (list)
+ {
+ /* Start with a reference count of one */
+
+ list->fl_crefs = 1;
+
+ /* Initialize the list access mutex */
+
+ (void)sem_init(&list->fl_sem, 0, 1);
+ }
+
+ return list;
+}
+
+/****************************************************************************
+ * Name: files_addreflist
+ *
+ * Description:
+ * Increase the reference count on a file list
+ *
+ ****************************************************************************/
+
+int files_addreflist(FAR struct filelist *list)
+{
+ if (list)
+ {
+ /* Increment the reference count on the list.
+ * NOTE: that we disable interrupts to do this
+ * (vs. taking the list semaphore). We do this
+ * because file cleanup operations often must be
+ * done from the IDLE task which cannot wait
+ * on semaphores.
+ */
+
+ register irqstate_t flags = irqsave();
+ list->fl_crefs++;
+ irqrestore(flags);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: files_releaselist
+ *
+ * Description:
+ * Release a reference to the file list
+ *
+ ****************************************************************************/
+
+int files_releaselist(FAR struct filelist *list)
+{
+ int crefs;
+ if (list)
+ {
+ /* Decrement the reference count on the list.
+ * NOTE: that we disable interrupts to do this
+ * (vs. taking the list semaphore). We do this
+ * because file cleanup operations often must be
+ * done from the IDLE task which cannot wait
+ * on semaphores.
+ */
+
+ register irqstate_t flags = irqsave();
+ crefs = --(list->fl_crefs);
+ irqrestore(flags);
+
+ /* If the count decrements to zero, then there is no reference
+ * to the structure and it should be deallocated. Since there
+ * are references, it would be an error if any task still held
+ * a reference to the list's semaphore.
+ */
+
+ if (crefs <= 0)
+ {
+ int i;
+
+ /* Close each file descriptor .. Normally, you would need
+ * take the list semaphore, but it is safe to ignore the
+ * semaphore in this context because there are no references
+ */
+
+ for (i = 0; i < CONFIG_NFILE_DESCRIPTORS; i++)
+ {
+ (void)_files_close(&list->fl_files[i]);
+ }
+
+ /* Destroy the semaphore and release the filelist */
+
+ (void)sem_destroy(&list->fl_sem);
+ sched_free(list);
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: files_dup
+ *
+ * Description:
+ * Assign an inode to a specific files structure. This is the heart of
+ * dup2.
+ *
+ ****************************************************************************/
+
+int files_dup(FAR struct file *filep1, FAR struct file *filep2)
+{
+ FAR struct filelist *list;
+ FAR struct inode *inode;
+ int err;
+ int ret;
+
+ if (!filep1 || !filep1->f_inode || !filep2)
+ {
+ err = EBADF;
+ goto errout;
+ }
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ if (INODE_IS_MOUNTPT(filep1->f_inode))
+ {
+ err = ENOSYS; /* Not yet supported */
+ goto errout;
+ }
+#endif
+
+ list = sched_getfiles();
+ if (!list)
+ {
+ err = EMFILE;
+ goto errout;
+ }
+
+ _files_semtake(list);
+
+ /* If there is already an inode contained in the new file structure,
+ * close the file and release the inode.
+ */
+
+ ret = _files_close(filep2);
+ if (ret < 0)
+ {
+ /* An error occurred while closing the driver */
+
+ goto errout_with_ret;
+ }
+
+ /* Increment the reference count on the contained inode */
+
+ inode = filep1->f_inode;
+ inode_addref(inode);
+
+ /* Then clone the file structure */
+
+ filep2->f_oflags = filep1->f_oflags;
+ filep2->f_pos = filep1->f_pos;
+ filep2->f_inode = inode;
+
+ /* Call the open method on the file, driver, mountpoint so that it
+ * can maintain the correct open counts.
+ */
+
+ if (inode->u.i_ops && inode->u.i_ops->open)
+ {
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+#if 0 /* Not implemented */
+ if (INODE_IS_MOUNTPT(inode))
+ {
+ /* Open a file on the mountpoint */
+
+ ret = inode->u.i_mops->open(filep2, ?, filep2->f_oflags, ?);
+ }
+ else
+#endif
+#endif
+ {
+ /* Open the pseudo file or device driver */
+
+ ret = inode->u.i_ops->open(filep2);
+ }
+
+ /* Handle open failures */
+
+ if (ret < 0)
+ {
+ goto errout_with_inode;
+ }
+ }
+
+ _files_semgive(list);
+ return OK;
+
+/* Handler various error conditions */
+
+errout_with_inode:
+ inode_release(filep2->f_inode);
+ filep2->f_oflags = 0;
+ filep2->f_pos = 0;
+ filep2->f_inode = NULL;
+
+errout_with_ret:
+ err = -ret;
+ _files_semgive(list);
+
+errout:
+ set_errno(err);
+ return ERROR;
+}
+
+/****************************************************************************
+ * Name: files_allocate
+ *
+ * Description:
+ * Allocate a struct files instance and associate it with an inode instance.
+ * Returns the file descriptor == index into the files array.
+ *
+ ****************************************************************************/
+
+int files_allocate(FAR struct inode *inode, int oflags, off_t pos, int minfd)
+{
+ FAR struct filelist *list;
+ int i;
+
+ list = sched_getfiles();
+ if (list)
+ {
+ _files_semtake(list);
+ for (i = minfd; i < CONFIG_NFILE_DESCRIPTORS; i++)
+ {
+ if (!list->fl_files[i].f_inode)
+ {
+ list->fl_files[i].f_oflags = oflags;
+ list->fl_files[i].f_pos = pos;
+ list->fl_files[i].f_inode = inode;
+ _files_semgive(list);
+ return i;
+ }
+ }
+
+ _files_semgive(list);
+ }
+
+ return ERROR;
+}
+
+/****************************************************************************
+ * Name: files_close
+ *
+ * Description:
+ * Close an inode (if open)
+ *
+ * Assumuptions:
+ * Caller holds the list semaphore because the file descriptor will be freed.
+ *
+ ****************************************************************************/
+
+int files_close(int filedes)
+{
+ FAR struct filelist *list;
+ int ret;
+
+ /* Get the thread-specific file list */
+
+ list = sched_getfiles();
+ if (!list)
+ {
+ return -EMFILE;
+ }
+
+ /* If the file was properly opened, there should be an inode assigned */
+
+ if (filedes < 0 || filedes >= CONFIG_NFILE_DESCRIPTORS || !list->fl_files[filedes].f_inode)
+ {
+ return -EBADF;
+ }
+
+ /* Perform the protected close operation */
+
+ _files_semtake(list);
+ ret = _files_close(&list->fl_files[filedes]);
+ _files_semgive(list);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: files_release
+ *
+ * Assumuptions:
+ * Similar to files_close(). Called only from open() logic on error
+ * conditions.
+ *
+ ****************************************************************************/
+
+void files_release(int filedes)
+{
+ FAR struct filelist *list;
+
+ list = sched_getfiles();
+ if (list)
+ {
+ if (filedes >=0 && filedes < CONFIG_NFILE_DESCRIPTORS)
+ {
+ _files_semtake(list);
+ list->fl_files[filedes].f_oflags = 0;
+ list->fl_files[filedes].f_pos = 0;
+ list->fl_files[filedes].f_inode = NULL;
+ _files_semgive(list);
+ }
+ }
+}
+
diff --git a/nuttx/fs/fs_findblockdriver.c b/nuttx/fs/fs_findblockdriver.c
new file mode 100644
index 000000000..febf28a7e
--- /dev/null
+++ b/nuttx/fs/fs_findblockdriver.c
@@ -0,0 +1,131 @@
+/****************************************************************************
+ * fs/fs_openblockdriver.c
+ *
+ * Copyright (C) 2008 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in pathname and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of pathname 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/mount.h>
+#include <debug.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: find_blockdriver
+ *
+ * Description:
+ * Return the inode of the block driver specified by 'pathname'
+ *
+ * Inputs:
+ * pathname - the full path to the block driver to be located
+ * mountflags - if MS_RDONLY is not set, then driver must support write
+ * operations (see include/sys/mount.h)
+ * ppinode - address of the location to return the inode reference
+ *
+ * Return:
+ * Returns zero on success or a negated errno on failure:
+ *
+ * EINVAL - pathname or pinode is NULL
+ * ENOENT - No block driver of this name is registered
+ * ENOTBLK - The inode associated with the pathname is not a block driver
+ * EACCESS - The MS_RDONLY option was not set but this driver does not
+ * support write access
+ *
+ ****************************************************************************/
+
+int find_blockdriver(FAR const char *pathname, int mountflags, FAR struct inode **ppinode)
+{
+ FAR struct inode *inode;
+ int ret = 0; /* Assume success */
+
+ /* Sanity checks */
+
+#ifdef CONFIG_DEBUG
+ if (!pathname || !ppinode)
+ {
+ ret = -EINVAL;
+ goto errout;
+ }
+#endif
+
+ /* Find the inode registered with this pathname */
+
+ inode = inode_find(pathname, NULL);
+ if (!inode)
+ {
+ fdbg("Failed to find %s\n", pathname);
+ ret = -ENOENT;
+ goto errout;
+ }
+
+ /* Verify that the inode is a block driver. */
+
+ if (!INODE_IS_BLOCK(inode))
+ {
+ fdbg("%s is not a block driver\n", pathname);
+ ret = -ENOTBLK;
+ goto errout_with_inode;
+ }
+
+ /* Make sure that the inode supports the requested access */
+
+ if (!inode->u.i_bops || !inode->u.i_bops->read ||
+ (!inode->u.i_bops->write && (mountflags & MS_RDONLY) == 0))
+ {
+ fdbg("%s does not support requested access\n", pathname);
+ ret = -EACCES;
+ goto errout_with_inode;
+ }
+
+ *ppinode = inode;
+ return OK;
+
+errout_with_inode:
+ inode_release(inode);
+errout:
+ return ret;
+}
diff --git a/nuttx/fs/fs_foreachinode.c b/nuttx/fs/fs_foreachinode.c
new file mode 100644
index 000000000..c5874c669
--- /dev/null
+++ b/nuttx/fs/fs_foreachinode.c
@@ -0,0 +1,233 @@
+/****************************************************************************
+ * fs/fs_foreachinode.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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Is it better to allocate the struct inode_path_s from the heap? or
+ * from the stack? This decision depends on how often this is down and
+ * how much stack space you can afford.
+ */
+
+#define ENUM_INODE_ALLOC 1
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure manages the full path to the inode. */
+
+struct inode_path_s
+{
+ foreach_inode_t handler;
+ FAR void *arg;
+ char path[CONFIG_PATH_MAX];
+};
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+/****************************************************************************
+ * Name: foreach_inodelevel
+ *
+ * Description:
+ * This is the recursive 'heart' of foreach_inode. It will visit each
+ * inode at this level in the hierarchy and recurse handle each inode
+ * at the next level down.
+ *
+ * Assumptions:
+ * The caller holds the inode semaphore.
+ *
+ ****************************************************************************/
+
+int foreach_inodelevel(FAR struct inode *node, struct inode_path_s *info)
+{
+ int ret = OK;
+
+ /* Visit each node at this level */
+
+ for (; node; node = node->i_peer)
+ {
+ /* Give the next inode to the callback */
+
+ ret = info->handler(node, info->path, info->arg);
+
+ /* Break out of the looop early if the handler returns a non-zero
+ * value
+ */
+
+ if (ret != 0)
+ {
+ break;
+ }
+
+ /* If there is a level 'beneath' this one, then recurse to visit all
+ * of the inodes at that level.
+ */
+
+ if (node->i_child)
+ {
+ /* Construct the path to the next level */
+
+ int pathlen = strlen(info->path);
+ int namlen = strlen(node->i_name) + 1;
+
+ /* Make sure that this would not exceed the maximum path length */
+
+ if (pathlen + namlen > PATH_MAX)
+ {
+ ret = -ENAMETOOLONG;
+ break;
+ }
+
+ /* Append the path segment to this inode and recurse */
+
+ sprintf(&info->path[pathlen], "/%s", node->i_name);
+ ret = foreach_inodelevel(node->i_child, info);
+
+ /* Truncate the path name back to the correct length */
+
+ info->path[pathlen] = '\0';
+
+ /* Return early if the handler at the lower level returned a non-
+ * zero value
+ */
+
+ if (ret != 0)
+ {
+ break;
+ }
+ }
+ }
+
+ /* Return the result of the traversal. */
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+/****************************************************************************
+ * Name: foreach_inode
+ *
+ * Description:
+ * Visit each inode in the pseudo-file system. The traversal is terminated
+ * when the callback 'handler' returns a non-zero value, or when all of
+ * the inodes have been visited.
+ *
+ * NOTE 1: Use with caution... The pseudo-file system is locked throughout
+ * the traversal.
+ * NOTE 2: The search algorithm is recursive and could, in principle, use
+ * an indeterminant amount of stack space. This will not usually be a
+ * real work issue.
+ *
+ ****************************************************************************/
+
+int foreach_inode(foreach_inode_t handler, FAR void *arg)
+{
+#ifdef ENUM_INODE_ALLOC
+ FAR struct inode_path_s *info;
+ int ret;
+
+ /* Allocate the mountpoint info structure */
+
+ info = (FAR struct inode_path_s *)malloc(sizeof(struct inode_path_s));
+ if (!info)
+ {
+ return -ENOMEM;
+ }
+
+ /* Initialize the info structure */
+
+ info->handler = handler;
+ info->arg = arg;
+ info->path[0] = '\0';
+
+ /* Start the recursion at the root inode */
+
+ inode_semtake();
+ ret = foreach_inodelevel(root_inode, info);
+ inode_semgive();
+
+ /* Free the info structure and return the result */
+
+ free(info);
+ return ret;
+
+#else
+ struct inode_path_s info;
+ int ret;
+
+ /* Initialize the info structure */
+
+ info.handler = handler;
+ info.arg = arg;
+ info.path[0] = '\0';
+
+ /* Start the recursion at the root inode */
+
+ inode_semtake();
+ ret = foreach_inodelevel(root_inode, &info);
+ inode_semgive();
+
+ return ret;
+
+#endif
+}
+
diff --git a/nuttx/fs/fs_foreachmountpoint.c b/nuttx/fs/fs_foreachmountpoint.c
new file mode 100644
index 000000000..74e3e8356
--- /dev/null
+++ b/nuttx/fs/fs_foreachmountpoint.c
@@ -0,0 +1,177 @@
+/****************************************************************************
+ * fs/fs_foreachmountpoint.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/statfs.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+#ifndef CONFIG_DISABLE_MOUNTPOUNT
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure just remembers the final consumer of the mountpoint
+ * information (and its argument).
+ */
+
+struct enum_mountpoint_s
+{
+ foreach_mountpoint_t handler;
+ FAR void *arg;
+};
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int mountpoint_filter(FAR struct inode *node,
+ FAR char dirpath[PATH_MAX], FAR void *arg)
+{
+ FAR struct enum_mountpoint_s *info = (FAR struct enum_mountpoint_s *)arg;
+ struct statfs statbuf;
+ int pathlen;
+ int namlen;
+ int ret = OK;
+
+ DEBUGASSERT(node && info && info->handler);
+
+ /* Check if the inode is a mountpoint. Mountpoints must support statfs.
+ * If this one does not for some reason, then it will be ignored.
+ *
+ * The root node is a special case: It has no operations (u.i_mops == NULL)
+ */
+
+ if (INODE_IS_MOUNTPT(node) && node->u.i_mops && node->u.i_mops->statfs)
+ {
+ /* Yes... get the full path to the inode by concatenating the inode
+ * name and the path to the directory containing the inode.
+ */
+
+ pathlen = strlen(dirpath);
+ namlen = strlen(node->i_name) + 1;
+
+ /* Make sure that this would not exceed the maximum path length */
+
+ if (pathlen + namlen > PATH_MAX)
+ {
+ return -ENAMETOOLONG;
+ }
+
+ /* Append the inode name to the directory path */
+
+ sprintf(&dirpath[pathlen], "/%s", node->i_name);
+
+ /* Get the status of the file system */
+
+ ret = node->u.i_mops->statfs(node, &statbuf);
+ if (ret == OK)
+ {
+ /* And pass the full path and file system status to the handler */
+
+ ret = info->handler(dirpath, &statbuf, info->arg);
+ }
+
+ /* Truncate the path name back to the correct length */
+
+ dirpath[pathlen] = '\0';
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+ /****************************************************************************
+ * Name: foreach_mountpoint
+ *
+ * Description:
+ * Visit each mountpoint in the pseudo-file system. The traversal is
+ * terminated when the callback 'handler' returns a non-zero value, or when
+ * all of the mountpoints have been visited.
+ *
+ * This is just a front end "filter" to foreach_inode() that forwards only
+ * mountpoint inodes. It is intended to support the mount() command to
+ * when the mount command is used to enumerate mounts.
+ *
+ * NOTE 1: Use with caution... The pseudo-file system is locked throughout
+ * the traversal.
+ * NOTE 2: The search algorithm is recursive and could, in principle, use
+ * an indeterminant amount of stack space. This will not usually be a
+ * real work issue.
+ *
+ ****************************************************************************/
+
+int foreach_mountpoint(foreach_mountpoint_t handler, FAR void *arg)
+{
+ struct enum_mountpoint_s info;
+
+ /* Let foreach_inode do the real work */
+
+ info.handler = handler;
+ info.arg = arg;
+
+ return foreach_inode(mountpoint_filter, (FAR void *)&info);
+}
+
+#endif
diff --git a/nuttx/fs/fs_fsync.c b/nuttx/fs/fs_fsync.c
new file mode 100644
index 000000000..877c33a83
--- /dev/null
+++ b/nuttx/fs/fs_fsync.c
@@ -0,0 +1,138 @@
+/****************************************************************************
+ * fs/fs_fsync.c
+ *
+ * Copyright (C) 2007-2009 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 <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/sched.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fsync
+ *
+ * Description:
+ * This func simply binds inode sync methods to the sync system call.
+ *
+ ****************************************************************************/
+
+int fsync(int fd)
+{
+ FAR struct filelist *list;
+ FAR struct file *this_file;
+ struct inode *inode;
+ int ret;
+
+ /* Get the thread-specific file list */
+
+ list = sched_getfiles();
+ if (!list)
+ {
+ ret = EMFILE;
+ goto errout;
+ }
+
+ /* Did we get a valid file descriptor? */
+
+ if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS)
+ {
+ ret = EBADF;
+ goto errout;
+ }
+
+ /* Was this file opened for write access? */
+
+ this_file = &list->fl_files[fd];
+ if ((this_file->f_oflags & O_WROK) == 0)
+ {
+ ret = EBADF;
+ goto errout;
+ }
+
+ /* Is this inode a registered mountpoint? Does it support the
+ * sync operations may be relevant to device drivers but only
+ * the mountpoint operations vtable contains a sync method.
+ */
+
+ inode = this_file->f_inode;
+ if (!inode || !INODE_IS_MOUNTPT(inode) ||
+ !inode->u.i_mops || !inode->u.i_mops->sync)
+ {
+ ret = EINVAL;
+ goto errout;
+ }
+
+ /* Yes, then tell the mountpoint to sync this file */
+
+ ret = inode->u.i_mops->sync(this_file);
+ if (ret >= 0)
+ {
+ return OK;
+ }
+
+ ret = -ret;
+
+errout:
+ set_errno(ret);
+ return ERROR;
+}
+
diff --git a/nuttx/fs/fs_inode.c b/nuttx/fs/fs_inode.c
new file mode 100644
index 000000000..14da54cea
--- /dev/null
+++ b/nuttx/fs/fs_inode.c
@@ -0,0 +1,377 @@
+/****************************************************************************
+ * fs/fs_inode.c
+ *
+ * Copyright (C) 2007-2009, 2011-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 <assert.h>
+#include <semaphore.h>
+#include <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+static sem_t tree_sem;
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+FAR struct inode *root_inode = NULL;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: _inode_compare
+ *
+ * Description:
+ * Compare two inode names
+ *
+ ****************************************************************************/
+
+static int _inode_compare(FAR const char *fname,
+ FAR struct inode *node)
+{
+ char *nname = node->i_name;
+
+ if (!nname)
+ {
+ return 1;
+ }
+
+ if (!fname)
+ {
+ return -1;
+ }
+
+ for (;;)
+ {
+ /* At end of node name? */
+
+ if (!*nname)
+ {
+ /* Yes.. also end of find name? */
+
+ if (!*fname || *fname == '/')
+ {
+ /* Yes.. return match */
+
+ return 0;
+ }
+ else
+ {
+ /* No... return find name > node name */
+
+ return 1;
+ }
+ }
+
+ /* At end of find name?*/
+
+ else if (!*fname || *fname == '/')
+ {
+ /* Yes... return find name < node name */
+
+ return -1;
+ }
+
+ /* Check for non-matching characters */
+
+ else if (*fname > *nname)
+ {
+ return 1;
+ }
+ else if (*fname < *nname)
+ {
+ return -1;
+ }
+
+ /* Not at the end of either string and all of the
+ * characters still match. keep looking.
+ */
+
+ else
+ {
+ fname++;
+ nname++;
+ }
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: fs_initialize
+ *
+ * Description:
+ * This is called from the OS initialization logic to configure the file
+ * system.
+ *
+ ****************************************************************************/
+
+void fs_initialize(void)
+{
+ /* Initialize the semaphore to one (to support one-at-
+ * a-time access to the inode tree).
+ */
+
+ (void)sem_init(&tree_sem, 0, 1);
+
+ /* Initialize files array (if it is used) */
+
+#ifdef CONFIG_HAVE_WEAKFUNCTIONS
+ if (files_initialize != NULL)
+#endif
+ {
+ files_initialize();
+ }
+}
+
+/****************************************************************************
+ * Name: inode_semtake
+ *
+ * Description:
+ * Get exclusive access to the in-memory inode tree (tree_sem).
+ *
+ ****************************************************************************/
+
+void inode_semtake(void)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&tree_sem) != 0)
+ {
+ /* The only case that an error should occr here is if
+ * the wait was awakened by a signal.
+ */
+
+ ASSERT(get_errno() == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Name: inode_semgive
+ *
+ * Description:
+ * Relinquish exclusive access to the in-memory inode tree (tree_sem).
+ *
+ ****************************************************************************/
+
+void inode_semgive(void)
+{
+ sem_post(&tree_sem);
+}
+
+/****************************************************************************
+ * Name: inode_search
+ *
+ * Description:
+ * Find the inode associated with 'path' returning the inode references
+ * and references to its companion nodes.
+ *
+ * Assumptions:
+ * The caller holds the tree_sem
+ *
+ ****************************************************************************/
+
+FAR struct inode *inode_search(const char **path,
+ FAR struct inode **peer,
+ FAR struct inode **parent,
+ const char **relpath)
+{
+ const char *name = *path + 1; /* Skip over leading '/' */
+ FAR struct inode *node = root_inode;
+ FAR struct inode *left = NULL;
+ FAR struct inode *above = NULL;
+
+ while (node)
+ {
+ int result = _inode_compare(name, node);
+
+ /* Case 1: The name is less than the name of the node.
+ * Since the names are ordered, these means that there
+ * is no peer node with this name and that there can be
+ * no match in the fileystem.
+ */
+
+ if (result < 0)
+ {
+ node = NULL;
+ break;
+ }
+
+ /* Case 2: the name is greater than the name of the node.
+ * In this case, the name may still be in the list to the
+ * "right"
+ */
+
+ else if (result > 0)
+ {
+ left = node;
+ node = node->i_peer;
+ }
+
+ /* The names match */
+
+ else
+ {
+ /* Now there are three more possibilities:
+ * (1) This is the node that we are looking for or,
+ * (2) The node we are looking for is "below" this one.
+ * (3) This node is a mountpoint and will absorb all request
+ * below this one
+ */
+
+ name = inode_nextname(name);
+ if (!*name || INODE_IS_MOUNTPT(node))
+ {
+ /* Either (1) we are at the end of the path, so this must be the
+ * node we are looking for or else (2) this node is a mountpoint
+ * and will handle the remaining part of the pathname
+ */
+
+ if (relpath)
+ {
+ *relpath = name;
+ }
+ break;
+ }
+ else
+ {
+ /* More to go, keep looking at the next level "down" */
+
+ above = node;
+ left = NULL;
+ node = node->i_child;
+ }
+ }
+ }
+
+ /* node is null. This can happen in one of four cases:
+ * With node = NULL
+ * (1) We went left past the final peer: The new node
+ * name is larger than any existing node name at
+ * that level.
+ * (2) We broke out in the middle of the list of peers
+ * because the name was not found in the ordered
+ * list.
+ * (3) We went down past the final parent: The new node
+ * name is "deeper" than anything that we currently
+ * have in the tree.
+ * with node != NULL
+ * (4) When the node matching the full path is found
+ */
+
+ if (peer)
+ {
+ *peer = left;
+ }
+
+ if (parent)
+ {
+ *parent = above;
+ }
+
+ *path = name;
+ return node;
+}
+
+/****************************************************************************
+ * Name: inode_free
+ *
+ * Description:
+ * Free resources used by an inode
+ *
+ ****************************************************************************/
+
+void inode_free(FAR struct inode *node)
+{
+ if (node)
+ {
+ inode_free(node->i_peer);
+ inode_free(node->i_child);
+ kfree(node);
+ }
+}
+
+/****************************************************************************
+ * Name: inode_nextname
+ *
+ * Description:
+ * Given a path with node names separated by '/', return the next node
+ * name.
+ *
+ ****************************************************************************/
+
+FAR const char *inode_nextname(FAR const char *name)
+{
+ /* Search for the '/' delimiter or the NUL terminator at the end of the
+ * string.
+ */
+
+ while (*name && *name != '/')
+ {
+ name++;
+ }
+
+ /* If we found the '/' delimiter, then the path segment we want begins at
+ * the next character (which might also be the NUL terminator).
+ */
+
+ if (*name)
+ {
+ name++;
+ }
+
+ return name;
+}
+
diff --git a/nuttx/fs/fs_inodeaddref.c b/nuttx/fs/fs_inodeaddref.c
new file mode 100644
index 000000000..32d4426d2
--- /dev/null
+++ b/nuttx/fs/fs_inodeaddref.c
@@ -0,0 +1,83 @@
+/****************************************************************************
+ * fs_inodeaddref.c
+ *
+ * Copyright (C) 2007-2009 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 <errno.h>
+#include <nuttx/fs/fs.h>
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: inode_addref
+ *
+ * Description:
+ * Increment the reference count on an inode (as when a file descriptor
+ * is dup'ed).
+ *
+ ****************************************************************************/
+
+void inode_addref(FAR struct inode *inode)
+{
+ if (inode)
+ {
+ inode_semtake();
+ inode->i_crefs++;
+ inode_semgive();
+ }
+}
diff --git a/nuttx/fs/fs_inodefind.c b/nuttx/fs/fs_inodefind.c
new file mode 100644
index 000000000..905539366
--- /dev/null
+++ b/nuttx/fs/fs_inodefind.c
@@ -0,0 +1,99 @@
+/****************************************************************************
+ * fs/fs_inodefind.c
+ *
+ * Copyright (C) 2007-2009 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 <errno.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: inode_find
+ *
+ * Description:
+ * This is called from the open() logic to get a reference to the inode
+ * associated with a path.
+ *
+ ****************************************************************************/
+
+FAR struct inode *inode_find(FAR const char *path, FAR const char **relpath)
+{
+ FAR struct inode *node;
+
+ if (!*path || path[0] != '/')
+ {
+ return NULL;
+ }
+
+ /* Find the node matching the path. If found, increment the count of
+ * references on the node.
+ */
+
+ inode_semtake();
+ node = inode_search(&path, (FAR struct inode**)NULL, (FAR struct inode**)NULL, relpath);
+ if (node)
+ {
+ node->i_crefs++;
+ }
+
+ inode_semgive();
+ return node;
+}
+
diff --git a/nuttx/fs/fs_inoderelease.c b/nuttx/fs/fs_inoderelease.c
new file mode 100644
index 000000000..bc5026534
--- /dev/null
+++ b/nuttx/fs/fs_inoderelease.c
@@ -0,0 +1,106 @@
+/****************************************************************************
+ * fs_inoderelease.c
+ *
+ * Copyright (C) 2007-2009 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 <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: inode_release
+ *
+ * Description:
+ * This is called from close() logic when it no longer refers to the inode.
+ *
+ ****************************************************************************/
+
+void inode_release(FAR struct inode *node)
+{
+ if (node)
+ {
+ /* Decrement the references of the inode */
+
+ inode_semtake();
+ if (node->i_crefs)
+ {
+ node->i_crefs--;
+ }
+
+ /* If the subtree was previously deleted and the reference
+ * count has decrement to zero, then delete the inode
+ * now.
+ */
+
+ if (node->i_crefs <= 0 && (node->i_flags & FSNODEFLAG_DELETED) != 0)
+ {
+ inode_semgive();
+ inode_free(node->i_child);
+ kfree(node);
+ }
+ else
+ {
+ inode_semgive();
+ }
+ }
+}
+
diff --git a/nuttx/fs/fs_inoderemove.c b/nuttx/fs/fs_inoderemove.c
new file mode 100644
index 000000000..c349b1759
--- /dev/null
+++ b/nuttx/fs/fs_inoderemove.c
@@ -0,0 +1,161 @@
+/****************************************************************************
+ * fs/fs_inoderemove.c
+ *
+ * Copyright (C) 2007-2009 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 <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: inode_unlink
+ ****************************************************************************/
+
+static void inode_unlink(struct inode *node,
+ struct inode *peer,
+ struct inode *parent)
+{
+ /* If peer is non-null, then remove the node from the right of
+ * of that peer node.
+ */
+
+ if (peer)
+ {
+ peer->i_peer = node->i_peer;
+ }
+
+ /* If parent is non-null, then remove the node from head of
+ * of the list of children.
+ */
+
+ else if (parent)
+ {
+ parent->i_child = node->i_peer;
+ }
+
+ /* Otherwise, we must be removing the root inode. */
+
+ else
+ {
+ root_inode = node->i_peer;
+ }
+
+ node->i_peer = NULL;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: inode_remove
+ *
+ * Description:
+ * Remove a node from the in-memory, inode tree
+ *
+ * NOTE: Caller must hold the inode semaphore
+ *
+ ****************************************************************************/
+
+int inode_remove(FAR const char *path)
+{
+ const char *name = path;
+ FAR struct inode *node;
+ FAR struct inode *left;
+ FAR struct inode *parent;
+
+ if (!*path || path[0] != '/')
+ {
+ return -EINVAL;
+ }
+
+ /* Find the node to delete */
+
+ node = inode_search(&name, &left, &parent, (const char **)NULL);
+ if (node)
+ {
+ /* Found it, now remove it from the tree */
+
+ inode_unlink(node, left, parent);
+
+ /* We cannot delete it if there reference to the inode */
+
+ if (node->i_crefs)
+ {
+ /* In that case, we will mark it deleted, when the FS
+ * releases the inode, we will then, finally delete
+ * the subtree.
+ */
+
+ node->i_flags |= FSNODEFLAG_DELETED;
+ return -EBUSY;
+ }
+ else
+ {
+ /* And delete it now -- recursively to delete all of its children */
+
+ inode_free(node->i_child);
+ kfree(node);
+ return OK;
+ }
+ }
+
+ /* The node does not exist or it has references */
+
+ return -ENOENT;
+}
diff --git a/nuttx/fs/fs_inodereserve.c b/nuttx/fs/fs_inodereserve.c
new file mode 100644
index 000000000..73e0fff28
--- /dev/null
+++ b/nuttx/fs/fs_inodereserve.c
@@ -0,0 +1,237 @@
+/****************************************************************************
+ * fs/fs_registerreserve.c
+ *
+ * Copyright (C) 2007-2009, 2011-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 <assert.h>
+#include <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: inode_namelen
+ ****************************************************************************/
+
+static int inode_namelen(FAR const char *name)
+{
+ const char *tmp = name;
+ while(*tmp && *tmp != '/') tmp++;
+ return tmp - name;
+}
+
+/****************************************************************************
+ * Name: inode_namecpy
+ ****************************************************************************/
+
+static void inode_namecpy(char *dest, const char *src)
+{
+ while(*src && *src != '/') *dest++ = *src++;
+ *dest='\0';
+}
+
+/****************************************************************************
+ * Name: inode_alloc
+ ****************************************************************************/
+
+static FAR struct inode *inode_alloc(FAR const char *name)
+{
+ int namelen = inode_namelen(name);
+ FAR struct inode *node = (FAR struct inode*)kzalloc(FSNODE_SIZE(namelen));
+ if (node)
+ {
+ inode_namecpy(node->i_name, name);
+ }
+
+ return node;
+}
+
+/****************************************************************************
+ * Name: inode_insert
+ ****************************************************************************/
+
+static void inode_insert(FAR struct inode *node,
+ FAR struct inode *peer,
+ FAR struct inode *parent)
+{
+ /* If peer is non-null, then new node simply goes to the right
+ * of that peer node.
+ */
+
+ if (peer)
+ {
+ node->i_peer = peer->i_peer;
+ peer->i_peer = node;
+ }
+
+ /* If parent is non-null, then it must go at the head of its
+ * list of children.
+ */
+
+ else if (parent)
+ {
+ node->i_peer = parent->i_child;
+ parent->i_child = node;
+ }
+
+ /* Otherwise, this must be the new root_inode */
+
+ else
+ {
+ node->i_peer = root_inode;
+ root_inode = node;
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: inode_reserve
+ *
+ * Description:
+ * Reserve an (initialized) inode the pseudo file system.
+ *
+ * NOTE: Caller must hold the inode semaphore
+ *
+ * Input parameters:
+ * path - The path to the inode to create
+ * inode - The location to return the inode pointer
+ *
+ * Returned Value:
+ * Zero on success (with the inode point in 'inode'); A negated errno
+ * value is returned on failure:
+ *
+ * EINVAL - 'path' is invalid for this operation
+ * EEXIST - An inode already exists at 'path'
+ * ENOMEM - Failed to allocate in-memory resources for the operation
+ *
+ ****************************************************************************/
+
+int inode_reserve(FAR const char *path, FAR struct inode **inode)
+{
+ const char *name = path;
+ FAR struct inode *left;
+ FAR struct inode *parent;
+
+ /* Assume failure */
+
+ DEBUGASSERT(path && inode);
+ *inode = NULL;
+
+ /* Handle paths that are interpreted as the root directory */
+
+ if (!*path || path[0] != '/')
+ {
+ return -EINVAL;
+ }
+
+ /* Find the location to insert the new subtree */
+
+ if (inode_search(&name, &left, &parent, (FAR const char **)NULL) != NULL)
+ {
+ /* It is an error if the node already exists in the tree */
+
+ return -EEXIST;
+ }
+
+ /* Now we now where to insert the subtree */
+
+ for (;;)
+ {
+ FAR struct inode *node;
+
+ /* Create a new node -- we need to know if this is the
+ * the leaf node or some intermediary. We can find this
+ * by looking at the next name.
+ */
+
+ FAR const char *next_name = inode_nextname(name);
+ if (*next_name)
+ {
+ /* Insert an operationless node */
+
+ node = inode_alloc(name);
+ if (node)
+ {
+ inode_insert(node, left, parent);
+
+ /* Set up for the next time through the loop */
+
+ name = next_name;
+ left = NULL;
+ parent = node;
+ continue;
+ }
+ }
+ else
+ {
+ node = inode_alloc(name);
+ if (node)
+ {
+ inode_insert(node, left, parent);
+ *inode = node;
+ return OK;
+ }
+ }
+
+ /* We get here on failures to allocate node memory */
+
+ return -ENOMEM;
+ }
+}
diff --git a/nuttx/fs/fs_internal.h b/nuttx/fs/fs_internal.h
new file mode 100644
index 000000000..786c683dc
--- /dev/null
+++ b/nuttx/fs/fs_internal.h
@@ -0,0 +1,327 @@
+/****************************************************************************
+ * fs/fs_internal.h
+ *
+ * Copyright (C) 2007, 2009, 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_FS_INTERNAL_H
+#define __FS_FS_INTERNAL_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <dirent.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/compiler.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define FSNODEFLAG_TYPE_MASK 0x00000003
+#define FSNODEFLAG_TYPE_DRIVER 0x00000000
+#define FSNODEFLAG_TYPE_BLOCK 0x00000001
+#define FSNODEFLAG_TYPE_MOUNTPT 0x00000002
+#define FSNODEFLAG_DELETED 0x00000004
+
+#define INODE_IS_DRIVER(i) \
+ (((i)->i_flags & FSNODEFLAG_TYPE_MASK) == FSNODEFLAG_TYPE_DRIVER)
+#define INODE_IS_BLOCK(i) \
+ (((i)->i_flags & FSNODEFLAG_TYPE_BLOCK) == FSNODEFLAG_TYPE_BLOCK)
+#define INODE_IS_MOUNTPT(i) \
+ (((i)->i_flags & FSNODEFLAG_TYPE_MOUNTPT) == FSNODEFLAG_TYPE_MOUNTPT)
+
+#define INODE_SET_DRIVER(i) \
+ ((i)->i_flags &= ~FSNODEFLAG_TYPE_MASK)
+#define INODE_SET_BLOCK(i) \
+ ((i)->i_flags = (((i)->i_flags & ~FSNODEFLAG_TYPE_MASK) | FSNODEFLAG_TYPE_BLOCK))
+#define INODE_SET_MOUNTPT(i) \
+ ((i)->i_flags = (((i)->i_flags & ~FSNODEFLAG_TYPE_MASK) | FSNODEFLAG_TYPE_MOUNTPT))
+
+/* Mountpoint fd_flags values */
+
+#define DIRENTFLAGS_PSEUDONODE 1
+
+#define DIRENT_SETPSEUDONODE(f) do (f) |= DIRENTFLAGS_PSEUDONODE; while (0)
+#define DIRENT_ISPSEUDONODE(f) (((f) & DIRENTFLAGS_PSEUDONODE) != 0)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Callback used by foreach_inode to traverse all inodes in the pseudo-
+ * file system.
+ */
+
+typedef int (*foreach_inode_t)(FAR struct inode *node,
+ FAR char dirpath[PATH_MAX],
+ FAR void *arg);
+
+/****************************************************************************
+ * Global Variables
+ ****************************************************************************/
+
+extern FAR struct inode *root_inode;
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+/* fs_inode.c ***************************************************************/
+/****************************************************************************
+ * Name: inode_semtake
+ *
+ * Description:
+ * Get exclusive access to the in-memory inode tree (tree_sem).
+ *
+ ****************************************************************************/
+
+EXTERN void inode_semtake(void);
+
+/****************************************************************************
+ * Name: inode_semgive
+ *
+ * Description:
+ * Relinquish exclusive access to the in-memory inode tree (tree_sem).
+ *
+ ****************************************************************************/
+
+EXTERN void inode_semgive(void);
+
+/****************************************************************************
+ * Name: inode_search
+ *
+ * Description:
+ * Find the inode associated with 'path' returning the inode references
+ * and references to its companion nodes.
+ *
+ * Assumptions:
+ * The caller holds the tree_sem
+ *
+ ****************************************************************************/
+
+EXTERN FAR struct inode *inode_search(FAR const char **path,
+ FAR struct inode **peer,
+ FAR struct inode **parent,
+ FAR const char **relpath);
+
+/****************************************************************************
+ * Name: inode_free
+ *
+ * Description:
+ * Free resources used by an inode
+ *
+ ****************************************************************************/
+
+EXTERN void inode_free(FAR struct inode *node);
+
+/****************************************************************************
+ * Name: inode_nextname
+ *
+ * Description:
+ * Given a path with node names separated by '/', return the next node
+ * name.
+ *
+ ****************************************************************************/
+
+EXTERN const char *inode_nextname(FAR const char *name);
+
+/* fs_inodereserver.c *******************************************************/
+/****************************************************************************
+ * Name: inode_reserve
+ *
+ * Description:
+ * Reserve an (initialized) inode the pseudo file system.
+ *
+ * NOTE: Caller must hold the inode semaphore
+ *
+ * Input parameters:
+ * path - The path to the inode to create
+ * inode - The location to return the inode pointer
+ *
+ * Returned Value:
+ * Zero on success (with the inode point in 'inode'); A negated errno
+ * value is returned on failure:
+ *
+ * EINVAL - 'path' is invalid for this operation
+ * EEXIST - An inode already exists at 'path'
+ * ENOMEM - Failed to allocate in-memory resources for the operation
+ *
+ ****************************************************************************/
+
+EXTERN int inode_reserve(FAR const char *path, FAR struct inode **inode);
+
+/* fs_inoderemove.c *********************************************************/
+/****************************************************************************
+ * Name: inode_remove
+ *
+ * Description:
+ * Remove a node from the in-memory, inode tree
+ *
+ * NOTE: Caller must hold the inode semaphore
+ *
+ ****************************************************************************/
+
+EXTERN int inode_remove(FAR const char *path);
+
+/* fs_inodefind.c ***********************************************************/
+/****************************************************************************
+ * Name: inode_find
+ *
+ * Description:
+ * This is called from the open() logic to get a reference to the inode
+ * associated with a path.
+ *
+ ****************************************************************************/
+
+EXTERN FAR struct inode *inode_find(FAR const char *path, const char **relpath);
+
+/* fs_inodeaddref.c *********************************************************/
+
+EXTERN void inode_addref(FAR struct inode *inode);
+
+/* fs_inoderelease.c ********************************************************/
+
+EXTERN void inode_release(FAR struct inode *inode);
+
+/* fs_foreachinode.c ********************************************************/
+/****************************************************************************
+ * Name: foreach_inode
+ *
+ * Description:
+ * Visit each inode in the pseudo-file system. The traversal is terminated
+ * when the callback 'handler' returns a non-zero value, or when all of
+ * the inodes have been visited.
+ *
+ * NOTE 1: Use with caution... The pseudo-file system is locked throughout
+ * the traversal.
+ * NOTE 2: The search algorithm is recursive and could, in principle, use
+ * an indeterminant amount of stack space. This will not usually be a
+ * real work issue.
+ *
+ ****************************************************************************/
+
+EXTERN int foreach_inode(foreach_inode_t handler, FAR void *arg);
+
+/* fs_files.c ***************************************************************/
+/****************************************************************************
+ * Name: files_initialize
+ *
+ * Description:
+ * This is called from the FS initialization logic to configure the files.
+ *
+ ****************************************************************************/
+
+EXTERN void weak_function files_initialize(void);
+
+/****************************************************************************
+ * Name: files_allocate
+ *
+ * Description:
+ * Allocate a struct files instance and associate it with an inode instance.
+ * Returns the file descriptor == index into the files array.
+ *
+ ****************************************************************************/
+
+EXTERN int files_allocate(FAR struct inode *inode, int oflags, off_t pos, int minfd);
+
+/****************************************************************************
+ * Name: files_close
+ *
+ * Description:
+ * Close an inode (if open)
+ *
+ * Assumuptions:
+ * Caller holds the list semaphore because the file descriptor will be freed.
+ *
+ ****************************************************************************/
+
+EXTERN int files_close(int filedes);
+
+/****************************************************************************
+ * Name: files_release
+ *
+ * Assumuptions:
+ * Similar to files_close(). Called only from open() logic on error
+ * conditions.
+ *
+ ****************************************************************************/
+
+EXTERN void files_release(int filedes);
+
+/* fs_findblockdriver.c *****************************************************/
+/****************************************************************************
+ * Name: find_blockdriver
+ *
+ * Description:
+ * Return the inode of the block driver specified by 'pathname'
+ *
+ * Inputs:
+ * pathname - the full path to the block driver to be located
+ * mountflags - if MS_RDONLY is not set, then driver must support write
+ * operations (see include/sys/mount.h)
+ * ppinode - address of the location to return the inode reference
+ *
+ * Return:
+ * Returns zero on success or a negated errno on failure:
+ *
+ * EINVAL - pathname or pinode is NULL
+ * ENOENT - No block driver of this name is registered
+ * ENOTBLK - The inode associated with the pathname is not a block driver
+ * EACCESS - The MS_RDONLY option was not set but this driver does not
+ * support write access
+ *
+ ****************************************************************************/
+
+EXTERN int find_blockdriver(FAR const char *pathname, int mountflags,
+ FAR struct inode **ppinode);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __FS_FS_INTERNAL_H */
diff --git a/nuttx/fs/fs_ioctl.c b/nuttx/fs/fs_ioctl.c
new file mode 100644
index 000000000..3440bc0d0
--- /dev/null
+++ b/nuttx/fs/fs_ioctl.c
@@ -0,0 +1,150 @@
+/****************************************************************************
+ * fs/fs_ioctl.c
+ *
+ * Copyright (C) 2007-2010, 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/ioctl.h>
+#include <sched.h>
+#include <errno.h>
+
+#include <net/if.h>
+
+#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0
+# include <nuttx/net/net.h>
+#endif
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ioctl
+ *
+ * Description:
+ * Perform device specific operations.
+ *
+ * Parameters:
+ * fd File/socket descriptor of device
+ * req The ioctl command
+ * arg The argument of the ioctl cmd
+ *
+ * Return:
+ * >=0 on success (positive non-zero values are cmd-specific)
+ * -1 on failure withi errno set properly:
+ *
+ * EBADF
+ * 'fd' is not a valid descriptor.
+ * EFAULT
+ * 'arg' references an inaccessible memory area.
+ * EINVAL
+ * 'cmd' or 'arg' is not valid.
+ * ENOTTY
+ * 'fd' is not associated with a character special device.
+ * ENOTTY
+ * The specified request does not apply to the kind of object that the
+ * descriptor 'fd' references.
+ *
+ ****************************************************************************/
+
+int ioctl(int fd, int req, unsigned long arg)
+{
+ int err;
+#if CONFIG_NFILE_DESCRIPTORS > 0
+ FAR struct filelist *list;
+ FAR struct file *this_file;
+ FAR struct inode *inode;
+ int ret = OK;
+
+ /* Did we get a valid file descriptor? */
+
+ if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS)
+#endif
+ {
+ /* Perform the socket ioctl */
+
+#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0
+ if ((unsigned int)fd < (CONFIG_NFILE_DESCRIPTORS+CONFIG_NSOCKET_DESCRIPTORS))
+ {
+ return netdev_ioctl(fd, req, arg);
+ }
+ else
+#endif
+ {
+ err = EBADF;
+ goto errout;
+ }
+ }
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+ /* Get the thread-specific file list */
+
+ list = sched_getfiles();
+ if (!list)
+ {
+ err = EMFILE;
+ goto errout;
+ }
+
+ /* Is a driver registered? Does it support the ioctl method? */
+
+ this_file = &list->fl_files[fd];
+ inode = this_file->f_inode;
+
+ if (inode && inode->u.i_ops && inode->u.i_ops->ioctl)
+ {
+ /* Yes, then let it perform the ioctl */
+
+ ret = (int)inode->u.i_ops->ioctl(this_file, req, arg);
+ if (ret < 0)
+ {
+ err = -ret;
+ goto errout;
+ }
+ }
+
+ return ret;
+#endif
+
+errout:
+ set_errno(err);
+ return ERROR;
+}
+
diff --git a/nuttx/fs/fs_lseek.c b/nuttx/fs/fs_lseek.c
new file mode 100644
index 000000000..c57658434
--- /dev/null
+++ b/nuttx/fs/fs_lseek.c
@@ -0,0 +1,176 @@
+/****************************************************************************
+ * fs/fs_lseek.c
+ *
+ * Copyright (C) 2008 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 <unistd.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "fs_internal.h"
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: lseek
+ *
+ * Description:
+ * The lseek() function repositions the offset of the open file associated
+ * with the file descriptor fildes to the argument 'offset' according to the
+ * directive 'whence' as follows:
+ *
+ * SEEK_SET
+ * The offset is set to offset bytes.
+ * SEEK_CUR
+ * The offset is set to its current location plus offset bytes.
+ * SEEK_END
+ * The offset is set to the size of the file plus offset bytes.
+ *
+ * The lseek() function allows the file offset to be set beyond the end of the
+ * file (but this does not change the size of the file). If data is later written
+ * at this point, subsequent reads of the data in the gap (a "hole") return null
+ * bytes ('\0') until data is actually written into the gap.
+ *
+ * Parameters:
+ * fd File descriptor of device
+ * offset Defines the offset to position to
+ * whence Defines how to use offset
+ *
+ * Return:
+ * The resulting offset on success. -1 on failure withi errno set properly:
+ *
+ * EBADF fildes is not an open file descriptor.
+ * EINVAL whence is not one of SEEK_SET, SEEK_CUR, SEEK_END; or the
+ * resulting file offset would be negative, or beyond the end of a
+ * seekable device.
+ * EOVERFLOW The resulting file offset cannot be represented in an off_t.
+ * ESPIPE fildes is associated with a pipe, socket, or FIFO.
+ *
+ ****************************************************************************/
+
+off_t lseek(int fd, off_t offset, int whence)
+{
+ FAR struct filelist *list;
+ FAR struct file *filep;
+ FAR struct inode *inode;
+ int err;
+
+ /* Did we get a valid file descriptor? */
+
+ if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS)
+ {
+ err = EBADF;
+ goto errout;
+ }
+
+ /* Get the thread-specific file list */
+
+ list = sched_getfiles();
+ if (!list)
+ {
+ err = EMFILE;
+ goto errout;
+ }
+
+ /* Is a driver registered? */
+
+ filep = &list->fl_files[fd];
+ inode = filep->f_inode;
+
+ if (inode && inode->u.i_ops)
+ {
+ /* Does it support the seek method */
+
+ if (inode->u.i_ops->seek)
+ {
+ /* Yes, then let it perform the seek */
+
+ err = (int)inode->u.i_ops->seek(filep, offset, whence);
+ if (err < 0)
+ {
+ err = -err;
+ goto errout;
+ }
+ }
+ else
+ {
+ /* No... there are a couple of default actions we can take */
+
+ switch (whence)
+ {
+ case SEEK_CUR:
+ offset += filep->f_pos;
+
+ case SEEK_SET:
+ if (offset >= 0)
+ {
+ filep->f_pos = offset; /* Might be beyond the end-of-file */
+ break;
+ }
+ else
+ {
+ err = EINVAL;
+ goto errout;
+ }
+ break;
+
+ case SEEK_END:
+ err = ENOSYS;
+ goto errout;
+
+ default:
+ err = EINVAL;
+ goto errout;
+ }
+ }
+ }
+
+ return filep->f_pos;
+
+errout:
+ set_errno(err);
+ return (off_t)ERROR;
+}
+
+#endif
diff --git a/nuttx/fs/fs_mkdir.c b/nuttx/fs/fs_mkdir.c
new file mode 100644
index 000000000..6e201ce34
--- /dev/null
+++ b/nuttx/fs/fs_mkdir.c
@@ -0,0 +1,130 @@
+/****************************************************************************
+ * fs/fs_mkdir.c
+ *
+ * Copyright (C) 2007, 2008 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/stat.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mkdir
+ *
+ * Description: Create a directory
+ *
+ ****************************************************************************/
+
+int mkdir(const char *pathname, mode_t mode)
+{
+ FAR struct inode *inode;
+ const char *relpath = NULL;
+ int ret;
+
+ /* Get an inode for this file */
+
+ inode = inode_find(pathname, &relpath);
+ if (!inode)
+ {
+ /* There is no mountpoint that includes in this path */
+
+ ret = ENOENT;
+ goto errout;
+ }
+
+ /* Verify that the inode is a valid mountpoint. */
+
+ if (!INODE_IS_MOUNTPT(inode) || !inode->u.i_mops)
+ {
+ ret = ENXIO;
+ goto errout_with_inode;
+ }
+
+ /* Perform the mkdir operation using the relative path
+ * at the mountpoint.
+ */
+
+ if (inode->u.i_mops->mkdir)
+ {
+ ret = inode->u.i_mops->mkdir(inode, relpath, mode);
+ if (ret < 0)
+ {
+ ret = -ret;
+ goto errout_with_inode;
+ }
+ }
+ else
+ {
+ ret = ENOSYS;
+ goto errout_with_inode;
+ }
+
+ /* Directory successfully created */
+
+ inode_release(inode);
+ return OK;
+
+ errout_with_inode:
+ inode_release(inode);
+ errout:
+ set_errno(ret);
+ return ERROR;
+}
+
diff --git a/nuttx/fs/fs_mount.c b/nuttx/fs/fs_mount.c
new file mode 100644
index 000000000..228c1fc6e
--- /dev/null
+++ b/nuttx/fs/fs_mount.c
@@ -0,0 +1,397 @@
+/****************************************************************************
+ * fs/fs_mount.c
+ *
+ * Copyright (C) 2007-2009, 2011-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/mount.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+
+#ifdef CONFIG_APPS_BINDIR
+# include <apps/apps.h>
+#endif
+
+#include "fs_internal.h"
+
+/* At least one filesystem must be defined, or this file will not compile.
+ * It may be desire-able to make filesystems dynamically registered at
+ * some time in the future, but at present, this file needs to know about
+ * every configured filesystem.
+ */
+
+#ifdef CONFIG_FS_READABLE
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Configuration ************************************************************/
+/* In the canonical case, a file system is bound to a block driver. However,
+ * some less typical cases a block driver is not required. Examples are
+ * pseudo file systems (like BINFS) and MTD file systems (like NXFFS).
+ *
+ * These file systems all require block drivers:
+ */
+
+#if defined(CONFIG_FS_FAT) || defined(CONFIG_FS_ROMFS)
+# define BDFS_SUPPORT 1
+#endif
+
+/* These file systems do not require block drivers */
+
+#if defined(CONFIG_FS_NXFFS) || defined(CONFIG_APPS_BINDIR) || defined(CONFIG_NFS)
+# define NONBDFS_SUPPORT
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct fsmap_t
+{
+ FAR const char *fs_filesystemtype;
+ FAR const struct mountpt_operations *fs_mops;
+};
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+#ifdef BDFS_SUPPORT
+#ifdef CONFIG_FS_FAT
+extern const struct mountpt_operations fat_operations;
+#endif
+#ifdef CONFIG_FS_ROMFS
+extern const struct mountpt_operations romfs_operations;
+#endif
+
+static const struct fsmap_t g_bdfsmap[] =
+{
+#ifdef CONFIG_FS_FAT
+ { "vfat", &fat_operations },
+#endif
+#ifdef CONFIG_FS_ROMFS
+ { "romfs", &romfs_operations },
+#endif
+ { NULL, NULL },
+};
+#endif /* BDFS_SUPPORT*/
+
+#ifdef NONBDFS_SUPPORT
+#ifdef CONFIG_FS_NXFFS
+extern const struct mountpt_operations nxffs_operations;
+#endif
+#ifdef CONFIG_NFS
+extern const struct mountpt_operations nfs_operations;
+#endif
+
+static const struct fsmap_t g_nonbdfsmap[] =
+{
+#ifdef CONFIG_FS_NXFFS
+ { "nxffs", &nxffs_operations },
+#endif
+#ifdef CONFIG_NFS
+ { "nfs", &nfs_operations },
+#endif
+#ifdef CONFIG_APPS_BINDIR
+ { "binfs", &binfs_operations },
+#endif
+ { NULL, NULL },
+};
+#endif /* NONBDFS_SUPPORT */
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mount_findfs
+ *
+ * Description:
+ * find the specified filesystem
+ *
+ ****************************************************************************/
+
+#if defined(BDFS_SUPPORT) || defined(NONBDFS_SUPPORT)
+static FAR const struct mountpt_operations *
+mount_findfs(FAR const struct fsmap_t *fstab, FAR const char *filesystemtype)
+{
+ FAR const struct fsmap_t *fsmap;
+
+ for (fsmap = fstab; fsmap->fs_filesystemtype; fsmap++)
+ {
+ if (strcmp(filesystemtype, fsmap->fs_filesystemtype) == 0)
+ {
+ return fsmap->fs_mops;
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mount
+ *
+ * Description:
+ * mount() attaches the filesystem specified by the 'source' block device
+ * name into the root file system at the path specified by 'target.'
+ *
+ * Return:
+ * Zero is returned on success; -1 is returned on an error and errno is
+ * set appropriately:
+ *
+ * EACCES A component of a path was not searchable or mounting a read-only
+ * filesystem was attempted without giving the MS_RDONLY flag.
+ * EBUSY 'source' is already mounted.
+ * EFAULT One of the pointer arguments points outside the user address
+ * space.
+ * EINVAL 'source' had an invalid superblock.
+ * ENODEV 'filesystemtype' not configured
+ * ENOENT A pathname was empty or had a nonexistent component.
+ * ENOMEM Could not allocate a memory to copy filenames or data into.
+ * ENOTBLK 'source' is not a block device
+ *
+ ****************************************************************************/
+
+int mount(FAR const char *source, FAR const char *target,
+ FAR const char *filesystemtype, unsigned long mountflags,
+ FAR const void *data)
+{
+#if defined(BDFS_SUPPORT) || defined(NONBDFS_SUPPORT)
+#ifdef BDFS_SUPPORT
+ FAR struct inode *blkdrvr_inode = NULL;
+#endif
+ FAR struct inode *mountpt_inode;
+ FAR const struct mountpt_operations *mops;
+ void *fshandle;
+ int errcode;
+ int ret;
+
+ /* Verify required pointer arguments */
+
+ DEBUGASSERT(target && filesystemtype);
+
+ /* Find the specified filesystem. Try the block driver file systems first */
+
+#ifdef BDFS_SUPPORT
+ if ((mops = mount_findfs(g_bdfsmap, filesystemtype)) != NULL)
+ {
+ /* Make sure that a block driver argument was provided */
+
+ DEBUGASSERT(source);
+
+ /* Find the block driver */
+
+ ret = find_blockdriver(source, mountflags, &blkdrvr_inode);
+ if (ret < 0)
+ {
+ fdbg("Failed to find block driver %s\n", source);
+ errcode = -ret;
+ goto errout;
+ }
+ }
+ else
+#endif /* BDFS_SUPPORT */
+#ifdef NONBDFS_SUPPORT
+ if ((mops = mount_findfs(g_nonbdfsmap, filesystemtype)) != NULL)
+ {
+ }
+ else
+#endif /* NONBDFS_SUPPORT */
+ {
+ fdbg("Failed to find file system %s\n", filesystemtype);
+ errcode = ENODEV;
+ goto errout;
+ }
+
+ /* Insert a dummy node -- we need to hold the inode semaphore
+ * to do this because we will have a momentarily bad structure.
+ */
+
+ inode_semtake();
+ ret = inode_reserve(target, &mountpt_inode);
+ if (ret < 0)
+ {
+ /* inode_reserve can fail for a couple of reasons, but the most likely
+ * one is that the inode already exists. inode_reserve may return:
+ *
+ * -EINVAL - 'path' is invalid for this operation
+ * -EEXIST - An inode already exists at 'path'
+ * -ENOMEM - Failed to allocate in-memory resources for the operation
+ */
+
+ fdbg("Failed to reserve inode\n");
+ errcode = -ret;
+ goto errout_with_semaphore;
+ }
+
+ /* Bind the block driver to an instance of the file system. The file
+ * system returns a reference to some opaque, fs-dependent structure
+ * that encapsulates this binding.
+ */
+
+ if (!mops->bind)
+ {
+ /* The filesystem does not support the bind operation ??? */
+
+ fdbg("Filesystem does not support bind\n");
+ errcode = EINVAL;
+ goto errout_with_mountpt;
+ }
+
+ /* Increment reference count for the reference we pass to the file system */
+
+#ifdef BDFS_SUPPORT
+#ifdef NONBDFS_SUPPORT
+ if (blkdrvr_inode)
+#endif
+ {
+ blkdrvr_inode->i_crefs++;
+ }
+#endif
+
+ /* On failure, the bind method returns -errorcode */
+
+#ifdef BDFS_SUPPORT
+ ret = mops->bind(blkdrvr_inode, data, &fshandle);
+#else
+ ret = mops->bind(NULL, data, &fshandle);
+#endif
+ if (ret != 0)
+ {
+ /* The inode is unhappy with the blkdrvr for some reason. Back out
+ * the count for the reference we failed to pass and exit with an
+ * error.
+ */
+
+ fdbg("Bind method failed: %d\n", ret);
+#ifdef BDFS_SUPPORT
+#ifdef NONBDFS_SUPPORT
+ if (blkdrvr_inode)
+#endif
+ {
+ blkdrvr_inode->i_crefs--;
+ }
+#endif
+ errcode = -ret;
+ goto errout_with_mountpt;
+ }
+
+ /* We have it, now populate it with driver specific information. */
+
+ INODE_SET_MOUNTPT(mountpt_inode);
+
+ mountpt_inode->u.i_mops = mops;
+#ifdef CONFIG_FILE_MODE
+ mountpt_inode->i_mode = mode;
+#endif
+ mountpt_inode->i_private = fshandle;
+ inode_semgive();
+
+ /* We can release our reference to the blkdrver_inode, if the filesystem
+ * wants to retain the blockdriver inode (which it should), then it must
+ * have called inode_addref(). There is one reference on mountpt_inode
+ * that will persist until umount() is called.
+ */
+
+#ifdef BDFS_SUPPORT
+#ifdef NONBDFS_SUPPORT
+ if (blkdrvr_inode)
+#endif
+ {
+ inode_release(blkdrvr_inode);
+ }
+#endif
+
+ return OK;
+
+ /* A lot of goto's! But they make the error handling much simpler */
+
+errout_with_mountpt:
+ mountpt_inode->i_crefs = 0;
+ inode_remove(target);
+ inode_semgive();
+#ifdef BDFS_SUPPORT
+#ifdef NONBDFS_SUPPORT
+ if (blkdrvr_inode)
+#endif
+ {
+ inode_release(blkdrvr_inode);
+ }
+#endif
+
+ inode_release(mountpt_inode);
+ goto errout;
+
+errout_with_semaphore:
+ inode_semgive();
+#ifdef BDFS_SUPPORT
+#ifdef NONBDFS_SUPPORT
+ if (blkdrvr_inode)
+#endif
+ {
+ inode_release(blkdrvr_inode);
+ }
+#endif
+
+errout:
+ set_errno(errcode);
+ return ERROR;
+
+#else
+ fdbg("No filesystems enabled\n");
+ set_errno(ENOSYS);
+ return error;
+#endif /* BDFS_SUPPORT || NONBDFS_SUPPORT */
+}
+
+#endif /* CONFIG_FS_READABLE */
+
diff --git a/nuttx/fs/fs_open.c b/nuttx/fs/fs_open.c
new file mode 100644
index 000000000..5b318b702
--- /dev/null
+++ b/nuttx/fs/fs_open.c
@@ -0,0 +1,206 @@
+/****************************************************************************
+ * fs_open.c
+ *
+ * Copyright (C) 2007-2009, 2011-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 <fcntl.h>
+#include <sched.h>
+#include <errno.h>
+#ifdef CONFIG_FILE_MODE
+#include <stdarg.h>
+#endif
+#include <nuttx/fs/fs.h>
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: inode_checkflags
+ *
+ * Description:
+ * Check if the access described by 'oflags' is supported on 'inode'
+ *
+ ****************************************************************************/
+
+int inode_checkflags(FAR struct inode *inode, int oflags)
+{
+ if (((oflags & O_RDOK) != 0 && !inode->u.i_ops->read) ||
+ ((oflags & O_WROK) != 0 && !inode->u.i_ops->write))
+ {
+ return -EACCES;
+ }
+ else
+ {
+ return OK;
+ }
+}
+
+/****************************************************************************
+ * Name: open
+ *
+ * Description:
+ * Standard 'open' interface
+ *
+ ****************************************************************************/
+
+int open(const char *path, int oflags, ...)
+{
+ FAR struct filelist *list;
+ FAR struct inode *inode;
+ FAR const char *relpath = NULL;
+#if defined(CONFIG_FILE_MODE) || !defined(CONFIG_DISABLE_MOUNTPOINT)
+ mode_t mode = 0666;
+#endif
+ int ret;
+ int fd;
+
+ /* Get the thread-specific file list */
+
+ list = sched_getfiles();
+ if (!list)
+ {
+ ret = EMFILE;
+ goto errout;
+ }
+
+#ifdef CONFIG_FILE_MODE
+# ifdef CONFIG_CPP_HAVE_WARNING
+# warning "File creation not implemented"
+# endif
+
+ /* If the file is opened for creation, then get the mode bits */
+
+ if (oflags & (O_WRONLY|O_CREAT) != 0)
+ {
+ va_list ap;
+ va_start(ap, oflags);
+ mode = va_arg(ap, mode_t);
+ va_end(ap);
+ }
+#endif
+
+ /* Get an inode for this file */
+
+ inode = inode_find(path, &relpath);
+ if (!inode)
+ {
+ /* "O_CREAT is not set and the named file does not exist. Or, a
+ * directory component in pathname does not exist or is a dangling
+ * symbolic link."
+ */
+
+ ret = ENOENT;
+ goto errout;
+ }
+
+ /* Verify that the inode is valid and either a "normal" or a mountpoint. We
+ * specifically exclude block drivers.
+ */
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ if ((!INODE_IS_DRIVER(inode) && !INODE_IS_MOUNTPT(inode)) || !inode->u.i_ops)
+#else
+ if (!INODE_IS_DRIVER(inode) || !inode->u.i_ops)
+#endif
+ {
+ ret = ENXIO;
+ goto errout_with_inode;
+ }
+
+ /* Make sure that the inode supports the requested access */
+
+ ret = inode_checkflags(inode, oflags);
+ if (ret < 0)
+ {
+ ret = -ret;
+ goto errout_with_inode;
+ }
+
+ /* Associate the inode with a file structure */
+
+ fd = files_allocate(inode, oflags, 0, 0);
+ if (fd < 0)
+ {
+ ret = EMFILE;
+ goto errout_with_inode;
+ }
+
+ /* Perform the driver open operation. NOTE that the open method may be
+ * called many times. The driver/mountpoint logic should handled this
+ * because it may also be closed that many times.
+ */
+
+ ret = OK;
+ if (inode->u.i_ops->open)
+ {
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ if (INODE_IS_MOUNTPT(inode))
+ {
+ ret = inode->u.i_mops->open((FAR struct file*)&list->fl_files[fd],
+ relpath, oflags, mode);
+ }
+ else
+#endif
+ {
+ ret = inode->u.i_ops->open((FAR struct file*)&list->fl_files[fd]);
+ }
+ }
+
+ if (ret < 0)
+ {
+ ret = -ret;
+ goto errout_with_fd;
+ }
+
+ return fd;
+
+ errout_with_fd:
+ files_release(fd);
+ errout_with_inode:
+ inode_release(inode);
+ errout:
+ set_errno(ret);
+ return ERROR;
+}
diff --git a/nuttx/fs/fs_openblockdriver.c b/nuttx/fs/fs_openblockdriver.c
new file mode 100644
index 000000000..c59a255b0
--- /dev/null
+++ b/nuttx/fs/fs_openblockdriver.c
@@ -0,0 +1,128 @@
+/****************************************************************************
+ * fs/fs_openblockdriver.c
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in pathname and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of pathname 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 <debug.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: open_blockdriver
+ *
+ * Description:
+ * Return the inode of the block driver specified by 'pathname'
+ *
+ * Inputs:
+ * pathname - the full path to the block driver to be opened
+ * mountflags - if MS_RDONLY is not set, then driver must support write
+ * operations (see include/sys/mount.h)
+ * ppinode - address of the location to return the inode reference
+ *
+ * Return:
+ * Returns zero on success or a negated errno on failure:
+ *
+ * EINVAL - pathname or pinode is NULL
+ * ENOENT - No block driver of this name is registered
+ * ENOTBLK - The inode associated with the pathname is not a block driver
+ * EACCESS - The MS_RDONLY option was not set but this driver does not
+ * support write access
+ *
+ ****************************************************************************/
+
+int open_blockdriver(FAR const char *pathname, int mountflags,
+ FAR struct inode **ppinode)
+{
+ FAR struct inode *inode;
+ int ret;
+
+ /* Minimal sanity checks */
+
+#ifdef CONFIG_DEBUG
+ if (!ppinode)
+ {
+ ret = -EINVAL;
+ goto errout;
+ }
+#endif
+
+ /* Find the inode associated with this block driver name. find_blockdriver
+ * will perform all additional error checking.
+ */
+
+ ret = find_blockdriver(pathname, mountflags, &inode);
+ if (ret < 0)
+ {
+ fdbg("Failed to file %s block driver\n", pathname);
+ goto errout;
+ }
+
+ /* Open the block driver. Note that no mutually exclusive access
+ * to the driver is enforced here. That must be done in the driver
+ * if needed.
+ */
+
+ if (inode->u.i_bops->open)
+ {
+ ret = inode->u.i_bops->open(inode);
+ if (ret < 0)
+ {
+ fdbg("%s driver open failed\n", pathname);
+ goto errout_with_inode;
+ }
+ }
+
+ *ppinode = inode;
+ return OK;
+
+errout_with_inode:
+ inode_release(inode);
+errout:
+ return ret;
+}
diff --git a/nuttx/fs/fs_opendir.c b/nuttx/fs/fs_opendir.c
new file mode 100644
index 000000000..1c87c984d
--- /dev/null
+++ b/nuttx/fs/fs_opendir.c
@@ -0,0 +1,312 @@
+/****************************************************************************
+ * fs/fs_opendir.c
+ *
+ * Copyright (C) 2007-2009, 2011 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 <stdbool.h>
+#include <dirent.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/dirent.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: open_mountpoint
+ *
+ * Description:
+ * Handle the case where the inode to be opened is within a mountpoint.
+ *
+ * Inputs:
+ * inode -- the inode of the mountpoint to open
+ * relpath -- the relative path within the mountpoint to open
+ * dir -- the dirent structure to be initialized
+ *
+ * Return:
+ * On success, OK is returned; Otherwise, a positive errno is returned.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+static inline int open_mountpoint(FAR struct inode *inode,
+ FAR const char *relpath,
+ FAR struct fs_dirent_s *dir)
+{
+ int ret;
+
+ /* The inode itself as the 'root' of mounted volume. The actually
+ * directory is at relpath into the* mounted filesystem.
+ *
+ * Verify that the mountpoint inode supports the opendir() method
+ */
+
+ if (!inode->u.i_mops || !inode->u.i_mops->opendir)
+ {
+ return ENOSYS;
+ }
+
+ /* Take reference to the mountpoint inode (fd_root). Note that we do
+ * not use inode_addref() because we already hold the tree semaphore.
+ */
+
+ inode->i_crefs++;
+
+ /* Perform the opendir() operation */
+
+ ret = inode->u.i_mops->opendir(inode, relpath, dir);
+ if (ret < 0)
+ {
+ /* We now need to back off our reference to the inode. We can't
+ * call inode_release() to do that unless we release the tree
+ * semaphore. The following should be safe because: (1) after the
+ * reference count was incremented above it should be >=1 so it should
+ * not decrement below zero, and (2) we hold the tree semaphore so no
+ * other thread should be able to change the reference count.
+ */
+
+ inode->i_crefs--;
+ DEBUGASSERT(inode->i_crefs >= 0);
+
+ /* Negate the error value so that it can be used to set errno */
+
+ return -ret;
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: open_pseudodir
+ *
+ * Description:
+ * Handle the case where the inode to be opened is within the top-level
+ * pseudo-file system.
+ *
+ * Inputs:
+ * inode -- the inode of the mountpoint to open
+ * dir -- the dirent structure to be initialized
+ *
+ * Return:
+ * On success, OK is returned; Otherwise, a positive errno is returned.
+ *
+ ****************************************************************************/
+
+static void open_pseudodir(FAR struct inode *inode, FAR struct fs_dirent_s *dir)
+{
+ /* We have a valid pseudo-filesystem node. Take two references on the
+ * inode -- one for the parent (fd_root) and one for the child (fd_next).
+ * Note that we do not call inode_addref because we are holding the tree
+ * semaphore and that would result in deadlock.
+ */
+
+ inode->i_crefs += 2;
+ dir->u.pseudo.fd_next = inode; /* This is the next node to use for readdir() */
+
+ /* Flag the inode as belonging to the pseudo-filesystem */
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ DIRENT_SETPSEUDONODE(dir->fd_flags);
+#endif
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: opendir
+ *
+ * Description:
+ * The opendir() function opens a directory stream corresponding to the
+ * directory name, and returns a pointer to the directory stream. The
+ * stream is positioned at the first entry in the directory.
+ *
+ * Inputs:
+ * path -- the directory to open
+ *
+ * Return:
+ * The opendir() function returns a pointer to the directory stream. On
+ * error, NULL is returned, and errno is set appropriately.
+ *
+ * EACCES - Permission denied.
+ * EMFILE - Too many file descriptors in use by process.
+ * ENFILE - Too many files are currently open in the
+ * system.
+ * ENOENT - Directory does not exist, or name is an empty
+ * string.
+ * ENOMEM - Insufficient memory to complete the operation.
+ * ENOTDIR - 'path' is not a directory.
+ *
+ ****************************************************************************/
+
+FAR DIR *opendir(FAR const char *path)
+{
+ FAR struct inode *inode = NULL;
+ FAR struct fs_dirent_s *dir;
+ FAR const char *relpath;
+ bool bisroot = false;
+ int ret;
+
+ /* If we are given 'nothing' then we will interpret this as
+ * request for the root inode.
+ */
+
+ inode_semtake();
+ if (!path || *path == 0 || strcmp(path, "/") == 0)
+ {
+ inode = root_inode;
+ bisroot = true;
+ relpath = NULL;
+ }
+ else
+ {
+ /* We don't know what to do with relative pathes */
+
+ if (*path != '/')
+ {
+ ret = -ENOTDIR;
+ goto errout_with_semaphore;
+ }
+
+ /* Find the node matching the path. */
+
+ inode = inode_search(&path, (FAR struct inode**)NULL, (FAR struct inode**)NULL, &relpath);
+ }
+
+ /* Did we get an inode? */
+
+ if (!inode)
+ {
+ /* 'path' is not a does not exist.*/
+
+ ret = ENOTDIR;
+ goto errout_with_semaphore;
+ }
+
+ /* Allocate a type DIR -- which is little more than an inode
+ * container.
+ */
+
+ dir = (FAR struct fs_dirent_s *)kzalloc(sizeof(struct fs_dirent_s));
+ if (!dir)
+ {
+ /* Insufficient memory to complete the operation.*/
+
+ ret = ENOMEM;
+ goto errout_with_semaphore;
+ }
+
+ /* Populate the DIR structure and return it to the caller. The way that
+ * we do this depends on whenever this is a "normal" pseudo-file-system
+ * inode or a file system mountpoint.
+ */
+
+ dir->fd_root = inode; /* Save the inode where we start */
+ dir->fd_position = 0; /* This is the position in the read stream */
+
+ /* First, handle the special case of the root inode. This must be
+ * special-cased here because the root inode might ALSO be a mountpoint.
+ */
+
+ if (bisroot)
+ {
+ /* Whatever payload the root inode carries, the root inode is always
+ * a directory inode in the pseudo-file system
+ */
+
+ open_pseudodir(inode, dir);
+ }
+
+ /* Is this a node in the pseudo filesystem? Or a mountpoint? If the node
+ * is the root (bisroot == TRUE), then this is a special case.
+ */
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ else if (INODE_IS_MOUNTPT(inode))
+ {
+ /* Yes, the node is a file system mointpoint. */
+
+ ret = open_mountpoint(inode, relpath, dir);
+ if (ret != OK)
+ {
+ goto errout_with_direntry;
+ }
+ }
+#endif
+ else
+ {
+ /* The node is part of the root pseudo file system. Does the inode have a child?
+ * If so that the child would be the 'root' of a list of nodes under
+ * the directory.
+ */
+
+ inode = inode->i_child;
+ if (!inode)
+ {
+ ret = ENOTDIR;
+ goto errout_with_direntry;
+ }
+
+ /* It looks we have a valid pseudo-filesystem directory node. */
+
+ open_pseudodir(inode, dir);
+ }
+
+ inode_semgive();
+ return ((DIR*)dir);
+
+ /* Nasty goto's make error handling simpler */
+
+errout_with_direntry:
+ kfree(dir);
+
+errout_with_semaphore:
+ inode_semgive();
+ set_errno(ret);
+ return NULL;
+}
diff --git a/nuttx/fs/fs_poll.c b/nuttx/fs/fs_poll.c
new file mode 100644
index 000000000..2e3adeb5d
--- /dev/null
+++ b/nuttx/fs/fs_poll.c
@@ -0,0 +1,327 @@
+/****************************************************************************
+ * fs/fs_poll.c
+ *
+ * Copyright (C) 2008-2009, 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 <stdint.h>
+#include <stdbool.h>
+#include <poll.h>
+#include <wdog.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/sched.h>
+#include <nuttx/clock.h>
+
+#include "fs_internal.h"
+
+#ifndef CONFIG_DISABLE_POLL
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define poll_semgive(sem) sem_post(sem)
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: poll_semtake
+ ****************************************************************************/
+
+static void poll_semtake(FAR sem_t *sem)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(sem) != 0)
+ {
+ /* The only case that an error should occur here is if
+ * the wait was awakened by a signal.
+ */
+
+ ASSERT(get_errno() == EINTR);
+ }
+}
+
+/****************************************************************************
+ * Name: poll_fdsetup
+ *
+ * Description:
+ * Configure (or unconfigure) one file/socket descriptor for the poll
+ * operation. If fds and sem are non-null, then the poll is being setup.
+ * if fds and sem are NULL, then the poll is being torn down.
+ *
+ ****************************************************************************/
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+static int poll_fdsetup(int fd, FAR struct pollfd *fds, bool setup)
+{
+ FAR struct filelist *list;
+ FAR struct file *this_file;
+ FAR struct inode *inode;
+ int ret = -ENOSYS;
+
+ /* Check for a valid file descriptor */
+
+ if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS)
+ {
+ /* Perform the socket ioctl */
+
+#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0
+ if ((unsigned int)fd < (CONFIG_NFILE_DESCRIPTORS+CONFIG_NSOCKET_DESCRIPTORS))
+ {
+ return net_poll(fd, fds, setup);
+ }
+ else
+#endif
+ {
+ return -EBADF;
+ }
+ }
+
+ /* Get the thread-specific file list */
+
+ list = sched_getfiles();
+ if (!list)
+ {
+ return -EMFILE;
+ }
+
+ /* Is a driver registered? Does it support the poll method?
+ * If not, return -ENOSYS
+ */
+
+ this_file = &list->fl_files[fd];
+ inode = this_file->f_inode;
+
+ if (inode && inode->u.i_ops && inode->u.i_ops->poll)
+ {
+ /* Yes, then setup the poll */
+
+ ret = (int)inode->u.i_ops->poll(this_file, fds, setup);
+ }
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: poll_setup
+ *
+ * Description:
+ * Setup the poll operation for each descriptor in the list.
+ *
+ ****************************************************************************/
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+static inline int poll_setup(FAR struct pollfd *fds, nfds_t nfds, sem_t *sem)
+{
+ int ret;
+ int i;
+
+ /* Process each descriptor in the list */
+
+ for (i = 0; i < nfds; i++)
+ {
+ /* Setup the poll descriptor */
+
+ fds[i].sem = sem;
+ fds[i].revents = 0;
+ fds[i].priv = NULL;
+
+ /* Set up the poll */
+
+ ret = poll_fdsetup(fds[i].fd, &fds[i], true);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: poll_teardown
+ *
+ * Description:
+ * Teardown the poll operation for each descriptor in the list and return
+ * the count of non-zero poll events.
+ *
+ ****************************************************************************/
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+static inline int poll_teardown(FAR struct pollfd *fds, nfds_t nfds, int *count)
+{
+ int status;
+ int ret = OK;
+ int i;
+
+ /* Process each descriptor in the list */
+
+ *count = 0;
+ for (i = 0; i < nfds; i++)
+ {
+ /* Teardown the poll */
+
+ status = poll_fdsetup(fds[i].fd, &fds[i], false);
+ if (status < 0)
+ {
+ ret = status;
+ }
+
+ /* Check if any events were posted */
+
+ if (fds[i].revents != 0)
+ {
+ (*count)++;
+ }
+
+ /* Un-initialize the poll structure */
+
+ fds[i].sem = NULL;
+ }
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: poll_timeout
+ *
+ * Description:
+ * The wdog expired before any other events were received.
+ *
+ ****************************************************************************/
+
+static void poll_timeout(int argc, uint32_t isem, ...)
+{
+ /* Wake up the poller */
+
+ FAR sem_t *sem = (FAR sem_t *)isem;
+ poll_semgive(sem);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: poll
+ *
+ * Description:
+ * poll() waits for one of a set of file descriptors to become ready to
+ * perform I/O. If none of the events requested (and no error) has
+ * occurred for any of the file descriptors, then poll() blocks until
+ * one of the events occurs.
+ *
+ * Inputs:
+ * fds - List of structures describing file descriptors to be monitored
+ * nfds - The number of entries in the list
+ * timeout - Specifies an upper limit on the time for which poll() will
+ * block in milliseconds. A negative value of timeout means an infinite
+ * timeout.
+ *
+ * Return:
+ * On success, the number of structures that have nonzero revents fields.
+ * A value of 0 indicates that the call timed out and no file descriptors
+ * were ready. On error, -1 is returned, and errno is set appropriately:
+ *
+ * EBADF - An invalid file descriptor was given in one of the sets.
+ * EFAULT - The fds address is invalid
+ * EINTR - A signal occurred before any requested event.
+ * EINVAL - The nfds value exceeds a system limit.
+ * ENOMEM - There was no space to allocate internal data structures.
+ * ENOSYS - One or more of the drivers supporting the file descriptor
+ * does not support the poll method.
+ *
+ ****************************************************************************/
+
+int poll(FAR struct pollfd *fds, nfds_t nfds, int timeout)
+{
+ WDOG_ID wdog;
+ sem_t sem;
+ int count = 0;
+ int ret;
+
+ sem_init(&sem, 0, 0);
+ ret = poll_setup(fds, nfds, &sem);
+ if (ret >= 0)
+ {
+ if (timeout >= 0)
+ {
+ /* Wait for the poll event with a timeout. Note that the
+ * millisecond timeout has to be converted to system clock
+ * ticks for wd_start
+ */
+
+ wdog = wd_create();
+ wd_start(wdog, MSEC2TICK(timeout), poll_timeout, 1, (uint32_t)&sem);
+ poll_semtake(&sem);
+ wd_delete(wdog);
+ }
+ else
+ {
+ /* Wait for the poll event with no timeout */
+
+ poll_semtake(&sem);
+ }
+
+ /* Teardown the poll operation and get the count of events */
+
+ ret = poll_teardown(fds, nfds, &count);
+ }
+
+ sem_destroy(&sem);
+
+ /* Check for errors */
+
+ if (ret < 0)
+ {
+ set_errno(-ret);
+ return ERROR;
+ }
+
+ return count;
+}
+
+#endif /* CONFIG_DISABLE_POLL */
diff --git a/nuttx/fs/fs_read.c b/nuttx/fs/fs_read.c
new file mode 100644
index 000000000..30dd4af03
--- /dev/null
+++ b/nuttx/fs/fs_read.c
@@ -0,0 +1,147 @@
+/****************************************************************************
+ * fs_read.c
+ *
+ * Copyright (C) 2007-2009, 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/socket.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+static inline ssize_t file_read(int fd, FAR void *buf, size_t nbytes)
+{
+ FAR struct filelist *list;
+ int ret = -EBADF;
+
+ /* Get the thread-specific file list */
+
+ list = sched_getfiles();
+ if (!list)
+ {
+ /* Failed to get the file list */
+
+ ret = -EMFILE;
+ }
+
+ /* Were we given a valid file descriptor? */
+
+ else if ((unsigned int)fd < CONFIG_NFILE_DESCRIPTORS)
+ {
+ FAR struct file *this_file = &list->fl_files[fd];
+ FAR struct inode *inode = this_file->f_inode;
+
+ /* Yes.. Was this file opened for read access? */
+
+ if ((this_file->f_oflags & O_RDOK) == 0)
+ {
+ /* No.. File is not read-able */
+
+ ret = -EACCES;
+ }
+
+ /* Is a driver or mountpoint registered? If so, does it support
+ * the read method?
+ */
+
+ else if (inode && inode->u.i_ops && inode->u.i_ops->read)
+ {
+ /* Yes.. then let it perform the read. NOTE that for the case
+ * of the mountpoint, we depend on the read methods bing
+ * identical in signature and position in the operations vtable.
+ */
+
+ ret = (int)inode->u.i_ops->read(this_file, (char*)buf, (size_t)nbytes);
+ }
+ }
+
+ /* If an error occurred, set errno and return -1 (ERROR) */
+
+ if (ret < 0)
+ {
+ set_errno(-ret);
+ return ERROR;
+ }
+
+ /* Otherwise, return the number of bytes read */
+
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+ssize_t read(int fd, FAR void *buf, size_t nbytes)
+{
+ /* Did we get a valid file descriptor? */
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+ if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS)
+#endif
+ {
+ /* No.. If networking is enabled, read() is the same as recv() with
+ * the flags parameter set to zero.
+ */
+
+#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0
+ return recv(fd, buf, nbytes, 0);
+#else
+ /* No networking... it is a bad descriptor in any event */
+
+ set_errno(EBADF);
+ return ERROR;
+#endif
+ }
+
+ /* The descriptor is in a valid range to file descriptor... do the read */
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+ return file_read(fd, buf, nbytes);
+#endif
+}
diff --git a/nuttx/fs/fs_readdir.c b/nuttx/fs/fs_readdir.c
new file mode 100644
index 000000000..f5b266392
--- /dev/null
+++ b/nuttx/fs/fs_readdir.c
@@ -0,0 +1,230 @@
+/****************************************************************************
+ * fs/fs_readdir.c
+ *
+ * Copyright (C) 2007-2009, 2011 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 <string.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/dirent.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: readpseudodir
+ ****************************************************************************/
+
+static inline int readpseudodir(struct fs_dirent_s *idir)
+{
+ FAR struct inode *prev;
+
+ /* Check if we are at the end of the list */
+
+ if (!idir->u.pseudo.fd_next)
+ {
+ /* End of file and error conditions are not distinguishable
+ * with readdir. Here we return -ENOENT to signal the end
+ * of the directory.
+ */
+
+ return -ENOENT;
+ }
+
+ /* Copy the inode name into the dirent structure */
+
+ strncpy(idir->fd_dir.d_name, idir->u.pseudo.fd_next->i_name, NAME_MAX+1);
+
+ /* If the node has file operations, we will say that it is
+ * a file.
+ */
+
+ idir->fd_dir.d_type = 0;
+ if (idir->u.pseudo.fd_next->u.i_ops)
+ {
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ if (INODE_IS_BLOCK(idir->u.pseudo.fd_next))
+ {
+ idir->fd_dir.d_type |= DTYPE_BLK;
+ }
+ if (INODE_IS_MOUNTPT(idir->u.pseudo.fd_next))
+ {
+ idir->fd_dir.d_type |= DTYPE_DIRECTORY;
+ }
+ else
+#endif
+ {
+ idir->fd_dir.d_type |= DTYPE_CHR;
+ }
+ }
+
+ /* If the node has child node(s), then we will say that it
+ * is a directory. NOTE: that the node can be both!
+ */
+
+ if (idir->u.pseudo.fd_next->i_child || !idir->u.pseudo.fd_next->u.i_ops)
+ {
+ idir->fd_dir.d_type |= DTYPE_DIRECTORY;
+ }
+
+ /* Now get the inode to vist next time that readdir() is called */
+
+ inode_semtake();
+
+ prev = idir->u.pseudo.fd_next;
+ idir->u.pseudo.fd_next = prev->i_peer; /* The next node to visit */
+
+ if (idir->u.pseudo.fd_next)
+ {
+ /* Increment the reference count on this next node */
+
+ idir->u.pseudo.fd_next->i_crefs++;
+ }
+
+ inode_semgive();
+
+ if (prev)
+ {
+ inode_release(prev);
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: readdir
+ *
+ * Description:
+ * The readdir() function returns a pointer to a dirent structure
+ * representing the next directory entry in the directory stream pointed
+ * to by dir. It returns NULL on reaching the end-of-file or if an error
+ * occurred.
+ *
+ * Inputs:
+ * dirp -- An instance of type DIR created by a previous call to opendir();
+ *
+ * Return:
+ * The readdir() function returns a pointer to a dirent structure, or NULL
+ * if an error occurs or end-of-file is reached. On error, errno is set
+ * appropriately.
+ *
+ * EBADF - Invalid directory stream descriptor dir
+ *
+ ****************************************************************************/
+
+FAR struct dirent *readdir(DIR *dirp)
+{
+ FAR struct fs_dirent_s *idir = (struct fs_dirent_s *)dirp;
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ struct inode *inode;
+#endif
+ int ret;
+
+ /* Sanity checks */
+
+ if (!idir || !idir->fd_root)
+ {
+ ret = EBADF;
+ goto errout;
+ }
+
+ /* The way we handle the readdir depends on the type of inode
+ * that we are dealing with.
+ */
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ inode = idir->fd_root;
+ if (INODE_IS_MOUNTPT(inode) && !DIRENT_ISPSEUDONODE(idir->fd_flags))
+ {
+ /* The node is a file system mointpoint. Verify that the mountpoint
+ * supports the readdir() method
+ */
+
+ if (!inode->u.i_mops || !inode->u.i_mops->readdir)
+ {
+ ret = EACCES;
+ goto errout;
+ }
+
+ /* Perform the readdir() operation */
+
+ ret = inode->u.i_mops->readdir(inode, idir);
+ }
+ else
+#endif
+ {
+ /* The node is part of the root pseudo file system */
+
+ ret = readpseudodir(idir);
+ }
+
+ /* ret < 0 is an error. Special case: ret = -ENOENT is end of file */
+
+ if ( ret < 0)
+ {
+ if (ret == -ENOENT)
+ {
+ ret = OK;
+ }
+ else
+ {
+ ret = -ret;
+ }
+ goto errout;
+ }
+
+ /* Success */
+
+ idir->fd_position++;
+ return &idir->fd_dir;
+
+errout:
+ set_errno(ret);
+ return NULL;
+}
+
diff --git a/nuttx/fs/fs_registerblockdriver.c b/nuttx/fs/fs_registerblockdriver.c
new file mode 100644
index 000000000..bb5a3121b
--- /dev/null
+++ b/nuttx/fs/fs_registerblockdriver.c
@@ -0,0 +1,124 @@
+/****************************************************************************
+ * fs/fs_registerblockdriver.c
+ *
+ * Copyright (C) 2007-2009, 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 <errno.h>
+
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: register_blockdriver
+ *
+ * Description:
+ * Register a block driver inode the pseudo file system.
+ *
+ * Input parameters:
+ * path - The path to the inode to create
+ * bops - The block driver operations structure
+ * mode - inmode priviledges (not used)
+ * priv - Private, user data that will be associated with the inode.
+ *
+ * Returned Value:
+ * Zero on success (with the inode point in 'inode'); A negated errno
+ * value is returned on a failure (all error values returned by
+ * inode_reserve):
+ *
+ * EINVAL - 'path' is invalid for this operation
+ * EEXIST - An inode already exists at 'path'
+ * ENOMEM - Failed to allocate in-memory resources for the operation
+ *
+ ****************************************************************************/
+
+int register_blockdriver(FAR const char *path,
+ FAR const struct block_operations *bops,
+ mode_t mode, FAR void *priv)
+{
+ FAR struct inode *node;
+ int ret;
+
+ /* Insert an inode for the device driver -- we need to hold the inode
+ * semaphore to prevent access to the tree while we this. This is because
+ * we will have a momentarily bad true until we populate the inode with
+ * valid data.
+ */
+
+ inode_semtake();
+ ret = inode_reserve(path, &node);
+ if (ret >= 0)
+ {
+ /* We have it, now populate it with block driver specific information. */
+
+ INODE_SET_BLOCK(node);
+
+ node->u.i_bops = bops;
+#ifdef CONFIG_FILE_MODE
+ node->i_mode = mode;
+#endif
+ node->i_private = priv;
+ ret = OK;
+ }
+
+ inode_semgive();
+ return ret;
+}
+
diff --git a/nuttx/fs/fs_registerdriver.c b/nuttx/fs/fs_registerdriver.c
new file mode 100644
index 000000000..cc1759ee7
--- /dev/null
+++ b/nuttx/fs/fs_registerdriver.c
@@ -0,0 +1,120 @@
+/****************************************************************************
+ * fs/fs_registerdriver.c
+ *
+ * Copyright (C) 2007-2009, 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 <errno.h>
+
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: register_driver
+ *
+ * Description:
+ * Register a character driver inode the pseudo file system.
+ *
+ * Input parameters:
+ * path - The path to the inode to create
+ * fops - The file operations structure
+ * mode - inmode priviledges (not used)
+ * priv - Private, user data that will be associated with the inode.
+ *
+ * Returned Value:
+ * Zero on success (with the inode point in 'inode'); A negated errno
+ * value is returned on a failure (all error values returned by
+ * inode_reserve):
+ *
+ * EINVAL - 'path' is invalid for this operation
+ * EEXIST - An inode already exists at 'path'
+ * ENOMEM - Failed to allocate in-memory resources for the operation
+ *
+ ****************************************************************************/
+
+int register_driver(FAR const char *path, FAR const struct file_operations *fops,
+ mode_t mode, FAR void *priv)
+{
+ FAR struct inode *node;
+ int ret;
+
+ /* Insert a dummy node -- we need to hold the inode semaphore because we
+ * will have a momentarily bad structure.
+ */
+
+ inode_semtake();
+ ret = inode_reserve(path, &node);
+ if (ret >= 0)
+ {
+ /* We have it, now populate it with driver specific information. */
+
+ INODE_SET_DRIVER(node);
+
+ node->u.i_ops = fops;
+#ifdef CONFIG_FILE_MODE
+ node->i_mode = mode;
+#endif
+ node->i_private = priv;
+ ret = OK;
+ }
+
+ inode_semgive();
+ return ret;
+}
diff --git a/nuttx/fs/fs_rename.c b/nuttx/fs/fs_rename.c
new file mode 100644
index 000000000..157775132
--- /dev/null
+++ b/nuttx/fs/fs_rename.c
@@ -0,0 +1,153 @@
+/****************************************************************************
+ * fs/fs_rename.c
+ *
+ * Copyright (C) 2007-2009 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 <stdio.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rename
+ *
+ * Description: Remove a file managed a mountpoint
+ *
+ ****************************************************************************/
+
+int rename(FAR const char *oldpath, FAR const char *newpath)
+{
+ FAR struct inode *oldinode;
+ FAR struct inode *newinode;
+ const char *oldrelpath = NULL;
+ const char *newrelpath = NULL;
+ int ret;
+
+ /* Get an inode for the old relpath */
+
+ oldinode = inode_find(oldpath, &oldrelpath);
+ if (!oldinode)
+ {
+ /* There is no mountpoint that includes in this path */
+
+ ret = ENOENT;
+ goto errout;
+ }
+
+ /* Verify that the old inode is a valid mountpoint. */
+
+ if (!INODE_IS_MOUNTPT(oldinode) || !oldinode->u.i_mops)
+ {
+ ret = ENXIO;
+ goto errout_with_oldinode;
+ }
+
+ /* Get an inode for the new relpath -- it should like on the same mountpoint */
+
+ newinode = inode_find(newpath, &newrelpath);
+ if (!newinode)
+ {
+ /* There is no mountpoint that includes in this path */
+
+ ret = ENOENT;
+ goto errout_with_oldinode;
+ }
+
+ /* Verify that the two pathes lie on the same mountpt inode */
+
+ if (oldinode != newinode)
+ {
+ ret = EXDEV;
+ goto errout_with_newinode;
+ }
+
+ /* Perform the rename operation using the relative pathes
+ * at the common mountpoint.
+ */
+
+ if (oldinode->u.i_mops->rename)
+ {
+ ret = oldinode->u.i_mops->rename(oldinode, oldrelpath, newrelpath);
+ if (ret < 0)
+ {
+ ret = -ret;
+ goto errout_with_newinode;
+ }
+ }
+ else
+ {
+ ret = ENOSYS;
+ goto errout_with_newinode;
+ }
+
+ /* Successfully renamed */
+
+ inode_release(oldinode);
+ inode_release(newinode);
+ return OK;
+
+ errout_with_newinode:
+ inode_release(newinode);
+ errout_with_oldinode:
+ inode_release(oldinode);
+ errout:
+ set_errno(ret);
+ return ERROR;
+}
diff --git a/nuttx/fs/fs_rewinddir.c b/nuttx/fs/fs_rewinddir.c
new file mode 100644
index 000000000..c22c15f18
--- /dev/null
+++ b/nuttx/fs/fs_rewinddir.c
@@ -0,0 +1,145 @@
+/****************************************************************************
+ * fs/fs_rewinddir.c
+ *
+ * Copyright (C) 2007-2009, 2011 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 <dirent.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/dirent.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rewindpseudodir
+ ****************************************************************************/
+
+static inline void rewindpseudodir(struct fs_dirent_s *idir)
+{
+ struct inode *prev;
+
+ inode_semtake();
+
+ /* Reset the position to the beginning */
+
+ prev = idir->u.pseudo.fd_next; /* (Save to delete later) */
+ idir->u.pseudo.fd_next = idir->fd_root; /* The next node to visit */
+ idir->fd_position = 0; /* Reset position */
+
+ /* Increment the reference count on the root=next node. We
+ * should now have two references on the inode.
+ */
+
+ idir->fd_root->i_crefs++;
+ inode_semgive();
+
+ /* Then release the reference to the old next inode */
+
+ if (prev)
+ {
+ inode_release(prev);
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rewinddir
+ *
+ * Description:
+ * The rewinddir() function resets the position of the
+ * directory stream dir to the beginning of the directory.
+ *
+ * Inputs:
+ * dirp -- An instance of type DIR created by a previous
+ * call to opendir();
+ *
+ * Return:
+ * None
+ *
+ ****************************************************************************/
+
+void rewinddir(FAR DIR *dirp)
+{
+ struct fs_dirent_s *idir = (struct fs_dirent_s *)dirp;
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ struct inode *inode;
+#endif
+
+ /* Sanity checks */
+
+ if (!idir || !idir->fd_root)
+ {
+ return;
+ }
+
+ /* The way we handle the readdir depends on the type of inode
+ * that we are dealing with.
+ */
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ inode = idir->fd_root;
+ if (INODE_IS_MOUNTPT(inode))
+ {
+ /* The node is a file system mointpoint. Verify that the mountpoint
+ * supports the rewinddir() method
+ */
+
+ if (inode->u.i_mops && inode->u.i_mops->rewinddir)
+ {
+ /* Perform the rewinddir() operation */
+
+ inode->u.i_mops->rewinddir(inode, idir);
+ }
+ }
+ else
+#endif
+ {
+ /* The node is part of the root pseudo file system */
+
+ rewindpseudodir(idir);
+ }
+}
diff --git a/nuttx/fs/fs_rmdir.c b/nuttx/fs/fs_rmdir.c
new file mode 100644
index 000000000..6c64de5cf
--- /dev/null
+++ b/nuttx/fs/fs_rmdir.c
@@ -0,0 +1,130 @@
+/****************************************************************************
+ * fs/fs_rmdir.c
+ *
+ * Copyright (C) 2007-2009 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 <unistd.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rmdir
+ *
+ * Description: Remove a file managed a mountpoint
+ *
+ ****************************************************************************/
+
+int rmdir(FAR const char *pathname)
+{
+ FAR struct inode *inode;
+ const char *relpath = NULL;
+ int ret;
+
+ /* Get an inode for this file */
+
+ inode = inode_find(pathname, &relpath);
+ if (!inode)
+ {
+ /* There is no mountpoint that includes in this path */
+
+ ret = ENOENT;
+ goto errout;
+ }
+
+ /* Verify that the inode is a valid mountpoint. */
+
+ if (!INODE_IS_MOUNTPT(inode) || !inode->u.i_mops)
+ {
+ ret = ENXIO;
+ goto errout_with_inode;
+ }
+
+ /* Perform the rmdir operation using the relative path
+ * at the mountpoint.
+ */
+
+ if (inode->u.i_mops->rmdir)
+ {
+ ret = inode->u.i_mops->rmdir(inode, relpath);
+ if (ret < 0)
+ {
+ ret = -ret;
+ goto errout_with_inode;
+ }
+ }
+ else
+ {
+ ret = ENOSYS;
+ goto errout_with_inode;
+ }
+
+ /* Successfully removed the directory */
+
+ inode_release(inode);
+ return OK;
+
+ errout_with_inode:
+ inode_release(inode);
+ errout:
+ set_errno(ret);
+ return ERROR;
+}
+
diff --git a/nuttx/fs/fs_seekdir.c b/nuttx/fs/fs_seekdir.c
new file mode 100644
index 000000000..781d35f27
--- /dev/null
+++ b/nuttx/fs/fs_seekdir.c
@@ -0,0 +1,230 @@
+/****************************************************************************
+ * fs/fs_seekdir.c
+ *
+ * Copyright (C) 2007, 2008, 2011 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 <dirent.h>
+#include <errno.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/dirent.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: seekpseudodir
+ ****************************************************************************/
+
+static inline void seekpseudodir(struct fs_dirent_s *idir, off_t offset)
+{
+ struct inode *curr;
+ struct inode *prev;
+ off_t pos;
+
+ /* Determine a starting point for the seek. If the seek
+ * is "forward" from the current position, then we will
+ * start at the current poisition. Otherwise, we will
+ * "rewind" to the root dir.
+ */
+
+ if ( offset < idir->fd_position )
+ {
+ pos = 0;
+ curr = idir->fd_root;
+ }
+ else
+ {
+ pos = idir->fd_position;
+ curr = idir->u.pseudo.fd_next;
+ }
+
+ /* Traverse the peer list starting at the 'root' of the
+ * the list until we find the node at 'offset". If devices
+ * are being registered and unregistered, then this can
+ * be a very unpredictable operation.
+ */
+
+ inode_semtake();
+ for (; curr && pos != offset; pos++, curr = curr->i_peer);
+
+ /* Now get the inode to vist next time that readdir() is called */
+
+ prev = idir->u.pseudo.fd_next;
+ idir->u.pseudo.fd_next = curr; /* The next node to visit (might be null) */
+ idir->fd_position = pos; /* Might be beyond the last dirent */
+
+ if (curr)
+ {
+ /* Increment the reference count on this next node */
+
+ curr->i_crefs++;
+ }
+
+ inode_semgive();
+
+ if (prev)
+ {
+ inode_release(prev);
+ }
+}
+
+/****************************************************************************
+ * Name: seekmountptdir
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+static inline void seekmountptdir(struct fs_dirent_s *idir, off_t offset)
+{
+ struct inode *inode;
+ off_t pos;
+
+ /* Determine a starting point for the seek. If the seek
+ * is "forward" from the current position, then we will
+ * start at the current poisition. Otherwise, we will
+ * "rewind" to the root dir.
+ */
+
+ inode = idir->fd_root;
+ if ( offset < idir->fd_position )
+ {
+ if (inode->u.i_mops && inode->u.i_mops->rewinddir)
+ {
+ /* Perform the rewinddir() operation */
+
+ inode->u.i_mops->rewinddir(inode, idir);
+ pos = 0;
+ }
+ else
+ {
+ /* We can't do the seek and there is no way to return
+ * an error indication.
+ */
+
+ return;
+ }
+ }
+ else
+ {
+ pos = idir->fd_position;
+ }
+
+ /* This is a brute force approach... we will just read
+ * directory entries until we are at the desired position.
+ */
+
+ while (pos < offset)
+ {
+ if (!inode->u.i_mops || !inode->u.i_mops->readdir ||
+ inode->u.i_mops->readdir(inode, idir) < 0)
+ {
+ /* We can't read the next entry and there is no way to return
+ * an error indication.
+ */
+
+ return;
+ }
+
+ /* Increment the position on each successful read */
+
+ pos++;
+ }
+
+ /* If we get here the directory position has been successfully set */
+
+ idir->fd_position = pos;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: seekdir
+ *
+ * Description:
+ * The seekdir() function sets the location in the directory stream from
+ * which the next readdir() call will start. seekdir() should be used with
+ * an offset returned by telldir().
+ *
+ * Inputs:
+ * dirp -- An instance of type DIR created by a previous
+ * call to opendir();
+ * offset -- offset to seek to
+ *
+ * Return:
+ * None
+ *
+ ****************************************************************************/
+
+void seekdir(FAR DIR *dirp, off_t offset)
+{
+ struct fs_dirent_s *idir = (struct fs_dirent_s *)dirp;
+
+ /* Sanity checks */
+
+ if (!idir || !idir->fd_root)
+ {
+ return;
+ }
+
+ /* The way we handle the readdir depends on the type of inode
+ * that we are dealing with.
+ */
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ if (INODE_IS_MOUNTPT(idir->fd_root))
+ {
+ /* The node is a file system mointpoint */
+
+ seekmountptdir(idir, offset);
+ }
+ else
+#endif
+ {
+ /* The node is part of the root pseudo file system */
+
+ seekpseudodir(idir, offset);
+ }
+}
diff --git a/nuttx/fs/fs_select.c b/nuttx/fs/fs_select.c
new file mode 100644
index 000000000..e40c5bc13
--- /dev/null
+++ b/nuttx/fs/fs_select.c
@@ -0,0 +1,239 @@
+/****************************************************************************
+ * fs/fs_select.c
+ *
+ * Copyright (C) 2008-2009, 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/select.h>
+
+#include <string.h>
+#include <poll.h>
+#include <time.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+#ifndef CONFIG_DISABLE_POLL
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: select
+ *
+ * Description:
+ * select() allows a program to monitor multiple file descriptors, waiting
+ * until one or more of the file descriptors become "ready" for some class
+ * of I/O operation (e.g., input possible). A file descriptor is
+ * considered ready if it is possible to perform the corresponding I/O
+ * operation (e.g., read(2)) without blocking.
+ *
+ * NOTE: poll() is the fundamental API for performing such monitoring
+ * operation under NuttX. select() is provided for compatibility and
+ * is simply a layer of added logic on top of poll(). As such, select()
+ * is more wasteful of resources and poll() is the recommended API to be
+ * used.
+ *
+ * Input parameters:
+ * nfds - the maximum fd number (+1) of any descriptor in any of the
+ * three sets.
+ * readfds - the set of descriptions to monitor for read-ready events
+ * writefds - the set of descriptions to monitor for write-ready events
+ * exceptfds - the set of descriptions to monitor for error events
+ * timeout - Return at this time if none of these events of interest
+ * occur.
+ *
+ * Return:
+ * 0: Timer expired
+ * >0: The number of bits set in the three sets of descriptors
+ * -1: An error occurred (errno will be set appropriately)
+ *
+ ****************************************************************************/
+
+int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds,
+ FAR fd_set *exceptfds, FAR struct timeval *timeout)
+{
+ struct pollfd *pollset;
+ int fd;
+ int npfds;
+ int msec;
+ int ndx;
+ int ret;
+
+ /* Allocate the descriptor list for poll() */
+
+ pollset = (struct pollfd *)kzalloc(nfds * sizeof(struct pollfd));
+ if (!pollset)
+ {
+ set_errno(ENOMEM);
+ return ERROR;
+ }
+
+ /* Initialize the descriptor list for poll() */
+
+ for (fd = 0, npfds = 0; fd < nfds; fd++)
+ {
+ int incr = 0;
+
+ /* The readfs set holds the set of FDs that the caller can be assured
+ * of reading from without blocking. Note that POLLHUP is included as
+ * a read-able condition. POLLHUP will be reported at the end-of-file
+ * or when a connection is lost. In either case, the read() can then
+ * be performed without blocking.
+ */
+
+ if (readfds && FD_ISSET(fd, readfds))
+ {
+ pollset[npfds].fd = fd;
+ pollset[npfds].events |= POLLIN;
+ incr = 1;
+ }
+
+ /* The writefds set holds the set of FDs that the caller can be assured
+ * of writing to without blocking.
+ */
+
+ if (writefds && FD_ISSET(fd, writefds))
+ {
+ pollset[npfds].fd = fd;
+ pollset[npfds].events |= POLLOUT;
+ incr = 1;
+ }
+
+ /* The exceptfds set holds the set of FDs that are watched for exceptions */
+
+ if (exceptfds && FD_ISSET(fd, exceptfds))
+ {
+ pollset[npfds].fd = fd;
+ incr = 1;
+ }
+
+ npfds += incr;
+ }
+
+ /* Convert the timeout to milliseconds */
+
+ msec = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
+
+ /* Then let poll do all of the real work. */
+
+ ret = poll(pollset, npfds, msec);
+
+ /* Now set up the return values */
+
+ if (readfds)
+ {
+ memset(readfds, 0, sizeof(fd_set));
+ }
+
+ if (writefds)
+ {
+ memset(writefds, 0, sizeof(fd_set));
+ }
+
+ if (exceptfds)
+ {
+ memset(exceptfds, 0, sizeof(fd_set));
+ }
+
+ /* Convert the poll descriptor list back into selects 3 bitsets */
+
+ if (ret > 0)
+ {
+ ret = 0;
+ for (ndx = 0; ndx < npfds; ndx++)
+ {
+ /* Check for read conditions. Note that POLLHUP is included as a
+ * read condition. POLLHUP will be reported when no more data will
+ * be available (such as when a connection is lost). In either
+ * case, the read() can then be performed without blocking.
+ */
+
+ if (readfds)
+ {
+ if (pollset[ndx].revents & (POLLIN|POLLHUP))
+ {
+ FD_SET(pollset[ndx].fd, readfds);
+ ret++;
+ }
+ }
+
+ /* Check for write conditions */
+
+ if (writefds)
+ {
+ if (pollset[ndx].revents & POLLOUT)
+ {
+ FD_SET(pollset[ndx].fd, writefds);
+ ret++;
+ }
+ }
+
+ /* Check for exceptions */
+
+ if (exceptfds)
+ {
+ if (pollset[ndx].revents & POLLERR)
+ {
+ FD_SET(pollset[ndx].fd, exceptfds);
+ ret++;
+ }
+ }
+ }
+ }
+
+ kfree(pollset);
+ return ret;
+}
+
+#endif /* CONFIG_DISABLE_POLL */
+
diff --git a/nuttx/fs/fs_stat.c b/nuttx/fs/fs_stat.c
new file mode 100644
index 000000000..cf27e87a6
--- /dev/null
+++ b/nuttx/fs/fs_stat.c
@@ -0,0 +1,223 @@
+/****************************************************************************
+ * fs/fs_stat.c
+ *
+ * Copyright (C) 2007-2009 , 2012Gregory 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/stat.h>
+#include <string.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: statpseudo
+ ****************************************************************************/
+
+static inline int statpseudo(FAR struct inode *inode, FAR struct stat *buf)
+{
+ /* Most of the stat entries just do not apply */
+
+ memset(buf, 0, sizeof(struct stat) );
+ if (inode->u.i_ops)
+ {
+ if (inode->u.i_ops->read)
+ {
+ buf->st_mode = S_IROTH|S_IRGRP|S_IRUSR;
+ }
+
+ if (inode->u.i_ops->write)
+ {
+ buf->st_mode |= S_IWOTH|S_IWGRP|S_IWUSR;
+ }
+
+ if (INODE_IS_MOUNTPT(inode))
+ {
+ buf->st_mode |= S_IFDIR;
+ }
+ else if (INODE_IS_BLOCK(inode))
+ {
+ /* What is it also has child inodes? */
+
+ buf->st_mode |= S_IFBLK;
+ }
+ else
+ {
+ /* What is it if it also has child inodes? */
+
+ buf->st_mode |= S_IFCHR;
+ }
+ }
+ else
+ {
+ /* If it has no operations, then it must just be a intermediate
+ * node in the inode tree. It is something like a directory.
+ * We'll say that all pseudo-directories are read-able but not
+ * write-able.
+ */
+
+ buf->st_mode |= S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: statroot
+ ****************************************************************************/
+
+static inline int statroot(FAR struct stat *buf)
+{
+ /* There is no inode associated with the fake root directory */
+
+ memset(buf, 0, sizeof(struct stat) );
+ buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
+ return OK;
+}
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stat
+ *
+ * Return: Zero on success; -1 on failure with errno set:
+ *
+ * EACCES Search permission is denied for one of the directories in the
+ * path prefix of path.
+ * EFAULT Bad address.
+ * ENOENT A component of the path path does not exist, or the path is an
+ * empty string.
+ * ENOMEM Out of memory
+ * ENOTDIR A component of the path is not a directory.
+ *
+ ****************************************************************************/
+
+int stat(const char *path, FAR struct stat *buf)
+{
+ FAR struct inode *inode;
+ const char *relpath = NULL;
+ int ret = OK;
+
+ /* Sanity checks */
+
+ if (!path || !buf)
+ {
+ ret = EFAULT;
+ goto errout;
+ }
+
+ if (!path[0])
+ {
+ ret = ENOENT;
+ goto errout;
+ }
+
+ /* Check for the fake root directory (which has no inode) */
+
+ if (strcmp(path, "/") == 0)
+ {
+ return statroot(buf);
+ }
+
+ /* Get an inode for this file */
+
+ inode = inode_find(path, &relpath);
+ if (!inode)
+ {
+ /* This name does not refer to a psudeo-inode and there is no
+ * mountpoint that includes in this path.
+ */
+
+ ret = ENOENT;
+ goto errout;
+ }
+
+ /* The way we handle the stat depends on the type of inode that we
+ * are dealing with.
+ */
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ if (INODE_IS_MOUNTPT(inode))
+ {
+ /* The node is a file system mointpoint. Verify that the mountpoint
+ * supports the stat() method
+ */
+
+ if (inode->u.i_mops && inode->u.i_mops->stat)
+ {
+ /* Perform the stat() operation */
+
+ ret = inode->u.i_mops->stat(inode, relpath, buf);
+ }
+ }
+ else
+#endif
+ {
+ /* The node is part of the root pseudo file system */
+
+ ret = statpseudo(inode, buf);
+ }
+
+ /* Check if the stat operation was successful */
+
+ if (ret < 0)
+ {
+ ret = -ret;
+ goto errout_with_inode;
+ }
+
+ /* Successfully stat'ed the file */
+
+ inode_release(inode);
+ return OK;
+
+/* Failure conditions always set the errno appropriately */
+
+errout_with_inode:
+ inode_release(inode);
+errout:
+ set_errno(ret);
+ return ERROR;
+}
diff --git a/nuttx/fs/fs_statfs.c b/nuttx/fs/fs_statfs.c
new file mode 100644
index 000000000..df7321bbd
--- /dev/null
+++ b/nuttx/fs/fs_statfs.c
@@ -0,0 +1,165 @@
+/****************************************************************************
+ * fs/fs_statfs.c
+ *
+ * Copyright (C) 2007-2009, 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/statfs.h>
+#include <string.h>
+#include <limits.h>
+#include <sched.h>
+#include <errno.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: statpseudo
+ ****************************************************************************/
+
+static inline int statpseudofs(FAR struct inode *inode, FAR struct statfs *buf)
+{
+ memset(buf, 0, sizeof(struct statfs));
+ buf->f_type = PROC_SUPER_MAGIC;
+ buf->f_namelen = NAME_MAX;
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stat
+ *
+ * Return: Zero on success; -1 on failure with errno set:
+ *
+ * EACCES Search permission is denied for one of the directories in the
+ * path prefix of path.
+ * EFAULT Bad address.
+ * ENOENT A component of the path path does not exist, or the path is an
+ * empty string.
+ * ENOMEM Out of memory
+ * ENOTDIR A component of the path is not a directory.
+ * ENOSYS The file system does not support this call.
+ *
+ ****************************************************************************/
+
+int statfs(FAR const char *path, FAR struct statfs *buf)
+{
+ FAR struct inode *inode;
+ FAR const char *relpath = NULL;
+ int ret = OK;
+
+ /* Sanity checks */
+
+ if (!path || !buf)
+ {
+ ret = EFAULT;
+ goto errout;
+ }
+
+ if (!path[0])
+ {
+ ret = ENOENT;
+ goto errout;
+ }
+
+ /* Get an inode for this file */
+
+ inode = inode_find(path, &relpath);
+ if (!inode)
+ {
+ /* This name does not refer to a psudeo-inode and there is no
+ * mountpoint that includes in this path.
+ */
+
+ ret = ENOENT;
+ goto errout;
+ }
+
+ /* The way we handle the statfs depends on the type of inode that we
+ * are dealing with.
+ */
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ if (INODE_IS_MOUNTPT(inode))
+ {
+ /* The node is a file system mointpoint. Verify that the mountpoint
+ * supports the statfs() method
+ */
+
+ if (inode->u.i_mops && inode->u.i_mops->statfs)
+ {
+ /* Perform the statfs() operation */
+
+ ret = inode->u.i_mops->statfs(inode, buf);
+ }
+ }
+ else
+#endif
+ {
+ /* The node is part of the root pseudo file system */
+
+ ret = statpseudofs(inode, buf);
+ }
+
+ /* Check if the statfs operation was successful */
+
+ if (ret < 0)
+ {
+ ret = -ret;
+ goto errout_with_inode;
+ }
+
+ /* Successfully statfs'ed the file */
+
+ inode_release(inode);
+ return OK;
+
+/* Failure conditions always set the errno appropriately */
+
+errout_with_inode:
+ inode_release(inode);
+errout:
+ set_errno(ret);
+ return ERROR;
+}
diff --git a/nuttx/fs/fs_syslog.c b/nuttx/fs/fs_syslog.c
new file mode 100644
index 000000000..1d569082a
--- /dev/null
+++ b/nuttx/fs/fs_syslog.c
@@ -0,0 +1,515 @@
+/****************************************************************************
+ * fs/syslog.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 <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/arch.h>
+#include <nuttx/syslog.h>
+
+#include "fs_internal.h"
+
+#if defined(CONFIG_SYSLOG) && defined(CONFIG_SYSLOG_CHAR)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+ /* Open the device/file write-only, try to create (file) it if it doesn't
+ * exist, if the file that already exists, then append the new log data to
+ * end of the file.
+ */
+
+#define SYSLOG_OFLAGS (O_WRONLY | O_CREAT | O_APPEND)
+
+/* An invalid thread ID */
+
+#define NO_HOLDER ((pid_t)-1)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This enumeration represents the state of the SYSLOG device interface */
+
+enum syslog_state_e
+{
+ SYSLOG_UNINITIALIZED = 0, /* SYSLOG has not been initialized */
+ SYSLOG_INITIALIZING, /* SYSLOG is being initialized */
+ SYSLOG_REOPEN, /* SYSLOG open failed... try again later */
+ SYSLOG_FAILURE, /* SYSLOG open failed... don't try again */
+ SYSLOG_OPENED, /* SYSLOG device is open and ready to use */
+};
+
+/* This structure contains all SYSLOGing state information */
+
+struct syslog_dev_s
+{
+ uint8_t sl_state; /* See enum syslog_state_e */
+ sem_t sl_sem; /* Enforces mutually exclusive access */
+ pid_t sl_holder; /* PID of the thread that holds the semaphore */
+ struct file sl_file; /* The syslog file structure */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is the device structure for the console or syslogging function. */
+
+static struct syslog_dev_s g_sysdev;
+static const uint8_t g_syscrlf[2] = { '\r', '\n' };
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: syslog_takesem
+ *
+ * Description:
+ * Write to the syslog device
+ *
+ ****************************************************************************/
+
+static inline int syslog_takesem(void)
+{
+ pid_t me = getpid();
+ int ret;
+
+ /* Does this thread already hold the semaphore? That could happen if
+ * we wer called recursively, i.e., if the logic kicked off by
+ * syslog_write() where to generate more debug output. Return an error
+ * in that case.
+ */
+
+ if (g_sysdev.sl_holder == me)
+ {
+ /* Return an error (instead of deadlocking) */
+
+ return -EWOULDBLOCK;
+ }
+
+ /* Either the semaphore is available or is currently held by another
+ * thread. Wait for it to become available.
+ */
+
+ ret = sem_wait(&g_sysdev.sl_sem);
+ if (ret < 0)
+ {
+ return -get_errno();
+ }
+
+ /* We hold the semaphore. We can safely mark ourself as the holder
+ * of the semaphore.
+ */
+
+ g_sysdev.sl_holder = me;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: syslog_givesem
+ *
+ * Description:
+ * Write to the syslog device
+ *
+ ****************************************************************************/
+
+static inline void syslog_givesem(void)
+{
+ pid_t me = getpid();
+ DEBUGASSERT(g_sysdev.sl_holder == me);
+
+ /* Relinquish the semaphore */
+
+ g_sysdev.sl_holder = NO_HOLDER;
+ sem_post(&g_sysdev.sl_sem);
+}
+
+/****************************************************************************
+ * Name: syslog_write
+ *
+ * Description:
+ * Write to the syslog device
+ *
+ ****************************************************************************/
+
+static inline ssize_t syslog_write(FAR const void *buf, size_t nbytes)
+{
+ FAR struct inode *inode;
+
+ /* Let the driver perform the write */
+
+ inode = g_sysdev.sl_file.f_inode;
+ return inode->u.i_ops->write(&g_sysdev.sl_file, buf, nbytes);
+}
+
+/****************************************************************************
+ * Name: syslog_flush
+ *
+ * Description:
+ * Flush any buffer data in the file system to media.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+static inline void syslog_flush(void)
+{
+ FAR struct inode *inode = g_sysdev.sl_file.f_inode;
+
+ /* Is this a mountpoint? Does it support the sync method? */
+
+ if (INODE_IS_MOUNTPT(inode) && inode->u.i_mops->sync)
+ {
+ /* Yes... synchronize to the stream */
+
+ (void)inode->u.i_mops->sync(&g_sysdev.sl_file);
+ }
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: syslog_initialize
+ *
+ * Description:
+ * Initialize to use the character device (or file) at
+ * CONFIG_SYSLOG_DEVPATH as the SYSLOG sink.
+ *
+ * NOTE that this implementation excludes using a network connection as
+ * SYSLOG device. That would be a good extension.
+ *
+ ****************************************************************************/
+
+int syslog_initialize(void)
+{
+ FAR struct inode *inode;
+ FAR const char *relpath = NULL;
+ int ret;
+
+ /* At this point, the only expected states are SYSLOG_UNINITIALIZED or
+ * SYSLOG_REOPEN.. Not SYSLOG_INITIALIZING, SYSLOG_FAILURE, SYSLOG_OPENED.
+ */
+
+ DEBUGASSERT(g_sysdev.sl_state == SYSLOG_UNINITIALIZED ||
+ g_sysdev.sl_state == SYSLOG_REOPEN);
+
+ g_sysdev.sl_state = SYSLOG_INITIALIZING;
+
+ /* Try to open the device.
+ *
+ * Note that we cannot just call open. The syslog device must work on all
+ * threads. Open returns a file descriptor that is valid only for the
+ * task that opened the device (and its pthread children). Instead, we
+ * essentially re-implement the guts of open() here so that we can get to
+ * the thread-independent structures of the inode.
+ */
+
+ /* Get an inode for this file/device */
+
+ inode = inode_find(CONFIG_SYSLOG_DEVPATH, &relpath);
+ if (!inode)
+ {
+ /* The inode was not found. In this case, we will attempt to re-open
+ * the device repeatedly. The assumption is that the device path is
+ * value but that the driver has not yet been registered.
+ */
+
+ g_sysdev.sl_state = SYSLOG_REOPEN;
+ return -ENOENT;
+ }
+
+ /* Verify that the inode is valid and either a character driver or a
+ * mountpoint.
+ */
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ if ((!INODE_IS_DRIVER(inode) && !INODE_IS_MOUNTPT(inode)))
+#else
+ if (!INODE_IS_DRIVER(inode))
+#endif
+ {
+ ret = ENXIO;
+ goto errout_with_inode;
+ }
+
+ /* Make sure that the "entity" at this inode supports write access */
+
+ if (!inode->u.i_ops || !inode->u.i_ops->write)
+ {
+ return -EACCES;
+ goto errout_with_inode;
+ }
+
+ /* Initialize the file structure */
+
+ g_sysdev.sl_file.f_oflags = SYSLOG_OFLAGS;
+ g_sysdev.sl_file.f_pos = 0;
+ g_sysdev.sl_file.f_inode = inode;
+
+ /* Perform the low-level open operation. */
+
+ ret = OK;
+ if (inode->u.i_ops->open)
+ {
+ /* Is the inode a mountpoint? */
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ if (INODE_IS_MOUNTPT(inode))
+ {
+ /* Yes. Open the device write-only, try to create it if it
+ * doesn't exist, if the file that already exists, then append the
+ * new log data to end of the file.
+ */
+
+ ret = inode->u.i_mops->open(&g_sysdev.sl_file, relpath,
+ SYSLOG_OFLAGS, 0666);
+ }
+
+ /* No... then it must be a character driver in the NuttX pseudo-
+ * file system.
+ */
+
+ else
+#endif
+ {
+ ret = inode->u.i_ops->open(&g_sysdev.sl_file);
+ }
+ }
+
+ /* Was the file/device successfully opened? */
+
+ if (ret < 0)
+ {
+ ret = -ret;
+ goto errout_with_inode;
+ }
+
+ /* The SYSLOG device is open and ready for writing. */
+
+ sem_init(&g_sysdev.sl_sem, 0, 1);
+ g_sysdev.sl_holder = NO_HOLDER;
+ g_sysdev.sl_state = SYSLOG_OPENED;
+ return OK;
+
+ errout_with_inode:
+ g_sysdev.sl_state = SYSLOG_FAILURE;
+ inode_release(inode);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: syslog_putc
+ *
+ * Description:
+ * This is the low-level system logging interface. The debugging/syslogging
+ * interfaces are lib_rawprintf() and lib_lowprinf(). The difference is
+ * the lib_rawprintf() writes to fd=1 (stdout) and lib_lowprintf() uses
+ * a lower level interface that works from interrupt handlers. This
+ * function is a a low-level interface used to implement lib_lowprintf().
+ *
+ ****************************************************************************/
+
+int syslog_putc(int ch)
+{
+ ssize_t nbytes;
+ int ret;
+
+ /* Ignore any output:
+ *
+ * (1) Before the SYSLOG device has been initialized. This could happen
+ * from debug output that occurs early in the boot sequence before
+ * syslog_initialize() is called (SYSLOG_UNINITIALIZED).
+ * (2) While the device is being initialized. The case could happen if
+ * debug output is generated while syslog_initialize() executes
+ * (SYSLOG_INITIALIZING).
+ * (3) While we are generating SYSLOG output. The case could happen if
+ * debug output is generated while syslog_putc() executes
+ * (This case is actually handled inside of syslog_semtake()).
+ * (4) Any debug output generated from interrupt handlers. A disadvantage
+ * of using the generic character device for the SYSLOG is that it
+ * cannot handle debug output generated from interrupt level handlers.
+ * (5) If an irrecoverable failure occurred during initialization. In
+ * this case, we won't ever bother to try again (ever).
+ *
+ * NOTE: That the third case is different. It applies only to the thread
+ * that currently holds the sl_sem sempaphore. Other threads should wait.
+ * that is why that case is handled in syslog_semtake().
+ */
+
+ /* Case (4) */
+
+ if (up_interrupt_context())
+ {
+ return -ENOSYS; /* Not supported */
+ }
+
+ /* We can save checks in the usual case: That after the SYSLOG device
+ * has been successfully opened.
+ */
+
+ if (g_sysdev.sl_state != SYSLOG_OPENED)
+ {
+ /* Case (1) and (2) */
+
+ if (g_sysdev.sl_state == SYSLOG_UNINITIALIZED ||
+ g_sysdev.sl_state == SYSLOG_INITIALIZING)
+ {
+ return -EAGAIN; /* Can't access the SYSLOG now... maybe next time? */
+ }
+
+ /* Case (5) */
+
+ if (g_sysdev.sl_state == SYSLOG_FAILURE)
+ {
+ return -ENXIO; /* There is no SYSLOG device */
+ }
+
+ /* syslog_initialize() is called as soon as enough of the operating
+ * system is in place to support the open operation... but it is
+ * possible that the SYSLOG device is not yet registered at that time.
+ * In this case, we know that the system is sufficiently initialized
+ * to support an attempt to re-open the SYSLOG device.
+ *
+ * NOTE that the scheduler is locked. That is because we do not have
+ * fully initialized semaphore capability until the SYSLOG device is
+ * successfully initialized
+ */
+
+ sched_lock();
+ if (g_sysdev.sl_state == SYSLOG_REOPEN)
+ {
+ /* Try again to initialize the device. We may do this repeatedly
+ * because the log device might be something that was not ready
+ * the first time that syslog_intialize() was called (such as a
+ * USB serial device that has not yet been connected or a file in
+ * an NFS mounted file system that has not yet been mounted).
+ */
+
+ ret = syslog_initialize();
+ if (ret < 0)
+ {
+ sched_unlock();
+ return ret;
+ }
+ }
+
+ sched_unlock();
+ DEBUGASSERT(g_sysdev.sl_state == SYSLOG_OPENED);
+ }
+
+ /* Ignore carriage returns */
+
+ if (ch == '\r')
+ {
+ return ch;
+ }
+
+ /* The syslog device is ready for writing and we have something of
+ * value to write.
+ */
+
+ ret = syslog_takesem();
+ if (ret < 0)
+ {
+ /* We probably already hold the semaphore and were probably
+ * re-entered by the logic kicked off by syslog_write().
+ * We might also have been interrupted by a signal. Either
+ * way, we are outta here.
+ */
+
+ return ret;
+ }
+
+ /* Pre-pend a newline with a carriage return. */
+
+ if (ch == '\n')
+ {
+ /* Write the CR-LF sequence */
+
+ nbytes = syslog_write(g_syscrlf, 2);
+
+ /* Synchronize the file when each CR-LF is encountered (i.e.,
+ * implements line buffering always).
+ */
+
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+ if (nbytes > 0)
+ {
+ syslog_flush();
+ }
+#endif
+ }
+ else
+ {
+ /* Write the non-newline character (and don't flush) */
+
+ nbytes = syslog_write(&ch, 1);
+ }
+
+ syslog_givesem();
+
+ /* Check if the write was successful */
+
+ if (nbytes < 0)
+ {
+ return nbytes;
+ }
+
+ return ch;
+}
+
+#endif /* CONFIG_SYSLOG && CONFIG_SYSLOG_CHAR */
diff --git a/nuttx/fs/fs_umount.c b/nuttx/fs/fs_umount.c
new file mode 100644
index 000000000..5bf44e4f7
--- /dev/null
+++ b/nuttx/fs/fs_umount.c
@@ -0,0 +1,206 @@
+/****************************************************************************
+ * fs/fs_umount.c
+ *
+ * Copyright (C) 2007-2009 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/mount.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: umount
+ *
+ * Description:
+ * umount() detaches the filesystem mounted at the path specified by
+ * 'target.'
+ *
+ * Return:
+ * Zero is returned on success; -1 is returned on an error and errno is
+ * set appropriately:
+ *
+ * EACCES A component of a path was not searchable or mounting a read-only
+ * filesystem was attempted without giving the MS_RDONLY flag.
+ * EBUSY The target could not be unmounted because it is busy.
+ * EFAULT The pointer argument points outside the user address space.
+ *
+ ****************************************************************************/
+
+int umount(const char *target)
+{
+ FAR struct inode *mountpt_inode;
+ FAR struct inode *blkdrvr_inode = NULL;
+ int errcode = OK;
+ int status;
+
+ /* Verify required pointer arguments */
+
+ if (!target)
+ {
+ errcode = EFAULT;
+ goto errout;
+ }
+
+ /* Find the mountpt */
+
+ mountpt_inode = inode_find(target, NULL);
+ if (!mountpt_inode)
+ {
+ errcode = ENOENT;
+ goto errout;
+ }
+
+ /* Verify that the inode is a mountpoint */
+
+ if (!INODE_IS_MOUNTPT(mountpt_inode))
+ {
+ errcode = EINVAL;
+ goto errout_with_mountpt;
+ }
+
+ /* Unbind the block driver from the file system (destroying any fs
+ * private data.
+ */
+
+ if (!mountpt_inode->u.i_mops->unbind)
+ {
+ /* The filesystem does not support the unbind operation ??? */
+
+ errcode = EINVAL;
+ goto errout_with_mountpt;
+ }
+
+ /* The unbind method returns the number of references to the
+ * filesystem (i.e., open files), zero if the unbind was
+ * performed, or a negated error code on a failure.
+ */
+
+ inode_semtake(); /* Hold the semaphore through the unbind logic */
+ status = mountpt_inode->u.i_mops->unbind( mountpt_inode->i_private, &blkdrvr_inode);
+ if (status < 0)
+ {
+ /* The inode is unhappy with the blkdrvr for some reason */
+
+ errcode = -status;
+ goto errout_with_semaphore;
+ }
+ else if (status > 0)
+ {
+ errcode = EBUSY;
+ goto errout_with_semaphore;
+ }
+
+ /* Successfully unbound */
+
+ mountpt_inode->i_private = NULL;
+
+ /* Successfully unbound, remove the mountpoint inode from
+ * the inode tree. The inode will not be deleted yet because
+ * there is still at least reference on it (from the mount)
+ */
+
+ status = inode_remove(target);
+ inode_semgive();
+
+ /* The return value of -EBUSY is normal (in fact, it should
+ * not be OK)
+ */
+
+ if (status != OK && status != -EBUSY)
+ {
+ errcode = -status;
+ goto errout_with_mountpt;
+ }
+
+ /* Release the mountpoint inode and any block driver inode
+ * returned by the file system unbind above. This should cause
+ * the inode to be deleted (unless there are other references)
+ */
+
+ inode_release(mountpt_inode);
+
+ /* Did the unbind method return a contained block driver */
+
+ if (blkdrvr_inode)
+ {
+ inode_release(blkdrvr_inode);
+ }
+
+ return OK;
+
+ /* A lot of goto's! But they make the error handling much simpler */
+
+errout_with_semaphore:
+ inode_semgive();
+errout_with_mountpt:
+ inode_release(mountpt_inode);
+ if (blkdrvr_inode)
+ {
+ inode_release(blkdrvr_inode);
+ }
+errout:
+ set_errno(errcode);
+ return ERROR;
+}
+
diff --git a/nuttx/fs/fs_unlink.c b/nuttx/fs/fs_unlink.c
new file mode 100644
index 000000000..f57bd33b5
--- /dev/null
+++ b/nuttx/fs/fs_unlink.c
@@ -0,0 +1,130 @@
+/****************************************************************************
+ * fs_unlink.c
+ *
+ * Copyright (C) 2007-2009 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 <unistd.h>
+#include <errno.h>
+#include <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: unlink
+ *
+ * Description: Remove a file managed a mountpoint
+ *
+ ****************************************************************************/
+
+int unlink(FAR const char *pathname)
+{
+ FAR struct inode *inode;
+ const char *relpath = NULL;
+ int ret;
+
+ /* Get an inode for this file */
+
+ inode = inode_find(pathname, &relpath);
+ if (!inode)
+ {
+ /* There is no mountpoint that includes in this path */
+
+ ret = ENOENT;
+ goto errout;
+ }
+
+ /* Verify that the inode is a valid mountpoint. */
+
+ if (!INODE_IS_MOUNTPT(inode) || !inode->u.i_mops)
+ {
+ ret = ENXIO;
+ goto errout_with_inode;
+ }
+
+ /* Perform the unlink operation using the relative path
+ * at the mountpoint.
+ */
+
+ if (inode->u.i_mops->unlink)
+ {
+ ret = inode->u.i_mops->unlink(inode, relpath);
+ if (ret < 0)
+ {
+ ret = -ret;
+ goto errout_with_inode;
+ }
+ }
+ else
+ {
+ ret = ENOSYS;
+ goto errout_with_inode;
+ }
+
+ /* Successfully unlinked */
+
+ inode_release(inode);
+ return OK;
+
+ errout_with_inode:
+ inode_release(inode);
+ errout:
+ set_errno(ret);
+ return ERROR;
+}
+
diff --git a/nuttx/fs/fs_unregisterblockdriver.c b/nuttx/fs/fs_unregisterblockdriver.c
new file mode 100644
index 000000000..4d169fddf
--- /dev/null
+++ b/nuttx/fs/fs_unregisterblockdriver.c
@@ -0,0 +1,86 @@
+/****************************************************************************
+ * fs/fs_unregisterblockdriver.c
+ *
+ * Copyright (C) 2007-2009 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 <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: unregister_blockdriver
+ *
+ * Description:
+ * Remove the block driver inode at 'path' from the pseudo-file system
+ *
+ ****************************************************************************/
+
+int unregister_blockdriver(const char *path)
+{
+ int ret;
+
+ inode_semtake();
+ ret = inode_remove(path);
+ inode_semgive();
+ return ret;
+}
diff --git a/nuttx/fs/fs_unregisterdriver.c b/nuttx/fs/fs_unregisterdriver.c
new file mode 100644
index 000000000..c9ac0b619
--- /dev/null
+++ b/nuttx/fs/fs_unregisterdriver.c
@@ -0,0 +1,86 @@
+/****************************************************************************
+ * fs/fs_unregisterdriver.c
+ *
+ * Copyright (C) 2007-2009, 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 <nuttx/fs/fs.h>
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: unregister_driver
+ *
+ * Description:
+ * Remove the character driver inode at 'path' from the pseudo-file system
+ *
+ ****************************************************************************/
+
+int unregister_driver(FAR const char *path)
+{
+ int ret;
+
+ inode_semtake();
+ ret = inode_remove(path);
+ inode_semgive();
+ return ret;
+}
diff --git a/nuttx/fs/fs_write.c b/nuttx/fs/fs_write.c
new file mode 100644
index 000000000..93dad92c4
--- /dev/null
+++ b/nuttx/fs/fs_write.c
@@ -0,0 +1,188 @@
+/****************************************************************************
+ * fs/fs_write.c
+ *
+ * Copyright (C) 2007-2009, 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 <unistd.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <errno.h>
+
+#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0
+# include <sys/socket.h>
+#endif
+
+#include "fs_internal.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+static inline ssize_t file_write(int fd, FAR const void *buf, size_t nbytes)
+{
+ FAR struct filelist *list;
+ FAR struct file *this_file;
+ FAR struct inode *inode;
+ int ret;
+ int err;
+
+ /* Get the thread-specific file list */
+
+ list = sched_getfiles();
+ if (!list)
+ {
+ err = EMFILE;
+ goto errout;
+ }
+
+ /* Was this file opened for write access? */
+
+ this_file = &list->fl_files[fd];
+ if ((this_file->f_oflags & O_WROK) == 0)
+ {
+ err = EBADF;
+ goto errout;
+ }
+
+ /* Is a driver registered? Does it support the write method? */
+
+ inode = this_file->f_inode;
+ if (!inode || !inode->u.i_ops || !inode->u.i_ops->write)
+ {
+ err = EBADF;
+ goto errout;
+ }
+
+ /* Yes, then let the driver perform the write */
+
+ ret = inode->u.i_ops->write(this_file, buf, nbytes);
+ if (ret < 0)
+ {
+ err = -ret;
+ goto errout;
+ }
+
+ return ret;
+
+errout:
+ set_errno(err);
+ return ERROR;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/***************************************************************************
+ * Name: write
+ *
+ * Description:
+ * write() writes up to nytes bytes to the file referenced by the file
+ * descriptor fd from the buffer starting at buf.
+ *
+ * Parameters:
+ * fd file descriptor (or socket descriptor) to write to
+ * buf Data to write
+ * nbytes Length of data to write
+ *
+ * Returned Value:
+ * On success, the number of bytes written are returned (zero indicates
+ * nothing was written). On error, -1 is returned, and errno is set appro-
+ * priately:
+ *
+ * EAGAIN
+ * Non-blocking I/O has been selected using O_NONBLOCK and the write
+ * would block.
+ * EBADF
+ * fd is not a valid file descriptor or is not open for writing.
+ * EFAULT
+ * buf is outside your accessible address space.
+ * EFBIG
+ * An attempt was made to write a file that exceeds the implementation
+ * defined maximum file size or the process' file size limit, or
+ * to write at a position past the maximum allowed offset.
+ * EINTR
+ * The call was interrupted by a signal before any data was written.
+ * EINVAL
+ * fd is attached to an object which is unsuitable for writing; or
+ * the file was opened with the O_DIRECT flag, and either the address
+ * specified in buf, the value specified in count, or the current
+ * file offset is not suitably aligned.
+ * EIO
+ * A low-level I/O error occurred while modifying the inode.
+ * ENOSPC
+ * The device containing the file referred to by fd has no room for
+ * the data.
+ * EPIPE
+ * fd is connected to a pipe or socket whose reading end is closed.
+ * When this happens the writing process will also receive a SIGPIPE
+ * signal. (Thus, the write return value is seen only if the program
+ * catches, blocks or ignores this signal.)
+ *
+ * Assumptions:
+ *
+ ********************************************************************************************/
+
+ssize_t write(int fd, FAR const void *buf, size_t nbytes)
+{
+ /* Did we get a valid file descriptor? */
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+ if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS)
+#endif
+ {
+ /* Write to a socket descriptor is equivalent to send with flags == 0 */
+
+#if defined(CONFIG_NET_TCP) && CONFIG_NSOCKET_DESCRIPTORS > 0
+ return send(fd, buf, nbytes, 0);
+#else
+ set_errno(EBADF);
+ return ERROR;
+#endif
+ }
+
+ /* The descriptor is in the right range to be a file descriptor... write to the file */
+
+#if CONFIG_NFILE_DESCRIPTORS > 0
+ return file_write(fd, buf, nbytes);
+#endif
+}
+
diff --git a/nuttx/fs/mmap/Kconfig b/nuttx/fs/mmap/Kconfig
new file mode 100644
index 000000000..03482e2d8
--- /dev/null
+++ b/nuttx/fs/mmap/Kconfig
@@ -0,0 +1,27 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+config FS_RAMMAP
+ bool "File mapping emulation"
+ default n
+ ---help---
+ NuttX operates in a flat open address space and is focused on MCUs that do
+ support Memory Management Units (MMUs). Therefore, NuttX generally does not
+ require mmap() functionality and the MCUs generally cannot support true
+ memory-mapped files.
+
+ However, memory mapping of files is the mechanism used by NXFLAT, the NuttX
+ tiny binary format, to get files into memory in order to execute them.
+ mmap() support is therefore required to support NXFLAT.
+
+ If FS_RAMMAP is defined in the configuration, then mmap() will
+ support simulation of memory mapped files by copying files whole
+ into RAM. These copied files have some of the properties of
+ standard memory mapped files.
+
+ See nuttx/fs/mmap/README.txt for additonal information.
+
+if FS_RAMMAP
+endif
diff --git a/nuttx/fs/mmap/Make.defs b/nuttx/fs/mmap/Make.defs
new file mode 100644
index 000000000..59857fe9c
--- /dev/null
+++ b/nuttx/fs/mmap/Make.defs
@@ -0,0 +1,43 @@
+############################################################################
+# fs/mmap/Make.defs
+#
+# Copyright (C) 2011 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.
+#
+############################################################################
+
+ASRCS +=
+CSRCS += fs_mmap.c
+
+ifeq ($(CONFIG_FS_RAMMAP),y)
+CSRCS += fs_munmap.c fs_rammap.c
+endif
+
+MMAPDEPPATH = --dep-path mmap
diff --git a/nuttx/fs/mmap/README.txt b/nuttx/fs/mmap/README.txt
new file mode 100644
index 000000000..cf4c51e9e
--- /dev/null
+++ b/nuttx/fs/mmap/README.txt
@@ -0,0 +1,78 @@
+fs/mmap README File
+===================
+
+NuttX operates in a flat open address space and is focused on MCUs that do
+support Memory Management Units (MMUs). Therefore, NuttX generally does not
+require mmap() functionality and the MCUs generally cannot support true
+memory-mapped files.
+
+However, memory mapping of files is the mechanism used by NXFLAT, the NuttX
+tiny binary format, to get files into memory in order to execute them.
+mmap() support is therefore required to support NXFLAT. There are two
+conditions where mmap() can be supported:
+
+1. mmap can be used to support eXecute In Place (XIP) on random access media
+ under the following very restrictive conditions:
+
+ a. The filesystem supports the FIOC_MMAP ioctl command. Any file
+ system that maps files contiguously on the media should support
+ this ioctl. (vs. file system that scatter files over the media
+ in non-contiguous sectors). As of this writing, ROMFS is the
+ only file system that meets this requirement.
+
+ b. The underlying block driver supports the BIOC_XIPBASE ioctl
+ command that maps the underlying media to a randomly accessible
+ address. At present, only the RAM/ROM disk driver does this.
+
+ Some limitations of this approach are as follows:
+
+ a. Since no real mapping occurs, all of the file contents are "mapped"
+ into memory.
+
+ b. All mapped files are read-only.
+
+ c. There are no access privileges.
+
+2. If CONFIG_FS_RAMMAP is defined in the configuration, then mmap() will
+ support simulation of memory mapped files by copying files whole
+ into RAM. These copied files have some of the properties of
+ standard memory mapped files. There are many, many exceptions
+ exceptions, however. Some of these include:
+
+ a. The goal is to have a single region of memory that represents a single
+ file and can be shared by many threads. That is, given a filename a
+ thread should be able to open the file, get a file descriptor, and
+ call mmap() to get a memory region. Different file descriptors opened
+ with the same file path should get the same memory region when mapped.
+
+ The limitation in the current design is that there is insufficient
+ knowledge to know that these different file descriptors correspond to
+ the same file. So, for the time being, a new memory region is created
+ each time that rammap() is called. Not very useful!
+
+ b. The entire mapped portion of the file must be present in memory.
+ Since it is assumed the the MCU does not have an MMU, on-demanding
+ paging in of file blocks cannot be supported. Since the while mapped
+ portion of the file must be present in memory, there are limitations
+ in the size of files that may be memory mapped (especially on MCUs
+ with no significant RAM resources).
+
+ c. All mapped files are read-only. You can write to the in-memory image,
+ but the file contents will not change.
+
+ d. There are no access privileges.
+
+ e. Since there are no processes in NuttX, all mmap() and munmap()
+ operations have immediate, global effects. Under Linux, for example,
+ munmap() would eliminate only the mapping with a process; the mappings
+ to the same file in other processes would not be effected.
+
+ f. Like true mapped file, the region will persist after closing the file
+ descriptor. However, at present, these ram copied file regions are
+ *not* automatically "unmapped" (i.e., freed) when a thread is terminated.
+ This is primarily because it is not possible to know how many users
+ of the mapped region there are and, therefore, when would be the
+ appropriate time to free the region (other than when munmap is called).
+
+ NOTE: Note, if the design limitation of a) were solved, then it would be
+ easy to solve exception d) as well.
diff --git a/nuttx/fs/mmap/fs_mmap.c b/nuttx/fs/mmap/fs_mmap.c
new file mode 100644
index 000000000..85d796586
--- /dev/null
+++ b/nuttx/fs/mmap/fs_mmap.c
@@ -0,0 +1,174 @@
+/****************************************************************************
+ * fs/mmap/fs_mmap.c
+ *
+ * Copyright (C) 2008-2009, 2011 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/mman.h>
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <errno.h>
+#include <debug.h>
+
+#include "fs_internal.h"
+#include "fs_rammap.h"
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mmap
+ *
+ * Description:
+ * NuttX operates in a flat open address space. Therefore, it generally
+ * does not require mmap() functionality. There are two exceptions:
+ *
+ * 1. mmap() is the API that is used to support direct access to random
+ * access media under the following very restrictive conditions:
+ *
+ * a. The filesystem supports the FIOC_MMAP ioctl command. Any file
+ * system that maps files contiguously on the media should support
+ * this ioctl. (vs. file system that scatter files over the media
+ * in non-contiguous sectors). As of this writing, ROMFS is the
+ * only file system that meets this requirement.
+ * b. The underlying block driver supports the BIOC_XIPBASE ioctl
+ * command that maps the underlying media to a randomly accessible
+ * address. At present, only the RAM/ROM disk driver does this.
+ *
+ * 2. If CONFIG_FS_RAMMAP is defined in the configuration, then mmap() will
+ * support simulation of memory mapped files by copying files whole
+ * into RAM.
+ *
+ * Parameters:
+ * start A hint at where to map the memory -- ignored. The address
+ * of the underlying media is fixed and cannot be re-mapped without
+ * MMU support.
+ * length The length of the mapping. For exception #1 above, this length
+ * ignored: The entire underlying media is always accessible.
+ * prot See the PROT_* definitions in sys/mman.h.
+ * PROT_NONE - Will cause an error
+ * PROT_READ - PROT_WRITE and PROT_EXEC also assumed
+ * PROT_WRITE - PROT_READ and PROT_EXEC also assumed
+ * PROT_EXEC - PROT_READ and PROT_WRITE also assumed
+ * flags See the MAP_* definitions in sys/mman.h.
+ * MAP_SHARED - Required
+ * MAP_PRIVATE - Will cause an error
+ * MAP_FIXED - Will cause an error
+ * MAP_FILE - Ignored
+ * MAP_ANONYMOUS - Will cause an error
+ * MAP_ANON - Will cause an error
+ * MAP_GROWSDOWN - Ignored
+ * MAP_DENYWRITE - Will cause an error
+ * MAP_EXECUTABLE - Ignored
+ * MAP_LOCKED - Ignored
+ * MAP_NORESERVE - Ignored
+ * MAP_POPULATE - Ignored
+ * MAP_NONBLOCK - Ignored
+ * fd file descriptor of the backing file -- required.
+ * offset The offset into the file to map
+ *
+ * Returned Value:
+ * On success, mmap() returns a pointer to the mapped area. On error, the
+ * value MAP_FAILED is returned, and errno is set appropriately.
+ *
+ * ENOSYS
+ * Returned if any of the unsupported mmap() features are attempted
+ * EBADF
+ * 'fd' is not a valid file descriptor.
+ * EINVAL
+ * Length is 0. flags contained neither MAP_PRIVATE or MAP_SHARED, or
+ * contained both of these values.
+ * ENODEV
+ * The underlying filesystem of the specified file does not support
+ * memory mapping.
+ *
+ ****************************************************************************/
+
+FAR void *mmap(FAR void *start, size_t length, int prot, int flags,
+ int fd, off_t offset)
+{
+ FAR void *addr;
+ int ret;
+
+ /* Since only a tiny subset of mmap() functionality, we have to verify many
+ * things.
+ */
+
+#ifdef CONFIG_DEBUG
+ if (prot == PROT_NONE ||
+ (flags & (MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_DENYWRITE)) != 0)
+ {
+ fdbg("Unsupported options, prot=%x flags=%04x\n", prot, flags);
+ errno = ENOSYS;
+ return MAP_FAILED;
+ }
+
+ if (length == 0 || (flags & MAP_SHARED) == 0)
+ {
+ fdbg("Invalid options, lengt=%d flags=%04x\n", length, flags);
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+#endif
+
+ /* Okay now we can assume a shared mapping from a file. This is the
+ * only option supported
+ *
+ * Perform the ioctl to get the base address of the file in 'mapped'
+ * in memory. (casting to uintptr_t first eliminates complaints on some
+ * architectures where the sizeof long is different from the size of
+ * a pointer).
+ */
+
+ ret = ioctl(fd, FIOC_MMAP, (unsigned long)((uintptr_t)&addr));
+ if (ret < 0)
+ {
+#ifdef CONFIG_FS_RAMMAP
+ return rammap(fd, length, offset);
+#else
+ fdbg("ioctl(FIOC_MMAP) failed: %d\n", errno);
+ return MAP_FAILED;
+#endif
+ }
+
+ /* Return the offset address */
+
+ return (void*)(((uint8_t*)addr) + offset);
+}
diff --git a/nuttx/fs/mmap/fs_munmap.c b/nuttx/fs/mmap/fs_munmap.c
new file mode 100644
index 000000000..5d9416d45
--- /dev/null
+++ b/nuttx/fs/mmap/fs_munmap.c
@@ -0,0 +1,212 @@
+/****************************************************************************
+ * fs/mmap/fs_munmap.c
+ *
+ * Copyright (C) 2011 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/mman.h>
+
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+
+#include "fs_internal.h"
+#include "fs_rammap.h"
+
+#ifdef CONFIG_FS_RAMMAP
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: munmap
+ *
+ * Description:
+ *
+ * munmap() system call deletes mappings for the specified address range.
+ * All memory starting with 'start' and continuing for a length of 'length'
+ * bytes are removed.
+ *
+ * NuttX operates in a flat open address space. Therefore, it generally
+ * does not require mmap() and, hence, munmap functionality. There are
+ * two exceptions where mmap() is available:
+ *
+ * 1. mmap() is the API that is used to support direct access to random
+ * access media under the following very restrictive conditions:
+ *
+ * a. The filesystem supports the FIOC_MMAP ioctl command. Any file
+ * system that maps files contiguously on the media should support
+ * this ioctl. (vs. file system that scatter files over the media
+ * in non-contiguous sectors). As of this writing, ROMFS is the
+ * only file system that meets this requirement.
+ * b. The underlying block driver supports the BIOC_XIPBASE ioctl
+ * command that maps the underlying media to a randomly accessible
+ * address. At present, only the RAM/ROM disk driver does this.
+ *
+ * munmap() is still not required in this first case. In this first
+ * The mapped address is a static address in the MCUs address space
+ * does not need to be munmapped. Support for munmap() in this case
+ * provided by the simple definition in sys/mman.h:
+ *
+ * #define munmap(start, length)
+ *
+ * 2. If CONFIG_FS_RAMMAP is defined in the configuration, then mmap() will
+ * support simulation of memory mapped files by copying files whole
+ * into RAM. munmap() is required in this case to free the allocated
+ * memory holding the shared copy of the file.
+ *
+ * Parameters:
+ * start The start address of the mapping to delete. For this
+ * simplified munmap() implementation, the *must* be the start
+ * address of the memory region (the same address returned by
+ * mmap()).
+ * length The length region to be umapped.
+ *
+ * Returned Value:
+ * On success, munmap() returns 0, on failure -1, and errno is set
+ * (probably to EINVAL).
+ *
+ ****************************************************************************/
+
+int munmap(FAR void *start, size_t length)
+{
+ FAR struct fs_rammap_s *prev;
+ FAR struct fs_rammap_s *curr;
+ FAR void *newaddr;
+ unsigned int offset;
+ int ret;
+ int err;
+
+ /* Find a region containing this start and length in the list of regions */
+
+ rammap_initialize();
+ ret = sem_wait(&g_rammaps.exclsem);
+ if (ret < 0)
+ {
+ return ERROR;
+ }
+
+ /* Seach the list of regions */
+
+ for (prev = NULL, curr = g_rammaps.head; curr; prev = curr, curr = curr->flink)
+ {
+ /* Does this region include any part of the specified range? */
+
+ if ((uintptr_t)start < (uintptr_t)curr->addr + curr->length &&
+ (uintptr_t)start + length >= (uintptr_t)curr->addr)
+ {
+ break;
+ }
+ }
+
+ /* Did we find the region */
+
+ if (!curr)
+ {
+ fdbg("Region not found\n");
+ err = EINVAL;
+ goto errout_with_semaphore;
+ }
+
+ /* Get the offset from the beginning of the region and the actual number
+ * of bytes to "unmap". All mappings must extend to the end of the region.
+ * There is no support for free a block of memory but leaving a block of
+ * memory at the end. This is a consequence of using realloc() to
+ * simulate the unmapping.
+ */
+
+ offset = start - curr->addr;
+ if (offset + length < curr->length)
+ {
+ fdbg("Cannot umap without unmapping to the end\n");
+ err = ENOSYS;
+ goto errout_with_semaphore;
+ }
+
+ /* Okay.. the region is beging umapped to the end. Make sure the length
+ * indicates that.
+ */
+
+ length = curr->length - offset;
+
+ /* Are we unmapping the entire region (offset == 0)? */
+
+ if (length >= curr->length)
+ {
+ /* Yes.. remove the mapping from the list */
+
+ if (prev)
+ {
+ prev->flink = curr->flink;
+ }
+ else
+ {
+ g_rammaps.head = curr->flink;
+ }
+
+ /* Then free the region */
+
+ kfree(curr);
+ }
+
+ /* No.. We have been asked to "unmap' only a portion of the memory
+ * (offset > 0).
+ */
+
+ else
+ {
+ newaddr = krealloc(curr->addr, sizeof(struct fs_rammap_s) + length);
+ DEBUGASSERT(newaddr == (FAR void*)(curr->addr));
+ curr->length = length;
+ }
+
+ sem_post(&g_rammaps.exclsem);
+ return OK;
+
+errout_with_semaphore:
+ sem_post(&g_rammaps.exclsem);
+ errno = err;
+ return ERROR;
+}
+
+#endif /* CONFIG_FS_RAMMAP */
diff --git a/nuttx/fs/mmap/fs_rammap.c b/nuttx/fs/mmap/fs_rammap.c
new file mode 100644
index 000000000..f43541cc9
--- /dev/null
+++ b/nuttx/fs/mmap/fs_rammap.c
@@ -0,0 +1,245 @@
+/****************************************************************************
+ * fs/mmap/fs_rammmap.c
+ *
+ * Copyright (C) 2011 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/mman.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+
+#include "fs_internal.h"
+#include "fs_rammap.h"
+
+#ifdef CONFIG_FS_RAMMAP
+
+/****************************************************************************
+ * Global Data
+ ****************************************************************************/
+
+/* This is the list of all mapped files */
+
+struct fs_allmaps_s g_rammaps;
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rammap_initialize
+ *
+ * Description:
+ * Verified that this capability has been initialized.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void rammap_initialize(void)
+{
+ if (!g_rammaps.initialized)
+ {
+ sem_init(&g_rammaps.exclsem, 0, 1);
+ g_rammaps.initialized = true;
+ }
+}
+
+/****************************************************************************
+ * Name: rammmap
+ *
+ * Description:
+ * Support simulation of memory mapped files by copying files into RAM.
+ *
+ * Parameters:
+ * fd file descriptor of the backing file -- required.
+ * length The length of the mapping. For exception #1 above, this length
+ * ignored: The entire underlying media is always accessible.
+ * offset The offset into the file to map
+ *
+ * Returned Value:
+ * On success, rammmap() returns a pointer to the mapped area. On error, the
+ * value MAP_FAILED is returned, and errno is set appropriately.
+ *
+ * EBADF
+ * 'fd' is not a valid file descriptor.
+ * EINVAL
+ * 'length' or 'offset' are invalid
+ * ENOMEM
+ * Insufficient memory is available to map the file.
+ *
+ ****************************************************************************/
+
+FAR void *rammap(int fd, size_t length, off_t offset)
+{
+ FAR struct fs_rammap_s *map;
+ FAR uint8_t *alloc;
+ FAR uint8_t *rdbuffer;
+ ssize_t nread;
+ off_t fpos;
+ int err;
+ int ret;
+
+ /* There is a major design flaw that I have not yet thought of fix for:
+ * The goal is to have a single region of memory that represents a single
+ * file and can be shared by many threads. That is, given a filename a
+ * thread should be able to open the file, get a file descriptor, and
+ * call mmap() to get a memory region. Different file descriptors opened
+ * with the same file path should get the same memory region when mapped.
+ *
+ * The design flaw is that I don't have sufficient knowledge to know that
+ * these different file descriptors map to the same file. So, for the time
+ * being, a new memory region is created each time that rammap() is called.
+ * Not very useful!
+ */
+
+ /* Allocate a region of memory of the specified size */
+
+ alloc = (FAR uint8_t *)kmalloc(sizeof(struct fs_rammap_s) + length);
+ if (!alloc)
+ {
+ fdbg("Region allocation failed, length: %d\n", (int)length);
+ err = ENOMEM;
+ goto errout;
+ }
+
+ /* Initialize the region */
+
+ map = (FAR struct fs_rammap_s *)alloc;
+ memset(map, 0, sizeof(struct fs_rammap_s));
+ map->addr = alloc + sizeof(struct fs_rammap_s);
+ map->length = length;
+ map->offset = offset;
+
+ /* Seek to the specified file offset */
+
+ fpos = lseek(fd, offset, SEEK_SET);
+ if (fpos == (off_t)-1)
+ {
+ /* Seek failed... errno has already been set, but EINVAL is probably
+ * the correct response.
+ */
+
+ fdbg("Seek to position %d failed\n", (int)offset);
+ err = EINVAL;
+ goto errout_with_region;
+ }
+
+ /* Read the file data into the memory region */
+
+ rdbuffer = map->addr;
+ while (length > 0)
+ {
+ nread = read(fd, rdbuffer, length);
+ if (nread < 0)
+ {
+ /* Handle the special case where the read was interrupted by a
+ * signal.
+ */
+
+ err = get_errno();
+ if (err != EINTR)
+ {
+ /* All other read errors are bad. errno is already set.
+ * (but maybe should be forced to EINVAL?). NOTE that if
+ * FS DEBUG is enabled, then the following fdbg() macro will
+ * destroy the errno value.
+ */
+
+ fdbg("Read failed: offset=%d errno=%d\n", (int)offset, err);
+#ifdef CONFIG_DEBUG_FS
+ goto errout_with_region;
+#else
+ goto errout_with_errno;
+#endif
+ }
+ }
+
+ /* Check for end of file. */
+
+ if (nread == 0)
+ {
+ break;
+ }
+
+ /* Increment number of bytes read */
+
+ rdbuffer += nread;
+ length -= nread;
+ }
+
+ /* Zero any memory beyond the amount read from the file */
+
+ memset(rdbuffer, 0, length);
+
+ /* Add the buffer to the list of regions */
+
+ rammap_initialize();
+ ret = sem_wait(&g_rammaps.exclsem);
+ if (ret < 0)
+ {
+ goto errout_with_errno;
+ }
+
+ map->flink = g_rammaps.head;
+ g_rammaps.head = map;
+
+ sem_post(&g_rammaps.exclsem);
+ return map->addr;
+
+errout_with_region:
+ kfree(alloc);
+errout:
+ set_errno(err);
+ return MAP_FAILED;
+
+errout_with_errno:
+ kfree(alloc);
+ return MAP_FAILED;
+}
+
+#endif /* CONFIG_FS_RAMMAP */
diff --git a/nuttx/fs/mmap/fs_rammap.h b/nuttx/fs/mmap/fs_rammap.h
new file mode 100644
index 000000000..293a91ffb
--- /dev/null
+++ b/nuttx/fs/mmap/fs_rammap.h
@@ -0,0 +1,150 @@
+/****************************************************************************
+ * fs/mmap/rammap.h
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_MMAP_RAMMAP_H
+#define __FS_MMAP_RAMMAP_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <semaphore.h>
+
+#ifdef CONFIG_FS_RAMMAP
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* This structure describes one file that has been copied to memory and
+ * managed as a share-able "memory mapped" file. This functionality is
+ * intended to provide a substitute for memory mapped files for architectures
+ * that do not have MMUs and, hence, cannot support on demand paging of
+ * blocks of a file.
+ *
+ * This copied file has many of the properties of a standard memory mapped
+ * file except:
+ *
+ * - All of the file must be present in memory. This limits the size of
+ * files that may be memory mapped (especially on MCUs with no significant
+ * RAM resources).
+ * - All mapped files are read-only. You can write to the in-memory image,
+ * but the file contents will not change.
+ * - There are not access privileges.
+ */
+
+struct fs_rammap_s
+{
+ struct fs_rammap_s *flink; /* Implements a singly linked list */
+ FAR void *addr; /* Start of allocated memory */
+ size_t length; /* Length of region */
+ off_t offset; /* File offset */
+};
+
+/* This structure defines all "mapped" files */
+
+struct fs_allmaps_s
+{
+ bool initialized; /* True: This structure has been initialized */
+ sem_t exclsem; /* Provides exclusive access the list */
+ struct fs_rammap_s *head; /* List of mapped files */
+};
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/* This is the list of all mapped files */
+
+extern struct fs_allmaps_s g_rammaps;
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rammap_initialize
+ *
+ * Description:
+ * Verified that this capability has been initialized.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+extern void rammap_initialize(void);
+
+/****************************************************************************
+ * Name: rammmap
+ *
+ * Description:
+ * Support simulation of memory mapped files by copying files into RAM.
+ *
+ * Parameters:
+ * fd file descriptor of the backing file -- required.
+ * length The length of the mapping. For exception #1 above, this length
+ * ignored: The entire underlying media is always accessible.
+ * offset The offset into the file to map
+ *
+ * Returned Value:
+ * On success, rammmap() returns a pointer to the mapped area. On error, the
+ * value MAP_FAILED is returned, and errno is set appropriately.
+ *
+ * EBADF
+ * 'fd' is not a valid file descriptor.
+ * EINVAL
+ * 'length' or 'offset' are invalid
+ * ENOMEM
+ * Insufficient memory is available to map the file.
+ *
+ ****************************************************************************/
+
+extern FAR void *rammap(int fd, size_t length, off_t offset);
+
+#endif /* CONFIG_FS_RAMMAP */
+#endif /* __FS_MMAP_RAMMAP_H */
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 */
diff --git a/nuttx/fs/nxffs/Kconfig b/nuttx/fs/nxffs/Kconfig
new file mode 100644
index 000000000..b233e85ea
--- /dev/null
+++ b/nuttx/fs/nxffs/Kconfig
@@ -0,0 +1,51 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+config FS_NXFFS
+ bool "NXFFS file system"
+ default n
+ depends on !DISABLE_MOUNTPOINT
+ ---help---
+ Enable NuttX FLASH file system (NXFF) support.
+
+if FS_NXFFS
+config NXFFS_ERASEDSTATE
+ bool "FLASH erased state"
+ default n
+ ---help---
+ The erased state of FLASH.
+ This must have one of the values of 0xff or 0x00.
+ Default: 0xff.
+
+config NXFFS_PACKTHRESHOLD
+ bool "Re-packing threshold"
+ default n
+ ---help---
+ When packing flash file data,
+ don't both with file chunks smaller than this number of data bytes.
+ Default: 32.
+
+config NXFFS_MAXNAMLEN
+ bool "Maximum file name length"
+ default n
+ ---help---
+ The maximum size of an NXFFS file name.
+ Default: 255.
+
+config NXFFS_TAILTHRESHOLD
+ bool "Tail threshold"
+ default n
+ ---help---
+ clean-up can either mean
+ packing files together toward the end of the file or, if file are
+ deleted at the end of the file, clean up can simply mean erasing
+ the end of FLASH memory so that it can be re-used again. However,
+ doing this can also harm the life of the FLASH part because it can
+ mean that the tail end of the FLASH is re-used too often. This
+ threshold determines if/when it is worth erased the tail end of FLASH
+ and making it available for re-use (and possible over-wear).
+ Default: 8192.
+
+endif
diff --git a/nuttx/fs/nxffs/Make.defs b/nuttx/fs/nxffs/Make.defs
new file mode 100644
index 000000000..b67ae4472
--- /dev/null
+++ b/nuttx/fs/nxffs/Make.defs
@@ -0,0 +1,46 @@
+############################################################################
+# fs/nxffs/Make.defs
+#
+# Copyright (C) 2011 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_FS_NXFFS),y)
+ASRCS +=
+CSRCS += nxffs_block.c nxffs_blockstats.c nxffs_cache.c nxffs_dirent.c \
+ nxffs_dump.c nxffs_initialize.c nxffs_inode.c nxffs_ioctl.c \
+ nxffs_open.c nxffs_pack.c nxffs_read.c nxffs_reformat.c \
+ nxffs_stat.c nxffs_unlink.c nxffs_util.c nxffs_write.c
+
+# Argument for dependency checking
+
+NXFFSDEPPATH = --dep-path nxffs
+endif
diff --git a/nuttx/fs/nxffs/README.txt b/nuttx/fs/nxffs/README.txt
new file mode 100644
index 000000000..a10fb97a5
--- /dev/null
+++ b/nuttx/fs/nxffs/README.txt
@@ -0,0 +1,180 @@
+NXFFS README
+^^^^^^^^^^^^
+
+This README file contains information about the implemenation of the NuttX
+wear-leveling FLASH file system, NXFFS.
+
+Contents:
+
+ General NXFFS organization
+ General operation
+ Headers
+ NXFFS Limitations
+ Multiple Writers
+ ioctls
+ Things to Do
+
+General NXFFS organization
+==========================
+
+The following example assumes 4 logical blocks per FLASH erase block. The
+actual relationship is determined by the FLASH geometry reported by the MTD
+driver.
+
+ERASE LOGICAL Inodes begin with a inode header. inode may
+BLOCK BLOCK CONTENTS be marked as "deleted," pending re-packing.
+ n 4*n --+--------------+
+ |BBBBBBBBBBBBBB| Logic block header
+ |IIIIIIIIIIIIII| Inodes begin with a inode header
+ |DDDDDDDDDDDDDD| Data block containing inode data block
+ | (Inode Data) |
+ 4*n+1 --+--------------+
+ |BBBBBBBBBBBBBB| Logic block header
+ |DDDDDDDDDDDDDD| Inodes may consist of multiple data blocks
+ | (Inode Data) |
+ |IIIIIIIIIIIIII| Next inode header
+ | | Possibly a few unused bytes at the end of a block
+ 4*n+2 --+--------------+
+ |BBBBBBBBBBBBBB| Logic block header
+ |DDDDDDDDDDDDDD|
+ | (Inode Data) |
+ 4*n+3 --+--------------+
+ |BBBBBBBBBBBBBB| Logic block header
+ |IIIIIIIIIIIIII| Next inode header
+ |DDDDDDDDDDDDDD|
+ | (Inode Data) |
+ n+1 4*(n+1) --+--------------+
+ |BBBBBBBBBBBBBB| Logic block header
+ | | All FLASH is unused after the end of the final
+ | | inode.
+ --+--------------+
+
+General operation
+=================
+
+ Inodes are written starting at the beginning of FLASH. As inodes are
+ deleted, they are marked as deleted but not removed. As new inodes are
+ written, allocations proceed to toward the end of the FLASH -- thus,
+ supporting wear leveling by using all FLASH blocks equally.
+
+ When the FLASH becomes full (no more space at the end of the FLASH), a
+ re-packing operation must be performed: All inodes marked deleted are
+ finally removed and the remaining inodes are packed at the beginning of
+ the FLASH. Allocations then continue at the freed FLASH memory at the
+ end of the FLASH.
+
+Headers
+=======
+ BLOCK HEADER:
+ The block header is used to determine if the block has every been
+ formatted and also indicates bad blocks which should never be used.
+
+ INODE HEADER:
+ Each inode begins with an inode header that contains, among other things,
+ the name of the inode, the offset to the first data block, and the
+ length of the inode data.
+
+ At present, the only kind of inode support is a file. So for now, the
+ term file and inode are interchangeable.
+
+ INODE DATA HEADER:
+ Inode data is enclosed in a data header. For a given inode, there
+ is at most one inode data block per logical block. If the inode data
+ spans more than one logical block, then the inode data may be enclosed
+ in multiple data blocks, one per logical block.
+
+NXFFS Limitations
+=================
+
+This implementation is very simple as, as a result, has several limitations
+that you should be aware before opting to use NXFFS:
+
+1. Since the files are contiguous in FLASH and since allocations always
+ proceed toward the end of the FLASH, there can only be one file opened
+ for writing at a time. Multiple files may be opened for reading.
+
+2. Files may not be increased in size after they have been closed. The
+ O_APPEND open flag is not supported.
+
+3. Files are always written sequential. Seeking within a file opened for
+ writing will not work.
+
+4. There are no directories, however, '/' may be used within a file name
+ string providing some illusion of directories.
+
+5. Files may be opened for reading or for writing, but not both: The O_RDWR
+ open flag is not supported.
+
+6. The re-packing process occurs only during a write when the free FLASH
+ memory at the end of the FLASH is exhausted. Thus, occasionally, file
+ writing may take a long time.
+
+7. Another limitation is that there can be only a single NXFFS volume
+ mounted at any time. This has to do with the fact that we bind to
+ an MTD driver (instead of a block driver) and bypass all of the normal
+ mount operations.
+
+Multiple Writers
+================
+
+As mentioned in the limitations above, there can be only one file opened
+for writing at a time. If one thread has a file opened for writing and
+another thread attempts to open a file for writing, then that second
+thread will be blocked and will have to wait for the first thread to
+close the file.
+
+Such behavior may or may not be a problem for your application, depending
+(1) how long the first thread keeps the file open for writing and (2) how
+critical the behavior of the second thread is. Note that writing to FLASH
+can always trigger a major FLASH reorganization and, hence, there is no
+way to guarantee the first condition: The first thread may have the file
+open for a long time even if it only intends to write a small amount.
+
+Also note that a deadlock condition would occur if the SAME thread
+attempted to open two files for writing. The thread would would be
+blocked waiting for itself to close the first file.
+
+ioctls
+======
+
+The file system supports to ioctls:
+
+FIOC_REFORMAT: Will force the flash to be erased and a fresh, empty
+ NXFFS file system to be written on it.
+FIOC_OPTIMIZE: Will force immediate repacking of the file system. This
+ will increase the amount of wear on the FLASH if you use this!
+
+Things to Do
+============
+
+- The statfs() implementation is minimal. It whould have some calcuation
+ of the f_bfree, f_bavail, f_files, f_ffree return values.
+- There are too many allocs and frees. More structures may need to be
+ pre-allocated.
+- The file name is always extracted and held in allocated, variable-length
+ memory. The file name is not used during reading and eliminating the
+ file name in the entry structure would improve performance.
+- There is a big inefficiency in reading. On each read, the logic searches
+ for the read position from the beginning of the file each time. This
+ may be necessary whenever an lseek() is done, but not in general. Read
+ performance could be improved by keeping FLASH offset and read positional
+ information in the read open file structure.
+- Fault tolerance must be improved. We need to be absolutely certain that
+ any FLASH errors do not cause the file system to behavior incorrectly.
+- Wear leveling might be improved (?). Files are re-packed at the front
+ of FLASH as part of the clean-up operation. However, that means the files
+ that are not modified often become fixed in place at the beginning of
+ FLASH. This reduces the size of the pool moving files at the end of the
+ FLASH. As the file system becomes more filled with fixed files at the
+ front of the device, the level of wear on the blocks at the end of the
+ FLASH increases.
+- When the time comes to reorganization the FLASH, the system may be
+ inavailable for a long time. That is a bad behavior. What is needed,
+ I think, is a garbage collection task that runs periodically so that
+ when the big reorganizaiton event occurs, most of the work is already
+ done. That garbarge collection should search for valid blocks that no
+ longer contain valid data. It should pre-erase them, put them in
+ a good but empty state... all ready for file system re-organization.
+
+
+
diff --git a/nuttx/fs/nxffs/nxffs.h b/nuttx/fs/nxffs/nxffs.h
new file mode 100644
index 000000000..616dc7197
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs.h
@@ -0,0 +1,1083 @@
+/****************************************************************************
+ * fs/nxffs/nxffs.h
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_NXFFS_NXFFS_H
+#define __FS_NXFFS_NXFFS_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <semaphore.h>
+
+#include <nuttx/mtd.h>
+#include <nuttx/fs/nxffs.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* NXFFS Definitions ********************************************************/
+/* General NXFFS organization. The following example assumes 4 logical
+ * blocks per FLASH erase block. The actual relationship is determined by
+ * the FLASH geometry reported by the MTD driver.
+ *
+ * ERASE LOGICAL Inodes begin with a inode header. inode may
+ * BLOCK BLOCK CONTENTS be marked as "deleted," pending re-packing.
+ * n 4*n --+--------------+
+ * |BBBBBBBBBBBBBB| Logic block header
+ * |IIIIIIIIIIIIII| Inodes begin with a inode header
+ * |DDDDDDDDDDDDDD| Data block containing inode data block
+ * | (Inode Data) |
+ * 4*n+1 --+--------------+
+ * |BBBBBBBBBBBBBB| Logic block header
+ * |DDDDDDDDDDDDDD| Inodes may consist of multiple data blocks
+ * | (Inode Data) |
+ * |IIIIIIIIIIIIII| Next inode header
+ * | | Possibly a few unused bytes at the end of a block
+ * 4*n+2 --+--------------+
+ * |BBBBBBBBBBBBBB| Logic block header
+ * |DDDDDDDDDDDDDD|
+ * | (Inode Data) |
+ * 4*n+3 --+--------------+
+ * |BBBBBBBBBBBBBB| Logic block header
+ * |IIIIIIIIIIIIII| Next inode header
+ * |DDDDDDDDDDDDDD|
+ * | (Inode Data) |
+ * n+1 4*(n+1) --+--------------+
+ * |BBBBBBBBBBBBBB| Logic block header
+ * | | All FLASH is unused after the end of the final
+ * | | inode.
+ * --+--------------+
+ *
+ * General operation:
+ * Inodes are written starting at the beginning of FLASH. As inodes are
+ * deleted, they are marked as deleted but not removed. As new inodes are
+ * written, allocations proceed to toward the end of the FLASH -- thus,
+ * supporting wear leveling by using all FLASH blocks equally.
+ *
+ * When the FLASH becomes full (no more space at the end of the FLASH), a
+ * re-packing operation must be performed: All inodes marked deleted are
+ * finally removed and the remaining inodes are packed at the beginning of
+ * the FLASH. Allocations then continue at the freed FLASH memory at the
+ * end of the FLASH.
+ *
+ * BLOCK HEADER:
+ * The block header is used to determine if the block has every been
+ * formatted and also indicates bad blocks which should never be used.
+ *
+ * INODE HEADER:
+ * Each inode begins with an inode header that contains, among other things,
+ * the name of the inode, the offset to the first data block, and the
+ * length of the inode data.
+ *
+ * At present, the only kind of inode support is a file. So for now, the
+ * term file and inode are interchangeable.
+ *
+ * INODE DATA HEADER:
+ * Inode data is enclosed in a data header. For a given inode, there
+ * is at most one inode data block per logical block. If the inode data
+ * spans more than one logical block, then the inode data may be enclosed
+ * in multiple data blocks, one per logical block.
+ *
+ * NXFFS Limitations:
+ * 1. Since the files are contiguous in FLASH and since allocations always
+ * proceed toward the end of the FLASH, there can only be one file opened
+ * for writing at a time. Multiple files may be opened for reading.
+ * 2. Files may not be increased in size after they have been closed. The
+ * O_APPEND open flag is not supported.
+ * 3. Files are always written sequential. Seeking within a file opened for
+ * writing will not work.
+ * 4. There are no directories, however, '/' may be used within a file name
+ * string providing some illusion of directories.
+ * 5. Files may be opened for reading or for writing, but not both: The O_RDWR
+ * open flag is not supported.
+ * 6. The re-packing process occurs only during a write when the free FLASH
+ * memory at the end of the FLASH is exhausted. Thus, occasionally, file
+ * writing may take a long time.
+ * 7. Another limitation is that there can be only a single NXFFS volume
+ * mounted at any time. This has to do with the fact that we bind to
+ * an MTD driver (instead of a block driver) and bypass all of the normal
+ * mount operations.
+ */
+
+/* Values for logical block state. Basically, there are only two, perhaps
+ * three, states:
+ *
+ * BLOCK_STATE_GOOD - The block is not known to be bad.
+ * BLOCK_STATE_BAD - An error was found on the block and it is marked bad.
+ * Other values - The block is bad and has an invalid state.
+ *
+ * Care is taken so that the GOOD to BAD transition only involves burning
+ * bits from the erased to non-erased state.
+ */
+
+#define BLOCK_STATE_GOOD (CONFIG_NXFFS_ERASEDSTATE ^ 0x44)
+#define BLOCK_STATE_BAD (CONFIG_NXFFS_ERASEDSTATE ^ 0x55)
+
+/* Values for NXFFS inode state. Similar there are 2 (maybe 3) inode states:
+ *
+ * INODE_STATE_FILE - The inode is a valid usuable, file
+ * INODE_STATE_DELETED - The inode has been deleted.
+ * Other values - The inode is bad and has an invalid state.
+ *
+ * Care is taken so that the VALID to DELETED transition only involves burning
+ * bits from the erased to non-erased state.
+ */
+
+#define INODE_STATE_FILE (CONFIG_NXFFS_ERASEDSTATE ^ 0x22)
+#define INODE_STATE_DELETED (CONFIG_NXFFS_ERASEDSTATE ^ 0xaa)
+
+/* Number of bytes in an the NXFFS magic sequences */
+
+#define NXFFS_MAGICSIZE 4
+
+/* When we allocate FLASH for a new inode data block, we will require that
+ * space is available to hold this minimum number of data bytes in addition
+ * to the size of the data block headeer.
+ */
+
+#define NXFFS_MINDATA 16
+
+/* Internal definitions *****************************************************/
+/* If we encounter this number of erased bytes, we assume that all of the
+ * flash beyond this point is erased.
+ */
+
+#define NXFFS_NERASED 128
+
+/* Quasi-standard definitions */
+
+#ifndef MIN
+# define MIN(a,b) (a < b ? a : b)
+#endif
+
+#ifndef MAX
+# define MAX(a,b) (a > b ? a : b)
+#endif
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* This structure defines each packed block on the FLASH media */
+
+struct nxffs_block_s
+{
+ uint8_t magic[4]; /* 0-3: Magic number for valid block */
+ uint8_t state; /* 4: Block state: See BLOCK_STATE_* */
+};
+#define SIZEOF_NXFFS_BLOCK_HDR 5
+
+/* This structure defines each packed NXFFS inode header on the FLASH media */
+
+struct nxffs_inode_s
+{
+ uint8_t magic[4]; /* 0-3: Magic number for valid inode */
+ uint8_t state; /* 4: Inode state: See INODE_STATE_* */
+ uint8_t namlen; /* 5: Length of the inode name */
+ uint8_t noffs[4]; /* 6-9: FLASH offset to the file name */
+ uint8_t doffs[4]; /* 10-13: FLASH offset to the first data block */
+ uint8_t utc[4]; /* 14-17: Creation time */
+ uint8_t crc[4]; /* 18-21: CRC32 */
+ uint8_t datlen[4]; /* 22-25: Length of data in bytes */
+};
+#define SIZEOF_NXFFS_INODE_HDR 26
+
+/* This structure defines each packed NXFFS data header on the FLASH media */
+
+struct nxffs_data_s
+{
+ uint8_t magic[4]; /* 0-3: Magic number for valid data */
+ uint8_t crc[4]; /* 4-7: CRC32 */
+ uint8_t datlen[2]; /* 8-9: Length of data in bytes */
+};
+#define SIZEOF_NXFFS_DATA_HDR 10
+
+/* This is an in-memory representation of the NXFFS inode as extracted from
+ * FLASH and with additional state information.
+ */
+
+struct nxffs_entry_s
+{
+ off_t hoffset; /* FLASH offset to the inode header */
+ off_t noffset; /* FLASH offset to the inode name */
+ off_t doffset; /* FLASH offset to the first data header */
+ FAR char *name; /* inode name */
+ uint32_t utc; /* Time stamp */
+ uint32_t datlen; /* Length of inode data */
+};
+
+/* This structure describes int in-memory representation of the data block */
+
+struct nxffs_blkentry_s
+{
+ off_t hoffset; /* Offset to the block data header */
+ uint16_t datlen; /* Length of data following the header */
+ uint16_t foffset; /* Offset to start of data */
+};
+
+/* This structure describes the state of one open file. This structure
+ * is protected by the volume semaphore.
+ */
+
+struct nxffs_ofile_s
+{
+ struct nxffs_ofile_s *flink; /* Supports a singly linked list */
+ int16_t crefs; /* Reference count */
+ mode_t oflags; /* Open mode */
+ struct nxffs_entry_s entry; /* Describes the NXFFS inode entry */
+};
+
+/* A file opened for writing require some additional information */
+
+struct nxffs_wrfile_s
+{
+ /* The following fields provide the common open file information. */
+
+ struct nxffs_ofile_s ofile;
+
+ /* The following fields are required to support the write operation */
+
+ bool truncate; /* Delete a file of the same name */
+ uint16_t datlen; /* Number of bytes written in data block */
+ off_t doffset; /* FLASH offset to the current data header */
+ uint32_t crc; /* Accumulated data block CRC */
+};
+
+/* This structure represents the overall state of on NXFFS instance. */
+
+struct nxffs_volume_s
+{
+ FAR struct mtd_dev_s *mtd; /* Supports FLASH access */
+ sem_t exclsem; /* Used to assure thread-safe access */
+ sem_t wrsem; /* Enforces single writer restriction */
+ struct mtd_geometry_s geo; /* Device geometry */
+ uint8_t blkper; /* R/W blocks per erase block */
+ uint16_t iooffset; /* Next offset in read/write access (in ioblock) */
+ off_t inoffset; /* Offset to the first valid inode header */
+ off_t froffset; /* Offset to the first free byte */
+ off_t nblocks; /* Number of R/W blocks on volume */
+ off_t ioblock; /* Current block number being accessed */
+ off_t cblock; /* Starting block number in cache */
+ FAR struct nxffs_ofile_s *ofiles; /* A singly-linked list of open files */
+ FAR uint8_t *cache; /* On cached erase block for general I/O */
+ FAR uint8_t *pack; /* A full erase block to support packing */
+};
+
+/* This structure describes the state of the blocks on the NXFFS volume */
+
+struct nxffs_blkstats_s
+{
+ off_t nblocks; /* Total number of FLASH blocks */
+ off_t ngood; /* Number of good FLASH blocks found */
+ off_t nbad; /* Number of well-formatted FLASH blocks marked as bad */
+ off_t nunformat; /* Number of unformatted FLASH blocks */
+ off_t ncorrupt; /* Number of blocks with correupted format info */
+};
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/* The magic number that appears that the beginning of each NXFFS (logical)
+ * block
+ */
+
+extern const uint8_t g_blockmagic[NXFFS_MAGICSIZE];
+
+/* The magic number that appears that the beginning of each NXFFS inode */
+
+extern const uint8_t g_inodemagic[NXFFS_MAGICSIZE];
+
+/* The magic number that appears that the beginning of each NXFFS inode
+ * data block.
+ */
+
+extern const uint8_t g_datamagic[NXFFS_MAGICSIZE];
+
+/* If CONFIG_NXFSS_PREALLOCATED is defined, then this is the single, pre-
+ * allocated NXFFS volume instance.
+ */
+
+#ifdef CONFIG_NXFSS_PREALLOCATED
+extern struct nxffs_volume_s g_volume;
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_limits
+ *
+ * Description:
+ * Recalculate file system limits: (1) the FLASH offset to the first,
+ * valid inode, and (2) the FLASH offset to the first, unused byte after
+ * the last inode (invalid or not).
+ *
+ * The first, lower limit must be recalculated: (1) initially, (2)
+ * whenever the first inode is deleted, or (3) whenever inode is moved
+ * as part of the file system packing operation.
+ *
+ * The second, upper limit must be (1) incremented whenever new file
+ * data is written, or (2) recalculated as part of the file system packing
+ * operation.
+ *
+ * Input Parameters:
+ * volume - Identifies the NXFFS volume
+ *
+ * Returned Value:
+ * Zero on success. Otherwise, a negated error is returned indicating the
+ * nature of the failure.
+ *
+ * Defined in nxffs_initialize.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_limits(FAR struct nxffs_volume_s *volume);
+
+/****************************************************************************
+ * Name: nxffs_rdle16
+ *
+ * Description:
+ * Get a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * val - A pointer to the first byte of the little endian value.
+ *
+ * Returned Values:
+ * A uint16_t representing the whole 16-bit integer value
+ *
+ * Defined in nxffs_util.c
+ *
+ ****************************************************************************/
+
+extern uint16_t nxffs_rdle16(FAR const uint8_t *val);
+
+/****************************************************************************
+ * Name: nxffs_wrle16
+ *
+ * Description:
+ * Put a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 16-bit value to be saved.
+ *
+ * Returned Values:
+ * None
+ *
+ * Defined in nxffs_util.c
+ *
+ ****************************************************************************/
+
+extern void nxffs_wrle16(uint8_t *dest, uint16_t val);
+
+/****************************************************************************
+ * Name: nxffs_rdle32
+ *
+ * Description:
+ * Get a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * val - A pointer to the first byte of the little endian value.
+ *
+ * Returned Values:
+ * A uint32_t representing the whole 32-bit integer value
+ *
+ * Defined in nxffs_util.c
+ *
+ ****************************************************************************/
+
+extern uint32_t nxffs_rdle32(FAR const uint8_t *val);
+
+/****************************************************************************
+ * Name: nxffs_wrle32
+ *
+ * Description:
+ * Put a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Value:
+ * None
+ *
+ * Defined in nxffs_util.c
+ *
+ ****************************************************************************/
+
+extern void nxffs_wrle32(uint8_t *dest, uint32_t val);
+
+/****************************************************************************
+ * Name: nxffs_erased
+ *
+ * Description:
+ * Check if a block of memory is in the erased state.
+ *
+ * Input Parameters:
+ * buffer - Address of the start of the memory to check.
+ * buflen - The number of bytes to check.
+ *
+ * Returned Values:
+ * The number of erased bytes found at the beginning of the memory region.
+ *
+ * Defined in nxffs_util.c
+ *
+ ****************************************************************************/
+
+extern size_t nxffs_erased(FAR const uint8_t *buffer, size_t buflen);
+
+/****************************************************************************
+ * Name: nxffs_rdcache
+ *
+ * Description:
+ * Read one I/O block into the volume cache memory.
+ *
+ * Input Parameters:
+ * volume - Describes the current volume
+ * block - The first logical block to read
+ *
+ * Returned Value:
+ * Negated errnos are returned only in the case of MTD reported failures.
+ * Nothing in the volume data itself will generate errors.
+ *
+ * Defined in nxffs_cache.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_rdcache(FAR struct nxffs_volume_s *volume, off_t block);
+
+/****************************************************************************
+ * Name: nxffs_wrcache
+ *
+ * Description:
+ * Write one or more logical blocks from the volume cache memory.
+ *
+ * Input Parameters:
+ * volume - Describes the current volume
+ *
+ * Returned Value:
+ * Negated errnos are returned only in the case of MTD reported failures.
+ *
+ * Defined in nxffs_cache.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_wrcache(FAR struct nxffs_volume_s *volume);
+
+/****************************************************************************
+ * Name: nxffs_ioseek
+ *
+ * Description:
+ * Seek to a position in FLASH memory. This simply sets up the offsets
+ * and pointer values. This is a necessary step prior to using
+ * nxffs_getc().
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * offset - The physical offset in bytes from the beginning of the FLASH
+ * in bytes.
+ *
+ * Defined in nxffs_cache.c
+ *
+ ****************************************************************************/
+
+extern void nxffs_ioseek(FAR struct nxffs_volume_s *volume, off_t offset);
+
+/****************************************************************************
+ * Name: nxffs_iotell
+ *
+ * Description:
+ * Report the current position.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ *
+ * Returned Value:
+ * The offset from the beginning of FLASH to the current seek position.
+ *
+ * Defined in nxffs_cache.c
+ *
+ ****************************************************************************/
+
+extern off_t nxffs_iotell(FAR struct nxffs_volume_s *volume);
+
+/****************************************************************************
+ * Name: nxffs_getc
+ *
+ * Description:
+ * Get the next byte from FLASH. This function allows the data in the
+ * formatted FLASH blocks to be read as a continuous byte stream, skipping
+ * over bad blocks and block headers as necessary.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume. The paramters ioblock and iooffset
+ * in the volume structure determine the behavior of nxffs_getc().
+ * reserve - If less than this much space is available at the end of the
+ * block, then skip to the next block.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno indicating the
+ * nature of the failure.
+ *
+ * Defined in nxffs_cache.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_getc(FAR struct nxffs_volume_s *volume, uint16_t reserve);
+
+/****************************************************************************
+ * Name: nxffs_freeentry
+ *
+ * Description:
+ * The inode values returned by nxffs_nextentry() include allocated memory
+ * (specifically, the file name string). This function should be called
+ * to dispose of that memory when the inode entry is no longer needed.
+ *
+ * Note that the nxffs_entry_s containing structure is not freed. The
+ * caller may call kfree upon return of this function if necessary to
+ * free the entry container.
+ *
+ * Input parameters:
+ * entry - The entry to be freed.
+ *
+ * Returned Value:
+ * None
+ *
+ * Defined in nxffs_inode.c
+ *
+ ****************************************************************************/
+
+extern void nxffs_freeentry(FAR struct nxffs_entry_s *entry);
+
+/****************************************************************************
+ * Name: nxffs_nextentry
+ *
+ * Description:
+ * Search for the next valid inode starting at the provided FLASH offset.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume.
+ * offset - The FLASH memory offset to begin searching.
+ * entry - A pointer to memory provided by the caller in which to return
+ * the inode description.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno is returned
+ * that indicates the nature of the failure.
+ *
+ * Defined in nxffs_inode.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_nextentry(FAR struct nxffs_volume_s *volume, off_t offset,
+ FAR struct nxffs_entry_s *entry);
+
+/****************************************************************************
+ * Name: nxffs_findinode
+ *
+ * Description:
+ * Search for an inode with the provided name starting with the first
+ * valid inode and proceeding to the end FLASH or until the matching
+ * inode is found.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * name - The name of the inode to find
+ * entry - The location to return information about the inode.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno is returned
+ * that indicates the nature of the failure.
+ *
+ * Defined in nxffs_inode.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_findinode(FAR struct nxffs_volume_s *volume,
+ FAR const char *name,
+ FAR struct nxffs_entry_s *entry);
+
+/****************************************************************************
+ * Name: nxffs_inodeend
+ *
+ * Description:
+ * Return an *approximiate* FLASH offset to end of the inode data. The
+ * returned value is guaranteed to be be less then or equal to the offset
+ * of the thing-of-interest in FLASH. Parsing for interesting things
+ * can begin at that point.
+ *
+ * Assumption: The inode header has been verified by the caller and is
+ * known to contain valid data.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * entry - Describes the inode.
+ *
+ * Returned Value:
+ * A FLASH offset to the (approximate) end of the inode data. No errors
+ * are detected.
+ *
+ * Defined in nxffs_inode.c
+ *
+ ****************************************************************************/
+
+extern off_t nxffs_inodeend(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_entry_s *entry);
+
+/****************************************************************************
+ * Name: nxffs_verifyblock
+ *
+ * Description:
+ * Assure the the provided (logical) block number is in the block cache
+ * and that it has a valid block header (i.e., proper magic and
+ * marked good)
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * block - The (logical) block number to load and verify.
+ *
+ * Returned Values:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure.
+ *
+ * Defined in nxffs_block.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_verifyblock(FAR struct nxffs_volume_s *volume, off_t block);
+
+/****************************************************************************
+ * Name: nxffs_validblock
+ *
+ * Description:
+ * Find the next valid (logical) block in the volume.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * block - On entry, this provides the starting block number. If the
+ * function is succesfful, then this memory location will hold the
+ * block number of the next valid block on return.
+ *
+ * Returned Value:
+ * Zero on success otherwise a negated errno value indicating the nature
+ * of the failure.
+ *
+ * Defined in nxffs_block.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_validblock(struct nxffs_volume_s *volume, off_t *block);
+
+/****************************************************************************
+ * Name: nxffs_blockstats
+ *
+ * Description:
+ * Analyze the NXFFS volume. This operation must be performed when the
+ * volume is first mounted in order to detect if the volume has been
+ * formatted and contains a usable NXFFS file system.
+ *
+ * Input Parameters:
+ * volume - Describes the current NXFFS volume.
+ * stats - On return, will hold nformation describing the state of the
+ * volume.
+ *
+ * Returned Value:
+ * Negated errnos are returned only in the case of MTD reported failures.
+ * Nothing in the volume data itself will generate errors.
+ *
+ * Defined in nxffs_blockstats.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_blockstats(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_blkstats_s *stats);
+
+/****************************************************************************
+ * Name: nxffs_reformat
+ *
+ * Description:
+ * Erase and reformat the entire volume. Verify each block and mark
+ * improperly erased blocks as bad.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume to be reformatted.
+ *
+ * Returned Value:
+ * Zero on success or a negated errno on a failure. Failures will be
+ * returned n the case of MTD reported failures o.
+ * Nothing in the volume data itself will generate errors.
+ *
+ * Defined in nxffs_reformat.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_reformat(FAR struct nxffs_volume_s *volume);
+
+/****************************************************************************
+ * Name: nxffs_findofile
+ *
+ * Description:
+ * Search the list of already opened files to see if the inode of this
+ * name is one of the opened files.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume.
+ * name - The name of the inode to check.
+ *
+ * Returned Value:
+ * If an inode of this name is found in the list of opened inodes, then
+ * a reference to the open file structure is returned. NULL is returned
+ * otherwise.
+ *
+ * Defined in nxffs_open.c
+ *
+ ****************************************************************************/
+
+extern FAR struct nxffs_ofile_s *nxffs_findofile(FAR struct nxffs_volume_s *volume,
+ FAR const char *name);
+
+/****************************************************************************
+ * Name: nxffs_findwriter
+ *
+ * Description:
+ * Search the list of already opened files and return the open file
+ * instance for the write.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume.
+ *
+ * Returned Value:
+ * If there is an active writer of the volume, its open file instance is
+ * returned. NULL is returned otherwise.
+ *
+ * Defined in nxffs_open.c
+ *
+ ****************************************************************************/
+
+extern FAR struct nxffs_wrfile_s *nxffs_findwriter(FAR struct nxffs_volume_s *volume);
+
+/****************************************************************************
+ * Name: nxffs_wrinode
+ *
+ * Description:
+ * Write the inode header (only to FLASH. This is done in two contexts:
+ *
+ * 1. When an inode is closed, or
+ * 2. As part of the file system packing logic when an inode is moved.
+ *
+ * Note that in either case, the inode name has already been written to
+ * FLASH.
+ *
+ * Input parameters
+ * volume - Describes the NXFFS volume
+ * entry - Describes the inode header to write
+ *
+ * Returned Value:
+ * Zero is returned on success; Otherwise, a negated errno value is returned
+ * indicating the nature of the failure.
+ *
+ * Defined in nxffs_open.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_wrinode(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_entry_s *entry);
+
+/****************************************************************************
+ * Name: nxffs_updateinode
+ *
+ * Description:
+ * The packing logic has moved an inode. Check if any open files are using
+ * this inode and, if so, move the data in the open file structure as well.
+ *
+ * Input parameters
+ * volume - Describes the NXFFS volume
+ * entry - Describes the new inode entry
+ *
+ * Returned Value:
+ * Zero is returned on success; Otherwise, a negated errno value is returned
+ * indicating the nature of the failure.
+ *
+ ****************************************************************************/
+
+extern int nxffs_updateinode(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_entry_s *entry);
+
+/****************************************************************************
+ * Name: nxffs_wrreserve
+ *
+ * Description:
+ * Find a valid location for a file system object of 'size'. A valid
+ * location will have these properties:
+ *
+ * 1. It will lie in the free flash region.
+ * 2. It will have enough contiguous memory to hold the entire object
+ * 3. The memory at this location will be fully erased.
+ *
+ * This function will only perform the checks of 1) and 2). The
+ * end-of-filesystem offset, froffset, is update past this memory which,
+ * in effect, reserves the memory.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * size - The size of the object to be reserved.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure. Of special interest
+ * the return error of -ENOSPC which means that the FLASH volume is
+ * full and should be repacked.
+ *
+ * On successful return the following are also valid:
+ *
+ * volume->ioblock - Read/write block number of the block containing the
+ * candidate oject position
+ * volume->iooffset - The offset in the block to the candidate object
+ * position.
+ * volume->froffset - Updated offset to the first free FLASH block after
+ * the reserved memory.
+ *
+ * Defined in nxffs_write.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_wrreserve(FAR struct nxffs_volume_s *volume, size_t size);
+
+/****************************************************************************
+ * Name: nxffs_wrverify
+ *
+ * Description:
+ * Find a valid location for the object. A valid location will have
+ * these properties:
+ *
+ * 1. It will lie in the free flash region.
+ * 2. It will have enough contiguous memory to hold the entire header
+ * (excluding the file name which may lie in the next block).
+ * 3. The memory at this location will be fully erased.
+ *
+ * This function will only perform the check 3). On entry it assumes the
+ * following settings (left by nxffs_wrreserve()):
+ *
+ * volume->ioblock - Read/write block number of the block containing the
+ * candidate oject position
+ * volume->iooffset - The offset in the block to the candidate object
+ * position.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * size - The size of the object to be verifed.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure. Of special interest
+ * the return error of -ENOSPC which means that the FLASH volume is
+ * full and should be repacked.
+ *
+ * On successful return the following are also valid:
+ *
+ * volume->ioblock - Read/write block number of the block containing the
+ * verified object position
+ * volume->iooffset - The offset in the block to the verified object
+ * position.
+ * volume->froffset - Updated offset to the first free FLASH block.
+ *
+ * Defined in nxffs_write.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_wrverify(FAR struct nxffs_volume_s *volume, size_t size);
+
+/****************************************************************************
+ * Name: nxffs_wrblkhdr
+ *
+ * Description:
+ * Write the block header information. This is done (1) whenever the end-
+ * block is encountered and (2) also when the file is closed in order to
+ * flush the final block of data to FLASH.
+ *
+ * Input Parameters:
+ * volume - Describes the state of the NXFFS volume
+ * wrfile - Describes the state of the open file
+ *
+ * Returned Value:
+ * Zero is returned on success; Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ * Defined in nxffs_write.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_wrblkhdr(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_wrfile_s *wrfile);
+
+/****************************************************************************
+ * Name: nxffs_nextblock
+ *
+ * Description:
+ * Search for the next valid data block starting at the provided
+ * FLASH offset.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume.
+ * datlen - A memory location to return the data block length.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno is returned
+ * that indicates the nature of the failure.
+ *
+ * Defined in nxffs_read.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_nextblock(FAR struct nxffs_volume_s *volume, off_t offset,
+ FAR struct nxffs_blkentry_s *blkentry);
+
+/****************************************************************************
+ * Name: nxffs_rdblkhdr
+ *
+ * Description:
+ * Read and verify the data block header at the specified offset.
+ *
+ * Input Parameters:
+ * volume - Describes the current volume.
+ * offset - The byte offset from the beginning of FLASH where the data block
+ * header is expected.
+ * datlen - A memory location to return the data block length.
+ *
+ * Returned Value:
+ * Zero on success. Otherwise, a negated errno value is returned
+ * indicating the nature of the failure.
+ *
+ * Defined in nxffs_read.c
+ *
+ ****************************************************************************/
+
+extern int nxffs_rdblkhdr(FAR struct nxffs_volume_s *volume, off_t offset,
+ FAR uint16_t *datlen);
+
+/****************************************************************************
+ * Name: nxffs_rminode
+ *
+ * Description:
+ * Remove an inode from FLASH. This is the internal implementation of
+ * the file system unlinke operation.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume.
+ * name - the name of the inode to be deleted.
+ *
+ * Returned Value:
+ * Zero is returned if the inode is successfully deleted. Otherwise, a
+ * negated errno value is returned indicating the nature of the failure.
+ *
+ ****************************************************************************/
+
+extern int nxffs_rminode(FAR struct nxffs_volume_s *volume, FAR const char *name);
+
+/****************************************************************************
+ * Name: nxffs_pack
+ *
+ * Description:
+ * Pack and re-write the filesystem in order to free up memory at the end
+ * of FLASH.
+ *
+ * Input Parameters:
+ * volume - The volume to be packed.
+ *
+ * Returned Values:
+ * Zero on success; Otherwise, a negated errno value is returned to
+ * indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+extern int nxffs_pack(FAR struct nxffs_volume_s *volume);
+
+/****************************************************************************
+ * Standard mountpoint operation methods
+ *
+ * Description:
+ * See include/nuttx/fs/fs.h
+ *
+ * - nxffs_open() and nxffs_close() are defined in nxffs_open.c
+ * - nxffs_read() is defined in nxffs_read.c
+ * - nxffs_write() is defined in nxffs_write.c
+ * - nxffs_ioctl() is defined in nxffs_ioctl.c
+ * - nxffs_opendir(), nxffs_readdir(), and nxffs_rewindir() are defined in
+ * nxffs_dirent.c
+ * - nxffs_bind() and nxffs_unbind() are defined in nxffs_initialize.c
+ * - nxffs_stat() and nxffs_statfs() are defined in nxffs_stat.c
+ * - nxffs_unlink() is defined nxffs_unlink.c
+ *
+ ****************************************************************************/
+
+struct file; /* Forward references */
+struct inode;
+struct fs_dirent_s;
+struct statfs;
+struct stat;
+
+extern int nxffs_open(FAR struct file *filep, FAR const char *relpath,
+ int oflags, mode_t mode);
+extern int nxffs_close(FAR struct file *filep);
+extern ssize_t nxffs_read(FAR struct file *filep, FAR char *buffer,
+ size_t buflen);
+extern ssize_t nxffs_write(FAR struct file *filep, FAR const char *buffer,
+ size_t buflen);
+extern int nxffs_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+extern int nxffs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
+ FAR struct fs_dirent_s *dir);
+extern int nxffs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir);
+extern int nxffs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir);
+extern int nxffs_bind(FAR struct inode *blkdriver, FAR const void *data,
+ FAR void **handle);
+extern int nxffs_unbind(FAR void *handle, FAR struct inode **blkdriver);
+extern int nxffs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf);
+extern int nxffs_stat(FAR struct inode *mountpt, FAR const char *relpath,
+ FAR struct stat *buf);
+extern int nxffs_unlink(FAR struct inode *mountpt, FAR const char *relpath);
+
+#endif /* __FS_NXFFS_NXFFS_H */
+
+
diff --git a/nuttx/fs/nxffs/nxffs_block.c b/nuttx/fs/nxffs/nxffs_block.c
new file mode 100644
index 000000000..6701b6e6b
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_block.c
@@ -0,0 +1,186 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_block.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/mtd.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_verifyblock
+ *
+ * Description:
+ * Assure the the provided (logical) block number is in the block cache
+ * and that it has a valid block header (i.e., proper magic and
+ * marked good)
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * block - The (logical) block number to load and verify.
+ *
+ * Returned Values:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure. -ENOENT is returned
+ * if the block is a bad block.
+ *
+ ****************************************************************************/
+
+int nxffs_verifyblock(FAR struct nxffs_volume_s *volume, off_t block)
+{
+ FAR struct nxffs_block_s *blkhdr;
+ int ret;
+
+ /* Make sure the the block is in the cache */
+
+ ret = nxffs_rdcache(volume, block);
+ if (ret < 0)
+ {
+ /* Perhaps we are at the end of the media */
+
+ fvdbg("Failed to read data into cache: %d\n", ret);
+ return ret;
+ }
+
+ /* Check if the block has a magic number (meaning that it is not
+ * erased) and that it is valid (meaning that it is not marked
+ * for deletion)
+ */
+
+ blkhdr = (FAR struct nxffs_block_s *)volume->cache;
+ if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) == 0)
+ {
+ /* This does appear to be a block */
+
+ if (blkhdr->state == BLOCK_STATE_GOOD)
+ {
+ /* The block is valid */
+
+ return OK;
+ }
+ else if (blkhdr->state == BLOCK_STATE_BAD)
+ {
+ /* -ENOENT is a special indication that this is a properly marked
+ * bad block
+ */
+
+ return -ENOENT;
+ }
+ }
+
+ /* Whatever is here where a block header should be is invalid */
+
+ return -EINVAL;
+}
+
+/****************************************************************************
+ * Name: nxffs_validblock
+ *
+ * Description:
+ * Find the next valid (logical) block in the volume.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * block - On entry, this provides the starting block number. If the
+ * function is succesfful, then this memory location will hold the
+ * block number of the next valid block on return.
+ *
+ * Returned Value:
+ * Zero on success otherwise a negated errno value indicating the nature
+ * of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_validblock(struct nxffs_volume_s *volume, off_t *block)
+{
+ off_t i;
+ int ret;
+
+ DEBUGASSERT(volume && block);
+
+ /* Loop for each possible block or until a valid block is found */
+
+ for (i = *block; i < volume->nblocks; i++)
+ {
+ /* Loop until we find a valid block */
+
+ ret = nxffs_verifyblock(volume, i);
+ if (ret == OK)
+ {
+ /* We found it, return the block number */
+
+ *block = i;
+ return OK;
+ }
+ }
+
+ /* ENOSPC is special return value that means that there is no further,
+ * valid blocks left in the volume.
+ */
+
+ fdbg("No valid block found\n");
+ return -ENOSPC;
+}
diff --git a/nuttx/fs/nxffs/nxffs_blockstats.c b/nuttx/fs/nxffs/nxffs_blockstats.c
new file mode 100644
index 000000000..348374e67
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_blockstats.c
@@ -0,0 +1,152 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_blockstats.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/mtd.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_blockstats
+ *
+ * Description:
+ * Analyze the NXFFS volume. This operation must be performed when the
+ * volume is first mounted in order to detect if the volume has been
+ * formatted and contains a usable NXFFS file system.
+ *
+ * Input Parameters:
+ * volume - Describes the current NXFFS volume.
+ * stats - On return, will hold nformation describing the state of the
+ * volume.
+ *
+ * Returned Value:
+ * Negated errnos are returned only in the case of MTD reported failures.
+ * Nothing in the volume data itself will generate errors.
+ *
+ ****************************************************************************/
+
+int nxffs_blockstats(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_blkstats_s *stats)
+{
+ FAR uint8_t *bptr; /* Pointer to next block data */
+ off_t ioblock; /* I/O block number */
+ int lblock; /* Logical block index */
+ int ret;
+
+ /* Process each erase block */
+
+ memset(stats, 0, sizeof(struct nxffs_blkstats_s));
+
+ for (ioblock = 0; ioblock < volume->nblocks; ioblock += volume->blkper)
+ {
+ /* Read the full erase block */
+
+ ret = MTD_BREAD(volume->mtd, ioblock, volume->blkper, volume->pack);
+ if (ret < volume->blkper)
+ {
+ fdbg("Failed to read erase block %d: %d\n", ioblock / volume->blkper, -ret);
+ return ret;
+ }
+
+ /* Process each logical block */
+
+ for (bptr = volume->pack, lblock = 0;
+ lblock < volume->blkper;
+ bptr += volume->geo.blocksize, lblock++)
+ {
+ FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s*)bptr;
+
+ /* Collect statistics */
+
+ stats->nblocks++;
+ if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) != 0)
+ {
+ stats->nunformat++;
+ }
+ else if (blkhdr->state == BLOCK_STATE_BAD)
+ {
+ stats->nbad++;
+ }
+ else if (blkhdr->state == BLOCK_STATE_GOOD)
+ {
+ stats-> ngood++;
+ }
+ else
+ {
+ stats->ncorrupt++;
+ }
+ }
+ }
+
+ fdbg("Number blocks: %d\n", stats->nblocks);
+ fdbg(" Good blocks: %d\n", stats->ngood);
+ fdbg(" Bad blocks: %d\n", stats->nbad);
+ fdbg(" Unformatted blocks: %d\n", stats->nunformat);
+ fdbg(" Corrupt blocks: %d\n", stats->ncorrupt);
+ return OK;
+}
+
+
diff --git a/nuttx/fs/nxffs/nxffs_cache.c b/nuttx/fs/nxffs/nxffs_cache.c
new file mode 100644
index 000000000..0cc97980e
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_cache.c
@@ -0,0 +1,261 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_cache.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/mtd.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_rdcache
+ *
+ * Description:
+ * Read one I/O block into the volume block cache memory.
+ *
+ * Input Parameters:
+ * volume - Describes the current volume
+ * block - The first logical block to read
+ *
+ * Returned Value:
+ * Negated errnos are returned only in the case of MTD reported failures.
+ * Nothing in the volume data itself will generate errors.
+ *
+ ****************************************************************************/
+
+int nxffs_rdcache(FAR struct nxffs_volume_s *volume, off_t block)
+{
+ size_t nxfrd;
+
+ /* Check if the requested data is already in the cache */
+
+ if (block != volume->cblock)
+ {
+ /* Read the specified blocks into cache */
+
+ nxfrd = MTD_BREAD(volume->mtd, block, 1, volume->cache);
+ if (nxfrd != 1)
+ {
+ fvdbg("Read block %d failed: %d\n", block, nxfrd);
+ return -EIO;
+ }
+
+ /* Remember what is in the cache */
+
+ volume->cblock = block;
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxffs_wrcache
+ *
+ * Description:
+ * Write one or more logical blocks from the volume cache memory.
+ *
+ * Input Parameters:
+ * volume - Describes the current volume
+ *
+ * Returned Value:
+ * Negated errnos are returned only in the case of MTD reported failures.
+ *
+ ****************************************************************************/
+
+int nxffs_wrcache(FAR struct nxffs_volume_s *volume)
+{
+ size_t nxfrd;
+
+ /* Write the current block from the cache */
+
+ nxfrd = MTD_BWRITE(volume->mtd, volume->cblock, 1, volume->cache);
+ if (nxfrd != 1)
+ {
+ fdbg("Write block %d failed: %d\n", volume->cblock, nxfrd);
+ return -EIO;
+ }
+
+ /* Write was successful */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxffs_ioseek
+ *
+ * Description:
+ * Seek to a position in FLASH memory. This simply sets up the offsets
+ * and pointer values. This is a necessary step prior to using
+ * nxffs_getc().
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * offset - The physical offset in bytes from the beginning of the FLASH
+ * in bytes.
+ *
+ ****************************************************************************/
+
+void nxffs_ioseek(FAR struct nxffs_volume_s *volume, off_t offset)
+{
+ /* Convert the offset into a block number and a byte offset into the
+ * block.
+ */
+
+ volume->ioblock = offset / volume->geo.blocksize;
+ volume->iooffset = offset - volume->geo.blocksize * volume->ioblock;
+}
+
+/****************************************************************************
+ * Name: nxffs_iotell
+ *
+ * Description:
+ * Report the current position.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ *
+ * Returned Value:
+ * The offset from the beginning of FLASH to the current seek position.
+ *
+ ****************************************************************************/
+
+off_t nxffs_iotell(FAR struct nxffs_volume_s *volume)
+{
+ return volume->ioblock * volume->geo.blocksize + volume->iooffset;
+}
+
+/****************************************************************************
+ * Name: nxffs_getc
+ *
+ * Description:
+ * Get the next byte from FLASH. This function allows the data in the
+ * formatted FLASH blocks to be read as a continuous byte stream, skipping
+ * over bad blocks and block headers as necessary.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume. The paramters ioblock and iooffset
+ * in the volume structure determine the behavior of nxffs_getc().
+ * reserve - If less than this much space is available at the end of the
+ * block, then skip to the next block.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno indicating the
+ * nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_getc(FAR struct nxffs_volume_s *volume, uint16_t reserve)
+{
+ int ret;
+
+ DEBUGASSERT(reserve > 0);
+
+ /* Loop to skip over bad blocks */
+
+ do
+ {
+ /* Check if we have the reserve amount at the end of the current block */
+
+ if (volume->iooffset + reserve > volume->geo.blocksize)
+ {
+ /* Check for attempt to read past the end of FLASH */
+
+ off_t nextblock = volume->ioblock + 1;
+ if (nextblock >= volume->nblocks)
+ {
+ fvdbg("End of FLASH encountered\n");
+ return -ENOSPC;
+ }
+
+ /* Set up the seek to the data just after the header in the
+ * next block.
+ */
+
+ volume->ioblock = nextblock;
+ volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR;
+ }
+
+ /* Make sure that the block is in the cache. The special error
+ * -ENOENT indicates the block was read successfully but was not
+ * marked as a good block. In this case we need to skip to the
+ * next block. All other errors are fatal.
+ */
+
+ ret = nxffs_verifyblock(volume, volume->ioblock);
+ if (ret < 0 && ret != -ENOENT)
+ {
+ fvdbg("Failed to read valid data into cache: %d\n", ret);
+ return ret;
+ }
+ }
+ while (ret != OK);
+
+ /* Return the the character at this offset. Note that on return,
+ * iooffset could point to the byte outside of the current block.
+ */
+
+ ret = (int)volume->cache[volume->iooffset];
+ volume->iooffset++;
+ return ret;
+}
diff --git a/nuttx/fs/nxffs/nxffs_dirent.c b/nuttx/fs/nxffs/nxffs_dirent.c
new file mode 100644
index 000000000..221549438
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_dirent.c
@@ -0,0 +1,217 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_dirent.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <dirent.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/mtd.h>
+#include <nuttx/fs/dirent.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_opendir
+ *
+ * Description:
+ * Open a directory for read access
+ *
+ ****************************************************************************/
+
+int nxffs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
+ FAR struct fs_dirent_s *dir)
+{
+ struct nxffs_volume_s *volume;
+ int ret;
+
+ fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Recover the file system state from the NuttX inode instance */
+
+ volume = mountpt->i_private;
+ ret = sem_wait(&volume->exclsem);
+ if (ret != OK)
+ {
+ goto errout;
+ }
+
+ /* The requested directory must be the volume-relative "root" directory */
+
+ if (relpath && relpath[0] != '\0')
+ {
+ ret = -ENOENT;
+ goto errout_with_semaphore;
+ }
+
+ /* Set the offset to the offset to the first valid inode */
+
+ dir->u.nxffs.nx_offset = volume->inoffset;
+ ret = OK;
+
+errout_with_semaphore:
+ sem_post(&volume->exclsem);
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_readdir
+ *
+ * Description: Read the next directory entry
+ *
+ ****************************************************************************/
+
+int nxffs_readdir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir)
+{
+ FAR struct nxffs_volume_s *volume;
+ FAR struct nxffs_entry_s entry;
+ off_t offset;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Recover the file system state from the NuttX inode instance */
+
+ volume = mountpt->i_private;
+ ret = sem_wait(&volume->exclsem);
+ if (ret != OK)
+ {
+ goto errout;
+ }
+
+ /* Read the next inode header from the offset */
+
+ offset = dir->u.nxffs.nx_offset;
+ ret = nxffs_nextentry(volume, offset, &entry);
+
+ /* If the read was successful, then handle the reported inode. Note
+ * that when the last inode has been reported, the value -ENOENT will
+ * be returned.. which is correct for the readdir() method.
+ */
+
+ if (ret == OK)
+ {
+ /* Return the filename and file type */
+
+ fvdbg("Offset %d: \"%s\"\n", entry.hoffset, entry.name);
+ dir->fd_dir.d_type = DTYPE_FILE;
+ strncpy(dir->fd_dir.d_name, entry.name, NAME_MAX+1);
+
+ /* Discard this entry and set the next offset. */
+
+ dir->u.nxffs.nx_offset = nxffs_inodeend(volume, &entry);
+ nxffs_freeentry(&entry);
+ ret = OK;
+ }
+
+ sem_post(&volume->exclsem);
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_rewindir
+ *
+ * Description:
+ * Reset directory read to the first entry
+ *
+ ****************************************************************************/
+
+int nxffs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir)
+{
+ FAR struct nxffs_volume_s *volume;
+ int ret;
+
+ fvdbg("Entry\n");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Recover the file system state from the NuttX inode instance */
+
+ volume = mountpt->i_private;
+ ret = sem_wait(&volume->exclsem);
+ if (ret != OK)
+ {
+ goto errout;
+ }
+
+ /* Reset the offset to the FLASH offset to the first valid inode */
+
+ dir->u.nxffs.nx_offset = volume->inoffset;
+ ret = OK;
+
+ sem_post(&volume->exclsem);
+errout:
+ return ret;
+}
diff --git a/nuttx/fs/nxffs/nxffs_dump.c b/nuttx/fs/nxffs/nxffs_dump.c
new file mode 100644
index 000000000..6a89aaf1d
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_dump.c
@@ -0,0 +1,479 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_dump.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <debug.h>
+#include <errno.h>
+#include <crc32.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mtd.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Re-define fdbg so that the output does not have so much diagnostic info.
+ * This should still, however, always agree with the defintion in debug.h.
+ */
+
+#undef fdbg
+#define fdbg lib_rawprintf
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nxffs_blkinfo_s
+{
+ struct mtd_geometry_s geo;
+ FAR uint8_t *buffer;
+ off_t nblocks;
+ off_t block;
+ off_t offset;
+ bool verbose;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const char g_hdrformat[] = " BLOCK:OFFS TYPE STATE LENGTH\n";
+static const char g_format[] = " %5d:%-5d %s %s %5d\n";
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_analyzeinode
+ *
+ * Description:
+ * Analyze one candidate inode found in the block.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_FS)
+static inline ssize_t nxffs_analyzeinode(FAR struct nxffs_blkinfo_s *blkinfo,
+ int offset)
+{
+ FAR struct nxffs_inode_s inode;
+ off_t nextblock;
+ uint8_t state;
+ uint32_t noffs;
+ uint32_t doffs;
+ uint32_t utc;
+ uint32_t ecrc;
+ uint32_t datlen;
+ uint32_t crc;
+ size_t spaceleft;
+
+ /* Verify that there is space for an inode header remaining in the block */
+
+ if (offset + SIZEOF_NXFFS_INODE_HDR > blkinfo->geo.blocksize)
+ {
+ /* No.. then this can't be an inode header */
+
+ return ERROR;
+ }
+
+ /* Unpack the header */
+
+ memcpy(&inode, &blkinfo->buffer[offset], SIZEOF_NXFFS_INODE_HDR);
+ noffs = nxffs_rdle32(inode.noffs);
+ doffs = nxffs_rdle32(inode.doffs);
+ utc = nxffs_rdle32(inode.utc);
+ ecrc = nxffs_rdle32(inode.crc);
+ datlen = nxffs_rdle32(inode.datlen);
+
+ /* Misc. sanity checks */
+
+ if (noffs < blkinfo->offset + offset + SIZEOF_NXFFS_BLOCK_HDR)
+ {
+ /* The name begins before the inode header. This can't can't be
+ * a real inode header (or it is a corrupted one).
+ */
+
+ return ERROR;
+ }
+
+
+ /* Can we verify the inode? We need to have the inode name in the same
+ * block to do that (or get access to the next block)
+ */
+
+ if (doffs < blkinfo->offset + offset + SIZEOF_NXFFS_BLOCK_HDR)
+ {
+ /* The first data block begins before the inode header. This can't can't
+ * be a real inode header (or it is a corrupted one).
+ */
+
+ return ERROR;
+ }
+
+ spaceleft = (blkinfo->nblocks - blkinfo->block) * blkinfo->geo.blocksize;
+ spaceleft -= (offset + SIZEOF_NXFFS_BLOCK_HDR);
+ if (datlen > spaceleft)
+ {
+ /* The data length is greater than what would fit in the rest of FLASH
+ * (even ignoring block and data header sizes.
+ */
+
+ return ERROR;
+ }
+
+ /* The name begins after the inode header. Does it begin in this block? */
+
+ nextblock = blkinfo->offset + blkinfo->geo.blocksize;
+ if (noffs > nextblock)
+ {
+ /* Not than we cannot verify the inode header */
+
+ if (blkinfo->verbose)
+ {
+ fdbg(g_format, blkinfo->block, offset, "INODE", "UNVERFD", datlen);
+ }
+ return ERROR;
+ }
+
+ /* The name begins in this block. Does it also end in this block? */
+
+ if (noffs + inode.namlen > nextblock)
+ {
+ /* No.. Assume that this is not an inode. */
+
+ return ERROR;
+ }
+
+ /* Calculate the CRC */
+
+ state = inode.state;
+ inode.state = CONFIG_NXFFS_ERASEDSTATE;
+ nxffs_wrle32(inode.crc, 0);
+
+ crc = crc32((FAR const uint8_t *)&inode, SIZEOF_NXFFS_INODE_HDR);
+ crc = crc32part(&blkinfo->buffer[noffs - blkinfo->offset], inode.namlen, crc);
+
+ if (crc != ecrc)
+ {
+ fdbg(g_format, blkinfo->block, offset, "INODE", "CRC BAD", datlen);
+ return ERROR;
+ }
+
+ /* If must be a good header */
+
+ if (state == INODE_STATE_FILE)
+ {
+ if (blkinfo->verbose)
+ {
+ fdbg(g_format, blkinfo->block, offset, "INODE", "OK ", datlen);
+ }
+ }
+ else if (state == INODE_STATE_DELETED)
+ {
+ if (blkinfo->verbose)
+ {
+ fdbg(g_format, blkinfo->block, offset, "INODE", "DELETED", datlen);
+ }
+ }
+ else
+ {
+ fdbg(g_format, blkinfo->block, offset, "INODE", "CORRUPT", datlen);
+ }
+
+ /* Return the block-relative offset to the next byte after the inode name */
+
+ return noffs + inode.namlen - offset - blkinfo->offset;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxffs_analyzedata
+ *
+ * Description:
+ * Analyze one candidate inode found in the block.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_FS)
+static inline ssize_t nxffs_analyzedata(FAR struct nxffs_blkinfo_s *blkinfo,
+ int offset)
+{
+ struct nxffs_data_s dathdr;
+ uint32_t ecrc;
+ uint16_t datlen;
+ uint32_t crc;
+
+ /* Copy and unpack the data block header */
+
+ memcpy(&dathdr, &blkinfo->buffer[offset], SIZEOF_NXFFS_DATA_HDR);
+ ecrc = nxffs_rdle32(dathdr.crc);
+ datlen = nxffs_rdle16(dathdr.datlen);
+
+ /* Sanity checks */
+
+ if (offset + SIZEOF_NXFFS_DATA_HDR + datlen > blkinfo->geo.blocksize)
+ {
+ /* Data does not fit in within the block, this can't be a data block */
+
+ return ERROR;
+ }
+
+ /* Calculate the CRC */
+
+ nxffs_wrle32(dathdr.crc, 0);
+
+ crc = crc32((FAR const uint8_t *)&dathdr, SIZEOF_NXFFS_DATA_HDR);
+ crc = crc32part(&blkinfo->buffer[offset + SIZEOF_NXFFS_DATA_HDR], datlen, crc);
+
+ if (crc != ecrc)
+ {
+ fdbg(g_format, blkinfo->block, offset, "DATA ", "CRC BAD", datlen);
+ return ERROR;
+ }
+
+ /* If must be a good header */
+
+ if (blkinfo->verbose)
+ {
+ fdbg(g_format, blkinfo->block, offset, "DATA ", "OK ", datlen);
+ }
+ return SIZEOF_NXFFS_DATA_HDR + datlen;
+}
+#endif
+
+/****************************************************************************
+ * Name: nxffs_analyze
+ *
+ * Description:
+ * Analyze the content of one block.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_FS)
+static inline void nxffs_analyze(FAR struct nxffs_blkinfo_s *blkinfo)
+{
+ FAR struct nxffs_block_s *blkhdr;
+ ssize_t nbytes;
+ int hdrndx;
+ int datndx;
+ int inndx;
+ int i;
+
+ /* Verify that there is a header on the block */
+
+ blkhdr = (FAR struct nxffs_block_s *)blkinfo->buffer;
+ if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) != 0)
+ {
+ fdbg(g_format, blkinfo->block, 0, "BLOCK", "NO FRMT",
+ blkinfo->geo.blocksize);
+ }
+ else if (blkhdr->state == BLOCK_STATE_GOOD)
+ {
+ size_t datsize = blkinfo->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR;
+ size_t nerased = nxffs_erased(blkinfo->buffer + SIZEOF_NXFFS_BLOCK_HDR, datsize);
+ if (nerased == datsize)
+ {
+ if (blkinfo->verbose)
+ {
+ fdbg(g_format, blkinfo->block, 0, "BLOCK", "ERASED ",
+ blkinfo->geo.blocksize);
+ }
+ return;
+ }
+#if 0 /* Too much output, to little information */
+ else
+ {
+ fdbg(g_format, blkinfo->block, 0, "BLOCK", "IN USE ",
+ blkinfo->geo.blocksize);
+ }
+#endif
+ }
+ else if (blkhdr->state == BLOCK_STATE_BAD)
+ {
+ fdbg(g_format, blkinfo->block, 0, "BLOCK", "BAD ",
+ blkinfo->geo.blocksize);
+ }
+ else
+ {
+ fdbg(g_format, blkinfo->block, 0, "BLOCK", "CORRUPT",
+ blkinfo->geo.blocksize);
+ }
+
+ /* Serach for Inode and data block headers. */
+
+ inndx = 0;
+ datndx = 0;
+
+ for (i = SIZEOF_NXFFS_BLOCK_HDR; i < blkinfo->geo.blocksize; i++)
+ {
+ uint8_t ch = blkinfo->buffer[i];
+
+ if (ch == g_inodemagic[inndx])
+ {
+ inndx++;
+ datndx = 0;
+
+ if (inndx == NXFFS_MAGICSIZE)
+ {
+ hdrndx = i - NXFFS_MAGICSIZE + 1;
+ nbytes = nxffs_analyzeinode(blkinfo, hdrndx);
+ if (nbytes > 0)
+ {
+ i = hdrndx + nbytes - 1;
+ }
+ inndx = 0;
+ }
+ }
+ else if (ch == g_datamagic[datndx])
+ {
+ datndx++;
+ inndx = 0;
+
+ if (datndx == NXFFS_MAGICSIZE)
+ {
+ hdrndx = i - NXFFS_MAGICSIZE + 1;
+ nbytes = nxffs_analyzedata(blkinfo, hdrndx);
+ if (nbytes > 0)
+ {
+ i = hdrndx + nbytes - 1;
+ }
+ datndx = 0;
+ }
+ }
+ }
+
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_dump
+ *
+ * Description:
+ * Dump a summary of the contents of an NXFFS file system. CONFIG_DEBUG
+ * and CONFIG_DEBUG_FS must be enabled for this function to do anything.
+ *
+ * Input Parameters:
+ * mtd - The MTD device that provides the interface to NXFFS-formatted
+ * media.
+ * verbose - FALSE: only show errors
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_dump(FAR struct mtd_dev_s *mtd, bool verbose)
+{
+#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_FS)
+ struct nxffs_blkinfo_s blkinfo;
+ int ret;
+
+ /* Get the volume geometry. (casting to uintptr_t first eliminates
+ * complaints on some architectures where the sizeof long is different
+ * from the size of a pointer).
+ */
+
+ memset(&blkinfo, 0, sizeof(struct nxffs_blkinfo_s));
+ ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&blkinfo.geo));
+ if (ret < 0)
+ {
+ fdbg("ERROR: MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", -ret);
+ goto errout;
+ }
+
+ /* Save the verbose output indication */
+
+ blkinfo.verbose = verbose;
+
+ /* Allocate a buffer to hold one block */
+
+ blkinfo.buffer = (FAR uint8_t *)kmalloc(blkinfo.geo.blocksize);
+ if (!blkinfo.buffer)
+ {
+ fdbg("ERROR: Failed to allocate block cache\n");
+ ret = -ENOMEM;
+ goto errout;
+ }
+
+ /* Now read every block on the device */
+
+ fdbg("NXFFS Dump:\n");
+ fdbg(g_hdrformat);
+
+ blkinfo.nblocks = blkinfo.geo.erasesize * blkinfo.geo.neraseblocks / blkinfo.geo.blocksize;
+ for (blkinfo.block = 0, blkinfo.offset = 0;
+ blkinfo.block < blkinfo.nblocks;
+ blkinfo.block++, blkinfo.offset += blkinfo.geo.blocksize)
+ {
+ /* Read the next block */
+
+ ret = MTD_BREAD(mtd, blkinfo.block, 1, blkinfo.buffer);
+ if (ret < 0)
+ {
+ fdbg("ERROR: Failed to read block %d\n", blkinfo.block);
+ goto errout_with_block;
+ }
+
+ /* Analyze the block */
+
+ nxffs_analyze(&blkinfo);
+ }
+ fdbg("%d blocks analyzed\n", blkinfo.nblocks);
+
+errout_with_block:
+ kfree(blkinfo.buffer);
+errout:
+ return ret;
+
+#else
+ return -ENOSYS;
+#endif
+}
diff --git a/nuttx/fs/nxffs/nxffs_initialize.c b/nuttx/fs/nxffs/nxffs_initialize.c
new file mode 100644
index 000000000..6d93a318a
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_initialize.c
@@ -0,0 +1,520 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_initialize.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/ioctl.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/* See fs_mount.c -- this structure is explicitly externed there.
+ * We use the old-fashioned kind of initializers so that this will compile
+ * with any compiler.
+ */
+
+const struct mountpt_operations nxffs_operations =
+{
+ nxffs_open, /* open */
+ nxffs_close, /* close */
+ nxffs_read, /* read */
+ nxffs_write, /* write */
+ NULL, /* seek -- Use f_pos in struct file */
+ nxffs_ioctl, /* ioctl */
+ NULL, /* sync -- No buffered data */
+
+ nxffs_opendir, /* opendir */
+ NULL, /* closedir */
+ nxffs_readdir, /* readdir */
+ nxffs_rewinddir, /* rewinddir */
+
+ nxffs_bind, /* bind */
+ nxffs_unbind, /* unbind */
+ nxffs_statfs, /* statfs */
+
+ nxffs_unlink, /* unlink */
+ NULL, /* mkdir -- no directories */
+ NULL, /* rmdir -- no directories */
+ NULL, /* rename -- cannot rename in place if name is longer */
+ nxffs_stat /* stat */
+};
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/* The magic number that appears that the beginning of each NXFFS (logical)
+ * block
+ */
+
+const uint8_t g_blockmagic[NXFFS_MAGICSIZE] = { 'B', 'l', 'c', 'k' };
+
+/* The magic number that appears that the beginning of each NXFFS inode */
+
+const uint8_t g_inodemagic[NXFFS_MAGICSIZE] = { 'I', 'n', 'o', 'd' };
+
+/* The magic number that appears that the beginning of each NXFFS inode
+ * data block.
+ */
+
+const uint8_t g_datamagic[NXFFS_MAGICSIZE] = { 'D', 'a', 't', 'a' };
+
+/* If CONFIG_NXFSS_PREALLOCATED is defined, then this is the single, pre-
+ * allocated NXFFS volume instance.
+ */
+
+#ifdef CONFIG_NXFSS_PREALLOCATED
+struct nxffs_volume_s g_volume;
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_initialize
+ *
+ * Description:
+ * Initialize to provide NXFFS on an MTD interface
+ *
+ * Input Parameters:
+ * mtd - The MTD device that supports the FLASH interface.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_initialize(FAR struct mtd_dev_s *mtd)
+{
+ FAR struct nxffs_volume_s *volume;
+ struct nxffs_blkstats_s stats;
+ off_t threshold;
+ int ret;
+
+ /* If CONFIG_NXFSS_PREALLOCATED is defined, then this is the single, pre-
+ * allocated NXFFS volume instance.
+ */
+
+#ifdef CONFIG_NXFSS_PREALLOCATED
+
+ volume = &g_volume;
+ memset(volume, 0, sizeof(struct nxffs_volume_s));
+
+#else
+
+ /* Allocate a NXFFS volume structure */
+
+ volume = (FAR struct nxffs_volume_s *)kzalloc(sizeof(struct nxffs_volume_s));
+ if (!volume)
+ {
+ ret = -ENOMEM;
+ goto errout;
+ }
+#endif
+
+ /* Initialize the NXFFS volume structure */
+
+ volume->mtd = mtd;
+ volume->cblock = (off_t)-1;
+ sem_init(&volume->exclsem, 0, 1);
+ sem_init(&volume->wrsem, 0, 1);
+
+ /* Get the volume geometry. (casting to uintptr_t first eliminates
+ * complaints on some architectures where the sizeof long is different
+ * from the size of a pointer).
+ */
+
+ ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&volume->geo));
+ if (ret < 0)
+ {
+ fdbg("MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", -ret);
+ goto errout_with_volume;
+ }
+
+ /* Allocate one I/O block buffer to general files system access */
+
+ volume->cache = (FAR uint8_t *)kmalloc(volume->geo.blocksize);
+ if (!volume->cache)
+ {
+ fdbg("Failed to allocate an erase block buffer\n");
+ ret = -ENOMEM;
+ goto errout_with_volume;
+ }
+
+ /* Pre-allocate one, full, in-memory erase block. This is needed for filesystem
+ * packing (but is useful in other places as well). This buffer is not needed
+ * often, but is best to have pre-allocated and in-place.
+ */
+
+ volume->pack = (FAR uint8_t *)kmalloc(volume->geo.erasesize);
+ if (!volume->pack)
+ {
+ fdbg("Failed to allocate an I/O block buffer\n");
+ ret = -ENOMEM;
+ goto errout_with_cache;
+ }
+
+ /* Get the number of R/W blocks per erase block and the total number o
+ * R/W blocks
+ */
+
+ volume->blkper = volume->geo.erasesize / volume->geo.blocksize;
+ volume->nblocks = volume->geo.neraseblocks * volume->blkper;
+ DEBUGASSERT((off_t)volume->blkper * volume->geo.blocksize == volume->geo.erasesize);
+
+ /* Check if there is a valid NXFFS file system on the flash */
+
+ ret = nxffs_blockstats(volume, &stats);
+ if (ret < 0)
+ {
+ fdbg("Failed to collect block statistics: %d\n", -ret);
+ goto errout_with_buffer;
+ }
+
+ /* If the proportion of good blocks is low or the proportion of unformatted
+ * blocks is high, then reformat the FLASH.
+ */
+
+ threshold = stats.nblocks / 5;
+ if (stats.ngood < threshold || stats.nunformat > threshold)
+ {
+ /* Reformat the volume */
+
+ ret = nxffs_reformat(volume);
+ if (ret < 0)
+ {
+ fdbg("Failed to reformat the volume: %d\n", -ret);
+ goto errout_with_buffer;
+ }
+
+ /* Get statistics on the re-formatted volume */
+
+#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_FS)
+ ret = nxffs_blockstats(volume, &stats);
+ if (ret < 0)
+ {
+ fdbg("Failed to collect block statistics: %d\n", -ret);
+ goto errout_with_buffer;
+ }
+#endif
+ }
+
+ /* Get the file system limits */
+
+ ret = nxffs_limits(volume);
+ if (ret == OK)
+ {
+ return OK;
+ }
+ fdbg("Failed to calculate file system limits: %d\n", -ret);
+
+errout_with_buffer:
+ kfree(volume->pack);
+errout_with_cache:
+ kfree(volume->cache);
+errout_with_volume:
+#ifndef CONFIG_NXFSS_PREALLOCATED
+ kfree(volume);
+#endif
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_limits
+ *
+ * Description:
+ * Recalculate file system limits: (1) the FLASH offset to the first,
+ * valid inode, and (2) the FLASH offset to the first, unused byte after
+ * the last inode (invalid or not).
+ *
+ * The first, lower limit must be recalculated: (1) initially, (2)
+ * whenever the first inode is deleted, or (3) whenever inode is moved
+ * as part of the file system packing operation.
+ *
+ * The second, upper limit must be (1) incremented whenever new file
+ * data is written, or (2) recalculated as part of the file system packing
+ * operation.
+ *
+ * Input Parameters:
+ * volume - Identifies the NXFFS volume
+ *
+ * Returned Value:
+ * Zero on success. Otherwise, a negated error is returned indicating the
+ * nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_limits(FAR struct nxffs_volume_s *volume)
+{
+ FAR struct nxffs_entry_s entry;
+ off_t block;
+ off_t offset;
+ bool noinodes = false;
+ int nerased;
+ int ret;
+
+ /* Get the offset to the first valid block on the FLASH */
+
+ block = 0;
+ ret = nxffs_validblock(volume, &block);
+ if (ret < 0)
+ {
+ fdbg("Failed to find a valid block: %d\n", -ret);
+ return ret;
+ }
+
+ /* Then find the first valid inode in or beyond the first valid block */
+
+ offset = block * volume->geo.blocksize;
+ ret = nxffs_nextentry(volume, offset, &entry);
+ if (ret < 0)
+ {
+ /* The value -ENOENT is special. This simply means that the FLASH
+ * was searched to the end and no valid inode was found... the file
+ * system is empty (or, in more perverse cases, all inodes are
+ * deleted or corrupted).
+ */
+
+ if (ret != -ENOENT)
+ {
+ fdbg("nxffs_nextentry failed: %d\n", -ret);
+ return ret;
+ }
+
+ /* Set a flag the just indicates that no inodes were found. Later,
+ * we will set the location of the first inode to be the same as
+ * the location of the free FLASH region.
+ */
+
+ fvdbg("No inodes found\n");
+ noinodes = true;
+ }
+ else
+ {
+ /* Save the offset to the first inode */
+
+ volume->inoffset = entry.hoffset;
+ fvdbg("First inode at offset %d\n", volume->inoffset);
+
+ /* Discard this entry and set the next offset. */
+
+ offset = nxffs_inodeend(volume, &entry);
+ nxffs_freeentry(&entry);
+ }
+
+ /* Now, search for the last valid entry */
+
+ if (!noinodes)
+ {
+ while ((ret = nxffs_nextentry(volume, offset, &entry)) == OK)
+ {
+ /* Discard the entry and guess the next offset. */
+
+ offset = nxffs_inodeend(volume, &entry);
+ nxffs_freeentry(&entry);
+ }
+ fvdbg("Last inode before offset %d\n", offset);
+ }
+
+ /* No inodes were found after this offset. Now search for a block of
+ * erased flash.
+ */
+
+ nxffs_ioseek(volume, offset);
+ nerased = 0;
+ for (;;)
+ {
+ int ch = nxffs_getc(volume, 1);
+ if (ch < 0)
+ {
+ /* Failed to read the next byte... this could mean that the FLASH
+ * is full?
+ */
+
+ if (volume->ioblock + 1 >= volume->nblocks &&
+ volume->iooffset + 1 >= volume->geo.blocksize)
+ {
+ /* Yes.. the FLASH is full. Force the offsets to the end of FLASH */
+
+ volume->froffset = volume->nblocks * volume->geo.blocksize;
+ fvdbg("Assume no free FLASH, froffset: %d\n", volume->froffset);
+ if (noinodes)
+ {
+ volume->inoffset = volume->froffset;
+ fvdbg("No inodes, inoffset: %d\n", volume->inoffset);
+ }
+ return OK;
+ }
+
+ // No? Then it is some other failure that we do not know how to handle
+
+ fdbg("nxffs_getc failed: %d\n", -ch);
+ return ch;
+ }
+
+ /* Check for another erased byte */
+
+ else if (ch == CONFIG_NXFFS_ERASEDSTATE)
+ {
+ /* If we have encountered NXFFS_NERASED number of consecutive
+ * erased bytes, then presume we have reached the end of valid
+ * data.
+ */
+
+ if (++nerased >= NXFFS_NERASED)
+ {
+ /* Okay.. we have a long stretch of erased FLASH in a valid
+ * FLASH block. Let's say that this is the beginning of
+ * the free FLASH region.
+ */
+
+ volume->froffset = offset;
+ fvdbg("Free FLASH region begins at offset: %d\n", volume->froffset);
+ if (noinodes)
+ {
+ volume->inoffset = offset;
+ fvdbg("First inode at offset %d\n", volume->inoffset);
+ }
+ return OK;
+ }
+ }
+ else
+ {
+ offset += nerased + 1;
+ nerased = 0;
+ }
+ }
+
+ /* Won't get here */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxffs_bind
+ *
+ * Description:
+ * This function implements a portion of the mount operation. Normmally,
+ * the bind() method allocates and initializes the mountpoint private data
+ * then binds the blockdriver inode to the filesystem private data. The
+ * final binding of the private data (containing the blockdriver) to the
+ * mountpoint is performed by mount().
+ *
+ * For the NXFFS, this sequence is quite different for the following
+ * reasons:
+ *
+ * 1. A block driver is not used. Instead, an MTD instance was provided
+ * to nxfs_initialize prior to mounting. So, in this sense, the NXFFS
+ * file system is already bound.
+ *
+ * 2. Since the volume was already bound to the MTD driver, all allocations
+ * and initializations have already been performed. Essentially, all
+ * mount operations have been bypassed and now we just need to provide
+ * the pre-allocated volume instance.
+ *
+ * 3. The tricky thing is that there is no mechanism to associate multiple
+ * NXFFS volumes to the multiple volumes bound to different MTD drivers.
+ * Hence, the limitation of a single NXFFS volume.
+ *
+ ****************************************************************************/
+
+int nxffs_bind(FAR struct inode *blkdriver, FAR const void *data,
+ FAR void **handle)
+{
+#ifndef CONFIG_NXFSS_PREALLOCATED
+# error "No design to support dynamic allocation of volumes"
+#else
+
+ /* If CONFIG_NXFSS_PREALLOCATED is defined, then this is the single, pre-
+ * allocated NXFFS volume instance.
+ */
+
+ DEBUGASSERT(g_volume.cache);
+ *handle = &g_volume;
+#endif
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxffs_unbind
+ *
+ * Description: This implements the filesystem portion of the umount
+ * operation.
+ *
+ ****************************************************************************/
+
+int nxffs_unbind(FAR void *handle, FAR struct inode **blkdriver)
+{
+#ifndef CONFIG_NXFSS_PREALLOCATED
+# error "No design to support dynamic allocation of volumes"
+#else
+ return g_volume.ofiles ? -EBUSY : OK;
+#endif
+}
diff --git a/nuttx/fs/nxffs/nxffs_inode.c b/nuttx/fs/nxffs/nxffs_inode.c
new file mode 100644
index 000000000..505a6c686
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_inode.c
@@ -0,0 +1,502 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_inode.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <crc32.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/mtd.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_rdentry
+ *
+ * Description:
+ * Read the inode entry at this offset. Called only from nxffs_nextentry().
+ *
+ * Input Parameters:
+ * volume - Describes the current volume.
+ * offset - The byte offset from the beginning of FLASH where the inode
+ * header is expected.
+ * entry - A memory location to return the expanded inode header
+ * information.
+ *
+ * Returned Value:
+ * Zero on success. Otherwise, a negated errno value is returned
+ * indicating the nature of the failure.
+ *
+ * On return, the
+ *
+ ****************************************************************************/
+
+static int nxffs_rdentry(FAR struct nxffs_volume_s *volume, off_t offset,
+ FAR struct nxffs_entry_s *entry)
+{
+ struct nxffs_inode_s inode;
+ uint32_t ecrc;
+ uint32_t crc;
+ uint8_t state;
+ int namlen;
+ int ret;
+
+ DEBUGASSERT(volume && entry);
+ memset(entry, 0, sizeof(struct nxffs_entry_s));
+
+ /* Read the header at the FLASH offset */
+
+ nxffs_ioseek(volume, offset);
+ memcpy(&inode, &volume->cache[volume->iooffset], SIZEOF_NXFFS_INODE_HDR);
+
+ /* Check if the file state is recognized. */
+
+ state = inode.state;
+ if (state != INODE_STATE_FILE && state != INODE_STATE_DELETED)
+ {
+ /* This can't be a valid inode.. don't bother with the rest */
+
+ ret = -ENOENT;
+ goto errout_no_offset;
+ }
+
+ /* Copy the packed header into the user-friendly buffer */
+
+ entry->hoffset = offset;
+ entry->noffset = nxffs_rdle32(inode.noffs);
+ entry->doffset = nxffs_rdle32(inode.doffs);
+ entry->utc = nxffs_rdle32(inode.utc);
+ entry->datlen = nxffs_rdle32(inode.datlen);
+
+ /* Modify the packed header and perform the (partial) CRC calculation */
+
+ ecrc = nxffs_rdle32(inode.crc);
+ inode.state = CONFIG_NXFFS_ERASEDSTATE;
+ memset(inode.crc, 0, 4);
+ crc = crc32((FAR const uint8_t *)&inode, SIZEOF_NXFFS_INODE_HDR);
+
+ /* Allocate memory to hold the variable-length file name */
+
+ namlen = inode.namlen;
+ entry->name = (FAR char *)kmalloc(namlen + 1);
+ if (!entry->name)
+ {
+ fdbg("Failed to allocate name, namlen: %d\n", namlen);
+ ret = -ENOMEM;
+ goto errout_no_offset;
+ }
+
+ /* Seek to the expected location of the name in FLASH */
+
+ nxffs_ioseek(volume, entry->noffset);
+
+ /* Make sure that the block is in memory (the name may not be in the
+ * same block as the inode header.
+ */
+
+ ret = nxffs_rdcache(volume, volume->ioblock);
+ if (ret < 0)
+ {
+ fdbg("nxffsx_rdcache failed: %d\n", -ret);
+ goto errout_with_name;
+ }
+
+ /* Read the file name from the expected offset in FLASH */
+
+ memcpy(entry->name, &volume->cache[volume->iooffset], namlen);
+ entry->name[namlen] = '\0';
+
+ /* Finish the CRC calculation and verify the entry */
+
+ crc = crc32part((FAR const uint8_t *)entry->name, namlen, crc);
+ if (crc != ecrc)
+ {
+ fdbg("CRC entry: %08x CRC calculated: %08x\n", ecrc, crc);
+ ret = -EIO;
+ goto errout_with_name;
+ }
+
+ /* We have a good inode header.. but it still could a deleted file.
+ * Check the file state.
+ */
+
+ if (state != INODE_STATE_FILE)
+ {
+ /* It is a deleted file. But still, the data offset and the
+ * start size are good so we can use this information to advance
+ * further in FLASH memory and reduce the search time.
+ */
+
+ offset = nxffs_inodeend(volume, entry);
+ nxffs_freeentry(entry);
+ ret = -ENOENT;
+ goto errout;
+ }
+
+ /* Everything is good.. leave the offset pointing to the valid inode
+ * header.
+ */
+
+ return OK;
+
+ /* On errors where we are suspicious of the validity of the inode header,
+ * we need to increment the file position to just after the "good" magic
+ * word.
+ */
+
+errout_with_name:
+ nxffs_freeentry(entry);
+errout_no_offset:
+ offset += NXFFS_MAGICSIZE;
+errout:
+ nxffs_ioseek(volume, offset);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_freeentry
+ *
+ * Description:
+ * The inode values returned by nxffs_nextentry() include allocated memory
+ * (specifically, the file name string). This function should be called
+ * to dispose of that memory when the inode entry is no longer needed.
+ *
+ * Note that the nxffs_entry_s containing structure is not freed. The
+ * caller may call kfree upon return of this function if necessary to
+ * free the entry container.
+ *
+ * Input parameters:
+ * entry - The entry to be freed.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void nxffs_freeentry(FAR struct nxffs_entry_s *entry)
+{
+ if (entry->name)
+ {
+ kfree(entry->name);
+ entry->name = NULL;
+ }
+}
+
+/****************************************************************************
+ * Name: nxffs_nextentry
+ *
+ * Description:
+ * Search for the next valid inode starting at the provided FLASH offset.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume.
+ * offset - The FLASH memory offset to begin searching.
+ * entry - A pointer to memory provided by the caller in which to return
+ * the inode description.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno is returned
+ * that indicates the nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_nextentry(FAR struct nxffs_volume_s *volume, off_t offset,
+ FAR struct nxffs_entry_s *entry)
+{
+ int nmagic;
+ int ch;
+ int nerased;
+ int ret;
+
+ /* Seek to the first FLASH offset provided by the caller. */
+
+ nxffs_ioseek(volume, offset);
+
+ /* Then begin searching */
+
+ nerased = 0;
+ nmagic = 0;
+ for (;;)
+ {
+ /* Read the next character */
+
+ ch = nxffs_getc(volume, SIZEOF_NXFFS_INODE_HDR - nmagic);
+ if (ch < 0)
+ {
+ fvdbg("nxffs_getc failed: %d\n", -ch);
+ return ch;
+ }
+
+ /* Check for another erased byte */
+
+ else if (ch == CONFIG_NXFFS_ERASEDSTATE)
+ {
+ /* If we have encountered NXFFS_NERASED number of consecutive
+ * erased bytes, then presume we have reached the end of valid
+ * data.
+ */
+
+ if (++nerased >= NXFFS_NERASED)
+ {
+ fvdbg("No entry found\n");
+ return -ENOENT;
+ }
+ }
+ else
+ {
+ nerased = 0;
+
+ /* Check for the magic sequence indicating the start of an NXFFS
+ * inode. There is the possibility of this magic sequnce occurring
+ * in FLASH data. However, the header CRC should distinguish
+ * between real NXFFS inode headers and such false alarms.
+ */
+
+ if (ch != g_inodemagic[nmagic])
+ {
+ /* Ooops... this is the not the right character for the magic
+ * Sequence. Check if we need to restart or to cancel the sequence:
+ */
+
+ if (ch == g_inodemagic[0])
+ {
+ nmagic = 1;
+ }
+ else
+ {
+ nmagic = 0;
+ }
+ }
+ else if (nmagic < NXFFS_MAGICSIZE - 1)
+ {
+ /* We have one more character in the magic sequence */
+
+ nmagic++;
+ }
+
+ /* We have found the magic sequence in the FLASH data that may
+ * indicate the beginning of an NXFFS inode.
+ */
+
+ else
+ {
+ /* The the FLASH offset where we found the matching magic number */
+
+ offset = nxffs_iotell(volume) - NXFFS_MAGICSIZE;
+
+ /* Try to extract the inode header from that position */
+
+ ret = nxffs_rdentry(volume, offset, entry);
+ if (ret == OK)
+ {
+ fvdbg("Found a valid fileheader, offset: %d\n", offset);
+ return OK;
+ }
+
+ /* False alarm.. keep looking */
+
+ nmagic = 0;
+ }
+ }
+ }
+
+ /* We won't get here, but to keep some compilers happy: */
+
+ return -ENOENT;
+}
+
+/****************************************************************************
+ * Name: nxffs_findinode
+ *
+ * Description:
+ * Search for an inode with the provided name starting with the first
+ * valid inode and proceeding to the end FLASH or until the matching
+ * inode is found.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * name - The name of the inode to find
+ * entry - The location to return information about the inode.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno is returned
+ * that indicates the nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_findinode(FAR struct nxffs_volume_s *volume, FAR const char *name,
+ FAR struct nxffs_entry_s *entry)
+{
+ off_t offset;
+ int ret;
+
+ /* Start with the first valid inode that was discovered when the volume
+ * was created (or modified after the last file system re-packing).
+ */
+
+ offset = volume->inoffset;
+
+ /* Loop, checking each NXFFS inode until either: (1) we find the NXFFS inode
+ * with the matching name, or (2) we reach the end of data written on the
+ * media.
+ */
+
+ for (;;)
+ {
+ /* Get the next, valid NXFFS inode entry */
+
+ ret = nxffs_nextentry(volume, offset, entry);
+ if (ret < 0)
+ {
+ fvdbg("No inode found: %d\n", -ret);
+ return ret;
+ }
+
+ /* Is this the NXFFS inode we are looking for? */
+
+ else if (strcmp(name, entry->name) == 0)
+ {
+ /* Yes, return success with the entry data in 'entry' */
+
+ return OK;
+ }
+
+ /* Discard this entry and try the next one. Here we set the
+ * next offset using the raw data length as the offset
+ * increment. This is, of course, not accurate because it
+ * does not account for the data headers that enclose the
+ * data. But it is guaranteed to be less than or equal to
+ * the correct offset and, hence, better then searching
+ * byte-for-byte.
+ */
+
+ offset = nxffs_inodeend(volume, entry);
+ nxffs_freeentry(entry);
+ }
+
+ /* We won't get here, but for some compilers: */
+
+ return -ENOENT;
+}
+
+/****************************************************************************
+ * Name: nxffs_inodeend
+ *
+ * Description:
+ * Return an *approximiate* FLASH offset to end of the inode data. The
+ * returned value is guaranteed to be be less then or equal to the offset
+ * of the thing-of-interest in FLASH. Parsing for interesting things
+ * can begin at that point.
+ *
+ * Assumption: The inode header has been verified by the caller and is
+ * known to contain valid data.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * entry - Describes the inode.
+ *
+ * Returned Value:
+ * A FLASH offset to the (approximate) end of the inode data. No errors
+ * are detected.
+ *
+ ****************************************************************************/
+
+off_t nxffs_inodeend(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_entry_s *entry)
+{
+ /* A zero length file will have no data blocks */
+
+ if (entry->doffset)
+ {
+ /* This is the maximum size of one data block. It is the physcal size
+ * of the block minus the minimum number of headers: block sna data
+ */
+
+ uint16_t maxsize = volume->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR - SIZEOF_NXFFS_DATA_HDR;
+
+ /* This is the minimum number of blocks require to span all of the
+ * inode data. One additional block could possibly be required -- we
+ * could make this accurate by looking at the size of the first, perhaps
+ * partial, data block.
+ */
+
+ off_t minblocks = (entry->datlen + maxsize - 1) / maxsize;
+
+ /* And this is our best, simple guess at the end of the inode data */
+
+ return entry->doffset + entry->datlen + minblocks * SIZEOF_NXFFS_DATA_HDR;
+ }
+
+ /* Otherwise, return an offset that accounts only for the inode header and
+ * the inode name.
+ */
+
+ /* All valid inodes will have a name associated with them */
+
+ DEBUGASSERT(entry->noffset);
+ return entry->noffset + strlen(entry->name);
+}
+
+
diff --git a/nuttx/fs/nxffs/nxffs_ioctl.c b/nuttx/fs/nxffs/nxffs_ioctl.c
new file mode 100644
index 000000000..332878eb0
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_ioctl.c
@@ -0,0 +1,150 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_ioctl.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/mtd.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_ioctl
+ *
+ * Description:
+ * Standard mountpoint ioctl method.
+ *
+ ****************************************************************************/
+
+int nxffs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ FAR struct nxffs_volume_s *volume;
+ int ret;
+
+ fvdbg("cmd: %d arg: %08lx\n", cmd, arg);
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover the file system state from the open file */
+
+ volume = filep->f_inode->i_private;
+ DEBUGASSERT(volume != NULL);
+
+ /* Get exclusive access to the volume. Note that the volume exclsem
+ * protects the open file list.
+ */
+
+ ret = sem_wait(&volume->exclsem);
+ if (ret != OK)
+ {
+ ret = -errno;
+ fdbg("sem_wait failed: %d\n", ret);
+ goto errout;
+ }
+
+ /* Only a reformat and optimize commands are supported */
+
+ if (cmd == FIOC_REFORMAT)
+ {
+ fvdbg("Reformat command\n");
+
+ /* We cannot reformat the volume if there are any open inodes */
+
+ if (volume->ofiles)
+ {
+ fdbg("Open files\n");
+ ret = -EBUSY;
+ goto errout_with_semaphore;
+ }
+
+ /* Re-format the volume -- all is lost */
+
+ ret = nxffs_reformat(volume);
+ }
+
+ else if (cmd == FIOC_OPTIMIZE)
+ {
+ fvdbg("Optimize command\n");
+
+ /* Pack the volume */
+
+ ret = nxffs_pack(volume);
+ }
+ else
+ {
+ /* No other commands supported */
+
+ ret = -ENOTTY;
+ }
+
+errout_with_semaphore:
+ sem_post(&volume->exclsem);
+errout:
+ return ret;
+}
diff --git a/nuttx/fs/nxffs/nxffs_open.c b/nuttx/fs/nxffs/nxffs_open.c
new file mode 100644
index 000000000..eb7817c57
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_open.c
@@ -0,0 +1,1263 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_open.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <crc32.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/mtd.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Since we are limited to a single file opened for writing, it makes sense
+ * to pre-allocate the write state structure.
+ */
+
+#ifdef CONFIG_NXFSS_PREALLOCATED
+static struct nxffs_wrfile_s g_wrfile;
+#endif
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_hdrpos
+ *
+ * Description:
+ * Find a valid location for the inode header. A valid location will have
+ * these properties:
+ *
+ * 1. It will lie in the free flash region.
+ * 2. It will have enough contiguous memory to hold the entire header
+ * (excluding the file name which may lie in the next block).
+ * 3. The memory at this location will be fully erased.
+ *
+ * This function will only perform the checks of 1) and 2).
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * wrfile - Contains the current guess for the header position. On
+ * successful return, this field will hold the selected header
+ * position.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure. Of special interest
+ * the return error of -ENOSPC which means that the FLASH volume is
+ * full and should be repacked.
+ *
+ * On successful return the following are also valid:
+ *
+ * wrfile->ofile.entry.hoffset - FLASH offset to candidate header position
+ * volume->ioblock - Read/write block number of the block containing the
+ * header position
+ * volume->iooffset - The offset in the block to the candidate header
+ * position.
+ * volume->froffset - Updated offset to the first free FLASH block.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_hdrpos(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_wrfile_s *wrfile)
+{
+ int ret;
+
+ /* Reserve memory for the object */
+
+ ret = nxffs_wrreserve(volume, SIZEOF_NXFFS_INODE_HDR);
+ if (ret == OK)
+ {
+ /* Save the offset to the FLASH region reserved for the inode header */
+
+ wrfile->ofile.entry.hoffset = nxffs_iotell(volume);
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_nampos
+ *
+ * Description:
+ * Find a valid location for the inode name. A valid location will have
+ * these properties:
+ *
+ * 1. It will lie in the free flash region.
+ * 2. It will have enough contiguous memory to hold the entire name
+ * 3. The memory at this location will be fully erased.
+ *
+ * This function will only perform the checks of 1) and 2).
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * wrfile - Contains the current guess for the name position. On
+ * successful return, this field will hold the selected name
+ * position.
+ * namlen - The length of the name.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure. Of special interest
+ * the return error of -ENOSPC which means that the FLASH volume is
+ * full and should be repacked.
+ *
+ * On successful return the following are also valid:
+ *
+ * wrfile->ofile.entry.noffset - FLASH offset to candidate name position
+ * volume->ioblock - Read/write block number of the block containing the
+ * name position
+ * volume->iooffset - The offset in the block to the candidate name
+ * position.
+ * volume->froffset - Updated offset to the first free FLASH block.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_nampos(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_wrfile_s *wrfile,
+ int namlen)
+{
+ int ret;
+
+ /* Reserve memory for the object */
+
+ ret = nxffs_wrreserve(volume, namlen);
+ if (ret == OK)
+ {
+ /* Save the offset to the FLASH region reserved for the inode name */
+
+ wrfile->ofile.entry.noffset = nxffs_iotell(volume);
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_hdrerased
+ *
+ * Description:
+ * Find a valid location for the inode header. A valid location will have
+ * these properties:
+ *
+ * 1. It will lie in the free flash region.
+ * 2. It will have enough contiguous memory to hold the entire header
+ * (excluding the file name which may lie in the next block).
+ * 3. The memory at this location will be fully erased.
+ *
+ * This function will only perform the check 3).
+ *
+ * On entry it assumes:
+ *
+ * volume->ioblock - Read/write block number of the block containing the
+ * header position
+ * volume->iooffset - The offset in the block to the candidate header
+ * position.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * wrfile - Contains the current guess for the header position. On
+ * successful return, this field will hold the selected header
+ * position.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure. Of special interest
+ * the return error of -ENOSPC which means that the FLASH volume is
+ * full and should be repacked.
+ *
+ * On successful return the following are also valid:
+ *
+ * wrfile->ofile.entry.hoffset - FLASH offset to candidate header position
+ * volume->ioblock - Read/write block number of the block containing the
+ * header position
+ * volume->iooffset - The offset in the block to the candidate header
+ * position.
+ * volume->froffset - Updated offset to the first free FLASH block.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_hdrerased(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_wrfile_s *wrfile)
+{
+ int ret;
+
+ /* Find a valid location to save the inode header */
+
+ ret = nxffs_wrverify(volume, SIZEOF_NXFFS_INODE_HDR);
+ if (ret == OK)
+ {
+ /* This is where we will put the header */
+
+ wrfile->ofile.entry.hoffset = nxffs_iotell(volume);
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_namerased
+ *
+ * Description:
+ * Find a valid location for the inode name. A valid location will have
+ * these properties:
+ *
+ * 1. It will lie in the free flash region.
+ * 2. It will have enough contiguous memory to hold the entire name
+ * (excluding the file name which may lie in the next block).
+ * 3. The memory at this location will be fully erased.
+ *
+ * This function will only perform the check 3).
+ *
+ * On entry it assumes:
+ *
+ * volume->ioblock - Read/write block number of the block containing the
+ * name position
+ * volume->iooffset - The offset in the block to the candidate name
+ * position.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * wrfile - Contains the current guess for the name position. On
+ * successful return, this field will hold the selected name
+ * position.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure. Of special interest
+ * the return error of -ENOSPC which means that the FLASH volume is
+ * full and should be repacked.
+ *
+ * On successful return the following are also valid:
+ *
+ * wrfile->ofile.entry.noffset - FLASH offset to candidate name position
+ * volume->ioblock - Read/write block number of the block containing the
+ * name position
+ * volume->iooffset - The offset in the block to the candidate name
+ * position.
+ * volume->froffset - Updated offset to the first free FLASH block.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_namerased(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_wrfile_s *wrfile,
+ int namlen)
+{
+ int ret;
+
+ /* Find a valid location to save the inode name */
+
+ ret = nxffs_wrverify(volume, namlen);
+ if (ret == OK)
+ {
+ /* This is where we will put the name */
+
+ wrfile->ofile.entry.noffset = nxffs_iotell(volume);
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_wrname
+ *
+ * Description:
+ * Write the inode name to cache at the position verified by
+ * nxffs_namerased().
+ *
+ * On entry it assumes:
+ *
+ * entry->noffset - FLASH offset to final name position
+ * volume->ioblock - Read/write block number of the block containing the
+ * name position
+ * volume->iooffset - The offset in the block to the candidate name
+ * position.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * entry - Describes the entry to be written.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_wrname(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_entry_s *entry,
+ int namlen)
+{
+ int ret;
+
+ /* Seek to the inode name position and assure that it is in the volume
+ * cache.
+ */
+
+ nxffs_ioseek(volume, entry->noffset);
+ ret = nxffs_rdcache(volume, volume->ioblock);
+ if (ret < 0)
+ {
+ fdbg("Failed to read inode name block %d: %d\n",
+ volume->ioblock, -ret);
+ return ret;
+ }
+
+ /* Copy the inode name to the volume cache and write the inode name block */
+
+ memcpy(&volume->cache[volume->iooffset], entry->name, namlen);
+ ret = nxffs_wrcache(volume);
+ if (ret < 0)
+ {
+ fdbg("Failed to write inode header block %d: %d\n",
+ volume->ioblock, -ret);
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_wropen
+ *
+ * Description:
+ * Handle opening for writing. Only a single writer is permitted and only
+ * file creation is supported.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_wropen(FAR struct nxffs_volume_s *volume,
+ FAR const char *name, mode_t oflags,
+ FAR struct nxffs_ofile_s **ppofile)
+{
+ FAR struct nxffs_wrfile_s *wrfile;
+ FAR struct nxffs_entry_s entry;
+ bool packed;
+ bool truncate = false;
+ int namlen;
+ int ret;
+
+ /* Limitation: Only a single writer is permitted. Writing may involve
+ * extension of the file system in FLASH. Since files are contiguous
+ * in FLASH, only a single file may be extending the FLASH region.
+ */
+
+ ret = sem_wait(&volume->wrsem);
+ if (ret != OK)
+ {
+ fdbg("sem_wait failed: %d\n", ret);
+ ret = -errno;
+ goto errout;
+ }
+
+ /* Get exclusive access to the volume. Note that the volume exclsem
+ * protects the open file list. Note that exclsem is ALWAYS taken
+ * after wrsem to avoid deadlocks.
+ */
+
+ ret = sem_wait(&volume->exclsem);
+ if (ret != OK)
+ {
+ fdbg("sem_wait failed: %d\n", ret);
+ ret = -errno;
+ goto errout_with_wrsem;
+ }
+
+ /* Check if the file exists */
+
+ ret = nxffs_findinode(volume, name, &entry);
+ if (ret == OK)
+ {
+ FAR struct nxffs_ofile_s *ofile;
+
+ /* It exists. Is the file already open for reading? */
+
+ ofile = nxffs_findofile(volume, name);
+ if (ofile)
+ {
+ /* The file is already open.
+ * Limitation: Files cannot be open both for reading and writing.
+ */
+
+ fdbg("File is open for reading\n");
+ ret = -ENOSYS;
+ goto errout_with_exclsem;
+ }
+
+ /* It would be an error if we are asked to create the file
+ * exclusively.
+ */
+
+ else if ((oflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
+ {
+ fdbg("File exists, can't create O_EXCL\n");
+ ret = -EEXIST;
+ goto errout_with_exclsem;
+ }
+
+ /* Were we asked to truncate the file? NOTE: Don't truncate the
+ * file if we were not also asked to created it. See below...
+ * we will not re-create the file unless O_CREAT is also specified.
+ */
+
+ else if ((oflags & (O_CREAT|O_TRUNC)) == (O_CREAT|O_TRUNC))
+ {
+ /* Just schedule the removal the file and fall through to re-create it.
+ * Note that the old file of the same name will not actually be removed
+ * until the new file is successfully written.
+ */
+
+ truncate = true;
+ }
+
+ /* The file exists and we were not asked to truncate (and recreate) it.
+ * Limitation: Cannot write to existing files.
+ */
+
+ else
+ {
+ fdbg("File %s exists and we were not asked to truncate it\n");
+ ret = -ENOSYS;
+ goto errout_with_exclsem;
+ }
+ }
+
+ /* Okay, the file is not open and does not exists (maybe because we deleted
+ * it). Now, make sure that we were asked to created it.
+ */
+
+ if ((oflags & O_CREAT) == 0)
+ {
+ fdbg("Not asked to create the file\n");
+ ret = -ENOENT;
+ goto errout_with_exclsem;
+ }
+
+ /* Make sure that the length of the file name will fit in a uint8_t */
+
+ namlen = strlen(name);
+ if (namlen > CONFIG_NXFFS_MAXNAMLEN)
+ {
+ fdbg("Name is too long: %d\n", namlen);
+ ret = -EINVAL;
+ goto errout_with_exclsem;
+ }
+
+ /* Yes.. Create a new structure that will describe the state of this open
+ * file. NOTE that a special variant of the open file structure is used
+ * that includes additional information to support the write operation.
+ */
+
+#ifdef CONFIG_NXFSS_PREALLOCATED
+ wrfile = &g_wrfile;
+ memset(wrfile, 0, sizeof(struct nxffs_wrfile_s));
+#else
+ wrfile = (FAR struct nxffs_wrfile_s *)kzalloc(sizeof(struct nxffs_wrfile_s));
+ if (!wrfile)
+ {
+ ret = -ENOMEM;
+ goto errout_with_exclsem;
+ }
+#endif
+
+ /* Initialize the open file state structure */
+
+ wrfile->ofile.crefs = 1;
+ wrfile->ofile.oflags = oflags;
+ wrfile->ofile.entry.utc = time(NULL);
+ wrfile->truncate = truncate;
+
+ /* Save a copy of the inode name. */
+
+ wrfile->ofile.entry.name = strdup(name);
+ if (!wrfile->ofile.entry.name)
+ {
+ ret = -ENOMEM;
+ goto errout_with_ofile;
+ }
+
+ /* Allocate FLASH memory for the file and set up for the write.
+ *
+ * Loop until the inode header is configured or until a failure occurs.
+ * Note that nothing is written to FLASH. The inode header is not
+ * written until the file is closed.
+ */
+
+ packed = false;
+ for (;;)
+ {
+ /* File a valid location to position the inode header. Start with the
+ * first byte in the free FLASH region.
+ */
+
+ ret = nxffs_hdrpos(volume, wrfile);
+ if (ret == OK)
+ {
+ /* Find a region of memory in the block that is fully erased */
+
+ ret = nxffs_hdrerased(volume, wrfile);
+ if (ret == OK)
+ {
+ /* Valid memory for the inode header was found. Break out of
+ * the loop.
+ */
+
+ break;
+ }
+ }
+
+ /* If no valid memory is found searching to the end of the volume,
+ * then -ENOSPC will be returned. Other errors are not handled.
+ */
+
+ if (ret != -ENOSPC || packed)
+ {
+ fdbg("Failed to find inode header memory: %d\n", -ret);
+ goto errout_with_name;
+ }
+
+ /* -ENOSPC is a special case.. It means that the volume is full.
+ * Try to pack the volume in order to free up some space.
+ */
+
+ ret = nxffs_pack(volume);
+ if (ret < 0)
+ {
+ fdbg("Failed to pack the volume: %d\n", -ret);
+ goto errout_with_name;
+ }
+
+ /* After packing the volume, froffset will be updated to point to the
+ * new free flash region. Try again.
+ */
+
+ packed = true;
+ }
+
+ /* Loop until the inode name is configured or until a failure occurs.
+ * Note that nothing is written to FLASH.
+ */
+
+ for (;;)
+ {
+ /* File a valid location to position the inode name. Start with the
+ * first byte in the free FLASH region.
+ */
+
+ ret = nxffs_nampos(volume, wrfile, namlen);
+ if (ret == OK)
+ {
+ /* Find a region of memory in the block that is fully erased */
+
+ ret = nxffs_namerased(volume, wrfile, namlen);
+ if (ret == OK)
+ {
+ /* Valid memory for the inode header was found. Write the
+ * inode name to this location.
+ */
+
+ ret = nxffs_wrname(volume, &wrfile->ofile.entry, namlen);
+ if (ret < 0)
+ {
+ fdbg("Failed to write the inode name: %d\n", -ret);
+ goto errout_with_name;
+ }
+
+ /* Then just break out of the loop reporting success. Note
+ * that the alllocated inode name string is retained; it
+ * will be needed later to calculate the inode CRC.
+ */
+
+ break;
+ }
+ }
+
+ /* If no valid memory is found searching to the end of the volume,
+ * then -ENOSPC will be returned. Other errors are not handled.
+ */
+
+ if (ret != -ENOSPC || packed)
+ {
+ fdbg("Failed to find inode name memory: %d\n", -ret);
+ goto errout_with_name;
+ }
+
+ /* -ENOSPC is a special case.. It means that the volume is full.
+ * Try to pack the volume in order to free up some space.
+ */
+
+ ret = nxffs_pack(volume);
+ if (ret < 0)
+ {
+ fdbg("Failed to pack the volume: %d\n", -ret);
+ goto errout_with_name;
+ }
+
+ /* After packing the volume, froffset will be updated to point to the
+ * new free flash region. Try again.
+ */
+
+ packed = true;
+ }
+
+ /* Add the open file structure to the head of the list of open files */
+
+ wrfile->ofile.flink = volume->ofiles;
+ volume->ofiles = &wrfile->ofile;
+
+ /* Indicate that the volume is open for writing and return the open file
+ * instance. Releasing exclsem allows other readers while the write is
+ * in progress. But wrsem is still held for this open file, preventing
+ * any further writers until this inode is closed.s
+ */
+
+ *ppofile = &wrfile->ofile;
+ sem_post(&volume->exclsem);
+ return OK;
+
+errout_with_name:
+ kfree(wrfile->ofile.entry.name);
+errout_with_ofile:
+#ifndef CONFIG_NXFSS_PREALLOCATED
+ kfree(wrfile);
+#endif
+
+errout_with_exclsem:
+ sem_post(&volume->exclsem);
+errout_with_wrsem:
+ sem_post(&volume->wrsem);
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_rdopen
+ *
+ * Description:
+ * Open an existing file for reading.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_rdopen(FAR struct nxffs_volume_s *volume,
+ FAR const char *name,
+ FAR struct nxffs_ofile_s **ppofile)
+{
+ FAR struct nxffs_ofile_s *ofile;
+ int ret;
+
+ /* Get exclusive access to the volume. Note that the volume exclsem
+ * protects the open file list.
+ */
+
+ ret = sem_wait(&volume->exclsem);
+ if (ret != OK)
+ {
+ fdbg("sem_wait failed: %d\n", ret);
+ ret = -errno;
+ goto errout;
+ }
+
+ /* Check if the file has already been opened (for reading) */
+
+ ofile = nxffs_findofile(volume, name);
+ if (ofile)
+ {
+ /* The file is already open.
+ * Limitation: Files cannot be open both for reading and writing.
+ */
+
+ if ((ofile->oflags & O_WROK) != 0)
+ {
+ fdbg("File is open for writing\n");
+ ret = -ENOSYS;
+ goto errout_with_exclsem;
+ }
+
+ /* Just increment the reference count on the ofile */
+
+ ofile->crefs++;
+ fdbg("crefs: %d\n", ofile->crefs);
+ }
+
+ /* The file has not yet been opened.
+ * Limitation: The file must exist. We do not support creation of files
+ * read-only.
+ */
+
+ else
+ {
+ /* Not already open.. create a new open structure */
+
+ ofile = (FAR struct nxffs_ofile_s *)kzalloc(sizeof(struct nxffs_ofile_s));
+ if (!ofile)
+ {
+ fdbg("ofile allocation failed\n");
+ ret = -ENOMEM;
+ goto errout_with_exclsem;
+ }
+
+ /* Initialize the open file state structure */
+
+ ofile->crefs = 1;
+ ofile->oflags = O_RDOK;
+
+ /* Find the file on this volume associated with this file name */
+
+ ret = nxffs_findinode(volume, name, &ofile->entry);
+ if (ret != OK)
+ {
+ fvdbg("Inode '%s' not found: %d\n", name, -ret);
+ goto errout_with_ofile;
+ }
+
+ /* Add the open file structure to the head of the list of open files */
+
+ ofile->flink = volume->ofiles;
+ volume->ofiles = ofile;
+ }
+
+ /* Return the open file state structure */
+
+ *ppofile = ofile;
+ sem_post(&volume->exclsem);
+ return OK;
+
+errout_with_ofile:
+ kfree(ofile);
+errout_with_exclsem:
+ sem_post(&volume->exclsem);
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_remofile
+ *
+ * Description:
+ * Remove an entry from the open file list.
+ *
+ ****************************************************************************/
+
+static inline void nxffs_remofile(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_ofile_s *ofile)
+{
+ FAR struct nxffs_ofile_s *prev;
+ FAR struct nxffs_ofile_s *curr;
+
+ /* Find the open file structure to be removed */
+
+ for (prev = NULL, curr = volume->ofiles;
+ curr && curr != ofile;
+ prev = curr, curr = curr->flink);
+
+ /* Was it found? */
+
+ if (curr)
+ {
+ /* Yes.. at the head of the list? */
+
+ if (prev)
+ {
+ prev->flink = ofile->flink;
+ }
+ else
+ {
+ volume->ofiles = ofile->flink;
+ }
+ }
+ else
+ {
+ fdbg("ERROR: Open inode %p not found\n", ofile);
+ }
+}
+
+/****************************************************************************
+ * Name: nxffs_freeofile
+ *
+ * Description:
+ * Free resources held by an open file.
+ *
+ ****************************************************************************/
+
+static inline void nxffs_freeofile(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_ofile_s *ofile)
+{
+ /* Release the open file entry */
+
+ nxffs_freeentry(&ofile->entry);
+
+ /* Then free the open file container (unless this the pre-alloated
+ * write-only open file container)
+ */
+
+#ifdef CONFIG_NXFSS_PREALLOCATED
+ if ((FAR struct nxffs_wrfile_s*)ofile != &g_wrfile)
+#endif
+ {
+ kfree(ofile);
+ }
+}
+
+/****************************************************************************
+ * Name: nxffs_wrclose
+ *
+ * Description:
+ * Perform special operations when a file is closed:
+ * 1. Write the file block header
+ * 2. Remove any file with the same name that was discovered when the
+ * file was open for writing, and finally,
+ * 3. Write the new file inode.
+ *
+ * Input parameters
+ * volume - Describes the NXFFS volume
+ * wrfile - Describes the state of the open file
+ *
+ ****************************************************************************/
+
+static inline int nxffs_wrclose(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_wrfile_s *wrfile)
+{
+ int ret;
+
+ /* Is there an unfinalized write data? */
+
+ if (wrfile->datlen > 0)
+ {
+ /* Yes.. Write the final file block header */
+
+ ret = nxffs_wrblkhdr(volume, wrfile);
+ if (ret < 0)
+ {
+ fdbg("Failed to write the final block of the file: %d\n", -ret);
+ goto errout;
+ }
+ }
+
+ /* Truncation is implemented by writing the new file, then deleting the
+ * older version of the file. Note that we removed the entry from the
+ * open file list earlier in the close sequence; this will prevent the
+ * open file check from failing when we remove the old version of the
+ * file.
+ */
+
+ if (wrfile->truncate && wrfile->ofile.entry.name)
+ {
+ fvdbg("Removing old file: %s\n", wrfile->ofile.entry.name);
+
+ ret = nxffs_rminode(volume, wrfile->ofile.entry.name);
+ if (ret < 0)
+ {
+ fdbg("nxffs_rminode failed: %d\n", -ret);
+ goto errout;
+ }
+ }
+
+ /* Write the inode header to FLASH */
+
+ ret = nxffs_wrinode(volume, &wrfile->ofile.entry);
+
+ /* The volume is now available for other writers */
+
+errout:
+ sem_post(&volume->wrsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_findofile
+ *
+ * Description:
+ * Search the list of already opened files to see if the inode of this
+ * name is one of the opened files.
+ *
+ * Input Parameters:
+ * name - The name of the inode to check.
+ *
+ * Returned Value:
+ * If an inode of this name is found in the list of opened inodes, then
+ * a reference to the open file structure is returned. NULL is returned
+ * otherwise.
+ *
+ ****************************************************************************/
+
+FAR struct nxffs_ofile_s *nxffs_findofile(FAR struct nxffs_volume_s *volume,
+ FAR const char *name)
+{
+ FAR struct nxffs_ofile_s *ofile;
+
+ /* Check every open file. Note that the volume exclsem protects the
+ * list of open files.
+ */
+
+ for (ofile = volume->ofiles; ofile; ofile = ofile->flink)
+ {
+ /* Check for a name match */
+
+ if (strcmp(name, ofile->entry.name) == 0)
+ {
+ return ofile;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ * Name: nxffs_findwriter
+ *
+ * Description:
+ * Search the list of already opened files and return the open file
+ * instance for the write.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume.
+ *
+ * Returned Value:
+ * If there is an active writer of the volume, its open file instance is
+ * returned. NULL is returned otherwise.
+ *
+ ****************************************************************************/
+
+FAR struct nxffs_wrfile_s *nxffs_findwriter(FAR struct nxffs_volume_s *volume)
+{
+ /* We can tell if the write is in-use because it will have an allocated
+ * name attached.
+ */
+
+#ifdef CONFIG_NXFSS_PREALLOCATED
+ return g_wrfile.ofile.entry.name != NULL ? &g_wrfile : NULL;
+#else
+# error "Missing implementation"
+#endif
+}
+
+/****************************************************************************
+ * Name: nxffs_open
+ *
+ * Description:
+ * This is the standard mountpoint open method.
+ *
+ ****************************************************************************/
+
+int nxffs_open(FAR struct file *filep, FAR const char *relpath,
+ int oflags, mode_t mode)
+{
+ FAR struct nxffs_volume_s *volume;
+ FAR struct nxffs_ofile_s *ofile = NULL;
+ int ret;
+
+ fvdbg("Open '%s'\n", relpath);
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv == NULL && filep->f_inode != NULL);
+
+ /* Get the mountpoint private data from the NuttX inode reference in the
+ * file structure
+ */
+
+ volume = (FAR struct nxffs_volume_s*)filep->f_inode->i_private;
+ DEBUGASSERT(volume != NULL);
+
+#ifdef CONFIG_FILE_MODE
+# warning "Missing check for privileges based on inode->i_mode"
+#endif
+
+ /* Limitation: A file must be opened for reading or writing, but not both.
+ * There is no general for extending the size of of a file. Extending the
+ * file size of possible if the file to be extended is the last in the
+ * sequence on FLASH, but since that case is not the general case, no file
+ * extension is supported.
+ */
+
+ switch (oflags & (O_WROK|O_RDOK))
+ {
+ case 0:
+ default:
+ fdbg("One of O_WRONLY/O_RDONLY must be provided\n");
+ return -EINVAL;
+
+ case O_WROK:
+ ret = nxffs_wropen(volume, relpath, oflags, &ofile);
+ break;
+
+ case O_RDOK:
+ ret = nxffs_rdopen(volume, relpath, &ofile);
+ break;
+
+ case O_WROK|O_RDOK:
+ fdbg("O_RDWR is not supported\n");
+ return -ENOSYS;
+ }
+
+ /* Save the reference to the open-specific state in filep->f_priv */
+
+ if (ret == OK)
+ {
+ filep->f_priv = ofile;
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_close
+ *
+ * Description:
+ * This is the standard mountpoint close method.
+ *
+ ****************************************************************************/
+
+int nxffs_close(FAR struct file *filep)
+{
+ FAR struct nxffs_volume_s *volume;
+ FAR struct nxffs_ofile_s *ofile;
+ int ret = -ENOSYS;
+
+ fvdbg("Closing\n");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover the open file state from the struct file instance */
+
+ ofile = (FAR struct nxffs_ofile_s *)filep->f_priv;
+
+ /* Recover the volume state from the open file */
+
+ volume = (FAR struct nxffs_volume_s *)filep->f_inode->i_private;
+ DEBUGASSERT(volume != NULL);
+
+ /* Get exclusive access to the volume. Note that the volume exclsem
+ * protects the open file list.
+ */
+
+ ret = sem_wait(&volume->exclsem);
+ if (ret != OK)
+ {
+ ret = -errno;
+ fdbg("sem_wait failed: %d\n", ret);
+ goto errout;
+ }
+
+ /* Decrement the reference count on the open file */
+
+ ret = OK;
+ if (ofile->crefs == 1)
+ {
+ /* Decrementing the reference count would take it zero.
+ *
+ * Remove the entry from the open file list. We do this early
+ * to avoid some chick-and-egg problems with file truncation.
+ */
+
+ nxffs_remofile(volume, ofile);
+
+ /* Handle special finalization of the write operation. */
+
+ if ((ofile->oflags & O_WROK) != 0)
+ {
+ ret = nxffs_wrclose(volume, (FAR struct nxffs_wrfile_s *)ofile);
+ }
+
+ /* Release all resouces held by the open file */
+
+ nxffs_freeofile(volume, ofile);
+ }
+ else
+ {
+ /* Just decrement the reference count */
+
+ ofile->crefs--;
+ }
+
+ filep->f_priv = NULL;
+
+ sem_post(&volume->exclsem);
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_wrinode
+ *
+ * Description:
+ * Write the inode header (only to FLASH. This is done in two contexts:
+ *
+ * 1. When an inode is closed, or
+ * 2. As part of the file system packing logic when an inode is moved.
+ *
+ * Note that in either case, the inode name has already been written to
+ * FLASH.
+ *
+ * Input parameters
+ * volume - Describes the NXFFS volume
+ * entry - Describes the inode header to write
+ *
+ * Returned Value:
+ * Zero is returned on success; Otherwise, a negated errno value is returned
+ * indicating the nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_wrinode(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_entry_s *entry)
+{
+ FAR struct nxffs_inode_s *inode;
+ uint32_t crc;
+ int namlen;
+ int ret;
+
+ /* Seek to the inode header position and assure that it is in the volume
+ * cache.
+ */
+
+ nxffs_ioseek(volume, entry->hoffset);
+ ret = nxffs_rdcache(volume, volume->ioblock);
+ if (ret < 0)
+ {
+ fdbg("Failed to read inode header block %d: %d\n",
+ volume->ioblock, -ret);
+ goto errout;
+ }
+
+ /* Get the length of the inode name */
+
+ namlen = strlen(entry->name);
+ DEBUGASSERT(namlen < CONFIG_NXFFS_MAXNAMLEN); /* This was verified earlier */
+
+ /* Initialize the inode header */
+
+ inode = (FAR struct nxffs_inode_s *)&volume->cache[volume->iooffset];
+ memcpy(inode->magic, g_inodemagic, NXFFS_MAGICSIZE);
+
+ inode->state = CONFIG_NXFFS_ERASEDSTATE;
+ inode->namlen = namlen;
+
+ nxffs_wrle32(inode->noffs, entry->noffset);
+ nxffs_wrle32(inode->doffs, entry->doffset);
+ nxffs_wrle32(inode->utc, entry->utc);
+ nxffs_wrle32(inode->crc, 0);
+ nxffs_wrle32(inode->datlen, entry->datlen);
+
+ /* Calculate the CRC */
+
+ crc = crc32((FAR const uint8_t *)inode, SIZEOF_NXFFS_INODE_HDR);
+ crc = crc32part((FAR const uint8_t *)entry->name, namlen, crc);
+
+ /* Finish the inode header */
+
+ inode->state = INODE_STATE_FILE;
+ nxffs_wrle32(inode->crc, crc);
+
+ /* Write the block with the inode header */
+
+ ret = nxffs_wrcache(volume);
+ if (ret < 0)
+ {
+ fdbg("Failed to write inode header block %d: %d\n",
+ volume->ioblock, -ret);
+ }
+
+ /* The volume is now available for other writers */
+
+errout:
+ sem_post(&volume->wrsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_updateinode
+ *
+ * Description:
+ * The packing logic has moved an inode. Check if any open files are using
+ * this inode and, if so, move the data in the open file structure as well.
+ *
+ * Input parameters
+ * volume - Describes the NXFFS volume
+ * entry - Describes the new inode entry
+ *
+ * Returned Value:
+ * Zero is returned on success; Otherwise, a negated errno value is returned
+ * indicating the nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_updateinode(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_entry_s *entry)
+{
+ FAR struct nxffs_ofile_s *ofile;
+
+ /* Find the open inode structure matching this name */
+
+ ofile = nxffs_findofile(volume, entry->name);
+ if (ofile)
+ {
+ /* Yes.. the file is open. Update the FLASH offsets to inode headers */
+
+ ofile->entry.hoffset = entry->hoffset;
+ ofile->entry.noffset = entry->noffset;
+ ofile->entry.doffset = entry->doffset;
+ }
+ return OK;
+}
+
diff --git a/nuttx/fs/nxffs/nxffs_pack.c b/nuttx/fs/nxffs/nxffs_pack.c
new file mode 100644
index 000000000..5a82ae4fd
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_pack.c
@@ -0,0 +1,1572 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_pack.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <crc32.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+/* This structure supports access to one inode data stream */
+
+struct nxffs_packstream_s
+{
+ struct nxffs_entry_s entry; /* Describes the inode header */
+ off_t fpos; /* Current file position */
+ off_t blkoffset; /* Offset to the current data block */
+ uint16_t blklen; /* Size of this block */
+ uint16_t blkpos; /* Position in block corresponding to fpos */
+};
+
+/* The structure supports the overall packing operation */
+
+struct nxffs_pack_s
+{
+ /* These describe the source and destination streams */
+
+ struct nxffs_packstream_s src;
+ struct nxffs_packstream_s dest;
+
+ /* These describe the state of the current contents of the (destination)
+ * volume->pack buffer.
+ */
+
+ FAR uint8_t *iobuffer; /* I/O block start position */
+ off_t ioblock; /* I/O block number */
+ off_t block0; /* First I/O block number in the erase block */
+ uint16_t iooffset; /* I/O block offset */
+};
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_getblock
+ *
+ * Description:
+ * Return the I/O block number that includes the provided offset.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * offset - FLASH offset
+ *
+ * Returned Value:
+ * The I/O block number.
+ *
+ ****************************************************************************/
+
+static off_t nxffs_getblock(FAR struct nxffs_volume_s *volume, off_t offset)
+{
+ return offset / volume->geo.blocksize;
+}
+
+/****************************************************************************
+ * Name: nxffs_getoffset
+ *
+ * Description:
+ * Given an I/O block number return the block offset corresponding to the
+ * FLASH offset;
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * offset - FLASH offset
+ *
+ * Returned Value:
+ * The I/O block number.
+ *
+ ****************************************************************************/
+
+static off_t nxffs_getoffset(FAR struct nxffs_volume_s *volume,
+ off_t offset, off_t block)
+{
+ return offset - block * volume->geo.blocksize;
+}
+
+/****************************************************************************
+ * Name: nxffs_packtell
+ *
+ * Description:
+ * Report the current destination position in the pack buffer.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * pack - The volume packing state structure.
+ *
+ * Returned Value:
+ * The offset from the beginning of FLASH to the current seek position.
+ *
+ ****************************************************************************/
+
+static off_t nxffs_packtell(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_pack_s *pack)
+{
+ return pack->ioblock * volume->geo.blocksize + pack->iooffset;
+}
+
+/****************************************************************************
+ * Name: nxffs_packvalid
+ *
+ * Description:
+ * Check if the current destination block is valid.
+ *
+ * Input Parameters:
+ * pack - The volume packing state structure.
+ *
+ * Returned Values:
+ * None
+ *
+ ****************************************************************************/
+
+static inline bool nxffs_packvalid(FAR struct nxffs_pack_s *pack)
+{
+ FAR struct nxffs_block_s *blkhdr;
+
+ blkhdr = (FAR struct nxffs_block_s *)pack->iobuffer;
+ return (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) == 0 &&
+ blkhdr->state == BLOCK_STATE_GOOD);
+}
+
+/****************************************************************************
+ * Name: nxffs_mediacheck
+ *
+ * Description:
+ * Verify that there is at least one valid block and at least one valid
+ * inode header on the media. On successful return, the volume packing
+ * structure is initialized and contains the offset to the first valid
+ * inode header is returned.
+ *
+ * Input Parameters:
+ * volume - The volume to be packed.
+ * pack - The volume packing state structure.
+ *
+ * Returned Values:
+ * The offset to the data area on the first valid block. Zero is return
+ * if there are no valid blocks or if there are no valid inode headers
+ * after the first valid block.
+ *
+ ****************************************************************************/
+
+static inline off_t nxffs_mediacheck(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_pack_s *pack)
+{
+ off_t froffset;
+ int ret;
+
+ /* Initialize the packing structure to all zero */
+
+ memset(pack, 0, sizeof(struct nxffs_pack_s));
+
+ /* Find the FLASH offset to the first valid block */
+
+ volume->ioblock = 0;
+ ret = nxffs_validblock(volume, &volume->ioblock);
+ if (ret < 0)
+ {
+ /* No valid blocks? Return offset zero. */
+
+ return 0;
+ }
+
+ /* The offset to the free location to pack is then just after the block
+ * header in this block.
+ */
+
+ volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR;
+ froffset = nxffs_iotell(volume);
+
+ /* Get the offset to the first valid inode entry after this free offset */
+
+ ret = nxffs_nextentry(volume, froffset, &pack->src.entry);
+ if (ret < 0)
+ {
+ /* No valid entries on the media -- Return offset zero */
+
+ return 0;
+ }
+
+ /* Okay.. the start block and first entry have been found */
+
+ return froffset;
+}
+
+/****************************************************************************
+ * Name: nxffs_startpos
+ *
+ * Description:
+ * Find the position in FLASH memory where we should begin packing. That
+ * position is the place where there is a gap between the last and the next
+ * valid inode. On entry, the volume packing structure should be as it
+ * was initialized by nxffx_mediacheck. on successful return, the volume
+ * packing state structure will be updated to begin the packing operation.
+ *
+ * Input Parameters:
+ * volume - The volume to be packed
+ * pack - The volume packing state structure.
+ * froffset - On input, this is the location where we should be searching
+ * for the location to begin packing. On successful return, froffset
+ * will be set the the offset in FLASH where the first inode should be
+ * copied to. If -ENOSPC is returned -- meaning that the FLASH is full
+ * -- then no packing can be performed. In this case, then the free
+ * flash offset is returned through this location.
+ *
+ * Returned Values:
+ * Zero on success; Otherwise, a negated errno value is returned to
+ * indicate the nature of the failure. If -ENOSPC is returned then the
+ * free FLASH offset is also returned.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_startpos(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_pack_s *pack,
+ off_t *froffset)
+{
+ struct nxffs_blkentry_s blkentry;
+ off_t offset = *froffset;
+ off_t wasted;
+ off_t nbytes;
+ int ret;
+
+ /* Loop until we find a gap of unused FLASH large enough to warrant
+ * compacting.
+ */
+
+ for(;;)
+ {
+ /* Is there wasted space between the offset where the we could have
+ * valid data and the offset to the beginning of the first valid
+ * inode header? NOTE: The threshold check is not accurate, there
+ * may or may not be intervening block headers making the separation
+ * seem larger than it is.
+ */
+
+ DEBUGASSERT(pack->src.entry.hoffset >= offset);
+ wasted = pack->src.entry.hoffset - offset;
+ if (wasted > CONFIG_NXFFS_PACKTHRESHOLD)
+ {
+ /* This is where we must begin packing. Describe the destination
+ * inode header (only non-zero entries need to be initialized).
+ */
+
+ pack->dest.entry.name = pack->src.entry.name;
+ pack->dest.entry.utc = pack->src.entry.utc;
+ pack->dest.entry.datlen = pack->src.entry.datlen;
+
+ /* The destination entry now "owns" the name string */
+
+ pack->src.entry.name = NULL;
+
+ /* Return the FLASH offset to the destination inode header */
+
+ *froffset = offset;
+ return OK;
+ }
+
+ /* Free the allocated memory in the entry */
+
+ nxffs_freeentry(&pack->src.entry);
+
+ /* Update the offset to the first byte at the end of the last data
+ * block.
+ */
+
+ nbytes = 0;
+ offset = pack->src.entry.doffset;
+
+ while (nbytes < pack->src.entry.datlen)
+ {
+ /* Read the next data block header */
+
+ ret = nxffs_nextblock(volume, offset, &blkentry);
+ if (ret < 0)
+ {
+ fdbg("Failed to find next data block: %d\n", -ret);
+ return ret;
+ }
+
+ /* Get the number of blocks and pointer to where the next
+ * data block might lie.
+ */
+
+ nbytes += blkentry.datlen;
+ offset = blkentry.hoffset + SIZEOF_NXFFS_DATA_HDR + blkentry.datlen;
+ }
+
+ /* Make sure there is space at this location for an inode header */
+
+ nxffs_ioseek(volume, offset);
+ if (volume->iooffset + SIZEOF_NXFFS_INODE_HDR > volume->geo.blocksize)
+ {
+ /* No.. not enough space here. Find the next valid block */
+
+ volume->ioblock++;
+ ret = nxffs_validblock(volume, &volume->ioblock);
+ if (ret < 0)
+ {
+ /* No valid blocks? Then there is nothing we can do. Return
+ * the end-of-flash indication.
+ */
+
+ *froffset = volume->froffset;
+ return -ENOSPC;
+ }
+
+ volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR;
+ offset = nxffs_iotell(volume);
+ }
+
+ /* Get the offset to the next valid inode entry */
+
+ ret = nxffs_nextentry(volume, offset, &pack->src.entry);
+ if (ret < 0)
+ {
+ /* No more valid inode entries. Just return an end-of-flash error
+ * indication. However, there could be many deleted inodes; set
+ * volume->froffset to indicate the true free FLASH position.
+ */
+
+ *froffset = offset;
+ return -ENOSPC;
+ }
+ }
+
+ /* We won't get here */
+
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: nxffs_srcsetup
+ *
+ * Description:
+ * Given a valid src inode, configure the src data stream.
+ *
+ * Input Parameters:
+ * volume - The volume to be packed
+ * pack - The volume packing state structure.
+ * offset - FLASH offset to the data block header (will be zero for zero-
+ * files.
+ *
+ * Returned Values:
+ * Zero on success; Otherwise, a negated errno value is returned to
+ * indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static int nxffs_srcsetup(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_pack_s *pack, off_t offset)
+{
+ /* Start with the first data block */
+
+ pack->src.blkoffset = offset;
+ pack->src.blkpos = 0;
+
+ /* Zero-length files have no valid data block offset */
+
+ if (offset > 0)
+ {
+ /* Seek to the data block header, read and verify the block header */
+
+ int ret = nxffs_rdblkhdr(volume, offset, &pack->src.blklen);
+ if (ret < 0)
+ {
+ fdbg("Failed to verify the data block header: %d\n", -ret);
+ }
+ return ret;
+ }
+
+ DEBUGASSERT(pack->src.entry.datlen == 0);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxffs_destsetup
+ *
+ * Description:
+ * Given a valid dest inode, configure the dest data stream.
+ *
+ * Input Parameters:
+ * volume - The volume to be packed
+ * pack - The volume packing state structure.
+ *
+ * Returned Values:
+ * Zero on success; Otherwise, a negated errno value is returned to
+ * indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static int nxffs_destsetup(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_pack_s *pack)
+{
+ size_t mindata;
+ int namlen;
+ int ret;
+
+ /* The destination can be in one of three of states:
+ *
+ * State 1: The inode position was not yet been found. This condition can
+ * only occur on initial entry into nxffs_packblock() when there we no space
+ * for the inode header at the end of the previous block. We must now be
+ * at the beginning of a shiny new I/O block, so we should always have
+ * space for a new inode header right here.
+ */
+
+ if (pack->dest.entry.hoffset == 0)
+ {
+ /* Is there room for an inode structure in this block? */
+
+ if(pack->iooffset + SIZEOF_NXFFS_INODE_HDR > volume->geo.blocksize)
+ {
+ /* No.. that inode name will not fit in this block. Return an
+ * indication that we are at the end of the block and try again
+ * later.
+ */
+
+ return -ENOSPC;
+ }
+
+ /* The inode header will be placed at this position (but not until
+ * we are finished.
+ */
+
+ pack->dest.entry.hoffset = nxffs_packtell(volume, pack);
+
+ /* Make sure that the initialize state of the inode header memory is
+ * erased. This is important because we may not write to inode header
+ * until it has already been written to FLASH.
+ */
+
+ memset(&pack->iobuffer[pack->iooffset], CONFIG_NXFFS_ERASEDSTATE,
+ SIZEOF_NXFFS_INODE_HDR);
+
+ /* Then set the new FLASH offset */
+
+ pack->iooffset += SIZEOF_NXFFS_INODE_HDR;
+ }
+
+ /* State 2: inode position found, inode header not written, inode name
+ * position not determined.
+ */
+
+ if (pack->dest.entry.noffset == 0)
+ {
+ /* Find the offset to the string memory. Will if fit in this block?
+ * Note: iooffset has already been incremented to account for the
+ * size of the inode header.
+ */
+
+ namlen = strlen(pack->dest.entry.name);
+ if (pack->iooffset + namlen > volume->geo.blocksize)
+ {
+ /* No.. that inode name will not fit in this block. Return an
+ * indication that we are at the end of the block and try again
+ * later.
+ */
+
+ return -ENOSPC;
+ }
+
+ /* Yes.. Write the inode name to the volume packing buffer now, but do
+ * not free the name string memory yet; it will be needed later to\
+ * calculate the header CRC.
+ */
+
+ memcpy(&pack->iobuffer[pack->iooffset], pack->dest.entry.name, namlen);
+
+ /* Reserve space for the inode name */
+
+ pack->dest.entry.noffset = nxffs_packtell(volume, pack);
+ pack->iooffset += namlen;
+ }
+
+ /* State 3: Inode header not-written, inode name written. Still need the position
+ * of the first data block.
+ *
+ * Deal with the special case where the source inode is a zero length file
+ * with no data blocks to be transferred.
+ */
+
+ if (pack->src.entry.doffset > 0)
+ {
+ if (pack->dest.entry.doffset == 0)
+ {
+ /* Will the data block header plus a minimal amount of data fit in this
+ * block? (or the whole file if the file is very small).
+ */
+
+ mindata = MIN(NXFFS_MINDATA, pack->dest.entry.datlen);
+ if (pack->iooffset + SIZEOF_NXFFS_DATA_HDR + mindata > volume->geo.blocksize)
+ {
+ /* No.. return an indication that we are at the end of the block
+ * and try again later.
+ */
+
+ ret = -ENOSPC;
+ goto errout;
+ }
+
+ /* Yes.. reserve space for the data block header */
+
+ pack->dest.entry.doffset = nxffs_packtell(volume, pack);
+ pack->iooffset += SIZEOF_NXFFS_DATA_HDR;
+
+ /* Initialize the output data stream to start with the first data block */
+
+ pack->dest.blkoffset = pack->dest.entry.doffset;
+ pack->dest.blklen = 0;
+ pack->dest.blkpos = 0;
+ }
+
+ /* State 4: Starting a new block. Verify that there is space in the current
+ * block for another (minimal sized) block
+ */
+
+ if (pack->dest.blkoffset == 0)
+ {
+ /* Will the data block header plus a minimal amount of data fit in this
+ * block? (or the whole file if the file is very small).
+ */
+
+ mindata = MIN(NXFFS_MINDATA, pack->dest.entry.datlen);
+ if (pack->iooffset + SIZEOF_NXFFS_DATA_HDR + mindata > volume->geo.blocksize)
+ {
+ /* No.. return an indication that we are at the end of the block
+ * and try again later.
+ */
+
+ ret = -ENOSPC;
+ goto errout;
+ }
+
+ /* Yes.. reserve space for the data block header */
+
+ pack->dest.blkoffset = nxffs_packtell(volume, pack);
+ pack->iooffset += SIZEOF_NXFFS_DATA_HDR;
+ pack->dest.blklen = 0;
+ pack->dest.blkpos = 0;
+ }
+ }
+
+ ret = OK;
+
+errout:
+ volume->froffset = nxffs_packtell(volume, pack);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_wrinodehdr
+ *
+ * Description:
+ * Write the destination inode header (only) to FLASH. Note that the inode
+ * name has already been written to FLASH (thus greatly simplifying the
+ * the complexity of this operation).
+ *
+ * Input Parameters:
+ * volume - The volume to be packed
+ * pack - The volume packing state structure.
+ *
+ * Returned Values:
+ * Zero on success; Otherwise, a negated errno value is returned to
+ * indicate the nature of the failure (not used).
+ *
+ ****************************************************************************/
+
+static int nxffs_wrinodehdr(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_pack_s *pack)
+{
+ FAR struct nxffs_inode_s *inode;
+ off_t ioblock;
+ uint16_t iooffset;
+ uint32_t crc;
+ int namlen;
+ int ret;
+
+ /* Get seek positions corresponding to the inode header location */
+
+ ioblock = nxffs_getblock(volume, pack->dest.entry.hoffset);
+ iooffset = nxffs_getoffset(volume, pack->dest.entry.hoffset, ioblock);
+
+ /* The inode header is not written until all of the inode data has been
+ * packed into its new location. As a result, there are two possibilities:
+ *
+ * 1. The inode header lies in the current, unwritten erase block,
+ * 2. The inode header resides in an earlier erase block and has already
+ * been written to FLASH.
+ *
+ * Recall that the inode name has already been written to FLASH. If that
+ * were not the case, then there would be other complex possibilities.
+ *
+ * Case 2: Does the inode header reside in a block before the beginning
+ * of the current erase block?
+ */
+
+ if (ioblock < pack->block0)
+ {
+ /* Case 2: The inode header lies in an earlier erase block that has
+ * already been written to FLASH. In this case, if we are very
+ * careful, we can just use the standard routine to write the inode
+ * header that is called during the normal file close operation:
+ */
+
+ ret = nxffs_wrinode(volume, &pack->dest.entry);
+ }
+ else
+ {
+ /* Cases 1: Both the inode header and name are in the unwritten cache
+ * memory.
+ *
+ * Initialize the inode header.
+ */
+
+ iooffset += (ioblock - pack->block0) * volume->geo.blocksize;
+ inode = (FAR struct nxffs_inode_s *)&volume->pack[iooffset];
+ memcpy(inode->magic, g_inodemagic, NXFFS_MAGICSIZE);
+
+ nxffs_wrle32(inode->noffs, pack->dest.entry.noffset);
+ nxffs_wrle32(inode->doffs, pack->dest.entry.doffset);
+ nxffs_wrle32(inode->utc, pack->dest.entry.utc);
+ nxffs_wrle32(inode->crc, 0);
+ nxffs_wrle32(inode->datlen, pack->dest.entry.datlen);
+
+ /* Get the length of the inode name */
+
+ namlen = strlen(pack->dest.entry.name);
+ DEBUGASSERT(namlen < CONFIG_NXFFS_MAXNAMLEN);
+
+ inode->state = CONFIG_NXFFS_ERASEDSTATE;
+ inode->namlen = namlen;
+
+ /* Calculate the CRC */
+
+ crc = crc32((FAR const uint8_t *)inode, SIZEOF_NXFFS_INODE_HDR);
+ crc = crc32part((FAR const uint8_t *)pack->dest.entry.name, namlen, crc);
+
+ /* Finish the inode header */
+
+ inode->state = INODE_STATE_FILE;
+ nxffs_wrle32(inode->crc, crc);
+
+ /* If any open files reference this inode, then update the open file
+ * state.
+ */
+
+ ret = nxffs_updateinode(volume, &pack->dest.entry);
+ if (ret < 0)
+ {
+ fdbg("Failed to update inode info: %s\n", -ret);
+ }
+ }
+
+ /* Reset the dest inode information */
+
+ nxffs_freeentry(&pack->dest.entry);
+ memset(&pack->dest, 0, sizeof(struct nxffs_packstream_s));
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_wrdatthdr
+ *
+ * Description:
+ * Write the destination data block header to FLASH.
+ *
+ * Input Parameters:
+ * volume - The volume to be packed
+ * pack - The volume packing state structure.
+ *
+ * Returned Values:
+ * Zero on success; Otherwise, a negated errno value is returned to
+ * indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static void nxffs_wrdathdr(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_pack_s *pack)
+{
+ FAR struct nxffs_data_s *dathdr;
+ off_t ioblock;
+ uint16_t iooffset;
+ uint32_t crc;
+
+ if (pack->dest.blklen > 0)
+ {
+ /* Get the offset in the block corresponding to the location of the data
+ * block header. NOTE: This must lie in the same block as we currently have
+ * buffered.
+ */
+
+ ioblock = nxffs_getblock(volume, pack->dest.blkoffset);
+ iooffset = nxffs_getoffset(volume, pack->dest.blkoffset, ioblock);
+ DEBUGASSERT(pack->dest.blkoffset && ioblock == pack->ioblock);
+
+ /* Write the data block header to memory */
+
+ dathdr = (FAR struct nxffs_data_s *)&pack->iobuffer[iooffset];
+ memcpy(dathdr->magic, g_datamagic, NXFFS_MAGICSIZE);
+ nxffs_wrle32(dathdr->crc, 0);
+ nxffs_wrle16(dathdr->datlen, pack->dest.blklen);
+
+ /* Update the entire data block CRC (including the header) */
+
+ crc = crc32(&pack->iobuffer[iooffset], pack->dest.blklen + SIZEOF_NXFFS_DATA_HDR);
+ nxffs_wrle32(dathdr->crc, crc);
+ }
+
+ /* Setup state to allocate the next data block */
+
+ pack->dest.blkoffset = 0;
+ pack->dest.blklen = 0;
+ pack->dest.blkpos = 0;
+}
+
+/****************************************************************************
+ * Name: nxffs_packtransfer
+ *
+ * Description:
+ * Transfer data from the source to the destination buffer.
+ *
+ * Input Parameters:
+ * volume - The volume to be packed
+ * pack - The volume packing state structure.
+ *
+ * Returned Values:
+ * None.
+ *
+ ****************************************************************************/
+
+static void nxffs_packtransfer(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_pack_s *pack)
+{
+ /* Determine how much data is available in the dest pack buffer */
+
+ uint16_t destlen = volume->geo.blocksize - pack->iooffset;
+
+ /* Dermined how much data is available in the src data block */
+
+ uint16_t srclen = pack->src.blklen - pack->src.blkpos;
+
+ /* Transfer the smaller of the two amounts data */
+
+ uint16_t xfrlen = MIN(srclen, destlen);
+ if (xfrlen > 0)
+ {
+ nxffs_ioseek(volume, pack->src.blkoffset + SIZEOF_NXFFS_DATA_HDR + pack->src.blkpos);
+ memcpy(&pack->iobuffer[pack->iooffset], &volume->cache[volume->iooffset], xfrlen);
+
+ /* Increment counts and offset for this data transfer */
+
+ pack->src.fpos += xfrlen; /* Source data offsets */
+ pack->src.blkpos += xfrlen;
+ pack->dest.fpos += xfrlen; /* Destination data offsets */
+ pack->dest.blkpos += xfrlen;
+ pack->dest.blklen += xfrlen; /* Destination data block size */
+ pack->iooffset += xfrlen; /* Destination I/O block offset */
+ volume->iooffset += xfrlen; /* Source I/O block offset */
+ volume->froffset += xfrlen; /* Free FLASH offset */
+ }
+}
+
+/****************************************************************************
+ * Name: nxffs_endsrcblock
+ *
+ * Description:
+ * The end of a source data block has been encountered. Locate the next
+ * source block and setup to continue the transfer.
+ *
+ * Input Parameters:
+ * volume - The volume to be packed
+ * pack - The volume packing state structure.
+ *
+ * Returned Values:
+ * Zero on success; Otherwise, a negated errno value is returned to
+ * indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static int nxffs_endsrcblock(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_pack_s *pack)
+{
+ struct nxffs_blkentry_s blkentry;
+ off_t offset;
+ int ret;
+
+ /* Yes.. find the next data block in the source input stream. */
+
+ offset = pack->src.blkoffset + SIZEOF_NXFFS_DATA_HDR + pack->src.blklen;
+ ret = nxffs_nextblock(volume, offset, &blkentry);
+ if (ret < 0)
+ {
+ fdbg("Failed to find next data block: %d\n", -ret);
+ return ret;
+ }
+
+ /* Set up the source stream */
+
+ pack->src.blkoffset = blkentry.hoffset;
+ pack->src.blklen = blkentry.datlen;
+ pack->src.blkpos = 0;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxffs_packblock
+ *
+ * Description:
+ * Resume packing from the source stream into the newly identified
+ * destination block.
+ *
+ * Input Parameters:
+ * volume - The volume to be packed
+ * pack - The volume packing state structure.
+ *
+ * Returned Values:
+ * Zero on success; Otherwise, a negated errno value is returned to
+ * indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_packblock(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_pack_s *pack)
+{
+ off_t offset;
+ int ret;
+
+ /* Are we currently processing a block from the source stream? */
+
+ if (pack->src.blkoffset == 0)
+ {
+ /* No.. setup the source stream */
+
+ ret = nxffs_srcsetup(volume, pack, pack->src.entry.doffset);
+ if (ret < 0)
+ {
+ fdbg("Failed to configure the src stream: %d\n", -ret);
+ return ret;
+ }
+ }
+
+ /* We enter here on a new block every time, so we always have to setup
+ * the dest data stream. There should never be data block allocated at
+ * this point in time.
+ */
+
+ DEBUGASSERT(pack->dest.blkoffset == 0 && pack->dest.blkpos == 0);
+
+ ret = nxffs_destsetup(volume, pack);
+ if (ret < 0)
+ {
+ /* -ENOSPC is a special return value which simply means that all of
+ * the FLASH has been used up to the end of the current. We need to
+ * return OK in this case and resume at the next block.
+ */
+
+ if (ret == -ENOSPC)
+ {
+ return OK;
+ }
+ else
+ {
+ fdbg("Failed to configure the dest stream: %d\n", -ret);
+ return ret;
+ }
+ }
+
+ /* Loop, transferring data from the source block to the destination pack
+ * buffer until either (1) the source stream is exhausted, (2) the destination
+ * block is full, or (3) an error occurs.
+ */
+
+ for (;;)
+ {
+ /* Transfer data from the source buffer to the destination buffer */
+
+ nxffs_packtransfer(volume, pack);
+
+ /* Now, either the (1) src block has been fully transferred, (2) all
+ * of the source data has been transferred, or (3) the the destination
+ * block is full, .. or all three.
+ *
+ * Check if all of the bytes in the source inode have been transferred.
+ */
+
+ if (pack->src.fpos >= pack->src.entry.datlen)
+ {
+ /* Write the final destination data block header and inode
+ * headers.
+ */
+
+ nxffs_wrdathdr(volume, pack);
+ nxffs_wrinodehdr(volume, pack);
+
+ /* Find the next valid source inode */
+
+ offset = pack->src.blkoffset + pack->src.blklen;
+ memset(&pack->src, 0, sizeof(struct nxffs_packstream_s));
+
+ ret = nxffs_nextentry(volume, offset, &pack->src.entry);
+ if (ret < 0)
+ {
+ /* No more valid inode entries. Just return an end-of-flash error
+ * indication.
+ */
+
+ return -ENOSPC;
+ }
+
+ /* Setup the new source stream */
+
+ ret = nxffs_srcsetup(volume, pack, pack->src.entry.doffset);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Setup the dest stream */
+
+ memset(&pack->dest, 0, sizeof(struct nxffs_packstream_s));
+ pack->dest.entry.name = pack->src.entry.name;
+ pack->dest.entry.utc = pack->src.entry.utc;
+ pack->dest.entry.datlen = pack->src.entry.datlen;
+ pack->src.entry.name = NULL;
+
+ /* Is there sufficient space at the end of the I/O block to hold
+ * the inode header?
+ */
+
+ if (pack->iooffset + SIZEOF_NXFFS_INODE_HDR > volume->geo.blocksize)
+ {
+ /* No, just return success... we will handle this condition when
+ * this function is called on the next I/O block.
+ */
+
+ return OK;
+ }
+
+ /* Configure the destination stream */
+
+ ret = nxffs_destsetup(volume, pack);
+ if (ret < 0)
+ {
+ /* -ENOSPC is a special return value which simply means that all of the
+ * has been used up to the end. We need to return OK in this case and
+ * resume at the next block.
+ */
+
+ if (ret == -ENOSPC)
+ {
+ return OK;
+ }
+ else
+ {
+ fdbg("Failed to configure the dest stream: %d\n", -ret);
+ return ret;
+ }
+ }
+ }
+
+ /* Not at the end of the source data stream. Check if we are at the
+ * end of the current source data block.
+ */
+
+ else if (pack->src.blkpos >= pack->src.blklen)
+ {
+ ret = nxffs_endsrcblock(volume, pack);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+
+ /* Check if the destination block is full */
+
+ if (pack->iooffset >= volume->geo.blocksize)
+ {
+ /* Yes.. Write the destination data block header and return success */
+
+ nxffs_wrdathdr(volume, pack);
+ return OK;
+ }
+ }
+
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: nxffs_setupwriter
+ *
+ * Description:
+ * Writing is performed at the end of the free FLASH region. When we
+ * finish packing the other inodes, we still need to pack the partially
+ * written file at the end of FLASH. This function performs the setup
+ * necessary to perform that packing phase.
+ *
+ * Input Parameters:
+ * volume - The volume to be packed
+ * pack - The volume packing state structure.
+ *
+ * Returned Values:
+ * If there is an active writer of the volume, its open file instance is
+ * returned. NULL is returned otherwise.
+ *
+ ****************************************************************************/
+
+static FAR struct nxffs_wrfile_s *
+nxffs_setupwriter(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_pack_s *pack)
+{
+ FAR struct nxffs_wrfile_s *wrfile;
+
+ /* Is there a writer? */
+
+ wrfile = nxffs_findwriter(volume);
+ if (wrfile)
+ {
+ /* Yes... It is the activity of this write that probably initiated
+ * this packing activity. The writer may have failed in one of several
+ * different stages:
+ *
+ * hoffset == 0: The write failed early before even FLASH for the inode
+ * header was set aside.
+ * noffset == 0: The write failed after the inode header was set aside,
+ * but before the inode name was written.
+ * doffset == 0: The write failed after writing the inode name, bue
+ * before any data blocks were written to FLASH.
+ *
+ * If no FLASH has been set aside for the write, then we don't need to
+ * do anything here.
+ */
+
+ if (wrfile->ofile.entry.hoffset > 0)
+ {
+ /* Initialize for the packing operation. */
+
+ memset(&pack->dest, 0, sizeof(struct nxffs_packstream_s));
+ pack->dest.entry.name = strdup(wrfile->ofile.entry.name);
+ pack->dest.entry.utc = wrfile->ofile.entry.utc;
+ pack->dest.entry.datlen = wrfile->ofile.entry.datlen;
+
+ memset(&pack->src, 0, sizeof(struct nxffs_packstream_s));
+ memcpy(&pack->src.entry, &wrfile->ofile.entry, sizeof(struct nxffs_entry_s));
+ pack->src.entry.name = NULL;
+ return wrfile;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ * Name: nxffs_packwriter
+ *
+ * Description:
+ * There is a write in progress at the time that the volume is packed.
+ * This is the normal case because it is the write failures that trigger
+ * the packing operation to begin with.
+ *
+ * Writing is performed at the end of the free FLASH region and this
+ * implemenation is restricted to a single writer. The new inode is not
+ * written to FLASH until the the writer is closed and so will not be
+ * found by nxffs_packblock().
+ *
+ * Input Parameters:
+ * volume - The volume to be packed
+ * pack - The volume packing state structure.
+ *
+ * Returned Values:
+ * Zero on success; Otherwise, a negated errno value is returned to
+ * indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_packwriter(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_pack_s *pack,
+ FAR struct nxffs_wrfile_s *wrfile)
+{
+ int ret;
+
+ /* Are we currently processing a block from the source stream? */
+
+ if (pack->src.blkoffset == 0)
+ {
+ /* No.. setup the source stream */
+
+ ret = nxffs_srcsetup(volume, pack, pack->src.entry.doffset);
+ if (ret < 0)
+ {
+ fdbg("Failed to configure the src stream: %d\n", -ret);
+ return ret;
+ }
+ }
+
+ /* We enter here on a new block every time, so we always have to setup
+ * the dest data stream. There should never be data block allocated at
+ * this point in time.
+ */
+
+ DEBUGASSERT(pack->dest.blkoffset == 0 && pack->dest.blkpos == 0);
+
+ ret = nxffs_destsetup(volume, pack);
+ if (ret < 0)
+ {
+ /* -ENOSPC is a special return value which simply means that all of the
+ * has been used up to the end. We need to return OK in this case and
+ * resume at the next block.
+ */
+
+ if (ret == -ENOSPC)
+ {
+ return OK;
+ }
+ else
+ {
+ fdbg("Failed to configure the dest stream: %d\n", -ret);
+ return ret;
+ }
+ }
+
+ /* Loop, transferring data from the source block to the destination pack
+ * buffer until either (1) the source stream is exhausted, (2) the destination
+ * block is full, or (3) an error occurs.
+ */
+
+ for (;;)
+ {
+ /* Transfer data from the source buffer to the destination buffer */
+
+ nxffs_packtransfer(volume, pack);
+
+ /* Now, either the (1) src block has been fully transferred, (2) all
+ * of the source data has been transferred, or (3) the the destination
+ * block is full, .. or all three.
+ *
+ * Check if all of the bytes in the source inode have been transferred.
+ */
+
+ if (pack->src.fpos >= pack->src.entry.datlen)
+ {
+ /* Write the final destination data block header and inode
+ * headers.
+ */
+
+ nxffs_wrdathdr(volume, pack);
+
+ /* Set the new offsets in the open file instance. */
+
+ wrfile->ofile.entry.hoffset = pack->dest.entry.hoffset;
+ wrfile->ofile.entry.noffset = pack->dest.entry.noffset;
+ wrfile->ofile.entry.doffset = pack->dest.entry.doffset;
+
+ /* Return an end-of-flash error to indicate that all of the write
+ * data has been transferred.
+ */
+
+ return -ENOSPC;
+ }
+
+ /* Not at the end of the source data stream. Check if we are at the
+ * end of the current source data block.
+ */
+
+ else if (pack->src.blkpos >= pack->src.blklen)
+ {
+ ret = nxffs_endsrcblock(volume, pack);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+
+ /* Check if the destination block is full */
+
+ if (pack->iooffset >= volume->geo.blocksize)
+ {
+ /* Yes.. Write the destination data block header and return success */
+
+ nxffs_wrdathdr(volume, pack);
+ return OK;
+ }
+ }
+
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_pack
+ *
+ * Description:
+ * Pack and re-write the filesystem in order to free up memory at the end
+ * of FLASH.
+ *
+ * Input Parameters:
+ * volume - The volume to be packed.
+ *
+ * Returned Values:
+ * Zero on success; Otherwise, a negated errno value is returned to
+ * indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_pack(FAR struct nxffs_volume_s *volume)
+{
+ struct nxffs_pack_s pack;
+ FAR struct nxffs_wrfile_s *wrfile;
+ off_t iooffset;
+ off_t eblock;
+ off_t block;
+ bool packed;
+ int i;
+ int ret;
+
+ /* Get the offset to the first valid inode entry */
+
+ wrfile = NULL;
+ packed = false;
+
+ iooffset = nxffs_mediacheck(volume, &pack);
+ if (iooffset == 0)
+ {
+ /* Offset zero is only returned if no valid blocks were found on the
+ * FLASH media or if there are no valid inode entries on the FLASH after
+ * the first valid block. There are two possibilities: (1) there
+ * really is nothing on the FLASH, or (2) there is a file being written
+ * to the FLASH now.
+ */
+
+ /* Is there a writer? */
+
+ wrfile = nxffs_setupwriter(volume, &pack);
+ if (wrfile)
+ {
+ /* If there is a write, just set ioffset to the offset of data in
+ * first block. Setting 'packed' to true will supress normal inode
+ * packing operation. Then we can start compacting the FLASH.
+ */
+
+ iooffset = SIZEOF_NXFFS_BLOCK_HDR;
+ packed = true;
+ goto start_pack;
+ }
+ else
+ {
+ /* No, there is no write in progress. We just have an empty flash
+ * full of deleted files. In this case, the media needs to be re-
+ * formatted.
+ */
+
+ ret = nxffs_reformat(volume);
+ if (ret == OK)
+ {
+ /* The free flash offset will be in the first valid block of
+ * the FLASH.
+ */
+
+ block = 0;
+ ret = nxffs_validblock(volume, &block);
+ if (ret == OK)
+ {
+ /* Set to the offset past the block header in the first
+ * valid block
+ */
+
+ volume->froffset =
+ block * volume->geo.blocksize + SIZEOF_NXFFS_BLOCK_HDR;
+ }
+ }
+
+ return ret;
+ }
+ }
+
+ /* There is a valid format and valid inodes on the media.. setup up to
+ * begin the packing operation.
+ */
+
+ ret = nxffs_startpos(volume, &pack, &iooffset);
+ if (ret < 0)
+ {
+ /* This is a normal situation if the volume is full */
+
+ if (ret == -ENOSPC)
+ {
+ /* In the case where the volume is full, nxffs_startpos() will
+ * recalculate the free FLASH offset and store it in iooffset. There
+ * may be deleted files at the end of FLASH. In this case, we don't
+ * have to pack any files, we simply have to erase FLASH at the end.
+ * But don't do this unless there is some particularly big FLASH
+ * savings (otherwise, we risk wearing out these final blocks).
+ */
+
+ if (iooffset + CONFIG_NXFFS_TAILTHRESHOLD < volume->froffset)
+ {
+ /* Setting 'packed' to true will supress normal inode packing
+ * operation.
+ */
+
+ packed = true;
+
+ /* Writing is performed at the end of the free FLASH region.
+ * If we are not packing files, we could still need to pack
+ * the partially written file at the end of FLASH.
+ */
+
+ wrfile = nxffs_setupwriter(volume, &pack);
+ }
+
+ /* Otherwise return OK.. meaning that there is nothing more we can
+ * do to recover FLASH space.
+ */
+
+ else
+ {
+ return OK;
+ }
+ }
+ else
+ {
+ fvdbg("Failed to find a packing position: %d\n", -ret);
+ return ret;
+ }
+ }
+
+ /* Otherwise, begin pack at this src/dest block combination. Initialize
+ * ioblock and iooffset with the position of the first inode header. In
+ * this case, the FLASH offset to the first inode header is return in
+ * iooffset.
+ */
+
+start_pack:
+
+ pack.ioblock = nxffs_getblock(volume, iooffset);
+ pack.iooffset = nxffs_getoffset(volume, iooffset, pack.ioblock);
+ volume->froffset = iooffset;
+
+ /* Then pack all erase blocks starting with the erase block that contains
+ * the ioblock and through the final erase block on the FLASH.
+ */
+
+ for (eblock = pack.ioblock / volume->blkper;
+ eblock < volume->geo.neraseblocks;
+ eblock++)
+ {
+ /* Read the erase block into the pack buffer. We need to do this even
+ * if we are overwriting the entire block so that we skip over
+ * previously marked bad blocks.
+ */
+
+ pack.block0 = eblock * volume->blkper;
+ ret = MTD_BREAD(volume->mtd, pack.block0, volume->blkper, volume->pack);
+ if (ret < 0)
+ {
+ fdbg("Failed to read erase block %d: %d\n", eblock, -ret);
+ goto errout_with_pack;
+ }
+
+ /* Pack each I/O block */
+
+ for (i = 0, block = pack.block0, pack.iobuffer = volume->pack;
+ i < volume->blkper;
+ i++, block++, pack.iobuffer += volume->geo.blocksize)
+ {
+ /* The first time here, the ioblock may point to an offset into
+ * the erase block. We just need to skip over those cases.
+ */
+
+ if (block >= pack.ioblock)
+ {
+ /* Set the I/O position. Note on the first time we get
+ * pack.iooffset will hold the offset in the first I/O block
+ * to the first inode header. After that, it will always
+ * refer to the first byte after the block header.
+ */
+
+ pack.ioblock = block;
+
+ /* If this is not a valid block or if we have already
+ * finished packing the valid inode entries, then just fall
+ * through, reset the FLASH memory to the erase state, and
+ * write the reset values to FLASH. (The first block that
+ * we want to process will always be valid -- we have
+ * already verified that).
+ */
+
+ if (nxffs_packvalid(&pack))
+ {
+ /* Have we finished packing inodes? */
+
+ if (!packed)
+ {
+ DEBUGASSERT(wrfile == NULL);
+
+ /* Pack inode data into this block */
+
+ ret = nxffs_packblock(volume, &pack);
+ if (ret < 0)
+ {
+ /* The error -ENOSPC is a special value that simply
+ * means that there is nothing further to be packed.
+ */
+
+ if (ret == -ENOSPC)
+ {
+ packed = true;
+
+ /* Writing is performed at the end of the free
+ * FLASH region and this implemenation is restricted
+ * to a single writer. The new inode is not
+ * written to FLASH until the the writer is closed
+ * and so will not be found by nxffs_packblock().
+ */
+
+ wrfile = nxffs_setupwriter(volume, &pack);
+ }
+ else
+ {
+ /* Otherwise, something really bad happened */
+
+ fdbg("Failed to pack into block %d: %d\n",
+ block, ret);
+ goto errout_with_pack;
+ }
+ }
+ }
+
+ /* If all of the "normal" inodes have been packed, then check if
+ * we need to pack the current, in-progress write operation.
+ */
+
+ if (wrfile)
+ {
+ DEBUGASSERT(packed == true);
+
+ /* Pack write data into this block */
+
+ ret = nxffs_packwriter(volume, &pack, wrfile);
+ if (ret < 0)
+ {
+ /* The error -ENOSPC is a special value that simply
+ * means that there is nothing further to be packed.
+ */
+
+ if (ret == -ENOSPC)
+ {
+ wrfile = NULL;
+ }
+ else
+ {
+ /* Otherwise, something really bad happened */
+
+ fdbg("Failed to pack into block %d: %d\n",
+ block, ret);
+ goto errout_with_pack;
+ }
+ }
+ }
+ }
+
+ /* Set any unused portion at the end of the block to the
+ * erased state.
+ */
+
+ if (pack.iooffset < volume->geo.blocksize)
+ {
+ memset(&pack.iobuffer[pack.iooffset],
+ CONFIG_NXFFS_ERASEDSTATE,
+ volume->geo.blocksize - pack.iooffset);
+ }
+
+ /* Next time through the loop, pack.iooffset will point to the
+ * first byte after the block header.
+ */
+
+ pack.iooffset = SIZEOF_NXFFS_BLOCK_HDR;
+ }
+ }
+
+ /* We now have an in-memory image of how we want this erase block to
+ * appear. Now it is safe to erase the block.
+ */
+
+ ret = MTD_ERASE(volume->mtd, eblock, 1);
+ if (ret < 0)
+ {
+ fdbg("Failed to erase block %d [%d]: %d\n",
+ eblock, pack.block0, -ret);
+ goto errout_with_pack;
+ }
+
+ /* Write the packed I/O block to FLASH */
+
+ ret = MTD_BWRITE(volume->mtd, pack.block0, volume->blkper, volume->pack);
+ if (ret < 0)
+ {
+ fdbg("Failed to write erase block %d [%]: %d\n",
+ eblock, pack.block0, -ret);
+ goto errout_with_pack;
+ }
+ }
+
+errout_with_pack:
+ nxffs_freeentry(&pack.src.entry);
+ nxffs_freeentry(&pack.dest.entry);
+ return ret;
+}
diff --git a/nuttx/fs/nxffs/nxffs_read.c b/nuttx/fs/nxffs/nxffs_read.c
new file mode 100644
index 000000000..b638dbfd4
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_read.c
@@ -0,0 +1,473 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_read.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <fcntl.h>
+#include <crc32.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/mtd.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_rdseek
+ *
+ * Description:
+ * Seek to the file position before read or write access. Note that the
+ * simplier nxffs_ioseek() cannot be used for this purpose. File offsets
+ * are not easily mapped to FLASH offsets due to intervening block and
+ * data headers.
+ *
+ * Input Parameters:
+ * volume - Describes the current volume
+ * entry - Describes the open inode
+ * fpos - The desired file position
+ * blkentry - Describes the block entry that we are positioned in
+ *
+ ****************************************************************************/
+
+static ssize_t nxffs_rdseek(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_entry_s *entry,
+ off_t fpos,
+ FAR struct nxffs_blkentry_s *blkentry)
+{
+ size_t datstart;
+ size_t datend;
+ off_t offset;
+ int ret;
+
+ /* The initial FLASH offset will be the offset to first data block of
+ * the inode
+ */
+
+ offset = entry->doffset;
+ if (offset == 0)
+ {
+ /* Zero length files will have no data blocks */
+
+ return -ENOSPC;
+ }
+
+ /* Loop until we read the data block containing the desired position */
+
+ datend = 0;
+ do
+ {
+ /* Check if the next data block contains the sought after file position */
+
+ ret = nxffs_nextblock(volume, offset, blkentry);
+ if (ret < 0)
+ {
+ fdbg("nxffs_nextblock failed: %d\n", -ret);
+ return ret;
+ }
+
+ /* Get the range of data offsets for this data block */
+
+ datstart = datend;
+ datend += blkentry->datlen;
+
+ /* Offset to search for the the next data block */
+
+ offset = blkentry->hoffset + SIZEOF_NXFFS_DATA_HDR + blkentry->datlen;
+ }
+ while (datend <= fpos);
+
+ /* Return the offset to the data within the current data block */
+
+ blkentry->foffset = fpos - datstart;
+ nxffs_ioseek(volume, blkentry->hoffset + SIZEOF_NXFFS_DATA_HDR + blkentry->foffset);
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+/****************************************************************************
+ * Name: nxffs_read
+ *
+ * Description:
+ * This is an implementation of the NuttX standard file system read
+ * method.
+ *
+ ****************************************************************************/
+
+ssize_t nxffs_read(FAR struct file *filep, FAR char *buffer, size_t buflen)
+{
+ FAR struct nxffs_volume_s *volume;
+ FAR struct nxffs_ofile_s *ofile;
+ struct nxffs_blkentry_s blkentry;
+ ssize_t total;
+ size_t available;
+ size_t readsize;
+ int ret;
+
+ fvdbg("Read %d bytes from offset %d\n", buflen, filep->f_pos);
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover the open file state from the struct file instance */
+
+ ofile = (FAR struct nxffs_ofile_s *)filep->f_priv;
+
+ /* Recover the volume state from the open file */
+
+ volume = (FAR struct nxffs_volume_s *)filep->f_inode->i_private;
+ DEBUGASSERT(volume != NULL);
+
+ /* Get exclusive access to the volume. Note that the volume exclsem
+ * protects the open file list.
+ */
+
+ ret = sem_wait(&volume->exclsem);
+ if (ret != OK)
+ {
+ ret = -errno;
+ fdbg("sem_wait failed: %d\n", ret);
+ goto errout;
+ }
+
+ /* Check if the file was opened with read access */
+
+ if ((ofile->oflags & O_RDOK) == 0)
+ {
+ fdbg("File not open for read access\n");
+ ret = -EACCES;
+ goto errout_with_semaphore;
+ }
+
+ /* Loop until all bytes have been read */
+
+ for (total = 0; total < buflen; )
+ {
+ /* Don't seek past the end of the file */
+
+ if (filep->f_pos >= ofile->entry.datlen)
+ {
+ /* Return the partial read */
+
+ filep->f_pos = ofile->entry.datlen;
+ break;
+ }
+
+ /* Seek to the current file offset */
+
+ ret = nxffs_rdseek(volume, &ofile->entry, filep->f_pos, &blkentry);
+ if (ret < 0)
+ {
+ fdbg("nxffs_rdseek failed: %d\n", -ret);
+ ret = -EACCES;
+ goto errout_with_semaphore;
+ }
+
+ /* How many bytes are available at this offset */
+
+ available = blkentry.datlen - blkentry.foffset;
+
+ /* Don't read more than we need to */
+
+ readsize = buflen - total;
+ if (readsize > available)
+ {
+ readsize = available;
+ }
+
+ /* Read data from that file offset */
+
+ memcpy(&buffer[total], &volume->cache[volume->iooffset], readsize);
+
+ /* Update the file offset */
+
+ filep->f_pos += readsize;
+ total += readsize;
+ }
+
+ sem_post(&volume->exclsem);
+ return total;
+
+errout_with_semaphore:
+ sem_post(&volume->exclsem);
+errout:
+ return (ssize_t)ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_nextblock
+ *
+ * Description:
+ * Search for the next valid data block starting at the provided
+ * FLASH offset.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume.
+ * datlen - A memory location to return the data block length.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno is returned
+ * that indicates the nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_nextblock(FAR struct nxffs_volume_s *volume, off_t offset,
+ FAR struct nxffs_blkentry_s *blkentry)
+{
+ int nmagic;
+ int ch;
+ int nerased;
+ int ret;
+
+ /* Seek to the first FLASH offset provided by the caller. */
+
+ nxffs_ioseek(volume, offset);
+
+ /* Skip the block header */
+
+ if (volume->iooffset < SIZEOF_NXFFS_BLOCK_HDR)
+ {
+ volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR;
+ }
+
+ /* Then begin searching */
+
+ nerased = 0;
+ nmagic = 0;
+
+ for (;;)
+ {
+ /* Read the next character */
+
+ ch = nxffs_getc(volume, SIZEOF_NXFFS_DATA_HDR - nmagic);
+ if (ch < 0)
+ {
+ fvdbg("nxffs_getc failed: %d\n", -ch);
+ return ch;
+ }
+
+ /* Check for another erased byte */
+
+ else if (ch == CONFIG_NXFFS_ERASEDSTATE)
+ {
+ /* If we have encountered NXFFS_NERASED number of consecutive
+ * erased bytes, then presume we have reached the end of valid
+ * data.
+ */
+
+ if (++nerased >= NXFFS_NERASED)
+ {
+ fvdbg("No entry found\n");
+ return -ENOENT;
+ }
+ }
+ else
+ {
+ nerased = 0;
+
+ /* Check for the magic sequence indicating the start of an NXFFS
+ * data block or start of the next inode. There is the possibility
+ * of this magic sequnce occurring in FLASH data. However, the
+ * data block CRC should distinguish between real NXFFS data blocks
+ * headers and such false alarms.
+ */
+
+ if (ch != g_datamagic[nmagic])
+ {
+ /* Ooops... this is the not the right character for the magic
+ * Sequence. Check if we need to restart or to cancel the sequence:
+ */
+
+ if (ch == g_datamagic[0])
+ {
+ nmagic = 1;
+ }
+ else
+ {
+ nmagic = 0;
+ }
+ }
+ else if (nmagic < NXFFS_MAGICSIZE - 1)
+ {
+ /* We have one more character in the magic sequence */
+
+ nmagic++;
+ }
+
+ /* We have found the magic sequence in the FLASH data that may
+ * indicate the beginning of an NXFFS data block.
+ */
+
+ else
+ {
+ /* The offset to the header must be 4 bytes before the current
+ * FLASH seek location.
+ */
+
+ blkentry->hoffset = nxffs_iotell(volume) - NXFFS_MAGICSIZE;
+
+ /* Read the block header and verify the block at that address */
+
+ ret = nxffs_rdblkhdr(volume, blkentry->hoffset, &blkentry->datlen);
+ if (ret == OK)
+ {
+ fvdbg("Found a valid data block, offset: %d datlen: %d\n",
+ blkentry->hoffset, blkentry->datlen);
+ return OK;
+ }
+
+ /* False alarm.. Restore the volume cache position (that was
+ * destroyed by nxfs_rdblkhdr()) and keep looking.
+ */
+
+ nxffs_ioseek(volume, blkentry->hoffset + NXFFS_MAGICSIZE);
+ nmagic = 0;
+ }
+ }
+ }
+
+ /* We won't get here, but to keep some compilers happy: */
+
+ return -ENOENT;
+}
+
+/****************************************************************************
+ * Name: nxffs_rdblkhdr
+ *
+ * Description:
+ * Read and verify the data block header at the specified offset.
+ *
+ * Input Parameters:
+ * volume - Describes the current volume.
+ * offset - The byte offset from the beginning of FLASH where the data block
+ * header is expected.
+ * datlen - A memory location to return the data block length.
+ *
+ * Returned Value:
+ * Zero on success. Otherwise, a negated errno value is returned
+ * indicating the nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_rdblkhdr(FAR struct nxffs_volume_s *volume, off_t offset,
+ FAR uint16_t *datlen)
+{
+ struct nxffs_data_s blkhdr;
+ uint32_t ecrc;
+ uint32_t crc;
+ uint16_t doffset;
+ uint16_t dlen;
+ int ret;
+
+ /* Make sure the the block containing the data block header is in the cache */
+
+ nxffs_ioseek(volume, offset);
+ ret = nxffs_rdcache(volume, volume->ioblock);
+ if (ret < 0)
+ {
+ fvdbg("Failed to read data into cache: %d\n", ret);
+ return ret;
+ }
+
+ /* Read the header at the FLASH offset */
+
+ doffset = volume->iooffset;
+ memcpy(&blkhdr, &volume->cache[doffset], SIZEOF_NXFFS_DATA_HDR);
+
+ /* Extract the data length */
+
+ dlen = nxffs_rdle16(blkhdr.datlen);
+
+ /* Get the offset to the beginning of the data */
+
+ doffset += SIZEOF_NXFFS_DATA_HDR;
+
+ /* Make sure that all of the data fits within the block */
+
+ if ((uint32_t)doffset + (uint32_t)dlen > (uint32_t)volume->geo.blocksize)
+ {
+ fdbg("Data length=%d is unreasonable at offset=%d\n", dlen, doffset);
+ return -EIO;
+ }
+
+ /* Extract the expected CRC and calculate the CRC of the data block */
+
+ ecrc = nxffs_rdle32(blkhdr.crc);
+
+ nxffs_wrle32(blkhdr.crc, 0);
+ crc = crc32((FAR const uint8_t *)&blkhdr, SIZEOF_NXFFS_DATA_HDR);
+ crc = crc32part(&volume->cache[doffset], dlen, crc);
+
+ if (crc != ecrc)
+ {
+ fdbg("CRC failure\n");
+ return -EIO;
+ }
+
+ /* Looks good! Return the data length and success */
+
+ *datlen = dlen;
+ return OK;
+}
+
+
+
diff --git a/nuttx/fs/nxffs/nxffs_reformat.c b/nuttx/fs/nxffs/nxffs_reformat.c
new file mode 100644
index 000000000..d3c00893d
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_reformat.c
@@ -0,0 +1,267 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_reformat.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/mtd.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_format
+ *
+ * Description:
+ * Erase and reformat the entire volume.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume to be reformatted.
+ *
+ * Returned Value:
+ * Zero on success or a negated errno on a failure. Failures will be
+ * returned n the case of MTD reported failures o.
+ * Nothing in the volume data itself will generate errors.
+ *
+ ****************************************************************************/
+
+static int nxffs_format(FAR struct nxffs_volume_s *volume)
+{
+ FAR uint8_t *blkptr; /* Pointer to next block data */
+ off_t eblock; /* Erase block number */
+ off_t lblock; /* Logical block number */
+ ssize_t nxfrd; /* Number of blocks transferred */
+ int i;
+ int ret;
+
+ /* Create an image of one properly formatted erase sector */
+
+ memset(volume->pack, CONFIG_NXFFS_ERASEDSTATE, volume->geo.erasesize);
+ for (blkptr = volume->pack, i = 0;
+ i < volume->blkper;
+ blkptr += volume->geo.blocksize, i++)
+ {
+ FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s*)blkptr;
+ memcpy(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE);
+ blkhdr->state = BLOCK_STATE_GOOD;
+ }
+
+ /* Erase and format each erase block */
+
+ for (eblock = 0; eblock < volume->geo.neraseblocks; eblock++)
+ {
+ /* Erase the block */
+
+ ret = MTD_ERASE(volume->mtd, eblock, 1);
+ if (ret < 0)
+ {
+ fdbg("Erase block %d failed: %d\n", eblock, ret);
+ return ret;
+ }
+
+ /* Write the formatted image to the erase block */
+
+ lblock = eblock * volume->blkper;
+ nxfrd = MTD_BWRITE(volume->mtd, lblock, volume->blkper, volume->pack);
+ if (nxfrd != volume->blkper)
+ {
+ fdbg("Write erase block %d failed: %d\n", lblock, nxfrd);
+ return -EIO;
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxffs_badblocks
+ *
+ * Description:
+ * Verify each block and mark improperly erased blocks as bad.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume to be reformatted.
+ *
+ * Returned Value:
+ * Zero on success or a negated errno on a failure. Failures will be
+ * returned n the case of MTD reported failures o.
+ * Nothing in the volume data itself will generate errors.
+ *
+ ****************************************************************************/
+
+static int nxffs_badblocks(FAR struct nxffs_volume_s *volume)
+{
+ FAR uint8_t *blkptr; /* Pointer to next block data */
+ off_t eblock; /* Erase block number */
+ off_t lblock; /* Logical block number */
+ ssize_t nxfrd; /* Number of blocks transferred */
+ bool good; /* TRUE: block is good */
+ bool modified; /* TRUE: The erase block has been modified */
+ int i;
+
+ /* Read and verify each erase block */
+
+ for (eblock = 0; eblock < volume->geo.neraseblocks; eblock++)
+ {
+ /* Read the entire erase block */
+
+ lblock = eblock * volume->blkper;
+ nxfrd = MTD_BREAD(volume->mtd, lblock, volume->blkper, volume->pack);
+ if (nxfrd != volume->blkper)
+ {
+ fdbg("Read erase block %d failed: %d\n", lblock, nxfrd);
+ return -EIO;
+ }
+
+ /* Process each logical block */
+
+ modified = false;
+ for (blkptr = volume->pack, i = 0;
+ i < volume->blkper;
+ blkptr += volume->geo.blocksize, i++)
+ {
+ FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s*)blkptr;
+
+ /* Check block header */
+
+ good = true;
+ if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) != 0 ||
+ blkhdr->state != BLOCK_STATE_GOOD)
+ {
+ good = false;
+ }
+
+ /* Check that block data is erased */
+
+ else
+ {
+ size_t blocksize = volume->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR;
+ size_t erasesize = nxffs_erased(&blkptr[SIZEOF_NXFFS_BLOCK_HDR], blocksize);
+ good = (blocksize == erasesize);
+ }
+
+ /* If the block is bad, attempt to re-write the block header indicating
+ * a bad block (of course, if the block has failed, this may not be
+ * possible, depending upon failure modes.
+ */
+
+ if (!good)
+ {
+ memcpy(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE);
+ blkhdr->state = BLOCK_STATE_BAD;
+ modified = true;
+ }
+ }
+
+ /* If the erase block was modified, then re-write it */
+
+ nxfrd = MTD_BWRITE(volume->mtd, lblock, volume->blkper, volume->pack);
+ if (nxfrd != volume->blkper)
+ {
+ fdbg("Write erase block %d failed: %d\n", lblock, nxfrd);
+ return -EIO;
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_reformat
+ *
+ * Description:
+ * Erase and reformat the entire volume. Verify each block and mark
+ * improperly erased blocks as bad.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume to be reformatted.
+ *
+ * Returned Value:
+ * Zero on success or a negated errno on a failure. Failures will be
+ * returned n the case of MTD reported failures o.
+ * Nothing in the volume data itself will generate errors.
+ *
+ ****************************************************************************/
+
+int nxffs_reformat(FAR struct nxffs_volume_s *volume)
+{
+ int ret;
+
+ /* Erase and reformat the entire volume */
+
+ ret = nxffs_format(volume);
+ if (ret < 0)
+ {
+ fdbg("Failed to reformat the volume: %d\n", -ret);
+ return ret;
+ }
+
+ /* Check for bad blocks */
+
+ ret = nxffs_badblocks(volume);
+ if (ret < 0)
+ {
+ fdbg("Bad block check failed: %d\n", -ret);
+ }
+ return ret;
+}
diff --git a/nuttx/fs/nxffs/nxffs_stat.c b/nuttx/fs/nxffs/nxffs_stat.c
new file mode 100644
index 000000000..d4d58a72c
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_stat.c
@@ -0,0 +1,186 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_stat.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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/stat.h>
+#include <sys/statfs.h>
+
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/mtd.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_statfs
+ *
+ * Description: Return filesystem statistics
+ *
+ ****************************************************************************/
+
+int nxffs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
+{
+ FAR struct nxffs_volume_s *volume;
+ int ret;
+
+ fvdbg("Entry\n");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the NuttX inode structure */
+
+ volume = mountpt->i_private;
+ ret = sem_wait(&volume->exclsem);
+ if (ret != OK)
+ {
+ goto errout;
+ }
+
+ /* Fill in the statfs info */
+#warning "Need f_bfree, f_bavail, f_files, f_ffree calculation"
+
+ memset(buf, 0, sizeof(struct statfs));
+ buf->f_type = NXFFS_MAGIC;
+ buf->f_bsize = volume->geo.blocksize;
+ buf->f_blocks = volume->nblocks;
+ buf->f_namelen = volume->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR - SIZEOF_NXFFS_INODE_HDR;
+ ret = OK;
+
+ sem_post(&volume->exclsem);
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_stat
+ *
+ * Description: Return information about a file or directory
+ *
+ ****************************************************************************/
+
+int nxffs_stat(FAR struct inode *mountpt, FAR const char *relpath,
+ FAR struct stat *buf)
+{
+ FAR struct nxffs_volume_s *volume;
+ struct nxffs_entry_s entry;
+ int ret;
+
+ fvdbg("Entry\n");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private && buf);
+
+ /* Get the mountpoint private data from the NuttX inode structure */
+
+ volume = mountpt->i_private;
+ ret = sem_wait(&volume->exclsem);
+ if (ret != OK)
+ {
+ goto errout;
+ }
+
+ /* Initialize the return stat instance */
+
+ memset(buf, 0, sizeof(struct stat));
+ buf->st_blksize = volume->geo.blocksize;
+ buf->st_blocks = entry.datlen / (volume->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR);
+
+ /* The requested directory must be the volume-relative "root" directory */
+
+ if (relpath && relpath[0] != '\0')
+ {
+ /* Not the top directory.. find the NXFFS inode with this name */
+
+ ret = nxffs_findinode(volume, relpath, &entry);
+ if (ret < 0)
+ {
+ fdbg("Inode '%s' not found: %d\n", -ret);
+ goto errout_with_semaphore;
+ }
+
+ buf->st_mode = S_IFREG|S_IXOTH|S_IXGRP|S_IXUSR;
+ buf->st_size = entry.datlen;
+ buf->st_atime = entry.utc;
+ buf->st_mtime = entry.utc;
+ buf->st_ctime = entry.utc;
+ }
+ else
+ {
+ /* It's a read/execute-only directory name */
+
+ buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR|S_IXOTH|S_IXGRP|S_IXUSR;
+ }
+
+ ret = OK;
+
+errout_with_semaphore:
+ sem_post(&volume->exclsem);
+errout:
+ return ret;
+}
diff --git a/nuttx/fs/nxffs/nxffs_unlink.c b/nuttx/fs/nxffs/nxffs_unlink.c
new file mode 100644
index 000000000..73b0f360a
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_unlink.c
@@ -0,0 +1,187 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_unlink.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/mtd.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_rminode
+ *
+ * Description:
+ * Remove an inode from FLASH. This is the internal implementation of
+ * the file system unlinke operation.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume.
+ * name - the name of the inode to be deleted.
+ *
+ * Returned Value:
+ * Zero is returned if the inode is successfully deleted. Otherwise, a
+ * negated errno value is returned indicating the nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_rminode(FAR struct nxffs_volume_s *volume, FAR const char *name)
+{
+ FAR struct nxffs_ofile_s *ofile;
+ FAR struct nxffs_inode_s *inode;
+ struct nxffs_entry_s entry;
+ int ret;
+
+ /* Check if the file is open */
+
+ ofile = nxffs_findofile(volume, name);
+ if (ofile)
+ {
+ /* We can't remove the inode if it is open */
+
+ fdbg("Inode '%s' is open\n", name);
+ ret = -EBUSY;
+ goto errout;
+ }
+
+ /* Find the NXFFS inode */
+
+ ret = nxffs_findinode(volume, name, &entry);
+ if (ret < 0)
+ {
+ fdbg("Inode '%s' not found\n", name);
+ goto errout;
+ }
+
+ /* Set the position to the FLASH offset of the file header (nxffs_findinode
+ * should have left the block in the cache).
+ */
+
+ nxffs_ioseek(volume, entry.hoffset);
+
+ /* Make sure the the block is in the cache */
+
+ ret = nxffs_rdcache(volume, volume->ioblock);
+ if (ret < 0)
+ {
+ fdbg("Failed to read data into cache: %d\n", ret);
+ goto errout_with_entry;
+ }
+
+ /* Change the file status... it is no longer valid */
+
+ inode = (FAR struct nxffs_inode_s *)&volume->cache[volume->iooffset];
+ inode->state = INODE_STATE_DELETED;
+
+ /* Then write the cached block back to FLASH */
+
+ ret = nxffs_wrcache(volume);
+ if (ret < 0)
+ {
+ fdbg("Failed to read data into cache: %d\n", ret);
+ }
+
+errout_with_entry:
+ nxffs_freeentry(&entry);
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_unlink
+ *
+ * Description: Remove a file
+ *
+ ****************************************************************************/
+
+int nxffs_unlink(FAR struct inode *mountpt, FAR const char *relpath)
+{
+ FAR struct nxffs_volume_s *volume;
+ int ret;
+
+ fvdbg("Entry\n");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the NuttX inode structure */
+
+ volume = mountpt->i_private;
+ ret = sem_wait(&volume->exclsem);
+ if (ret != OK)
+ {
+ goto errout;
+ }
+
+ /* Then remove the NXFFS inode */
+
+ ret = nxffs_rminode(volume, relpath);
+
+ sem_post(&volume->exclsem);
+errout:
+ return ret;
+}
diff --git a/nuttx/fs/nxffs/nxffs_util.c b/nuttx/fs/nxffs/nxffs_util.c
new file mode 100644
index 000000000..f424e71e0
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_util.c
@@ -0,0 +1,181 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_util.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_rdle16
+ *
+ * Description:
+ * Get a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * val - A pointer to the first byte of the little endian value.
+ *
+ * Returned Value:
+ * A uint16_t representing the whole 16-bit integer value
+ *
+ ****************************************************************************/
+
+uint16_t nxffs_rdle16(FAR const uint8_t *val)
+{
+ return (uint16_t)val[1] << 8 | (uint16_t)val[0];
+}
+
+/****************************************************************************
+ * Name: nxffs_wrle16
+ *
+ * Description:
+ * Put a (possibly unaligned) 16-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 16-bit value to be saved.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void nxffs_wrle16(uint8_t *dest, uint16_t val)
+{
+ dest[0] = val & 0xff; /* Little endian means LS byte first in byte stream */
+ dest[1] = val >> 8;
+}
+
+/****************************************************************************
+ * Name: nxffs_rdle32
+ *
+ * Description:
+ * Get a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * val - A pointer to the first byte of the little endian value.
+ *
+ * Returned Value:
+ * A uint32_t representing the whole 32-bit integer value
+ *
+ ****************************************************************************/
+
+uint32_t nxffs_rdle32(FAR const uint8_t *val)
+{
+ /* Little endian means LS halfword first in byte stream */
+
+ return (uint32_t)nxffs_rdle16(&val[2]) << 16 | (uint32_t)nxffs_rdle16(val);
+}
+
+/****************************************************************************
+ * Name: nxffs_wrle32
+ *
+ * Description:
+ * Put a (possibly unaligned) 32-bit little endian value.
+ *
+ * Input Parameters:
+ * dest - A pointer to the first byte to save the little endian value.
+ * val - The 32-bit value to be saved.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void nxffs_wrle32(uint8_t *dest, uint32_t val)
+{
+ /* Little endian means LS halfword first in byte stream */
+
+ nxffs_wrle16(dest, (uint16_t)(val & 0xffff));
+ nxffs_wrle16(dest+2, (uint16_t)(val >> 16));
+}
+
+/****************************************************************************
+ * Name: nxffs_erased
+ *
+ * Description:
+ * Check if a block of memory is in the erased state.
+ *
+ * Input Parameters:
+ * buffer - Address of the start of the memory to check.
+ * buflen - The number of bytes to check.
+ *
+ * Returned Values:
+ * The number of erased bytes found at the beginning of the memory region.
+ *
+ ****************************************************************************/
+
+size_t nxffs_erased(FAR const uint8_t *buffer, size_t buflen)
+{
+ size_t nerased = 0;
+
+ for (; nerased < buflen; nerased++)
+ {
+ if (*buffer != CONFIG_NXFFS_ERASEDSTATE)
+ {
+ break;
+ }
+ buffer++;
+ }
+
+ return nerased;
+}
diff --git a/nuttx/fs/nxffs/nxffs_write.c b/nuttx/fs/nxffs/nxffs_write.c
new file mode 100644
index 000000000..bf32b0fb4
--- /dev/null
+++ b/nuttx/fs/nxffs/nxffs_write.c
@@ -0,0 +1,853 @@
+/****************************************************************************
+ * fs/nxffs/nxffs_write.c
+ *
+ * Copyright (C) 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <string.h>
+#include <fcntl.h>
+#include <crc32.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/mtd.h>
+
+#include "nxffs.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_hdrpos
+ *
+ * Description:
+ * Find a valid location for the data block header. A valid location will
+ * have these properties:
+ *
+ * 1. It will lie in the free flash region.
+ * 2. It will have enough contiguous memory to hold the entire header
+ * PLUS some meaningful amount of data (NXFFS_MINDATA).
+ * 3. The memory at this location will be fully erased.
+ *
+ * This function will only perform the checks of 1) and 2).
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * wrfile - Contains the current guess for the header position. On
+ * successful return, this field will hold the selected header
+ * position.
+ * size - The minimum size of the current write.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure. Of special interest
+ * the return error of -ENOSPC which means that the FLASH volume is
+ * full and should be repacked.
+ *
+ * On successful return the following are also valid:
+ *
+ * wrfile->doffset - Flash offset to candidate data block header position
+ * volume->ioblock - Read/write block number of the block containing the
+ * header position
+ * volume->iooffset - The offset in the block to the candidate header
+ * position.
+ * volume->froffset - Updated offset to the first free FLASH block.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_hdrpos(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_wrfile_s *wrfile,
+ size_t size)
+{
+ int ret;
+
+ /* Reserve memory for the object */
+
+ ret = nxffs_wrreserve(volume, SIZEOF_NXFFS_DATA_HDR + size);
+ if (ret == OK)
+ {
+ /* Save the offset to the FLASH region reserved for the data block
+ * header
+ */
+
+ wrfile->doffset = nxffs_iotell(volume);
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_hdrerased
+ *
+ * Description:
+ * Find a valid location for the data block header. A valid location will
+ * have these properties:
+ *
+ * 1. It will lie in the free flash region.
+ * 2. It will have enough contiguous memory to hold the entire header
+ * PLUS some meaningful amount of data (NXFFS_MINDATA).
+ * 3. The memory at this location will be fully erased.
+ *
+ * This function will only perform the check 3). On entry it assumes:
+ *
+ * volume->ioblock - Read/write block number of the block containing the
+ * header position
+ * volume->iooffset - The offset in the block to the candidate header
+ * position.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * wrfile - Contains the current guess for the header position. On
+ * successful return, this field will hold the selected header
+ * position.
+ * size - The minimum size of the current write.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure. Of special interest
+ * the return error of -ENOSPC which means that the FLASH volume is
+ * full and should be repacked.
+ *
+ * On successful return the following are also valid:
+ *
+ * wrfile->doffset - Flash offset to candidate data block header position
+ * volume->ioblock - Read/write block number of the block containing the
+ * header position
+ * volume->iooffset - The offset in the block to the candidate header
+ * position.
+ * volume->froffset - Updated offset to the first free FLASH block.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_hdrerased(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_wrfile_s *wrfile,
+ size_t size)
+{
+ int ret;
+
+ /* Find a valid location to save the inode header */
+
+ ret = nxffs_wrverify(volume, SIZEOF_NXFFS_DATA_HDR + size);
+ if (ret == OK)
+ {
+ /* This is where we will put the data block header */
+
+ wrfile->doffset = nxffs_iotell(volume);
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_wralloc
+ *
+ * Description:
+ * Allocate FLASH memory for the data block.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * wrfile - Describes the open file to be written.
+ * size - Size of the current write operation.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_wralloc(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_wrfile_s *wrfile,
+ size_t size)
+{
+ bool packed;
+ int ret;
+
+ /* Allocate FLASH memory for the data block.
+ *
+ * Loop until the data block header is configured or until a failure
+ * occurs. Note that nothing is written to FLASH. The data block header
+ * is not written until either (1) the file is closed, or (2) the data
+ * region is fully populated.
+ */
+
+ packed = false;
+ for (;;)
+ {
+ size_t mindata = MIN(NXFFS_MINDATA, size);
+
+ /* File a valid location to position the data block. Start with
+ * the first byte in the free FLASH region.
+ */
+
+ ret = nxffs_hdrpos(volume, wrfile, mindata);
+ if (ret == OK)
+ {
+ /* Find a region of memory in the block that is fully erased */
+
+ ret = nxffs_hdrerased(volume, wrfile, mindata);
+ if (ret == OK)
+ {
+ /* Valid memory for the data block was found. Return success. */
+
+ return OK;
+ }
+ }
+
+ /* If no valid memory is found searching to the end of the volume,
+ * then -ENOSPC will be returned. Other errors are not handled.
+ */
+
+ if (ret != -ENOSPC || packed)
+ {
+ fdbg("Failed to find inode header memory: %d\n", -ret);
+ return -ENOSPC;
+ }
+
+ /* -ENOSPC is a special case.. It means that the volume is full.
+ * Try to pack the volume in order to free up some space.
+ */
+
+ ret = nxffs_pack(volume);
+ if (ret < 0)
+ {
+ fdbg("Failed to pack the volume: %d\n", -ret);
+ return ret;
+ }
+
+ /* After packing the volume, froffset will be updated to point to the
+ * new free flash region. Try again.
+ */
+
+ nxffs_ioseek(volume, volume->froffset);
+ packed = true;
+ }
+
+ /* Can't get here */
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxffs_reverify
+ *
+ * Description:
+ * Verify that the partial flash data block in the volume cache is valid.
+ * On entry, this function assumes:
+ *
+ * volume->ioblock - Read/write block number of the block containing the
+ * data block.
+ * volume->iooffset - The offset in the block to the data block.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * wrfile - Describes the open file to be written.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure.
+ *
+ ****************************************************************************/
+
+static inline int nxffs_reverify(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_wrfile_s *wrfile)
+{
+ uint32_t crc;
+ off_t offset;
+
+ if (wrfile->datlen > 0)
+ {
+ /* Get the offset to the start of the data */
+
+ offset = volume->iooffset + SIZEOF_NXFFS_DATA_HDR;
+ DEBUGASSERT(offset + wrfile->datlen < volume->geo.blocksize);
+
+ /* Calculate the CRC of the partial data block */
+
+ crc = crc32(&volume->cache[offset], wrfile->datlen);
+
+ /* It must match the previoulsy calculated CRC value */
+
+ if (crc != wrfile->crc)
+ {
+ fdbg("CRC failure\n");
+ return -EIO;
+ }
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxffs_wrappend
+ *
+ * Description:
+ * Append FLASH data to the data block.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * wrfile - Describes the open file to be written.
+ * buffer - Address of buffer of data to be written.
+ * buflen - The number of bytes remaimining to be written
+ *
+ * Returned Value:
+ * The number of bytes written is returned on success. Otherwise, a
+ * negated errno value is returned indicating the nature of the failure.
+ *
+ ****************************************************************************/
+
+static inline ssize_t nxffs_wrappend(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_wrfile_s *wrfile,
+ FAR const char *buffer, size_t buflen)
+{
+ ssize_t maxsize;
+ size_t nbytestowrite;
+ ssize_t nbytesleft;
+ off_t offset;
+ int ret;
+
+ /* Get the offset to the start of unwritten data */
+
+ offset = volume->iooffset + wrfile->datlen + SIZEOF_NXFFS_DATA_HDR;
+
+ /* Determine that maximum amount of data that can be written to this
+ * block.
+ */
+
+ maxsize = volume->geo.blocksize - offset;
+ DEBUGASSERT(maxsize > 0);
+
+ /* But don't try to write over any unerased bytes */
+
+ maxsize = nxffs_erased(&volume->cache[offset], maxsize);
+
+ /* Write as many bytes as we can into the data buffer */
+
+ nbytestowrite = MIN(maxsize, buflen);
+ nbytesleft = maxsize - nbytestowrite;
+
+ if (nbytestowrite > 0)
+ {
+ /* Copy the data into the volume write cache */
+
+ memcpy(&volume->cache[offset], buffer, nbytestowrite);
+
+ /* Increment the number of bytes written to the data block */
+
+ wrfile->datlen += nbytestowrite;
+
+ /* Re-calculate the CRC */
+
+ offset = volume->iooffset + SIZEOF_NXFFS_DATA_HDR;
+ wrfile->crc = crc32(&volume->cache[offset], wrfile->datlen);
+
+ /* And write the partial write block to FLASH -- unless the data
+ * block is full. In that case, the block will be written below.
+ */
+
+ if (nbytesleft > 0)
+ {
+ ret = nxffs_wrcache(volume);
+ if (ret < 0)
+ {
+ fdbg("nxffs_wrcache failed: %d\n", -ret);
+ return ret;
+ }
+ }
+ }
+
+ /* Check if the data block is now full */
+
+ if (nbytesleft <= 0)
+ {
+ /* The data block is full, write the block to FLASH */
+
+ ret = nxffs_wrblkhdr(volume, wrfile);
+ if (ret < 0)
+ {
+ fdbg("nxffs_wrblkdhr failed: %d\n", -ret);
+ return ret;
+ }
+ }
+
+ /* Return the number of bytes written to FLASH this time */
+
+ return nbytestowrite;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nxffs_write
+ *
+ * Description:
+ * This is an implementation of the NuttX standard file system write
+ * method.
+ *
+ ****************************************************************************/
+
+ssize_t nxffs_write(FAR struct file *filep, FAR const char *buffer, size_t buflen)
+{
+ FAR struct nxffs_volume_s *volume;
+ FAR struct nxffs_wrfile_s *wrfile;
+ ssize_t remaining;
+ ssize_t nwritten;
+ ssize_t total;
+ int ret;
+
+ fvdbg("Write %d bytes to offset %d\n", buflen, filep->f_pos);
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover the open file state from the struct file instance */
+
+ wrfile = (FAR struct nxffs_wrfile_s *)filep->f_priv;
+
+ /* Recover the volume state from the open file */
+
+ volume = (FAR struct nxffs_volume_s *)filep->f_inode->i_private;
+ DEBUGASSERT(volume != NULL);
+
+ /* Get exclusive access to the volume. Note that the volume exclsem
+ * protects the open file list.
+ */
+
+ ret = sem_wait(&volume->exclsem);
+ if (ret != OK)
+ {
+ ret = -errno;
+ fdbg("sem_wait failed: %d\n", ret);
+ goto errout;
+ }
+
+ /* Check if the file was opened with write access */
+
+ if ((wrfile->ofile.oflags & O_WROK) == 0)
+ {
+ fdbg("File not open for write access\n");
+ ret = -EACCES;
+ goto errout_with_semaphore;
+ }
+
+ /* Loop until we successfully appended all of the data to the file (or an
+ * error occurs)
+ */
+
+ for (total = 0; total < buflen; )
+ {
+ remaining = buflen - total;
+
+ /* Have we already allocated the data block? */
+
+ if (wrfile->doffset == 0)
+ {
+ /* No, allocate the data block now, re-packing if necessary. */
+
+ wrfile->datlen = 0;
+ ret = nxffs_wralloc(volume, wrfile, remaining);
+ if (ret < 0)
+ {
+ fdbg("Failed to allocate a data block: %d\n", -ret);
+ goto errout_with_semaphore;
+ }
+ }
+
+ /* Seek to the FLASH block containing the data block */
+
+ nxffs_ioseek(volume, wrfile->doffset);
+
+ /* Verify that the FLASH data that was previously written is still intact */
+
+ ret = nxffs_reverify(volume, wrfile);
+ if (ret < 0)
+ {
+ fdbg("Failed to verify FLASH data block: %d\n", -ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Append the data to the end of the data block and write the updated
+ * block to flash.
+ */
+
+ nwritten = nxffs_wrappend(volume, wrfile, &buffer[total], remaining);
+ if (nwritten < 0)
+ {
+ fdbg("Failed to append to FLASH to a data block: %d\n", -ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Decrement the number of bytes remaining to be written */
+
+ total += nwritten;
+ }
+
+ /* Success.. return the number of bytes written */
+
+ ret = total;
+ filep->f_pos = wrfile->datlen;
+
+errout_with_semaphore:
+ sem_post(&volume->exclsem);
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: nxffs_wrreserve
+ *
+ * Description:
+ * Find a valid location for a file system object of 'size'. A valid
+ * location will have these properties:
+ *
+ * 1. It will lie in the free flash region.
+ * 2. It will have enough contiguous memory to hold the entire object
+ * 3. The memory at this location will be fully erased.
+ *
+ * This function will only perform the checks of 1) and 2). The
+ * end-of-filesystem offset, froffset, is update past this memory which,
+ * in effect, reserves the memory.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * size - The size of the object to be reserved.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure. Of special interest
+ * the return error of -ENOSPC which means that the FLASH volume is
+ * full and should be repacked.
+ *
+ * On successful return the following are also valid:
+ *
+ * volume->ioblock - Read/write block number of the block containing the
+ * candidate oject position
+ * volume->iooffset - The offset in the block to the candidate object
+ * position.
+ * volume->froffset - Updated offset to the first free FLASH block after
+ * the reserved memory.
+ *
+ ****************************************************************************/
+
+int nxffs_wrreserve(FAR struct nxffs_volume_s *volume, size_t size)
+{
+ int ret;
+
+ /* Seek to the beginning of the free FLASH region */
+
+ nxffs_ioseek(volume, volume->froffset);
+
+ /* Check for a seek past the end of the volume */
+
+ if (volume->ioblock >= volume->nblocks)
+ {
+ /* Return -ENOSPC to indicate that the volume is full */
+
+ return -ENOSPC;
+ }
+
+ /* Skip over block headers */
+
+ if (volume->iooffset < SIZEOF_NXFFS_BLOCK_HDR)
+ {
+ volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR;
+ }
+
+ /* Make sure that there is space there to hold the entire object */
+
+ if (volume->iooffset + size > volume->geo.blocksize)
+ {
+ /* We will need to skip to the next block. But first, check if we are
+ * already at the final block.
+ */
+
+ if (volume->ioblock + 1 >= volume->nblocks)
+ {
+ /* Return -ENOSPC to indicate that the volume is full */
+
+ fdbg("No space in last block\n");
+ return -ENOSPC;
+ }
+
+ /* This is not the last block in the volume, so just seek to the
+ * beginning of the next, valid block.
+ */
+
+ volume->ioblock++;
+ ret = nxffs_validblock(volume, &volume->ioblock);
+ if (ret < 0)
+ {
+ fdbg("No more valid blocks\n");
+ return ret;
+ }
+ volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR;
+ }
+
+ /* Update the pointer to the first next free FLASH memory -- reserving this
+ * block of memory.
+ */
+
+ volume->froffset = nxffs_iotell(volume) + size;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxffs_wrverify
+ *
+ * Description:
+ * Find a valid location for the object. A valid location will have
+ * these properties:
+ *
+ * 1. It will lie in the free flash region.
+ * 2. It will have enough contiguous memory to hold the entire header
+ * (excluding the file name which may lie in the next block).
+ * 3. The memory at this location will be fully erased.
+ *
+ * This function will only perform the check 3). On entry it assumes the
+ * following settings (left by nxffs_wrreserve()):
+ *
+ * volume->ioblock - Read/write block number of the block containing the
+ * candidate oject position
+ * volume->iooffset - The offset in the block to the candidate object
+ * position.
+ *
+ * Input Parameters:
+ * volume - Describes the NXFFS volume
+ * size - The size of the object to be verifed.
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned indicating the nature of the failure. Of special interest
+ * the return error of -ENOSPC which means that the FLASH volume is
+ * full and should be repacked.
+ *
+ * On successful return the following are also valid:
+ *
+ * volume->ioblock - Read/write block number of the block containing the
+ * verified object position
+ * volume->iooffset - The offset in the block to the verified object
+ * position.
+ * volume->froffset - Updated offset to the first free FLASH block.
+ *
+ ****************************************************************************/
+
+int nxffs_wrverify(FAR struct nxffs_volume_s *volume, size_t size)
+{
+ uint16_t iooffset;
+ int nerased;
+ int ret;
+ int i;
+
+ /* Search to the very last block in the volume if we have to */
+
+ while (volume->ioblock < volume->nblocks)
+ {
+ /* Make sure that the block is in memory */
+
+ ret = nxffs_rdcache(volume, volume->ioblock);
+ if (ret < 0)
+ {
+ fdbg("Failed to read block %d: %d\n", volume->ioblock, -ret);
+ return ret;
+ }
+
+ /* Search to the very end of this block if we have to */
+
+ iooffset = volume->iooffset;
+ nerased = 0;
+
+ for (i = volume->iooffset; i < volume->geo.blocksize; i++)
+ {
+ /* Is this byte erased? */
+
+ if (volume->cache[i] == CONFIG_NXFFS_ERASEDSTATE)
+ {
+ /* Yes.. increment the count of contiguous, erased bytes */
+
+ nerased++;
+
+ /* Is the whole header memory erased? */
+
+ if (nerased >= size)
+ {
+ /* Yes.. this this is where we will put the object */
+
+ off_t offset = volume->ioblock * volume->geo.blocksize + iooffset;
+
+ /* Update the free flash offset and return success */
+
+ volume->froffset = offset + size;
+ return OK;
+ }
+ }
+
+ /* This byte is not erased! (It should be unless the block is bad) */
+
+ else
+ {
+ nerased = 0;
+ iooffset = i + 1;
+ }
+ }
+
+ /* If we get here, then we have looked at every byte in the the block
+ * and did not find any sequence of erased bytes long enough to hold
+ * the object. Skip to the next, valid block.
+ */
+
+ volume->ioblock++;
+ ret = nxffs_validblock(volume, &volume->ioblock);
+ if (ret < 0)
+ {
+ fdbg("No more valid blocks\n");
+ return ret;
+ }
+
+ volume->iooffset = SIZEOF_NXFFS_BLOCK_HDR;
+ volume->froffset = volume->ioblock * volume->geo.blocksize + SIZEOF_NXFFS_BLOCK_HDR;
+ }
+
+ /* Return -ENOSPC if there is no erased memory left in the volume for
+ * the object.
+ */
+
+ fdbg("Not enough memory left to hold the file header\n");
+ return -ENOSPC;
+}
+
+/****************************************************************************
+ * Name: nxffs_wrblkhdr
+ *
+ * Description:
+ * Write the block header information. This is done (1) whenever the end-
+ * block is encountered and (2) also when the file is closed in order to
+ * flush the final block of data to FLASH.
+ *
+ * Input Parameters:
+ * volume - Describes the state of the NXFFS volume
+ * wrfile - Describes the state of the open file
+ *
+ * Returned Value:
+ * Zero is returned on success; Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int nxffs_wrblkhdr(FAR struct nxffs_volume_s *volume,
+ FAR struct nxffs_wrfile_s *wrfile)
+{
+ FAR struct nxffs_data_s *dathdr;
+ int ret;
+
+ /* Write the data block header to memory */
+
+ nxffs_ioseek(volume, wrfile->doffset);
+ dathdr = (FAR struct nxffs_data_s *)&volume->cache[volume->iooffset];
+ memcpy(dathdr->magic, g_datamagic, NXFFS_MAGICSIZE);
+ nxffs_wrle32(dathdr->crc, 0);
+ nxffs_wrle16(dathdr->datlen, wrfile->datlen);
+
+ /* Update the entire data block CRC (including the header) */
+
+ wrfile->crc = crc32(&volume->cache[volume->iooffset], wrfile->datlen + SIZEOF_NXFFS_DATA_HDR);
+ nxffs_wrle32(dathdr->crc, wrfile->crc);
+
+ /* And write the data block to FLASH */
+
+ ret = nxffs_wrcache(volume);
+ if (ret < 0)
+ {
+ fdbg("nxffs_wrcache failed: %d\n", -ret);
+ goto errout;
+ }
+
+ /* After the block has been successfully written to flash, update the inode
+ * statistics and reset the write state.
+ *
+ * volume:
+ * froffset - The offset the next free FLASH region. Set to just after
+ * the inode data block that we just wrote. This is where we will
+ * begin the search for the next inode header or data block.
+ */
+
+ volume->froffset = (wrfile->doffset + wrfile->datlen + SIZEOF_NXFFS_DATA_HDR);
+
+ /* wrfile->file.entry:
+ * datlen: Total file length accumulated so far. When the file is
+ * closed, this will hold the file length.
+ * doffset: Offset to the first data block. Only the offset to the
+ * first data block is saved.
+ */
+
+ wrfile->ofile.entry.datlen += wrfile->datlen;
+ if (wrfile->ofile.entry.doffset == 0)
+ {
+ wrfile->ofile.entry.doffset = wrfile->doffset;
+ }
+
+ /* Return success */
+
+ ret = OK;
+
+errout:
+ wrfile->crc = 0;
+ wrfile->doffset = 0;
+ wrfile->datlen = 0;
+ return ret;
+}
+
diff --git a/nuttx/fs/romfs/Kconfig b/nuttx/fs/romfs/Kconfig
new file mode 100644
index 000000000..6a91011ab
--- /dev/null
+++ b/nuttx/fs/romfs/Kconfig
@@ -0,0 +1,14 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+config FS_ROMFS
+ bool "ROMFS file system"
+ default n
+ depends on !DISABLE_MOUNTPOINT
+ ---help---
+ Enable ROMFS filesystem support
+
+if FS_ROMFS
+endif
diff --git a/nuttx/fs/romfs/Make.defs b/nuttx/fs/romfs/Make.defs
new file mode 100644
index 000000000..77de93c05
--- /dev/null
+++ b/nuttx/fs/romfs/Make.defs
@@ -0,0 +1,45 @@
+############################################################################
+# fs/romfs/Make.defs
+#
+# Copyright (C) 2008, 2011 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_FS_ROMFS),y)
+# Files required for ROMFS file system support
+
+ASRCS +=
+CSRCS += fs_romfs.c fs_romfsutil.c
+
+# Argument for dependency checking
+
+ROMFSDEPPATH = --dep-path romfs
+endif
diff --git a/nuttx/fs/romfs/fs_romfs.c b/nuttx/fs/romfs/fs_romfs.c
new file mode 100644
index 000000000..b95619d75
--- /dev/null
+++ b/nuttx/fs/romfs/fs_romfs.c
@@ -0,0 +1,1090 @@
+/****************************************************************************
+ * rm/romfs/fs_romfs.h
+ *
+ * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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/statfs.h>
+#include <sys/stat.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/fs/dirent.h>
+
+#include "fs_romfs.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int romfs_open(FAR struct file *filep, const char *relpath,
+ int oflags, mode_t mode);
+static int romfs_close(FAR struct file *filep);
+static ssize_t romfs_read(FAR struct file *filep, char *buffer, size_t buflen);
+static off_t romfs_seek(FAR struct file *filep, off_t offset, int whence);
+static int romfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+
+static int romfs_opendir(struct inode *mountpt, const char *relpath,
+ struct fs_dirent_s *dir);
+static int romfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir);
+static int romfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir);
+
+static int romfs_bind(FAR struct inode *blkdriver, const void *data,
+ void **handle);
+static int romfs_unbind(void *handle, FAR struct inode **blkdriver);
+static int romfs_statfs(struct inode *mountpt, struct statfs *buf);
+
+static int romfs_stat(struct inode *mountpt, const char *relpath, struct stat *buf);
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/* See fs_mount.c -- this structure is explicitly externed there.
+ * We use the old-fashioned kind of initializers so that this will compile
+ * with any compiler.
+ */
+
+const struct mountpt_operations romfs_operations =
+{
+ romfs_open, /* open */
+ romfs_close, /* close */
+ romfs_read, /* read */
+ NULL, /* write */
+ romfs_seek, /* seek */
+ romfs_ioctl, /* ioctl */
+ NULL, /* sync */
+
+ romfs_opendir, /* opendir */
+ NULL, /* closedir */
+ romfs_readdir, /* readdir */
+ romfs_rewinddir, /* rewinddir */
+
+ romfs_bind, /* bind */
+ romfs_unbind, /* unbind */
+ romfs_statfs, /* statfs */
+
+ NULL, /* unlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* rename */
+ romfs_stat /* stat */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: romfs_open
+ ****************************************************************************/
+
+static int romfs_open(FAR struct file *filep, const char *relpath,
+ int oflags, mode_t mode)
+{
+ struct romfs_dirinfo_s dirinfo;
+ struct romfs_mountpt_s *rm;
+ struct romfs_file_s *rf;
+ int ret;
+
+ fvdbg("Open '%s'\n", relpath);
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv == NULL && filep->f_inode != NULL);
+
+ /* mountpoint private data from the inode reference from the file
+ * structure
+ */
+
+ rm = (struct romfs_mountpt_s*)filep->f_inode->i_private;
+
+ DEBUGASSERT(rm != NULL);
+
+ /* Check if the mount is still healthy */
+
+ romfs_semtake(rm);
+ ret = romfs_checkmount(rm);
+ if (ret != OK)
+ {
+ fdbg("romfs_checkmount failed: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* ROMFS is read-only. Any attempt to open with any kind of write
+ * access is not permitted.
+ */
+
+ if ((oflags & O_WRONLY) != 0 || (oflags & O_RDONLY) == 0)
+ {
+ fdbg("Only O_RDONLY supported\n");
+ ret = -EACCES;
+ goto errout_with_semaphore;
+ }
+
+ /* Initialize the directory info structure */
+
+ memset(&dirinfo, 0, sizeof(struct romfs_dirinfo_s));
+
+ /* Locate the directory entry for this path */
+
+ ret = romfs_finddirentry(rm, &dirinfo, relpath);
+ if (ret < 0)
+ {
+ fdbg("Failed to find directory directory entry for '%s': %d\n",
+ relpath, ret);
+ goto errout_with_semaphore;
+ }
+
+ /* The full path exists -- but is the final component a file
+ * or a directory?
+ */
+
+ if (IS_DIRECTORY(dirinfo.rd_next))
+ {
+ /* It is a directory */
+
+ ret = -EISDIR;
+ fdbg("'%s' is a directory\n", relpath);
+ goto errout_with_semaphore;
+ }
+
+#ifdef CONFIG_FILE_MODE
+# warning "Missing check for privileges based on inode->i_mode"
+#endif
+
+ /* Create an instance of the file private data to describe the opened
+ * file.
+ */
+
+ rf = (struct romfs_file_s *)zalloc(sizeof(struct romfs_file_s));
+ if (!rf)
+ {
+ fdbg("Failed to allocate private data\n", ret);
+ ret = -ENOMEM;
+ goto errout_with_semaphore;
+ }
+
+ /* Initialize the file private data (only need to initialize
+ * non-zero elements)
+ */
+
+ rf->rf_open = true;
+ rf->rf_size = dirinfo.rd_size;
+
+ /* Get the start of the file data */
+
+ ret = romfs_datastart(rm, dirinfo.rd_dir.fr_curroffset,
+ &rf->rf_startoffset);
+ if (ret < 0)
+ {
+ fdbg("Failed to locate start of file data: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Configure buffering to support access to this file */
+
+ ret = romfs_fileconfigure(rm, rf);
+ if (ret < 0)
+ {
+ fdbg("Failed configure buffering: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Attach the private date to the struct file instance */
+
+ filep->f_priv = rf;
+
+ /* Then insert the new instance into the mountpoint structure.
+ * 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
+ * (but a simple reference count could have done that).
+ */
+
+ rf->rf_next = rm->rm_head;
+ rm->rm_head = rf->rf_next;
+
+ romfs_semgive(rm);
+ return OK;
+
+ /* Error exits */
+
+errout_with_semaphore:
+ romfs_semgive(rm);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: romfs_close
+ ****************************************************************************/
+
+static int romfs_close(FAR struct file *filep)
+{
+ struct romfs_mountpt_s *rm;
+ struct romfs_file_s *rf;
+ int ret = OK;
+
+ fvdbg("Closing\n");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ rf = filep->f_priv;
+ rm = filep->f_inode->i_private;
+
+ DEBUGASSERT(rm != NULL);
+
+ /* Do not check if the mount is healthy. We must support closing of
+ * the file even when there is healthy mount.
+ */
+
+ /* Deallocate the memory structures created when the open method
+ * was called.
+ *
+ * Free the sector buffer that was used to manage partial sector
+ * accesses.
+ */
+
+ if (!rm->rm_xipbase && rf->rf_buffer)
+ {
+ free(rf->rf_buffer);
+ }
+
+ /* Then free the file structure itself. */
+
+ free(rf);
+ filep->f_priv = NULL;
+ return ret;
+}
+
+/****************************************************************************
+ * Name: romfs_read
+ ****************************************************************************/
+
+static ssize_t romfs_read(FAR struct file *filep, char *buffer, size_t buflen)
+{
+ struct romfs_mountpt_s *rm;
+ struct romfs_file_s *rf;
+ unsigned int bytesread;
+ unsigned int readsize;
+ unsigned int nsectors;
+ uint32_t offset;
+ size_t bytesleft;
+ off_t sector;
+ uint8_t *userbuffer = (uint8_t*)buffer;
+ int sectorndx;
+ int ret;
+
+ 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 */
+
+ rf = filep->f_priv;
+ rm = filep->f_inode->i_private;
+
+ DEBUGASSERT(rm != NULL);
+
+ /* Make sure that the mount is still healthy */
+
+ romfs_semtake(rm);
+ ret = romfs_checkmount(rm);
+ if (ret != OK)
+ {
+ fdbg("romfs_checkmount failed: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Get the number of bytes left in the file */
+
+ bytesleft = rf->rf_size - filep->f_pos;
+
+ /* Truncate read count so that it does not exceed the number
+ * of bytes left in the file.
+ */
+
+ if (buflen > bytesleft)
+ {
+ buflen = bytesleft;
+ }
+
+ /* Loop until either (1) all data has been transferred, or (2) an
+ * error occurs.
+ */
+
+ readsize = 0;
+ while (buflen > 0)
+ {
+ /* Get the first sector and index to read from. */
+
+ offset = rf->rf_startoffset + filep->f_pos;
+ sector = SEC_NSECTORS(rm, offset);
+ sectorndx = offset & SEC_NDXMASK(rm);
+ bytesread = 0;
+
+ /* Check if the user has provided a buffer large enough to
+ * hold one or more complete sectors -AND- the read is
+ * aligned to a sector boundary.
+ */
+
+ nsectors = SEC_NSECTORS(rm, buflen);
+ if (nsectors > 0 && sectorndx == 0)
+ {
+ /* Read maximum contiguous sectors directly to the user's
+ * buffer without using our tiny read buffer.
+ */
+
+ /* Read all of the sectors directly into user memory */
+
+ fvdbg("Read %d sectors starting with %d\n", nsectors, sector);
+ ret = romfs_hwread(rm, userbuffer, sector, nsectors);
+ if (ret < 0)
+ {
+ fdbg("romfs_hwread failed: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ sector += nsectors;
+ bytesread = nsectors * rm->rm_hwsectorsize;
+ }
+ else
+ {
+ /* We are reading a partial sector. First, read the whole sector
+ * into the file data buffer. This is a caching buffer so if
+ * it is already there then all is well.
+ */
+
+ fvdbg("Read sector %d\n", sector);
+ ret = romfs_filecacheread(rm, rf, sector);
+ if (ret < 0)
+ {
+ fdbg("romfs_filecacheread failed: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Copy the partial sector into the user buffer */
+
+ bytesread = rm->rm_hwsectorsize - sectorndx;
+ if (bytesread > buflen)
+ {
+ /* We will not read to the end of the buffer */
+
+ bytesread = buflen;
+ }
+ else
+ {
+ /* We will read to the end of the buffer (or beyond) */
+
+ sector++;
+ }
+
+ fvdbg("Return %d bytes from sector offset %d\n", bytesread, sectorndx);
+ memcpy(userbuffer, &rf->rf_buffer[sectorndx], bytesread);
+ }
+
+ /* Set up for the next sector read */
+
+ userbuffer += bytesread;
+ filep->f_pos += bytesread;
+ readsize += bytesread;
+ buflen -= bytesread;
+ }
+
+ romfs_semgive(rm);
+ return readsize;
+
+errout_with_semaphore:
+ romfs_semgive(rm);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: romfs_seek
+ ****************************************************************************/
+
+static off_t romfs_seek(FAR struct file *filep, off_t offset, int whence)
+{
+ struct romfs_mountpt_s *rm;
+ struct romfs_file_s *rf;
+ off_t position;
+ int ret;
+
+ fvdbg("Seek to offset: %d whence: %d\n", offset, whence);
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ rf = filep->f_priv;
+ rm = filep->f_inode->i_private;
+
+ DEBUGASSERT(rm != NULL);
+
+ /* Map the offset according to the whence option */
+
+ switch (whence)
+ {
+ case SEEK_SET: /* The offset is set to offset bytes. */
+ position = offset;
+ break;
+
+ case SEEK_CUR: /* The offset is set to its current location plus
+ * offset bytes. */
+
+ position = offset + filep->f_pos;
+ break;
+
+ case SEEK_END: /* The offset is set to the size of the file plus
+ * offset bytes. */
+
+ position = offset + rf->rf_size;
+ break;
+
+ default:
+ fdbg("Whence is invalid: %d\n", whence);
+ return -EINVAL;
+ }
+
+ /* Make sure that the mount is still healthy */
+
+ romfs_semtake(rm);
+ ret = romfs_checkmount(rm);
+ if (ret != OK)
+ {
+ fdbg("romfs_checkmount failed: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Limit positions to the end of the file. */
+
+ if (position > rf->rf_size)
+ {
+ /* Otherwise, the position is limited to the file size */
+
+ position = rf->rf_size;
+ }
+
+ /* Set file position and return success */
+
+ filep->f_pos = position;
+ fvdbg("New file position: %d\n", filep->f_pos);
+
+ romfs_semgive(rm);
+ return OK;
+
+errout_with_semaphore:
+ romfs_semgive(rm);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: romfs_ioctl
+ ****************************************************************************/
+
+static int romfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ struct romfs_mountpt_s *rm;
+ struct romfs_file_s *rf;
+ FAR void **ppv = (FAR void**)arg;
+
+ fvdbg("cmd: %d arg: %08lx\n", cmd, arg);
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ rf = filep->f_priv;
+ rm = filep->f_inode->i_private;
+
+ DEBUGASSERT(rm != NULL);
+
+ /* Only one ioctl command is supported */
+
+ if (cmd == FIOC_MMAP && rm->rm_xipbase && ppv)
+ {
+ /* Return the address on the media corresponding to the start of
+ * the file.
+ */
+
+ *ppv = (void*)(rm->rm_xipbase + rf->rf_startoffset);
+ return OK;
+ }
+
+ fdbg("Invalid cmd: %d \n", cmd);
+ return -ENOTTY;
+}
+
+/****************************************************************************
+ * Name: romfs_opendir
+ *
+ * Description:
+ * Open a directory for read access
+ *
+ ****************************************************************************/
+
+static int romfs_opendir(struct inode *mountpt, const char *relpath,
+ struct fs_dirent_s *dir)
+{
+ struct romfs_mountpt_s *rm;
+ struct romfs_dirinfo_s dirinfo;
+ int ret;
+
+ fvdbg("relpath: '%s'\n", relpath);
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Recover our private data from the inode instance */
+
+ rm = mountpt->i_private;
+
+ /* Make sure that the mount is still healthy */
+
+ romfs_semtake(rm);
+ ret = romfs_checkmount(rm);
+ if (ret != OK)
+ {
+ fdbg("romfs_checkmount failed: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Find the requested directory */
+
+ ret = romfs_finddirentry(rm, &dirinfo, relpath);
+ if (ret < 0)
+ {
+ fdbg("Failed to find directory '%s': %d\n", relpath, ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Verify that it is some kind of directory */
+
+ if (!IS_DIRECTORY(dirinfo.rd_next))
+ {
+ /* The entry is not a directory */
+
+ fdbg("'%s' is not a directory: %d\n", relpath);
+ ret = -ENOTDIR;
+ goto errout_with_semaphore;
+ }
+
+ /* The entry is a directory */
+
+ memcpy(&dir->u.romfs, &dirinfo.rd_dir, sizeof(struct fs_romfsdir_s));
+ romfs_semgive(rm);
+ return OK;
+
+errout_with_semaphore:
+ romfs_semgive(rm);
+ return ERROR;
+}
+
+/****************************************************************************
+ * Name: romfs_readdir
+ *
+ * Description: Read the next directory entry
+ *
+ ****************************************************************************/
+
+static int romfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
+{
+ struct romfs_mountpt_s *rm;
+ uint32_t linkoffset;
+ uint32_t next;
+ uint32_t info;
+ uint32_t size;
+ int ret;
+
+ fvdbg("Entry\n");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Recover our private data from the inode instance */
+
+ rm = mountpt->i_private;
+
+ /* Make sure that the mount is still healthy */
+
+ romfs_semtake(rm);
+ ret = romfs_checkmount(rm);
+ if (ret != OK)
+ {
+ fdbg("romfs_checkmount failed: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Loop, skipping over unsupported items in the file system */
+
+ for (;;)
+ {
+ /* Have we reached the end of the directory */
+
+ if (!dir->u.romfs.fr_curroffset)
+ {
+ /* We signal the end of the directory by returning the
+ * special error -ENOENT
+ */
+
+ fdbg("End of directory\n");
+ ret = -ENOENT;
+ goto errout_with_semaphore;
+ }
+
+ /* Parse the directory entry */
+
+ ret = romfs_parsedirentry(rm, dir->u.romfs.fr_curroffset, &linkoffset,
+ &next, &info, &size);
+ if (ret < 0)
+ {
+ fdbg("romfs_parsedirentry failed: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Save the filename */
+
+ ret = romfs_parsefilename(rm, dir->u.romfs.fr_curroffset, dir->fd_dir.d_name);
+ if (ret < 0)
+ {
+ fdbg("romfs_parsefilename failed: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Set up the next directory entry offset */
+
+ dir->u.romfs.fr_curroffset = next & RFNEXT_OFFSETMASK;
+
+ /* Check the file type */
+
+ if (IS_DIRECTORY(next))
+ {
+ dir->fd_dir.d_type = DTYPE_DIRECTORY;
+ break;
+ }
+ else if (IS_FILE(next))
+ {
+ dir->fd_dir.d_type = DTYPE_FILE;
+ break;
+ }
+ }
+
+errout_with_semaphore:
+ romfs_semgive(rm);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: romfs_rewindir
+ *
+ * Description: Reset directory read to the first entry
+ *
+ ****************************************************************************/
+
+static int romfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir)
+{
+ struct romfs_mountpt_s *rm;
+ int ret;
+
+ fvdbg("Entry\n");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Recover our private data from the inode instance */
+
+ rm = mountpt->i_private;
+
+ /* Make sure that the mount is still healthy */
+
+ romfs_semtake(rm);
+ ret = romfs_checkmount(rm);
+ if (ret == OK)
+ {
+ dir->u.romfs.fr_curroffset = dir->u.romfs.fr_firstoffset;
+ }
+
+ romfs_semgive(rm);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: romfs_bind
+ *
+ * Description: This implements a portion of the mount operation. This
+ * function allocates and initializes the mountpoint private data and
+ * binds the blockdriver inode to the filesystem private data. The final
+ * binding of the private data (containing the blockdriver) to the
+ * mountpoint is performed by mount().
+ *
+ ****************************************************************************/
+
+static int romfs_bind(FAR struct inode *blkdriver, const void *data,
+ void **handle)
+{
+ struct romfs_mountpt_s *rm;
+ int ret;
+
+ fvdbg("Entry\n");
+
+ /* Open the block driver */
+
+ if (!blkdriver || !blkdriver->u.i_bops)
+ {
+ fdbg("No block driver/ops\n");
+ return -ENODEV;
+ }
+
+ if (blkdriver->u.i_bops->open &&
+ blkdriver->u.i_bops->open(blkdriver) != OK)
+ {
+ fdbg("No open method\n");
+ return -ENODEV;
+ }
+
+ /* Create an instance of the mountpt state structure */
+
+ rm = (struct romfs_mountpt_s *)zalloc(sizeof(struct romfs_mountpt_s));
+ if (!rm)
+ {
+ fdbg("Failed to allocate mountpoint structure\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize the allocated mountpt state structure. The filesystem is
+ * responsible for one reference ont the blkdriver inode and does not
+ * have to addref() here (but does have to release in ubind().
+ */
+
+ sem_init(&rm->rm_sem, 0, 0); /* Initialize the semaphore that controls access */
+ rm->rm_blkdriver = blkdriver; /* Save the block driver reference */
+
+ /* Get the hardware configuration and setup buffering appropriately */
+
+ ret = romfs_hwconfigure(rm);
+ if (ret < 0)
+ {
+ fdbg("romfs_hwconfigure failed: %d\n", ret);
+ goto errout_with_sem;
+ }
+
+ /* Then complete the mount by getting the ROMFS configuratrion from
+ * the ROMF header
+ */
+
+ ret = romfs_fsconfigure(rm);
+ if (ret < 0)
+ {
+ fdbg("romfs_fsconfigure failed: %d\n", ret);
+ goto errout_with_buffer;
+ }
+
+ /* Mounted! */
+
+ *handle = (void*)rm;
+ romfs_semgive(rm);
+ return OK;
+
+errout_with_buffer:
+ if (!rm->rm_xipbase)
+ {
+ free(rm->rm_buffer);
+ }
+
+errout_with_sem:
+ sem_destroy(&rm->rm_sem);
+ free(rm);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: romfs_unbind
+ *
+ * Description: This implements the filesystem portion of the umount
+ * operation.
+ *
+ ****************************************************************************/
+
+static int romfs_unbind(void *handle, FAR struct inode **blkdriver)
+{
+ struct romfs_mountpt_s *rm = (struct romfs_mountpt_s*)handle;
+ int ret;
+
+ fvdbg("Entry\n");
+
+#ifdef CONFIG_DEBUG
+ if (!rm)
+ {
+ return -EINVAL;
+ }
+#endif
+
+ /* Check if there are sill any files opened on the filesystem. */
+
+ romfs_semtake(rm);
+ if (rm->rm_head)
+ {
+ /* We cannot unmount now.. there are open files */
+
+ fdbg("There are open files\n");
+ ret = -EBUSY;
+ }
+ else
+ {
+ /* Unmount ... close the block driver */
+
+ if (rm->rm_blkdriver)
+ {
+ struct inode *inode = rm->rm_blkdriver;
+ if (inode)
+ {
+ if (inode->u.i_bops && inode->u.i_bops->close)
+ {
+ (void)inode->u.i_bops->close(inode);
+ }
+
+ /* We hold a reference to the block driver but should
+ * not but mucking with inodes in this context. So, we will just return
+ * our contained reference to the block driver inode and let the umount
+ * logic dispose of it.
+ */
+
+ if (blkdriver)
+ {
+ *blkdriver = inode;
+ }
+ }
+ }
+
+ /* Release the mountpoint private data */
+
+ if (!rm->rm_xipbase && rm->rm_buffer)
+ {
+ free(rm->rm_buffer);
+ }
+
+ sem_destroy(&rm->rm_sem);
+ free(rm);
+ return OK;
+ }
+
+ romfs_semgive(rm);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: romfs_statfs
+ *
+ * Description: Return filesystem statistics
+ *
+ ****************************************************************************/
+
+static int romfs_statfs(struct inode *mountpt, struct statfs *buf)
+{
+ struct romfs_mountpt_s *rm;
+ int ret;
+
+ fvdbg("Entry\n");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ rm = mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ romfs_semtake(rm);
+ ret = romfs_checkmount(rm);
+ if (ret < 0)
+ {
+ fdbg("romfs_checkmount failed: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Fill in the statfs info */
+
+ memset(buf, 0, sizeof(struct statfs));
+ buf->f_type = ROMFS_MAGIC;
+
+ /* We will claim that the optimal transfer size is the size of one sector */
+
+ buf->f_bsize = rm->rm_hwsectorsize;
+
+ /* Everything else follows in units of sectors */
+
+ buf->f_blocks = SEC_NSECTORS(rm, rm->rm_volsize + SEC_NDXMASK(rm));
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_namelen = NAME_MAX;
+
+ romfs_semgive(rm);
+ return OK;
+
+errout_with_semaphore:
+ romfs_semgive(rm);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: romfs_stat
+ *
+ * Description: Return information about a file or directory
+ *
+ ****************************************************************************/
+
+static int romfs_stat(struct inode *mountpt, const char *relpath, struct stat *buf)
+{
+ struct romfs_mountpt_s *rm;
+ struct romfs_dirinfo_s dirinfo;
+ int ret;
+
+ fvdbg("Entry\n");
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ rm = mountpt->i_private;
+
+ /* Check if the mount is still healthy */
+
+ romfs_semtake(rm);
+ ret = romfs_checkmount(rm);
+ if (ret != OK)
+ {
+ fdbg("romfs_checkmount failed: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Find the directory entry corresponding to relpath. */
+
+ ret = romfs_finddirentry(rm, &dirinfo, relpath);
+
+ /* If nothing was found, then we fail with EEXIST */
+
+ if (ret < 0)
+ {
+ fvdbg("Failed to find directory: %d\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ memset(buf, 0, sizeof(struct stat));
+ if (IS_DIRECTORY(dirinfo.rd_next))
+ {
+ /* It's a read-only directory name */
+
+ buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
+ if (IS_EXECUTABLE(dirinfo.rd_next))
+ {
+ buf->st_mode |= S_IXOTH|S_IXGRP|S_IXUSR;
+ }
+ }
+ else if (IS_FILE(dirinfo.rd_next))
+ {
+ /* It's a read-only file name */
+
+ buf->st_mode = S_IFREG|S_IROTH|S_IRGRP|S_IRUSR;
+ if (IS_EXECUTABLE(dirinfo.rd_next))
+ {
+ buf->st_mode |= S_IXOTH|S_IXGRP|S_IXUSR;
+ }
+ }
+ else
+ {
+ /* Otherwise, pretend like the unsupported node does not exist */
+
+ fvdbg("Unsupported inode: %d\n", dirinfo.rd_next);
+ ret = -ENOENT;
+ goto errout_with_semaphore;
+ }
+
+ /* File/directory size, access block size */
+
+ buf->st_size = dirinfo.rd_size;
+ buf->st_blksize = rm->rm_hwsectorsize;
+ buf->st_blocks = (buf->st_size + buf->st_blksize - 1) / buf->st_blksize;
+
+ ret = OK;
+
+errout_with_semaphore:
+ romfs_semgive(rm);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
diff --git a/nuttx/fs/romfs/fs_romfs.h b/nuttx/fs/romfs/fs_romfs.h
new file mode 100644
index 000000000..4081517fb
--- /dev/null
+++ b/nuttx/fs/romfs/fs_romfs.h
@@ -0,0 +1,231 @@
+/****************************************************************************
+ * fs/romfs/fs_romfs.h
+ *
+ * Copyright (C) 2008-2009, 2011 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __FS_ROMFS_FS_ROMFS_H
+#define __FS_ROMFS_FS_ROMFS_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <nuttx/fs/dirent.h>
+
+#include "../fs_internal.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/* Volume header (multi-byte values are big-endian) */
+
+#define ROMFS_VHDR_ROM1FS 0 /* 0-7: "-rom1fs-" */
+#define ROMFS_VHDR_SIZE 8 /* 8-11: Number of accessible bytes in this fs. */
+#define ROMFS_VHDR_CHKSUM 12 /* 12-15: Checksum of the first 512 bytes. */
+#define ROMFS_VHDR_VOLNAME 16 /* 16-..: Zero terminated volume name, padded to
+ * 16 byte boundary. */
+
+#define ROMFS_VHDR_MAGIC "-rom1fs-"
+
+/* File header offset (multi-byte values are big-endian) */
+
+#define ROMFS_FHDR_NEXT 0 /* 0-3: Offset of the next file header
+ * (zero if no more files) */
+#define ROMFS_FHDR_INFO 4 /* 4-7: Info for directories/hard links/
+ * devices */
+#define ROMFS_FHDR_SIZE 8 /* 8-11: Size of this file in bytes */
+#define ROMFS_FHDR_CHKSUM 12 /* 12-15: Checksum covering the meta data,
+ * including the file name, and
+ * padding. */
+#define ROMFS_FHDR_NAME 16 /* 16-..: Zero terminated volume name, padded
+ * to 16 byte boundary. */
+
+/* Bits 0-3 of the rf_next offset provide mode information. These are the
+ * values specified in */
+
+#define RFNEXT_MODEMASK 7 /* Bits 0-2: Mode; bit 3: Executable */
+#define RFNEXT_ALLMODEMASK 15 /* Bits 0-3: All mode bits */
+#define RFNEXT_OFFSETMASK (~15) /* Bits n-3: Offset to next entry */
+
+#define RFNEXT_HARDLINK 0 /* rf_info = Link destination file header */
+#define RFNEXT_DIRECTORY 1 /* rf_info = First file's header */
+#define RFNEXT_FILE 2 /* rf_info = Unused, must be zero */
+#define RFNEXT_SOFTLINK 3 /* rf_info = Unused, must be zero */
+#define RFNEXT_BLOCKDEV 4 /* rf_info = 16/16 bits major/minor number */
+#define RFNEXT_CHARDEV 5 /* rf_info = 16/16 bits major/minor number */
+#define RFNEXT_SOCKET 6 /* rf_info = Unused, must be zero */
+#define RFNEXT_FIFO 7 /* rf_info = Unused, must be zero */
+#define RFNEXT_EXEC 8 /* Modifier of RFNEXT_DIRECTORY and RFNEXT_FILE */
+
+#define IS_MODE(rfn,mode) ((((uint32_t)(rfn))&RFNEXT_MODEMASK)==(mode))
+#define IS_HARDLINK(rfn) IS_MODE(rfn,RFNEXT_HARDLINK)
+#define IS_DIRECTORY(rfn) IS_MODE(rfn,RFNEXT_DIRECTORY)
+#define IS_FILE(rfn) IS_MODE(rfn,RFNEXT_FILE)
+#define IS_EXECUTABLE(rfn) (((rfn) & RFNEXT_EXEC) != 0)
+
+/* RFNEXT_SOFTLINK, RFNEXT_BLOCKDEV, RFNEXT_CHARDEV, RFNEXT_SOCKET, and
+ * RFNEXT_FIFO are not presently supported in NuttX.
+ */
+
+/* Alignment macros */
+
+#define ROMFS_ALIGNMENT 16
+#define ROMFS_MAXPADDING (ROMFS_ALIGNMENT-1)
+#define ROMFS_ALIGNMASK (~ROMFS_MAXPADDING)
+#define ROMFS_ALIGNUP(addr) ((((uint32_t)(addr))+ROMFS_MAXPADDING)&ROMFS_ALIGNMASK)
+#define ROMFS_ALIGNDOWN(addr) (((uint32_t)(addr))&ROMFS_ALIGNMASK)
+
+/* Offset and sector conversions */
+
+#define SEC_NDXMASK(r) ((r)->rm_hwsectorsize - 1)
+#define SEC_NSECTORS(r,o) ((o) / (r)->rm_hwsectorsize)
+#define SEC_ALIGN(r,o) ((o) & ~SEC_NDXMASK(r))
+
+/* Maximum numbr of links that will be followed before we decide that there
+ * is a problem.
+ */
+
+#define ROMF_MAX_LINKS 64
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* This structure represents the overall mountpoint state. An instance of this
+ * structure is retained as inode private data on each mountpoint that is
+ * mounted with a fat32 filesystem.
+ */
+
+struct romfs_file_s;
+struct romfs_mountpt_s
+{
+ struct inode *rm_blkdriver; /* The block driver inode that hosts the FAT32 fs */
+ struct romfs_file_s *rm_head; /* A list to all files opened on this mountpoint */
+
+ bool rm_mounted; /* true: The file system is ready */
+ uint16_t rm_hwsectorsize; /* HW: Sector size reported by block driver*/
+ sem_t rm_sem; /* Used to assume thread-safe access */
+ uint32_t rm_rootoffset; /* Saved offset to the first root directory entry */
+ uint32_t rm_hwnsectors; /* HW: The number of sectors reported by the hardware */
+ uint32_t rm_volsize; /* Size of the ROMFS volume */
+ uint32_t rm_cachesector; /* Current sector in the rm_buffer */
+ uint8_t *rm_xipbase; /* Base address of directly accessible media */
+ uint8_t *rm_buffer; /* Device sector buffer, allocated if rm_xipbase==0 */
+};
+
+/* This structure represents on open file under the mountpoint. An instance
+ * of this structure is retained as struct file specific information on each
+ * opened file.
+ */
+
+struct romfs_file_s
+{
+ struct romfs_file_s *rf_next; /* Retained in a singly linked list */
+ bool rf_open; /* true: The file is (still) open */
+ uint32_t rf_startoffset; /* Offset to the start of the file data */
+ uint32_t rf_size; /* Size of the file in bytes */
+ uint32_t rf_cachesector; /* Current sector in the rf_buffer */
+ uint8_t *rf_buffer; /* File sector buffer, allocated if rm_xipbase==0 */
+};
+
+/* This structure is used internally for describing the result of
+ * walking a path
+ */
+
+struct romfs_dirinfo_s
+{
+ /* These values describe the directory containing the terminal
+ * path component (of the terminal component itself if it is
+ * a directory.
+ */
+
+ struct fs_romfsdir_s rd_dir; /* Describes directory. */
+
+ /* Values from the ROMFS file entry */
+
+ uint32_t rd_next; /* Offset of the next file header+flags */
+ uint32_t rd_size; /* Size (if file) */
+};
+
+/****************************************************************************
+ * Global Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C" {
+#else
+#define EXTERN extern
+#endif
+
+EXTERN void romfs_semtake(struct romfs_mountpt_s *rm);
+EXTERN void romfs_semgive(struct romfs_mountpt_s *rm);
+EXTERN int romfs_hwread(struct romfs_mountpt_s *rm, uint8_t *buffer,
+ uint32_t sector, unsigned int nsectors);
+EXTERN int romfs_filecacheread(struct romfs_mountpt_s *rm,
+ struct romfs_file_s *rf, uint32_t sector);
+EXTERN int romfs_hwconfigure(struct romfs_mountpt_s *rm);
+EXTERN int romfs_fsconfigure(struct romfs_mountpt_s *rm);
+EXTERN int romfs_fileconfigure(struct romfs_mountpt_s *rm,
+ struct romfs_file_s *rf);
+EXTERN int romfs_checkmount(struct romfs_mountpt_s *rm);
+EXTERN int romfs_finddirentry(struct romfs_mountpt_s *rm,
+ struct romfs_dirinfo_s *dirinfo,
+ const char *path);
+EXTERN int romfs_parsedirentry(struct romfs_mountpt_s *rm,
+ uint32_t offset, uint32_t *poffset, uint32_t *pnext,
+ uint32_t *pinfo, uint32_t *psize);
+EXTERN int romfs_parsefilename(struct romfs_mountpt_s *rm, uint32_t offset,
+ char *pname);
+EXTERN int romfs_datastart(struct romfs_mountpt_s *rm, uint32_t offset,
+ uint32_t *start);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __FS_ROMFS_FS_ROMFS_H */
diff --git a/nuttx/fs/romfs/fs_romfsutil.c b/nuttx/fs/romfs/fs_romfsutil.c
new file mode 100644
index 000000000..6ea114b5e
--- /dev/null
+++ b/nuttx/fs/romfs/fs_romfsutil.c
@@ -0,0 +1,979 @@
+/****************************************************************************
+ * rm/romfs/fs_romfsutil.h
+ *
+ * Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * References: Linux/Documentation/filesystems/romfs.txt
+ *
+ * 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 <stdint.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/ioctl.h>
+#include <nuttx/fs/dirent.h>
+
+#include "fs_romfs.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: romfs_swap32
+ *
+ * Desciption:
+ * Convert the 32-bit big endian value to little endian
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_ENDIAN_BIG
+static inline uint32_t romfs_swap32(uint32_t value)
+{
+ return ((((value) & 0x000000ff) << 24) | (((value) & 0x0000ff00) << 8) |
+ (((value) & 0x00ff0000) >> 8) | (((value) & 0xff000000) >> 24));
+}
+#endif
+
+/****************************************************************************
+ * Name: romfs_devread32
+ *
+ * Desciption:
+ * Read the big-endian 32-bit value from the mount device buffer
+ *
+ * Assumption:
+ * All values are aligned to 32-bit boundaries
+ *
+ ****************************************************************************/
+
+static uint32_t romfs_devread32(struct romfs_mountpt_s *rm, int ndx)
+{
+ /* Extract the value */
+
+ uint32_t value = *(uint32_t*)&rm->rm_buffer[ndx];
+
+ /* Value is begin endian -- return the native host endian-ness. */
+#ifdef CONFIG_ENDIAN_BIG
+ return value;
+#else
+ return romfs_swap32(value);
+#endif
+}
+
+/****************************************************************************
+ * Name: romfs_checkentry
+ *
+ * Desciption:
+ * Check if the entry at offset is a directory or file path segment
+ *
+ ****************************************************************************/
+
+static inline int romfs_checkentry(struct romfs_mountpt_s *rm, uint32_t offset,
+ const char *entryname, int entrylen,
+ struct romfs_dirinfo_s *dirinfo)
+{
+ char name[NAME_MAX+1];
+ uint32_t linkoffset;
+ uint32_t next;
+ uint32_t info;
+ uint32_t size;
+ int ret;
+
+ /* Parse the directory entry at this offset (which may be re-directed
+ * to some other entry if HARLINKED).
+ */
+
+ ret = romfs_parsedirentry(rm, offset, &linkoffset, &next, &info, &size);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Now we are pointing to the real entry of interest. Is it a
+ * directory? Or a file?
+ */
+
+ if (IS_DIRECTORY(next) || IS_FILE(next))
+ {
+ /* Get the name of the directory entry. */
+
+ ret = romfs_parsefilename(rm, offset, name);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Then check if this the name segment we are looking for. The
+ * string comparison is awkward because there is no terminator
+ * on entryname (there is a terminator on name, however)
+ */
+
+ if (memcmp(entryname, name, entrylen) == 0 &&
+ strlen(name) == entrylen)
+ {
+ /* Found it -- save the component info and return success */
+
+ if (IS_DIRECTORY(next))
+ {
+ dirinfo->rd_dir.fr_firstoffset = info;
+ dirinfo->rd_dir.fr_curroffset = info;
+ dirinfo->rd_size = 0;
+ }
+ else
+ {
+ dirinfo->rd_dir.fr_curroffset = offset;
+ dirinfo->rd_size = size;
+ }
+ dirinfo->rd_next = next;
+ return OK;
+ }
+ }
+
+ /* The entry is not a directory or it does not have the matching name */
+
+ return -ENOENT;
+}
+
+/****************************************************************************
+ * Name: romfs_devcacheread
+ *
+ * Desciption:
+ * Read the specified sector for specified offset into the sector cache.
+ * Return the index into the sector corresponding to the offset
+ *
+ ****************************************************************************/
+
+int16_t romfs_devcacheread(struct romfs_mountpt_s *rm, uint32_t offset)
+{
+ uint32_t sector;
+ int ret;
+
+ /* rm->rm_cachesector holds the current sector that is buffer in or referenced
+ * by rm->tm_buffer. If the requested sector is the same as this sector,
+ * then we do nothing.
+ */
+
+ sector = SEC_NSECTORS(rm, offset);
+ if (rm->rm_cachesector != sector)
+ {
+ /* Check the access mode */
+
+ if (rm->rm_xipbase)
+ {
+ /* In XIP mode, rf_buffer is just an offset pointer into the device
+ * address space.
+ */
+
+ rm->rm_buffer = rm->rm_xipbase + SEC_ALIGN(rm, offset);
+ }
+ else
+ {
+ /* In non-XIP mode, we will have to read the new sector.*/
+
+ ret = romfs_hwread(rm, rm->rm_buffer, sector, 1);
+ if (ret < 0)
+ {
+ return (int16_t)ret;
+ }
+ }
+
+ /* Update the cached sector number */
+
+ rm->rm_cachesector = sector;
+ }
+
+ /* Return the offset */
+
+ return offset & SEC_NDXMASK(rm);
+}
+
+/****************************************************************************
+ * Name: romfs_followhardlinks
+ *
+ * Desciption:
+ * Given the offset to a file header, check if the file is a hardlink.
+ * If so, traverse the hard links until the terminal, non-linked header
+ * so found and return that offset.
+ *
+ ****************************************************************************/
+
+static int romfs_followhardlinks(struct romfs_mountpt_s *rm, uint32_t offset,
+ uint32_t *poffset)
+{
+ uint32_t next;
+ int16_t ndx;
+ int i;
+
+ /* Loop while we are redirected by hardlinks */
+
+ for (i = 0; i < ROMF_MAX_LINKS; i++)
+ {
+ /* Read the sector containing the offset into memory */
+
+ ndx = romfs_devcacheread(rm, offset);
+ if (ndx < 0)
+ {
+ return ndx;
+ }
+
+ /* Check if this is a hard link */
+
+ next = romfs_devread32(rm, ndx + ROMFS_FHDR_NEXT);
+ if (!IS_HARDLINK(next))
+ {
+ *poffset = offset;
+ return OK;
+ }
+
+ /* Follow the hard-link */
+
+ offset = romfs_devread32(rm, ndx + ROMFS_FHDR_INFO);
+ }
+
+ return -ELOOP;
+}
+
+/****************************************************************************
+ * Name: romfs_searchdir
+ *
+ * Desciption:
+ * This is part of the romfs_finddirentry log. Search the directory
+ * beginning at dirinfo->fr_firstoffset for entryname.
+ *
+ ****************************************************************************/
+
+static inline int romfs_searchdir(struct romfs_mountpt_s *rm,
+ const char *entryname, int entrylen,
+ struct romfs_dirinfo_s *dirinfo)
+{
+ uint32_t offset;
+ uint32_t next;
+ int16_t ndx;
+ int ret;
+
+ /* Then loop through the current directory until the directory
+ * with the matching name is found. Or until all of the entries
+ * the directory have been examined.
+ */
+
+ offset = dirinfo->rd_dir.fr_firstoffset;
+ do
+ {
+ /* Read the sector into memory (do this before calling
+ * romfs_checkentry() so we won't have to read the sector
+ * twice in the event that the offset refers to a hardlink).
+ */
+
+ ndx = romfs_devcacheread(rm, offset);
+ if (ndx < 0)
+ {
+ return ndx;
+ }
+
+ /* Because everything is chunked and aligned to 16-bit boundaries,
+ * we know that most the basic node info fits into the sector.
+ */
+
+ next = romfs_devread32(rm, ndx + ROMFS_FHDR_NEXT) & RFNEXT_OFFSETMASK;
+
+ /* Check if the name this entry is a directory with the matching
+ * name
+ */
+
+ ret = romfs_checkentry(rm, offset, entryname, entrylen, dirinfo);
+ if (ret == OK)
+ {
+ /* Its a match! Return success */
+
+ return OK;
+ }
+
+ /* No match... select the offset to the next entry */
+
+ offset = next;
+ }
+ while (next != 0);
+
+ /* There is nothing in this directory with that name */
+
+ return -ENOENT;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: romfs_semtake
+ ****************************************************************************/
+
+void romfs_semtake(struct romfs_mountpt_s *rm)
+{
+ /* Take the semaphore (perhaps waiting) */
+
+ while (sem_wait(&rm->rm_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: romfs_semgive
+ ****************************************************************************/
+
+void romfs_semgive(struct romfs_mountpt_s *rm)
+{
+ sem_post(&rm->rm_sem);
+}
+
+/****************************************************************************
+ * Name: romfs_hwread
+ *
+ * Desciption: Read the specified sector into the sector buffer
+ *
+ ****************************************************************************/
+
+int romfs_hwread(struct romfs_mountpt_s *rm, uint8_t *buffer, uint32_t sector,
+ unsigned int nsectors)
+{
+ int ret = -ENODEV;
+
+ /* Check the access mode */
+
+ if (rm->rm_xipbase)
+ {
+ /* In XIP mode, we just copy the requested data */
+
+ memcpy(buffer,
+ rm->rm_xipbase + sector*rm->rm_hwsectorsize,
+ nsectors*rm->rm_hwsectorsize);
+ ret = OK;
+ }
+ else
+ {
+ /* In non-XIP mode, we have to read the data from the device */
+
+ struct inode *inode = rm->rm_blkdriver;
+ ssize_t nsectorsread;
+
+ DEBUGASSERT(inode);
+ if (inode->u.i_bops && inode->u.i_bops->read)
+ {
+ nsectorsread =
+ inode->u.i_bops->read(inode, buffer, sector, nsectors);
+
+ if (nsectorsread == (ssize_t)nsectors)
+ {
+ ret = OK;
+ }
+ else if (nsectorsread < 0)
+ {
+ ret = nsectorsread;
+ }
+ }
+ }
+ return ret;
+}
+
+/****************************************************************************
+ * Name: romfs_filecacheread
+ *
+ * Desciption:
+ * Read the specified sector into the sector cache
+ *
+ ****************************************************************************/
+
+int romfs_filecacheread(struct romfs_mountpt_s *rm, struct romfs_file_s *rf, uint32_t sector)
+{
+ int ret;
+
+ fvdbg("sector: %d cached: %d sectorsize: %d XIP base: %p buffer: %p\n",
+ sector, rf->rf_cachesector, rm->rm_hwsectorsize,
+ rm->rm_xipbase, rf->rf_buffer);
+
+ /* rf->rf_cachesector holds the current sector that is buffer in or referenced
+ * by rf->rf_buffer. If the requested sector is the same as this sector,
+ * then we do nothing.
+ */
+
+ if (rf->rf_cachesector != sector)
+ {
+ /* Check the access mode */
+
+ if (rm->rm_xipbase)
+ {
+ /* In XIP mode, rf_buffer is just an offset pointer into the device
+ * address space.
+ */
+
+ rf->rf_buffer = rm->rm_xipbase + sector * rm->rm_hwsectorsize;
+ fvdbg("XIP buffer: %p\n", rf->rf_buffer);
+ }
+ else
+ {
+ /* In non-XIP mode, we will have to read the new sector.*/
+
+ fvdbg("Calling romfs_hwread\n");
+ ret = romfs_hwread(rm, rf->rf_buffer, sector, 1);
+ if (ret < 0)
+ {
+ fdbg("romfs_hwread failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* Update the cached sector number */
+
+ rf->rf_cachesector = sector;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: romfs_hwconfigure
+ *
+ * Desciption:
+ * This function is called as part of the ROMFS mount operation It
+ * configures the ROMFS filestem for use on this block driver. This includes
+ * the accounting for the geometry of the device, setting up any XIP modes
+ * of operation, and/or allocating any cache buffers.
+ *
+ ****************************************************************************/
+
+int romfs_hwconfigure(struct romfs_mountpt_s *rm)
+{
+ struct inode *inode = rm->rm_blkdriver;
+ struct geometry geo;
+ int ret;
+
+ /* Get the underlying device geometry */
+
+#ifdef CONFIG_DEBUG
+ if (!inode || !inode->u.i_bops || !inode->u.i_bops->geometry)
+ {
+ return -ENODEV;
+ }
+#endif
+
+ ret = inode->u.i_bops->geometry(inode, &geo);
+ if (ret != OK)
+ {
+ return ret;
+ }
+
+ if (!geo.geo_available)
+ {
+ return -EBUSY;
+ }
+
+ /* Save that information in the mount structure */
+
+ rm->rm_hwsectorsize = geo.geo_sectorsize;
+ rm->rm_hwnsectors = geo.geo_nsectors;
+
+ /* Determine if block driver supports the XIP mode of operation */
+
+ rm->rm_cachesector = (uint32_t)-1;
+
+ if (inode->u.i_bops->ioctl)
+ {
+ ret = inode->u.i_bops->ioctl(inode, BIOC_XIPBASE,
+ (unsigned long)&rm->rm_xipbase);
+ if (ret == OK && rm->rm_xipbase)
+ {
+ /* Yes.. Then we will directly access the media (vs.
+ * copying into an allocated sector buffer.
+ */
+
+ rm->rm_buffer = rm->rm_xipbase;
+ rm->rm_cachesector = 0;
+ return OK;
+ }
+ }
+
+ /* Allocate the device cache buffer for normal sector accesses */
+
+ rm->rm_buffer = (uint8_t*)malloc(rm->rm_hwsectorsize);
+ if (!rm->rm_buffer)
+ {
+ return -ENOMEM;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: romfs_fsconfigure
+ *
+ * Desciption:
+ * This function is called as part of the ROMFS mount operation It
+ * sets up the mount structure to include configuration information contained
+ * in the ROMFS header. This is the place where we actually determine if
+ * the media contains a ROMFS filesystem.
+ *
+ ****************************************************************************/
+
+int romfs_fsconfigure(struct romfs_mountpt_s *rm)
+{
+ const char *name;
+ int16_t ndx;
+
+ /* Then get information about the ROMFS filesystem on the devices managed
+ * by this block driver. Read sector zero which contains the volume header.
+ */
+
+ ndx = romfs_devcacheread(rm, 0);
+ if (ndx < 0)
+ {
+ return ndx;
+ }
+
+ /* Verify the magic number at that identifies this as a ROMFS filesystem */
+
+ if (memcmp(rm->rm_buffer, ROMFS_VHDR_MAGIC, 8) != 0)
+ {
+ return -EINVAL;
+ }
+
+ /* Then extract the values we need from the header and return success */
+
+ rm->rm_volsize = romfs_devread32(rm, ROMFS_VHDR_SIZE);
+
+ /* The root directory entry begins right after the header */
+
+ name = (const char*)&rm->rm_buffer[ROMFS_VHDR_VOLNAME];
+ rm->rm_rootoffset = ROMFS_ALIGNUP(ROMFS_VHDR_VOLNAME + strlen(name) + 1);
+
+ /* and return success */
+
+ rm->rm_mounted = true;
+ return OK;
+}
+
+/****************************************************************************
+ * Name: romfs_ffileconfigure
+ *
+ * Desciption:
+ * This function is called as part of the ROMFS file open operation It
+ * sets up the file structure to handle buffer appropriately, depending
+ * upon XIP mode or not.
+ *
+ ****************************************************************************/
+
+int romfs_fileconfigure(struct romfs_mountpt_s *rm, struct romfs_file_s *rf)
+{
+ /* Check if XIP access mode is supported. If so, then we do not need
+ * to allocate anything.
+ */
+
+ if (rm->rm_xipbase)
+ {
+ /* We'll put a valid address in rf_buffer just in case. */
+
+ rf->rf_cachesector = 0;
+ rf->rf_buffer = rm->rm_xipbase;
+ }
+ else
+ {
+ /* Nothing in the cache buffer */
+
+ rf->rf_cachesector = (uint32_t)-1;
+
+ /* Create a file buffer to support partial sector accesses */
+
+ rf->rf_buffer = (uint8_t*)malloc(rm->rm_hwsectorsize);
+ if (!rf->rf_buffer)
+ {
+ return -ENOMEM;
+ }
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: romfs_checkmount
+ *
+ * Desciption: Check if the mountpoint is still valid.
+ *
+ * The caller should hold the mountpoint semaphore
+ *
+ ****************************************************************************/
+
+int romfs_checkmount(struct romfs_mountpt_s *rm)
+{
+ struct romfs_file_s *file;
+ struct inode *inode;
+ struct geometry geo;
+ int ret;
+
+ /* If the fs_mounted flag is false, then we have already handled the loss
+ * of the mount.
+ */
+
+ DEBUGASSERT(rm && rm->rm_blkdriver);
+ if (rm->rm_mounted)
+ {
+ /* We still think the mount is healthy. Check an see if this is
+ * still the case
+ */
+
+ inode = rm->rm_blkdriver;
+ if (inode->u.i_bops && inode->u.i_bops->geometry)
+ {
+ ret = inode->u.i_bops->geometry(inode, &geo);
+ if (ret == OK && geo.geo_available && !geo.geo_mediachanged)
+ {
+ return OK;
+ }
+ }
+
+ /* If we get here, the mount is NOT healthy */
+
+ rm->rm_mounted = false;
+
+ /* Make sure that this is flagged in every opened file */
+
+ for (file = rm->rm_head; file; file = file->rf_next)
+ {
+ file->rf_open = false;
+ }
+ }
+ return -ENODEV;
+}
+
+/****************************************************************************
+ * Name: romfs_finddirentry
+ *
+ * Desciption:
+ * Given a path to something that may or may not be in the file system,
+ * return the directory entry of the item.
+ *
+ ****************************************************************************/
+
+int romfs_finddirentry(struct romfs_mountpt_s *rm, struct romfs_dirinfo_s *dirinfo,
+ const char *path)
+{
+ const char *entryname;
+ const char *terminator;
+ int entrylen;
+ int ret;
+
+ /* Start with the first element after the root directory */
+
+ dirinfo->rd_dir.fr_firstoffset = rm->rm_rootoffset;
+ dirinfo->rd_dir.fr_curroffset = rm->rm_rootoffset;
+ dirinfo->rd_next = RFNEXT_DIRECTORY;
+ dirinfo->rd_size = 0;
+
+ /* The root directory is a special case */
+
+ if (!path || path[0] == '\0')
+ {
+ return OK;
+ }
+
+ /* Then loop for each directory/file component in the full path */
+
+ entryname = path;
+ terminator = NULL;
+
+ for (;;)
+ {
+ /* Find the start of the next path component */
+
+ while (*entryname == '/') entryname++;
+
+ /* Find the end of the next path component */
+
+ terminator = strchr(entryname, '/');
+ if (!terminator)
+ {
+ entrylen = strlen(entryname);
+ }
+ else
+ {
+ entrylen = terminator - entryname;
+ }
+
+ /* Long path segment names will be truncated to NAME_MAX */
+
+ if (entrylen > NAME_MAX)
+ {
+ entrylen = NAME_MAX;
+ }
+
+ /* Then find the entry in the current directory with the
+ * matching name.
+ */
+
+ ret = romfs_searchdir(rm, entryname, entrylen, dirinfo);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Was that the last path component? */
+
+ if (!terminator)
+ {
+ /* Yes.. return success */
+
+ return OK;
+ }
+
+ /* No... If that was not the last path component, then it had
+ * better have been a directory
+ */
+
+ if (!IS_DIRECTORY(dirinfo->rd_next))
+ {
+ return -ENOTDIR;
+ }
+
+ /* Setup to search the next directory for the next component
+ * of the path
+ */
+
+ entryname = terminator;
+ }
+
+ return ERROR; /* Won't get here */
+}
+
+/****************************************************************************
+ * Name: romfs_parsedirentry
+ *
+ * Desciption:
+ * Return the directory entry at this offset. If rf is NULL, then the
+ * mount device resources are used. Otherwise, file resources are used.
+ *
+ ****************************************************************************/
+
+int romfs_parsedirentry(struct romfs_mountpt_s *rm, uint32_t offset, uint32_t *poffset,
+ uint32_t *pnext, uint32_t *pinfo, uint32_t *psize)
+{
+ uint32_t save;
+ uint32_t next;
+ int16_t ndx;
+ int ret;
+
+ /* Read the sector into memory */
+
+ ndx = romfs_devcacheread(rm, offset);
+ if (ndx < 0)
+ {
+ return ndx;
+ }
+
+ /* Yes.. Save the first 'next' value. That has the offset needed to
+ * traverse the parent directory. But we may need to change the type
+ * after we follow the hard links.
+ */
+
+ save = romfs_devread32(rm, ndx + ROMFS_FHDR_NEXT);
+
+ /* Traverse hardlinks as necesssary to get to the real file header */
+
+ ret = romfs_followhardlinks(rm, offset, poffset);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Because everything is chunked and aligned to 16-bit boundaries,
+ * we know that most the basic node info fits into the sector. The
+ * associated name may not, however.
+ */
+
+ next = romfs_devread32(rm, ndx + ROMFS_FHDR_NEXT);
+ *pnext = (save & RFNEXT_OFFSETMASK) | (next & RFNEXT_ALLMODEMASK);
+ *pinfo = romfs_devread32(rm, ndx + ROMFS_FHDR_INFO);
+ *psize = romfs_devread32(rm, ndx + ROMFS_FHDR_SIZE);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: romfs_parsefilename
+ *
+ * Desciption:
+ * Return the filename from directory entry at this offset
+ *
+ ****************************************************************************/
+
+int romfs_parsefilename(struct romfs_mountpt_s *rm, uint32_t offset, char *pname)
+{
+ int16_t ndx;
+ uint16_t namelen;
+ uint16_t chunklen;
+ bool done;
+
+ /* Loop until the whole name is obtained or until NAME_MAX characters
+ * of the name have been parsed.
+ */
+
+ offset += ROMFS_FHDR_NAME;
+ for (namelen = 0, done = false; namelen < NAME_MAX && !done;)
+ {
+ /* Read the sector into memory */
+
+ ndx = romfs_devcacheread(rm, offset);
+ if (ndx < 0)
+ {
+ return ndx;
+ }
+
+ /* Is the name terminated in this 16-byte block */
+
+ if (rm->rm_buffer[ndx + 15] == '\0')
+ {
+ /* Yes.. then this chunk is less than 16 */
+
+ chunklen = strlen((char*)&rm->rm_buffer[ndx]);
+ done = true;
+ }
+ else
+ {
+ /* No.. then this chunk is 16 bytes in length */
+
+ chunklen = 16;
+ }
+
+ /* Check if we would exceed the NAME_MAX */
+
+ if (namelen + chunklen > NAME_MAX)
+ {
+ chunklen = NAME_MAX - namelen;
+ done = true;
+ }
+
+ /* Copy the chunk */
+
+ memcpy(&pname[namelen], &rm->rm_buffer[ndx], chunklen);
+ namelen += chunklen;
+ }
+
+ /* Terminate the name (NAME_MAX+1 chars total) and return success */
+
+ pname[namelen] = '\0';
+ return OK;
+}
+
+/****************************************************************************
+ * Name: romfs_datastart
+ *
+ * Desciption:
+ * Given the offset to a file header, return the offset to the start of
+ * the file data
+ *
+ ****************************************************************************/
+
+int romfs_datastart(struct romfs_mountpt_s *rm, uint32_t offset, uint32_t *start)
+{
+ int16_t ndx;
+ int ret;
+
+ /* Traverse hardlinks as necesssary to get to the real file header */
+
+ ret = romfs_followhardlinks(rm, offset, &offset);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Loop until the header size is obtained. */
+
+ offset += ROMFS_FHDR_NAME;
+ for (;;)
+ {
+ /* Read the sector into memory */
+
+ ndx = romfs_devcacheread(rm, offset);
+ if (ndx < 0)
+ {
+ return ndx;
+ }
+
+ /* Get the offset to the next chunk */
+
+ offset += 16;
+ if (offset >= rm->rm_volsize)
+ {
+ return -EIO;
+ }
+
+ /* Is the name terminated in this 16-byte block */
+
+ if (rm->rm_buffer[ndx + 15] == '\0')
+ {
+ /* Yes.. then the data starts at the next chunk */
+
+ *start = offset;
+ return OK;
+ }
+ }
+
+ return -EINVAL; /* Won't get here */
+}
+