| /**************************************************************************** |
| * fs/spiffs/src/spiffs_core.c |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| * SPDX-FileCopyrightText: 2018 Gregory Nutt |
| * |
| * 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 |
| strlcpy((FAR char *)objndx_hdr.name + SPIFFS_LEADING_SLASH_SIZE, |
| (FAR 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) |
| { |
| strlcpy((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=%" PRIuOFF ") 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=%" PRIdOFF " of size=%" PRIdOFF "\n", |
| len, offset, 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 %" PRIdOFF |
| " in objhdr, %04x:%04x, nwritten=%zu\n", |
| fobj->objid, 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=%" PRIuOFF "]\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 %" PRIdOFF |
| " in objhdr, %04x:%04x, nwritten=%zu, ret=%d\n", |
| fobj->objid, 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; |
| } |