Browse Source

Upgrade to latest SPIFFS and add building of file systems (#1226)

Fixes #1164 and thus also #1150, #1149, #1147 and #898.

* Move to latest version of SPIFFS
* Add SPIFFS porting layer for NodeMCU
* Add option to delete output if it doesn't fit
* Change FLASHSIZE to be in bits by default: default 4mb 32mb
* Add SPIFFS_MAX_FILESYSTEM_SIZE override
* Add notes on SPIFFS_FIXED_LOCATION
* Add 1M boundary
* Include the current version of the LICENSE
Philip Gladstone 8 years ago
parent
commit
af39a0bc25

+ 23 - 2
Makefile

@@ -90,7 +90,7 @@ ESPTOOL ?= ../tools/esptool.py
 CSRCS ?= $(wildcard *.c)
 ASRCs ?= $(wildcard *.s)
 ASRCS ?= $(wildcard *.S)
-SUBDIRS ?= $(patsubst %/,%,$(dir $(wildcard */Makefile)))
+SUBDIRS ?= $(patsubst %/,%,$(dir $(filter-out tools/Makefile,$(wildcard */Makefile))))
 
 ODIR := .output
 OBJODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/obj
@@ -112,6 +112,14 @@ OIMAGES := $(GEN_IMAGES:%=$(IMAGEODIR)/%)
 BINODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/bin
 OBINS := $(GEN_BINS:%=$(BINODIR)/%)
 
+ifndef PDIR
+ifneq ($(wildcard $(TOP_DIR)/local/fs/*),)
+SPECIAL_MKTARGETS += spiffs-image
+else
+SPECIAL_MKTARGETS += spiffs-image-remove
+endif
+endif
+
 #
 # Note: 
 # https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
@@ -218,11 +226,24 @@ endif
 endif
 endif
 
+.PHONY: spiffs-image-remove
+
+spiffs-image-remove:
+	$(MAKE) -C tools remove-image
+
+.PHONY: spiffs-image
+
+spiffs-image: bin/0x10000.bin
+	$(MAKE) -C tools
+
 .PHONY: pre_build
 
 ifneq ($(wildcard $(TOP_DIR)/server-ca.crt),)
-pre_build:
+pre_build: $(TOP_DIR)/app/modules/server-ca.crt.h
+
+$(TOP_DIR)/app/modules/server-ca.crt.h: $(TOP_DIR)/server-ca.crt
 	python $(TOP_DIR)/tools/make_server_cert.py $(TOP_DIR)/server-ca.crt > $(TOP_DIR)/app/modules/server-ca.crt.h
+
 DEFINES += -DHAVE_SSL_SERVER_CRT=\"server-ca.crt.h\"
 else
 pre_build:

+ 1 - 0
README.md

@@ -46,6 +46,7 @@ wifi.sta.config("SSID", "password")
 The entire [NodeMCU documentation](https://nodemcu.readthedocs.io) is maintained right in this repository at [/docs](docs). The fact that the API documentation is mainted in the same repository as the code that *provides* the API ensures consistency between the two. With every commit the documentation is rebuilt by Read the Docs and thus transformed from terse Markdown into a nicely browsable HTML site at [https://nodemcu.readthedocs.io](https://nodemcu.readthedocs.io). 
 
 - How to [build the firmware](https://nodemcu.readthedocs.io/en/dev/en/build/)
+- How to [build the filesystem](https://nodemcu.readthedocs.io/en/dev/en/spiffs/)
 - How to [flash the firmware](https://nodemcu.readthedocs.io/en/dev/en/flash/)
 - How to [upload code and NodeMCU IDEs](https://nodemcu.readthedocs.io/en/dev/en/upload/)
 - API documentation for every module

+ 11 - 0
app/include/user_config.h

@@ -69,6 +69,17 @@ extern void luaL_assertfail(const char *file, int line, const char *message);
 #define BUILD_SPIFFS	1
 #define SPIFFS_CACHE 1
 
+// Uncomment this next line for fastest startup 
+// It reduces the format time dramatically
+// #define SPIFFS_MAX_FILESYSTEM_SIZE	32768
+//
+// You can force the spiffs file system to be at a fixed location
+// #define SPIFFS_FIXED_LOCATION   	0x100000
+//
+// You can force the SPIFFS file system to end on the next !M boundary
+// (minus the 16k parameter space). THis is useful for certain OTA scenarios
+// #define SPIFFS_SIZE_1M_BOUNDARY
+
 // #define LUA_NUMBER_INTEGRAL
 
 #define READLINE_INTERVAL 80

+ 10 - 7
app/modules/file.c

@@ -52,8 +52,9 @@ static int file_format( lua_State* L )
   file_close(L);
   if( !fs_format() )
   {
-    NODE_ERR( "\ni*** ERROR ***: unable to format. FS might be compromised.\n" );
+    NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" );
     NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
+    luaL_error(L, "Failed to format file system");
   }
   else{
     NODE_ERR( "format done.\n" );
@@ -104,7 +105,7 @@ static int file_exists( lua_State* L )
 {
   size_t len;
   const char *fname = luaL_checklstring( L, 1, &len );    
-  luaL_argcheck(L, len <= FS_NAME_MAX_LENGTH, 1, "filename too long");
+  luaL_argcheck(L, len < FS_NAME_MAX_LENGTH && c_strlen(fname) == len, 1, "filename invalid");
 
   spiffs_stat stat;
   int rc = SPIFFS_stat(&fs, (char *)fname, &stat);
@@ -119,7 +120,7 @@ static int file_remove( lua_State* L )
 {
   size_t len;
   const char *fname = luaL_checklstring( L, 1, &len );    
-  luaL_argcheck(L, len <= FS_NAME_MAX_LENGTH, 1, "filename too long");
+  luaL_argcheck(L, len < FS_NAME_MAX_LENGTH && c_strlen(fname) == len, 1, "filename invalid");
   file_close(L);
   SPIFFS_remove(&fs, (char *)fname);
   return 0;  
@@ -156,10 +157,10 @@ static int file_rename( lua_State* L )
   }
 
   const char *oldname = luaL_checklstring( L, 1, &len );
-  luaL_argcheck(L, len <= FS_NAME_MAX_LENGTH, 1, "filename too long");
+  luaL_argcheck(L, len < FS_NAME_MAX_LENGTH && c_strlen(oldname) == len, 1, "filename invalid");
   
   const char *newname = luaL_checklstring( L, 2, &len );  
-  luaL_argcheck(L, len <= FS_NAME_MAX_LENGTH, 2, "filename too long");
+  luaL_argcheck(L, len < FS_NAME_MAX_LENGTH && c_strlen(newname) == len, 2, "filename invalid");
 
   if(SPIFFS_OK==myspiffs_rename( oldname, newname )){
     lua_pushboolean(L, 1);
@@ -173,11 +174,13 @@ static int file_rename( lua_State* L )
 static int file_fsinfo( lua_State* L )
 {
   u32_t total, used;
-  SPIFFS_info(&fs, &total, &used);
+  if (SPIFFS_info(&fs, &total, &used)) {
+    return luaL_error(L, "file system failed");
+  }
   NODE_DBG("total: %d, used:%d\n", total, used);
   if(total>0x7FFFFFFF || used>0x7FFFFFFF || used > total)
   {
-    return luaL_error(L, "file system error");;
+    return luaL_error(L, "file system error");
   }
   lua_pushinteger(L, total-used);
   lua_pushinteger(L, used);

+ 1 - 1
app/modules/node.c

@@ -398,7 +398,7 @@ static int node_compile( lua_State* L )
   int file_fd = FS_OPEN_OK - 1;
   size_t len;
   const char *fname = luaL_checklstring( L, 1, &len );
-  if ( len > FS_NAME_MAX_LENGTH )
+  if ( len >= FS_NAME_MAX_LENGTH )
     return luaL_error(L, "filename too long");
 
   char output[FS_NAME_MAX_LENGTH];

+ 1 - 1
app/platform/flash_fs.h

@@ -6,7 +6,7 @@
 
 #if defined( BUILD_SPIFFS )
 
-#include "spiffs.h"
+#include "myspiffs.h"
 
 #define FS_OPEN_OK	1
 

+ 1 - 1
app/spiffs/LICENSE

@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2013-2015 Peter Andersson (pelleplutt1976<at>gmail.com)
+Copyright (c) 2013-2016 Peter Andersson (pelleplutt1976<at>gmail.com)
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in

+ 1 - 1
app/spiffs/Makefile

@@ -22,7 +22,7 @@ endif
 #   makefile at its root level - these are then overridden
 #   for a subtree within the makefile rooted therein
 #
-#DEFINES += 
+DEFINES += -Dprintf=c_printf
 
 #############################################################
 # Recursion Magic - Don't touch this!!

+ 0 - 306
app/spiffs/docs/INTEGRATION

@@ -1,306 +0,0 @@
-* QUICK AND DIRTY INTEGRATION EXAMPLE
-
-So, assume you're running a Cortex-M3 board with a 2 MB SPI flash on it. The 
-SPI flash has 64kB blocks. Your project is built using gnumake, and now you 
-want to try things out.
-
-First, you simply copy the files in src/ to your own source folder. Exclude
-all files in test folder. Then you point out these files in your make script 
-for compilation.
-
-Also copy the spiffs_config.h over from the src/default/ folder.
-
-Try building. This fails, nagging about inclusions and u32_t and whatnot. Open 
-the spiffs_config.h and delete the bad inclusions. Also, add following 
-typedefs:
-
-  typedef signed int s32_t;
-  typedef unsigned int u32_t;
-  typedef signed short s16_t;
-  typedef unsigned short u16_t;
-  typedef signed char s8_t;
-  typedef unsigned char u8_t;
-  
-Now it should build. Over to the mounting business. Assume you already 
-implemented the read, write and erase functions to your SPI flash:
-
-  void my_spi_read(int addr, int size, char *buf)
-  void my_spi_write(int addr, int size, char *buf)
-  void my_spi_erase(int addr, int size)
-
-In your main.c or similar, include the spiffs.h and do that spiffs struct:
-
-  #include <spiffs.h>
-  
-  static spiffs fs;
-
-Also, toss up some of the needed buffers:
-
-  #define LOG_PAGE_SIZE       256
-  
-  static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
-  static u8_t spiffs_fds[32*4];
-  static u8_t spiffs_cache_buf[(LOG_PAGE_SIZE+32)*4];
-  
-Now, write the my_spiffs_mount function:
-
-  void my_spiffs_mount() {
-    spiffs_config cfg;
-    cfg.phys_size = 2*1024*1024; // use all spi flash
-    cfg.phys_addr = 0; // start spiffs at start of spi flash
-    cfg.phys_erase_block = 65536; // according to datasheet
-    cfg.log_block_size = 65536; // let us not complicate things
-    cfg.log_page_size = LOG_PAGE_SIZE; // as we said
-    
-    cfg.hal_read_f = my_spi_read;
-    cfg.hal_write_f = my_spi_write;
-    cfg.hal_erase_f = my_spi_erase;
-    
-    int res = SPIFFS_mount(&fs,
-      &cfg,
-      spiffs_work_buf,
-      spiffs_fds,
-      sizeof(spiffs_fds),
-      spiffs_cache_buf,
-      sizeof(spiffs_cache_buf),
-      0);
-    printf("mount res: %i\n", res);
-  }
-
-Now, build warns about the my_spi_read, write and erase functions. Wrong 
-signatures, so go wrap them:
-
-  static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) {
-    my_spi_read(addr, size, dst);
-    return SPIFFS_OK;
-  }
-
-  static s32_t my_spiffs_write(u32_t addr, u32_t size, u8_t *src) {
-    my_spi_write(addr, size, dst);
-    return SPIFFS_OK;
-  }
-
-  static s32_t my_spiffs_erase(u32_t addr, u32_t size) {
-    my_spi_erase(addr, size);
-    return SPIFFS_OK;
-  } 
-
-Redirect the config in my_spiffs_mount to the wrappers instead:
-
-    cfg.hal_read_f = my_spiffs_read;
-    cfg.hal_write_f = my_spiffs_write;
-    cfg.hal_erase_f = my_spiffs_erase;
-
-Ok, now you should be able to build and run. However, you get this output:
-  
-  mount res: -1
-  
-but you wanted 
-
-  mount res: 0
-  
-This is probably due to you having experimented with your SPI flash, so it
-contains rubbish from spiffs's point of view. Do a mass erase and run again.
-
-If all is ok now, you're good to go. Try creating a file and read it back:
-
-  static void test_spiffs() {
-    char buf[12];
-    
-    // Surely, I've mounted spiffs before entering here
-  
-    spiffs_file fd = SPIFFS_open(&fs, "my_file", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
-    if (SPIFFS_write(&fs, fd, (u8_t *)"Hello world", 12) < 0) printf("errno %i\n", SPIFFS_errno(&fs));
-    SPIFFS_close(&fs, fd); 
-  
-    fd = SPIFFS_open(&fs, "my_file", SPIFFS_RDWR, 0);
-    if (SPIFFS_read(&fs, fd, (u8_t *)buf, 12) < 0) printf("errno %i\n", SPIFFS_errno(&fs));
-    SPIFFS_close(&fs, fd);
-  
-    printf("--> %s <--\n", buf);
-  }
-  
-Compile, run, cross fingers hard, and you'll get the output:
-
-  --> Hello world <-- 
-  
-Got errors? Check spiffs.h for error definitions to get a clue what went voodoo.
-
-
-* THINGS TO CHECK
-
-When you alter the spiffs_config values, make sure you also check the typedefs
-in spiffs_config.h:
-
- - spiffs_block_ix
- - spiffs_page_ix
- - spiffs_obj_id
- - spiffs_span_ix
-
-The sizes of these typedefs must not underflow, else spiffs might end up in
-eternal loops. Each typedef is commented what check for.
-
-Also, if you alter the code or just want to verify your configuration, you can
-run
-
- > make test
-
-in the spiffs folder. This will run all testcases using the configuration in
-default/spiffs_config.h and test/params_test.h. The tests are written for linux
-but should run under cygwin also.
-
-
-* INTEGRATING SPIFFS
-
-In order to integrate spiffs to your embedded target, you will basically need:
- - A SPI flash device which your processor can communicate with
- - An implementation for reading, writing and erasing the flash
- - Memory (flash or ram) for the code
- - Memory (ram) for the stack
-
-Other stuff may be needed, threaded systems might need mutexes and so on. 
-
-** Logical structure
-
-First and foremost, one must decide how to divide up the SPI flash for spiffs. 
-Having the datasheet for the actual SPI flash in hand will help. Spiffs can be 
-defined to use all or only parts of the SPI flash.
-
-If following seems arcane, read the "HOW TO CONFIG" chapter first.
-
- - Decide the logical size of blocks. This must be a multiple of the biggest 
-   physical SPI flash block size. To go safe, use the physical block size - 
-   which in many cases is 65536 bytes.
- - Decide the logical size of pages. This must be a 2nd logarithm part of the
-   logical block size. To go safe, use 256 bytes to start with.
- - Decide how much of the SPI flash memory to be used for spiffs. This must be
-   on logical block boundary. If unsafe, use 1 megabyte to start with.
- - Decide where on the SPI flash memory the spiffs area should start. This must
-   be on physical block/sector boundary. If unsafe, use address 0.
-
-** SPI flash API
-
-The target must provide three functions to spiffs:
-
- - s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst)
- - s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src)
- - s32_t (*spiffs_erase)(u32_t addr, u32_t size)
- 
-These functions define the only communication between the SPI flash and the
-spiffs stack. 
- 
-On success these must return 0 (or SPIFFS_OK). Anything else will be considered
-an error. 
-
-The size for read and write requests will never exceed the logical page size,
-but it may be less.
-
-The address and size on erase requests will always be on physical block size
-boundaries.  
-
-** Mount specification
-
-In spiffs.h, there is a SPIFFS_mount function defined, used to mount spiffs on
-the SPI flash.
-
-s32_t SPIFFS_mount(
-	spiffs *fs, 
-	spiffs_config *config, 
-	u8_t *work,
-    u8_t *fd_space, 
-    u32_t fd_space_size,
-    void *cache, 
-    u32_t cache_size,
-    spiffs_check_callback check_cb_f)
-    
- - fs            Points to a spiffs struct. This may be totally uninitialized.
- - config        Points to a spiffs_config struct. This struct must be
-                 initialized when mounting. See below.
- - work          A ram memory buffer being double the size of the logical page
-                 size. This buffer is used excessively by the spiffs stack. If
-                 logical page size is 256, this buffer must be 512 bytes.
- - fd_space      A ram memory buffer used for file descriptors.
- - fd_space_size The size of the file descriptor buffer. A file descriptor
-                 normally is around 32 bytes depending on the build config -
-                 the bigger the buffer, the more file descriptors are
-                 available.
- - cache         A ram memory buffer used for cache. Ignored if cache is
-                 disabled in build config. 
- - cache_size    The size of the cache buffer. Ignored if cache is disabled in
-                 build config. One cache page will be slightly larger than the 
-                 logical page size. The more ram, the more cache pages, the 
-                 quicker the system.
- - check_cb_f    Callback function for monitoring spiffs consistency checks and
-                 mending operations. May be null.
-
-The config struct must be initialized prior to mounting. One must always 
-define the SPI flash access functions:
-
- spiffs_config.hal_read_f - pointing to the function reading the SPI flash
-
- spiffs_config.hal_write_f - pointing to the function writing the SPI flash
-
- spiffs_config.hal_erase_f - pointing to the function erasing the SPI flash
- 
-Depending on the build config - if SPIFFS_SINGLETON is set to zero - following
-parameters must be defined:
- 
- spiffs_config.phys_size - the physical number of bytes accounted for 
-                           spiffs on the SPI flash
-
- spiffs_config.phys_addr - the physical starting address on the SPI flash
-
- spiffs_config.phys_erase_block - the physical size of the largest block/sector
-                                  on the SPI flash found within the spiffs
-                                  usage address space
-
- spiffs_config.log_block_size - the logical size of a spiffs block
-
- spiffs_config.log_page_size - the logical size of a spiffs page
- 
-If SPIFFS_SINGLETON is set to one, above parameters must be set ny defines in
-the config header file, spiffs_config.h.
-
-
-** Build config
-
-makefile: The files needed to be compiled to your target resides in files.mk to
-be included in your makefile, either by cut and paste or by inclusion.
-
-Types: spiffs uses the types u8_t, s8_t, u16_t, s16_t, u32_t, s32_t; these must 
-be typedeffed.
-
-spiffs_config.h: you also need to define a spiffs_config.h header. Example of
-this is found in the default/ directory.
-
-
-** RAM
-
-Spiffs needs ram. It needs a working buffer being double the size of the 
-logical page size. It also needs at least one file descriptor. If cache is 
-enabled (highly recommended), it will also need a bunch of cache pages.
-
-Say you have a logical page size of 256 bytes. You want to be able to have four
-files open simultaneously, and you can give spiffs four cache pages. This 
-roughly sums up to:
-
-256*2 (work buffer) + 
-32*4 (file descriptors) + 
-(256+32)*4 (cache pages) + 40 (cache metadata)
-
-i.e. 1832 bytes. 
-
-This is apart from call stack usage.
-
-To get the exact amount of bytes needed on your specific target, enable 
-SPIFFS_BUFFER_HELP in spiffs_config.h, rebuild and call:
-
-  SPIFFS_buffer_bytes_for_filedescs
-  SPIFFS_buffer_bytes_for_cache
-  
-Having these figures you can disable SPIFFS_BUFFER_HELP again to save flash.
- 
-
-* HOW TO CONFIG
-
-TODO

+ 15 - 1
app/spiffs/docs/TODO

@@ -1 +1,15 @@
-* When mending lost pages, also see if they fit into length specified in object index header
+* When mending lost pages, also see if they fit into length specified in object index header
+
+SPIFFS2 thoughts
+
+* Instead of exact object id:s in the object lookup tables, use a hash of span index and object id.
+  Eg. object id xor:ed with bit-reversed span index.
+  This should decrease number of actual pages that needs to be visited when looking thru the obj lut.
+
+* Logical number of each block. When moving stuff in a garbage collected page, the free
+  page is assigned the same number as the garbage collected. Thus, object index pages do not have to
+  be rewritten.
+
+* Steal one page, use as a bit parity page. When starting an fs modification operation, write one bit
+  as zero. When ending, write another bit as zero. On mount, if number of zeroes in page is uneven, a
+  check is automatically run.

+ 20 - 0
app/spiffs/myspiffs.h

@@ -0,0 +1,20 @@
+#include "spiffs.h"
+bool myspiffs_mount();
+void myspiffs_unmount();
+int myspiffs_open(const char *name, int flags);
+int myspiffs_close( int fd );
+size_t myspiffs_write( int fd, const void* ptr, size_t len );
+size_t myspiffs_read( int fd, void* ptr, size_t len);
+int myspiffs_lseek( int fd, int off, int whence );
+int myspiffs_eof( int fd );
+int myspiffs_tell( int fd );
+int myspiffs_getc( int fd );
+int myspiffs_ungetc( int c, int fd );
+int myspiffs_flush( int fd );
+int myspiffs_error( int fd );
+void myspiffs_clearerr( int fd );
+int myspiffs_check( void );
+int myspiffs_rename( const char *old, const char *newname );
+size_t myspiffs_size( int fd );
+int myspiffs_format (void);
+

+ 26 - 0
app/spiffs/nodemcu_spiffs.h

@@ -0,0 +1,26 @@
+#ifndef _NODEMCU_SPIFFS_H
+#define _NODEMCU_SPIFFS_H
+
+#ifndef NODEMCU_SPIFFS_NO_INCLUDE
+#include "c_stdint.h"
+#include "c_stddef.h"
+#include "c_stdio.h"
+#include "user_interface.h"
+typedef uint32_t intptr_t;
+#endif
+
+// Turn off stats
+#define SPIFFS_CACHE_STATS 	    0
+#define SPIFFS_GC_STATS             0
+
+// Needs to align stuff
+#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES	1
+
+// Enable magic so we can find the file system (but not yet)
+#define SPIFFS_USE_MAGIC            1
+#define SPIFFS_USE_MAGIC_LENGTH     1
+
+// Reduce the chance of returning disk full
+#define SPIFFS_GC_MAX_RUNS          256
+
+#endif

+ 137 - 38
app/spiffs/spiffs.c

@@ -1,10 +1,13 @@
 #include "c_stdio.h"
 #include "platform.h"
 #include "spiffs.h"
-  
+
 spiffs fs;
 
-#define LOG_PAGE_SIZE       256
+#define LOG_PAGE_SIZE       	256
+#define LOG_BLOCK_SIZE		(INTERNAL_FLASH_SECTOR_SIZE * 2)
+#define LOG_BLOCK_SIZE_SMALL_FS	(INTERNAL_FLASH_SECTOR_SIZE)
+#define MIN_BLOCKS_FS		4
   
 static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
 static u8_t spiffs_fds[32*4];
@@ -44,25 +47,121 @@ The small 4KB sectors allow for greater flexibility in applications th
 
 ********************/
 
-void myspiffs_mount() {
-  spiffs_config cfg;
+static bool myspiffs_set_location(spiffs_config *cfg, int align, int offset, int block_size) {
 #ifdef SPIFFS_FIXED_LOCATION
-  cfg.phys_addr = SPIFFS_FIXED_LOCATION;
+  cfg->phys_addr = (SPIFFS_FIXED_LOCATION + block_size - 1) & ~(block_size-1);
+#else
+  cfg->phys_addr = ( u32_t )platform_flash_get_first_free_block_address( NULL ) + offset; 
+  cfg->phys_addr = (cfg->phys_addr + align - 1) & ~(align - 1);
+#endif
+#ifdef SPIFFS_SIZE_1M_BOUNDARY
+  cfg->phys_size = ((0x100000 - 16384 - ( ( u32_t )cfg->phys_addr )) & ~(block_size - 1)) & 0xfffff;
+#else
+  cfg->phys_size = (INTERNAL_FLASH_SIZE - ( ( u32_t )cfg->phys_addr )) & ~(block_size - 1);
+#endif
+  if ((int) cfg->phys_size < 0) {
+    return FALSE;
+  }
+  cfg->log_block_size = block_size; 
+
+  return (cfg->phys_size / block_size) >= MIN_BLOCKS_FS;
+}
+
+/*
+ * Returns  TRUE if FS was found
+ * align must be a power of two
+ */
+static bool myspiffs_set_cfg(spiffs_config *cfg, int align, int offset, bool force_create) {
+  cfg->phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE; // according to datasheet
+  cfg->log_page_size = LOG_PAGE_SIZE; // as we said
+
+  cfg->hal_read_f = my_spiffs_read;
+  cfg->hal_write_f = my_spiffs_write;
+  cfg->hal_erase_f = my_spiffs_erase;
+
+  if (!myspiffs_set_location(cfg, align, offset, LOG_BLOCK_SIZE)) {
+    if (!myspiffs_set_location(cfg, align, offset, LOG_BLOCK_SIZE_SMALL_FS)) {
+      return FALSE;
+    }
+  }
+
+  NODE_DBG("fs.start:%x,max:%x\n",cfg->phys_addr,cfg->phys_size);
+
+#ifdef SPIFFS_USE_MAGIC_LENGTH
+  if (force_create) {
+    return TRUE;
+  }
+
+  int size = SPIFFS_probe_fs(cfg);
+
+  if (size > 0 && size < cfg->phys_size) {
+    NODE_DBG("Overriding size:%x\n",size);
+    cfg->phys_size = size;
+  }
+  if (size > 0) {
+    return TRUE;
+  }
+  return FALSE;
 #else
-  cfg.phys_addr = ( u32_t )platform_flash_get_first_free_block_address( NULL ); 
+  return TRUE;
+#endif
+}
+
+static bool myspiffs_find_cfg(spiffs_config *cfg, bool force_create) {
+  int i;
+
+  if (!force_create) {
+#ifdef SPIFFS_FIXED_LOCATION
+    if (myspiffs_set_cfg(cfg, 0, 0, FALSE)) {
+      return TRUE;
+    }
+#else
+    if (INTERNAL_FLASH_SIZE >= 700000) {
+      for (i = 0; i < 8; i++) {
+	if (myspiffs_set_cfg(cfg, 0x10000, 0x10000 * i, FALSE)) {
+	  return TRUE;
+	}
+      }
+    }
+
+    for (i = 0; i < 8; i++) {
+      if (myspiffs_set_cfg(cfg, LOG_BLOCK_SIZE, LOG_BLOCK_SIZE * i, FALSE)) {
+	return TRUE;
+      }
+    }
+#endif
+  }
+
+  // No existing file system -- set up for a format
+  if (INTERNAL_FLASH_SIZE >= 700000) {
+    myspiffs_set_cfg(cfg, 0x10000, 0x10000, TRUE);
+#ifndef SPIFFS_MAX_FILESYSTEM_SIZE
+    if (cfg->phys_size < 400000) {
+      // Don't waste so much in alignment
+      myspiffs_set_cfg(cfg, LOG_BLOCK_SIZE, LOG_BLOCK_SIZE * 4, TRUE);
+    }
+#endif
+  } else {
+    myspiffs_set_cfg(cfg, LOG_BLOCK_SIZE, 0, TRUE);
+  }
+
+#ifdef SPIFFS_MAX_FILESYSTEM_SIZE
+  if (cfg->phys_size > SPIFFS_MAX_FILESYSTEM_SIZE) {
+    cfg->phys_size = (SPIFFS_MAX_FILESYSTEM_SIZE) & ~(cfg->log_block_size - 1);
+  }
 #endif
-  cfg.phys_addr += 0x3FFF;
-  cfg.phys_addr &= 0xFFFFC000;  // align to 4 sector.
-  cfg.phys_size = INTERNAL_FLASH_SIZE - ( ( u32_t )cfg.phys_addr );
-  cfg.phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE; // according to datasheet
-  cfg.log_block_size = INTERNAL_FLASH_SECTOR_SIZE; // let us not complicate things
-  cfg.log_page_size = LOG_PAGE_SIZE; // as we said
-  NODE_DBG("fs.start:%x,max:%x\n",cfg.phys_addr,cfg.phys_size);
-
-  cfg.hal_read_f = my_spiffs_read;
-  cfg.hal_write_f = my_spiffs_write;
-  cfg.hal_erase_f = my_spiffs_erase;
   
+  return FALSE;
+}
+
+static bool myspiffs_mount_internal(bool force_mount) {
+  spiffs_config cfg;
+  if (!myspiffs_find_cfg(&cfg, force_mount) && !force_mount) {
+    return FALSE;
+  }
+
+  fs.err_code = 0;
+
   int res = SPIFFS_mount(&fs,
     &cfg,
     spiffs_work_buf,
@@ -76,7 +175,12 @@ void myspiffs_mount() {
 #endif
     // myspiffs_check_callback);
     0);
-  NODE_DBG("mount res: %i\n", res);
+  NODE_DBG("mount res: %d, %d\n", res, fs.err_code);
+  return res == SPIFFS_OK;
+}
+
+bool myspiffs_mount() {
+  return myspiffs_mount_internal(FALSE);
 }
 
 void myspiffs_unmount() {
@@ -88,23 +192,16 @@ void myspiffs_unmount() {
 int myspiffs_format( void )
 {
   SPIFFS_unmount(&fs);
-  u32_t sect_first, sect_last;
-#ifdef SPIFFS_FIXED_LOCATION
-  sect_first = SPIFFS_FIXED_LOCATION;
-#else
-  sect_first = ( u32_t )platform_flash_get_first_free_block_address( NULL ); 
-#endif
-  sect_first += 0x3FFF;
-  sect_first &= 0xFFFFC000;  // align to 4 sector.
-  sect_first = platform_flash_get_sector_of_address(sect_first);
-  sect_last = INTERNAL_FLASH_SIZE - SYS_PARAM_SEC_NUM;
-  sect_last = platform_flash_get_sector_of_address(sect_last);
-  NODE_DBG("sect_first: %x, sect_last: %x\n", sect_first, sect_last);
-  while( sect_first <= sect_last )
-    if( platform_flash_erase_sector( sect_first ++ ) == PLATFORM_ERR )
-      return 0;
-  myspiffs_mount();
-  return 1;
+  myspiffs_mount_internal(TRUE);
+  SPIFFS_unmount(&fs);
+
+  NODE_DBG("Formatting: size 0x%x, addr 0x%x\n", fs.cfg.phys_size, fs.cfg.phys_addr);
+
+  if (SPIFFS_format(&fs) < 0) {
+    return 0;
+  }
+
+  return myspiffs_mount();
 }
 
 int myspiffs_check( void )
@@ -120,8 +217,7 @@ int myspiffs_open(const char *name, int flags){
 }
 
 int myspiffs_close( int fd ){
-  SPIFFS_close(&fs, (spiffs_file)fd);
-  return 0;
+  return SPIFFS_close(&fs, (spiffs_file)fd);
 }
 size_t myspiffs_write( int fd, const void* ptr, size_t len ){
 #if 0
@@ -184,7 +280,10 @@ int myspiffs_rename( const char *old, const char *newname ){
   return SPIFFS_rename(&fs, (char *)old, (char *)newname);
 }
 size_t myspiffs_size( int fd ){
-  return SPIFFS_size(&fs, (spiffs_file)fd);
+  int32_t curpos = SPIFFS_tell(&fs, (spiffs_file) fd);
+  int32_t size = SPIFFS_lseek(&fs, (spiffs_file) fd, SPIFFS_SEEK_END, 0);
+  (void) SPIFFS_lseek(&fs, (spiffs_file) fd, SPIFFS_SEEK_SET, curpos);
+  return size;
 }
 #if 0
 void test_spiffs() {

+ 159 - 52
app/spiffs/spiffs.h

@@ -5,8 +5,6 @@
  *      Author: petera
  */
 
-
-
 #ifndef SPIFFS_H_
 #define SPIFFS_H_
 #if defined(__cplusplus)
@@ -49,6 +47,15 @@ extern "C" {
 
 #define SPIFFS_ERR_NO_DELETED_BLOCKS    -10029
 
+#define SPIFFS_ERR_FILE_EXISTS          -10030
+
+#define SPIFFS_ERR_NOT_A_FILE           -10031
+#define SPIFFS_ERR_RO_NOT_IMPL          -10032
+#define SPIFFS_ERR_RO_ABORTED_OPERATION -10033
+#define SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS -10034
+#define SPIFFS_ERR_PROBE_NOT_A_FS       -10035
+#define SPIFFS_ERR_NAME_TOO_LONG        -10036
+
 #define SPIFFS_ERR_INTERNAL             -10050
 
 #define SPIFFS_ERR_TEST                 -10100
@@ -63,12 +70,26 @@ typedef u16_t spiffs_mode;
 // object type
 typedef u8_t spiffs_obj_type;
 
+struct spiffs_t;
+
+#if SPIFFS_HAL_CALLBACK_EXTRA
+
+/* spi read call function type */
+typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst);
+/* spi write call function type */
+typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src);
+/* spi erase call function type */
+typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size);
+
+#else // SPIFFS_HAL_CALLBACK_EXTRA
+
 /* spi read call function type */
 typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst);
 /* spi write call function type */
 typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src);
 /* spi erase call function type */
 typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size);
+#endif // SPIFFS_HAL_CALLBACK_EXTRA
 
 /* file system check callback report operation */
 typedef enum {
@@ -85,12 +106,30 @@ typedef enum {
   SPIFFS_CHECK_FIX_LOOKUP,
   SPIFFS_CHECK_DELETE_ORPHANED_INDEX,
   SPIFFS_CHECK_DELETE_PAGE,
-  SPIFFS_CHECK_DELETE_BAD_FILE,
+  SPIFFS_CHECK_DELETE_BAD_FILE
 } spiffs_check_report;
 
 /* file system check callback function */
+#if SPIFFS_HAL_CALLBACK_EXTRA
+typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report,
+    u32_t arg1, u32_t arg2);
+#else // SPIFFS_HAL_CALLBACK_EXTRA
 typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report,
     u32_t arg1, u32_t arg2);
+#endif // SPIFFS_HAL_CALLBACK_EXTRA
+
+/* file system listener callback operation */
+typedef enum {
+  /* the file has been created */
+  SPIFFS_CB_CREATED = 0,
+  /* the file has been updated or moved to another page */
+  SPIFFS_CB_UPDATED,
+  /* the file has been deleted */
+  SPIFFS_CB_DELETED
+} spiffs_fileop_type;
+
+/* file system listener callback function */
+typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix);
 
 #ifndef SPIFFS_DBG
 #define SPIFFS_DBG(...) \
@@ -108,18 +147,28 @@ typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_repor
 
 /* Any write to the filehandle is appended to end of the file */
 #define SPIFFS_APPEND                   (1<<0)
+#define SPIFFS_O_APPEND                 SPIFFS_APPEND
 /* If the opened file exists, it will be truncated to zero length before opened */
 #define SPIFFS_TRUNC                    (1<<1)
+#define SPIFFS_O_TRUNC                  SPIFFS_TRUNC
 /* If the opened file does not exist, it will be created before opened */
 #define SPIFFS_CREAT                    (1<<2)
+#define SPIFFS_O_CREAT                  SPIFFS_CREAT
 /* The opened file may only be read */
 #define SPIFFS_RDONLY                   (1<<3)
-/* The opened file may only be writted */
+#define SPIFFS_O_RDONLY                 SPIFFS_RDONLY
+/* The opened file may only be written */
 #define SPIFFS_WRONLY                   (1<<4)
-/* The opened file may be both read and writted */
+#define SPIFFS_O_WRONLY                 SPIFFS_WRONLY
+/* The opened file may be both read and written */
 #define SPIFFS_RDWR                     (SPIFFS_RDONLY | SPIFFS_WRONLY)
-/* Any writes to the filehandle will never be cached */
+#define SPIFFS_O_RDWR                   SPIFFS_RDWR
+/* Any writes to the filehandle will never be cached but flushed directly */
 #define SPIFFS_DIRECT                   (1<<5)
+#define SPIFFS_O_DIRECT                 SPIFFS_DIRECT
+/* If SPIFFS_O_CREAT and SPIFFS_O_EXCL are set, SPIFFS_open() shall fail if the file exists */
+#define SPIFFS_EXCL                     (1<<6)
+#define SPIFFS_O_EXCL                   SPIFFS_EXCL
 
 #define SPIFFS_SEEK_SET                 (0)
 #define SPIFFS_SEEK_CUR                 (1)
@@ -164,10 +213,15 @@ typedef struct {
   // logical size of a page, must be at least
   // log_block_size / 8
   u32_t log_page_size;
+
+#endif
+#if SPIFFS_FILEHDL_OFFSET
+  // an integer offset added to each file handle
+  u16_t fh_ix_offset;
 #endif
 } spiffs_config;
 
-typedef struct {
+typedef struct spiffs_t {
   // file system configuration
   spiffs_config cfg;
   // number of logical blocks
@@ -222,9 +276,12 @@ typedef struct {
 
   // check callback function
   spiffs_check_callback check_cb_f;
-
+  // file callback function
+  spiffs_file_callback file_cb_f;
   // mounted flag
   u8_t mounted;
+  // user data
+  void *user_data;
   // config magic
   u32_t config_magic;
 } spiffs;
@@ -234,6 +291,7 @@ typedef struct {
   spiffs_obj_id obj_id;
   u32_t size;
   spiffs_obj_type type;
+  spiffs_page_ix pix;
   u8_t name[SPIFFS_OBJ_NAME_LEN];
 } spiffs_stat;
 
@@ -253,6 +311,40 @@ typedef struct {
 
 // functions
 
+#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
+/**
+ * Special function. This takes a spiffs config struct and returns the number
+ * of blocks this file system was formatted with. This function relies on
+ * that following info is set correctly in given config struct:
+ *
+ * phys_addr, log_page_size, and log_block_size.
+ *
+ * Also, hal_read_f must be set in the config struct.
+ *
+ * One must be sure of the correct page size and that the physical address is
+ * correct in the probed file system when calling this function. It is not
+ * checked if the phys_addr actually points to the start of the file system,
+ * so one might get a false positive if entering a phys_addr somewhere in the
+ * middle of the file system at block boundary. In addition, it is not checked
+ * if the page size is actually correct. If it is not, weird file system sizes
+ * will be returned.
+ *
+ * If this function detects a file system it returns the assumed file system
+ * size, which can be used to set the phys_size.
+ *
+ * Otherwise, it returns an error indicating why it is not regarded as a file
+ * system.
+ *
+ * Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK
+ * macros. It returns the error code directly, instead of as read by
+ * SPIFFS_errno.
+ *
+ * @param config        essential parts of the physical and logical
+ *                      configuration of the file system.
+ */
+s32_t SPIFFS_probe_fs(spiffs_config *config);
+#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
+
 /**
  * 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
@@ -286,19 +378,18 @@ void SPIFFS_unmount(spiffs *fs);
  * @param path          the path of the new file
  * @param mode          ignored, for posix compliance
  */
-s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode);
+s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode);
 
 /**
  * Opens/creates a file.
  * @param fs            the file system struct
  * @param path          the path of the new file
  * @param flags         the flags for the open command, can be combinations of
- *                      SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
- *                      SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT
+ *                      SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY,
+ *                      SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL
  * @param mode          ignored, for posix compliance
  */
-spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode mode);
-
+spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode);
 
 /**
  * Opens a file by given dir entry.
@@ -306,7 +397,7 @@ spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode
  * a normal SPIFFS_open would need to traverse the filesystem again to find
  * the file, whilst SPIFFS_open_by_dirent already knows where the file resides.
  * @param fs            the file system struct
- * @param path          the dir entry to the file
+ * @param e             the dir entry to the file
  * @param flags         the flags for the open command, can be combinations of
  *                      SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
  *                      SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
@@ -315,6 +406,22 @@ 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);
 
+/**
+ * Opens a file by given page index.
+ * Optimization purposes, opens a file by directly pointing to the page
+ * index in the spi flash.
+ * If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE
+ * is returned.
+ * @param fs            the file system struct
+ * @param page_ix       the page index
+ * @param flags         the flags for the open command, can be combinations of
+ *                      SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
+ *                      SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
+ *                      SPIFFS_CREAT will have no effect in this case.
+ * @param mode          ignored, for posix compliance
+ */
+spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode);
+
 /**
  * Reads from given filehandle.
  * @param fs            the file system struct
@@ -336,13 +443,14 @@ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
 s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
 
 /**
- * Moves the read/write file offset
+ * Moves the read/write file offset. Resulting offset is returned or negative if error.
+ * lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset.
  * @param fs            the file system struct
  * @param fh            the filehandle
  * @param offs          how much/where to move the offset
  * @param whence        if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes
  *                      if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset
- *                      if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offset
+ *                      if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative
  */
 s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence);
 
