3677 lines
116 KiB
C
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;
|
|
}
|