diff --git a/app/modules/file.c b/app/modules/file.c index 19487d13..d055ef87 100644 --- a/app/modules/file.c +++ b/app/modules/file.c @@ -110,7 +110,7 @@ static int file_seek (lua_State *L) int op = luaL_checkoption(L, 1, "cur", modenames); long offset = luaL_optlong(L, 2, 0); op = fs_seek(file_fd, offset, mode[op]); - if (op) + if (op < 0) lua_pushnil(L); /* error */ else lua_pushinteger(L, fs_tell(file_fd)); diff --git a/app/spiffs/spiffs.h b/app/spiffs/spiffs.h index 7132b92d..e1e76d81 100644 --- a/app/spiffs/spiffs.h +++ b/app/spiffs/spiffs.h @@ -13,7 +13,6 @@ extern "C" { #endif -#include "c_stdio.h" #include "spiffs_config.h" #define SPIFFS_OK 0 @@ -41,6 +40,14 @@ extern "C" { #define SPIFFS_ERR_NOT_WRITABLE -10021 #define SPIFFS_ERR_NOT_READABLE -10022 #define SPIFFS_ERR_CONFLICTING_NAME -10023 +#define SPIFFS_ERR_NOT_CONFIGURED -10024 + +#define SPIFFS_ERR_NOT_A_FS -10025 +#define SPIFFS_ERR_MOUNTED -10026 +#define SPIFFS_ERR_ERASE_FAIL -10027 +#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028 + +#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029 #define SPIFFS_ERR_INTERNAL -10050 @@ -215,6 +222,11 @@ typedef struct { // check callback function spiffs_check_callback check_cb_f; + + // mounted flag + u8_t mounted; + // config magic + u32_t config_magic; } spiffs; /* spiffs file status struct */ @@ -242,7 +254,10 @@ typedef struct { // functions /** - * Initializes the file system dynamic parameters and mounts the filesystem + * Initializes the file system dynamic parameters and mounts the filesystem. + * If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS + * if the flash does not contain a recognizable file system. + * In this case, SPIFFS_format must be called prior to remounting. * @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 @@ -308,7 +323,7 @@ spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_fl * @param len how much to read * @returns number of bytes read, or -1 if error */ -s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, u32_t len); +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len); /** * Writes to given filehandle. @@ -318,7 +333,7 @@ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, u32_t len); * @param len how much to write * @returns number of bytes written, or -1 if error */ -s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len); +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len); /** * Moves the read/write file offset @@ -426,6 +441,12 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); */ s32_t SPIFFS_check(spiffs *fs); +/** + * Searches for a block with only deleted entries. If found, it is erased. + * @param fs the file system struct + */ +s32_t SPIFFS_erase_deleted_block(spiffs *fs); + /** * Returns number of total bytes available and number of used bytes. @@ -442,13 +463,69 @@ s32_t SPIFFS_check(spiffs *fs); s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used); /** - * Check if EOF reached. + * Formats the entire file system. All data will be lost. + * The filesystem must not be mounted when calling this. + * + * NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount + * MUST be called prior to formatting in order to configure the filesystem. + * If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling + * SPIFFS_format. + * If SPIFFS_mount fails, SPIFFS_format can be called directly without calling + * SPIFFS_unmount first. + * * @param fs the file system struct - * @param fh the filehandle of the file to check */ -s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); -s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); -s32_t SPIFFS_size(spiffs *fs, spiffs_file fh); +s32_t SPIFFS_format(spiffs *fs); + +/** + * Returns nonzero if spiffs is mounted, or zero if unmounted. + * @param fs the file system struct + */ +u8_t SPIFFS_mounted(spiffs *fs); + +/** + * Tries to find a block where most or all pages are deleted, and erase that + * block if found. Does not care for wear levelling. Will not move pages + * around. + * If parameter max_free_pages are set to 0, only blocks with only deleted + * pages will be selected. + * + * NB: the garbage collector is automatically called when spiffs needs free + * pages. The reason for this function is to give possibility to do background + * tidying when user knows the system is idle. + * + * Use with care. + * + * Setting max_free_pages to anything larger than zero will eventually wear + * flash more as a block containing free pages can be erased. + * + * Will set err_no to SPIFFS_OK if a block was found and erased, + * SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found, + * or other error. + * + * @param fs the file system struct + * @param max_free_pages maximum number allowed free pages in block + */ +s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages); + +/** + * Will try to make room for given amount of bytes in the filesystem by moving + * pages and erasing blocks. + * If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If + * there already is this amount (or more) of free space, SPIFFS_gc will + * silently return. It is recommended to call SPIFFS_info before invoking + * this method in order to determine what amount of bytes to give. + * + * NB: the garbage collector is automatically called when spiffs needs free + * pages. The reason for this function is to give possibility to do background + * tidying when user knows the system is idle. + * + * Use with care. + * + * @param fs the file system struct + * @param size amount of bytes that should be freed + */ +s32_t SPIFFS_gc(spiffs *fs, u32_t size); #if SPIFFS_TEST_VISUALISATION /** @@ -495,6 +572,10 @@ int myspiffs_check( void ); int myspiffs_rename( const char *old, const char *newname ); size_t myspiffs_size( int fd ); +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); +s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); +s32_t SPIFFS_size(spiffs *fs, spiffs_file fh); + #if defined(__cplusplus) } #endif diff --git a/app/spiffs/spiffs_cache.c b/app/spiffs/spiffs_cache.c index 6de0e493..b246117c 100644 --- a/app/spiffs/spiffs_cache.c +++ b/app/spiffs/spiffs_cache.c @@ -195,7 +195,7 @@ s32_t spiffs_phys_wr( cache->last_access++; cp->last_access = cache->last_access; - if (cp->flags && SPIFFS_CACHE_FLAG_WRTHRU) { + if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) { // page is being updated, no write-cache, just pass thru return fs->cfg.hal_write_f(addr, len, src); } else { diff --git a/app/spiffs/spiffs_config.h b/app/spiffs/spiffs_config.h index d80e7d22..0dc19958 100644 --- a/app/spiffs/spiffs_config.h +++ b/app/spiffs/spiffs_config.h @@ -119,6 +119,14 @@ typedef uint8_t u8_t; #define SPIFFS_COPY_BUFFER_STACK (64) #endif +// Enable this to have an identifiable spiffs filesystem. This will look for +// a magic in all sectors to determine if this is a valid spiffs system or +// not on mount point. If not, SPIFFS_format must be called prior to mounting +// again. +#ifndef SPIFFS_USE_MAGIC +#define SPIFFS_USE_MAGIC (0) +#endif + // SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level // These should be defined on a multithreaded system diff --git a/app/spiffs/spiffs_gc.c b/app/spiffs/spiffs_gc.c index 2ad31d07..a86f8128 100644 --- a/app/spiffs/spiffs_gc.c +++ b/app/spiffs/spiffs_gc.c @@ -8,31 +8,11 @@ static s32_t spiffs_gc_erase_block( spiffs *fs, spiffs_block_ix bix) { s32_t res; - u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix); - s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs); SPIFFS_GC_DBG("gc: erase block %i\n", bix); - - // here we ignore res, just try erasing the block - while (size > 0) { - SPIFFS_GC_DBG("gc: erase %08x:%08x\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); - (void)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++; - - // register erase count for this block - res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, - SPIFFS_ERASE_COUNT_PADDR(fs, bix), - sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count); + res = spiffs_erase_block(fs, bix); SPIFFS_CHECK_RES(res); - fs->max_erase_count++; - if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) { - fs->max_erase_count = 0; - } - #if SPIFFS_CACHE { u32_t i; @@ -48,7 +28,7 @@ static s32_t spiffs_gc_erase_block( // the block is erased. Compared to the non-quick gc, the quick one ensures // that no updates are needed on existing objects on pages that are erased. s32_t spiffs_gc_quick( - spiffs *fs) { + spiffs *fs, u16_t max_free_pages) { s32_t res = SPIFFS_OK; u32_t blocks = fs->block_count; spiffs_block_ix cur_block = 0; @@ -67,6 +47,7 @@ s32_t spiffs_gc_quick( // check each block while (res == SPIFFS_OK && blocks--) { u16_t deleted_pages_in_block = 0; + u16_t free_pages_in_block = 0; int obj_lookup_page = 0; // check each object lookup page @@ -83,9 +64,12 @@ s32_t spiffs_gc_quick( deleted_pages_in_block++; } else if (obj_id == SPIFFS_OBJ_ID_FREE) { // kill scan, go for next block - obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); - res = 1; // kill object lu loop - break; + free_pages_in_block++; + if (free_pages_in_block > max_free_pages) { + obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); + res = 1; // kill object lu loop + break; + } } else { // kill scan, go for next block obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); @@ -98,7 +82,9 @@ s32_t spiffs_gc_quick( } // per object lookup page if (res == 1) res = SPIFFS_OK; - if (res == SPIFFS_OK && deleted_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + if (res == SPIFFS_OK && + deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) && + free_pages_in_block <= max_free_pages) { // found a fully deleted block fs->stats_p_deleted -= deleted_pages_in_block; res = spiffs_gc_erase_block(fs, cur_block); @@ -110,10 +96,13 @@ s32_t spiffs_gc_quick( cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); } // per block + if (res == SPIFFS_OK) { + res = SPIFFS_ERR_NO_DELETED_BLOCKS; + } return res; } -// Checks if garbaga collecting is necessary. If so a candidate block is found, +// Checks if garbage collecting is necessary. If so a candidate block is found, // cleansed and erased s32_t spiffs_gc_check( spiffs *fs, @@ -130,12 +119,15 @@ s32_t spiffs_gc_check( } u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs); - if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { +// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { +// SPIFFS_GC_DBG("gc: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); +// return SPIFFS_ERR_FULL; +// } + if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) { + SPIFFS_GC_DBG("gc_check: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); return SPIFFS_ERR_FULL; } - //printf("gcing started %i dirty, blocks %i free, want %i bytes\n", fs->stats_p_allocated + fs->stats_p_deleted, fs->free_blocks, len); - 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, @@ -145,11 +137,13 @@ s32_t spiffs_gc_check( spiffs_block_ix *cands; int count; spiffs_block_ix cand; - res = spiffs_gc_find_candidate(fs, &cands, &count); + s32_t prev_free_pages = free_pages; + // if the fs is crammed, ignore block age when selecting candidate - kind of a bad state + res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0); SPIFFS_CHECK_RES(res); if (count == 0) { SPIFFS_GC_DBG("gc_check: no candidates, return\n"); - return res; + return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL; } #if SPIFFS_GC_STATS fs->stats_gc_runs++; @@ -176,6 +170,12 @@ s32_t spiffs_gc_check( (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) - fs->stats_p_allocated - fs->stats_p_deleted; + if (prev_free_pages <= 0 && prev_free_pages == free_pages) { + // abort early to reduce wear, at least tried once + SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n"); + break; + } + } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || (s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs))); @@ -234,7 +234,8 @@ s32_t spiffs_gc_erase_page_stats( s32_t spiffs_gc_find_candidate( spiffs *fs, spiffs_block_ix **block_candidates, - int *candidate_count) { + int *candidate_count, + char fs_crammed) { s32_t res = SPIFFS_OK; u32_t blocks = fs->block_count; spiffs_block_ix cur_block = 0; @@ -307,7 +308,7 @@ s32_t spiffs_gc_find_candidate( s32_t score = deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + used_pages_in_block * SPIFFS_GC_HEUR_W_USED + - erase_age * SPIFFS_GC_HEUR_W_ERASE_AGE; + erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE); int cand_ix = 0; SPIFFS_GC_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) { diff --git a/app/spiffs/spiffs_hydrogen.c b/app/spiffs/spiffs_hydrogen.c index 3ca01463..34b5861b 100644 --- a/app/spiffs/spiffs_hydrogen.c +++ b/app/spiffs/spiffs_hydrogen.c @@ -21,6 +21,36 @@ u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) { #endif #endif +u8_t SPIFFS_mounted(spiffs *fs) { + return SPIFFS_CHECK_MOUNT(fs); +} + +s32_t SPIFFS_format(spiffs *fs) { + SPIFFS_API_CHECK_CFG(fs); + if (SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_MOUNTED; + return -1; + } + + s32_t res; + SPIFFS_LOCK(fs); + + spiffs_block_ix bix = 0; + while (bix < fs->block_count) { + fs->max_erase_count = 0; + res = spiffs_erase_block(fs, bix); + if (res != SPIFFS_OK) { + res = SPIFFS_ERR_ERASE_FAIL; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + bix++; + } + + SPIFFS_UNLOCK(fs); + + return 0; +} + 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, @@ -34,10 +64,10 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, c_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)(((u32_t)fd_space) & (ptr_size-1)); -// #pragma GCC diagnostic pop +#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); @@ -46,10 +76,10 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, 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)(((u32_t)cache) & (ptr_size-1)); -// #pragma GCC diagnostic pop +#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) { u8_t *cache_8 = (u8_t *)cache; cache_8 += (ptr_size-addr_lsb); @@ -61,11 +91,20 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, } #if SPIFFS_CACHE fs->cache = cache; - fs->cache_size = cache_size; + fs->cache_size = (cache_size > (config->log_page_size*32)) ? config->log_page_size*32 : cache_size; spiffs_cache_init(fs); #endif - s32_t res = spiffs_obj_lu_scan(fs); + s32_t res; + +#if SPIFFS_USE_MAGIC + res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + fs->config_magic = SPIFFS_CONFIG_MAGIC; + + 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)); @@ -79,13 +118,15 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, fs->check_cb_f = check_cb_f; + fs->mounted = 1; + SPIFFS_UNLOCK(fs); return 0; } void SPIFFS_unmount(spiffs *fs) { - if (!SPIFFS_CHECK_MOUNT(fs)) return; + if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return; SPIFFS_LOCK(fs); u32_t i; spiffs_fd *fds = (spiffs_fd *)fs->fd_space; @@ -98,7 +139,8 @@ void SPIFFS_unmount(spiffs *fs) { spiffs_fd_return(fs, cur_fd->file_nbr); } } - fs->block_count = 0; + fs->mounted = 0; + SPIFFS_UNLOCK(fs); } @@ -112,6 +154,7 @@ void SPIFFS_clearerr(spiffs *fs) { s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode) { (void)mode; + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); spiffs_obj_id obj_id; @@ -127,6 +170,7 @@ s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode) { spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode mode) { (void)mode; + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -185,6 +229,7 @@ spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode } spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -213,7 +258,8 @@ spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_fl return fd->file_nbr; } -s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, u32_t len) { +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -281,7 +327,8 @@ 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, u32_t len) { +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -400,6 +447,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) { } s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -440,10 +488,11 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { SPIFFS_UNLOCK(fs); - return 0; + return offs; } s32_t SPIFFS_remove(spiffs *fs, char *path) { + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -477,6 +526,7 @@ s32_t SPIFFS_remove(spiffs *fs, char *path) { } s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -525,6 +575,7 @@ static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spi } s32_t SPIFFS_stat(spiffs *fs, char *path, spiffs_stat *s) { + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -542,6 +593,7 @@ s32_t SPIFFS_stat(spiffs *fs, char *path, spiffs_stat *s) { } s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -595,6 +647,7 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { } s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); s32_t res = SPIFFS_OK; #if SPIFFS_CACHE_WR @@ -608,6 +661,11 @@ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { } void SPIFFS_close(spiffs *fs, spiffs_file fh) { + if (!SPIFFS_CHECK_CFG((fs))) { + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; + return; + } + if (!SPIFFS_CHECK_MOUNT(fs)) { fs->err_code = SPIFFS_ERR_NOT_MOUNTED; return; @@ -623,6 +681,7 @@ void SPIFFS_close(spiffs *fs, spiffs_file fh) { } s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) { + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -652,9 +711,8 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) { res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (u8_t*)new, 0, &pix_dummy); - if (res != SPIFFS_OK) { - spiffs_fd_return(fs, fd->file_nbr); - } + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); SPIFFS_UNLOCK(fs); @@ -664,10 +722,17 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) { spiffs_DIR *SPIFFS_opendir(spiffs *fs, char *name, spiffs_DIR *d) { (void)name; + + if (!SPIFFS_CHECK_CFG((fs))) { + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; + return 0; + } + if (!SPIFFS_CHECK_MOUNT(fs)) { fs->err_code = SPIFFS_ERR_NOT_MOUNTED; return 0; } + d->fs = fs; d->block = 0; d->entry = 0; @@ -743,12 +808,14 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { } s32_t SPIFFS_closedir(spiffs_DIR *d) { + SPIFFS_API_CHECK_CFG(d->fs); SPIFFS_API_CHECK_MOUNT(d->fs); return 0; } s32_t SPIFFS_check(spiffs *fs) { s32_t res; + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -766,6 +833,7 @@ s32_t SPIFFS_check(spiffs *fs) { s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -787,25 +855,6 @@ s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { return res; } -s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { - SPIFFS_API_CHECK_MOUNT(fs); - SPIFFS_LOCK(fs); - - spiffs_fd *fd; - s32_t res; - res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES(fs, res); - -#if SPIFFS_CACHE_WR - spiffs_fflush_cache(fs, fh); -#endif - - res = (fd->fdoffset == fd->size); - - SPIFFS_UNLOCK(fs); - return res; -} - s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) { SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -825,6 +874,25 @@ s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) { return res; } +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + res = (fd->fdoffset == fd->size); + + SPIFFS_UNLOCK(fs); + return res; +} + s32_t SPIFFS_size(spiffs *fs, spiffs_file fh) { SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -844,9 +912,38 @@ s32_t SPIFFS_size(spiffs *fs, spiffs_file fh) { return res; } +s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) { + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_gc_quick(fs, max_free_pages); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +} + + +s32_t SPIFFS_gc(spiffs *fs, u32_t size) { + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_gc_check(fs, size); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +} + + #if SPIFFS_TEST_VISUALISATION s32_t SPIFFS_vis(spiffs *fs) { s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); diff --git a/app/spiffs/spiffs_nucleus.c b/app/spiffs/spiffs_nucleus.c index 4ab63b23..8e81520d 100644 --- a/app/spiffs/spiffs_nucleus.c +++ b/app/spiffs/spiffs_nucleus.c @@ -142,9 +142,13 @@ s32_t spiffs_obj_lu_find_entry_visitor( cur_block++; cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); if (cur_block >= fs->block_count) { - // block wrap - cur_block = 0; - cur_block_addr = 0; + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_VIS_END; + } else { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } } } @@ -213,6 +217,45 @@ s32_t spiffs_obj_lu_find_entry_visitor( return SPIFFS_VIS_END; } +s32_t spiffs_erase_block( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res; + u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix); + s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + // here we ignore res, just try erasing the block + while (size > 0) { + SPIFFS_DBG("erase %08x:%08x\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + (void)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++; + + // register erase count for this block + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count); + SPIFFS_CHECK_RES(res); + +#if SPIFFS_USE_MAGIC + // finally, write magic + spiffs_obj_id magic = SPIFFS_MAGIC(fs); + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_MAGIC_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&magic); + SPIFFS_CHECK_RES(res); +#endif + + fs->max_erase_count++; + if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) { + fs->max_erase_count = 0; + } + + return res; +} + static s32_t spiffs_obj_lu_scan_v( spiffs *fs, @@ -238,40 +281,44 @@ static s32_t spiffs_obj_lu_scan_v( return SPIFFS_VIS_COUNTINUE; } + // Scans thru all obj lu and counts free, deleted and used pages // Find the maximum block erase count +// Checks magic if enabled s32_t spiffs_obj_lu_scan( spiffs *fs) { s32_t res; spiffs_block_ix bix; int entry; +#if SPIFFS_USE_MAGIC + spiffs_block_ix unerased_bix = (spiffs_block_ix)-1; +#endif - fs->free_blocks = 0; - fs->stats_p_allocated = 0; - fs->stats_p_deleted = 0; - - res = spiffs_obj_lu_find_entry_visitor(fs, - 0, - 0, - 0, - 0, - spiffs_obj_lu_scan_v, - 0, - 0, - &bix, - &entry); - - if (res == SPIFFS_VIS_END) { - res = SPIFFS_OK; - } - - SPIFFS_CHECK_RES(res); - + // find out erase count + // if enabled, check magic bix = 0; spiffs_obj_id erase_count_final; spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE; spiffs_obj_id erase_count_max = 0; while (bix < fs->block_count) { +#if SPIFFS_USE_MAGIC + spiffs_obj_id magic; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_MAGIC_PADDR(fs, bix) , + sizeof(spiffs_obj_id), (u8_t *)&magic); + + SPIFFS_CHECK_RES(res); + if (magic != SPIFFS_MAGIC(fs)) { + if (unerased_bix == (spiffs_block_ix)-1) { + // allow one unerased block as it might be powered down during an erase + unerased_bix = bix; + } else { + // more than one unerased block, bail out + SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS); + } + } +#endif spiffs_obj_id erase_count; res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, @@ -297,6 +344,38 @@ s32_t spiffs_obj_lu_scan( fs->max_erase_count = erase_count_final; +#if SPIFFS_USE_MAGIC + if (unerased_bix != (spiffs_block_ix)-1) { + // found one unerased block, remedy + SPIFFS_DBG("mount: erase block %i\n", bix); + res = spiffs_erase_block(fs, unerased_bix); + SPIFFS_CHECK_RES(res); + } +#endif + + // count blocks + + fs->free_blocks = 0; + fs->stats_p_allocated = 0; + fs->stats_p_deleted = 0; + + res = spiffs_obj_lu_find_entry_visitor(fs, + 0, + 0, + 0, + 0, + spiffs_obj_lu_scan_v, + 0, + 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + SPIFFS_CHECK_RES(res); + return res; } @@ -310,7 +389,10 @@ s32_t spiffs_obj_lu_find_free( int *lu_entry) { s32_t res; if (!fs->cleaning && fs->free_blocks < 2) { - res = spiffs_gc_quick(fs); + res = spiffs_gc_quick(fs, 0); + if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) { + res = SPIFFS_OK; + } SPIFFS_CHECK_RES(res); if (fs->free_blocks < 2) { return SPIFFS_ERR_FULL; diff --git a/app/spiffs/spiffs_nucleus.h b/app/spiffs/spiffs_nucleus.h index cc414432..02ba7a96 100644 --- a/app/spiffs/spiffs_nucleus.h +++ b/app/spiffs/spiffs_nucleus.h @@ -124,13 +124,17 @@ #define SPIFFS_EV_IX_NEW 1 #define SPIFFS_EV_IX_DEL 2 -#define SPIFFS_OBJ_ID_IX_FLAG (1<<(8*sizeof(spiffs_obj_id)-1)) +#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1))) #define SPIFFS_UNDEFINED_LEN (u32_t)(-1) #define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0) #define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) +#define SPIFFS_MAGIC(fs) ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) + +#define SPIFFS_CONFIG_MAGIC (0x20090315) + #if SPIFFS_SINGLETON == 0 #define SPIFFS_CFG_LOG_PAGE_SZ(fs) \ ((fs)->cfg.log_page_size) @@ -189,9 +193,18 @@ // returns data size in a data page #define SPIFFS_DATA_PAGE_SIZE(fs) \ ( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) ) -// returns physical address for block's erase count +// returns physical address for block's erase count, +// always in the physical last entry of the last object lookup page #define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \ ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) ) +// returns physical address for block's magic, +// always in the physical second last entry of the last object lookup page +#define SPIFFS_MAGIC_PADDR(fs, bix) \ + ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 ) +// checks if there is any room for magic in the object luts +#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \ + ( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \ + <= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) ) // define helpers object @@ -238,7 +251,10 @@ #define SPIFFS_CHECK_MOUNT(fs) \ - ((fs)->block_count > 0) + ((fs)->mounted != 0) + +#define SPIFFS_CHECK_CFG(fs) \ + ((fs)->config_magic == SPIFFS_CONFIG_MAGIC) #define SPIFFS_CHECK_RES(res) \ do { \ @@ -251,6 +267,12 @@ return -1; \ } +#define SPIFFS_API_CHECK_CFG(fs) \ + if (!SPIFFS_CHECK_CFG((fs))) { \ + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \ + return -1; \ + } + #define SPIFFS_API_CHECK_RES(fs, res) \ if ((res) < SPIFFS_OK) { \ (fs)->err_code = (res); \ @@ -381,6 +403,8 @@ typedef struct { // object structs // page header, part of each page except object lookup pages +// NB: this is always aligned when the data page is an object index, +// as in this case struct spiffs_page_object_ix is used typedef struct __attribute(( packed )) { // object id spiffs_obj_id obj_id; @@ -391,11 +415,10 @@ typedef struct __attribute(( packed )) { } spiffs_page_header; // object index header page header -typedef struct __attribute(( packed )) { +typedef struct __attribute(( packed, aligned(4) )) +{ // common page header spiffs_page_header p_hdr; - // alignment - u8_t _align[4 - ((sizeof(spiffs_page_header)+sizeof(spiffs_obj_type)+SPIFFS_OBJ_NAME_LEN)&3)==0 ? 4 : ((sizeof(spiffs_page_header)+sizeof(spiffs_obj_type)+SPIFFS_OBJ_NAME_LEN)&3)]; // size of object u32_t size; // type of object @@ -405,9 +428,8 @@ typedef struct __attribute(( packed )) { } spiffs_page_object_ix_header; // object index page header -typedef struct __attribute(( packed )) { +typedef struct __attribute(( packed, aligned(4) )) { spiffs_page_header p_hdr; - u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)]; } spiffs_page_object_ix; // callback func for object lookup visitor @@ -478,6 +500,10 @@ s32_t spiffs_obj_lu_find_entry_visitor( spiffs_block_ix *block_ix, int *lu_entry); +s32_t spiffs_erase_block( + spiffs *fs, + spiffs_block_ix bix); + // --------------- s32_t spiffs_obj_lu_scan( @@ -625,14 +651,15 @@ s32_t spiffs_gc_erase_page_stats( s32_t spiffs_gc_find_candidate( spiffs *fs, spiffs_block_ix **block_candidate, - int *candidate_count); + int *candidate_count, + char fs_crammed); s32_t spiffs_gc_clean( spiffs *fs, spiffs_block_ix bix); s32_t spiffs_gc_quick( - spiffs *fs); + spiffs *fs, u16_t max_free_pages); // ---------------