3f47fd767a
Summary: 1.Add configuration to allocate memory from the specified section 2.Replace all memory operations (kmm_) in the vfs with fs_heap_. When FS_HEAPSIZE > 0, memory is requested for the file system by specifying a configured heap location. By default (i.e. FS_HEAPSIZE=0) fs_heap_ is equivalent to kmm_ Signed-off-by: chenrun1 <chenrun1@xiaomi.com>
495 lines
14 KiB
C
495 lines
14 KiB
C
/****************************************************************************
|
|
* fs/spiffs/src/spiffs_volume.c
|
|
* SPIFFS Utility Functions for Volume and File Object Support
|
|
*
|
|
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
*
|
|
* This is a port of version 0.3.7 of SPIFFS by Peter Andersion. That
|
|
* version was originally released under the MIT license but is here re-
|
|
* released under the NuttX BSD 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 <string.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <assert.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
|
|
#include "fs_heap.h"
|
|
#include "spiffs.h"
|
|
#include "spiffs_core.h"
|
|
#include "spiffs_cache.h"
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: spiffs_stat_pgndx
|
|
*
|
|
* Description:
|
|
* Checks if there are any cached writes for the object ID associated with
|
|
* given file object. If so, these writes are flushed.
|
|
*
|
|
* Input Parameters:
|
|
* fobj - A reference to the file object to flush
|
|
*
|
|
* Returned Value:
|
|
* On success, then number of bytes flushed is returned. A negated errno
|
|
* value is returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int spiffs_stat_pgndx(FAR struct spiffs_s *fs, int16_t pgndx, int16_t objid,
|
|
FAR struct stat *buf)
|
|
{
|
|
struct spiffs_pgobj_ndxheader_s objhdr;
|
|
uint32_t obj_id_addr;
|
|
int16_t ndx;
|
|
mode_t mode;
|
|
int ret;
|
|
|
|
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_READ, objid,
|
|
SPIFFS_PAGE_TO_PADDR(fs, pgndx),
|
|
sizeof(struct spiffs_pgobj_ndxheader_s),
|
|
(FAR uint8_t *) & objhdr);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
obj_id_addr =
|
|
SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pgndx)) +
|
|
SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pgndx) * sizeof(int16_t);
|
|
|
|
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, objid,
|
|
obj_id_addr, sizeof(int16_t), (FAR uint8_t *)&ndx);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Build the struct stat */
|
|
|
|
mode = S_IRWXO | S_IRWXG | S_IRWXU; /* Assume all permissions */
|
|
mode |= S_IFREG; /* Assume regular file */
|
|
|
|
/* REVISIT: Should the file object type derive from objhdr.type? */
|
|
|
|
memset(buf, 0, sizeof(struct stat));
|
|
buf->st_mode = mode;
|
|
buf->st_size = objhdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objhdr.size;
|
|
buf->st_blksize = fs->geo.blocksize;
|
|
buf->st_blocks = fs->media_size / fs->geo.blocksize;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: spiffs_find_fobj_bypgndx
|
|
*
|
|
* Description:
|
|
* Given the page index of the object header, find the corresponding file
|
|
* object instance.
|
|
*
|
|
* Input Parameters:
|
|
* fs - A reference to the SPIFFS volume object instance
|
|
* pgndx - The page index to match
|
|
* ppfobj - A user provided location in which to return the matching file
|
|
* file object instance
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) is returned on success; A negated errno value is returned on
|
|
* any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int spiffs_find_fobj_bypgndx(FAR struct spiffs_s *fs, int16_t pgndx,
|
|
FAR struct spiffs_file_s **ppfobj)
|
|
{
|
|
FAR struct spiffs_file_s *fobj;
|
|
int ret = -ENOENT;
|
|
|
|
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))
|
|
{
|
|
if (fobj->objhdr_pgndx == pgndx)
|
|
{
|
|
ret = OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ppfobj != NULL)
|
|
{
|
|
*ppfobj = fobj;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: spiffs_find_fobj_byobjid
|
|
*
|
|
* Description:
|
|
* Given a object ID, find the corresponding file object instance
|
|
*
|
|
* Input Parameters:
|
|
* fs - A reference to the SPIFFS volume object instance
|
|
* objid - The object ID to match
|
|
* ppfobj - A user provided location in which to return the matching file
|
|
* file object instance
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) is returned on success; A negated errno value is returned on
|
|
* any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int spiffs_find_fobj_byobjid(FAR struct spiffs_s *fs, int16_t objid,
|
|
FAR struct spiffs_file_s **ppfobj)
|
|
{
|
|
FAR struct spiffs_file_s *fobj;
|
|
int ret = -ENOENT;
|
|
|
|
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))
|
|
{
|
|
if (fobj->objid == objid)
|
|
{
|
|
ret = OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ppfobj != NULL)
|
|
{
|
|
*ppfobj = fobj;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: spiffs_fobj_flush
|
|
*
|
|
* Description:
|
|
* Checks if there are any cached writes for the object ID associated with
|
|
* given file object. If so, these writes are flushed.
|
|
*
|
|
* Input Parameters:
|
|
* fs - A reference to the SPIFFS volume object instance
|
|
* fobj - A reference to the file object to flush
|
|
*
|
|
* Returned Value:
|
|
* On success, then number of bytes flushed is returned. A negated errno
|
|
* value is returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
ssize_t spiffs_fobj_flush(FAR struct spiffs_s *fs,
|
|
FAR struct spiffs_file_s *fobj)
|
|
{
|
|
ssize_t nwritten = 0;
|
|
|
|
/* If we were asked to use direct hardware accessed, then don't bother
|
|
* flushing the cache.
|
|
*/
|
|
|
|
if ((fobj->oflags & O_DIRECT) == 0)
|
|
{
|
|
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->cache_page)
|
|
{
|
|
spiffs_cacheinfo("Flushing cache page %d for objid=%d "
|
|
"offset=%" PRIu32 " 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);
|
|
if (nwritten < 0)
|
|
{
|
|
ferr("ERROR: spiffs_fobj_write failed %d\n", (int)nwritten);
|
|
}
|
|
|
|
spiffs_cache_page_release(fs, fobj->cache_page);
|
|
}
|
|
}
|
|
|
|
return nwritten;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: spiffs_fobj_write
|
|
*
|
|
* Description:
|
|
* Write to a file object
|
|
*
|
|
* Input Parameters:
|
|
* fs - A reference to the volume structure
|
|
* fobj - A reference to the file object to write to
|
|
* buffer - The data to be written
|
|
* offset - The FLASH offset to be written
|
|
* len - The number of bytes to be written
|
|
*
|
|
* Returned Value:
|
|
* On success, then number of bytes written is returned. A negated errno
|
|
* value is returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
ssize_t spiffs_fobj_write(FAR struct spiffs_s *fs,
|
|
FAR struct spiffs_file_s *fobj,
|
|
FAR const void *buffer, off_t offset, size_t len)
|
|
{
|
|
ssize_t remaining = len;
|
|
ssize_t total = 0;
|
|
|
|
if (fobj->size != SPIFFS_UNDEFINED_LEN)
|
|
{
|
|
while (offset < fobj->size)
|
|
{
|
|
ssize_t nwritten;
|
|
ssize_t wrsize;
|
|
|
|
wrsize = MIN((ssize_t)(fobj->size - offset), remaining);
|
|
nwritten = spiffs_fobj_modify(fs, fobj, offset,
|
|
(FAR uint8_t *)buffer, wrsize);
|
|
if (nwritten <= 0)
|
|
{
|
|
return nwritten;
|
|
}
|
|
|
|
remaining -= nwritten;
|
|
buffer += nwritten;
|
|
offset += nwritten;
|
|
total += nwritten;
|
|
}
|
|
}
|
|
|
|
while (remaining > 0)
|
|
{
|
|
ssize_t nappend;
|
|
|
|
nappend = spiffs_fobj_append(fs, fobj, offset,
|
|
(FAR uint8_t *)buffer, remaining);
|
|
if (nappend < 0)
|
|
{
|
|
if (nappend == -ENOSPC && total > 0)
|
|
{
|
|
return total;
|
|
}
|
|
|
|
return nappend;
|
|
}
|
|
|
|
remaining -= nappend;
|
|
buffer += nappend;
|
|
offset += nappend;
|
|
total += nappend;
|
|
}
|
|
|
|
return (ssize_t)total;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: spiffs_fobj_read
|
|
*
|
|
* Description:
|
|
* Read from a file object
|
|
*
|
|
* Input Parameters:
|
|
* fs - A reference to the volume structure
|
|
* fobj - A reference to the file object to read from
|
|
* buffer - The location that the data is read to
|
|
* offset - The FLASH offset to be read
|
|
* len - The number of bytes to be read
|
|
* fpos - The file position to read from
|
|
*
|
|
* Returned Value:
|
|
* On success, then number of bytes written is returned. A negated errno
|
|
* value is returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
ssize_t spiffs_fobj_read(FAR struct spiffs_s *fs,
|
|
FAR struct spiffs_file_s *fobj, FAR void *buffer,
|
|
size_t buflen, off_t fpos)
|
|
{
|
|
ssize_t nread;
|
|
ssize_t total;
|
|
|
|
/* Make sure that read access is supported */
|
|
|
|
if ((fobj->oflags & O_RDONLY) == 0)
|
|
{
|
|
return -EACCES;
|
|
}
|
|
|
|
/* Handle the special case of zero-length files */
|
|
|
|
if (fobj->size == SPIFFS_UNDEFINED_LEN && buflen > 0)
|
|
{
|
|
/* Return zero (meaning EOF) */
|
|
|
|
return 0;
|
|
}
|
|
|
|
spiffs_fobj_flush(fs, fobj);
|
|
|
|
/* Check for attempts to read beyond the end of the file */
|
|
|
|
if (fpos + buflen >= fobj->size)
|
|
{
|
|
/* Truncate */
|
|
|
|
buflen = fobj->size - fpos;
|
|
}
|
|
|
|
/* Read data from the file object until either we have read all of the
|
|
* requested data, or until a read error occurs.
|
|
*/
|
|
|
|
total = 0;
|
|
while (buflen > 0)
|
|
{
|
|
/* Read from the file object */
|
|
|
|
nread = spiffs_object_read(fs, fobj, fpos, buflen,
|
|
(FAR uint8_t *)buffer);
|
|
if (nread < 0)
|
|
{
|
|
ferr("ERROR: spiffs_object_read() failed: %d\n", (int)nread);
|
|
return nread;
|
|
}
|
|
|
|
total += nread;
|
|
buffer += nread;
|
|
fpos += nread;
|
|
buflen -= nread;
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: spiffs_fobj_free
|
|
*
|
|
* Description:
|
|
* Free all resources used a file object
|
|
*
|
|
* Input Parameters:
|
|
* fs - A reference to the volume structure
|
|
* fobj - A reference to the file object to be removed
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
void spiffs_fobj_free(FAR struct spiffs_s *fs,
|
|
FAR struct spiffs_file_s *fobj, bool unlink)
|
|
{
|
|
FAR struct spiffs_file_s *curr;
|
|
int ret;
|
|
|
|
finfo("Removing fobj for objid=%04x\n", fobj->objid);
|
|
|
|
/* Flush any buffered write data */
|
|
|
|
ret = spiffs_fobj_flush(fs, fobj);
|
|
if (ret < 0)
|
|
{
|
|
ferr("ERROR: spiffs_fobj_flush failed: %d\n", ret);
|
|
}
|
|
|
|
/* Remove the file object from the list of file objects in the volume
|
|
* structure.
|
|
*/
|
|
|
|
for (curr = (FAR struct spiffs_file_s *)dq_peek(&fs->objq);
|
|
curr != NULL;
|
|
curr = (FAR struct spiffs_file_s *)dq_next((FAR dq_entry_t *)curr))
|
|
{
|
|
/* Is this the entry we are searching for? */
|
|
|
|
if (curr == fobj)
|
|
{
|
|
/* Yes, remove it from the list of file objects */
|
|
|
|
dq_rem((FAR dq_entry_t *)curr, &fs->objq);
|
|
break;
|
|
}
|
|
}
|
|
|
|
DEBUGASSERT(curr != NULL);
|
|
|
|
/* Now we can remove the file by truncating it to zero length if it was
|
|
* unlinked.
|
|
*/
|
|
|
|
if (unlink)
|
|
{
|
|
ret = spiffs_fobj_truncate(fs, fobj, 0, true);
|
|
if (ret < 0)
|
|
{
|
|
ferr("ERROR: spiffs_fobj_truncate failed: %d\n", ret);
|
|
}
|
|
}
|
|
|
|
/* Then free the file object itself (which contains the lock we hold) */
|
|
|
|
fs_heap_free(fobj);
|
|
}
|