@@ -351,7 +459,7 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence);
  * @param fs            the file system struct
  * @param path          the path of the file to remove
  */
-s32_t SPIFFS_remove(spiffs *fs, char *path);
+s32_t SPIFFS_remove(spiffs *fs, const char *path);
 
 /**
  * Removes a file by filehandle
@@ -366,7 +474,7 @@ s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh);
  * @param path          the path of the file to stat
  * @param s             the stat struct to populate
  */
-s32_t SPIFFS_stat(spiffs *fs, char *path, spiffs_stat *s);
+s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s);
 
 /**
  * Gets file status by filehandle
@@ -388,7 +496,7 @@ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh);
  * @param fs            the file system struct
  * @param fh            the filehandle of the file to close
  */
-void SPIFFS_close(spiffs *fs, spiffs_file fh);
+s32_t SPIFFS_close(spiffs *fs, spiffs_file fh);
 
 /**
  * Renames a file
@@ -396,7 +504,7 @@ void SPIFFS_close(spiffs *fs, spiffs_file fh);
  * @param old           path of file to rename
  * @param newPath       new path of file
  */
-s32_t SPIFFS_rename(spiffs *fs, char *old, char *newPath);
+s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath);
 
 /**
  * Returns last error of last file operation.
@@ -419,7 +527,7 @@ void SPIFFS_clearerr(spiffs *fs);
  * @param name          the name of the directory
  * @param d             pointer the directory stream to be populated
  */
-spiffs_DIR *SPIFFS_opendir(spiffs *fs, char *name, spiffs_DIR *d);
+spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d);
 
 /**
  * Closes a directory stream
@@ -441,13 +549,6 @@ 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.
  * This is an estimation, and depends on if there a many files with little
@@ -527,6 +628,36 @@ s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages);
  */
 s32_t SPIFFS_gc(spiffs *fs, u32_t size);
 
+/**
+ * Check if EOF reached.
+ * @param fs            the file system struct
+ * @param fh            the filehandle of the file to check
+ */
+s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh);
+
+/**
+ * Get position in file.
+ * @param fs            the file system struct
+ * @param fh            the filehandle of the file to check
+ */
+s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh);
+
+/**
+ * Registers a callback function that keeps track on operations on file
+ * headers. Do note, that this callback is called from within internal spiffs
+ * mechanisms. Any operations on the actual file system being callbacked from
+ * in this callback will mess things up for sure - do not do this.
+ * This can be used to track where files are and move around during garbage
+ * collection, which in turn can be used to build location tables in ram.
+ * Used in conjuction with SPIFFS_open_by_page this may improve performance
+ * when opening a lot of files.
+ * Must be invoked after mount.
+ *
+ * @param fs            the file system struct
+ * @param cb_func       the callback on file operations
+ */
+s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func);
+
 #if SPIFFS_TEST_VISUALISATION
 /**
  * Prints out a visualization of the filesystem.
@@ -553,30 +684,6 @@ u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages);
 
 #if SPIFFS_CACHE
 #endif
-
-void myspiffs_mount();
-void myspiffs_unmount();
-int myspiffs_open(const char *name, int flags);
-int myspiffs_close( int fd );
-size_t myspiffs_write( int fd, const void* ptr, size_t len );
-size_t myspiffs_read( int fd, void* ptr, size_t len);
-int myspiffs_lseek( int fd, int off, int whence );
-int myspiffs_eof( int fd );
-int myspiffs_tell( int fd );
-int myspiffs_getc( int fd );
-int myspiffs_ungetc( int c, int fd );
-int myspiffs_flush( int fd );
-int myspiffs_error( int fd );
-void myspiffs_clearerr( int fd );
-int myspiffs_check( void );
-int myspiffs_rename( const char *old, const char *newname );
-size_t myspiffs_size( int fd );
-int myspiffs_format (void);
-
-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

+ 11 - 15
app/spiffs/spiffs_cache.c

@@ -39,7 +39,7 @@ static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) {
         (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
         (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) {
       u8_t *mem =  spiffs_get_cache_page(fs, cache, ix);
-      res = fs->cfg.hal_write_f(SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem);
+      res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem);
     }
 
     cp->flags = 0;
@@ -137,10 +137,7 @@ s32_t spiffs_phys_rd(
   } else {
     if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) {
       // for second layer lookup functions, we do not cache in order to prevent shredding
-      return fs->cfg.hal_read_f(
-          addr ,
-          len,
-          dst);
+      return SPIFFS_HAL_READ(fs, addr, len, dst);
     }
 #if SPIFFS_CACHE_STATS
     fs->cache_misses++;
@@ -151,8 +148,7 @@ s32_t spiffs_phys_rd(
       cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
       cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
     }
-
-    s32_t res2 = fs->cfg.hal_read_f(
+    s32_t res2 = SPIFFS_HAL_READ(fs,
         addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
         SPIFFS_CFG_LOG_PAGE_SZ(fs),
         spiffs_get_cache_page(fs, cache, cp->ix));
@@ -161,7 +157,7 @@ s32_t spiffs_phys_rd(
     }
   }
   u8_t *mem =  spiffs_get_cache_page(fs, cache, cp->ix);
-  c_memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
+  memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
   return res;
 }
 
@@ -186,24 +182,24 @@ s32_t spiffs_phys_wr(
         (op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) {
       // page is being deleted, wipe from cache - unless it is a lookup page
       spiffs_cache_page_free(fs, cp->ix, 0);
-      return fs->cfg.hal_write_f(addr, len, src);
+      return SPIFFS_HAL_WRITE(fs, addr, len, src);
     }
 
     u8_t *mem =  spiffs_get_cache_page(fs, cache, cp->ix);
-    c_memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len);
+    memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len);
 
     cache->last_access++;
     cp->last_access = cache->last_access;
 
     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);
+      return SPIFFS_HAL_WRITE(fs, addr, len, src);
     } else {
       return SPIFFS_OK;
     }
   } else {
     // no cache page, no write cache - just write thru
-    return fs->cfg.hal_write_f(addr, len, src);
+    return SPIFFS_HAL_WRITE(fs, addr, len, src);
   }
 }
 
@@ -282,17 +278,17 @@ void spiffs_cache_init(spiffs *fs) {
   }
 
   spiffs_cache cache;
-  c_memset(&cache, 0, sizeof(spiffs_cache));
+  memset(&cache, 0, sizeof(spiffs_cache));
   cache.cpage_count = cache_entries;
   cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache));
 
   cache.cpage_use_map = 0xffffffff;
   cache.cpage_use_mask = cache_mask;
-  c_memcpy(fs->cache, &cache, sizeof(spiffs_cache));
+  memcpy(fs->cache, &cache, sizeof(spiffs_cache));
 
   spiffs_cache *c = spiffs_get_cache(fs);
 
-  c_memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs));
+  memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs));
 
   c->cpage_use_map &= ~(c->cpage_use_mask);
   for (i = 0; i < cache.cpage_count; i++) {

+ 65 - 43
app/spiffs/spiffs_check.c

@@ -19,9 +19,24 @@
  *      Author: petera
  */
 
+
 #include "spiffs.h"
 #include "spiffs_nucleus.h"
 
+#if !SPIFFS_READ_ONLY
+
+#if SPIFFS_HAL_CALLBACK_EXTRA
+#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \
+  do { \
+    if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \
+  } while (0)
+#else
+#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \
+  do { \
+    if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \
+  } while (0)
+#endif
+
 //---------------------------------------
 // Look up consistency
 
@@ -93,6 +108,7 @@ static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_
   } else {
     // calc entry in index
     entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix);
+
   }
   // load index
   res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
@@ -194,9 +210,9 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
             res = spiffs_page_delete(fs, new_pix);
             SPIFFS_CHECK_RES(res);
             res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
-            if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
+            CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
           } else {
-            if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix);
+            CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix);
           }
           SPIFFS_CHECK_RES(res);
         }
@@ -216,7 +232,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
           SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting %04x to new page %04x\n", cur_pix, new_pix);
           SPIFFS_CHECK_RES(res);
           *reload_lu = 1;
-          if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
+          CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
         }
       } else {
         SPIFFS_CHECK_RES(res);
@@ -254,7 +270,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
                 SPIFFS_CHECK_RES(res);
                 res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
                 *reload_lu = 1;
-                if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
+                CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
               }
               SPIFFS_CHECK_RES(res);
             }
@@ -306,7 +322,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
               new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG;
               res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
               SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page %04x as %04x to pix %04x\n", cur_pix, new_ph.obj_id, new_pix);
-              if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
+              CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
               SPIFFS_CHECK_RES(res);
               *reload_lu = 1;
             } else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) ||
@@ -315,7 +331,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
               //   rewrite as obj_id_lu
               new_ph.obj_id =  lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG;
               SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page %04x as %04x\n", cur_pix, new_ph.obj_id);
-              if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
+              CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
               res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
               SPIFFS_CHECK_RES(res);
               *reload_lu = 1;
@@ -353,7 +369,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
       // if only data page exists, make this page index
       if (data_pix && objix_pix_d == 0) {
         SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n");
-        if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix);
+        CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix);
         spiffs_page_header new_ph;
         spiffs_page_ix new_pix;
         new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX);
@@ -369,7 +385,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
       // if only index exists, make data page
       if (data_pix == 0 && objix_pix_d) {
         SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n");
-        if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix);
+        CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix);
         spiffs_page_header new_ph;
         spiffs_page_ix new_pix;
         new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL);
@@ -406,7 +422,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
           // page referenced by object index but not final
           // just finalize
           SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n");
-          if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
+          CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
           u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL;
           res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
               0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags),
@@ -418,7 +434,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
 
   if (delete_page) {
     SPIFFS_CHECK_DBG("LU: FIXUP: deleting page %04x\n", cur_pix);
-    if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
+    CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
     res = spiffs_page_delete(fs, cur_pix);
     SPIFFS_CHECK_RES(res);
   }
@@ -427,14 +443,14 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
 }
 
 static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry,
-    u32_t user_data, void *user_p) {
-  (void)user_data;
-  (void)user_p;
+    const void *user_const_p, void *user_var_p) {
+  (void)user_const_p;
+  (void)user_var_p;
   s32_t res = SPIFFS_OK;
   spiffs_page_header p_hdr;
   spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
 
-  if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS,
+  CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS,
       (cur_block * 256)/fs->block_count, 0);
 
   // load header
@@ -460,7 +476,7 @@ s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) {
   (void)check_all_objects;
   s32_t res = SPIFFS_OK;
 
-  if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0);
+  CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0);
 
   res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0);
 
@@ -469,10 +485,10 @@ s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) {
   }
 
   if (res != SPIFFS_OK) {
-    if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0);
+    CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0);
   }
 
-  if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0);
+  CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0);
 
   return res;
 }
@@ -501,19 +517,22 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
   while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) {
     // set this flag to abort all checks and rescan the page range
     u8_t restart = 0;
-    c_memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
+    memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
 
     spiffs_block_ix cur_block = 0;
     // build consistency bitmap for id range traversing all blocks
     while (!restart && cur_block < fs->block_count) {
-      if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS,
+      CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS,
           (pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) +
           ((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count),
           0);
-
       // traverse each page except for lookup pages
       spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block;
       while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) {
+        //if ((cur_pix & 0xff) == 0)
+        //  SPIFFS_CHECK_DBG("PA: processing pix %08x, block %08x of pix %08x, block %08x\n",
+        //      cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count);
+
         // read header
         spiffs_page_header p_hdr;
         res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
@@ -598,11 +617,11 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
               if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
                 // index bad also, cannot mend this file
                 SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend - delete object\n", res);
-                if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0);
+                CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0);
                 // delete file
                 res = spiffs_page_delete(fs, cur_pix);
               } else {
-                if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix);
+                CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix);
               }
               SPIFFS_CHECK_RES(res);
               restart = 1;
@@ -636,7 +655,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
                if (data_pix == 0) {
                  // not found, this index is badly borked
                  SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id %04x\n", p_hdr.obj_id);
-                 if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
+                 CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
                  res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
                  SPIFFS_CHECK_RES(res);
                  break;
@@ -648,10 +667,10 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
                  if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
                    // index bad also, cannot mend this file
                    SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res);
-                   if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
+                   CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
                    res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
                  } else {
-                   if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
+                   CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
                  }
                  SPIFFS_CHECK_RES(res);
                  restart = 1;
@@ -669,7 +688,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
                   // the object which is referring to this page
                   SPIFFS_CHECK_DBG("PA: FIXUP: removing object %04x and page %04x\n",
                       p_hdr.obj_id, cur_pix);
-                  if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
+                  CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
                   res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
                   SPIFFS_CHECK_RES(res);
                   // extra precaution, delete this page also
@@ -764,19 +783,19 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
               if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
                 // index bad also, cannot mend this file
                 SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res);
-                if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
+                CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
                 res = spiffs_page_delete(fs, cur_pix);
                 SPIFFS_CHECK_RES(res);
                 res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
               } else {
-                if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
+                CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
               }
               SPIFFS_CHECK_RES(res);
               restart = 1;
               continue;
             } else if (delete_page) {
               SPIFFS_CHECK_DBG("PA: FIXUP: deleting page %04x\n", cur_pix);
-              if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
+              CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
               res = spiffs_page_delete(fs, cur_pix);
             }
             SPIFFS_CHECK_RES(res);
@@ -818,6 +837,8 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
         }
       }
     }
+
+    SPIFFS_CHECK_DBG("PA: processed %04x, restart %i\n", pix_offset, restart);
     // next page range
     if (!restart) {
       pix_offset += pages_per_scan;
@@ -828,12 +849,12 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
 
 // Checks consistency amongst all pages and fixes irregularities
 s32_t spiffs_page_consistency_check(spiffs *fs) {
-  if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0);
+  CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0);
   s32_t res = spiffs_page_consistency_check_i(fs);
   if (res != SPIFFS_OK) {
-    if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0);
+    CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0);
   }
-  if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0);
+  CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0);
   return res;
 }
 
@@ -855,14 +876,14 @@ static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) {
 }
 
 static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block,
-    int cur_entry, u32_t user_data, void *user_p) {
-  (void)user_data;
+    int cur_entry, const void *user_const_p, void *user_var_p) {
+  (void)user_const_p;
   s32_t res_c = SPIFFS_VIS_COUNTINUE;
   s32_t res = SPIFFS_OK;
-  u32_t *log_ix = (u32_t *)user_p;
+  u32_t *log_ix = (u32_t*)user_var_p;
   spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work;
 
-  if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS,
+  CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS,
       (cur_block * 256)/fs->block_count, 0);
 
   if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
@@ -879,7 +900,7 @@ static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id o
         (SPIFFS_PH_FLAG_DELET)) {
       SPIFFS_CHECK_DBG("IX: pix %04x, obj id:%04x spix:%04x header not fully deleted - deleting\n",
           cur_pix, obj_id, p_hdr.span_ix);
-      if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id);
+      CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id);
       res = spiffs_page_delete(fs, cur_pix);
       SPIFFS_CHECK_RES(res);
       return res_c;
@@ -935,7 +956,7 @@ static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id o
       if (delete) {
         SPIFFS_CHECK_DBG("IX: FIXUP: pix %04x, obj id:%04x spix:%04x is orphan index - deleting\n",
             cur_pix, obj_id, p_hdr.span_ix);
-        if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id);
+        CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id);
         res = spiffs_page_delete(fs, cur_pix);
         SPIFFS_CHECK_RES(res);
       }
@@ -956,18 +977,19 @@ s32_t spiffs_object_index_consistency_check(spiffs *fs) {
   // indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit.
   // In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate
   // a reachable/unreachable object id.
-  c_memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
+  memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
   u32_t obj_id_log_ix = 0;
-  if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0);
+  CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0);
   res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix,
-      0, 0);
+        0, 0);
   if (res == SPIFFS_VIS_END) {
     res = SPIFFS_OK;
   }
   if (res != SPIFFS_OK) {
-    if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0);
+    CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0);
   }
-  if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0);
+  CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0);
   return res;
 }
 
+#endif // !SPIFFS_READ_ONLY

+ 63 - 21
app/spiffs/spiffs_config.h

@@ -8,30 +8,28 @@
 #ifndef SPIFFS_CONFIG_H_
 #define SPIFFS_CONFIG_H_
 
-#include "user_config.h"
-#include "c_stdio.h"
-#include "c_stdint.h"
-#include "c_string.h"
-
-// For u32_t etc
-#include "user_interface.h"
+// ----------- 8< ------------
+// Following includes are for the linux test build of spiffs
+// These may/should/must be removed/altered/replaced in your target
+#include "nodemcu_spiffs.h"
+// ----------- >8 ------------
 
 // compile time switches
 
 // Set generic spiffs debug output call.
-#ifndef SPIFFS_DGB
+#ifndef SPIFFS_DBG
 #define SPIFFS_DBG(...) //printf(__VA_ARGS__)
 #endif
 // Set spiffs debug output call for garbage collecting.
-#ifndef SPIFFS_GC_DGB
+#ifndef SPIFFS_GC_DBG
 #define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__)
 #endif
 // Set spiffs debug output call for caching.
-#ifndef SPIFFS_CACHE_DGB
+#ifndef SPIFFS_CACHE_DBG
 #define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__)
 #endif
 // Set spiffs debug output call for system consistency checks.
-#ifndef SPIFFS_CHECK_DGB
+#ifndef SPIFFS_CHECK_DBG
 #define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__)
 #endif
 
@@ -55,10 +53,8 @@
 
 // Enable/disable statistics on caching. Debug/test purpose only.
 #ifndef  SPIFFS_CACHE_STATS
-#define SPIFFS_CACHE_STATS              0
+#define SPIFFS_CACHE_STATS              1
 #endif
-#else
-#define SPIFFS_CACHE_WR                 0
 #endif
 
 // Always check header of each accessed page to ensure consistent state.
@@ -74,7 +70,7 @@
 
 // Enable/disable statistics on gc. Debug/test purpose only.
 #ifndef SPIFFS_GC_STATS
-#define SPIFFS_GC_STATS                 0
+#define SPIFFS_GC_STATS                 1
 #endif
 
 // Garbage collecting examines all pages in a block which and sums up
@@ -99,7 +95,9 @@
 #define SPIFFS_GC_HEUR_W_ERASE_AGE      (50)
 #endif
 
-// Object name maximum length.
+// Object name maximum length. Note that this length include the
+// zero-termination character, meaning maximum string of characters
+// can at most be SPIFFS_OBJ_NAME_LEN - 1.
 #ifndef SPIFFS_OBJ_NAME_LEN
 #define SPIFFS_OBJ_NAME_LEN             (32)
 #endif
@@ -119,19 +117,29 @@
 #define SPIFFS_USE_MAGIC                (0)
 #endif
 
+#if SPIFFS_USE_MAGIC
+// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is
+// enabled, the magic will also be dependent on the length of the filesystem.
+// For example, a filesystem configured and formatted for 4 megabytes will not
+// be accepted for mounting with a configuration defining the filesystem as 2
+// megabytes.
+#ifndef SPIFFS_USE_MAGIC_LENGTH
+#define SPIFFS_USE_MAGIC_LENGTH         (0)
+#endif
+#endif
+
 // SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level
 // These should be defined on a multithreaded system
 
