| /**************************************************************************** |
| * fs/spiffs/src/spiffs_check.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. |
| * |
| ****************************************************************************/ |
| |
| /* Contains functionality for checking file system consistency |
| * and mending problems. |
| * Three levels of consistency checks are implemented: |
| * |
| * Look up consistency |
| * Checks if indices in lookup pages are coherent with page headers |
| * Object index consistency |
| * Checks if there are any orphaned object indices (missing object index |
| * headers). |
| * If an object index is found but not its header, the object index is |
| * deleted. |
| * This is critical for the following page consistency check. |
| * Page consistency |
| * Checks for pages that ought to be indexed, ought not to be indexed, are |
| * multiple indexed |
| */ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <debug.h> |
| |
| #include "spiffs.h" |
| #include "spiffs_core.h" |
| #include "spiffs_cache.h" |
| #include "spiffs_check.h" |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: spiffs_check_get_data_pgndx |
| * |
| * Description: |
| * Searches in the object indices and returns the referenced page index |
| * given the object ID and the data span index destroys fs->lu_work |
| * |
| * Input Parameters: |
| * fs - A reference to the SPIFFS volume object instance |
| * objid - Object ID |
| * data_spndx - Data span index |
| * pgnx - Page index |
| * objndx_pgndx - Object index page index |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success; A negated errno value is returned on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| static int spiffs_check_get_data_pgndx(FAR struct spiffs_s *fs, |
| int16_t objid, int16_t data_spndx, |
| FAR int16_t *pgndx, |
| FAR int16_t *objndx_pgndx) |
| { |
| uint32_t addr; |
| int16_t objndx_spndx; |
| int ret; |
| |
| /* Calculate object index span index for given data page span index */ |
| |
| objndx_spndx = SPIFFS_OBJNDX_ENTRY_SPNDX(fs, data_spndx); |
| |
| /* Find the object index for the object ID and span index */ |
| |
| ret = spiffs_objlu_find_id_and_span(fs, objid | SPIFFS_OBJID_NDXFLAG, |
| objndx_spndx, 0, objndx_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_objlu_find_id_and_span() failed: %d\n", ret); |
| return ret; |
| } |
| |
| /* Load the object index entry */ |
| |
| addr = SPIFFS_PAGE_TO_PADDR(fs, *objndx_pgndx); |
| if (objndx_spndx == 0) |
| { |
| /* Get the referenced page from object index header */ |
| |
| addr += sizeof(struct spiffs_pgobj_ndxheader_s) + |
| data_spndx * sizeof(int16_t); |
| } |
| else |
| { |
| /* Get the referenced page from object index */ |
| |
| addr += sizeof(struct spiffs_page_objndx_s) + |
| SPIFFS_OBJNDX_ENTRY(fs, data_spndx) * |
| sizeof(int16_t); |
| } |
| |
| /* Read the page from FLASH (or the cache) */ |
| |
| ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, |
| addr, sizeof(int16_t), (FAR uint8_t *)pgndx); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_read() failed: %d\n", ret); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: spiffs_check_rewrite_page |
| * |
| * Description: |
| * Copies page contents to a new page |
| * |
| * Input Parameters: |
| * fs - A reference to the SPIFFS volume object instance |
| * cur_pgndx - Current page index |
| * pghdr - Reference to page header |
| * new_pgndx - Location to return the new page index |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success; A negated errno value is returned on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| static int spiffs_check_rewrite_page(FAR struct spiffs_s *fs, |
| int16_t cur_pgndx, |
| FAR struct spiffs_page_header_s *pghdr, |
| FAR int16_t *new_pgndx) |
| { |
| int ret; |
| |
| ret = spiffs_page_allocate_data(fs, pghdr->objid, pghdr, 0, 0, 0, 0, |
| new_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_page_allocate_data() failed: %d\n", ret); |
| return ret; |
| } |
| |
| ret = spiffs_phys_cpy(fs, 0, |
| SPIFFS_PAGE_TO_PADDR(fs, *new_pgndx) + |
| sizeof(struct spiffs_page_header_s), |
| SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx) + |
| sizeof(struct spiffs_page_header_s), |
| SPIFFS_DATA_PAGE_SIZE(fs)); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_phys_cpy() failed: %d\n", ret); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: spiffs_check_rewrite_index |
| * |
| * Description: |
| * Rewrites the object index for given object ID and replaces the |
| * data page index to a new page index |
| * |
| * Input Parameters: |
| * fs - A reference to the SPIFFS volume object instance |
| * objid - Object ID |
| * data_spndx - Data span index |
| * pgnx - Page index |
| * objndx_pgndx - Object index page index |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success; A negated errno value is returned on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| static int spiffs_check_rewrite_index(FAR struct spiffs_s *fs, |
| int16_t objid, int16_t data_spndx, |
| int16_t new_data_pgndx, |
| int16_t objndx_pgndx) |
| { |
| FAR struct spiffs_page_header_s *objndx_phdr; |
| int16_t blkndx; |
| int16_t free_pgndx; |
| int entry; |
| int 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) |
| { |
| fwarn("WARNING: spiffs_objlu_find_free() failed: %d\n", ret); |
| return ret; |
| } |
| |
| free_pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, blkndx, entry); |
| |
| /* Calculate object index span index for given data page span index */ |
| |
| int16_t objndx_spndx = SPIFFS_OBJNDX_ENTRY_SPNDX(fs, data_spndx); |
| if (objndx_spndx == 0) |
| { |
| /* Calculate index in index header */ |
| |
| entry = data_spndx; |
| } |
| else |
| { |
| /* Calculate entry in index */ |
| |
| entry = SPIFFS_OBJNDX_ENTRY(fs, data_spndx); |
| } |
| |
| /* Load index */ |
| |
| ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, |
| 0, SPIFFS_PAGE_TO_PADDR(fs, objndx_pgndx), |
| SPIFFS_GEO_PAGE_SIZE(fs), fs->lu_work); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_read() failed: %d\n", ret); |
| return ret; |
| } |
| |
| objndx_phdr = (FAR struct spiffs_page_header_s *)fs->lu_work; |
| |
| /* Be ultra safe, double check header against provided data. Return |
| * -EFAULT to indicate this condition. |
| */ |
| |
| if (objndx_phdr->objid != objid) |
| { |
| spiffs_page_delete(fs, free_pgndx); |
| return -EFAULT; |
| } |
| |
| if (objndx_phdr->spndx != objndx_spndx) |
| { |
| spiffs_page_delete(fs, free_pgndx); |
| return -EFAULT; |
| } |
| |
| if ((objndx_phdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_NDXDELE | |
| SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | |
| SPIFFS_PH_FLAG_DELET)) != |
| (SPIFFS_PH_FLAG_NDXDELE | SPIFFS_PH_FLAG_DELET)) |
| { |
| spiffs_page_delete(fs, free_pgndx); |
| return -EFAULT; |
| } |
| |
| /* Rewrite in memory */ |
| |
| if (objndx_spndx == 0) |
| { |
| ((FAR int16_t *)((FAR uint8_t *)fs->lu_work + |
| sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx] = |
| new_data_pgndx; |
| } |
| else |
| { |
| ((FAR int16_t *)((FAR uint8_t *)fs->lu_work + |
| sizeof(struct spiffs_page_objndx_s))) |
| [SPIFFS_OBJNDX_ENTRY(fs, data_spndx)] = new_data_pgndx; |
| } |
| |
| 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), fs->lu_work); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_write() failed: %d\n", ret); |
| return ret; |
| } |
| |
| 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 *)&objid); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_write() 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; |
| } |
| |
| /**************************************************************************** |
| * Name: spiffs_check_delobj_lazy |
| * |
| * Description: |
| * Deletes an object just by marking object index header as deleted |
| * |
| * Input Parameters: |
| * fs - A reference to the SPIFFS volume object instance |
| * objid - Object ID to be deleted |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success; A negated errno value is returned on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| static int spiffs_check_delobj_lazy(FAR struct spiffs_s *fs, int16_t objid) |
| { |
| int16_t objhdr_pgndx; |
| uint8_t flags = 0xff; |
| int ret; |
| |
| ret = spiffs_objlu_find_id_and_span(fs, objid, 0, 0, &objhdr_pgndx); |
| if (ret == -ENOENT) |
| { |
| return OK; |
| } |
| else if (ret < 0) |
| { |
| ferr("ERROR: spiffs_objlu_find_id_and_span() failed: %d\n", ret); |
| return ret; |
| } |
| |
| #ifdef CONFIG_SPIFFS_NO_BLIND_WRITES |
| /* Perform a read-modify-write */ |
| |
| ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, 0, |
| SPIFFS_PAGE_TO_PADDR(fs, objhdr_pgndx) + |
| offsetof(struct spiffs_page_header_s, flags), |
| sizeof(flags), &flags); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_read() failed: %d\n", ret); |
| return ret; |
| } |
| #endif |
| |
| /* Clear the deleted flag in FLASH to mark the page deleted */ |
| |
| flags &= ~SPIFFS_PH_FLAG_NDXDELE; |
| ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, 0, |
| SPIFFS_PAGE_TO_PADDR(fs, objhdr_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_check_rewrite_index |
| * |
| * Description: |
| * Validates the given look up entry |
| * |
| * Input Parameters: |
| * fs - A reference to the SPIFFS volume object instance |
| * objid - Object ID |
| * pghdr - Page header |
| * cur_pgndx - Current page index |
| * cur_block - Current block |
| * cur_entry - Current entry |
| * reload_lu - Reload lookup entry (returned) |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success; A negated errno value is returned on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| static int |
| spiffs_check_luentry_validate(FAR struct spiffs_s *fs, |
| int16_t lu_objid, |
| FAR struct spiffs_page_header_s *pghdr, |
| int16_t cur_pgndx, |
| int16_t cur_block, |
| int cur_entry, |
| FAR bool *reload_lu) |
| { |
| int16_t objndx_pgndx; |
| int16_t ref_pgndx; |
| bool delete_page = false; |
| int ret = OK; |
| |
| /* Check validity, take actions */ |
| |
| if (((lu_objid == SPIFFS_OBJID_DELETED) && |
| (pghdr->flags & SPIFFS_PH_FLAG_DELET)) || |
| ((lu_objid == SPIFFS_OBJID_FREE) && |
| (pghdr->flags & SPIFFS_PH_FLAG_USED) == 0)) |
| { |
| /* Look up entry deleted / free but used in page header */ |
| |
| spiffs_checkinfo("pgndx=%04x deleted/free in lu but not on page\n", |
| cur_pgndx); |
| |
| *reload_lu = true; |
| delete_page = true; |
| |
| if (pghdr->flags & SPIFFS_PH_FLAG_INDEX) |
| { |
| /* Header says data page data page can be removed if not |
| * referenced by some object index |
| */ |
| |
| ret = spiffs_check_get_data_pgndx(fs, pghdr->objid, |
| pghdr->spndx, |
| &ref_pgndx, &objndx_pgndx); |
| if (ret == -ENOENT) |
| { |
| /* No object with this objid, so remove page safely */ |
| |
| ret = OK; |
| } |
| else if (ret < 0) |
| { |
| ferr("ERROR: spiffs_check_get_data_pgndx() failed: %d\n", ret); |
| return ret; |
| } |
| else if (ref_pgndx == cur_pgndx) |
| { |
| int16_t new_pgndx; |
| |
| /* Data page referenced by object index but deleted in lu copy |
| * page to new place and re-write the object index to new place |
| */ |
| |
| ret = spiffs_check_rewrite_page(fs, cur_pgndx, pghdr, |
| &new_pgndx); |
| |
| spiffs_checkinfo("Data page not found elsewhere, rewriting " |
| "%04x to new page %04x\n", |
| cur_pgndx, new_pgndx); |
| |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_check_rewrite_page() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| *reload_lu = true; |
| |
| spiffs_checkinfo("Page %04x rewritten to %04x, " |
| "affected objndx_pgndx %04x\n", |
| cur_pgndx, new_pgndx, objndx_pgndx); |
| |
| ret = spiffs_check_rewrite_index(fs, |
| pghdr->objid, pghdr->spndx, |
| new_pgndx, objndx_pgndx); |
| if (ret == -EFAULT) |
| { |
| int ret2; |
| |
| /* Index bad also, cannot mend this file */ |
| |
| spiffs_checkinfo("Index bad %d, cannot mend!\n", ret); |
| |
| ret2 = spiffs_page_delete(fs, new_pgndx); |
| if (ret2 < 0) |
| { |
| ferr("ERROR: spiffs_page_delete() failed: %d\n", ret2); |
| return ret2; |
| } |
| |
| ret2 = spiffs_check_delobj_lazy(fs, pghdr->objid); |
| if (ret2 < 0) |
| { |
| ferr("ERROR: spiffs_check_delobj_lazy() failed: %d\n", |
| ret2); |
| return ret2; |
| } |
| } |
| |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_check_rewrite_index() failed: %d\n", |
| ret); |
| return ret; |
| } |
| } |
| } |
| else |
| { |
| /* Header says index page index page can be removed if other index |
| * with same objid and span index is found |
| */ |
| |
| ret = spiffs_objlu_find_id_and_span(fs, |
| pghdr->objid | SPIFFS_OBJID_NDXFLAG, |
| pghdr->spndx, cur_pgndx, 0); |
| if (ret == -ENOENT) |
| { |
| /* No such index page found, check for a data page amongst page |
| * headers. lu cannot be trusted |
| */ |
| |
| ret = |
| spiffs_objlu_find_id_and_span_byphdr(fs, |
| pghdr->objid | |
| SPIFFS_OBJID_NDXFLAG, |
| 0, 0, 0); |
| if (ret >= 0) |
| { |
| int16_t new_pgndx; |
| |
| /* Ignore other errors. Got a data page also, assume lu |
| * corruption only, rewrite to new page |
| */ |
| |
| ret = spiffs_check_rewrite_page(fs, cur_pgndx, pghdr, |
| &new_pgndx); |
| |
| spiffs_checkinfo("Index page with data not found, " |
| "rewriting %04x to new page %04x\n", |
| cur_pgndx, new_pgndx); |
| |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_check_rewrite_page() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| *reload_lu = true; |
| } |
| } |
| else if (ret < 0) |
| { |
| ferr("ERROR: spiffs_objlu_find_id_and_span_byphdr(): %d\n", |
| ret); |
| return ret; |
| } |
| } |
| } |
| |
| if (lu_objid != SPIFFS_OBJID_FREE && lu_objid != SPIFFS_OBJID_DELETED) |
| { |
| /* look up entry used */ |
| |
| if ((pghdr->objid | SPIFFS_OBJID_NDXFLAG) != |
| (lu_objid | SPIFFS_OBJID_NDXFLAG)) |
| { |
| spiffs_checkinfo("pgndx %04x differ in objid lu=" |
| "%04x ph:%04x\n", cur_pgndx, lu_objid, |
| pghdr->objid); |
| delete_page = true; |
| if ((pghdr->flags & SPIFFS_PH_FLAG_DELET) == 0 || |
| (pghdr->flags & SPIFFS_PH_FLAG_FINAL) || |
| (pghdr->flags & (SPIFFS_PH_FLAG_INDEX | |
| SPIFFS_PH_FLAG_NDXDELE)) == 0) |
| { |
| /* Page deleted or not finalized, just remove it */ |
| } |
| else if (pghdr->flags & SPIFFS_PH_FLAG_INDEX) |
| { |
| /* if data page, check for reference to this page */ |
| |
| ret = spiffs_check_get_data_pgndx(fs, |
| pghdr->objid, |
| pghdr->spndx, |
| &ref_pgndx, |
| &objndx_pgndx); |
| if (ret == -ENOENT) |
| { |
| /* no object with this objid, so remove page safely */ |
| |
| ret = OK; |
| } |
| else if (ret < 0) |
| { |
| ferr("ERROR: spiffs_check_get_data_pgndx() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| /* if found, rewrite page with object ID, update index, and |
| * delete current |
| */ |
| |
| else if (ref_pgndx == cur_pgndx) |
| { |
| int16_t new_pgndx; |
| |
| ret = spiffs_check_rewrite_page(fs, cur_pgndx, pghdr, |
| &new_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_check_rewrite_page() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| ret = spiffs_check_rewrite_index(fs, pghdr->objid, |
| pghdr->spndx, new_pgndx, |
| objndx_pgndx); |
| if (ret == -EFAULT) |
| { |
| int ret2; |
| |
| /* Index bad also, cannot mend this file */ |
| |
| spiffs_checkinfo("Index bad %d, cannot mend!\n", ret); |
| |
| ret2 = spiffs_page_delete(fs, new_pgndx); |
| if (ret2 < 0) |
| { |
| ferr("ERROR: spiffs_page_delete() failed: %d\n", |
| ret2); |
| return ret2; |
| } |
| |
| ret2 = spiffs_check_delobj_lazy(fs, pghdr->objid); |
| if (ret2 < 0) |
| { |
| ferr("ERROR: spiffs_check_delobj_lazy(): %d\n", |
| ret2); |
| return ret2; |
| } |
| |
| *reload_lu = true; |
| } |
| |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_check_rewrite_index(): %d\n", |
| ret); |
| return ret; |
| } |
| } |
| } |
| else |
| { |
| int16_t objndx_pgndx_lu; |
| int16_t objndx_pgndx_ph; |
| |
| /* Else if index, check for other pages with both ID's and |
| * span index |
| * |
| * See if other object index page exists for lookup objid |
| * and span index |
| */ |
| |
| ret = spiffs_objlu_find_id_and_span(fs, |
| lu_objid | SPIFFS_OBJID_NDXFLAG, |
| pghdr->spndx, 0, |
| &objndx_pgndx_lu); |
| if (ret == -ENOENT) |
| { |
| ret = OK; |
| objndx_pgndx_lu = 0; |
| } |
| else if (ret < 0) |
| { |
| ferr("ERROR: spiffs_objlu_find_id_and_span() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| /* See if other object index exists for page header objid and |
| * span index |
| */ |
| |
| ret = spiffs_objlu_find_id_and_span(fs, |
| pghdr->objid | SPIFFS_OBJID_NDXFLAG, |
| pghdr->spndx, 0, &objndx_pgndx_ph); |
| if (ret == -ENOENT) |
| { |
| ret = OK; |
| objndx_pgndx_ph = 0; |
| } |
| else if (ret < 0) |
| { |
| ferr("ERROR: spiffs_objlu_find_id_and_span() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| /* If both ID's found, just delete current */ |
| |
| if (objndx_pgndx_ph == 0 || objndx_pgndx_lu == 0) |
| { |
| struct spiffs_page_header_s new_ph; |
| int16_t data_pgndx_lu; |
| int16_t data_pgndx_ph; |
| int16_t new_pgndx; |
| |
| /* Otherwise try finding first corresponding data pages. |
| * |
| * See if other data page exists for look up objid and |
| * span index |
| */ |
| |
| ret = |
| spiffs_objlu_find_id_and_span(fs, |
| lu_objid & ~SPIFFS_OBJID_NDXFLAG, |
| 0, 0, &data_pgndx_lu); |
| if (ret == -ENOENT) |
| { |
| ret = OK; |
| objndx_pgndx_lu = 0; |
| } |
| else if (ret < 0) |
| { |
| ferr("ERROR: spiffs_objlu_find_id_and_span(): %d\n", |
| ret); |
| return ret; |
| } |
| |
| /* See if other data page exists for page header objid |
| * and span index |
| */ |
| |
| ret = |
| spiffs_objlu_find_id_and_span(fs, |
| pghdr->objid & |
| ~SPIFFS_OBJID_NDXFLAG, |
| 0, 0, &data_pgndx_ph); |
| if (ret == -ENOENT) |
| { |
| ret = OK; |
| objndx_pgndx_ph = 0; |
| } |
| else if (ret < 0) |
| { |
| ferr("ERROR: spiffs_objlu_find_id_and_span(): %d\n", |
| ret); |
| return ret; |
| } |
| |
| new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | |
| SPIFFS_PH_FLAG_INDEX | |
| SPIFFS_PH_FLAG_FINAL); |
| new_ph.spndx = pghdr->spndx; |
| |
| if ((objndx_pgndx_lu != 0 && data_pgndx_lu != 0 && |
| data_pgndx_ph != 0 && objndx_pgndx_ph == 0) || |
| (objndx_pgndx_lu == 0 && data_pgndx_ph && |
| objndx_pgndx_ph == 0)) |
| { |
| /* Got a data page for page header objid rewrite as |
| * objid_ph |
| */ |
| |
| new_ph.objid = pghdr->objid | SPIFFS_OBJID_NDXFLAG; |
| ret = spiffs_check_rewrite_page(fs, cur_pgndx, &new_ph, |
| &new_pgndx); |
| |
| spiffs_checkinfo( |
| "Rewrite page %04x as %04x to pgndx %04x\n", |
| cur_pgndx, new_ph.objid, new_pgndx); |
| |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_check_rewrite_page(): %d\n", |
| ret); |
| return ret; |
| } |
| |
| *reload_lu = true; |
| } |
| else if ((objndx_pgndx_ph != 0 && data_pgndx_ph != 0 && |
| data_pgndx_lu != 0 && objndx_pgndx_lu == 0) || |
| (objndx_pgndx_ph == 0 && data_pgndx_lu && |
| objndx_pgndx_lu == 0)) |
| { |
| /* Got a data page for look up objid rewrite as |
| * objid_lu |
| */ |
| |
| new_ph.objid = lu_objid | SPIFFS_OBJID_NDXFLAG; |
| |
| spiffs_checkinfo("Rewrite page %04x as %04x\n", |
| cur_pgndx, new_ph.objid); |
| |
| ret = spiffs_check_rewrite_page(fs, cur_pgndx, &new_ph, |
| &new_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_check_rewrite_page(): %d\n", |
| ret); |
| return ret; |
| } |
| |
| *reload_lu = true; |
| } |
| else |
| { |
| /* Cannot safely do anything */ |
| |
| spiffs_checkinfo("Nothing to do, just delete\n"); |
| } |
| } |
| } |
| } |
| else if (((lu_objid & SPIFFS_OBJID_NDXFLAG) != 0 && |
| (pghdr->flags & SPIFFS_PH_FLAG_INDEX) != 0) || |
| ((lu_objid & SPIFFS_OBJID_NDXFLAG) == 0 && |
| (pghdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) |
| { |
| int16_t data_pgndx; |
| int16_t objndx_pgndx_d; |
| |
| spiffs_checkinfo("%04x lu/page index marking differ\n", cur_pgndx); |
| |
| /* see if other data page exists for given objid and span index */ |
| |
| ret = spiffs_objlu_find_id_and_span(fs, |
| lu_objid & ~SPIFFS_OBJID_NDXFLAG, |
| pghdr->spndx, cur_pgndx, &data_pgndx); |
| if (ret == -ENOENT) |
| { |
| ret = OK; |
| data_pgndx = 0; |
| } |
| else if (ret < 0) |
| { |
| ferr("ERROR: spiffs_objlu_find_id_and_span() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| /* See if other object index exists for given objid and span |
| * index |
| */ |
| |
| ret = spiffs_objlu_find_id_and_span(fs, |
| lu_objid | SPIFFS_OBJID_NDXFLAG, |
| pghdr->spndx, cur_pgndx, |
| &objndx_pgndx_d); |
| if (ret == -ENOENT) |
| { |
| ret = OK; |
| objndx_pgndx_d = 0; |
| } |
| else if (ret < 0) |
| { |
| ferr("ERROR: spiffs_objlu_find_id_and_span() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| delete_page = true; |
| |
| /* If other data page exists and object index exists, just delete |
| * page |
| */ |
| |
| if (data_pgndx != 0 && objndx_pgndx_d != 0) |
| { |
| spiffs_checkinfo( |
| "Other index and data page exists, simply remove\n"); |
| } |
| |
| /* If only data page exists, make this page index */ |
| |
| else if (data_pgndx && objndx_pgndx_d == 0) |
| { |
| struct spiffs_page_header_s new_ph; |
| int16_t new_pgndx; |
| |
| spiffs_checkinfo("Other data page exists, make this index\n"); |
| |
| new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | |
| SPIFFS_PH_FLAG_FINAL | |
| SPIFFS_PH_FLAG_INDEX); |
| new_ph.objid = lu_objid | SPIFFS_OBJID_NDXFLAG; |
| new_ph.spndx = pghdr->spndx; |
| |
| ret = spiffs_page_allocate_data(fs, new_ph.objid, &new_ph, |
| 0, 0, 0, 1, &new_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_page_allocate_data() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| ret = spiffs_phys_cpy(fs, 0, |
| SPIFFS_PAGE_TO_PADDR(fs, new_pgndx) + |
| sizeof(struct spiffs_page_header_s), |
| SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx) + |
| sizeof(struct spiffs_page_header_s), |
| SPIFFS_GEO_PAGE_SIZE(fs) - |
| sizeof(struct spiffs_page_header_s)); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_phys_cpy() failed: %d\n", ret); |
| return ret; |
| } |
| } |
| |
| /* If only index exists, make data page */ |
| |
| else if (data_pgndx == 0 && objndx_pgndx_d) |
| { |
| struct spiffs_page_header_s new_ph; |
| int16_t new_pgndx; |
| |
| spiffs_checkinfo("Other index page exists, make this data\n"); |
| |
| new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | |
| SPIFFS_PH_FLAG_FINAL); |
| new_ph.objid = lu_objid & ~SPIFFS_OBJID_NDXFLAG; |
| new_ph.spndx = pghdr->spndx; |
| |
| ret = spiffs_page_allocate_data(fs, new_ph.objid, &new_ph, |
| 0, 0, 0, 1, &new_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_page_allocate_data() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| ret = spiffs_phys_cpy(fs, 0, |
| SPIFFS_PAGE_TO_PADDR(fs, new_pgndx) + |
| sizeof(struct spiffs_page_header_s), |
| SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx) + |
| sizeof(struct spiffs_page_header_s), |
| SPIFFS_GEO_PAGE_SIZE(fs) - |
| sizeof(struct spiffs_page_header_s)); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_phys_cpy() failed: %d\n", ret); |
| return ret; |
| } |
| } |
| else |
| { |
| /* If nothing exists, we cannot safely make a decision - |
| * delete |
| */ |
| } |
| } |
| else if ((pghdr->flags & SPIFFS_PH_FLAG_DELET) == 0) |
| { |
| spiffs_checkinfo("pgndx=%04x busy in lu but deleted on page\n", |
| cur_pgndx); |
| delete_page = 1; |
| } |
| else if ((pghdr->flags & SPIFFS_PH_FLAG_FINAL)) |
| { |
| spiffs_checkinfo("pgndx=%04x busy but not final\n", |
| cur_pgndx); |
| |
| /* Page can be removed if not referenced by object index */ |
| |
| *reload_lu = true; |
| ret = spiffs_check_get_data_pgndx(fs, lu_objid, pghdr->spndx, |
| &ref_pgndx, &objndx_pgndx); |
| if (ret == -ENOENT) |
| { |
| /* No object with this ID, so remove page safely */ |
| |
| ret = OK; |
| delete_page = true; |
| } |
| else if (ret < 0) |
| { |
| ferr("ERROR: spiffs_phys_cpy() failed: %d\n", ret); |
| return ret; |
| } |
| else if (ref_pgndx != cur_pgndx) |
| { |
| spiffs_checkinfo( |
| "Other finalized page is referred, just delete\n"); |
| delete_page = true; |
| } |
| else |
| { |
| uint8_t flags = 0xff; |
| |
| /* page referenced by object index but not final |
| * just finalize |
| */ |
| |
| spiffs_checkinfo("Unfinalized page is referred, finalizing\n"); |
| |
| #ifdef CONFIG_SPIFFS_NO_BLIND_WRITES |
| ret = spiffs_cache_read(fs, |
| SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, |
| 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx) + |
| offsetof(struct spiffs_page_header_s, flags), |
| sizeof(flags), &flags); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_read() failed: %d\n", ret); |
| return ret; |
| } |
| #endif |
| |
| flags &= ~SPIFFS_PH_FLAG_FINAL; |
| ret = spiffs_cache_write(fs, |
| SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, |
| 0, SPIFFS_PAGE_TO_PADDR(fs, cur_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; |
| } |
| } |
| } |
| } |
| |
| if (delete_page) |
| { |
| spiffs_checkinfo("Deleting page %04x\n", cur_pgndx); |
| ret = spiffs_page_delete(fs, cur_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_page_delete() failed: %d\n", ret); |
| return ret; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: spiffs_lucheck_callback |
| * |
| * Description: |
| * This is a callback from spiffs_foreach_objlu(). It is part of the |
| * logic of spiffs_check_luconsistency(). It checks the page page |
| * header for each entry for validity. |
| * |
| * Input Parameters: |
| * fs - A reference to the SPIFFS volume object instance |
| * objid - Object ID |
| * cur_block - Current block |
| * cur_entry - Current entry |
| * user_const - User provided constant data |
| * user_var - User provided variable data |
| * |
| * Returned Value: |
| * Returns SPIFFS_VIS_COUNTINUE_RELOAD, SPIFFS_VIS_COUNTINUE, or a |
| * negated errno value in the event of a failure. |
| * |
| ****************************************************************************/ |
| |
| static int spiffs_lucheck_callback(FAR struct spiffs_s *fs, int16_t objid, |
| int16_t cur_block, int cur_entry, |
| FAR const void *user_const, |
| FAR void *user_var) |
| { |
| struct spiffs_page_header_s pghdr; |
| int16_t cur_pgndx; |
| int ret = OK; |
| bool reload_lu = false; |
| |
| cur_pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, cur_block, cur_entry); |
| |
| /* Load header */ |
| |
| ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, |
| 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx), |
| sizeof(struct spiffs_page_header_s), |
| (FAR uint8_t *)&pghdr); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_read() failed: %d\n", ret); |
| return ret; |
| } |
| |
| ret = spiffs_check_luentry_validate(fs, objid, &pghdr, cur_pgndx, |
| cur_block, cur_entry, &reload_lu); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_check_luentry_validate() failed: %d\n", ret); |
| return ret; |
| } |
| |
| return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE; |
| } |
| |
| /**************************************************************************** |
| * Name: spifss_check_objndx_search |
| * |
| * Description: |
| * Searches for given object ID in temporary object ID index. |
| * |
| * Input Parameters: |
| * fs - A reference to the SPIFFS volume object instance |
| * objid - The Object ID |
| * |
| * Returned Value: |
| * The index associated with the objid is returned on success. -ENOENT |
| * is resulted if the objid was not found. |
| * |
| ****************************************************************************/ |
| |
| static int spifss_check_objndx_search(FAR struct spiffs_s *fs, int16_t objid) |
| { |
| FAR int16_t *obj_table = (FAR int16_t *)fs->work; |
| int i; |
| |
| objid &= ~SPIFFS_OBJID_NDXFLAG; |
| for (i = 0; i < SPIFFS_GEO_PAGE_SIZE(fs) / sizeof(int16_t); i++) |
| { |
| if ((obj_table[i] & ~SPIFFS_OBJID_NDXFLAG) == objid) |
| { |
| return i; |
| } |
| } |
| |
| return -ENOENT; |
| } |
| |
| /**************************************************************************** |
| * Name: spiffs_check_objidconsistency_callback |
| * |
| * Description: |
| * Check object index consistency. This is callback from |
| * spiffs_foreach_objlu() and logically a part of |
| * spiffs_check_objidconsistency() |
| * |
| * Input Parameters: |
| * fs - A reference to the SPIFFS volume object instance |
| * objid - Object ID |
| * cur_block - Current block |
| * cur_entry - Current entry |
| * user_const - User provided constant data |
| * user_var - User provided variable data |
| * |
| * Returned Value: |
| * Returns SPIFFS_VIS_COUNTINUE_RELOAD, SPIFFS_VIS_COUNTINUE, or a |
| * negated errno value in the event of a failure. |
| * |
| ****************************************************************************/ |
| |
| static int spiffs_check_objidconsistency_callback(FAR struct spiffs_s *fs, |
| int16_t objid, |
| int16_t cur_block, |
| int cur_entry, |
| FAR const void *user_const, |
| FAR void *user_var) |
| { |
| FAR uint32_t *log_ndx = (FAR uint32_t *)user_var; |
| FAR int16_t *obj_table = (FAR int16_t *)fs->work; |
| int retc = SPIFFS_VIS_COUNTINUE; |
| int ret = OK; |
| |
| if (objid != SPIFFS_OBJID_FREE && objid != SPIFFS_OBJID_DELETED && |
| (objid & SPIFFS_OBJID_NDXFLAG) != 0) |
| { |
| struct spiffs_page_header_s pghdr; |
| int16_t cur_pgndx; |
| |
| cur_pgndx = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PGNDX(fs, cur_block, cur_entry); |
| |
| /* Load header */ |
| |
| ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, |
| 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx), |
| sizeof(struct spiffs_page_header_s), (uint8_t *)&pghdr); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_read() failed: %d\n", ret); |
| return ret; |
| } |
| |
| if (pghdr.spndx == 0 && |
| (pghdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | |
| SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_NDXDELE)) == |
| (SPIFFS_PH_FLAG_DELET)) |
| { |
| spiffs_checkinfo("pgndx=%04x, objid=%04x spndx=%04x " |
| "header not fully deleted - deleting\n", |
| cur_pgndx, objid, pghdr.spndx); |
| |
| ret = spiffs_page_delete(fs, cur_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_page_delete() failed: %d\n", ret); |
| return ret; |
| } |
| |
| return retc; |
| } |
| |
| if ((pghdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | |
| SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_NDXDELE)) == |
| (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_NDXDELE)) |
| { |
| return retc; |
| } |
| |
| if (pghdr.spndx == 0) |
| { |
| int ret2; |
| |
| /* objndx header page, register objid as reachable */ |
| |
| ret2 = spifss_check_objndx_search(fs, objid); |
| if (ret2 < 0) |
| { |
| /* Not registered, do it */ |
| |
| obj_table[*log_ndx] = objid & ~SPIFFS_OBJID_NDXFLAG; |
| (*log_ndx)++; |
| if (*log_ndx >= SPIFFS_GEO_PAGE_SIZE(fs) / sizeof(int16_t)) |
| { |
| *log_ndx = 0; |
| } |
| } |
| } |
| else |
| { |
| bool delete = false; |
| int ret2; |
| |
| /* Span index |
| * objndx page, see if header can be found |
| */ |
| |
| ret2 = spifss_check_objndx_search(fs, objid); |
| if (ret2 < 0) |
| { |
| int16_t objhdr_pgndx; |
| |
| /* Not in temporary index, try finding it */ |
| |
| ret = spiffs_objlu_find_id_and_span(fs, |
| objid | SPIFFS_OBJID_NDXFLAG, |
| 0, 0, &objhdr_pgndx); |
| retc = SPIFFS_VIS_COUNTINUE_RELOAD; |
| |
| if (ret >= 0) |
| { |
| /* Found, register as reachable */ |
| |
| obj_table[*log_ndx] = objid & ~SPIFFS_OBJID_NDXFLAG; |
| } |
| else if (ret == -ENOENT) |
| { |
| /* Not found, register as unreachable */ |
| |
| delete = true; |
| obj_table[*log_ndx] = objid | SPIFFS_OBJID_NDXFLAG; |
| } |
| else if (ret < 0) |
| { |
| ferr("ERROR: spiffs_objlu_find_id_and_span() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| (*log_ndx)++; |
| if (*log_ndx >= SPIFFS_GEO_PAGE_SIZE(fs) / sizeof(int16_t)) |
| { |
| *log_ndx = 0; |
| } |
| } |
| else |
| { |
| /* In temporary index, check reachable flag */ |
| |
| if ((obj_table[ret2] & SPIFFS_OBJID_NDXFLAG)) |
| { |
| /* Registered as unreachable */ |
| |
| delete = true; |
| } |
| } |
| |
| if (delete) |
| { |
| spiffs_checkinfo("pgndx=%04x objid=%04x spndx:%04x" |
| " is orphan index - deleting\n", |
| cur_pgndx, objid, pghdr.spndx); |
| |
| ret = spiffs_page_delete(fs, cur_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_page_delete() failed: %d\n", ret); |
| return ret; |
| } |
| } |
| } |
| } |
| |
| return retc; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: spiffs_check_luconsistency |
| * |
| * Description: |
| * Scans all object look up. For each entry, corresponding page header is |
| * checked for validity. If an object index header page is found, this is |
| * also checked |
| * |
| * Input Parameters: |
| * fs - A reference to the SPIFFS volume object instance |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success; A negated errno value is returned on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| int spiffs_check_luconsistency(FAR struct spiffs_s *fs) |
| { |
| int ret = OK; |
| |
| ret = spiffs_foreach_objlu(fs, 0, 0, 0, 0, spiffs_lucheck_callback, |
| 0, 0, 0, 0); |
| if (ret == SPIFFS_VIS_END) |
| { |
| ret = OK; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: spiffs_check_pgconsistency |
| * |
| * Description: |
| * Checks consistency amongst all pages and fixes irregularities |
| * Scans all pages (except lu pages), reserves 4 bits in working memory |
| * for each page |
| * |
| * bit 0: 0 == FREE|DELETED, 1 == USED |
| * bit 1: 0 == UNREFERENCED, 1 == REFERENCED |
| * bit 2: 0 == NOT_INDEX, 1 == INDEX |
| * bit 3: unused |
| * |
| * A consistent file system will have only pages being |
| * |
| * - x000 free, unreferenced, not index |
| * - x011 used, referenced only once, not index |
| * - x101 used, unreferenced, index |
| * |
| * The working memory might not fit all pages so several scans might be |
| * needed |
| * |
| * Input Parameters: |
| * fs - A reference to the SPIFFS volume object instance |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success; A negated errno value is returned on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| int spiffs_check_pgconsistency(FAR struct spiffs_s *fs) |
| { |
| const uint32_t bits = 4; |
| const int16_t pages_per_scan = SPIFFS_GEO_PAGE_SIZE(fs) * 8 / bits; |
| int16_t pgndx_offset = 0; |
| int ret = OK; |
| |
| /* For each range of pages fitting into work memory */ |
| |
| while (pgndx_offset < SPIFFS_GEO_PAGES_PER_BLOCK(fs) * |
| SPIFFS_GEO_BLOCK_COUNT(fs)) |
| { |
| int16_t cur_block = 0; |
| bool restart = false; |
| |
| memset(fs->work, 0, SPIFFS_GEO_PAGE_SIZE(fs)); |
| |
| /* Build consistency bitmap for ID range traversing all blocks */ |
| |
| while (!restart && cur_block < SPIFFS_GEO_BLOCK_COUNT(fs)) |
| { |
| /* Traverse each page except for lookup pages */ |
| |
| int16_t cur_pgndx = SPIFFS_OBJ_LOOKUP_PAGES(fs) + |
| SPIFFS_GEO_PAGES_PER_BLOCK(fs) * cur_block; |
| |
| while (!restart && cur_pgndx < |
| SPIFFS_GEO_PAGES_PER_BLOCK(fs) * (cur_block + 1)) |
| { |
| struct spiffs_page_header_s pghdr; |
| uint32_t pgndx_bytendx; |
| uint8_t pgndx_bitndx; |
| bool within_range; |
| |
| /* read header */ |
| |
| ret = |
| spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, |
| 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx), |
| sizeof(struct spiffs_page_header_s), |
| (FAR uint8_t *)&pghdr); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_read() failed: %d\n", ret); |
| return ret; |
| } |
| |
| within_range = (cur_pgndx >= pgndx_offset && |
| cur_pgndx < pgndx_offset + pages_per_scan); |
| pgndx_bytendx = (cur_pgndx - pgndx_offset) / (8 / bits); |
| pgndx_bitndx = (cur_pgndx & ((8 / bits) - 1)) * bits; |
| |
| if (within_range && |
| (pghdr.flags & SPIFFS_PH_FLAG_DELET) && |
| (pghdr.flags & SPIFFS_PH_FLAG_USED) == 0) |
| { |
| /* Used */ |
| |
| fs->work[pgndx_bytendx] |= (1 << (pgndx_bitndx + 0)); |
| } |
| |
| if ((pghdr.flags & SPIFFS_PH_FLAG_DELET) && |
| (pghdr.flags & SPIFFS_PH_FLAG_NDXDELE) && |
| (pghdr.flags & (SPIFFS_PH_FLAG_INDEX | |
| SPIFFS_PH_FLAG_USED)) == 0) |
| { |
| FAR struct spiffs_page_header_s *objndx_phdr; |
| FAR int16_t *object_page_index; |
| int16_t data_spndx_offset; |
| int entries; |
| int i; |
| |
| /* Found non-deleted index */ |
| |
| if (within_range) |
| { |
| fs->work[pgndx_bytendx] |= (1 << (pgndx_bitndx + 2)); |
| } |
| |
| /* Load non-deleted index */ |
| |
| ret = spiffs_cache_read(fs, |
| SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, |
| 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx), |
| SPIFFS_GEO_PAGE_SIZE(fs), fs->lu_work); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_read() failed: %d\n", ret); |
| return ret; |
| } |
| |
| /* traverse index for referenced pages */ |
| |
| objndx_phdr = |
| (FAR struct spiffs_page_header_s *)fs->lu_work; |
| |
| if (pghdr.spndx == 0) |
| { |
| /* object header page index */ |
| |
| entries = SPIFFS_OBJHDR_NDXLEN(fs); |
| data_spndx_offset = 0; |
| object_page_index = |
| (FAR int16_t *)((FAR uint8_t *)fs->lu_work + |
| sizeof(struct spiffs_pgobj_ndxheader_s)); |
| } |
| else |
| { |
| /* Object page index */ |
| |
| entries = SPIFFS_OBJNDX_LEN(fs); |
| data_spndx_offset = SPIFFS_OBJHDR_NDXLEN(fs) + |
| SPIFFS_OBJNDX_LEN(fs) * |
| (pghdr.spndx - 1); |
| object_page_index = |
| (FAR int16_t *)((FAR uint8_t *) fs->lu_work + |
| sizeof(struct spiffs_page_objndx_s)); |
| } |
| |
| /* For all entries in index */ |
| |
| for (i = 0; !restart && i < entries; i++) |
| { |
| int16_t rpgndx = object_page_index[i]; |
| bool rpgndx_within_range; |
| |
| rpgndx_within_range = (rpgndx >= pgndx_offset && |
| rpgndx < pgndx_offset + |
| pages_per_scan); |
| |
| if ((rpgndx != (int16_t) - 1 && |
| rpgndx > SPIFFS_GEO_PAGE_COUNT(fs)) || |
| (rpgndx_within_range && |
| SPIFFS_IS_LOOKUP_PAGE(fs, rpgndx))) |
| { |
| int16_t data_pgndx; |
| |
| /* Bad reference */ |
| |
| spiffs_checkinfo("pgndx=%04x bad pgndx / LU " |
| "referenced from page %04x\n", |
| rpgndx, cur_pgndx); |
| |
| /* Check for data page elsewhere */ |
| |
| ret = spiffs_objlu_find_id_and_span(fs, |
| objndx_phdr->objid & |
| ~SPIFFS_OBJID_NDXFLAG, |
| data_spndx_offset + i, |
| 0, &data_pgndx); |
| if (ret == -ENOENT) |
| { |
| ret = OK; |
| data_pgndx = 0; |
| } |
| else if (ret < 0) |
| { |
| ferr("ERR: spiffs_objlu_find_id_and_span %d\n", |
| ret); |
| return ret; |
| } |
| |
| if (data_pgndx == 0) |
| { |
| struct spiffs_page_header_s new_ph; |
| |
| /* If not, allocate free page */ |
| |
| new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | |
| SPIFFS_PH_FLAG_FINAL); |
| new_ph.objid = objndx_phdr->objid & |
| ~SPIFFS_OBJID_NDXFLAG; |
| new_ph.spndx = data_spndx_offset + i; |
| |
| ret = spiffs_page_allocate_data(fs, |
| new_ph.objid, |
| &new_ph, 0, 0, 0, 1, |
| &data_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERR: spiffs_page_allocate_data %d\n", |
| ret); |
| return ret; |
| } |
| |
| spiffs_checkinfo("Found no existing data page," |
| " created new @ %04x\n", |
| data_pgndx); |
| } |
| |
| /* Remap index */ |
| |
| spiffs_checkinfo("Rewriting index pgndx=%04x\n", |
| cur_pgndx); |
| |
| ret = |
| spiffs_check_rewrite_index(fs, |
| objndx_phdr->objid | |
| SPIFFS_OBJID_NDXFLAG, |
| data_spndx_offset + i, |
| data_pgndx, cur_pgndx); |
| if (ret == -EFAULT) |
| { |
| /* Index bad also, cannot mend this file */ |
| |
| spiffs_checkinfo("Index bad %d, cannot mend - " |
| "delete object\n", |
| ret); |
| |
| /* Delete file */ |
| |
| ret = spiffs_page_delete(fs, cur_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_page_delete(): %d\n", |
| ret); |
| return ret; |
| } |
| } |
| else if (ret < 0) |
| { |
| ferr("ERR: spiffs_check_rewrite_index(): %d\n", |
| ret); |
| return ret; |
| } |
| |
| restart = true; |
| } |
| else if (rpgndx_within_range) |
| { |
| /* Valid reference. read referenced page header */ |
| |
| struct spiffs_page_header_s rphdr; |
| ret = |
| spiffs_cache_read(fs, |
| SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, |
| 0, SPIFFS_PAGE_TO_PADDR(fs, rpgndx), |
| sizeof(struct spiffs_page_header_s), |
| (FAR uint8_t *)&rphdr); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_read() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| /* Cross reference page header check */ |
| |
| if (rphdr.objid != (pghdr.objid & |
| ~SPIFFS_OBJID_NDXFLAG) || |
| rphdr.spndx != data_spndx_offset + i || |
| (rphdr.flags & (SPIFFS_PH_FLAG_DELET | |
| SPIFFS_PH_FLAG_INDEX | |
| SPIFFS_PH_FLAG_USED)) != |
| (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) |
| { |
| int16_t data_pgndx; |
| |
| spiffs_checkinfo( |
| "pgndx=%04x has inconsistent page header " |
| "index objid/span:%04x/%04x, " |
| "ref objid/span:%04x/%04x flags=%02x\n", |
| rpgndx, pghdr.objid & ~SPIFFS_OBJID_NDXFLAG, |
| data_spndx_offset + i, rphdr.objid, |
| rphdr.spndx, rphdr.flags); |
| |
| /* Try finding correct page */ |
| |
| ret = |
| spiffs_objlu_find_id_and_span(fs, |
| pghdr.objid & |
| ~SPIFFS_OBJID_NDXFLAG, |
| data_spndx_offset + i, |
| rpgndx, &data_pgndx); |
| if (ret == -ENOENT) |
| { |
| ret = OK; |
| data_pgndx = 0; |
| } |
| else if (ret < 0) |
| { |
| ferr("spiffs_objlu_find_id_and_span: %d\n", |
| ret); |
| return ret; |
| } |
| |
| if (data_pgndx == 0) |
| { |
| /* Not found, this index is badly borked */ |
| |
| spiffs_checkinfo( |
| "Index bad, delete object objid %04x\n", |
| pghdr.objid); |
| |
| ret = spiffs_check_delobj_lazy(fs, |
| pghdr.objid); |
| if (ret < 0) |
| { |
| ferr("spiffs_check_delobj_lazy: %d\n", |
| ret); |
| return ret; |
| } |
| |
| break; |
| } |
| else |
| { |
| /* Found it, so rewrite index */ |
| |
| spiffs_checkinfo( |
| "Found correct data pgndx=%04x, " |
| "rewrite index pgndx=%04x objid=%04x\n", |
| data_pgndx, cur_pgndx, pghdr.objid); |
| |
| ret = |
| spiffs_check_rewrite_index( |
| fs, pghdr.objid, data_spndx_offset + i, |
| data_pgndx, cur_pgndx); |
| if (ret == -EFAULT) |
| { |
| /* Index bad, cannot mend this file */ |
| |
| spiffs_checkinfo( |
| "Index bad %d, cannot mend!\n", ret); |
| |
| ret = spiffs_check_delobj_lazy( |
| fs, pghdr.objid); |
| } |
| else if (ret < 0) |
| { |
| ferr("spiffs_check_rewrite_index %d\n", |
| ret); |
| return ret; |
| } |
| |
| restart = true; |
| } |
| } |
| else |
| { |
| /* Mark rpgndx as referenced */ |
| |
| const uint32_t rpgndx_byte_ix = |
| (rpgndx - pgndx_offset) / (8 / bits); |
| const uint8_t rpgndx_bit_ix = |
| (rpgndx & ((8 / bits) - 1)) * bits; |
| |
| if ((fs->work[rpgndx_byte_ix] & |
| (1 << (rpgndx_bit_ix + 1))) != 0) |
| { |
| spiffs_checkinfo( |
| "pgndx=%04x multiple referenced " |
| "from page %04x\n", |
| rpgndx, cur_pgndx); |
| |
| /* Here, we should have fixed all broken |
| * references - getting this means there |
| * must be multiple files with same object |
| * ID. Only solution is to delete |
| * the object which is referring to this |
| * page |
| */ |
| |
| spiffs_checkinfo("Removing objid=%04x and" |
| "page=%04x\n", |
| pghdr.objid, cur_pgndx); |
| |
| ret = spiffs_check_delobj_lazy( |
| fs, pghdr.objid); |
| if (ret < 0) |
| { |
| ferr("spiffs_check_delobj_lazy: %d\n", |
| ret); |
| return ret; |
| } |
| |
| /* Precaution, delete this page also */ |
| |
| ret = spiffs_page_delete(fs, cur_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERR: spiffs_page_delete(): %d\n", |
| ret); |
| return ret; |
| } |
| |
| restart = true; |
| } |
| |
| fs->work[rpgndx_byte_ix] |= |
| (1 << (rpgndx_bit_ix + 1)); |
| } |
| } |
| } |
| } |
| |
| /* Next page */ |
| |
| cur_pgndx++; |
| } |
| |
| /* Next block */ |
| |
| cur_block++; |
| } |
| |
| /* Check consistency bitmap */ |
| |
| if (!restart) |
| { |
| uint32_t byte_ndx; |
| int16_t objndx_pgndx; |
| int16_t rpgndx; |
| uint8_t bit_ndx; |
| |
| for (byte_ndx = 0; |
| !restart && byte_ndx < SPIFFS_GEO_PAGE_SIZE(fs); |
| byte_ndx++) |
| { |
| for (bit_ndx = 0; !restart && bit_ndx < 8 / bits; bit_ndx++) |
| { |
| uint8_t bitmask; |
| int16_t cur_pgndx; |
| |
| bitmask = (fs->work[byte_ndx] >> (bit_ndx * bits)) & 0x7; |
| cur_pgndx = pgndx_offset + byte_ndx * (8 / bits) + bit_ndx; |
| |
| /* 000 ok - free, unreferenced, not index */ |
| |
| if (bitmask == 0x1) |
| { |
| struct spiffs_page_header_s pghdr; |
| bool rewrite_ndx_to_this = false; |
| bool delete_page = false; |
| |
| /* 001 */ |
| |
| spiffs_checkinfo( |
| "pgndx=%04x USED, UNREFERENCED, not index\n", |
| cur_pgndx); |
| |
| /* Check corresponding object index entry */ |
| |
| ret = spiffs_cache_read(fs, |
| SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, |
| 0, |
| SPIFFS_PAGE_TO_PADDR(fs, cur_pgndx), |
| sizeof(struct spiffs_page_header_s), |
| (FAR uint8_t *)&pghdr); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_read() failed: %d\n", |
| ret); |
| return ret; |
| } |
| |
| ret = spiffs_check_get_data_pgndx(fs, pghdr.objid, |
| pghdr.spndx, &rpgndx, |
| &objndx_pgndx); |
| if (ret >= 0) |
| { |
| if (((rpgndx == (int16_t) - 1 || |
| rpgndx > SPIFFS_GEO_PAGE_COUNT(fs)) || |
| (SPIFFS_IS_LOOKUP_PAGE(fs, rpgndx)))) |
| { |
| /* Pointing to a bad page altogether, rewrite |
| * index to this |
| */ |
| |
| rewrite_ndx_to_this = true; |
| |
| spiffs_checkinfo( |
| "Corresponding ref is bad: " |
| "%04x, rewrite to this %04x\n", |
| rpgndx, cur_pgndx); |
| } |
| else |
| { |
| struct spiffs_page_header_s rphdr; |
| |
| /* Pointing to something else, check what */ |
| |
| ret = |
| spiffs_cache_read(fs, |
| SPIFFS_OP_T_OBJ_LU2 | |
| SPIFFS_OP_C_READ, |
| 0, |
| SPIFFS_PAGE_TO_PADDR(fs, rpgndx), |
| sizeof(struct spiffs_page_header_s), |
| (FAR uint8_t *)&rphdr); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_cache_read(): %d\n", |
| ret); |
| return ret; |
| } |
| |
| if (((pghdr.objid & ~SPIFFS_OBJID_NDXFLAG) == |
| rphdr.objid) && |
| ((rphdr.flags & (SPIFFS_PH_FLAG_INDEX | |
| SPIFFS_PH_FLAG_DELET | |
| SPIFFS_PH_FLAG_USED | |
| SPIFFS_PH_FLAG_FINAL)) == |
| (SPIFFS_PH_FLAG_INDEX | |
| SPIFFS_PH_FLAG_DELET))) |
| { |
| /* Pointing to something else valid, just |
| * delete this page then |
| */ |
| |
| spiffs_checkinfo( |
| "Corresponding ref is good but " |
| "different: %04x, delete this %04x\n", |
| rpgndx, cur_pgndx); |
| |
| delete_page = true; |
| } |
| |
| /* Pointing to something weird, update index |
| * to point to this page instead |
| */ |
| |
| else if (rpgndx != cur_pgndx) |
| { |
| spiffs_checkinfo |
| ("PA: corresponding ref is weird: " |
| "%04x %s%s%s%s, rewrite this " |
| "%04x\n", rpgndx, |
| (rphdr.flags & SPIFFS_PH_FLAG_INDEX) ? |
| "" : "INDEX ", |
| (rphdr.flags & SPIFFS_PH_FLAG_DELET) ? |
| "" : "DELETED ", |
| (rphdr.flags & SPIFFS_PH_FLAG_USED) ? |
| "NOTUSED " : "", |
| (rphdr.flags & SPIFFS_PH_FLAG_FINAL) ? |
| "NOTFINAL " : "", cur_pgndx); |
| |
| rewrite_ndx_to_this = true; |
| } |
| else |
| { |
| /* Should not happen, destined for fubar */ |
| } |
| } |
| } |
| else if (ret == -ENOENT) |
| { |
| spiffs_checkinfo("Corresponding ref not found, " |
| "delete %04x\n", |
| cur_pgndx); |
| |
| delete_page = true; |
| ret = OK; |
| } |
| |
| if (rewrite_ndx_to_this) |
| { |
| /* If pointing to invalid page, redirect index to |
| * this page |
| */ |
| |
| spiffs_checkinfo( |
| "Rewrite index objid=%04x data spndx=%04x" |
| " to point to this pgndx: %04x\n", |
| pghdr.objid, pghdr.spndx, cur_pgndx); |
| |
| ret = spiffs_check_rewrite_index(fs, pghdr.objid, |
| pghdr.spndx, cur_pgndx, objndx_pgndx); |
| if (ret == -EFAULT) |
| { |
| int ret2; |
| |
| /* Index bad also, cannot mend this file */ |
| |
| spiffs_checkinfo("PA: FIXUP: index bad %d" |
| ", cannot mend!\n", ret); |
| |
| ret2 = spiffs_page_delete(fs, cur_pgndx); |
| if (ret2 < 0) |
| { |
| ferr("ERROR: spiffs_page_delete(): %d\n", |
| ret2); |
| return ret2; |
| } |
| |
| ret2 = spiffs_check_delobj_lazy(fs, |
| pghdr.objid); |
| if (ret2 < 0) |
| { |
| ferr( |
| "ERR: spiffs_check_delobj_lazy(): %d\n", |
| ret2); |
| return ret2; |
| } |
| } |
| else if (ret < 0) |
| { |
| ferr("ERR: spiffs_check_rewrite_index(): %d\n", |
| ret); |
| return ret; |
| } |
| |
| restart = true; |
| continue; |
| } |
| else if (delete_page) |
| { |
| spiffs_checkinfo("Deleting page %04x\n", |
| cur_pgndx); |
| |
| ret = spiffs_page_delete(fs, cur_pgndx); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_page_delete(): %d\n", ret); |
| return ret; |
| } |
| } |
| } |
| |
| if (bitmask == 0x2) |
| { |
| /* 010 */ |
| |
| spiffs_checkinfo( |
| "pgndx=%04x FREE, REFERENCED, not index\n", |
| cur_pgndx); |
| |
| /* No op, this should be taken care of when checking |
| * valid references |
| */ |
| } |
| |
| /* 011 OK - busy, referenced, not index */ |
| |
| if (bitmask == 0x4) |
| { |
| /* 100 */ |
| |
| spiffs_checkinfo( |
| "pgndx=%04x FREE, unreferenced, INDEX\n", cur_pgndx); |
| |
| /* This should never happen, major fubar */ |
| } |
| |
| /* 101 OK - busy, unreferenced, index */ |
| |
| if (bitmask == 0x6) |
| { |
| /* 110 */ |
| |
| spiffs_checkinfo( |
| "pgndx=%04x FREE, REFERENCED, INDEX\n", cur_pgndx); |
| |
| /* No op, this should be taken care of when checking |
| * valid references |
| */ |
| } |
| |
| if (bitmask == 0x7) |
| { |
| /* 111 */ |
| |
| spiffs_checkinfo( |
| "pgndx=%04x USED, REFERENCED, INDEX\n", cur_pgndx); |
| |
| /* No op, this should be taken care of when checking |
| * valid references |
| */ |
| } |
| } |
| } |
| } |
| |
| spiffs_checkinfo("Processed %04x, restart %d\n", |
| pgndx_offset, restart); |
| |
| /* next page range */ |
| |
| if (!restart) |
| { |
| pgndx_offset += pages_per_scan; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: spiffs_check_pgconsistency |
| * |
| * Description: |
| * Removes orphaned and partially deleted index pages. |
| * Scans for index pages. When an index page is found, corresponding index |
| * header is searched for. If no such page exists, the index page cannot |
| * be reached as no index header exists and must be deleted. |
| * |
| * Input Parameters: |
| * fs - A reference to the SPIFFS volume object instance |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success; A negated errno value is returned on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| int spiffs_check_objidconsistency(FAR struct spiffs_s *fs) |
| { |
| uint32_t objid_logndx = 0; |
| int ret = OK; |
| |
| /* Implementation not: |
| * fs->work is used for a temporary object index memory, listing found |
| * object ids and indicating whether they can be reached or not. Acting |
| * as a FIFO if object ids cannot fit. In the temporary object index |
| * memory, SPIFFS_OBJID_NDXFLAG bit is used to indicate a reachable/ |
| * unreachable object ID. |
| */ |
| |
| memset(fs->work, 0, SPIFFS_GEO_PAGE_SIZE(fs)); |
| |
| ret = spiffs_foreach_objlu(fs, 0, 0, 0, 0, |
| spiffs_check_objidconsistency_callback, 0, |
| &objid_logndx, 0, 0); |
| if (ret == SPIFFS_VIS_END) |
| { |
| ret = OK; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: spiffs_dump |
| * |
| * Description: |
| * Dump logical flash content |
| * |
| * Input Parameters: |
| * fs - A reference to the volume structure |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success; A negated errno value is returned on |
| * any failure. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_SPIFFS_DUMP |
| int spiffs_dump(FAR struct spiffs_s *fs) |
| { |
| FAR int16_t *objlu_buf = (FAR int16_t *)fs->lu_work; |
| uint32_t pages_per_block; |
| uint32_t blocks; |
| uint32_t obj_lupages; |
| uint32_t data_pgsize; |
| uint32_t ndata_pages; |
| int16_t pgndx = 0; |
| int16_t erase_count; |
| char buffer[80]; |
| int entries_per_page; |
| int len = 0; |
| int ret = OK; |
| |
| entries_per_page = (SPIFFS_GEO_PAGE_SIZE(fs) / sizeof(int16_t)); |
| |
| while (pgndx < SPIFFS_GEO_PAGE_COUNT(fs)) |
| { |
| /* Check each object lookup page */ |
| |
| int obj_lookup_page = 0; |
| int cur_entry = 0; |
| |
| while (ret >= 0 && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) |
| { |
| int entry_offset = obj_lookup_page * entries_per_page; |
| |
| ret = spiffs_cache_read(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, |
| 0, pgndx * SPIFFS_GEO_PAGE_SIZE(fs) + |
| SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), |
| 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_GEO_PAGES_PER_BLOCK(fs) - |
| SPIFFS_OBJ_LOOKUP_PAGES(fs))) |
| { |
| int16_t objid = objlu_buf[cur_entry - entry_offset]; |
| |
| if (cur_entry == 0) |
| { |
| len += snprintf(&buffer[len], 80 - len, "%04x ", pgndx); |
| } |
| else if ((cur_entry & 0x3f) == 0) |
| { |
| len += snprintf(&buffer[len], 80 - len, " "); |
| } |
| |
| if ((objid == SPIFFS_OBJID_FREE) != 0) |
| { |
| len += snprintf(&buffer[len], 80 - len, "."); |
| } |
| else if ((objid == SPIFFS_OBJID_DELETED) != 0) |
| { |
| len += snprintf(&buffer[len], 80 - len, "x"); |
| } |
| else if ((objid & SPIFFS_OBJID_NDXFLAG) != 0) |
| { |
| len += snprintf(&buffer[len], 80 - len, "I"); |
| } |
| else |
| { |
| len += snprintf(&buffer[len], 80 - len, "D"); |
| } |
| |
| cur_entry++; |
| |
| if ((cur_entry & 0x3f) == 0) |
| { |
| len += snprintf(&buffer[len], 80 - len, "\n"); |
| spiffs_checkinfo("%s", buffer); |
| len = 0; |
| } |
| } |
| |
| obj_lookup_page++; |
| } |
| |
| ret = spiffs_cache_read(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, |
| SPIFFS_ERASE_COUNT_PADDR(fs, pgndx), |
| sizeof(int16_t), (FAR uint8_t *)&erase_count); |
| if (ret < 0) |
| { |
| ferr("ERROR: spiffs_mtd_read() failed: %d\n", ret); |
| return ret; |
| } |
| |
| if (erase_count != (int16_t)-1) |
| { |
| len += snprintf(&buffer[len], 80 - len, |
| " era_cnt=%d\n", erase_count); |
| } |
| else |
| { |
| len += snprintf(&buffer[len], 80 - len, " era_cnt (N/A)\n"); |
| } |
| |
| spiffs_checkinfo("%s", buffer); |
| len = 0; |
| |
| pgndx++; |
| } |
| |
| spiffs_checkinfo("era_cnt_max: %d\n", fs->max_erase_count); |
| spiffs_checkinfo("blocks: %d\n", SPIFFS_GEO_PAGE_COUNT(fs)); |
| spiffs_checkinfo("free_blocks: %d\n", fs->free_blocks); |
| spiffs_checkinfo("page_alloc: %d\n", fs->alloc_pages); |
| spiffs_checkinfo("page_delet: %d\n", fs->deleted_pages); |
| |
| /* The following duplicates some logic from spiffs_statfs(). |
| * -2 for spare blocks, +1 for emergency page. |
| */ |
| |
| pages_per_block = SPIFFS_GEO_PAGES_PER_BLOCK(fs); |
| blocks = SPIFFS_GEO_BLOCK_COUNT(fs); |
| obj_lupages = SPIFFS_OBJ_LOOKUP_PAGES(fs); |
| data_pgsize = SPIFFS_DATA_PAGE_SIZE(fs); |
| ndata_pages = (blocks - 2) * (pages_per_block - obj_lupages) + 1; |
| |
| spiffs_checkinfo("used: %ld of %ld\n", |
| (long)(fs->alloc_pages * data_pgsize), |
| (long)(ndata_pages * data_pgsize)); |
| return OK; |
| } |
| #endif |