nuttx/fs/spiffs/src/spiffs_volume.c
chenrun1 3f47fd767a fs/xxfs:Replace kmm with fs heap
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>
2024-10-10 15:30:41 +02:00

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);
}