-// define this to entering a mutex if you're running on a multithreaded system
+// define this to enter a mutex if you're running on a multithreaded system
 #ifndef SPIFFS_LOCK
 #define SPIFFS_LOCK(fs)
 #endif
-// define this to exiting a mutex if you're running on a multithreaded system
+// define this to exit a mutex if you're running on a multithreaded system
 #ifndef SPIFFS_UNLOCK
 #define SPIFFS_UNLOCK(fs)
 #endif
 
-
 // Enable if only one spiffs instance with constant configuration will exist
 // on the target. This will reduce calculations, flash and memory accesses.
 // Parts of configuration must be defined below instead of at time of mount.
@@ -159,7 +167,41 @@
 #endif
 #endif
 
-// Set SPFIFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
+// Enable this if your target needs aligned data for index tables
+#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
+#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES       0
+#endif
+
+// Enable this if you want the HAL callbacks to be called with the spiffs struct
+#ifndef SPIFFS_HAL_CALLBACK_EXTRA
+#define SPIFFS_HAL_CALLBACK_EXTRA         0
+#endif
+
+// Enable this if you want to add an integer offset to all file handles
+// (spiffs_file). This is useful if running multiple instances of spiffs on
+// same target, in order to recognise to what spiffs instance a file handle
+// belongs.
+// NB: This adds config field fh_ix_offset in the configuration struct when
+// mounting, which must be defined.
+#ifndef SPIFFS_FILEHDL_OFFSET
+#define SPIFFS_FILEHDL_OFFSET                 0
+#endif
+
+// Enable this to compile a read only version of spiffs.
+// This will reduce binary size of spiffs. All code comprising modification
+// of the file system will not be compiled. Some config will be ignored.
+// HAL functions for erasing and writing to spi-flash may be null. Cache
+// can be disabled for even further binary size reduction (and ram savings).
+// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL.
+// If the file system cannot be mounted due to aborted erase operation and
+// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be
+// returned.
+// Might be useful for e.g. bootloaders and such.
+#ifndef SPIFFS_READ_ONLY
+#define SPIFFS_READ_ONLY                      0
+#endif
+
+// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
 // in the api. This function will visualize all filesystem using given printf
 // function.
 #ifndef SPIFFS_TEST_VISUALISATION
@@ -167,7 +209,7 @@
 #endif
 #if SPIFFS_TEST_VISUALISATION
 #ifndef spiffs_printf
-#define spiffs_printf(...)                c_printf(__VA_ARGS__)
+#define spiffs_printf(...)                printf(__VA_ARGS__)
 #endif
 // spiffs_printf argument for a free page
 #ifndef SPIFFS_TEST_VIS_FREE_STR

+ 9 - 8
app/spiffs/spiffs_gc.c

@@ -1,6 +1,8 @@
 #include "spiffs.h"
 #include "spiffs_nucleus.h"
 
+#if !SPIFFS_READ_ONLY
+
 // Erases a logical block and updates the erase counter.
 // If cache is enabled, all pages that might be cached in this block
 // is dropped.
@@ -36,7 +38,7 @@ s32_t spiffs_gc_quick(
   int cur_entry = 0;
   spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
 
-  SPIFFS_GC_DBG("gc_quick: running\n", cur_block);
+  SPIFFS_GC_DBG("gc_quick: running\n");
 #if SPIFFS_GC_STATS
   fs->stats_gc_runs++;
 #endif
@@ -246,17 +248,15 @@ s32_t spiffs_gc_find_candidate(
   // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score
   int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t)));
   *candidate_count = 0;
-  c_memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
+  memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
 
   // divide up work area into block indices and scores
-  // todo alignment?
-  // YES DO PROPER ALIGNMENT !^@#%!@%!
-  if (max_candidates & 1)
-    ++max_candidates; // HACK WORKAROUND ICK for sizeof(spiffs_block_idx)==2
-
   spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
   s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix));
 
+   // align cand_scores on s32_t boundary
+  cand_scores = (s32_t*)(((ptrdiff_t)cand_scores + sizeof(ptrdiff_t) - 1) & ~(sizeof(ptrdiff_t) - 1));
+
   *block_candidates = cand_blocks;
 
   int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
@@ -385,7 +385,7 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
 
   SPIFFS_GC_DBG("gc_clean: cleaning block %i\n", bix);
 
-  c_memset(&gc, 0, sizeof(spiffs_gc));
+  memset(&gc, 0, sizeof(spiffs_gc));
   gc.state = FIND_OBJ_DATA;
 
   if (fs->free_cursor_block_ix == bix) {
@@ -570,3 +570,4 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
   return res;
 }
 
+#endif // !SPIFFS_READ_ONLY

+ 277 - 116
app/spiffs/spiffs_hydrogen.c

@@ -8,7 +8,17 @@
 #include "spiffs.h"
 #include "spiffs_nucleus.h"
 
+#if SPIFFS_FILEHDL_OFFSET
+#define SPIFFS_FH_OFFS(fs, fh)   ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0)
+#define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0)
+#else
+#define SPIFFS_FH_OFFS(fs, fh)   (fh)
+#define SPIFFS_FH_UNOFFS(fs, fh) (fh)
+#endif
+
+#if SPIFFS_CACHE == 1
 static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh);
+#endif
 
 #if SPIFFS_BUFFER_HELP
 u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) {
@@ -26,6 +36,10 @@ u8_t SPIFFS_mounted(spiffs *fs) {
 }
 
 s32_t SPIFFS_format(spiffs *fs) {
+#if SPIFFS_READ_ONLY
+  (void)fs;
+  return SPIFFS_ERR_RO_NOT_IMPL;
+#else
   SPIFFS_API_CHECK_CFG(fs);
   if (SPIFFS_CHECK_MOUNT(fs)) {
     fs->err_code = SPIFFS_ERR_MOUNTED;
@@ -49,25 +63,35 @@ s32_t SPIFFS_format(spiffs *fs) {
   SPIFFS_UNLOCK(fs);
 
   return 0;
+#endif // SPIFFS_READ_ONLY
+}
+
+#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
+
+s32_t SPIFFS_probe_fs(spiffs_config *config) {
+  s32_t res = spiffs_probe(config);
+  return res;
 }
 
+#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==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,
     spiffs_check_callback check_cb_f) {
+  void *user_data;
   SPIFFS_LOCK(fs);
-  c_memset(fs, 0, sizeof(spiffs));
-  c_memcpy(&fs->cfg, config, sizeof(spiffs_config));
+  user_data = fs->user_data;
+  memset(fs, 0, sizeof(spiffs));
+  memcpy(&fs->cfg, config, sizeof(spiffs_config));
+  fs->user_data = user_data;
   fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs);
   fs->work = &work[0];
   fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)];
-  c_memset(fd_space, 0, fd_space_size);
-  // align fd_space pointer to pointer size byte boundary, below is safe
+  memset(fd_space, 0, fd_space_size);
+  // align fd_space pointer to pointer size byte boundary
   u8_t ptr_size = sizeof(void*);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
-  u8_t addr_lsb = ((u8_t)fd_space) & (ptr_size-1);
-#pragma GCC diagnostic pop
+  u8_t addr_lsb = ((u8_t)(intptr_t)fd_space) & (ptr_size-1);
   if (addr_lsb) {
     fd_space += (ptr_size-addr_lsb);
     fd_space_size -= (ptr_size-addr_lsb);
@@ -75,11 +99,8 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
   fs->fd_space = fd_space;
   fs->fd_count = (fd_space_size/sizeof(spiffs_fd));
 
-  // align cache pointer to 4 byte boundary, below is safe
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
-  addr_lsb = ((u8_t)cache) & (ptr_size-1);
-#pragma GCC diagnostic pop
+  // align cache pointer to 4 byte boundary
+  addr_lsb = ((u8_t)(intptr_t)cache) & (ptr_size-1);
   if (addr_lsb) {
     u8_t *cache_8 = (u8_t *)cache;
     cache_8 += (ptr_size-addr_lsb);
@@ -89,9 +110,10 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
   if (cache_size & (ptr_size-1)) {
     cache_size -= (cache_size & (ptr_size-1));
   }
+
 #if SPIFFS_CACHE
   fs->cache = cache;
-  fs->cache_size = (cache_size > (config->log_page_size*32)) ? config->log_page_size*32 : cache_size;
+  fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs)*32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs)*32 : cache_size;
   spiffs_cache_init(fs);
 #endif
 
@@ -152,43 +174,68 @@ void SPIFFS_clearerr(spiffs *fs) {
   fs->err_code = SPIFFS_OK;
 }
 
-s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode) {
+s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) {
+#if SPIFFS_READ_ONLY
+  (void)fs; (void)path; (void)mode;
+  return SPIFFS_ERR_RO_NOT_IMPL;
+#else
   (void)mode;
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
+  if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) {
+    SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG);
+  }
   SPIFFS_LOCK(fs);
   spiffs_obj_id obj_id;
   s32_t res;
 
-  res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (u8_t *)path);
+  res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t*)path);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
-  res = spiffs_object_create(fs, obj_id, (u8_t *)path, SPIFFS_TYPE_FILE, 0);
+  res = spiffs_object_create(fs, obj_id, (const u8_t*)path, SPIFFS_TYPE_FILE, 0);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
   SPIFFS_UNLOCK(fs);
   return 0;
+#endif // SPIFFS_READ_ONLY
 }
 
-spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode mode) {
+spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) {
   (void)mode;
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
+  if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) {
+    SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG);
+  }
   SPIFFS_LOCK(fs);
 
   spiffs_fd *fd;
   spiffs_page_ix pix;
 
+#if SPIFFS_READ_ONLY
+  // not valid flags in read only mode
+  flags &= ~SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC;
+#endif // SPIFFS_READ_ONLY
+
   s32_t res = spiffs_fd_find_new(fs, &fd);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
-  res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)path, &pix);
-  if ((flags & SPIFFS_CREAT) == 0) {
+  res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix);
+  if ((flags & SPIFFS_O_CREAT) == 0) {
     if (res < SPIFFS_OK) {
       spiffs_fd_return(fs, fd->file_nbr);
     }
     SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
   }
 
-  if ((flags & SPIFFS_CREAT) && res == SPIFFS_ERR_NOT_FOUND) {
+  if (res == SPIFFS_OK &&
+      (flags & (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) == (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) {
+    // creat and excl and file exists - fail
+    res = SPIFFS_ERR_FILE_EXISTS;
+    spiffs_fd_return(fs, fd->file_nbr);
+    SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+  }
+
+  if ((flags & SPIFFS_O_CREAT) && res == SPIFFS_ERR_NOT_FOUND) {
+#if !SPIFFS_READ_ONLY
     spiffs_obj_id obj_id;
     // no need to enter conflicting name here, already looked for it above
     res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, 0);
@@ -196,12 +243,13 @@ spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode
       spiffs_fd_return(fs, fd->file_nbr);
     }
     SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
-    res = spiffs_object_create(fs, obj_id, (u8_t*)path, SPIFFS_TYPE_FILE, &pix);
+    res = spiffs_object_create(fs, obj_id, (const u8_t*)path, SPIFFS_TYPE_FILE, &pix);
     if (res < SPIFFS_OK) {
       spiffs_fd_return(fs, fd->file_nbr);
     }
     SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
-    flags &= ~SPIFFS_TRUNC;
+    flags &= ~SPIFFS_O_TRUNC;
+#endif // !SPIFFS_READ_ONLY
   } else {
     if (res < SPIFFS_OK) {
       spiffs_fd_return(fs, fd->file_nbr);
@@ -213,19 +261,21 @@ spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode
     spiffs_fd_return(fs, fd->file_nbr);
   }
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
-  if (flags & SPIFFS_TRUNC) {
+#if !SPIFFS_READ_ONLY
+  if (flags & SPIFFS_O_TRUNC) {
     res = spiffs_object_truncate(fd, 0, 0);
     if (res < SPIFFS_OK) {
       spiffs_fd_return(fs, fd->file_nbr);
     }
     SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
   }
+#endif // !SPIFFS_READ_ONLY
 
   fd->fdoffset = 0;
 
   SPIFFS_UNLOCK(fs);
 
-  return fd->file_nbr;
+  return SPIFFS_FH_OFFS(fs, fd->file_nbr);
 }
 
 spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) {
@@ -243,19 +293,67 @@ spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_fl
     spiffs_fd_return(fs, fd->file_nbr);
   }
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
-  if (flags & SPIFFS_TRUNC) {
+#if !SPIFFS_READ_ONLY
+  if (flags & SPIFFS_O_TRUNC) {
+    res = spiffs_object_truncate(fd, 0, 0);
+    if (res < SPIFFS_OK) {
+      spiffs_fd_return(fs, fd->file_nbr);
+    }
+    SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+  }
+#endif // !SPIFFS_READ_ONLY
+
+  fd->fdoffset = 0;
+
+  SPIFFS_UNLOCK(fs);
+
+  return SPIFFS_FH_OFFS(fs, fd->file_nbr);
+}
+
+spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) {
+  SPIFFS_API_CHECK_CFG(fs);
+  SPIFFS_API_CHECK_MOUNT(fs);
+  SPIFFS_LOCK(fs);
+
+  spiffs_fd *fd;
+
+  s32_t res = spiffs_fd_find_new(fs, &fd);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+  if (SPIFFS_IS_LOOKUP_PAGE(fs, page_ix)) {
+    res = SPIFFS_ERR_NOT_A_FILE;
+    spiffs_fd_return(fs, fd->file_nbr);
+    SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+  }
+
+  res = spiffs_object_open_by_page(fs, page_ix, fd, flags, mode);
+  if (res == SPIFFS_ERR_IS_FREE ||
+      res == SPIFFS_ERR_DELETED ||
+      res == SPIFFS_ERR_NOT_FINALIZED ||
+      res == SPIFFS_ERR_NOT_INDEX ||
+      res == SPIFFS_ERR_INDEX_SPAN_MISMATCH) {
+    res = SPIFFS_ERR_NOT_A_FILE;
+  }
+  if (res < SPIFFS_OK) {
+    spiffs_fd_return(fs, fd->file_nbr);
+  }
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+#if !SPIFFS_READ_ONLY
+  if (flags & SPIFFS_O_TRUNC) {
     res = spiffs_object_truncate(fd, 0, 0);
     if (res < SPIFFS_OK) {
       spiffs_fd_return(fs, fd->file_nbr);
     }
     SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
   }
+#endif // !SPIFFS_READ_ONLY
 
   fd->fdoffset = 0;
 
   SPIFFS_UNLOCK(fs);
 
-  return fd->file_nbr;
+  return SPIFFS_FH_OFFS(fs, fd->file_nbr);
 }
 
 s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
@@ -266,14 +364,21 @@ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
   spiffs_fd *fd;
   s32_t res;
 
+  fh = SPIFFS_FH_UNOFFS(fs, fh);
   res = spiffs_fd_get(fs, fh, &fd);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
-  if ((fd->flags & SPIFFS_RDONLY) == 0) {
+  if ((fd->flags & SPIFFS_O_RDONLY) == 0) {
     res = SPIFFS_ERR_NOT_READABLE;
     SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
   }
 
+  if (fd->size == SPIFFS_UNDEFINED_LEN && len > 0) {
+    // special case for zero sized files
+    res = SPIFFS_ERR_END_OF_OBJECT;
+    SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+  }
+
 #if SPIFFS_CACHE_WR
   spiffs_fflush_cache(fs, fh);
 #endif
@@ -305,6 +410,7 @@ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
   return len;
 }
 
+#if !SPIFFS_READ_ONLY
 static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) {
   (void)fs;
   s32_t res = SPIFFS_OK;
@@ -326,8 +432,13 @@ static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offs
   return len;
 
 }
+#endif // !SPIFFS_READ_ONLY
 
 s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
+#if SPIFFS_READ_ONLY
+  (void)fs; (void)fh; (void)buf; (void)len;
+  return SPIFFS_ERR_RO_NOT_IMPL;
+#else
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
   SPIFFS_LOCK(fs);
@@ -336,14 +447,19 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
   s32_t res;
   u32_t offset;
 
+  fh = SPIFFS_FH_UNOFFS(fs, fh);
   res = spiffs_fd_get(fs, fh, &fd);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
-  if ((fd->flags & SPIFFS_WRONLY) == 0) {
+  if ((fd->flags & SPIFFS_O_WRONLY) == 0) {
     res = SPIFFS_ERR_NOT_WRITABLE;
     SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
   }
 
+  if ((fd->flags & SPIFFS_O_APPEND)) {
+    fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size;
+  }
+
   offset = fd->fdoffset;
 
 #if SPIFFS_CACHE_WR
@@ -352,7 +468,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
     fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd);
   }
 #endif
-  if (fd->flags & SPIFFS_APPEND) {
+  if (fd->flags & SPIFFS_O_APPEND) {
     if (fd->size == SPIFFS_UNDEFINED_LEN) {
       offset = 0;
     } else {
@@ -366,7 +482,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
   }
 
 #if SPIFFS_CACHE_WR
-  if ((fd->flags & SPIFFS_DIRECT) == 0) {
+  if ((fd->flags & SPIFFS_O_DIRECT) == 0) {
     if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) {
       // small write, try to cache it
       u8_t alloc_cpage = 1;
@@ -383,7 +499,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
               spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
               fd->cache_page->offset, fd->cache_page->size);
           spiffs_cache_fd_release(fs, fd->cache_page);
-          SPIFFS_API_CHECK_RES(fs, res);
+          SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
         } else {
           // writing within cache
           alloc_cpage = 0;
@@ -407,14 +523,14 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
             offset, offset_in_cpage, len);
         spiffs_cache *cache = spiffs_get_cache(fs);
         u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix);
-        c_memcpy(&cpage_data[offset_in_cpage], buf, len);
+        memcpy(&cpage_data[offset_in_cpage], buf, len);
         fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len);
         fd->fdoffset += len;
         SPIFFS_UNLOCK(fs);
         return len;
       } else {
         res = spiffs_hydro_write(fs, fd, buf, offset, len);
-        SPIFFS_API_CHECK_RES(fs, res);
+        SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
         fd->fdoffset += len;
         SPIFFS_UNLOCK(fs);
         return res;
@@ -429,21 +545,22 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
             spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
             fd->cache_page->offset, fd->cache_page->size);
         spiffs_cache_fd_release(fs, fd->cache_page);
-        SPIFFS_API_CHECK_RES(fs, res);
+        SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
         res = spiffs_hydro_write(fs, fd, buf, offset, len);
-        SPIFFS_API_CHECK_RES(fs, res);
+        SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
       }
     }
   }
 #endif
 
   res = spiffs_hydro_write(fs, fd, buf, offset, len);
-  SPIFFS_API_CHECK_RES(fs, res);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
   fd->fdoffset += len;
 
   SPIFFS_UNLOCK(fs);
 
   return res;
+#endif // SPIFFS_READ_ONLY
 }
 
 s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
@@ -453,8 +570,9 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
 
   spiffs_fd *fd;
   s32_t res;
+  fh = SPIFFS_FH_UNOFFS(fs, fh);
   res = spiffs_fd_get(fs, fh, &fd);
-  SPIFFS_API_CHECK_RES(fs, res);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
 #if SPIFFS_CACHE_WR
   spiffs_fflush_cache(fs, fh);
@@ -469,7 +587,7 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
     break;
   }
 
-  if (offs > (s32_t)fd->size) {
+  if ((offs > (s32_t)fd->size) && (SPIFFS_UNDEFINED_LEN != fd->size)) {
     res = SPIFFS_ERR_END_OF_OBJECT;
   }
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
@@ -491,9 +609,16 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
   return offs;
 }
 
-s32_t SPIFFS_remove(spiffs *fs, char *path) {
+s32_t SPIFFS_remove(spiffs *fs, const char *path) {
+#if SPIFFS_READ_ONLY
+  (void)fs; (void)path;
+  return SPIFFS_ERR_RO_NOT_IMPL;
+#else
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
+  if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) {
+    SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG);
+  }
   SPIFFS_LOCK(fs);
 
   spiffs_fd *fd;
@@ -503,7 +628,7 @@ s32_t SPIFFS_remove(spiffs *fs, char *path) {
   res = spiffs_fd_find_new(fs, &fd);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
-  res = spiffs_object_find_object_index_header_by_name(fs, (u8_t *)path, &pix);
+  res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix);
   if (res != SPIFFS_OK) {
     spiffs_fd_return(fs, fd->file_nbr);
   }
@@ -523,19 +648,25 @@ s32_t SPIFFS_remove(spiffs *fs, char *path) {
 
   SPIFFS_UNLOCK(fs);
   return 0;
+#endif // SPIFFS_READ_ONLY
 }
 
 s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) {
+#if SPIFFS_READ_ONLY
+  (void)fs; (void)fh;
+  return SPIFFS_ERR_RO_NOT_IMPL;
+#else
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
   SPIFFS_LOCK(fs);
 
   spiffs_fd *fd;
   s32_t res;
+  fh = SPIFFS_FH_UNOFFS(fs, fh);
   res = spiffs_fd_get(fs, fh, &fd);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
-  if ((fd->flags & SPIFFS_WRONLY) == 0) {
+  if ((fd->flags & SPIFFS_O_WRONLY) == 0) {
     res = SPIFFS_ERR_NOT_WRITABLE;
     SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
   }
@@ -551,9 +682,11 @@ s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) {
   SPIFFS_UNLOCK(fs);
 
   return 0;
+#endif // SPIFFS_READ_ONLY
 }
 
 static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) {
+  (void)fh;
   spiffs_page_object_ix_header objix_hdr;
   spiffs_obj_id obj_id;
   s32_t res =_spiffs_rd(fs,  SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh,
@@ -566,23 +699,27 @@ static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spi
       obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id);
   SPIFFS_API_CHECK_RES(fs, res);
 
-  s->obj_id = obj_id;
+  s->obj_id = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
   s->type = objix_hdr.type;
   s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size;
+  s->pix = pix;
   strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN);
 
   return res;
 }
 
-s32_t SPIFFS_stat(spiffs *fs, char *path, spiffs_stat *s) {
+s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) {
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
+  if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) {
+    SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG);
+  }
   SPIFFS_LOCK(fs);
 
   s32_t res;
   spiffs_page_ix pix;
 
-  res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)path, &pix);
+  res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
   res = spiffs_stat_pix(fs, pix, 0, s);
@@ -600,6 +737,7 @@ s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) {
   spiffs_fd *fd;
   s32_t res;
 
+  fh = SPIFFS_FH_UNOFFS(fs, fh);
   res = spiffs_fd_get(fs, fh, &fd);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
@@ -616,15 +754,18 @@ s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) {
 
 // Checks if there are any cached writes for the object id associated with
 // given filehandle. If so, these writes are flushed.
+#if SPIFFS_CACHE == 1
 static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) {
+  (void)fs;
+  (void)fh;
   s32_t res = SPIFFS_OK;
-#if SPIFFS_CACHE_WR
+#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR
 
   spiffs_fd *fd;
   res = spiffs_fd_get(fs, fh, &fd);
   SPIFFS_API_CHECK_RES(fs, res);
 
-  if ((fd->flags & SPIFFS_DIRECT) == 0) {
+  if ((fd->flags & SPIFFS_O_DIRECT) == 0) {
     if (fd->cache_page == 0) {
       // see if object id is associated with cache already
       fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd);
@@ -645,13 +786,16 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) {
 
   return res;
 }
+#endif
 
 s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) {
+  (void)fh;
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
   s32_t res = SPIFFS_OK;
-#if SPIFFS_CACHE_WR
+#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR
   SPIFFS_LOCK(fs);
+  fh = SPIFFS_FH_UNOFFS(fs, fh);
   res = spiffs_fflush_cache(fs, fh);
   SPIFFS_API_CHECK_RES_UNLOCK(fs,res);
   SPIFFS_UNLOCK(fs);
@@ -660,38 +804,46 @@ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) {
   return res;
 }
 
-void SPIFFS_close(spiffs *fs, spiffs_file fh) {
-  if (!SPIFFS_CHECK_CFG((fs))) {
-    (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED;
-    return;
-  }
+s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) {
+  SPIFFS_API_CHECK_CFG(fs);
+  SPIFFS_API_CHECK_MOUNT(fs);
 
-  if (!SPIFFS_CHECK_MOUNT(fs)) {
-    fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
-    return;
-  }
+  s32_t res = SPIFFS_OK;
   SPIFFS_LOCK(fs);
 
+  fh = SPIFFS_FH_UNOFFS(fs, fh);
 #if SPIFFS_CACHE
-  spiffs_fflush_cache(fs, fh);
+  res = spiffs_fflush_cache(fs, fh);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 #endif
-  spiffs_fd_return(fs, fh);
+  res = spiffs_fd_return(fs, fh);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
   SPIFFS_UNLOCK(fs);
+
+  return res;
 }
 
-s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) {
+s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) {
+#if SPIFFS_READ_ONLY
+  (void)fs; (void)old_path; (void)new_path;
+  return SPIFFS_ERR_RO_NOT_IMPL;
+#else
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
+  if (strlen(new_path) > SPIFFS_OBJ_NAME_LEN - 1 ||
+      strlen(old_path) > SPIFFS_OBJ_NAME_LEN - 1) {
+    SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG);
+  }
   SPIFFS_LOCK(fs);
 
   spiffs_page_ix pix_old, pix_dummy;
   spiffs_fd *fd;
 
-  s32_t res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)old, &pix_old);
+  s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)old_path, &pix_old);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
-  res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)new, &pix_dummy);
+  res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)new_path, &pix_dummy);
   if (res == SPIFFS_ERR_NOT_FOUND) {
     res = SPIFFS_OK;
   } else if (res == SPIFFS_OK) {
@@ -708,7 +860,7 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) {
   }
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
-  res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (u8_t*)new,
+  res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t*)new_path,
       0, &pix_dummy);
 
   spiffs_fd_return(fs, fd->file_nbr);
@@ -718,9 +870,10 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) {
   SPIFFS_UNLOCK(fs);
 
   return res;
+#endif // SPIFFS_READ_ONLY
 }
 
-spiffs_DIR *SPIFFS_opendir(spiffs *fs, char *name, spiffs_DIR *d) {
+spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) {
   (void)name;
 
   if (!SPIFFS_CHECK_CFG((fs))) {
@@ -744,9 +897,9 @@ static s32_t spiffs_read_dir_v(
     spiffs_obj_id obj_id,
     spiffs_block_ix bix,
     int ix_entry,
-    u32_t user_data,
-    void *user_p) {
-  (void)user_data;
+    const void *user_const_p,
+    void *user_var_p) {
+  (void)user_const_p;
   s32_t res;
   spiffs_page_object_ix_header objix_hdr;
   if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED ||
@@ -762,10 +915,9 @@ static s32_t spiffs_read_dir_v(
       objix_hdr.p_hdr.span_ix == 0 &&
       (objix_hdr.p_hdr.flags& (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) ==
           (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
-    struct spiffs_dirent *e = (struct spiffs_dirent *)user_p;
+    struct spiffs_dirent *e = (struct spiffs_dirent*)user_var_p;
     e->obj_id = obj_id;
-    strncpy((char *)e->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN);
-
+    strcpy((char *)e->name, (char *)objix_hdr.name);
     e->type = objix_hdr.type;
     e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size;
     e->pix = pix;
@@ -780,7 +932,7 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
     d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
     return 0;
   }
-  SPIFFS_LOCK(fs);
+  SPIFFS_LOCK(d->fs);
 
   spiffs_block_ix bix;
   int entry;
@@ -804,7 +956,7 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
   } else {
     d->fs->err_code = res;
   }
-  SPIFFS_UNLOCK(fs);
+  SPIFFS_UNLOCK(d->fs);
   return ret;
 }
 
@@ -815,6 +967,10 @@ s32_t SPIFFS_closedir(spiffs_DIR *d) {
 }
 
 s32_t SPIFFS_check(spiffs *fs) {
+#if SPIFFS_READ_ONLY
+  (void)fs;
+  return SPIFFS_ERR_RO_NOT_IMPL;
+#else
   s32_t res;
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
@@ -830,6 +986,7 @@ s32_t SPIFFS_check(spiffs *fs) {
 
   SPIFFS_UNLOCK(fs);
   return res;
+#endif // SPIFFS_READ_ONLY
 }
 
 s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) {
@@ -856,91 +1013,96 @@ s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) {
   return res;
 }
 
-s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) {
+s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) {
+#if SPIFFS_READ_ONLY
+  (void)fs; (void)max_free_pages;
+  return SPIFFS_ERR_RO_NOT_IMPL;
+#else
+  s32_t res;
+  SPIFFS_API_CHECK_CFG(fs);
   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;
+  res = spiffs_gc_quick(fs, max_free_pages);
 
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
   SPIFFS_UNLOCK(fs);
-  return res;
+  return 0;
+#endif // SPIFFS_READ_ONLY
 }
 
-s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) {
-  SPIFFS_API_CHECK_MOUNT(fs);
-  SPIFFS_LOCK(fs);
 
-  spiffs_fd *fd;
+s32_t SPIFFS_gc(spiffs *fs, u32_t size) {
+#if SPIFFS_READ_ONLY
+  (void)fs; (void)size;
+  return SPIFFS_ERR_RO_NOT_IMPL;
+#else
   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
+  SPIFFS_API_CHECK_CFG(fs);
+  SPIFFS_API_CHECK_MOUNT(fs);
+  SPIFFS_LOCK(fs);
 
-  res = (fd->fdoffset == fd->size);
+  res = spiffs_gc_check(fs, size);
 
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
   SPIFFS_UNLOCK(fs);
-  return res;
+  return 0;
+#endif // SPIFFS_READ_ONLY
 }
 
-s32_t SPIFFS_size(spiffs *fs, spiffs_file fh) {
+s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) {
+  s32_t res;
+  SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
   SPIFFS_LOCK(fs);
 
+  fh = SPIFFS_FH_UNOFFS(fs, fh);
+
   spiffs_fd *fd;
-  s32_t res;
   res = spiffs_fd_get(fs, fh, &fd);
-  SPIFFS_API_CHECK_RES(fs, res);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
 #if SPIFFS_CACHE_WR
-  spiffs_fflush_cache(fs, fh);
+  res = spiffs_fflush_cache(fs, fh);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 #endif
 
-  res = fd->size;
+  res = (fd->fdoffset >= (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size));
 
   SPIFFS_UNLOCK(fs);
   return res;
 }
 
-s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) {
+s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) {
   s32_t res;
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
   SPIFFS_LOCK(fs);
 
-  res = spiffs_gc_quick(fs, max_free_pages);
+  fh = SPIFFS_FH_UNOFFS(fs, fh);
 
+  spiffs_fd *fd;
+  res = spiffs_fd_get(fs, fh, &fd);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
-  SPIFFS_UNLOCK(fs);
-  return 0;
-}
 
+#if SPIFFS_CACHE_WR
+  res = spiffs_fflush_cache(fs, fh);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+#endif
 
-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 = fd->fdoffset;
 
-  res = spiffs_gc_check(fs, size);
+  SPIFFS_UNLOCK(fs);
+  return res;
+}
 
