mirror of
https://github.com/apache/nuttx.git
synced 2026-05-21 21:34:07 +08:00
2fa3b72b3c
When writing to the next sector after the forward position has been written by seek, the old sector buffer is used, which may corrupt the file system. Therefore, the sector buffer must always be updated after a writing by seek. Signed-off-by: SPRESENSE <41312067+SPRESENSE@users.noreply.github.com>
2216 lines
58 KiB
C
2216 lines
58 KiB
C
/****************************************************************************
|
|
* fs/smartfs/smartfs_smart.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* 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/mutex.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/fs/fat.h>
|
|
#include <nuttx/fs/ioctl.h>
|
|
#include <nuttx/mtd/mtd.h>
|
|
#include <nuttx/fs/smart.h>
|
|
|
|
#include "inode/inode.h"
|
|
#include "smartfs.h"
|
|
#include "fs_heap.h"
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct smartfs_dir_s
|
|
{
|
|
struct fs_dirent_s fs_base; /* VFS directory structure */
|
|
uint16_t fs_firstsector; /* First sector of directory list */
|
|
uint16_t fs_currsector; /* Current sector of directory list */
|
|
uint16_t fs_curroffset; /* Current offset within current sector */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static int smartfs_open(FAR struct file *filep, FAR 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, FAR char *buffer,
|
|
size_t buflen);
|
|
static ssize_t smartfs_write(FAR struct file *filep, FAR 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_closedir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir);
|
|
static int smartfs_readdir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir,
|
|
FAR struct dirent *dentry);
|
|
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(FAR 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,
|
|
FAR 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 mutex_t g_lock = NXMUTEX_INITIALIZER;
|
|
|
|
/****************************************************************************
|
|
* 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 g_smartfs_operations =
|
|
{
|
|
smartfs_open, /* open */
|
|
smartfs_close, /* close */
|
|
smartfs_read, /* read */
|
|
smartfs_write, /* write */
|
|
smartfs_seek, /* seek */
|
|
smartfs_ioctl, /* ioctl */
|
|
NULL, /* mmap */
|
|
smartfs_truncate, /* truncate */
|
|
NULL, /* poll */
|
|
NULL, /* readv */
|
|
NULL, /* writev */
|
|
|
|
smartfs_sync, /* sync */
|
|
smartfs_dup, /* dup */
|
|
smartfs_fstat, /* fstat */
|
|
NULL, /* fchstat */
|
|
|
|
smartfs_opendir, /* opendir */
|
|
smartfs_closedir, /* closedir */
|
|
smartfs_readdir, /* readdir */
|
|
smartfs_rewinddir, /* rewinddir */
|
|
|
|
smartfs_bind, /* bind */
|
|
smartfs_unbind, /* unbind */
|
|
smartfs_statfs, /* statfs */
|
|
|
|
smartfs_unlink, /* unlink */
|
|
smartfs_mkdir, /* mkdir */
|
|
smartfs_rmdir, /* rmdir */
|
|
smartfs_rename, /* rename */
|
|
smartfs_stat, /* stat */
|
|
NULL /* chstat */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_open
|
|
****************************************************************************/
|
|
|
|
static int smartfs_open(FAR struct file *filep, FAR const char *relpath,
|
|
int oflags, mode_t mode)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
int ret;
|
|
uint16_t parentdirsector;
|
|
size_t pathlen;
|
|
FAR const char *filename;
|
|
FAR 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 lock */
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Locate the directory entry for this path */
|
|
|
|
pathlen = strlen(relpath) + 1;
|
|
sf = fs_heap_malloc(sizeof(*sf) + pathlen - 1);
|
|
if (sf == NULL)
|
|
{
|
|
ret = -ENOMEM;
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
/* Save the full path to smartfs_ofile_s stuctrue. This is needed
|
|
* to FIOC_FILEPATH ioctlc all support.
|
|
*/
|
|
|
|
memcpy(sf->path, relpath, pathlen);
|
|
|
|
/* Allocate a sector buffer if CRC enabled in the MTD layer */
|
|
|
|
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
|
|
sf->buffer = fs_heap_malloc(fs->fs_llformat.availbytes);
|
|
if (sf->buffer == NULL)
|
|
{
|
|
/* Error ... no memory */
|
|
|
|
fs_heap_free(sf);
|
|
ret = -ENOMEM;
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
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 = (FAR 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_lock;
|
|
|
|
errout_with_buffer:
|
|
if (sf->entry.name != NULL)
|
|
{
|
|
/* Free the space for the name too */
|
|
|
|
fs_heap_free(sf->entry.name);
|
|
sf->entry.name = NULL;
|
|
}
|
|
|
|
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
|
|
fs_heap_free(sf->buffer);
|
|
#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
|
|
fs_heap_free(sf);
|
|
|
|
errout_with_lock:
|
|
nxmutex_unlock(&g_lock);
|
|
if (ret == -EINVAL)
|
|
{
|
|
ret = -EIO;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_close
|
|
****************************************************************************/
|
|
|
|
static int smartfs_close(FAR struct file *filep)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
FAR struct smartfs_ofile_s *sf;
|
|
FAR struct smartfs_ofile_s *nextfile;
|
|
FAR struct smartfs_ofile_s *prevfile;
|
|
int ret;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(filep->f_priv != 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 lock */
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
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 */
|
|
|
|
fs_heap_free(sf->entry.name);
|
|
sf->entry.name = NULL;
|
|
}
|
|
|
|
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
|
|
if (sf->buffer)
|
|
{
|
|
fs_heap_free(sf->buffer);
|
|
}
|
|
#endif
|
|
|
|
fs_heap_free(sf);
|
|
|
|
okout:
|
|
nxmutex_unlock(&g_lock);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_read
|
|
****************************************************************************/
|
|
|
|
static ssize_t smartfs_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
FAR struct smartfs_ofile_s *sf;
|
|
struct smart_read_write_s readwrite;
|
|
FAR 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);
|
|
|
|
/* 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 lock */
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
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 = (FAR 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_lock;
|
|
}
|
|
|
|
/* Point header to the read data to get used byte count */
|
|
|
|
header = (FAR struct smartfs_chain_header_s *)fs->fs_rwbuffer;
|
|
|
|
/* Get number of used bytes in this sector */
|
|
|
|
bytesinsector = SMARTFS_USED(header);
|
|
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 bytes to 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_lock:
|
|
nxmutex_unlock(&g_lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_write
|
|
****************************************************************************/
|
|
|
|
static ssize_t smartfs_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
FAR struct smartfs_ofile_s *sf;
|
|
struct smart_read_write_s readwrite;
|
|
FAR struct smartfs_chain_header_s *header;
|
|
size_t byteswritten;
|
|
int ret;
|
|
|
|
DEBUGASSERT(filep->f_priv != 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 lock */
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
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_lock;
|
|
}
|
|
|
|
/* 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_lock;
|
|
}
|
|
}
|
|
|
|
/* First test if we are overwriting an existing location or writing to
|
|
* a new one.
|
|
*/
|
|
|
|
header = (FAR 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 = (FAR 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_lock;
|
|
}
|
|
|
|
/* 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 = (FAR 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_lock;
|
|
}
|
|
|
|
/* Now get the chained sector info and reset the offset */
|
|
|
|
sf->curroffset = sizeof(struct smartfs_chain_header_s);
|
|
sf->currsector = SMARTFS_NEXTSECTOR(header);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
|
|
|
|
/* If data is written to a forward position using seek, the sector
|
|
* buffer must be updated because it may be referenced later.
|
|
*/
|
|
|
|
if (byteswritten > 0)
|
|
{
|
|
readwrite.logsector = sf->currsector;
|
|
readwrite.offset = 0;
|
|
readwrite.count = fs->fs_llformat.availbytes;
|
|
readwrite.buffer = (FAR uint8_t *)sf->buffer;
|
|
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long)&readwrite);
|
|
if (ret < 0)
|
|
{
|
|
ferr("ERROR: Error %d reading sector %d\n", ret, sf->currsector);
|
|
goto errout_with_lock;
|
|
}
|
|
}
|
|
|
|
#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
|
|
|
|
/* 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 = (FAR 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_lock;
|
|
}
|
|
}
|
|
|
|
#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_lock;
|
|
}
|
|
|
|
/* Copy the new sector to the old one and chain it */
|
|
|
|
header = (FAR struct smartfs_chain_header_s *)sf->buffer;
|
|
SMARTFS_SET_NEXTSECTOR(header, ret);
|
|
|
|
/* Now sync the file to write this sector out */
|
|
|
|
ret = smartfs_sync_internal(fs, sf);
|
|
if (ret != OK)
|
|
{
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
/* 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_lock;
|
|
}
|
|
|
|
/* 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_lock;
|
|
}
|
|
|
|
/* Copy the new sector to the old one and chain it */
|
|
|
|
header = (FAR struct smartfs_chain_header_s *)fs->fs_rwbuffer;
|
|
SMARTFS_SET_NEXTSECTOR(header, ret);
|
|
readwrite.offset = offsetof(struct smartfs_chain_header_s,
|
|
nextsector);
|
|
readwrite.buffer = (FAR 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_lock;
|
|
}
|
|
|
|
/* 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_lock:
|
|
nxmutex_unlock(&g_lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_seek
|
|
****************************************************************************/
|
|
|
|
static off_t smartfs_seek(FAR struct file *filep, off_t offset, int whence)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
FAR struct smartfs_ofile_s *sf;
|
|
int ret;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(filep->f_priv != 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 lock */
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
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;
|
|
}
|
|
|
|
nxmutex_unlock(&g_lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_ioctl
|
|
****************************************************************************/
|
|
|
|
static int smartfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|
{
|
|
FAR struct smartfs_ofile_s *priv;
|
|
FAR struct inode *inode;
|
|
int ret;
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
priv = filep->f_priv;
|
|
inode = filep->f_inode;
|
|
|
|
switch (cmd)
|
|
{
|
|
case FIOC_FILEPATH:
|
|
{
|
|
FAR char *path = (FAR char *)(uintptr_t)arg;
|
|
ret = inode_getpath(inode, path, PATH_MAX);
|
|
if (ret >= 0)
|
|
{
|
|
size_t len = strlen(path);
|
|
if (path[len - 1] != '/')
|
|
{
|
|
path[len++] = '/';
|
|
}
|
|
|
|
strlcat(path, priv->path, PATH_MAX - len);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
ret = -ENOTTY;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_sync
|
|
*
|
|
* Description: Synchronize the file state on disk to match internal, in-
|
|
* memory state.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int smartfs_sync(FAR struct file *filep)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
FAR struct smartfs_ofile_s *sf;
|
|
int ret;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(filep->f_priv != 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 lock */
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = smartfs_sync_internal(fs, sf);
|
|
|
|
nxmutex_unlock(&g_lock);
|
|
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 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 = 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(buf != NULL);
|
|
|
|
/* Recover our private data from the struct file instance */
|
|
|
|
DEBUGASSERT(filep->f_priv != NULL);
|
|
sf = filep->f_priv;
|
|
inode = filep->f_inode;
|
|
|
|
fs = inode->i_private;
|
|
DEBUGASSERT(fs != NULL);
|
|
|
|
/* Take the lock */
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Return information about the directory entry in the stat structure */
|
|
|
|
smartfs_stat_common(fs, &sf->entry, buf);
|
|
nxmutex_unlock(&g_lock);
|
|
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);
|
|
|
|
/* 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 lock */
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
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_lock;
|
|
}
|
|
|
|
/* 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_lock:
|
|
|
|
/* Relinquish exclusive access */
|
|
|
|
nxmutex_unlock(&g_lock);
|
|
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)
|
|
{
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
FAR struct smartfs_dir_s *sdir;
|
|
int ret;
|
|
struct smartfs_entry_s entry;
|
|
uint16_t parentdirsector;
|
|
FAR const char *filename;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
|
|
|
|
/* Recover our private data from the inode instance */
|
|
|
|
fs = mountpt->i_private;
|
|
sdir = fs_heap_zalloc(sizeof(*sdir));
|
|
if (sdir == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Take the lock */
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_sdir;
|
|
}
|
|
|
|
/* Search for the path on the volume */
|
|
|
|
entry.name = NULL;
|
|
ret = smartfs_finddirentry(fs, &entry, relpath, &parentdirsector,
|
|
&filename);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
/* Populate our private data in the fs_dirent_s struct */
|
|
|
|
sdir->fs_firstsector = entry.firstsector;
|
|
sdir->fs_currsector = entry.firstsector;
|
|
sdir->fs_curroffset = sizeof(struct smartfs_chain_header_s);
|
|
|
|
*dir = &sdir->fs_base;
|
|
nxmutex_unlock(&g_lock);
|
|
return OK;
|
|
|
|
errout_with_lock:
|
|
|
|
/* If space for the entry name was allocated, then free it */
|
|
|
|
if (entry.name != NULL)
|
|
{
|
|
fs_heap_free(entry.name);
|
|
entry.name = NULL;
|
|
}
|
|
|
|
nxmutex_unlock(&g_lock);
|
|
|
|
errout_with_sdir:
|
|
fs_heap_free(sdir);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_closedir
|
|
*
|
|
* Description: Close directory
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int smartfs_closedir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir)
|
|
{
|
|
DEBUGASSERT(dir);
|
|
fs_heap_free(dir);
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_readdir
|
|
*
|
|
* Description: Read the next directory entry
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int smartfs_readdir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir,
|
|
FAR struct dirent *dentry)
|
|
{
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
FAR struct smartfs_dir_s *sdir;
|
|
FAR struct smartfs_chain_header_s *header;
|
|
struct smart_read_write_s readwrite;
|
|
FAR struct smartfs_entry_header_s *entry;
|
|
uint16_t entrysize;
|
|
int ret;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
|
|
|
|
/* Recover our private data from the inode instance */
|
|
|
|
fs = mountpt->i_private;
|
|
sdir = (FAR struct smartfs_dir_s *)dir;
|
|
|
|
/* Take the lock */
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
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 (sdir->fs_currsector != SMARTFS_ERASEDSTATE_16BIT)
|
|
{
|
|
/* Read the logical sector */
|
|
|
|
readwrite.logsector = sdir->fs_currsector;
|
|
readwrite.count = fs->fs_llformat.availbytes;
|
|
readwrite.buffer = (FAR uint8_t *)fs->fs_rwbuffer;
|
|
readwrite.offset = 0;
|
|
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long)&readwrite);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
/* Now search for entries, starting at curroffset */
|
|
|
|
/* Note: directories don't use the header's used field
|
|
* so we search all possilble directory entries.
|
|
*/
|
|
|
|
while (sdir->fs_curroffset + entrysize < ret)
|
|
{
|
|
/* Point to next entry */
|
|
|
|
entry = (FAR struct smartfs_entry_header_s *)&fs->fs_rwbuffer[
|
|
sdir->fs_curroffset];
|
|
|
|
/* Test if this entry is valid and active */
|
|
|
|
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
|
|
if (((smartfs_rdle16(&entry->flags)
|
|
& SMARTFS_DIRENT_EMPTY) ==
|
|
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) ||
|
|
((smartfs_rdle16(&entry->flags)
|
|
& SMARTFS_DIRENT_ACTIVE) !=
|
|
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
|
|
#else
|
|
if (((entry->flags & SMARTFS_DIRENT_EMPTY) ==
|
|
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) ||
|
|
((entry->flags & SMARTFS_DIRENT_ACTIVE) !=
|
|
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
|
|
#endif
|
|
{
|
|
/* This entry isn't valid, skip it */
|
|
|
|
sdir->fs_curroffset += entrysize;
|
|
continue;
|
|
}
|
|
|
|
/* Entry found! Report it */
|
|
|
|
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
|
|
if ((smartfs_rdle16(&entry->flags) & SMARTFS_DIRENT_TYPE) ==
|
|
SMARTFS_DIRENT_TYPE_DIR)
|
|
#else
|
|
if ((entry->flags & SMARTFS_DIRENT_TYPE) ==
|
|
SMARTFS_DIRENT_TYPE_DIR)
|
|
#endif
|
|
{
|
|
dentry->d_type = DTYPE_DIRECTORY;
|
|
}
|
|
else
|
|
{
|
|
dentry->d_type = DTYPE_FILE;
|
|
}
|
|
|
|
/* Copy the entry name to dirent */
|
|
|
|
strlcpy(dentry->d_name, entry->name, sizeof(dentry->d_name));
|
|
|
|
/* Now advance to the next entry */
|
|
|
|
sdir->fs_curroffset += entrysize;
|
|
if (sdir->fs_curroffset + entrysize >=
|
|
fs->fs_llformat.availbytes)
|
|
{
|
|
/* We advanced past the end of the sector. Go to next sector */
|
|
|
|
sdir->fs_curroffset =
|
|
sizeof(struct smartfs_chain_header_s);
|
|
header = (FAR struct smartfs_chain_header_s *)fs->fs_rwbuffer;
|
|
sdir->fs_currsector = SMARTFS_NEXTSECTOR(header);
|
|
}
|
|
|
|
/* Now exit */
|
|
|
|
ret = OK;
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
/* 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 = (FAR struct smartfs_chain_header_s *)fs->fs_rwbuffer;
|
|
sdir->fs_curroffset = sizeof(struct smartfs_chain_header_s);
|
|
sdir->fs_currsector = SMARTFS_NEXTSECTOR(header);
|
|
}
|
|
|
|
/* If we arrive here, then there are no more entries */
|
|
|
|
ret = -ENOENT;
|
|
|
|
errout_with_lock:
|
|
nxmutex_unlock(&g_lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_rewindir
|
|
*
|
|
* Description: Reset directory read to the first entry
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int smartfs_rewinddir(FAR struct inode *mountpt,
|
|
FAR struct fs_dirent_s *dir)
|
|
{
|
|
FAR struct smartfs_dir_s *sdir = (FAR struct smartfs_dir_s *)dir;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
|
|
|
|
/* Reset the directory to the first entry */
|
|
|
|
sdir->fs_currsector = sdir->fs_firstsector;
|
|
sdir->fs_curroffset = sizeof(struct smartfs_chain_header_s);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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, FAR const void *data,
|
|
FAR void **handle)
|
|
{
|
|
FAR 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 = (FAR struct smartfs_mountpt_s *)
|
|
fs_heap_zalloc(sizeof(struct smartfs_mountpt_s));
|
|
if (!fs)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
if (ret < 0)
|
|
{
|
|
fs_heap_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)
|
|
{
|
|
nxmutex_unlock(&g_lock);
|
|
fs_heap_free(fs);
|
|
return ret;
|
|
}
|
|
|
|
*handle = fs;
|
|
nxmutex_unlock(&g_lock);
|
|
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 = nxmutex_lock(&g_lock);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if (fs->fs_head != NULL)
|
|
{
|
|
/* We cannot unmount now.. there are open files */
|
|
|
|
nxmutex_unlock(&g_lock);
|
|
|
|
/* 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);
|
|
}
|
|
|
|
nxmutex_unlock(&g_lock);
|
|
fs_heap_free(fs);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_statfs
|
|
*
|
|
* Description: Return filesystem statistics
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int smartfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
|
|
{
|
|
FAR 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 = nxmutex_lock(&g_lock);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Implement the logic!! */
|
|
|
|
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;
|
|
|
|
nxmutex_unlock(&g_lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_unlink
|
|
*
|
|
* Description: Remove a file
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int smartfs_unlink(FAR struct inode *mountpt, FAR const char *relpath)
|
|
{
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
int ret;
|
|
struct smartfs_entry_s entry;
|
|
FAR 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 = nxmutex_lock(&g_lock);
|
|
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_lock;
|
|
}
|
|
|
|
/* 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_lock;
|
|
}
|
|
|
|
ret = OK;
|
|
|
|
errout_with_lock:
|
|
if (entry.name != NULL)
|
|
{
|
|
fs_heap_free(entry.name);
|
|
}
|
|
|
|
nxmutex_unlock(&g_lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_mkdir
|
|
*
|
|
* Description: Create a directory
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int smartfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath,
|
|
mode_t mode)
|
|
{
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
int ret;
|
|
struct smartfs_entry_s entry;
|
|
uint16_t parentdirsector;
|
|
FAR const char *filename;
|
|
|
|
/* Sanity checks */
|
|
|
|
DEBUGASSERT(mountpt && mountpt->i_private);
|
|
|
|
/* Get the mountpoint private data from the inode structure */
|
|
|
|
fs = mountpt->i_private;
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
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_lock;
|
|
}
|
|
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_lock;
|
|
}
|
|
|
|
/* 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_lock;
|
|
}
|
|
|
|
ret = OK;
|
|
}
|
|
|
|
errout_with_lock:
|
|
if (entry.name != NULL)
|
|
{
|
|
/* Free the filename space allocation */
|
|
|
|
fs_heap_free(entry.name);
|
|
entry.name = NULL;
|
|
}
|
|
|
|
nxmutex_unlock(&g_lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_rmdir
|
|
*
|
|
* Description: Remove a directory
|
|
*
|
|
****************************************************************************/
|
|
|
|
int smartfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath)
|
|
{
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
int ret;
|
|
struct smartfs_entry_s entry;
|
|
FAR 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 lock */
|
|
|
|
ret = nxmutex_lock(&g_lock);
|
|
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_lock;
|
|
}
|
|
|
|
/* TODO: Need to check permissions? */
|
|
|
|
/* Check if the directory is empty */
|
|
|
|
ret = smartfs_countdirentries(fs, &entry);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
/* Only continue if there are zero entries in the directory */
|
|
|
|
if (ret != 0)
|
|
{
|
|
ret = -ENOTEMPTY;
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
/* Okay, we are clear to delete the directory. Use the deleteentry
|
|
* routine.
|
|
*/
|
|
|
|
ret = smartfs_deleteentry(fs, &entry);
|
|
if (ret < 0)
|
|
{
|
|
goto errout_with_lock;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Just report the error */
|
|
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
ret = OK;
|
|
|
|
errout_with_lock:
|
|
if (entry.name != NULL)
|
|
{
|
|
fs_heap_free(entry.name);
|
|
}
|
|
|
|
nxmutex_unlock(&g_lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: smartfs_rename
|
|
*
|
|
* Description: Rename a file or directory
|
|
*
|
|
****************************************************************************/
|
|
|
|
int smartfs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath,
|
|
FAR const char *newrelpath)
|
|
{
|
|
FAR struct smartfs_mountpt_s *fs;
|
|
int ret;
|
|
struct smartfs_entry_s oldentry;
|
|
uint16_t oldparentdirsector;
|
|
FAR const char *oldfilename;
|
|
struct smartfs_entry_s newentry;
|
|
uint16_t newparentdirsector;
|
|
FAR const char *newfilename;
|
|
mode_t mode;
|
|
uint16_t type;
|
|
FAR 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 = nxmutex_lock(&g_lock);
|
|
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_lock;
|
|
}
|
|
|
|
/* 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_lock;
|
|
}
|
|
|
|
/* 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_lock;
|
|
}
|
|
|
|
/* Now mark the old entry as inactive */
|
|
|
|
readwrite.logsector = oldentry.dsector;
|
|
readwrite.offset = 0;
|
|
readwrite.count = fs->fs_llformat.availbytes;
|
|
readwrite.buffer = (FAR 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_lock;
|
|
}
|
|
|
|
direntry = (FAR struct smartfs_entry_header_s *)
|
|
&fs->fs_rwbuffer[oldentry.doffset];
|
|
#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
|
|
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
|
|
smartfs_wrle16(&direntry->flags,
|
|
smartfs_rdle16(&direntry->flags)
|
|
& ~SMARTFS_DIRENT_ACTIVE);
|
|
#else
|
|
direntry->flags &= ~SMARTFS_DIRENT_ACTIVE;
|
|
#endif
|
|
#else /* CONFIG_SMARTFS_ERASEDSTATE == 0xff */
|
|
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
|
|
smartfs_wrle16(&direntry->flags,
|
|
smartfs_rdle16(&direntry->flags)
|
|
| SMARTFS_DIRENT_ACTIVE);
|
|
#else
|
|
direntry->flags |= SMARTFS_DIRENT_ACTIVE;
|
|
#endif
|
|
#endif /* CONFIG_SMARTFS_ERASEDSTATE == 0xff */
|
|
|
|
/* Now write the updated flags back to the device */
|
|
|
|
readwrite.offset = oldentry.doffset;
|
|
readwrite.count = sizeof(direntry->flags);
|
|
readwrite.buffer = (FAR 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_lock;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Trying to create in a directory that doesn't exist */
|
|
|
|
ret = -ENOENT;
|
|
goto errout_with_lock;
|
|
}
|
|
|
|
ret = OK;
|
|
|
|
errout_with_lock:
|
|
if (oldentry.name != NULL)
|
|
{
|
|
fs_heap_free(oldentry.name);
|
|
oldentry.name = NULL;
|
|
}
|
|
|
|
if (newentry.name != NULL)
|
|
{
|
|
fs_heap_free(newentry.name);
|
|
newentry.name = NULL;
|
|
}
|
|
|
|
nxmutex_unlock(&g_lock);
|
|
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;
|
|
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 = nxmutex_lock(&g_lock);
|
|
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_lock;
|
|
}
|
|
|
|
/* Return information about the directory entry in the stat structure */
|
|
|
|
smartfs_stat_common(fs, &entry, buf);
|
|
ret = OK;
|
|
|
|
errout_with_lock:
|
|
if (entry.name != NULL)
|
|
{
|
|
fs_heap_free(entry.name);
|
|
entry.name = NULL;
|
|
}
|
|
|
|
nxmutex_unlock(&g_lock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|