diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2012-09-17 18:18:44 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2012-09-17 18:18:44 +0000 |
commit | 57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff (patch) | |
tree | 25d07d14e920d31c0b1947c9ca586f2a01fc32d8 /nuttx/fs | |
download | px4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.tar.gz px4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.tar.bz2 px4-firmware-57623d42ebb04f0a0b9e6eb7c0847a3ece2aa0ff.zip |
Resync new repository with old repo r5166
git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5153 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'nuttx/fs')
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 */ +} + |