-  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) {
+  SPIFFS_LOCK(fs);
+  fs->file_cb_f = cb_func;
   SPIFFS_UNLOCK(fs);
   return 0;
 }
 
-
 #if SPIFFS_TEST_VISUALISATION
 s32_t SPIFFS_vis(spiffs *fs) {
   s32_t res = SPIFFS_OK;
@@ -1008,11 +1170,10 @@ s32_t SPIFFS_vis(spiffs *fs) {
   spiffs_printf("free_blocks: %i\n", fs->free_blocks);
   spiffs_printf("page_alloc:  %i\n", fs->stats_p_allocated);
   spiffs_printf("page_delet:  %i\n", fs->stats_p_deleted);
+  SPIFFS_UNLOCK(fs);
   u32_t total, used;
   SPIFFS_info(fs, &total, &used);
   spiffs_printf("used:        %i of %i\n", used, total);
-
-  SPIFFS_UNLOCK(fs);
   return res;
 }
 #endif

+ 181 - 66
app/spiffs/spiffs_nucleus.c

@@ -29,6 +29,7 @@ static s32_t spiffs_page_data_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pi
   return res;
 }
 
+#if !SPIFFS_READ_ONLY
 static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) {
   s32_t res = SPIFFS_OK;
   if (pix == (spiffs_page_ix)-1) {
@@ -56,6 +57,7 @@ static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix p
 #endif
   return res;
 }
+#endif // !SPIFFS_READ_ONLY
 
 #if !SPIFFS_CACHE
 
@@ -64,7 +66,7 @@ s32_t spiffs_phys_rd(
     u32_t addr,
     u32_t len,
     u8_t *dst) {
-  return fs->cfg.hal_read_f(addr, len, dst);
+  return SPIFFS_HAL_READ(fs, addr, len, dst);
 }
 
 s32_t spiffs_phys_wr(
@@ -72,17 +74,19 @@ s32_t spiffs_phys_wr(
     u32_t addr,
     u32_t len,
     u8_t *src) {
-  return fs->cfg.hal_write_f(addr, len, src);
+  return SPIFFS_HAL_WRITE(fs, addr, len, src);
 }
 
 #endif
 
+#if !SPIFFS_READ_ONLY
 s32_t spiffs_phys_cpy(
     spiffs *fs,
     spiffs_file fh,
     u32_t dst,
     u32_t src,
     u32_t len) {
+  (void)fh;
   s32_t res;
   u8_t b[SPIFFS_COPY_BUFFER_STACK];
   while (len > 0) {
@@ -97,10 +101,11 @@ s32_t spiffs_phys_cpy(
   }
   return SPIFFS_OK;
 }
+#endif // !SPIFFS_READ_ONLY
 
 // Find object lookup entry containing given id with visitor.
 // Iterate over object lookup pages in each block until a given object id entry is found.
-// When found, the visitor function is called with block index, entry index and user_data.
+// When found, the visitor function is called with block index, entry index and user data.
 // If visitor returns SPIFFS_VIS_CONTINUE, the search goes on. Otherwise, the search will be
 // ended and visitor's return code is returned to caller.
 // If no visitor is given (0) the search returns on first entry with matching object id.
@@ -112,8 +117,8 @@ s32_t spiffs_phys_cpy(
 //                              SPIFFS_VIS_NO_WRAP
 // @param obj_id                argument object id
 // @param v                     visitor callback function
-// @param user_data             any data, passed to the callback visitor function
-// @param user_p                any pointer, passed to the callback visitor function
+// @param user_const_p          any const pointer, passed to the callback visitor function
+// @param user_var_p            any pointer, passed to the callback visitor function
 // @param block_ix              reported block index where match was found
 // @param lu_entry              reported look up index where match was found
 s32_t spiffs_obj_lu_find_entry_visitor(
@@ -123,8 +128,8 @@ s32_t spiffs_obj_lu_find_entry_visitor(
     u8_t flags,
     spiffs_obj_id obj_id,
     spiffs_visitor_f v,
-    u32_t user_data,
-    void *user_p,
+    const void *user_const_p,
+    void *user_var_p,
     spiffs_block_ix *block_ix,
     int *lu_entry) {
   s32_t res = SPIFFS_OK;
@@ -174,8 +179,8 @@ s32_t spiffs_obj_lu_find_entry_visitor(
                 (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry-entry_offset],
                 cur_block,
                 cur_entry,
-                user_data,
-                user_p);
+                user_const_p,
+                user_var_p);
             if (res == SPIFFS_VIS_COUNTINUE || res == SPIFFS_VIS_COUNTINUE_RELOAD) {
               if (res == SPIFFS_VIS_COUNTINUE_RELOAD) {
                 res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
@@ -217,6 +222,7 @@ s32_t spiffs_obj_lu_find_entry_visitor(
   return SPIFFS_VIS_END;
 }
 
+#if !SPIFFS_READ_ONLY
 s32_t spiffs_erase_block(
     spiffs *fs,
     spiffs_block_ix bix) {
@@ -227,7 +233,8 @@ s32_t spiffs_erase_block(
   // 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));
+    SPIFFS_HAL_ERASE(fs, addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
+
     addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs);
     size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs);
   }
@@ -241,7 +248,7 @@ s32_t spiffs_erase_block(
 
 #if SPIFFS_USE_MAGIC
   // finally, write magic
-  spiffs_obj_id magic = SPIFFS_MAGIC(fs);
+  spiffs_obj_id magic = SPIFFS_MAGIC(fs, bix);
   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);
@@ -255,6 +262,59 @@ s32_t spiffs_erase_block(
 
   return res;
 }
+#endif // !SPIFFS_READ_ONLY
+
+#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
+s32_t spiffs_probe(
+    spiffs_config *cfg) {
+  s32_t res;
+  u32_t paddr;
+  spiffs dummy_fs; // create a dummy fs struct just to be able to use macros
+  memcpy(&dummy_fs.cfg, cfg, sizeof(spiffs_config));
+  dummy_fs.block_count = 0;
+
+  // Read three magics, as one block may be in an aborted erase state.
+  // At least two of these must contain magic and be in decreasing order.
+  spiffs_obj_id magic[3];
+  spiffs_obj_id bix_count[3];
+
+  spiffs_block_ix bix;
+  for (bix = 0; bix < 3; bix++) {
+    paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix);
+#if SPIFFS_HAL_CALLBACK_EXTRA
+    // not any proper fs to report here, so callback with null
+    // (cross fingers that no-one gets angry)
+    res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]);
+#else
+    res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]);
+#endif
+    bix_count[bix] = magic[bix] ^ SPIFFS_MAGIC(&dummy_fs, 0);
+    SPIFFS_CHECK_RES(res);
+  }
+
+  // check that we have sane number of blocks
+  if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS;
+  // check that the order is correct, take aborted erases in calculation
+  // first block aborted erase
+  if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) {
+    return (bix_count[1]+1) * cfg->log_block_size;
+  }
+  // second block aborted erase
+  if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) {
+    return bix_count[0] * cfg->log_block_size;
+  }
+  // third block aborted erase
+  if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) {
+    return bix_count[0] * cfg->log_block_size;
+  }
+  // no block has aborted erase
+  if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) {
+    return bix_count[0] * cfg->log_block_size;
+  }
+
+  return SPIFFS_ERR_PROBE_NOT_A_FS;
+}
+#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
 
 
 static s32_t spiffs_obj_lu_scan_v(
@@ -262,11 +322,11 @@ static s32_t spiffs_obj_lu_scan_v(
     spiffs_obj_id obj_id,
     spiffs_block_ix bix,
     int ix_entry,
-    u32_t user_data,
-    void *user_p) {
+    const void *user_const_p,
+    void *user_var_p) {
   (void)bix;
-  (void)user_data;
-  (void)user_p;
+  (void)user_const_p;
+  (void)user_var_p;
   if (obj_id == SPIFFS_OBJ_ID_FREE) {
     if (ix_entry == 0) {
       fs->free_blocks++;
@@ -309,7 +369,7 @@ s32_t spiffs_obj_lu_scan(
         sizeof(spiffs_obj_id), (u8_t *)&magic);
 
     SPIFFS_CHECK_RES(res);
-    if (magic != SPIFFS_MAGIC(fs)) {
+    if (magic != SPIFFS_MAGIC(fs, bix)) {
       if (unerased_bix == (spiffs_block_ix)-1) {
         // allow one unerased block as it might be powered down during an erase
         unerased_bix = bix;
@@ -348,7 +408,11 @@ s32_t spiffs_obj_lu_scan(
   if (unerased_bix != (spiffs_block_ix)-1) {
     // found one unerased block, remedy
     SPIFFS_DBG("mount: erase block %i\n", bix);
+#if SPIFFS_READ_ONLY
+    res = SPIFFS_ERR_RO_ABORTED_OPERATION;
+#else
     res = spiffs_erase_block(fs, unerased_bix);
+#endif // SPIFFS_READ_ONLY
     SPIFFS_CHECK_RES(res);
   }
 #endif
@@ -379,6 +443,7 @@ s32_t spiffs_obj_lu_scan(
   return res;
 }
 
+#if !SPIFFS_READ_ONLY
 // Find free object lookup entry
 // Iterate over object lookup pages in each block until a free object id entry is found
 s32_t spiffs_obj_lu_find_free(
@@ -407,12 +472,13 @@ s32_t spiffs_obj_lu_find_free(
       fs->free_blocks--;
     }
   }
-  if (res == SPIFFS_VIS_END) {
+  if (res == SPIFFS_ERR_FULL) {
     SPIFFS_DBG("fs full\n");
   }
 
-  return res == SPIFFS_VIS_END ? SPIFFS_ERR_FULL : res;
+  return res;
 }
+#endif // !SPIFFS_READ_ONLY
 
 // Find object lookup entry containing given id
 // Iterate over object lookup pages in each block until a given object id entry is found
@@ -437,8 +503,8 @@ static s32_t spiffs_obj_lu_find_id_and_span_v(
     spiffs_obj_id obj_id,
     spiffs_block_ix bix,
     int ix_entry,
-    u32_t user_data,
-    void *user_p) {
+    const void *user_const_p,
+    void *user_var_p) {
   s32_t res;
   spiffs_page_header ph;
   spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry);
@@ -446,10 +512,10 @@ static s32_t spiffs_obj_lu_find_id_and_span_v(
       SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph);
   SPIFFS_CHECK_RES(res);
   if (ph.obj_id == obj_id &&
-      ph.span_ix == (spiffs_span_ix)user_data &&
+      ph.span_ix == *((spiffs_span_ix*)user_var_p) &&
       (ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET &&
       !((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (ph.flags & SPIFFS_PH_FLAG_IXDELE) == 0 && ph.span_ix == 0) &&
-      (user_p == 0 || *((spiffs_page_ix *)user_p) != pix)) {
+      (user_const_p == 0 || *((const spiffs_page_ix*)user_const_p) != pix)) {
     return SPIFFS_OK;
   } else {
     return SPIFFS_VIS_COUNTINUE;
@@ -474,8 +540,8 @@ s32_t spiffs_obj_lu_find_id_and_span(
       SPIFFS_VIS_CHECK_ID,
       obj_id,
       spiffs_obj_lu_find_id_and_span_v,
-      (u32_t)spix,
       exclusion_pix ? &exclusion_pix : 0,
+      &spix,
       &bix,
       &entry);
 
@@ -513,8 +579,8 @@ s32_t spiffs_obj_lu_find_id_and_span_by_phdr(
       SPIFFS_VIS_CHECK_PH,
       obj_id,
       spiffs_obj_lu_find_id_and_span_v,
-      (u32_t)spix,
       exclusion_pix ? &exclusion_pix : 0,
+      &spix,
       &bix,
       &entry);
 
@@ -534,6 +600,7 @@ s32_t spiffs_obj_lu_find_id_and_span_by_phdr(
   return res;
 }
 
+#if !SPIFFS_READ_ONLY
 // Allocates a free defined page with given obj_id
 // Occupies object lookup entry and page
 // data may be NULL; where only page header is stored, len and page_offs is ignored
@@ -591,7 +658,9 @@ s32_t spiffs_page_allocate_data(
 
   return res;
 }
+#endif // !SPIFFS_READ_ONLY
 
+#if !SPIFFS_READ_ONLY
 // Moves a page from src to a free page and finalizes it. Updates page index. Page data is given in param page.
 // If page data is null, provided header is used for metainfo and page data is physically copied.
 s32_t spiffs_page_move(
@@ -654,7 +723,9 @@ s32_t spiffs_page_move(
   res = spiffs_page_delete(fs, src_pix);
   return res;
 }
+#endif // !SPIFFS_READ_ONLY
 
+#if !SPIFFS_READ_ONLY
 // Deletes a page and removes it from object lookup.
 s32_t spiffs_page_delete(
     spiffs *fs,
@@ -683,12 +754,14 @@ s32_t spiffs_page_delete(
 
   return res;
 }
+#endif // !SPIFFS_READ_ONLY
 
+#if !SPIFFS_READ_ONLY
 // Create an object index header page with empty index and undefined length
 s32_t spiffs_object_create(
     spiffs *fs,
     spiffs_obj_id obj_id,
-    u8_t name[SPIFFS_OBJ_NAME_LEN],
+    const u8_t name[SPIFFS_OBJ_NAME_LEN],
     spiffs_obj_type type,
     spiffs_page_ix *objix_hdr_pix) {
   s32_t res = SPIFFS_OK;
@@ -719,7 +792,7 @@ s32_t spiffs_object_create(
   oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED);
   oix_hdr.type = type;
   oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page
-  strncpy((char *)&oix_hdr.name, (char *)name, SPIFFS_OBJ_NAME_LEN);
+  strncpy((char*)&oix_hdr.name, (const char*)name, SPIFFS_OBJ_NAME_LEN);
 
 
   // update page
@@ -735,7 +808,9 @@ s32_t spiffs_object_create(
 
   return res;
 }
+#endif // !SPIFFS_READ_ONLY
 
+#if !SPIFFS_READ_ONLY
 // update object index header with any combination of name/size/index
 // new_objix_hdr_data may be null, if so the object index header page is loaded
 // name may be null, if so name is not changed
@@ -746,7 +821,7 @@ s32_t spiffs_object_update_index_hdr(
     spiffs_obj_id obj_id,
     spiffs_page_ix objix_hdr_pix,
     u8_t *new_objix_hdr_data,
-    u8_t name[SPIFFS_OBJ_NAME_LEN],
+    const u8_t name[SPIFFS_OBJ_NAME_LEN],
     u32_t size,
     spiffs_page_ix *new_pix) {
   s32_t res = SPIFFS_OK;
@@ -770,7 +845,7 @@ s32_t spiffs_object_update_index_hdr(
 
   // change name
   if (name) {
-    strncpy((char *)objix_hdr->name, (char *)name, SPIFFS_OBJ_NAME_LEN);
+    strncpy((char*)objix_hdr->name, (const char*)name, SPIFFS_OBJ_NAME_LEN);
   }
   if (size) {
     objix_hdr->size = size;
@@ -790,18 +865,19 @@ s32_t spiffs_object_update_index_hdr(
 
   return res;
 }
+#endif // !SPIFFS_READ_ONLY
 
 void spiffs_cb_object_event(
     spiffs *fs,
     spiffs_fd *fd,
     int ev,
-    spiffs_obj_id obj_id,
+    spiffs_obj_id obj_id_raw,
     spiffs_span_ix spix,
     spiffs_page_ix new_pix,
     u32_t new_size) {
   (void)fd;
   // update index caches in all file descriptors
-  obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG;
+  spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG;
   u32_t i;
   spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
   for (i = 0; i < fs->fd_count; i++) {
@@ -828,6 +904,22 @@ void spiffs_cb_object_event(
       }
     }
   }
+
+  // callback to user if object index header
+  if (fs->file_cb_f && spix == 0 && (obj_id_raw & SPIFFS_OBJ_ID_IX_FLAG)) {
+    spiffs_fileop_type op;
+    if (ev == SPIFFS_EV_IX_NEW) {
+      op = SPIFFS_CB_CREATED;
+    } else if (ev == SPIFFS_EV_IX_UPD) {
+      op = SPIFFS_CB_UPDATED;
+    } else if (ev == SPIFFS_EV_IX_DEL) {
+      op = SPIFFS_CB_DELETED;
+    } else {
+      SPIFFS_DBG("       callback: WARNING unknown callback event %02x\n", ev);
+      return; // bail out
+    }
+    fs->file_cb_f(fs, op, obj_id, new_pix);
+  }
 }
 
 // Open object by id
@@ -886,6 +978,7 @@ s32_t spiffs_object_open_by_page(
   return res;
 }
 
+#if !SPIFFS_READ_ONLY
 // Append to object
 // keep current object index (header) page in fs->work buffer
 s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
@@ -990,8 +1083,8 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
           SPIFFS_CHECK_RES(res);
           spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0);
           // quick "load" of new object index page
-          c_memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
-          c_memcpy(fs->work, &p_hdr, sizeof(spiffs_page_header));
+          memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
+          memcpy(fs->work, &p_hdr, sizeof(spiffs_page_header));
           SPIFFS_DBG("append: %04x create objix page, %04x:%04x, written %i\n", fd->obj_id
               , cur_objix_pix, cur_objix_spix, written);
         } else {
@@ -1125,8 +1218,10 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
   }
 
   return res;
-}
+} // spiffs_object_append
+#endif // !SPIFFS_READ_ONLY
 
+#if !SPIFFS_READ_ONLY
 // Modify object
 // keep current object index (header) page in fs->work buffer
 s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
@@ -1326,16 +1421,17 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
   }
 
   return res;
-}
+} // spiffs_object_modify
+#endif // !SPIFFS_READ_ONLY
 
 static s32_t spiffs_object_find_object_index_header_by_name_v(
     spiffs *fs,
     spiffs_obj_id obj_id,
     spiffs_block_ix bix,
     int ix_entry,
-    u32_t user_data,
-    void *user_p) {
-  (void)user_data;
+    const void *user_const_p,
+    void *user_var_p) {
+  (void)user_var_p;
   s32_t res;
   spiffs_page_object_ix_header objix_hdr;
   spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry);
@@ -1349,7 +1445,7 @@ static s32_t spiffs_object_find_object_index_header_by_name_v(
   if (objix_hdr.p_hdr.span_ix == 0 &&
       (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) ==
           (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
-    if (strncmp((char *)user_p, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN) == 0) {
+    if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) {
       return SPIFFS_OK;
     }
   }
@@ -1360,7 +1456,7 @@ static s32_t spiffs_object_find_object_index_header_by_name_v(
 // Finds object index header page by name
 s32_t spiffs_object_find_object_index_header_by_name(
     spiffs *fs,
-    u8_t name[SPIFFS_OBJ_NAME_LEN],
+    const u8_t name[SPIFFS_OBJ_NAME_LEN],
     spiffs_page_ix *pix) {
   s32_t res;
   spiffs_block_ix bix;
@@ -1372,8 +1468,8 @@ s32_t spiffs_object_find_object_index_header_by_name(
       0,
       0,
       spiffs_object_find_object_index_header_by_name_v,
-      0,
       name,
+      0,
       &bix,
       &entry);
 
@@ -1392,6 +1488,7 @@ s32_t spiffs_object_find_object_index_header_by_name(
   return res;
 }
 
+#if !SPIFFS_READ_ONLY
 // Truncates object to new size. If new size is null, object may be removed totally
 s32_t spiffs_object_truncate(
     spiffs_fd *fd,
@@ -1400,8 +1497,16 @@ s32_t spiffs_object_truncate(
   s32_t res = SPIFFS_OK;
   spiffs *fs = fd->fs;
 
-  res = spiffs_gc_check(fs, remove ? 0 : SPIFFS_DATA_PAGE_SIZE(fs));
-  SPIFFS_CHECK_RES(res);
+  if ((fd->size == SPIFFS_UNDEFINED_LEN || fd->size == 0) && !remove) {
+    // no op
+    return res;
+  }
+
+  // need 2 pages if not removing: object index page + possibly chopped data page
+  if (remove == 0) {
+    res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs) * 2);
+    SPIFFS_CHECK_RES(res);
+  }
 
   spiffs_page_ix objix_pix = fd->objix_hdr_pix;
   spiffs_span_ix data_spix = (fd->size > 0 ? fd->size-1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs);
@@ -1440,11 +1545,18 @@ s32_t spiffs_object_truncate(
         SPIFFS_CHECK_RES(res);
         spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0);
         if (prev_objix_spix > 0) {
-          // update object index header page
-          SPIFFS_DBG("truncate: update objix hdr page %04x:%04x to size %i\n", fd->objix_hdr_pix, prev_objix_spix, cur_size);
-          res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
-              fd->objix_hdr_pix, 0, 0, cur_size, &new_objix_hdr_pix);
-          SPIFFS_CHECK_RES(res);
+          // Update object index header page, unless we totally want to remove the file.
+          // If fully removing, we're not keeping consistency as good as when storing the header between chunks,
+          // would we be aborted. But when removing full files, a crammed system may otherwise
+          // report ERR_FULL a la windows. We cannot have that.
+          // Hence, take the risk - if aborted, a file check would free the lost pages and mend things
+          // as the file is marked as fully deleted in the beginning.
+          if (remove == 0) {
+            SPIFFS_DBG("truncate: update objix hdr page %04x:%04x to size %i\n", fd->objix_hdr_pix, prev_objix_spix, cur_size);
+            res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
+                fd->objix_hdr_pix, 0, 0, cur_size, &new_objix_hdr_pix);
+            SPIFFS_CHECK_RES(res);
+          }
           fd->size = cur_size;
         }
       }
@@ -1480,7 +1592,7 @@ s32_t spiffs_object_truncate(
 
     SPIFFS_DBG("truncate: got data pix %04x\n", data_pix);
 
-    if (cur_size - SPIFFS_DATA_PAGE_SIZE(fs) >= new_size) {
+    if (new_size == 0 || remove || cur_size - new_size >= SPIFFS_DATA_PAGE_SIZE(fs)) {
       // delete full data page
       res = spiffs_page_data_check(fs, fd, data_pix, data_spix);
       if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) {
@@ -1575,7 +1687,7 @@ s32_t spiffs_object_truncate(
       } else {
         // make uninitialized object
         SPIFFS_DBG("truncate: reset objix_hdr page %04x\n", objix_pix);
-        c_memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff,
+        memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff,
             SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header));
         res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
             objix_pix, fs->work, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix);
@@ -1611,7 +1723,8 @@ s32_t spiffs_object_truncate(
   fd->size = cur_size;
 
   return res;
-}
+} // spiffs_object_truncate
+#endif // !SPIFFS_READ_ONLY
 
 s32_t spiffs_object_read(
     spiffs_fd *fd,
@@ -1691,6 +1804,7 @@ s32_t spiffs_object_read(
   return res;
 }
 
+#if !SPIFFS_READ_ONLY
 typedef struct {
   spiffs_obj_id min_obj_id;
   spiffs_obj_id max_obj_id;
@@ -1699,10 +1813,10 @@ typedef struct {
 } spiffs_free_obj_id_state;
 
 static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
-    u32_t user_data, void *user_p) {
+    const void *user_const_p, void *user_var_p) {
   if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED) {
-    spiffs_obj_id min_obj_id = user_data;
-    u8_t *conflicting_name = (u8_t *)user_p;
+    spiffs_obj_id min_obj_id = *((spiffs_obj_id*)user_var_p);
+    const u8_t *conflicting_name = (const u8_t*)user_const_p;
 
     // if conflicting name parameter is given, also check if this name is found in object index hdrs
     if (conflicting_name && (id & SPIFFS_OBJ_ID_IX_FLAG)) {
@@ -1715,7 +1829,7 @@ static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id i
       if (objix_hdr.p_hdr.span_ix == 0 &&
           (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) ==
               (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
-        if (strncmp((char *)user_p, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN) == 0) {
+        if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) {
           return SPIFFS_ERR_CONFLICTING_NAME;
         }
       }
@@ -1732,11 +1846,11 @@ static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id i
 }
 
 static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
-    u32_t user_data, void *user_p) {
-  (void)user_data;
+    const void *user_const_p, void *user_var_p) {
+  (void)user_var_p;
   if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED && (id & SPIFFS_OBJ_ID_IX_FLAG)) {
     s32_t res;
-    spiffs_free_obj_id_state *state = (spiffs_free_obj_id_state *)user_p;
+    const spiffs_free_obj_id_state *state = (const spiffs_free_obj_id_state*)user_const_p;
     spiffs_page_object_ix_header objix_hdr;
 
     res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
@@ -1745,7 +1859,7 @@ static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id
         ((objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) ==
             (SPIFFS_PH_FLAG_DELET))) {
       // ok object look up entry
-      if (state->conflicting_name && strncmp((const char *)state->conflicting_name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN) == 0) {
+      if (state->conflicting_name && strcmp((const char *)state->conflicting_name, (char *)objix_hdr.name) == 0) {
         return SPIFFS_ERR_CONFLICTING_NAME;
       }
 
@@ -1764,10 +1878,10 @@ static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id
 // Scans thru all object lookup for object index header pages. If total possible number of
 // object ids cannot fit into a work buffer, these are grouped. When a group containing free
 // object ids is found, the object lu is again scanned for object ids within group and bitmasked.
-// Finally, the bitmasked is searched for a free id
-s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *conflicting_name) {
+// Finally, the bitmask is searched for a free id
+s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, const u8_t *conflicting_name) {
   s32_t res = SPIFFS_OK;
-  u32_t max_objects = (SPIFFS_CFG_PHYS_SZ(fs) / (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) / 2;
+  u32_t max_objects = (fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) / 2;
   spiffs_free_obj_id_state state;
   spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE;
   state.min_obj_id = 1;
@@ -1783,9 +1897,9 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co
       u32_t i, j;
       SPIFFS_DBG("free_obj_id: BITM min:%04x max:%04x\n", state.min_obj_id, state.max_obj_id);
 
-      c_memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
-      res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, state.min_obj_id,
-          conflicting_name, 0, 0);
+      memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
+      res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v,
+          conflicting_name, &state.min_obj_id, 0, 0);
       if (res == SPIFFS_VIS_END) res = SPIFFS_OK;
       SPIFFS_CHECK_RES(res);
       // traverse bitmask until found free obj_id
@@ -1848,8 +1962,8 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co
       state.compaction = (state.max_obj_id-state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t)));
       SPIFFS_DBG("free_obj_id: COMP min:%04x max:%04x compact:%i\n", state.min_obj_id, state.max_obj_id, state.compaction);
 
-      c_memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
-      res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, 0, &state, 0, 0);
+      memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
+      res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, &state, 0, 0, 0);
       if (res == SPIFFS_VIS_END) res = SPIFFS_OK;
       SPIFFS_CHECK_RES(res);
       state.conflicting_name = 0; // searched for conflicting name once, no need to do it again
@@ -1858,6 +1972,7 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co
 
   return res;
 }
+#endif // !SPIFFS_READ_ONLY
 
 s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd) {
   u32_t i;

+ 51 - 12
app/spiffs/spiffs_nucleus.h

@@ -131,7 +131,15 @@
 #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)))
+#if SPIFFS_USE_MAGIC
+#if !SPIFFS_USE_MAGIC_LENGTH
+#define SPIFFS_MAGIC(fs, bix)           \
+  ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs)))
+#else // SPIFFS_USE_MAGIC_LENGTH
+#define SPIFFS_MAGIC(fs, bix)           \
+  ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix))))
+#endif // SPIFFS_USE_MAGIC_LENGTH
+#endif // SPIFFS_USE_MAGIC
 
 #define SPIFFS_CONFIG_MAGIC             (0x20090315)
 
@@ -264,26 +272,26 @@
 #define SPIFFS_API_CHECK_MOUNT(fs) \
   if (!SPIFFS_CHECK_MOUNT((fs))) { \
     (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \
-    return -1; \
+    return SPIFFS_ERR_NOT_MOUNTED; \
   }
 
 #define SPIFFS_API_CHECK_CFG(fs) \
   if (!SPIFFS_CHECK_CFG((fs))) { \
     (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \
-    return -1; \
+    return SPIFFS_ERR_NOT_CONFIGURED; \
   }
 
 #define SPIFFS_API_CHECK_RES(fs, res) \
   if ((res) < SPIFFS_OK) { \
     (fs)->err_code = (res); \
-    return -1; \
+    return (res); \
   }
 
 #define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
   if ((res) < SPIFFS_OK) { \
     (fs)->err_code = (res); \
     SPIFFS_UNLOCK(fs); \
-    return -1; \
+    return (res); \
   }
 
 #define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \
@@ -311,6 +319,26 @@
 // stop searching at end of all look up pages
 #define SPIFFS_VIS_NO_WRAP      (1<<2)
 
+#if SPIFFS_HAL_CALLBACK_EXTRA
+
+#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \
+  (_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src))
+#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \
+  (_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst))
+#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \
+  (_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len))
+
+#else // SPIFFS_HAL_CALLBACK_EXTRA
+
+#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \
+  (_fs)->cfg.hal_write_f((_paddr), (_len), (_src))
+#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \
+  (_fs)->cfg.hal_read_f((_paddr), (_len), (_dst))
+#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \
+  (_fs)->cfg.hal_erase_f((_paddr), (_len))
+
+#endif // SPIFFS_HAL_CALLBACK_EXTRA
+
 #if SPIFFS_CACHE
 
 #define SPIFFS_CACHE_FLAG_DIRTY       (1<<0)
