nuttx/fs/smartfs/smartfs_utils.c
Alin Jerpelea 3e45517599 Fix buffer overrun and memory leak on smartfs and improvement to cxd56xx
Author: Alan Carvalho de Assis <acassis@gmail.com>

    Check all .c and .h against nxstyle and fix it.

Author: Alin Jerpelea <alin.jerpelea@sony.com>

    fs: smartfs: Fix over capacity write

    When the remaining capacity of flash is one sector, if a new root
    directory is created by file open, then the root directory's chain is
    broken and it causes to SmartFS filesystem crash. Once this fatal
    problem occurs, it's impossible to recover even if the system reboot.
    Fix it by finally update link of root directory.

    fs: smartfs: Fix buffer overrun

    fs: smartfs: Fix uninitialized variable warnings

    fs: smartfs: Memory leak fix

    boards: cxd56xx: Update spresense board.h

    - Fix PMIC assignment
    - Add specific pin configurations for spresense
    - Remove unnecessary definitions

    arch: cxd56xx: Add ITM syslog init at startup

    arch: cxd56xx: Enable DMA settings dynamically
2020-01-08 07:51:11 -03:00

2128 lines
65 KiB
C

/****************************************************************************
* fs/smartfs/smartfs_utils.c
*
* Copyright (C) 2013-2014 Ken Pettit. All rights reserved.
* Author: Ken Pettit <pettitkd@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <semaphore.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/ioctl.h>
#include "smartfs.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define WORKBUFFER_SIZE 256
/****************************************************************************
* Private Data
****************************************************************************/
#if defined(CONFIG_SMARTFS_MULTI_ROOT_DIRS) || \
(defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS))
static struct smartfs_mountpt_s *g_mounthead = NULL;
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: smartfs_semtake
****************************************************************************/
void smartfs_semtake(struct smartfs_mountpt_s *fs)
{
nxsem_wait_uninterruptible(fs->fs_sem);
}
/****************************************************************************
* Name: smartfs_semgive
****************************************************************************/
void smartfs_semgive(struct smartfs_mountpt_s *fs)
{
nxsem_post(fs->fs_sem);
}
/****************************************************************************
* Name: smartfs_rdle16
*
* Description:
* Get a (possibly unaligned) 16-bit little endian value.
*
* Input Parameters:
* val - A pointer to the first byte of the little endian value.
*
* Returned Value:
* A uint16_t representing the whole 16-bit integer value
*
****************************************************************************/
uint16_t smartfs_rdle16(FAR const void *val)
{
return (uint16_t)((FAR const uint8_t *)val)[1] << 8 |
(uint16_t)((FAR const uint8_t *)val)[0];
}
/****************************************************************************
* Name: smartfs_wrle16
*
* Description:
* Put a (possibly unaligned) 16-bit little endian value.
*
* Input Parameters:
* dest - A pointer to the first byte to save the little endian value.
* val - The 16-bit value to be saved.
*
* Returned Value:
* None
*
****************************************************************************/
void smartfs_wrle16(FAR void *dest, uint16_t val)
{
((FAR uint8_t *) dest)[0] = val & 0xff; /* Little endian means LS byte first in byte stream */
((FAR uint8_t *) dest)[1] = val >> 8;
}
/****************************************************************************
* Name: smartfs_rdle32
*
* Description:
* Get a (possibly unaligned) 32-bit little endian value.
*
* Input Parameters:
* val - A pointer to the first byte of the little endian value.
*
* Returned Value:
* A uint32_t representing the whole 32-bit integer value
*
****************************************************************************/
uint32_t smartfs_rdle32(FAR const void *val)
{
/* Little endian means LS halfword first in byte stream */
return (uint32_t)smartfs_rdle16(&((FAR const uint8_t *)val)[2]) << 16 |
(uint32_t)smartfs_rdle16(val);
}
/****************************************************************************
* Name: smartfs_wrle32
*
* Description:
* Put a (possibly unaligned) 32-bit little endian value.
*
* Input Parameters:
* dest - A pointer to the first byte to save the little endian value.
* val - The 32-bit value to be saved.
*
* Returned Value:
* None
*
****************************************************************************/
void smartfs_wrle32(uint8_t *dest, uint32_t val)
{
/* Little endian means LS halfword first in byte stream */
smartfs_wrle16(dest, (uint16_t)(val & 0xffff));
smartfs_wrle16(dest + 2, (uint16_t)(val >> 16));
}
/****************************************************************************
* Name: smartfs_mount
*
* Description: This function is called only when the mountpoint is first
* established. It initializes the mountpoint structure and verifies
* that a valid SMART filesystem is provided by the block driver.
*
* The caller should hold the mountpoint semaphore
*
****************************************************************************/
int smartfs_mount(struct smartfs_mountpt_s *fs, bool writeable)
{
FAR struct inode *inode;
struct geometry geo;
int ret = OK;
#if defined(CONFIG_SMARTFS_MULTI_ROOT_DIRS)
struct smartfs_mountpt_s *nextfs;
#endif
/* Assume that the mount is not successful */
fs->fs_mounted = false;
/* Check if there is media available */
inode = fs->fs_blkdriver;
if (!inode || !inode->u.i_bops || !inode->u.i_bops->geometry ||
inode->u.i_bops->geometry(inode, &geo) != OK || !geo.geo_available)
{
ret = -ENODEV;
goto errout;
}
/* Make sure that that the media is write-able (if write access is needed) */
if (writeable && !geo.geo_writeenabled)
{
ret = -EACCES;
goto errout;
}
/* Get the SMART low-level format information to validate the device has been
* formatted and scan properly for logical to physical sector mapping.
*/
ret = FS_IOCTL(fs, BIOC_GETFORMAT, (unsigned long) &fs->fs_llformat);
if (ret != OK)
{
ferr("ERROR: Error getting device low level format: %d\n", ret);
goto errout;
}
/* Validate the low-level format is valid */
if (!(fs->fs_llformat.flags & SMART_FMT_ISFORMATTED))
{
ferr("ERROR: No low-level format found\n");
ret = -ENODEV;
goto errout;
}
/* Allocate a read/write buffer */
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
/* Scan linked list of mounted file systems to find another FS with
* the same blockdriver. We will reuse the buffers.
*/
nextfs = g_mounthead;
while (nextfs != NULL)
{
/* Test if this FS uses the same block driver */
if (nextfs->fs_blkdriver == fs->fs_blkdriver)
{
/* Yep, it's the same block driver. Reuse the buffers.
* we can do this because we are protected by the same
* semaphore.
*/
fs->fs_rwbuffer = nextfs->fs_rwbuffer;
fs->fs_workbuffer = nextfs->fs_workbuffer;
break;
}
/* Advance to next FS */
nextfs = nextfs->fs_next;
}
/* If we didn't find a FS above, then allocate some buffers */
if (nextfs == NULL)
{
fs->fs_rwbuffer = (char *) kmm_malloc(fs->fs_llformat.availbytes);
fs->fs_workbuffer = (char *) kmm_malloc(WORKBUFFER_SIZE);
}
/* Now add ourselves to the linked list of SMART mounts */
fs->fs_next = g_mounthead;
g_mounthead = fs;
/* Set our root directory sector based on the directory entry
* reported by the block driver (based on which device is
* associated with this mount.
*/
fs->fs_rootsector = SMARTFS_ROOT_DIR_SECTOR + fs->fs_llformat.rootdirnum;
#else /* CONFIG_SMARTFS_MULTI_ROOT_DIRS */
#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
/* Now add ourselves to the linked list of SMART mounts */
fs->fs_next = g_mounthead;
g_mounthead = fs;
#endif
fs->fs_rwbuffer = (char *) kmm_malloc(fs->fs_llformat.availbytes);
fs->fs_workbuffer = (char *) kmm_malloc(WORKBUFFER_SIZE);
fs->fs_rootsector = SMARTFS_ROOT_DIR_SECTOR;
#endif /* CONFIG_SMARTFS_MULTI_ROOT_DIRS */
/* We did it! */
fs->fs_mounted = TRUE;
finfo("SMARTFS:\n");
finfo("\t Sector size: %d\n", fs->fs_llformat.sectorsize);
finfo("\t Bytes/sector %d\n", fs->fs_llformat.availbytes);
finfo("\t Num sectors: %d\n", fs->fs_llformat.nsectors);
finfo("\t Free sectors: %d\n", fs->fs_llformat.nfreesectors);
finfo("\t Max filename: %d\n", CONFIG_SMARTFS_MAXNAMLEN);
#ifdef CONFIG_SMARTFS_MULTI_ROOT_DIRS
finfo("\t RootDirEntries: %d\n", fs->fs_llformat.nrootdirentries);
#endif
finfo("\t RootDirSector: %d\n", fs->fs_rootsector);
errout:
return ret;
}
/****************************************************************************
* Name: smartfs_unmount
*
* Description: This function is called only when the mountpoint is being
* unbound. If we are serving multiple directories, then we have to
* remove ourselves from the mount linked list, and potentially free
* the shared buffers.
*
* The caller should hold the mountpoint semaphore
*
****************************************************************************/
int smartfs_unmount(struct smartfs_mountpt_s *fs)
{
int ret = OK;
struct inode *inode;
#if defined(CONFIG_SMARTFS_MULTI_ROOT_DIRS) || \
(defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS))
struct smartfs_mountpt_s *nextfs;
struct smartfs_mountpt_s *prevfs;
int count = 0;
int found = FALSE;
#endif
#if defined(CONFIG_SMARTFS_MULTI_ROOT_DIRS) || \
(defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS))
/* Start at the head of the mounts and search for our entry. Also
* count the number of entries that match our blkdriver.
*/
nextfs = g_mounthead;
prevfs = NULL;
while (nextfs != NULL)
{
/* Test if this FS's blkdriver matches ours (it could be us) */
if (nextfs->fs_blkdriver == fs->fs_blkdriver)
count++;
/* Test if this entry is our's */
if (nextfs == fs)
{
found = TRUE;
}
/* Keep track of the previous entry until our's is found */
if (!found)
{
/* Save this entry as the previous entry */
prevfs = nextfs;
}
/* Advance to the next entry */
nextfs = nextfs->fs_next;
}
/* Ensure we found our FS */
if (!found)
{
/* Our entry not found! Invalid unmount or bug somewhere */
return -EINVAL;
}
/* If the count is only one, then we need to delete the shared
* buffers because we are the last ones.
*/
if (count == 1)
{
/* Close the block driver */
if (fs->fs_blkdriver)
{
inode = fs->fs_blkdriver;
if (inode)
{
if (inode->u.i_bops && inode->u.i_bops->close)
{
inode->u.i_bops->close(inode);
}
}
}
/* Free the buffers */
kmm_free(fs->fs_rwbuffer);
kmm_free(fs->fs_workbuffer);
/* Set the buffer's to invalid value to catch program bugs */
fs->fs_rwbuffer = (char *) 0xdeadbeef;
fs->fs_workbuffer = (char *) 0xdeadbeef;
}
/* Now removed ourselves from the linked list */
if (fs == g_mounthead)
{
/* We were the first ones. Set a new head */
g_mounthead = fs->fs_next;
}
else
{
/* Remove from the middle of the list somewhere */
prevfs->fs_next = fs->fs_next;
}
#else
if (fs->fs_blkdriver)
{
inode = fs->fs_blkdriver;
if (inode)
{
if (inode->u.i_bops && inode->u.i_bops->close)
{
inode->u.i_bops->close(inode);
}
}
}
/* Release the mountpoint private data */
kmm_free(fs->fs_rwbuffer);
kmm_free(fs->fs_workbuffer);
#endif
return ret;
}
/****************************************************************************
* Name: smartfs_finddirentry
*
* Description: Finds an entry in the filesystem as specified by relpath.
* If found, the direntry will be populated with information
* for accessing the entry.
*
* If the final directory segment of relpath just before the
* last segment (the target file/dir) is valid, then the
* parentdirsector will indicate the logical sector number of
* the parent directory where a new entry should be created,
* and the filename pointer will point to the final segment
* (i.e. the "filename").
*
****************************************************************************/
int smartfs_finddirentry(struct smartfs_mountpt_s *fs,
struct smartfs_entry_s *direntry, const char *relpath,
uint16_t *parentdirsector, const char **filename)
{
int ret = -ENOENT;
const char *segment;
const char *ptr;
uint16_t seglen;
uint16_t depth = 0;
uint16_t dirstack[CONFIG_SMARTFS_DIRDEPTH];
uint16_t dirsector;
uint16_t entrysize;
uint16_t offset;
struct smartfs_chain_header_s *header;
struct smart_read_write_s readwrite;
struct smartfs_entry_header_s *entry;
/* Set the initial value of the output */
*parentdirsector = 0xffff;
*filename = NULL;
/* Initialize directory level zero as the root sector */
dirstack[0] = fs->fs_rootsector;
entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize;
/* Test if this is a request for the root directory */
if (*relpath == '\0')
{
direntry->firstsector = fs->fs_rootsector;
direntry->flags = SMARTFS_DIRENT_TYPE_DIR | 0777;
direntry->utc = 0;
direntry->dsector = 0;
direntry->doffset = 0;
direntry->dfirst = fs->fs_rootsector;
direntry->name = NULL;
direntry->datlen = 0;
*parentdirsector = 0; /* Our parent is the format sector I guess */
return OK;
}
/* Parse through each segment of relpath */
segment = relpath;
while (segment != NULL && *segment != '\0')
{
/* Find the end of this segment. It will be '/' or NULL. */
ptr = segment;
seglen = 0;
while (*ptr != '/' && *ptr != '\0')
{
seglen++;
ptr++;
}
/* Check to avoid buffer overflow */
if (seglen >= WORKBUFFER_SIZE)
{
ret = -ENAMETOOLONG;
goto errout;
}
strncpy(fs->fs_workbuffer, segment, seglen);
fs->fs_workbuffer[seglen] = '\0';
/* Search for "." and ".." as segment names */
if (strcmp(fs->fs_workbuffer, ".") == 0)
{
/* Just ignore this segment. Advance ptr if not on NULL */
if (*ptr == '/')
{
ptr++;
}
segment = ptr;
continue;
}
else if (strcmp(fs->fs_workbuffer, "..") == 0)
{
/* Up one level */
if (depth == 0)
{
/* We went up one level past our mount point! */
goto errout;
}
/* "Pop" to the previous directory level */
depth--;
if (*ptr == '/')
{
ptr++;
}
segment = ptr;
continue;
}
else
{
/* Search for the entry in the current directory */
dirsector = dirstack[depth];
/* Read the directory */
offset = 0xffff;
#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
while (dirsector != 0xffff)
#else
while (dirsector != 0)
#endif
{
/* Read the next directory in the chain */
readwrite.logsector = dirsector;
readwrite.count = fs->fs_llformat.availbytes;
readwrite.buffer = (uint8_t *)fs->fs_rwbuffer;
readwrite.offset = 0;
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
if (ret < 0)
{
goto errout;
}
/* Point to next sector in chain */
header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
dirsector = SMARTFS_NEXTSECTOR(header);
/* Search for the entry */
offset = sizeof(struct smartfs_chain_header_s);
entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
while (offset < readwrite.count)
{
/* Test if this entry is valid and active */
#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 */
offset += entrysize;
entry = (struct smartfs_entry_header_s *)
&fs->fs_rwbuffer[offset];
continue;
}
/* Test if the name matches */
if (strncmp(entry->name, fs->fs_workbuffer,
fs->fs_llformat.namesize) == 0)
{
/* We found it! If this is the last segment entry,
* then report the entry. If it isn't the last
* entry, then validate it is a directory entry and
* open it and continue searching.
*/
if (*ptr == '\0')
{
/* We are at the last segment. Report the entry */
/* Fill in the entry */
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
direntry->firstsector = smartfs_rdle16(&entry->firstsector);
direntry->flags = smartfs_rdle16(&entry->flags);
direntry->utc = smartfs_rdle32(&entry->utc);
#else
direntry->firstsector = entry->firstsector;
direntry->flags = entry->flags;
direntry->utc = entry->utc;
#endif
direntry->dsector = readwrite.logsector;
direntry->doffset = offset;
direntry->dfirst = dirstack[depth];
if (direntry->name == NULL)
{
direntry->name = (FAR char *)
kmm_malloc(fs->fs_llformat.namesize + 1);
}
memset(direntry->name, 0,
fs->fs_llformat.namesize + 1);
strncpy(direntry->name, entry->name,
fs->fs_llformat.namesize);
direntry->datlen = 0;
/* Scan the file's sectors to calculate the length and
* perform a rudimentary check.
*/
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
if ((smartfs_rdle16(&entry->flags) & SMARTFS_DIRENT_TYPE) ==
SMARTFS_DIRENT_TYPE_FILE)
{
dirsector = smartfs_rdle16(&entry->firstsector);
#else
if ((entry->flags & SMARTFS_DIRENT_TYPE) ==
SMARTFS_DIRENT_TYPE_FILE)
{
dirsector = entry->firstsector;
#endif
readwrite.count = sizeof(struct smartfs_chain_header_s);
readwrite.buffer = (uint8_t *)fs->fs_rwbuffer;
readwrite.offset = 0;
while (dirsector != SMARTFS_ERASEDSTATE_16BIT)
{
/* Read the next sector of the file */
readwrite.logsector = dirsector;
ret = FS_IOCTL(fs, BIOC_READSECT,
(unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error in sector chain at %d!\n",
dirsector);
break;
}
/* Add used bytes to the total and point to next sector */
if (*((FAR uint16_t *)header->used) !=
SMARTFS_ERASEDSTATE_16BIT)
{
direntry->datlen += *((uint16_t *)header->used);
}
dirsector = SMARTFS_NEXTSECTOR(header);
}
}
*parentdirsector = dirstack[depth];
*filename = segment;
ret = OK;
goto errout;
}
else
{
/* Validate it's a directory */
#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
{
/* Not a directory! Report the error */
ret = -ENOTDIR;
goto errout;
}
/* "Push" the directory and continue searching */
if (depth >= CONFIG_SMARTFS_DIRDEPTH - 1)
{
/* Directory depth too big */
ret = -ENAMETOOLONG;
goto errout;
}
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
dirstack[++depth] = smartfs_rdle16(&entry->firstsector);
#else
dirstack[++depth] = entry->firstsector;
#endif
segment = ptr + 1;
break;
}
}
/* Not this entry. Skip to the next one */
offset += entrysize;
entry = (struct smartfs_entry_header_s *)
&fs->fs_rwbuffer[offset];
}
/* Test if a directory entry was found and break if it was */
if (offset < readwrite.count)
{
break;
}
}
/* If we found a dir entry, then continue searching */
if (offset < readwrite.count)
{
/* Update the segment pointer */
if (*ptr != '\0')
{
ptr++;
}
segment = ptr;
continue;
}
/* Entry not found! Report the error. Also, if this is the last
* segment, then report the parent directory sector.
*/
if (*ptr == '\0')
{
*parentdirsector = dirstack[depth];
*filename = segment;
}
else
{
*parentdirsector = 0xffff;
*filename = NULL;
}
ret = -ENOENT;
goto errout;
}
}
errout:
return ret;
}
/****************************************************************************
* Name: smartfs_createentry
*
* Description: Creates a new entry in the specified parent directory, using
* the specified type and name. If the given sectorno is
* 0xffff, then a new sector is allocated for the new entry,
* otherwise the supplied sectorno is used.
*
****************************************************************************/
int smartfs_createentry(FAR struct smartfs_mountpt_s *fs,
uint16_t parentdirsector, FAR const char *filename,
uint16_t type, mode_t mode,
FAR struct smartfs_entry_s *direntry,
uint16_t sectorno, FAR struct smartfs_ofile_s *sf)
{
struct smart_read_write_s readwrite;
int ret;
uint16_t psector;
uint16_t nextsector;
uint16_t offset;
uint16_t found;
uint16_t entrysize;
struct smartfs_entry_header_s *entry;
struct smartfs_chain_header_s *chainheader;
int update_chain = 0;
struct smart_read_write_s update_readwrite;
struct smartfs_chain_header_s update_header;
/* Start at the 1st sector in the parent directory */
psector = parentdirsector;
found = FALSE;
entrysize = sizeof(struct smartfs_entry_header_s) +
fs->fs_llformat.namesize;
/* Validate the name isn't too long */
if (strlen(filename) > fs->fs_llformat.namesize)
{
return -ENAMETOOLONG;
}
/* Read the parent directory sector and find a place to insert
* the new entry.
*/
while (1)
{
/* Read the next sector */
readwrite.logsector = psector;
readwrite.count = fs->fs_llformat.availbytes;
readwrite.offset = 0;
readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
if (ret < 0)
{
goto errout;
}
/* Get the next chained sector */
chainheader = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
nextsector = SMARTFS_NEXTSECTOR(chainheader);
/* Search for an empty entry in this sector */
offset = sizeof(struct smartfs_chain_header_s);
entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
while (offset + entrysize < readwrite.count)
{
/* Check if this entry is available */
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
if ((smartfs_rdle16(&entry->flags) == SMARTFS_ERASEDSTATE_16BIT) ||
((smartfs_rdle16(&entry->flags) &
#else
if ((entry->flags == SMARTFS_ERASEDSTATE_16BIT) ||
((entry->flags &
#endif
(SMARTFS_DIRENT_EMPTY | SMARTFS_DIRENT_ACTIVE)) ==
(~SMARTFS_ERASEDSTATE_16BIT &
(SMARTFS_DIRENT_EMPTY | SMARTFS_DIRENT_ACTIVE))))
{
/* We found an empty entry. Use it. */
found = TRUE;
break;
}
/* Not available. Skip to next entry */
offset += entrysize;
entry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
}
/* If we found an entry, stop the search */
if (found)
{
break;
}
/* If there are no more sectors, then we need to add one to make
* room for the new entry.
*/
if (nextsector == SMARTFS_ERASEDSTATE_16BIT)
{
/* Allocate a new sector and chain it to the last one */
ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xffff);
if (ret < 0)
{
goto errout;
}
nextsector = (uint16_t) ret;
/* Chain the next sector into this sector. */
*((uint16_t *)update_header.nextsector) = nextsector;
update_readwrite.logsector = psector;
update_readwrite.offset = offsetof(struct smartfs_chain_header_s,
nextsector);
update_readwrite.count = sizeof(uint16_t);
update_readwrite.buffer = update_header.nextsector;
update_chain = 1;
}
/* Now update to the next sector */
psector = nextsector;
}
/* We found an insertion point. Create the entry at sector,offset */
#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
smartfs_wrle16(&entry->flags, (uint16_t) (SMARTFS_DIRENT_ACTIVE |
SMARTFS_DIRENT_DELETING | SMARTFS_DIRENT_RESERVED | type | (mode &
SMARTFS_DIRENT_MODE)));
#else
entry->flags = (uint16_t) (SMARTFS_DIRENT_ACTIVE |
SMARTFS_DIRENT_DELETING | SMARTFS_DIRENT_RESERVED | type | (mode &
SMARTFS_DIRENT_MODE));
#endif
#else /* CONFIG_SMARTFS_ERASEDSTATE == 0xff */
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
smartfs_wrle16(&entry->flags, (uint16_t) (SMARTFS_DIRENT_EMPTY | type |
(mode & SMARTFS_DIRENT_MODE)));
#else
entry->flags = (uint16_t) (SMARTFS_DIRENT_EMPTY | type |
(mode & SMARTFS_DIRENT_MODE));
#endif
#endif /* CONFIG_SMARTFS_ERASEDSTATE == 0xff */
if (sectorno == 0xffff)
{
/* Allocate a new sector for the file / dir */
ret = FS_IOCTL(fs, BIOC_ALLOCSECT, 0xffff);
if (ret < 0)
{
goto errout;
}
nextsector = (uint16_t) ret;
/* Set the newly allocated sector's type (file or dir) */
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
if (sf)
{
/* Using sector buffer and we have an open file context. Just update
* the sector buffer in the open file context.
*/
memset(sf->buffer, CONFIG_SMARTFS_ERASEDSTATE, fs->fs_llformat.availbytes);
chainheader = (struct smartfs_chain_header_s *) sf->buffer;
chainheader->type = SMARTFS_SECTOR_TYPE_FILE;
sf->bflags = SMARTFS_BFLAG_DIRTY | SMARTFS_BFLAG_NEWALLOC;
}
else
#endif
{
if ((type & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_DIR)
{
chainheader->type = SMARTFS_SECTOR_TYPE_DIR;
}
else
{
chainheader->type = SMARTFS_SECTOR_TYPE_FILE;
}
readwrite.count = 1;
readwrite.offset = offsetof(struct smartfs_chain_header_s, type);
readwrite.buffer = (uint8_t *) &chainheader->type;
readwrite.logsector = nextsector;
ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error %d setting new sector type for sector %d\n",
ret, nextsector);
goto errout;
}
}
}
else
{
/* Use the provided sector number */
nextsector = sectorno;
}
/* Create the directory entry to be written in the parent's sector */
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
smartfs_wrle16(&entry->firstsector, nextsector);
smartfs_wrle16(&entry->utc, time(NULL));
#else
entry->firstsector = nextsector;
entry->utc = time(NULL);
#endif
memset(entry->name, 0, fs->fs_llformat.namesize);
strncpy(entry->name, filename, fs->fs_llformat.namesize);
/* Now write the new entry to the parent directory sector */
readwrite.logsector = psector;
readwrite.offset = offset;
readwrite.count = entrysize;
readwrite.buffer = (uint8_t *) &fs->fs_rwbuffer[offset];
ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
if (ret < 0)
{
goto errout;
}
if (update_chain)
{
/* Update chain header after the next sector was written */
ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &update_readwrite);
if (ret < 0)
{
ferr("ERROR: Error chaining sector %d\n",
update_readwrite.logsector);
goto errout;
}
}
/* Now fill in the entry */
direntry->firstsector = nextsector;
direntry->dsector = psector;
direntry->doffset = offset;
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
direntry->flags = smartfs_rdle16(&entry->flags);
direntry->utc = smartfs_rdle32(&entry->utc);
#else
direntry->flags = entry->flags;
direntry->utc = entry->utc;
#endif
direntry->datlen = 0;
if (direntry->name == NULL)
{
direntry->name = (FAR char *)kmm_malloc(fs->fs_llformat.namesize + 1);
}
memset(direntry->name, 0, fs->fs_llformat.namesize + 1);
strncpy(direntry->name, filename, fs->fs_llformat.namesize);
ret = OK;
errout:
return ret;
}
/****************************************************************************
* Name: smartfs_deleteentry
*
* Description: Deletes an entry from the filesystem (file or dir) by
* freeing all the entry's sectors and then marking it inactive
* in it's parent's directory sector. For a directory, it
* does not validate the directory is empty, nor does it do
* a recursive delete.
*
****************************************************************************/
int smartfs_deleteentry(struct smartfs_mountpt_s *fs,
struct smartfs_entry_s *entry)
{
int ret;
uint16_t nextsector;
uint16_t sector;
uint16_t count;
uint16_t entrysize;
uint16_t offset;
struct smartfs_entry_header_s *direntry;
struct smartfs_chain_header_s *header;
struct smart_read_write_s readwrite;
/* Okay, delete the file. Loop through each sector and release them
*
* TODO: We really should walk the list backward to avoid lost
* sectors in the event we lose power. However this requires
* allocating a buffer to build the sector list since we don't
* store a doubly-linked list of sectors on the device. We
* could test if the sector data buffer is big enough and
* just use that, and only allocate a new buffer if the
* sector buffer isn't big enough. Do do this, however, we
* need to change the code below as it is using the a few
* bytes of the buffer to read in header info.
*/
nextsector = entry->firstsector;
header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
readwrite.offset = 0;
readwrite.count = sizeof(struct smartfs_chain_header_s);
readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
while (nextsector != SMARTFS_ERASEDSTATE_16BIT)
{
/* Read the next sector into our buffer */
sector = nextsector;
readwrite.logsector = sector;
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error reading sector %d\n", nextsector);
break;
}
/* Release this sector */
nextsector = SMARTFS_NEXTSECTOR(header);
ret = FS_IOCTL(fs, BIOC_FREESECT, sector);
}
/* Remove the entry from the directory tree */
readwrite.logsector = entry->dsector;
readwrite.offset = 0;
readwrite.count = fs->fs_llformat.availbytes;
readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error reading directory info at sector %d\n",
entry->dsector);
goto errout;
}
/* Mark this entry as inactive */
direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[entry->doffset];
#if CONFIG_SMARTFS_ERASEDSTATE == 0xff
#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 */
/* Write the updated flags back to the sector */
readwrite.offset = entry->doffset;
readwrite.count = sizeof(uint16_t);
readwrite.buffer = (uint8_t *) &direntry->flags;
ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error marking entry inactive at sector %d\n",
entry->dsector);
goto errout;
}
/* Test if any entries in this sector are being used */
if ((entry->dsector != fs->fs_rootsector) &&
(entry->dsector != entry->dfirst))
{
/* Scan the sector and count used entries */
count = 0;
offset = sizeof(struct smartfs_chain_header_s);
entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize;
while (offset + entrysize < fs->fs_llformat.availbytes)
{
/* Test the next entry */
direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
if (((smartfs_rdle16(&direntry->flags) & SMARTFS_DIRENT_EMPTY) !=
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) &&
((smartfs_rdle16(&direntry->flags) & SMARTFS_DIRENT_ACTIVE) ==
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
#else
if (((direntry->flags & SMARTFS_DIRENT_EMPTY) !=
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) &&
((direntry->flags & SMARTFS_DIRENT_ACTIVE) ==
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
#endif
{
/* Count this entry */
count++;
}
/* Advance to next entry */
offset += entrysize;
}
/* Test if the count it zero. If it is, then we will release the sector */
if (count == 0)
{
/* Okay, to release the sector, we must find the sector that we
* are chained to and remove ourselves from the chain. First
* save our nextsector value so we can "unchain" ourselves.
*/
nextsector = SMARTFS_NEXTSECTOR(header);
/* Now loop through the dir sectors to find ourselves in the chain */
sector = entry->dfirst;
readwrite.offset = 0;
readwrite.count = sizeof(struct smartfs_chain_header_s);
readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
while (sector != SMARTFS_ERASEDSTATE_16BIT)
{
/* Read the header for the next sector */
readwrite.logsector = sector;
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error reading sector %d\n", nextsector);
break;
}
/* Test if this sector "points" to us */
if (SMARTFS_NEXTSECTOR(header) == entry->dsector)
{
/* We found ourselves in the chain. Update the chain. */
SMARTFS_NEXTSECTOR(header) = nextsector;
readwrite.offset = offsetof(struct smartfs_chain_header_s,
nextsector);
readwrite.count = sizeof(uint16_t);
readwrite.buffer = header->nextsector;
ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error unchaining sector (%d)\n", nextsector);
goto errout;
}
/* Now release our sector */
ret = FS_IOCTL(fs, BIOC_FREESECT, (unsigned long) entry->dsector);
if (ret < 0)
{
ferr("ERROR: Error freeing sector %d\n", entry->dsector);
goto errout;
}
/* Break out of the loop, we are done! */
break;
}
/* Chain to the next sector */
sector = SMARTFS_NEXTSECTOR(header);
}
}
}
ret = OK;
errout:
return ret;
}
/****************************************************************************
* Name: smartfs_countdirentries
*
* Description: Counts the number of items in the specified directory entry.
* This routine assumes you have validated the entry you are
* passing is in fact a directory sector, though it checks
* just in case you were stupid :-)
*
****************************************************************************/
int smartfs_countdirentries(struct smartfs_mountpt_s *fs,
struct smartfs_entry_s *entry)
{
int ret;
uint16_t nextsector;
uint16_t offset;
uint16_t entrysize;
int count;
struct smartfs_entry_header_s *direntry;
struct smartfs_chain_header_s *header;
struct smart_read_write_s readwrite;
/* Walk through the directory's sectors and count entries */
count = 0;
nextsector = entry->firstsector;
while (nextsector != SMARTFS_ERASEDSTATE_16BIT)
{
/* Read the next sector into our buffer */
readwrite.logsector = nextsector;
readwrite.offset = 0;
readwrite.count = fs->fs_llformat.availbytes;
readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error reading sector %d\n", nextsector);
break;
}
/* Validate this is a directory type sector */
header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
if (header->type != SMARTFS_SECTOR_TYPE_DIR)
{
ferr("ERROR: Sector %d is not a DIR sector!\n", nextsector);
goto errout;
}
/* Loop for all entries in this sector and count them */
offset = sizeof(struct smartfs_chain_header_s);
entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize;
direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
while (offset + entrysize < readwrite.count)
{
#ifdef CONFIG_SMARTFS_ALIGNED_ACCESS
if (((smartfs_rdle16(&direntry->flags) & SMARTFS_DIRENT_EMPTY) !=
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) &&
((smartfs_rdle16(&direntry->flags) & SMARTFS_DIRENT_ACTIVE) ==
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
#else
if (((direntry->flags & SMARTFS_DIRENT_EMPTY) !=
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) &&
((direntry->flags & SMARTFS_DIRENT_ACTIVE) ==
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
#endif
{
/* Count this entry */
count++;
}
offset += entrysize;
direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
}
/* Get the next sector from the header */
nextsector = SMARTFS_NEXTSECTOR(header);
}
ret = count;
errout:
return ret;
}
/****************************************************************************
* Name: smartfs_sync_internal
*
* Description:
* Synchronize the file state on disk to match internal, in-memory state.
*
****************************************************************************/
int smartfs_sync_internal(FAR struct smartfs_mountpt_s *fs,
FAR struct smartfs_ofile_s *sf)
{
FAR struct smartfs_chain_header_s *header;
struct smart_read_write_s readwrite;
int ret = OK;
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
if (sf->bflags & SMARTFS_BFLAG_DIRTY)
{
/* Update the header with the number of bytes written */
header = (struct smartfs_chain_header_s *)sf->buffer;
if (*((uint16_t *)header->used) == SMARTFS_ERASEDSTATE_16BIT)
{
*((uint16_t *)header->used) = sf->byteswritten;
}
else
{
*((uint16_t *)header->used) += sf->byteswritten;
}
/* Write the entire sector to FLASH */
readwrite.logsector = sf->currsector;
readwrite.offset = 0;
readwrite.count = fs->fs_llformat.availbytes;
readwrite.buffer = sf->buffer;
ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error %d writing used bytes for sector %d\n",
ret, sf->currsector);
goto errout;
}
sf->byteswritten = 0;
sf->bflags = 0;
}
#else /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
/* Test if we have written bytes to the current sector that
* need to be recorded in the chain header's used bytes field.
*/
if (sf->byteswritten > 0)
{
finfo("Syncing sector %d\n", sf->currsector);
/* Read the existing sector used bytes value */
readwrite.logsector = sf->currsector;
readwrite.offset = 0;
readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
readwrite.count = sizeof(struct smartfs_chain_header_s);
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error %d reading sector %d data\n",
ret, sf->currsector);
goto errout;
}
/* Add new byteswritten to existing value */
header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
if (*((uint16_t *) header->used) == SMARTFS_ERASEDSTATE_16BIT)
{
*((uint16_t *) header->used) = sf->byteswritten;
}
else
{
*((uint16_t *) header->used) += sf->byteswritten;
}
readwrite.offset = offsetof(struct smartfs_chain_header_s, used);
readwrite.count = sizeof(uint16_t);
readwrite.buffer = (uint8_t *) &fs->fs_rwbuffer[readwrite.offset];
ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error %d writing used bytes for sector %d\n",
ret, sf->currsector);
goto errout;
}
sf->byteswritten = 0;
}
#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
errout:
return ret;
}
/****************************************************************************
* Name: smartfs_seek_internal
*
* Description:
* Performs the logic of the seek function. This is an internal function
* because it does not provide semaphore protection and therefore must be
* called from one of the other public interface routines (open, seek,
* etc.).
*
****************************************************************************/
off_t smartfs_seek_internal(FAR struct smartfs_mountpt_s *fs,
FAR struct smartfs_ofile_s *sf,
off_t offset, int whence)
{
FAR struct smartfs_chain_header_s *header;
struct smart_read_write_s readwrite;
off_t newpos;
off_t sectorstartpos;
int ret;
/* Test if this is a seek to get the current file pos */
if ((whence == SEEK_CUR) && (offset == 0))
{
return sf->filepos;
}
/* Test if we need to sync the file */
if (sf->byteswritten > 0)
{
/* Perform a sync */
smartfs_sync_internal(fs, sf);
}
/* Calculate the file position to seek to based on current position */
switch (whence)
{
case SEEK_SET:
default:
newpos = offset;
break;
case SEEK_CUR:
newpos = sf->filepos + offset;
break;
case SEEK_END:
newpos = sf->entry.datlen + offset;
break;
}
/* Ensure newpos is in range */
if (newpos < 0)
{
newpos = 0;
}
if (newpos > sf->entry.datlen)
{
newpos = sf->entry.datlen;
}
/* Now perform the seek. Test if we are seeking within the current
* sector and can skip the search to save time.
*/
sectorstartpos = sf->filepos - (sf->curroffset - sizeof(struct
smartfs_chain_header_s));
if (newpos >= sectorstartpos && newpos < sectorstartpos +
fs->fs_llformat.availbytes - sizeof(struct smartfs_chain_header_s))
{
/* Seeking within the current sector. Just update the offset */
sf->curroffset = sizeof(struct smartfs_chain_header_s) +
newpos - sectorstartpos;
sf->filepos = newpos;
return newpos;
}
/* Nope, we have to search for the sector and offset. If the new pos is greater
* than the current pos, then we can start from the beginning of the current
* sector, otherwise we have to start from the beginning of the file.
*/
if (newpos > sf->filepos)
{
sf->filepos = sectorstartpos;
}
else
{
sf->currsector = sf->entry.firstsector;
sf->filepos = 0;
}
header = (struct smartfs_chain_header_s *) fs->fs_rwbuffer;
while ((sf->currsector != SMARTFS_ERASEDSTATE_16BIT) &&
(sf->filepos + fs->fs_llformat.availbytes -
sizeof(struct smartfs_chain_header_s) < newpos))
{
/* Read the sector's header */
readwrite.logsector = sf->currsector;
readwrite.offset = 0;
readwrite.count = sizeof(struct smartfs_chain_header_s);
readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error %d reading sector %d header\n",
ret, sf->currsector);
goto errout;
}
/* Point to next sector and update filepos */
sf->currsector = SMARTFS_NEXTSECTOR(header);
sf->filepos += SMARTFS_USED(header);
}
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
/* When using sector buffering, we must read in the last buffer to our
* sf->buffer in case any changes are made.
*/
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;
}
}
#endif
/* Now calculate the offset */
sf->curroffset = sizeof(struct smartfs_chain_header_s) + newpos - sf->filepos;
sf->filepos = newpos;
return newpos;
errout:
return ret;
}
/****************************************************************************
* Name: smartfs_shrinkfile
*
* Description:
* Shrink the size of an existing file to the specified length
*
****************************************************************************/
int smartfs_shrinkfile(FAR struct smartfs_mountpt_s *fs,
FAR struct smartfs_ofile_s *sf, off_t length)
{
FAR struct smartfs_chain_header_s *header;
FAR struct smartfs_entry_s *entry;
FAR uint8_t *dest;
struct smart_read_write_s readwrite;
uint16_t nextsector;
uint16_t sector;
off_t remaining;
off_t destsize;
off_t available;
off_t offset;
int ret;
/* Walk through the directory's sectors and count entries */
entry = &sf->entry;
nextsector = entry->firstsector;
header = (struct smartfs_chain_header_s *)fs->fs_rwbuffer;
remaining = length;
available = fs->fs_llformat.availbytes - sizeof(struct smartfs_chain_header_s);
while (nextsector != SMARTFS_ERASEDSTATE_16BIT)
{
/* Read the next sector into our buffer */
readwrite.logsector = nextsector;
readwrite.offset = 0;
readwrite.count = fs->fs_llformat.availbytes;
readwrite.buffer = (FAR uint8_t *)fs->fs_rwbuffer;
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
if (ret < 0)
{
ferr("ERROR: Error reading sector %d header\n", nextsector);
return ret;
}
/* Get the next chained sector */
sector = SMARTFS_NEXTSECTOR(header);
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
/* When we have a sector buffer in use, simply skip the first sector.
* It will be handled below.
*/
if (nextsector == entry->firstsector)
{
if (remaining > available)
{
remaining -= available;
}
else
{
remaining = 0;
}
}
else
#endif
/* Are we retaining the sector it its entirety? */
if (remaining >= available)
{
/* Yes... skip to the next sector */
remaining -= available;
}
/* Are we removing the sector it its entirety? */
else if (remaining <= 0 && nextsector != entry->firstsector)
{
/* Yes.. just release the sector */
ret = FS_IOCTL(fs, BIOC_FREESECT, (unsigned long)nextsector);
if (ret < 0)
{
ferr("ERROR: Error freeing sector %d\n", nextsector);
return ret;
}
}
else
{
/* No.. Fill our buffer with erased data, retaining any still-
* valid bytes at the beginning of the buffer.
*
* Because of the preceding tests we know that
* 0 <= remaining < available. A special case is remaining == 0
* and nextsector == firstsector. In that case, we need to
* overwrite the sector data with the erased state value. The
* underlying SMART block driver will detect this and release the
* old sector and create a new one with the new (blank) data.
*
* Otherwise, we need to preserve the header and overwrite some of
* the data.
*/
if (remaining == 0)
{
dest = (FAR uint8_t *)fs->fs_rwbuffer;
destsize = fs->fs_llformat.availbytes;
}
else
{
offset = sizeof(struct smartfs_chain_header_s) + remaining;
dest = (FAR uint8_t *)&fs->fs_rwbuffer[offset];
destsize = fs->fs_llformat.availbytes - offset;
*((uint16_t *)header->used) = remaining;
*((uint16_t *)header->nextsector) = SMARTFS_ERASEDSTATE_16BIT;
remaining = 0;
}
memset(dest, CONFIG_SMARTFS_ERASEDSTATE, destsize);
header->type = SMARTFS_SECTOR_TYPE_FILE;
/* Now write the new sector data */
readwrite.count = fs->fs_llformat.availbytes;
ret = FS_IOCTL(fs, BIOC_WRITESECT, (unsigned long)&readwrite);
if (ret < 0)
{
ferr("ERROR: Error blanking 1st sector (%d) of file\n", nextsector);
return ret;
}
}
/* Now move on to the next sector */
nextsector = sector;
}
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
/* Now deal with the first sector in the event we are using a sector buffer
* like we would be if CRC is enabled.
*
* Using sector buffer and we have an open file context. Just update the
* sector buffer in the open file context.
*/
if (length < fs->fs_llformat.availbytes)
{
/* Read the entire sector */
readwrite.logsector = entry->firstsector;
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)
{
return ret;
}
/* Retain any valid data at the beginning of the sector, including the
* header. Special case length == 0
*/
if (length == 0)
{
dest = (FAR uint8_t *)sf->buffer;
destsize = fs->fs_llformat.availbytes;
}
else
{
offset = sizeof(struct smartfs_chain_header_s) + length;
dest = (FAR uint8_t *)&sf->buffer[offset];
destsize = fs->fs_llformat.availbytes - offset;
header = (struct smartfs_chain_header_s *)sf->buffer;
*((uint16_t *)header->used) = length;
*((uint16_t *)header->nextsector) = SMARTFS_ERASEDSTATE_16BIT;
}
memset(dest, CONFIG_SMARTFS_ERASEDSTATE, destsize);
header = (struct smartfs_chain_header_s *)sf->buffer;
header->type = SMARTFS_SECTOR_TYPE_FILE;
sf->bflags = SMARTFS_BFLAG_DIRTY;
}
#endif
entry->datlen = length;
return OK;
}
/****************************************************************************
* Name: smartfs_extendfile
*
* Description:
* Zero-extend the length of a regular file to 'length'.
*
****************************************************************************/
int smartfs_extendfile(FAR struct smartfs_mountpt_s *fs,
FAR struct smartfs_ofile_s *sf, off_t length)
{
struct smart_read_write_s readwrite;
FAR struct smartfs_chain_header_s *header;
#ifndef CONFIG_SMARTFS_USE_SECTOR_BUFFER
FAR uint8_t *buffer;
#endif
off_t remaining;
off_t savepos;
off_t oldsize;
int ret;
/* We are zero-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.
*/
#ifndef CONFIG_SMARTFS_USE_SECTOR_BUFFER
/* In order to perform the writes we will have to have a sector buffer. If
* SmartFS is not configured with a sector buffer then we will, then we
* will, unfortunately, need to allocate one.
*/
buffer = (FAR uint8_t *)kmm_malloc(SMARTFS_TRUNCBUFFER_SIZE);
if (buffer == NULL)
{
return -ENOMEM;
}
#endif
/* Loop until either (1) the file has been fully extended with zeroed data
* or (2) an error occurs. We assume we start with the current sector in
* cache (ff_currentsector).
*/
oldsize = sf->entry.datlen;
remaining = length - oldsize;
DEBUGASSERT(length > oldsize);
/* Seek to the end of the file for the append/write operation, remembering
* the current file position. It will be restored before returning; the
* truncate operation must not alter the file position.
*/
savepos = sf->filepos;
smartfs_seek_internal(fs, sf, 0, SEEK_END);
while (remaining > 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 > remaining)
{
readwrite.count = remaining;
}
memset(&sf->buffer[sf->curroffset], 0, readwrite.count);
sf->bflags |= SMARTFS_BFLAG_DIRTY;
#else /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
readwrite.offset = sf->curroffset;
readwrite.logsector = sf->currsector;
readwrite.buffer = buffer;
/* Select max size that available in the current sector */
readwrite.count = fs->fs_llformat.availbytes - sf->curroffset;
if (readwrite.count > remaining)
{
/* Limit the write to the size for our smaller working buffer */
readwrite.count = SMARTFS_TRUNCBUFFER_SIZE;
}
if (readwrite.count > remaining)
{
/* Futher limit the write to the remaining bytes to write */
readwrite.count = remaining;
}
/* 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_buffer;
}
}
#endif /* CONFIG_SMARTFS_USE_SECTOR_BUFFER */
/* Update our control variables */
sf->entry.datlen += readwrite.count;
sf->byteswritten += readwrite.count;
sf->curroffset += readwrite.count;
remaining -= readwrite.count;
/* Test if we wrote a full sector of data */
#ifdef CONFIG_SMARTFS_USE_SECTOR_BUFFER
if (sf->curroffset == fs->fs_llformat.availbytes && remaining)
{
/* 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_buffer;
}
/* 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_buffer;
}
/* 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_buffer;
}
/* Allocate a new sector if needed */
if (remaining > 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_buffer;
}
/* Copy the new sector to the old one and chain it */
header = (struct smartfs_chain_header_s *)fs->fs_rwbuffer;
*((FAR uint16_t *)header->nextsector) = (uint16_t)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_buffer;
}
/* 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 */
}
/* The file was successfully extended with zeros */
ret = OK;
errout_with_buffer:
#ifndef CONFIG_SMARTFS_USE_SECTOR_BUFFER
/* Release the allocated buffer */
kmm_free(buffer);
#endif
/* Restore the original file position */
smartfs_seek_internal(fs, sf, savepos, SEEK_SET);
return ret;
}
/****************************************************************************
* Name: smartfs_get_first_mount
*
* Description: Returns a pointer to the first mounted smartfs volume.
*
****************************************************************************/
#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_SMARTFS)
FAR struct smartfs_mountpt_s *smartfs_get_first_mount(void)
{
return g_mounthead;
}
#endif