nuttx/fs/spiffs/src/spiffs_vfs.c

1818 lines
49 KiB
C
Raw Normal View History

This commit brings in an inital port of the SPIFFS flash file system into NuttX. The file system is still untested at this point (and subject to some additional review). It is, however, marked EXPERIMENTAL should this should not cause a problem for anyone. Squashed commit of the following: fs/spiffs: Fix last compilation issue. Now compiles without error. It is still not quite ready for testing as there is additional code review that must be be performed. It is now marked as EXPERIMENTAL so that it can be brought onto the master branch with little risk. fs/spiffs: Remove some dead code. fs/spiffs: Weak start of analysis of spiffs_nucleus.c. Renamed to spiffs_core.c fs/spiffs: Rename spiffs_nucleus.c to spiffs_core.c fs/spiffs: Remove spiffs_config.h. All configuration settings are now available in the SPIFFS Kconfig options. fs/spiffs: Finished review, update, and repartitioning of spiffs_check.c. Added spiffs_check.h. fs/spiffs: Finished review, update, and repartitioning of spiffs_cache.c. Added spiffs_cache.h. fs/spiffs: Clean up some defines used in debug output statements. fs/spiffs: Finished review, update, and repartitioning of spiffs_gc.c. Added spiffs_gc.h. fs/spiffs: Now that VFS interface is completed, I have begun the long march of repartitioning the remaining functionality, reviewing logic, identifying dead code, and cleaning up loose ends. fs/spiffs: Initial integration of MTD interface, replacing the SPIFFS native flash interface. Lots of open issues such as the use of pages vs. blocks vs. erase blocks and units of addresses, offsets, and lengths that are passed in function calls. Remove SPIFFS_USE_MAGIC support. That option (which default to OFF anyway), wrote a magic value at the beginning of every sector and support verifiable identification of the file system. It was not being and used and removing it makes life simpler. fs/spiffs: Remove semaphore lock on the file object structure. Ultimately, the file access must modify the volume and access the volume structue which also has a exclusivity lock. So use of the volume lock alone should be sufficient. Integrated the SPIFFS rename logic into the NuttX VFS. Removed non-standard application calls or convert them to IOCTL commands. These were converted to IOCTL commands: (1) integrity check, (2) garbage collection, and (3) format flash. These were removed: (1) Integrity check callback. These provided a lot of good information about the state of the file system, but such callbacks are not compatible with a POSIX compliant file system. (2) Index maps. The index maps were a performance improvement feature. The user could provide the memory and request that a region of a a file use that memory for improved lookup performance when accessing parts of the file. The fallback is the less performance lookup by traversing the FLASH memory. (3) Removed the quick garbage collection interface (the code is still used internally). Only the full garbage collection is available to the user application via IOCTL. configs/sim/spiffs: A simulator configuration to use for testing SPIFFS. fs/spiffs: Integrate SPIFFS logic into NuttX VFS bind() and unbind() methods. fs/mount/fs_mount.c: Add SPIFFS to the list of drivers that require MTD vs block drivers. fs/spiffs: Trivial changes, mostly from analysis of how to integrate the rename() VFS method. fs/spiffs: Connect NuttX VFS unlink method to the SPIFFS_remove() function. Lots of name-changing. fs/spiffs: Remove non-standard errno support. Remove bogus SPIFFS_LOCK() and SPIFFS_UNLOCK() macros. fs/spiffs: Add NuttX VFS implementation for statfs() method. Clean up some of the accumulating compilation problems. fs/spiffs: Add stat(), truncate() methods. Dummy out unsupport mkdir() and rmdir() methods. fs/spiffs: Replace some of the custom error numbers with standard error numbers. fs/spiffs: Hooks read(), write(), fstat(), ioctl(), opendir(), closedir(), rewindif(), and readdir() into the NuttX VFS. fs/spiffs: Beginning the organization to work with the NuttX VFS. Lots of things are get broken! fs/spiffs: Add spiffs.c which will be the interface between SPIFFS and NuttX. No very close at present, however. fs/spiffs: Clean up some compile problems introduced by coding standard changes. fs/spiffs: A little closer to NuttX coding standard. fs/spiffs: Ran tools/indent.sh against all files. Closer to NuttX coding standard, but needs a lot more effort to be fully compliant. fs/spiffs: This commit brings in version 0.3.7 of Peter Anderson's SPIFFS. The initial commit includes the core FS files (with some definitions destributed to their correct header files) and hooks into the build system.
2018-09-25 02:05:09 +02:00
/****************************************************************************
* fs/spiffs/spiffs.c
* Interface between SPIFFS and the NuttX VFS
*
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Includes logic taken from 0.3.7 of SPIFFS by Peter Andersion. That
* version was originally released under the MIT license.
*
* Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976@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/stat.h>
#include <sys/statfs.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <dirent.h>
#include <semaphore.h>
#include <errno.h>
#include <assert.h>
#include <queue.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <nuttx/fs/dirent.h>
#include <nuttx/fs/ioctl.h>
#include "spiffs.h"
#include "spiffs_core.h"
#include "spiffs_cache.h"
#include "spiffs_gc.h"
#include "spiffs_check.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define spiffs_lock_volume(fs) (spiffs_lock_reentrant(&fs->exclsem))
#define spiffs_unlock_volume(fs) (spiffs_unlock_reentrant(&fs->exclsem))
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* SPIFFS helpers */
static void spiffs_lock_reentrant(FAR struct spiffs_sem_s *sem);
static void spiffs_unlock_reentrant(FAR struct spiffs_sem_s *sem);
/* File system operations */
static int spiffs_open(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode);
static int spiffs_close(FAR struct file *filep);
static ssize_t spiffs_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static ssize_t spiffs_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen);
static off_t spiffs_seek(FAR struct file *filep, off_t offset, int whence);
static int spiffs_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
static int spiffs_sync(FAR struct file *filep);
static int spiffs_dup(FAR const struct file *oldp, FAR struct file *newp);
static int spiffs_fstat(FAR const struct file *filep, FAR struct stat *buf);
static int spiffs_truncate(FAR struct file *filep, off_t length);
static int spiffs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct fs_dirent_s *dir);
static int spiffs_closedir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
static int spiffs_readdir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
static int spiffs_rewinddir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir);
static int spiffs_bind(FAR struct inode *mtdinode, FAR const void *data,
FAR void **handle);
static int spiffs_unbind(FAR void *handle, FAR struct inode **mtdinode,
unsigned int flags);
static int spiffs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf);
static int spiffs_unlink(FAR struct inode *mountpt, FAR const char *relpath);
static int spiffs_mkdir(FAR struct inode *mountpt, FAR const char *relpath,
mode_t mode);
static int spiffs_rmdir(FAR struct inode *mountpt, FAR const char *relpath);
static int spiffs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath,
FAR const char *newrelpath);
static int spiffs_stat(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct stat *buf);
/****************************************************************************
* Public Data
****************************************************************************/
const struct mountpt_operations spiffs_operations =
{
spiffs_open, /* open */
spiffs_close, /* close */
spiffs_read, /* read */
spiffs_write, /* write */
spiffs_seek, /* seek */
spiffs_ioctl, /* ioctl */
spiffs_sync, /* sync */
spiffs_dup, /* dup */
spiffs_fstat, /* fstat */
spiffs_truncate, /* truncate */
spiffs_opendir, /* opendir */
spiffs_closedir, /* closedir */
spiffs_readdir, /* readdir */
spiffs_rewinddir, /* rewinddir */
spiffs_bind, /* bind */
spiffs_unbind, /* unbind */
spiffs_statfs, /* statfs */
spiffs_unlink, /* unlink */
spiffs_mkdir, /* mkdir */
spiffs_rmdir, /* rmdir */
spiffs_rename, /* rename */
spiffs_stat, /* stat */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: spiffs_lock_reentrant
****************************************************************************/
static void spiffs_lock_reentrant(FAR struct spiffs_sem_s *rsem)
{
pid_t me;
/* Do we already hold the semaphore? */
me = getpid();
if (me == rsem->holder)
{
/* Yes... just increment the count */
rsem->count++;
DEBUGASSERT(rsem->count > 0);
}
/* Take the semaphore (perhaps waiting) */
else
{
int ret;
do
{
ret = nxsem_wait(&rsem->sem);
/* The only case that an error should occur here is if the wait
* was awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
/* No we hold the semaphore */
rsem->holder = me;
rsem->count = 1;
}
}
/****************************************************************************
* Name: spiffs_unlock_reentrant
****************************************************************************/
static void spiffs_unlock_reentrant(FAR struct spiffs_sem_s *rsem)
{
DEBUGASSERT(rsem->holder == getpid());
/* Is this our last count on the semaphore? */
if (rsem->count > 1)
{
/* No.. just decrement the count */
rsem->count--;
}
/* Yes.. then we can really release the semaphore */
else
{
rsem->holder = SPIFFS_NO_HOLDER;
rsem->count = 0;
nxsem_post(&rsem->sem);
}
}
/****************************************************************************
* Name: spiffs_readdir_callback
****************************************************************************/
static int spiffs_consistency_check(FAR struct spiffs_s *fs)
{
int status;
int ret = OK;
status = spiffs_check_luconsistency(fs);
if (status < 0)
{
fwarn("WARNING spiffs_check_luconsistency failed: %d\n", status);
if (ret == OK)
{
ret = status;
}
}
status = spiffs_check_objidconsistency(fs);
if (status < 0)
{
fwarn("WARNING spiffs_check_objidconsistency failed: %d\n", status);
if (ret == OK)
{
ret = status;
}
}
status = spiffs_check_pgconsistency(fs);
if (status < 0)
{
fwarn("WARNING spiffs_check_pgconsistency failed: %d\n", status);
if (ret == OK)
{
ret = status;
}
}
status = spiffs_obj_lu_scan(fs);
if (status < 0)
{
fwarn("WARNING spiffs_obj_lu_scan failed: %d\n", status);
if (ret == OK)
{
ret = status;
}
}
return ret;
}
/****************************************************************************
* Name: spiffs_readdir_callback
****************************************************************************/
static int spiffs_readdir_callback(FAR struct spiffs_s *fs,
int16_t objid, int16_t blkndx, int ix_entry,
FAR const void *user_const,
FAR void *user_var)
{
struct spiffs_pgobj_ixheader_s objhdr;
int16_t pgndx;
int ret;
if (objid == SPIFFS_OBJ_ID_FREE || objid == SPIFFS_OBJ_ID_DELETED ||
(objid & SPIFFS_OBJ_ID_IX_FLAG) == 0)
{
return SPIFFS_VIS_COUNTINUE;
}
pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, blkndx, ix_entry);
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, pgndx),
sizeof(struct spiffs_pgobj_ixheader_s),
(FAR uint8_t *) & objhdr);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read failed: %d\n", ret);
return ret;
}
if ((objid & SPIFFS_OBJ_ID_IX_FLAG) &&
objhdr.p_hdr.spndx == 0 &&
(objhdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL |
SPIFFS_PH_FLAG_IXDELE)) ==
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE))
{
FAR struct fs_dirent_s *dir = (FAR struct fs_dirent_s *)user_var;
FAR struct dirent *entryp;
DEBUGASSERT(dir != NULL);
entryp = &dir->fd_dir;
strncpy(entryp->d_name, (FAR char *)objhdr.name, NAME_MAX + 1);
entryp->d_type = objhdr.type;
return OK;
}
return SPIFFS_VIS_COUNTINUE;
}
/****************************************************************************
* Name: spiffs_open
****************************************************************************/
static int spiffs_open(FAR struct file *filep, FAR const char *relpath,
int oflags, mode_t mode)
{
FAR struct inode *inode;
FAR struct spiffs_s *fs;
FAR struct spiffs_file_s *fobj;
off_t offset;
int16_t pgndx;
int ret;
finfo("relpath: %s oflags; %04x\n", relpath, oflags);
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);
/* Skip over any leading directory separators (shouldn't be any) */
for (; *relpath == '/'; relpath++)
{
}
/* Check the length of the relative path */
if (strlen(relpath) > CONFIG_SPIFFS_NAME_MAX - 1)
{
return -ENAMETOOLONG;
}
/* Allocate a new file object with a reference count of one. */
fobj = (FAR struct spiffs_file_s *)kmm_zalloc(sizeof(struct spiffs_file_s));
if (fobj == NULL)
{
return -ENOMEM;
}
fobj->crefs = 1;
/* Get exclusive access to the file system */
spiffs_lock_volume(fs);
/* Check of the file object already exists */
ret = spiffs_find_objhdr_pgndx(fs,
(FAR const uint8_t *)relpath,
&pgndx);
if (ret < 0 && (oflags & O_CREAT) == 0)
{
/* It does not exist and we were not asked to create it */
goto errout_with_fileobject;
}
else if (ret == OK && (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
{
/* O_CREAT and O_EXCL and file exists - fail */
ret = -EEXIST;
goto errout_with_fileobject;
}
else if ((oflags & O_CREAT) != 0 && ret == -ENOENT)
{
int16_t objid;
/* The file does not exist. We need to create the it. */
ret = spiffs_obj_lu_find_free_obj_id(fs, &objid, 0);
if (ret < 0)
{
goto errout_with_fileobject;
}
ret = spiffs_object_create(fs, objid, (FAR const uint8_t *)relpath,
DTYPE_FILE, &pgndx);
if (ret < 0)
{
goto errout_with_fileobject;
}
/* Since we created the file, we don't need to truncate it */
oflags &= ~O_TRUNC;
}
else if (ret < 0)
{
goto errout_with_fileobject;
}
/* Open the file */
ret = spiffs_object_open_bypage(fs, pgndx, fobj, oflags, mode);
if (ret < 0)
{
goto errout_with_fileobject;
}
/* Truncate the file to zero length */
if ((oflags & O_TRUNC) != 0)
{
ret = spiffs_object_truncate(fs, fobj, 0, false);
if (ret < 0)
{
goto errout_with_fileobject;
}
}
/* Save the struct spiffs_file_s instance as the file private data */
filep->f_priv = fobj;
/* In write/append mode, we need to set the file pointer to the end of the
* file.
*/
offset = 0;
if ((oflags & (O_APPEND | O_WROK)) == (O_APPEND | O_WROK))
{
offset = fobj->size == SPIFFS_UNDEFINED_LEN ? 0 : fobj->size;
}
/* Save the file position */
filep->f_pos = offset;
/* Add the new file object to the tail of the open file list */
dq_addlast((FAR dq_entry_t *)fobj, &fs->objq);
spiffs_unlock_volume(fs);
return OK;
errout_with_fileobject:
kmm_free(fobj);
spiffs_unlock_volume(fs);
return ret;
}
/****************************************************************************
* Name: spiffs_close
****************************************************************************/
static int spiffs_close(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct spiffs_s *fs;
FAR struct spiffs_file_s *fobj;
finfo("filep: %p\n", filep);
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
/* Get the mountpoint inode reference from the file structure and the
* volume state data from the inode structure
*/
inode = filep->f_inode;
fs = inode->i_private;
DEBUGASSERT(fs != NULL);
/* Recover our private data from the struct file instance */
fobj = filep->f_priv;
/* Get exclusive access to the file system */
spiffs_lock_volume(fs);
/* Decrement the reference count on the file */
DEBUGASSERT(fobj->crefs > 0);
if (fobj->crefs > 0)
{
fobj->crefs--;
}
filep->f_priv = NULL;
/* If the reference count decremented to zero and the file has been
* unlinked, then free resources related to the open file.
*/
if (fobj->crefs == 0 && (fobj->flags & SFO_FLAG_UNLINKED) != 0)
{
/* Free the file object while we hold the lock? Weird but this
* should be safe because the object is unlinked and could not
* have any other references.
*/
spiffs_fobj_free(fs, fobj);
return OK;
}
/* Release the lock on the file system */
spiffs_unlock_volume(fs);
return OK;
}
/****************************************************************************
* Name: spiffs_read
****************************************************************************/
static ssize_t spiffs_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct inode *inode;
FAR struct spiffs_s *fs;
FAR struct spiffs_file_s *fobj;
ssize_t nread;
finfo("filep: %p buffer: %p buflen: %lu\n",
filep, buffer, (unsigned long)buflen);
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
/* Get the mountpoint inode reference from the file structure and the
* volume state data from the inode structure
*/
inode = filep->f_inode;
fs = inode->i_private;
DEBUGASSERT(fs != NULL);
/* Recover the file object state from the struct file instance */
fobj = filep->f_priv;
/* Get exclusive access to the file system */
spiffs_lock_volume(fs);
/* Read from FLASH */
nread = spiffs_fobj_read(fs, fobj, buffer, buflen, filep->f_pos);
if (nread > 0)
{
filep->f_pos += nread;
}
/* Release the lock on the file system */
spiffs_unlock_volume(fs);
return nread;
}
/****************************************************************************
* Name: spiffs_write
****************************************************************************/
static ssize_t spiffs_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen)
{
FAR struct inode *inode;
FAR struct spiffs_s *fs;
FAR struct spiffs_file_s *fobj;
ssize_t nwritten;
off_t offset;
int ret;
finfo("filep: %p buffer: %p buflen: %lu\n",
filep, buffer, (unsigned long)buflen);
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
/* Get the mountpoint inode reference from the file structure and the
* volume state data from the inode structure
*/
inode = filep->f_inode;
fs = inode->i_private;
DEBUGASSERT(fs != NULL);
/* Recover the file object state from the struct file instance */
fobj = filep->f_priv;
/* Get exclusive access to the file system */
spiffs_lock_volume(fs);
/* Verify that the file was opened with write access */
if ((fobj->oflags & O_WROK) == 0)
{
ret = -EACCES;
goto errout_with_lock;
}
/* Write to FLASH (or cache) */
offset = filep->f_pos;
if (fobj->cache_page == 0)
{
/* See if object ID is associated with cache already */
fobj->cache_page = spiffs_cache_page_get_byobjid(fs, fobj);
}
if ((fobj->flags & O_DIRECT) == 0)
{
if (buflen < (size_t)SPIFFS_CFG_LOG_PAGE_SZ(fs))
{
/* Small write, try to cache it */
bool alloc_cpage = true;
if (fobj->cache_page != NULL)
{
/* We have a cached page for this object already, check cache
* page boundaries
*/
if (offset < fobj->cache_page->offset ||
offset > fobj->cache_page->offset + fobj->cache_page->size ||
offset + buflen > fobj->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs))
{
/* Boundary violation, write back cache first and allocate
* new
*/
spiffs_cacheinfo("Cache page=%d for fobj ID=%d "
"Boundary violation, offset=%d size=%d\n",
fobj->cache_page->cpndx, fobj->objid,
fobj->cache_page->offset, fobj->cache_page->size);
nwritten = spiffs_fobj_write(fs, fobj,
spiffs_get_cache_page(fs, spiffs_get_cache(fs),
fobj->cache_page->cpndx),
fobj->cache_page->offset,
fobj->cache_page->size);
spiffs_cache_page_release(fs, fobj->cache_page);
if (nwritten < 0)
{
ret = (int)nwritten;
goto errout_with_lock;
}
}
else
{
/* Writing within cache */
alloc_cpage = false;
}
}
if (alloc_cpage)
{
fobj->cache_page = spiffs_cache_page_allocate_byobjid(fs, fobj);
if (fobj->cache_page)
{
fobj->cache_page->offset = offset;
fobj->cache_page->size = 0;
spiffs_cacheinfo("Allocated cache page %d for fobj %d\n",
fobj->cache_page->cpndx, fobj->objid);
}
}
if (fobj->cache_page)
{
FAR struct spiffs_cache_s *cache;
FAR uint8_t *cpage_data;
off_t offset_in_cpage;
offset_in_cpage = offset - fobj->cache_page->offset;
spiffs_cacheinfo("Storing to cache page %d for fobj %d offset=%d:%d buflen=%d\n",
fobj->cache_page->cpndx, fobj->objid, offset,
offset_in_cpage, buflen);
cache = spiffs_get_cache(fs);
cpage_data = spiffs_get_cache_page(fs, cache, fobj->cache_page->cpndx);
memcpy(&cpage_data[offset_in_cpage], buffer, buflen);
fobj->cache_page->size = MAX(fobj->cache_page->size, offset_in_cpage + buflen);
nwritten = buflen;
goto success_with_lock;
}
else
{
nwritten = spiffs_fobj_write(fs, fobj, buffer, offset, buflen);
if (nwritten < 0)
{
ret = (int)nwritten;
goto errout_with_lock;
}
goto success_with_lock;
}
}
else
{
/* Big write, no need to cache it - but first check if there is a
* cached write already
*/
if (fobj->cache_page)
{
/* Write back cache first */
spiffs_cacheinfo("Cache page=%d for fobj ID=%d "
"Boundary violation, offset=%d size=%d\n",
fobj->cache_page->cpndx, fobj->objid,
fobj->cache_page->offset, fobj->cache_page->size);
nwritten = spiffs_fobj_write(fs, fobj,
spiffs_get_cache_page(fs,
spiffs_get_cache(fs),
fobj->cache_page->cpndx),
fobj->cache_page->offset,
fobj->cache_page->size);
spiffs_cache_page_release(fs, fobj->cache_page);
if (nwritten < 0)
{
ret = (int)nwritten;
goto errout_with_lock;
}
/* Data written below */
}
}
}
nwritten = spiffs_fobj_write(fs, fobj, buffer, offset, buflen);
if (nwritten < 0)
{
ret = (int)nwritten;
goto errout_with_lock;
}
success_with_lock:
/* Update the file position */
filep->f_pos += nwritten;
/* Release our access to the volume */
spiffs_unlock_volume(fs);
return nwritten;
errout_with_lock:
spiffs_unlock_volume(fs);
return (ssize_t)ret;
}
/****************************************************************************
* Name: spiffs_seek
****************************************************************************/
static off_t spiffs_seek(FAR struct file *filep, off_t offset, int whence)
{
FAR struct inode *inode;
FAR struct spiffs_s *fs;
FAR struct spiffs_file_s *fobj;
int16_t data_spndx;
int16_t objndx_spndx;
off_t fsize;
off_t pos;
int ret;
finfo("filep=%p offset=%ld whence=%d\n", filep, (long)offset, whence);
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
/* Get the mountpoint inode reference from the file structure and the
* volume state data from the inode structure
*/
inode = filep->f_inode;
fs = inode->i_private;
DEBUGASSERT(fs != NULL);
/* Recover the file object state from the struct file instance */
fobj = filep->f_priv;
/* Get exclusive access to the file system */
spiffs_lock_volume(fs);
/* Get the new file offset */
spiffs_fflush_cache(fs, fobj);
fsize = fobj->size == SPIFFS_UNDEFINED_LEN ? 0 : fobj->size;
/* Map the offset according to the whence option */
switch (whence)
{
case SEEK_SET: /* The offset is set to offset bytes. */
pos = offset;
break;
case SEEK_CUR: /* The offset is set to its current location plus
* offset bytes. */
pos = offset + filep->f_pos;
break;
case SEEK_END: /* The offset is set to the size of the file plus
* offset bytes. */
pos = fsize + offset;
break;
default:
return -EINVAL;
}
/* Verify the resulting file position */
if (pos < 0)
{
ret = -EINVAL;
goto errout_with_lock;
}
/* Attempts to set the position beyond the end of file should
* work if the file is open for write access.
*
* REVISIT: This simple implementation has no per-open storage that
* would be needed to retain the open flags.
*/
if (pos > fsize)
{
filep->f_pos = fsize;
ret = -ENOSYS;
goto errout_with_lock;
}
/* Set up for the new file position */
data_spndx = (pos > 0 ? (pos - 1) : 0) / SPIFFS_DATA_PAGE_SIZE(fs);
objndx_spndx = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spndx);
if (fobj->objndx_spndx != objndx_spndx)
{
int16_t pgndx;
ret = spiffs_obj_lu_find_id_and_span(fs, fobj->objid | SPIFFS_OBJ_ID_IX_FLAG,
objndx_spndx, 0, &pgndx);
if (ret < 0)
{
goto errout_with_lock;
}
fobj->objndx_spndx = objndx_spndx;
fobj->objndx_pgndx = pgndx;
}
filep->f_pos = pos;
spiffs_unlock_volume(fs);
return pos;
errout_with_lock:
spiffs_unlock_volume(fs);
return (off_t)ret;
}
/****************************************************************************
* Name: spiffs_ioctl
****************************************************************************/
static int spiffs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode;
FAR struct spiffs_s *fs;
int ret;
finfo("filep=%p cmd=%d arg=%ld\n", filep, cmd, (long)arg);
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
/* Get the mountpoint inode reference from the file structure and the
* volume state data from the inode structure
*/
inode = filep->f_inode;
fs = inode->i_private;
DEBUGASSERT(fs != NULL);
/* Get exclusive access to the file system */
spiffs_lock_volume(fs);
/* Handle the IOCTL according tot he command */
switch (cmd)
{
/* Run a consistency check on the file system media.
* IN: None
* OUT: None
*/
case BIOC_CHECK:
{
ret = spiffs_consistency_check(fs);
}
break;
/* Force reformatting of media. All data will be lost.
* IN: None
* OUT: None
*/
case BIOC_FORMAT:
{
/* Check if the MTD driver supports the MTDIOC_BULKERASE command */
ret = MTD_IOCTL(fs->mtd, MTDIOC_BULKERASE, 0);
if (ret < 0)
{
/* No.. we will have to erase a block at a time */
int16_t blkndx = 0;
while (blkndx < fs->geo.neraseblocks)
{
fs->max_erase_count = 0;
ret = spiffs_erase_block(fs, blkndx);
if (ret < 0)
{
return ret;
}
blkndx++;
}
}
}
break;
/* Run garbage collection.
* IN: On entry holds the number of bytes to be recovered.
* OUT: None
*/
case BIOC_GC:
{
ret = spiffs_gc_check(fs, (size_t)arg);
}
break;
default:
/* Pass through to the contained MTD driver */
ret = MTD_IOCTL(fs->mtd, cmd, arg);
break;
}
spiffs_unlock_volume(fs);
return ret;
}
/****************************************************************************
* Name: spiffs_sync
*
* Description: Synchronize the file state on disk to match internal, in-
* memory state.
*
****************************************************************************/
static int spiffs_sync(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct spiffs_s *fs;
FAR struct spiffs_file_s *fobj;
int ret;
finfo("filep=%p\n", filep);
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);
/* Get the mountpoint inode reference from the file structure and the
* volume state data from the inode structure
*/
inode = filep->f_inode;
fs = inode->i_private;
DEBUGASSERT(fs != NULL);
/* Recover the file object state from the struct file instance */
fobj = filep->f_priv;
/* Get exclusive access to the file system */
spiffs_lock_volume(fs);
/* Flush all cached write data */
ret = spiffs_fflush_cache(fs, fobj);
spiffs_unlock_volume(fs);
return ret;
}
/****************************************************************************
* Name: spiffs_dup
****************************************************************************/
static int spiffs_dup(FAR const struct file *oldp, FAR struct file *newp)
{
FAR struct inode *inode;
FAR struct spiffs_s *fs;
FAR struct spiffs_file_s *fobj;
finfo("Dup %p->%p\n", oldp, newp);
DEBUGASSERT(oldp->f_priv != NULL && oldp->f_inode != NULL &&
newp->f_priv == NULL && newp->f_inode != NULL);
/* Get the mountpoint inode reference from the file structure and the
* volume state data from the inode structure
*/
inode = oldp->f_inode;
fs = inode->i_private;
DEBUGASSERT(fs != NULL);
/* Recover our private data from the struct file instance */
fobj = oldp->f_priv;
/* Increment the reference count (atomically)*/
spiffs_lock_volume(fs);
fobj->crefs++;
spiffs_unlock_volume(fs);
/* Save a copy of the file object as the dup'ed file. */
newp->f_priv = fobj;
return OK;
}
/****************************************************************************
* Name: spiffs_fstat
*
* Description:
* Obtain information about an open file associated with the file
* descriptor 'fobj', and will write it to the area pointed to by 'buf'.
*
****************************************************************************/
static int spiffs_fstat(FAR const struct file *filep, FAR struct stat *buf)
{
FAR struct inode *inode;
FAR struct spiffs_s *fs;
FAR struct spiffs_file_s *fobj;
int ret;
finfo("filep=%p buf=%p\n", filep, buf);
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL && buf != NULL);
/* Get the mountpoint inode reference from the file structure and the
* volume state data from the inode structure
*/
inode = filep->f_inode;
fs = inode->i_private;
DEBUGASSERT(fs != NULL);
/* Recover the file object state from the struct file instance */
fobj = filep->f_priv;
/* Get exclusive access to the file system */
spiffs_lock_volume(fs);
/* Flush the cache and perform the common stat() operation */
spiffs_fflush_cache(fs, fobj);
ret = spiffs_stat_pgndx(fs, fobj->objhdr_pgndx, fobj->objid, buf);
if (ret < 0)
{
ferr("ERROR: spiffs_stat_pgndx failed: %d\n", ret);
}
spiffs_unlock_volume(fs);
return ret;
}
/****************************************************************************
* Name: spiffs_truncate
****************************************************************************/
static int spiffs_truncate(FAR struct file *filep, off_t length)
{
FAR struct inode *inode;
FAR struct spiffs_s *fs;
FAR struct spiffs_file_s *fobj;
off_t fsize;
int ret;
finfo("filep: %p length: %ld\n", filep, (long)length);
DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL && length >= 0);
/* Get the mountpoint inode reference from the file structure and the
* volume state data from the inode structure
*/
inode = filep->f_inode;
fs = inode->i_private;
DEBUGASSERT(fs != NULL);
/* Recover the file object state from the struct file instance */
fobj = filep->f_priv;
/* Get exclusive access to the file system */
spiffs_lock_volume(fs);
/* REVISIT: spiffs_object_truncate() can only truncate to smaller sizes. */
ret = spiffs_object_truncate(fs, fobj, length, false);
if (ret < 0)
{
ferr("ERROR: spiffs_object_truncate failed: %d/n", ret);
}
/* Check if we need to reset the file pointer. Probably could use
* 'length', but let's use the authoritative file file size for the
* comparison.
*/
fsize = fobj->size == SPIFFS_UNDEFINED_LEN ? 0 : fobj->size;
if (ret >= 0 && fsize < filep->f_pos)
{
/* Reset the file pointer to the new end-of-file position */
filep->f_pos = fsize;
}
spiffs_unlock_volume(fs);
return ret;
}
/****************************************************************************
* Name: spiffs_opendir
****************************************************************************/
static int spiffs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct fs_dirent_s *dir)
{
finfo("mountpt: %p relpath: %s dir: %p\n",
mountpt, relpath, dir);
DEBUGASSERT(mountpt != NULL && relpath != NULL && dir != NULL);
/* Initialize for traversal of the 'directory' */
dir->u.spiffs.block = 0;
dir->u.spiffs.entry = 0;
return OK;
}
/****************************************************************************
* Name: spiffs_closedir
****************************************************************************/
static int spiffs_closedir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
finfo("mountpt: %p dir: %p\n", mountpt, dir);
DEBUGASSERT(mountpt != NULL && dir != NULL);
/* There is nothing to be done */
return OK;
}
/****************************************************************************
* Name: spiffs_readdir
****************************************************************************/
static int spiffs_readdir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
FAR struct spiffs_s *fs;
int16_t blkndx;
int entry;
int ret;
finfo("mountpt: %p dir: %p\n", mountpt, dir);
DEBUGASSERT(mountpt != NULL && dir != NULL);
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
DEBUGASSERT(fs != NULL);
/* Lock the SPIFFS volume */
spiffs_lock_volume(fs);
/* And visit the next file object */
ret = spiffs_foreach_objlu(fs, dir->u.spiffs.block, dir->u.spiffs.entry,
SPIFFS_VIS_NO_WRAP, 0, spiffs_readdir_callback,
NULL, dir, &blkndx, &entry);
if (ret == OK)
{
dir->u.spiffs.block = blkndx;
dir->u.spiffs.entry = entry + 1;
}
/* Release the lock on the file system */
spiffs_unlock_volume(fs);
return ret;
}
/****************************************************************************
* Name: spiffs_rewinddir
****************************************************************************/
static int spiffs_rewinddir(FAR struct inode *mountpt,
FAR struct fs_dirent_s *dir)
{
finfo("mountpt: %p dir: %p\n", mountpt, dir);
DEBUGASSERT(mountpt != NULL && dir != NULL);
/* Reset as when opendir() was called. */
dir->u.spiffs.block = 0;
dir->u.spiffs.entry = 0;
return OK;
}
/****************************************************************************
* Name: spiffs_bind
****************************************************************************/
static int spiffs_bind(FAR struct inode *mtdinode, FAR const void *data,
FAR void **handle)
{
FAR struct spiffs_s *fs;
FAR struct mtd_dev_s *mtd;
FAR uint8_t *work;
size_t cache_size;
size_t cache_max;
size_t work_size;
size_t addrmask;
int ret;
finfo("mtdinode: %p data: %p handle: %p\n", mtdinode, data, handle);
DEBUGASSERT(mtdinode == NULL && handle != NULL);
/* Extract the MTD interface reference */
DEBUGASSERT(INODE_IS_MTD(mtdinode) && mtdinode->u.i_mtd != NULL);
mtd = mtdinode->u.i_mtd;
/* Create an instance of the SPIFFS file system */
fs = (FAR struct spiffs_s *)kmm_zalloc(sizeof(struct spiffs_s));
if (fs == NULL)
{
ferr("ERROR: Failed to allocate volume structure\n");
return -ENOMEM;
}
fs->mtd = mtd;
/* Get the MTD geometry */
ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&fs->geo));
if (ret < 0)
{
ferr("ERROR: MTD_IOCTL(MTDIOC_GEOMETRY) failed: %d\n", ret);
goto errout_with_volume;
}
fs->phys_size = fs->geo.neraseblocks * fs->geo.erasesize;
/* Get the aligned cache size */
addrmask = (sizeof(FAR void *) - 1);
cache_size = (CONFIG_SPIFFS_CACHE_SIZE + addrmask) & ~addrmask;
/* Don't let the cache size exceed the maximum that is needed */
cache_max = SPIFFS_CFG_LOG_PAGE_SZ(fs) << 5;
if (cache_size > cache_max)
{
cache_size = cache_max;
}
/* Allocate the cache */
fs->cache_size = cache_size;
fs->cache = (FAR void *)kmm_malloc(cache_size);
if (fs->cache == NULL)
{
ferr("ERROR: Failed to allocate volume structure\n");
ret = -ENOMEM;
goto errout_with_volume;
}
spiffs_cache_initialize(fs);
/* Allocate the memory work buffer comprising 2*config->page_size bytes
* used throughout all file system operations.
*
* NOTE: Currently page size is equivalent to block size.
*/
work_size = SPIFFS_CFG_LOG_PAGE_SZ(fs) << 1;
work = (FAR uint8_t *)kmm_malloc(work_size);
if (work == NULL)
{
ferr("ERROR: Failed to allocate work buffer\n");
ret = -ENOMEM;
goto errout_with_cache;
}
fs->work = &work[0];
fs->lu_work = &work[work_size >> 1];
fs->config_magic = SPIFFS_SUPER_MAGIC;
/* Check the file system */
ret = spiffs_obj_lu_scan(fs);
if (ret < 0)
{
ferr("ERROR: spiffs_obj_lu_scan() failed: %d\n", ret);
goto errout_with_work;
}
finfo("page index byte len: %u\n",
(unsigned int)SPIFFS_CFG_LOG_PAGE_SZ(fs));
finfo("object lookup pages: %u\n",
(unsigned int)SPIFFS_OBJ_LOOKUP_PAGES(fs));
finfo("page pages per block: %u\n",
(unsigned int)SPIFFS_PAGES_PER_BLOCK(fs));
finfo("page header length: %u\n",
(unsigned int)sizeof(struct spiffs_page_header_s));
finfo("object header index entries: %u\n",
(unsigned int)SPIFFS_OBJ_HDR_IX_LEN(fs));
finfo("object index entries: %u\n",
(unsigned int)SPIFFS_OBJ_IX_LEN(fs));
finfo("free blocks: %u\n",
(unsigned int)fs->free_blocks);
#ifdef CONFIG_SPIFFS_CHECK_ONMOUNT
/* Perform the full consistency check */
ret = spiffs_consistency_check(fs);
if (ret < 0)
{
fwarn("WARNING: File system is damaged: %d\n", ret);
}
#endif
/* Return the new file system handle */
*handle = (FAR void *)fs;
return OK;
errout_with_work:
kmm_free(fs->work);
errout_with_cache:
kmm_free(fs->cache);
errout_with_volume:
kmm_free(fs);
return ret;
}
/****************************************************************************
* Name: spiffs_unbind
****************************************************************************/
static int spiffs_unbind(FAR void *handle, FAR struct inode **mtdinode,
unsigned int flags)
{
FAR struct spiffs_s *fs = (FAR struct spiffs_s *)handle;
FAR struct spiffs_file_s *fobj;
int ret;
finfo("handle: %p mtdinode: %p flags: %02x\n",
handle, mtdinode, flags);
DEBUGASSERT(fs != NULL);
/* Lock the file system */
spiffs_lock_volume(fs);
/* Are there open file system? If so, are we being forced to unmount? */
if (!dq_empty(&fs->objq) && (flags & MNT_FORCE) == 0)
{
fwarn("WARNING: Open files and umount not forced\n");
ret = -EBUSY;
goto errout_with_lock;
}
/* Release all of the open file objects... Very scary stuff. */
while ((fobj = (FAR struct spiffs_file_s *)dq_peek(&fs->objq)) != NULL)
{
/* Free the file object */
spiffs_fobj_free(fs, fobj);
}
/* Free allocated working buffers */
if (fs->work != NULL)
{
kmm_free(fs->work);
}
if (fs->cache != NULL)
{
kmm_free(fs->cache);
}
/* Free the volume memory (note that the semaphore is now stale!) */
nxsem_destroy(&fs->exclsem.sem);
kmm_free(fs);
ret = OK;
errout_with_lock:
spiffs_unlock_volume(fs);
return ret;
}
/****************************************************************************
* Name: spiffs_statfs
****************************************************************************/
static int spiffs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
{
FAR struct spiffs_s *fs;
FAR struct spiffs_file_s *fobj;
uint32_t pages_per_block;
uint32_t blocks;
uint32_t obj_lupages;
uint32_t data_pgsize;
uint32_t ndata_pages;
uint32_t nfile_objs;
uint32_t used;
finfo("mountpt: %p buf: %p\n", mountpt, buf);
DEBUGASSERT(mountpt != NULL && buf != NULL);
/* Get the mountpoint private data from the inode structure */
fs = mountpt->i_private;
DEBUGASSERT(fs != NULL);
/* Lock the SPIFFS volume */
spiffs_lock_volume(fs);
/* Collect some statistics */
pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs);
blocks = fs->geo.neraseblocks;
obj_lupages = SPIFFS_OBJ_LOOKUP_PAGES(fs);
data_pgsize = SPIFFS_DATA_PAGE_SIZE(fs);
/* -2 for spare blocks, +1 for emergency page */
ndata_pages = (blocks - 2) * (pages_per_block - obj_lupages) + 1;
used = fs->stats_p_allocated * data_pgsize;
/* Count the number of file objects */
nfile_objs = 0;
for (fobj = (FAR struct spiffs_file_s *)dq_peek(&fs->objq);
fobj != NULL;
fobj = (FAR struct spiffs_file_s *)dq_next((FAR dq_entry_t *)fobj))
{
nfile_objs++;
}
/* Fill in the statfs structure */
buf->f_type = SPIFFS_SUPER_MAGIC;
buf->f_namelen = CONFIG_SPIFFS_NAME_MAX - 1;
buf->f_bsize = data_pgsize;
buf->f_blocks = ndata_pages;
buf->f_bfree = ndata_pages - used;
buf->f_bavail = buf->f_bfree;
buf->f_files = nfile_objs;
buf->f_ffree = buf->f_bfree; /* SWAG */
/* Release the lock on the file system */
spiffs_unlock_volume(fs);
return OK;
}
/****************************************************************************
* Name: spiffs_unlink
****************************************************************************/
static int spiffs_unlink(FAR struct inode *mountpt, FAR const char *relpath)
{
FAR struct spiffs_s *fs;
FAR struct spiffs_file_s *fobj;
int16_t pgndx;
int ret;
finfo("mountpt: %p relpath: %s\n", mountpt, relpath);
DEBUGASSERT(mountpt != NULL && relpath != NULL);
if (strlen(relpath) > CONFIG_SPIFFS_NAME_MAX - 1)
{
return -ENAMETOOLONG;
}
/* Get the file system structure from the inode reference. */
fs = mountpt->i_private;
DEBUGASSERT(fs != NULL);
/* Get exclusive access to the file system */
spiffs_lock_volume(fs);
/* Find the page index to the object header associated with this path */
ret = spiffs_find_objhdr_pgndx(fs, (FAR const uint8_t *)relpath, &pgndx);
if (ret < OK)
{
fwarn("WARNING: No objhdr found for relpath '%s': %d\n", relpath, ret);
goto errout_with_lock;
}
else if (ret != -ENOENT)
{
ferr("ERROR: spiffs_find_objhdr_pgndx failed: %d\n", ret);
goto errout_with_lock;
}
/* Check to see if there is an open file reference for the object at this
* page index.
*/
ret = spiffs_find_fobj_bypgndx(fs, pgndx, &fobj);
if (ret >= 0)
{
/* If so, then we cannot unlink the file now. Just mark the file as
* 'unlinked' so that it can be removed when the file object is
* released.
*/
fobj->flags |= SFO_FLAG_UNLINKED;
}
else
{
/* Otherwise, we will ne to re-open the file */
/* Allocate new file object */
fobj = (FAR struct spiffs_file_s *)kmm_zalloc(sizeof(struct spiffs_file_s));
if (fobj == NULL)
{
fwarn("WARNING: Failed to allocate fobjs\n");
ret = -ENOMEM;
goto errout_with_lock;
}
/* Use the page index to open the file */
ret = spiffs_object_open_bypage(fs, pgndx, fobj, 0, 0);
if (ret < 0)
{
ferr("ERROR: spiffs_object_open_bypage failed: %d\n", ret);
kmm_free(fobj);
goto errout_with_lock;
}
/* Now we can remove the file by truncating it to zero length */
ret = spiffs_object_truncate(fs, fobj, 0, true);
kmm_free(fobj);
if (ret < 0)
{
ferr("ERROR: spiffs_object_truncate failed: %d\n", ret);
goto errout_with_lock;
}
}
/* Release the lock on the volume */
spiffs_unlock_volume(fs);
return OK;
errout_with_lock:
spiffs_unlock_volume(fs);
return ret;
}
/****************************************************************************
* Name: spiffs_mkdir
****************************************************************************/
static int spiffs_mkdir(FAR struct inode *mountpt, FAR const char *relpath,
mode_t mode)
{
finfo("mountpt: %p relpath: %s mode: %04x\n", mountpt, relpath, mode);
DEBUGASSERT(mountpt != NULL && relpath != NULL);
/* Directories are not supported */
return -ENOSYS;
}
/****************************************************************************
* Name: spiffs_rmdir
****************************************************************************/
static int spiffs_rmdir(FAR struct inode *mountpt, FAR const char *relpath)
{
finfo("mountpt: %p relpath: %s\n", mountpt, relpath);
DEBUGASSERT(mountpt != NULL && relpath != NULL);
/* Directories are not supported */
return -ENOSYS;
}
/****************************************************************************
* Name: spiffs_rename
****************************************************************************/
static int spiffs_rename(FAR struct inode *mountpt, FAR const char *oldrelpath,
FAR const char *newrelpath)
{
FAR struct spiffs_s *fs;
FAR struct spiffs_file_s *fobj;
int16_t oldpgndx;
int16_t newpgndx;
int ret;
finfo("mountpt: %p oldrelpath: %s newrelpath: %s\n",
mountpt, oldrelpath, newrelpath);
DEBUGASSERT(mountpt != NULL && oldrelpath != NULL && newrelpath != NULL);
/* Get the file system structure from the inode reference. */
fs = mountpt->i_private;
DEBUGASSERT(fs != NULL);
if (strlen(newrelpath) > CONFIG_SPIFFS_NAME_MAX - 1 ||
strlen(oldrelpath) > CONFIG_SPIFFS_NAME_MAX - 1)
{
return -ENAMETOOLONG;
}
/* Get exclusive access to the file system */
spiffs_lock_volume(fs);
/* Get the page index of the object header for the oldrelpath */
ret = spiffs_find_objhdr_pgndx(fs, (FAR const uint8_t *)oldrelpath,
&oldpgndx);
if (ret < 0)
{
fwarn("WARNING: spiffs_find_objhdr_pgndx failed: %d\n");
goto errout_with_lock;
}
/* Check if there is any file object corresponding to the newrelpath */
ret = spiffs_find_objhdr_pgndx(fs, (FAR const uint8_t *)newrelpath,
&newpgndx);
if (ret == -ENOENT)
{
ret = OK;
}
else if (ret == OK)
{
ret = -EEXIST;
}
if (ret < 0)
{
goto errout_with_lock;
}
/* Allocate new file object. NOTE: The file could already be open. */
fobj = (FAR struct spiffs_file_s *)kmm_zalloc(sizeof(struct spiffs_file_s));
if (fobj == NULL)
{
return -ENOMEM;
}
/* Use the page index to open the file */
ret = spiffs_object_open_bypage(fs, oldpgndx, fobj, 0, 0);
if (ret < 0)
{
goto errout_with_fobj;
}
/* Then update the file name */
ret = spiffs_object_update_index_hdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx, 0,
(FAR const uint8_t *)newrelpath, 0,
&newpgndx);
errout_with_fobj:
kmm_free(fobj);
errout_with_lock:
spiffs_unlock_volume(fs);
return ret;
}
/****************************************************************************
* Name: spiffs_stat
****************************************************************************/
static int spiffs_stat(FAR struct inode *mountpt, FAR const char *relpath,
FAR struct stat *buf)
{
FAR struct spiffs_s *fs;
int16_t pgndx;
int ret;
finfo("mountpt=%p relpath=%s buf=%p\n", mountpt, relpath, buf);
DEBUGASSERT(mountpt != NULL && relpath != NULL && buf != NULL);
if (strlen(relpath) > CONFIG_SPIFFS_NAME_MAX - 1)
{
return -ENAMETOOLONG;
}
/* Get the file system structure from the inode reference. */
fs = mountpt->i_private;
DEBUGASSERT(fs != NULL);
/* Get exclusive access to the file system */
spiffs_lock_volume(fs);
/* Find the object associated with this relative path */
ret = spiffs_find_objhdr_pgndx(fs, (FAR const uint8_t *)relpath, &pgndx);
if (ret < 0)
{
goto errout_with_lock;
}
/* And get information about the object */
ret = spiffs_stat_pgndx(fs, pgndx, 0, buf);
if (ret < 0)
{
ferr("ERROR: spiffs_stat_pgndx failed: %d\n", ret);
}
errout_with_lock:
spiffs_unlock_volume(fs);
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/