@@ -416,9 +444,14 @@ typedef struct __attribute(( packed )) {
 
 // object index header page header
 typedef struct __attribute(( packed ))
+#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
+                __attribute(( aligned(sizeof(spiffs_page_ix)) ))
+#endif
 {
   // common page header
   spiffs_page_header p_hdr;
+  // alignment
+  u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
   // size of object
   u32_t size;
   // type of object
@@ -430,11 +463,12 @@ typedef struct __attribute(( packed ))
 // object index page header
 typedef struct __attribute(( packed )) {
  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
 typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
-    u32_t user_data, void *user_p);
+    const void *user_const_p, void *user_var_p);
 
 
 #if SPIFFS_CACHE
@@ -495,8 +529,8 @@ s32_t spiffs_obj_lu_find_entry_visitor(
     u8_t flags,
     spiffs_obj_id obj_id,
     spiffs_visitor_f v,
-    u32_t user_data,
-    void *user_p,
+    const void *user_const_p,
+    void *user_var_p,
     spiffs_block_ix *block_ix,
     int *lu_entry);
 
@@ -504,6 +538,11 @@ s32_t spiffs_erase_block(
     spiffs *fs,
     spiffs_block_ix bix);
 
+#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH
+s32_t spiffs_probe(
+    spiffs_config *cfg);
+#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH
+
 // ---------------
 
 s32_t spiffs_obj_lu_scan(
@@ -512,7 +551,7 @@ s32_t spiffs_obj_lu_scan(
 s32_t spiffs_obj_lu_find_free_obj_id(
     spiffs *fs,
     spiffs_obj_id *obj_id,
-    u8_t *conflicting_name);
+    const u8_t *conflicting_name);
 
 s32_t spiffs_obj_lu_find_free(
     spiffs *fs,
@@ -573,7 +612,7 @@ s32_t spiffs_page_delete(
 s32_t spiffs_object_create(
     spiffs *fs,
     spiffs_obj_id obj_id,
-    u8_t name[SPIFFS_OBJ_NAME_LEN],
+    const u8_t name[SPIFFS_OBJ_NAME_LEN],
     spiffs_obj_type type,
     spiffs_page_ix *objix_hdr_pix);
 
@@ -583,7 +622,7 @@ s32_t spiffs_object_update_index_hdr(
     spiffs_obj_id obj_id,
     spiffs_page_ix objix_hdr_pix,
     u8_t *new_objix_hdr_data,
-    u8_t name[SPIFFS_OBJ_NAME_LEN],
+    const u8_t name[SPIFFS_OBJ_NAME_LEN],
     u32_t size,
     spiffs_page_ix *new_pix);
 
@@ -635,7 +674,7 @@ s32_t spiffs_object_truncate(
 
 s32_t spiffs_object_find_object_index_header_by_name(
     spiffs *fs,
-    u8_t name[SPIFFS_OBJ_NAME_LEN],
+    const u8_t name[SPIFFS_OBJ_NAME_LEN],
     spiffs_page_ix *pix);
 
 // ---------------

+ 0 - 7
app/spiffs/test/main.c

@@ -1,7 +0,0 @@
-#include "testrunner.h"
-#include <stdlib.h>
-
-int main(int argc, char **args) {
-  run_tests(argc, args);
-  exit(EXIT_SUCCESS);
-}

+ 0 - 36
app/spiffs/test/params_test.h

@@ -1,36 +0,0 @@
-/*
- * params_test.h
- *
- *  Created on: May 26, 2013
- *      Author: petera
- */
-
-#ifndef PARAMS_TEST_H_
-#define PARAMS_TEST_H_
-
-// total emulated spi flash size
-#define PHYS_FLASH_SIZE       (16*1024*1024)
-// spiffs file system size
-#define SPIFFS_FLASH_SIZE     (2*1024*1024)
-// spiffs file system offset in emulated spi flash
-#define SPIFFS_PHYS_ADDR      (4*1024*1024)
-
-#define SECTOR_SIZE         65536
-#define LOG_BLOCK           (SECTOR_SIZE*2)
-#define LOG_PAGE            (SECTOR_SIZE/256)
-
-#define FD_BUF_SIZE     64*6
-#define CACHE_BUF_SIZE  (LOG_PAGE + 32)*8
-
-#define ASSERT(c, m) real_assert((c),(m), __FILE__, __LINE__);
-
-typedef signed int s32_t;
-typedef unsigned int u32_t;
-typedef signed short s16_t;
-typedef unsigned short u16_t;
-typedef signed char s8_t;
-typedef unsigned char u8_t;
-
-void real_assert(int c, const char *n, const char *file, int l);
-
-#endif /* PARAMS_TEST_H_ */

+ 0 - 160
app/spiffs/test/test_bugreports.c

@@ -1,160 +0,0 @@
-/*
- * test_bugreports.c
- *
- *  Created on: Mar 8, 2015
- *      Author: petera
- */
-
-
-
-#include "testrunner.h"
-#include "test_spiffs.h"
-#include "spiffs_nucleus.h"
-#include "spiffs.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <unistd.h>
-
-
-SUITE(bug_tests)
-void setup() {
-  _setup_test_only();
-}
-void teardown() {
-  _teardown();
-}
-
-TEST(nodemcu_full_fs_1) {
-  fs_reset_specific(0, 4096*20, 4096, 4096, 256);
-
-  int res;
-  spiffs_file fd;
-
-  printf("  fill up system by writing one byte a lot\n");
-  fd = SPIFFS_open(FS, "test1.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
-  TEST_CHECK(fd > 0);
-  int i;
-  spiffs_stat s;
-  res = SPIFFS_OK;
-  for (i = 0; i < 100*1000; i++) {
-    u8_t buf = 'x';
-    res = SPIFFS_write(FS, fd, &buf, 1);
-  }
-
-  int errno = SPIFFS_errno(FS);
-  int res2 = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res2 == SPIFFS_OK);
-  printf("  >>> file %s size: %i\n", s.name, s.size);
-
-  TEST_CHECK(errno == SPIFFS_ERR_FULL);
-  SPIFFS_close(FS, fd);
-
-  printf("  remove big file\n");
-  res = SPIFFS_remove(FS, "test1.txt");
-
-  printf("res:%i errno:%i\n",res, SPIFFS_errno(FS));
-
-  TEST_CHECK(res == SPIFFS_OK);
-  res2 = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res2 == -1);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res2 = SPIFFS_stat(FS, "test1.txt", &s);
-  TEST_CHECK(res2 == -1);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND);
-
-  printf("  create small file\n");
-  fd = SPIFFS_open(FS, "test2.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
-  TEST_CHECK(fd > 0);
-  res = SPIFFS_OK;
-  for (i = 0; res >= 0 && i < 1000; i++) {
-    u8_t buf = 'x';
-    res = SPIFFS_write(FS, fd, &buf, 1);
-  }
-  TEST_CHECK(res >= SPIFFS_OK);
-
-  res2 = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res2 == SPIFFS_OK);
-  printf("  >>> file %s size: %i\n", s.name, s.size);
-
-  TEST_CHECK(s.size == 1000);
-  SPIFFS_close(FS, fd);
-
-  return TEST_RES_OK;
-
-} TEST_END(nodemcu_full_fs_1)
-
-TEST(nodemcu_full_fs_2) {
-  fs_reset_specific(0, 4096*22, 4096, 4096, 256);
-
-  int res;
-  spiffs_file fd;
-
-  printf("  fill up system by writing one byte a lot\n");
-  fd = SPIFFS_open(FS, "test1.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
-  TEST_CHECK(fd > 0);
-  int i;
-  spiffs_stat s;
-  res = SPIFFS_OK;
-  for (i = 0; i < 100*1000; i++) {
-    u8_t buf = 'x';
-    res = SPIFFS_write(FS, fd, &buf, 1);
-  }
-
-  int errno = SPIFFS_errno(FS);
-  int res2 = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res2 == SPIFFS_OK);
-  printf("  >>> file %s size: %i\n", s.name, s.size);
-
-  TEST_CHECK(errno == SPIFFS_ERR_FULL);
-  SPIFFS_close(FS, fd);
-
-  res2 = SPIFFS_stat(FS, "test1.txt", &s);
-  TEST_CHECK(res2 == SPIFFS_OK);
-
-  SPIFFS_clearerr(FS);
-  printf("  create small file\n");
-  fd = SPIFFS_open(FS, "test2.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK);
-  TEST_CHECK(fd > 0);
-
-  for (i = 0; i < 1000; i++) {
-    u8_t buf = 'x';
-    res = SPIFFS_write(FS, fd, &buf, 1);
-  }
-
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FULL);
-  res2 = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res2 == SPIFFS_OK);
-  printf("  >>> file %s size: %i\n", s.name, s.size);
-  TEST_CHECK(s.size == 0);
-  SPIFFS_clearerr(FS);
-
-  printf("  remove files\n");
-  res = SPIFFS_remove(FS, "test1.txt");
-  TEST_CHECK(res == SPIFFS_OK);
-  res = SPIFFS_remove(FS, "test2.txt");
-  TEST_CHECK(res == SPIFFS_OK);
-
-  printf("  create medium file\n");
-  fd = SPIFFS_open(FS, "test3.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK);
-  TEST_CHECK(fd > 0);
-
-  for (i = 0; i < 20*1000; i++) {
-    u8_t buf = 'x';
-    res = SPIFFS_write(FS, fd, &buf, 1);
-  }
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK);
-
-  res2 = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res2 == SPIFFS_OK);
-  printf("  >>> file %s size: %i\n", s.name, s.size);
-  TEST_CHECK(s.size == 20*1000);
-
-  return TEST_RES_OK;
-
-} TEST_END(nodemcu_full_fs_2)
-
-SUITE_END(bug_tests)

+ 0 - 418
app/spiffs/test/test_check.c

@@ -1,418 +0,0 @@
-/*
- * test_dev.c
- *
- *  Created on: Jul 14, 2013
- *      Author: petera
- */
-
-
-#include "testrunner.h"
-#include "test_spiffs.h"
-#include "spiffs_nucleus.h"
-#include "spiffs.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <unistd.h>
-
-
-SUITE(check_tests)
-void setup() {
-  _setup();
-}
-void teardown() {
-  _teardown();
-}
-
-TEST(evil_write) {
-  fs_set_validate_flashing(0);
-  printf("writing corruption to block 1 data range (leaving lu intact)\n");
-  u32_t data_range = SPIFFS_CFG_LOG_BLOCK_SZ(FS) -
-      SPIFFS_CFG_LOG_PAGE_SZ(FS) * (SPIFFS_OBJ_LOOKUP_PAGES(FS));
-  u8_t *corruption = malloc(data_range);
-  memrand(corruption, data_range);
-  u32_t addr = 0 * SPIFFS_CFG_LOG_PAGE_SZ(FS) * SPIFFS_OBJ_LOOKUP_PAGES(FS);
-  area_write(addr, corruption, data_range);
-  free(corruption);
-
-  int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
-  int res = test_create_and_write_file("file", size, size);
-
-  printf("CHECK1-----------------\n");
-  SPIFFS_check(FS);
-  printf("CHECK2-----------------\n");
-  SPIFFS_check(FS);
-  printf("CHECK3-----------------\n");
-  SPIFFS_check(FS);
-
-  res = test_create_and_write_file("file2", size, size);
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-} TEST_END(evil_write)
-
-
-TEST(lu_check1) {
-  int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
-  int res = test_create_and_write_file("file", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  spiffs_stat s;
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  // modify lu entry data page index 1
-  spiffs_page_ix pix;
-  res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix);
-  TEST_CHECK(res >= 0);
-
-  // reset lu entry to being erased, but keep page data
-  spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED;
-  spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
-  int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
-  u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry*sizeof(spiffs_obj_id);
-
-  area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
-
-#if SPIFFS_CACHE
-  spiffs_cache *cache = spiffs_get_cache(FS);
-  cache->cpage_use_map = 0;
-#endif
-  SPIFFS_check(FS);
-
-  return TEST_RES_OK;
-} TEST_END(lu_check1)
-
-
-TEST(page_cons1) {
-  int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
-  int res = test_create_and_write_file("file", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  spiffs_stat s;
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  // modify object index, find object index header
-  spiffs_page_ix pix;
-  res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
-  TEST_CHECK(res >= 0);
-
-  // set object index entry 2 to a bad page
-  u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 0 * sizeof(spiffs_page_ix);
-  spiffs_page_ix bad_pix_ref = 0x55;
-  area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
-  area_write(addr+2, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
-
-  // delete all cache
-#if SPIFFS_CACHE
-  spiffs_cache *cache = spiffs_get_cache(FS);
-  cache->cpage_use_map = 0;
-#endif
-
-  SPIFFS_check(FS);
-
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-} TEST_END(page_cons1)
-
-
-TEST(page_cons2) {
-  int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
-  int res = test_create_and_write_file("file", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  spiffs_stat s;
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  // modify object index, find object index header
-  spiffs_page_ix pix;
-  res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
-  TEST_CHECK(res >= 0);
-
-  // find data page span index 0
-  spiffs_page_ix dpix;
-  res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &dpix);
-  TEST_CHECK(res >= 0);
-
-  // set object index entry 1+2 to a data page 0
-  u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix);
-  spiffs_page_ix bad_pix_ref = dpix;
-  area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
-  area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
-
-  // delete all cache
-#if SPIFFS_CACHE
-  spiffs_cache *cache = spiffs_get_cache(FS);
-  cache->cpage_use_map = 0;
-#endif
-
-  SPIFFS_check(FS);
-
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-} TEST_END(page_cons2)
-
-
-
-TEST(page_cons3) {
-  int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
-  int res = test_create_and_write_file("file", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  spiffs_stat s;
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  // modify object index, find object index header
-  spiffs_page_ix pix;
-  res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
-  TEST_CHECK(res >= 0);
-
-  // set object index entry 1+2 lookup page
-  u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix);
-  spiffs_page_ix bad_pix_ref = SPIFFS_PAGES_PER_BLOCK(FS) * (*FS.block_count - 2);
-  area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
-  area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
-
-  // delete all cache
-#if SPIFFS_CACHE
-  spiffs_cache *cache = spiffs_get_cache(FS);
-  cache->cpage_use_map = 0;
-#endif
-
-  SPIFFS_check(FS);
-
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-} TEST_END(page_cons3)
-
-
-TEST(page_cons_final) {
-  int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
-  int res = test_create_and_write_file("file", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  spiffs_stat s;
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  // modify page header, make unfinalized
-  spiffs_page_ix pix;
-  res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix);
-  TEST_CHECK(res >= 0);
-
-  // set page span ix 1 as unfinalized
-  u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags);
-  u8_t flags;
-  area_read(addr, (u8_t*)&flags, 1);
-  flags |= SPIFFS_PH_FLAG_FINAL;
-  area_write(addr, (u8_t*)&flags, 1);
-
-  // delete all cache
-#if SPIFFS_CACHE
-  spiffs_cache *cache = spiffs_get_cache(FS);
-  cache->cpage_use_map = 0;
-#endif
-
-  SPIFFS_check(FS);
-
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-} TEST_END(page_cons_final)
-
-
-TEST(index_cons1) {
-  int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
-  int res = test_create_and_write_file("file", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  spiffs_stat s;
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  // modify lu entry data page index header
-  spiffs_page_ix pix;
-  res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
-  TEST_CHECK(res >= 0);
-
-  printf("  deleting lu entry pix %04x\n", pix);
-  // reset lu entry to being erased, but keep page data
-  spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED;
-  spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
-  int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
-  u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
-
-  area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
-
-#if SPIFFS_CACHE
-  spiffs_cache *cache = spiffs_get_cache(FS);
-  cache->cpage_use_map = 0;
-#endif
-  SPIFFS_check(FS);
-
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-} TEST_END(index_cons1)
-
-
-TEST(index_cons2) {
-  int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
-  int res = test_create_and_write_file("file", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  spiffs_stat s;
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  // modify lu entry data page index header
-  spiffs_page_ix pix;
-  res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
-  TEST_CHECK(res >= 0);
-
-  printf("  writing lu entry for index page, ix %04x, as data page\n", pix);
-  spiffs_obj_id obj_id = 0x1234;
-  spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
-  int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
-  u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
-
-  area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
-
-#if SPIFFS_CACHE
-  spiffs_cache *cache = spiffs_get_cache(FS);
-  cache->cpage_use_map = 0;
-#endif
-  SPIFFS_check(FS);
-
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-} TEST_END(index_cons2)
-
-
-TEST(index_cons3) {
-  int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
-  int res = test_create_and_write_file("file", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  spiffs_stat s;
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  // modify lu entry data page index header
-  spiffs_page_ix pix;
-  res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
-  TEST_CHECK(res >= 0);
-
-  printf("  setting lu entry pix %04x to another index page\n", pix);
-  // reset lu entry to being erased, but keep page data
-  spiffs_obj_id obj_id = 1234 | SPIFFS_OBJ_ID_IX_FLAG;
-  spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
-  int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
-  u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
-
-  area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
-
-#if SPIFFS_CACHE
-  spiffs_cache *cache = spiffs_get_cache(FS);
-  cache->cpage_use_map = 0;
-#endif
-  SPIFFS_check(FS);
-
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-} TEST_END(index_cons3)
-
-TEST(index_cons4) {
-  int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
-  int res = test_create_and_write_file("file", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  spiffs_stat s;
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  // modify lu entry data page index header, flags
-  spiffs_page_ix pix;
-  res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
-  TEST_CHECK(res >= 0);
-
-  printf("  cue objix hdr deletion in page %04x\n", pix);
-  // set flags as deleting ix header
-  u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags);
-  u8_t flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE);
-
-  area_write(addr, (u8_t*)&flags, 1);
-
-#if SPIFFS_CACHE
-  spiffs_cache *cache = spiffs_get_cache(FS);
-  cache->cpage_use_map = 0;
-#endif
-  SPIFFS_check(FS);
-
-  return TEST_RES_OK;
-} TEST_END(index_cons4)
-
-
-
-SUITE_END(check_tests)

+ 0 - 120
app/spiffs/test/test_dev.c

@@ -1,120 +0,0 @@
-/*
- * test_dev.c
- *
- *  Created on: Jul 14, 2013
- *      Author: petera
- */
-
-
-#include "testrunner.h"
-#include "test_spiffs.h"
-#include "spiffs_nucleus.h"
-#include "spiffs.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <unistd.h>
-
-
-SUITE(dev_tests)
-void setup() {
-  _setup();
-}
-void teardown() {
-  _teardown();
-}
-
-TEST(interrupted_write) {
-  char *name = "interrupt";
-  char *name2 = "interrupt2";
-  int res;
-  spiffs_file fd;
-
-  const u32_t sz = SPIFFS_CFG_LOG_PAGE_SZ(FS)*8;
-  u8_t *buf = malloc(sz);
-  memrand(buf, sz);
-
-  printf("  create reference file\n");
-  fd = SPIFFS_open(FS, name, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
-  TEST_CHECK(fd > 0);
-  clear_flash_ops_log();
-  res = SPIFFS_write(FS, fd, buf, sz);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  u32_t written = get_flash_ops_log_write_bytes();
-  printf("  written bytes: %i\n", written);
-
-
-  printf("  create error file\n");
-  fd = SPIFFS_open(FS, name2, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
-  TEST_CHECK(fd > 0);
-  clear_flash_ops_log();
-  invoke_error_after_write_bytes(written/2, 0);
-  res = SPIFFS_write(FS, fd, buf, sz);
-  SPIFFS_close(FS, fd);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_TEST);
-
-  clear_flash_ops_log();
-
-#if SPIFFS_CACHE
-  // delete all cache
-  spiffs_cache *cache = spiffs_get_cache(FS);
-  cache->cpage_use_map = 0;
-#endif
-
-
-  printf("  read error file\n");
-  fd = SPIFFS_open(FS, name2, SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-
-  spiffs_stat s;
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  printf("  file size: %i\n", s.size);
-
-  if (s.size > 0) {
-    u8_t *buf2 = malloc(s.size);
-    res = SPIFFS_read(FS, fd, buf2, s.size);
-    TEST_CHECK(res >= 0);
-
-    u32_t ix = 0;
-    for (ix = 0; ix < s.size; ix += 16) {
-      int i;
-      printf("  ");
-      for (i = 0; i < 16; i++) {
-        printf("%02x", buf[ix+i]);
-      }
-      printf("  ");
-      for (i = 0; i < 16; i++) {
-        printf("%02x", buf2[ix+i]);
-      }
-      printf("\n");
-    }
-    free(buf2);
-  }
-  SPIFFS_close(FS, fd);
-
-
-  printf("  FS check\n");
-  SPIFFS_check(FS);
-
-  printf("  read error file again\n");
-  fd = SPIFFS_open(FS, name2, SPIFFS_APPEND | SPIFFS_RDWR, 0);
-  TEST_CHECK(fd > 0);
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  printf("  file size: %i\n", s.size);
-  printf("  write file\n");
-  res = SPIFFS_write(FS, fd, buf, sz);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  free(buf);
-
-  return TEST_RES_OK;
-
-} TEST_END(interrupted_write)
-
-SUITE_END(dev_tests)

+ 0 - 1394
app/spiffs/test/test_hydrogen.c

@@ -1,1394 +0,0 @@
-/*
- * test_suites.c
- *
- *  Created on: Jun 19, 2013
- *      Author: petera
- */
-
-
-#include "testrunner.h"
-#include "test_spiffs.h"
-#include "spiffs_nucleus.h"
-#include "spiffs.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <unistd.h>
-
-SUITE(hydrogen_tests)
-void setup() {
-  _setup();
-}
-void teardown() {
-  _teardown();
-}
-
-TEST(info)
-{
-  u32_t used, total;
-  int res = SPIFFS_info(FS, &total, &used);
-  TEST_CHECK(res == SPIFFS_OK);
-  TEST_CHECK(used == 0);
-  TEST_CHECK(total < __fs.cfg.phys_size);
-  return TEST_RES_OK;
-}
-TEST_END(info)
-
-
-TEST(missing_file)
-{
-  spiffs_file fd = SPIFFS_open(FS, "this_wont_exist", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd < 0);
-  return TEST_RES_OK;
-}
-TEST_END(missing_file)
-
-
-TEST(bad_fd)
-{
-  int res;
-  spiffs_stat s;
-  spiffs_file fd = SPIFFS_open(FS, "this_wont_exist", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd < 0);
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR);
-  res = SPIFFS_fremove(FS, fd);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR);
-  res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR);
-  res = SPIFFS_read(FS, fd, 0, 0);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR);
-  res = SPIFFS_write(FS, fd, 0, 0);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR);
-  return TEST_RES_OK;
-}
-TEST_END(bad_fd)
-
-
-TEST(closed_fd)
-{
-  int res;
-  spiffs_stat s;
-  res = test_create_file("file");
-  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd >= 0);
-  SPIFFS_close(FS, fd);
-
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res = SPIFFS_fremove(FS, fd);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res = SPIFFS_read(FS, fd, 0, 0);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res = SPIFFS_write(FS, fd, 0, 0);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  return TEST_RES_OK;
-}
-TEST_END(closed_fd)
-
-
-TEST(deleted_same_fd)
-{
-  int res;
-  spiffs_stat s;
-  spiffs_file fd;
-  res = test_create_file("remove");
-  fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0);
-  TEST_CHECK(fd >= 0);
-  fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0);
-  TEST_CHECK(fd >= 0);
-  res = SPIFFS_fremove(FS, fd);
-  TEST_CHECK(res >= 0);
-
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res = SPIFFS_fremove(FS, fd);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res = SPIFFS_read(FS, fd, 0, 0);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res = SPIFFS_write(FS, fd, 0, 0);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-
-  return TEST_RES_OK;
-}
-TEST_END(deleted_same_fd)
-
-
-TEST(deleted_other_fd)
-{
-  int res;
-  spiffs_stat s;
-  spiffs_file fd, fd_orig;
-  res = test_create_file("remove");
-  fd_orig = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0);
-  TEST_CHECK(fd_orig >= 0);
-  fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0);
-  TEST_CHECK(fd >= 0);
-  res = SPIFFS_fremove(FS, fd_orig);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd_orig);
-
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res = SPIFFS_fremove(FS, fd);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res = SPIFFS_read(FS, fd, 0, 0);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-  res = SPIFFS_write(FS, fd, 0, 0);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
-
-  return TEST_RES_OK;
-}
-TEST_END(deleted_other_fd)
-
-
-TEST(file_by_open)
-{
-  int res;
-  spiffs_stat s;
-  spiffs_file fd = SPIFFS_open(FS, "filebopen", SPIFFS_CREAT, 0);
-  TEST_CHECK(fd >= 0);
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  TEST_CHECK(strcmp((char*)s.name, "filebopen") == 0);
-  TEST_CHECK(s.size == 0);
-  SPIFFS_close(FS, fd);
-
-  fd = SPIFFS_open(FS, "filebopen", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd >= 0);
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  TEST_CHECK(strcmp((char*)s.name, "filebopen") == 0);
-  TEST_CHECK(s.size == 0);
-  SPIFFS_close(FS, fd);
-  return TEST_RES_OK;
-}
-TEST_END(file_by_open)
-
-
-TEST(file_by_creat)
-{
-  int res;
-  res = test_create_file("filebcreat");
-  TEST_CHECK(res >= 0);
-  res = SPIFFS_creat(FS, "filebcreat", 0);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS)==SPIFFS_ERR_CONFLICTING_NAME);
-  return TEST_RES_OK;
-}
-TEST_END(file_by_creat)
-
-TEST(list_dir)
-{
-  int res;
-
-  char *files[4] = {
-      "file1",
-      "file2",
-      "file3",
-      "file4"
-  };
-  int file_cnt = sizeof(files)/sizeof(char *);
-
-  int i;
-
-  for (i = 0; i < file_cnt; i++) {
-    res = test_create_file(files[i]);
-    TEST_CHECK(res >= 0);
-  }
-
-  spiffs_DIR d;
-  struct spiffs_dirent e;
-  struct spiffs_dirent *pe = &e;
-
-  SPIFFS_opendir(FS, "/", &d);
-  int found = 0;
-  while ((pe = SPIFFS_readdir(&d, pe))) {
-    printf("  %s [%04x] size:%i\n", pe->name, pe->obj_id, pe->size);
-    for (i = 0; i < file_cnt; i++) {
-      if (strcmp(files[i], pe->name) == 0) {
-        found++;
-        break;
-      }
-    }
-  }
-  SPIFFS_closedir(&d);
-
-  TEST_CHECK(found == file_cnt);
-
-  return TEST_RES_OK;
-}
-TEST_END(list_dir)
-
-
-TEST(open_by_dirent) {
-  int res;
-
-  char *files[4] = {
-      "file1",
-      "file2",
-      "file3",
-      "file4"
-  };
-  int file_cnt = sizeof(files)/sizeof(char *);
-
-  int i;
-  int size = SPIFFS_DATA_PAGE_SIZE(FS);
-
-  for (i = 0; i < file_cnt; i++) {
-    res = test_create_and_write_file(files[i], size, size);
-    TEST_CHECK(res >= 0);
-  }
-
-  spiffs_DIR d;
-  struct spiffs_dirent e;
-  struct spiffs_dirent *pe = &e;
-
-  int found = 0;
-  SPIFFS_opendir(FS, "/", &d);
-  while ((pe = SPIFFS_readdir(&d, pe))) {
-    spiffs_file fd = SPIFFS_open_by_dirent(FS, pe, SPIFFS_RDWR, 0);
-    TEST_CHECK(fd >= 0);
-    res = read_and_verify_fd(fd, pe->name);
-    TEST_CHECK(res == SPIFFS_OK);
-    fd = SPIFFS_open_by_dirent(FS, pe, SPIFFS_RDWR, 0);
-    TEST_CHECK(fd >= 0);
-    res = SPIFFS_fremove(FS, fd);
-    TEST_CHECK(res == SPIFFS_OK);
-    SPIFFS_close(FS, fd);
-    found++;
-  }
-  SPIFFS_closedir(&d);
-
-  TEST_CHECK(found == file_cnt);
-
-  found = 0;
-  SPIFFS_opendir(FS, "/", &d);
-  while ((pe = SPIFFS_readdir(&d, pe))) {
-    found++;
-  }
-  SPIFFS_closedir(&d);
-
-  TEST_CHECK(found == 0);
-
-  return TEST_RES_OK;
-
-} TEST_END(open_by_dirent)
-
-
-TEST(rename) {
-  int res;
-
-  char *src_name = "baah";
-  char *dst_name = "booh";
-  char *dst_name2 = "beeh";
-  int size = SPIFFS_DATA_PAGE_SIZE(FS);
-
-  res = test_create_and_write_file(src_name, size, size);
-  TEST_CHECK(res >= 0);
-
-  res = SPIFFS_rename(FS, src_name, dst_name);
-  TEST_CHECK(res >= 0);
-
-  res = SPIFFS_rename(FS, dst_name, dst_name);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_CONFLICTING_NAME);
-
-  res = SPIFFS_rename(FS, src_name, dst_name2);
-  TEST_CHECK(res < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND);
-
-  return TEST_RES_OK;
-} TEST_END(rename)
-
-
-TEST(remove_single_by_path)
-{
-  int res;
-  spiffs_file fd;
-  res = test_create_file("remove");
-  TEST_CHECK(res >= 0);
-  res = SPIFFS_remove(FS, "remove");
-  TEST_CHECK(res >= 0);
-  fd = SPIFFS_open(FS, "remove", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND);
-
-  return TEST_RES_OK;
-}
-TEST_END(remove_single_by_path)
-
-
-TEST(remove_single_by_fd)
-{
-  int res;
-  spiffs_file fd;
-  res = test_create_file("remove");
-  TEST_CHECK(res >= 0);
-  fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0);
-  TEST_CHECK(fd >= 0);
-  res = SPIFFS_fremove(FS, fd);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-  fd = SPIFFS_open(FS, "remove", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND);
-
-  return TEST_RES_OK;
-}
-TEST_END(remove_single_by_fd)
-
-
-TEST(write_big_file_chunks_page)
-{
-  int size = ((50*(FS)->cfg.phys_size)/100);
-  printf("  filesize %i\n", size);
-  int res = test_create_and_write_file("bigfile", size, SPIFFS_DATA_PAGE_SIZE(FS));
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("bigfile");
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-}
-TEST_END(write_big_file_chunks_page)
-
-
-TEST(write_big_files_chunks_page)
-{
-  char name[32];
-  int f;
-  int files = 10;
-  int res;
-  int size = ((50*(FS)->cfg.phys_size)/100)/files;
-  printf("  filesize %i\n", size);
-  for (f = 0; f < files; f++) {
-    sprintf(name, "bigfile%i", f);
-    res = test_create_and_write_file(name, size, SPIFFS_DATA_PAGE_SIZE(FS));
-    TEST_CHECK(res >= 0);
-  }
-  for (f = 0; f < files; f++) {
-    sprintf(name, "bigfile%i", f);
-    res = read_and_verify(name);
-    TEST_CHECK(res >= 0);
-  }
-
-  return TEST_RES_OK;
-}
-TEST_END(write_big_files_chunks_page)
-
-
-TEST(write_big_file_chunks_index)
-{
-  int size = ((50*(FS)->cfg.phys_size)/100);
-  printf("  filesize %i\n", size);
-  int res = test_create_and_write_file("bigfile", size, SPIFFS_DATA_PAGE_SIZE(FS) * SPIFFS_OBJ_HDR_IX_LEN(FS));
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("bigfile");
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-}
-TEST_END(write_big_file_chunks_index)
-
-
-TEST(write_big_files_chunks_index)
-{
-  char name[32];
-  int f;
-  int files = 10;
-  int res;
-  int size = ((50*(FS)->cfg.phys_size)/100)/files;
-  printf("  filesize %i\n", size);
-  for (f = 0; f < files; f++) {
-    sprintf(name, "bigfile%i", f);
-    res = test_create_and_write_file(name, size, SPIFFS_DATA_PAGE_SIZE(FS) * SPIFFS_OBJ_HDR_IX_LEN(FS));
-    TEST_CHECK(res >= 0);
-  }
-  for (f = 0; f < files; f++) {
-    sprintf(name, "bigfile%i", f);
-    res = read_and_verify(name);
-    TEST_CHECK(res >= 0);
-  }
-
-  return TEST_RES_OK;
-}
-TEST_END(write_big_files_chunks_index)
-
-
-TEST(write_big_file_chunks_huge)
-{
-  int size = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS);
-  printf("  filesize %i\n", size);
-  int res = test_create_and_write_file("bigfile", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("bigfile");
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-}
-TEST_END(write_big_file_chunks_huge)
-
-
-TEST(write_big_files_chunks_huge)
-{
-  char name[32];
-  int f;
-  int files = 10;
-  int res;
-  int size = ((50*(FS)->cfg.phys_size)/100)/files;
-  printf("  filesize %i\n", size);
-  for (f = 0; f < files; f++) {
-    sprintf(name, "bigfile%i", f);
-    res = test_create_and_write_file(name, size, size);
-    TEST_CHECK(res >= 0);
-  }
-  for (f = 0; f < files; f++) {
-    sprintf(name, "bigfile%i", f);
-    res = read_and_verify(name);
-    TEST_CHECK(res >= 0);
-  }
-
-  return TEST_RES_OK;
-}
-TEST_END(write_big_files_chunks_huge)
-
-
-TEST(truncate_big_file)
-{
-  int size = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS);
-  printf("  filesize %i\n", size);
-  int res = test_create_and_write_file("bigfile", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("bigfile");
-  TEST_CHECK(res >= 0);
-  spiffs_file fd = SPIFFS_open(FS, "bigfile", SPIFFS_RDWR, 0);
-  TEST_CHECK(fd > 0);
-  res = SPIFFS_fremove(FS, fd);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  fd = SPIFFS_open(FS, "bigfile", SPIFFS_RDWR, 0);
-  TEST_CHECK(fd < 0);
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND);
-
-  return TEST_RES_OK;
-}
-TEST_END(truncate_big_file)
-
-
-TEST(simultaneous_write) {
-  int res = SPIFFS_creat(FS, "simul1", 0);
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd1 = SPIFFS_open(FS, "simul1", SPIFFS_RDWR, 0);
-  TEST_CHECK(fd1 > 0);
-  spiffs_file fd2 = SPIFFS_open(FS, "simul1", SPIFFS_RDWR, 0);
-  TEST_CHECK(fd2 > 0);
-  spiffs_file fd3 = SPIFFS_open(FS, "simul1", SPIFFS_RDWR, 0);
-  TEST_CHECK(fd3 > 0);
-
-  u8_t data1 = 1;
-  u8_t data2 = 2;
-  u8_t data3 = 3;
-
-  res = SPIFFS_write(FS, fd1, &data1, 1);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd1);
-  res = SPIFFS_write(FS, fd2, &data2, 1);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd2);
-  res = SPIFFS_write(FS, fd3, &data3, 1);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd3);
-
-  spiffs_stat s;
-  res = SPIFFS_stat(FS, "simul1", &s);
-  TEST_CHECK(res >= 0);
-
-  TEST_CHECK(s.size == 1);
-
-  u8_t rdata;
-  spiffs_file fd = SPIFFS_open(FS, "simul1", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  res = SPIFFS_read(FS, fd, &rdata, 1);
-  TEST_CHECK(res >= 0);
-
-  TEST_CHECK(rdata == data3);
-
-  return TEST_RES_OK;
-}
-TEST_END(simultaneous_write)
-
-
-TEST(simultaneous_write_append) {
-  int res = SPIFFS_creat(FS, "simul2", 0);
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd1 = SPIFFS_open(FS, "simul2", SPIFFS_RDWR | SPIFFS_APPEND, 0);
-  TEST_CHECK(fd1 > 0);
-  spiffs_file fd2 = SPIFFS_open(FS, "simul2", SPIFFS_RDWR | SPIFFS_APPEND, 0);
-  TEST_CHECK(fd2 > 0);
-  spiffs_file fd3 = SPIFFS_open(FS, "simul2", SPIFFS_RDWR | SPIFFS_APPEND, 0);
-  TEST_CHECK(fd3 > 0);
-
-  u8_t data1 = 1;
-  u8_t data2 = 2;
-  u8_t data3 = 3;
-
-  res = SPIFFS_write(FS, fd1, &data1, 1);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd1);
-  res = SPIFFS_write(FS, fd2, &data2, 1);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd2);
-  res = SPIFFS_write(FS, fd3, &data3, 1);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd3);
-
-  spiffs_stat s;
-  res = SPIFFS_stat(FS, "simul2", &s);
-  TEST_CHECK(res >= 0);
-
-  TEST_CHECK(s.size == 3);
-
-  u8_t rdata[3];
-  spiffs_file fd = SPIFFS_open(FS, "simul2", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  res = SPIFFS_read(FS, fd, &rdata, 3);
-  TEST_CHECK(res >= 0);
-
-  TEST_CHECK(rdata[0] == data1);
-  TEST_CHECK(rdata[1] == data2);
-  TEST_CHECK(rdata[2] == data3);
-
-  return TEST_RES_OK;
-}
-TEST_END(simultaneous_write_append)
-
-
-TEST(file_uniqueness)
-{
-  int res;
-  spiffs_file fd;
-  char fname[32];
-  int files = ((SPIFFS_CFG_PHYS_SZ(FS) * 75) / 100) / 2 / SPIFFS_CFG_LOG_PAGE_SZ(FS);
-      //(FS_PURE_DATA_PAGES(FS) / 2) - SPIFFS_PAGES_PER_BLOCK(FS)*8;
-  int i;
-  printf("  creating %i files\n", files);
-  for (i = 0; i < files; i++) {
-    char content[20];
-    sprintf(fname, "file%i", i);
-    sprintf(content, "%i", i);
-    res = test_create_file(fname);
-    TEST_CHECK(res >= 0);
-    fd = SPIFFS_open(FS, fname, SPIFFS_APPEND | SPIFFS_RDWR, 0);
-    TEST_CHECK(fd >= 0);
-    res = SPIFFS_write(FS, fd, content, strlen(content)+1);
-    TEST_CHECK(res >= 0);
-    SPIFFS_close(FS, fd);
-  }
-  printf("  checking %i files\n", files);
-  for (i = 0; i < files; i++) {
-    char content[20];
-    char ref_content[20];
-    sprintf(fname, "file%i", i);
-    sprintf(content, "%i", i);
-    fd = SPIFFS_open(FS, fname, SPIFFS_RDONLY, 0);
-    TEST_CHECK(fd >= 0);
-    res = SPIFFS_read(FS, fd, ref_content, strlen(content)+1);
-    TEST_CHECK(res >= 0);
-    TEST_CHECK(strcmp(ref_content, content) == 0);
-    SPIFFS_close(FS, fd);
-  }
-  printf("  removing %i files\n", files/2);
-  for (i = 0; i < files; i += 2) {
-    sprintf(fname, "file%i", i);
-    res = SPIFFS_remove(FS, fname);
-    TEST_CHECK(res >= 0);
-  }
-  printf("  creating %i files\n", files/2);
-  for (i = 0; i < files; i += 2) {
-    char content[20];
-    sprintf(fname, "file%i", i);
-    sprintf(content, "new%i", i);
-    res = test_create_file(fname);
-    TEST_CHECK(res >= 0);
-    fd = SPIFFS_open(FS, fname, SPIFFS_APPEND | SPIFFS_RDWR, 0);
-    TEST_CHECK(fd >= 0);
-    res = SPIFFS_write(FS, fd, content, strlen(content)+1);
-    TEST_CHECK(res >= 0);
-    SPIFFS_close(FS, fd);
-  }
-  printf("  checking %i files\n", files);
-  for (i = 0; i < files; i++) {
-    char content[20];
-    char ref_content[20];
-    sprintf(fname, "file%i", i);
-    if ((i & 1) == 0) {
-      sprintf(content, "new%i", i);
-    } else {
-      sprintf(content, "%i", i);
-    }
-    fd = SPIFFS_open(FS, fname, SPIFFS_RDONLY, 0);
-    TEST_CHECK(fd >= 0);
-    res = SPIFFS_read(FS, fd, ref_content, strlen(content)+1);
-    TEST_CHECK(res >= 0);
-    TEST_CHECK(strcmp(ref_content, content) == 0);
-    SPIFFS_close(FS, fd);
-  }
-
-  return TEST_RES_OK;
-}
-TEST_END(file_uniqueness)
-
-int create_and_read_back(int size, int chunk) {
-  char *name = "file";
-  spiffs_file fd;
-  s32_t res;
-
-  u8_t *buf = malloc(size);
-  memrand(buf, size);
-
-  res = test_create_file(name);
-  CHECK(res >= 0);
-  fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0);
-  CHECK(fd >= 0);
-  res = SPIFFS_write(FS, fd, buf, size);
-  CHECK(res >= 0);
-
-  spiffs_stat stat;
-  res = SPIFFS_fstat(FS, fd, &stat);
-  CHECK(res >= 0);
-  CHECK(stat.size == size);
-
-  SPIFFS_close(FS, fd);
-
-  fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0);
-  CHECK(fd >= 0);
-
-  u8_t *rbuf = malloc(size);
-  int offs = 0;
-  while (offs < size) {
-    int len = MIN(size - offs, chunk);
-    res = SPIFFS_read(FS, fd, &rbuf[offs], len);
-    CHECK(res >= 0);
-    CHECK(memcmp(&rbuf[offs], &buf[offs], len) == 0);
-
-    offs += chunk;
-  }
-
-  CHECK(memcmp(&rbuf[0], &buf[0], size) == 0);
-
-  SPIFFS_close(FS, fd);
-
-  free(rbuf);
-  free(buf);
-
-  return 0;
-}
-
-TEST(read_chunk_1)
-{
-  TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*8, 1) == 0);
-  return TEST_RES_OK;
-}
-TEST_END(read_chunk_1)
-
-
-TEST(read_chunk_page)
-{
-  TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))*2,
-      SPIFFS_DATA_PAGE_SIZE(FS)) == 0);
-  return TEST_RES_OK;
-}
-TEST_END(read_chunk_page)
-
-
-TEST(read_chunk_index)
-{
-  TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))*4,
-      SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))) == 0);
-  return TEST_RES_OK;
-}
-TEST_END(read_chunk_index)
-
-
-TEST(read_chunk_huge)
-{
-  int sz = (2*(FS)->cfg.phys_size)/3;
-  TEST_CHECK(create_and_read_back(sz, sz) == 0);
-  return TEST_RES_OK;
-}
-TEST_END(read_chunk_huge)
-
-
-TEST(read_beyond)
-{
-  char *name = "file";
-  spiffs_file fd;
-  s32_t res;
-  u32_t size = SPIFFS_DATA_PAGE_SIZE(FS)*2;
-
-  u8_t *buf = malloc(size);
-  memrand(buf, size);
-
-  res = test_create_file(name);
-  CHECK(res >= 0);
-  fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0);
-  CHECK(fd >= 0);
-  res = SPIFFS_write(FS, fd, buf, size);
-  CHECK(res >= 0);
-
-  spiffs_stat stat;
-  res = SPIFFS_fstat(FS, fd, &stat);
-  CHECK(res >= 0);
-  CHECK(stat.size == size);
-
-  SPIFFS_close(FS, fd);
-
-  fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0);
-  CHECK(fd >= 0);
-
-  u8_t *rbuf = malloc(size+10);
-  res = SPIFFS_read(FS, fd, rbuf, size+10);
-
-  SPIFFS_close(FS, fd);
-
-  free(rbuf);
-  free(buf);
-
-  TEST_CHECK(res == size);
-
-  return TEST_RES_OK;
-}
-TEST_END(read_beyond)
-
-
-TEST(bad_index_1) {
-  int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
-  int res = test_create_and_write_file("file", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  spiffs_stat s;
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  // modify object index, find object index header
-  spiffs_page_ix pix;
-  res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
-  TEST_CHECK(res >= 0);
-
-  // set object index entry 2 to a bad page, free
-  u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 2 * sizeof(spiffs_page_ix);
-  spiffs_page_ix bad_pix_ref = (spiffs_page_ix)-1;
-  area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
-
-#if SPIFFS_CACHE
-  // delete all cache
-  spiffs_cache *cache = spiffs_get_cache(FS);
-  cache->cpage_use_map = 0;
-#endif
-
-  res = read_and_verify("file");
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_INDEX_REF_FREE);
-
-  return TEST_RES_OK;
-} TEST_END(bad_index_1)
-
-
-TEST(bad_index_2) {
-  int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
-  int res = test_create_and_write_file("file", size, size);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("file");
-  TEST_CHECK(res >= 0);
-
-  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
-  TEST_CHECK(fd > 0);
-  spiffs_stat s;
-  res = SPIFFS_fstat(FS, fd, &s);
-  TEST_CHECK(res >= 0);
-  SPIFFS_close(FS, fd);
-
-  // modify object index, find object index header
-  spiffs_page_ix pix;
-  res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
-  TEST_CHECK(res >= 0);
-
-  // set object index entry 2 to a bad page, lu
-  u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 2 * sizeof(spiffs_page_ix);
-  spiffs_page_ix bad_pix_ref = SPIFFS_OBJ_LOOKUP_PAGES(FS)-1;
-  area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
-
-#if SPIFFS_CACHE
-  // delete all cache
-  spiffs_cache *cache = spiffs_get_cache(FS);
-  cache->cpage_use_map = 0;
-#endif
-
-  res = read_and_verify("file");
-  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_INDEX_REF_LU);
-
-  return TEST_RES_OK;
-} TEST_END(bad_index_2)
-
-
-TEST(lseek_simple_modification) {
-  int res;
-  spiffs_file fd;
-  char *fname = "seekfile";
-  int i;
-  int len = 4096;
-  fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
-  TEST_CHECK(fd > 0);
-  int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
-  u8_t *buf = malloc(len);
-  memrand(buf, len);
-  res = SPIFFS_write(FS, fd, buf, len);
-  TEST_CHECK(res >= 0);
-  write(pfd, buf, len);
-  free(buf);
-  res = read_and_verify(fname);
-  TEST_CHECK(res >= 0);
-
-  res = SPIFFS_lseek(FS, fd, len/2, SPIFFS_SEEK_SET);
-  TEST_CHECK(res >= 0);
-  lseek(pfd, len/2, SEEK_SET);
-  len = len/4;
-  buf = malloc(len);
-  memrand(buf, len);
-  res = SPIFFS_write(FS, fd, buf, len);
-  TEST_CHECK(res >= 0);
-  write(pfd, buf, len);
-  free(buf);
-
-  res = read_and_verify(fname);
-  TEST_CHECK(res >= 0);
-
-  SPIFFS_close(FS, fd);
-  close(pfd);
-
-  return TEST_RES_OK;
-}
-TEST_END(lseek_simple_modification)
-
-
-TEST(lseek_modification_append) {
-  int res;
-  spiffs_file fd;
-  char *fname = "seekfile";
-  int i;
-  int len = 4096;
-  fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
-  TEST_CHECK(fd > 0);
-  int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
-  u8_t *buf = malloc(len);
-  memrand(buf, len);
-  res = SPIFFS_write(FS, fd, buf, len);
-  TEST_CHECK(res >= 0);
-  write(pfd, buf, len);
-  free(buf);
-  res = read_and_verify(fname);
-  TEST_CHECK(res >= 0);
-
-  res = SPIFFS_lseek(FS, fd, len/2, SPIFFS_SEEK_SET);
-  TEST_CHECK(res >= 0);
-  lseek(pfd, len/2, SEEK_SET);
-
-  buf = malloc(len);
-  memrand(buf, len);
-  res = SPIFFS_write(FS, fd, buf, len);
-  TEST_CHECK(res >= 0);
-  write(pfd, buf, len);
-  free(buf);
-
-  res = read_and_verify(fname);
-  TEST_CHECK(res >= 0);
-
-  SPIFFS_close(FS, fd);
-  close(pfd);
-
-  return TEST_RES_OK;
-}
-TEST_END(lseek_modification_append)
-
-
-TEST(lseek_modification_append_multi) {
-  int res;
-  spiffs_file fd;
-  char *fname = "seekfile";
-  int len = 1024;
-  int runs = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS) / (len/2);
-
-  fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
-  TEST_CHECK(fd > 0);
-  int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
-  u8_t *buf = malloc(len);
-  memrand(buf, len);
-  res = SPIFFS_write(FS, fd, buf, len);
-  TEST_CHECK(res >= 0);
-  write(pfd, buf, len);
-  free(buf);
-  res = read_and_verify(fname);
-  TEST_CHECK(res >= 0);
-
-  while (runs--) {
-    res = SPIFFS_lseek(FS, fd, -len/2, SPIFFS_SEEK_END);
-    TEST_CHECK(res >= 0);
-    lseek(pfd, -len/2, SEEK_END);
-
-    buf = malloc(len);
-    memrand(buf, len);
-    res = SPIFFS_write(FS, fd, buf, len);
-    TEST_CHECK(res >= 0);
-    write(pfd, buf, len);
-    free(buf);
-
-    res = read_and_verify(fname);
-    TEST_CHECK(res >= 0);
-  }
-
-  SPIFFS_close(FS, fd);
-  close(pfd);
-
-  return TEST_RES_OK;
-}
-TEST_END(lseek_modification_append_multi)
-
-
-TEST(lseek_read) {
-  int res;
-  spiffs_file fd;
-  char *fname = "seekfile";
-  int len = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS);
-  int runs = 100000;
-
-  fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
-  TEST_CHECK(fd > 0);
-  u8_t *refbuf = malloc(len);
-  memrand(refbuf, len);
-  res = SPIFFS_write(FS, fd, refbuf, len);
-  TEST_CHECK(res >= 0);
-
-  int offs = 0;
-  res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_SET);
-  TEST_CHECK(res >= 0);
-
-  while (runs--) {
-    int i;
-    u8_t buf[64];
-    if (offs + 41 + sizeof(buf) >= len) {
-      offs = (offs + 41 + sizeof(buf)) % len;
-      res = SPIFFS_lseek(FS, fd, offs, SPIFFS_SEEK_SET);
-      TEST_CHECK(res >= 0);
-    }
-    res = SPIFFS_lseek(FS, fd, 41, SPIFFS_SEEK_CUR);
-    TEST_CHECK(res >= 0);
-    offs += 41;
-    res = SPIFFS_read(FS, fd, buf, sizeof(buf));
-    TEST_CHECK(res >= 0);
-    for (i = 0; i < sizeof(buf); i++) {
-      if (buf[i] != refbuf[offs+i]) {
-        printf("  mismatch at offs %i\n", offs);
-      }
-      TEST_CHECK(buf[i] == refbuf[offs+i]);
-    }
-    offs += sizeof(buf);
-
-    res = SPIFFS_lseek(FS, fd, -((u32_t)sizeof(buf)+11), SPIFFS_SEEK_CUR);
-    TEST_CHECK(res >= 0);
-    offs -= (sizeof(buf)+11);
-    res = SPIFFS_read(FS, fd, buf, sizeof(buf));
-    TEST_CHECK(res >= 0);
-    for (i = 0; i < sizeof(buf); i++) {
-      if (buf[i] != refbuf[offs+i]) {
-        printf("  mismatch at offs %i\n", offs);
-      }
-      TEST_CHECK(buf[i] == refbuf[offs+i]);
-    }
-    offs += sizeof(buf);
-  }
-
-  free(refbuf);
-  SPIFFS_close(FS, fd);
-
-  return TEST_RES_OK;
-}
-TEST_END(lseek_read)
-
-
-TEST(write_small_file_chunks_1)
-{
-  int res = test_create_and_write_file("smallfile", 256, 1);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("smallfile");
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-}
-TEST_END(write_small_file_chunks_1)
-
-
-TEST(write_small_files_chunks_1)
-{
-  char name[32];
-  int f;
-  int size = 512;
-  int files = ((20*(FS)->cfg.phys_size)/100)/size;
-  int res;
-  for (f = 0; f < files; f++) {
-    sprintf(name, "smallfile%i", f);
-    res = test_create_and_write_file(name, size, 1);
-    TEST_CHECK(res >= 0);
-  }
-  for (f = 0; f < files; f++) {
-    sprintf(name, "smallfile%i", f);
-    res = read_and_verify(name);
-    TEST_CHECK(res >= 0);
-  }
-
-  return TEST_RES_OK;
-}
-TEST_END(write_small_files_chunks_1)
-
-TEST(write_big_file_chunks_1)
-{
-  int size = ((50*(FS)->cfg.phys_size)/100);
-  printf("  filesize %i\n", size);
-  int res = test_create_and_write_file("bigfile", size, 1);
-  TEST_CHECK(res >= 0);
-  res = read_and_verify("bigfile");
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-}
-TEST_END(write_big_file_chunks_1)
-
-TEST(write_big_files_chunks_1)
-{
-  char name[32];
-  int f;
-  int files = 10;
-  int res;
-  int size = ((50*(FS)->cfg.phys_size)/100)/files;
-  printf("  filesize %i\n", size);
-  for (f = 0; f < files; f++) {
-    sprintf(name, "bigfile%i", f);
-    res = test_create_and_write_file(name, size, 1);
-    TEST_CHECK(res >= 0);
-  }
-  for (f = 0; f < files; f++) {
-    sprintf(name, "bigfile%i", f);
-    res = read_and_verify(name);
-    TEST_CHECK(res >= 0);
-  }
-
-  return TEST_RES_OK;
-}
-TEST_END(write_big_files_chunks_1)
-
-
-TEST(long_run_config_many_small_one_long)
-{
-  tfile_conf cfgs[] = {
-      {   .tsize = LARGE,     .ttype = MODIFIED,      .tlife = LONG
-      },
-      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = LONG
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = LONG
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = LONG
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = LONG
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = LONG
-      },
-  };
-
-  int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 206, 5, 0);
-  TEST_CHECK(res >= 0);
-  return TEST_RES_OK;
-}
-TEST_END(long_run_config_many_small_one_long)
-
-TEST(long_run_config_many_medium)
-{
-  tfile_conf cfgs[] = {
-      {   .tsize = MEDIUM,    .ttype = MODIFIED,      .tlife = LONG
-      },
-      {   .tsize = MEDIUM,    .ttype = APPENDED,      .tlife = LONG
-      },
-      {   .tsize = LARGE,     .ttype = MODIFIED,      .tlife = LONG
-      },
-      {   .tsize = MEDIUM,    .ttype = APPENDED,      .tlife = LONG
-      },
-      {   .tsize = MEDIUM,    .ttype = MODIFIED,      .tlife = LONG
-      },
-      {   .tsize = MEDIUM,    .ttype = MODIFIED,      .tlife = LONG
-      },
-      {   .tsize = MEDIUM,    .ttype = APPENDED,      .tlife = LONG
-      },
-      {   .tsize = LARGE,     .ttype = MODIFIED,      .tlife = LONG
-      },
-      {   .tsize = MEDIUM,    .ttype = APPENDED,      .tlife = LONG
-      },
-      {   .tsize = MEDIUM,    .ttype = MODIFIED,      .tlife = LONG
-      },
-      {   .tsize = MEDIUM,    .ttype = MODIFIED,      .tlife = LONG
-      },
-      {   .tsize = MEDIUM,    .ttype = APPENDED,      .tlife = LONG
-      },
-      {   .tsize = LARGE,     .ttype = MODIFIED,      .tlife = LONG
-      },
-      {   .tsize = MEDIUM,    .ttype = APPENDED,      .tlife = LONG
-      },
-      {   .tsize = MEDIUM,    .ttype = MODIFIED,      .tlife = LONG
-      },
-  };
-
-  int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 305, 5, 0);
-  TEST_CHECK(res >= 0);
-  return TEST_RES_OK;
-}
-TEST_END(long_run_config_many_medium)
-
-
-TEST(long_run_config_many_small)
-{
-  tfile_conf cfgs[] = {
-      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = LONG
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = LONG
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = SHORT
-      },
-      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = MEDIUM
-      },
-      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = SHORT
-      },
-  };
-
-  int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 115, 6, 0);
-  TEST_CHECK(res >= 0);
-  return TEST_RES_OK;
-}
-TEST_END(long_run_config_many_small)
-
-
-TEST(long_run)
-{
-  tfile_conf cfgs[] = {
-      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = MEDIUM
-      },
-      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = SHORT
-      },
-      {   .tsize = MEDIUM,    .ttype = MODIFIED,      .tlife = SHORT
-      },
-      {   .tsize = MEDIUM,    .ttype = APPENDED,      .tlife = SHORT
-      },
-  };
-
-  int macro_runs = 500;
-  printf("  ");
-  u32_t clob_size = SPIFFS_CFG_PHYS_SZ(FS)/4;
-  int res = test_create_and_write_file("long_clobber", clob_size, clob_size);
-  TEST_CHECK(res >= 0);
-
-  res = read_and_verify("long_clobber");
-  TEST_CHECK(res >= 0);
-
-  while (macro_runs--) {
-    //printf("  ---- run %i ----\n", macro_runs);
-    if ((macro_runs % 20) == 0) {
-      printf(".");
-      fflush(stdout);
-    }
-    res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 11, 2, 0);
-    TEST_CHECK(res >= 0);
-  }
-  printf("\n");
-
-  res = read_and_verify("long_clobber");
-  TEST_CHECK(res >= 0);
-
-  res = SPIFFS_check(FS);
-  TEST_CHECK(res >= 0);
-
-  return TEST_RES_OK;
-}
-TEST_END(long_run)
-
-SUITE_END(hydrogen_tests)
-

