2123 lines
56 KiB
C
2123 lines
56 KiB
C
/****************************************************************************
|
|
* 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 <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/mtd.h>
|
|
#include <nuttx/fs/smart.h>
|
|
|
|
#include "smartfs.h"
|
|
|
|
/****************************************************************************
|
|
* 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_fstat(FAR const struct file *filep,
|
|
FAR struct stat *buf);
|
|
static int smartfs_truncate(FAR struct file *filep, off_t length);
|
|
|
|
static int smartfs_opendir(FAR struct inode *mountpt,
|
|
FAR const char *relpath,
|
|
FAR struct fs_dirent_s *dir);
|
|
static int smartfs_readdir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir);
|
|
static int smartfs_rewinddir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir);
|
|
|
|
static int smartfs_bind(FAR struct inode *blkdriver,
|
|
FAR const void *data,
|
|
FAR void **handle);
|
|
static int smartfs_unbind(void *handle, FAR struct inode **blkdriver,
|
|
unsigned int flags);
|
|
static int smartfs_statfs(FAR struct inode *mountpt,
|
|
FAR struct statfs *buf);
|
|
|
|
static int smartfs_unlink(FAR struct inode *mountpt,
|
|
FAR const char *relpath);
|
|
static int smartfs_mkdir(FAR struct inode *mountpt,
|
|
FAR const char *relpath,
|
|
mode_t mode);
|
|
static int smartfs_rmdir(FAR struct inode *mountpt,
|
|
FAR const char *relpath);
|
|
static int smartfs_rename(FAR struct inode *mountpt,
|
|
FAR const char *oldrelpath,
|
|
const char *newrelpath);
|
|
static void smartfs_stat_common(FAR struct smartfs_mountpt_s *fs,
|
|
FAR struct smartfs_entry_s *entry,
|
|
FAR struct stat *buf);
|
|
static int smartfs_stat(FAR struct inode *mountpt,
|
|
FAR const char *relpath,
|
|
FAR struct stat *buf);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static uint8_t g_seminitialized = FALSE;
|
|
static sem_t g_sem;
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
/* 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_fstat, /* fstat */
|
|
smartfs_truncate, /* truncate */
|
|
|
|
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;
|
|
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
|
|
struct smart_read_write_s readwrite;
|
|
#endif
|
|
|
|
/* 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 */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Locate the directory entry for this path */
|
|
|
|
sf = (struct smartfs_ofile_s *)kmm_malloc(sizeof *sf);
|
|
if (sf == NULL)
|
|
{
|
|
ret = -ENOMEM;
|
|
goto errout_with_semaphore;
|
|
}
|
|
|
|
/* Allocate a sector buffer if CRC enabled in the MTD layer */
|
|
|
|
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
|
|
sf->buffer = (uint8_t *)kmm_malloc(fs->fs_llformat.availbytes);
|
|
if (sf->buffer == NULL)
|
|
{
|
|
/* Error ... no memory */
|
|
|
|
kmm_free(sf);
|
|
ret = -ENOMEM;
|
|
goto errout_with_semaphore;
|
|
}
|
|
|
|
sf->bflags = 0;
|
|
#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
|
|
|
|
sf->entry.name = NULL;
|
|
ret = smartfs_finddirentry(fs, &sf->entry, relpath, &parentdirsector,
|
|
&filename);
|
|
|
|
/* Three possibilities: (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 */
|
|
|
|
/* If O_TRUNC is specified and the file is opened for writing,
|
|
* then truncate the file. This operation requires that the file
|
|
* is writeable. O_TRUNC without write access is ignored.
|
|
*/
|
|
|
|
if ((oflags & (O_TRUNC | O_WROK)) == (O_TRUNC | O_WROK))
|
|
{
|
|
/* Truncate the file as part of the open */
|
|
|
|
ret = smartfs_shrinkfile(fs, sf, 0);
|
|
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, sf);
|
|
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;
|
|
|
|
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
|
|
|
|
/* When using sector buffering, current sector with its header should
|
|
* always be present in sf->buffer. Otherwise data corruption may arise
|
|
* when writing.
|
|
*/
|
|
|
|
if (sf->currsector != SMARTFS_ERASEDSTATE_16BIT)
|
|
{
|
|
readwrite.logsector = sf->currsector;
|
|
readwrite.offset = 0;
|
|
readwrite.count = fs->fs_llformat.availbytes;
|
|
readwrite.buffer = (uint8_t *) sf->buffer;
|
|
|
|
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
|
|
if (ret < 0)
|
|
{
|
|
ferr("ERROR: Error %d reading sector %d header\n",
|
|
ret, sf->currsector);
|
|
goto errout_with_buffer;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* 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 */
|
|
|
|
kmm_free(sf->entry.name);
|
|
sf->entry.name = NULL;
|
|
}
|
|
|
|
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
|
|
kmm_free(sf->buffer);
|
|
#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
|
|
kmm_free(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;
|
|
int ret;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
inode = filep->f_inode;
|
|
fs = inode->i_private;
|
|
sf = filep->f_priv;
|
|
|
|
/* Sync the file */
|
|
|
|
smartfs_sync(filep);
|
|
|
|
/* Take the semaphore */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* 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;
|
|
prevfile = nextfile;
|
|
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 */
|
|
|
|
kmm_free(sf->entry.name);
|
|
sf->entry.name = NULL;
|
|
}
|
|
|
|
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
|
|
if (sf->buffer)
|
|
{
|
|
kmm_free(sf->buffer);
|
|
}
|
|
#endif
|
|
|
|
kmm_free(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 */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return (ssize_t)ret;
|
|
}
|
|
|
|
/* 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 current 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)
|
|
{
|
|
ferr("ERROR: 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_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 */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return (ssize_t)ret;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* Test if we opened for APPEND mode. If we did, then seek to the
|
|
* end of the file.
|
|
*/
|
|
|
|
if (sf->oflags & O_APPEND)
|
|
{
|
|
ret = smartfs_seek_internal(fs, sf, 0, SEEK_END);
|
|
if (ret < 0)
|
|
{
|
|
ret = -EIO;
|
|
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. */
|
|
|
|
if (readwrite.count > 0)
|
|
{
|
|
ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
|
|
if (ret < 0)
|
|
{
|
|
ferr("ERROR: 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)
|
|
{
|
|
ferr("ERROR: 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.
|
|
*/
|
|
|
|
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
|
|
readwrite.count = fs->fs_llformat.availbytes - sf->curroffset;
|
|
if (readwrite.count > buflen)
|
|
{
|
|
readwrite.count = buflen;
|
|
}
|
|
|
|
memcpy(&sf->buffer[sf->curroffset], &buffer[byteswritten],
|
|
readwrite.count);
|
|
sf->bflags |= SMARTFS_BFLAG_DIRTY;
|
|
|
|
#else /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
|
|
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 */
|
|
|
|
if (readwrite.count > 0)
|
|
{
|
|
ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
|
|
if (ret < 0)
|
|
{
|
|
ferr("ERROR: Error %d writing sector %d data\n",
|
|
ret, sf->currsector);
|
|
goto errout_with_semaphore;
|
|
}
|
|
}
|
|
|
|
#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
|
|
|
|
/* 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 */
|
|
|
|
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
|
|
if (sf->curroffset == fs->fs_llformat.availbytes && buflen)
|
|
{
|
|
/* First get a new chained sector */
|
|
|
|
ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xffff);
|
|
if (ret < 0)
|
|
{
|
|
ferr("ERROR: 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 *) sf->buffer;
|
|
*((uint16_t *) header->nextsector) = (uint16_t) ret;
|
|
|
|
/* Now sync the file to write this sector out */
|
|
|
|
ret = smartfs_sync_internal(fs, sf);
|
|
if (ret != OK)
|
|
{
|
|
goto errout_with_semaphore;
|
|
}
|
|
|
|
/* Record the new sector in our tracking variables and
|
|
* reset the offset to "zero".
|
|
*/
|
|
|
|
if (sf->currsector == SMARTFS_NEXTSECTOR(header))
|
|
{
|
|
/* Error allocating logical sector! */
|
|
|
|
ferr("ERROR: Duplicate logical sector %d\n", sf->currsector);
|
|
}
|
|
|
|
sf->bflags = SMARTFS_BFLAG_DIRTY;
|
|
sf->currsector = SMARTFS_NEXTSECTOR(header);
|
|
sf->curroffset = sizeof(struct smartfs_chain_header_s);
|
|
memset(sf->buffer, CONFIG_SMARTFS_ERASEDSTATE,
|
|
fs->fs_llformat.availbytes);
|
|
header->type = SMARTFS_DIRENT_TYPE_FILE;
|
|
}
|
|
#else /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
|
|
|
|
if (sf->curroffset == fs->fs_llformat.availbytes)
|
|
{
|
|
/* Sync the file to write this sector out */
|
|
|
|
ret = smartfs_sync_internal(fs, sf);
|
|
if (ret != OK)
|
|
{
|
|
goto errout_with_semaphore;
|
|
}
|
|
|
|
/* Allocate a new sector if needed */
|
|
|
|
if (buflen > 0)
|
|
{
|
|
/* Allocate a new sector */
|
|
|
|
ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xffff);
|
|
if (ret < 0)
|
|
{
|
|
ferr("ERROR: 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)
|
|
{
|
|
ferr("ERROR: 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".
|
|
*/
|
|
|
|
if (sf->currsector == SMARTFS_NEXTSECTOR(header))
|
|
{
|
|
/* Error allocating logical sector! */
|
|
|
|
ferr("ERROR: Duplicate logical sector %d\n",
|
|
sf->currsector);
|
|
}
|
|
|
|
sf->currsector = SMARTFS_NEXTSECTOR(header);
|
|
sf->curroffset = sizeof(struct smartfs_chain_header_s);
|
|
}
|
|
}
|
|
#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
|
|
}
|
|
|
|
ret = byteswritten;
|
|
|
|
errout_with_semaphore:
|
|
smartfs_semgive(fs);
|
|
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 */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return (off_t)ret;
|
|
}
|
|
|
|
/* 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;
|
|
|
|
/* 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 */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct smartfs_ofile_s *sf;
|
|
|
|
finfo("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 */
|
|
|
|
sf = oldp->f_priv;
|
|
|
|
DEBUGASSERT(sf != NULL);
|
|
|
|
/* Just increment the reference count on the ofile */
|
|
|
|
sf->crefs++;
|
|
newp->f_priv = (FAR void *)sf;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_fstat
|
|
*
|
|
* Description:
|
|
* Obtain information about an open file associated with the file
|
|
* descriptor 'fd', and will write it to the area pointed to by 'buf'.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int smartfs_fstat(FAR const struct file *filep, FAR struct stat *buf)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
FAR struct smartfs_ofile_s *sf;
|
|
int ret;
|
|
|
|
DEBUGASSERT(filep != NULL && buf != NULL);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
|
|
sf = filep->f_priv;
|
|
inode = filep->f_inode;
|
|
|
|
fs = inode->i_private;
|
|
DEBUGASSERT(fs != NULL);
|
|
|
|
/* Take the semaphore */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Return information about the directory entry in the stat structure */
|
|
|
|
smartfs_stat_common(fs, &sf->entry, buf);
|
|
smartfs_semgive(fs);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_truncate
|
|
*
|
|
* Description:
|
|
* Set the length of the open, regular file associated with the file
|
|
* structure 'filep' to 'length'.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int smartfs_truncate(FAR struct file *filep, off_t length)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
FAR struct smartfs_ofile_s *sf;
|
|
off_t oldsize;
|
|
int ret;
|
|
|
|
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 */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Test the permissions. Only allow truncation if the file was opened with
|
|
* write flags.
|
|
*/
|
|
|
|
if ((sf->oflags & O_WROK) == 0)
|
|
{
|
|
ret = -EACCES;
|
|
goto errout_with_semaphore;
|
|
}
|
|
|
|
/* Are we shrinking the file? Or extending it? */
|
|
|
|
oldsize = sf->entry.datlen;
|
|
if (oldsize == length)
|
|
{
|
|
/* Let's not and say we did */
|
|
|
|
ret = OK;
|
|
}
|
|
else if (oldsize > length)
|
|
{
|
|
/* We are shrinking the file */
|
|
|
|
ret = smartfs_shrinkfile(fs, sf, length);
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise we are extending the file. This is essentially the same
|
|
* as a write except that (1) we write zeros and (2) we don't update
|
|
* the file position.
|
|
*/
|
|
|
|
ret = smartfs_extendfile(fs, sf, length);
|
|
}
|
|
|
|
errout_with_semaphore:
|
|
|
|
/* Relinquish exclusive access */
|
|
|
|
smartfs_semgive(fs);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_opendir
|
|
*
|
|
* Description: Open a directory for read access
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int smartfs_opendir(FAR struct inode *mountpt,
|
|
FAR const char *relpath,
|
|
FAR 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 */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
kmm_free(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 */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* 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 + entrysize >=
|
|
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 *)
|
|
kmm_zalloc(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)
|
|
{
|
|
nxsem_init(&g_sem, 0, 0); /* Initialize the semaphore that controls access */
|
|
g_seminitialized = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* Take the semaphore for the mount */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
kmm_free(fs);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
smartfs_semgive(fs);
|
|
kmm_free(fs);
|
|
return ret;
|
|
}
|
|
|
|
*handle = (FAR void *)fs;
|
|
smartfs_semgive(fs);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_unbind
|
|
*
|
|
* Description: This implements the filesystem portion of the umount
|
|
* operation.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int smartfs_unbind(FAR void *handle, FAR struct inode **blkdriver,
|
|
unsigned int flags)
|
|
{
|
|
FAR struct smartfs_mountpt_s *fs = (FAR 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 */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if (fs->fs_head != NULL)
|
|
{
|
|
/* We cannot unmount now.. there are open files */
|
|
|
|
smartfs_semgive(fs);
|
|
|
|
/* This implementation currently only supports unmounting if there are
|
|
* no open file references.
|
|
*/
|
|
|
|
return (flags != 0) ? -ENOSYS : -EBUSY;
|
|
}
|
|
else
|
|
{
|
|
/* Unmount ... close the block driver */
|
|
|
|
ret = smartfs_unmount(fs);
|
|
}
|
|
|
|
smartfs_semgive(fs);
|
|
kmm_free(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;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(mountpt && mountpt->i_private);
|
|
|
|
/* Get the mountpoint private data from the inode structure */
|
|
|
|
fs = mountpt->i_private;
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* 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;
|
|
if (buf->f_blocks == 65535)
|
|
{
|
|
buf->f_blocks++;
|
|
}
|
|
|
|
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;
|
|
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;
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* 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:
|
|
if (entry.name != NULL)
|
|
{
|
|
kmm_free(entry.name);
|
|
}
|
|
|
|
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;
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* 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-existent dir segment) */
|
|
|
|
goto errout_with_semaphore;
|
|
}
|
|
|
|
/* Check mode */
|
|
|
|
/* Create the directory */
|
|
|
|
ret = smartfs_createentry(fs, parentdirsector, filename,
|
|
SMARTFS_DIRENT_TYPE_DIR, mode, &entry, 0xffff, NULL);
|
|
if (ret != OK)
|
|
{
|
|
goto errout_with_semaphore;
|
|
}
|
|
|
|
ret = OK;
|
|
}
|
|
|
|
errout_with_semaphore:
|
|
if (entry.name != NULL)
|
|
{
|
|
/* Free the filename space allocation */
|
|
|
|
kmm_free(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;
|
|
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 */
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* 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 empty */
|
|
|
|
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:
|
|
if (entry.name != NULL)
|
|
{
|
|
kmm_free(entry.name);
|
|
}
|
|
|
|
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;
|
|
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;
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* 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. */
|
|
|
|
ret = smartfs_finddirentry(fs, &newentry, newrelpath, &newparentdirsector,
|
|
&newfilename);
|
|
if (ret == OK)
|
|
{
|
|
/* It is an error if the directory entry at newrelpath already
|
|
* exists. The necessary steps to avoid this case should have been
|
|
* handled by higher level logic in the VFS.
|
|
*/
|
|
|
|
ret = -EEXIST;
|
|
goto errout_with_semaphore;
|
|
}
|
|
|
|
/* 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,
|
|
NULL);
|
|
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)
|
|
{
|
|
ferr("ERROR: 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)
|
|
{
|
|
ferr("ERROR: 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)
|
|
{
|
|
kmm_free(oldentry.name);
|
|
oldentry.name = NULL;
|
|
}
|
|
|
|
if (newentry.name != NULL)
|
|
{
|
|
kmm_free(newentry.name);
|
|
newentry.name = NULL;
|
|
}
|
|
|
|
smartfs_semgive(fs);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_stat_common
|
|
*
|
|
* Description:
|
|
* Return information about a directory entry
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void smartfs_stat_common(FAR struct smartfs_mountpt_s *fs,
|
|
FAR struct smartfs_entry_s *entry,
|
|
FAR struct stat *buf)
|
|
{
|
|
/* 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;
|
|
}
|
|
else
|
|
{
|
|
/* Mask out the file type */
|
|
|
|
buf->st_mode = entry->flags & ~S_IFMT;
|
|
|
|
/* Add the file type based on the SmartFS entry flags */
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_stat
|
|
*
|
|
* Description:
|
|
* Return information about a file or directory
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int smartfs_stat(FAR struct inode *mountpt, FAR const char *relpath,
|
|
FAR struct stat *buf)
|
|
{
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
FAR struct smartfs_entry_s entry;
|
|
FAR const char *filename;
|
|
uint16_t parentdirsector;
|
|
int ret;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(mountpt && mountpt->i_private);
|
|
|
|
/* Get the mountpoint private data from the inode structure */
|
|
|
|
fs = mountpt->i_private;
|
|
|
|
ret = smartfs_semtake(fs);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* Return information about the directory entry in the stat structure */
|
|
|
|
smartfs_stat_common(fs, &entry, buf);
|
|
ret = OK;
|
|
|
|
errout_with_semaphore:
|
|
if (entry.name != NULL)
|
|
{
|
|
kmm_free(entry.name);
|
|
entry.name = NULL;
|
|
}
|
|
|
|
smartfs_semgive(fs);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|