mirror of
https://github.com/pellepl/spiffs.git
synced 2026-09-13 12:40:02 +00:00
some refactoring
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,6 @@
|
||||
FILES += spiffs_nucleus.c \
|
||||
spiffs_gc.c \
|
||||
spiffs_hydrogen.c \
|
||||
spiffs_cache.c
|
||||
spiffs_cache.c \
|
||||
spiffs_check.c
|
||||
|
||||
|
||||
+15
-13
@@ -13,23 +13,23 @@
|
||||
#include "spiffs_config.h"
|
||||
|
||||
#define SPIFFS_OK 0
|
||||
#define SPIFFS_ERR_FULL -10000
|
||||
#define SPIFFS_ERR_NOT_FOUND -10001
|
||||
#define SPIFFS_ERR_END_OF_OBJECT -10002
|
||||
#define SPIFFS_ERR_DELETED -10003
|
||||
#define SPIFFS_ERR_NOT_FINALIZED -10004
|
||||
#define SPIFFS_ERR_NOT_INDEX -10005
|
||||
#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10006
|
||||
#define SPIFFS_ERR_NOT_MOUNTED -10000
|
||||
#define SPIFFS_ERR_FULL -10001
|
||||
#define SPIFFS_ERR_NOT_FOUND -10002
|
||||
#define SPIFFS_ERR_END_OF_OBJECT -10003
|
||||
#define SPIFFS_ERR_DELETED -10004
|
||||
#define SPIFFS_ERR_NOT_FINALIZED -10005
|
||||
#define SPIFFS_ERR_NOT_INDEX -10006
|
||||
#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007
|
||||
#define SPIFFS_ERR_FILE_CLOSED -10008
|
||||
#define SPIFFS_ERR_FILE_DELETED -10009
|
||||
#define SPIFFS_ERR_BAD_DESCRIPTOR -10010
|
||||
#define SPIFFS_ERR_IS_INDEX -10011
|
||||
#define SPIFFS_ERR_INDEX_WRONG_ID -10012
|
||||
#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10013
|
||||
#define SPIFFS_ERR_DATA_WRONG_ID -10014
|
||||
#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10012
|
||||
#define SPIFFS_ERR_INDEX_WRONG_ID -10013
|
||||
#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014
|
||||
#define SPIFFS_ERR_DATA_WRONG_ID -10015
|
||||
|
||||
#define SPIFFS_COUNTINUE -11000
|
||||
|
||||
// spi read call type
|
||||
typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst);
|
||||
@@ -173,7 +173,7 @@ typedef struct {
|
||||
// functions
|
||||
|
||||
/**
|
||||
* Initializes the file system dynamic parameters
|
||||
* Initializes the file system dynamic parameters and mounts the filesystem
|
||||
* @param fs the file system struct
|
||||
* @param config the physical and logical configuration of the file system
|
||||
* @param work a memory work buffer comprising 2*config->log_page_size
|
||||
@@ -183,10 +183,12 @@ typedef struct {
|
||||
* @param cache memory for cache, may be null
|
||||
* @param cache_size memory size of cache
|
||||
*/
|
||||
s32_t SPIFFS_init(spiffs *fs, spiffs_config *config, u8_t *work,
|
||||
s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
|
||||
u8_t *fd_space, u32_t fd_space_size,
|
||||
void *cache, u32_t cache_size);
|
||||
|
||||
void SPIFFS_unmount(spiffs *fs);
|
||||
|
||||
s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_attr attr);
|
||||
spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_attr attr, spiffs_mode mode);
|
||||
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
|
||||
|
||||
+1
-1
@@ -236,7 +236,7 @@ spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) {
|
||||
return cp;
|
||||
}
|
||||
|
||||
void spiffs_cache_fh_release(spiffs *fs, spiffs_cache_page *cp) {
|
||||
void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) {
|
||||
if (cp == 0) return;
|
||||
int i;
|
||||
spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* spiffs_check.c
|
||||
*
|
||||
* Created on: Jul 7, 2013
|
||||
* Author: petera
|
||||
*/
|
||||
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
|
||||
|
||||
static s32_t spiffs_object_check_data_page(
|
||||
spiffs *fs,
|
||||
spiffs_obj_id obj_id,
|
||||
spiffs_span_ix data_spix,
|
||||
spiffs_page_ix *pix) {
|
||||
s32_t res;
|
||||
spiffs_page_ix objix_pix;
|
||||
spiffs_page_ix ref_pix;
|
||||
|
||||
// calculate object index span index for given data page span index
|
||||
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
|
||||
|
||||
// find obj index for obj id and span index
|
||||
res = spiffs_obj_lu_find_id_and_index(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, &objix_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
// load obj index
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs),
|
||||
(u8_t*)fs->work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
if (objix_spix == 0) {
|
||||
// get referenced page from object index header
|
||||
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
|
||||
ref_pix = ((spiffs_page_ix*)((void*)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix];
|
||||
} else {
|
||||
// get referenced page from object index
|
||||
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
|
||||
ref_pix = ((spiffs_page_ix*)((void*)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)];
|
||||
}
|
||||
|
||||
*pix = ref_pix;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static s32_t spiffs_area_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr,
|
||||
spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) {
|
||||
u8_t delete_page = 0;
|
||||
s32_t res = SPIFFS_OK;
|
||||
// check validity, take actions
|
||||
if (lu_obj_id == SPIFFS_OBJ_ID_ERASED) {
|
||||
// TODO
|
||||
if (p_hdr->flags & SPIFFS_PH_FLAG_DELET) {
|
||||
print("WARNING: pix %04x deleted in lu but not on page\n", cur_pix);
|
||||
// page can be removed if not referenced by object index
|
||||
spiffs_page_ix ref_pix;
|
||||
res = spiffs_object_check_data_page(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no object with this id, so remove page safely
|
||||
res = SPIFFS_OK;
|
||||
delete_page = 1;
|
||||
ref_pix = cur_pix+1;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
if (ref_pix != cur_pix) {
|
||||
delete_page = 1;
|
||||
} else {
|
||||
// TODO - page referenced by object index but deleted in lu
|
||||
}
|
||||
}
|
||||
} else if (lu_obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
// TODO
|
||||
if (p_hdr->flags != 0xff) {
|
||||
print("WARNING: pix %04x free in lu but not on page\n", cur_pix);
|
||||
// page can be removed if not referenced by object index
|
||||
spiffs_page_ix ref_pix;
|
||||
res = spiffs_object_check_data_page(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no object with this id, so remove page safely
|
||||
res = SPIFFS_OK;
|
||||
delete_page = 1;
|
||||
ref_pix = cur_pix+1;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
if (ref_pix != cur_pix) {
|
||||
delete_page = 1;
|
||||
} else {
|
||||
// TODO - page referenced by object index but free in lu
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO
|
||||
if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) {
|
||||
print("WARNING: pix %04x busy in lu but free on page\n", cur_pix);
|
||||
delete_page = 1;
|
||||
}
|
||||
if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) {
|
||||
print("WARNING: pix %04x busy but not final\n", cur_pix);
|
||||
// page can be removed if not referenced by object index
|
||||
spiffs_page_ix ref_pix;
|
||||
res = spiffs_object_check_data_page(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
// no object with this id, so remove page safely
|
||||
res = SPIFFS_OK;
|
||||
delete_page = 1;
|
||||
ref_pix = cur_pix+1;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
*reload_lu = 1;
|
||||
if (ref_pix != cur_pix) {
|
||||
delete_page = 1;
|
||||
} else {
|
||||
// TODO - page referenced by object index but not final
|
||||
}
|
||||
}
|
||||
if (p_hdr->obj_id != lu_obj_id) {
|
||||
print("WARNING: pix %04x has id %04x in lu but %04x on page\n", cur_pix, lu_obj_id, p_hdr->obj_id);
|
||||
if (p_hdr->obj_id == SPIFFS_OBJ_ID_FREE || p_hdr->obj_id == SPIFFS_OBJ_ID_ERASED) {
|
||||
delete_page = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) {
|
||||
// index page
|
||||
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) {
|
||||
print("WARNING: pix %04x marked as index in lu but as data on page\n", cur_pix);
|
||||
}
|
||||
} else {
|
||||
// data page
|
||||
if ((p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0) {
|
||||
print("WARNING: pix %04x as data in lu but as index on page\n", cur_pix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (delete_page) {
|
||||
print("FIX: deleting page %04x\n", cur_pix);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static s32_t spiffs_area_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry,
|
||||
u32_t user_data, void *user_p) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
|
||||
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
int reload_lu = 0;
|
||||
|
||||
res = spiffs_area_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu);
|
||||
|
||||
if (reload_lu) {
|
||||
// reload lu
|
||||
u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
int obj_lookup_page = cur_entry / entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_BLOCK_TO_PADDR(fs, cur_block) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
|
||||
if (res == SPIFFS_OK) {
|
||||
return SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// 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 checked
|
||||
s32_t spiffs_area_check(spiffs *fs, u8_t check_all_objects) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs);
|
||||
spiffs_block_ix cur_block = 0;
|
||||
u32_t cur_block_addr = SPIFFS_BLOCK_TO_PADDR(fs, 0);
|
||||
|
||||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_area_check_v, 0, 0, 0, 0);
|
||||
|
||||
if (res == SPIFFS_VIS_END) {
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Scans all object look up for consistency within given object id. When an index is found, it is loaded
|
||||
// and checked for validity against each referenced page. When an object index header is found, size is
|
||||
// checked.
|
||||
// When an index points to a bad page (deleted, other id, bad span index, etc), the whole area is searched
|
||||
// for correct page, followed by an index update. If no such page is found, a free page is allocated and
|
||||
// referenced instead meaning there will be a page hole filled with 0xff in the file.
|
||||
s32_t spiffs_object_check(spiffs *fs, spiffs_obj_id obj_id, u8_t mend) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
spiffs_page_ix pix;
|
||||
spiffs_page_object_ix_header objix_hdr;
|
||||
|
||||
// find obj index header for obj id
|
||||
res = spiffs_obj_lu_find_id_and_index(fs, obj_id, 0, &pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
// load header, get size
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t*)&objix_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
return res;
|
||||
}
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
#include "spiffs.h"
|
||||
#include "spiffs_nucleus.h"
|
||||
|
||||
s32_t spiffs_gc_check(
|
||||
spiffs *fs,
|
||||
u32_t len) {
|
||||
s32_t res;
|
||||
u32_t free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
int tries = 0;
|
||||
|
||||
if (fs->free_blocks > 2 &&
|
||||
len < free_pages * SPIFFS_DATA_PAGE_SIZE(fs)) {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
do {
|
||||
SPIFFS_GC_DBG("\ngc_check #i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n",
|
||||
tries,
|
||||
fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
|
||||
len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs));
|
||||
|
||||
spiffs_block_ix *cands;
|
||||
int count;
|
||||
spiffs_block_ix cand;
|
||||
res = spiffs_gc_find_candidate(fs, &cands, &count);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (count == 0) {
|
||||
SPIFFS_GC_DBG("gc_check: no candidates, return\n");
|
||||
return res;
|
||||
}
|
||||
#if SPIFFS_GC_STATS
|
||||
fs->stats_gc_runs++;
|
||||
#endif
|
||||
cand = cands[0];
|
||||
res = spiffs_gc_clean(fs, cand);
|
||||
SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
res = spiffs_gc_erase_page_stats(fs, cand);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, cand);
|
||||
s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
|
||||
// here we ignore res, just try erasing the block
|
||||
SPIFFS_GC_DBG("gc_check: erase block %i\n", cand);
|
||||
while (size > 0) {
|
||||
SPIFFS_DBG("gc_check: erase %08x:%08x\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
|
||||
res = fs->cfg.hal_erase_f(addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
|
||||
addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs);
|
||||
size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs);
|
||||
}
|
||||
fs->free_blocks++;
|
||||
free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
#if SPIFFS_CACHE
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) {
|
||||
spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, cand) + i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} while (tries++ < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
|
||||
len > free_pages*SPIFFS_DATA_PAGE_SIZE(fs)));
|
||||
SPIFFS_GC_DBG("gc_check: finished\n");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
s32_t spiffs_gc_erase_page_stats(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
int obj_lookup_page = 0;
|
||||
u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
int cur_entry = 0;
|
||||
u32_t dele = 0;
|
||||
u32_t allo = 0;
|
||||
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_ERASED) {
|
||||
dele++;
|
||||
} else {
|
||||
allo++;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
SPIFFS_GC_DBG("gc_check: wipe pallo:%i pdele:%i\n", allo, dele);
|
||||
fs->stats_p_allocated -= allo;
|
||||
fs->stats_p_deleted -= dele;
|
||||
return res;
|
||||
}
|
||||
|
||||
s32_t spiffs_gc_find_candidate(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix **block_candidates,
|
||||
int *candidate_count) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t blocks = fs->block_count;
|
||||
spiffs_block_ix cur_block = 0;
|
||||
u32_t cur_block_addr = SPIFFS_CFG_PHYS_ADDR(fs);
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
int cur_entry = 0;
|
||||
|
||||
// using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(int)score
|
||||
int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(int)));
|
||||
*candidate_count = 0;
|
||||
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
|
||||
// divide up work area into block indices and scores
|
||||
// todo alignment?
|
||||
spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
|
||||
int *cand_scores = (int *)(fs->work + max_candidates * sizeof(spiffs_block_ix));
|
||||
|
||||
*block_candidates = cand_blocks;
|
||||
|
||||
u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
|
||||
// check each block
|
||||
while (res == SPIFFS_OK && blocks--) {
|
||||
u16_t deleted_pages_in_block = 0;
|
||||
u16_t used_pages_in_block = 0;
|
||||
|
||||
int obj_lookup_page = 0;
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
// when a free entry is encountered, scan logic ensures that all following entries are free also
|
||||
break;
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_ERASED) {
|
||||
deleted_pages_in_block++;
|
||||
} else {
|
||||
used_pages_in_block++;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
|
||||
// calculate score and insert into candidate table
|
||||
// stoneage sort, but probably not so many blocks
|
||||
if (res == SPIFFS_OK && deleted_pages_in_block > 0) {
|
||||
int score = deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + used_pages_in_block * SPIFFS_GC_HEUR_W_USED;
|
||||
int cand_ix = 0;
|
||||
SPIFFS_DBG("gc_check: bix:%i del:%i use:%i score:%i\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
|
||||
while (cand_ix < max_candidates) {
|
||||
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
|
||||
cand_blocks[cand_ix] = cur_block;
|
||||
cand_scores[cand_ix] = score;
|
||||
break;
|
||||
} else if (cand_scores[cand_ix] < score) {
|
||||
int reorder_cand_ix = max_candidates - 2;
|
||||
while (reorder_cand_ix >= cand_ix) {
|
||||
cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix];
|
||||
cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix];
|
||||
reorder_cand_ix--;
|
||||
}
|
||||
cand_blocks[cand_ix] = cur_block;
|
||||
cand_scores[cand_ix] = score;
|
||||
break;
|
||||
}
|
||||
cand_ix++;
|
||||
}
|
||||
(*candidate_count)++;
|
||||
}
|
||||
|
||||
cur_entry = 0;
|
||||
cur_block++;
|
||||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
} // per block
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
MOVE_OBJ_IX,
|
||||
FIND_OBJ_DATA,
|
||||
MOVE_OBJ_DATA,
|
||||
FINISHED
|
||||
} spiffs_gc_clean_state;
|
||||
|
||||
typedef struct {
|
||||
spiffs_gc_clean_state state;
|
||||
spiffs_obj_id cur_obj_id;
|
||||
spiffs_span_ix cur_objix_spix;
|
||||
spiffs_page_ix cur_objix_pix;
|
||||
int stored_scan_entry_index;
|
||||
u8_t obj_id_found;
|
||||
} spiffs_gc;
|
||||
|
||||
// Empties given block by moving all data into free pages of another block
|
||||
// Strategy:
|
||||
// scan object lookup for object index pages, move to new page in other block
|
||||
// loop:
|
||||
// scan object lookup for object data pages
|
||||
// for first found id, check spix and load corresponding object index page to memory
|
||||
// push object scan lookup entry index
|
||||
// rescan object lookup, find data pages with same id and referenced by same object index
|
||||
// move data page, update object index in memory
|
||||
// when reached end of lookup, store updated object index
|
||||
// pop object scan lookup entry index
|
||||
// repeat loop until end of object lookup
|
||||
//
|
||||
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
int cur_entry = 0;
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
spiffs_gc gc;
|
||||
spiffs_page_ix cur_pix = 0;
|
||||
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
|
||||
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
|
||||
|
||||
memset(&gc, 0, sizeof(spiffs_gc));
|
||||
gc.state = MOVE_OBJ_IX;
|
||||
|
||||
if (fs->free_cursor_block_ix == bix) {
|
||||
// move free cursor to next block
|
||||
fs->free_cursor_block_ix = (bix+1)%fs->block_count;
|
||||
fs->free_cursor_obj_lu_entry = 0;
|
||||
SPIFFS_DBG("gc_clean: move free cursor to block %i\n", fs->free_cursor_block_ix);
|
||||
}
|
||||
|
||||
while (res == SPIFFS_OK && gc.state != FINISHED) {
|
||||
SPIFFS_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry);
|
||||
gc.obj_id_found = 0;
|
||||
|
||||
// scan through lookup pages
|
||||
int obj_lookup_page = cur_entry / entries_per_page;
|
||||
u8_t scan = 1;
|
||||
// check each object lookup page
|
||||
while (scan && res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (scan && res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);
|
||||
|
||||
// act upon object id depending on gc state
|
||||
switch (gc.state) {
|
||||
case MOVE_OBJ_IX:
|
||||
if (obj_id != SPIFFS_OBJ_ID_ERASED && obj_id != SPIFFS_OBJ_ID_FREE && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
// found an index object id
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix new_pix;
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
// move page
|
||||
res = spiffs_page_move(fs, 0, 0, &p_hdr, cur_pix, &new_pix);
|
||||
SPIFFS_DBG("gc_clean: MOVE_OBJIX move objix %04x:%04x page %04x to %04x\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, obj_id, p_hdr.span_ix, new_pix, 0);
|
||||
// move wipes obj_lu, reload it
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// page is deleted but not deleted in lookup, scrap it
|
||||
SPIFFS_DBG("gc_clean: MOVE_OBJIX wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
if (res == SPIFFS_OK) {
|
||||
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
|
||||
}
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
break;
|
||||
case FIND_OBJ_DATA:
|
||||
if (obj_id != SPIFFS_OBJ_ID_ERASED && obj_id != SPIFFS_OBJ_ID_FREE) {
|
||||
SPIFFS_DBG("gc_clean: FIND_DATA state:%i - found obj id %04x\n", gc.state, obj_id);
|
||||
gc.obj_id_found = 1;
|
||||
gc.cur_obj_id = obj_id;
|
||||
scan = 0;
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_DATA:
|
||||
if (obj_id == gc.cur_obj_id) {
|
||||
spiffs_page_header p_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA found data page %04x:%04x @ %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
|
||||
if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
|
||||
} else {
|
||||
spiffs_page_ix new_data_pix;
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
// move page
|
||||
res = spiffs_page_move(fs, 0, 0, &p_hdr, cur_pix, &new_data_pix);
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA move objix %04x:%04x page %04x to %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// move wipes obj_lu, reload it
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// page is deleted but not deleted in lookup, scrap it
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
new_data_pix = SPIFFS_OBJ_ID_FREE;
|
||||
}
|
||||
// update memory representation of object index page with new data page
|
||||
if (gc.cur_objix_spix == 0) {
|
||||
// update object index header page
|
||||
((spiffs_page_ix*)((void*)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
|
||||
} else {
|
||||
// update object index page
|
||||
((spiffs_page_ix*)((void*)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
scan = 0;
|
||||
break;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
|
||||
if (res != SPIFFS_OK) break;
|
||||
|
||||
// state finalization and switch
|
||||
switch (gc.state) {
|
||||
case MOVE_OBJ_IX:
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
cur_entry = 0; // restart entry scan index
|
||||
break;
|
||||
case FIND_OBJ_DATA:
|
||||
if (gc.obj_id_found) {
|
||||
// find out corresponding obj ix page and load it to memory
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix objix_pix;
|
||||
gc.stored_scan_entry_index = cur_entry;
|
||||
cur_entry = 0;
|
||||
gc.state = MOVE_OBJ_DATA;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
|
||||
SPIFFS_DBG("gc_clean: FIND_DATA find objix span_ix:%04x\n", gc.cur_objix_spix);
|
||||
res = spiffs_obj_lu_find_id_and_index(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, &objix_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_DBG("gc_clean: FIND_DATA found object index at page %04x\n", objix_pix);
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id, gc.cur_objix_spix);
|
||||
gc.cur_objix_pix = objix_pix;
|
||||
} else {
|
||||
gc.state = FINISHED;
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_DATA: {
|
||||
// store modified objix (hdr) page
|
||||
spiffs_page_ix new_objix_pix;
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
cur_entry = gc.stored_scan_entry_index;
|
||||
if (gc.cur_objix_spix == 0) {
|
||||
// store object index header page
|
||||
res = spiffs_object_update_index_hdr(fs, 0, gc.cur_objix_pix, fs->work, 0, 0, &new_objix_pix);
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, %04x:%04x\n", new_objix_pix, 0);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// store object index page
|
||||
spiffs_page_ix new_objix_pix;
|
||||
res = spiffs_page_move(fs, 0, fs->work, 0, gc.cur_objix_pix, &new_objix_pix);
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA store modified objix page, %04x:%04x\n", new_objix_pix, objix->p_hdr.span_ix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cur_entry = 0;
|
||||
break;
|
||||
}
|
||||
SPIFFS_DBG("gc_clean: state-> %i\n", gc.state);
|
||||
} // while state != FINISHED
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
+111
-7
@@ -10,7 +10,89 @@
|
||||
|
||||
static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh);
|
||||
|
||||
|
||||
s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
|
||||
u8_t *fd_space, u32_t fd_space_size,
|
||||
void *cache, u32_t cache_size) {
|
||||
SPIFFS_LOCK(fs);
|
||||
memset(fs, 0, sizeof(spiffs));
|
||||
memcpy(&fs->cfg, config, sizeof(spiffs_config));
|
||||
fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
fs->work = &work[0];
|
||||
fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)];
|
||||
memset(fd_space, 0, fd_space_size);
|
||||
// align fd_space pointer to pointer size byte boundary, below is safe
|
||||
u8_t ptr_size = sizeof(void*);
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
|
||||
u8_t addr_lsb = ((u8_t)fd_space) & (ptr_size-1);
|
||||
#pragma GCC diagnostic pop
|
||||
if (addr_lsb) {
|
||||
fd_space += (ptr_size-addr_lsb);
|
||||
fd_space_size -= (ptr_size-addr_lsb);
|
||||
}
|
||||
fs->fd_space = fd_space;
|
||||
fs->fd_count = (fd_space_size/sizeof(spiffs_fd));
|
||||
|
||||
// align cache pointer to 4 byte boundary, below is safe
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
|
||||
addr_lsb = ((u8_t)cache) & (ptr_size-1);
|
||||
#pragma GCC diagnostic pop
|
||||
if (addr_lsb) {
|
||||
cache += (ptr_size-addr_lsb);
|
||||
cache_size -= (ptr_size-addr_lsb);
|
||||
}
|
||||
if (cache_size & (ptr_size-1)) {
|
||||
cache_size -= (cache_size & (ptr_size-1));
|
||||
}
|
||||
#if SPIFFS_CACHE
|
||||
fs->cache = cache;
|
||||
fs->cache_size = cache_size;
|
||||
spiffs_cache_init(fs);
|
||||
#endif
|
||||
|
||||
s32_t res = spiffs_obj_lu_scan(fs);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
SPIFFS_DBG("page index byte len: %i\n", SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
SPIFFS_DBG("object lookup pages: %i\n", SPIFFS_OBJ_LOOKUP_PAGES(fs));
|
||||
SPIFFS_DBG("page pages per block: %i\n", SPIFFS_PAGES_PER_BLOCK(fs));
|
||||
SPIFFS_DBG("page header length: %i\n", sizeof(spiffs_page_header));
|
||||
SPIFFS_DBG("object header index entries: %i\n", SPIFFS_OBJ_HDR_IX_LEN(fs));
|
||||
SPIFFS_DBG("object index entries: %i\n", SPIFFS_OBJ_IX_LEN(fs));
|
||||
SPIFFS_DBG("available file descriptors: %i\n", fs->fd_count);
|
||||
SPIFFS_DBG("free blocks: %i\n", fs->free_blocks);
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SPIFFS_unmount(spiffs *fs) {
|
||||
if (!SPIFFS_CHECK_MOUNT(fs)) return;
|
||||
SPIFFS_LOCK(fs);
|
||||
int i;
|
||||
spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
|
||||
for (i = 0; i < fs->fd_count; i++) {
|
||||
spiffs_fd *cur_fd = &fds[i];
|
||||
if (cur_fd->file_nbr != 0) {
|
||||
#if SPIFFS_CACHE
|
||||
(void)spiffs_fflush_cache(fs, cur_fd->file_nbr);
|
||||
#endif
|
||||
spiffs_fd_return(fs, cur_fd->file_nbr);
|
||||
}
|
||||
}
|
||||
fs->block_count = 0;
|
||||
SPIFFS_UNLOCK(fs);
|
||||
}
|
||||
|
||||
s32_t SPIFFS_errno(spiffs *fs) {
|
||||
return fs->errno;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_attr attr) {
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
spiffs_obj_id obj_id;
|
||||
s32_t res;
|
||||
@@ -24,6 +106,7 @@ s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_attr attr) {
|
||||
}
|
||||
|
||||
spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_attr attr, spiffs_mode mode) {
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
@@ -80,6 +163,7 @@ spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_attr attr, spiffs_m
|
||||
}
|
||||
|
||||
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
@@ -138,6 +222,7 @@ static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offs
|
||||
}
|
||||
|
||||
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
@@ -186,7 +271,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
|
||||
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
|
||||
res = spiffs_hydro_write(fs, fd,
|
||||
get_cache_page(fs, get_cache(fs), fd->cache_page->ix), fd->cache_page->offset, fd->cache_page->size);
|
||||
spiffs_cache_fh_release(fs, fd->cache_page);
|
||||
spiffs_cache_fd_release(fs, fd->cache_page);
|
||||
} else {
|
||||
// writing within cache
|
||||
alloc_cpage = 0;
|
||||
@@ -230,7 +315,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
|
||||
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
|
||||
res = spiffs_hydro_write(fs, fd,
|
||||
get_cache_page(fs, get_cache(fs), fd->cache_page->ix), fd->cache_page->offset, fd->cache_page->size);
|
||||
spiffs_cache_fh_release(fs, fd->cache_page);
|
||||
spiffs_cache_fd_release(fs, fd->cache_page);
|
||||
res = spiffs_hydro_write(fs, fd, buf, offset, len);
|
||||
SPIFFS_API_CHECK_RES(fs, res);
|
||||
}
|
||||
@@ -248,6 +333,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
|
||||
}
|
||||
|
||||
s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
@@ -291,6 +377,7 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
|
||||
}
|
||||
|
||||
s32_t SPIFFS_remove(spiffs *fs, const char *path) {
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd fd;
|
||||
@@ -310,6 +397,7 @@ s32_t SPIFFS_remove(spiffs *fs, const char *path) {
|
||||
}
|
||||
|
||||
s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) {
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
@@ -318,7 +406,7 @@ s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) {
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
spiffs_cache_fh_release(fs, fd->cache_page);
|
||||
spiffs_cache_fd_release(fs, fd->cache_page);
|
||||
#endif
|
||||
|
||||
res = spiffs_object_truncate(fd, 0, 1);
|
||||
@@ -331,6 +419,7 @@ s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) {
|
||||
}
|
||||
|
||||
s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) {
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_fd *fd;
|
||||
@@ -379,7 +468,7 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) {
|
||||
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
|
||||
res = spiffs_hydro_write(fs, fd,
|
||||
get_cache_page(fs, get_cache(fs), fd->cache_page->ix), fd->cache_page->offset, fd->cache_page->size);
|
||||
spiffs_cache_fh_release(fs, fd->cache_page);
|
||||
spiffs_cache_fd_release(fs, fd->cache_page);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -388,6 +477,7 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) {
|
||||
}
|
||||
|
||||
s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) {
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
s32_t res = SPIFFS_OK;
|
||||
#if SPIFFS_CACHE_WR
|
||||
SPIFFS_LOCK(fs);
|
||||
@@ -400,6 +490,10 @@ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) {
|
||||
}
|
||||
|
||||
void SPIFFS_close(spiffs *fs, spiffs_file fh) {
|
||||
if (!SPIFFS_CHECK_MOUNT(fs)) {
|
||||
fs->errno = SPIFFS_ERR_NOT_MOUNTED;
|
||||
return;
|
||||
}
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
@@ -411,6 +505,10 @@ void SPIFFS_close(spiffs *fs, spiffs_file fh) {
|
||||
}
|
||||
|
||||
spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) {
|
||||
if (!SPIFFS_CHECK_MOUNT(fs)) {
|
||||
fs->errno = SPIFFS_ERR_NOT_MOUNTED;
|
||||
return 0;
|
||||
}
|
||||
d->fs = fs;
|
||||
d->block = 0;
|
||||
d->entry = 0;
|
||||
@@ -428,7 +526,7 @@ static s32_t spiffs_read_dir_v(
|
||||
spiffs_page_object_ix_header objix_hdr;
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_ERASED ||
|
||||
(obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) {
|
||||
return SPIFFS_COUNTINUE;
|
||||
return SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
|
||||
spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry);
|
||||
@@ -446,10 +544,14 @@ static s32_t spiffs_read_dir_v(
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
return SPIFFS_COUNTINUE;
|
||||
return SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
|
||||
struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
|
||||
if (!SPIFFS_CHECK_MOUNT(d->fs)) {
|
||||
d->fs->errno = SPIFFS_ERR_NOT_MOUNTED;
|
||||
return 0;
|
||||
}
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
spiffs_block_ix bix;
|
||||
@@ -479,11 +581,13 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
|
||||
}
|
||||
|
||||
s32_t SPIFFS_closedir(spiffs_DIR *d) {
|
||||
return SPIFFS_OK;
|
||||
SPIFFS_API_CHECK_MOUNT(d->fs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_check(spiffs *fs) {
|
||||
s32_t res;
|
||||
SPIFFS_API_CHECK_MOUNT(fs);
|
||||
SPIFFS_LOCK(fs);
|
||||
|
||||
res = spiffs_area_check(fs, 0);
|
||||
|
||||
+30
-588
@@ -47,68 +47,6 @@ static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix p
|
||||
return res;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_init(spiffs *fs, spiffs_config *config, u8_t *work,
|
||||
u8_t *fd_space, u32_t fd_space_size,
|
||||
void *cache, u32_t cache_size) {
|
||||
SPIFFS_LOCK(fs);
|
||||
memset(fs, 0, sizeof(spiffs));
|
||||
memcpy(&fs->cfg, config, sizeof(spiffs_config));
|
||||
fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
fs->work = &work[0];
|
||||
fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)];
|
||||
memset(fd_space, 0, fd_space_size);
|
||||
// align fd_space pointer to pointer size byte boundary, below is safe
|
||||
u8_t ptr_size = sizeof(void*);
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
|
||||
u8_t addr_lsb = ((u8_t)fd_space) & (ptr_size-1);
|
||||
#pragma GCC diagnostic pop
|
||||
if (addr_lsb) {
|
||||
fd_space += (ptr_size-addr_lsb);
|
||||
fd_space_size -= (ptr_size-addr_lsb);
|
||||
}
|
||||
fs->fd_space = fd_space;
|
||||
fs->fd_count = (fd_space_size/sizeof(spiffs_fd));
|
||||
|
||||
// align cache pointer to 4 byte boundary, below is safe
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
|
||||
addr_lsb = ((u8_t)cache) & (ptr_size-1);
|
||||
#pragma GCC diagnostic pop
|
||||
if (addr_lsb) {
|
||||
cache += (ptr_size-addr_lsb);
|
||||
cache_size -= (ptr_size-addr_lsb);
|
||||
}
|
||||
if (cache_size & (ptr_size-1)) {
|
||||
cache_size -= (cache_size & (ptr_size-1));
|
||||
}
|
||||
#if SPIFFS_CACHE
|
||||
fs->cache = cache;
|
||||
fs->cache_size = cache_size;
|
||||
spiffs_cache_init(fs);
|
||||
#endif
|
||||
|
||||
s32_t res = spiffs_obj_lu_scan(fs);
|
||||
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
|
||||
|
||||
SPIFFS_DBG("page index byte len: %i\n", SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
SPIFFS_DBG("object lookup pages: %i\n", SPIFFS_OBJ_LOOKUP_PAGES(fs));
|
||||
SPIFFS_DBG("page pages per block: %i\n", SPIFFS_PAGES_PER_BLOCK(fs));
|
||||
SPIFFS_DBG("page header length: %i\n", sizeof(spiffs_page_header));
|
||||
SPIFFS_DBG("object header index entries: %i\n", SPIFFS_OBJ_HDR_IX_LEN(fs));
|
||||
SPIFFS_DBG("object index entries: %i\n", SPIFFS_OBJ_IX_LEN(fs));
|
||||
SPIFFS_DBG("available file descriptors: %i\n", fs->fd_count);
|
||||
SPIFFS_DBG("free blocks: %i\n", fs->free_blocks);
|
||||
|
||||
SPIFFS_UNLOCK(fs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32_t SPIFFS_errno(spiffs *fs) {
|
||||
return fs->errno;
|
||||
}
|
||||
|
||||
#if !SPIFFS_CACHE
|
||||
|
||||
s32_t spiffs_phys_rd(
|
||||
@@ -129,7 +67,7 @@ s32_t spiffs_phys_wr(
|
||||
|
||||
#endif
|
||||
|
||||
s32_t spiffs_phys_cpy(
|
||||
static s32_t spiffs_phys_cpy(
|
||||
spiffs *fs,
|
||||
spiffs_file fh,
|
||||
u32_t dst,
|
||||
@@ -207,7 +145,7 @@ s32_t spiffs_obj_lu_find_entry_visitor(
|
||||
if (index_entry) *index_entry = cur_entry;
|
||||
if (v) {
|
||||
res = v(fs, obj_lu_buf[cur_entry-entry_offset], cur_block, cur_entry, user_data, user_p);
|
||||
if (res == SPIFFS_COUNTINUE) {
|
||||
if (res == SPIFFS_VIS_COUNTINUE) {
|
||||
res = SPIFFS_OK;
|
||||
cur_entry++;
|
||||
entry_count--;
|
||||
@@ -215,8 +153,9 @@ s32_t spiffs_obj_lu_find_entry_visitor(
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
entry_count--;
|
||||
cur_entry++;
|
||||
@@ -228,7 +167,7 @@ s32_t spiffs_obj_lu_find_entry_visitor(
|
||||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
if (cur_block >= fs->block_count) {
|
||||
if (flags & SPIFFS_VIS_NO_WRAP) {
|
||||
return SPIFFS_ERR_NOT_FOUND;
|
||||
return SPIFFS_VIS_END;
|
||||
} else {
|
||||
// block wrap
|
||||
cur_block = 0;
|
||||
@@ -239,7 +178,7 @@ s32_t spiffs_obj_lu_find_entry_visitor(
|
||||
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
return SPIFFS_ERR_NOT_FOUND;
|
||||
return SPIFFS_VIS_END;
|
||||
}
|
||||
|
||||
|
||||
@@ -261,7 +200,7 @@ static s32_t spiffs_obj_lu_scan_v(
|
||||
fs->stats_p_allocated++;
|
||||
}
|
||||
|
||||
return SPIFFS_COUNTINUE;
|
||||
return SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
|
||||
// Scans thru all obj lu and counts free, deleted and used pages
|
||||
@@ -286,6 +225,10 @@ s32_t spiffs_obj_lu_scan(
|
||||
&bix,
|
||||
&entry);
|
||||
|
||||
if (res == SPIFFS_VIS_END) {
|
||||
res = SPIFFS_OK;
|
||||
}
|
||||
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
return res;
|
||||
@@ -309,10 +252,10 @@ s32_t spiffs_obj_lu_find_free(
|
||||
fs->free_blocks--;
|
||||
}
|
||||
}
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) {
|
||||
if (res == SPIFFS_VIS_END) {
|
||||
SPIFFS_DBG("fs full\n");
|
||||
}
|
||||
return res == SPIFFS_ERR_NOT_FOUND ? SPIFFS_ERR_FULL : res;
|
||||
return res == SPIFFS_VIS_END ? SPIFFS_ERR_FULL : res;
|
||||
}
|
||||
|
||||
// Find object lookup entry containing given id
|
||||
@@ -326,6 +269,9 @@ s32_t spiffs_obj_lu_find_id(
|
||||
int *index_entry) {
|
||||
s32_t res = spiffs_obj_lu_find_entry_visitor(
|
||||
fs, starting_block, starting_index_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, index_entry);
|
||||
if (res == SPIFFS_VIS_END) {
|
||||
res = SPIFFS_ERR_NOT_FOUND;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -346,7 +292,7 @@ static s32_t spiffs_obj_lu_find_id_and_index_v(
|
||||
if (ph.span_ix == (spiffs_span_ix)user_data && ((ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) == SPIFFS_PH_FLAG_DELET)) {
|
||||
return SPIFFS_OK;
|
||||
} else {
|
||||
return SPIFFS_COUNTINUE;
|
||||
return SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,6 +318,10 @@ s32_t spiffs_obj_lu_find_id_and_index(
|
||||
&bix,
|
||||
&entry);
|
||||
|
||||
if (res == SPIFFS_VIS_END) {
|
||||
res = SPIFFS_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
if (pix) {
|
||||
@@ -1154,7 +1104,7 @@ static s32_t spiffs_object_find_object_index_header_by_name_v(
|
||||
spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry);
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_ERASED ||
|
||||
(obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) {
|
||||
return SPIFFS_COUNTINUE;
|
||||
return SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr);
|
||||
@@ -1167,7 +1117,7 @@ static s32_t spiffs_object_find_object_index_header_by_name_v(
|
||||
}
|
||||
}
|
||||
|
||||
return SPIFFS_COUNTINUE;
|
||||
return SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
|
||||
// Finds object index header page by name
|
||||
@@ -1190,6 +1140,9 @@ s32_t spiffs_object_find_object_index_header_by_name(
|
||||
&bix,
|
||||
&entry);
|
||||
|
||||
if (res == SPIFFS_VIS_END) {
|
||||
res = SPIFFS_ERR_NOT_FOUND;
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
if (pix) {
|
||||
@@ -1471,423 +1424,6 @@ s32_t spiffs_object_read(
|
||||
return res;
|
||||
}
|
||||
|
||||
s32_t spiffs_gc_check(
|
||||
spiffs *fs,
|
||||
u32_t len) {
|
||||
s32_t res;
|
||||
u32_t free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
int tries = 0;
|
||||
|
||||
if (fs->free_blocks > 2 &&
|
||||
len < free_pages * SPIFFS_DATA_PAGE_SIZE(fs)) {
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
do {
|
||||
SPIFFS_GC_DBG("\ngc_check #i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n",
|
||||
tries,
|
||||
fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
|
||||
len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs));
|
||||
|
||||
spiffs_block_ix *cands;
|
||||
int count;
|
||||
spiffs_block_ix cand;
|
||||
res = spiffs_gc_find_candidate(fs, &cands, &count);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (count == 0) {
|
||||
SPIFFS_GC_DBG("gc_check: no candidates, return\n");
|
||||
return res;
|
||||
}
|
||||
#if SPIFFS_GC_STATS
|
||||
fs->stats_gc_runs++;
|
||||
#endif
|
||||
cand = cands[0];
|
||||
res = spiffs_gc_clean(fs, cand);
|
||||
SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
res = spiffs_gc_erase_page_stats(fs, cand);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, cand);
|
||||
s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
|
||||
// here we ignore res, just try erasing the block
|
||||
SPIFFS_GC_DBG("gc_check: erase block %i\n", cand);
|
||||
while (size > 0) {
|
||||
SPIFFS_DBG("gc_check: erase %08x:%08x\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
|
||||
res = fs->cfg.hal_erase_f(addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
|
||||
addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs);
|
||||
size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs);
|
||||
}
|
||||
fs->free_blocks++;
|
||||
free_pages =
|
||||
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count
|
||||
- fs->stats_p_allocated - fs->stats_p_deleted;
|
||||
#if SPIFFS_CACHE
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) {
|
||||
spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, cand) + i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} while (tries++ < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
|
||||
len > free_pages*SPIFFS_DATA_PAGE_SIZE(fs)));
|
||||
SPIFFS_GC_DBG("gc_check: finished\n");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
s32_t spiffs_gc_erase_page_stats(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix bix) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
int obj_lookup_page = 0;
|
||||
u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
int cur_entry = 0;
|
||||
u32_t dele = 0;
|
||||
u32_t allo = 0;
|
||||
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_ERASED) {
|
||||
dele++;
|
||||
} else {
|
||||
allo++;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
SPIFFS_GC_DBG("gc_check: wipe pallo:%i pdele:%i\n", allo, dele);
|
||||
fs->stats_p_allocated -= allo;
|
||||
fs->stats_p_deleted -= dele;
|
||||
return res;
|
||||
}
|
||||
|
||||
s32_t spiffs_gc_find_candidate(
|
||||
spiffs *fs,
|
||||
spiffs_block_ix **block_candidates,
|
||||
int *candidate_count) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t blocks = fs->block_count;
|
||||
spiffs_block_ix cur_block = 0;
|
||||
u32_t cur_block_addr = SPIFFS_CFG_PHYS_ADDR(fs);
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
int cur_entry = 0;
|
||||
|
||||
// using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(int)score
|
||||
int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(int)));
|
||||
*candidate_count = 0;
|
||||
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
|
||||
// divide up work area into block indices and scores
|
||||
// todo alignment?
|
||||
spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
|
||||
int *cand_scores = (int *)(fs->work + max_candidates * sizeof(spiffs_block_ix));
|
||||
|
||||
*block_candidates = cand_blocks;
|
||||
|
||||
u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
|
||||
// check each block
|
||||
while (res == SPIFFS_OK && blocks--) {
|
||||
u16_t deleted_pages_in_block = 0;
|
||||
u16_t used_pages_in_block = 0;
|
||||
|
||||
int obj_lookup_page = 0;
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
// when a free entry is encountered, scan logic ensures that all following entries are free also
|
||||
break;
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_ERASED) {
|
||||
deleted_pages_in_block++;
|
||||
} else {
|
||||
used_pages_in_block++;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
|
||||
// calculate score and insert into candidate table
|
||||
// stoneage sort, but probably not so many blocks
|
||||
if (res == SPIFFS_OK && deleted_pages_in_block > 0) {
|
||||
int score = deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + used_pages_in_block * SPIFFS_GC_HEUR_W_USED;
|
||||
int cand_ix = 0;
|
||||
SPIFFS_DBG("gc_check: bix:%i del:%i use:%i score:%i\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
|
||||
while (cand_ix < max_candidates) {
|
||||
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
|
||||
cand_blocks[cand_ix] = cur_block;
|
||||
cand_scores[cand_ix] = score;
|
||||
break;
|
||||
} else if (cand_scores[cand_ix] < score) {
|
||||
int reorder_cand_ix = max_candidates - 2;
|
||||
while (reorder_cand_ix >= cand_ix) {
|
||||
cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix];
|
||||
cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix];
|
||||
reorder_cand_ix--;
|
||||
}
|
||||
cand_blocks[cand_ix] = cur_block;
|
||||
cand_scores[cand_ix] = score;
|
||||
break;
|
||||
}
|
||||
cand_ix++;
|
||||
}
|
||||
(*candidate_count)++;
|
||||
}
|
||||
|
||||
cur_entry = 0;
|
||||
cur_block++;
|
||||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
} // per block
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
MOVE_OBJ_IX,
|
||||
FIND_OBJ_DATA,
|
||||
MOVE_OBJ_DATA,
|
||||
FINISHED
|
||||
} spiffs_gc_clean_state;
|
||||
|
||||
typedef struct {
|
||||
spiffs_gc_clean_state state;
|
||||
spiffs_obj_id cur_obj_id;
|
||||
spiffs_span_ix cur_objix_spix;
|
||||
spiffs_page_ix cur_objix_pix;
|
||||
int stored_scan_entry_index;
|
||||
u8_t obj_id_found;
|
||||
} spiffs_gc;
|
||||
|
||||
// Empties given block by moving all data into free pages of another block
|
||||
// Strategy:
|
||||
// scan object lookup for object index pages, move to new page in other block
|
||||
// loop:
|
||||
// scan object lookup for object data pages
|
||||
// for first found id, check spix and load corresponding object index page to memory
|
||||
// push object scan lookup entry index
|
||||
// rescan object lookup, find data pages with same id and referenced by same object index
|
||||
// move data page, update object index in memory
|
||||
// when reached end of lookup, store updated object index
|
||||
// pop object scan lookup entry index
|
||||
// repeat loop until end of object lookup
|
||||
//
|
||||
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
int cur_entry = 0;
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
spiffs_gc gc;
|
||||
spiffs_page_ix cur_pix = 0;
|
||||
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
|
||||
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
|
||||
|
||||
memset(&gc, 0, sizeof(spiffs_gc));
|
||||
gc.state = MOVE_OBJ_IX;
|
||||
|
||||
if (fs->free_cursor_block_ix == bix) {
|
||||
// move free cursor to next block
|
||||
fs->free_cursor_block_ix = (bix+1)%fs->block_count;
|
||||
fs->free_cursor_obj_lu_entry = 0;
|
||||
SPIFFS_DBG("gc_clean: move free cursor to block %i\n", fs->free_cursor_block_ix);
|
||||
}
|
||||
|
||||
while (res == SPIFFS_OK && gc.state != FINISHED) {
|
||||
SPIFFS_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry);
|
||||
gc.obj_id_found = 0;
|
||||
|
||||
// scan through lookup pages
|
||||
int obj_lookup_page = cur_entry / entries_per_page;
|
||||
u8_t scan = 1;
|
||||
// check each object lookup page
|
||||
while (scan && res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (scan && res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && cur_entry < SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);
|
||||
|
||||
// act upon object id depending on gc state
|
||||
switch (gc.state) {
|
||||
case MOVE_OBJ_IX:
|
||||
if (obj_id != SPIFFS_OBJ_ID_ERASED && obj_id != SPIFFS_OBJ_ID_FREE && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||
// found an index object id
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix new_pix;
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
// move page
|
||||
res = spiffs_page_move(fs, 0, 0, &p_hdr, cur_pix, &new_pix);
|
||||
SPIFFS_DBG("gc_clean: MOVE_OBJIX move objix %04x:%04x page %04x to %04x\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, obj_id, p_hdr.span_ix, new_pix, 0);
|
||||
// move wipes obj_lu, reload it
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// page is deleted but not deleted in lookup, scrap it
|
||||
SPIFFS_DBG("gc_clean: MOVE_OBJIX wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
if (res == SPIFFS_OK) {
|
||||
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
|
||||
}
|
||||
}
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
break;
|
||||
case FIND_OBJ_DATA:
|
||||
if (obj_id != SPIFFS_OBJ_ID_ERASED && obj_id != SPIFFS_OBJ_ID_FREE) {
|
||||
SPIFFS_DBG("gc_clean: FIND_DATA state:%i - found obj id %04x\n", gc.state, obj_id);
|
||||
gc.obj_id_found = 1;
|
||||
gc.cur_obj_id = obj_id;
|
||||
scan = 0;
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_DATA:
|
||||
if (obj_id == gc.cur_obj_id) {
|
||||
spiffs_page_header p_hdr;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA found data page %04x:%04x @ %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
|
||||
if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
|
||||
} else {
|
||||
spiffs_page_ix new_data_pix;
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
// move page
|
||||
res = spiffs_page_move(fs, 0, 0, &p_hdr, cur_pix, &new_data_pix);
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA move objix %04x:%04x page %04x to %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// move wipes obj_lu, reload it
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
|
||||
SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// page is deleted but not deleted in lookup, scrap it
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
|
||||
res = spiffs_page_delete(fs, cur_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
new_data_pix = SPIFFS_OBJ_ID_FREE;
|
||||
}
|
||||
// update memory representation of object index page with new data page
|
||||
if (gc.cur_objix_spix == 0) {
|
||||
// update object index header page
|
||||
((spiffs_page_ix*)((void*)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
|
||||
} else {
|
||||
// update object index page
|
||||
((spiffs_page_ix*)((void*)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
scan = 0;
|
||||
break;
|
||||
}
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
|
||||
if (res != SPIFFS_OK) break;
|
||||
|
||||
// state finalization and switch
|
||||
switch (gc.state) {
|
||||
case MOVE_OBJ_IX:
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
cur_entry = 0; // restart entry scan index
|
||||
break;
|
||||
case FIND_OBJ_DATA:
|
||||
if (gc.obj_id_found) {
|
||||
// find out corresponding obj ix page and load it to memory
|
||||
spiffs_page_header p_hdr;
|
||||
spiffs_page_ix objix_pix;
|
||||
gc.stored_scan_entry_index = cur_entry;
|
||||
cur_entry = 0;
|
||||
gc.state = MOVE_OBJ_DATA;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
|
||||
SPIFFS_DBG("gc_clean: FIND_DATA find objix span_ix:%04x\n", gc.cur_objix_spix);
|
||||
res = spiffs_obj_lu_find_id_and_index(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, &objix_pix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_DBG("gc_clean: FIND_DATA found object index at page %04x\n", objix_pix);
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id, gc.cur_objix_spix);
|
||||
gc.cur_objix_pix = objix_pix;
|
||||
} else {
|
||||
gc.state = FINISHED;
|
||||
}
|
||||
break;
|
||||
case MOVE_OBJ_DATA: {
|
||||
// store modified objix (hdr) page
|
||||
spiffs_page_ix new_objix_pix;
|
||||
gc.state = FIND_OBJ_DATA;
|
||||
cur_entry = gc.stored_scan_entry_index;
|
||||
if (gc.cur_objix_spix == 0) {
|
||||
// store object index header page
|
||||
res = spiffs_object_update_index_hdr(fs, 0, gc.cur_objix_pix, fs->work, 0, 0, &new_objix_pix);
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, %04x:%04x\n", new_objix_pix, 0);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
} else {
|
||||
// store object index page
|
||||
spiffs_page_ix new_objix_pix;
|
||||
res = spiffs_page_move(fs, 0, fs->work, 0, gc.cur_objix_pix, &new_objix_pix);
|
||||
SPIFFS_DBG("gc_clean: MOVE_DATA store modified objix page, %04x:%04x\n", new_objix_pix, objix->p_hdr.span_ix);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cur_entry = 0;
|
||||
break;
|
||||
}
|
||||
SPIFFS_DBG("gc_clean: state-> %i\n", gc.state);
|
||||
} // while state != FINISHED
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
spiffs_obj_id min_obj_id;
|
||||
spiffs_obj_id max_obj_id;
|
||||
@@ -1905,7 +1441,7 @@ static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id i
|
||||
fs->work[byte_ix] |= (1<<bit_ix);
|
||||
}
|
||||
}
|
||||
return SPIFFS_COUNTINUE;
|
||||
return SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
|
||||
static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
|
||||
@@ -1929,7 +1465,7 @@ static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id
|
||||
}
|
||||
}
|
||||
}
|
||||
return SPIFFS_COUNTINUE;
|
||||
return SPIFFS_VIS_COUNTINUE;
|
||||
}
|
||||
|
||||
// Scans thru all object lookup for object index header pages. If total possible number of
|
||||
@@ -1955,7 +1491,7 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id) {
|
||||
|
||||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, state.min_obj_id, 0, 0, 0);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) res = SPIFFS_OK;
|
||||
if (res == SPIFFS_VIS_END) res = SPIFFS_OK;
|
||||
SPIFFS_CHECK_RES(res);
|
||||
// traverse bitmask until found free obj_id
|
||||
for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) {
|
||||
@@ -2019,7 +1555,7 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id) {
|
||||
|
||||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, 0, &state, 0, 0);
|
||||
if (res == SPIFFS_ERR_NOT_FOUND) res = SPIFFS_OK;
|
||||
if (res == SPIFFS_VIS_END) res = SPIFFS_OK;
|
||||
SPIFFS_CHECK_RES(res);
|
||||
}
|
||||
}
|
||||
@@ -2065,97 +1601,3 @@ s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) {
|
||||
}
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
// 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 checked
|
||||
s32_t spiffs_area_check(spiffs *fs, u8_t check_all_objects) {
|
||||
s32_t res = SPIFFS_OK;
|
||||
s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs);
|
||||
spiffs_block_ix cur_block = 0;
|
||||
u32_t cur_block_addr = SPIFFS_BLOCK_TO_PADDR(fs, 0);
|
||||
|
||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
||||
int cur_entry = 0;
|
||||
spiffs_page_ix cur_pix;
|
||||
u32_t entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
||||
|
||||
// check each block
|
||||
while (res == SPIFFS_OK && entry_count > 0) {
|
||||
int obj_lookup_page = cur_entry / entries_per_page;
|
||||
// check each object lookup page
|
||||
while (res == SPIFFS_OK && obj_lookup_page < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
|
||||
int entry_offset = obj_lookup_page * entries_per_page;
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||
// check each entry
|
||||
while (res == SPIFFS_OK &&
|
||||
cur_entry - entry_offset < entries_per_page && // for non-last obj lookup pages
|
||||
cur_entry < SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) // for last obj lookup page
|
||||
{
|
||||
spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
|
||||
spiffs_page_header p_hdr;
|
||||
cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
|
||||
|
||||
// load header
|
||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
||||
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
if (obj_id == SPIFFS_OBJ_ID_ERASED) {
|
||||
// TODO
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||
print("WARNING: pix %04x deleted in lu but not on page\n", cur_pix);
|
||||
}
|
||||
} else if (obj_id == SPIFFS_OBJ_ID_FREE) {
|
||||
// TODO
|
||||
if (p_hdr.flags != 0xff) {
|
||||
print("WARNING: pix %04x free in lu but not on page\n", cur_pix);
|
||||
}
|
||||
} else {
|
||||
// TODO
|
||||
if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) == 0) {
|
||||
print("WARNING: pix %04x busy in lu but free on page\n", cur_pix);
|
||||
}
|
||||
if ((p_hdr.flags & SPIFFS_PH_FLAG_FINAL)) {
|
||||
print("WARNING: pix %04x busy but not final\n", cur_pix);
|
||||
}
|
||||
if (p_hdr.obj_id != obj_id) {
|
||||
print("WARNING: pix %04x has id %04x in lu but %04x on page\n", cur_pix, obj_id, p_hdr.obj_id);
|
||||
}
|
||||
if (obj_id & SPIFFS_OBJ_ID_IX_FLAG) {
|
||||
// index page
|
||||
if (p_hdr.flags & SPIFFS_PH_FLAG_INDEX) {
|
||||
print("WARNING: pix %04x marked as index in lu but as data on page\n", cur_pix);
|
||||
}
|
||||
} else {
|
||||
// data page
|
||||
if ((p_hdr.flags & SPIFFS_PH_FLAG_INDEX) == 0) {
|
||||
print("WARNING: pix %04x as data in lu but as index on page\n", cur_pix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entry_count--;
|
||||
cur_entry++;
|
||||
} // per entry
|
||||
obj_lookup_page++;
|
||||
} // per object lookup page
|
||||
cur_entry = 0;
|
||||
cur_block++;
|
||||
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
|
||||
} // per block
|
||||
|
||||
SPIFFS_CHECK_RES(res);
|
||||
|
||||
return SPIFFS_OK;
|
||||
}
|
||||
|
||||
// Scans all object look up for consistency within given object id. When an index is found, it is loaded
|
||||
// and checked for validity against each referenced page. When an object index header is found, size is
|
||||
// checked.
|
||||
// When an index points to a bad page (deleted, other id, bad span index, etc), the whole area is searched
|
||||
// for correct page, followed by an index update. If no such page is found, a free page is allocated and
|
||||
// referenced instead meaning there will be a "hole" with 0xff in the file.
|
||||
s32_t spiffs_object_check(spiffs *fs, spiffs_obj_id objid) {
|
||||
|
||||
}
|
||||
|
||||
+47
-7
@@ -110,6 +110,9 @@
|
||||
#ifndef SPIFFS_NUCLEUS_H_
|
||||
#define SPIFFS_NUCLEUS_H_
|
||||
|
||||
#define SPIFFS_VIS_COUNTINUE -11000
|
||||
#define SPIFFS_VIS_END -11001
|
||||
|
||||
#define SPIFFS_EV_IX_UPD 0
|
||||
#define SPIFFS_EV_IX_NEW 1
|
||||
#define SPIFFS_EV_IX_DEL 2
|
||||
@@ -217,12 +220,20 @@
|
||||
// if 0, this is an index page, else a data page
|
||||
#define SPIFFS_PH_FLAG_INDEX (1<<3)
|
||||
|
||||
#define SPIFFS_CHECK_MOUNT(fs) \
|
||||
((fs)->block_count > 0)
|
||||
|
||||
#define SPIFFS_CHECK_RES(res) \
|
||||
do { \
|
||||
if ((res) < SPIFFS_OK) return (res); \
|
||||
} while (0);
|
||||
|
||||
#define SPIFFS_API_CHECK_MOUNT(fs) \
|
||||
if (!SPIFFS_CHECK_MOUNT((fs))) { \
|
||||
(fs)->errno = SPIFFS_ERR_NOT_MOUNTED; \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
#define SPIFFS_API_CHECK_RES(fs, res) \
|
||||
if ((res) < SPIFFS_OK) { \
|
||||
(fs)->errno = (res); \
|
||||
@@ -236,6 +247,22 @@
|
||||
return -1; \
|
||||
}
|
||||
|
||||
|
||||
#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \
|
||||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH;\
|
||||
if (((ph).obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != ((objid) & ~SPIFFS_OBJ_ID_IX_FLAG)) return SPIFFS_ERR_INDEX_WRONG_ID;
|
||||
|
||||
#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
|
||||
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \
|
||||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH; \
|
||||
if (((ph).obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != ((objid) & ~SPIFFS_OBJ_ID_IX_FLAG)) return SPIFFS_ERR_DATA_WRONG_ID;
|
||||
|
||||
|
||||
#define SPIFFS_VIS_CHECK_ID (1<<0)
|
||||
#define SPIFFS_VIS_NO_WRAP (1<<1)
|
||||
|
||||
@@ -390,14 +417,14 @@ s32_t spiffs_phys_wr(
|
||||
u32_t addr,
|
||||
u32_t len,
|
||||
u8_t *src);
|
||||
|
||||
#if 0
|
||||
s32_t spiffs_phys_cpy(
|
||||
spiffs *fs,
|
||||
spiffs_file fh,
|
||||
u32_t dst,
|
||||
u32_t src,
|
||||
u32_t len);
|
||||
|
||||
#endif
|
||||
s32_t spiffs_phys_count_free_blocks(
|
||||
spiffs *fs);
|
||||
|
||||
@@ -570,12 +597,25 @@ s32_t spiffs_fd_get(
|
||||
spiffs_fd **fd);
|
||||
|
||||
#if SPIFFS_CACHE
|
||||
void spiffs_cache_init(spiffs *fs);
|
||||
void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix);
|
||||
void spiffs_cache_init(
|
||||
spiffs *fs);
|
||||
|
||||
void spiffs_cache_drop_page(
|
||||
spiffs *fs,
|
||||
spiffs_page_ix pix);
|
||||
|
||||
#if SPIFFS_CACHE_WR
|
||||
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd);
|
||||
void spiffs_cache_fh_release(spiffs *fs, spiffs_cache_page *cp);
|
||||
spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd);
|
||||
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd);
|
||||
|
||||
void spiffs_cache_fd_release(
|
||||
spiffs *fs,
|
||||
spiffs_cache_page *cp);
|
||||
|
||||
spiffs_cache_page *spiffs_cache_page_get_by_fd(
|
||||
spiffs *fs,
|
||||
spiffs_fd *fd);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
+31
-165
@@ -1370,175 +1370,41 @@ void setup() {
|
||||
void teardown() {
|
||||
_teardown();
|
||||
}
|
||||
#if 0
|
||||
TEST(write_small_file_chunks_1)
|
||||
{
|
||||
int res = test_create_and_write_file("smallfile", 600, 1);
|
||||
TEST(check1) {
|
||||
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||
int res = test_create_and_write_file("file", size, size);
|
||||
TEST_CHECK(res >= 0);
|
||||
res = read_and_verify("smallfile");
|
||||
res = read_and_verify("file");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
spiffs_file fd = SPIFFS_open(FS, "file", 0, 0);
|
||||
TEST_CHECK(fd > 0);
|
||||
spiffs_stat s;
|
||||
res = SPIFFS_fstat(FS, fd, &s);
|
||||
TEST_CHECK(res >= 0);
|
||||
SPIFFS_close(FS, fd);
|
||||
|
||||
// modify lu entry data page index 1
|
||||
spiffs_page_ix pix;
|
||||
res = spiffs_obj_lu_find_id_and_index(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, &pix);
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
// reset lu entry to being erased, but keep page data
|
||||
spiffs_obj_id obj_id = SPIFFS_OBJ_ID_ERASED;
|
||||
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
|
||||
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
|
||||
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry*sizeof(spiffs_obj_id);
|
||||
|
||||
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
|
||||
|
||||
spiffs_cache *cache = get_cache(FS);
|
||||
cache->cpage_use_map = 0;
|
||||
|
||||
SPIFFS_check(FS);
|
||||
|
||||
|
||||
return TEST_RES_OK;
|
||||
}
|
||||
TEST_END(write_small_file_chunks_1)
|
||||
} TEST_END(check1)
|
||||
|
||||
TEST(write_big_file_chunks_page)
|
||||
{
|
||||
int size = ((50*(FS)->cfg.phys_size)/100);
|
||||
printf(" filesize %i\n", size);
|
||||
int res = test_create_and_write_file("bigfile", size, SPIFFS_DATA_PAGE_SIZE(FS));
|
||||
TEST_CHECK(res >= 0);
|
||||
res = read_and_verify("bigfile");
|
||||
TEST_CHECK(res >= 0);
|
||||
|
||||
return TEST_RES_OK;
|
||||
}
|
||||
TEST_END(write_big_file_chunks_page)
|
||||
#else
|
||||
|
||||
|
||||
TEST(long_run_config_many_small)
|
||||
{
|
||||
tfile_conf cfgs[] = {
|
||||
{ .tsize = SMALL, .ttype = APPENDED, .tlife = LONG
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = APPENDED, .tlife = LONG
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
|
||||
},
|
||||
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
|
||||
},
|
||||
};
|
||||
|
||||
int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 115, 6, 1);
|
||||
TEST_CHECK(res >= 0);
|
||||
return TEST_RES_OK;
|
||||
}
|
||||
TEST_END(long_run_config_many_small)
|
||||
#endif
|
||||
SUITE_END(dev_tests)
|
||||
|
||||
|
||||
+8
-1
@@ -244,6 +244,13 @@ int read_and_verify(char *name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void area_write(u32_t addr, u8_t *buf, u32_t size) {
|
||||
int i;
|
||||
for (i = 0; i < size; i++) {
|
||||
area[addr + i] = *buf++;
|
||||
}
|
||||
}
|
||||
|
||||
void fs_reset() {
|
||||
memset(area, 0xff, sizeof(area));
|
||||
spiffs_config c;
|
||||
@@ -257,7 +264,7 @@ void fs_reset() {
|
||||
c.phys_size = sizeof(area);
|
||||
memset(erases,0,sizeof(erases));
|
||||
memset(_cache,0,sizeof(_cache));
|
||||
SPIFFS_init(&__fs, &c, _work, _fds, sizeof(_fds), _cache, sizeof(_cache));
|
||||
SPIFFS_mount(&__fs, &c, _work, _fds, sizeof(_fds), _cache, sizeof(_cache));
|
||||
}
|
||||
|
||||
void real_assert(int c, const char *n, const char *file, int l) {
|
||||
|
||||
@@ -20,6 +20,8 @@ void dump_page(spiffs *fs, spiffs_page_ix p);
|
||||
void hexdump(u32_t addr, u32_t len);
|
||||
char *make_test_fname(const char *name);
|
||||
void clear_test_path();
|
||||
u8_t area_read(u32_t addr);
|
||||
void area_write(u32_t addr, u8_t *buf, u32_t size);
|
||||
|
||||
|
||||
#endif /* TEST_SPIFFS_H_ */
|
||||
|
||||
+2
-2
@@ -8,6 +8,6 @@
|
||||
#include "testrunner.h"
|
||||
|
||||
void add_suites() {
|
||||
ADD_SUITE(hydrogen_tests)
|
||||
//ADD_SUITE(dev_tests);
|
||||
//ADD_SUITE(hydrogen_tests)
|
||||
ADD_SUITE(dev_tests);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user