+ 0 - 746
app/spiffs/test/test_spiffs.c

@@ -1,746 +0,0 @@
-/*
- * test_spiffs.c
- *
- *  Created on: Jun 19, 2013
- *      Author: petera
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "params_test.h"
-#include "spiffs.h"
-#include "spiffs_nucleus.h"
-
-#include "testrunner.h"
-
-#include "test_spiffs.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <unistd.h>
-
-static unsigned char area[PHYS_FLASH_SIZE];
-
-static int erases[256];
-static char _path[256];
-static u32_t bytes_rd = 0;
-static u32_t bytes_wr = 0;
-static u32_t reads = 0;
-static u32_t writes = 0;
-static u32_t error_after_bytes_written = 0;
-static u32_t error_after_bytes_read = 0;
-static char error_after_bytes_written_once_only = 0;
-static char error_after_bytes_read_once_only = 0;
-static char log_flash_ops = 1;
-static u32_t fs_check_fixes = 0;
-
-spiffs __fs;
-static u8_t _work[LOG_PAGE*2];
-static u8_t _fds[FD_BUF_SIZE];
-static u8_t _cache[CACHE_BUF_SIZE];
-
-static int check_valid_flash = 1;
-
-#define TEST_PATH "test_data/"
-
-char *make_test_fname(const char *name) {
-  sprintf(_path, "%s%s", TEST_PATH, name);
-  return _path;
-}
-
-void clear_test_path() {
-  DIR *dp;
-  struct dirent *ep;
-  dp = opendir(TEST_PATH);
-
-  if (dp != NULL) {
-    while ((ep = readdir(dp))) {
-      if (ep->d_name[0] != '.') {
-        sprintf(_path, "%s%s", TEST_PATH, ep->d_name);
-        remove(_path);
-      }
-    }
-    closedir(dp);
-  }
-}
-
-static s32_t _read(u32_t addr, u32_t size, u8_t *dst) {
-  if (log_flash_ops) {
-    bytes_rd += size;
-    reads++;
-    if (error_after_bytes_read > 0 && bytes_rd >= error_after_bytes_read) {
-      if (error_after_bytes_read_once_only) {
-        error_after_bytes_read = 0;
-      }
-      return SPIFFS_ERR_TEST;
-    }
-  }
-  if (addr < __fs.cfg.phys_addr) {
-    printf("FATAL read addr too low %08x < %08x\n", addr, SPIFFS_PHYS_ADDR);
-    exit(0);
-  }
-  if (addr + size > __fs.cfg.phys_addr + __fs.cfg.phys_size) {
-    printf("FATAL read addr too high %08x + %08x > %08x\n", addr, size, SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE);
-    exit(0);
-  }
-  memcpy(dst, &area[addr], size);
-  return 0;
-}
-
-static s32_t _write(u32_t addr, u32_t size, u8_t *src) {
-  int i;
-  //printf("wr %08x %i\n", addr, size);
-  if (log_flash_ops) {
-    bytes_wr += size;
-    writes++;
-    if (error_after_bytes_written > 0 && bytes_wr >= error_after_bytes_written) {
-      if (error_after_bytes_written_once_only) {
-        error_after_bytes_written = 0;
-      }
-      return SPIFFS_ERR_TEST;
-    }
-  }
-
-  if (addr < __fs.cfg.phys_addr) {
-    printf("FATAL write addr too low %08x < %08x\n", addr, SPIFFS_PHYS_ADDR);
-    exit(0);
-  }
-  if (addr + size > __fs.cfg.phys_addr + __fs.cfg.phys_size) {
-    printf("FATAL write addr too high %08x + %08x > %08x\n", addr, size, SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE);
-    exit(0);
-  }
-
-  for (i = 0; i < size; i++) {
-    if (((addr + i) & (__fs.cfg.log_page_size-1)) != offsetof(spiffs_page_header, flags)) {
-      if (check_valid_flash && ((area[addr + i] ^ src[i]) & src[i])) {
-        printf("trying to write %02x to %02x at addr %08x\n", src[i], area[addr + i], addr+i);
-        spiffs_page_ix pix = (addr + i) / LOG_PAGE;
-        dump_page(&__fs, pix);
-        return -1;
-      }
-    }
-    area[addr + i] &= src[i];
-  }
-  return 0;
-}
-
-static s32_t _erase(u32_t addr, u32_t size) {
-  if (addr & (__fs.cfg.phys_erase_block-1)) {
-    printf("trying to erase at addr %08x, out of boundary\n", addr);
-    return -1;
-  }
-  if (size & (__fs.cfg.phys_erase_block-1)) {
-    printf("trying to erase at with size %08x, out of boundary\n", size);
-    return -1;
-  }
-  erases[(addr-__fs.cfg.phys_addr)/__fs.cfg.phys_erase_block]++;
-  memset(&area[addr], 0xff, size);
-  return 0;
-}
-
-void hexdump_mem(u8_t *b, u32_t len) {
-  while (len--) {
-    if ((((intptr_t)b)&0x1f) == 0) {
-      printf("\n");
-    }
-    printf("%02x", *b++);
-  }
-  printf("\n");
-}
-
-void hexdump(u32_t addr, u32_t len) {
-  int remainder = (addr % 32) == 0 ? 0 : 32 - (addr % 32);
-  u32_t a;
-  for (a = addr - remainder; a < addr+len; a++) {
-    if ((a & 0x1f) == 0) {
-      if (a != addr) {
-        printf("  ");
-        int j;
-        for (j = 0; j < 32; j++) {
-          if (a-32+j < addr)
-            printf(" ");
-          else {
-            printf("%c", (area[a-32+j] < 32 || area[a-32+j] >= 0x7f) ? '.' : area[a-32+j]);
-          }
-        }
-      }
-      printf("%s    %08x: ", a<=addr ? "":"\n", a);
-    }
-    if (a < addr) {
-      printf("  ");
-    } else {
-      printf("%02x", area[a]);
-    }
-  }
-  int j;
-  printf("  ");
-  for (j = 0; j < 32; j++) {
-    if (a-32+j < addr)
-      printf(" ");
-    else {
-      printf("%c", (area[a-32+j] < 32 || area[a-32+j] >= 0x7f) ? '.' : area[a-32+j]);
-    }
-  }
-  printf("\n");
-}
-
-void dump_page(spiffs *fs, spiffs_page_ix p) {
-  printf("page %04x  ", p);
-  u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, p);
-  if (p % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
-    // obj lu page
-    printf("OBJ_LU");
-  } else {
-    u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , p)) +
-        SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, p) * sizeof(spiffs_obj_id);
-    spiffs_obj_id obj_id = *((spiffs_obj_id *)&area[obj_id_addr]);
-    // data page
-    spiffs_page_header *ph = (spiffs_page_header *)&area[addr];
-    printf("DATA %04x:%04x  ", obj_id, ph->span_ix);
-    printf("%s", ((ph->flags & SPIFFS_PH_FLAG_FINAL) == 0) ? "FIN " : "fin ");
-    printf("%s", ((ph->flags & SPIFFS_PH_FLAG_DELET) == 0) ? "DEL " : "del ");
-    printf("%s", ((ph->flags & SPIFFS_PH_FLAG_INDEX) == 0) ? "IDX " : "idx ");
-    printf("%s", ((ph->flags & SPIFFS_PH_FLAG_USED) == 0) ? "USD " : "usd ");
-    printf("%s  ", ((ph->flags & SPIFFS_PH_FLAG_IXDELE) == 0) ? "IDL " : "idl ");
-    if (obj_id & SPIFFS_OBJ_ID_IX_FLAG) {
-      // object index
-      printf("OBJ_IX");
-      if (ph->span_ix == 0) {
-        printf("_HDR  ");
-        spiffs_page_object_ix_header *oix_hdr = (spiffs_page_object_ix_header *)&area[addr];
-        printf("'%s'  %i bytes  type:%02x", oix_hdr->name, oix_hdr->size, oix_hdr->type);
-      }
-    } else {
-      // data page
-      printf("CONTENT");
-    }
-  }
-  printf("\n");
-  u32_t len = fs->cfg.log_page_size;
-  hexdump(addr, len);
-}
-
-void area_write(u32_t addr, u8_t *buf, u32_t size) {
-  int i;
-  for (i = 0; i < size; i++) {
-    area[addr + i] = *buf++;
-  }
-}
-
-void area_read(u32_t addr, u8_t *buf, u32_t size) {
-  int i;
-  for (i = 0; i < size; i++) {
-    *buf++ = area[addr + i];
-  }
-}
-
-void dump_erase_counts(spiffs *fs) {
-  spiffs_block_ix bix;
-  printf("  BLOCK     |\n");
-  printf("   AGE COUNT|\n");
-  for (bix = 0; bix < fs->block_count; bix++) {
-    printf("----%3i ----|", bix);
-  }
-  printf("\n");
-  for (bix = 0; bix < fs->block_count; bix++) {
-    spiffs_obj_id erase_mark;
-    _spiffs_rd(fs, 0, 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix), sizeof(spiffs_obj_id), (u8_t *)&erase_mark);
-    if (erases[bix] == 0) {
-      printf("            |");
-    } else {
-      printf("%7i %4i|", (fs->max_erase_count - erase_mark), erases[bix]);
-    }
-  }
-  printf("\n");
-}
-
-void dump_flash_access_stats() {
-  printf("  RD: %10i reads  %10i bytes %10i avg bytes/read\n", reads, bytes_rd, reads == 0 ? 0 : (bytes_rd / reads));
-  printf("  WR: %10i writes %10i bytes %10i avg bytes/write\n", writes, bytes_wr, writes == 0 ? 0 : (bytes_wr / writes));
-}
-
-
-static u32_t old_perc = 999;
-static void spiffs_check_cb_f(spiffs_check_type type, spiffs_check_report report,
-    u32_t arg1, u32_t arg2) {
-/*  if (report == SPIFFS_CHECK_PROGRESS && old_perc != arg1) {
-    old_perc = arg1;
-    printf("CHECK REPORT: ");
-    switch(type) {
-    case SPIFFS_CHECK_LOOKUP:
-      printf("LU "); break;
-    case SPIFFS_CHECK_INDEX:
-      printf("IX "); break;
-    case SPIFFS_CHECK_PAGE:
-      printf("PA "); break;
-    }
-    printf("%i%%\n", arg1 * 100 / 256);
-  }*/
-  if (report != SPIFFS_CHECK_PROGRESS) {
-    if (report != SPIFFS_CHECK_ERROR) fs_check_fixes++;
-    printf("   check: ");
-    switch (type) {
-    case SPIFFS_CHECK_INDEX:
-      printf("INDEX  "); break;
-    case SPIFFS_CHECK_LOOKUP:
-      printf("LOOKUP "); break;
-    case SPIFFS_CHECK_PAGE:
-      printf("PAGE   "); break;
-    default:
-      printf("????   "); break;
-    }
-    if (report == SPIFFS_CHECK_ERROR) {
-      printf("ERROR %i", arg1);
-    } else if (report == SPIFFS_CHECK_DELETE_BAD_FILE) {
-      printf("DELETE BAD FILE %04x", arg1);
-    } else if (report == SPIFFS_CHECK_DELETE_ORPHANED_INDEX) {
-      printf("DELETE ORPHANED INDEX %04x", arg1);
-    } else if (report == SPIFFS_CHECK_DELETE_PAGE) {
-      printf("DELETE PAGE %04x", arg1);
-    } else if (report == SPIFFS_CHECK_FIX_INDEX) {
-      printf("FIX INDEX %04x:%04x", arg1, arg2);
-    } else if (report == SPIFFS_CHECK_FIX_LOOKUP) {
-      printf("FIX INDEX %04x:%04x", arg1, arg2);
-    } else {
-      printf("??");
-    }
-    printf("\n");
-  }
-}
-
-void fs_reset_specific(u32_t phys_addr, u32_t phys_size,
-    u32_t phys_sector_size,
-    u32_t log_block_size, u32_t log_page_size) {
-  memset(area, 0xcc, sizeof(area));
-  memset(&area[phys_addr], 0xff, phys_size);
-
-  spiffs_config c;
-  c.hal_erase_f = _erase;
-  c.hal_read_f = _read;
-  c.hal_write_f = _write;
-  c.log_block_size = log_block_size;
-  c.log_page_size = log_page_size;
-  c.phys_addr = phys_addr;
-  c.phys_erase_block = phys_sector_size;
-  c.phys_size = phys_size;
-
-  memset(erases,0,sizeof(erases));
-  memset(_cache,0,sizeof(_cache));
-
-  SPIFFS_mount(&__fs, &c, _work, _fds, sizeof(_fds), _cache, sizeof(_cache), spiffs_check_cb_f);
-
-  clear_flash_ops_log();
-  log_flash_ops = 1;
-  fs_check_fixes = 0;
-}
-
-void fs_reset() {
-  fs_reset_specific(SPIFFS_PHYS_ADDR, SPIFFS_FLASH_SIZE, SECTOR_SIZE, LOG_BLOCK, LOG_PAGE);
-}
-
-void set_flash_ops_log(int enable) {
-  log_flash_ops = enable;
-}
-
-void clear_flash_ops_log() {
-  bytes_rd = 0;
-  bytes_wr = 0;
-  reads = 0;
-  writes = 0;
-  error_after_bytes_read = 0;
-  error_after_bytes_written = 0;
-}
-
-u32_t get_flash_ops_log_read_bytes() {
-  return bytes_rd;
-}
-
-u32_t get_flash_ops_log_write_bytes() {
-  return bytes_wr;
-}
-
-void invoke_error_after_read_bytes(u32_t b, char once_only) {
-  error_after_bytes_read = b;
-  error_after_bytes_read_once_only = once_only;
-}
-void invoke_error_after_write_bytes(u32_t b, char once_only) {
-  error_after_bytes_written = b;
-  error_after_bytes_written_once_only = once_only;
-}
-
-void fs_set_validate_flashing(int i) {
-  check_valid_flash = i;
-}
-
-void real_assert(int c, const char *n, const char *file, int l) {
-  if (c == 0) {
-    printf("ASSERT: %s %s @ %i\n", (n ? n : ""), file, l);
-    printf("fs errno:%i\n", __fs.err_code);
-    exit(0);
-  }
-}
-
-int read_and_verify(char *name) {
-  s32_t res;
-  int fd = SPIFFS_open(&__fs, name, SPIFFS_RDONLY, 0);
-  if (fd < 0) {
-    printf("  read_and_verify: could not open file %s\n", name);
-    return fd;
-  }
-  return read_and_verify_fd(fd, name);
-}
-
-int read_and_verify_fd(spiffs_file fd, char *name) {
-  s32_t res;
-  int pfd = open(make_test_fname(name), O_RDONLY);
-  spiffs_stat s;
-  res = SPIFFS_fstat(&__fs, fd, &s);
-  if (res < 0) {
-    printf("  read_and_verify: could not stat file %s\n", name);
-    return res;
-  }
-  if (s.size == 0) {
-    SPIFFS_close(&__fs, fd);
-    close(pfd);
-    return 0;
-  }
-
-  //printf("verifying %s, len %i\n", name, s.size);
-  int offs = 0;
-  u8_t buf_d[256];
-  u8_t buf_v[256];
-  while (offs < s.size) {
-    int read_len = MIN(s.size - offs, sizeof(buf_d));
-    res = SPIFFS_read(&__fs, fd, buf_d, read_len);
-    if (res < 0) {
-      printf("  read_and_verify: could not read file %s offs:%i len:%i filelen:%i\n", name, offs, read_len, s.size);
-      return res;
-    }
-    int pres = read(pfd, buf_v, read_len);
-    (void)pres;
-    //printf("reading offs:%i len:%i spiffs_res:%i posix_res:%i\n", offs, read_len, res, pres);
-    int i;
-    int veri_ok = 1;
-    for (i = 0; veri_ok && i < read_len; i++) {
-      if (buf_d[i] != buf_v[i]) {
-        printf("file verification mismatch @ %i, %02x %c != %02x %c\n", offs+i, buf_d[i], buf_d[i], buf_v[i], buf_v[i]);
-        int j = MAX(0, i-16);
-        int k = MIN(sizeof(buf_d), i+16);
-        k = MIN(s.size-offs, k);
-        int l;
-        for (l = j; l < k; l++) {
-          printf("%c", buf_d[l] > 31 ? buf_d[l] : '.');
-        }
-        printf("\n");
-        for (l = j; l < k; l++) {
-          printf("%c", buf_v[l] > 31 ? buf_v[l] : '.');
-        }
-        printf("\n");
-        veri_ok = 0;
-      }
-    }
-    if (!veri_ok) {
-      SPIFFS_close(&__fs, fd);
-      close(pfd);
-      printf("data mismatch\n");
-      return -1;
-    }
-
-    offs += read_len;
-  }
-
-  SPIFFS_close(&__fs, fd);
-  close(pfd);
-
-  return 0;
-}
-
-static void test_on_stop(test *t) {
-  printf("  spiffs errno:%i\n", SPIFFS_errno(&__fs));
-#if SPIFFS_TEST_VISUALISATION
-  SPIFFS_vis(FS);
-#endif
-
-}
-
-void memrand(u8_t *b, int len) {
-  int i;
-  for (i = 0; i < len; i++) {
-    b[i] = rand();
-  }
-}
-
-int test_create_file(char *name) {
-  spiffs_stat s;
-  spiffs_file fd;
-  int res = SPIFFS_creat(FS, name, 0);
-  CHECK_RES(res);
-  fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0);
-  CHECK(fd >= 0);
-  res = SPIFFS_fstat(FS, fd, &s);
-  CHECK_RES(res);
-  CHECK(strcmp((char*)s.name, name) == 0);
-  CHECK(s.size == 0);
-  SPIFFS_close(FS, fd);
-  return 0;
-}
-
-int test_create_and_write_file(char *name, int size, int chunk_size) {
-  int res;
-  spiffs_file fd;
-  printf("    create and write %s", name);
-  res = test_create_file(name);
-  if (res < 0) {
-    printf(" failed creation, %i\n",res);
-  }
-  CHECK(res >= 0);
-  fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0);
-  if (res < 0) {
-    printf(" failed open, %i\n",res);
-  }
-  CHECK(fd >= 0);
-  int pfd = open(make_test_fname(name), O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
-  int offset = 0;
-  int mark = 0;
-  while (offset < size) {
-    int len = MIN(size-offset, chunk_size);
-    if (offset > mark) {
-      mark += size/16;
-      printf(".");
-      fflush(stdout);
-    }
-    u8_t *buf = malloc(len);
-    memrand(buf, len);
-    res = SPIFFS_write(FS, fd, buf, len);
-    write(pfd, buf, len);
-    free(buf);
-    if (res < 0) {
-      printf("\n  error @ offset %i, res %i\n", offset, res);
-    }
-    offset += len;
-    CHECK(res >= 0);
-  }
-  printf("\n");
-  close(pfd);
-
-  spiffs_stat stat;
-  res = SPIFFS_fstat(FS, fd, &stat);
-  if (res < 0) {
-    printf(" failed fstat, %i\n",res);
-  }
-  CHECK(res >= 0);
-  if (stat.size != size) {
-    printf(" failed size, %i != %i\n", stat.size, size);
-  }
-  CHECK(stat.size == size);
-
-  SPIFFS_close(FS, fd);
-  return 0;
-}
-
-#if SPIFFS_CACHE
-#if SPIFFS_CACHE_STATS
-static u32_t chits_tot = 0;
-static u32_t cmiss_tot = 0;
-#endif
-#endif
-
-void _setup_test_only() {
-  fs_set_validate_flashing(1);
-  test_init(test_on_stop);
-}
-
-void _setup() {
-  fs_reset();
-  _setup_test_only();
-}
-
-void _teardown() {
-  printf("  free blocks     : %i of %i\n", (FS)->free_blocks, (FS)->block_count);
-  printf("  pages allocated : %i\n", (FS)->stats_p_allocated);
-  printf("  pages deleted   : %i\n", (FS)->stats_p_deleted);
-#if SPIFFS_GC_STATS
-  printf("  gc runs         : %i\n", (FS)->stats_gc_runs);
-#endif
-#if SPIFFS_CACHE
-#if SPIFFS_CACHE_STATS
-  chits_tot += (FS)->cache_hits;
-  cmiss_tot += (FS)->cache_misses;
-  printf("  cache hits      : %i (sum %i)\n", (FS)->cache_hits, chits_tot);
-  printf("  cache misses    : %i (sum %i)\n", (FS)->cache_misses, cmiss_tot);
-  printf("  cache utiliz    : %f\n", ((float)chits_tot/(float)(chits_tot + cmiss_tot)));
-#endif
-#endif
-  dump_flash_access_stats();
-  clear_flash_ops_log();
-#if SPIFFS_GC_STATS
-  if ((FS)->stats_gc_runs > 0)
-#endif
-  dump_erase_counts(FS);
-  printf("  fs consistency check:\n");
-  SPIFFS_check(FS);
-  clear_test_path();
-
-  //hexdump_mem(&area[SPIFFS_PHYS_ADDR - 16], 32);
-  //hexdump_mem(&area[SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE - 16], 32);
-}
-
-u32_t tfile_get_size(tfile_size s) {
-  switch (s) {
-  case EMPTY:
-    return 0;
-  case SMALL:
-    return SPIFFS_DATA_PAGE_SIZE(FS)/2;
-  case MEDIUM:
-    return SPIFFS_DATA_PAGE_SIZE(FS) * (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS));
-  case LARGE:
-    return (FS)->cfg.phys_size/3;
-  }
-  return 0;
-}
-
-int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files, int dbg) {
-  int res;
-  tfile *tfiles = malloc(sizeof(tfile) * max_concurrent_files);
-  memset(tfiles, 0, sizeof(tfile) * max_concurrent_files);
-  int run = 0;
-  int cur_config_ix = 0;
-  char name[32];
-  while (run < max_runs)  {
-    if (dbg) printf(" run %i/%i\n", run, max_runs);
-    int i;
-    for (i = 0; i < max_concurrent_files; i++) {
-      sprintf(name, "file%i_%i", (1+run), i);
-      tfile *tf = &tfiles[i];
-      if (tf->state == 0 && cur_config_ix < cfg_count) {
-// create a new file
-        strcpy(tf->name, name);
-        tf->state = 1;
-        tf->cfg = cfgs[cur_config_ix];
-        int size = tfile_get_size(tf->cfg.tsize);
-        if (dbg) printf("   create new %s with cfg %i/%i, size %i\n", name, (1+cur_config_ix), cfg_count, size);
-
-        if (tf->cfg.tsize == EMPTY) {
-          res = SPIFFS_creat(FS, name, 0);
-          CHECK_RES(res);
-          int pfd = open(make_test_fname(name), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
-          close(pfd);
-          int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0;
-          spiffs_file fd = SPIFFS_open(FS, name, extra_flags | SPIFFS_RDWR, 0);
-          CHECK(fd > 0);
-          tf->fd = fd;
-        } else {
-          int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0;
-          spiffs_file fd = SPIFFS_open(FS, name, extra_flags | SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
-          CHECK(fd > 0);
-          extra_flags = tf->cfg.ttype == APPENDED ? O_APPEND : 0;
-          int pfd = open(make_test_fname(name), extra_flags | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
-          tf->fd = fd;
-          u8_t *buf = malloc(size);
-          memrand(buf, size);
-          res = SPIFFS_write(FS, fd, buf, size);
-          CHECK_RES(res);
-          write(pfd, buf, size);
-          close(pfd);
-          free(buf);
-          res = read_and_verify(name);
-          CHECK_RES(res);
-        }
-
-        cur_config_ix++;
-      } else if (tf->state > 0) {
-// hande file lifecycle
-        switch (tf->cfg.ttype) {
-        case UNTAMPERED: {
-          break;
-        }
-        case APPENDED: {
-          if (dbg) printf("   appending %s\n", tf->name);
-          int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
-          u8_t *buf = malloc(size);
-          memrand(buf, size);
-          res = SPIFFS_write(FS, tf->fd, buf, size);
-          CHECK_RES(res);
-          int pfd = open(make_test_fname(tf->name), O_APPEND | O_RDWR);
-          write(pfd, buf, size);
-          close(pfd);
-          free(buf);
-          res = read_and_verify(tf->name);
-          CHECK_RES(res);
-          break;
-        }
-        case MODIFIED: {
-          if (dbg) printf("   modify %s\n", tf->name);
-          spiffs_stat stat;
-          res = SPIFFS_fstat(FS, tf->fd, &stat);
-          CHECK_RES(res);
-          int size = stat.size / tf->cfg.tlife + SPIFFS_DATA_PAGE_SIZE(FS)/3;
-          int offs = (stat.size / tf->cfg.tlife) * tf->state;
-          res = SPIFFS_lseek(FS, tf->fd, offs, SPIFFS_SEEK_SET);
-          CHECK_RES(res);
-          u8_t *buf = malloc(size);
-          memrand(buf, size);
-          res = SPIFFS_write(FS, tf->fd, buf, size);
-          CHECK_RES(res);
-          int pfd = open(make_test_fname(tf->name), O_RDWR);
-          lseek(pfd, offs, SEEK_SET);
-          write(pfd, buf, size);
-          close(pfd);
-          free(buf);
-          res = read_and_verify(tf->name);
-          CHECK_RES(res);
-          break;
-        }
-        case REWRITTEN: {
-          if (tf->fd > 0) {
-            SPIFFS_close(FS, tf->fd);
-          }
-          if (dbg) printf("   rewriting %s\n", tf->name);
-          spiffs_file fd = SPIFFS_open(FS, tf->name, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
-          CHECK(fd > 0);
-          int pfd = open(make_test_fname(tf->name), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
-          tf->fd = fd;
-          int size = tfile_get_size(tf->cfg.tsize);
-          u8_t *buf = malloc(size);
-          memrand(buf, size);
-          res = SPIFFS_write(FS, fd, buf, size);
-          CHECK_RES(res);
-          write(pfd, buf, size);
-          close(pfd);
-          free(buf);
-          res = read_and_verify(tf->name);
-          CHECK_RES(res);
-          break;
-        }
-        }
-        tf->state++;
-        if (tf->state > tf->cfg.tlife) {
-// file outlived its time, kill it
-          if (tf->fd > 0) {
-            SPIFFS_close(FS, tf->fd);
-          }
-          if (dbg) printf("   removing %s\n", tf->name);
-          res = read_and_verify(tf->name);
-          CHECK_RES(res);
-          res = SPIFFS_remove(FS, tf->name);
-          CHECK_RES(res);
-          remove(make_test_fname(tf->name));
-          memset(tf, 0, sizeof(tf));
-        }
-
-      }
-    }
-
-    run++;
-  }
-  free(tfiles);
-  return 0;
-}
-
-
-

+ 0 - 90
app/spiffs/test/test_spiffs.h

@@ -1,90 +0,0 @@
-/*
- * test_spiffs.h
- *
- *  Created on: Jun 19, 2013
- *      Author: petera
- */
-
-#ifndef TEST_SPIFFS_H_
-#define TEST_SPIFFS_H_
-
-#include "spiffs.h"
-
-#define FS &__fs
-
-extern spiffs __fs;
-
-
-#define CHECK(r) if (!(r)) return -1;
-#define CHECK_RES(r) if (r < 0) return -1;
-#define FS_PURE_DATA_PAGES(fs) \
-    ((fs)->cfg.phys_size / (fs)->cfg.log_page_size - (fs)->block_count * SPIFFS_OBJ_LOOKUP_PAGES(fs))
-#define FS_PURE_DATA_SIZE(fs) \
-    FS_PURE_DATA_PAGES(fs) * SPIFFS_DATA_PAGE_SIZE(fs)
-
-typedef enum {
-  EMPTY,
-  SMALL,
-  MEDIUM,
-  LARGE,
-} tfile_size;
-
-typedef enum {
-  UNTAMPERED,
-  APPENDED,
-  MODIFIED,
-  REWRITTEN,
-} tfile_type;
-
-typedef enum {
-  SHORT = 4,
-  NORMAL = 20,
-  LONG = 100,
-} tfile_life;
-
-typedef struct  {
-  tfile_size tsize;
-  tfile_type ttype;
-  tfile_life tlife;
-} tfile_conf;
-
-typedef struct  {
-  int state;
-  spiffs_file fd;
-  tfile_conf cfg;
-  char name[32];
-} tfile;
-
-
-void fs_reset();
-void fs_reset_specific(u32_t phys_addr, u32_t phys_size,
-    u32_t phys_sector_size,
-    u32_t log_block_size, u32_t log_page_size);
-int read_and_verify(char *name);
-int read_and_verify_fd(spiffs_file fd, char *name);
-void dump_page(spiffs *fs, spiffs_page_ix p);
-void hexdump(u32_t addr, u32_t len);
-char *make_test_fname(const char *name);
-void clear_test_path();
-void area_write(u32_t addr, u8_t *buf, u32_t size);
-void area_read(u32_t addr, u8_t *buf, u32_t size);
-void dump_erase_counts(spiffs *fs);
-void dump_flash_access_stats();
-void set_flash_ops_log(int enable);
-void clear_flash_ops_log();
-u32_t get_flash_ops_log_read_bytes();
-u32_t get_flash_ops_log_write_bytes();
-void invoke_error_after_read_bytes(u32_t b, char once_only);
-void invoke_error_after_write_bytes(u32_t b, char once_only);
-
-void memrand(u8_t *b, int len);
-int test_create_file(char *name);
-int test_create_and_write_file(char *name, int size, int chunk_size);
-void _setup();
-void _setup_test_only();
-void _teardown();
-u32_t tfile_get_size(tfile_size s);
-int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files, int dbg);
-
-
-#endif /* TEST_SPIFFS_H_ */

+ 0 - 175
app/spiffs/test/testrunner.c

@@ -1,175 +0,0 @@
-/*
- * testrunner.c
- *
- *  Created on: Jun 18, 2013
- *      Author: petera
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <unistd.h>
-
-#include "testrunner.h"
-
-static struct {
-  test *tests;
-  test *_last_test;
-  int test_count;
-  void (*on_stop)(test *t);
-  test_res *failed;
-  test_res *failed_last;
-  test_res *stopped;
-  test_res *stopped_last;
-  FILE *spec;
-} test_main;
-
-void test_init(void (*on_stop)(test *t)) {
-  test_main.on_stop = on_stop;
-}
-
-static char check_spec(char *name) {
-  if (test_main.spec) {
-    fseek(test_main.spec, 0, SEEK_SET);
-    char *line = NULL;
-    size_t sz;
-    ssize_t read;
-    while ((read = getline(&line, &sz, test_main.spec)) != -1) {
-      if (strncmp(line, name, strlen(name)) == 0) {
-        free(line);
-        return 1;
-      }
-    }
-    free(line);
-    return 0;
-  } else {
-    return 1;
-  }
-}
-
-void add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t)) {
-  if (f == 0) return;
-  if (!check_spec(name)) return;
-  DBGT("adding test %s\n", name);
-  test *t = malloc(sizeof(test));
-  memset(t, 0, sizeof(test));
-  t->f = f;
-  strcpy(t->name, name);
-  t->setup = setup;
-  t->teardown = teardown;
-  if (test_main.tests == 0) {
-    test_main.tests = t;
-  } else {
-    test_main._last_test->_next = t;
-  }
-  test_main._last_test = t;
-  test_main.test_count++;
-}
-
-static void add_res(test *t, test_res **head, test_res **last) {
-  test_res *tr = malloc(sizeof(test_res));
-  memset(tr,0,sizeof(test_res));
-  strcpy(tr->name, t->name);
-  if (*head == 0) {
-    *head = tr;
-  } else {
-    (*last)->_next = tr;
-  }
-  *last = tr;
-}
-
-static void dump_res(test_res **head) {
-  test_res *tr = (*head);
-  while (tr) {
-    test_res *next_tr = tr->_next;
-    printf("  %s\n", tr->name);
-    free(tr);
-    tr = next_tr;
-  }
-}
-
-void run_tests(int argc, char **args) {
-  memset(&test_main, 0, sizeof(test_main));
-  if (argc > 1) {
-    printf("running tests from %s\n", args[1]);
-    FILE *fd = fopen(args[1], "r");
-    if (fd == NULL) {
-      printf("%s not found\n", args[1]);
-      exit(EXIT_FAILURE);
-    }
-    test_main.spec = fd;
-  }
-
-  DBGT("adding suites...\n");
-  add_suites();
-  DBGT("%i tests added\n", test_main.test_count);
-  if (test_main.spec) {
-    fclose(test_main.spec);
-  }
-
-  if (test_main.test_count == 0) {
-    printf("No tests to run\n");
-    return;
-  }
-
-  int fd_success = open("_tests_ok", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
-  int fd_bad = open("_tests_fail", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
-
-  DBGT("running tests...\n");
-  int ok = 0;
-  int failed = 0;
-  int stopped = 0;
-  test *cur_t = test_main.tests;
-  int i = 1;
-  while (cur_t) {
-    cur_t->setup(cur_t);
-    test *next_test = cur_t->_next;
-    DBGT("TEST %i/%i : running test %s\n", i, test_main.test_count, cur_t->name);
-    i++;
-    int res = cur_t->f(cur_t);
-    cur_t->teardown(cur_t);
-    int fd = res == TEST_RES_OK ? fd_success : fd_bad;
-    write(fd, cur_t->name, strlen(cur_t->name));
-    write(fd, "\n", 1);
-    switch (res) {
-    case TEST_RES_OK:
-      ok++;
-      printf("  .. ok\n");
-      break;
-    case TEST_RES_FAIL:
-      failed++;
-      printf("  .. FAILED\n");
-      if (test_main.on_stop) test_main.on_stop(cur_t);
-      add_res(cur_t, &test_main.failed, &test_main.failed_last);
-      break;
-    case TEST_RES_ASSERT:
-      stopped++;
-      printf("  .. ABORTED\n");
-      if (test_main.on_stop) test_main.on_stop(cur_t);
-      add_res(cur_t, &test_main.stopped, &test_main.stopped_last);
-      break;
-    }
-    free(cur_t);
-    cur_t = next_test;
-  }
-  close(fd_success);
-  close(fd_bad);
-  DBGT("ran %i tests\n", test_main.test_count);
-  printf("Test report, %i tests\n", test_main.test_count);
-  printf("%i succeeded\n", ok);
-  printf("%i failed\n", failed);
-  dump_res(&test_main.failed);
-  printf("%i stopped\n", stopped);
-  dump_res(&test_main.stopped);
-  if (ok < test_main.test_count) {
-    printf("\nFAILED\n");
-  } else {
-    printf("\nALL TESTS OK\n");
-  }
-}

+ 0 - 110
app/spiffs/test/testrunner.h

@@ -1,110 +0,0 @@
-/*
- * testrunner.h
- *
- *  Created on: Jun 19, 2013
- *      Author: petera
- */
-
-/*
-
-SUITE(mysuite)
-
-void setup(test *t) {}
-
-void teardown(test *t) {}
-
-TEST(mytest) {
-  printf("mytest runs now..\n");
-  return 0;
-} TEST_END(mytest)
-
-SUITE_END(mysuite)
-
-
-
-SUITE(mysuite2)
-
-void setup(test *t) {}
-
-void teardown(test *t) {}
-
-TEST(mytest2a) {
-  printf("mytest2a runs now..\n");
-  return 0;
-} TEST_END(mytest2a)
-
-TEST(mytest2b) {
-  printf("mytest2b runs now..\n");
-  return 0;
-} TEST_END(mytest2b)
-
-SUITE_END(mysuite2)
-
-
-
-void add_suites() {
-  ADD_SUITE(mysuite);
-  ADD_SUITE(mysuite2);
-}
- */
-
-#ifndef TESTS_H_
-#define TESTS_H_
-
-#define TEST_RES_OK   0
-#define TEST_RES_FAIL -1
-#define TEST_RES_ASSERT -2
-
-struct test_s;
-
-typedef int (*test_f)(struct test_s *t);
-
-typedef struct test_s {
-  test_f f;
-  char name[256];
-  void *data;
-  void (*setup)(struct test_s *t);
-  void (*teardown)(struct test_s *t);
-  struct test_s *_next;
-} test;
-
-typedef struct test_res_s {
-  char name[256];
-  struct test_res_s *_next;
-} test_res;
-
-#define TEST_CHECK(x) if (!(x)) { \
-  printf("  TEST FAIL %s:%i\n", __FILE__, __LINE__); \
-  goto __fail_stop; \
-}
-#define TEST_ASSERT(x) if (!(x)) { \
-  printf("  TEST ASSERT %s:%i\n", __FILE__, __LINE__); \
-  goto __fail_assert; \
-}
-
-#define DBGT(...) printf(__VA_ARGS__)
-
-#define str(s) #s
-
-#define SUITE(sui) \
-  extern void __suite_##sui() {
-#define SUITE_END(sui) \
-  }
-#define ADD_SUITE(sui) \
-  __suite_##sui();
-#define TEST(tf) \
-  int tf(struct test_s *t) { do
-#define TEST_END(tf) \
-  while(0); \
-  __fail_stop: return TEST_RES_FAIL; \
-  __fail_assert: return TEST_RES_ASSERT; \
-  } \
-  add_test(tf, str(tf), setup, teardown);
-
-
-void add_suites();
-void test_init(void (*on_stop)(test *t));
-void add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t));
-void run_tests(int argc, char **args);
-
-#endif /* TESTS_H_ */

