summaryrefslogtreecommitdiff
path: root/nuttx/fs/smartfs
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-04-30 20:13:30 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-04-30 20:13:30 -0600
commit2fd7dfe98ac64e201c22e8cb07bab75433dd6b60 (patch)
tree16be537427845b002de41ca5e212d0859779a04e /nuttx/fs/smartfs
parent68dd7cdd0c0e72d1ea1dcf484feab17c9a74f5c7 (diff)
downloadnuttx-2fd7dfe98ac64e201c22e8cb07bab75433dd6b60.tar.gz
nuttx-2fd7dfe98ac64e201c22e8cb07bab75433dd6b60.tar.bz2
nuttx-2fd7dfe98ac64e201c22e8cb07bab75433dd6b60.zip
Add Ken Pettit's SMART FS
Diffstat (limited to 'nuttx/fs/smartfs')
-rw-r--r--nuttx/fs/smartfs/Kconfig53
-rw-r--r--nuttx/fs/smartfs/Make.defs54
-rw-r--r--nuttx/fs/smartfs/smartfs.h353
-rw-r--r--nuttx/fs/smartfs/smartfs_mksmartfs.c191
-rw-r--r--nuttx/fs/smartfs/smartfs_smart.c1981
-rw-r--r--nuttx/fs/smartfs/smartfs_utils.c1293
6 files changed, 3925 insertions, 0 deletions
diff --git a/nuttx/fs/smartfs/Kconfig b/nuttx/fs/smartfs/Kconfig
new file mode 100644
index 000000000..b082092e3
--- /dev/null
+++ b/nuttx/fs/smartfs/Kconfig
@@ -0,0 +1,53 @@
+#
+# For a description of the syntax of this configuration file,
+# see misc/tools/kconfig-language.txt.
+#
+
+config FS_SMARTFS
+ bool "SMART file system"
+ default n
+ depends on !DISABLE_MOUNTPOINT
+ ---help---
+ Enable NuttX SMART Flash file system (SMARTFS) support.
+
+if FS_SMARTFS
+config SMARTFS_ERASEDSTATE
+ hex "FLASH erased state"
+ default 0xff
+ ---help---
+ The erased state of FLASH.
+ This must have one of the values of 0xff or 0x00.
+ Default: 0xff.
+
+config SMARTFS_MAXNAMLEN
+ int "Maximum file name length"
+ default 16
+ ---help---
+ The maximum size of a SMARTFS file name at a given
+ directory level. Overall path name is not restricted
+ by this value, only individual segments in a path,
+ such as (assuming /usr is the mount point):
+
+ /usr/games/chess/player_stats/game1.xml
+
+ Default: 16.
+
+config SMARTFS_MULTI_ROOT_DIRS
+ bool "Support multiple Root Directories / Mount Points"
+ default n
+ ---help---
+ Enables support for multiple root directory entries
+ on the SMART FLASH. Multiple root directories means
+ the device can have multiple mount point in the VFS,
+ each with it's own unique directory structure under it.
+
+ When this option is enabled, instead of reporting the
+ SMART block devices as "/dev/mtdsmart0", they will be
+ reported as "/dev/mtdsmart0d1", "/dev/mtdsmart0d2", etc.
+ The number of root entries actually created is set
+ during the "mksmartfs" command when the device is
+ initialized.
+
+ Default: y.
+
+endif
diff --git a/nuttx/fs/smartfs/Make.defs b/nuttx/fs/smartfs/Make.defs
new file mode 100644
index 000000000..9604d546f
--- /dev/null
+++ b/nuttx/fs/smartfs/Make.defs
@@ -0,0 +1,54 @@
+############################################################################
+# Make.defs
+#
+# Copyright (C) 2008, 2011, 2013 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_SMARTFS),y)
+
+# Files required for SmartFS file system support
+
+ASRCS +=
+CSRCS += smartfs_smart.c smartfs_utils.c
+
+# Files required for mksmartfs utility function
+
+ASRCS +=
+CSRCS += smartfs_mksmartfs.c
+
+# Include SMART build support
+
+DEPPATH += --dep-path smartfs
+VPATH += :smartfs
+CFLAGS += ${shell $(INCDIR) $(INCDIROPT) "$(CC)" $(TOPDIR)$(DELIM)fs$(DELIM)smartfs}
+
+endif
diff --git a/nuttx/fs/smartfs/smartfs.h b/nuttx/fs/smartfs/smartfs.h
new file mode 100644
index 000000000..d1d3928c4
--- /dev/null
+++ b/nuttx/fs/smartfs/smartfs.h
@@ -0,0 +1,353 @@
+/****************************************************************************
+ * fs/smartfs/smartfs.h
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * 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_SMARTFS_SMARTFS_H
+#define __FS_SMARTFS_SMARTFS_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/smart.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* SMART Definitions ********************************************************/
+/* General SMART organization. The following example assumes 4 logical
+ * sectors per FLASH erase block. The actual relationship is determined by
+ * the FLASH geometry reported by the MTD driver.
+ *
+ * ERASE LOGICAL Sectors begin with a sector header. Sectors may
+ * BLOCK SECTOR CONTENTS be marked as "released," pending garbage collection
+ * n 4*n --+---------------+
+ * Hdr |LLLLLLLLLLLLLLL| Logical sector number (2 bytes)
+ * |QQQQQQQQQQQQQQQ| Sequence number (2 bytes)
+ * |SSSSSSSSSSSSSSS| Status bits (1 byte)
+ * +---------------+
+ * |TTTTTTTTTTTTTTT| Sector Type (dir or file) (1 byte)
+ * |NNNNNNNNNNNNNNN| Number of next logical sector in chain
+ * |UUUUUUUUUUUUUUU| Number of bytes used in this sector
+ * | |
+ * | (Sector Data) |
+ * | |
+ * 4*n+1 --+---------------+
+ * Sector Hdr |LLLLLLLLLLLLLLL| Logical sector number (2 bytes)
+ * |QQQQQQQQQQQQQQQ| Sequence number (2 bytes)
+ * |SSSSSSSSSSSSSSS| Status bits (1 byte)
+ * +---------------+
+ * FS Hdr |TTTTTTTTTTTTTTT| Sector Type (dir or file) (1 byte)
+ * |NNNNNNNNNNNNNNN| Number of next logical sector in chain
+ * |UUUUUUUUUUUUUUU| Number of bytes used in this sector
+ * | |
+ * | (Sector Data) |
+ * | |
+ * 4*n+2 --+---------------+
+ * Sector Hdr |LLLLLLLLLLLLLLL| Logical sector number (2 bytes)
+ * |QQQQQQQQQQQQQQQ| Sequence number (2 bytes)
+ * |SSSSSSSSSSSSSSS| Status bits (1 byte)
+ * +---------------+
+ * FS Hdr |TTTTTTTTTTTTTTT| Sector Type (dir or file) (1 byte)
+ * |NNNNNNNNNNNNNNN| Number of next logical sector in chain
+ * |UUUUUUUUUUUUUUU| Number of bytes used in this sector
+ * | |
+ * | (Sector Data) |
+ * | |
+ * 4*n+3 --+---------------+
+ * Sector Hdr |LLLLLLLLLLLLLLL| Logical sector number (2 bytes)
+ * |QQQQQQQQQQQQQQQ| Sequence number (2 bytes)
+ * |SSSSSSSSSSSSSSS| Status bits (1 byte)
+ * +---------------+
+ * FS Hdr |TTTTTTTTTTTTTTT| Sector Type (dir or file) (1 byte)
+ * |NNNNNNNNNNNNNNN| Number of next logical sector in chain
+ * |UUUUUUUUUUUUUUU| Number of bytes used in this sector
+ * | |
+ * | (Sector Data) |
+ * | |
+ * n+1 4*(n+1) --+---------------+
+ * Sector Hdr |LLLLLLLLLLLLLLL| Logical sector number (2 bytes)
+ * |QQQQQQQQQQQQQQQ| Sequence number (2 bytes)
+ * |SSSSSSSSSSSSSSS| Status bits (1 byte)
+ * +---------------+
+ * FS Hdr |TTTTTTTTTTTTTTT| Sector Type (dir or file) (1 byte)
+ * |NNNNNNNNNNNNNNN| Number of next logical sector in chain
+ * |UUUUUUUUUUUUUUU| Number of bytes used in this sector
+ * | |
+ * | |
+ * | |
+ * --+---------------+
+ *
+ * General operation:
+ * Physical sectors are allocated and assigned a logical sector number
+ * and a starting sequence number of zero.
+ *
+ * SECTOR HEADER:
+ * The sector header (first 5 bytes) tracks the state of each sector and
+ * is used by the SMART MTD block driver. At the block level, there is
+ * no notion of sector chaining, only allocated sectors within erase
+ * blocks.
+ *
+ * FILE SYSTEM (FS) HEADER:
+ * The file system header (next 5 bytes) tracks file and directory entries
+ * and chains.
+ *
+ * SMART Limitations:
+ * 1. SMART currently depends on the underlying MTD block driver supporting
+ * single-byte programming operation. This is due to the method it
+ * uses for marking a sector as "released", committed, etc.
+ * 2. Garbage collection can occur when a new sector is allocated or when
+ * existing sector data is overwritten with new data. Thus, occasionally,
+ * file writing may take longer than other times.
+ * 3. The implementation curently does not track bad blocks on the device.
+ * 4. There is no true wear-leveling implemented yet, though provesion have
+ * been made to reserve logical sectors to allow it to be added using
+ * a "sector aging" tracking mechanism.
+ */
+
+/* Values for SMART inode state.
+ *
+ * SMART_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)
+
+/* Directory entry flag definitions */
+
+#define SMARTFS_DIRENT_EMPTY 0x8000 /* Set to non-erase state when entry used */
+#define SMARTFS_DIRENT_ACTIVE 0x4000 /* Set to erase state when entry is active */
+#define SMARTFS_DIRENT_TYPE 0x2000 /* Indicates the type of entry (file/dir) */
+#define SMARTFS_DIRENT_DELETING 0x1000 /* Directory entry is being deleted */
+#define SMARTFS_DIRENT_RESERVED 0x0E00 /* Reserved bits */
+#define SMARTFS_DIRENT_MODE 0x01FF /* Mode the file was created with */
+
+#define SMARTFS_DIRENT_TYPE_DIR 0x2000
+#define SMARTFS_DIRENT_TYPE_FILE 0x0000
+
+/* Number of bytes in the SMART magic sequences */
+
+#define SMART_MAGICSIZE 4
+
+/* 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
+
+/* Underlying MTD Block driver access functions */
+
+#define FS_BOPS(f) (f)->fs_blkdriver->u.i_bops
+#define FS_IOCTL(f,c,a) (FS_BOPS(f)->ioctl ? FS_BOPS(f)->ioctl((f)->fs_blkdriver,c,a) : (-ENOSYS))
+
+/* The logical sector number of the root directory. */
+
+#define SMARTFS_ROOT_DIR_SECTOR 3
+
+/* Defines the sector types */
+
+#define SMARTFS_SECTOR_TYPE_DIR 1
+#define SMARTFS_SECTOR_TYPE_FILE 2
+
+#ifndef CONFIG_SMARTFS_DIRDEPTH
+#define CONFIG_SMARTFS_DIRDEPTH 8
+#endif
+
+#define SMARTFS_ERASEDSTATE_16BIT (uint16_t) ((CONFIG_SMARTFS_ERASEDSTATE << 8) | \
+ CONFIG_SMARTFS_ERASEDSTATE)
+
+#ifndef offsetof
+#define offsetof(type, member) ( (size_t) &( ( (type *) 0)->member))
+#endif
+
+#define SMARTFS_NEXTSECTOR(h) ( *((uint16_t *) h->nextsector))
+#define SMARTFS_USED(h) ( *((uint16_t *) h->used))
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* This structure defines each packed block on the FLASH media */
+
+/* This is an in-memory representation of the SMART inode as extracted from
+ * FLASH and with additional state information.
+ */
+
+struct smartfs_entry_s
+{
+ uint16_t firstsector; /* Sector number of the name */
+ uint16_t dsector; /* Sector number of the directory entry */
+ uint16_t doffset; /* Offset of the directory entry */
+ uint16_t dfirst; /* 1st sector number of the directory entry */
+ uint16_t flags; /* Flags, including mode */
+ FAR char *name; /* inode name */
+ uint32_t utc; /* Time stamp */
+ uint32_t datlen; /* Length of inode data */
+};
+
+/* This is an on-device representation of the SMART inode it esists on
+ * the FLASH.
+ */
+
+struct smartfs_entry_header_s
+{
+ uint16_t flags; /* Flags, including permissions:
+ 15: Empty entry
+ 14: Active entry
+ 12-0: Permissions bits */
+ int16_t firstsector; /* Sector number of the name */
+ uint32_t utc; /* Time stamp */
+ char name[0]; /* inode name */
+};
+
+/* This structure describes the smartfs header at the start of each
+ * sector. It manages the sector chain and used bytes in the sector.
+ */
+
+struct smartfs_chain_header_s
+{
+ uint8_t type; /* Type of sector entry (file or dir) */
+ uint8_t nextsector[2];/* Next logical sector in the chain */
+ uint8_t used[2]; /* Number of bytes used in this sector */
+};
+
+/* This structure describes the state of one open file. This structure
+ * is protected by the volume semaphore.
+ */
+
+struct smartfs_ofile_s
+{
+ struct smartfs_ofile_s *fnext; /* Supports a singly linked list */
+ int16_t crefs; /* Reference count */
+ mode_t oflags; /* Open mode */
+ struct smartfs_entry_s entry; /* Describes the SMARTFS inode entry */
+ size_t filepos; /* Current file position */
+ uint16_t currsector; /* Current sector of filepos */
+ uint16_t curroffset; /* Current offset in sector */
+ uint16_t byteswritten;/* Count of bytes written to currsector
+ * that have not been recorded in the
+ * sector yet. We delay updating the
+ * used field until the file is closed,
+ * a seek, or more data is written that
+ * causes the sector to change. */
+};
+
+/* 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 smartfs filesystem.
+ */
+
+struct smartfs_mountpt_s
+{
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ struct smartfs_mountpt_s *fs_next; /* Pointer to next SMART filesystem */
+#endif
+ FAR struct inode *fs_blkdriver; /* Our underlying block device */
+ sem_t *fs_sem; /* Used to assure thread-safe access */
+ FAR struct smartfs_ofile_s *fs_head; /* A singly-linked list of open files */
+ bool fs_mounted; /* true: The file system is ready */
+ struct smart_format_s fs_llformat; /* Low level device format info */
+ char *fs_rwbuffer; /* Read/Write working buffer */
+ char *fs_workbuffer;/* Working buffer */
+ uint8_t fs_rootsector;/* Root directory sector num */
+};
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Internal function prototypes
+ ****************************************************************************/
+
+/* Semaphore access for internal use */
+
+void smartfs_semtake(struct smartfs_mountpt_s *fs);
+void smartfs_semgive(struct smartfs_mountpt_s *fs);
+
+/* Forward references for utility functions */
+
+struct smartfs_mountpt_s;
+
+/* Utility functions */
+
+int smartfs_mount(struct smartfs_mountpt_s *fs, bool writeable);
+
+int smartfs_unmount(struct smartfs_mountpt_s *fs);
+
+int smartfs_finddirentry(struct smartfs_mountpt_s *fs,
+ struct smartfs_entry_s *direntry, const char *relpath,
+ uint16_t *parentdirsector, const char **filename);
+
+int smartfs_createentry(struct smartfs_mountpt_s *fs,
+ uint16_t parentdirsector, const char* filename,
+ uint16_t type,
+ mode_t mode, struct smartfs_entry_s *direntry,
+ uint16_t sectorno);
+
+int smartfs_deleteentry(struct smartfs_mountpt_s *fs,
+ struct smartfs_entry_s *entry);
+
+int smartfs_countdirentries(struct smartfs_mountpt_s *fs,
+ struct smartfs_entry_s *entry);
+
+int smartfs_truncatefile(struct smartfs_mountpt_s *fs,
+ struct smartfs_entry_s *entry);
+
+struct file; /* Forward references */
+struct inode;
+struct fs_dirent_s;
+struct statfs;
+struct stat;
+
+#endif /* __FS_SMARTFS_SMARTFS_H */
diff --git a/nuttx/fs/smartfs/smartfs_mksmartfs.c b/nuttx/fs/smartfs/smartfs_mksmartfs.c
new file mode 100644
index 000000000..9c2dacc7e
--- /dev/null
+++ b/nuttx/fs/smartfs/smartfs_mksmartfs.c
@@ -0,0 +1,191 @@
+/****************************************************************************
+ * fs/smartfs/smartfs_mksmartfs.c
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * 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/kmalloc.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/mksmartfs.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/smart.h>
+
+#include "smartfs.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: mksmartfs
+ *
+ * Description:
+ * Make a SMART Flash file system image on the specified block device
+ *
+ * Inputs:
+ * pathname - the full path to a registered block driver
+ * nrootdirs - Number of root directory entries to create.
+ *
+ * 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).
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+int mksmartfs(FAR const char *pathname, uint8_t nrootdirs)
+#else
+int mksmartfs(FAR const char *pathname)
+#endif
+{
+ struct inode* inode;
+ struct smart_format_s fmt;
+ int ret;
+ int x;
+ uint8_t type;
+ struct smart_read_write_s request;
+
+ /* Find the inode of the block driver indentified by 'source' */
+
+ ret = open_blockdriver(pathname, 0, &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 (!inode->u.i_bops->write || !inode->u.i_bops->geometry)
+ {
+ fdbg("%s does not support write or geometry methods\n", pathname);
+ ret = -EACCES;
+ goto errout_with_driver;
+ }
+
+ /* Validate the block device is a SMART device */
+
+ /* Perform a low-level SMART format */
+
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ ret = inode->u.i_bops->ioctl(inode, BIOC_LLFORMAT, nrootdirs);
+#else
+ ret = inode->u.i_bops->ioctl(inode, BIOC_LLFORMAT, 0);
+#endif
+ if (ret != OK)
+ {
+ fdbg("Error creating low-level format: %d\n", ret);
+ goto errout_with_driver;
+ }
+
+ /* Get the format information so we know how big the sectors are */
+
+ ret = inode->u.i_bops->ioctl(inode, BIOC_GETFORMAT, (unsigned long) &fmt);
+
+ /* Now Write the filesystem to media. Loop for each root dir entry and
+ * allocate the reserved Root Dir Enty, then write a blank root dir for it.
+ */
+
+ type = SMARTFS_SECTOR_TYPE_DIR;
+ request.offset = 0;
+ request.count = 1;
+ request.buffer = &type;
+ x = 0;
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ for (; x < nrootdirs; x++)
+#endif
+ {
+ ret = inode->u.i_bops->ioctl(inode, BIOC_ALLOCSECT, SMARTFS_ROOT_DIR_SECTOR + x);
+ if (ret != SMARTFS_ROOT_DIR_SECTOR + x)
+ {
+ ret = -EIO;
+ goto errout_with_driver;
+ }
+
+ /* Mark this block as a directory entry */
+
+ request.logsector = SMARTFS_ROOT_DIR_SECTOR + x;
+
+ /* Issue a write to the sector, single byte */
+
+ ret = inode->u.i_bops->ioctl(inode, BIOC_WRITESECT, (unsigned long) &request);
+ if (ret != 0)
+ {
+ ret = -EIO;
+ goto errout_with_driver;
+ }
+ }
+
+errout_with_driver:
+ /* Close the driver */
+
+ (void)close_blockdriver(inode);
+
+errout:
+ /* Release all allocated memory */
+
+ /* Return any reported errors */
+
+ if (ret < 0)
+ {
+ errno = -ret;
+ return ERROR;
+ }
+
+ return OK;
+}
diff --git a/nuttx/fs/smartfs/smartfs_smart.c b/nuttx/fs/smartfs/smartfs_smart.c
new file mode 100644
index 000000000..552c0e96e
--- /dev/null
+++ b/nuttx/fs/smartfs/smartfs_smart.c
@@ -0,0 +1,1981 @@
+/****************************************************************************
+ * fs/smartfs/smartfs_smart.c
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * 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 <nuttx/fs/ioctl.h>
+#include <nuttx/mtd.h>
+#include <nuttx/smart.h>
+
+#include "smartfs.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int smartfs_open(FAR struct file *filep, const char *relpath,
+ int oflags, mode_t mode);
+static int smartfs_close(FAR struct file *filep);
+static ssize_t smartfs_read(FAR struct file *filep, char *buffer, size_t buflen);
+static ssize_t smartfs_write(FAR struct file *filep, const char *buffer,
+ size_t buflen);
+static off_t smartfs_seek(FAR struct file *filep, off_t offset, int whence);
+static int smartfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
+
+static int smartfs_sync(FAR struct file *filep);
+static int smartfs_dup(FAR const struct file *oldp, FAR struct file *newp);
+
+static int smartfs_opendir(struct inode *mountpt, const char *relpath,
+ struct fs_dirent_s *dir);
+static int smartfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir);
+static int smartfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir);
+
+static int smartfs_bind(FAR struct inode *blkdriver, const void *data,
+ void **handle);
+static int smartfs_unbind(void *handle, FAR struct inode **blkdriver);
+static int smartfs_statfs(struct inode *mountpt, struct statfs *buf);
+
+static int smartfs_unlink(struct inode *mountpt, const char *relpath);
+static int smartfs_mkdir(struct inode *mountpt, const char *relpath,
+ mode_t mode);
+static int smartfs_rmdir(struct inode *mountpt, const char *relpath);
+static int smartfs_rename(struct inode *mountpt, const char *oldrelpath,
+ const char *newrelpath);
+static int smartfs_stat(struct inode *mountpt, const char *relpath, struct stat *buf);
+
+static off_t smartfs_seek_internal(struct smartfs_mountpt_s *fs,
+ struct smartfs_ofile_s *sf,
+ off_t offset, int whence);
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+static uint8_t g_seminitialized = FALSE;
+static sem_t g_sem;
+
+/****************************************************************************
+ * 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 smartfs_operations =
+{
+ smartfs_open, /* open */
+ smartfs_close, /* close */
+ smartfs_read, /* read */
+ smartfs_write, /* write */
+ smartfs_seek, /* seek */
+ smartfs_ioctl, /* ioctl */
+
+ smartfs_sync, /* sync */
+ smartfs_dup, /* dup */
+
+ smartfs_opendir, /* opendir */
+ NULL, /* closedir */
+ smartfs_readdir, /* readdir */
+ smartfs_rewinddir, /* rewinddir */
+
+ smartfs_bind, /* bind */
+ smartfs_unbind, /* unbind */
+ smartfs_statfs, /* statfs */
+
+ smartfs_unlink, /* unlinke */
+ smartfs_mkdir, /* mkdir */
+ smartfs_rmdir, /* rmdir */
+ smartfs_rename, /* rename */
+ smartfs_stat /* stat */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: smartfs_open
+ ****************************************************************************/
+
+static int smartfs_open(FAR struct file *filep, const char *relpath,
+ int oflags, mode_t mode)
+{
+ struct inode *inode;
+ struct smartfs_mountpt_s *fs;
+ int ret;
+ uint16_t parentdirsector;
+ const char *filename;
+ struct smartfs_ofile_s *sf;
+
+ /* 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);
+
+ /* Take the semaphore */
+
+ smartfs_semtake(fs);
+
+ /* Locate the directory entry for this path */
+
+ sf = (struct smartfs_ofile_s *) kmalloc(sizeof *sf);
+ if (sf == NULL)
+ {
+ ret = ENOMEM;
+ goto errout_with_semaphore;
+ }
+
+ sf->entry.name = NULL;
+ ret = smartfs_finddirentry(fs, &sf->entry, relpath, &parentdirsector,
+ &filename);
+
+ /* 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)
+ {
+ /* The name exists -- but is is a file or a directory ? */
+
+ if (sf->entry.flags & SMARTFS_DIRENT_TYPE_DIR)
+ {
+ /* Can't open a dir as a file! */
+
+ ret = EISDIR;
+ goto errout_with_buffer;
+ }
+
+ /* 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_buffer;
+ }
+
+ /* TODO: Test open mode based on the file mode */
+
+ /* The file exists. Check if we are opening it for O_CREAT or
+ * O_TRUNC mode and delete the sector chain if we are. */
+
+ if ((oflags & (O_CREAT | O_TRUNC)) != 0)
+ {
+ /* Don't truncate if open for APPEND */
+
+ if (!(oflags & O_APPEND))
+ {
+ /* Truncate the file as part of the open */
+
+ ret = smartfs_truncatefile(fs, &sf->entry);
+ if (ret < 0)
+ {
+ goto errout_with_buffer;
+ }
+ }
+ }
+ }
+ 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_buffer;
+ }
+
+ /* Yes... test if the parent directory is valid */
+
+ if (parentdirsector != 0xFFFF)
+ {
+ /* We can create in the given parent directory */
+
+ ret = smartfs_createentry(fs, parentdirsector, filename,
+ SMARTFS_DIRENT_TYPE_FILE, mode,
+ &sf->entry, 0xFFFF);
+ if (ret != OK)
+ {
+ goto errout_with_buffer;
+ }
+ }
+ else
+ {
+ /* Trying to create in a directory that doesn't exist */
+
+ ret = -ENOENT;
+ goto errout_with_buffer;
+ }
+ }
+ else
+ {
+ goto errout_with_buffer;
+ }
+
+ /* Now perform the "open" on the file in direntry */
+
+ sf->oflags = oflags;
+ sf->crefs = 1;
+ sf->filepos = 0;
+ sf->curroffset = sizeof(struct smartfs_chain_header_s);
+ sf->currsector = sf->entry.firstsector;
+ sf->byteswritten = 0;
+
+ /* Test if we opened for APPEND mode. If we did, then seek to the
+ * end of the file.
+ */
+
+ if (oflags & O_APPEND)
+ {
+ /* Perform the seek */
+
+ smartfs_seek_internal(fs, sf, 0, SEEK_END);
+ }
+
+ /* Attach the private date to the struct file instance */
+
+ filep->f_priv = sf;
+
+ /* 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).
+ */
+
+ sf->fnext = fs->fs_head;
+ fs->fs_head = sf;
+
+ ret = OK;
+ goto errout_with_semaphore;
+
+errout_with_buffer:
+ if (sf->entry.name != NULL)
+ {
+ /* Free the space for the name too */
+
+ kfree(sf->entry.name);
+ sf->entry.name = NULL;
+ }
+
+ kfree(sf);
+
+errout_with_semaphore:
+ smartfs_semgive(fs);
+ if (ret == -EINVAL)
+ {
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_close
+ ****************************************************************************/
+
+static int smartfs_close(FAR struct file *filep)
+{
+ struct inode *inode;
+ struct smartfs_mountpt_s *fs;
+ struct smartfs_ofile_s *sf;
+ struct smartfs_ofile_s *nextfile;
+ struct smartfs_ofile_s *prevfile;
+
+ /* 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;
+ sf = filep->f_priv;
+
+ /* Sync the file */
+
+ smartfs_sync(filep);
+
+ /* Take the semaphore */
+
+ smartfs_semtake(fs);
+
+ /* Check if we are the last one with a reference to the file and
+ * only close if we are. */
+
+ if (sf->crefs > 1)
+ {
+ /* The file is opened more than once. Just decrement the
+ * reference count and return. */
+
+ sf->crefs--;
+ goto okout;
+ }
+
+ /* Remove ourselves from the linked list */
+
+ nextfile = fs->fs_head;
+ while ((nextfile != sf) && (nextfile != NULL))
+ {
+ /* Save the previous file pointer too */
+
+ prevfile = nextfile;
+ nextfile = nextfile->fnext;
+ }
+
+ if (nextfile != NULL)
+ {
+ /* Test if we were the first entry */
+
+ if (nextfile == fs->fs_head)
+ {
+ /* Assign a new head */
+
+ fs->fs_head = nextfile->fnext;
+ }
+ else
+ {
+ /* Take ourselves out of the list */
+
+ prevfile->fnext = nextfile->fnext;
+ }
+ }
+
+ /* Now free the pointer */
+
+ filep->f_priv = NULL;;
+ if (sf->entry.name != NULL)
+ {
+ /* Free the space for the name too */
+
+ kfree(sf->entry.name);
+ sf->entry.name = NULL;
+ }
+ kfree(sf);
+
+okout:
+ smartfs_semgive(fs);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: smartfs_read
+ ****************************************************************************/
+
+static ssize_t smartfs_read(FAR struct file *filep, char *buffer, size_t buflen)
+{
+ struct inode *inode;
+ struct smartfs_mountpt_s *fs;
+ struct smartfs_ofile_s *sf;
+ struct smart_read_write_s readwrite;
+ struct smartfs_chain_header_s *header;
+ int ret = OK;
+ uint32_t bytesread;
+ uint16_t bytestoread;
+ uint16_t bytesinsector;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ sf = filep->f_priv;
+ inode = filep->f_inode;
+ fs = inode->i_private;
+
+ DEBUGASSERT(fs != NULL);
+
+ /* Take the semaphore */
+
+ smartfs_semtake(fs);
+
+ /* Loop until all byte read or error */
+
+ bytesread = 0;
+ while (bytesread != buflen)
+ {
+ /* Test if we are at the end of data */
+
+ if (sf->currsector == SMARTFS_ERASEDSTATE_16BIT)
+ {
+ /* Break and return the number of bytes we read (may be zero) */
+
+ break;
+ }
+
+ /* Read the curent sector into our buffer */
+
+ readwrite.logsector = sf->currsector;
+ readwrite.offset = 0;
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ readwrite.count = fs->fs_llformat.availbytes;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d reading sector %d data\n", ret, sf->currsector);
+ goto errout_with_semaphore;
+ }
+
+ /* Point header to the read data to get used byte count */
+
+ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
+
+ /* Get number of used bytes in this sector */
+
+ bytesinsector = *((uint16_t *) header->used);
+ if (bytesinsector == SMARTFS_ERASEDSTATE_16BIT)
+ {
+ /* No bytes to read from this sector */
+
+ bytesinsector = 0;
+ }
+
+ /* Calculate the number of bytes to read into the buffer */
+
+ bytestoread = bytesinsector - (sf->curroffset -
+ sizeof(struct smartfs_chain_header_s));
+ if (bytestoread + bytesread > buflen)
+ {
+ /* Truncate bytesto read based on buffer len */
+
+ bytestoread = buflen - bytesread;
+ }
+
+ /* Copy data to the read buffer */
+
+ if (bytestoread > 0)
+ {
+ /* Do incremental copy from this sector */
+
+ memcpy(&buffer[bytesread], &fs->fs_rwbuffer[sf->curroffset], bytestoread);
+ bytesread += bytestoread;
+ sf->filepos += bytestoread;
+ sf->curroffset += bytestoread;
+ }
+
+ /* Test if we are at the end of the data in this sector */
+
+ if ((bytestoread == 0) || (sf->curroffset == fs->fs_llformat.availbytes))
+ {
+ /* Set the next sector as the current sector */
+
+ sf->currsector = SMARTFS_NEXTSECTOR(header);
+ sf->curroffset = sizeof(struct smartfs_chain_header_s);
+
+ /* Test if at end of data */
+
+ if (sf->currsector == SMARTFS_ERASEDSTATE_16BIT)
+ {
+ /* No more data! Return what we have */
+
+ break;
+ }
+ }
+ }
+
+ /* Return the number of bytes we read */
+
+ ret = bytesread;
+
+errout_with_semaphore:
+ smartfs_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_sync_internal
+ *
+ * Description: Synchronize the file state on disk to match internal, in-
+ * memory state.
+ *
+ ****************************************************************************/
+
+static int smartfs_sync_internal(struct smartfs_mountpt_s *fs,
+ struct smartfs_ofile_s *sf)
+{
+ struct smart_read_write_s readwrite;
+ struct smartfs_chain_header_s *header;
+ int ret;
+
+ /* Test if we have written bytes to the current sector that
+ * need to be recorded in the chain header's used bytes field. */
+
+ if (sf->byteswritten > 0)
+ {
+ fdbg("Syncing sector %d\n", sf->currsector);
+
+ /* Read the existing sector used bytes value */
+
+ readwrite.logsector = sf->currsector;
+ readwrite.offset = 0;
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ readwrite.count = sizeof(struct smartfs_chain_header_s);
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d reading sector %d data\n", ret, sf->currsector);
+ goto errout;
+ }
+
+ /* Add new byteswritten to existing value */
+
+ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
+ if (*((uint16_t *) header->used) == SMARTFS_ERASEDSTATE_16BIT)
+ {
+ *((uint16_t *) header->used) = sf->byteswritten;
+ }
+ else
+ {
+ *((uint16_t *) header->used) += sf->byteswritten;
+ }
+
+ readwrite.offset = offsetof(struct smartfs_chain_header_s, used);
+ readwrite.count = sizeof(uint16_t);
+ readwrite.buffer = (uint8_t *) &fs->fs_rwbuffer[readwrite.offset];
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d writing used bytes for sector %d\n", ret, sf->currsector);
+ goto errout;
+ }
+
+ sf->byteswritten = 0;
+ }
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_write
+ ****************************************************************************/
+
+static ssize_t smartfs_write(FAR struct file *filep, const char *buffer,
+ size_t buflen)
+{
+ struct inode *inode;
+ struct smartfs_mountpt_s *fs;
+ struct smartfs_ofile_s *sf;
+ struct smart_read_write_s readwrite;
+ struct smartfs_chain_header_s *header;
+ size_t byteswritten;
+ int ret;
+
+ /* Sanity checks. I have seen the following assertion misfire if
+ * CONFIG_DEBUG_MM is enabled while re-directing output to a
+ * file. In this case, the debug output can get generated while
+ * the file is being opened, FAT data structures are being allocated,
+ * and things are generally in a perverse state.
+ */
+
+#ifdef CONFIG_DEBUG_MM
+ if (filep->f_priv == NULL || filep->f_inode == NULL)
+ {
+ return -ENXIO;
+ }
+#else
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+#endif
+
+ /* Recover our private data from the struct file instance */
+
+ sf = filep->f_priv;
+ inode = filep->f_inode;
+ fs = inode->i_private;
+
+ DEBUGASSERT(fs != NULL);
+
+ /* Take the semaphore */
+
+ smartfs_semtake(fs);
+
+ /* Test the permissions. Only allow write if the file was opened with
+ * write flags.
+ */
+
+ if ((sf->oflags & O_WROK) == 0)
+ {
+ ret = -EACCES;
+ goto errout_with_semaphore;
+ }
+
+ /* First test if we are overwriting an existing location or writing to
+ * a new one. */
+
+ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
+ byteswritten = 0;
+ while ((sf->filepos < sf->entry.datlen) && (buflen > 0))
+ {
+ /* Overwriting data caused by a seek, etc. In this case, we need
+ * to check if the write causes the file length to be extended
+ * or not and update it accordingly. We will write data up to
+ * the current end-of-file and then break, allowing the next while
+ * loop below to write the additional data to the end of the file.
+ */
+
+ readwrite.offset = sf->curroffset;
+ readwrite.logsector = sf->currsector;
+ readwrite.buffer = (uint8_t *) &buffer[byteswritten];
+ readwrite.count = fs->fs_llformat.availbytes - sf->curroffset;
+
+ /* Limit the write based on available data to write */
+
+ if (readwrite.count > buflen)
+ readwrite.count = buflen;
+
+ /* Limit the write based on current file length */
+
+ if (readwrite.count > sf->entry.datlen - sf->filepos)
+ {
+ /* Limit the write length so we write to the current EOF. */
+
+ readwrite.count = sf->entry.datlen - sf->filepos;
+ }
+
+ /* Now perform the write. */
+
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d writing sector %d data\n", ret, sf->currsector);
+ goto errout_with_semaphore;
+ }
+
+ /* Update our control variables */
+
+ sf->filepos += readwrite.count;
+ sf->curroffset += readwrite.count;
+ buflen -= readwrite.count;
+ byteswritten += readwrite.count;
+
+ /* Test if we wrote to the end of the current sector */
+
+ if (sf->curroffset == fs->fs_llformat.availbytes)
+ {
+ /* Wrote to the end of the sector. Update to point to the
+ * next sector for additional writes. First read the sector
+ * header to get the sector chain info.
+ */
+
+ readwrite.offset = 0;
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ readwrite.count = sizeof(struct smartfs_chain_header_s);
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d reading sector %d header\n", ret, sf->currsector);
+ goto errout_with_semaphore;
+ }
+
+ /* Now get the chained sector info and reset the offset */
+
+ sf->curroffset = sizeof(struct smartfs_chain_header_s);
+ sf->currsector = SMARTFS_NEXTSECTOR(header);
+ }
+ }
+
+ /* Now append data to end of the file. */
+
+ while (buflen > 0)
+ {
+ /* We will fill up the current sector. Write data to
+ * the current sector first.
+ */
+
+ readwrite.offset = sf->curroffset;
+ readwrite.logsector = sf->currsector;
+ readwrite.buffer = (uint8_t *) &buffer[byteswritten];
+ readwrite.count = fs->fs_llformat.availbytes - sf->curroffset;
+ if (readwrite.count > buflen)
+ {
+ /* Limit the write base on remaining bytes to write */
+
+ readwrite.count = buflen;
+ }
+
+ /* Perform the write */
+
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d writing sector %d data\n", ret, sf->currsector);
+ goto errout_with_semaphore;
+ }
+
+ /* Update our control variables */
+
+ sf->entry.datlen += readwrite.count;
+ sf->byteswritten += readwrite.count;
+ sf->filepos += readwrite.count;
+ sf->curroffset += readwrite.count;
+ buflen -= readwrite.count;
+ byteswritten += readwrite.count;
+
+ /* Test if we wrote a full sector of data */
+
+ if (sf->curroffset == fs->fs_llformat.availbytes)
+ {
+ /* Sync the file to write this sector out */
+
+ smartfs_sync_internal(fs, sf);
+
+ /* Allocate a new sector if needed */
+
+ if (buflen > 0)
+ {
+ /* Allocate a new sector */
+
+ ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xFFFF);
+ if (ret < 0)
+ {
+ fdbg("Error %d allocating new sector\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Copy the new sector to the old one and chain it */
+
+ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
+ *((uint16_t *) header->nextsector) = (uint16_t) ret;
+ readwrite.offset = offsetof(struct smartfs_chain_header_s,
+ nextsector);
+ readwrite.buffer = (uint8_t *) header->nextsector;
+ readwrite.count = sizeof(uint16_t);
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d writing next sector\n", ret);
+ goto errout_with_semaphore;
+ }
+
+ /* Record the new sector in our tracking variables and
+ * reset the offset to "zero".
+ */
+
+ sf->currsector = SMARTFS_NEXTSECTOR(header);
+ sf->curroffset = sizeof(struct smartfs_chain_header_s);
+ }
+ }
+ }
+
+ ret = byteswritten;
+
+errout_with_semaphore:
+ smartfs_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_seek_internal
+ *
+ * Description: Performs the logic of the seek function. This is an internal
+ * function because it does not provide semaphore protection and
+ * therefore must be called from one of the other public
+ * interface routines (open, seek, etc.).
+ *
+ ****************************************************************************/
+
+static off_t smartfs_seek_internal(struct smartfs_mountpt_s *fs,
+ struct smartfs_ofile_s *sf,
+ off_t offset, int whence)
+{
+ struct smart_read_write_s readwrite;
+ struct smartfs_chain_header_s *header;
+ int ret;
+ off_t newpos;
+ off_t sectorstartpos;
+
+ /* Test if this is a seek to get the current file pos */
+
+ if ((whence == SEEK_CUR) && (offset == 0))
+ {
+ return sf->filepos;
+ }
+
+ /* Test if we need to sync the file */
+
+ if (sf->byteswritten > 0)
+ {
+ /* Perform a sync */
+
+ smartfs_sync_internal(fs, sf);
+ }
+
+ /* Calculate the file position to seek to based on current position */
+
+ switch (whence)
+ {
+ case SEEK_SET:
+ newpos = offset;
+ break;
+
+ case SEEK_CUR:
+ newpos = sf->filepos + offset;
+ break;
+
+ case SEEK_END:
+ newpos = sf->entry.datlen - offset;
+ break;
+ }
+
+ /* Ensure newpos is in range */
+
+ if (newpos < 0)
+ {
+ newpos = 0;
+ }
+
+ if (newpos > sf->entry.datlen)
+ {
+ newpos = sf->entry.datlen;
+ }
+
+ /* Now perform the seek. Test if we are seeking within the current
+ * sector and can skip the search to save time.
+ */
+
+ sectorstartpos = sf->filepos - (sf->curroffset - sizeof(struct
+ smartfs_chain_header_s));
+ if (newpos >= sectorstartpos && newpos < sectorstartpos +
+ fs->fs_llformat.availbytes - sizeof(struct smartfs_chain_header_s))
+ {
+ /* Seeking within the current sector. Just update the offset */
+
+ sf->curroffset = sizeof(struct smartfs_chain_header_s) + newpos-sectorstartpos;
+ sf->filepos = newpos;
+
+ return newpos;
+ }
+
+ /* Nope, we have to search for the sector and offset. If the new pos is greater
+ * than the current pos, then we can start from the beginning of the current
+ * sector, otherwise we have to start from the beginning of the file.
+ */
+
+ if (newpos > sf->filepos)
+ {
+ sf->filepos = sectorstartpos;
+ }
+ else
+ {
+ sf->currsector = sf->entry.firstsector;
+ sf->filepos = 0;
+ }
+
+ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
+ while ((sf->currsector != SMARTFS_ERASEDSTATE_16BIT) &&
+ (sf->filepos + fs->fs_llformat.availbytes -
+ sizeof(struct smartfs_chain_header_s) < newpos))
+ {
+ /* Read the sector's header */
+
+ readwrite.logsector = sf->currsector;
+ readwrite.offset = 0;
+ readwrite.count = sizeof(struct smartfs_chain_header_s);
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d reading sector %d header\n", ret, sf->currsector);
+ goto errout;
+ }
+
+ /* Point to next sector and update filepos */
+
+ sf->currsector = SMARTFS_NEXTSECTOR(header);
+ sf->filepos += SMARTFS_USED(header);
+ }
+
+ /* Now calculate the offset */
+
+ sf->curroffset = sizeof(struct smartfs_chain_header_s) + newpos - sf->filepos;
+ sf->filepos = newpos;
+ return newpos;
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_seek
+ ****************************************************************************/
+
+static off_t smartfs_seek(FAR struct file *filep, off_t offset, int whence)
+{
+ struct inode *inode;
+ struct smartfs_mountpt_s *fs;
+ struct smartfs_ofile_s *sf;
+ int ret;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ sf = filep->f_priv;
+ inode = filep->f_inode;
+ fs = inode->i_private;
+
+ DEBUGASSERT(fs != NULL);
+
+ /* Take the semaphore */
+
+ smartfs_semtake(fs);
+
+ /* Call our internal routine to perform the seek */
+
+ ret = smartfs_seek_internal(fs, sf, offset, whence);
+
+ if (ret >= 0)
+ {
+ filep->f_pos = ret;
+ }
+
+ smartfs_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_ioctl
+ ****************************************************************************/
+
+static int smartfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
+{
+ /* We don't use any ioctls */
+
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: smartfs_sync
+ *
+ * Description: Synchronize the file state on disk to match internal, in-
+ * memory state.
+ *
+ ****************************************************************************/
+
+static int smartfs_sync(FAR struct file *filep)
+{
+ struct inode *inode;
+ struct smartfs_mountpt_s *fs;
+ struct smartfs_ofile_s *sf;
+ int ret = OK;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ sf = filep->f_priv;
+ inode = filep->f_inode;
+ fs = inode->i_private;
+
+ DEBUGASSERT(fs != NULL);
+
+ /* Take the semaphore */
+
+ smartfs_semtake(fs);
+
+ ret = smartfs_sync_internal(fs, sf);
+
+ smartfs_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smart_dup
+ *
+ * Description: Duplicate open file data in the new file structure.
+ *
+ ****************************************************************************/
+
+static int smartfs_dup(FAR const struct file *oldp, FAR struct file *newp)
+{
+ FAR struct smart_mountpt_s *fs;
+ struct smartfs_ofile_s *sf;
+
+ fvdbg("Dup %p->%p\n", oldp, newp);
+
+ /* Sanity checks */
+
+ DEBUGASSERT(oldp->f_priv != NULL &&
+ newp->f_priv == NULL &&
+ newp->f_inode != NULL);
+
+ /* Recover our private data from the struct file instance */
+
+ fs = (struct smart_mountpt_s *)oldp->f_inode->i_private;
+ sf = oldp->f_priv;
+
+ DEBUGASSERT(fs != NULL);
+ DEBUGASSERT(sf != NULL);
+
+ /* Just increment the reference count on the ofile */
+
+ sf->crefs++;
+ newp->f_priv = (FAR void *)sf;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: smartfs_opendir
+ *
+ * Description: Open a directory for read access
+ *
+ ****************************************************************************/
+
+static int smartfs_opendir(struct inode *mountpt, const char *relpath, struct fs_dirent_s *dir)
+{
+ struct smartfs_mountpt_s *fs;
+ int ret;
+ struct smartfs_entry_s entry;
+ uint16_t parentdirsector;
+ const char *filename;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Recover our private data from the inode instance */
+
+ fs = mountpt->i_private;
+
+ /* Take the semaphore */
+
+ smartfs_semtake(fs);
+
+ /* Search for the path on the volume */
+
+ entry.name = NULL;
+ ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector,
+ &filename);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Populate our private data in the fs_dirent_s struct */
+
+ dir->u.smartfs.fs_firstsector = entry.firstsector;
+ dir->u.smartfs.fs_currsector = entry.firstsector;
+ dir->u.smartfs.fs_curroffset = sizeof(struct smartfs_chain_header_s);
+
+ ret = OK;
+
+errout_with_semaphore:
+ /* If space for the entry name was allocated, then free it */
+
+ if (entry.name != NULL)
+ {
+ kfree(entry.name);
+ entry.name = NULL;
+ }
+
+ smartfs_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_readdir
+ *
+ * Description: Read the next directory entry
+ *
+ ****************************************************************************/
+
+static int smartfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
+{
+ struct smartfs_mountpt_s *fs;
+ int ret;
+ uint16_t entrysize;
+ uint16_t namelen;
+ struct smartfs_chain_header_s *header;
+ struct smart_read_write_s readwrite;
+ struct smartfs_entry_header_s *entry;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Recover our private data from the inode instance */
+
+ fs = mountpt->i_private;
+
+ /* Take the semaphore */
+
+ smartfs_semtake(fs);
+
+ /* Read sectors and search entries until one found or no more */
+
+ entrysize = sizeof(struct smartfs_entry_header_s) +
+ fs->fs_llformat.namesize;
+ while (dir->u.smartfs.fs_currsector != SMARTFS_ERASEDSTATE_16BIT)
+ {
+ /* Read the logical sector */
+
+ readwrite.logsector = dir->u.smartfs.fs_currsector;
+ readwrite.count = fs->fs_llformat.availbytes;
+ readwrite.buffer = (uint8_t *)fs->fs_rwbuffer;
+ readwrite.offset = 0;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Now search for entries, starting at curroffset */
+
+ while (dir->u.smartfs.fs_curroffset < ret)
+ {
+ /* Point to next entry */
+
+ entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[
+ dir->u.smartfs.fs_curroffset];
+
+ /* Test if this entry is valid and active */
+
+ if (((entry->flags & SMARTFS_DIRENT_EMPTY) ==
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) ||
+ ((entry->flags & SMARTFS_DIRENT_ACTIVE) !=
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+ {
+ /* This entry isn't valid, skip it */
+
+ dir->u.smartfs.fs_curroffset += entrysize;
+ entry = (struct smartfs_entry_header_s *)
+ &fs->fs_rwbuffer[dir->u.smartfs.fs_curroffset];
+
+ continue;
+ }
+
+ /* Entry found! Report it */
+
+ if ((entry->flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR)
+ {
+ dir->fd_dir.d_type = DTYPE_DIRECTORY;
+ }
+ else
+ {
+ dir->fd_dir.d_type = DTYPE_FILE;
+ }
+
+ /* Copy the entry name to dirent */
+
+ namelen = fs->fs_llformat.namesize;
+ if (namelen > NAME_MAX)
+ {
+ namelen = NAME_MAX;
+ }
+
+ memset(dir->fd_dir.d_name, 0, namelen);
+ strncpy(dir->fd_dir.d_name, entry->name, namelen);
+
+ /* Now advance to the next entry */
+
+ dir->u.smartfs.fs_curroffset += entrysize;
+ if (dir->u.smartfs.fs_curroffset >= fs->fs_llformat.availbytes)
+ {
+ /* We advanced past the end of the sector. Go to next sector */
+
+ dir->u.smartfs.fs_curroffset = sizeof(struct smartfs_chain_header_s);
+ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
+ dir->u.smartfs.fs_currsector = SMARTFS_NEXTSECTOR(header);
+ }
+
+ /* Now exit */
+
+ ret = OK;
+ goto errout_with_semaphore;
+ }
+
+ /* No more entries in this sector. Move on to next sector and
+ * continue the search. If no more sectors, then we are all
+ * done and will report ENOENT.
+ */
+
+ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
+ dir->u.smartfs.fs_curroffset = sizeof(struct smartfs_chain_header_s);
+ dir->u.smartfs.fs_currsector = SMARTFS_NEXTSECTOR(header);
+ }
+
+ /* If we arrive here, then there are no more entries */
+
+ ret = -ENOENT;
+
+errout_with_semaphore:
+ smartfs_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_rewindir
+ *
+ * Description: Reset directory read to the first entry
+ *
+ ****************************************************************************/
+
+static int smartfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir)
+{
+ int ret = OK;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
+
+ /* Reset the directory to the first entry */
+
+ dir->u.smartfs.fs_currsector = dir->u.smartfs.fs_firstsector;
+ dir->u.smartfs.fs_curroffset = sizeof(struct smartfs_chain_header_s);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_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 smartfs_bind(FAR struct inode *blkdriver, const void *data,
+ void **handle)
+{
+ struct smartfs_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 smartfs_mountpt_s *)kzalloc(sizeof(struct smartfs_mountpt_s));
+ if (!fs)
+ {
+ return -ENOMEM;
+ }
+
+ /* If the global semaphore hasn't been initialized, then
+ * initialized it now. */
+
+ fs->fs_sem = &g_sem;
+ if (!g_seminitialized)
+ {
+ sem_init(&g_sem, 0, 0); /* Initialize the semaphore that controls access */
+ g_seminitialized = TRUE;
+ }
+ else
+ {
+ /* Take the semaphore for the mount */
+
+ smartfs_semtake(fs);
+ }
+
+ /* 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 */
+ fs->fs_head = NULL;
+
+ /* Now perform the mount. */
+
+ ret = smartfs_mount(fs, true);
+ if (ret != 0)
+ {
+ kfree(fs);
+ smartfs_semgive(fs);
+ return ret;
+ }
+
+ *handle = (void*)fs;
+ smartfs_semgive(fs);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: smartfs_unbind
+ *
+ * Description: This implements the filesystem portion of the umount
+ * operation.
+ *
+ ****************************************************************************/
+
+static int smartfs_unbind(void *handle, FAR struct inode **blkdriver)
+{
+ struct smartfs_mountpt_s *fs = (struct smartfs_mountpt_s*)handle;
+ int ret;
+
+ if (!fs)
+ {
+ return -EINVAL;
+ }
+
+ /* Check if there are sill any files opened on the filesystem. */
+
+ ret = OK; /* Assume success */
+ smartfs_semtake(fs);
+ if (fs->fs_head != NULL)
+ {
+ /* We cannot unmount now.. there are open files */
+
+ smartfs_semgive(fs);
+
+ ret = -EBUSY;
+ }
+ else
+ {
+ /* Unmount ... close the block driver */
+
+ ret = smartfs_unmount(fs);
+ }
+
+ smartfs_semgive(fs);
+ kfree(fs);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_statfs
+ *
+ * Description: Return filesystem statistics
+ *
+ ****************************************************************************/
+
+static int smartfs_statfs(struct inode *mountpt, struct statfs *buf)
+{
+ struct smartfs_mountpt_s *fs;
+ int ret = OK;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = mountpt->i_private;
+
+ smartfs_semtake(fs);
+
+ /* Implement the logic!! */
+
+ memset(buf, 0, sizeof(struct statfs));
+ buf->f_type = SMARTFS_MAGIC;
+
+ /* Re-request the low-level format info to update free blocks */
+
+ ret = FS_IOCTL(fs, BIOC_GETFORMAT, (unsigned long) &fs->fs_llformat);
+
+ buf->f_namelen = fs->fs_llformat.namesize;
+ buf->f_bsize = fs->fs_llformat.sectorsize;
+ buf->f_blocks = fs->fs_llformat.nsectors;
+ buf->f_bfree = fs->fs_llformat.nfreesectors;
+ buf->f_bavail = fs->fs_llformat.nfreesectors;
+ buf->f_files = 0;
+ buf->f_ffree = fs->fs_llformat.nfreesectors;
+
+ smartfs_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_unlink
+ *
+ * Description: Remove a file
+ *
+ ****************************************************************************/
+
+static int smartfs_unlink(struct inode *mountpt, const char *relpath)
+{
+ struct smartfs_mountpt_s *fs;
+ int ret = OK;
+ struct smartfs_entry_s entry;
+ const char *filename;
+ uint16_t parentdirsector;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = mountpt->i_private;
+
+ smartfs_semtake(fs);
+
+ /* Locate the directory entry for this path */
+
+ entry.name = NULL;
+ ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector,
+ &filename);
+
+ if (ret == OK)
+ {
+ /* The name exists -- validate it is a file, not a dir */
+
+ if ((entry.flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR)
+ {
+ ret = -EISDIR;
+ goto errout_with_semaphore;
+ }
+
+ /* TODO: Need to check permissions? */
+
+ /* Okay, we are clear to delete the file. Use the deleteentry routine. */
+
+ smartfs_deleteentry(fs, &entry);
+
+ }
+ else
+ {
+ /* Just report the error */
+
+ goto errout_with_semaphore;
+ }
+
+ ret = OK;
+
+errout_with_semaphore:
+ smartfs_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_mkdir
+ *
+ * Description: Create a directory
+ *
+ ****************************************************************************/
+
+static int smartfs_mkdir(struct inode *mountpt, const char *relpath, mode_t mode)
+{
+ struct smartfs_mountpt_s *fs;
+ int ret;
+ struct smartfs_entry_s entry;
+ uint16_t parentdirsector;
+ const char *filename;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = mountpt->i_private;
+
+ smartfs_semtake(fs);
+
+ /* Locate the directory entry for this path */
+
+ entry.name = NULL;
+ ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector,
+ &filename);
+
+ /* 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)
+ {
+ /* The name exists -- can't create */
+
+ ret = -EEXIST;
+ goto errout_with_semaphore;
+ }
+ else if (ret == -ENOENT)
+ {
+ /* It doesn't exist ... we can create it, but only if we have
+ * the right permissions and if the parentdirsector is valid. */
+
+ if (parentdirsector == 0xFFFF)
+ {
+ /* Invalid entry in the path (non-existant dir segment) */
+
+ goto errout_with_semaphore;
+ }
+
+ /* Check mode */
+
+ /* Create the directory */
+
+ ret = smartfs_createentry(fs, parentdirsector, filename,
+ SMARTFS_DIRENT_TYPE_DIR, mode, &entry, 0xFFFF);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ ret = OK;
+ }
+
+errout_with_semaphore:
+ if (entry.name != NULL)
+ {
+ /* Free the filename space allocation */
+
+ kfree(entry.name);
+ entry.name = NULL;
+ }
+
+ smartfs_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_rmdir
+ *
+ * Description: Remove a directory
+ *
+ ****************************************************************************/
+
+int smartfs_rmdir(struct inode *mountpt, const char *relpath)
+{
+ struct smartfs_mountpt_s *fs;
+ int ret = OK;
+ struct smartfs_entry_s entry;
+ const char *filename;
+ uint16_t parentdirsector;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = mountpt->i_private;
+
+ /* Take the semaphore */
+
+ smartfs_semtake(fs);
+
+ /* Locate the directory entry for this path */
+
+ entry.name = NULL;
+ ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector,
+ &filename);
+
+ if (ret == OK)
+ {
+ /* The name exists -- validate it is a dir, not a file */
+
+ if ((entry.flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_FILE)
+ {
+ ret = -ENOTDIR;
+ goto errout_with_semaphore;
+ }
+
+ /* TODO: Need to check permissions? */
+
+ /* Check if the directory is emtpy */
+
+ ret = smartfs_countdirentries(fs, &entry);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Only continue if there are zero entries in the directory */
+
+ if (ret != 0)
+ {
+ ret = -ENOTEMPTY;
+ goto errout_with_semaphore;
+ }
+
+ /* Okay, we are clear to delete the directory. Use the deleteentry routine. */
+
+ ret = smartfs_deleteentry(fs, &entry);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+ }
+ else
+ {
+ /* Just report the error */
+
+ goto errout_with_semaphore;
+ }
+
+ ret = OK;
+
+errout_with_semaphore:
+ smartfs_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_rename
+ *
+ * Description: Rename a file or directory
+ *
+ ****************************************************************************/
+
+int smartfs_rename(struct inode *mountpt, const char *oldrelpath,
+ const char *newrelpath)
+{
+ struct smartfs_mountpt_s *fs;
+ int ret;
+ struct smartfs_entry_s oldentry;
+ uint16_t oldparentdirsector;
+ const char *oldfilename;
+ struct smartfs_entry_s newentry;
+ uint16_t newparentdirsector;
+ const char *newfilename;
+ mode_t mode;
+ uint16_t type;
+ uint16_t sector;
+ uint16_t offset;
+ uint16_t entrysize;
+ struct smartfs_entry_header_s *direntry;
+ struct smart_read_write_s readwrite;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = mountpt->i_private;
+
+ smartfs_semtake(fs);
+
+ /* Search for old entry to validate it exists */
+
+ oldentry.name = NULL;
+ newentry.name = NULL;
+ ret = smartfs_finddirentry(fs, &oldentry, oldrelpath, &oldparentdirsector,
+ &oldfilename);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Search for the new entry and validate it DOESN'T exist, unless we
+ * are copying to a directory and keeping the same filename, such as:
+ *
+ * mv /mntpoint/somefile.txt /mntpoint/somedir
+ *
+ * in which case, we need to validate the /mntpoint/somedir/somefile.txt
+ * doens't exsit.
+ */
+
+ ret = smartfs_finddirentry(fs, &newentry, newrelpath, &newparentdirsector,
+ &newfilename);
+ if (ret == OK)
+ {
+ /* Test if it's a file. If it is, then it's an error */
+
+ if ((newentry.flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_FILE)
+ {
+ ret = -EEXIST;
+ goto errout_with_semaphore;
+ }
+
+ /* Nope, it's a directory. Now search the directory for oldfilename */
+
+ sector = newentry.firstsector;
+ while (sector != SMARTFS_ERASEDSTATE_16BIT)
+ {
+ /* Read the next sector of diretory entries */
+
+ readwrite.logsector = sector;
+ readwrite.offset = 0;
+ readwrite.count = fs->fs_llformat.availbytes;
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d reading sector %d data\n", ret, oldentry.dsector);
+ goto errout_with_semaphore;
+ }
+
+ /* Search for newfilename in this sector */
+
+ offset = sizeof(struct smartfs_chain_header_s);
+ entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize;
+ while (offset + entrysize < fs->fs_llformat.availbytes)
+ {
+ /* Test the next entry */
+
+ direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
+ if (((direntry->flags & SMARTFS_DIRENT_EMPTY) ==
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) ||
+ ((direntry->flags & SMARTFS_DIRENT_ACTIVE) !=
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+ {
+ /* This entry isn't valid, skip it */
+
+ offset += entrysize;
+ continue;
+ }
+
+ /* Test if this entry matches newfilename */
+
+ if (strcmp(newfilename, direntry->name) == 0)
+ {
+ /* Uh-oh, looks like the entry already exists */
+
+ ret = -EEXIST;
+ goto errout_with_semaphore;
+ }
+
+ offset += entrysize;
+ }
+
+ /* Chain to next sector */
+
+ sector = SMARTFS_NEXTSECTOR(((struct smartfs_chain_header_s *)fs->fs_rwbuffer));
+ }
+
+ /* Okay, we will create newfilename in the newentry directory */
+
+ newparentdirsector = newentry.firstsector;
+ newfilename = oldfilename;
+ }
+
+ /* Test if the new parent directory is valid */
+
+ if (newparentdirsector != 0xFFFF)
+ {
+ /* We can move to the given parent directory */
+
+ mode = oldentry.flags & SMARTFS_DIRENT_MODE;
+ type = oldentry.flags & SMARTFS_DIRENT_TYPE;
+ ret = smartfs_createentry(fs, newparentdirsector, newfilename,
+ type, mode, &newentry, oldentry.firstsector);
+ if (ret != OK)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Now mark the old entry as inactive */
+
+ readwrite.logsector = oldentry.dsector;
+ readwrite.offset = 0;
+ readwrite.count = fs->fs_llformat.availbytes;
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d reading sector %d data\n", ret, oldentry.dsector);
+ goto errout_with_semaphore;
+ }
+
+ direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[oldentry.doffset];
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ direntry->flags &= ~SMARTFS_DIRENT_ACTIVE;
+#else
+ direntry->flags |= SMARTFS_DIRENT_ACTIVE;
+#endif
+
+ /* Now write the updated flags back to the device */
+
+ readwrite.offset = oldentry.doffset;
+ readwrite.count = sizeof(direntry->flags);
+ readwrite.buffer = (uint8_t *) &direntry->flags;
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d writing flag bytes for sector %d\n", ret, readwrite.logsector);
+ goto errout_with_semaphore;
+ }
+ }
+ else
+ {
+ /* Trying to create in a directory that doesn't exist */
+
+ ret = -ENOENT;
+ goto errout_with_semaphore;
+ }
+
+ ret = OK;
+
+errout_with_semaphore:
+ if (oldentry.name != NULL)
+ {
+ kfree(oldentry.name);
+ oldentry.name = NULL;
+ }
+
+ if (newentry.name != NULL)
+ {
+ kfree(newentry.name);
+ newentry.name = NULL;
+ }
+
+ smartfs_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_stat
+ *
+ * Description: Return information about a file or directory
+ *
+ ****************************************************************************/
+
+static int smartfs_stat(struct inode *mountpt, const char *relpath, struct stat *buf)
+{
+ struct smartfs_mountpt_s *fs;
+ struct smartfs_entry_s entry;
+ int ret = -ENOENT;
+ uint16_t parentdirsector;
+ const char *filename;
+
+ /* Sanity checks */
+
+ DEBUGASSERT(mountpt && mountpt->i_private);
+
+ /* Get the mountpoint private data from the inode structure */
+
+ fs = mountpt->i_private;
+
+ smartfs_semtake(fs);
+
+ /* Find the directory entry corresponding to relpath */
+
+ entry.name = NULL;
+ ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector,
+ &filename);
+ if (ret < 0)
+ {
+ goto errout_with_semaphore;
+ }
+
+ /* Initialize the stat structure */
+
+ memset(buf, 0, sizeof(struct stat));
+ if (entry.firstsector == fs->fs_rootsector)
+ {
+ /* 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;
+ }
+
+ buf->st_mode = entry.flags & 0xFFF;
+ if ((entry.flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR)
+ {
+ buf->st_mode |= S_IFDIR;
+ }
+ else
+ {
+ buf->st_mode |= S_IFREG;
+ }
+
+ buf->st_size = entry.datlen;
+ buf->st_blksize = fs->fs_llformat.availbytes;
+ buf->st_blocks = (buf->st_size + buf->st_blksize - 1) / buf->st_blksize;
+ buf->st_atime = 0;
+ buf->st_ctime = 0;
+
+ ret = OK;
+
+errout_with_semaphore:
+ if (entry.name != NULL)
+ {
+ kfree(entry.name);
+ entry.name = NULL;
+ }
+
+ smartfs_semgive(fs);
+ return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
diff --git a/nuttx/fs/smartfs/smartfs_utils.c b/nuttx/fs/smartfs/smartfs_utils.c
new file mode 100644
index 000000000..a72fef691
--- /dev/null
+++ b/nuttx/fs/smartfs/smartfs_utils.c
@@ -0,0 +1,1293 @@
+/****************************************************************************
+ * fs/smartfs/smartfs_utils.c
+ *
+ * Copyright (C) 2013 Ken Pettit. All rights reserved.
+ * Author: Ken Pettit <pettitkd@gmail.com>
+ *
+ * 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/ioctl.h>
+
+#include "smartfs.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+static struct smartfs_mountpt_s* g_mounthead = NULL;
+#endif
+
+/****************************************************************************
+ * Public Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: smartfs_semtake
+ ****************************************************************************/
+
+void smartfs_semtake(struct smartfs_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: smartfs_semgive
+ ****************************************************************************/
+
+void smartfs_semgive(struct smartfs_mountpt_s *fs)
+{
+ sem_post(fs->fs_sem);
+}
+
+/****************************************************************************
+ * Name: smartfs_mount
+ *
+ * Desciption: This function is called only when the mountpoint is first
+ * established. It initializes the mountpoint structure and verifies
+ * that a valid SMART filesystem is provided by the block driver.
+ *
+ * The caller should hold the mountpoint semaphore
+ *
+ ****************************************************************************/
+
+int smartfs_mount(struct smartfs_mountpt_s *fs, bool writeable)
+{
+ FAR struct inode *inode;
+ struct geometry geo;
+ int ret = OK;
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ struct smartfs_mountpt_s *nextfs;
+#endif
+
+ /* Assume that the mount is not successful */
+
+ fs->fs_mounted = false;
+
+ /* 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;
+ }
+
+ /* Get the SMART low-level format information to validate the device has been
+ * formatted and scan properly for logical to physical sector mapping.
+ */
+
+ ret = FS_IOCTL(fs, BIOC_GETFORMAT, (unsigned long) &fs->fs_llformat);
+ if (ret != OK)
+ {
+ fdbg("Error getting device low level format: %d\n", ret);
+ goto errout;
+ }
+
+ /* Validate the low-level format is valid */
+
+ if (!(fs->fs_llformat.flags & SMART_FMT_ISFORMATTED))
+ {
+ fdbg("No low-level format found\n");
+ ret = -ENODEV;
+ goto errout;
+ }
+
+ /* Allocate a read/write buffer */
+
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ /* Scan linked list of mounted file systems to find another FS with
+ * the same blockdriver. We will reuse the buffers.
+ */
+
+ nextfs = g_mounthead;
+ while (nextfs != NULL)
+ {
+ /* Test if this FS uses the same block driver */
+
+ if (nextfs->fs_blkdriver == fs->fs_blkdriver)
+ {
+ /* Yep, it's the same block driver. Reuse the buffers.
+ * we can do this because we are protected by the same
+ * semaphore.
+ */
+
+ fs->fs_rwbuffer = nextfs->fs_rwbuffer;
+ fs->fs_workbuffer = nextfs->fs_workbuffer;
+ break;
+ }
+
+ /* Advance to next FS */
+
+ nextfs = nextfs->fs_next;
+ }
+
+ /* If we didn't find a FS above, then allocate some buffers */
+
+ if (nextfs == NULL)
+ {
+ fs->fs_rwbuffer = (char *) kmalloc(fs->fs_llformat.availbytes);
+ fs->fs_workbuffer = (char *) kmalloc(256);
+ }
+
+ /* Now add ourselves to the linked list of SMART mounts */
+
+ fs->fs_next = g_mounthead;
+ g_mounthead = fs;
+
+ /* Set our root directory sector based on the directory entry
+ * reported by the block driver (based on which device is
+ * associated with this mount.
+ */
+
+ fs->fs_rootsector = SMARTFS_ROOT_DIR_SECTOR + fs->fs_llformat.rootdirnum;
+
+#else
+ fs->fs_rwbuffer = (char *) kmalloc(fs->fs_llformat.availbytes);
+ fs->fs_workbuffer = (char *) kmalloc(256);
+ fs->fs_rootsector = SMARTFS_ROOT_DIR_SECTOR;
+#endif
+
+ /* We did it! */
+
+ fs->fs_mounted = TRUE;
+
+ fdbg("SMARTFS:\n");
+ fdbg("\t Sector size: %d\n", fs->fs_llformat.sectorsize);
+ fdbg("\t Bytes/sector %d\n", fs->fs_llformat.availbytes);
+ fdbg("\t Num sectors: %d\n", fs->fs_llformat.nsectors);
+ fdbg("\t Free sectors: %d\n", fs->fs_llformat.nfreesectors);
+ fdbg("\t Max filename: %d\n", CONFIG_SMARTFS_MAXNAMLEN);
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ fdbg("\t RootDirEntries: %d\n", fs->fs_llformat.nrootdirentries);
+#endif
+ fdbg("\t RootDirSector: %d\n", fs->fs_rootsector);
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_unmount
+ *
+ * Desciption: This function is called only when the mountpoint is being
+ * unbound. If we are serving multiple directories, then we have to
+ * remove ourselves from the mount linked list, and potentially free
+ * the shared buffers.
+ *
+ * The caller should hold the mountpoint semaphore
+ *
+ ****************************************************************************/
+
+int smartfs_unmount(struct smartfs_mountpt_s *fs)
+{
+ int ret = OK;
+ struct inode *inode;
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ struct smartfs_mountpt_s *nextfs;
+ struct smartfs_mountpt_s *prevfs;
+ int count = 0;
+ int found = FALSE;
+#endif
+
+#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
+ /* Start at the head of the mounts and search for our entry. Also
+ * count the number of entries that match our blkdriver.
+ */
+
+ nextfs = g_mounthead;
+ prevfs = NULL;
+ while (nextfs != NULL)
+ {
+ /* Test if this FS's blkdriver matches ours (it could be us) */
+
+ if (nextfs->fs_blkdriver == fs->fs_blkdriver)
+ count++;
+
+ /* Test if this entry is our's */
+
+ if (nextfs == fs)
+ {
+ found = TRUE;
+ }
+
+ /* Keep track of the previous entry until our's is found */
+
+ if (!found)
+ {
+ /* Save this entry as the previous entry */
+
+ prevfs = nextfs;
+ }
+
+ /* Advance to the next entry */
+
+ nextfs = nextfs->fs_next;
+ }
+
+ /* Ensure we found our FS */
+
+ if (!found)
+ {
+ /* Our entry not found! Invalid unmount or bug somewhere */
+
+ return -EINVAL;
+ }
+
+ /* If the count is only one, then we need to delete the shared
+ * buffers because we are the last ones.
+ */
+
+ if (count == 1)
+ {
+ /* Close the block driver */
+
+ if (fs->fs_blkdriver)
+ {
+ inode = fs->fs_blkdriver;
+ if (inode)
+ {
+ if (inode->u.i_bops && inode->u.i_bops->close)
+ {
+ (void)inode->u.i_bops->close(inode);
+ }
+ }
+ }
+
+ /* Free the buffers */
+
+ kfree(fs->fs_rwbuffer);
+ kfree(fs->fs_workbuffer);
+
+ /* Set the buffer's to invalid value to catch program bugs */
+
+ fs->fs_rwbuffer = (char *) 0xDEADBEEF;
+ fs->fs_workbuffer = (char *) 0xDEADBEEF;
+ }
+
+ /* Now removed ourselves from the linked list */
+
+ if (fs == g_mounthead)
+ {
+ /* We were the first ones. Set a new head */
+
+ g_mounthead = fs->fs_next;
+ }
+ else
+ {
+ /* Remove from the middle of the list somewhere */
+
+ prevfs->fs_next = fs->fs_next;
+ }
+#else
+ if (fs->fs_blkdriver)
+ {
+ inode = fs->fs_blkdriver;
+ if (inode)
+ {
+ if (inode->u.i_bops && inode->u.i_bops->close)
+ {
+ (void)inode->u.i_bops->close(inode);
+ }
+ }
+ }
+
+ /* Release the mountpoint private data */
+
+ kfree(fs->fs_rwbuffer);
+ kfree(fs->fs_workbuffer);
+#endif
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_finddirentry
+ *
+ * Description: Finds an entry in the filesystem as specified by relpath.
+ * If found, the direntry will be populated with information
+ * for accessing the entry.
+ *
+ * If the final directory segement of relpath just before the
+ * last segment (the target file/dir) is valid, then the
+ * parentdirsector will indicate the logical sector number of
+ * the parent directory where a new entry should be created,
+ * and the filename pointer will point to the final segment
+ * (i.e. the "filename").
+ *
+ ****************************************************************************/
+
+int smartfs_finddirentry(struct smartfs_mountpt_s *fs,
+ struct smartfs_entry_s *direntry, const char *relpath,
+ uint16_t *parentdirsector, const char **filename)
+{
+ int ret = -ENOENT;
+ const char *segment;
+ const char *ptr;
+ uint16_t seglen;
+ uint16_t depth = 0;
+ uint16_t dirstack[CONFIG_SMARTFS_DIRDEPTH];
+ uint16_t dirsector;
+ uint16_t entrysize;
+ uint16_t offset;
+ struct smartfs_chain_header_s *header;
+ struct smart_read_write_s readwrite;
+ struct smartfs_entry_header_s *entry;
+
+ /* Initialize directory level zero as the root sector */
+
+ dirstack[0] = fs->fs_rootsector;
+ entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize;
+
+ /* Test if this is a request for the root directory */
+
+ if (*relpath == '\0')
+ {
+ direntry->firstsector = fs->fs_rootsector;
+ direntry->flags = SMARTFS_DIRENT_TYPE_DIR | 0777;
+ direntry->utc = 0;
+ direntry->dsector = 0;
+ direntry->doffset = 0;
+ direntry->dfirst = fs->fs_rootsector;
+ direntry->name = NULL;
+ direntry->datlen = 0;
+
+ *parentdirsector = 0; /* Our parent is the format sector I guess */
+ return OK;
+ }
+
+ /* Parse through each segment of relpath */
+
+ segment = relpath;
+ while (segment != NULL && *segment != '\0')
+ {
+ /* Find the end of this segment. It will be '/' or NULL. */
+
+ ptr = segment;
+ seglen = 0;
+ while (*ptr != '/' && *ptr != '\0')
+ {
+ seglen++;
+ ptr++;
+ }
+
+ strncpy(fs->fs_workbuffer, segment, seglen);
+ fs->fs_workbuffer[seglen] = '\0';
+
+ /* Search for "." and ".." as segment names */
+
+ if (strcmp(fs->fs_workbuffer, ".") == 0)
+ {
+ /* Just ignore this segment. Advance ptr if not on NULL */
+
+ if (*ptr == '/')
+ {
+ ptr++;
+ }
+
+ segment = ptr;
+ continue;
+ }
+ else if (strcmp(fs->fs_workbuffer, "..") == 0)
+ {
+ /* Up one level */
+
+ if (depth == 0)
+ {
+ /* We went up one level past our mount point! */
+
+ goto errout;
+ }
+
+ /* "Pop" to the previous directory level */
+
+ depth--;
+ if (*ptr == '/')
+ {
+ ptr++;
+ }
+
+ segment = ptr;
+ continue;
+ }
+ else
+ {
+ /* Search for the entry in the current directory */
+
+ dirsector = dirstack[depth];
+
+ /* Read the directory */
+
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ while (dirsector != 0xFFFF)
+#else
+ while (dirsector != 0)
+#endif
+ {
+ /* Read the next directory in the chain */
+
+ readwrite.logsector = dirsector;
+ readwrite.count = fs->fs_llformat.availbytes;
+ readwrite.buffer = (uint8_t *)fs->fs_rwbuffer;
+ readwrite.offset = 0;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ goto errout;
+ }
+
+ /* Point to next sector in chain */
+
+ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
+ dirsector = SMARTFS_NEXTSECTOR(header);
+
+ /* Search for the entry */
+
+ offset = sizeof(struct smartfs_chain_header_s);
+ entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
+ while (offset < readwrite.count)
+ {
+ /* Test if this entry is valid and active */
+
+ if (((entry->flags & SMARTFS_DIRENT_EMPTY) ==
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) ||
+ ((entry->flags & SMARTFS_DIRENT_ACTIVE) !=
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+ {
+ /* This entry isn't valid, skip it */
+
+ offset += entrysize;
+ entry = (struct smartfs_entry_header_s *)
+ &fs->fs_rwbuffer[offset];
+
+ continue;
+ }
+
+ /* Test if the name matches */
+
+ if (strcmp(entry->name, fs->fs_workbuffer) == 0)
+ {
+ /* We found it! If this is the last segment entry,
+ * then report the entry. If it isn't the last
+ * entry, then validate it is a directory entry and
+ * open it and continue searching.
+ */
+
+ if (*ptr == '\0')
+ {
+ /* We are at the last segment. Report the entry */
+
+ /* Fill in the entry */
+
+ direntry->firstsector = entry->firstsector;
+ direntry->flags = entry->flags;
+ direntry->utc = entry->utc;
+ direntry->dsector = readwrite.logsector;
+ direntry->doffset = offset;
+ direntry->dfirst = dirstack[depth];
+ if (direntry->name == NULL)
+ {
+ direntry->name = (char *) kmalloc(fs->fs_llformat.namesize);
+ }
+
+ strcpy(direntry->name, entry->name);
+ direntry->datlen = 0;
+
+ /* Scan the file's sectors to calculate the length and perform
+ * a rudamentary check.
+ */
+
+ if ((entry->flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_FILE)
+ {
+ dirsector = entry->firstsector;
+ readwrite.count = sizeof(struct smartfs_chain_header_s);
+ readwrite.buffer = (uint8_t *)fs->fs_rwbuffer;
+ readwrite.offset = 0;
+
+ while (dirsector != SMARTFS_ERASEDSTATE_16BIT)
+ {
+ /* Read the next sector of the file */
+
+ readwrite.logsector = dirsector;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error in sector chain at %d!\n", dirsector);
+ break;
+ }
+
+ /* Add used bytes to the total and point to next sector */
+
+ if (*((uint16_t *) header->used) != SMARTFS_ERASEDSTATE_16BIT)
+ {
+ direntry->datlen += *((uint16_t *) header->used);
+ }
+
+ dirsector = SMARTFS_NEXTSECTOR(header);
+ }
+ }
+
+ *parentdirsector = dirstack[depth];
+ *filename = segment;
+ ret = OK;
+ goto errout;
+ }
+ else
+ {
+ /* Validate it's a directory */
+
+ if ((entry->flags & SMARTFS_DIRENT_TYPE) !=
+ SMARTFS_DIRENT_TYPE_DIR)
+ {
+ /* Not a directory! Report the error */
+
+ ret = -ENOTDIR;
+ goto errout;
+ }
+
+ /* "Push" the directory and continue searching */
+
+ if (depth >= CONFIG_SMARTFS_DIRDEPTH - 1)
+ {
+ /* Directory depth too big */
+
+ ret = -ENAMETOOLONG;
+ goto errout;
+ }
+
+ dirstack[++depth] = entry->firstsector;
+ segment = ptr + 1;
+ break;
+ }
+ }
+
+ /* Not this entry. Skip to the next one */
+
+ offset += entrysize;
+ entry = (struct smartfs_entry_header_s *)
+ &fs->fs_rwbuffer[offset];
+ }
+
+ /* Test if a directory entry was found and break if it was */
+
+ if (offset < readwrite.count)
+ {
+ break;
+ }
+ }
+
+ /* If we found a dir entry, then continue searching */
+
+ if (offset < readwrite.count)
+ {
+ /* Update the segment pointer */
+
+ if (*ptr != '\0')
+ {
+ ptr++;
+ }
+
+ segment = ptr;
+ continue;
+ }
+
+ /* Entry not found! Report the error. Also, if this is the last
+ * segment, then report the parent directory sector.
+ */
+
+ if (*ptr == '\0')
+ {
+ *parentdirsector = dirstack[depth];
+ *filename = segment;
+ }
+ else
+ {
+ *parentdirsector = 0xFFFF;
+ *filename = NULL;
+ }
+
+ ret = -ENOENT;
+ goto errout;
+ }
+ }
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_createentry
+ *
+ * Description: Creates a new entry in the specified parent directory, using
+ * the specified type and name. If the given sectorno is
+ * 0xFFFF, then a new sector is allocated for the new entry,
+ * otherwise the supplied sectorno is used.
+ *
+ ****************************************************************************/
+
+int smartfs_createentry(struct smartfs_mountpt_s *fs,
+ uint16_t parentdirsector, const char* filename,
+ uint16_t type,
+ mode_t mode, struct smartfs_entry_s *direntry,
+ uint16_t sectorno)
+{
+ struct smart_read_write_s readwrite;
+ int ret;
+ uint16_t psector;
+ uint16_t nextsector;
+ uint16_t offset;
+ uint16_t found;
+ uint16_t entrysize;
+ struct smartfs_entry_header_s *entry;
+ struct smartfs_chain_header_s *chainheader;
+
+ /* Start at the 1st sector in the parent directory */
+
+ psector = parentdirsector;
+ found = FALSE;
+ entrysize = sizeof(struct smartfs_entry_header_s) +
+ fs->fs_llformat.namesize;
+
+ /* Validate the name isn't too long */
+
+ if (strlen(filename) + 1 > fs->fs_llformat.namesize)
+ {
+ return -ENAMETOOLONG;
+ }
+
+ /* Read the parent directory sector and find a place to insert
+ * the new entry.
+ */
+
+ while (1)
+ {
+ /* Read the next sector */
+
+ readwrite.logsector = psector;
+ readwrite.count = fs->fs_llformat.availbytes;
+ readwrite.offset = 0;
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ goto errout;
+ }
+
+ /* Get the next chained sector */
+
+ chainheader = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
+ nextsector = SMARTFS_NEXTSECTOR(chainheader);
+
+ /* Search for an empty entry in this sector */
+
+ offset = sizeof(struct smartfs_chain_header_s);
+ entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
+ while (offset + entrysize < readwrite.count)
+ {
+ /* Check if this entry is available */
+
+ if ((entry->flags == SMARTFS_ERASEDSTATE_16BIT) ||
+ ((entry->flags &
+ (SMARTFS_DIRENT_EMPTY | SMARTFS_DIRENT_ACTIVE) ) ==
+ (~SMARTFS_ERASEDSTATE_16BIT &
+ (SMARTFS_DIRENT_EMPTY | SMARTFS_DIRENT_ACTIVE) )))
+ {
+ /* We found an empty entry. Use it. */
+
+ found = TRUE;
+ break;
+ }
+
+ /* Not available. Skip to next entry */
+
+ offset += entrysize;
+ entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
+ }
+
+ /* If we found an entry, stop the search */
+
+ if (found)
+ {
+ break;
+ }
+
+ /* If there are no more sectors, then we need to add one to make
+ * room for the new entry.
+ */
+
+ if (nextsector == SMARTFS_ERASEDSTATE_16BIT)
+ {
+ /* Allocate a new sector and chain it to the last one */
+
+ ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xFFFF);
+ if (ret < 0)
+ {
+ goto errout;
+ }
+
+ nextsector = (uint16_t) ret;
+
+ /* Chain the next sector into this sector sector */
+
+ *((uint16_t *) chainheader->nextsector) = nextsector;
+ readwrite.offset = offsetof(struct smartfs_chain_header_s,
+ nextsector);
+ readwrite.count = sizeof(uint16_t);
+ readwrite.buffer = chainheader->nextsector;
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error chaining sector %d\n", nextsector);
+ goto errout;
+ }
+ }
+
+ /* Now update to the next sector */
+
+ psector = nextsector;
+ }
+
+ /* We found an insertion point. Create the entry at sector,offset */
+
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ entry->flags = (uint16_t) (SMARTFS_DIRENT_ACTIVE | SMARTFS_DIRENT_DELETING |
+ SMARTFS_DIRENT_RESERVED | type | (mode & SMARTFS_DIRENT_MODE));
+#else
+ entry->flags = (uint16_t) (SMARTFS_DIRENT_EMPTY | type | (mode & SMARTFS_DIRENT_MODE));
+#endif
+
+ if (sectorno == 0xFFFF)
+ {
+ /* Allocate a new sector for the file / dir */
+
+ ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xFFFF);
+ if (ret < 0)
+ {
+ goto errout;
+ }
+
+ nextsector = ret;
+
+ /* Set the newly allocated sector's type (file or dir) */
+
+ nextsector = (uint16_t) ret;
+ if ((type & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR)
+ {
+ chainheader->type = SMARTFS_SECTOR_TYPE_DIR;
+ }
+ else
+ {
+ chainheader->type = SMARTFS_SECTOR_TYPE_FILE;
+ }
+
+ readwrite.count = 1;
+ readwrite.offset = offsetof(struct smartfs_chain_header_s, type);
+ readwrite.buffer = (uint8_t *) &chainheader->type;
+ readwrite.logsector = nextsector;
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error %d setting new sector type for sector %d\n",ret, nextsector);
+ goto errout;
+ }
+ }
+ else
+ {
+ /* Use the provided sector number */
+
+ nextsector = sectorno;
+ }
+
+ /* Create the directory entry to be written in the parent's sector */
+
+ entry->firstsector = nextsector;
+ entry->utc = 0;
+ memset(entry->name, 0, fs->fs_llformat.namesize);
+ strncpy(entry->name, filename, fs->fs_llformat.namesize-1);
+
+ /* Now write the new entry to the parent directory sector */
+
+ readwrite.logsector = psector;
+ readwrite.offset = offset;
+ readwrite.count = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize;
+ readwrite.buffer = (uint8_t *) &fs->fs_rwbuffer[offset];
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ goto errout;
+ }
+
+ /* Now fill in the entry */
+
+ direntry->firstsector = nextsector;
+ direntry->dsector = psector;
+ direntry->doffset = offset;
+ direntry->flags = entry->flags;
+ direntry->utc = 0;
+ direntry->datlen = 0;
+ if (direntry->name == NULL)
+ {
+ direntry->name = (FAR char *) kmalloc(fs->fs_llformat.namesize);
+ }
+
+ memset(direntry->name, 0, fs->fs_llformat.namesize);
+ strncpy(direntry->name, filename, fs->fs_llformat.namesize-1);
+
+ ret = OK;
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_deleteentry
+ *
+ * Description: Deletes an entry from the filesystem (file or dir) by
+ * freeing all the entry's sectors and then marking it inactive
+ * in it's parent's directory sector. For a directory, it
+ * does not validate the directory is empty, nor does it do
+ * a recursive delete.
+ *
+ ****************************************************************************/
+
+int smartfs_deleteentry(struct smartfs_mountpt_s *fs,
+ struct smartfs_entry_s *entry)
+{
+ int ret;
+ uint16_t nextsector;
+ uint16_t sector;
+ uint16_t count;
+ uint16_t entrysize;
+ uint16_t offset;
+ struct smartfs_entry_header_s *direntry;
+ struct smartfs_chain_header_s *header;
+ struct smart_read_write_s readwrite;
+
+ /* Okay, delete the file. Loop through each sector and release them
+
+ * TODO: We really should walk the list backward to avoid lost
+ * sectors in the event we lose power. However this requires
+ * allocting a buffer to build the sector list since we don't
+ * store a doubly-linked list of sectors on the deivce. We
+ * could test if the sector data buffer is big enough and
+ * just use that, and only allocate a new buffer if the
+ * sector buffer isn't big enough. Do do this, however, we
+ * need to change the code below as it is using the a few
+ * bytes of the buffer to read in header info.
+ */
+
+ nextsector = entry->firstsector;
+ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
+ readwrite.offset = 0;
+ readwrite.count = sizeof(struct smartfs_chain_header_s);
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ while (nextsector != SMARTFS_ERASEDSTATE_16BIT)
+ {
+ /* Read the next sector into our buffer */
+
+ sector = nextsector;
+ readwrite.logsector = sector;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error reading sector %d\n", nextsector);
+ break;
+ }
+
+ /* Release this sector */
+
+ nextsector = SMARTFS_NEXTSECTOR(header);
+ ret = FS_IOCTL(fs, BIOC_FREESECT, sector);
+ }
+
+ /* Remove the entry from the directory tree */
+
+ readwrite.logsector = entry->dsector;
+ readwrite.offset = 0;
+ readwrite.count = fs->fs_llformat.availbytes;
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error reading directory info at sector %s\n", entry->dsector);
+ goto errout;
+ }
+
+ /* Mark this entry as inactive */
+
+ direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[entry->doffset];
+#if CONFIG_SMARTFS_ERASEDSTATE == 0xFF
+ direntry->flags &= ~SMARTFS_DIRENT_ACTIVE;
+#else
+ direntry->flags |= SMARTFS_DIRENT_ACTIVE;
+#endif
+
+ /* Write the updated flags back to the sector */
+
+ readwrite.offset = entry->doffset;
+ readwrite.count = sizeof(uint16_t);
+ readwrite.buffer = (uint8_t *) &direntry->flags;
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error marking entry inactive at sector %s\n", entry->dsector);
+ goto errout;
+ }
+
+ /* Test if any entries in this sector are being used */
+
+ if ((entry->dsector != fs->fs_rootsector) &&
+ (entry->dsector != entry->dfirst))
+ {
+ /* Scan the sector and count used entries */
+
+ count = 0;
+ offset = sizeof(struct smartfs_chain_header_s);
+ entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize;
+ while (offset + entrysize < fs->fs_llformat.availbytes)
+ {
+ /* Test the next entry */
+
+ direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
+ if (((direntry->flags & SMARTFS_DIRENT_EMPTY) !=
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) &&
+ ((direntry->flags & SMARTFS_DIRENT_ACTIVE) ==
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+ {
+ /* Count this entry */
+ count++;
+ }
+
+ /* Advance to next entry */
+
+ offset += entrysize;
+ }
+
+ /* Test if the count it zero. If it is, then we will release the sector */
+
+ if (count == 0)
+ {
+ /* Okay, to release the sector, we must find the sector that we
+ * are chained to and remove ourselves from the chain. First
+ * save our nextsector value so we can "unchain" ourselves.
+ */
+
+ nextsector = SMARTFS_NEXTSECTOR(header);
+
+ /* Now loop through the dir sectors to find ourselves in the chain */
+
+ sector = entry->dfirst;
+ readwrite.offset = 0;
+ readwrite.count = sizeof(struct smartfs_chain_header_s);
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ while (sector != SMARTFS_ERASEDSTATE_16BIT)
+ {
+ /* Read the header for the next sector */
+
+ readwrite.logsector = sector;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error reading sector %d\n", nextsector);
+ break;
+ }
+
+ /* Test if this sector "points" to us */
+
+ if (SMARTFS_NEXTSECTOR(header) == entry->dsector)
+ {
+ /* We found ourselves in the chain. Update the chain. */
+
+ SMARTFS_NEXTSECTOR(header) = nextsector;
+ readwrite.offset = offsetof(struct smartfs_chain_header_s, nextsector);
+ readwrite.count = sizeof(uint16_t);
+ readwrite.buffer = header->nextsector;
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error unchaining sector (%d)\n", nextsector);
+ goto errout;
+ }
+
+ /* Now release our sector */
+
+ ret = FS_IOCTL(fs, BIOC_FREESECT, (unsigned long) entry->dsector);
+ if (ret < 0)
+ {
+ fdbg("Error freeing sector %d\n", entry->dsector);
+ goto errout;
+ }
+
+ /* Break out of the loop, we are done! */
+
+ break;
+ }
+
+ /* Chain to the next sector */
+
+ sector = SMARTFS_NEXTSECTOR(header);
+ }
+ }
+ }
+
+ ret = OK;
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_countdirentries
+ *
+ * Description: Counts the number of items in the specified directory entry.
+ * This routine assumes you have validated the entry you are
+ * passing is in fact a directory sector, though it checks
+ * just in case you were stupid :-)
+ *
+ ****************************************************************************/
+
+int smartfs_countdirentries(struct smartfs_mountpt_s *fs,
+ struct smartfs_entry_s *entry)
+{
+ int ret;
+ uint16_t nextsector;
+ uint16_t offset;
+ uint16_t entrysize;
+ int count;
+ struct smartfs_entry_header_s *direntry;
+ struct smartfs_chain_header_s *header;
+ struct smart_read_write_s readwrite;
+
+ /* Walk through the directory's sectors and count entries */
+
+ count = 0;
+ nextsector = entry->firstsector;
+ while (nextsector != SMARTFS_ERASEDSTATE_16BIT)
+ {
+ /* Read the next sector into our buffer */
+
+ readwrite.logsector = nextsector;
+ readwrite.offset = 0;
+ readwrite.count = fs->fs_llformat.availbytes;
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error reading sector %d\n", nextsector);
+ break;
+ }
+
+ /* Validate this is a directory type sector */
+
+ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
+ if (header->type != SMARTFS_SECTOR_TYPE_DIR)
+ {
+ fdbg("Sector %d is not a DIR sector!\n", nextsector);
+ goto errout;
+ }
+
+ /* Loop for all entries in this sector and count them */
+
+ offset = sizeof(struct smartfs_chain_header_s);
+ entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize;
+ direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
+ while (offset + entrysize < readwrite.count)
+ {
+ if (((direntry->flags & SMARTFS_DIRENT_EMPTY) !=
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) &&
+ ((direntry->flags & SMARTFS_DIRENT_ACTIVE) ==
+ (SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
+ {
+ /* Count this entry */
+ count++;
+ }
+
+ offset += entrysize;
+ direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
+ }
+
+ /* Get the next sector from the header */
+
+ nextsector = SMARTFS_NEXTSECTOR(header);
+ }
+
+ ret = count;
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: smartfs_truncatefile
+ *
+ * Description: Truncates an existing file on the device so that it occupies
+ * zero bytes and can be completely re-written.
+ *
+ ****************************************************************************/
+
+int smartfs_truncatefile(struct smartfs_mountpt_s *fs,
+ struct smartfs_entry_s *entry)
+{
+ int ret;
+ uint16_t nextsector;
+ uint16_t sector;
+ struct smartfs_chain_header_s *header;
+ struct smart_read_write_s readwrite;
+
+ /* Walk through the directory's sectors and count entries */
+
+ nextsector = entry->firstsector;
+ header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
+
+ while (nextsector != SMARTFS_ERASEDSTATE_16BIT)
+ {
+ /* Read the next sector's header into our buffer */
+
+ readwrite.logsector = nextsector;
+ readwrite.offset = 0;
+ readwrite.count = sizeof(struct smartfs_chain_header_s);
+ readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
+ ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error reading sector %d header\n", nextsector);
+ goto errout;
+ }
+
+ /* Get the next chained sector */
+
+ sector = SMARTFS_NEXTSECTOR(header);
+
+ /* If this is the 1st sector of the file, then just overwrite
+ * the sector data with the erased state value. The underlying
+ * SMART block driver will detect this and release the old
+ * sector and create a new one with the new (blank) data.
+ */
+
+ if (nextsector == entry->firstsector)
+ {
+ /* Fill our buffer with erased data */
+
+ memset(fs->fs_rwbuffer, CONFIG_SMARTFS_ERASEDSTATE, fs->fs_llformat.availbytes);
+ header->type = SMARTFS_SECTOR_TYPE_FILE;
+
+ /* Now write the new sector data */
+
+ readwrite.count = fs->fs_llformat.availbytes;
+ ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
+ if (ret < 0)
+ {
+ fdbg("Error blanking 1st sector (%d) of file\n", nextsector);
+ goto errout;
+ }
+
+ /* Set the entry's data length to zero ... we just truncated */
+
+ entry->datlen = 0;
+ }
+ else
+ {
+ /* Not the 1st sector -- release it */
+
+ ret = FS_IOCTL(fs, BIOC_FREESECT, (unsigned long) nextsector);
+ if (ret < 0)
+ {
+ fdbg("Error freeing sector %d\n", nextsector);
+ goto errout;
+ }
+ }
+
+ /* Now move on to the next sector */
+
+ nextsector = sector;
+ }
+
+ ret = OK;
+
+errout:
+ return ret;
+}