diff options
Diffstat (limited to 'nuttx/fs/smartfs/smartfs_smart.c')
-rw-r--r-- | nuttx/fs/smartfs/smartfs_smart.c | 1981 |
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 + ****************************************************************************/ |