Updated SPIFFS to 0.3.2, plus alignment fix.

This commit is contained in:
Johny Mattsson 2015-07-16 16:11:22 +10:00
parent 239008d5b9
commit 8d68ad7e15
8 changed files with 413 additions and 117 deletions

View File

@ -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));

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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);
// ---------------