nuttx/fs/spiffs/src/spiffs_core.c
2021-03-30 12:29:11 -05:00

3677 lines
116 KiB
C

/****************************************************************************
* fs/spiffs/src/spiffs_core.c
*
* 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 <inttypes.h>
#include <string.h>
#include <nuttx/kmalloc.h>
#include "spiffs.h"
#include "spiffs_mtd.h"
#include "spiffs_gc.h"
#include "spiffs_cache.h"
#include "spiffs_core.h"
/****************************************************************************
* Private Types
****************************************************************************/
struct spiffs_free_objid_s
{
int16_t min_objid;
int16_t max_objid;
uint32_t compaction;
FAR const uint8_t *conflicting_name;
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: spiffs_page_data_check
*
* Description:
*
****************************************************************************/
static int spiffs_page_data_check(FAR struct spiffs_s *fs,
FAR struct spiffs_file_s *fobj,
int16_t pgndx, int16_t spndx)
{
#ifdef CONFIG_SPIFFS_PAGE_CHECK
struct spiffs_page_header_s ph;
#endif
int ret = OK;
if (pgndx == (int16_t)-1)
{
/* Referring to page 0xffff...., bad object index */
return SPIFFS_ERR_INDEX_REF_FREE;
}
if (pgndx % SPIFFS_GEO_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs))
{
/* Referring to an object lookup page, bad object index */
return SPIFFS_ERR_INDEX_REF_LU;
}
if (pgndx > SPIFFS_GEO_PAGE_COUNT(fs))
{
/* Referring to a bad page */
return SPIFFS_ERR_INDEX_REF_INVALID;
}
#ifdef CONFIG_SPIFFS_PAGE_CHECK
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ,
fobj->objid, SPIFFS_PAGE_TO_PADDR(fs, pgndx),
sizeof(struct spiffs_page_header_s),
(FAR uint8_t *)&ph);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
}
else if ((ph.flags & SPIFFS_PH_FLAG_USED) != 0)
{
ret = SPIFFS_ERR_IS_FREE;
}
else if ((ph.flags & SPIFFS_PH_FLAG_DELET) == 0)
{
ret = SPIFFS_ERR_DELETED;
}
else if ((ph.flags & SPIFFS_PH_FLAG_FINAL) != 0)
{
ret = SPIFFS_ERR_NOT_FINALIZED;
}
else if ((ph.flags & SPIFFS_PH_FLAG_INDEX) == 0)
{
ret = SPIFFS_ERR_IS_INDEX;
}
else if (ph.spndx != (spndx))
{
ret = SPIFFS_ERR_DATA_SPAN_MISMATCH;
}
#endif
return ret;
}
/****************************************************************************
* Name: spiffs_page_index_check
*
* Description:
*
****************************************************************************/
static int spiffs_page_index_check(FAR struct spiffs_s *fs,
FAR struct spiffs_file_s *fobj,
int16_t pgndx, int16_t spndx)
{
#ifdef CONFIG_SPIFFS_PAGE_CHECK
struct spiffs_page_header_s ph;
int ret;
#endif
if (pgndx == (int16_t) - 1)
{
/* Referring to page 0xffff...., bad object index */
return SPIFFS_ERR_INDEX_FREE;
}
if (pgndx % SPIFFS_GEO_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs))
{
/* Referring to an object lookup page, bad object index */
return SPIFFS_ERR_INDEX_LU;
}
if (pgndx > SPIFFS_GEO_PAGE_COUNT(fs))
{
/* Referring to a bad page */
return SPIFFS_ERR_INDEX_INVALID;
}
#ifdef CONFIG_SPIFFS_PAGE_CHECK
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_READ,
fobj->objid, SPIFFS_PAGE_TO_PADDR(fs, pgndx),
sizeof(struct spiffs_page_header_s), (uint8_t *) & ph);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
ret = spiffs_validate_objndx(&ph, fobj->objid, spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_validate_objndx() failed: %d\n", ret);
return ret;
}
#endif
return OK;
}
/****************************************************************************
* Name: spiffs_objlu_scan_callback
*
* Description:
*
****************************************************************************/
static int spiffs_objlu_scan_callback(FAR struct spiffs_s *fs, int16_t objid,
int16_t blkndx, int entry,
FAR const void *user_const,
FAR void *user_var)
{
if (objid == SPIFFS_OBJID_FREE)
{
if (entry == 0)
{
/* TODO: Optimize further, return SPIFFS_NEXT_BLOCK */
fs->free_blocks++;
}
}
else if (objid == SPIFFS_OBJID_DELETED)
{
fs->deleted_pages++;
}
else
{
fs->alloc_pages++;
}
return SPIFFS_VIS_COUNTINUE;
}
/****************************************************************************
* Name: spiffs_objlu_find_id_and_span_callback
*
* Description:
*
****************************************************************************/
static int spiffs_objlu_find_id_and_span_callback(FAR struct spiffs_s *fs,
int16_t objid,
int16_t blkndx,
int entry,
FAR const void *user_const,
FAR void *user_var)
{
struct spiffs_page_header_s ph;
int16_t pgndx;
int ret;
pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, entry);
ret = spiffs_cache_read(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
SPIFFS_PAGE_TO_PADDR(fs, pgndx),
sizeof(struct spiffs_page_header_s), (FAR uint8_t *)&ph);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
if (ph.objid == objid && ph.spndx == *((FAR int16_t *)user_var) &&
(ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET |
SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET &&
!((objid & SPIFFS_OBJID_NDXFLAG) != 0 &&
(ph.flags & SPIFFS_PH_FLAG_NDXDELE) == 0 && ph.spndx == 0) &&
(user_const == NULL || *((FAR const int16_t *)user_const) != pgndx))
{
return OK;
}
else
{
return SPIFFS_VIS_COUNTINUE;
}
}
/****************************************************************************
* Name: spiffs_find_objhdr_pgndx_callback
*
* Description:
*
****************************************************************************/
static int spiffs_find_objhdr_pgndx_callback(FAR struct spiffs_s *fs,
int16_t objid,
int16_t blkndx, int entry,
FAR const void *user_const,
FAR void *user_var)
{
struct spiffs_pgobj_ndxheader_s objhdr;
int16_t pgndx;
int ret;
if (objid == SPIFFS_OBJID_FREE || objid == SPIFFS_OBJID_DELETED ||
(objid & SPIFFS_OBJID_NDXFLAG) == 0)
{
return SPIFFS_VIS_COUNTINUE;
}
pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, 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_ndxheader_s),
(FAR uint8_t *)&objhdr);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
if (objhdr.phdr.spndx == 0 &&
(objhdr.phdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL |
SPIFFS_PH_FLAG_NDXDELE)) ==
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_NDXDELE))
{
if (
#ifdef CONFIG_SPIFFS_LEADING_SLASH
((FAR char *)objhdr.name)[0] == '/' &&
#endif
strcmp((FAR const char *)user_const,
(FAR char *)objhdr.name + SPIFFS_LEADING_SLASH_SIZE) == 0)
{
return OK;
}
}
return SPIFFS_VIS_COUNTINUE;
}
/****************************************************************************
* Name: spiffs_objlu_find_free_objid_bitmap_callback
*
* Description:
*
****************************************************************************/
static int
spiffs_objlu_find_free_objid_bitmap_callback(FAR struct spiffs_s *fs,
int16_t objid, int16_t blkndx,
int entry,
FAR const void *user_const,
FAR void *user_var)
{
if (objid != SPIFFS_OBJID_FREE && objid != SPIFFS_OBJID_DELETED)
{
int16_t min_objid = *((FAR int16_t *)user_var);
FAR const uint8_t *conflicting_name = (FAR const uint8_t *)user_const;
uint32_t bitndx;
int bytendx;
/* If conflicting name parameter is given, also check if this name is
* found in object index headers
*/
if (conflicting_name != NULL && (objid & SPIFFS_OBJID_NDXFLAG) != 0)
{
struct spiffs_pgobj_ndxheader_s objhdr;
int16_t pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx,
entry);
int ret;
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_ndxheader_s),
(FAR uint8_t *)&objhdr);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
if (objhdr.phdr.spndx == 0 &&
(objhdr.phdr.flags & (SPIFFS_PH_FLAG_DELET |
SPIFFS_PH_FLAG_FINAL |
SPIFFS_PH_FLAG_NDXDELE)) ==
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_NDXDELE))
{
if (strcmp((FAR const char *)user_const,
(FAR char *)objhdr.name) == 0)
{
return -EEXIST;
}
}
}
objid &= ~SPIFFS_OBJID_NDXFLAG;
bitndx = (objid - min_objid) & 7;
bytendx = (objid - min_objid) >> 3;
if (bytendx >= 0 && (uint32_t)bytendx < SPIFFS_GEO_PAGE_SIZE(fs))
{
fs->work[bytendx] |= (1 << bitndx);
}
}
return SPIFFS_VIS_COUNTINUE;
}
/****************************************************************************
* Name: spiffs_objlu_find_free_objid_compact_callback
*
* Description:
*
****************************************************************************/
static int
spiffs_objlu_find_free_objid_compact_callback(FAR struct spiffs_s *fs,
int16_t objid, int16_t blkndx,
int entry,
FAR const void *user_const,
FAR void *user_var)
{
if (objid != SPIFFS_OBJID_FREE && objid != SPIFFS_OBJID_DELETED &&
(objid & SPIFFS_OBJID_NDXFLAG) != 0)
{
FAR const struct spiffs_free_objid_s *state =
(FAR const struct spiffs_free_objid_s *)user_const;
struct spiffs_pgobj_ndxheader_s objhdr;
int ret;
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0,
SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, blkndx,
entry),
sizeof(struct spiffs_pgobj_ndxheader_s),
(FAR uint8_t *)&objhdr);
if (ret >= 0 && objhdr.phdr.spndx == 0 &&
((objhdr.phdr.flags & (SPIFFS_PH_FLAG_INDEX |
SPIFFS_PH_FLAG_FINAL |
SPIFFS_PH_FLAG_DELET)) ==
(SPIFFS_PH_FLAG_DELET)))
{
/* OK object look up entry */
if (state->conflicting_name &&
strcmp((FAR const char *)state->conflicting_name,
(FAR char *)objhdr.name) == 0)
{
return -EEXIST;
}
objid &= ~SPIFFS_OBJID_NDXFLAG;
if (objid >= state->min_objid && objid <= state->max_objid)
{
FAR uint8_t *map = (FAR uint8_t *)fs->work;
int ndx = (objid - state->min_objid) / state->compaction;
map[ndx]++;
}
}
}
return SPIFFS_VIS_COUNTINUE;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: spiffs_validate_objndx
*
* Description:
* Returns zero (OK) if everything checks out. Otherwise, a negated errno
* value is return to indicate the nature of the problem
*
****************************************************************************/
int spiffs_validate_objndx(FAR struct spiffs_page_header_s *ph,
int16_t objid, int16_t spndx)
{
int ret = OK;
if ((ph->flags & SPIFFS_PH_FLAG_USED) != 0)
{
ret = SPIFFS_ERR_IS_FREE;
}
else if ((ph->flags & SPIFFS_PH_FLAG_DELET) == 0)
{
ret = SPIFFS_ERR_DELETED;
}
else if ((ph->flags & SPIFFS_PH_FLAG_FINAL) != 0)
{
ret = SPIFFS_ERR_NOT_FINALIZED;
}
else if ((ph->flags & SPIFFS_PH_FLAG_INDEX) != 0)
{
ret = SPIFFS_ERR_NOT_INDEX;
}
else if ((objid & SPIFFS_OBJID_NDXFLAG) == 0)
{
ret = SPIFFS_ERR_NOT_INDEX;
}
else if (ph->spndx != spndx)
{
ret = SPIFFS_ERR_INDEX_SPAN_MISMATCH;
}
return ret;
}
/****************************************************************************
* Name: spiffs_phys_cpy
*
* Description:
*
****************************************************************************/
int spiffs_phys_cpy(FAR struct spiffs_s *fs,
int16_t objid, uint32_t dest, uint32_t src, uint32_t len)
{
uint8_t cpybuf[CONFIG_SPIFFS_COPYBUF_STACK];
int ret;
while (len > 0)
{
uint32_t chunk_size = MIN(CONFIG_SPIFFS_COPYBUF_STACK, len);
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS,
objid, src, chunk_size, cpybuf);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD,
objid, dest, chunk_size, cpybuf);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
len -= chunk_size;
src += chunk_size;
dest += chunk_size;
}
return OK;
}
/****************************************************************************
* Name: spiffs_foreach_objlu
*
* Description:
* Find object lookup entry containing given objid with visitor.
* Iterate over object lookup pages in each block until a given object
* objid entry is found.
*
* When found, the visitor function is called with block index, entry
* index and user data.
*
* If visitor returns SPIFFS_VIS_CONTINUE, the search goes on. Otherwise,
* the search will be ended and visitor's return code is returned to
* caller.
*
* If no visitor is given (0) the search returns on first entry with
* matching object objid.
*
* If no match is found in all look up, SPIFFS_VIS_END is returned.
*
* Input Parameters:
* fs - The file system
* starting_block - The starting block to start search in
* starting_lu_entry - The look up index entry to start search in
* flags - ORed combination of SPIFFS_VIS_CHECK_ID,
* SPIFFS_VIS_CHECK_PH, SPIFFS_VIS_NO_WRAP
* objid - Argument object ID
* cb - Visitor callback function
* user_const - Any const pointer, passed to the callback visitor
* function
* user_var - Any pointer, passed to the callback visitor function
* blkndx - Reported block index where match was found
* lu_entry - Reported look up index where match was found
*
****************************************************************************/
int spiffs_foreach_objlu(FAR struct spiffs_s *fs, int16_t starting_block,
int starting_lu_entry, uint8_t flags, int16_t objid,
spiffs_callback_t cb, FAR const void *user_const,
FAR void *user_var, FAR int16_t *blkndx,
FAR int *lu_entry)
{
FAR int16_t *objlu_buf;
uint32_t cur_block_addr;
int32_t entry_count;
int16_t cur_block;
int cur_entry;
int entries_per_page;
int ret;
entry_count = SPIFFS_GEO_BLOCK_COUNT(fs) *
SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs);
cur_block = starting_block;
cur_block_addr = starting_block * SPIFFS_GEO_BLOCK_SIZE(fs);
objlu_buf = (FAR int16_t *)fs->lu_work;
cur_entry = starting_lu_entry;
entries_per_page = (SPIFFS_GEO_PAGE_SIZE(fs) / sizeof(int16_t));
ret = OK;
/* Wrap initial */
if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1)
{
cur_entry = 0;
cur_block++;
cur_block_addr = cur_block * SPIFFS_GEO_BLOCK_SIZE(fs);
if (cur_block >= SPIFFS_GEO_BLOCK_COUNT(fs))
{
if (flags & SPIFFS_VIS_NO_WRAP)
{
return SPIFFS_VIS_END;
}
else
{
/* Block wrap */
cur_block = 0;
cur_block_addr = 0;
}
}
}
/* Check each block */
while (ret >= 0 && entry_count > 0)
{
int obj_lookup_page = cur_entry / entries_per_page;
/* Check each object lookup page */
while (ret >= 0 &&
obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs))
{
int entry_offset;
off_t physoff;
entry_offset = obj_lookup_page * entries_per_page;
physoff = cur_block_addr +
SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page);
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, physoff, SPIFFS_GEO_PAGE_SIZE(fs),
fs->lu_work);
/* Check each entry */
while (ret >= 0 &&
cur_entry - entry_offset < entries_per_page &&
cur_entry < (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs))
{
if ((flags & SPIFFS_VIS_CHECK_ID) == 0 ||
objlu_buf[cur_entry - entry_offset] == objid)
{
if (blkndx)
{
*blkndx = cur_block;
}
if (lu_entry)
{
*lu_entry = cur_entry;
}
if (cb)
{
ret = cb(fs,
(flags & SPIFFS_VIS_CHECK_PH) ? objid :
objlu_buf[cur_entry - entry_offset],
cur_block, cur_entry, user_const, user_var);
if (ret == SPIFFS_VIS_COUNTINUE ||
ret == SPIFFS_VIS_COUNTINUE_RELOAD)
{
if (ret == SPIFFS_VIS_COUNTINUE_RELOAD)
{
physoff = cur_block_addr +
SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page);
ret = spiffs_cache_read(fs,
SPIFFS_OP_T_OBJ_LU |
SPIFFS_OP_C_READ, 0,
physoff,
SPIFFS_GEO_PAGE_SIZE(fs),
fs->lu_work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: "
"%d\n",
ret);
return ret;
}
}
ret = OK;
cur_entry++;
entry_count--;
continue;
}
else
{
return ret;
}
}
else
{
return OK;
}
}
entry_count--;
cur_entry++;
}
obj_lookup_page++;
}
cur_entry = 0;
cur_block++;
cur_block_addr += SPIFFS_GEO_BLOCK_SIZE(fs);
if (cur_block >= SPIFFS_GEO_BLOCK_COUNT(fs))
{
if (flags & SPIFFS_VIS_NO_WRAP)
{
return SPIFFS_VIS_END;
}
else
{
/* Block wrap */
cur_block = 0;
cur_block_addr = 0;
}
}
}
if (ret < 0)
{
return ret;
}
return SPIFFS_VIS_END;
}
/****************************************************************************
* Name: spiffs_erase_block
*
* Description:
*
****************************************************************************/
int spiffs_erase_block(FAR struct spiffs_s *fs, int16_t blkndx)
{
uint32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, blkndx);
int32_t size = SPIFFS_GEO_BLOCK_SIZE(fs);
int ret;
/* Here we ignore the return value and just try erasing the block */
while (size > 0)
{
finfo("erase %08lx:%" PRIu32 "\n",
(unsigned long)addr, SPIFFS_GEO_EBLOCK_SIZE(fs));
spiffs_mtd_erase(fs, addr, SPIFFS_GEO_EBLOCK_SIZE(fs));
addr += SPIFFS_GEO_EBLOCK_SIZE(fs);
size -= SPIFFS_GEO_EBLOCK_SIZE(fs);
}
fs->free_blocks++;
/* Register erase count for this block */
ret = spiffs_cache_write(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0,
SPIFFS_ERASE_COUNT_PADDR(fs, blkndx),
sizeof(int16_t),
(FAR uint8_t *)&fs->max_erase_count);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
fs->max_erase_count++;
if (fs->max_erase_count == SPIFFS_OBJID_NDXFLAG)
{
fs->max_erase_count = 0;
}
return ret;
}
/****************************************************************************
* Name: spiffs_objlu_scan
*
* Description:
* Scans through all obj lu and counts free, deleted and used pages.
* Find the maximum block erase count Checks magic if enabled
*
****************************************************************************/
int spiffs_objlu_scan(FAR struct spiffs_s *fs)
{
int16_t blkndx;
int16_t erase_count_final;
int16_t erase_count_min;
int16_t erase_count_max;
int entry;
int ret;
/* Find out erase count. */
blkndx = 0;
erase_count_min = SPIFFS_OBJID_FREE;
erase_count_max = 0;
while (blkndx < SPIFFS_GEO_BLOCK_COUNT(fs))
{
int16_t erase_count;
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_ERASE_COUNT_PADDR(fs, blkndx),
sizeof(int16_t), (FAR uint8_t *)&erase_count);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
if (erase_count != SPIFFS_OBJID_FREE)
{
erase_count_min = MIN(erase_count_min, erase_count);
erase_count_max = MAX(erase_count_max, erase_count);
}
blkndx++;
}
if (erase_count_min == 0 && erase_count_max == SPIFFS_OBJID_FREE)
{
/* Clean system, set counter to zero */
erase_count_final = 0;
}
else if (erase_count_max - erase_count_min > (SPIFFS_OBJID_FREE / 2))
{
/* Wrap, take min */
erase_count_final = erase_count_min + 1;
}
else
{
erase_count_final = erase_count_max + 1;
}
fs->max_erase_count = erase_count_final;
/* Count blocks */
fs->free_blocks = 0;
fs->alloc_pages = 0;
fs->deleted_pages = 0;
ret = spiffs_foreach_objlu(fs, 0, 0, 0, 0, spiffs_objlu_scan_callback,
0, 0, &blkndx, &entry);
if (ret == SPIFFS_VIS_END)
{
ret = OK;
}
else if (ret < 0)
{
ferr("ERROR: spiffs_foreach_objlu() failed: %d\n", ret);
}
return ret;
}
/****************************************************************************
* Name: spiffs_objlu_find_free
*
* Description:
* Find free object lookup entry. Iterate over object lookup pages in each
* block until a free object ID entry is found
*
****************************************************************************/
int spiffs_objlu_find_free(FAR struct spiffs_s *fs, int16_t starting_block,
int starting_lu_entry, FAR int16_t *blkndx,
FAR int *lu_entry)
{
int ret;
if (fs->free_blocks < 2)
{
ret = spiffs_gc_quick(fs, 0);
/* spiffs_gc_quick() will return -ENODATA if no blocks were freed */
if (ret == -ENODATA)
{
ret = OK;
}
else if (ret < 0)
{
return ret;
}
if (fs->free_blocks < 2)
{
return -ENOSPC;
}
}
ret = spiffs_objlu_find_id(fs, starting_block, starting_lu_entry,
SPIFFS_OBJID_FREE, blkndx, lu_entry);
if (ret >= 0)
{
fs->free_blkndx = *blkndx;
fs->free_entry = (*lu_entry) + 1;
if (*lu_entry == 0)
{
fs->free_blocks--;
}
}
if (ret == -ENOSPC)
{
fwarn("WARNING: Filesystem full\n");
}
return ret;
}
/****************************************************************************
* Name: spiffs_objlu_find_id
*
* Description:
* Find object lookup entry containing given objid. Iterate over object
* lookup pages in each block until a given object objid entry is found
*
****************************************************************************/
int spiffs_objlu_find_id(FAR struct spiffs_s *fs, int16_t starting_block,
int starting_lu_entry, int16_t objid,
FAR int16_t *blkndx, FAR int *lu_entry)
{
int ret = spiffs_foreach_objlu(fs, starting_block, starting_lu_entry,
SPIFFS_VIS_CHECK_ID, objid, 0, 0, 0,
blkndx, lu_entry);
if (ret == SPIFFS_VIS_END)
{
ret = -ENOENT;
}
return ret;
}
/****************************************************************************
* Name: spiffs_objlu_find_id_and_span
*
* Description:
* Find object lookup entry containing given objid and span index.
* Iterate over object lookup pages in each block until a given object
* objid entry is found
*
****************************************************************************/
int spiffs_objlu_find_id_and_span(FAR struct spiffs_s *fs, int16_t objid,
int16_t spndx, int16_t exclusion_pgndx,
FAR int16_t *pgndx)
{
int16_t blkndx;
int entry;
int ret;
ret = spiffs_foreach_objlu(fs, fs->lu_blkndx, fs->lu_entry,
SPIFFS_VIS_CHECK_ID, objid,
spiffs_objlu_find_id_and_span_callback,
exclusion_pgndx ? &exclusion_pgndx : 0,
&spndx, &blkndx, &entry);
if (ret == SPIFFS_VIS_END)
{
ret = -ENOENT;
}
else if (ret < 0)
{
ferr("ERROR: spiffs_foreach_objlu() failed: %d\n", ret);
return ret;
}
if (pgndx != NULL)
{
*pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, entry);
}
fs->lu_blkndx = blkndx;
fs->lu_entry = entry;
return ret;
}
/****************************************************************************
* Name: spiffs_objlu_find_id_and_span_byphdr
*
* Description:
* Find object lookup entry containing given objid and span index in page
* headers only. Iterate over object lookup pages in each block until a
* given object objid entry is found
*
****************************************************************************/
int spiffs_objlu_find_id_and_span_byphdr(FAR struct spiffs_s *fs,
int16_t objid, int16_t spndx,
int16_t exclusion_pgndx,
FAR int16_t *pgndx)
{
int16_t blkndx;
int entry;
int ret;
ret = spiffs_foreach_objlu(fs, fs->lu_blkndx, fs->lu_entry,
SPIFFS_VIS_CHECK_PH, objid,
spiffs_objlu_find_id_and_span_callback,
exclusion_pgndx ? &exclusion_pgndx : 0,
&spndx, &blkndx, &entry);
if (ret == SPIFFS_VIS_END)
{
ret = -ENOENT;
}
if (ret < 0)
{
return ret;
}
if (pgndx != NULL)
{
*pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, entry);
}
fs->lu_blkndx = blkndx;
fs->lu_entry = entry;
return ret;
}
/****************************************************************************
* Name: spiffs_page_allocate_data
*
* Description:
* Allocates a free defined page with given objid. Occupies object
* lookup entry and page. Data may be NULL; where only page header is
* stored, len and page_offs is ignored
*
****************************************************************************/
int spiffs_page_allocate_data(FAR struct spiffs_s *fs, int16_t objid,
FAR struct spiffs_page_header_s *ph,
FAR uint8_t *data, uint32_t len,
uint32_t page_offs,
bool finalize, FAR int16_t *pgndx)
{
int16_t blkndx;
int entry;
int ret = OK;
/* Find free entry */
ret = spiffs_objlu_find_free(fs, fs->free_blkndx, fs->free_entry,
&blkndx, &entry);
if (ret < 0)
{
ferr("ERROR: spiffs_objlu_find_free() failed: %d\n", ret);
return ret;
}
/* Occupy page in object lookup */
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
0, SPIFFS_BLOCK_TO_PADDR(fs, blkndx) +
entry * sizeof(int16_t), sizeof(int16_t),
(FAR uint8_t *)&objid);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
fs->alloc_pages++;
/* Write page header */
ph->flags &= ~SPIFFS_PH_FLAG_USED;
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, 0,
SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, blkndx, entry),
sizeof(struct spiffs_page_header_s),
(FAR uint8_t *)ph);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
/* Write page data */
if (data)
{
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, 0,
SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, blkndx, entry) +
sizeof(struct spiffs_page_header_s) + page_offs,
len, data);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
}
/* Finalize header if necessary */
if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL) != 0)
{
ph->flags &= ~SPIFFS_PH_FLAG_FINAL;
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, 0,
SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, blkndx, entry) +
offsetof(struct spiffs_page_header_s, flags),
sizeof(uint8_t), (FAR uint8_t *)&ph->flags);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
}
/* Return written page */
if (pgndx != NULL)
{
*pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, entry);
}
return ret;
}
/****************************************************************************
* Name: spiffs_page_move
*
* Description:
* Moves a page from src to a free page and finalizes it. Updates page
* index. Page data is given in param page. If page data is NULL,
* provided header is used for meta-info and page data is physically
* copied.
*
****************************************************************************/
int spiffs_page_move(FAR struct spiffs_s *fs,
int16_t objid, FAR uint8_t *page_data, int16_t ndx,
FAR struct spiffs_page_header_s *page_hdr,
int16_t src_pgndx, FAR int16_t *dst_pgndx)
{
struct spiffs_page_header_s *phdr;
bool was_final = false;
int16_t free_pgndx;
int16_t blkndx;
int entry;
int ret;
/* Find free entry */
ret = spiffs_objlu_find_free(fs, fs->free_blkndx, fs->free_entry,
&blkndx, &entry);
if (ret < 0)
{
ferr("ERROR: spiffs_gc_epage_stats() failed: %d\n", ret);
return ret;
}
free_pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, entry);
if (dst_pgndx != NULL)
{
*dst_pgndx = free_pgndx;
}
phdr = page_data ? (struct spiffs_page_header_s *) page_data : page_hdr;
if (page_data != NULL)
{
/* Got page data */
was_final = (phdr->flags & SPIFFS_PH_FLAG_FINAL) == 0;
/* Write unfinalized page */
phdr->flags |= SPIFFS_PH_FLAG_FINAL;
phdr->flags &= ~SPIFFS_PH_FLAG_USED;
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, 0,
SPIFFS_PAGE_TO_PADDR(fs, free_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), page_data);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
}
else
{
/* Copy page data */
ret = spiffs_phys_cpy(fs, objid, SPIFFS_PAGE_TO_PADDR(fs, free_pgndx),
SPIFFS_PAGE_TO_PADDR(fs, src_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs));
if (ret < 0)
{
ferr("ERROR: spiffs_phys_cpy() failed: %d\n", ret);
return ret;
}
}
/* Mark entry in destination object lookup */
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, 0,
SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pgndx)) +
SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pgndx) *
sizeof(int16_t), sizeof(int16_t),
(FAR uint8_t *)&ndx);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
fs->alloc_pages++;
if (was_final)
{
/* Mark finalized in destination page */
phdr->flags &= ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED);
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
objid,
SPIFFS_PAGE_TO_PADDR(fs, free_pgndx) +
offsetof(struct spiffs_page_header_s, flags),
sizeof(uint8_t), (FAR uint8_t *)&phdr->flags);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
}
/* Mark source deleted */
ret = spiffs_page_delete(fs, src_pgndx);
return ret;
}
/****************************************************************************
* Name: spiffs_page_delete
*
* Description:
* Deletes a page and removes it from object lookup.
*
****************************************************************************/
int spiffs_page_delete(FAR struct spiffs_s *fs, int16_t pgndx)
{
uint8_t flags;
int ret;
/* Mark deleted entry in source object lookup */
int16_t d_objid = SPIFFS_OBJID_DELETED;
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, 0,
SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pgndx)) +
SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pgndx) *
sizeof(int16_t), sizeof(int16_t),
(FAR uint8_t *)&d_objid);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
fs->deleted_pages++;
fs->alloc_pages--;
/* Mark deleted in source page */
flags = 0xff;
#ifdef CONFIG_SPIFFS_NO_BLIND_WRITES
/* Perform read-modify-write operation */
physoff = SPIFFS_PAGE_TO_PADDR(fs, pgndx) +
offsetof(struct spiffs_page_header_s, flags)
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, 0,
physoff, sizeof(flags), &flags);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
#endif
flags &= ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED);
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, 0,
SPIFFS_PAGE_TO_PADDR(fs, pgndx) +
offsetof(struct spiffs_page_header_s, flags),
sizeof(flags), &flags);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
}
return ret;
}
/****************************************************************************
* Name: spiffs_fobj_create
*
* Description:
* Create an object index header page with empty index and undefined length
*
****************************************************************************/
int spiffs_fobj_create(FAR struct spiffs_s *fs,
int16_t objid, const uint8_t name[],
uint8_t type, FAR int16_t *objhdr_pgndx)
{
struct spiffs_pgobj_ndxheader_s objndx_hdr;
int16_t blkndx;
int entry;
int ret = OK;
ret = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs));
if (ret < 0)
{
ferr("ERROR: spiffs_gc_check() failed: %d\n", ret);
return ret;
}
objid |= SPIFFS_OBJID_NDXFLAG;
/* Find free entry */
ret = spiffs_objlu_find_free(fs, fs->free_blkndx, fs->free_entry,
&blkndx, &entry);
if (ret < 0)
{
ferr("ERROR: spiffs_objlu_find_free() failed: %d\n", ret);
return ret;
}
finfo("Found free page @ %04x blkndx=%04x entry=%04x\n",
(int16_t) SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, entry),
blkndx, entry);
/* Occupy page in object lookup */
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, 0,
SPIFFS_BLOCK_TO_PADDR(fs, blkndx) +
entry * sizeof(int16_t), sizeof(int16_t),
(FAR uint8_t *)&objid);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
fs->alloc_pages++;
/* Write empty object index page */
objndx_hdr.phdr.objid = objid;
objndx_hdr.phdr.spndx = 0;
objndx_hdr.phdr.flags =
0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX |
SPIFFS_PH_FLAG_USED);
objndx_hdr.type = type;
objndx_hdr.size = SPIFFS_UNDEFINED_LEN;
#ifdef CONFIG_SPIFFS_LEADING_SLASH
objndx_hdr.name[0] = '/';
#endif
strncpy((char *)objndx_hdr.name + SPIFFS_LEADING_SLASH_SIZE,
(const char *)name,
CONFIG_SPIFFS_NAME_MAX);
/* Update page */
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, 0,
SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, blkndx,
entry),
sizeof(struct spiffs_pgobj_ndxheader_s),
(FAR uint8_t *)&objndx_hdr);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
spiffs_fobj_event(fs, (FAR struct spiffs_page_objndx_s *)&objndx_hdr,
SPIFFS_EV_NDXNEW, objid, 0,
SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, entry),
SPIFFS_UNDEFINED_LEN);
if (objhdr_pgndx)
{
*objhdr_pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, entry);
}
return ret < 0 ? ret : OK;
}
/****************************************************************************
* Name: spiffs_fobj_update_ndxhdr
*
* Description:
* Update object index header with any combination of name/size/index.
* new_objhdr_data may be NULL, if so the object index header page is
* loaded. Name may be NULL, if so name is not changed. size may be NULL,
* if so size is not changed
*
****************************************************************************/
int spiffs_fobj_update_ndxhdr(FAR struct spiffs_s *fs,
FAR struct spiffs_file_s *fobj,
int16_t objid, int16_t objhdr_pgndx,
FAR uint8_t *new_objhdr_data,
const uint8_t name[],
uint32_t size, FAR int16_t *new_pgndx)
{
FAR struct spiffs_pgobj_ndxheader_s *objhdr;
int16_t new_objhdr_pgndx;
int ret = OK;
objid |= SPIFFS_OBJID_NDXFLAG;
if (new_objhdr_data)
{
/* object index header page already given to us, no need to load it */
objhdr = (FAR struct spiffs_pgobj_ndxheader_s *) new_objhdr_data;
}
else
{
/* Read object index header page */
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_READ,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, objhdr_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
objhdr = (FAR struct spiffs_pgobj_ndxheader_s *)fs->work;
}
ret = spiffs_validate_objndx(&objhdr->phdr, objid, 0);
if (ret < 0)
{
ferr("ERROR: spiffs_validate_objndx() failed: %d\n", ret);
return ret;
}
/* Change name */
if (name != NULL)
{
strncpy((FAR char *)objhdr->name + SPIFFS_LEADING_SLASH_SIZE,
(FAR const char *)name,
CONFIG_SPIFFS_NAME_MAX);
}
if (size != 0)
{
objhdr->size = size;
}
/* Move and update page */
ret = spiffs_page_move(fs, fobj == NULL ? 0 : fobj->objid,
(FAR uint8_t *)objhdr, objid, 0, objhdr_pgndx,
&new_objhdr_pgndx);
if (ret >= 0)
{
if (new_pgndx != NULL)
{
*new_pgndx = new_objhdr_pgndx;
}
/* Object index update */
spiffs_fobj_event(fs, (FAR struct spiffs_page_objndx_s *)objhdr,
new_objhdr_data ? SPIFFS_EV_NDXUPD :
SPIFFS_EV_NDXUPD_HDR, objid,
objhdr->phdr.spndx, new_objhdr_pgndx,
objhdr->size);
if (fobj != NULL)
{
fobj->objhdr_pgndx = new_objhdr_pgndx; /* If this is not in the
* registered cluster */
}
}
return ret;
}
/****************************************************************************
* Name: spiffs_fobj_event
*
* Description:
*
****************************************************************************/
void spiffs_fobj_event(FAR struct spiffs_s *fs,
FAR struct spiffs_page_objndx_s *objndx,
int ev, int16_t objid_raw, int16_t spndx,
int16_t new_pgndx, uint32_t new_size)
{
FAR static const char *evname[] =
{
"UPD", "NEW", "DEL", "MOV", "HUP", "???"
};
FAR struct spiffs_file_s *fobj;
FAR struct spiffs_file_s *next;
int16_t objid = objid_raw & ~SPIFFS_OBJID_NDXFLAG;
finfo("Event=%s objid=%04x spndx=%04x npgndx=%04x nsz=%" PRIu32 "\n",
evname[MIN(ev, 5)], objid_raw, spndx, new_pgndx, new_size);
/* Update index caches in all file descriptors */
for (fobj = (FAR struct spiffs_file_s *)dq_peek(&fs->objq);
fobj != NULL;
fobj = next)
{
/* Set up for the next time through the loop (in case fobj is
* deleted)
*/
next = (FAR struct spiffs_file_s *)dq_next((FAR dq_entry_t *)fobj);
/* Is this the object we are looking for? */
if ((fobj->objid & ~SPIFFS_OBJID_NDXFLAG) != objid)
{
continue; /* Object not related to updated file */
}
if (spndx == 0)
{
/* Object index header update */
if (ev != SPIFFS_EV_NDXDEL)
{
finfo("Setting objid=%d (offset=%" PRIu32 ") objhdr_pgndx "
"to %04x size=%" PRIu32 "\n",
fobj->objid, fobj->offset, new_pgndx, new_size);
fobj->objhdr_pgndx = new_pgndx;
if (new_size != 0)
{
uint32_t act_new_size = 0;
/* Update size and offsets for fobj to this file */
fobj->size = new_size;
if (new_size != SPIFFS_UNDEFINED_LEN)
{
act_new_size = new_size;
}
if (act_new_size > 0 && fobj->cache_page)
{
act_new_size = MAX(act_new_size,
fobj->cache_page->offset +
fobj->cache_page->size);
}
if (fobj->offset > act_new_size)
{
fobj->offset = act_new_size;
}
if (fobj->cache_page &&
fobj->cache_page->offset > act_new_size + 1)
{
spiffs_cacheinfo("File truncated, "
"dropping cache page=%d, "
"no writeback\n",
fobj->cache_page->cpndx);
spiffs_cache_page_release(fs, fobj->cache_page);
}
}
}
else
{
/* Removing file */
if (fobj->cache_page)
{
spiffs_cacheinfo("File deleted, dropping cache page=%d, "
"no writeback\n",
fobj->cache_page->cpndx);
spiffs_cache_page_release(fs, fobj->cache_page);
}
finfo("Release objid=%d span=%04x objndx_pgndx to %04x\n",
fobj->objid, spndx, new_pgndx);
/* Remove the file object */
spiffs_fobj_free(fs, fobj, true);
}
}
if (fobj->objndx_spndx == spndx)
{
if (ev != SPIFFS_EV_NDXDEL)
{
finfo("Setting objid=%d span=%04x objndx_pgndx to %04x\n",
fobj->objid, spndx, new_pgndx);
fobj->objndx_pgndx = new_pgndx;
}
else
{
fobj->objndx_pgndx = 0;
}
}
}
}
/****************************************************************************
* Name: spiffs_fobj_open_bypage
*
* Description:
* Open object by page index
*
****************************************************************************/
int spiffs_fobj_open_bypage(FAR struct spiffs_s *fs, int16_t pgndx,
FAR struct spiffs_file_s *fobj)
{
struct spiffs_pgobj_ndxheader_s objndx_hdr;
off_t physoff;
int16_t objid;
int16_t blkndx;
int entry;
int ret = OK;
physoff = SPIFFS_PAGE_TO_PADDR(fs, pgndx);
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_READ,
fobj->objid, physoff,
sizeof(struct spiffs_pgobj_ndxheader_s),
(FAR uint8_t *)&objndx_hdr);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
blkndx = SPIFFS_BLOCK_FOR_PAGE(fs, pgndx);
entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pgndx);
physoff = SPIFFS_BLOCK_TO_PADDR(fs, blkndx) + entry * sizeof(int16_t);
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, 0,
physoff, sizeof(int16_t),
(FAR uint8_t *)&objid);
/* Fill in the parts of the open file structure known only to the core
* logic.
*/
fobj->objhdr_pgndx = pgndx;
fobj->size = objndx_hdr.size;
fobj->offset = 0;
fobj->objndx_pgndx = pgndx;
fobj->objndx_spndx = 0;
fobj->objid = objid;
ret = spiffs_validate_objndx(&objndx_hdr.phdr, fobj->objid, 0);
if (ret < 0)
{
ferr("ERROR: spiffs_validate_objndx() failed: %d\n", ret);
return ret;
}
finfo("objid=%d\n", fobj->objid);
return ret;
}
/****************************************************************************
* Name: spiffs_fobj_append
*
* Description:
* Append to object. Deep current object index (header) page in fs->work
* buffer
*
****************************************************************************/
ssize_t spiffs_fobj_append(FAR struct spiffs_s *fs,
FAR struct spiffs_file_s *fobj, off_t offset,
FAR uint8_t *data, size_t len)
{
struct spiffs_page_header_s phdr;
FAR struct spiffs_pgobj_ndxheader_s *objhdr;
FAR struct spiffs_page_objndx_s *objndx;
ssize_t nwritten = 0;
uint32_t page_offs;
int16_t cur_objndx_spndx;
int16_t prev_objndx_spndx;
int16_t cur_objndx_pgndx;
int16_t new_objhdr_page;
int16_t data_spndx;
int16_t data_page;
int ret = OK;
int ret2;
finfo("Append %zu bytes @ offs=%jd of size=%jd\n",
len, (intmax_t)offset, (intmax_t)fobj->size);
if (offset > fobj->size)
{
finfo("Offset replaced with size\n");
offset = fobj->size < 0 ? 0 : fobj->size;
}
/* Add an extra page of data worth for meta */
ret = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs));
if (ret < 0)
{
ferr("ERROR: spiffs_gc_epage_stats() failed: %d\n", ret);
return ret;
}
objhdr = (FAR struct spiffs_pgobj_ndxheader_s *)fs->work;
objndx = (FAR struct spiffs_page_objndx_s *)fs->work;
cur_objndx_spndx = 0;
prev_objndx_spndx = (int16_t)-1;
cur_objndx_pgndx = fobj->objhdr_pgndx;
data_spndx = offset / SPIFFS_DATA_PAGE_SIZE(fs);
page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs);
/* Write all data */
while (ret >= 0 && nwritten < len)
{
size_t to_write;
/* Calculate object index page span index */
cur_objndx_spndx = SPIFFS_OBJNDX_ENTRY_SPNDX(fs, data_spndx);
/* Handle storing and loading of object indices */
if (cur_objndx_spndx != prev_objndx_spndx)
{
/* New object index page. Within this clause we return directly
* if something fails, object index mess-up
*/
if (nwritten > 0)
{
/* Store previous object index page, unless first pass */
finfo("objid=%04x store objndx %04x:%04x, nwritten=%zu\n",
fobj->objid, cur_objndx_pgndx, prev_objndx_spndx,
nwritten);
if (prev_objndx_spndx == 0)
{
/* This is an update to object index header page */
objhdr->size = offset + nwritten;
if (offset == 0)
{
/* Was an empty object, update same page (size was
* 0xffffffff)
*/
ret = spiffs_page_index_check(fs, fobj,
cur_objndx_pgndx, 0);
if (ret < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: "
"%d\n",
ret);
return ret;
}
ret =
spiffs_cache_write(fs,
SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, cur_objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs),
fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n",
ret);
return ret;
}
}
else
{
/* Was a nonempty object, update to new page */
ret = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx,
fs->work, 0,
offset + nwritten,
&new_objhdr_page);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: "
"%d\n",
ret);
return ret;
}
finfo("objid=%04x store new objhdr, "
"%04x:%04x, nwritten=%zu\n",
fobj->objid, new_objhdr_page, 0, nwritten);
}
}
else
{
/* this is an update to an object index page */
ret = spiffs_page_index_check(fs, fobj, cur_objndx_pgndx,
prev_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: "
"%d\n", ret);
return ret;
}
ret = spiffs_cache_write(fs,
SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, cur_objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs),
fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
spiffs_fobj_event(fs,
(FAR struct spiffs_page_objndx_s *)
fs->work,
SPIFFS_EV_NDXUPD, fobj->objid,
objndx->phdr.spndx, cur_objndx_pgndx,
0);
/* Update length in object index header page */
ret = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx, 0, 0,
offset + nwritten,
&new_objhdr_page);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret);
return ret;
}
finfo("objid=%04x store new size I %jd in objhdr, "
"%04x:%04x, nwritten=%zu\n",
fobj->objid, (intmax_t)(offset + nwritten),
new_objhdr_page, 0,
nwritten);
}
fobj->size = offset + nwritten;
fobj->offset = offset + nwritten;
}
/* create or load new object index page */
if (cur_objndx_spndx == 0)
{
/* load object index header page, must always exist */
finfo("objid=%04x load objndxhdr page %04x:%04x\n",
fobj->objid, cur_objndx_pgndx, cur_objndx_spndx);
ret = spiffs_cache_read(fs,
SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_READ,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs,
cur_objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
ret = spiffs_validate_objndx(&objhdr->phdr, fobj->objid,
cur_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_validate_objndx() failed: %d\n", ret);
return ret;
}
}
else
{
int16_t len_objndx_spndx;
/* On subsequent passes, create a new object index page */
len_objndx_spndx =
SPIFFS_OBJNDX_ENTRY_SPNDX(fs, (fobj->size - 1) /
SPIFFS_DATA_PAGE_SIZE(fs));
if (nwritten > 0 || cur_objndx_spndx > len_objndx_spndx)
{
phdr.objid = fobj->objid | SPIFFS_OBJID_NDXFLAG;
phdr.spndx = cur_objndx_spndx;
phdr.flags =
0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX);
ret = spiffs_page_allocate_data(fs,
fobj->objid |
SPIFFS_OBJID_NDXFLAG,
&phdr, 0, 0, 0, 1,
&cur_objndx_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_allocate_data() failed: %d\n",
ret);
return ret;
}
/* Quick "load" of new object index page */
memset(fs->work, 0xff, SPIFFS_GEO_PAGE_SIZE(fs));
memcpy(fs->work, &phdr,
sizeof(struct spiffs_page_header_s));
spiffs_fobj_event(fs,
(FAR struct spiffs_page_objndx_s *)
fs->work,
SPIFFS_EV_NDXNEW, fobj->objid,
cur_objndx_spndx, cur_objndx_pgndx, 0);
finfo("objid=%04x create objndx page, "
"%04x:%04x, nwritten=%zu\n",
fobj->objid, cur_objndx_pgndx, cur_objndx_spndx,
nwritten);
}
else
{
int16_t pgndx;
/* On first pass, we load existing object index page */
finfo("objid=%04x find objndx spndx=%04x\n",
fobj->objid, cur_objndx_spndx);
if (fobj->objndx_spndx == cur_objndx_spndx)
{
pgndx = fobj->objndx_pgndx;
}
else
{
ret =
spiffs_objlu_find_id_and_span(fs,
fobj->objid |
SPIFFS_OBJID_NDXFLAG,
cur_objndx_spndx, 0,
&pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_objlu_find_id_and_span() "
"failed: %d\n",
ret);
return ret;
}
}
finfo("objid=%04x found object index at "
"page=%04x [fobj size=%" PRIu32 "]\n",
fobj->objid, pgndx, fobj->size);
ret = spiffs_cache_read(fs,
SPIFFS_OP_T_OBJNDX |
SPIFFS_OP_C_READ,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, pgndx),
SPIFFS_GEO_PAGE_SIZE(fs),
fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
ret = spiffs_validate_objndx(&objhdr->phdr, fobj->objid,
cur_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_validate_objndx() failed: %d\n",
ret);
return ret;
}
cur_objndx_pgndx = pgndx;
}
fobj->objndx_pgndx = cur_objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
fobj->offset = offset + nwritten;
fobj->size = offset + nwritten;
}
prev_objndx_spndx = cur_objndx_spndx;
}
/* Write data */
to_write = MIN(len - nwritten, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs);
if (page_offs == 0)
{
/* ATt beginning of a page, allocate and write a new page of data */
phdr.objid = fobj->objid & ~SPIFFS_OBJID_NDXFLAG;
phdr.spndx = data_spndx;
phdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); /* Finalize immediately */
ret = spiffs_page_allocate_data(fs,
fobj->objid &
~SPIFFS_OBJID_NDXFLAG,
&phdr, &data[nwritten], to_write,
page_offs, 1, &data_page);
finfo("objid=%04x store new data page, %04x:%04x "
"offset=%" PRId32 ", len=%zu nwritten=%zu\n",
fobj->objid, data_page, data_spndx, page_offs, to_write,
nwritten);
}
else
{
/* Append to existing page, fill out free data in existing page */
if (cur_objndx_spndx == 0)
{
/* Get data page from object index header page */
data_page =
((FAR int16_t *)((FAR uint8_t *)objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx];
}
else
{
/* Get data page from object index page */
data_page =
((FAR int16_t *)((FAR uint8_t *)objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)];
}
ret = spiffs_page_data_check(fs, fobj, data_page, data_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_data_check() failed: %d\n", ret);
return ret;
}
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, data_page) +
sizeof(struct spiffs_page_header_s) +
page_offs,
to_write, &data[nwritten]);
finfo("objid=%04x store to existing data page, "
"%04x:%04x offset=%" PRId32 ", "
"len=%zu, nwritten=%zu\n",
fobj->objid, data_page, data_spndx, page_offs, to_write,
nwritten);
}
if (ret < 0)
{
break;
}
/* Update memory representation of object index page with new data
* page
*/
if (cur_objndx_spndx == 0)
{
/* Update object index header page */
((FAR int16_t *)((FAR uint8_t *) objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx] =
data_page;
finfo("objid=%04x wrote page %04x to objhdr entry=%04x in mem\n",
fobj->objid, data_page, data_spndx);
objhdr->size = offset + nwritten;
}
else
{
/* Update object index page */
((FAR int16_t *)((FAR uint8_t *) objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)] = data_page;
finfo("objid=%04x wrote page=%04x to objndx entry %04x in mem\n",
fobj->objid, data_page,
(int16_t)SPIFFS_OBJNDX_ENTRY(fs, data_spndx));
}
/* Update internals */
page_offs = 0;
data_spndx++;
nwritten += to_write;
}
fobj->size = offset + nwritten;
fobj->offset = offset + nwritten;
fobj->objndx_pgndx = cur_objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
/* Finalize updated object indices */
ret2 = OK;
if (cur_objndx_spndx != 0)
{
/* Wrote beyond object index header page. Write last modified object
* index page, unless object header index page
*/
finfo("objid=%04x store objndx page, %04x:%04x, nwritten=%zu\n",
fobj->objid, cur_objndx_pgndx, cur_objndx_spndx, nwritten);
ret2 = spiffs_page_index_check(fs, fobj, cur_objndx_pgndx,
cur_objndx_spndx);
if (ret2 < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: %d\n", ret2);
return ret2;
}
ret2 = spiffs_cache_write(fs, SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, cur_objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
if (ret2 < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret2);
return ret2;
}
spiffs_fobj_event(fs,
(FAR struct spiffs_page_objndx_s *)fs->work,
SPIFFS_EV_NDXUPD, fobj->objid,
objndx->phdr.spndx, cur_objndx_pgndx, 0);
/* Update size in object header index page */
ret2 = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx, 0, 0,
offset + nwritten,
&new_objhdr_page);
finfo("objid=%04x store new size II %jd in objhdr, %04x:%04x, "
"nwritten=%zu, ret=%d\n",
fobj->objid, (intmax_t)(offset + nwritten),
new_objhdr_page, 0, nwritten,
ret2);
if (ret2 < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret2);
return ret2;
}
}
else
{
/* Wrote within object index header page */
if (offset == 0)
{
/* Wrote to empty object - simply update size and write whole
* page
*/
objhdr->size = offset + nwritten;
finfo("objid=%04x store fresh objhdr page, "
"%04x:%04x, nwritten=%zu\n",
fobj->objid, cur_objndx_pgndx, cur_objndx_spndx, nwritten);
ret2 = spiffs_page_index_check(fs, fobj, cur_objndx_pgndx,
cur_objndx_spndx);
if (ret2 < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: %d\n", ret2);
return ret2;
}
ret2 = spiffs_cache_write(fs,
SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs,
cur_objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
if (ret2 < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret2);
return ret2;
}
spiffs_fobj_event(fs,
(FAR struct spiffs_page_objndx_s *)fs->work,
SPIFFS_EV_NDXUPD_HDR, fobj->objid,
objhdr->phdr.spndx, cur_objndx_pgndx,
objhdr->size);
}
else
{
/* Modifying object index header page, update size and make new
* copy.
*/
ret2 = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx, fs->work, 0,
offset + nwritten,
&new_objhdr_page);
finfo("objid=%04x store modified objhdr page, %04x:%04x, "
"nwritten=%zu\n",
fobj->objid, new_objhdr_page, 0, nwritten);
if (ret2 < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret2);
return ret2;
}
}
}
return nwritten;
}
/****************************************************************************
* Name: spiffs_fobj_modify
*
* Description:
* Modify object. Keep current object index (header) page in fs->work
* buffer
*
****************************************************************************/
ssize_t spiffs_fobj_modify(FAR struct spiffs_s *fs,
FAR struct spiffs_file_s *fobj, off_t offset,
FAR uint8_t *data, size_t len)
{
struct spiffs_page_header_s phdr;
FAR struct spiffs_pgobj_ndxheader_s *objhdr;
FAR struct spiffs_page_objndx_s *objndx;
size_t nwritten = 0;
uint32_t page_offs;
int16_t cur_objndx_spndx;
int16_t prev_objndx_spndx;
int16_t cur_objndx_pgndx;
int16_t new_objhdr_pgndx;
int16_t data_spndx;
int16_t data_pgndx;
int ret = OK;
int ret2;
ret = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs));
if (ret < 0)
{
ferr("ERROR: spiffs_gc_check() failed: %d\n", ret);
return ret;
}
objhdr = (FAR struct spiffs_pgobj_ndxheader_s *)fs->work;
objndx = (FAR struct spiffs_page_objndx_s *)fs->work;
cur_objndx_spndx = 0;
prev_objndx_spndx = (int16_t) - 1;
cur_objndx_pgndx = fobj->objhdr_pgndx;
data_spndx = offset / SPIFFS_DATA_PAGE_SIZE(fs);
page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs);
/* Write all data */
while (ret >= 0 && nwritten < len)
{
size_t to_write;
int16_t orig_data_pgndx;
/* Calculate object index page span index */
cur_objndx_spndx = SPIFFS_OBJNDX_ENTRY_SPNDX(fs, data_spndx);
/* Handle storing and loading of object indices */
if (cur_objndx_spndx != prev_objndx_spndx)
{
/* New object index page. Within this clause we return directly
* if something fails, object index mess-up
*/
if (nwritten > 0)
{
/* Store previous object index (header) page, unless first
* pass.
*/
if (prev_objndx_spndx == 0)
{
/* Store previous object index header page */
ret = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx,
fs->work, 0, 0,
&new_objhdr_pgndx);
finfo("Store modified objhdr page, "
"%04x:%04x, nwritten=%zu\n",
new_objhdr_pgndx, 0, nwritten);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret);
return ret;
}
}
else
{
/* Store new version of previous object index page */
int16_t new_objndx_pgndx;
ret = spiffs_page_index_check(fs, fobj, cur_objndx_pgndx,
prev_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: %d\n",
ret);
return ret;
}
ret = spiffs_page_move(fs, fobj->objid,
(FAR uint8_t *)objndx,
fobj->objid, 0, cur_objndx_pgndx,
&new_objndx_pgndx);
finfo("Store previous modified objndx page, %04x:%04x, "
"nwritten=%zu\n",
new_objndx_pgndx, objndx->phdr.spndx, nwritten);
if (ret < 0)
{
ferr("ERROR: spiffs_page_move() failed: %d\n", ret);
return ret;
}
spiffs_fobj_event(fs,
(FAR struct spiffs_page_objndx_s *)
objndx,
SPIFFS_EV_NDXUPD, fobj->objid,
objndx->phdr.spndx, new_objndx_pgndx,
0);
}
}
/* Load next object index page */
if (cur_objndx_spndx == 0)
{
/* Load object index header page, must exist */
finfo("Load objndxhdr page %04x:%04x\n",
cur_objndx_pgndx, cur_objndx_spndx);
ret = spiffs_cache_read(fs,
SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_READ,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs,
cur_objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
ret = spiffs_validate_objndx(&objhdr->phdr, fobj->objid,
cur_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_validate_objndx() failed: %d\n", ret);
return ret;
}
}
else
{
int16_t pgndx;
/* Load existing object index page on first pass */
finfo("Find objndx spndx=%04x\n", cur_objndx_spndx);
if (fobj->objndx_spndx == cur_objndx_spndx)
{
pgndx = fobj->objndx_pgndx;
}
else
{
ret =
spiffs_objlu_find_id_and_span(fs,
fobj->objid |
SPIFFS_OBJID_NDXFLAG,
cur_objndx_spndx, 0,
&pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_objlu_find_id_and_span() failed: "
"%d\n",
ret);
return ret;
}
}
finfo("Found object index at page=%04x\n", pgndx);
ret = spiffs_cache_read(fs,
SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_READ,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
ret = spiffs_validate_objndx(&objhdr->phdr, fobj->objid,
cur_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_validate_objndx() failed: %d\n", ret);
return ret;
}
cur_objndx_pgndx = pgndx;
}
fobj->objndx_pgndx = cur_objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
fobj->offset = offset + nwritten;
prev_objndx_spndx = cur_objndx_spndx;
}
/* Write partial data */
to_write = MIN(len - nwritten, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs);
if (cur_objndx_spndx == 0)
{
/* Get data page from object index header page */
orig_data_pgndx =
((FAR int16_t *)((FAR uint8_t *)objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx];
}
else
{
/* Get data page from object index page */
orig_data_pgndx =
((FAR int16_t *)((FAR uint8_t *)objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)];
}
phdr.objid = fobj->objid & ~SPIFFS_OBJID_NDXFLAG;
phdr.spndx = data_spndx;
phdr.flags = 0xff;
if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs))
{
/* A full page, allocate and write a new page of data */
ret = spiffs_page_allocate_data(fs,
fobj->objid &
~SPIFFS_OBJID_NDXFLAG,
&phdr, &data[nwritten], to_write,
page_offs, 1, &data_pgndx);
finfo("Store new data page, %04x:%04x offset=%" PRId32 ", "
"len=%zu, nwritten=%zu\n",
data_pgndx, data_spndx, page_offs, to_write, nwritten);
}
else
{
/* Write to existing page, allocate new and copy unmodified data */
ret = spiffs_page_data_check(fs, fobj, orig_data_pgndx,
data_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_data_check() failed: %d\n", ret);
return ret;
}
ret = spiffs_page_allocate_data(fs,
fobj->objid &
~SPIFFS_OBJID_NDXFLAG,
&phdr, 0, 0, 0, 0, &data_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_allocate_data() failed: %d\n", ret);
break;
}
/* Copy unmodified data */
if (page_offs > 0)
{
/* Before modification */
ret = spiffs_phys_cpy(fs, fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, data_pgndx) +
sizeof(struct spiffs_page_header_s),
SPIFFS_PAGE_TO_PADDR(fs,
orig_data_pgndx) +
sizeof(struct spiffs_page_header_s),
page_offs);
if (ret < 0)
{
ferr("ERROR: spiffs_phys_cpy() failed: %d\n", ret);
break;
}
}
if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs))
{
/* After modification */
ret =
spiffs_phys_cpy(fs, fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, data_pgndx) +
sizeof(struct spiffs_page_header_s) +
page_offs +
to_write,
SPIFFS_PAGE_TO_PADDR(fs, orig_data_pgndx)
+ sizeof(struct spiffs_page_header_s) +
page_offs +
to_write,
SPIFFS_DATA_PAGE_SIZE(fs) -
(page_offs + to_write));
if (ret < 0)
{
ferr("ERROR: spiffs_phys_cpy() failed: %d\n", ret);
break;
}
}
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, data_pgndx) +
sizeof(struct spiffs_page_header_s) +
page_offs,
to_write, &data[nwritten]);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
break;
}
phdr.flags &= ~SPIFFS_PH_FLAG_FINAL;
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, data_pgndx) +
offsetof(struct spiffs_page_header_s,
flags),
sizeof(uint8_t),
(FAR uint8_t *)&phdr.flags);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
break;
}
finfo("Store to existing data page, src=%04x, dest=%04x:%04x "
"offset=%" PRId32 ", len=%zu, nwritten=%zu\n",
orig_data_pgndx, data_pgndx, data_spndx, page_offs,
to_write, nwritten);
}
/* Delete original data page */
ret = spiffs_page_delete(fs, orig_data_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_delete() failed: %d\n", ret);
break;
}
/* Update memory representation of object index page with new data
* page
*/
if (cur_objndx_spndx == 0)
{
/* Update object index header page */
((FAR int16_t *)((FAR uint8_t *)objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx] =
data_pgndx;
finfo("Wrote page %04x to objhdr entry=%04x in mem\n",
data_pgndx, data_spndx);
}
else
{
/* Update object index page */
((FAR int16_t *)((FAR uint8_t *)objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)] = data_pgndx;
finfo("Wrote page %04x to objndx entry %04x in mem\n",
data_pgndx, (int16_t)SPIFFS_OBJNDX_ENTRY(fs, data_spndx));
}
/* Update internals */
page_offs = 0;
data_spndx++;
nwritten += to_write;
}
fobj->offset = offset + nwritten;
fobj->objndx_pgndx = cur_objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
/* Finalize updated object indices */
ret2 = OK;
if (cur_objndx_spndx != 0)
{
int16_t new_objndx_pgndx;
/* Wrote beyond object index header page. Write last modified object
* index page. Move and update page
*/
ret2 = spiffs_page_index_check(fs, fobj, cur_objndx_pgndx,
cur_objndx_spndx);
if (ret2 < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: %d\n", ret2);
return ret2;
}
ret2 = spiffs_page_move(fs, fobj->objid, (uint8_t *) objndx,
fobj->objid, 0, cur_objndx_pgndx,
&new_objndx_pgndx);
finfo("Store modified objndx page, %04x:%04x, nwritten=%zu\n",
new_objndx_pgndx, cur_objndx_spndx, nwritten);
fobj->objndx_pgndx = new_objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
if (ret2 < 0)
{
ferr("ERROR: spiffs_page_move() failed: %d\n", ret2);
return ret2;
}
spiffs_fobj_event(fs, (FAR struct spiffs_page_objndx_s *)objndx,
SPIFFS_EV_NDXUPD, fobj->objid, objndx->phdr.spndx,
new_objndx_pgndx, 0);
}
else
{
/* Wrote within object index header page */
ret2 = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx, fs->work, 0, 0,
&new_objhdr_pgndx);
finfo("Store modified objhdr page, %04x:%04x, nwritten=%zu\n",
new_objhdr_pgndx, 0, nwritten);
if (ret2 < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret2);
return ret2;
}
}
return nwritten;
}
/****************************************************************************
* Name: spiffs_find_objhdr_pgndx
*
* Description:
* Finds object index header page by name
*
****************************************************************************/
int spiffs_find_objhdr_pgndx(FAR struct spiffs_s *fs,
const uint8_t name[CONFIG_SPIFFS_NAME_MAX],
FAR int16_t *pgndx)
{
int16_t blkndx;
int entry;
int ret;
ret = spiffs_foreach_objlu(fs, fs->lu_blkndx, fs->lu_entry,
0, 0, spiffs_find_objhdr_pgndx_callback,
name, 0, &blkndx, &entry);
if (ret == SPIFFS_VIS_END)
{
ret = -ENOENT;
}
else if (ret < 0)
{
ferr("ERROR: spiffs_foreach_objlu() failed: %d\n", ret);
return ret;
}
if (pgndx != NULL)
{
*pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, entry);
}
fs->lu_blkndx = blkndx;
fs->lu_entry = entry;
return ret;
}
/****************************************************************************
* Name: spiffs_fobj_truncate
*
* Description:
* Truncates object to new size. If new size is NULL, object may be removed
* totally
*
****************************************************************************/
int spiffs_fobj_truncate(FAR struct spiffs_s *fs,
FAR struct spiffs_file_s *fobj, off_t new_size,
bool remove_full)
{
FAR struct spiffs_pgobj_ndxheader_s *objhdr;
FAR struct spiffs_page_objndx_s *objndx;
uint32_t cur_size;
int16_t objndx_pgndx;
int16_t data_spndx;
int16_t cur_objndx_spndx;
int16_t prev_objndx_spndx;
int16_t data_pgndx;
int16_t new_objhdr_pgndx;
int ret = OK;
/* If the file has zero (or undefined) length and we were not asked to
* remove the file, then there is nothing to do.
*/
if ((fobj->size == SPIFFS_UNDEFINED_LEN || fobj->size == 0) &&
!remove_full)
{
/* Do nothing */
return ret;
}
/* Need 2 pages if not removing: object index page + possibly chopped data
* page
*/
if (!remove_full)
{
ret = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs) * 2);
if (ret < 0)
{
ferr("ERROR: spiffs_gc_check() failed: %d\n", ret);
return ret;
}
}
objhdr = (FAR struct spiffs_pgobj_ndxheader_s *)fs->work;
objndx = (FAR struct spiffs_page_objndx_s *)fs->work;
objndx_pgndx = fobj->objhdr_pgndx;
cur_objndx_spndx = 0;
prev_objndx_spndx = (int16_t)-1;
data_spndx = 0;
if (fobj->size > 0)
{
data_spndx = (fobj->size - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
}
cur_size = 0;
if (fobj->size != (uint32_t)SPIFFS_UNDEFINED_LEN)
{
cur_size = fobj->size;
}
/* Before truncating, check if object is to be fully removed and mark
* this
*/
if (remove_full && new_size == 0)
{
uint8_t flags = ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX |
SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_NDXDELE);
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, fobj->objhdr_pgndx) +
offsetof(struct spiffs_page_header_s, flags),
sizeof(uint8_t), (FAR uint8_t *)&flags);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
}
/* Delete from end of object until desired len is reached */
while (cur_size > new_size)
{
cur_objndx_spndx = SPIFFS_OBJNDX_ENTRY_SPNDX(fs, data_spndx);
/* Put object index for current data span index in work buffer */
if (prev_objndx_spndx != cur_objndx_spndx)
{
if (prev_objndx_spndx != (int16_t)- 1)
{
/* Remove previous object index page */
finfo("Delete objndx page %04x:%04x\n",
objndx_pgndx, prev_objndx_spndx);
ret = spiffs_page_index_check(fs, fobj, objndx_pgndx,
prev_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: %d\n",
ret);
return ret;
}
ret = spiffs_page_delete(fs, objndx_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_delete() failed: %d\n", ret);
return ret;
}
spiffs_fobj_event(fs, NULL, SPIFFS_EV_NDXDEL, fobj->objid,
objndx->phdr.spndx, objndx_pgndx, 0);
if (prev_objndx_spndx > 0)
{
/* Update object index header page, unless we totally want
* to remove the file.
*
* If fully removing, we're not keeping consistency as
* good as when storing the header between chunks,
* would we be aborted. But when removing full files, a
* crammed system may otherwise report ERR_FULL a la
* windows. We cannot have that. Hence, take the risk -
* if aborted, a file check would free the lost pages and
* mend things as the file is marked as fully deleted in
* the beginning.
*/
if (!remove_full)
{
finfo("Update objndx hdr page %04x:%04x to"
"size=%" PRIu32 "\n",
fobj->objhdr_pgndx, prev_objndx_spndx, cur_size);
ret = spiffs_fobj_update_ndxhdr(fs, fobj,
fobj->objid,
fobj->objhdr_pgndx,
0, 0, cur_size,
&new_objhdr_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: "
"%d\n",
ret);
return ret;
}
}
fobj->size = cur_size;
}
}
/* Load current object index (header) page */
if (cur_objndx_spndx == 0)
{
objndx_pgndx = fobj->objhdr_pgndx;
}
else
{
ret = spiffs_objlu_find_id_and_span(fs,
fobj->objid |
SPIFFS_OBJID_NDXFLAG,
cur_objndx_spndx, 0,
&objndx_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_objlu_find_id_and_span() failed: %d\n",
ret);
return ret;
}
}
finfo("Load objndx page %04x:%04x for data spndx=%04x\n",
objndx_pgndx, cur_objndx_spndx, data_spndx);
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_READ,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
ret = spiffs_validate_objndx(&objhdr->phdr, fobj->objid,
cur_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_validate_objndx() failed: %d\n", ret);
return ret;
}
fobj->objndx_pgndx = objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
fobj->offset = cur_size;
prev_objndx_spndx = cur_objndx_spndx;
}
if (cur_objndx_spndx == 0)
{
/* Get data page from object index header page */
data_pgndx =
((FAR int16_t *)((FAR uint8_t *) objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx];
((FAR int16_t *)((FAR uint8_t *)objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx] =
SPIFFS_OBJID_FREE;
}
else
{
/* Get data page from object index page */
data_pgndx =
((FAR int16_t *)((FAR uint8_t *)objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)];
((FAR int16_t *)((FAR uint8_t *)objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)] = SPIFFS_OBJID_FREE;
}
finfo("Got data pgndx %04x\n", data_pgndx);
if (new_size == 0 || remove_full ||
cur_size - new_size >= SPIFFS_DATA_PAGE_SIZE(fs))
{
/* Delete full data page */
ret = spiffs_page_data_check(fs, fobj, data_pgndx, data_spndx);
if (ret != SPIFFS_ERR_DELETED && ret < 0 &&
ret != SPIFFS_ERR_INDEX_REF_FREE)
{
ferr("ERROR: Failed to validate data pgndx=%d\n", ret);
break;
}
if (ret >= 0)
{
ret = spiffs_page_delete(fs, data_pgndx);
if (ret < 0)
{
ferr("ERROR: Failed to delete data pgndx=%d\n", ret);
break;
}
}
else if (ret == SPIFFS_ERR_DELETED ||
ret == SPIFFS_ERR_INDEX_REF_FREE)
{
ret = OK;
}
/* Update current size */
if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0)
{
cur_size -= SPIFFS_DATA_PAGE_SIZE(fs);
}
else
{
cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs);
}
fobj->size = cur_size;
fobj->offset = cur_size;
finfo("Delete data page %04x for data spndx=%04x,"
"cur_size=%" PRIu32 "\n",
data_pgndx, data_spndx, cur_size);
}
else
{
struct spiffs_page_header_s phdr;
int16_t new_data_pgndx;
uint32_t bytes_to_remove;
/* Delete last page, partially */
bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) -
(new_size % SPIFFS_DATA_PAGE_SIZE(fs));
finfo("Delete %" PRIu32 " bytes from data page=%04x for "
"data spndx=%04x, cur_size=%" PRIu32 "\n",
bytes_to_remove, data_pgndx, data_spndx, cur_size);
ret = spiffs_page_data_check(fs, fobj, data_pgndx, data_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_data_check() failed: %d\n", ret);
break;
}
phdr.objid = fobj->objid & ~SPIFFS_OBJID_NDXFLAG;
phdr.spndx = data_spndx;
phdr.flags = 0xff;
/* Allocate new page and copy unmodified data */
ret = spiffs_page_allocate_data(fs,
fobj->objid &
~SPIFFS_OBJID_NDXFLAG,
&phdr, 0, 0, 0, 0,
&new_data_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_allocate_data() failed: %d\n", ret);
break;
}
ret = spiffs_phys_cpy(fs, 0,
SPIFFS_PAGE_TO_PADDR(fs, new_data_pgndx) +
sizeof(struct spiffs_page_header_s),
SPIFFS_PAGE_TO_PADDR(fs, data_pgndx) +
sizeof(struct spiffs_page_header_s),
SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove);
if (ret < 0)
{
ferr("ERROR: spiffs_phys_cpy() failed: %d\n", ret);
break;
}
/* Delete original data page */
ret = spiffs_page_delete(fs, data_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_delete() failed: %d\n", ret);
break;
}
phdr.flags &= ~SPIFFS_PH_FLAG_FINAL;
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, new_data_pgndx) +
offsetof(struct spiffs_page_header_s,
flags),
sizeof(uint8_t),
(FAR uint8_t *)&phdr.flags);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
break;
}
/* Update memory representation of object index page with new data
* page
*/
if (cur_objndx_spndx == 0)
{
/* Update object index header page */
((FAR int16_t *)((FAR uint8_t *) objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx] =
new_data_pgndx;
finfo("Wrote page=%04x to objhdr entry %04x in mem\n",
new_data_pgndx,
(int16_t)SPIFFS_OBJNDX_ENTRY(fs, data_spndx));
}
else
{
/* Update object index page */
((FAR int16_t *)((FAR uint8_t *)objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)] = new_data_pgndx;
finfo("Wrote page %04x to objndx entry=%04x in mem\n",
new_data_pgndx,
(int16_t)SPIFFS_OBJNDX_ENTRY(fs, data_spndx));
}
cur_size = new_size;
fobj->size = new_size;
fobj->offset = cur_size;
break;
}
data_spndx--;
}
/* Update object indices */
if (cur_objndx_spndx == 0)
{
/* Update object index header page */
if (cur_size == 0)
{
if (remove_full)
{
/* Femove object altogether */
finfo("Femove object index header page=%04x\n", objndx_pgndx);
ret = spiffs_page_index_check(fs, fobj, objndx_pgndx, 0);
if (ret < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: %d\n",
ret);
return ret;
}
ret = spiffs_page_delete(fs, objndx_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_delete() failed: %d\n", ret);
return ret;
}
spiffs_fobj_event(fs, NULL, SPIFFS_EV_NDXDEL, fobj->objid,
0, objndx_pgndx, 0);
}
else
{
/* Make uninitialized object */
finfo("Reset objhdr page=%04x\n", objndx_pgndx);
memset(fs->work + sizeof(struct spiffs_pgobj_ndxheader_s),
0xff,
SPIFFS_GEO_PAGE_SIZE(fs) -
sizeof(struct spiffs_pgobj_ndxheader_s));
ret = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
objndx_pgndx, fs->work,
0, SPIFFS_UNDEFINED_LEN,
&new_objhdr_pgndx);
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret);
return ret;
}
}
}
else
{
/* Update object index header page */
finfo("Update object index header page with indices and size\n");
ret = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
objndx_pgndx, fs->work, 0,
cur_size, &new_objhdr_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret);
return ret;
}
}
}
else
{
int16_t new_objndx_pgndx;
/* Update both current object index page and object index header page */
ret = spiffs_page_index_check(fs, fobj, objndx_pgndx,
cur_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: %d\n", ret);
return ret;
}
/* Move and update object index page */
ret = spiffs_page_move(fs, fobj->objid, (FAR uint8_t *)objhdr,
fobj->objid, 0, objndx_pgndx,
&new_objndx_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_move() failed: %d\n", ret);
return ret;
}
spiffs_fobj_event(fs, (FAR struct spiffs_page_objndx_s *)objhdr,
SPIFFS_EV_NDXUPD, fobj->objid, objndx->phdr.spndx,
new_objndx_pgndx, 0);
finfo("Store modified objndx page, %04x:%04x\n",
new_objndx_pgndx, cur_objndx_spndx);
fobj->objndx_pgndx = new_objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
fobj->offset = cur_size;
/* Update object index header page with new size */
ret = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx, 0, 0,
cur_size, &new_objhdr_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n", ret);
return ret;
}
}
fobj->size = cur_size;
return ret;
}
/****************************************************************************
* Name: spiffs_object_read
*
* Description:
*
****************************************************************************/
ssize_t spiffs_object_read(FAR struct spiffs_s *fs,
FAR struct spiffs_file_s *fobj, off_t offset,
size_t len, FAR uint8_t *dest)
{
FAR struct spiffs_pgobj_ndxheader_s *objhdr;
FAR struct spiffs_page_objndx_s *objndx ;
uint32_t cur_offset;
int16_t objndx_pgndx;
int16_t data_pgndx;
int16_t data_spndx;
int16_t cur_objndx_spndx;
int16_t prev_objndx_spndx;
int ret = OK;
objhdr = (FAR struct spiffs_pgobj_ndxheader_s *)fs->work;
objndx = (FAR struct spiffs_page_objndx_s *)fs->work;
data_spndx = offset / SPIFFS_DATA_PAGE_SIZE(fs);
cur_offset = offset;
prev_objndx_spndx = (int16_t)-1;
while (cur_offset < offset + len)
{
uint32_t len_to_read;
cur_objndx_spndx = SPIFFS_OBJNDX_ENTRY_SPNDX(fs, data_spndx);
if (prev_objndx_spndx != cur_objndx_spndx)
{
/* Load current object index (header) page */
if (cur_objndx_spndx == 0)
{
objndx_pgndx = fobj->objhdr_pgndx;
}
else
{
finfo("Find objndx %04x:%04x\n",
fobj->objid, cur_objndx_spndx);
if (fobj->objndx_spndx == cur_objndx_spndx)
{
objndx_pgndx = fobj->objndx_pgndx;
}
else
{
ret =
spiffs_objlu_find_id_and_span(fs,
fobj->objid |
SPIFFS_OBJID_NDXFLAG,
cur_objndx_spndx, 0,
&objndx_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_objlu_find_id_and_span() failed: "
"%d\n",
ret);
return ret;
}
}
}
finfo("Load objndx page %d:%d for data spndx=%d\n",
objndx_pgndx, cur_objndx_spndx, data_spndx);
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_READ,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
ret = spiffs_validate_objndx(&objndx->phdr, fobj->objid,
cur_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_validate_objndx() failed: %d\n", ret);
return ret;
}
fobj->offset = cur_offset;
fobj->objndx_pgndx = objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
prev_objndx_spndx = cur_objndx_spndx;
}
if (cur_objndx_spndx == 0)
{
/* Get data page from object index header page */
data_pgndx =
((FAR int16_t *)((FAR uint8_t *)objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx];
}
else
{
/* Get data page from object index page */
data_pgndx =
((FAR int16_t *)((FAR uint8_t *) objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)];
}
/* All remaining data */
len_to_read = offset + len - cur_offset;
/* Remaining data in page */
len_to_read = MIN(len_to_read,
SPIFFS_DATA_PAGE_SIZE(fs) -
(cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)));
/* Remaining data in file */
len_to_read = MIN(len_to_read, fobj->size);
finfo("Read offset=%" PRIu32 " rd=%" PRIu32 " data spndx=%d is "
"data_pgndx=%d addr=%" PRIu32 "\n",
cur_offset, len_to_read, data_spndx, data_pgndx,
(uint32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pgndx) +
sizeof(struct spiffs_page_header_s) +
(cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))));
if (len_to_read <= 0)
{
len = cur_offset - offset;
break;
}
ret = spiffs_page_data_check(fs, fobj, data_pgndx, data_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_data_check() failed: %d\n", ret);
return ret;
}
ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, data_pgndx) +
sizeof(struct spiffs_page_header_s) +
(cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)),
len_to_read, dest);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
dest += len_to_read;
cur_offset += len_to_read;
fobj->offset = cur_offset;
data_spndx++;
}
return len;
}
/****************************************************************************
* Name: spiffs_objlu_find_free_objid
*
* Description:
* Scans through all object lookup for object index header pages. If total
* possible number of object ids cannot fit into a work buffer, these are
* grouped. When a group containing free object ids is found, the object
* lu is again scanned for object ids within group and bitmasked. Finally,
* the bitmask is searched for a free objid
*
****************************************************************************/
int spiffs_objlu_find_free_objid(FAR struct spiffs_s *fs, int16_t *objid,
FAR const uint8_t *conflicting_name)
{
uint32_t max_objects;
struct spiffs_free_objid_s state;
int16_t free_objid;
int ret = OK;
max_objects = (SPIFFS_GEO_BLOCK_COUNT(fs) *
SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) / 2;
free_objid = SPIFFS_OBJID_FREE;
state.min_objid = 1;
state.max_objid = max_objects + 1;
if ((state.max_objid & SPIFFS_OBJID_NDXFLAG) != 0)
{
state.max_objid = ((int16_t) - 1) & ~SPIFFS_OBJID_NDXFLAG;
}
state.compaction = 0;
state.conflicting_name = conflicting_name;
while (ret >= 0 && free_objid == SPIFFS_OBJID_FREE)
{
if (state.max_objid - state.min_objid <=
(int16_t)SPIFFS_GEO_PAGE_SIZE(fs) * 8)
{
uint32_t i;
uint32_t j;
/* Possible to represent in bitmap */
finfo("BITM min=%04x max=%04x\n",
state.min_objid, state.max_objid);
memset(fs->work, 0, SPIFFS_GEO_PAGE_SIZE(fs));
ret = spiffs_foreach_objlu(fs, 0, 0, 0, 0,
spiffs_objlu_find_free_objid_bitmap_callback,
conflicting_name, &state.min_objid,
0, 0);
if (ret == SPIFFS_VIS_END)
{
ret = OK;
}
else if (ret < 0)
{
ferr("ERROR: spiffs_foreach_objlu() failed: %d\n", ret);
return ret;
}
/* Traverse bitmask until found free objid */
for (i = 0; i < SPIFFS_GEO_PAGE_SIZE(fs); i++)
{
uint8_t mask = fs->work[i];
if (mask == 0xff)
{
continue;
}
for (j = 0; j < 8; j++)
{
if ((mask & (1 << j)) == 0)
{
*objid = (i << 3) + j + state.min_objid;
return OK;
}
}
}
return -ENOSPC;
}
else
{
/* Not possible to represent all ids in range in a bitmap, compact
* and count
*/
if (state.compaction != 0)
{
uint32_t i;
uint32_t min_i;
uint8_t *map;
uint8_t min_count;
/* Select element in compacted table, decrease range and
* recompact
*/
min_i = 0;
map = (uint8_t *)fs->work;
min_count = 0xff;
for (i = 0;
i < SPIFFS_GEO_PAGE_SIZE(fs) / sizeof(uint8_t);
i++)
{
if (map[i] < min_count)
{
min_count = map[i];
min_i = i;
if (min_count == 0)
{
break;
}
}
}
if (min_count == state.compaction)
{
/* There are no free objids! */
fwarn("WARNING: Compacted table is full\n");
return -ENOSPC;
}
finfo("COMP select index=%" PRIu32 " min_count=%d min=%04x "
"max=%04x compact:%" PRIu32 "\n",
min_i, min_count, state.min_objid, state.max_objid,
state.compaction);
if (min_count == 0)
{
/* No objid in this range, skip compacting and use
* directly
*/
*objid = min_i * state.compaction + state.min_objid;
return OK;
}
else
{
finfo("COMP SEL chunk=%" PRIu32 " min=%04x "
"-> %04" PRIx32 "\n",
state.compaction, state.min_objid,
state.min_objid + min_i * state.compaction);
state.min_objid += min_i * state.compaction;
state.max_objid = state.min_objid + state.compaction;
/* Decrease compaction */
}
if ((state.max_objid - state.min_objid <=
(int16_t)SPIFFS_GEO_PAGE_SIZE(fs) * 8))
{
/* No need for compacting, use bitmap */
continue;
}
}
/* In a work memory of <page_size> bytes, we may fit in
* <page_size> IDs. NOTE: Currently page size is equivalent to
* block size.
*
* TODO: What if compaction is > 255 - then we cannot fit it in a
* byte
*/
state.compaction = (state.max_objid - state.min_objid) /
((SPIFFS_GEO_PAGE_SIZE(fs) / sizeof(uint8_t)));
finfo("COMP min=%04x max=%04x compact=%" PRIu32 "\n",
state.min_objid, state.max_objid, state.compaction);
memset(fs->work, 0, SPIFFS_GEO_PAGE_SIZE(fs));
ret = spiffs_foreach_objlu(fs, 0, 0, 0, 0,
spiffs_objlu_find_free_objid_compact_callback,
&state, 0, 0, 0);
if (ret == SPIFFS_VIS_END)
{
ret = OK;
}
else if (ret < 0)
{
ferr("ERROR: spiffs_foreach_objlu() failed: %d\n", ret);
return ret;
}
state.conflicting_name = 0; /* Searched for conflicting name once,
* no need to do it again */
}
}
return ret;
}