summaryrefslogtreecommitdiff
path: root/nuttx/fs/smartfs/smartfs_smart.c
diff options
context:
space:
mode:
Diffstat (limited to 'nuttx/fs/smartfs/smartfs_smart.c')
-rw-r--r--nuttx/fs/smartfs/smartfs_smart.c1981
1 files changed, 1981 insertions, 0 deletions
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
+ ****************************************************************************/