+ 0 - 15
app/spiffs/test/testsuites.c

@@ -1,15 +0,0 @@
-/*
- * testsuites.c
- *
- *  Created on: Jun 19, 2013
- *      Author: petera
- */
-
-#include "testrunner.h"
-
-void add_suites() {
-  //ADD_SUITE(dev_tests);
-  ADD_SUITE(check_tests);
-  ADD_SUITE(hydrogen_tests)
-  ADD_SUITE(bug_tests)
-}

+ 9 - 11
app/user/user_main.c

@@ -90,16 +90,6 @@ void nodemcu_init(void)
         // Fit hardware real flash size.
         flash_rom_set_size_byte(flash_safe_get_size_byte());
 
-        if( !fs_format() )
-        {
-            NODE_ERR( "\ni*** ERROR ***: unable to format. FS might be compromised.\n" );
-            NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
-        }
-        else{
-            NODE_ERR( "format done.\n" );
-        }
-        fs_unmount();   // mounted by format.
-
         // Reboot to get SDK to use (or write) init data at new location
         system_restart ();
 
@@ -109,7 +99,15 @@ void nodemcu_init(void)
 #endif // defined(FLASH_SAFE_API)
 
 #if defined ( BUILD_SPIFFS )
-    fs_mount();
+    if (!fs_mount()) {
+        // Failed to mount -- try reformat
+	c_printf("Formatting file system.\n");
+        if (!fs_format()) {
+            NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" );
+            NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
+        }
+        // Note that fs_format leaves the file system mounted
+    }
     // test_spiffs();
 #endif
     // endpoint_setup();

