in fs/spiffs/src/spiffs_check.c [1407:2092]
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;
}