Przeglądaj źródła

Updated SPIFFS to 0.3.2, plus alignment fix.

Johny Mattsson 9 lat temu
rodzic
commit
8d68ad7e15

+ 1 - 1
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));

+ 90 - 9
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
+ */
+s32_t SPIFFS_format(spiffs *fs);
+
+/**
+ * Returns nonzero if spiffs is mounted, or zero if unmounted.
  * @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);
+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

+ 1 - 1
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 {

+ 8 - 0
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
 

+ 35 - 34
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) {

+ 119 - 22
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,7 +855,7 @@ s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) {
   return res;
 }
 
-s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) {
+s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) {
   SPIFFS_API_CHECK_MOUNT(fs);
   SPIFFS_LOCK(fs);
 
@@ -800,13 +868,13 @@ s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) {
   spiffs_fflush_cache(fs, fh);
 #endif
 
-  res = (fd->fdoffset == fd->size);
+  res = fd->fdoffset;
 
   SPIFFS_UNLOCK(fs);
   return res;
 }
 
-s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) {
+s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) {
   SPIFFS_API_CHECK_MOUNT(fs);
   SPIFFS_LOCK(fs);
 
@@ -819,7 +887,7 @@ s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) {
   spiffs_fflush_cache(fs, fh);
 #endif
 
-  res = fd->fdoffset;
+  res = (fd->fdoffset == fd->size);
 
   SPIFFS_UNLOCK(fs);
   return res;
@@ -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);
 

+ 107 - 25
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;

+ 37 - 10
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);
 
 // ---------------