+ 119 - 0
docs/en/spiffs.md

@@ -0,0 +1,119 @@
+# SPIFFS File System
+
+The NodeMCU project uses the [SPIFFS](https://github.com/pellepl/spiffs) 
+filesystem to store files in the flash chip. The technical details about how this is configured can be found below, along with various build time options.
+
+# spiffsimg - Manipulate SPI Flash File System disk images
+
+Ever wished you could prepare a SPIFFS image offline and flash the whole
+thing onto your microprocessor's storage instead of painstakingly upload
+file-by-file through your app on the micro? With spiffsimg you can!
+
+NodeMCU uses a SPIFFS filesystem that knows how big it is -- i.e. when you build a file system
+image, it must fit into the flash chip, and it cannot be expanded once flashed.
+It is important to give the `spiffimg` tool the correct size. You can provide either the `-c` option or both the `-U` and `-S` options.
+
+### Syntax 
+
+```
+spiffsimg -f <filename> 
+	[-o <offsetfile>]
+	[-c <size>] 
+	[-S <flashsize>]
+	[-U <usedsize>]
+	[-d]
+	[-l | -i | -r <scriptname> ]
+```
+
+### Supported operations:
+
+  * `-f` specifies the filename for the disk image. '%x' will be replaced by the calculated offset of the file system.
+  * `-o` specifies the file which is to contain the calculated offset.
+  * `-S` specifies the size of the flash chip. `32m` is 32 mbits, `1MB` is 1 megabyte.
+  * `-U` specifies the amount of flash used by the firmware. Decimal or Hex bytes.
+  * `-c` Create a blank disk image of the given size.
+  * `-l` List the contents of the given disk image.
+  * `-i` Interactive commands.
+  * `-r` Scripted commands from filename.
+  * `-d` causes the disk image to be deleted on error. This makes it easier to script.
+
+### Available commands:
+
+  * `ls` List contents. Output format is {type} {size} {name}.
+  * `cat <filename>` Dump file contents to stdout.
+  * `rm <filename>` Delete file.
+  * `info` Display SPIFFS usage estimates.
+  * `import <srcfile> <spiffsname>` Import a file into the disk image.
+  * `export <spiffsname> <dstfile>` Export a file from the disk image.
+
+### Example:
+```lua
+# spiffsimg -f flash.img -S 32m -U 524288 -i
+> import myapp/lua/init.lua init.lua
+> import myapp/lua/httpd.lua httpd.lua
+> import myapp/html/index.html http/index.html
+> import myapp/html/favicon.ico http/favicon.ico
+> ls
+f    122 init.lua
+f   5169 httpd.lua
+f   2121 http/index.html
+f    880 http/favicon.ico
+>^D
+#
+```
+
+### Known limitations:
+
+  * The block & page sizes are hard-coded to be compatible with nodemcu.
+  * Error handling is not entirely consistent, some errors result in an
+    early exit, others just print an error (both cause a non-zero exit though).
+  * Only flat SPIFFS is supported.
+
+
+# Technical Details
+
+The SPIFFS configuration is 4k sectors (the only size supported by the SDK) and 8k blocks. 256 byte pages. Magic is enabled and magic_len is also enabled. This allows the firmware to find the start of the filesystem (and also the size).
+One of the goals is to make the filsystem more persistent across reflashing of the firmware.
+
+There are two significant sizes of flash -- the 512K and 4M (or bigger). 
+
+The file system has to start on a 4k boundary, but since it ends on a much bigger boundary (a 16k boundary), it also starts on an 8k boundary. For the small flash chip, there is 
+not much spare space, so a newly formatted file system will start as low as possible (to get as much space as possible). For the large flash, the 
+file system will start on a 64k boundary. A newly formatted file system will start between 64k and 128k from the end of the firmware. This means that the file 
+system will survive lots of reflashing and at least 64k of firmware growth. 
+
+The spiffsimg tool can also be built (from source) in the nodemcu-firmware build tree. If there is any data in the `local/fs` directory tree, then it will
+be copied into the flash disk image. Two images will normally be created -- one for the 512k flash part and the other for the 4M flash part. If the data doesn't 
+fit into the 512k part after the firmware is included, then the file will not be generated.
+
+The disk image file is placed into the `bin` directory and it is named `0x<offset>-<size>.bin` where the offset is the location where it should be 
+flashed, and the size is the size of the flash part. It is quite valid (and quicker) to flash the 512k image into a 4M part. However, there will probably be
+limited space in the file system for creating new files.
+
+If no file system is found during platform boot, then a new file system will be formatted. This can take some time on the first boot.
+
+Note that the last 16k of the flash chip is reserved for the SDK to store parameters (such as the client wifi settings).
+
+In order to speed up the boot time, it is possible to define (at build time) the size of the SPIFFS Filesystem to be formatted. This can be as small as 32768 bytes which gives a filesystem with about 15k bytes of usable space.
+Just place the following define in `user_config.h` or some other file that is included during the build.
+
+```
+#define SPIFFS_MAX_FILESYSTEM_SIZE	32768
+```
+
+This filesystem size limit only affects the formatting of a file system -- if the firm finds an existing valid filesystem (of any size) it will use that. However, if the 
+filesystem is reformatted from Lua (using file.format()) then the new file system will obey the size limit. 
+
+There is also an option to control the positioning of the SPIFFS file system:
+
+```
+#define SPIFFS_FIXED_LOCATION   	0x100000
+```
+
+This specifies that the SPIFFS filesystem starts at 1Mb from the start of the flash. Unless otherwise specified, it will run to the end of the flash (excluding the 16k of space reserved by the SDK). 
+
+There is an option that limits the size of the file system to run up to the next 1MB boundary (minus the 16k for the parameter space). This may be useful when dealing with OTA upgrades.
+
+```
+#define SPIFFS_SIZE_1M_BOUNDARY
+```

+ 4 - 0
local/fs/.gitignore

@@ -0,0 +1,4 @@
+# Ignore everything
+*
+# But not this file itself.
+!.gitignore

+ 1 - 0
mkdocs.yml

@@ -26,6 +26,7 @@ pages:
     - Home: 'en/index.md'
     - Building the firmware: 'en/build.md'
     - Flashing the firmware: 'en/flash.md'
+    - Filesystem notes: 'en/spiffs.md'
     - Uploading code: 'en/upload.md'
     - FAQs:
         - Lua Developer FAQ: 'en/lua-developer-faq.md'

+ 47 - 0
tools/Makefile

@@ -0,0 +1,47 @@
+#############################################################
+# Options
+#
+
+FSSOURCE ?= ../local/fs/
+FLASHSIZE ?= 4mb 32mb 8mb
+SUBDIRS = 
+
+OBJDUMP = $(or $(shell which objdump),xtensa-lx106-elf-objdump)
+
+#############################################################
+# Get the files to pack into the spiffs image
+#
+
+SPIFFSFILES ?= $(patsubst $(FSSOURCE)%,%,$(shell find $(FSSOURCE) -name '*' '!' -name .gitignore ))
+
+#################################################################
+# Get the filesize of /bin/0x10000.bin
+#
+
+FLASH_USED_END = $$((0x`$(OBJDUMP) -t ../app/.output/eagle/debug/image/eagle.app.v6.out |grep _flash_used_end |cut -f1 -d" "` - 0x40200000))
+
+
+#############################################################
+# Rules base
+#
+#
+
+all:	spiffsscript
+
+spiffsscript: remove-image
+	$(MAKE) -C spiffsimg CC=gcc
+	rm -f ./spiffsimg/spiffs.lst
+	echo "" >> ./spiffsimg/spiffs.lst
+	@$(foreach f, $(SPIFFSFILES), echo "import $(FSSOURCE)$(f) $(f)" >> ./spiffsimg/spiffs.lst ;)
+	@$(foreach sz, $(FLASHSIZE), spiffsimg/spiffsimg -U $(FLASH_USED_END) -o ../bin/spiffs-$(sz).dat -f ../bin/0x%x-$(sz).bin -S $(sz) -r ./spiffsimg/spiffs.lst -d; )
+	@$(foreach sz, $(FLASHSIZE), if [ -r ../bin/spiffs-$(sz).dat ]; then echo Built $$(cat ../bin/spiffs-$(sz).dat)-$(sz).bin; fi; )
+	
+remove-image:
+	$(foreach sz, $(FLASHSIZE), if [ -r ../bin/spiffs-$(sz).dat ]; then rm -f ../bin/$$(cat ../bin/spiffs-$(sz).dat)-$(sz).bin; fi; )
+	rm -f ../bin/spiffs*.dat
+
+
+spiffsclean: remove-image
+	rm -f ./spiffsimg/spiffsimg
+	rm -f ./spiffsimg/spiffs.lst
+

+ 2 - 0
tools/spiffsimg/.gitignore

@@ -0,0 +1,2 @@
+spiffs.lst
+spiffsimg

+ 11 - 0
tools/spiffsimg/Makefile

@@ -0,0 +1,11 @@
+SRCS=\
+	main.c \
+  ../../app/spiffs/spiffs_cache.c  ../../app/spiffs/spiffs_check.c  ../../app/spiffs/spiffs_gc.c  ../../app/spiffs/spiffs_hydrogen.c  ../../app/spiffs/spiffs_nucleus.c
+
+CFLAGS=-g -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -I. -I../../app/spiffs -DNODEMCU_SPIFFS_NO_INCLUDE --include spiffs_typedefs.h
+
+spiffsimg: $(SRCS)
+	$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
+
+clean:
+	rm -f spiffsimg

+ 7 - 0
tools/spiffsimg/README.md

@@ -0,0 +1,7 @@
+# spiffsimg - Manipulate SPI Flash File System disk images
+
+Ever wished you could prepare a SPIFFS image offline and flash the whole
+thing onto your microprocessor's storage instead of painstakingly upload
+file-by-file through your app on the micro? With spiffsimg you can!
+
+For the full gory details see [spiffs.md](../../docs/en/spiffs.md)

+ 448 - 0
tools/spiffsimg/main.c

@@ -0,0 +1,448 @@
+/*
+ * Copyright 2015 Dius Computing Pty Ltd. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the
+ *   distribution.
+ * - Neither the name of the copyright holders nor the names of
+ *   its contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Johny Mattsson <jmattsson@dius.com.au>
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include "spiffs.h"
+
+static spiffs fs;
+static uint8_t *flash;
+
+static int retcode = 0;
+
+#define LOG_PAGE_SIZE 256
+
+// If the caclulated size is this or less, then use smaller blocks
+// (1 page) rather than the normal 2 pages. This gives a little bit
+// more flexibility for the file system, at the expense of a bit of
+// efficiency
+#define SMALL_FILESYSTEM	(128 * 1024)
+
+static int delete_on_die = 0;
+static const char *delete_list[10];
+static int delete_list_index = 0;
+
+static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
+static u8_t spiffs_fds[32*4];
+
+static s32_t flash_read (u32_t addr, u32_t size, u8_t *dst) {
+  memcpy (dst, flash + addr, size);
+  return SPIFFS_OK;
+}
+
+static s32_t flash_write (u32_t addr, u32_t size, u8_t *src) {
+  memcpy (flash + addr, src, size);
+  return SPIFFS_OK;
+}
+
+static s32_t flash_erase (u32_t addr, u32_t size) {
+  memset (flash + addr, 0xff, size);
+  return SPIFFS_OK;
+}
+
+
+static void die (const char *what)
+{
+  if (errno == 0) {
+    fprintf(stderr, "%s: fatal error\n", what);
+  } else {
+    perror (what);
+  }
+  if (delete_on_die) {
+    const char **p = delete_list;
+    while (*p) {
+      unlink(*p);
+      p++;
+    }
+  }
+  exit (1);
+}
+
+
+static void list (void)
+{
+  spiffs_DIR dir;
+  if (!SPIFFS_opendir (&fs, "/", &dir))
+    die ("spiffs_opendir");
+  struct spiffs_dirent de;
+  while (SPIFFS_readdir (&dir, &de))
+  {
+    static const char types[] = "?fdhs"; // file, dir, hardlink, softlink
+    char name[sizeof(de.name)+1] = { 0 };
+    memcpy (name, de.name, sizeof(de.name));
+    printf("%c %6u %s\n", types[de.type], de.size, name);
+  }
+  SPIFFS_closedir (&dir);
+}
+
+
+static void cat (char *fname)
+{
+  spiffs_file fh = SPIFFS_open (&fs, fname, SPIFFS_RDONLY, 0);
+  char buff[512];
+  s32_t n;
+  while ((n = SPIFFS_read (&fs, fh, buff, sizeof (buff))) > 0)
+    write (STDOUT_FILENO, buff, n);
+  SPIFFS_close (&fs, fh);
+}
+
+
+static void import (char *src, char *dst)
+{
+  int fd = open (src, O_RDONLY);
+  if (fd < 0)
+    die (src);
+
+  spiffs_file fh = SPIFFS_open (&fs, dst, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_WRONLY, 0);
+  if (fh < 0)
+    die ("spiffs_open");
+
+  char buff[16384];
+  s32_t n;
+  while ((n = read (fd, buff, sizeof (buff))) > 0)
+    if (SPIFFS_write (&fs, fh, buff, n) < 0)
+      die ("spiffs_write");
+
+  if (SPIFFS_close (&fs, fh) < 0) 
+    die("spiffs_close");
+  close (fd);
+}
+
+
+static void export (char *src, char *dst)
+{
+  spiffs_file fh = SPIFFS_open (&fs, src, SPIFFS_RDONLY, 0);
+  if (fh < 0)
+    die ("spiffs_open");
+
+  int fd = open (dst, O_CREAT | O_TRUNC | O_WRONLY, 0664);
+  if (fd < 0)
+    die (dst);
+
+  char buff[512];
+  s32_t n;
+  while ((n = SPIFFS_read (&fs, fh, buff, sizeof (buff))) > 0)
+    if (write (fd, buff, n) < 0)
+      die ("write");
+
+  SPIFFS_close (&fs, fh);
+  close (fd);
+}
+
+
+char *trim (char *in)
+{
+  if (!in)
+    return "";
+
+  char *out = 0;
+  while (*in)
+  {
+    if (!out && !isspace (*in))
+      out = in;
+    ++in;
+  }
+  if (!out)
+    return "";
+  while (--in > out && isspace (*in))
+    ;
+  in[1] = 0;
+  return out;
+}
+
+
+void syntax (void)
+{
+  fprintf (stderr,
+    "Syntax: spiffsimg -f <filename> [-d] [-o <locationfilename>] [-c size] [-S flashsize] [-U usedsize] [-l | -i | -r <scriptname> ]\n\n"
+  );
+  exit (1);
+}
+
+static size_t getsize(const char *s) 
+{
+  char *end = 0;
+  size_t val = strtoul(s, &end, 0);
+
+  if (end) {
+    int factor;
+    if (*end == 'k' || *end == 'K') {
+      factor = 1 << 10;
+      end++;
+    } else if (*end == 'm' || *end == 'M') {
+      factor = 1 << 20;
+      end++;
+    } else {
+      factor = 1;
+    }
+    // Capital B is bytes
+    if (*end == 'B') {
+      factor = factor << 3;
+    }
+    // we want bytes
+    val = (val * factor) / 8;
+  }
+
+  return val;
+}
+
+int main (int argc, char *argv[])
+{
+  if (argc == 1)
+    syntax ();
+
+  int opt;
+  const char *fname = 0;
+  bool create = false;
+  enum { CMD_NONE, CMD_LIST, CMD_INTERACTIVE, CMD_SCRIPT } command = CMD_NONE;
+  int sz = 0;
+  const char *script_name = 0;
+  const char *resolved = 0;
+  int flashsize = 0;
+  int used = 0;
+  while ((opt = getopt (argc, argv, "do:f:c:lir:S:U:")) != -1)
+  {
+    switch (opt)
+    {
+      case 'f': fname = optarg; break;
+      case 'o': resolved = optarg; break;
+      case 'c': create = true; sz = strtol(optarg, 0, 0); break;
+      case 'S': create = true; flashsize = getsize(optarg); break;
+      case 'U': create = true; used = strtol(optarg, 0, 0); break;
+      case 'd': delete_on_die = 1; break;
+      case 'l': command = CMD_LIST; break;
+      case 'i': command = CMD_INTERACTIVE; break;
+      case 'r': command = CMD_SCRIPT; script_name = optarg; break;
+      default: die ("unknown option");
+    }
+  }
+
+  if (!fname) {
+    die("Need a filename");
+  }
+
+  int fd;
+
+  if (create)
+  {
+    if (!sz) {
+      if (!flashsize || !used) {
+	die("Missing flashSize or Used");
+      }
+      sz = flashsize - used - 4 * 4096;   // This leaves space for the parameter area
+      if (sz < 0x4000) {
+	die("Not enough space");
+      }
+      if (sz > SMALL_FILESYSTEM) {
+	used = (used + 0xffff) & ~0xffff;
+      } else {
+	used = (used + 0x1fff) & ~0x1fff;
+      }
+      sz = flashsize - used - 4 * 4096;
+    }
+    sz &= ~(0x1fff);
+
+    char fnamebuff[1024];
+
+    sprintf(fnamebuff, fname, used);
+
+    delete_list[delete_list_index++] = strdup(fnamebuff);;
+    fd = open (fnamebuff, (create ? (O_CREAT | O_TRUNC) : 0) | O_RDWR, 0664);
+    if (fd == -1)
+      die ("open");
+
+    if (resolved) {
+      delete_list[delete_list_index++] = resolved;
+      FILE *f = fopen(resolved, "w");
+      fprintf(f, "0x%x", used);
+      fclose(f);
+    }
+
+    if (lseek (fd, sz -1, SEEK_SET) == -1)
+      die ("lseek");
+    if (write (fd, "", 1) != 1)
+      die ("write");
+  }
+  else {
+    fd = open (fname, O_RDWR, 0664);
+    if (fd == -1)
+      die ("open");
+
+    if (!sz)
+    {
+      off_t offs = lseek (fd, 0, SEEK_END);
+      if (offs == -1)
+	die ("lseek");
+      sz = offs;
+    }
+  }
+
+  if (sz & (0x1000 -1))
+    die ("file size not multiple of erase block size");
+
+  flash = mmap (0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+  if (!flash)
+    die ("mmap");
+
+  if (create)
+    memset (flash, 0xff, sz);
+
+  spiffs_config cfg;
+  cfg.phys_size = sz;
+  cfg.phys_addr = 0;
+  cfg.phys_erase_block = 0x1000;
+  cfg.log_block_size = 0x1000 * (sz > SMALL_FILESYSTEM ? 2 : 1);
+  cfg.log_page_size = LOG_PAGE_SIZE;
+  cfg.hal_read_f = flash_read;
+  cfg.hal_write_f = flash_write;
+  cfg.hal_erase_f = flash_erase;
+  if (cfg.phys_size < 4 * cfg.log_block_size) {
+    die("disk not large enough for four blocks");
+  }
+
+  if (SPIFFS_mount (&fs, &cfg,
+      spiffs_work_buf,
+      spiffs_fds,
+      sizeof(spiffs_fds),
+      malloc(65536), 65536, 0) != 0) {
+    if (create) {
+      if (SPIFFS_format(&fs) != 0) {
+        die("spiffs_format");
+      }
+      if (SPIFFS_mount (&fs, &cfg,
+          spiffs_work_buf,
+          spiffs_fds,
+          sizeof(spiffs_fds),
+          malloc(65536), 65536, 0) != 0) {
+        die ("spiffs_mount");
+      }
+      if (command == CMD_INTERACTIVE) {
+	printf("Created filesystem -- size 0x%x, block_size=%d\n", cfg.phys_size, cfg.log_block_size);
+      }
+    } else {
+      die ("spiffs_mount");
+    }
+  }
+
+  if (command == CMD_NONE)
+    ; // maybe just wanted to create an empty image?
+  else if (command == CMD_LIST)
+    list ();
+  else
+  {
+    FILE *in = (command == CMD_INTERACTIVE) ? stdin : fopen (script_name, "r");
+    if (!in)
+      die ("fopen");
+    char buff[128] = { 0 };
+    if (in == stdin)
+      printf("> ");
+    while (fgets (buff, sizeof (buff) -1, in))
+    {
+      char *line = trim (buff);
+      if (!line[0] || line[0] == '#')
+        continue;
+      if (strcmp (line, "ls") == 0)
+        list ();
+      else if (strncmp (line, "import ", 7) == 0)
+      {
+        char *src = 0, *dst = 0;
+        if (sscanf (line +7, " %ms %ms", &src, &dst) != 2)
+        {
+          fprintf (stderr, "SYNTAX ERROR: %s\n", line);
+          retcode = 1;
+        }
+        else
+          import (src, dst);
+        free (src);
+        free (dst);
+      }
+      else if (strncmp (line, "export ", 7) == 0)
+      {
+        char *src = 0, *dst = 0;
+        if (sscanf (line + 7, " %ms %ms", &src, &dst) != 2)
+        {
+          fprintf (stderr, "SYNTAX ERROR: %s\n", line);
+          retcode = 1;
+        }
+        else
+          export (src, dst);
+        free (src);
+        free (dst);
+      }
+      else if (strncmp (line, "rm ", 3) == 0)
+      {
+        if (SPIFFS_remove (&fs, trim (line + 3)) < 0)
+        {
+          fprintf (stderr, "FAILED: %s\n", line);
+          retcode = 1;
+        }
+      }
+      else if (strncmp (line, "cat ", 4) == 0)
+        cat (trim (line + 4));
+      else if (strncmp (line, "info", 4) == 0)
+      {
+        u32_t total, used;
+        if (SPIFFS_info (&fs, &total, &used) < 0)
+        {
+          fprintf (stderr, "FAILED: %s\n", line);
+          retcode = 1;
+        }
+        else
+          printf ("Total: %u, Used: %u\n", total, used);
+      }
+      else
+      {
+        printf ("SYNTAX ERROR: %s\n", line);
+        retcode = 1;
+      }
+
+      if (in == stdin)
+        printf ("> ");
+    }
+    if (in == stdin)
+      printf ("\n");
+  }
+
+  SPIFFS_unmount (&fs);
+  munmap (flash, sz);
+  close (fd);
+  return retcode;
+}

+ 14 - 0
tools/spiffsimg/spiffs_typedefs.h

@@ -0,0 +1,14 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+typedef  int32_t s32_t;
+typedef uint32_t u32_t;
+typedef  int16_t s16_t;
+typedef uint16_t u16_t;
+typedef   int8_t s8_t;
+typedef  uint8_t u8_t;
+typedef long long ptrdiff_t;
+
+#define offsetof(type, member)  __builtin_offsetof (type, member)

+ 15 - 0
tools/update_spiffs.sh

@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# This script updates the SPIFFS code from the master repository
+
+if [ ! -e ../tools/update_spiffs.sh ]; then
+  echo Must run from the tools directory
+  exit 1
+fi
+
+git clone https://github.com/pellepl/spiffs
+
+cp spiffs/src/*.[ch] ../app/spiffs
+cp spiffs/LICENSE ../app/spiffs
+
+rm -fr spiffs