From 9b2d10bdce6902c8fc89737028f81c070204dfb4 Mon Sep 17 00:00:00 2001
From: dongheng <Dong Heng>
Date: Fri, 15 Mar 2019 14:38:36 +0800
Subject: [PATCH] feat(spiffs): Bring from esp-idf

Commit ID: 13018449
---
 components/spiffs/CMakeLists.txt              |   16 +-
 components/spiffs/Kconfig                     |  161 ++
 components/spiffs/component.mk                |    9 +-
 components/spiffs/esp_spiffs.c                |  746 +++++
 components/spiffs/include/esp_spiffs.h        |  102 +
 components/spiffs/include/spiffs/esp_spiffs.h |   84 -
 components/spiffs/include/spiffs_config.h     |  316 +++
 components/spiffs/library/esp_spiffs.c        |  311 --
 components/spiffs/spiffs/.travis.yml          |    8 +
 components/spiffs/spiffs/FUZZING.md           |   47 +
 components/spiffs/spiffs/LICENSE              |   20 +
 components/spiffs/spiffs/README.md            |  212 ++
 components/spiffs/spiffs/afltests/100         |   15 +
 components/spiffs/spiffs/afltests/200         |  Bin 0 -> 176 bytes
 components/spiffs/spiffs/afltests/a           |   18 +
 components/spiffs/spiffs/afltests/b           |   15 +
 components/spiffs/spiffs/docs/TECH_SPEC       |  239 ++
 components/spiffs/spiffs/docs/TODO            |   15 +
 components/spiffs/spiffs/files.mk             |   12 +
 components/spiffs/spiffs/makefile             |  163 ++
 .../src/default}/spiffs_config.h              |  177 +-
 .../{include/spiffs => spiffs/src}/spiffs.h   |  314 ++-
 .../{library => spiffs/src}/spiffs_cache.c    |   72 +-
 .../{library => spiffs/src}/spiffs_check.c    |  178 +-
 .../{library => spiffs/src}/spiffs_gc.c       |  134 +-
 .../{library => spiffs/src}/spiffs_hydrogen.c |  715 ++++-
 .../{library => spiffs/src}/spiffs_nucleus.c  |  818 ++++--
 .../spiffs => spiffs/src}/spiffs_nucleus.h    |  172 +-
 components/spiffs/spiffs/src/test/main.c      |   12 +
 .../spiffs/spiffs/src/test/params_test.h      |   84 +
 .../spiffs/spiffs/src/test/test_bugreports.c  | 1266 +++++++++
 .../spiffs/spiffs/src/test/test_check.c       |  427 +++
 components/spiffs/spiffs/src/test/test_dev.c  |  122 +
 .../spiffs/spiffs/src/test/test_hydrogen.c    | 2507 +++++++++++++++++
 .../spiffs/spiffs/src/test/test_spiffs.c      | 1111 ++++++++
 .../spiffs/spiffs/src/test/test_spiffs.h      |  109 +
 .../spiffs/spiffs/src/test/testrunner.c       |  238 ++
 .../spiffs/spiffs/src/test/testrunner.h       |  165 ++
 .../spiffs/spiffs/src/test/testsuites.c       |   15 +
 components/spiffs/spiffs_api.c                |   93 +
 components/spiffs/spiffs_api.h                |   57 +
 components/spiffs/test/CMakeLists.txt         |    6 +
 components/spiffs/test/component.mk           |    1 +
 components/spiffs/test/test_spiffs.c          |  652 +++++
 components/spiffs/test_spiffs_host/Makefile   |   98 +
 .../spiffs/test_spiffs_host/Makefile.files    |   33 +
 .../spiffs/test_spiffs_host/component.mk      |   17 +
 components/spiffs/test_spiffs_host/main.cpp   |    3 +
 .../test_spiffs_host/partition_table.csv      |    6 +
 .../test_spiffs_host/sdkconfig/sdkconfig.h    |   19 +
 .../spiffs/test_spiffs_host/test_spiffs.cpp   |  107 +
 .../spiffs/test_spiffs_host/test_utils.c      |    7 +
 52 files changed, 11321 insertions(+), 923 deletions(-)
 create mode 100644 components/spiffs/Kconfig
 create mode 100644 components/spiffs/esp_spiffs.c
 create mode 100644 components/spiffs/include/esp_spiffs.h
 delete mode 100644 components/spiffs/include/spiffs/esp_spiffs.h
 create mode 100644 components/spiffs/include/spiffs_config.h
 delete mode 100644 components/spiffs/library/esp_spiffs.c
 create mode 100644 components/spiffs/spiffs/.travis.yml
 create mode 100644 components/spiffs/spiffs/FUZZING.md
 create mode 100644 components/spiffs/spiffs/LICENSE
 create mode 100644 components/spiffs/spiffs/README.md
 create mode 100644 components/spiffs/spiffs/afltests/100
 create mode 100644 components/spiffs/spiffs/afltests/200
 create mode 100644 components/spiffs/spiffs/afltests/a
 create mode 100644 components/spiffs/spiffs/afltests/b
 create mode 100644 components/spiffs/spiffs/docs/TECH_SPEC
 create mode 100644 components/spiffs/spiffs/docs/TODO
 create mode 100644 components/spiffs/spiffs/files.mk
 create mode 100644 components/spiffs/spiffs/makefile
 rename components/spiffs/{include/spiffs => spiffs/src/default}/spiffs_config.h (52%)
 rename components/spiffs/{include/spiffs => spiffs/src}/spiffs.h (59%)
 rename components/spiffs/{library => spiffs/src}/spiffs_cache.c (76%)
 rename components/spiffs/{library => spiffs/src}/spiffs_check.c (83%)
 rename components/spiffs/{library => spiffs/src}/spiffs_gc.c (75%)
 rename components/spiffs/{library => spiffs/src}/spiffs_hydrogen.c (52%)
 rename components/spiffs/{library => spiffs/src}/spiffs_nucleus.c (66%)
 rename components/spiffs/{include/spiffs => spiffs/src}/spiffs_nucleus.h (81%)
 create mode 100644 components/spiffs/spiffs/src/test/main.c
 create mode 100644 components/spiffs/spiffs/src/test/params_test.h
 create mode 100644 components/spiffs/spiffs/src/test/test_bugreports.c
 create mode 100644 components/spiffs/spiffs/src/test/test_check.c
 create mode 100644 components/spiffs/spiffs/src/test/test_dev.c
 create mode 100644 components/spiffs/spiffs/src/test/test_hydrogen.c
 create mode 100644 components/spiffs/spiffs/src/test/test_spiffs.c
 create mode 100644 components/spiffs/spiffs/src/test/test_spiffs.h
 create mode 100644 components/spiffs/spiffs/src/test/testrunner.c
 create mode 100644 components/spiffs/spiffs/src/test/testrunner.h
 create mode 100644 components/spiffs/spiffs/src/test/testsuites.c
 create mode 100644 components/spiffs/spiffs_api.c
 create mode 100644 components/spiffs/spiffs_api.h
 create mode 100644 components/spiffs/test/CMakeLists.txt
 create mode 100644 components/spiffs/test/component.mk
 create mode 100644 components/spiffs/test/test_spiffs.c
 create mode 100644 components/spiffs/test_spiffs_host/Makefile
 create mode 100644 components/spiffs/test_spiffs_host/Makefile.files
 create mode 100644 components/spiffs/test_spiffs_host/component.mk
 create mode 100644 components/spiffs/test_spiffs_host/main.cpp
 create mode 100644 components/spiffs/test_spiffs_host/partition_table.csv
 create mode 100644 components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h
 create mode 100644 components/spiffs/test_spiffs_host/test_spiffs.cpp
 create mode 100644 components/spiffs/test_spiffs_host/test_utils.c

diff --git a/components/spiffs/CMakeLists.txt b/components/spiffs/CMakeLists.txt
index f2b03f79..17e5a558 100644
--- a/components/spiffs/CMakeLists.txt
+++ b/components/spiffs/CMakeLists.txt
@@ -1,7 +1,15 @@
-set(COMPONENT_ADD_INCLUDEDIRS include include/spiffs)
-set(COMPONENT_SRCDIRS library)
+set(COMPONENT_ADD_INCLUDEDIRS "include")
+set(COMPONENT_PRIV_INCLUDEDIRS "." "spiffs/src")
+set(COMPONENT_SRCS "esp_spiffs.c"
+                   "spiffs_api.c"
+                   "spiffs/src/spiffs_cache.c"
+                   "spiffs/src/spiffs_check.c"
+                   "spiffs/src/spiffs_gc.c"
+                   "spiffs/src/spiffs_hydrogen.c"
+                   "spiffs/src/spiffs_nucleus.c")
 
-set(COMPONENT_REQUIRES  freertos)
-set(COMPONENT_PRIV_REQUIRES esp8266)
+set(COMPONENT_REQUIRES spi_flash)
+set(COMPONENT_PRIV_REQUIRES bootloader_support)
 
 register_component()
+
diff --git a/components/spiffs/Kconfig b/components/spiffs/Kconfig
new file mode 100644
index 00000000..697f2b68
--- /dev/null
+++ b/components/spiffs/Kconfig
@@ -0,0 +1,161 @@
+menu "SPIFFS Configuration"
+
+config SPIFFS_MAX_PARTITIONS
+    int "Maximum Number of Partitions"
+    default 3
+    range 1 10
+    help
+        Define maximum number of partitions that can be mounted.
+
+menu "SPIFFS Cache Configuration"
+config SPIFFS_CACHE
+    bool "Enable SPIFFS Cache"
+    default "y"
+    help
+        Enables/disable memory read caching of nucleus file system 
+        operations.
+
+config SPIFFS_CACHE_WR
+    bool "Enable SPIFFS Write Caching"
+    default "y"
+    depends on SPIFFS_CACHE
+    help
+        Enables memory write caching for file descriptors in hydrogen.
+
+config SPIFFS_CACHE_STATS
+    bool "Enable SPIFFS Cache Statistics"
+    default "n"
+    depends on SPIFFS_CACHE
+    help
+        Enable/disable statistics on caching. Debug/test purpose only.
+
+endmenu
+
+config SPIFFS_PAGE_CHECK
+    bool "Enable SPIFFS Page Check"
+    default "y"
+    help
+        Always check header of each accessed page to ensure consistent state.
+        If enabled it will increase number of reads from flash, especially
+        if cache is disabled.
+
+config SPIFFS_GC_MAX_RUNS
+    int "Set Maximum GC Runs"
+    default 10
+    range 1 255
+    help
+        Define maximum number of GC runs to perform to reach desired free pages.
+
+config SPIFFS_GC_STATS
+    bool "Enable SPIFFS GC Statistics"
+    default "n"
+    help
+        Enable/disable statistics on gc. Debug/test purpose only.
+
+config SPIFFS_PAGE_SIZE
+	int "SPIFFS logical page size"
+	default 256
+	range 256 1024
+	help
+		Logical page size of SPIFFS partition, in bytes. Must be multiple
+		of flash page size (which is usually 256 bytes).
+		Larger page sizes reduce overhead when storing large files, and
+		improve filesystem performance when reading large files.
+		Smaller page sizes reduce overhead when storing small (< page size)
+		files.
+
+config SPIFFS_OBJ_NAME_LEN
+    int "Set SPIFFS Maximum Name Length"
+    default 32
+    range 1 256
+    help
+        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.
+        
+        SPIFFS_OBJ_NAME_LEN + SPIFFS_META_LENGTH should not exceed
+        SPIFFS_PAGE_SIZE - 64.
+
+config SPIFFS_USE_MAGIC
+    bool "Enable SPIFFS Filesystem Magic"
+    default "y"
+    help
+        Enable this to have an identifiable spiffs filesystem. 
+        This will look for a magic in all sectors to determine if this
+        is a valid spiffs system or not at mount time.
+
+config SPIFFS_USE_MAGIC_LENGTH
+    bool "Enable SPIFFS Filesystem Length Magic"
+    default "y"
+    depends on SPIFFS_USE_MAGIC
+    help
+        If this option 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.
+
+config SPIFFS_META_LENGTH
+    int "Size of per-file metadata field"
+    default 4
+    help
+        This option sets the number of extra bytes stored in the file header.
+        These bytes can be used in an application-specific manner.
+        Set this to at least 4 bytes to enable support for saving file
+        modification time.
+        
+        SPIFFS_OBJ_NAME_LEN + SPIFFS_META_LENGTH should not exceed
+        SPIFFS_PAGE_SIZE - 64.
+
+config SPIFFS_USE_MTIME
+    bool "Save file modification time"
+    default "y"
+    depends on SPIFFS_META_LENGTH >= 4
+    help
+        If enabled, then the first 4 bytes of per-file metadata will be used
+        to store file modification time (mtime), accessible through
+        stat/fstat functions.
+        Modification time is updated when the file is opened.
+
+menu "Debug Configuration"
+
+config SPIFFS_DBG
+    bool "Enable general SPIFFS debug"
+    default "n"
+    help
+        Enabling this option will print general debug mesages to the console.
+
+config SPIFFS_API_DBG
+    bool "Enable SPIFFS API debug"
+    default "n"
+    help
+        Enabling this option will print API debug mesages to the console.
+
+config SPIFFS_GC_DBG
+    bool "Enable SPIFFS Garbage Cleaner debug"
+    default "n"
+    help
+        Enabling this option will print GC debug mesages to the console.
+
+config SPIFFS_CACHE_DBG
+    bool "Enable SPIFFS Cache debug"
+    default "n"
+    depends on SPIFFS_CACHE
+    help
+        Enabling this option will print cache debug mesages to the console.
+
+config SPIFFS_CHECK_DBG
+    bool "Enable SPIFFS Filesystem Check debug"
+    default "n"
+    help
+        Enabling this option will print Filesystem Check debug mesages
+        to the console.
+
+config SPIFFS_TEST_VISUALISATION
+    bool "Enable SPIFFS Filesystem Visualization"
+    default "n"
+    help
+        Enable this option to enable SPIFFS_vis function in the API.
+
+endmenu
+
+endmenu
diff --git a/components/spiffs/component.mk b/components/spiffs/component.mk
index ad45698a..f245a57e 100644
--- a/components/spiffs/component.mk
+++ b/components/spiffs/component.mk
@@ -1,6 +1,5 @@
-#
-# Component Makefile
-#
-COMPONENT_ADD_INCLUDEDIRS += include/spiffs
+COMPONENT_ADD_INCLUDEDIRS := include
+COMPONENT_PRIV_INCLUDEDIRS := . spiffs/src
+COMPONENT_SRCDIRS := . spiffs/src
 
-COMPONENT_SRCDIRS := library
+COMPONENT_SUBMODULES := spiffs
diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c
new file mode 100644
index 00000000..853986e4
--- /dev/null
+++ b/components/spiffs/esp_spiffs.c
@@ -0,0 +1,746 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "esp_spiffs.h"
+#include "spiffs.h"
+#include "spiffs_nucleus.h"
+#include "esp_log.h"
+#include "esp_partition.h"
+#include "esp_spi_flash.h"
+#include "esp_image_format.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/semphr.h"
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/lock.h>
+#include "esp_vfs.h"
+#include "esp_err.h"
+#include "rom/spi_flash.h"
+#include "spiffs_api.h"
+
+static const char* TAG = "SPIFFS";
+
+#ifdef CONFIG_SPIFFS_USE_MTIME
+_Static_assert(CONFIG_SPIFFS_META_LENGTH >= sizeof(time_t),
+        "SPIFFS_META_LENGTH size should be >= sizeof(time_t)");
+#endif //CONFIG_SPIFFS_USE_MTIME
+
+/**
+ * @brief SPIFFS DIR structure
+ */
+typedef struct {
+    DIR dir;            /*!< VFS DIR struct */
+    spiffs_DIR d;       /*!< SPIFFS DIR struct */
+    struct dirent e;    /*!< Last open dirent */
+    long offset;        /*!< Offset of the current dirent */
+    char path[SPIFFS_OBJ_NAME_LEN]; /*!< Requested directory name */
+} vfs_spiffs_dir_t;
+
+static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode);
+static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size);
+static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size);
+static int vfs_spiffs_close(void* ctx, int fd);
+static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode);
+static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st);
+static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st);
+static int vfs_spiffs_unlink(void* ctx, const char *path);
+static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2);
+static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst);
+static DIR* vfs_spiffs_opendir(void* ctx, const char* name);
+static int vfs_spiffs_closedir(void* ctx, DIR* pdir);
+static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir);
+static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir,
+                                struct dirent* entry, struct dirent** out_dirent);
+static long vfs_spiffs_telldir(void* ctx, DIR* pdir);
+static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset);
+static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode);
+static int vfs_spiffs_rmdir(void* ctx, const char* name);
+static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file f);
+static time_t vfs_spiffs_get_mtime(const spiffs_stat* s);
+
+static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS];
+
+static void esp_spiffs_free(esp_spiffs_t ** efs)
+{
+    esp_spiffs_t * e = *efs;
+    if (*efs == NULL) {
+        return;
+    }
+    *efs = NULL;
+
+    if (e->fs) {
+        SPIFFS_unmount(e->fs);
+        free(e->fs);
+    }
+    vSemaphoreDelete(e->lock);
+    free(e->fds);
+    free(e->cache);
+    free(e->work);
+    free(e);
+}
+
+static esp_err_t esp_spiffs_by_label(const char* label, int * index){
+    int i;
+    esp_spiffs_t * p;
+    for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) {
+        p = _efs[i];
+        if (p) {
+            if (!label && !p->by_label) {
+                *index = i;
+                return ESP_OK;
+            }
+            if (label && p->by_label && strncmp(label, p->partition->label, 17) == 0) {
+                *index = i;
+                return ESP_OK;
+            }
+        }
+    }
+    return ESP_ERR_NOT_FOUND;
+}
+
+static esp_err_t esp_spiffs_get_empty(int * index){
+    int i;
+    for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) {
+        if (_efs[i] == NULL) {
+            *index = i;
+            return ESP_OK;
+        }
+    }
+    return ESP_ERR_NOT_FOUND;
+}
+
+static esp_err_t esp_spiffs_init(const esp_vfs_spiffs_conf_t* conf)
+{
+    int index;
+    //find if such partition is already mounted
+    if (esp_spiffs_by_label(conf->partition_label, &index) == ESP_OK) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    if (esp_spiffs_get_empty(&index) != ESP_OK) {
+        ESP_LOGE(TAG, "max mounted partitions reached");
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    uint32_t flash_page_size = g_rom_flashchip.page_size;
+    uint32_t log_page_size = CONFIG_SPIFFS_PAGE_SIZE;
+    if (log_page_size % flash_page_size != 0) {
+        ESP_LOGE(TAG, "SPIFFS_PAGE_SIZE is not multiple of flash chip page size (%d)",
+                flash_page_size);
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    esp_partition_subtype_t subtype = conf->partition_label ?
+            ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_SPIFFS;
+    const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
+                                      subtype, conf->partition_label);
+    if (!partition) {
+        ESP_LOGE(TAG, "spiffs partition could not be found");
+        return ESP_ERR_NOT_FOUND;
+    }
+
+    if (partition->encrypted) {
+        ESP_LOGE(TAG, "spiffs can not run on encrypted partition");
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    esp_spiffs_t * efs = malloc(sizeof(esp_spiffs_t));
+    if (efs == NULL) {
+        ESP_LOGE(TAG, "esp_spiffs could not be malloced");
+        return ESP_ERR_NO_MEM;
+    }
+    memset(efs, 0, sizeof(esp_spiffs_t));
+
+    efs->cfg.hal_erase_f       = spiffs_api_erase;
+    efs->cfg.hal_read_f        = spiffs_api_read;
+    efs->cfg.hal_write_f       = spiffs_api_write;
+    efs->cfg.log_block_size    = g_rom_flashchip.sector_size;
+    efs->cfg.log_page_size     = log_page_size;
+    efs->cfg.phys_addr         = 0;
+    efs->cfg.phys_erase_block  = g_rom_flashchip.sector_size;
+    efs->cfg.phys_size         = partition->size;
+
+    efs->by_label = conf->partition_label != NULL;
+
+    efs->lock = xSemaphoreCreateMutex();
+    if (efs->lock == NULL) {
+        ESP_LOGE(TAG, "mutex lock could not be created");
+        esp_spiffs_free(&efs);
+        return ESP_ERR_NO_MEM;
+    }
+
+    efs->fds_sz = conf->max_files * sizeof(spiffs_fd);
+    efs->fds = malloc(efs->fds_sz);
+    if (efs->fds == NULL) {
+        ESP_LOGE(TAG, "fd buffer could not be malloced");
+        esp_spiffs_free(&efs);
+        return ESP_ERR_NO_MEM;
+    }
+    memset(efs->fds, 0, efs->fds_sz);
+
+#if SPIFFS_CACHE
+    efs->cache_sz = sizeof(spiffs_cache) + conf->max_files * (sizeof(spiffs_cache_page)
+                          + efs->cfg.log_page_size);
+    efs->cache = malloc(efs->cache_sz);
+    if (efs->cache == NULL) {
+        ESP_LOGE(TAG, "cache buffer could not be malloced");
+        esp_spiffs_free(&efs);
+        return ESP_ERR_NO_MEM;
+    }
+    memset(efs->cache, 0, efs->cache_sz);
+#endif
+
+    const uint32_t work_sz = efs->cfg.log_page_size * 2;
+    efs->work = malloc(work_sz);
+    if (efs->work == NULL) {
+        ESP_LOGE(TAG, "work buffer could not be malloced");
+        esp_spiffs_free(&efs);
+        return ESP_ERR_NO_MEM;
+    }
+    memset(efs->work, 0, work_sz);
+
+    efs->fs = malloc(sizeof(spiffs));
+    if (efs->fs == NULL) {
+        ESP_LOGE(TAG, "spiffs could not be malloced");
+        esp_spiffs_free(&efs);
+        return ESP_ERR_NO_MEM;
+    }
+    memset(efs->fs, 0, sizeof(spiffs));
+
+    efs->fs->user_data = (void *)efs;
+    efs->partition = partition;
+
+    s32_t res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz,
+                            efs->cache, efs->cache_sz, spiffs_api_check);
+
+    if (conf->format_if_mount_failed && res != SPIFFS_OK) {
+        ESP_LOGW(TAG, "mount failed, %i. formatting...", SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        res = SPIFFS_format(efs->fs);
+        if (res != SPIFFS_OK) {
+            ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(efs->fs));
+            SPIFFS_clearerr(efs->fs);
+            esp_spiffs_free(&efs);
+            return ESP_FAIL;
+        }
+        res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz,
+                            efs->cache, efs->cache_sz, spiffs_api_check);
+    }
+    if (res != SPIFFS_OK) {
+        ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        esp_spiffs_free(&efs);
+        return ESP_FAIL;
+    }
+    _efs[index] = efs;
+    return ESP_OK;
+}
+
+bool esp_spiffs_mounted(const char* partition_label)
+{
+    int index;
+    if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
+        return false;
+    }
+    return (SPIFFS_mounted(_efs[index]->fs));
+}
+
+esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes)
+{
+    int index;
+    if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
+        return ESP_ERR_INVALID_STATE;
+    }
+    SPIFFS_info(_efs[index]->fs, total_bytes, used_bytes);
+    return ESP_OK;
+}
+
+esp_err_t esp_spiffs_format(const char* partition_label)
+{
+    bool partition_was_mounted = false;
+    int index;
+    /* If the partition is not mounted, need to create SPIFFS structures
+     * and mount the partition, unmount, format, delete SPIFFS structures.
+     * See SPIFFS wiki for the reason why.
+     */
+    esp_err_t err = esp_spiffs_by_label(partition_label, &index);
+    if (err != ESP_OK) {
+        esp_vfs_spiffs_conf_t conf = {
+                .format_if_mount_failed = true,
+                .partition_label = partition_label,
+                .max_files = 1
+        };
+        err = esp_spiffs_init(&conf);
+        if (err != ESP_OK) {
+            return err;
+        }
+        err = esp_spiffs_by_label(partition_label, &index);
+        assert(err == ESP_OK && "failed to get index of the partition just mounted");
+    } else if (SPIFFS_mounted(_efs[index]->fs)) {
+        partition_was_mounted = true;
+    }
+
+    SPIFFS_unmount(_efs[index]->fs);
+
+    s32_t res = SPIFFS_format(_efs[index]->fs);
+    if (res != SPIFFS_OK) {
+        ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(_efs[index]->fs));
+        SPIFFS_clearerr(_efs[index]->fs);
+        /* If the partition was previously mounted, but format failed, don't
+         * try to mount the partition back (it will probably fail). On the
+         * other hand, if it was not mounted, need to clean up.
+         */
+        if (!partition_was_mounted) {
+            esp_spiffs_free(&_efs[index]);
+        }
+        return ESP_FAIL;
+    }
+
+    if (partition_was_mounted) {
+        res = SPIFFS_mount(_efs[index]->fs, &_efs[index]->cfg, _efs[index]->work,
+                            _efs[index]->fds, _efs[index]->fds_sz, _efs[index]->cache,
+                            _efs[index]->cache_sz, spiffs_api_check);
+        if (res != SPIFFS_OK) {
+            ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(_efs[index]->fs));
+            SPIFFS_clearerr(_efs[index]->fs);
+            return ESP_FAIL;
+        }
+    } else {
+        esp_spiffs_free(&_efs[index]);
+    }
+    return ESP_OK;
+}
+
+esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf)
+{
+    assert(conf->base_path);
+    const esp_vfs_t vfs = {
+        .flags = ESP_VFS_FLAG_CONTEXT_PTR,
+        .write_p = &vfs_spiffs_write,
+        .lseek_p = &vfs_spiffs_lseek,
+        .read_p = &vfs_spiffs_read,
+        .open_p = &vfs_spiffs_open,
+        .close_p = &vfs_spiffs_close,
+        .fstat_p = &vfs_spiffs_fstat,
+        .stat_p = &vfs_spiffs_stat,
+        .link_p = &vfs_spiffs_link,
+        .unlink_p = &vfs_spiffs_unlink,
+        .rename_p = &vfs_spiffs_rename,
+        .opendir_p = &vfs_spiffs_opendir,
+        .closedir_p = &vfs_spiffs_closedir,
+        .readdir_p = &vfs_spiffs_readdir,
+        .readdir_r_p = &vfs_spiffs_readdir_r,
+        .seekdir_p = &vfs_spiffs_seekdir,
+        .telldir_p = &vfs_spiffs_telldir,
+        .mkdir_p = &vfs_spiffs_mkdir,
+        .rmdir_p = &vfs_spiffs_rmdir
+    };
+
+    esp_err_t err = esp_spiffs_init(conf);
+    if (err != ESP_OK) {
+        return err;
+    }
+
+    int index;
+    if (esp_spiffs_by_label(conf->partition_label, &index) != ESP_OK) {
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    strlcat(_efs[index]->base_path, conf->base_path, ESP_VFS_PATH_MAX + 1);
+    err = esp_vfs_register(conf->base_path, &vfs, _efs[index]);
+    if (err != ESP_OK) {
+        esp_spiffs_free(&_efs[index]);
+        return err;
+    }
+
+    return ESP_OK;
+}
+
+esp_err_t esp_vfs_spiffs_unregister(const char* partition_label)
+{
+    int index;
+    if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
+        return ESP_ERR_INVALID_STATE;
+    }
+    esp_err_t err = esp_vfs_unregister(_efs[index]->base_path);
+    if (err != ESP_OK) {
+        return err;
+    }
+    esp_spiffs_free(&_efs[index]);
+    return ESP_OK;
+}
+
+static int spiffs_res_to_errno(s32_t fr)
+{
+    switch(fr) {
+    case SPIFFS_OK :
+        return 0;
+    case SPIFFS_ERR_NOT_MOUNTED :
+        return ENODEV;
+    case SPIFFS_ERR_NOT_A_FS :
+        return ENODEV;
+    case SPIFFS_ERR_FULL :
+        return ENOSPC;
+    case SPIFFS_ERR_BAD_DESCRIPTOR :
+        return EBADF;
+    case SPIFFS_ERR_MOUNTED :
+        return EEXIST;
+    case SPIFFS_ERR_FILE_EXISTS :
+        return EEXIST;
+    case SPIFFS_ERR_NOT_FOUND :
+        return ENOENT;
+    case SPIFFS_ERR_NOT_A_FILE :
+        return ENOENT;
+    case SPIFFS_ERR_DELETED :
+        return ENOENT;
+    case SPIFFS_ERR_FILE_DELETED :
+        return ENOENT;
+    case SPIFFS_ERR_NAME_TOO_LONG :
+        return ENAMETOOLONG;
+    case SPIFFS_ERR_RO_NOT_IMPL :
+        return EROFS;
+    case SPIFFS_ERR_RO_ABORTED_OPERATION :
+        return EROFS;
+    default :
+        return EIO;
+    }
+    return ENOTSUP;
+}
+
+static int spiffs_mode_conv(int m)
+{
+    int res = 0;
+    int acc_mode = m & O_ACCMODE;
+    if (acc_mode == O_RDONLY) {
+        res |= SPIFFS_O_RDONLY;
+    } else if (acc_mode == O_WRONLY) {
+        res |= SPIFFS_O_WRONLY;
+    } else if (acc_mode == O_RDWR) {
+        res |= SPIFFS_O_RDWR;
+    }
+    if ((m & O_CREAT) && (m & O_EXCL)) {
+        res |= SPIFFS_O_CREAT | SPIFFS_O_EXCL;
+    } else if ((m & O_CREAT) && (m & O_TRUNC)) {
+        res |= SPIFFS_O_CREAT | SPIFFS_O_TRUNC;
+    }
+    if (m & O_APPEND) {
+        res |= SPIFFS_O_CREAT | SPIFFS_O_APPEND;
+    }
+    return res;
+}
+
+static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode)
+{
+    assert(path);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    int spiffs_flags = spiffs_mode_conv(flags);
+    int fd = SPIFFS_open(efs->fs, path, spiffs_flags, mode);
+    if (fd < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    if (!(spiffs_flags & SPIFFS_RDONLY)) {
+        vfs_spiffs_update_mtime(efs->fs, fd);
+    }
+    return fd;
+}
+
+static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size)
+{
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    ssize_t res = SPIFFS_write(efs->fs, fd, (void *)data, size);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size)
+{
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    ssize_t res = SPIFFS_read(efs->fs, fd, dst, size);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static int vfs_spiffs_close(void* ctx, int fd)
+{
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    int res = SPIFFS_close(efs->fs, fd);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode)
+{
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    off_t res = SPIFFS_lseek(efs->fs, fd, offset, mode);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st)
+{
+    assert(st);
+    spiffs_stat s;
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    off_t res = SPIFFS_fstat(efs->fs, fd, &s);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    st->st_size = s.size;
+    st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG;
+    st->st_mtime = vfs_spiffs_get_mtime(&s);
+    st->st_atime = 0;
+    st->st_ctime = 0;
+    return res;
+}
+
+static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st)
+{
+    assert(path);
+    assert(st);
+    spiffs_stat s;
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    off_t res = SPIFFS_stat(efs->fs, path, &s);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+
+    st->st_size = s.size;
+    st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO;
+    st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG;
+    st->st_mtime = vfs_spiffs_get_mtime(&s);
+    st->st_atime = 0;
+    st->st_ctime = 0;
+    return res;
+}
+
+static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst)
+{
+    assert(src);
+    assert(dst);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    int res = SPIFFS_rename(efs->fs, src, dst);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static int vfs_spiffs_unlink(void* ctx, const char *path)
+{
+    assert(path);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    int res = SPIFFS_remove(efs->fs, path);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static DIR* vfs_spiffs_opendir(void* ctx, const char* name)
+{
+    assert(name);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    vfs_spiffs_dir_t * dir = calloc(1, sizeof(vfs_spiffs_dir_t));
+    if (!dir) {
+        errno = ENOMEM;
+        return NULL;
+    }
+    if (!SPIFFS_opendir(efs->fs, name, &dir->d)) {
+        free(dir);
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return NULL;
+    }
+    dir->offset = 0;
+    strlcpy(dir->path, name, SPIFFS_OBJ_NAME_LEN);
+    return (DIR*) dir;
+}
+
+static int vfs_spiffs_closedir(void* ctx, DIR* pdir)
+{
+    assert(pdir);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
+    int res = SPIFFS_closedir(&dir->d);
+    free(dir);
+    if (res < 0) {
+        errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+        SPIFFS_clearerr(efs->fs);
+        return -1;
+    }
+    return res;
+}
+
+static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir)
+{
+    assert(pdir);
+    vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
+    struct dirent* out_dirent;
+    int err = vfs_spiffs_readdir_r(ctx, pdir, &dir->e, &out_dirent);
+    if (err != 0) {
+        errno = err;
+        return NULL;
+    }
+    return out_dirent;
+}
+
+static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir, struct dirent* entry,
+                                struct dirent** out_dirent)
+{
+    assert(pdir);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
+    struct spiffs_dirent out;
+    size_t plen;
+    char * item_name;
+    do {
+        if (SPIFFS_readdir(&dir->d, &out) == 0) {
+            errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+            SPIFFS_clearerr(efs->fs);
+            if (!errno) {
+                *out_dirent = NULL;
+            }
+            return errno;
+        }
+        item_name = (char *)out.name;
+        plen = strlen(dir->path);
+
+    } while ((plen > 1) && (strncasecmp(dir->path, (const char*)out.name, plen) || out.name[plen] != '/' || !out.name[plen + 1]));
+
+    if (plen > 1) {
+        item_name += plen + 1;
+    } else if (item_name[0] == '/') {
+        item_name++;
+    }
+    entry->d_ino = 0;
+    entry->d_type = out.type;
+    snprintf(entry->d_name, SPIFFS_OBJ_NAME_LEN, "%s", item_name);
+    dir->offset++;
+    *out_dirent = entry;
+    return 0;
+}
+
+static long vfs_spiffs_telldir(void* ctx, DIR* pdir)
+{
+    assert(pdir);
+    vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
+    return dir->offset;
+}
+
+static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset)
+{
+    assert(pdir);
+    esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
+    vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
+    struct spiffs_dirent tmp;
+    if (offset < dir->offset) {
+        //rewind dir
+        SPIFFS_closedir(&dir->d);
+        if (!SPIFFS_opendir(efs->fs, NULL, &dir->d)) {
+            errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+            SPIFFS_clearerr(efs->fs);
+            return;
+        }
+        dir->offset = 0;
+    }
+    while (dir->offset < offset) {
+        if (SPIFFS_readdir(&dir->d, &tmp) == 0) {
+            errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
+            SPIFFS_clearerr(efs->fs);
+            return;
+        }
+        size_t plen = strlen(dir->path);
+        if (plen > 1) {
+            if (strncasecmp(dir->path, (const char *)tmp.name, plen) || tmp.name[plen] != '/' || !tmp.name[plen+1]) {
+                continue;
+            }
+        }
+        dir->offset++;
+    }
+}
+
+static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode)
+{
+    errno = ENOTSUP;
+    return -1;
+}
+
+static int vfs_spiffs_rmdir(void* ctx, const char* name)
+{
+    errno = ENOTSUP;
+    return -1;
+}
+
+static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2)
+{
+    errno = ENOTSUP;
+    return -1;
+}
+
+static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file fd)
+{
+#ifdef CONFIG_SPIFFS_USE_MTIME
+    time_t t = time(NULL);
+    spiffs_stat s;
+    int ret = SPIFFS_OK;
+    if (CONFIG_SPIFFS_META_LENGTH > sizeof(t)) {
+        ret = SPIFFS_fstat(fs, fd, &s);
+    }
+    if (ret == SPIFFS_OK) {
+        memcpy(s.meta, &t, sizeof(t));
+        ret = SPIFFS_fupdate_meta(fs, fd, s.meta);
+    }
+    if (ret != SPIFFS_OK) {
+        ESP_LOGW(TAG, "Failed to update mtime (%d)", ret);
+    }
+#endif //CONFIG_SPIFFS_USE_MTIME
+}
+
+static time_t vfs_spiffs_get_mtime(const spiffs_stat* s)
+{
+    time_t t = 0;
+#ifdef CONFIG_SPIFFS_USE_MTIME
+    memcpy(&t, s->meta, sizeof(t));
+#endif
+    return t;
+}
diff --git a/components/spiffs/include/esp_spiffs.h b/components/spiffs/include/esp_spiffs.h
new file mode 100644
index 00000000..ae1b9ad4
--- /dev/null
+++ b/components/spiffs/include/esp_spiffs.h
@@ -0,0 +1,102 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef _ESP_SPIFFS_H_
+#define _ESP_SPIFFS_H_
+
+#include <stdbool.h>
+#include "esp_err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Configuration structure for esp_vfs_spiffs_register
+ */
+typedef struct {
+        const char* base_path;          /*!< File path prefix associated with the filesystem. */
+        const char* partition_label;    /*!< Optional, label of SPIFFS partition to use. If set to NULL, first partition with subtype=spiffs will be used. */
+        size_t max_files;               /*!< Maximum files that could be open at the same time. */
+        bool format_if_mount_failed;    /*!< If true, it will format the file system if it fails to mount. */
+} esp_vfs_spiffs_conf_t;
+
+/**
+ * Register and mount SPIFFS to VFS with given path prefix.
+ *
+ * @param   conf                      Pointer to esp_vfs_spiffs_conf_t configuration structure
+ *
+ * @return  
+ *          - ESP_OK                  if success
+ *          - ESP_ERR_NO_MEM          if objects could not be allocated
+ *          - ESP_ERR_INVALID_STATE   if already mounted or partition is encrypted
+ *          - ESP_ERR_NOT_FOUND       if partition for SPIFFS was not found
+ *          - ESP_FAIL                if mount or format fails
+ */
+esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf);
+
+/**
+ * Unregister and unmount SPIFFS from VFS
+ *
+ * @param partition_label  Optional, label of the partition to unregister.
+ *                         If not specified, first partition with subtype=spiffs is used.
+ *
+ * @return  
+ *          - ESP_OK if successful
+ *          - ESP_ERR_INVALID_STATE already unregistered
+ */
+esp_err_t esp_vfs_spiffs_unregister(const char* partition_label);
+
+/**
+ * Check if SPIFFS is mounted
+ *
+ * @param partition_label  Optional, label of the partition to check.
+ *                         If not specified, first partition with subtype=spiffs is used.
+ *
+ * @return  
+ *          - true    if mounted
+ *          - false   if not mounted
+ */
+bool esp_spiffs_mounted(const char* partition_label);
+
+/**
+ * Format the SPIFFS partition
+ *
+ * @param partition_label  Optional, label of the partition to format.
+ *                         If not specified, first partition with subtype=spiffs is used.
+ * @return  
+ *          - ESP_OK      if successful
+ *          - ESP_FAIL    on error
+ */
+esp_err_t esp_spiffs_format(const char* partition_label);
+
+/**
+ * Get information for SPIFFS
+ *
+ * @param partition_label           Optional, label of the partition to get info for.
+ *                                  If not specified, first partition with subtype=spiffs is used.
+ * @param[out] total_bytes          Size of the file system
+ * @param[out] used_bytes           Current used bytes in the file system
+ *
+ * @return  
+ *          - ESP_OK                  if success
+ *          - ESP_ERR_INVALID_STATE   if not mounted
+ */
+esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ESP_SPIFFS_H_ */
diff --git a/components/spiffs/include/spiffs/esp_spiffs.h b/components/spiffs/include/spiffs/esp_spiffs.h
deleted file mode 100644
index d02340c8..00000000
--- a/components/spiffs/include/spiffs/esp_spiffs.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * ESPRSSIF MIT License
- *
- * Copyright (c) 2015 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
- *
- * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
- * it is free of charge, to any person obtaining a copy of this software and associated
- * documentation files (the "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
- * to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all copies or
- * substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-#ifndef __ESP_SPIFFS_H__
-#define __ESP_SPIFFS_H__
-
-#include "spiffs/spiffs.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** \defgroup Spiffs_APIs Spiffs APIs
-  * @brief Spiffs APIs
-  *
-  * More details about spiffs on https://github.com/pellepl/spiffs
-  *
-  */
-
-/** @addtogroup Spiffs_APIs
-  * @{
-  */
-
-struct esp_spiffs_config {
-    u32_t phys_size;        /**< physical size of the SPI Flash */
-    u32_t phys_addr;        /**< physical offset in spi flash used for spiffs, must be on block boundary */
-    u32_t phys_erase_block; /**< physical size when erasing a block */
-
-    u32_t log_block_size;   /**< logical size of a block, must be on physical block size boundary and must never be less than a physical block */
-    u32_t log_page_size;    /**< logical size of a page, at least log_block_size/8  */
-
-    u32_t fd_buf_size;      /**< file descriptor memory area size */
-    u32_t cache_buf_size;   /**< cache buffer size */
-};
-
-/**
-  * @brief  Initialize spiffs
-  *
-  * @param  struct esp_spiffs_config *config : ESP8266 spiffs configuration
-  *
-  * @return 0         : succeed (Equals SPIFFS_OK)
-  * @return otherwise : fail (-1 or SPIFFS_ERR_*)
-  */
-s32_t esp_spiffs_init(struct esp_spiffs_config *config);
-
-/**
-  * @brief  Deinitialize spiffs
-  *
-  * @param  uint8 format : 0, only deinit; otherwise, deinit spiffs and format.
-  *
-  * @return null
-  */
-void esp_spiffs_deinit(u8_t format);
-
-/**
-  * @}
-  */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __ESP_SPIFFS_H__ */
diff --git a/components/spiffs/include/spiffs_config.h b/components/spiffs/include/spiffs_config.h
new file mode 100644
index 00000000..a382ba6f
--- /dev/null
+++ b/components/spiffs/include/spiffs_config.h
@@ -0,0 +1,316 @@
+/*
+ * spiffs_config.h
+ *
+ *  Created on: Jul 3, 2013
+ *      Author: petera
+ */
+
+#ifndef SPIFFS_CONFIG_H_
+#define SPIFFS_CONFIG_H_
+
+// ----------- 8< ------------
+// Following includes are for the linux test build of spiffs
+// These may/should/must be removed/altered/replaced in your target
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <sdkconfig.h>
+#include <esp_log.h>
+
+// compile time switches
+#define SPIFFS_TAG "SPIFFS"
+
+// Set generic spiffs debug output call.
+#if CONFIG_SPIFFS_DBG
+#define SPIFFS_DBG(...)             ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
+#else
+#define SPIFFS_DBG(...)
+#endif
+#if CONFIG_SPIFFS_API_DBG
+#define SPIFFS_API_DBG(...)         ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
+#else
+#define SPIFFS_API_DBG(...)
+#endif
+#if CONFIG_SPIFFS_DBG
+#define SPIFFS_GC_DBG(...)          ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
+#else
+#define SPIFFS_GC_DBG(...)
+#endif
+#if CONFIG_SPIFFS_CACHE_DBG
+#define SPIFFS_CACHE_DBG(...)       ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
+#else
+#define SPIFFS_CACHE_DBG(...)
+#endif
+#if CONFIG_SPIFFS_CHECK_DBG
+#define SPIFFS_CHECK_DBG(...)       ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
+#else
+#define SPIFFS_CHECK_DBG(...)
+#endif
+
+// needed types
+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;
+
+struct spiffs_t;
+extern void spiffs_api_lock(struct spiffs_t *fs);
+extern void spiffs_api_unlock(struct spiffs_t *fs);
+
+// Defines spiffs debug print formatters
+// some general signed number
+#define _SPIPRIi   "%d"
+// address
+#define _SPIPRIad  "%08x"
+// block
+#define _SPIPRIbl  "%04x"
+// page
+#define _SPIPRIpg  "%04x"
+// span index
+#define _SPIPRIsp  "%04x"
+// file descriptor
+#define _SPIPRIfd  "%d"
+// file object id
+#define _SPIPRIid  "%04x"
+// file flags
+#define _SPIPRIfl  "%02x"
+
+
+// Enable/disable API functions to determine exact number of bytes
+// for filedescriptor and cache buffers. Once decided for a configuration,
+// this can be disabled to reduce flash.
+#define SPIFFS_BUFFER_HELP              0
+
+// Enables/disable memory read caching of nucleus file system operations.
+// If enabled, memory area must be provided for cache in SPIFFS_mount.
+#ifdef CONFIG_SPIFFS_CACHE
+#define SPIFFS_CACHE                (1)
+#else
+#define SPIFFS_CACHE                (0)
+#endif
+#if SPIFFS_CACHE
+// Enables memory write caching for file descriptors in hydrogen
+#ifdef CONFIG_SPIFFS_CACHE_WR
+#define SPIFFS_CACHE_WR             (1)
+#else
+#define SPIFFS_CACHE_WR             (0)
+#endif
+
+// Enable/disable statistics on caching. Debug/test purpose only.
+#ifdef CONFIG_SPIFFS_CACHE_STATS
+#define SPIFFS_CACHE_STATS          (1)
+#else
+#define SPIFFS_CACHE_STATS          (0)
+#endif
+#endif
+
+// Always check header of each accessed page to ensure consistent state.
+// If enabled it will increase number of reads, will increase flash.
+#ifdef CONFIG_SPIFFS_PAGE_CHECK
+#define SPIFFS_PAGE_CHECK           (1)
+#else
+#define SPIFFS_PAGE_CHECK           (0)
+#endif
+
+// Define maximum number of gc runs to perform to reach desired free pages.
+#define SPIFFS_GC_MAX_RUNS              CONFIG_SPIFFS_GC_MAX_RUNS
+
+// Enable/disable statistics on gc. Debug/test purpose only.
+#ifdef CONFIG_SPIFFS_GC_STATS
+#define SPIFFS_GC_STATS             (1)
+#else
+#define SPIFFS_GC_STATS             (0)
+#endif
+
+// Garbage collecting examines all pages in a block which and sums up
+// to a block score. Deleted pages normally gives positive score and
+// used pages normally gives a negative score (as these must be moved).
+// To have a fair wear-leveling, the erase age is also included in score,
+// whose factor normally is the most positive.
+// The larger the score, the more likely it is that the block will
+// picked for garbage collection.
+
+// Garbage collecting heuristics - weight used for deleted pages.
+#define SPIFFS_GC_HEUR_W_DELET          (5)
+// Garbage collecting heuristics - weight used for used pages.
+#define SPIFFS_GC_HEUR_W_USED           (-1)
+// Garbage collecting heuristics - weight used for time between
+// last erased and erase of this block.
+#define SPIFFS_GC_HEUR_W_ERASE_AGE      (50)
+
+// 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.
+#define SPIFFS_OBJ_NAME_LEN             (CONFIG_SPIFFS_OBJ_NAME_LEN)
+
+// Maximum length of the metadata associated with an object.
+// Setting to non-zero value enables metadata-related API but also
+// changes the on-disk format, so the change is not backward-compatible.
+//
+// Do note: the meta length must never exceed
+// logical_page_size - (SPIFFS_OBJ_NAME_LEN + SPIFFS_PAGE_EXTRA_SIZE)
+//
+// This is derived from following:
+// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) +
+// spiffs_object_ix_header fields + at least some LUT entries)
+#define SPIFFS_OBJ_META_LEN             (CONFIG_SPIFFS_META_LENGTH)
+#define SPIFFS_PAGE_EXTRA_SIZE          (64)
+_Static_assert(SPIFFS_OBJ_META_LEN + SPIFFS_OBJ_NAME_LEN + SPIFFS_PAGE_EXTRA_SIZE
+        <= CONFIG_SPIFFS_PAGE_SIZE, "SPIFFS_OBJ_META_LEN or SPIFFS_OBJ_NAME_LEN too long");
+
+// Size of buffer allocated on stack used when copying data.
+// Lower value generates more read/writes. No meaning having it bigger
+// than logical page size.
+#define SPIFFS_COPY_BUFFER_STACK        (256)
+
+// Enable this to have an identifiable spiffs filesystem. This will look for
+// a magic in all sectors to determine if this is a valid spiffs system or
+// not on mount point. If not, SPIFFS_format must be called prior to mounting
+// again.
+#ifdef CONFIG_SPIFFS_USE_MAGIC
+#define SPIFFS_USE_MAGIC                (1)
+#else
+#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.
+#ifdef CONFIG_SPIFFS_USE_MAGIC_LENGTH
+#define SPIFFS_USE_MAGIC_LENGTH         (1)
+#else
+#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 enter a mutex if you're running on a multithreaded system
+#define SPIFFS_LOCK(fs)   spiffs_api_lock(fs)
+// define this to exit a mutex if you're running on a multithreaded system
+#define SPIFFS_UNLOCK(fs) spiffs_api_unlock(fs)
+
+// 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.
+#define SPIFFS_SINGLETON 0
+
+// Enable this if your target needs aligned data for index tables
+#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES      0
+
+// Enable this if you want the HAL callbacks to be called with the spiffs struct
+#define SPIFFS_HAL_CALLBACK_EXTRA               1
+
+// 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.
+#define SPIFFS_FILEHDL_OFFSET                   0
+
+// 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.
+#define SPIFFS_READ_ONLY                        0
+
+// Enable this to add a temporal file cache using the fd buffer.
+// The effects of the cache is that SPIFFS_open will find the file faster in
+// certain cases. It will make it a lot easier for spiffs to find files
+// opened frequently, reducing number of readings from the spi flash for
+// finding those files.
+// This will grow each fd by 6 bytes. If your files are opened in patterns
+// with a degree of temporal locality, the system is optimized.
+// Examples can be letting spiffs serve web content, where one file is the css.
+// The css is accessed for each html file that is opened, meaning it is
+// accessed almost every second time a file is opened. Another example could be
+// a log file that is often opened, written, and closed.
+// The size of the cache is number of given file descriptors, as it piggybacks
+// on the fd update mechanism. The cache lives in the closed file descriptors.
+// When closed, the fd know the whereabouts of the file. Instead of forgetting
+// this, the temporal cache will keep handling updates to that file even if the
+// fd is closed. If the file is opened again, the location of the file is found
+// directly. If all available descriptors become opened, all cache memory is
+// lost.
+#define SPIFFS_TEMPORAL_FD_CACHE                1
+
+// Temporal file cache hit score. Each time a file is opened, all cached files
+// will lose one point. If the opened file is found in cache, that entry will
+// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this
+// value for the specific access patterns of the application. However, it must
+// be between 1 (no gain for hitting a cached entry often) and 255.
+#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE         4
+
+// Enable to be able to map object indices to memory.
+// This allows for faster and more deterministic reading if cases of reading
+// large files and when changing file offset by seeking around a lot.
+// When mapping a file's index, the file system will be scanned for index pages
+// and the info will be put in memory provided by user. When reading, the
+// memory map can be looked up instead of searching for index pages on the
+// medium. This way, user can trade memory against performance.
+// Whole, parts of, or future parts not being written yet can be mapped. The
+// memory array will be owned by spiffs and updated accordingly during garbage
+// collecting or when modifying the indices. The latter is invoked by when the
+// file is modified in some way. The index buffer is tied to the file
+// descriptor.
+#define SPIFFS_IX_MAP                           1
+
+// 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.
+#ifdef CONFIG_SPIFFS_TEST_VISUALISATION
+#define SPIFFS_TEST_VISUALISATION               1
+#else
+#define SPIFFS_TEST_VISUALISATION               0
+#endif
+#if SPIFFS_TEST_VISUALISATION
+#ifndef spiffs_printf
+#define spiffs_printf(...)                ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
+#endif
+// spiffs_printf argument for a free page
+#define SPIFFS_TEST_VIS_FREE_STR          "_"
+// spiffs_printf argument for a deleted page
+#define SPIFFS_TEST_VIS_DELE_STR          "/"
+// spiffs_printf argument for an index page for given object id
+#define SPIFFS_TEST_VIS_INDX_STR(id)      "i"
+// spiffs_printf argument for a data page for given object id
+#define SPIFFS_TEST_VIS_DATA_STR(id)      "d"
+#endif
+
+// Types depending on configuration such as the amount of flash bytes
+// given to spiffs file system in total (spiffs_file_system_size),
+// the logical block size (log_block_size), and the logical page size
+// (log_page_size)
+
+// Block index type. Make sure the size of this type can hold
+// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size
+typedef u16_t spiffs_block_ix;
+// Page index type. Make sure the size of this type can hold
+// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size
+typedef u16_t spiffs_page_ix;
+// Object id type - most significant bit is reserved for index flag. Make sure the
+// size of this type can hold the highest object id on a full system,
+// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2
+typedef u16_t spiffs_obj_id;
+// Object span index type. Make sure the size of this type can
+// hold the largest possible span index on the system -
+// i.e. (spiffs_file_system_size / log_page_size) - 1
+typedef u16_t spiffs_span_ix;
+
+#endif /* SPIFFS_CONFIG_H_ */
diff --git a/components/spiffs/library/esp_spiffs.c b/components/spiffs/library/esp_spiffs.c
deleted file mode 100644
index b5d77a89..00000000
--- a/components/spiffs/library/esp_spiffs.c
+++ /dev/null
@@ -1,311 +0,0 @@
-#include <fcntl.h>
-#include <stdio.h>
-
-#include "esp_spiffs.h"
-#include "spi_flash.h"
-
-#define NUM_SYS_FD 3
-
-static spiffs fs;
-
-static u8_t *spiffs_work_buf;
-static u8_t *spiffs_fd_buf;
-static u8_t *spiffs_cache_buf;
-
-#define FLASH_UNIT_SIZE 4
-
-static s32_t esp_spiffs_readwrite(u32_t addr, u32_t size, u8_t *p, int write)
-{
-    /*
-     * With proper configurarion spiffs never reads or writes more than
-     * LOG_PAGE_SIZE
-     */
-
-    if (size > fs.cfg.log_page_size) {
-        printf("Invalid size provided to read/write (%d)\n\r", (int) size);
-        return SPIFFS_ERR_NOT_CONFIGURED;
-    }
-
-    char tmp_buf[fs.cfg.log_page_size + FLASH_UNIT_SIZE * 2];
-    u32_t aligned_addr = addr & (-FLASH_UNIT_SIZE);
-    u32_t aligned_size =
-        ((size + (FLASH_UNIT_SIZE - 1)) & -FLASH_UNIT_SIZE) + FLASH_UNIT_SIZE;
-
-    int res = spi_flash_read(aligned_addr, (u32_t *) tmp_buf, aligned_size);
-
-    if (res != 0) {
-        printf("spi_flash_read failed: %d (%d, %d)\n\r", res, (int) aligned_addr,
-               (int) aligned_size);
-        return res;
-    }
-
-    if (!write) {
-        memcpy(p, tmp_buf + (addr - aligned_addr), size);
-        return SPIFFS_OK;
-    }
-
-    memcpy(tmp_buf + (addr - aligned_addr), p, size);
-
-    res = spi_flash_write(aligned_addr, (u32_t *) tmp_buf, aligned_size);
-
-    if (res != 0) {
-//	    printf("spi_flash_write failed: %d (%d, %d)\n\r", res,
-//	              (int) aligned_addr, (int) aligned_size);
-        return res;
-    }
-
-    return SPIFFS_OK;
-}
-
-static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst)
-{
-    return esp_spiffs_readwrite(addr, size, dst, 0);
-}
-
-static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src)
-{
-    return esp_spiffs_readwrite(addr, size, src, 1);
-}
-
-static s32_t esp_spiffs_erase(u32_t addr, u32_t size)
-{
-    /*
-     * With proper configurarion spiffs always
-     * provides here sector address & sector size
-     */
-    if (size != fs.cfg.phys_erase_block || addr % fs.cfg.phys_erase_block != 0) {
-        printf("Invalid size provided to esp_spiffs_erase (%d, %d)\n\r",
-               (int) addr, (int) size);
-        return SPIFFS_ERR_NOT_CONFIGURED;
-    }
-
-    return spi_flash_erase_sector(addr / fs.cfg.phys_erase_block);
-}
-
-s32_t esp_spiffs_init(struct esp_spiffs_config *config)
-{
-    if (SPIFFS_mounted(&fs)) {
-        return SPIFFS_ERR_MOUNTED;
-    }
-
-    spiffs_config cfg;
-    s32_t ret;
-
-    cfg.phys_size = config->phys_size;
-    cfg.phys_addr = config->phys_addr;
-    cfg.phys_erase_block = config->phys_erase_block;
-    cfg.log_block_size = config->log_block_size;
-    cfg.log_page_size = config->log_page_size;
-
-    cfg.hal_read_f = esp_spiffs_read;
-    cfg.hal_write_f = esp_spiffs_write;
-    cfg.hal_erase_f = esp_spiffs_erase;
-
-    if (spiffs_work_buf != NULL) {
-        free(spiffs_work_buf);
-        spiffs_work_buf = NULL;
-    }
-    spiffs_work_buf = malloc(config->log_page_size * 2);
-
-    if (spiffs_work_buf == NULL) {
-        return -1;
-    }
-
-    if (spiffs_fd_buf != NULL) {
-        free(spiffs_fd_buf);
-        spiffs_fd_buf = NULL;
-    }
-    spiffs_fd_buf = malloc(config->fd_buf_size);
-
-    if (spiffs_fd_buf == NULL) {
-        free(spiffs_work_buf);
-        return -1;
-    }
-
-    if (spiffs_cache_buf != NULL) {
-        free(spiffs_cache_buf);
-        spiffs_cache_buf = NULL;
-    }
-    spiffs_cache_buf = malloc(config->cache_buf_size);
-
-    if (spiffs_cache_buf == NULL) {
-        free(spiffs_work_buf);
-        free(spiffs_fd_buf);
-        return -1;
-    }
-
-    ret =  SPIFFS_mount(&fs, &cfg, spiffs_work_buf,
-                        spiffs_fd_buf, config->fd_buf_size,
-                        spiffs_cache_buf, config->cache_buf_size,
-                        0);
-
-    if (ret == -1) {
-        free(spiffs_work_buf);
-        free(spiffs_fd_buf);
-        free(spiffs_cache_buf);
-    }
-
-    ret = SPIFFS_errno(&fs);
-
-    return ret;        
-}
-
-void esp_spiffs_deinit(u8_t format)
-{
-    if (SPIFFS_mounted(&fs)) {
-        SPIFFS_unmount(&fs);
-        free(spiffs_work_buf);
-        free(spiffs_fd_buf);
-        free(spiffs_cache_buf);
-    }
-    if (format) {
-        SPIFFS_format(&fs);
-    }
-}
-
-int _spiffs_open_r(struct _reent *r, const char *filename, int flags, int mode)
-{
-    spiffs_mode sm = 0;
-    int res;
-    int rw = (flags & 3);
-
-    if (rw == O_RDONLY || rw == O_RDWR) {
-        sm |= SPIFFS_RDONLY;
-    }
-
-    if (rw == O_WRONLY || rw == O_RDWR) {
-        sm |= SPIFFS_WRONLY;
-    }
-
-    if (flags & O_CREAT) {
-        sm |= SPIFFS_CREAT;
-    }
-
-    if (flags & O_TRUNC) {
-        sm |= SPIFFS_TRUNC;
-    }
-
-    if (flags & O_APPEND) {
-        sm |= SPIFFS_APPEND;
-    }
-
-    /* Supported in newer versions of SPIFFS. */
-    /* if (flags && O_EXCL) sm |= SPIFFS_EXCL; */
-    /* if (flags && O_DIRECT) sm |= SPIFFS_DIRECT; */
-
-    res = SPIFFS_open(&fs, (char *) filename, sm, 0);
-
-    if (res >= 0) {
-        res += NUM_SYS_FD;
-    }
-    else { 
-        res = SPIFFS_errno(&fs);
-    }
-    return res;
-}
-
-_ssize_t _spiffs_read_r(struct _reent *r, int fd, void *buf, size_t len)
-{
-
-    ssize_t res;
-
-    if (fd < NUM_SYS_FD) {
-        res = -1;
-    } else {
-        res = SPIFFS_read(&fs, fd - NUM_SYS_FD, buf, len);
-        if(res < 0) {
-            res = SPIFFS_errno(&fs);
-        }
-    }
-
-    return res;
-}
-
-_ssize_t _spiffs_write_r(struct _reent *r, int fd, void *buf, size_t len)
-{
-
-    if (fd < NUM_SYS_FD) {
-        return -1;
-    }
-
-    int res = SPIFFS_write(&fs, fd - NUM_SYS_FD, (char *) buf, len);
-    if(res < 0){
-        return SPIFFS_errno(&fs);
-    }
-    return res;
-}
-
-_off_t _spiffs_lseek_r(struct _reent *r, int fd, _off_t where, int whence)
-{
-
-    ssize_t res;
-
-    if (fd < NUM_SYS_FD) {
-        res = -1;
-    } else {
-        res = SPIFFS_lseek(&fs, fd - NUM_SYS_FD, where, whence);
-        if(res < 0) {
-            res = SPIFFS_errno(&fs);
-        }
-    }
-
-    return res;
-}
-
-int _spiffs_close_r(struct _reent *r, int fd)
-{
-
-    if (fd < NUM_SYS_FD) {
-        return -1;
-    }
-
-    SPIFFS_close(&fs, fd - NUM_SYS_FD);
-    return 0;
-}
-
-int _spiffs_rename_r(struct _reent *r, const char *from, const char *to)
-{
-
-    int res = SPIFFS_rename(&fs, (char *) from, (char *) to);
-    if(res < 0) {
-        res = SPIFFS_errno(&fs);
-    }
-    return res;
-}
-
-int _spiffs_unlink_r(struct _reent *r, const char *filename)
-{
-
-    int res = SPIFFS_remove(&fs, (char *) filename);
-    if(res < 0) {
-        res = SPIFFS_errno(&fs);
-    }
-    return res;
-}
-
-int _spiffs_fstat_r(struct _reent *r, int fd, struct stat *s)
-{
-
-    int res;
-    spiffs_stat ss;
-    memset(s, 0, sizeof(*s));
-
-    if (fd < NUM_SYS_FD) {
-        s->st_ino = fd;
-        s->st_rdev = fd;
-        s->st_mode = S_IFCHR | 0666;
-        return 0;
-    }
-
-    res = SPIFFS_fstat(&fs, fd - NUM_SYS_FD, &ss);
-
-    if (res < 0) {
-        return SPIFFS_errno(&fs);
-    }
-
-    s->st_ino = ss.obj_id;
-    s->st_mode = 0666;
-    s->st_nlink = 1;
-    s->st_size = ss.size;
-    return 0;
-}
diff --git a/components/spiffs/spiffs/.travis.yml b/components/spiffs/spiffs/.travis.yml
new file mode 100644
index 00000000..8c36dc57
--- /dev/null
+++ b/components/spiffs/spiffs/.travis.yml
@@ -0,0 +1,8 @@
+language: c
+
+compiler:
+  - gcc
+
+before_script:
+
+script: make all && make clean && make test && make build-all && make clean test FLAGS=-DSPIFFS_OBJ_META_LEN=8
diff --git a/components/spiffs/spiffs/FUZZING.md b/components/spiffs/spiffs/FUZZING.md
new file mode 100644
index 00000000..f5178005
--- /dev/null
+++ b/components/spiffs/spiffs/FUZZING.md
@@ -0,0 +1,47 @@
+# Fuzzing SPIFFS
+
+The SPIFFS test suite includes a test program designed for fuzzing with
+[AFL](http://lcamtuf.coredump.cx/afl/). This automatically exercises the 
+SPIFFS API and verifies that the file system does not crash or interact incorrectly
+with the flash chip. 
+
+There are two steps to fuzzing. The first is to build the test suite with
+the AFL version of gcc. The CC variable should point to your copy of afl-gcc.
+
+```
+make clean test CC=/usr/local/bin/afl-gcc
+```
+
+There is a new test `afl_test` that reads from stdin a list of commands
+and arguments. These are interpreted and executed on the API. The `afltests`
+directory contains a number of test cases that can be fed to the `afl_test` test.
+
+
+The second is to run this test suite under afl as follows (where findings is 
+the output directory):
+
+```
+afl-fuzz -i afltests -o findings ./build/linux_spiffs_test -f afl_test
+```
+
+This run will take hours (or days) and will (hopefully) not find any crashes.
+If a crash (or hang) is found, then the input file that caused the crash is 
+saved. This allows the specific test case to be debugged.
+
+## Reducing the size of the file
+
+AFL comes with `afl-tmin` which can reduce the size of the test input file to
+make it easier to debug.
+
+```
+afl-tmin -i findings/crashes/<somefile> -o smalltest -- build/linux_spiffs_test -f afl_test
+```
+
+This will write a short version of the testcase file to `smalltest`. This can then be
+fed into the test program for debugging:
+
+```
+build/linux_spiffs_test -f afl_test < smalltest
+```
+
+This should still crash, but allows it to be run under a debugger. 
diff --git a/components/spiffs/spiffs/LICENSE b/components/spiffs/spiffs/LICENSE
new file mode 100644
index 00000000..5fb2427b
--- /dev/null
+++ b/components/spiffs/spiffs/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2017 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
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/components/spiffs/spiffs/README.md b/components/spiffs/spiffs/README.md
new file mode 100644
index 00000000..64ffcc36
--- /dev/null
+++ b/components/spiffs/spiffs/README.md
@@ -0,0 +1,212 @@
+# SPIFFS (SPI Flash File System) 
+**V0.3.7**
+
+[![Build Status](https://travis-ci.org/pellepl/spiffs.svg?branch=master)](https://travis-ci.org/pellepl/spiffs)
+
+Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976 at gmail.com)
+
+For legal stuff, see [LICENSE](https://github.com/pellepl/spiffs/blob/master/LICENSE). Basically, you may do whatever you want with the source. Use, modify, sell, print it out, roll it and smoke it - as long as I won't be held responsible.
+
+Love to hear feedback though!
+
+
+## INTRODUCTION
+
+Spiffs is a file system intended for SPI NOR flash devices on embedded targets.
+
+Spiffs is designed with following characteristics in mind:
+ - Small (embedded) targets, sparse RAM without heap
+ - Only big areas of data (blocks) can be erased
+ - An erase will reset all bits in block to ones
+ - Writing pulls one to zeroes
+ - Zeroes can only be pulled to ones by erase
+ - Wear leveling
+
+
+## BUILDING
+
+`mkdir build; make`
+
+Otherwise, configure the `builddir` variable towards the top of `makefile` as something opposed to the default `build`. Sanity check on the host via `make test` and refer to `.travis.yml` for the official in-depth testing procedure. See the wiki for [integrating](https://github.com/pellepl/spiffs/wiki/Integrate-spiffs) spiffs into projects and [spiffsimg](https://github.com/nodemcu/nodemcu-firmware/tree/master/tools/spiffsimg) from [nodemcu](https://github.com/nodemcu) is a good example on the subject.
+
+
+## FEATURES
+
+What spiffs does:
+ - Specifically designed for low ram usage
+ - Uses statically sized ram buffers, independent of number of files
+ - Posix-like api: open, close, read, write, seek, stat, etc
+ - It can run on any NOR flash, not only SPI flash - theoretically also on embedded flash of a microprocessor
+ - Multiple spiffs configurations can run on same target - and even on same SPI flash device
+ - Implements static wear leveling 
+ - Built in file system consistency checks
+ - Highly configurable
+ 
+What spiffs does not:
+ - Presently, spiffs does not support directories. It produces a flat structure. Creating a file with path *tmp/myfile.txt* will create a file called *tmp/myfile.txt* instead of a *myfile.txt* under directory *tmp*. 
+ - It is not a realtime stack. One write operation might last much longer than another.
+ - Poor scalability. Spiffs is intended for small memory devices - the normal sizes for SPI flashes. Going beyond ~128Mbyte is probably a bad idea. This is a side effect of the design goal to use as little ram as possible.
+ - Presently, it does not detect or handle bad blocks.
+ - One configuration, one binary. There's no generic spiffs binary that handles all types of configurations.
+
+ 
+## MORE INFO 
+ 
+See the [wiki](https://github.com/pellepl/spiffs/wiki) for [configuring](https://github.com/pellepl/spiffs/wiki/Configure-spiffs), [integrating](https://github.com/pellepl/spiffs/wiki/Integrate-spiffs), [using](https://github.com/pellepl/spiffs/wiki/Using-spiffs), and [optimizing](https://github.com/pellepl/spiffs/wiki/Performance-and-Optimizing) spiffs.
+ 
+For design, see [docs/TECH_SPEC](https://github.com/pellepl/spiffs/blob/master/docs/TECH_SPEC).
+
+For a generic spi flash driver, see [this](https://github.com/pellepl/spiflash_driver).
+
+## HISTORY
+
+### 0.3.7
+- fixed prevent seeking to negative offsets #158
+- fixed file descriptor offsets not updated for multiple fds on same file #157
+- fixed cache page not closed for removed files #156
+- fixed a lseek bug when seeking exactly to end of a fully indexed first level LUT #148
+- fixed wear leveling issue #145
+- fixed attempt to write out of bounds in flash #130, 
+- set file offset when seeking over end #121 (thanks @sensslen)
+- fixed seeking in virgin files #120 (thanks @sensslen)
+- Optional file metadata #128 (thanks @cesanta)
+- AFL testing framework #100 #143 (thanks @pjsg)
+- Testframe updates
+
+New API functions:
+- `SPIFFS_update_meta, SPIFFS_fupdate_meta` - updates metadata for a file
+
+New config defines:
+- `SPIFFS_OBJ_META_LEN` - enable possibility to add extra metadata to files
+
+### 0.3.6
+- Fix range bug in index memory mapping #98
+- Add index memory mapping #97
+- Optimize SPIFFS_read for large files #96
+- Add temporal cache for opening files #95
+- More robust gc #93 (thanks @dismirlian)
+- Fixed a double write of same data in certain cache situations
+- Fixed an open bug in READ_ONLY builds
+- File not visible in SPIFFS_readdir #90 (thanks @benpicco-tmp)
+- Cache load code cleanup #92 (thanks @niclash)
+- Fixed lock/unlock asymmetry #88 #87 (thanks @JackJefferson, @dpruessner)
+- Testframe updates
+
+New API functions:
+- `SPIFFS_ix_map` - map index meta data to memory for a file
+- `SPIFFS_ix_unmap` - unmaps index meta data for a file
+- `SPIFFS_ix_remap` - changes file offset for index metadata map
+- `SPIFFS_bytes_to_ix_map_entries` - utility, get length of needed vector for given amount of bytes
+- `SPIFFS_ix_map_entries_to_bytes` - utility, get number of bytes a vector can represent given length
+ 
+New config defines:
+- `SPIFFS_IX_MAP` - enable possibility to map index meta data to memory for reading faster 
+- `SPIFFS_TEMPORAL_FD_CACHE` - enable temporal cache for opening files faster
+- `SPIFFS_TEMPORAL_CACHE_HIT_SCORE` - for tuning the temporal cache
+
+### 0.3.5
+- Fixed a bug in fs check
+- API returns actual error codes #84) (thanks @Nails)
+- Fix compiler warnings for non-gcc #83 #81 (thanks @Nails)
+- Unable to recover from full fs #82 (thanks @rojer)
+- Define SPIFFS_O_* flags #80
+- Problem with long filenames #79 (thanks @psjg)
+- Duplicate file name bug fix #74 (thanks @igrr)
+- SPIFFS_eof and SPIFFS_tell return wrong value #72 (thanks @ArtemPisarenko)
+- Bunch of testframe updates #77 #78 #86 (thanks @dpreussner, @psjg a.o)  
+
+### 0.3.4
+- Added user callback file func.
+- Fixed a stat bug with obj id.
+- SPIFFS_probe_fs added
+- Add possibility to compile a read-only version of spiffs
+- Make magic dependent on fs length, if needed (see #59 & #66) (thanks @hreintke)
+- Exposed SPIFFS_open_by_page_function
+- Zero-size file cannot be seek #57 (thanks @lishen2)
+- Add tell and eof functions #54 (thanks @raburton)
+- Make api string params const #53 (thanks @raburton)
+- Preserve user_data during mount() #51 (thanks @rojer)
+
+New API functions:
+- `SPIFFS_set_file_callback_func` - register a callback informing about file events
+- `SPIFFS_probe_fs` - probe a spi flash trying to figure out size of fs
+- `SPIFFS_open_by_page` - open a file by page index
+- `SPIFFS_eof` - checks if end of file is reached
+- `SPIFFS_tell` - returns current file offset
+
+New config defines:
+- `SPIFFS_READ_ONLY`
+- `SPIFFS_USE_MAGIC_LENGTH`
+
+### 0.3.3
+**Might not be compatible with 0.3.2 structures. See issue #40**
+- Possibility to add integer offset to file handles
+- Truncate function presumes too few free pages #49
+- Bug in truncate function #48 (thanks @PawelDefee)
+- Update spiffs_gc.c - remove unnecessary parameter (thanks @PawelDefee)
+- Update INTEGRATION docs (thanks @PawelDefee)
+- Fix pointer truncation in 64-bit platforms (thanks @igrr)
+- Zero-sized files cannot be read #44 (thanks @rojer)
+- (More) correct calculation of max_id in obj_lu_find #42 #41 (thanks @lishen2)
+- Check correct error code in obj_lu_find_free #41 (thanks @lishen2)
+- Moar comments for SPIFFS_lseek (thanks @igrr)
+- Fixed padding in spiffs_page_object_ix #40 (thanks @jmattsson @lishen2)
+- Fixed gc_quick test (thanks @jmattsson)
+- Add SPIFFS_EXCL flag #36 
+- SPIFFS_close may fail silently if cache is enabled #37 
+- User data in callbacks #34
+- Ignoring SINGLETON build in cache setup (thanks Luca)
+- Compilation error fixed #32 (thanks @chotasanjiv)
+- Align cand_scores (thanks @hefloryd)
+- Fix build warnings when SPIFFS_CACHE is 0 (thanks @ajaybhargav)
+
+New config defines:
+- `SPIFFS_FILEHDL_OFFSET`
+
+### 0.3.2
+- Limit cache size if too much cache is given (thanks pgeiem)
+- New feature - Controlled erase. #23
+- SPIFFS_rename leaks file descriptors #28 (thanks benpicco)
+- moved dbg print defines in test framework to params_test.h
+- lseek should return the resulting offset (thanks hefloryd)
+- fixed type on dbg ifdefs
+- silence warning about signed/unsigned comparison when spiffs_obj_id is 32 bit (thanks benpicco)
+- Possible error in test_spiffs.c #21 (thanks yihcdaso-yeskela)
+- Cache might writethrough too often #16
+- even moar testrunner updates
+- Test framework update and some added tests
+- Some thoughts for next gen
+- Test sigsevs when having too many sectors #13  (thanks alonewolfx2)
+- GC might be suboptimal #11
+- Fix eternal readdir when objheader at last block, last entry
+  
+New API functions:
+- `SPIFFS_gc_quick` - call a nonintrusive gc
+- `SPIFFS_gc` - call a full-scale intrusive gc
+
+### 0.3.1
+- Removed two return warnings, was too triggerhappy on release
+
+### 0.3.0
+- Added existing namecheck when creating files
+- Lots of static analysis bugs #6
+- Added rename func
+- Fix SPIFFS_read length when reading beyond file size
+- Added reading beyond file length testcase
+- Made build a bit more configurable
+- Changed name in spiffs from "errno" to "err_code" due to conflicts compiling in mingw
+- Improved GC checks, fixed an append bug, more robust truncate for very special case
+- GC checks preempts GC, truncate even less picky
+- Struct alignment needed for some targets, define in spiffs config #10
+- Spiffs filesystem magic, definable in config
+
+New config defines:
+- `SPIFFS_USE_MAGIC` - enable or disable magic check upon mount
+- `SPIFFS_ALIGNED_OBJECT_INDEX_TABLES` - alignment for certain targets
+
+New API functions:
+- `SPIFFS_rename` - rename files
+- `SPIFFS_clearerr` - clears last errno
+- `SPIFFS_info` - returns info on used and total bytes in fs
+- `SPIFFS_format` - formats the filesystem
+- `SPIFFS_mounted` - checks if filesystem is mounted
diff --git a/components/spiffs/spiffs/afltests/100 b/components/spiffs/spiffs/afltests/100
new file mode 100644
index 00000000..6bb22391
--- /dev/null
+++ b/components/spiffs/spiffs/afltests/100
@@ -0,0 +1,15 @@
+�5S-C4
+d5rh
+OlWkR#C4
+d5rh
+O4W4R4O4W4�C4#d5rh
+O4d5rh
+OlWkRh
+O4Y5rh
+OlWkR4C44R45��
+O4W4�4C4C4
+O4O4W4R4O4W4�C4#d5rh
+O4d5rh
+W4R45r�
+O4W4�4#d5rh
+rz
diff --git a/components/spiffs/spiffs/afltests/200 b/components/spiffs/spiffs/afltests/200
new file mode 100644
index 0000000000000000000000000000000000000000..90143128ac9ae6c0fd6240ef00d7f89a8e0c619d
GIT binary patch
literal 176
zcmYdl2sU&!;Yu+r%HZ<P3D0I=fb&ekO@d7Pf#fR_XA__-3y|ON--Igxq!yw&2q*)>
pTqQs;kO?Sylp&_Fgu{(QvH)lnNclvNaRlrEnd@xA3N)>X3ji}~F~0x+

literal 0
HcmV?d00001

diff --git a/components/spiffs/spiffs/afltests/a b/components/spiffs/spiffs/afltests/a
new file mode 100644
index 00000000..24e3a212
--- /dev/null
+++ b/components/spiffs/spiffs/afltests/a
@@ -0,0 +1,18 @@
+ b55
+O4W4R4C4D4
+b45
+d5rh
+O4W4R4f4C4
+baaU
+d5rh
+OaWaRafaCa
+cd5rh
+OaWaRafaCa
+O4S4W4R4C4
+d5rh
+O4W4S4R4C4
+d5rh
+O4W4R4S4C4
+d5rh
+O4W4R4C4
+d5rh
diff --git a/components/spiffs/spiffs/afltests/b b/components/spiffs/spiffs/afltests/b
new file mode 100644
index 00000000..1f957748
--- /dev/null
+++ b/components/spiffs/spiffs/afltests/b
@@ -0,0 +1,15 @@
+b55
+O4
+W?W?W?W?W?f4
+W<W=W>W:W;f4
+C4
+b45
+d5rh
+O4W?R4f4C4
+baa
+d5rh
+OaWaRafaCa
+d5rh
+OaWaRafaCa
+O4W?R4C4
+d5rh
diff --git a/components/spiffs/spiffs/docs/TECH_SPEC b/components/spiffs/spiffs/docs/TECH_SPEC
new file mode 100644
index 00000000..b4755a6d
--- /dev/null
+++ b/components/spiffs/spiffs/docs/TECH_SPEC
@@ -0,0 +1,239 @@
+* USING SPIFFS
+
+TODO
+
+
+* SPIFFS DESIGN
+
+Spiffs is inspired by YAFFS. However, YAFFS is designed for NAND flashes, and
+for bigger targets with much more ram. Nevertheless, many wise thoughts have
+been borrowed from YAFFS when writing spiffs. Kudos!
+
+The main complication writing spiffs was that it cannot be assumed the target
+has a heap. Spiffs must go along only with the work ram buffer given to it. 
+This forces extra implementation on many areas of spiffs.
+
+
+** SPI flash devices using NOR technology
+
+Below is a small description of how SPI flashes work internally. This is to
+give an understanding of the design choices made in spiffs.
+
+SPI flash devices are physically divided in blocks. On some SPI flash devices,
+blocks are further divided into sectors. Datasheets sometimes name blocks as 
+sectors and vice versa.
+
+Common memory capacaties for SPI flashes are 512kB up to 8MB of data, where
+blocks may be 64kB. Sectors can be e.g. 4kB, if supported. Many SPI flashes 
+have uniform block sizes, whereas others have non-uniform - the latter meaning 
+that e.g. the first 16 blocks are 4kB big, and the rest are 64kB.
+
+The entire memory is linear and can be read and written in random access. 
+Erasing can only be done block- or sectorwise; or by mass erase.
+
+SPI flashes can normally be erased from 100.000 up to 1.000.000 cycles before
+they fail.
+
+A clean SPI flash from factory have all bits in entire memory set to one. A
+mass erase will reset the device to this state. Block or sector erasing will
+put the all bits in the area given by the sector or block to ones. Writing to a
+NOR flash pulls ones to zeroes. Writing 0xFF to an address is simply a no-op. 
+
+Writing 0b10101010 to a flash address holding 0b00001111 will yield 0b00001010.
+
+This way of "write by nand" is used considerably in spiffs.
+
+Common characteristics of NOR flashes are quick reads, but slow writes.
+
+And finally, unlike NAND flashes, NOR flashes seem to not need any error 
+correction. They always write correctly I gather.
+
+
+** Spiffs logical structure
+
+Some terminology before proceeding. Physical blocks/sectors means sizes stated
+in the datasheet. Logical blocks and pages is something the integrator choose.
+
+
+** Blocks and pages
+
+Spiffs is allocated to a part or all of the memory of the SPI flash device. 
+This area is divided into logical blocks, which in turn are divided into 
+logical pages. The boundary of a logical block must coincide with one or more 
+physical blocks. The sizes for logical blocks and logical pages always remain
+the same, they are uniform.
+
+Example: non-uniform flash mapped to spiffs with 128kB logical blocks
+
+PHYSICAL FLASH BLOCKS               SPIFFS LOGICAL BLOCKS: 128kB
+
++-----------------------+   - - -   +-----------------------+
+| Block 1 : 16kB        |           | Block 1 : 128kB       |
++-----------------------+           |                       |
+| Block 2 : 16kB        |           |                       |
++-----------------------+           |                       |
+| Block 3 : 16kB        |           |                       |
++-----------------------+           |                       |
+| Block 4 : 16kB        |           |                       |
++-----------------------+           |                       |
+| Block 5 : 64kB        |           |                       |
++-----------------------+   - - -   +-----------------------+
+| Block 6 : 64kB        |           | Block 2 : 128kB       |
++-----------------------+           |                       |
+| Block 7 : 64kB        |           |                       |
++-----------------------+   - - -   +-----------------------+
+| Block 8 : 64kB        |           | Block 3 : 128kB       |
++-----------------------+           |                       |
+| Block 9 : 64kB        |           |                       |
++-----------------------+   - - -   +-----------------------+
+| ...                   |           | ...                   |
+
+A logical block is divided further into a number of logical pages. A page 
+defines the smallest data holding element known to spiffs. Hence, if a file
+is created being one byte big, it will occupy one page for index and one page
+for data - it will occupy 2 x size of a logical page on flash.
+So it seems it is good to select a small page size.
+
+Each page has a metadata header being normally 5 to 9 bytes. This said, a very
+small page size will make metadata occupy a lot of the memory on the flash. A
+page size of 64 bytes will waste 8-14% on metadata, while 256 bytes 2-4%.
+So it seems it is good to select a big page size.
+
+Also, spiffs uses a ram buffer being two times the page size. This ram buffer
+is used for loading and manipulating pages, but it is also used for algorithms 
+to find free file ids, scanning the file system, etc. Having too small a page
+size means less work buffer for spiffs, ending up in more reads operations and
+eventually gives a slower file system.
+
+Choosing the page size for the system involves many factors:
+ - How big is the logical block size
+ - What is the normal size of most files
+ - How much ram can be spent
+ - How much data (vs metadata) must be crammed into the file system
+ - How fast must spiffs be
+ - Other things impossible to find out
+ 
+So, chosing the Optimal Page Size (tm) seems tricky, to say the least. Don't 
+fret - there is no optimal page size. This varies from how the target will use
+spiffs. Use the golden rule:
+
+        ~~~   Logical Page Size = Logical Block Size / 256   ~~~
+
+This is a good starting point. The final page size can then be derived through 
+heuristical experimenting for us non-analytical minds.
+
+
+** Objects, indices and look-ups
+
+A file, or an object as called in spiffs, is identified by an object id. 
+Another YAFFS rip-off. This object id is a part of the page header. So, all 
+pages know to which object/file they belong - not counting the free pages.
+
+An object is made up of two types of pages: object index pages and data pages.
+Data pages contain the data written by user. Index pages contain metadata about
+the object, more specifically what data pages are part of the object.
+
+The page header also includes something called a span index. Let's say a file
+is written covering three data pages. The first data page will then have span 
+index 0, the second span index 1, and the last data page will have span index
+2. Simple as that. 
+
+Finally, each page header contain flags, telling if the page is used, 
+deleted, finalized, holds index or data, and more.
+
+Object indices also have span indices, where an object index with span index 0
+is referred to as the object index header. This page does not only contain 
+references to data pages, but also extra info such as object name, object size
+in bytes, flags for file or directory, etc.
+
+If one were to create a file covering three data pages, named e.g. 
+"spandex-joke.txt", given object id 12, it could look like this:
+
+PAGE 0  <things to be unveiled soon>
+
+PAGE 1  page header:   [obj_id:12  span_ix:0  flags:USED|DATA]
+        <first data page of joke>
+
+PAGE 2  page header:   [obj_id:12  span_ix:1  flags:USED|DATA]
+        <second data page of joke>
+
+PAGE 3  page header:   [obj_id:545 span_ix:13 flags:USED|DATA]
+        <some data belonging to object 545, probably not very amusing>
+
+PAGE 4  page header:   [obj_id:12  span_ix:2  flags:USED|DATA]
+        <third data page of joke>
+
+PAGE 5  page header:   [obj_id:12  span_ix:0  flags:USED|INDEX]
+        obj ix header: [name:spandex-joke.txt  size:600 bytes  flags:FILE] 
+        obj ix:        [1 2 4]
+        
+Looking in detail at page 5, the object index header page, the object index
+array refers to each data page in order, as mentioned before. The index of the
+object index array correlates with the data page span index.
+
+                            entry ix:  0 1 2
+                              obj ix: [1 2 4]
+                                       | | |
+    PAGE 1, DATA, SPAN_IX 0    --------/ | |
+      PAGE 2, DATA, SPAN_IX 1    --------/ |
+        PAGE 4, DATA, SPAN_IX 2    --------/
+        
+Things to be unveiled in page 0 - well.. Spiffs is designed for systems low on 
+ram. We cannot keep a dynamic list on the whereabouts of each object index 
+header so we can find a file fast. There might not even be a heap! But, we do 
+not want to scan all page headers on the flash to find the object index header.
+
+The first page(s) of each block contains the so called object look-up. These 
+are not normal pages, they do not have a header. Instead, they are arrays 
+pointing out what object-id the rest of all pages in the block belongs to.
+
+By this look-up, only the first page(s) in each block must to scanned to find 
+the actual page which contains the object index header of the desired object.
+
+The object lookup is redundant metadata. The assumption is that it presents 
+less overhead reading a full page of data to memory from each block and search
+that, instead of reading a small amount of data from each page (i.e. the page 
+header) in all blocks. Each read operation from SPI flash normally contains 
+extra data as the read command itself and the flash address. Also, depending on
+the underlying implementation, other criterions may need to be passed for each 
+read transaction, like mutexes and such.
+
+The veiled example unveiled would look like this, with some extra pages:
+
+PAGE 0  [  12   12  545   12   12   34   34    4    0    0    0    0 ...]
+PAGE 1  page header:   [obj_id:12  span_ix:0  flags:USED|DATA] ...
+PAGE 2  page header:   [obj_id:12  span_ix:1  flags:USED|DATA] ...
+PAGE 3  page header:   [obj_id:545 span_ix:13 flags:USED|DATA] ...
+PAGE 4  page header:   [obj_id:12  span_ix:2  flags:USED|DATA] ...
+PAGE 5  page header:   [obj_id:12  span_ix:0  flags:USED|INDEX] ...
+PAGE 6  page header:   [obj_id:34  span_ix:0  flags:USED|DATA] ...
+PAGE 7  page header:   [obj_id:34  span_ix:1  flags:USED|DATA] ...
+PAGE 8  page header:   [obj_id:4   span_ix:1  flags:USED|INDEX] ...
+PAGE 9  page header:   [obj_id:23  span_ix:0  flags:DELETED|INDEX] ...
+PAGE 10 page header:   [obj_id:23  span_ix:0  flags:DELETED|DATA] ...
+PAGE 11 page header:   [obj_id:23  span_ix:1  flags:DELETED|DATA] ...
+PAGE 12 page header:   [obj_id:23  span_ix:2  flags:DELETED|DATA] ...
+...
+
+Ok, so why are page 9 to 12 marked as 0 when they belong to object id 23? These
+pages are deleted, so this is marked both in page header flags and in the look
+up. This is an example where spiffs uses NOR flashes "nand-way" of writing.
+
+As a matter of fact, there are two object id's which are special:
+
+obj id 0 (all bits zeroes) - indicates a deleted page in object look up  
+obj id 0xff.. (all bits ones) - indicates a free page in object look up 
+
+Actually, the object id's have another quirk: if the most significant bit is
+set, this indicates an object index page. If the most significant bit is zero,
+this indicates a data page. So to be fully correct, page 0 in above example 
+would look like this:
+
+PAGE 0  [  12   12  545   12  *12   34   34   *4    0    0    0    0 ...]
+
+where the asterisk means the msb of the object id is set.
+
+This is another way to speed up the searches when looking for object indices.
+By looking on the object id's msb in the object lookup, it is also possible 
+to find out whether the page is an object index page or a data page.
+
diff --git a/components/spiffs/spiffs/docs/TODO b/components/spiffs/spiffs/docs/TODO
new file mode 100644
index 00000000..c947316a
--- /dev/null
+++ b/components/spiffs/spiffs/docs/TODO
@@ -0,0 +1,15 @@
+* 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.
\ No newline at end of file
diff --git a/components/spiffs/spiffs/files.mk b/components/spiffs/spiffs/files.mk
new file mode 100644
index 00000000..631ec7e1
--- /dev/null
+++ b/components/spiffs/spiffs/files.mk
@@ -0,0 +1,12 @@
+ifndef spiffs
+$(warn defaulting path to generic spiffs module, spiffs variable not set)
+spiffs = ../generic/spiffs
+endif
+FLAGS	+= -DCONFIG_BUILD_SPIFFS
+INC	+= -I${spiffs}/src
+CPATH	+= ${spiffs}/src
+CFILES	+= spiffs_nucleus.c
+CFILES	+= spiffs_gc.c
+CFILES	+= spiffs_hydrogen.c
+CFILES	+= spiffs_cache.c
+CFILES	+= spiffs_check.c
diff --git a/components/spiffs/spiffs/makefile b/components/spiffs/spiffs/makefile
new file mode 100644
index 00000000..355b4679
--- /dev/null
+++ b/components/spiffs/spiffs/makefile
@@ -0,0 +1,163 @@
+BINARY = linux_spiffs_test
+
+############
+#
+# Paths
+#
+############
+
+sourcedir = src
+builddir = build
+
+
+#############
+#
+# Build tools
+#
+#############
+
+CC ?= gcc
+LD ?= ld
+GDB ?= gdb
+OBJCOPY ?= objcopy
+OBJDUMP ?= objdump
+MKDIR ?= mkdir -p
+
+###############
+#
+# Files and libs
+#
+###############
+
+NO_TEST ?= 0
+CFLAGS = $(FLAGS)
+ifeq (1, $(strip $(NO_TEST)))
+CFILES_TEST = main.c
+CFLAGS += -DNO_TEST -Werror
+else
+CFILES_TEST = main.c \
+	test_spiffs.c \
+	test_dev.c \
+	test_check.c \
+	test_hydrogen.c \
+	test_bugreports.c \
+	testsuites.c \
+	testrunner.c
+CFLAGS += -D_SPIFFS_TEST
+endif
+include files.mk
+INCLUDE_DIRECTIVES = -I./${sourcedir} -I./${sourcedir}/default -I./${sourcedir}/test 
+COMPILEROPTIONS = $(INCLUDE_DIRECTIVES)
+
+COMPILEROPTIONS_APP = $(INCLUDE_DIRECTIVES) \
+-Wall -Wno-format-y2k -W -Wstrict-prototypes -Wmissing-prototypes \
+-Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch \
+-Wshadow -Wcast-align -Wchar-subscripts -Winline -Wnested-externs\
+-Wredundant-decls
+		
+############
+#
+# Tasks
+#
+############
+
+vpath %.c ${sourcedir} ${sourcedir}/default ${sourcedir}/test
+
+OBJFILES = $(CFILES:%.c=${builddir}/%.o)
+OBJFILES_TEST = $(CFILES_TEST:%.c=${builddir}/%.o)
+
+DEPFILES = $(CFILES:%.c=${builddir}/%.d) $(CFILES_TEST:%.c=${builddir}/%.d)
+
+ALLOBJFILES += $(OBJFILES) $(OBJFILES_TEST)
+
+DEPENDENCIES = $(DEPFILES) 
+
+# link object files, create binary
+$(BINARY): $(ALLOBJFILES)
+	@echo "... linking"
+	@${CC} $(LINKEROPTIONS) -o ${builddir}/$(BINARY) $(ALLOBJFILES) $(LIBS)
+ifeq (1, $(strip $(NO_TEST)))
+	@echo "size: `du -b ${builddir}/${BINARY} | sed 's/\([0-9]*\).*/\1/g '` bytes"
+endif
+
+
+-include $(DEPENDENCIES)	   	
+
+# compile c files
+$(OBJFILES) : ${builddir}/%.o:%.c
+		@echo "... compile $@"
+		@${CC} $(COMPILEROPTIONS_APP) $(CFLAGS) -g -c -o $@ $<
+
+$(OBJFILES_TEST) : ${builddir}/%.o:%.c
+		@echo "... compile $@"
+		@${CC} ${COMPILEROPTIONS} $(CFLAGS) -g -c -o $@ $<
+
+# make dependencies
+#		@echo "... depend $@"; 
+$(DEPFILES) : ${builddir}/%.d:%.c
+		@rm -f $@; \
+		${CC} $(COMPILEROPTIONS) -M $< > $@.$$$$; \
+		sed 's,\($*\)\.o[ :]*, ${builddir}/\1.o $@ : ,g' < $@.$$$$ > $@; \
+		rm -f $@.$$$$
+
+all: mkdirs $(BINARY) 
+
+mkdirs:
+	-@${MKDIR} ${builddir}
+	-@${MKDIR} test_data
+
+FILTER ?=
+
+test: $(BINARY)
+ifdef $(FILTER)
+		./build/$(BINARY)
+else
+		./build/$(BINARY) -f $(FILTER)
+endif
+
+test_failed: $(BINARY)
+		./build/$(BINARY) _tests_fail
+	
+clean:
+	@echo ... removing build files in ${builddir}
+	@rm -f ${builddir}/*.o
+	@rm -f ${builddir}/*.d
+	@rm -f ${builddir}/*.elf
+	
+ONOFF = 1 0
+OFFON = 0 1
+build-all:
+	@for rdonly in $(ONOFF); do \
+		for singleton in $(ONOFF); do \
+			for hal_cb_xtra in $(OFFON); do \
+				for cache in $(OFFON); do \
+					for magic in $(OFFON); do \
+						for temporal_cache in $(OFFON); do \
+  						for ix_map in $(OFFON); do \
+  							echo; \
+  							echo ============================================================; \
+  							echo SPIFFS_READ_ONLY=$$rdonly; \
+  							echo SPIFFS_SINGLETON=$$singleton; \
+  							echo SPIFFS_HAL_CALLBACK_EXTRA=$$hal_cb_xtra; \
+  							echo SPIFFS_CACHE, SPIFFS_CACHE_WR=$$cache; \
+  							echo SPIFFS_USE_MAGIC, SPIFFS_USE_MAGIC_LENGTH=$$magic; \
+  							echo SPIFFS_TEMPORAL_FD_CACHE=$$temporal_cache; \
+  							echo SPIFFS_IX_MAP=$$ix_map; \
+  							$(MAKE) clean && $(MAKE) FLAGS="\
+  								-DSPIFFS_HAL_CALLBACK_EXTRA=$$hal_cb_xtra \
+  								-DSPIFFS_SINGLETON=$$singleton \
+  								-DSPIFFS_CACHE=$$cache \
+  								-DSPIFFS_CACHE_WR=$$cache \
+  								-DSPIFFS_READ_ONLY=$$rdonly \
+  								-DSPIFFS_USE_MAGIC=$$magic \
+  								-DSPIFFS_USE_MAGIC_LENGTH=$$magic \
+  								-DSPIFFS_TEMPORAL_FD_CACHE=$$temporal_cache \
+  								-DSPIFFS_IX_MAP=$$ix_map \
+  								" NO_TEST=1; \
+  						done || exit 1; \
+						done \
+					done \
+				done \
+			done \
+		done \
+	done 
diff --git a/components/spiffs/include/spiffs/spiffs_config.h b/components/spiffs/spiffs/src/default/spiffs_config.h
similarity index 52%
rename from components/spiffs/include/spiffs/spiffs_config.h
rename to components/spiffs/spiffs/src/default/spiffs_config.h
index 5e826a65..ce562bfa 100644
--- a/components/spiffs/include/spiffs/spiffs_config.h
+++ b/components/spiffs/spiffs/src/default/spiffs_config.h
@@ -11,42 +11,76 @@
 // ----------- 8< ------------
 // Following includes are for the linux test build of spiffs
 // These may/should/must be removed/altered/replaced in your target
+#include "params_test.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stddef.h>
-#include <stdint.h>
-
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-
+#include <unistd.h>
+#ifdef _SPIFFS_TEST
+#include "testrunner.h"
+#endif
 // ----------- >8 ------------
 
-typedef uint8_t   u8_t;
-typedef int8_t    s8_t;
-typedef uint16_t  u16_t;
-typedef int16_t   s16_t;
-typedef uint32_t  u32_t;
-typedef int32_t   s32_t;
-
 // compile time switches
 
 // Set generic spiffs debug output call.
 #ifndef SPIFFS_DBG
-#define SPIFFS_DBG(...) //printf(__VA_ARGS__)
+#define SPIFFS_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
 #endif
 // Set spiffs debug output call for garbage collecting.
 #ifndef SPIFFS_GC_DBG
-#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__)
+#define SPIFFS_GC_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
 #endif
 // Set spiffs debug output call for caching.
 #ifndef SPIFFS_CACHE_DBG
-#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__)
+#define SPIFFS_CACHE_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
 #endif
 // Set spiffs debug output call for system consistency checks.
 #ifndef SPIFFS_CHECK_DBG
-#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__)
+#define SPIFFS_CHECK_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
 #endif
+// Set spiffs debug output call for all api invocations.
+#ifndef SPIFFS_API_DBG
+#define SPIFFS_API_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
+#endif
+
+
+
+// Defines spiffs debug print formatters
+// some general signed number
+#ifndef _SPIPRIi
+#define _SPIPRIi   "%d"
+#endif
+// address
+#ifndef _SPIPRIad
+#define _SPIPRIad  "%08x"
+#endif
+// block
+#ifndef _SPIPRIbl
+#define _SPIPRIbl  "%04x"
+#endif
+// page
+#ifndef _SPIPRIpg
+#define _SPIPRIpg  "%04x"
+#endif
+// span index
+#ifndef _SPIPRIsp
+#define _SPIPRIsp  "%04x"
+#endif
+// file descriptor
+#ifndef _SPIPRIfd
+#define _SPIPRIfd  "%d"
+#endif
+// file object id
+#ifndef _SPIPRIid
+#define _SPIPRIid  "%04x"
+#endif
+// file flags
+#ifndef _SPIPRIfl
+#define _SPIPRIfl  "%02x"
+#endif
+
 
 // Enable/disable API functions to determine exact number of bytes
 // for filedescriptor and cache buffers. Once decided for a configuration,
@@ -60,7 +94,6 @@ typedef int32_t   s32_t;
 #ifndef  SPIFFS_CACHE
 #define SPIFFS_CACHE                    1
 #endif
-
 #if SPIFFS_CACHE
 // Enables memory write caching for file descriptors in hydrogen
 #ifndef  SPIFFS_CACHE_WR
@@ -111,11 +144,27 @@ typedef int32_t   s32_t;
 #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
 
+// Maximum length of the metadata associated with an object.
+// Setting to non-zero value enables metadata-related API but also
+// changes the on-disk format, so the change is not backward-compatible.
+//
+// Do note: the meta length must never exceed
+// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64)
+//
+// This is derived from following:
+// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) +
+// spiffs_object_ix_header fields + at least some LUT entries)
+#ifndef SPIFFS_OBJ_META_LEN
+#define SPIFFS_OBJ_META_LEN             (0)
+#endif
+
 // Size of buffer allocated on stack used when copying data.
 // Lower value generates more read/writes. No meaning having it bigger
 // than logical page size.
@@ -131,6 +180,17 @@ typedef int32_t   s32_t;
 #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
 
@@ -143,7 +203,6 @@ typedef int32_t   s32_t;
 #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.
@@ -173,14 +232,90 @@ typedef int32_t   s32_t;
 
 // Enable this if your target needs aligned data for index tables
 #ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
-#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES       1
+#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
+
+// Enable this to add a temporal file cache using the fd buffer.
+// The effects of the cache is that SPIFFS_open will find the file faster in
+// certain cases. It will make it a lot easier for spiffs to find files
+// opened frequently, reducing number of readings from the spi flash for
+// finding those files.
+// This will grow each fd by 6 bytes. If your files are opened in patterns
+// with a degree of temporal locality, the system is optimized.
+// Examples can be letting spiffs serve web content, where one file is the css.
+// The css is accessed for each html file that is opened, meaning it is
+// accessed almost every second time a file is opened. Another example could be
+// a log file that is often opened, written, and closed.
+// The size of the cache is number of given file descriptors, as it piggybacks
+// on the fd update mechanism. The cache lives in the closed file descriptors.
+// When closed, the fd know the whereabouts of the file. Instead of forgetting
+// this, the temporal cache will keep handling updates to that file even if the
+// fd is closed. If the file is opened again, the location of the file is found
+// directly. If all available descriptors become opened, all cache memory is
+// lost.
+#ifndef SPIFFS_TEMPORAL_FD_CACHE
+#define SPIFFS_TEMPORAL_FD_CACHE              1
+#endif
+
+// Temporal file cache hit score. Each time a file is opened, all cached files
+// will lose one point. If the opened file is found in cache, that entry will
+// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this
+// value for the specific access patterns of the application. However, it must
+// be between 1 (no gain for hitting a cached entry often) and 255.
+#ifndef SPIFFS_TEMPORAL_CACHE_HIT_SCORE
+#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE       4
+#endif
+
+// Enable to be able to map object indices to memory.
+// This allows for faster and more deterministic reading if cases of reading
+// large files and when changing file offset by seeking around a lot.
+// When mapping a file's index, the file system will be scanned for index pages
+// and the info will be put in memory provided by user. When reading, the
+// memory map can be looked up instead of searching for index pages on the
+// medium. This way, user can trade memory against performance.
+// Whole, parts of, or future parts not being written yet can be mapped. The
+// memory array will be owned by spiffs and updated accordingly during garbage
+// collecting or when modifying the indices. The latter is invoked by when the
+// file is modified in some way. The index buffer is tied to the file
+// descriptor.
+#ifndef SPIFFS_IX_MAP
+#define SPIFFS_IX_MAP                         1
 #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
-#define SPIFFS_TEST_VISUALISATION         0
+#define SPIFFS_TEST_VISUALISATION         1
 #endif
 #if SPIFFS_TEST_VISUALISATION
 #ifndef spiffs_printf
diff --git a/components/spiffs/include/spiffs/spiffs.h b/components/spiffs/spiffs/src/spiffs.h
similarity index 59%
rename from components/spiffs/include/spiffs/spiffs.h
rename to components/spiffs/spiffs/src/spiffs.h
index 2bf0a6e9..534c3df8 100644
--- a/components/spiffs/include/spiffs/spiffs.h
+++ b/components/spiffs/spiffs/src/spiffs.h
@@ -5,8 +5,6 @@
  *      Author: petera
  */
 
-
-
 #ifndef SPIFFS_H_
 #define SPIFFS_H_
 #if defined(__cplusplus)
@@ -49,6 +47,22 @@ 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_IX_MAP_UNMAPPED      -10037
+#define SPIFFS_ERR_IX_MAP_MAPPED        -10038
+#define SPIFFS_ERR_IX_MAP_BAD_RANGE     -10039
+
+#define SPIFFS_ERR_SEEK_BOUNDS          -10040
+
+
 #define SPIFFS_ERR_INTERNAL             -10050
 
 #define SPIFFS_ERR_TEST                 -10100
@@ -63,12 +77,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,16 +113,34 @@ 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(...) \
-    print(__VA_ARGS__)
+    printf(__VA_ARGS__)
 #endif
 #ifndef SPIFFS_GC_DBG
 #define SPIFFS_GC_DBG(...) printf(__VA_ARGS__)
@@ -108,18 +154,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 +220,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 +283,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,7 +298,11 @@ 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];
+#if SPIFFS_OBJ_META_LEN
+  u8_t meta[SPIFFS_OBJ_META_LEN];
+#endif
 } spiffs_stat;
 
 struct spiffs_dirent {
@@ -243,6 +311,9 @@ struct spiffs_dirent {
   spiffs_obj_type type;
   u32_t size;
   spiffs_page_ix pix;
+#if SPIFFS_OBJ_META_LEN
+  u8_t meta[SPIFFS_OBJ_META_LEN];
+#endif
 };
 
 typedef struct {
@@ -251,8 +322,57 @@ typedef struct {
   int entry;
 } spiffs_DIR;
 
+#if SPIFFS_IX_MAP
+
+typedef struct {
+  // buffer with looked up data pixes
+  spiffs_page_ix *map_buf;
+  // precise file byte offset
+  u32_t offset;
+  // start data span index of lookup buffer
+  spiffs_span_ix start_spix;
+  // end data span index of lookup buffer
+  spiffs_span_ix end_spix;
+} spiffs_ix_map;
+
+#endif
+
 // 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 +406,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 +425,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 +434,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 +471,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 +487,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 +502,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 +524,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 +532,25 @@ 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);
+
+#if SPIFFS_OBJ_META_LEN
+/**
+ * Updates file's metadata
+ * @param fs            the file system struct
+ * @param path          path to the file
+ * @param meta          new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
+ */
+s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta);
+
+/**
+ * Updates file's metadata
+ * @param fs            the file system struct
+ * @param fh            file handle of the file
+ * @param meta          new metadata. must be SPIFFS_OBJ_META_LEN bytes long.
+ */
+s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta);
+#endif
 
 /**
  * Returns last error of last file operation.
@@ -419,7 +573,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 +595,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 +674,115 @@ 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_IX_MAP
+
+/**
+ * Maps the first level index lookup to a given memory map.
+ * This will make reading big files faster, as the memory map will be used for
+ * looking up data pages instead of searching for the indices on the physical
+ * medium. When mapping, all affected indicies are found and the information is
+ * copied to the array.
+ * Whole file or only parts of it may be mapped. The index map will cover file
+ * contents from argument offset until and including arguments (offset+len).
+ * It is valid to map a longer range than the current file size. The map will
+ * then be populated when the file grows.
+ * On garbage collections and file data page movements, the map array will be
+ * automatically updated. Do not tamper with the map array, as this contains
+ * the references to the data pages. Modifying it from outside will corrupt any
+ * future readings using this file descriptor.
+ * The map will no longer be used when the file descriptor closed or the file
+ * is unmapped.
+ * This can be useful to get faster and more deterministic timing when reading
+ * large files, or when seeking and reading a lot within a file.
+ * @param fs      the file system struct
+ * @param fh      the file handle of the file to map
+ * @param map     a spiffs_ix_map struct, describing the index map
+ * @param offset  absolute file offset where to start the index map
+ * @param len     length of the mapping in actual file bytes
+ * @param map_buf the array buffer for the look up data - number of required
+ *                elements in the array can be derived from function
+ *                SPIFFS_bytes_to_ix_map_entries given the length
+ */
+s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map,
+    u32_t offset, u32_t len, spiffs_page_ix *map_buf);
+
+/**
+ * Unmaps the index lookup from this filehandle. All future readings will
+ * proceed as normal, requiring reading of the first level indices from
+ * physical media.
+ * The map and map buffer given in function SPIFFS_ix_map will no longer be
+ * referenced by spiffs.
+ * It is not strictly necessary to unmap a file before closing it, as closing
+ * a file will automatically unmap it.
+ * @param fs      the file system struct
+ * @param fh      the file handle of the file to unmap
+ */
+s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh);
+
+/**
+ * Moves the offset for the index map given in function SPIFFS_ix_map. Parts or
+ * all of the map buffer will repopulated.
+ * @param fs      the file system struct
+ * @param fh      the mapped file handle of the file to remap
+ * @param offset  new absolute file offset where to start the index map
+ */
+s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offs);
+
+/**
+ * Utility function to get number of spiffs_page_ix entries a map buffer must
+ * contain on order to map given amount of file data in bytes.
+ * See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes.
+ * @param fs      the file system struct
+ * @param bytes   number of file data bytes to map
+ * @return        needed number of elements in a spiffs_page_ix array needed to
+ *                map given amount of bytes in a file
+ */
+s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes);
+
+/**
+ * Utility function to amount of file data bytes that can be mapped when
+ * mapping a file with buffer having given number of spiffs_page_ix entries.
+ * See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries.
+ * @param fs      the file system struct
+ * @param map_page_ix_entries   number of entries in a spiffs_page_ix array
+ * @return        amount of file data in bytes that can be mapped given a map
+ *                buffer having given amount of spiffs_page_ix entries
+ */
+s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries);
+
+#endif // SPIFFS_IX_MAP
+
+
 #if SPIFFS_TEST_VISUALISATION
 /**
  * Prints out a visualization of the filesystem.
diff --git a/components/spiffs/library/spiffs_cache.c b/components/spiffs/spiffs/src/spiffs_cache.c
similarity index 76%
rename from components/spiffs/library/spiffs_cache.c
rename to components/spiffs/spiffs/src/spiffs_cache.c
index 4fde4d36..e7cd4b73 100644
--- a/components/spiffs/library/spiffs_cache.c
+++ b/components/spiffs/spiffs/src/spiffs_cache.c
@@ -20,12 +20,12 @@ static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix)
     if ((cache->cpage_use_map & (1<<i)) &&
         (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
         cp->pix == pix ) {
-      SPIFFS_CACHE_DBG("CACHE_GET: have cache page %i for %04x\n", i, pix);
+      //SPIFFS_CACHE_DBG("CACHE_GET: have cache page "_SPIPRIi" for "_SPIPRIpg"\n", i, pix);
       cp->last_access = cache->last_access;
       return cp;
     }
   }
-  //SPIFFS_CACHE_DBG("CACHE_GET: no cache for %04x\n", pix);
+  //SPIFFS_CACHE_DBG("CACHE_GET: no cache for "_SPIPRIpg"\n", pix);
   return 0;
 }
 
@@ -39,17 +39,20 @@ 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);
+      SPIFFS_CACHE_DBG("CACHE_FREE: write cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix);
+      res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem);
     }
 
-    cp->flags = 0;
-    cache->cpage_use_map &= ~(1 << ix);
-
+#if SPIFFS_CACHE_WR
     if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) {
-      SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i objid %04x\n", ix, cp->obj_id);
-    } else {
-      SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i pix %04x\n", ix, cp->pix);
+      SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" objid "_SPIPRIid"\n", ix, cp->obj_id);
+    } else
+#endif
+    {
+      SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix);
     }
+    cache->cpage_use_map &= ~(1 << ix);
+    cp->flags = 0;
   }
 
   return res;
@@ -98,7 +101,7 @@ static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) {
       spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
       cache->cpage_use_map |= (1<<i);
       cp->last_access = cache->last_access;
-      SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page %i\n", i);
+      //SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i);
       return cp;
     }
   }
@@ -130,38 +133,50 @@ s32_t spiffs_phys_rd(
   spiffs_cache_page *cp =  spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr));
   cache->last_access++;
   if (cp) {
+    // we've already got one, you see
 #if SPIFFS_CACHE_STATS
     fs->cache_hits++;
 #endif
     cp->last_access = cache->last_access;
+    u8_t *mem =  spiffs_get_cache_page(fs, cache, cp->ix);
+    _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
   } 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++;
 #endif
+    // this operation will always free one cache page (unless all already free),
+    // the result code stems from the write operation of the possibly freed cache page
     res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
+
     cp = spiffs_cache_page_allocate(fs);
     if (cp) {
       cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
       cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
-    }
+      SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for pix "_SPIPRIpg "\n", cp->ix, cp->pix);
 
-    s32_t res2 = fs->cfg.hal_read_f(
-        addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
-        SPIFFS_CFG_LOG_PAGE_SZ(fs),
-        spiffs_get_cache_page(fs, cache, cp->ix));
-    if (res2 != SPIFFS_OK) {
-      res = res2;
+      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));
+      if (res2 != SPIFFS_OK) {
+        // honor read failure before possible write failure (bad idea?)
+        res = res2;
+      }
+      u8_t *mem =  spiffs_get_cache_page(fs, cache, cp->ix);
+      _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
+    } else {
+      // this will never happen, last resort for sake of symmetry
+      s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst);
+      if (res2 != SPIFFS_OK) {
+        // honor read failure before possible write failure (bad idea?)
+        res = res2;
+      }
     }
   }
-  u8_t *mem =  spiffs_get_cache_page(fs, cache, cp->ix);
-  memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
   return res;
 }
 
@@ -186,24 +201,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);
-    memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len);
+    _SPIFFS_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);
   }
 }
 
@@ -245,6 +260,7 @@ spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) {
   cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR;
   cp->obj_id = fd->obj_id;
   fd->cache_page = cp;
+  SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for fd "_SPIPRIfd ":"_SPIPRIid "\n", cp->ix, fd->file_nbr, fd->obj_id);
   return cp;
 }
 
@@ -288,7 +304,7 @@ void spiffs_cache_init(spiffs *fs) {
 
   cache.cpage_use_map = 0xffffffff;
   cache.cpage_use_mask = cache_mask;
-  memcpy(fs->cache, &cache, sizeof(spiffs_cache));
+  _SPIFFS_MEMCPY(fs->cache, &cache, sizeof(spiffs_cache));
 
   spiffs_cache *c = spiffs_get_cache(fs);
 
diff --git a/components/spiffs/library/spiffs_check.c b/components/spiffs/spiffs/src/spiffs_check.c
similarity index 83%
rename from components/spiffs/library/spiffs_check.c
rename to components/spiffs/spiffs/src/spiffs_check.c
index 2180a2a1..dde85eff 100644
--- a/components/spiffs/library/spiffs_check.c
+++ b/components/spiffs/spiffs/src/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,
@@ -166,7 +182,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
   if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) ||
       ((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) {
     // look up entry deleted / free but used in page header
-    SPIFFS_CHECK_DBG("LU: pix %04x deleted/free in lu but not on page\n", cur_pix);
+    SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" deleted/free in lu but not on page\n", cur_pix);
     *reload_lu = 1;
     delete_page = 1;
     if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) {
@@ -183,20 +199,20 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
           // copy page to new place and re-write the object index to new place
           spiffs_page_ix new_pix;
           res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
-          SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting %04x to new page %04x\n", cur_pix, new_pix);
+          SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix);
           SPIFFS_CHECK_RES(res);
           *reload_lu = 1;
-          SPIFFS_CHECK_DBG("LU: FIXUP: %04x rewritten to %04x, affected objix_pix %04x\n", cur_pix, new_pix, objix_pix);
+          SPIFFS_CHECK_DBG("LU: FIXUP: "_SPIPRIpg" rewritten to "_SPIPRIpg", affected objix_pix "_SPIPRIpg"\n", cur_pix, new_pix, objix_pix);
           res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix);
           if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
             // index bad also, cannot mend this file
-            SPIFFS_CHECK_DBG("LU: FIXUP: index bad %i, cannot mend!\n", res);
+            SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
             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);
         }
@@ -213,10 +229,10 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
           // got a data page also, assume lu corruption only, rewrite to new page
           spiffs_page_ix new_pix;
           res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
-          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_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\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);
@@ -226,7 +242,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
   if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) {
     // look up entry used
     if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) {
-      SPIFFS_CHECK_DBG("LU: pix %04x differ in obj_id lu:%04x ph:%04x\n", cur_pix, lu_obj_id, p_hdr->obj_id);
+      SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" differ in obj_id lu:"_SPIPRIid" ph:"_SPIPRIid"\n", cur_pix, lu_obj_id, p_hdr->obj_id);
       delete_page = 1;
       if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 ||
           (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) ||
@@ -249,12 +265,12 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
               res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix);
               if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
                 // index bad also, cannot mend this file
-                SPIFFS_CHECK_DBG("LU: FIXUP: index bad %i, cannot mend!\n", res);
+                SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
                 res = spiffs_page_delete(fs, new_pix);
                 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);
             }
@@ -305,8 +321,8 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
               //   rewrite as obj_id_ph
               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);
+              SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid" to pix "_SPIPRIpg"\n", cur_pix, new_ph.obj_id, new_pix);
+              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) ||
@@ -314,8 +330,8 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
               //   got a data page for look up obj id
               //   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);
+              SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid"\n", cur_pix, new_ph.obj_id);
+              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;
@@ -328,7 +344,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
       }
     } else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) ||
         ((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) {
-      SPIFFS_CHECK_DBG("LU: %04x lu/page index marking differ\n", cur_pix);
+      SPIFFS_CHECK_DBG("LU: "_SPIPRIpg" lu/page index marking differ\n", cur_pix);
       spiffs_page_ix data_pix, objix_pix_d;
       // see if other data page exists for given obj id and span index
       res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix);
@@ -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);
@@ -386,10 +402,10 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
       }
     }
     else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) {
-      SPIFFS_CHECK_DBG("LU: pix %04x busy in lu but deleted on page\n", cur_pix);
+      SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy in lu but deleted on page\n", cur_pix);
       delete_page = 1;
     } else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) {
-      SPIFFS_CHECK_DBG("LU: pix %04x busy but not final\n", cur_pix);
+      SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy but not final\n", cur_pix);
       // page can be removed if not referenced by object index
       *reload_lu = 1;
       res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
@@ -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),
@@ -417,8 +433,8 @@ 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);
+    SPIFFS_CHECK_DBG("LU: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix);
+    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;
 }
@@ -506,14 +522,17 @@ static s32_t spiffs_page_consistency_check_i(spiffs *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 "_SPIPRIpg", block "_SPIPRIbl" of pix "_SPIPRIpg", block "_SPIPRIbl"\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,
@@ -570,7 +589,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
                 || (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) {
 
               // bad reference
-              SPIFFS_CHECK_DBG("PA: pix %04x bad pix / LU referenced from page %04x\n",
+              SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg"x bad pix / LU referenced from page "_SPIPRIpg"\n",
                   rpix, cur_pix);
               // check for data page elsewhere
               spiffs_page_ix data_pix;
@@ -589,20 +608,20 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
                 new_ph.span_ix = data_spix_offset + i;
                 res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix);
                 SPIFFS_CHECK_RES(res);
-                SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ %04x\n", data_pix);
+                SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ "_SPIPRIpg"\n", data_pix);
               }
               // remap index
-              SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix %04x\n", cur_pix);
+              SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix "_SPIPRIpg"\n", cur_pix);
               res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG,
                   data_spix_offset + i, data_pix, cur_pix);
               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);
+                SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend - delete object\n", res);
+                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;
@@ -621,7 +640,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
                   rp_hdr.span_ix != data_spix_offset + i ||
                   (rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) !=
                       (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) {
-               SPIFFS_CHECK_DBG("PA: pix %04x has inconsistent page header ix id/span:%04x/%04x, ref id/span:%04x/%04x flags:%02x\n",
+               SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" has inconsistent page header ix id/span:"_SPIPRIid"/"_SPIPRIsp", ref id/span:"_SPIPRIid"/"_SPIPRIsp" flags:"_SPIPRIfl"\n",
                     rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i,
                     rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags);
                // try finding correct page
@@ -635,23 +654,23 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
                SPIFFS_CHECK_RES(res);
                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);
+                 SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id "_SPIPRIid"\n", p_hdr.obj_id);
+                 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;
                } else {
                  // found it, so rewrite index
-                 SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix %04x, rewrite ix pix %04x id %04x\n",
+                 SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix "_SPIPRIpg", rewrite ix pix "_SPIPRIpg" id "_SPIPRIid"\n",
                      data_pix, cur_pix, p_hdr.obj_id);
                  res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix);
                  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);
+                   SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
+                   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;
@@ -662,14 +681,14 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
                 const u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits);
                 const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits;
                 if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) {
-                  SPIFFS_CHECK_DBG("PA: pix %04x multiple referenced from page %04x\n",
+                  SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" multiple referenced from page "_SPIPRIpg"\n",
                       rpix, cur_pix);
                   // Here, we should have fixed all broken references - getting this means there
                   // must be multiple files with same object id. Only solution is to delete
                   // the object which is referring to this page
-                  SPIFFS_CHECK_DBG("PA: FIXUP: removing object %04x and page %04x\n",
+                  SPIFFS_CHECK_DBG("PA: FIXUP: removing object "_SPIPRIid" and page "_SPIPRIpg"\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
@@ -706,7 +725,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
           if (bitmask == 0x1) {
 
             // 001
-            SPIFFS_CHECK_DBG("PA: pix %04x USED, UNREFERENCED, not index\n", cur_pix);
+            SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, UNREFERENCED, not index\n", cur_pix);
 
             u8_t rewrite_ix_to_this = 0;
             u8_t delete_page = 0;
@@ -722,7 +741,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
               if (((rpix == (spiffs_page_ix)-1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) {
                 // pointing to a bad page altogether, rewrite index to this
                 rewrite_ix_to_this = 1;
-                SPIFFS_CHECK_DBG("PA: corresponding ref is bad: %04x, rewrite to this %04x\n", rpix, cur_pix);
+                SPIFFS_CHECK_DBG("PA: corresponding ref is bad: "_SPIPRIpg", rewrite to this "_SPIPRIpg"\n", rpix, cur_pix);
               } else {
                 // pointing to something else, check what
                 spiffs_page_header rp_hdr;
@@ -733,12 +752,12 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
                     ((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) ==
                         (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) {
                   // pointing to something else valid, just delete this page then
-                  SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: %04x, delete this %04x\n", rpix, cur_pix);
+                  SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: "_SPIPRIpg", delete this "_SPIPRIpg"\n", rpix, cur_pix);
                   delete_page = 1;
                 } else {
                   // pointing to something weird, update index to point to this page instead
                   if (rpix != cur_pix) {
-                    SPIFFS_CHECK_DBG("PA: corresponding ref is weird: %04x %s%s%s%s, rewrite this %04x\n", rpix,
+                    SPIFFS_CHECK_DBG("PA: corresponding ref is weird: "_SPIPRIpg" %s%s%s%s, rewrite this "_SPIPRIpg"\n", rpix,
                         (rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ",
                             (rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ",
                                 (rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "",
@@ -751,32 +770,32 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
                 }
               }
             } else if (res == SPIFFS_ERR_NOT_FOUND) {
-              SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete %04x\n", cur_pix);
+              SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete "_SPIPRIpg"\n", cur_pix);
               delete_page = 1;
               res = SPIFFS_OK;
             }
 
             if (rewrite_ix_to_this) {
               // if pointing to invalid page, redirect index to this page
-              SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id %04x data spix %04x to point to this pix: %04x\n",
+              SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id "_SPIPRIid" data spix "_SPIPRIsp" to point to this pix: "_SPIPRIpg"\n",
                   p_hdr.obj_id, p_hdr.span_ix, cur_pix);
               res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix);
               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);
+                SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
+                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);
+              SPIFFS_CHECK_DBG("PA: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix);
+              CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
               res = spiffs_page_delete(fs, cur_pix);
             }
             SPIFFS_CHECK_RES(res);
@@ -784,7 +803,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
           if (bitmask == 0x2) {
 
             // 010
-            SPIFFS_CHECK_DBG("PA: pix %04x FREE, REFERENCED, not index\n", cur_pix);
+            SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, not index\n", cur_pix);
 
             // no op, this should be taken care of when checking valid references
           }
@@ -794,7 +813,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
           if (bitmask == 0x4) {
 
             // 100
-            SPIFFS_CHECK_DBG("PA: pix %04x FREE, unreferenced, INDEX\n", cur_pix);
+            SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, unreferenced, INDEX\n", cur_pix);
 
             // this should never happen, major fubar
           }
@@ -804,20 +823,22 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
           if (bitmask == 0x6) {
 
             // 110
-            SPIFFS_CHECK_DBG("PA: pix %04x FREE, REFERENCED, INDEX\n", cur_pix);
+            SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, INDEX\n", cur_pix);
 
             // no op, this should be taken care of when checking valid references
           }
           if (bitmask == 0x7) {
 
             // 111
-            SPIFFS_CHECK_DBG("PA: pix %04x USED, REFERENCED, INDEX\n", cur_pix);
+            SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, REFERENCED, INDEX\n", cur_pix);
 
             // no op, this should be taken care of when checking valid references
           }
         }
       }
     }
+
+    SPIFFS_CHECK_DBG("PA: processed "_SPIPRIpg", restart "_SPIPRIi"\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)) {
@@ -877,9 +898,9 @@ static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id o
     if (p_hdr.span_ix == 0 &&
         (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) ==
         (SPIFFS_PH_FLAG_DELET)) {
-      SPIFFS_CHECK_DBG("IX: pix %04x, obj id:%04x spix:%04x header not fully deleted - deleting\n",
+      SPIFFS_CHECK_DBG("IX: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" 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;
@@ -933,9 +954,9 @@ 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",
+        SPIFFS_CHECK_DBG("IX: FIXUP: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" 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);
       }
@@ -958,16 +979,17 @@ s32_t spiffs_object_index_consistency_check(spiffs *fs) {
   // a reachable/unreachable object id.
   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
diff --git a/components/spiffs/library/spiffs_gc.c b/components/spiffs/spiffs/src/spiffs_gc.c
similarity index 75%
rename from components/spiffs/library/spiffs_gc.c
rename to components/spiffs/spiffs/src/spiffs_gc.c
index 5752e708..db1af4cc 100644
--- a/components/spiffs/library/spiffs_gc.c
+++ b/components/spiffs/spiffs/src/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.
@@ -9,7 +11,7 @@ static s32_t spiffs_gc_erase_block(
     spiffs_block_ix bix) {
   s32_t res;
 
-  SPIFFS_GC_DBG("gc: erase block %i\n", bix);
+  SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix);
   res = spiffs_erase_block(fs, bix);
   SPIFFS_CHECK_RES(res);
 
@@ -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
@@ -120,19 +122,19 @@ s32_t spiffs_gc_check(
 
   u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
 //  if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
-//    SPIFFS_GC_DBG("gc: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
+//    SPIFFS_GC_DBG("gc: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
 //    return SPIFFS_ERR_FULL;
 //  }
   if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) {
-    SPIFFS_GC_DBG("gc_check: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
+    SPIFFS_GC_DBG("gc_check: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
     return SPIFFS_ERR_FULL;
   }
 
   do {
-    SPIFFS_GC_DBG("\ngc_check #%i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n",
+    SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\n",
         tries,
         fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
-        len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs));
+        len, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs)));
 
     spiffs_block_ix *cands;
     int count;
@@ -150,13 +152,13 @@ s32_t spiffs_gc_check(
 #endif
     cand = cands[0];
     fs->cleaning = 1;
-    //printf("gcing: cleaning block %i\n", cand);
+    //SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand);
     res = spiffs_gc_clean(fs, cand);
     fs->cleaning = 0;
     if (res < 0) {
-      SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
+      SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
     } else {
-      SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res);
+      SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
     }
     SPIFFS_CHECK_RES(res);
 
@@ -186,7 +188,7 @@ s32_t spiffs_gc_check(
     res = SPIFFS_ERR_FULL;
   }
 
-  SPIFFS_GC_DBG("gc_check: finished, %i dirty, blocks %i free, %i pages free, %i tries, res %i\n",
+  SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\n",
       fs->stats_p_allocated + fs->stats_p_deleted,
       fs->free_blocks, free_pages, tries, res);
 
@@ -224,7 +226,7 @@ s32_t spiffs_gc_erase_page_stats(
     } // per entry
     obj_lookup_page++;
   } // per object lookup page
-  SPIFFS_GC_DBG("gc_check: wipe pallo:%i pdele:%i\n", allo, dele);
+  SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi" pdele:"_SPIPRIi"\n", allo, dele);
   fs->stats_p_allocated -= allo;
   fs->stats_p_deleted -= dele;
   return res;
@@ -249,10 +251,12 @@ s32_t spiffs_gc_find_candidate(
   memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
 
   // divide up work area into block indices and scores
-  // todo alignment?
   spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
   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*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1));
+
   *block_candidates = cand_blocks;
 
   int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
@@ -290,7 +294,7 @@ s32_t spiffs_gc_find_candidate(
 
     // calculate score and insert into candidate table
     // stoneage sort, but probably not so many blocks
-    if (res == SPIFFS_OK && deleted_pages_in_block > 0) {
+    if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) {
       // read erase count
       spiffs_obj_id erase_count;
       res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
@@ -310,7 +314,7 @@ s32_t spiffs_gc_find_candidate(
           used_pages_in_block * SPIFFS_GC_HEUR_W_USED +
           erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE);
       int cand_ix = 0;
-      SPIFFS_GC_DBG("gc_check: bix:%i del:%i use:%i score:%i\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
+      SPIFFS_GC_DBG("gc_check: bix:"_SPIPRIbl" del:"_SPIPRIi" use:"_SPIPRIi" score:"_SPIPRIi"\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
       while (cand_ix < max_candidates) {
         if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
           cand_blocks[cand_ix] = cur_block;
@@ -352,6 +356,7 @@ typedef struct {
   spiffs_obj_id cur_obj_id;
   spiffs_span_ix cur_objix_spix;
   spiffs_page_ix cur_objix_pix;
+  spiffs_page_ix cur_data_pix;
   int stored_scan_entry_index;
   u8_t obj_id_found;
 } spiffs_gc;
@@ -371,15 +376,16 @@ typedef struct {
 //
 s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
   s32_t res = SPIFFS_OK;
-  int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
+  const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
+  // this is the global localizer being pushed and popped
   int cur_entry = 0;
   spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
-  spiffs_gc gc;
+  spiffs_gc gc; // our stack frame/state
   spiffs_page_ix cur_pix = 0;
   spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
   spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
 
-  SPIFFS_GC_DBG("gc_clean: cleaning block %i\n", bix);
+  SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix);
 
   memset(&gc, 0, sizeof(spiffs_gc));
   gc.state = FIND_OBJ_DATA;
@@ -388,12 +394,12 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
     // move free cursor to next block, cannot use free pages from the block we want to clean
     fs->free_cursor_block_ix = (bix+1)%fs->block_count;
     fs->free_cursor_obj_lu_entry = 0;
-    SPIFFS_GC_DBG("gc_clean: move free cursor to block %i\n", fs->free_cursor_block_ix);
+    SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix);
   }
 
   while (res == SPIFFS_OK && gc.state != FINISHED) {
-    SPIFFS_GC_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry);
-    gc.obj_id_found = 0;
+    SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry);
+    gc.obj_id_found = 0; // reset (to no found data page)
 
     // scan through lookup pages
     int obj_lookup_page = cur_entry / entries_per_page;
@@ -404,7 +410,7 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
       res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
           0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
           SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
-      // check each entry
+      // check each object lookup entry
       while (scan && res == SPIFFS_OK &&
           cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
         spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
@@ -413,21 +419,26 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
         // act upon object id depending on gc state
         switch (gc.state) {
         case FIND_OBJ_DATA:
+          // find a data page
           if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
               ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
-            SPIFFS_GC_DBG("gc_clean: FIND_DATA state:%i - found obj id %04x\n", gc.state, obj_id);
+            // found a data page, stop scanning and handle in switch case below
+            SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id);
             gc.obj_id_found = 1;
             gc.cur_obj_id = obj_id;
+            gc.cur_data_pix = cur_pix;
             scan = 0;
           }
           break;
         case MOVE_OBJ_DATA:
+          // evacuate found data pages for corresponding object index we have in memory,
+          // update memory representation
           if (obj_id == gc.cur_obj_id) {
             spiffs_page_header p_hdr;
             res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
                 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
             SPIFFS_CHECK_RES(res);
-            SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page %04x:%04x @ %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
+            SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix);
             if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) {
               SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
             } else {
@@ -435,7 +446,7 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
               if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
                 // move page
                 res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
-                SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix %04x:%04x page %04x to %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
+                SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix);
                 SPIFFS_CHECK_RES(res);
                 // move wipes obj_lu, reload it
                 res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
@@ -443,8 +454,10 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
                     SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
                 SPIFFS_CHECK_RES(res);
               } else {
-                // page is deleted but not deleted in lookup, scrap it
-                SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
+                // page is deleted but not deleted in lookup, scrap it -
+                // might seem unnecessary as we will erase this block, but
+                // we might get aborted
+                SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
                 res = spiffs_page_delete(fs, cur_pix);
                 SPIFFS_CHECK_RES(res);
                 new_data_pix = SPIFFS_OBJ_ID_FREE;
@@ -453,16 +466,17 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
               if (gc.cur_objix_spix == 0) {
                 // update object index header page
                 ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix;
-                SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
+                SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
               } else {
                 // update object index page
                 ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix;
-                SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
+                SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix));
               }
             }
           }
           break;
         case MOVE_OBJ_IX:
+          // find and evacuate object index pages
           if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
               (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
             // found an index object id
@@ -475,20 +489,24 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
             if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
               // move page
               res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
-              SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix %04x:%04x page %04x to %04x\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
+              SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix);
               SPIFFS_CHECK_RES(res);
-              spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, obj_id, p_hdr.span_ix, new_pix, 0);
+              spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr,
+                  SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0);
               // move wipes obj_lu, reload it
               res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
                   0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page),
                   SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
               SPIFFS_CHECK_RES(res);
             } else {
-              // page is deleted but not deleted in lookup, scrap it
-              SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix);
+              // page is deleted but not deleted in lookup, scrap it -
+              // might seem unnecessary as we will erase this block, but
+              // we might get aborted
+              SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix);
               res = spiffs_page_delete(fs, cur_pix);
               if (res == SPIFFS_OK) {
-                spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
+                spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0,
+                    SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
               }
             }
             SPIFFS_CHECK_RES(res);
@@ -497,72 +515,92 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
         default:
           scan = 0;
           break;
-        }
+        } // switch gc state
         cur_entry++;
       } // per entry
-      obj_lookup_page++;
+      obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop
     } // per object lookup page
-
     if (res != SPIFFS_OK) break;
 
     // state finalization and switch
     switch (gc.state) {
     case FIND_OBJ_DATA:
       if (gc.obj_id_found) {
+        // handle found data page -
         // find out corresponding obj ix page and load it to memory
         spiffs_page_header p_hdr;
         spiffs_page_ix objix_pix;
-        gc.stored_scan_entry_index = cur_entry;
-        cur_entry = 0;
+        gc.stored_scan_entry_index = cur_entry; // push cursor
+        cur_entry = 0; // restart scan from start
         gc.state = MOVE_OBJ_DATA;
         res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
             0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
         SPIFFS_CHECK_RES(res);
         gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
-        SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:%04x\n", gc.cur_objix_spix);
+        SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix);
         res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix);
+        if (res == SPIFFS_ERR_NOT_FOUND) {
+          // on borked systems we might get an ERR_NOT_FOUND here -
+          // this is handled by simply deleting the page as it is not referenced
+          // from anywhere
+          SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix);
+          res = spiffs_page_delete(fs, gc.cur_data_pix);
+          SPIFFS_CHECK_RES(res);
+          // then we restore states and continue scanning for data pages
+          cur_entry = gc.stored_scan_entry_index; // pop cursor
+          gc.state = FIND_OBJ_DATA;
+          break; // done
+        }
         SPIFFS_CHECK_RES(res);
-        SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page %04x\n", objix_pix);
+        SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix);
         res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
             0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
         SPIFFS_CHECK_RES(res);
+        // cannot allow a gc if the presumed index in fact is no index, a
+        // check must run or lot of data may be lost
         SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix);
         gc.cur_objix_pix = objix_pix;
       } else {
+        // no more data pages found, passed thru all block, start evacuating object indices
         gc.state = MOVE_OBJ_IX;
         cur_entry = 0; // restart entry scan index
       }
       break;
     case MOVE_OBJ_DATA: {
-      // store modified objix (hdr) page
+      // store modified objix (hdr) page residing in memory now that all
+      // data pages belonging to this object index and residing in the block
+      // we want to evacuate
       spiffs_page_ix new_objix_pix;
       gc.state = FIND_OBJ_DATA;
-      cur_entry = gc.stored_scan_entry_index;
+      cur_entry = gc.stored_scan_entry_index; // pop cursor
       if (gc.cur_objix_spix == 0) {
         // store object index header page
-        res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, &new_objix_pix);
-        SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, %04x:%04x\n", new_objix_pix, 0);
+        res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix);
+        SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0);
         SPIFFS_CHECK_RES(res);
       } else {
         // store object index page
         res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix);
-        SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, %04x:%04x\n", new_objix_pix, objix->p_hdr.span_ix);
+        SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix);
         SPIFFS_CHECK_RES(res);
-        spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
+        spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work,
+            SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
       }
     }
     break;
     case MOVE_OBJ_IX:
+      // scanned thru all block, no more object indices found - our work here is done
       gc.state = FINISHED;
       break;
     default:
       cur_entry = 0;
       break;
-    }
-    SPIFFS_GC_DBG("gc_clean: state-> %i\n", gc.state);
+    } // switch gc.state
+    SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state);
   } // while state != FINISHED
 
 
   return res;
 }
 
+#endif // !SPIFFS_READ_ONLY
diff --git a/components/spiffs/library/spiffs_hydrogen.c b/components/spiffs/spiffs/src/spiffs_hydrogen.c
similarity index 52%
rename from components/spiffs/library/spiffs_hydrogen.c
rename to components/spiffs/spiffs/src/spiffs_hydrogen.c
index 977c0039..235aaaa6 100644
--- a/components/spiffs/library/spiffs_hydrogen.c
+++ b/components/spiffs/spiffs/src/spiffs_hydrogen.c
@@ -8,7 +8,9 @@
 #include "spiffs.h"
 #include "spiffs_nucleus.h"
 
+#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 +28,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 +55,48 @@ 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) {
+  SPIFFS_API_DBG("%s\n", __func__);
+  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) {
+  SPIFFS_API_DBG("%s "
+                 " sz:"_SPIPRIi " logpgsz:"_SPIPRIi " logblksz:"_SPIPRIi " perasz:"_SPIPRIi
+                 " addr:"_SPIPRIad
+                 " fdsz:"_SPIPRIi " cachesz:"_SPIPRIi
+                 "\n",
+                 __func__,
+                 SPIFFS_CFG_PHYS_SZ(fs),
+                 SPIFFS_CFG_LOG_PAGE_SZ(fs),
+                 SPIFFS_CFG_LOG_BLOCK_SZ(fs),
+                 SPIFFS_CFG_PHYS_ERASE_SZ(fs),
+                 SPIFFS_CFG_PHYS_ADDR(fs),
+                 fd_space_size, cache_size);
+  void *user_data;
   SPIFFS_LOCK(fs);
+  user_data = fs->user_data;
   memset(fs, 0, sizeof(spiffs));
-  memcpy(&fs->cfg, config, sizeof(spiffs_config));
+  _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)];
   memset(fd_space, 0, fd_space_size);
-  // align fd_space pointer to pointer size byte boundary, below is safe
+  // 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 +104,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 +115,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
 
@@ -107,14 +134,14 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
   res = spiffs_obj_lu_scan(fs);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
-  SPIFFS_DBG("page index byte len:         %i\n", SPIFFS_CFG_LOG_PAGE_SZ(fs));
-  SPIFFS_DBG("object lookup pages:         %i\n", SPIFFS_OBJ_LOOKUP_PAGES(fs));
-  SPIFFS_DBG("page pages per block:        %i\n", SPIFFS_PAGES_PER_BLOCK(fs));
-  SPIFFS_DBG("page header length:          %i\n", sizeof(spiffs_page_header));
-  SPIFFS_DBG("object header index entries: %i\n", SPIFFS_OBJ_HDR_IX_LEN(fs));
-  SPIFFS_DBG("object index entries:        %i\n", SPIFFS_OBJ_IX_LEN(fs));
-  SPIFFS_DBG("available file descriptors:  %i\n", fs->fd_count);
-  SPIFFS_DBG("free blocks:                 %i\n", fs->free_blocks);
+  SPIFFS_DBG("page index byte len:         "_SPIPRIi"\n", (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs));
+  SPIFFS_DBG("object lookup pages:         "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_LOOKUP_PAGES(fs));
+  SPIFFS_DBG("page pages per block:        "_SPIPRIi"\n", (u32_t)SPIFFS_PAGES_PER_BLOCK(fs));
+  SPIFFS_DBG("page header length:          "_SPIPRIi"\n", (u32_t)sizeof(spiffs_page_header));
+  SPIFFS_DBG("object header index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_HDR_IX_LEN(fs));
+  SPIFFS_DBG("object index entries:        "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_IX_LEN(fs));
+  SPIFFS_DBG("available file descriptors:  "_SPIPRIi"\n", (u32_t)fs->fd_count);
+  SPIFFS_DBG("free blocks:                 "_SPIPRIi"\n", (u32_t)fs->free_blocks);
 
   fs->check_cb_f = check_cb_f;
 
@@ -126,6 +153,7 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
 }
 
 void SPIFFS_unmount(spiffs *fs) {
+  SPIFFS_API_DBG("%s\n", __func__);
   if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return;
   SPIFFS_LOCK(fs);
   u32_t i;
@@ -149,46 +177,74 @@ s32_t SPIFFS_errno(spiffs *fs) {
 }
 
 void SPIFFS_clearerr(spiffs *fs) {
+  SPIFFS_API_DBG("%s\n", __func__);
   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) {
+  SPIFFS_API_DBG("%s '%s'\n", __func__, path);
+#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, 0, 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) {
+  SPIFFS_API_DBG("%s '%s' "_SPIPRIfl "\n", __func__, path, flags);
   (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;
 
-  s32_t res = spiffs_fd_find_new(fs, &fd);
+#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, path);
   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 +252,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, 0, 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,29 +270,32 @@ 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) {
+  SPIFFS_API_DBG("%s '%s':"_SPIPRIid " "_SPIPRIfl "\n", __func__, e->name, e->obj_id, flags);
   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);
+  s32_t res = spiffs_fd_find_new(fs, &fd, 0);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
   res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode);
@@ -243,22 +303,71 @@ 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 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) {
+spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) {
+  SPIFFS_API_DBG("%s "_SPIPRIpg " "_SPIPRIfl "\n", __func__, page_ix, flags);
+  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, 0);
+  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 SPIFFS_FH_OFFS(fs, fd->file_nbr);
+}
+
+static s32_t spiffs_hydro_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
   SPIFFS_LOCK(fs);
@@ -266,14 +375,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 +421,17 @@ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
   return len;
 }
 
+s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
+  SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len);
+  s32_t res = spiffs_hydro_read(fs, fh, buf, len);
+  if (res == SPIFFS_ERR_END_OF_OBJECT) {
+    res = 0;
+  }
+  return res;
+}
+
+
+#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 +453,14 @@ 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) {
+  SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, 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 +469,18 @@ 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 +489,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 +503,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;
@@ -377,13 +514,13 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
             offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page
         {
           // boundary violation, write back cache first and allocate new
-          SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, boundary viol, offs:%i size:%i\n",
+          SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", boundary viol, offs:"_SPIPRIi" size:"_SPIPRIi"\n",
               fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
           res = spiffs_hydro_write(fs, fd,
               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;
@@ -395,26 +532,37 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
         if (fd->cache_page) {
           fd->cache_page->offset = offset;
           fd->cache_page->size = 0;
-          SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page %i for fd %i:%04x\n",
+          SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid"\n",
               fd->cache_page->ix, fd->file_nbr, fd->obj_id);
         }
       }
 
       if (fd->cache_page) {
         u32_t offset_in_cpage = offset - fd->cache_page->offset;
-        SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page %i for fd %i:%04x, offs %i:%i len %i\n",
+        SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", offs "_SPIPRIi":"_SPIPRIi" len "_SPIPRIi"\n",
             fd->cache_page->ix, fd->file_nbr, fd->obj_id,
             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);
-        memcpy(&cpage_data[offset_in_cpage], buf, len);
+#ifdef _SPIFFS_TEST
+        {
+          intptr_t __a1 = (u8_t*)&cpage_data[offset_in_cpage]-(u8_t*)cache;
+          intptr_t __a2 = (u8_t*)&cpage_data[offset_in_cpage]+len-(u8_t*)cache;
+          intptr_t __b = sizeof(spiffs_cache) + cache->cpage_count * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs));
+          if (__a1 > __b || __a2 > __b) {
+            printf("FATAL OOB: CACHE_WR: memcpy to cache buffer ixs:%4ld..%4ld of %4ld\n", __a1, __a2, __b);
+            ERREXIT();
+          }
+        }
+#endif
+        _SPIFFS_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;
@@ -423,58 +571,65 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
       // big write, no need to cache it - but first check if there is a cached write already
       if (fd->cache_page) {
         // write back cache first
-        SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, big write, offs:%i size:%i\n",
+        SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", big write, offs:"_SPIPRIi" size:"_SPIPRIi"\n",
             fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
         res = spiffs_hydro_write(fs, fd,
             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);
-        res = spiffs_hydro_write(fs, fd, buf, offset, len);
-        SPIFFS_API_CHECK_RES(fs, res);
+        SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+        // data written below
       }
     }
   }
 #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) {
+  SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " %s\n", __func__, fh, offs, (const char* []){"SET","CUR","END","???"}[MIN(whence,3)]);
   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(fs, res);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
 #if SPIFFS_CACHE_WR
   spiffs_fflush_cache(fs, fh);
 #endif
 
+  s32_t file_size = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size;
+
   switch (whence) {
   case SPIFFS_SEEK_CUR:
     offs = fd->fdoffset+offs;
     break;
   case SPIFFS_SEEK_END:
-    offs = (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size) + offs;
+    offs = file_size + offs;
     break;
   }
-
-  if (offs > (s32_t)fd->size) {
+  if (offs < 0) {
+    SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_SEEK_BOUNDS);
+  }
+  if (offs > file_size) {
+    fd->fdoffset = file_size;
     res = SPIFFS_ERR_END_OF_OBJECT;
   }
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
-  spiffs_span_ix data_spix = offs / SPIFFS_DATA_PAGE_SIZE(fs);
+  spiffs_span_ix data_spix = (offs > 0 ? (offs-1) : 0) / SPIFFS_DATA_PAGE_SIZE(fs);
   spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
   if (fd->cursor_objix_spix != objix_spix) {
     spiffs_page_ix pix;
@@ -491,19 +646,27 @@ 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) {
+  SPIFFS_API_DBG("%s '%s'\n", __func__, 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;
   spiffs_page_ix pix;
   s32_t res;
 
-  res = spiffs_fd_find_new(fs, &fd);
+  res = spiffs_fd_find_new(fs, &fd, 0);
   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 +686,26 @@ 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) {
+  SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, 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 +721,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 +738,31 @@ 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);
+#if SPIFFS_OBJ_META_LEN
+  _SPIFFS_MEMCPY(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN);
+#endif
 
   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_DBG("%s '%s'\n", __func__, path);
   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);
@@ -593,6 +773,7 @@ s32_t SPIFFS_stat(spiffs *fs, char *path, spiffs_stat *s) {
 }
 
 s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) {
+  SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh);
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
   SPIFFS_LOCK(fs);
@@ -600,6 +781,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,21 +798,24 @@ 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);
     }
     if (fd->cache_page) {
-      SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, flush, offs:%i size:%i\n",
+      SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", flush, offs:"_SPIPRIi" size:"_SPIPRIi"\n",
           fd->cache_page->ix, fd->file_nbr,  fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
       res = spiffs_hydro_write(fs, fd,
           spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
@@ -645,13 +830,17 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) {
 
   return res;
 }
+#endif
 
 s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) {
+  SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, 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 +849,48 @@ 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;
-  }
-
-  if (!SPIFFS_CHECK_MOUNT(fs)) {
-    fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
-    return;
-  }
-  SPIFFS_LOCK(fs);
-
-#if SPIFFS_CACHE
-  spiffs_fflush_cache(fs, fh);
-#endif
-  spiffs_fd_return(fs, fh);
-
-  SPIFFS_UNLOCK(fs);
-}
-
-s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) {
+s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) {
+  SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh);
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
+
+  s32_t res = SPIFFS_OK;
+  SPIFFS_LOCK(fs);
+
+  fh = SPIFFS_FH_UNOFFS(fs, fh);
+#if SPIFFS_CACHE
+  res = spiffs_fflush_cache(fs, fh);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+#endif
+  res = spiffs_fd_return(fs, fh);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+  SPIFFS_UNLOCK(fs);
+
+  return res;
+}
+
+s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) {
+  SPIFFS_API_DBG("%s %s %s\n", __func__, old_path, 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) {
@@ -699,7 +898,7 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) {
   }
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
-  res = spiffs_fd_find_new(fs, &fd);
+  res = spiffs_fd_find_new(fs, &fd, 0);
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
 
   res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0);
@@ -708,7 +907,50 @@ 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, 0, &pix_dummy);
+#if SPIFFS_TEMPORAL_FD_CACHE
+  if (res == SPIFFS_OK) {
+    spiffs_fd_temporal_cache_rehash(fs, old_path, new_path);
+  }
+#endif
+
+  spiffs_fd_return(fs, fd->file_nbr);
+
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+  SPIFFS_UNLOCK(fs);
+
+  return res;
+#endif // SPIFFS_READ_ONLY
+}
+
+#if SPIFFS_OBJ_META_LEN
+s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) {
+#if SPIFFS_READ_ONLY
+  (void)fs; (void)name; (void)meta;
+  return SPIFFS_ERR_RO_NOT_IMPL;
+#else
+  SPIFFS_API_CHECK_CFG(fs);
+  SPIFFS_API_CHECK_MOUNT(fs);
+  SPIFFS_LOCK(fs);
+
+  spiffs_page_ix pix, pix_dummy;
+  spiffs_fd *fd;
+
+  s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)name, &pix);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+  res = spiffs_fd_find_new(fs, &fd, 0);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+  res = spiffs_object_open_by_page(fs, pix, fd, 0, 0);
+  if (res != SPIFFS_OK) {
+    spiffs_fd_return(fs, fd->file_nbr);
+  }
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+  res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta,
       0, &pix_dummy);
 
   spiffs_fd_return(fs, fd->file_nbr);
@@ -718,9 +960,45 @@ 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) {
+s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) {
+#if SPIFFS_READ_ONLY
+  (void)fs; (void)fh; (void)meta;
+  return SPIFFS_ERR_RO_NOT_IMPL;
+#else
+  SPIFFS_API_CHECK_CFG(fs);
+  SPIFFS_API_CHECK_MOUNT(fs);
+  SPIFFS_LOCK(fs);
+
+  s32_t res;
+  spiffs_fd *fd;
+  spiffs_page_ix pix_dummy;
+
+  fh = SPIFFS_FH_UNOFFS(fs, fh);
+  res = spiffs_fd_get(fs, fh, &fd);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+  if ((fd->flags & SPIFFS_O_WRONLY) == 0) {
+    res = SPIFFS_ERR_NOT_WRITABLE;
+    SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+  }
+
+  res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta,
+      0, &pix_dummy);
+
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+  SPIFFS_UNLOCK(fs);
+
+  return res;
+#endif // SPIFFS_READ_ONLY
+}
+#endif // SPIFFS_OBJ_META_LEN
+
+spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) {
+  SPIFFS_API_DBG("%s\n", __func__);
   (void)name;
 
   if (!SPIFFS_CHECK_CFG((fs))) {
@@ -744,9 +1022,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 ||
@@ -760,26 +1038,29 @@ static s32_t spiffs_read_dir_v(
   if (res != SPIFFS_OK) return res;
   if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) &&
       objix_hdr.p_hdr.span_ix == 0 &&
-      (objix_hdr.p_hdr.flags& (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) ==
+      (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;
     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;
+#if SPIFFS_OBJ_META_LEN
+    _SPIFFS_MEMCPY(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN);
+#endif
     return SPIFFS_OK;
   }
-
   return SPIFFS_VIS_COUNTINUE;
 }
 
 struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
+  SPIFFS_API_DBG("%s\n", __func__);
   if (!SPIFFS_CHECK_MOUNT(d->fs)) {
     d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
     return 0;
   }
-  SPIFFS_LOCK(fs);
+  SPIFFS_LOCK(d->fs);
 
   spiffs_block_ix bix;
   int entry;
@@ -799,21 +1080,28 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
   if (res == SPIFFS_OK) {
     d->block = bix;
     d->entry = entry + 1;
+    e->obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG;
     ret = e;
   } else {
     d->fs->err_code = res;
   }
-  SPIFFS_UNLOCK(fs);
+  SPIFFS_UNLOCK(d->fs);
   return ret;
 }
 
 s32_t SPIFFS_closedir(spiffs_DIR *d) {
+  SPIFFS_API_DBG("%s\n", __func__);
   SPIFFS_API_CHECK_CFG(d->fs);
   SPIFFS_API_CHECK_MOUNT(d->fs);
   return 0;
 }
 
 s32_t SPIFFS_check(spiffs *fs) {
+  SPIFFS_API_DBG("%s\n", __func__);
+#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);
@@ -829,9 +1117,11 @@ 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) {
+  SPIFFS_API_DBG("%s\n", __func__);
   s32_t res = SPIFFS_OK;
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
@@ -856,6 +1146,11 @@ s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) {
 }
 
 s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) {
+  SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, 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);
@@ -866,10 +1161,16 @@ s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) {
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
   SPIFFS_UNLOCK(fs);
   return 0;
+#endif // SPIFFS_READ_ONLY
 }
 
 
 s32_t SPIFFS_gc(spiffs *fs, u32_t size) {
+  SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, size);
+#if SPIFFS_READ_ONLY
+  (void)fs; (void)size;
+  return SPIFFS_ERR_RO_NOT_IMPL;
+#else
   s32_t res;
   SPIFFS_API_CHECK_CFG(fs);
   SPIFFS_API_CHECK_MOUNT(fs);
@@ -880,8 +1181,199 @@ s32_t SPIFFS_gc(spiffs *fs, u32_t size) {
   SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
   SPIFFS_UNLOCK(fs);
   return 0;
+#endif // SPIFFS_READ_ONLY
 }
 
+s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) {
+  SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, 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;
+  res = spiffs_fd_get(fs, fh, &fd);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+#if SPIFFS_CACHE_WR
+  res = spiffs_fflush_cache(fs, fh);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+#endif
+
+  res = (fd->fdoffset >= (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size));
+
+  SPIFFS_UNLOCK(fs);
+  return res;
+}
+
+s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) {
+  SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, 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;
+  res = spiffs_fd_get(fs, fh, &fd);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+#if SPIFFS_CACHE_WR
+  res = spiffs_fflush_cache(fs, fh);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+#endif
+
+  res = fd->fdoffset;
+
+  SPIFFS_UNLOCK(fs);
+  return res;
+}
+
+s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) {
+  SPIFFS_API_DBG("%s\n", __func__);
+  SPIFFS_LOCK(fs);
+  fs->file_cb_f = cb_func;
+  SPIFFS_UNLOCK(fs);
+  return 0;
+}
+
+#if SPIFFS_IX_MAP
+
+s32_t SPIFFS_ix_map(spiffs *fs,  spiffs_file fh, spiffs_ix_map *map,
+    u32_t offset, u32_t len, spiffs_page_ix *map_buf) {
+  SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " "_SPIPRIi "\n", __func__, fh, offset, len);
+  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;
+  res = spiffs_fd_get(fs, fh, &fd);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+  if (fd->ix_map) {
+    SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_MAPPED);
+  }
+
+  map->map_buf = map_buf;
+  map->offset = offset;
+  // nb: spix range includes last
+  map->start_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs);
+  map->end_spix = (offset + len) / SPIFFS_DATA_PAGE_SIZE(fs);
+  memset(map_buf, 0, sizeof(spiffs_page_ix) * (map->end_spix - map->start_spix + 1));
+  fd->ix_map = map;
+
+  // scan for pixes
+  res = spiffs_populate_ix_map(fs, fd, 0, map->end_spix - map->start_spix + 1);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+  SPIFFS_UNLOCK(fs);
+  return res;
+}
+
+s32_t SPIFFS_ix_unmap(spiffs *fs,  spiffs_file fh) {
+  SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, 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;
+  res = spiffs_fd_get(fs, fh, &fd);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+  if (fd->ix_map == 0) {
+    SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED);
+  }
+
+  fd->ix_map = 0;
+
+  SPIFFS_UNLOCK(fs);
+  return res;
+}
+
+s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset) {
+  SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, offset);
+  s32_t res = SPIFFS_OK;
+  SPIFFS_API_CHECK_CFG(fs);
+  SPIFFS_API_CHECK_MOUNT(fs);
+  SPIFFS_LOCK(fs);
+
+  fh = SPIFFS_FH_UNOFFS(fs, fh);
+
+  spiffs_fd *fd;
+  res = spiffs_fd_get(fs, fh, &fd);
+  SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+  if (fd->ix_map == 0) {
+    SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED);
+  }
+
+  spiffs_ix_map *map = fd->ix_map;
+
+  s32_t spix_diff = offset / SPIFFS_DATA_PAGE_SIZE(fs) - map->start_spix;
+  map->offset = offset;
+
+  // move existing pixes if within map offs
+  if (spix_diff != 0) {
+    // move vector
+    int i;
+    const s32_t vec_len = map->end_spix - map->start_spix + 1; // spix range includes last
+    map->start_spix += spix_diff;
+    map->end_spix += spix_diff;
+    if (spix_diff >= vec_len) {
+      // moving beyond range
+      memset(&map->map_buf, 0, vec_len * sizeof(spiffs_page_ix));
+      // populate_ix_map is inclusive
+      res = spiffs_populate_ix_map(fs, fd, 0, vec_len-1);
+      SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+    } else if (spix_diff > 0) {
+      // diff positive
+      for (i = 0; i < vec_len - spix_diff; i++) {
+        map->map_buf[i] = map->map_buf[i + spix_diff];
+      }
+      // memset is non-inclusive
+      memset(&map->map_buf[vec_len - spix_diff], 0, spix_diff * sizeof(spiffs_page_ix));
+      // populate_ix_map is inclusive
+      res = spiffs_populate_ix_map(fs, fd, vec_len - spix_diff, vec_len-1);
+      SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+    } else {
+      // diff negative
+      for (i = vec_len - 1; i >= -spix_diff; i--) {
+        map->map_buf[i] = map->map_buf[i + spix_diff];
+      }
+      // memset is non-inclusive
+      memset(&map->map_buf[0], 0, -spix_diff * sizeof(spiffs_page_ix));
+      // populate_ix_map is inclusive
+      res = spiffs_populate_ix_map(fs, fd, 0, -spix_diff - 1);
+      SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+    }
+
+  }
+
+  SPIFFS_UNLOCK(fs);
+  return res;
+}
+
+s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes) {
+  SPIFFS_API_CHECK_CFG(fs);
+  // always add one extra page, the offset might change to the middle of a page
+  return (bytes + SPIFFS_DATA_PAGE_SIZE(fs) ) / SPIFFS_DATA_PAGE_SIZE(fs);
+}
+
+s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries) {
+  SPIFFS_API_CHECK_CFG(fs);
+  return map_page_ix_entries * SPIFFS_DATA_PAGE_SIZE(fs);
+}
+
+#endif // SPIFFS_IX_MAP
 
 #if SPIFFS_TEST_VISUALISATION
 s32_t SPIFFS_vis(spiffs *fs) {
@@ -908,7 +1400,7 @@ s32_t SPIFFS_vis(spiffs *fs) {
           cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) {
         spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset];
         if (cur_entry == 0) {
-          spiffs_printf("%4i ", bix);
+          spiffs_printf(_SPIPRIbl" ", bix);
         } else if ((cur_entry & 0x3f) == 0) {
           spiffs_printf("     ");
         }
@@ -936,7 +1428,7 @@ s32_t SPIFFS_vis(spiffs *fs) {
     SPIFFS_CHECK_RES(res);
 
     if (erase_count != (spiffs_obj_id)-1) {
-      spiffs_printf("\tera_cnt: %i\n", erase_count);
+      spiffs_printf("\tera_cnt: "_SPIPRIi"\n", erase_count);
     } else {
       spiffs_printf("\tera_cnt: N/A\n");
     }
@@ -944,17 +1436,16 @@ s32_t SPIFFS_vis(spiffs *fs) {
     bix++;
   } // per block
 
-  spiffs_printf("era_cnt_max: %i\n", fs->max_erase_count);
-  spiffs_printf("last_errno:  %i\n", fs->err_code);
-  spiffs_printf("blocks:      %i\n", fs->block_count);
-  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_printf("era_cnt_max: "_SPIPRIi"\n", fs->max_erase_count);
+  spiffs_printf("last_errno:  "_SPIPRIi"\n", fs->err_code);
+  spiffs_printf("blocks:      "_SPIPRIi"\n", fs->block_count);
+  spiffs_printf("free_blocks: "_SPIPRIi"\n", fs->free_blocks);
+  spiffs_printf("page_alloc:  "_SPIPRIi"\n", fs->stats_p_allocated);
+  spiffs_printf("page_delet:  "_SPIPRIi"\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);
+  spiffs_printf("used:        "_SPIPRIi" of "_SPIPRIi"\n", used, total);
   return res;
 }
 #endif
diff --git a/components/spiffs/library/spiffs_nucleus.c b/components/spiffs/spiffs/src/spiffs_nucleus.c
similarity index 66%
rename from components/spiffs/library/spiffs_nucleus.c
rename to components/spiffs/spiffs/src/spiffs_nucleus.c
index bfd028fc..27ecdff2 100644
--- a/components/spiffs/library/spiffs_nucleus.c
+++ b/components/spiffs/spiffs/src/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;
@@ -137,7 +142,7 @@ s32_t spiffs_obj_lu_find_entry_visitor(
   int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
 
   // wrap initial
-  if (cur_entry >= (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) {
+  if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) {
     cur_entry = 0;
     cur_block++;
     cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs);
@@ -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) {
@@ -226,8 +232,9 @@ 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_DBG("erase "_SPIPRIad":"_SPIPRIi"\n", 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
+  _SPIFFS_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;
@@ -347,8 +407,12 @@ s32_t spiffs_obj_lu_scan(
 #if SPIFFS_USE_MAGIC
   if (unerased_bix != (spiffs_block_ix)-1) {
     // found one unerased block, remedy
-    SPIFFS_DBG("mount: erase block %i\n", bix);
+    SPIFFS_DBG("mount: erase block "_SPIPRIbl"\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(
@@ -402,17 +467,18 @@ s32_t spiffs_obj_lu_find_free(
       SPIFFS_OBJ_ID_FREE, block_ix, lu_entry);
   if (res == SPIFFS_OK) {
     fs->free_cursor_block_ix = *block_ix;
-    fs->free_cursor_obj_lu_entry = *lu_entry;
+    fs->free_cursor_obj_lu_entry = (*lu_entry) + 1;
     if (*lu_entry == 0) {
       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,153 @@ s32_t spiffs_obj_lu_find_id_and_span_by_phdr(
   return res;
 }
 
+#if SPIFFS_IX_MAP
+
+// update index map of given fd with given object index data
+static void spiffs_update_ix_map(spiffs *fs,
+    spiffs_fd *fd, spiffs_span_ix objix_spix, spiffs_page_object_ix *objix) {
+#if SPIFFS_SINGLETON
+  (void)fs;
+#endif
+  spiffs_ix_map *map = fd->ix_map;
+  spiffs_span_ix map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix);
+  spiffs_span_ix map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->end_spix);
+
+  // check if updated ix is within map range
+  if (objix_spix < map_objix_start_spix || objix_spix > map_objix_end_spix) {
+    return;
+  }
+
+  // update memory mapped page index buffer to new pages
+
+  // get range of updated object index map data span indices
+  spiffs_span_ix objix_data_spix_start =
+      SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, objix_spix);
+  spiffs_span_ix objix_data_spix_end = objix_data_spix_start +
+      (objix_spix == 0 ? SPIFFS_OBJ_HDR_IX_LEN(fs) : SPIFFS_OBJ_IX_LEN(fs));
+
+  // calc union of object index range and index map range array
+  spiffs_span_ix map_spix = MAX(map->start_spix, objix_data_spix_start);
+  spiffs_span_ix map_spix_end = MIN(map->end_spix + 1, objix_data_spix_end);
+
+  while (map_spix < map_spix_end) {
+    spiffs_page_ix objix_data_pix;
+    if (objix_spix == 0) {
+      // get data page from object index header page
+      objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix_header)))[map_spix];
+    } else {
+      // get data page from object index page
+      objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, map_spix)];
+    }
+
+    if (objix_data_pix == (spiffs_page_ix)-1) {
+      // reached end of object, abort
+      break;
+    }
+
+    map->map_buf[map_spix - map->start_spix] = objix_data_pix;
+    SPIFFS_DBG("map "_SPIPRIid":"_SPIPRIsp" ("_SPIPRIsp"--"_SPIPRIsp") objix.spix:"_SPIPRIsp" to pix "_SPIPRIpg"\n",
+        fd->obj_id, map_spix - map->start_spix,
+        map->start_spix, map->end_spix,
+        objix->p_hdr.span_ix,
+        objix_data_pix);
+
+    map_spix++;
+  }
+}
+
+typedef struct {
+  spiffs_fd *fd;
+  u32_t remaining_objix_pages_to_visit;
+  spiffs_span_ix map_objix_start_spix;
+  spiffs_span_ix map_objix_end_spix;
+} spiffs_ix_map_populate_state;
+
+static s32_t spiffs_populate_ix_map_v(
+    spiffs *fs,
+    spiffs_obj_id obj_id,
+    spiffs_block_ix bix,
+    int ix_entry,
+    const void *user_const_p,
+    void *user_var_p) {
+  (void)user_const_p;
+  s32_t res;
+  spiffs_ix_map_populate_state *state = (spiffs_ix_map_populate_state *)user_var_p;
+  spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry);
+
+  // load header to check it
+  spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
+  res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
+      0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix), (u8_t *)objix);
+  SPIFFS_CHECK_RES(res);
+  SPIFFS_VALIDATE_OBJIX(objix->p_hdr, obj_id, objix->p_hdr.span_ix);
+
+  // check if hdr is ok, and if objix range overlap with ix map range
+  if ((objix->p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) ==
+      (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE) &&
+      objix->p_hdr.span_ix >= state->map_objix_start_spix &&
+      objix->p_hdr.span_ix <= state->map_objix_end_spix) {
+    // ok, load rest of object index
+    res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
+        0, SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_object_ix),
+        SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix),
+        (u8_t *)objix + sizeof(spiffs_page_object_ix));
+    SPIFFS_CHECK_RES(res);
+
+    spiffs_update_ix_map(fs, state->fd, objix->p_hdr.span_ix, objix);
+
+    state->remaining_objix_pages_to_visit--;
+    SPIFFS_DBG("map "_SPIPRIid" ("_SPIPRIsp"--"_SPIPRIsp") remaining objix pages "_SPIPRIi"\n",
+        state->fd->obj_id,
+        state->fd->ix_map->start_spix, state->fd->ix_map->end_spix,
+        state->remaining_objix_pages_to_visit);
+  }
+
+  if (res == SPIFFS_OK) {
+    res = state->remaining_objix_pages_to_visit ? SPIFFS_VIS_COUNTINUE : SPIFFS_VIS_END;
+  }
+  return res;
+}
+
+// populates index map, from vector entry start to vector entry end, inclusive
+s32_t spiffs_populate_ix_map(spiffs *fs, spiffs_fd *fd, u32_t vec_entry_start, u32_t vec_entry_end) {
+  s32_t res;
+  spiffs_ix_map *map = fd->ix_map;
+  spiffs_ix_map_populate_state state;
+  vec_entry_start = MIN((u32_t)(map->end_spix - map->start_spix), vec_entry_start);
+  vec_entry_end = MAX((u32_t)(map->end_spix - map->start_spix), vec_entry_end);
+  if (vec_entry_start > vec_entry_end) {
+    return SPIFFS_ERR_IX_MAP_BAD_RANGE;
+  }
+  state.map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_start);
+  state.map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_end);
+  state.remaining_objix_pages_to_visit =
+      state.map_objix_end_spix - state.map_objix_start_spix + 1;
+  state.fd = fd;
+
+  res = spiffs_obj_lu_find_entry_visitor(
+      fs,
+      SPIFFS_BLOCK_FOR_PAGE(fs, fd->objix_hdr_pix),
+      SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, fd->objix_hdr_pix),
+      SPIFFS_VIS_CHECK_ID,
+      fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG,
+      spiffs_populate_ix_map_v,
+      0,
+      &state,
+      0,
+      0);
+
+  if (res == SPIFFS_VIS_END) {
+    res = SPIFFS_OK;
+  }
+
+  return res;
+}
+
+#endif
+
+
+#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 +804,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 +869,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 +900,15 @@ 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[],
+    const u8_t meta[],
     spiffs_obj_type type,
     spiffs_page_ix *objix_hdr_pix) {
   s32_t res = SPIFFS_OK;
@@ -704,7 +924,7 @@ s32_t spiffs_object_create(
   // find free entry
   res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry);
   SPIFFS_CHECK_RES(res);
-  SPIFFS_DBG("create: found free page @ %04x bix:%i entry:%i\n", SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry);
+  SPIFFS_DBG("create: found free page @ "_SPIPRIpg" bix:"_SPIPRIbl" entry:"_SPIPRIsp"\n", (spiffs_page_ix)SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry);
 
   // occupy page in object lookup
   res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
@@ -719,15 +939,24 @@ 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);
+#if SPIFFS_OBJ_META_LEN
+  if (meta) {
+    _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN);
+  } else {
+    memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN);
+  }
+#else
+  (void) meta;
+#endif
 
   // update page
   res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
       0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&oix_hdr);
 
   SPIFFS_CHECK_RES(res);
-  spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN);
+  spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&oix_hdr,
+      SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN);
 
   if (objix_hdr_pix) {
     *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
@@ -735,7 +964,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 +977,8 @@ 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[],
+    const u8_t meta[],
     u32_t size,
     spiffs_page_ix *new_pix) {
   s32_t res = SPIFFS_OK;
@@ -770,8 +1002,15 @@ 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 SPIFFS_OBJ_META_LEN
+  if (meta) {
+    _SPIFFS_MEMCPY(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN);
+  }
+#else
+  (void) meta;
+#endif
   if (size) {
     objix_hdr->size = size;
   }
@@ -784,49 +1023,125 @@ s32_t spiffs_object_update_index_hdr(
       *new_pix = new_objix_hdr_pix;
     }
     // callback on object index update
-    spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size);
+    spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr,
+        new_objix_hdr_data ? SPIFFS_EV_IX_UPD : SPIFFS_EV_IX_UPD_HDR,
+            obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size);
     if (fd) fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster
   }
 
   return res;
 }
+#endif // !SPIFFS_READ_ONLY
 
 void spiffs_cb_object_event(
     spiffs *fs,
-    spiffs_fd *fd,
+    spiffs_page_object_ix *objix,
     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;
+#if SPIFFS_IX_MAP == 0
+  (void)objix;
+#endif
   // 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;
+  SPIFFS_DBG("       CALLBACK  %s obj_id:"_SPIPRIid" spix:"_SPIPRIsp" npix:"_SPIPRIpg" nsz:"_SPIPRIi"\n", (const char *[]){"UPD", "NEW", "DEL", "MOV", "HUP","???"}[MIN(ev,5)],
+      obj_id_raw, spix, new_pix, new_size);
   for (i = 0; i < fs->fd_count; i++) {
     spiffs_fd *cur_fd = &fds[i];
-    if (cur_fd->file_nbr == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue;
-    if (spix == 0) {
-      if (ev == SPIFFS_EV_IX_NEW || ev == SPIFFS_EV_IX_UPD) {
-        SPIFFS_DBG("       callback: setting fd %i:%04x objix_hdr_pix to %04x, size:%i\n", cur_fd->file_nbr, cur_fd->obj_id, new_pix, new_size);
+    if ((cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; // fd not related to updated file
+#if !SPIFFS_TEMPORAL_FD_CACHE
+    if (cur_fd->file_nbr == 0) continue; // fd closed
+#endif
+    if (spix == 0) { // object index header update
+      if (ev != SPIFFS_EV_IX_DEL) {
+#if SPIFFS_TEMPORAL_FD_CACHE
+        if (cur_fd->score == 0) continue; // never used fd
+#endif
+        SPIFFS_DBG("       callback: setting fd "_SPIPRIfd":"_SPIPRIid"(fdoffs:"_SPIPRIi" offs:"_SPIPRIi") objix_hdr_pix to "_SPIPRIpg", size:"_SPIPRIi"\n",
+            SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, cur_fd->fdoffset, cur_fd->offset, new_pix, new_size);
         cur_fd->objix_hdr_pix = new_pix;
         if (new_size != 0) {
+          // update size and offsets for fds to this file
           cur_fd->size = new_size;
+          u32_t act_new_size = new_size == SPIFFS_UNDEFINED_LEN ? 0 : new_size;
+#if SPIFFS_CACHE_WR
+          if (act_new_size > 0 && cur_fd->cache_page) {
+            act_new_size = MAX(act_new_size, cur_fd->cache_page->offset + cur_fd->cache_page->size);
+          }
+#endif
+          if (cur_fd->offset > act_new_size) {
+            cur_fd->offset = act_new_size;
+          }
+          if (cur_fd->fdoffset > act_new_size) {
+            cur_fd->fdoffset = act_new_size;
+          }
+#if SPIFFS_CACHE_WR
+          if (cur_fd->cache_page && cur_fd->cache_page->offset > act_new_size+1) {
+            SPIFFS_CACHE_DBG("CACHE_DROP: file trunced, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix);
+            spiffs_cache_fd_release(fs, cur_fd->cache_page);
+          }
+#endif
         }
-      } else if (ev == SPIFFS_EV_IX_DEL) {
+      } else {
+        // removing file
+#if SPIFFS_CACHE_WR
+        if (cur_fd->file_nbr && cur_fd->cache_page) {
+          SPIFFS_CACHE_DBG("CACHE_DROP: file deleted, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix);
+          spiffs_cache_fd_release(fs, cur_fd->cache_page);
+        }
+#endif
+        SPIFFS_DBG("       callback: release fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix);
         cur_fd->file_nbr = 0;
         cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED;
       }
-    }
+    } // object index header update
     if (cur_fd->cursor_objix_spix == spix) {
-      if (ev == SPIFFS_EV_IX_NEW || ev == SPIFFS_EV_IX_UPD) {
-        SPIFFS_DBG("       callback: setting fd %i:%04x span:%04x objix_pix to %04x\n", cur_fd->file_nbr, cur_fd->obj_id, spix, new_pix);
+      if (ev != SPIFFS_EV_IX_DEL) {
+        SPIFFS_DBG("       callback: setting fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix);
         cur_fd->cursor_objix_pix = new_pix;
       } else {
         cur_fd->cursor_objix_pix = 0;
       }
     }
+  } // fd update loop
+
+#if SPIFFS_IX_MAP
+
+  // update index maps
+  if (ev == SPIFFS_EV_IX_UPD || ev == SPIFFS_EV_IX_NEW) {
+    for (i = 0; i < fs->fd_count; i++) {
+      spiffs_fd *cur_fd = &fds[i];
+      // check fd opened, having ix map, match obj id
+      if (cur_fd->file_nbr == 0 ||
+          cur_fd->ix_map == 0 ||
+          (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue;
+      SPIFFS_DBG("       callback: map ix update fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix);
+      spiffs_update_ix_map(fs, cur_fd, spix, objix);
+    }
+  }
+
+#endif
+
+  // 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 ||
+        ev == SPIFFS_EV_IX_MOV ||
+        ev == SPIFFS_EV_IX_UPD_HDR) {
+      op = SPIFFS_CB_UPDATED;
+    } else if (ev == SPIFFS_EV_IX_DEL) {
+      op = SPIFFS_CB_DELETED;
+    } else {
+      SPIFFS_DBG("       callback: WARNING unknown callback event "_SPIPRIi"\n", ev);
+      return; // bail out
+    }
+    fs->file_cb_f(fs, op, obj_id, new_pix);
   }
 }
 
@@ -881,11 +1196,12 @@ s32_t spiffs_object_open_by_page(
 
   SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0);
 
-  SPIFFS_DBG("open: fd %i is obj id %04x\n", fd->file_nbr, fd->obj_id);
+  SPIFFS_DBG("open: fd "_SPIPRIfd" is obj id "_SPIPRIid"\n", SPIFFS_FH_OFFS(fs, fd->file_nbr), fd->obj_id);
 
   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) {
@@ -893,7 +1209,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
   s32_t res = SPIFFS_OK;
   u32_t written = 0;
 
-  SPIFFS_DBG("append: %i bytes @ offs %i of size %i\n", len, offset, fd->size);
+  SPIFFS_DBG("append: "_SPIPRIi" bytes @ offs "_SPIPRIi" of size "_SPIPRIi"\n", len, offset, fd->size);
 
   if (offset > fd->size) {
     SPIFFS_DBG("append: offset reversed to size\n");
@@ -902,7 +1218,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
 
   res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta
   if (res != SPIFFS_OK) {
-    SPIFFS_DBG("append: gc check fail %i\n", res);
+    SPIFFS_DBG("append: gc check fail "_SPIPRIi"\n", res);
   }
   SPIFFS_CHECK_RES(res);
 
@@ -930,7 +1246,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
       // within this clause we return directly if something fails, object index mess-up
       if (written > 0) {
         // store previous object index page, unless first pass
-        SPIFFS_DBG("append: %04x store objix %04x:%04x, written %i\n", fd->obj_id,
+        SPIFFS_DBG("append: "_SPIPRIid" store objix "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id,
             cur_objix_pix, prev_objix_spix, written);
         if (prev_objix_spix == 0) {
           // this is an update to object index header page
@@ -945,9 +1261,9 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
           } else {
             // was a nonempty object, update to new page
             res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
-                fd->objix_hdr_pix, fs->work, 0, offset+written, &new_objix_hdr_page);
+                fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page);
             SPIFFS_CHECK_RES(res);
-            SPIFFS_DBG("append: %04x store new objix_hdr, %04x:%04x, written %i\n", fd->obj_id,
+            SPIFFS_DBG("append: "_SPIPRIid" store new objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id,
                 new_objix_hdr_page, 0, written);
           }
         } else {
@@ -958,12 +1274,13 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
           res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT,
               fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
           SPIFFS_CHECK_RES(res);
-          spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD,fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0);
+          spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work,
+              SPIFFS_EV_IX_UPD,fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0);
           // update length in object index header page
           res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
-              fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page);
+              fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page);
           SPIFFS_CHECK_RES(res);
-          SPIFFS_DBG("append: %04x store new size I %i in objix_hdr, %04x:%04x, written %i\n", fd->obj_id,
+          SPIFFS_DBG("append: "_SPIPRIid" store new size I "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id,
               offset+written, new_objix_hdr_page, 0, written);
         }
         fd->size = offset+written;
@@ -973,7 +1290,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
       // create or load new object index page
       if (cur_objix_spix == 0) {
         // load object index header page, must always exist
-        SPIFFS_DBG("append: %04x load objixhdr page %04x:%04x\n", fd->obj_id, cur_objix_pix, cur_objix_spix);
+        SPIFFS_DBG("append: "_SPIPRIid" load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", fd->obj_id, cur_objix_pix, cur_objix_spix);
         res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
             fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
         SPIFFS_CHECK_RES(res);
@@ -988,23 +1305,24 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
           res = spiffs_page_allocate_data(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG,
               &p_hdr, 0, 0, 0, 1, &cur_objix_pix);
           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
           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
+          _SPIFFS_MEMCPY(fs->work, &p_hdr, sizeof(spiffs_page_header));
+          spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work,
+              SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0);
+          SPIFFS_DBG("append: "_SPIPRIid" create objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id
               , cur_objix_pix, cur_objix_spix, written);
         } else {
           // on first pass, we load existing object index page
           spiffs_page_ix pix;
-          SPIFFS_DBG("append: %04x find objix span_ix:%04x\n", fd->obj_id, cur_objix_spix);
+          SPIFFS_DBG("append: "_SPIPRIid" find objix span_ix:"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix);
           if (fd->cursor_objix_spix == cur_objix_spix) {
             pix = fd->cursor_objix_pix;
           } else {
             res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix);
             SPIFFS_CHECK_RES(res);
           }
-          SPIFFS_DBG("append: %04x found object index at page %04x [fd size %i]\n", fd->obj_id, pix, fd->size);
+          SPIFFS_DBG("append: "_SPIPRIid" found object index at page "_SPIPRIpg" [fd size "_SPIPRIi"]\n", fd->obj_id, pix, fd->size);
           res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
               fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
           SPIFFS_CHECK_RES(res);
@@ -1028,7 +1346,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
       p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL);  // finalize immediately
       res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
           &p_hdr, &data[written], to_write, page_offs, 1, &data_page);
-      SPIFFS_DBG("append: %04x store new data page, %04x:%04x offset:%i, len %i, written %i\n", fd->obj_id,
+      SPIFFS_DBG("append: "_SPIPRIid" store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id,
           data_page, data_spix, page_offs, to_write, written);
     } else {
       // append to existing page, fill out free data in existing page
@@ -1045,7 +1363,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
 
       res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
           fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]);
-      SPIFFS_DBG("append: %04x store to existing data page, %04x:%04x offset:%i, len %i, written %i\n", fd->obj_id
+      SPIFFS_DBG("append: "_SPIPRIid" store to existing data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id
           , data_page, data_spix, page_offs, to_write, written);
     }
 
@@ -1055,14 +1373,14 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
     if (cur_objix_spix == 0) {
       // update object index header page
       ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page;
-      SPIFFS_DBG("append: %04x wrote page %04x to objix_hdr entry %02x in mem\n", fd->obj_id
+      SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", fd->obj_id
           , data_page, data_spix);
       objix_hdr->size = offset+written;
     } else {
       // update object index page
       ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page;
-      SPIFFS_DBG("append: %04x wrote page %04x to objix entry %02x in mem\n", fd->obj_id
-          , data_page, SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
+      SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", fd->obj_id
+          , data_page, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
     }
 
     // update internals
@@ -1081,7 +1399,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
   if (cur_objix_spix != 0) {
     // wrote beyond object index header page
     // write last modified object index page, unless object header index page
-    SPIFFS_DBG("append: %04x store objix page, %04x:%04x, written %i\n", fd->obj_id,
+    SPIFFS_DBG("append: "_SPIPRIid" store objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id,
         cur_objix_pix, cur_objix_spix, written);
 
     res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix);
@@ -1090,12 +1408,13 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
     res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT,
         fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
     SPIFFS_CHECK_RES(res2);
-    spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0);
+    spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work,
+        SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0);
 
     // update size in object header index page
     res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
-        fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page);
-    SPIFFS_DBG("append: %04x store new size II %i in objix_hdr, %04x:%04x, written %i, res %i\n", fd->obj_id
+        fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page);
+    SPIFFS_DBG("append: "_SPIPRIid" store new size II "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi", res "_SPIPRIi"\n", fd->obj_id
         , offset+written, new_objix_hdr_page, 0, written, res2);
     SPIFFS_CHECK_RES(res2);
   } else {
@@ -1103,7 +1422,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
     if (offset == 0) {
       // wrote to empty object - simply update size and write whole page
       objix_hdr->size = offset+written;
-      SPIFFS_DBG("append: %04x store fresh objix_hdr page, %04x:%04x, written %i\n", fd->obj_id
+      SPIFFS_DBG("append: "_SPIPRIid" store fresh objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id
           , cur_objix_pix, cur_objix_spix, written);
 
       res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix);
@@ -1113,20 +1432,23 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
           fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
       SPIFFS_CHECK_RES(res2);
       // callback on object index update
-      spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size);
+      spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work,
+          SPIFFS_EV_IX_UPD_HDR, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size);
     } else {
       // modifying object index header page, update size and make new copy
       res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
-          fd->objix_hdr_pix, fs->work, 0, offset+written, &new_objix_hdr_page);
-      SPIFFS_DBG("append: %04x store modified objix_hdr page, %04x:%04x, written %i\n", fd->obj_id
+          fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page);
+      SPIFFS_DBG("append: "_SPIPRIid" store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id
           , new_objix_hdr_page, 0, written);
       SPIFFS_CHECK_RES(res2);
     }
   }
 
   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) {
@@ -1165,8 +1487,8 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
         if (prev_objix_spix == 0) {
           // store previous object index header page
           res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
-              fd->objix_hdr_pix, fs->work, 0, 0, &new_objix_hdr_pix);
-          SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %i\n", new_objix_hdr_pix, 0, written);
+              fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix);
+          SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written);
           SPIFFS_CHECK_RES(res);
         } else {
           // store new version of previous object index page
@@ -1176,16 +1498,17 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
           SPIFFS_CHECK_RES(res);
 
           res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix);
-          SPIFFS_DBG("modify: store previous modified objix page, %04x:%04x, written %i\n", new_objix_pix, objix->p_hdr.span_ix, written);
+          SPIFFS_DBG("modify: store previous modified objix page, "_SPIPRIid":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, objix->p_hdr.span_ix, written);
           SPIFFS_CHECK_RES(res);
-          spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
+          spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix,
+              SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
         }
       }
 
       // load next object index page
       if (cur_objix_spix == 0) {
         // load object index header page, must exist
-        SPIFFS_DBG("modify: load objixhdr page %04x:%04x\n", cur_objix_pix, cur_objix_spix);
+        SPIFFS_DBG("modify: load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", cur_objix_pix, cur_objix_spix);
         res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
             fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
         SPIFFS_CHECK_RES(res);
@@ -1193,14 +1516,14 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
       } else {
         // load existing object index page on first pass
         spiffs_page_ix pix;
-        SPIFFS_DBG("modify: find objix span_ix:%04x\n", cur_objix_spix);
+        SPIFFS_DBG("modify: find objix span_ix:"_SPIPRIsp"\n", cur_objix_spix);
         if (fd->cursor_objix_spix == cur_objix_spix) {
           pix = fd->cursor_objix_pix;
         } else {
           res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix);
           SPIFFS_CHECK_RES(res);
         }
-        SPIFFS_DBG("modify: found object index at page %04x\n", pix);
+        SPIFFS_DBG("modify: found object index at page "_SPIPRIpg"\n", pix);
         res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
             fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
         SPIFFS_CHECK_RES(res);
@@ -1231,7 +1554,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
       // a full page, allocate and write a new page of data
       res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
           &p_hdr, &data[written], to_write, page_offs, 1, &data_pix);
-      SPIFFS_DBG("modify: store new data page, %04x:%04x offset:%i, len %i, written %i\n", data_pix, data_spix, page_offs, to_write, written);
+      SPIFFS_DBG("modify: store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", data_pix, data_spix, page_offs, to_write, written);
     } else {
       // write to existing page, allocate new and copy unmodified data
 
@@ -1272,7 +1595,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
           (u8_t *)&p_hdr.flags);
       if (res != SPIFFS_OK) break;
 
-      SPIFFS_DBG("modify: store to existing data page, src:%04x, dst:%04x:%04x offset:%i, len %i, written %i\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written);
+      SPIFFS_DBG("modify: store to existing data page, src:"_SPIPRIpg", dst:"_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written);
     }
 
     // delete original data page
@@ -1282,11 +1605,11 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
     if (cur_objix_spix == 0) {
       // update object index header page
       ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix;
-      SPIFFS_DBG("modify: wrote page %04x to objix_hdr entry %02x in mem\n", data_pix, data_spix);
+      SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", data_pix, data_spix);
     } else {
       // update object index page
       ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix;
-      SPIFFS_DBG("modify: wrote page %04x to objix entry %02x in mem\n", data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
+      SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
     }
 
     // update internals
@@ -1311,31 +1634,33 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
     SPIFFS_CHECK_RES(res2);
 
     res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix);
-    SPIFFS_DBG("modify: store modified objix page, %04x:%04x, written %i\n", new_objix_pix, cur_objix_spix, written);
+    SPIFFS_DBG("modify: store modified objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, cur_objix_spix, written);
     fd->cursor_objix_pix = new_objix_pix;
     fd->cursor_objix_spix = cur_objix_spix;
     SPIFFS_CHECK_RES(res2);
-    spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
+    spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix,
+        SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
 
   } else {
     // wrote within object index header page
     res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
-        fd->objix_hdr_pix, fs->work, 0, 0, &new_objix_hdr_pix);
-    SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %i\n", new_objix_hdr_pix, 0, written);
+        fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix);
+    SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written);
     SPIFFS_CHECK_RES(res2);
   }
 
   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 +1674,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 (strcmp((char *)user_p, (char *)objix_hdr.name) == 0) {
+    if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) {
       return SPIFFS_OK;
     }
   }
@@ -1360,7 +1685,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 +1697,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,16 +1717,25 @@ 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,
     u32_t new_size,
-    u8_t remove) {
+    u8_t remove_full) {
   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_full) {
+    // no op
+    return res;
+  }
+
+  // need 2 pages if not removing: object index page + possibly chopped data page
+  if (remove_full == 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);
@@ -1414,7 +1748,7 @@ s32_t spiffs_object_truncate(
   spiffs_page_ix new_objix_hdr_pix;
 
   // before truncating, check if object is to be fully removed and mark this
-  if (remove && new_size == 0) {
+  if (remove_full && new_size == 0) {
     u8_t flags = ~( SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE);
     res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT,
         fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix) + offsetof(spiffs_page_header, flags),
@@ -1431,20 +1765,28 @@ s32_t spiffs_object_truncate(
     if (prev_objix_spix != cur_objix_spix) {
       if (prev_objix_spix != (spiffs_span_ix)-1) {
         // remove previous object index page
-        SPIFFS_DBG("truncate: delete objix page %04x:%04x\n", objix_pix, prev_objix_spix);
+        SPIFFS_DBG("truncate: delete objix page "_SPIPRIpg":"_SPIPRIsp"\n", objix_pix, prev_objix_spix);
 
         res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix);
         SPIFFS_CHECK_RES(res);
 
         res = spiffs_page_delete(fs, objix_pix);
         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);
+        spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0,
+            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_full == 0) {
+            SPIFFS_DBG("truncate: update objix hdr page "_SPIPRIpg":"_SPIPRIsp" to size "_SPIPRIi"\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, 0, cur_size, &new_objix_hdr_pix);
+            SPIFFS_CHECK_RES(res);
+          }
           fd->size = cur_size;
         }
       }
@@ -1456,7 +1798,7 @@ s32_t spiffs_object_truncate(
         SPIFFS_CHECK_RES(res);
       }
 
-      SPIFFS_DBG("truncate: load objix page %04x:%04x for data spix:%04x\n", objix_pix, cur_objix_spix, data_spix);
+      SPIFFS_DBG("truncate: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix);
       res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
           fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
       SPIFFS_CHECK_RES(res);
@@ -1478,20 +1820,20 @@ s32_t spiffs_object_truncate(
       ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE;
     }
 
-    SPIFFS_DBG("truncate: got data pix %04x\n", data_pix);
+    SPIFFS_DBG("truncate: got data pix "_SPIPRIpg"\n", data_pix);
 
-    if (cur_size - SPIFFS_DATA_PAGE_SIZE(fs) >= new_size) {
+    if (new_size == 0 || remove_full || 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) {
-        SPIFFS_DBG("truncate: err validating data pix %i\n", res);
+        SPIFFS_DBG("truncate: err validating data pix "_SPIPRIi"\n", res);
         break;
       }
 
       if (res == SPIFFS_OK) {
         res = spiffs_page_delete(fs, data_pix);
         if (res != SPIFFS_OK) {
-          SPIFFS_DBG("truncate: err deleting data pix %i\n", res);
+          SPIFFS_DBG("truncate: err deleting data pix "_SPIPRIi"\n", res);
           break;
         }
       } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) {
@@ -1506,13 +1848,13 @@ s32_t spiffs_object_truncate(
       }
       fd->size = cur_size;
       fd->offset = cur_size;
-      SPIFFS_DBG("truncate: delete data page %04x for data spix:%04x, cur_size:%i\n", data_pix, data_spix, cur_size);
+      SPIFFS_DBG("truncate: delete data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", data_pix, data_spix, cur_size);
     } else {
       // delete last page, partially
       spiffs_page_header p_hdr;
       spiffs_page_ix new_data_pix;
       u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs));
-      SPIFFS_DBG("truncate: delete %i bytes from data page %04x for data spix:%04x, cur_size:%i\n", bytes_to_remove, data_pix, data_spix, cur_size);
+      SPIFFS_DBG("truncate: delete "_SPIPRIi" bytes from data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", bytes_to_remove, data_pix, data_spix, cur_size);
 
       res = spiffs_page_data_check(fs, fd, data_pix, data_spix);
       if (res != SPIFFS_OK) break;
@@ -1544,11 +1886,11 @@ s32_t spiffs_object_truncate(
       if (cur_objix_spix == 0) {
         // update object index header page
         ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix;
-        SPIFFS_DBG("truncate: wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
+        SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
       } else {
         // update object index page
         ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix;
-        SPIFFS_DBG("truncate: wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
+        SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
       }
       cur_size = new_size;
       fd->size = new_size;
@@ -1562,30 +1904,31 @@ s32_t spiffs_object_truncate(
   if (cur_objix_spix == 0) {
     // update object index header page
     if (cur_size == 0) {
-      if (remove) {
+      if (remove_full) {
         // remove object altogether
-        SPIFFS_DBG("truncate: remove object index header page %04x\n", objix_pix);
+        SPIFFS_DBG("truncate: remove object index header page "_SPIPRIpg"\n", objix_pix);
 
         res = spiffs_page_index_check(fs, fd, objix_pix, 0);
         SPIFFS_CHECK_RES(res);
 
         res = spiffs_page_delete(fs, objix_pix);
         SPIFFS_CHECK_RES(res);
-        spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0);
+        spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0,
+            SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0);
       } else {
         // make uninitialized object
-        SPIFFS_DBG("truncate: reset objix_hdr page %04x\n", objix_pix);
+        SPIFFS_DBG("truncate: reset objix_hdr page "_SPIPRIpg"\n", objix_pix);
         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);
+            objix_pix, fs->work, 0, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix);
         SPIFFS_CHECK_RES(res);
       }
     } else {
       // update object index header page
       SPIFFS_DBG("truncate: update object index header page with indices and size\n");
       res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
-          objix_pix, fs->work, 0, cur_size, &new_objix_hdr_pix);
+          objix_pix, fs->work, 0, 0, cur_size, &new_objix_hdr_pix);
       SPIFFS_CHECK_RES(res);
     }
   } else {
@@ -1598,20 +1941,22 @@ s32_t spiffs_object_truncate(
     // move and update object index page
     res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix_hdr, fd->obj_id, 0, objix_pix, &new_objix_pix);
     SPIFFS_CHECK_RES(res);
-    spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
-    SPIFFS_DBG("truncate: store modified objix page, %04x:%04x\n", new_objix_pix, cur_objix_spix);
+    spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr,
+        SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
+    SPIFFS_DBG("truncate: store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, cur_objix_spix);
     fd->cursor_objix_pix = new_objix_pix;
     fd->cursor_objix_spix = cur_objix_spix;
     fd->offset = cur_size;
     // update object index header page with new size
     res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
-        fd->objix_hdr_pix, 0, 0, cur_size, &new_objix_hdr_pix);
+        fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix);
     SPIFFS_CHECK_RES(res);
   }
   fd->size = cur_size;
 
   return res;
-}
+} // spiffs_object_truncate
+#endif // !SPIFFS_READ_ONLY
 
 s32_t spiffs_object_read(
     spiffs_fd *fd,
@@ -1630,45 +1975,59 @@ s32_t spiffs_object_read(
   spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
 
   while (cur_offset < offset + len) {
-    cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
-    if (prev_objix_spix != cur_objix_spix) {
-      // load current object index (header) page
-      if (cur_objix_spix == 0) {
-        objix_pix = fd->objix_hdr_pix;
-      } else {
-        SPIFFS_DBG("read: find objix %04x:%04x\n", fd->obj_id, cur_objix_spix);
-        res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix);
-        SPIFFS_CHECK_RES(res);
-      }
-      SPIFFS_DBG("read: load objix page %04x:%04x for data spix:%04x\n", objix_pix, cur_objix_spix, data_spix);
-      res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
-          fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
-      SPIFFS_CHECK_RES(res);
-      SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix);
-
-      fd->offset = cur_offset;
-      fd->cursor_objix_pix = objix_pix;
-      fd->cursor_objix_spix = cur_objix_spix;
-
-      prev_objix_spix = cur_objix_spix;
-    }
-
-    if (cur_objix_spix == 0) {
-      // get data page from object index header page
-      data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix];
+#if SPIFFS_IX_MAP
+    // check if we have a memory, index map and if so, if we're within index map's range
+    // and if so, if the entry is populated
+    if (fd->ix_map && data_spix >= fd->ix_map->start_spix && data_spix <= fd->ix_map->end_spix
+        && fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]) {
+      data_pix = fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix];
     } else {
-      // get data page from object index page
-      data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)];
-    }
+#endif
+      cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
+      if (prev_objix_spix != cur_objix_spix) {
+        // load current object index (header) page
+        if (cur_objix_spix == 0) {
+          objix_pix = fd->objix_hdr_pix;
+        } else {
+          SPIFFS_DBG("read: find objix "_SPIPRIid":"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix);
+          if (fd->cursor_objix_spix == cur_objix_spix) {
+            objix_pix = fd->cursor_objix_pix;
+          } else {
+            res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix);
+            SPIFFS_CHECK_RES(res);
+          }
+        }
+        SPIFFS_DBG("read: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix);
+        res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
+            fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
+        SPIFFS_CHECK_RES(res);
+        SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix);
 
+        fd->offset = cur_offset;
+        fd->cursor_objix_pix = objix_pix;
+        fd->cursor_objix_spix = cur_objix_spix;
+
+        prev_objix_spix = cur_objix_spix;
+      }
+
+      if (cur_objix_spix == 0) {
+        // get data page from object index header page
+        data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix];
+      } else {
+        // get data page from object index page
+        data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)];
+      }
+#if SPIFFS_IX_MAP
+    }
+#endif
     // all remaining data
     u32_t len_to_read = offset + len - cur_offset;
     // remaining data in page
     len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)));
     // remaining data in file
     len_to_read = MIN(len_to_read, fd->size);
-    SPIFFS_DBG("read: offset:%i rd:%i data spix:%04x is data_pix:%04x addr:%08x\n", cur_offset, len_to_read, data_spix, data_pix,
-        SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)));
+    SPIFFS_DBG("read: offset:"_SPIPRIi" rd:"_SPIPRIi" data spix:"_SPIPRIsp" is data_pix:"_SPIPRIpg" addr:"_SPIPRIad"\n", cur_offset, len_to_read, data_spix, data_pix,
+        (u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))));
     if (len_to_read <= 0) {
       res = SPIFFS_ERR_END_OF_OBJECT;
       break;
@@ -1691,6 +2050,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 +2059,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 +2075,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 (strcmp((char *)user_p, (char *)objix_hdr.name) == 0) {
+        if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) {
           return SPIFFS_ERR_CONFLICTING_NAME;
         }
       }
@@ -1732,11 +2092,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,
@@ -1753,7 +2113,7 @@ static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id
       if (id >= state->min_obj_id && id <= state->max_obj_id) {
         u8_t *map = (u8_t *)fs->work;
         int ix = (id - state->min_obj_id) / state->compaction;
-        //SPIFFS_DBG("free_obj_id: add ix %i for id %04x min:%04x max%04x comp:%i\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction);
+        //SPIFFS_DBG("free_obj_id: add ix "_SPIPRIi" for id "_SPIPRIid" min"_SPIPRIid" max"_SPIPRIid" comp:"_SPIPRIi"\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction);
         map[ix]++;
       }
     }
@@ -1764,10 +2124,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;
@@ -1781,11 +2141,11 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co
     if (state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8) {
       // possible to represent in bitmap
       u32_t i, j;
-      SPIFFS_DBG("free_obj_id: BITM min:%04x max:%04x\n", state.min_obj_id, state.max_obj_id);
+      SPIFFS_DBG("free_obj_id: BITM min:"_SPIPRIid" max:"_SPIPRIid"\n", state.min_obj_id, state.max_obj_id);
 
       memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
-      res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, state.min_obj_id,
-          conflicting_name, 0, 0);
+      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
@@ -1826,14 +2186,14 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co
           return SPIFFS_ERR_FULL;
         }
 
-        SPIFFS_DBG("free_obj_id: COMP select index:%i min_count:%i min:%04x max:%04x compact:%i\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction);
+        SPIFFS_DBG("free_obj_id: COMP select index:"_SPIPRIi" min_count:"_SPIPRIi" min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction);
 
         if (min_count == 0) {
           // no id in this range, skip compacting and use directly
           *obj_id = min_i * state.compaction + state.min_obj_id;
           return SPIFFS_OK;
         } else {
-          SPIFFS_DBG("free_obj_id: COMP SEL chunk:%04x min:%04x -> %04x\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i *  state.compaction);
+          SPIFFS_DBG("free_obj_id: COMP SEL chunk:"_SPIPRIi" min:"_SPIPRIid" -> "_SPIPRIid"\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i *  state.compaction);
           state.min_obj_id += min_i *  state.compaction;
           state.max_obj_id = state.min_obj_id + state.compaction;
           // decrease compaction
@@ -1846,10 +2206,10 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co
       // in a work memory of log_page_size bytes, we may fit in log_page_size ids
       // todo what if compaction is > 255 - then we cannot fit it in a byte
       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);
+      SPIFFS_DBG("free_obj_id: COMP min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", state.min_obj_id, state.max_obj_id, state.compaction);
 
       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);
+      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,8 +2218,86 @@ 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) {
+#if SPIFFS_TEMPORAL_FD_CACHE
+// djb2 hash
+static u32_t spiffs_hash(spiffs *fs, const u8_t *name) {
+  (void)fs;
+  u32_t hash = 5381;
+  u8_t c;
+  int i = 0;
+  while ((c = name[i++]) && i < SPIFFS_OBJ_NAME_LEN) {
+    hash = (hash * 33) ^ c;
+  }
+  return hash;
+}
+#endif
+
+s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd, const char *name) {
+#if SPIFFS_TEMPORAL_FD_CACHE
+  u32_t i;
+  u16_t min_score = 0xffff;
+  u32_t cand_ix = (u32_t)-1;
+  u32_t name_hash = name ? spiffs_hash(fs, (const u8_t *)name) : 0;
+  spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
+
+  if (name) {
+    // first, decrease score of all closed descriptors
+    for (i = 0; i < fs->fd_count; i++) {
+      spiffs_fd *cur_fd = &fds[i];
+      if (cur_fd->file_nbr == 0) {
+        if (cur_fd->score > 1) { // score == 0 indicates never used fd
+          cur_fd->score--;
+        }
+      }
+    }
+  }
+
+  // find the free fd with least score or name match
+  for (i = 0; i < fs->fd_count; i++) {
+    spiffs_fd *cur_fd = &fds[i];
+    if (cur_fd->file_nbr == 0) {
+      if (name && cur_fd->name_hash == name_hash) {
+        cand_ix = i;
+        break;
+      }
+      if (cur_fd->score < min_score) {
+        min_score = cur_fd->score;
+        cand_ix = i;
+      }
+    }
+  }
+
+  if (cand_ix != (u32_t)-1) {
+    spiffs_fd *cur_fd = &fds[cand_ix];
+    if (name) {
+      if (cur_fd->name_hash == name_hash && cur_fd->score > 0) {
+        // opened an fd with same name hash, assume same file
+        // set search point to saved obj index page and hope we have a correct match directly
+        // when start searching - if not, we will just keep searching until it is found
+        fs->cursor_block_ix = SPIFFS_BLOCK_FOR_PAGE(fs, cur_fd->objix_hdr_pix);
+        fs->cursor_obj_lu_entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, cur_fd->objix_hdr_pix);
+        // update score
+        if (cur_fd->score < 0xffff-SPIFFS_TEMPORAL_CACHE_HIT_SCORE) {
+          cur_fd->score += SPIFFS_TEMPORAL_CACHE_HIT_SCORE;
+        } else {
+          cur_fd->score = 0xffff;
+        }
+      } else {
+        // no hash hit, restore this fd to initial state
+        cur_fd->score = SPIFFS_TEMPORAL_CACHE_HIT_SCORE;
+        cur_fd->name_hash = name_hash;
+      }
+    }
+    cur_fd->file_nbr = cand_ix+1;
+    *fd = cur_fd;
+    return SPIFFS_OK;
+  } else {
+    return SPIFFS_ERR_OUT_OF_FILE_DESCS;
+  }
+#else
+  (void)name;
   u32_t i;
   spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
   for (i = 0; i < fs->fd_count; i++) {
@@ -1871,6 +2309,7 @@ s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd) {
     }
   }
   return SPIFFS_ERR_OUT_OF_FILE_DESCS;
+#endif
 }
 
 s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) {
@@ -1883,6 +2322,9 @@ s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) {
     return SPIFFS_ERR_FILE_CLOSED;
   }
   fd->file_nbr = 0;
+#if SPIFFS_IX_MAP
+  fd->ix_map = 0;
+#endif
   return SPIFFS_OK;
 }
 
@@ -1897,3 +2339,21 @@ s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) {
   }
   return SPIFFS_OK;
 }
+
+#if SPIFFS_TEMPORAL_FD_CACHE
+void spiffs_fd_temporal_cache_rehash(
+    spiffs *fs,
+    const char *old_path,
+    const char *new_path) {
+  u32_t i;
+  u32_t old_hash = spiffs_hash(fs, (const u8_t *)old_path);
+  u32_t new_hash = spiffs_hash(fs, (const u8_t *)new_path);
+  spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
+  for (i = 0; i < fs->fd_count; i++) {
+    spiffs_fd *cur_fd = &fds[i];
+    if (cur_fd->score > 0 && cur_fd->name_hash == old_hash) {
+      cur_fd->name_hash = new_hash;
+    }
+  }
+}
+#endif
diff --git a/components/spiffs/include/spiffs/spiffs_nucleus.h b/components/spiffs/spiffs/src/spiffs_nucleus.h
similarity index 81%
rename from components/spiffs/include/spiffs/spiffs_nucleus.h
rename to components/spiffs/spiffs/src/spiffs_nucleus.h
index 80cc1cff..dd1c414b 100644
--- a/components/spiffs/include/spiffs/spiffs_nucleus.h
+++ b/components/spiffs/spiffs/src/spiffs_nucleus.h
@@ -116,13 +116,23 @@
 #define SPIFFS_ERR_CHECK_FLAGS_BAD      (SPIFFS_ERR_INTERNAL - 3)
 #define _SPIFFS_ERR_CHECK_LAST          (SPIFFS_ERR_INTERNAL - 4)
 
+// visitor result, continue searching
 #define SPIFFS_VIS_COUNTINUE            (SPIFFS_ERR_INTERNAL - 20)
+// visitor result, continue searching after reloading lu buffer
 #define SPIFFS_VIS_COUNTINUE_RELOAD     (SPIFFS_ERR_INTERNAL - 21)
+// visitor result, stop searching
 #define SPIFFS_VIS_END                  (SPIFFS_ERR_INTERNAL - 22)
 
-#define SPIFFS_EV_IX_UPD                0
-#define SPIFFS_EV_IX_NEW                1
-#define SPIFFS_EV_IX_DEL                2
+// updating an object index contents
+#define SPIFFS_EV_IX_UPD                (0)
+// creating a new object index
+#define SPIFFS_EV_IX_NEW                (1)
+// deleting an object index
+#define SPIFFS_EV_IX_DEL                (2)
+// moving an object index without updating contents
+#define SPIFFS_EV_IX_MOV                (3)
+// updating an object index header data only, not the table itself
+#define SPIFFS_EV_IX_UPD_HDR            (4)
 
 #define SPIFFS_OBJ_ID_IX_FLAG           ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1)))
 
@@ -131,7 +141,31 @@
 #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 defined(__GNUC__) || defined(__clang__)
+    /* For GCC and clang */
+#define SPIFFS_PACKED __attribute__((packed))
+#elif defined(__ICCARM__) || defined(__CC_ARM)
+    /* For IAR ARM and Keil MDK-ARM compilers */
+#define SPIFFS_PACKED 
+
+#else
+    /* Unknown compiler */
+#define SPIFFS_PACKED 
+#endif
+
+
+
+#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)
 
@@ -220,6 +254,17 @@
 // object index span index number for given data span index or entry
 #define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \
   ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs)))
+// get data span index for object index span index
+#define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \
+  ( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) )
+
+#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
 
 
 #define SPIFFS_OP_T_OBJ_LU    (0<<0)
@@ -264,26 +309,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) \
@@ -304,13 +349,33 @@
     if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH;
 
 
-// check id
+// check id, only visit matching objec ids
 #define SPIFFS_VIS_CHECK_ID     (1<<0)
 // report argument object id to visitor - else object lookup id is reported
 #define SPIFFS_VIS_CHECK_PH     (1<<1)
 // 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)
@@ -390,13 +455,23 @@ typedef struct {
   spiffs_span_ix cursor_objix_spix;
   // current absolute offset
   u32_t offset;
-  // current file descriptor offset
+  // current file descriptor offset (cached)
   u32_t fdoffset;
   // fd flags
   spiffs_flags flags;
 #if SPIFFS_CACHE_WR
   spiffs_cache_page *cache_page;
 #endif
+#if SPIFFS_TEMPORAL_FD_CACHE
+  // djb2 hash of filename
+  u32_t name_hash;
+  // hit score (score == 0 indicates never used fd)
+  u16_t score;
+#endif
+#if SPIFFS_IX_MAP
+  // spiffs index map, if 0 it means unmapped
+  spiffs_ix_map *ix_map;
+#endif
 } spiffs_fd;
 
 
@@ -405,7 +480,7 @@ typedef struct {
 // page header, part of each page except object lookup pages
 // NB: this is always aligned when the data page is an object index,
 // as in this case struct spiffs_page_object_ix is used
-typedef struct __attribute(( packed )) {
+typedef struct SPIFFS_PACKED {
   // object id
   spiffs_obj_id obj_id;
   // object span index
@@ -415,7 +490,7 @@ typedef struct __attribute(( packed )) {
 } spiffs_page_header;
 
 // object index header page header
-typedef struct __attribute(( packed ))
+typedef struct SPIFFS_PACKED
 #if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
                 __attribute(( aligned(sizeof(spiffs_page_ix)) ))
 #endif
@@ -423,24 +498,28 @@ typedef struct __attribute(( packed ))
   // 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)];
+  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
   spiffs_obj_type type;
   // name of object
   u8_t name[SPIFFS_OBJ_NAME_LEN];
+#if SPIFFS_OBJ_META_LEN
+  // metadata. not interpreted by SPIFFS in any way.
+  u8_t meta[SPIFFS_OBJ_META_LEN];
+#endif
 } spiffs_page_object_ix_header;
 
 // object index page header
-typedef struct __attribute(( packed )) {
+typedef struct SPIFFS_PACKED {
  spiffs_page_header p_hdr;
- u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)];
+ 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
@@ -501,8 +580,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);
 
@@ -510,6 +589,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(
@@ -518,7 +602,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,
@@ -579,7 +663,8 @@ 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[],
+    const u8_t meta[],
     spiffs_obj_type type,
     spiffs_page_ix *objix_hdr_pix);
 
@@ -589,13 +674,24 @@ 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[],
+    const u8_t meta[],
     u32_t size,
     spiffs_page_ix *new_pix);
 
-void spiffs_cb_object_event(
+#if SPIFFS_IX_MAP
+
+s32_t spiffs_populate_ix_map(
     spiffs *fs,
     spiffs_fd *fd,
+    u32_t vec_entry_start,
+    u32_t vec_entry_end);
+
+#endif
+
+void spiffs_cb_object_event(
+    spiffs *fs,
+    spiffs_page_object_ix *objix,
     int ev,
     spiffs_obj_id obj_id,
     spiffs_span_ix spix,
@@ -641,7 +737,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);
 
 // ---------------
@@ -671,7 +767,8 @@ s32_t spiffs_gc_quick(
 
 s32_t spiffs_fd_find_new(
     spiffs *fs,
-    spiffs_fd **fd);
+    spiffs_fd **fd,
+    const char *name);
 
 s32_t spiffs_fd_return(
     spiffs *fs,
@@ -682,6 +779,13 @@ s32_t spiffs_fd_get(
     spiffs_file f,
     spiffs_fd **fd);
 
+#if SPIFFS_TEMPORAL_FD_CACHE
+void spiffs_fd_temporal_cache_rehash(
+    spiffs *fs,
+    const char *old_path,
+    const char *new_path);
+#endif
+
 #if SPIFFS_CACHE
 void spiffs_cache_init(
     spiffs *fs);
@@ -715,4 +819,24 @@ s32_t spiffs_page_consistency_check(
 s32_t spiffs_object_index_consistency_check(
     spiffs *fs);
 
+// memcpy macro,
+// checked in test builds, otherwise plain memcpy (unless already defined)
+#ifdef _SPIFFS_TEST
+#define _SPIFFS_MEMCPY(__d, __s, __l) do { \
+    intptr_t __a1 = (intptr_t)((u8_t*)(__s)); \
+    intptr_t __a2 = (intptr_t)((u8_t*)(__s)+(__l)); \
+    intptr_t __b1 = (intptr_t)((u8_t*)(__d)); \
+    intptr_t __b2 = (intptr_t)((u8_t*)(__d)+(__l)); \
+    if (__a1 <= __b2 && __b1 <= __a2) { \
+      printf("FATAL OVERLAP: memcpy from %lx..%lx to %lx..%lx\n", __a1, __a2, __b1, __b2); \
+      ERREXIT(); \
+    } \
+    memcpy((__d),(__s),(__l)); \
+} while (0)
+#else
+#ifndef _SPIFFS_MEMCPY
+#define _SPIFFS_MEMCPY(__d, __s, __l) do{memcpy((__d),(__s),(__l));}while(0)
+#endif
+#endif //_SPIFFS_TEST
+
 #endif /* SPIFFS_NUCLEUS_H_ */
diff --git a/components/spiffs/spiffs/src/test/main.c b/components/spiffs/spiffs/src/test/main.c
new file mode 100644
index 00000000..4d363d59
--- /dev/null
+++ b/components/spiffs/spiffs/src/test/main.c
@@ -0,0 +1,12 @@
+#include <stdlib.h>
+
+#ifndef NO_TEST
+#include "testrunner.h"
+#endif
+
+int main(int argc, char **args) {
+#ifndef NO_TEST
+  run_tests(argc, args);
+#endif
+  exit(EXIT_SUCCESS);
+}
diff --git a/components/spiffs/spiffs/src/test/params_test.h b/components/spiffs/spiffs/src/test/params_test.h
new file mode 100644
index 00000000..74f553ff
--- /dev/null
+++ b/components/spiffs/spiffs/src/test/params_test.h
@@ -0,0 +1,84 @@
+/*
+ * params_test.h
+ *
+ *  Created on: May 26, 2013
+ *      Author: petera
+ */
+
+#ifndef PARAMS_TEST_H_
+#define PARAMS_TEST_H_
+
+//////////////// TEST PARAMS ////////////////
+
+// default test total emulated spi flash size
+#define PHYS_FLASH_SIZE       (16*1024*1024)
+// default test spiffs file system size
+#define SPIFFS_FLASH_SIZE     (2*1024*1024)
+// default test spiffs file system offset in emulated spi flash
+#define SPIFFS_PHYS_ADDR      (4*1024*1024)
+// default test sector size
+#define SECTOR_SIZE         65536
+// default test logical block size
+#define LOG_BLOCK           (SECTOR_SIZE*2)
+// default test logical page size
+#define LOG_PAGE            (SECTOR_SIZE/256)
+// default test number of filedescs
+#define DEFAULT_NUM_FD            16
+// default test number of cache pages
+#define DEFAULT_NUM_CACHE_PAGES   8
+
+// When testing, test bench create reference files for comparison on
+// the actual hard drive. By default, put these on ram drive for speed.
+#define TEST_PATH "/dev/shm/spiffs/test-data/"
+
+#define ASSERT(c, m) real_assert((c),(m), __FILE__, __LINE__);
+void real_assert(int c, const char *n, const char *file, int l);
+
+/////////// SPIFFS BUILD CONFIG  ////////////
+
+// test using filesystem magic
+#ifndef SPIFFS_USE_MAGIC
+#define SPIFFS_USE_MAGIC    1
+#endif
+// test using filesystem magic length
+#ifndef SPIFFS_USE_MAGIC_LENGTH
+#define SPIFFS_USE_MAGIC_LENGTH   1
+#endif
+// test using extra param in callback
+#ifndef SPIFFS_HAL_CALLBACK_EXTRA
+#define SPIFFS_HAL_CALLBACK_EXTRA       1
+#endif
+// test using filehandle offset
+#ifndef SPIFFS_FILEHDL_OFFSET
+#define SPIFFS_FILEHDL_OFFSET           1
+// use this offset
+#define TEST_SPIFFS_FILEHDL_OFFSET      0x1000
+#endif
+
+#ifdef NO_TEST
+#define SPIFFS_LOCK(fs)
+#define SPIFFS_UNLOCK(fs)
+#else
+struct spiffs_t;
+extern void test_lock(struct spiffs_t *fs);
+extern void test_unlock(struct spiffs_t *fs);
+#define SPIFFS_LOCK(fs)   test_lock(fs)
+#define SPIFFS_UNLOCK(fs) test_unlock(fs)
+#endif
+
+// dbg output
+#define SPIFFS_DBG(_f, ...) //printf("\x1b[32m" _f "\x1b[0m", ## __VA_ARGS__)
+#define SPIFFS_API_DBG(_f, ...) //printf("\n\x1b[1m\x1b[7m" _f "\x1b[0m", ## __VA_ARGS__)
+#define SPIFFS_GC_DBG(_f, ...) //printf("\x1b[36m" _f "\x1b[0m", ## __VA_ARGS__)
+#define SPIFFS_CACHE_DBG(_f, ...) //printf("\x1b[33m" _f "\x1b[0m", ## __VA_ARGS__)
+#define SPIFFS_CHECK_DBG(_f, ...) //printf("\x1b[31m" _f "\x1b[0m", ## __VA_ARGS__)
+
+// needed types
+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;
+
+#endif /* PARAMS_TEST_H_ */
diff --git a/components/spiffs/spiffs/src/test/test_bugreports.c b/components/spiffs/spiffs/src/test/test_bugreports.c
new file mode 100644
index 00000000..e9b59d18
--- /dev/null
+++ b/components/spiffs/spiffs/src/test/test_bugreports.c
@@ -0,0 +1,1266 @@
+/*
+ * 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>
+
+/* The follow defines control details of how the fuzzer can exercise the API. If you
+ * undef any of these, then the fuzzer is less brutal. FOr example, if you undef
+ * HAVE_REMOVE_OPEN, then the fuzzer will not attempt to remove (or rename) an open file
+ */
+#define HAVE_REMOVE_OPEN
+#define HAVE_MULTIPLE_OPEN
+#define NO_FORCE_CHECK
+
+SUITE(bug_tests)
+static void setup() {
+  _setup_test_only();
+}
+static void teardown() {
+  _teardown();
+}
+
+TEST(nodemcu_full_fs_1) {
+  fs_reset_specific(0, 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 < 0);
+  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
+  res2 = SPIFFS_stat(FS, "test1.txt", &s);
+  TEST_CHECK(res2 < 0);
+  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
+
+TEST(nodemcu_full_fs_2) {
+  fs_reset_specific(0, 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);
+#if 0
+  // before gc in v3.1
+  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);
+#else
+  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FULL);
+  SPIFFS_clearerr(FS);
+#endif
+  printf("  remove files\n");
+  res = SPIFFS_remove(FS, "test1.txt");
+  TEST_CHECK(res == SPIFFS_OK);
+#if 0
+  res = SPIFFS_remove(FS, "test2.txt");
+  TEST_CHECK(res == SPIFFS_OK);
+#endif
+
+  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
+
+TEST(magic_test) {
+  // this test only works on default sizes
+  TEST_ASSERT(sizeof(spiffs_obj_id) == sizeof(u16_t));
+
+  // one obj lu page, not full
+  fs_reset_specific(0, 0, 4096*16, 4096, 4096*1, 128);
+  TEST_CHECK(SPIFFS_CHECK_MAGIC_POSSIBLE(FS));
+  // one obj lu page, full
+  fs_reset_specific(0, 0, 4096*16, 4096, 4096*2, 128);
+  TEST_CHECK(!SPIFFS_CHECK_MAGIC_POSSIBLE(FS));
+  // two obj lu pages, not full
+  fs_reset_specific(0, 0, 4096*16, 4096, 4096*4, 128);
+  TEST_CHECK(SPIFFS_CHECK_MAGIC_POSSIBLE(FS));
+
+  return TEST_RES_OK;
+
+} TEST_END
+
+TEST(nodemcu_309) {
+  fs_reset_specific(0, 0, 4096*20, 4096, 4096, 256);
+
+  int res;
+  spiffs_file fd;
+  int j;
+
+  for (j = 1; j <= 3; j++) {
+    char fname[32];
+    sprintf(fname, "20K%i.txt", j);
+    fd = SPIFFS_open(FS, fname, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_DIRECT, 0);
+    TEST_CHECK(fd > 0);
+    int i;
+    res = SPIFFS_OK;
+    u8_t err = 0;
+    for (i = 1; i <= 1280; i++) {
+      char *buf = "0123456789ABCDE\n";
+      res = SPIFFS_write(FS, fd, buf, strlen(buf));
+      if (!err && res < 0) {
+        printf("err @ %i,%i\n", i, j);
+        err = 1;
+      }
+    }
+  }
+
+  int errno = SPIFFS_errno(FS);
+  TEST_CHECK(errno == SPIFFS_ERR_FULL);
+
+  u32_t total;
+  u32_t used;
+
+  SPIFFS_info(FS, &total, &used);
+  printf("total:%i\nused:%i\nremain:%i\nerrno:%i\n", total, used, total-used, errno);
+  //TEST_CHECK(total-used < 11000); // disabled, depends on too many variables
+
+  spiffs_DIR d;
+  struct spiffs_dirent e;
+  struct spiffs_dirent *pe = &e;
+
+  SPIFFS_opendir(FS, "/", &d);
+  int spoon_guard = 0;
+  while ((pe = SPIFFS_readdir(&d, pe))) {
+    printf("%s [%04x] size:%i\n", pe->name, pe->obj_id, pe->size);
+    TEST_CHECK(spoon_guard++ < 3);
+  }
+  TEST_CHECK(spoon_guard == 3);
+  SPIFFS_closedir(&d);
+
+  return TEST_RES_OK;
+
+} TEST_END
+
+
+TEST(robert) {
+  // create a clean file system starting at address 0, 2 megabytes big,
+  // sector size 65536, block size 65536, page size 256
+  fs_reset_specific(0, 0, 1024*1024*2, 65536, 65536, 256);
+
+  int res;
+  spiffs_file fd;
+  char fname[32];
+
+  sprintf(fname, "test.txt");
+  fd = SPIFFS_open(FS, fname, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
+  TEST_CHECK(fd > 0);
+  res = SPIFFS_OK;
+  char buf[500];
+  memset(buf, 0xaa, 500);
+  res = SPIFFS_write(FS, fd, buf, 500);
+  TEST_CHECK(res >= SPIFFS_OK);
+  SPIFFS_close(FS, fd);
+
+  int errno = SPIFFS_errno(FS);
+  TEST_CHECK(errno == SPIFFS_OK);
+
+  //SPIFFS_vis(FS);
+  // unmount
+  SPIFFS_unmount(FS);
+
+  // remount
+  res = fs_mount_specific(0, 1024*1024*2, 65536, 65536, 256);
+  TEST_CHECK(res== SPIFFS_OK);
+
+  //SPIFFS_vis(FS);
+
+  spiffs_stat s;
+  TEST_CHECK(SPIFFS_stat(FS, fname, &s) == SPIFFS_OK);
+  printf("file %s stat size %i\n", s.name, s.size);
+  TEST_CHECK(s.size == 500);
+
+  return TEST_RES_OK;
+
+} TEST_END
+
+
+TEST(spiffs_12) {
+  fs_reset_specific(0x4024c000, 0x4024c000 + 0, 192*1024, 4096, 4096*2, 256);
+
+  int res;
+  spiffs_file fd;
+  int j = 1;
+
+  while (1) {
+    char fname[32];
+    sprintf(fname, "file%i.txt", j);
+    fd = SPIFFS_open(FS, fname, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_DIRECT, 0);
+    if (fd <=0) break;
+
+    int i;
+    res = SPIFFS_OK;
+    for (i = 1; i <= 100; i++) {
+      char *buf = "0123456789ABCDE\n";
+      res = SPIFFS_write(FS, fd, buf, strlen(buf));
+      if (res < 0) break;
+    }
+    SPIFFS_close(FS, fd);
+    j++;
+  }
+
+  int errno = SPIFFS_errno(FS);
+  TEST_CHECK(errno == SPIFFS_ERR_FULL);
+
+  u32_t total;
+  u32_t used;
+
+  SPIFFS_info(FS, &total, &used);
+  printf("total:%i (%iK)\nused:%i (%iK)\nremain:%i (%iK)\nerrno:%i\n", total, total/1024, used, used/1024, total-used, (total-used)/1024, errno);
+
+  spiffs_DIR d;
+  struct spiffs_dirent e;
+  struct spiffs_dirent *pe = &e;
+
+  SPIFFS_opendir(FS, "/", &d);
+  while ((pe = SPIFFS_readdir(&d, pe))) {
+    printf("%s [%04x] size:%i\n", pe->name, pe->obj_id, pe->size);
+  }
+  SPIFFS_closedir(&d);
+
+  //SPIFFS_vis(FS);
+
+  //dump_page(FS, 0);
+  //dump_page(FS, 1);
+
+  return TEST_RES_OK;
+
+} TEST_END
+
+
+TEST(zero_sized_file_44) {
+  fs_reset();
+
+  spiffs_file fd = SPIFFS_open(FS, "zero", SPIFFS_RDWR | SPIFFS_CREAT, 0);
+  TEST_CHECK_GE(fd, 0);
+
+  int res = SPIFFS_close(FS, fd);
+  TEST_CHECK_GE(res, 0);
+
+  fd = SPIFFS_open(FS, "zero", SPIFFS_RDWR, 0);
+  TEST_CHECK_GE(fd, 0);
+
+  u8_t buf[8];
+  res = SPIFFS_read(FS, fd, buf, 8);
+  TEST_CHECK_EQ(res, 0);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_END_OF_OBJECT);
+
+  res = SPIFFS_read(FS, fd, buf, 0);
+  TEST_CHECK_GE(res, 0);
+
+  res = SPIFFS_read(FS, fd, buf, 0);
+  TEST_CHECK_GE(res, 0);
+
+  buf[0] = 1;
+  buf[1] = 2;
+
+  res = SPIFFS_write(FS, fd, buf, 2);
+  TEST_CHECK_EQ(res, 2);
+
+  res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_SET);
+  TEST_CHECK_GE(res, 0);
+
+  u8_t b;
+  res = SPIFFS_read(FS, fd, &b, 1);
+  TEST_CHECK_EQ(res, 1);
+  TEST_CHECK_EQ(b, 1);
+
+  res = SPIFFS_read(FS, fd, &b, 1);
+  TEST_CHECK_EQ(res, 1);
+  TEST_CHECK_EQ(b, 2);
+
+  res = SPIFFS_read(FS, fd, buf, 8);
+  TEST_CHECK_EQ(res, 0);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_END_OF_OBJECT);
+
+  return TEST_RES_OK;
+} TEST_END
+
+#if !SPIFFS_READ_ONLY
+TEST(truncate_48) {
+  fs_reset();
+
+  u32_t len = SPIFFS_DATA_PAGE_SIZE(FS)/2;
+
+  s32_t res = test_create_and_write_file("small", len, len);
+  TEST_CHECK_GE(res, 0);
+
+  spiffs_file fd = SPIFFS_open(FS, "small", SPIFFS_RDWR, 0);
+  TEST_CHECK_GE(fd, 0);
+
+  spiffs_fd *desc;
+#if SPIFFS_FILEHDL_OFFSET
+  res = spiffs_fd_get(FS, fd - TEST_SPIFFS_FILEHDL_OFFSET, &desc);
+#else
+  res = spiffs_fd_get(FS, fd, &desc);
+#endif
+
+  TEST_CHECK_GE(res, 0);
+
+  TEST_CHECK_EQ(desc->size, len);
+
+  u32_t new_len = len/2;
+  res = spiffs_object_truncate(desc, new_len, 0);
+  TEST_CHECK_GE(res, 0);
+
+  TEST_CHECK_EQ(desc->size, new_len);
+
+  res = SPIFFS_close(FS, fd);
+  TEST_CHECK_GE(res, 0);
+
+  spiffs_stat s;
+  res = SPIFFS_stat(FS, "small", &s);
+  TEST_CHECK_GE(res, 0);
+  TEST_CHECK_EQ(s.size, new_len);
+
+  res = SPIFFS_remove(FS, "small");
+  TEST_CHECK_GE(res, 0);
+
+  fd = SPIFFS_open(FS, "small", SPIFFS_RDWR, 0);
+  TEST_CHECK_LT(fd, 0);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_FOUND);
+
+  return TEST_RES_OK;
+} TEST_END
+#endif
+
+TEST(eof_tell_72) {
+  fs_reset();
+
+  s32_t res;
+
+  spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_CREAT | SPIFFS_RDWR | SPIFFS_APPEND, 0);
+  TEST_CHECK_GT(fd, 0);
+  TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 1);
+  TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 0);
+
+  res = SPIFFS_write(FS, fd, "test", 4);
+  TEST_CHECK_EQ(res, 4);
+  TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 1);
+  TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 4);
+
+  res = SPIFFS_fflush(FS, fd);
+  TEST_CHECK_EQ(res, SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 1);
+  TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 4);
+
+  res = SPIFFS_lseek(FS, fd, 2, SPIFFS_SEEK_SET);
+  TEST_CHECK_EQ(res, 2);
+  TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 0);
+  TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 2);
+
+  res = SPIFFS_write(FS, fd, "test", 4);
+  TEST_CHECK_EQ(res, 4);
+  TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 1);
+  TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 8);
+
+  res = SPIFFS_fflush(FS, fd);
+  TEST_CHECK_EQ(res, SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 1);
+  TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 8);
+
+  res = SPIFFS_close(FS, fd);
+  TEST_CHECK_EQ(res, SPIFFS_OK);
+  TEST_CHECK_LT(SPIFFS_eof(FS, fd), SPIFFS_OK);
+  TEST_CHECK_LT(SPIFFS_tell(FS, fd), SPIFFS_OK);
+
+  fd = SPIFFS_open(FS, "file", SPIFFS_RDWR, 0);
+  TEST_CHECK_GT(fd, 0);
+  TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 0);
+  TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 0);
+
+  res = SPIFFS_lseek(FS, fd, 2, SPIFFS_SEEK_SET);
+  TEST_CHECK_EQ(res, 2);
+  TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 0);
+  TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 2);
+
+  res = SPIFFS_write(FS, fd, "test", 4);
+  TEST_CHECK_EQ(res, 4);
+  TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 0);
+  TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 6);
+
+  res = SPIFFS_fflush(FS, fd);
+  TEST_CHECK_EQ(res, SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 0);
+  TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 6);
+
+  res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_END);
+  TEST_CHECK_EQ(res, 8);
+  TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 1);
+  TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 8);
+
+  return TEST_RES_OK;
+} TEST_END
+
+TEST(spiffs_dup_file_74) {
+  fs_reset_specific(0, 0, 64*1024, 4096, 4096*2, 256);
+  {
+    spiffs_file fd = SPIFFS_open(FS, "/config", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_WRONLY, 0);
+    TEST_CHECK(fd >= 0);
+    char buf[5];
+    strncpy(buf, "test", sizeof(buf));
+    SPIFFS_write(FS, fd, buf, 4);
+    SPIFFS_close(FS, fd);
+  }
+  {
+    spiffs_file fd = SPIFFS_open(FS, "/data", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_WRONLY, 0);
+    TEST_CHECK(fd >= 0);
+    SPIFFS_close(FS, fd);
+  }
+  {
+    spiffs_file fd = SPIFFS_open(FS, "/config", SPIFFS_RDONLY, 0);
+    TEST_CHECK(fd >= 0);
+    char buf[5];
+    int cb = SPIFFS_read(FS, fd, buf, sizeof(buf));
+    TEST_CHECK(cb > 0 && cb < sizeof(buf));
+    TEST_CHECK(strncmp("test", buf, cb) == 0);
+    SPIFFS_close(FS, fd);
+  }
+  {
+    spiffs_file fd = SPIFFS_open(FS, "/data", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_WRONLY, 0);
+    TEST_CHECK(fd >= 0);
+    spiffs_stat stat;
+    SPIFFS_fstat(FS, fd, &stat);
+    if (strcmp((const char*) stat.name, "/data") != 0) {
+      // oops! lets check the list of files...
+      spiffs_DIR dir;
+      SPIFFS_opendir(FS, "/", &dir);
+      struct spiffs_dirent dirent;
+      while (SPIFFS_readdir(&dir, &dirent)) {
+        printf("%s\n", dirent.name);
+      }
+      // this will print "/config" two times
+      TEST_CHECK(0);
+    }
+    SPIFFS_close(FS, fd);
+  }
+  return TEST_RES_OK;
+} TEST_END
+
+TEST(temporal_fd_cache) {
+  fs_reset_specific(0, 0, 1024*1024, 4096, 2*4096, 256);
+  (FS)->fd_count = 4;
+
+  char *fcss = "blaha.css";
+
+  char *fhtml[] = {
+      "index.html", "cykel.html", "bloja.html", "drivmedel.html", "smorgasbord.html",
+      "ombudsman.html", "fubbick.html", "paragrod.html"
+  };
+
+  const int hit_probabilities[] = {
+      25, 20, 16, 12, 10, 8, 5, 4
+  };
+
+  const int runs = 10000;
+
+  // create our webserver files
+  TEST_CHECK_EQ(test_create_and_write_file(fcss, 2000, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(test_create_and_write_file(fhtml[0], 4000, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(test_create_and_write_file(fhtml[1], 3000, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(test_create_and_write_file(fhtml[2], 2000, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(test_create_and_write_file(fhtml[3], 1000, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(test_create_and_write_file(fhtml[4], 1500, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(test_create_and_write_file(fhtml[5], 3000, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(test_create_and_write_file(fhtml[6], 2000, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(test_create_and_write_file(fhtml[7], 3500, 256), SPIFFS_OK);
+
+  clear_flash_ops_log();
+
+  int run = 0;
+  do {
+    // open & read an html
+    int dice = rand() % 100;
+    int probability = 0;
+    int html_ix = 0;
+    do {
+      probability += hit_probabilities[html_ix];
+      if (dice <= probability) {
+        break;
+      }
+      html_ix++;
+    } while(probability < 100);
+
+    TEST_CHECK_EQ(read_and_verify(fhtml[html_ix]), SPIFFS_OK);
+
+    // open & read css
+    TEST_CHECK_EQ(read_and_verify(fcss), SPIFFS_OK);
+  } while (run ++ < runs);
+
+  return TEST_RES_OK;
+} TEST_END
+
+static int run_fuzz_test(FILE *f, int maxfds, int debuglog) {
+  // There are a bunch of arbitrary constants in this test case. Changing them will
+  // almost certainly change the effets of an input file. It *may* be worth
+  // making some of these constants to come from the input file. 
+  int setup = fgetc(f);
+
+  int page_size = 128 << (setup & 3);
+  setup >>= 2;
+  int erase_size = 4096 << (setup & 3);
+  setup >>= 2;
+  int block_size = erase_size << (setup & 1);
+  setup >>= 1;
+  int blocks = 4 + (setup & 7);
+  fs_reset_specific(0, 0, blocks * block_size, erase_size, block_size, page_size);
+  int res;
+  (FS)->fd_count = 4;
+
+  int c;
+
+  spiffs_file fd[4];
+  memset(fd, -1, sizeof(fd));
+  int openindex[4];
+  memset(openindex, -1, sizeof(openindex));
+  char *filename[8];
+
+  int i;
+
+  for (i = 0; i < 8; i++) {
+    char buff[128];
+    sprintf(buff, "%dfile%d.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxasdasdasdadxxxxxxxxxxxxxxxxxxx", i, i);
+    buff[9 + 2 * i] = 0;
+    filename[i] = strdup(buff);
+  }
+
+  // The list of 8 modes that are chosen. SPIFFS_EXCL is not present -- it probably ought to be.
+  int modes[8] = {SPIFFS_RDONLY, SPIFFS_RDWR, SPIFFS_RDWR|SPIFFS_TRUNC, SPIFFS_RDWR|SPIFFS_CREAT, SPIFFS_RDWR|SPIFFS_CREAT|SPIFFS_TRUNC,
+      SPIFFS_WRONLY|SPIFFS_CREAT|SPIFFS_TRUNC, SPIFFS_RDWR|SPIFFS_CREAT|SPIFFS_TRUNC|SPIFFS_DIRECT, SPIFFS_WRONLY};
+
+  char buff[2048];
+  for (i = 0; i < sizeof(buff); i++) {
+    buff[i] = i * 19;
+  }
+
+#define LOGOP if (debuglog) printf
+
+  while ((c = fgetc(f)) >= 0) {
+    int add;
+    char rbuff[2048];
+    if (c <= ' ') {
+      continue;
+    }
+    int arg = fgetc(f);
+    if (arg < 0) {
+      break;
+    }
+    int fdn = ((arg >> 6) & 3) % maxfds;
+    int rc;
+    switch(c) {
+    case 'O':
+      if (fd[fdn] >= 0) {
+        LOGOP("  close(%d)\n", fd[fdn]);
+        SPIFFS_close(FS, fd[fdn]);
+        openindex[fdn] = -1;
+        fd[fdn] = -1;
+      }
+#ifndef HAVE_MULTIPLE_OPEN
+      {
+        int index = (arg >> 3) & 7;
+        for (i = 0; i < sizeof(openindex) / sizeof(openindex[0]); i++) {
+          if (openindex[i] == index) {
+            break;
+          }
+        }
+        if (i < sizeof(openindex) / sizeof(openindex[0])) {
+          break;
+        }
+      }
+#endif
+      LOGOP("  open(\"%s\", 0x%x)", filename[(arg>>3) & 7], modes[arg & 7]);
+      fd[fdn] = SPIFFS_open(FS, filename[(arg>>3) & 7], modes[arg & 7], 0);
+      if (fd[fdn] >= 0) {
+        openindex[fdn] = (arg >> 3) & 7;
+      }
+      LOGOP(" -> %d\n", fd[fdn]);
+      break;
+
+    case 'S':
+      if (fd[fdn] >= 0) {
+        int offset = (14 << (arg & 7)) + arg;
+        if (arg & 16) {
+          offset = -offset;
+        }
+        int whence = (arg & 63) % 3;
+        LOGOP("  lseek(%d, %d, %d)\n", fd[fdn], offset, whence);
+        SPIFFS_lseek(FS, fd[fdn], offset, whence);
+      }
+      break;
+
+    case 'R':
+      if (fd[fdn] >= 0) {
+        LOGOP("  read(%d, , %d)", fd[fdn], (15 << (arg & 7)) + (arg & 127));
+        int rlen = SPIFFS_read(FS, fd[fdn], rbuff, (15 << (arg & 7)) + (arg & 127));
+        LOGOP(" -> %d\n", rlen);
+      }
+      break;
+
+    case 'W':
+      if (fd[fdn] >= 0) {
+        LOGOP("  write(%d, , %d)", fd[fdn], (15 << (arg & 7)) + (arg & 127));
+        rc = SPIFFS_write(FS, fd[fdn], buff, (15 << (arg & 7)) + (arg & 127));
+        LOGOP(" -> %d\n", rc);
+      }
+      break;
+
+    case 'C':
+      if (fd[fdn] >= 0) {
+        LOGOP("  close(%d)\n", fd[fdn]);
+        SPIFFS_close(FS, fd[fdn]);
+      }
+      fd[fdn] = -1;
+      openindex[fdn] = -1;
+      break;
+
+    case 'b':
+      add = fgetc(f);
+      for (i = 0; i < sizeof(buff); i++) {
+        buff[i] = add + i * arg;
+      }
+      break;
+
+    case 'f':
+      if (fd[fdn] >= 0) {
+        LOGOP("  fflush(%d)\n", fd[fdn]);
+        SPIFFS_fflush(FS, fd[fdn]);
+      }
+      break;
+
+#ifdef HAVE_REMOVE_OPEN
+    case 'D':
+      if (fd[fdn] >= 0) {
+        LOGOP("  fremove(%d)\n", fd[fdn]);
+        SPIFFS_fremove(FS, fd[fdn]);
+      }
+      break;
+#endif
+
+    case 'd':
+#ifndef HAVE_REMOVE_OPEN
+    {
+      int index = arg & 7;
+      for (i = 0; i < sizeof(openindex) / sizeof(openindex[0]); i++) {
+        if (openindex[i] == index) {
+          break;
+        }
+      }
+      if (i < sizeof(openindex) / sizeof(openindex[0])) {
+        break;
+      }
+    }
+#endif
+    LOGOP("  remove(\"%s\")", filename[arg & 7]);
+    rc = SPIFFS_remove(FS, filename[arg & 7]);
+    LOGOP(" -> %d\n", rc);
+    break;
+
+    case 'r':
+#ifndef HAVE_REMOVE_OPEN
+    {
+      int index = arg & 7;
+      for (i = 0; i < sizeof(openindex) / sizeof(openindex[0]); i++) {
+        if (openindex[i] == index) {
+          break;
+        }
+      }
+      if (i < sizeof(openindex) / sizeof(openindex[0])) {
+        break;
+      }
+    }
+#endif
+    LOGOP("  rename(\"%s\", \"%s\")", filename[arg & 7], filename[(arg >> 3) & 7]);
+    rc = SPIFFS_rename(FS, filename[arg & 7], filename[(arg >> 3) & 7]);
+    LOGOP(" -> %d\n", rc);
+    break;
+
+    case 'U':
+      ungetc(arg, f);
+      for (i = 0; i < 4; i++) {
+        fd[i] = -1;
+      }
+      {
+#ifdef DO_UNMOUNT
+        LOGOP("  unmount\n");
+        SPIFFS_unmount(FS);
+#endif
+        char *tmpfile = strdup("/tmp/fsdump.XXXXXX");
+        LOGOP("  cycle and remount\n");
+        close(mkstemp(tmpfile));
+        fs_store_dump(tmpfile);
+        fs_mount_dump(tmpfile, 0, 0, blocks * block_size, erase_size, block_size, page_size);
+        unlink(tmpfile);
+        free(tmpfile);
+#ifndef NO_FORCE_CHECK
+        LOGOP("  forcecheck()");
+        rc = SPIFFS_check(FS);
+        LOGOP(" -> %d\n", rc);
+#endif
+      }
+      break;
+
+    case 'c':
+    {
+      LOGOP("  check()");
+      rc = SPIFFS_check(FS);
+      LOGOP(" -> %d\n", rc);
+      ungetc(arg, f);
+      break;
+    }
+
+    default:
+      ungetc(arg, f);
+      break;
+    }
+  }
+
+  for (i = 0; i < 4; i++) {
+    if (fd[i] >= 0) {
+      LOGOP("  close(%d)\n", fd[i]);
+      SPIFFS_close(FS, fd[i]);
+    }
+  }
+
+  return TEST_RES_OK;
+}
+
+#define FMEMARGS(x)	x, sizeof(x) - 1
+
+TEST(fuzzer_found_1) {
+  return run_fuzz_test(fmemopen(FMEMARGS("\021OlWkd5O4W4W0O5OlWkO5OlW0O5O4W0"), "r"), 4, 1);
+} TEST_END
+
+TEST(fuzzer_found_2) {
+  return run_fuzz_test(fmemopen(FMEMARGS("bO4W6W0d\036O4W6"), "r"), 4, 1);
+} TEST_END
+
+TEST(fuzzer_found_3) {
+  return run_fuzz_test(fmemopen(FMEMARGS("\264O4OqWeWWWWW@O4WWW\027"), "r"), 4, 1);
+} TEST_END
+
+TEST(fuzzer_found_4) {
+  unsigned char smalltest[] = {
+      0x62, 0x4f, 0x24, 0x57, 0x3f, 0x57, 0x3f, 0x57, 0x3f, 0x57, 0x3f, 0x57,
+      0x3f, 0x4f, 0x34, 0x57, 0x3f, 0x55, 0x4f, 0x61, 0x57, 0x61, 0x4f, 0x61,
+      0x57, 0x65, 0x43, 0x61, 0x4f, 0x24, 0x57, 0x30
+  };
+  unsigned int smalltest_len = 32;
+
+  return run_fuzz_test(fmemopen(smalltest, smalltest_len, "r"), 4, 1);
+} TEST_END
+
+TEST(fuzzer_found_single_1) {
+  return run_fuzz_test(fmemopen(FMEMARGS("\000O\004Odr4d\356Okr0WWUO;WWWWd\035W4"), "r"), 1, 1);
+} TEST_END
+
+TEST(log_afl_test) {
+  u32_t old_val = set_abort_on_error(1);
+  int rc = run_fuzz_test(stdin, 4, 1);
+  set_abort_on_error(old_val);
+  return rc;
+} TEST_END
+
+TEST(afl_test) {
+  u32_t old_val = set_abort_on_error(1);
+  int rc = run_fuzz_test(stdin, 4, 0);
+  set_abort_on_error(old_val);
+  return rc;
+} TEST_END
+
+TEST(afl_single) {
+  u32_t old_val = set_abort_on_error(1);
+  int rc = run_fuzz_test(stdin, 1, 0);
+  set_abort_on_error(old_val);
+  return rc;
+} TEST_END
+
+TEST(small_free_space) {
+  fs_reset_specific(0, 0, 400*1024, 4096, 2*4096, 256);
+  spiffs_file fd;
+  int res;
+  (FS)->fd_count = 4;
+
+  int tfd = SPIFFS_open(FS, "testfile", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
+  TEST_CHECK(tfd > 0);
+  char *tbuf = "some data";
+  res = SPIFFS_write(FS, tfd, tbuf, strlen(tbuf));
+
+  TEST_CHECK(res == strlen(tbuf));
+
+  res = SPIFFS_fflush(FS, tfd);
+  TEST_CHECK(res >= SPIFFS_OK);
+
+  SPIFFS_close(FS, tfd);
+
+  const int runs = 1000;
+
+  int fileCurrNumber = 0;
+  int fileDelNumber = 1;
+
+  int run = 0;
+  do {
+    u8_t buf[1000];
+
+    sprintf(buf, "%d", fileCurrNumber);
+    int i;
+    for (i = 0; i < 100; i++) {
+      strcat(buf, " azzaaax");
+    }
+
+    int maxFileNr = 500;
+    char *filename = "awyn";
+    char *fileext = ".dat";
+
+    u32_t total;
+    u32_t used;
+
+    SPIFFS_info(FS, &total, &used);
+
+    if (total - used < 20000) {
+      maxFileNr = 1;
+    }
+
+    fileCurrNumber++;
+    int fileCntr = fileCurrNumber + 1 - fileDelNumber;
+
+    char fileCurrName[64];
+    sprintf(fileCurrName, "%s%d%s", filename, fileCurrNumber, fileext);
+
+    fd = SPIFFS_open(FS, fileCurrName, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
+    TEST_CHECK(fd > 0);
+
+    //printf("About to write to %s\n", fileCurrName);
+    res = SPIFFS_write(FS, fd, buf, strlen(buf));
+
+    TEST_CHECK(res == strlen(buf));
+
+    res = SPIFFS_fflush(FS, fd);
+    TEST_CHECK_EQ(res, SPIFFS_OK);
+
+    SPIFFS_close(FS, fd);
+
+    if (fileCntr > maxFileNr) {
+      char fileDelName[64];
+      sprintf(fileDelName, "%s%d%s", filename, fileDelNumber, fileext);
+      //printf("Deleting %s (free space %d)\n", fileDelName, total - used);
+  
+      res = SPIFFS_remove(FS, fileDelName);
+
+      TEST_CHECK(res == SPIFFS_OK);
+      fileDelNumber++;
+    }
+  } while (run ++ < runs);
+
+  tfd = SPIFFS_open(FS, "testfile", SPIFFS_RDONLY, 0);
+  TEST_CHECK(tfd > 0);
+  char rbuf[32];
+  res = SPIFFS_read(FS, tfd, rbuf, sizeof(rbuf));
+
+  TEST_CHECK(res == strlen(tbuf));
+
+  SPIFFS_close(FS, tfd);
+
+  TEST_CHECK(memcmp(rbuf, tbuf, strlen(tbuf)) == 0);
+
+  return TEST_RES_OK;
+} TEST_END
+
+TEST(lots_of_overwrite) {
+  fs_reset_specific(0, 0, 3000*1024, 4096, 2*4096, 256);
+  spiffs_file fd;
+  int res;
+  (FS)->fd_count = 4;
+
+  int i;
+
+  for (i = 0; i < 5; i++) {
+
+    char filename[64];
+    sprintf(filename, "%d-tstfile", i);
+    int tfd = SPIFFS_open(FS, filename, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
+    TEST_CHECK(tfd > 0);
+    char tbuf[1024];
+    memset(tbuf, 'a', 700);
+    tbuf[700] = 0;
+    res = SPIFFS_write(FS, tfd, tbuf, strlen(tbuf));
+
+    TEST_CHECK(res == strlen(tbuf));
+
+    res = SPIFFS_fflush(FS, tfd);
+    TEST_CHECK(res >= SPIFFS_OK);
+
+    SPIFFS_close(FS, tfd);
+  }
+
+  const int runs = 100000;
+
+  int run = 0;
+  for (run = 0; run < runs; run++) {
+    u8_t buf[2000];
+
+    sprintf(buf, "%d", run);
+    int i;
+    for (i = 0; i < 100 + (run % 100); i++) {
+      strcat(buf, " azzaaax");
+    }
+
+    int tfd = SPIFFS_open(FS, "file.dat", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
+    TEST_CHECK(tfd > 0);
+    res = SPIFFS_write(FS, tfd, buf, strlen(buf));
+
+    TEST_CHECK(res == strlen(buf));
+
+    res = SPIFFS_fflush(FS, tfd);
+    TEST_CHECK(res >= SPIFFS_OK);
+
+    SPIFFS_close(FS, tfd);
+
+    tfd = SPIFFS_open(FS, "file.dat", SPIFFS_RDONLY, 0);
+    TEST_CHECK(tfd > 0);
+    char rbuf[2000];
+    res = SPIFFS_read(FS, tfd, rbuf, sizeof(rbuf));
+
+    TEST_CHECK(res == strlen(buf));
+
+    SPIFFS_close(FS, tfd);
+
+    TEST_CHECK(memcmp(rbuf, buf, strlen(buf)) == 0);
+
+    char filename[64];
+    sprintf(filename, "%d-tstfile", run % 5);
+    tfd = SPIFFS_open(FS, filename, SPIFFS_RDONLY, 0);
+    TEST_CHECK(tfd > 0);
+    char tbuf[1024];
+    memset(tbuf, 'a', 700);
+    tbuf[700] = 0;
+    res = SPIFFS_read(FS, tfd, rbuf, sizeof(rbuf));
+
+    TEST_CHECK(res == strlen(tbuf));
+
+    SPIFFS_close(FS, tfd);
+    TEST_CHECK(memcmp(rbuf, tbuf, strlen(tbuf)) == 0);
+  }
+
+  return TEST_RES_OK;
+} TEST_END
+
+
+#if 0
+TEST(spiffs_hidden_file_90) {
+  fs_mount_dump("imgs/90.hidden_file.spiffs", 0, 0, 1*1024*1024, 4096, 4096, 128);
+
+  SPIFFS_vis(FS);
+
+  dump_page(FS, 1);
+  dump_page(FS, 0x8fe);
+  dump_page(FS, 0x8ff);
+
+  {
+    spiffs_DIR dir;
+    SPIFFS_opendir(FS, "/", &dir);
+    struct spiffs_dirent dirent;
+    while (SPIFFS_readdir(&dir, &dirent)) {
+      printf("%-32s sz:%-7i obj_id:%08x pix:%08x\n", dirent.name, dirent.size, dirent.obj_id, dirent.pix);
+    }
+  }
+
+  printf("remove cli.bin res %i\n", SPIFFS_remove(FS, "cli.bin"));
+
+  {
+    spiffs_DIR dir;
+    SPIFFS_opendir(FS, "/", &dir);
+    struct spiffs_dirent dirent;
+    while (SPIFFS_readdir(&dir, &dirent)) {
+      printf("%-32s sz:%-7i obj_id:%08x pix:%08x\n", dirent.name, dirent.size, dirent.obj_id, dirent.pix);
+    }
+  }
+  return TEST_RES_OK;
+
+} TEST_END
+#endif
+#if 0
+TEST(null_deref_check_93) {
+  fs_mount_dump("imgs/93.dump.bin", 0, 0, 2*1024*1024, 4096, 4096, 256);
+
+  //int res = SPIFFS_open(FS, "d43.fw", SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_WRONLY, 0);
+  //TEST_CHECK_GE(res, SPIFFS_OK);
+
+  SPIFFS_vis(FS);
+
+  printf("\n\n-------------------------------------------------\n\n");
+
+  SPIFFS_check(FS);
+  //fs_store_dump("imgs/93.dump.checked.bin");
+
+  SPIFFS_vis(FS);
+
+  printf("\n\n-------------------------------------------------\n\n");
+
+  SPIFFS_check(FS);
+
+  SPIFFS_vis(FS);
+  printf("\n\n-------------------------------------------------\n\n");
+
+
+
+  return TEST_RES_OK;
+} TEST_END
+#endif
+
+TEST(spiffs_145) {
+  int res;
+  fs_reset_specific(0, 0, 1024*1024, 65536, 65536, 1024);
+  {
+    spiffs_file fd = SPIFFS_open(FS, "biggie", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_WRONLY, 0);
+    TEST_CHECK(fd >= 0);
+    char buf[1024*512];
+    memset(buf, 0xee, sizeof(buf));
+    TEST_CHECK_GT(SPIFFS_write(FS, fd, buf, sizeof(buf)), 0);
+    TEST_CHECK_EQ(SPIFFS_close(FS, fd), SPIFFS_OK);
+  }
+  const int runs = 1000;
+  int run = 0;
+  while (run++ < runs) {
+    spiffs_file fd = SPIFFS_open(FS, "clobber", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_WRONLY, 0);
+    TEST_CHECK(fd >= 0);
+    char buf[8192];
+    memset(buf, 0xee, sizeof(buf));
+    TEST_CHECK_GT(SPIFFS_write(FS, fd, buf, sizeof(buf)), 0);
+    TEST_CHECK_EQ(SPIFFS_close(FS, fd), SPIFFS_OK);
+    TEST_CHECK_EQ(SPIFFS_remove(FS, "clobber"), SPIFFS_OK);
+  }
+
+  // below stolen from SPIFFS_vis
+  spiffs *fs = FS;
+  int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
+  spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
+  spiffs_block_ix bix = 0;
+
+  while (bix < fs->block_count) {
+    // check each object lookup page
+    spiffs_obj_id erase_count;
+    TEST_CHECK_EQ(_spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
+        SPIFFS_ERASE_COUNT_PADDR(fs, bix),
+        sizeof(spiffs_obj_id), (u8_t *)&erase_count), SPIFFS_OK);
+    TEST_CHECK_NEQ(erase_count, (spiffs_obj_id)-1);
+    TEST_CHECK_NEQ(erase_count, 0);
+    bix++;
+  } // per block
+
+  return TEST_RES_OK;
+} TEST_END
+
+
+TEST(seek_bug_148) {
+  int res;
+#define MAGIC_SIZE_THAT_FAILS 26355 // happens to be SPIFFS_DATA_PAGE_SIZE(FS) * SPIFFS_OBJ_HDR_IX_LEN(FS)
+  fs_reset_specific(0, 0, 64*1024, 4096, 4096, 256);
+  u8_t buf[MAGIC_SIZE_THAT_FAILS];
+  spiffs_file fd = SPIFFS_open(FS, "EVENT", SPIFFS_O_CREAT | SPIFFS_O_RDWR, 0);
+  TEST_CHECK_GT(fd, 0);
+  TEST_CHECK_EQ(SPIFFS_write(FS, fd, &buf, sizeof(buf)), sizeof(buf));
+  TEST_CHECK_EQ(SPIFFS_close(FS, fd), SPIFFS_OK);
+  fd = SPIFFS_open(FS, "EVENT", SPIFFS_O_RDONLY, 0);
+  TEST_CHECK_GT(fd, 0);
+  TEST_CHECK_EQ(SPIFFS_lseek(FS, fd, 0, SEEK_END), MAGIC_SIZE_THAT_FAILS);
+  return TEST_RES_OK;
+} TEST_END
+
+
+TEST(remove_release_fd_152) {
+  int res;
+  fs_reset_specific(0, 0, 64*1024, 4096, 4096, 256);
+  u8_t buf[1024];
+  memrand(buf, sizeof(buf));
+  TEST_CHECK_EQ(count_taken_fds(FS), 0);
+  spiffs_file fd1 = SPIFFS_open(FS, "removemeandloseafd", SPIFFS_O_CREAT | SPIFFS_O_RDWR, 0);
+  TEST_CHECK_GT(fd1, 0);
+  TEST_CHECK_EQ(count_taken_fds(FS), 1);
+  TEST_CHECK_EQ(SPIFFS_write(FS, fd1, &buf, sizeof(buf)), sizeof(buf));
+  TEST_CHECK_EQ(SPIFFS_close(FS, fd1), SPIFFS_OK);
+  TEST_CHECK_EQ(count_taken_fds(FS), 0);
+  spiffs_file fd2 = SPIFFS_open(FS, "removemeandloseafd", SPIFFS_O_RDWR, 0);
+  TEST_CHECK_GT(fd2, 0);
+  TEST_CHECK_EQ(count_taken_fds(FS), 1);
+  spiffs_file fd3 = SPIFFS_open(FS, "removemeandloseafd", SPIFFS_O_RDWR, 0);
+  TEST_CHECK_GT(fd3, 0);
+  TEST_CHECK_EQ(count_taken_fds(FS), 2);
+  TEST_CHECK_EQ(SPIFFS_remove(FS, "removemeandloseafd"), SPIFFS_OK);
+  TEST_CHECK_EQ(count_taken_fds(FS), 0);
+  return TEST_RES_OK;
+} TEST_END
+
+TEST(certain_file_size_fail_165) {
+  fs_reset_specific(0, 0, 512*1024, 4*1024, 64*1024, 256);
+  const int NUM = 134;
+  const int SIZ = 200;
+  u8_t buf[SIZ];
+
+  TEST_CHECK_EQ(SPIFFS_creat(FS, "test", 0), SPIFFS_OK);
+  spiffs_file fd = SPIFFS_open(FS, "test", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0);
+  TEST_CHECK_GT(fd, 0);
+
+  int i;
+  for (i = 0; i < NUM; i++) {
+    TEST_CHECK_EQ(SPIFFS_write(FS, fd, buf, SIZ), SIZ);
+  }
+  TEST_CHECK_EQ(SPIFFS_close(FS, fd), SPIFFS_OK);
+  fd = SPIFFS_open(FS, "test", SPIFFS_O_RDONLY, 0);
+  TEST_CHECK_GT(fd, 0);
+
+  spiffs_stat s;
+  TEST_CHECK_EQ(SPIFFS_fstat(FS, fd, &s), SPIFFS_OK);
+  TEST_CHECK_EQ(s.size, NUM*SIZ);
+
+  int size = 0;
+  for (i = 0; i < NUM; i++) {
+    size += SPIFFS_read(FS, fd, buf, SIZ);
+  }
+  TEST_CHECK_EQ(size, NUM*SIZ);
+
+  return TEST_RES_OK;
+} TEST_END
+
+
+SUITE_TESTS(bug_tests)
+  ADD_TEST(nodemcu_full_fs_1)
+  ADD_TEST(nodemcu_full_fs_2)
+  ADD_TEST(magic_test)
+  ADD_TEST(nodemcu_309)
+  ADD_TEST(robert)
+  ADD_TEST(spiffs_12)
+  ADD_TEST(zero_sized_file_44)
+  ADD_TEST(truncate_48)
+  ADD_TEST(eof_tell_72)
+  ADD_TEST(spiffs_dup_file_74)
+  ADD_TEST(temporal_fd_cache)
+  ADD_TEST(spiffs_145)
+  ADD_TEST(seek_bug_148)
+  //ADD_TEST(small_free_space)
+  ADD_TEST(lots_of_overwrite)
+  ADD_TEST(fuzzer_found_1)
+  ADD_TEST(fuzzer_found_2)
+  ADD_TEST(fuzzer_found_3)
+  ADD_TEST(fuzzer_found_4)
+  ADD_TEST(remove_release_fd_152)
+  ADD_TEST(certain_file_size_fail_165)
+  ADD_TEST_NON_DEFAULT(fuzzer_found_single_1)
+  ADD_TEST_NON_DEFAULT(log_afl_test)
+  ADD_TEST_NON_DEFAULT(afl_test)
+  ADD_TEST_NON_DEFAULT(afl_single)
+#if 0
+  ADD_TEST(spiffs_hidden_file_90)
+#endif
+#if 0
+  ADD_TEST(null_deref_check_93)
+#endif
+SUITE_END(bug_tests)
diff --git a/components/spiffs/spiffs/src/test/test_check.c b/components/spiffs/spiffs/src/test/test_check.c
new file mode 100644
index 00000000..15865c47
--- /dev/null
+++ b/components/spiffs/spiffs/src/test/test_check.c
@@ -0,0 +1,427 @@
+/*
+ * 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)
+static void setup() {
+  _setup();
+}
+static 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
+
+
+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
+
+
+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 + 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
+
+
+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
+
+
+
+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
+
+
+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
+
+
+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
+
+
+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
+
+
+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
+
+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
+
+SUITE_TESTS(check_tests)
+  ADD_TEST(evil_write)
+  ADD_TEST(lu_check1)
+  ADD_TEST(page_cons1)
+  ADD_TEST(page_cons2)
+  ADD_TEST(page_cons3)
+  ADD_TEST(page_cons_final)
+  ADD_TEST(index_cons1)
+  ADD_TEST(index_cons2)
+  ADD_TEST(index_cons3)
+  ADD_TEST(index_cons4)
+SUITE_END(check_tests)
diff --git a/components/spiffs/spiffs/src/test/test_dev.c b/components/spiffs/spiffs/src/test/test_dev.c
new file mode 100644
index 00000000..552e9d48
--- /dev/null
+++ b/components/spiffs/spiffs/src/test/test_dev.c
@@ -0,0 +1,122 @@
+/*
+ * 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)
+static void setup() {
+  _setup();
+}
+static 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
+
+SUITE_TESTS(dev_tests)
+  ADD_TEST(interrupted_write)
+SUITE_END(dev_tests)
diff --git a/components/spiffs/spiffs/src/test/test_hydrogen.c b/components/spiffs/spiffs/src/test/test_hydrogen.c
new file mode 100644
index 00000000..52126b2a
--- /dev/null
+++ b/components/spiffs/spiffs/src/test/test_hydrogen.c
@@ -0,0 +1,2507 @@
+/*
+ * 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)
+static void setup() {
+  _setup();
+}
+static 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 < SPIFFS_CFG_PHYS_SZ(&__fs));
+  return TEST_RES_OK;
+}
+TEST_END
+
+#if SPIFFS_USE_MAGIC
+TEST(magic)
+{
+  fs_reset_specific(0, 0, 65536*16, 65536, 65536, 256);
+  SPIFFS_unmount(FS);
+
+  TEST_CHECK_EQ(fs_mount_specific(0, 65536*16, 65536, 65536, 256), SPIFFS_OK);
+  SPIFFS_unmount(FS);
+
+  TEST_CHECK_NEQ(fs_mount_specific(0, 65536*16, 65536, 65536, 128), SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FS);
+
+  TEST_CHECK_NEQ(fs_mount_specific(4, 65536*16, 65536, 65536, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FS);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+
+#if SPIFFS_USE_MAGIC_LENGTH
+TEST(magic_length)
+{
+  fs_reset_specific(0, 0, 65536*16, 65536, 65536, 256);
+  SPIFFS_unmount(FS);
+
+  TEST_CHECK_EQ(fs_mount_specific(0, 65536*16, 65536, 65536, 256), SPIFFS_OK);
+  SPIFFS_unmount(FS);
+
+  TEST_CHECK_NEQ(fs_mount_specific(0, 65536*8, 65536, 65536, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FS);
+
+  TEST_CHECK_NEQ(fs_mount_specific(0, 65536*15, 65536, 65536, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FS);
+
+  TEST_CHECK_NEQ(fs_mount_specific(0, 65536*17, 65536, 65536, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FS);
+
+  TEST_CHECK_NEQ(fs_mount_specific(0, 65536*256, 65536, 65536, 256), SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FS);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+#if SPIFFS_SINGLETON==0
+TEST(magic_length_probe)
+{
+  fs_reset_specific(0, 0, 65536*16, 65536, 65536, 256);
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 65536*16);
+
+  fs_reset_specific(0, 0, 65536*24, 65536, 65536, 256);
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 65536*24);
+
+  fs_reset_specific(0, 0, 32768*16, 32768, 32768, 128);
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 32768*16);
+
+  fs_reset_specific(0, 0, 16384*37, 16384, 16384, 128);
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 16384*37);
+
+  fs_reset_specific(0, 0, 4096*11, 4096, 4096, 256);
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 4096*11);
+
+  fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256);
+  __fs.cfg.log_page_size = 128;
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS);
+  __fs.cfg.log_page_size = 512;
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS);
+  __fs.cfg.log_page_size = 256;
+  __fs.cfg.log_block_size = 8192;
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS);
+  __fs.cfg.log_block_size = 2048;
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS);
+  __fs.cfg.log_block_size = 4096;
+  __fs.cfg.phys_addr += 2;
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS);
+
+  fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256);
+  __fs.cfg.phys_addr += 4096*6;
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS);
+
+  fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256);
+  area_set(4096*0, 0xff, 4096); // "erase" block 0
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 4096*8);
+
+  fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256);
+  area_set(4096*0, 0xff, 4096); // "erase" block 1
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 4096*8);
+
+  fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256);
+  area_set(4096*0, 0xff, 4096); // "erase" block 2
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 4096*8);
+
+  fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256);
+  area_set(4096*0, 0xff, 4096*2); // "erase" block 0 & 1
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS);
+
+  fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256);
+  area_set(4096*0, 0xff, 4096*2); // "erase" block 0
+  area_set(4096*0, 0xff, 4096); // "erase" block 2
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS);
+
+  fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256);
+  area_set(4096*1, 0xff, 4096*2); // "erase" block 1 & 2
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS);
+
+  fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256);
+  area_set(4096*0, 0xff, 4096*8); // "erase" all
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS);
+
+  fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256);
+  area_set(4096*0, 0xdd, 4096*8); // garble all
+  TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+#endif // SPIFFS_SINGLETON==0
+
+#endif // SPIFFS_USE_MAGIC_LENGTH
+
+#endif // SPIFFS_USE_MAGIC
+
+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
+
+
+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);
+#if SPIFFS_OBJ_META_LEN
+  u8_t new_meta[SPIFFS_OBJ_META_LEN] = {0};
+  res = SPIFFS_fupdate_meta(FS, fd, new_meta);
+  TEST_CHECK(res < 0);
+  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR);
+#endif
+  return TEST_RES_OK;
+}
+TEST_END
+
+
+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);
+#if SPIFFS_OBJ_META_LEN
+  u8_t new_meta[SPIFFS_OBJ_META_LEN] = {0};
+  res = SPIFFS_fupdate_meta(FS, fd, new_meta);
+  TEST_CHECK(res < 0);
+  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
+#endif
+  return TEST_RES_OK;
+}
+TEST_END
+
+
+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
+
+
+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
+
+
+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
+
+
+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
+
+TEST(file_by_open_excl)
+{
+  int res;
+  spiffs_stat s;
+  spiffs_file fd = SPIFFS_open(FS, "filebexcl", SPIFFS_CREAT | SPIFFS_EXCL, 0);
+  TEST_CHECK(fd >= 0);
+  res = SPIFFS_fstat(FS, fd, &s);
+  TEST_CHECK(res >= 0);
+  TEST_CHECK(strcmp((char*)s.name, "filebexcl") == 0);
+  TEST_CHECK(s.size == 0);
+  SPIFFS_close(FS, fd);
+
+  fd = SPIFFS_open(FS, "filebexcl", SPIFFS_CREAT | SPIFFS_EXCL, 0);
+  TEST_CHECK(fd < 0);
+  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_EXISTS);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+#if SPIFFS_FILEHDL_OFFSET
+TEST(open_fh_offs)
+{
+  spiffs_file fd1, fd2, fd3;
+  fd1 = SPIFFS_open(FS, "1", SPIFFS_CREAT | SPIFFS_EXCL, 0);
+  fd2 = SPIFFS_open(FS, "2", SPIFFS_CREAT | SPIFFS_EXCL, 0);
+  fd3 = SPIFFS_open(FS, "3", SPIFFS_CREAT | SPIFFS_EXCL, 0);
+  TEST_CHECK(fd1 >= TEST_SPIFFS_FILEHDL_OFFSET);
+  TEST_CHECK(fd2 >= TEST_SPIFFS_FILEHDL_OFFSET);
+  TEST_CHECK(fd3 >= TEST_SPIFFS_FILEHDL_OFFSET);
+  SPIFFS_close(FS, fd1);
+  fd1 = SPIFFS_open(FS, "2", SPIFFS_RDONLY, 0);
+  TEST_CHECK(fd1 >= TEST_SPIFFS_FILEHDL_OFFSET);
+  SPIFFS_close(FS, fd2);
+  fd2 = SPIFFS_open(FS, "3", SPIFFS_RDONLY, 0);
+  TEST_CHECK(fd2 >= TEST_SPIFFS_FILEHDL_OFFSET);
+  SPIFFS_close(FS, fd3);
+  fd3 = SPIFFS_open(FS, "1", SPIFFS_RDONLY, 0);
+  TEST_CHECK(fd3 >= TEST_SPIFFS_FILEHDL_OFFSET);
+  SPIFFS_close(FS, fd1);
+  SPIFFS_close(FS, fd2);
+  SPIFFS_close(FS, fd3);
+  fd1 = SPIFFS_open(FS, "3", SPIFFS_RDONLY, 0);
+  TEST_CHECK(fd1 >= TEST_SPIFFS_FILEHDL_OFFSET);
+  SPIFFS_close(FS, fd1);
+  fd1 = SPIFFS_open(FS, "foo", SPIFFS_RDONLY, 0);
+  TEST_CHECK(fd1 < TEST_SPIFFS_FILEHDL_OFFSET);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+#endif //SPIFFS_FILEHDL_OFFSET
+
+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], (char *)pe->name) == 0) {
+        found++;
+        break;
+      }
+    }
+    {
+      spiffs_stat s;
+      TEST_CHECK_EQ(SPIFFS_stat(FS, pe->name, &s), 0);
+      TEST_CHECK_EQ(pe->obj_id, s.obj_id);
+      TEST_CHECK_EQ(pe->size, s.size);
+      TEST_CHECK_EQ(pe->type, s.type);
+      TEST_CHECK_EQ(pe->pix, s.pix);
+#if SPIFFS_OBJ_META_LEN
+      TEST_CHECK_EQ(memcmp(pe->meta, s.meta, SPIFFS_OBJ_META_LEN), 0);
+#endif
+    }
+  }
+  SPIFFS_closedir(&d);
+
+  TEST_CHECK(found == file_cnt);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+
+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, (char *)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
+
+
+TEST(open_by_page) {
+  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, (char *)pe->name);
+    TEST_CHECK(res == SPIFFS_OK);
+    fd = SPIFFS_open_by_page(FS, pe->pix, 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);
+
+  spiffs_file fd;
+  // test opening a lookup page
+  fd = SPIFFS_open_by_page(FS, 0, SPIFFS_RDWR, 0);
+  TEST_CHECK_LT(fd, 0);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FILE);
+  // test opening a proper page but not being object index
+  fd = SPIFFS_open_by_page(FS, SPIFFS_OBJ_LOOKUP_PAGES(FS)+1, SPIFFS_RDWR, 0);
+  TEST_CHECK_LT(fd, 0);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FILE);
+
+  return TEST_RES_OK;
+} TEST_END
+
+
+static struct {
+  u32_t calls;
+  spiffs_fileop_type op;
+  spiffs_obj_id obj_id;
+  spiffs_page_ix pix;
+} ucb;
+
+void test_cb(spiffs *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix) {
+  ucb.calls++;
+  ucb.op = op;
+  ucb.obj_id = obj_id;
+  ucb.pix = pix;
+  //printf("%4i  op:%i objid:%04x pix:%04x\n", ucb.calls, ucb.op, ucb.obj_id, ucb.pix);
+}
+
+TEST(user_callback_basic) {
+  SPIFFS_set_file_callback_func(FS, test_cb);
+  int res;
+  memset(&ucb, 0, sizeof(ucb));
+  spiffs_file fd = SPIFFS_open(FS, "foo", SPIFFS_CREAT | SPIFFS_APPEND | SPIFFS_RDWR, 0);
+  TEST_CHECK_GE(fd, 0);
+  TEST_CHECK_EQ(ucb.calls, 1);
+  TEST_CHECK_EQ(ucb.op, SPIFFS_CB_CREATED);
+  spiffs_stat s;
+  res = SPIFFS_fstat(FS, fd, &s);
+  TEST_CHECK_GE(res, 0);
+  TEST_CHECK_EQ(ucb.obj_id, s.obj_id);
+  TEST_CHECK_EQ(ucb.pix, s.pix);
+
+  res = SPIFFS_write(FS, fd, "howdy partner", 14);
+  TEST_CHECK_GE(res, 0);
+  res = SPIFFS_fflush(FS, fd);
+  TEST_CHECK_GE(res, 0);
+  TEST_CHECK_EQ(ucb.calls, 2);
+  TEST_CHECK_EQ(ucb.op, SPIFFS_CB_UPDATED);
+  TEST_CHECK_EQ(ucb.obj_id, s.obj_id);
+  TEST_CHECK_EQ(ucb.pix, s.pix);
+
+  res = SPIFFS_fremove(FS, fd);
+  TEST_CHECK_GE(res, 0);
+  TEST_CHECK_EQ(ucb.calls, 3);
+  TEST_CHECK_EQ(ucb.op, SPIFFS_CB_DELETED);
+  TEST_CHECK_EQ(ucb.obj_id, s.obj_id);
+  TEST_CHECK_EQ(ucb.pix, s.pix);
+
+  return TEST_RES_OK;
+} TEST_END
+
+
+TEST(user_callback_gc) {
+  SPIFFS_set_file_callback_func(FS, test_cb);
+
+  char name[32];
+  int f;
+  int size = SPIFFS_DATA_PAGE_SIZE(FS);
+  int pages_per_block = SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS);
+  int files = (pages_per_block-1)/2;
+  int res;
+
+  // fill block with files
+  for (f = 0; f < files; f++) {
+    sprintf(name, "file%i", f);
+    res = test_create_and_write_file(name, size, 1);
+    TEST_CHECK(res >= 0);
+  }
+  for (f = 0; f < files; f++) {
+    sprintf(name, "file%i", f);
+    res = read_and_verify(name);
+    TEST_CHECK(res >= 0);
+  }
+  // remove all files in block
+  for (f = 0; f < files; f++) {
+    sprintf(name, "file%i", f);
+    res = SPIFFS_remove(FS, name);
+    TEST_CHECK(res >= 0);
+  }
+
+  memset(&ucb, 0, sizeof(ucb));
+  spiffs_file fd = SPIFFS_open(FS, "foo", SPIFFS_CREAT | SPIFFS_APPEND | SPIFFS_RDWR, 0);
+  TEST_CHECK_GE(fd, 0);
+  TEST_CHECK_EQ(ucb.calls, 1);
+  TEST_CHECK_EQ(ucb.op, SPIFFS_CB_CREATED);
+  spiffs_stat s;
+  res = SPIFFS_fstat(FS, fd, &s);
+  TEST_CHECK_GE(res, 0);
+  TEST_CHECK_EQ(ucb.obj_id, s.obj_id);
+  TEST_CHECK_EQ(ucb.pix, s.pix);
+
+  res = SPIFFS_write(FS, fd, "howdy partner", 14);
+  TEST_CHECK_GE(res, 0);
+  res = SPIFFS_fflush(FS, fd);
+  TEST_CHECK_GE(res, 0);
+  TEST_CHECK_EQ(ucb.calls, 2);
+  TEST_CHECK_EQ(ucb.op, SPIFFS_CB_UPDATED);
+  TEST_CHECK_EQ(ucb.obj_id, s.obj_id);
+  TEST_CHECK_EQ(ucb.pix, s.pix);
+
+  u32_t tot, us;
+  SPIFFS_info(FS, &tot, &us);
+
+  // do a hard gc, should move our file
+  res = SPIFFS_gc(FS, tot-us*2);
+  TEST_CHECK_GE(res, 0);
+
+  TEST_CHECK_GE(res, 0);
+  TEST_CHECK_EQ(ucb.calls, 3);
+  TEST_CHECK_EQ(ucb.op, SPIFFS_CB_UPDATED);
+  TEST_CHECK_EQ(ucb.obj_id, s.obj_id);
+  TEST_CHECK_NEQ(ucb.pix, s.pix);
+
+  res = SPIFFS_fstat(FS, fd, &s);
+  TEST_CHECK_GE(res, 0);
+  TEST_CHECK_EQ(ucb.pix, s.pix);
+
+  res = SPIFFS_fremove(FS, fd);
+  TEST_CHECK_GE(res, 0);
+  TEST_CHECK_EQ(ucb.calls, 4);
+  TEST_CHECK_EQ(ucb.op, SPIFFS_CB_DELETED);
+  TEST_CHECK_EQ(ucb.obj_id, s.obj_id);
+  TEST_CHECK_EQ(ucb.pix, s.pix);
+
+  return TEST_RES_OK;
+} TEST_END
+
+
+TEST(name_too_long) {
+  char name[SPIFFS_OBJ_NAME_LEN*2];
+  memset(name, 0, sizeof(name));
+  int i;
+  for (i = 0; i < SPIFFS_OBJ_NAME_LEN; i++) {
+    name[i] = 'A';
+  }
+
+  TEST_CHECK_LT(SPIFFS_creat(FS, name, 0), SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NAME_TOO_LONG);
+
+  TEST_CHECK_LT(SPIFFS_open(FS, name, SPIFFS_CREAT | SPIFFS_TRUNC, 0), SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NAME_TOO_LONG);
+
+  TEST_CHECK_LT(SPIFFS_remove(FS, name), SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NAME_TOO_LONG);
+
+  spiffs_stat s;
+  TEST_CHECK_LT(SPIFFS_stat(FS, name, &s), SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NAME_TOO_LONG);
+
+  TEST_CHECK_LT(SPIFFS_rename(FS, name, "a"), SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NAME_TOO_LONG);
+
+  TEST_CHECK_LT(SPIFFS_rename(FS, "a", name), SPIFFS_OK);
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NAME_TOO_LONG);
+
+  return TEST_RES_OK;
+} TEST_END
+
+
+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
+
+#if SPIFFS_OBJ_META_LEN
+TEST(update_meta) {
+  s32_t i, res, fd;
+  spiffs_stat s;
+  u8_t new_meta[SPIFFS_OBJ_META_LEN], new_meta2[SPIFFS_OBJ_META_LEN];
+
+  res = test_create_file("foo");
+  TEST_CHECK(res >= 0);
+
+  for (i = 0; i < SPIFFS_OBJ_META_LEN; i++) {
+    new_meta[i] = 0xaa;
+  }
+  res = SPIFFS_update_meta(FS, "foo", new_meta);
+  TEST_CHECK(res >= 0);
+
+  res = SPIFFS_stat(FS, "foo", &s);
+  TEST_CHECK(res >= 0);
+  TEST_CHECK_EQ(memcmp(s.meta, new_meta, SPIFFS_OBJ_META_LEN), 0);
+
+  for (i = 0; i < SPIFFS_OBJ_META_LEN; i++) {
+    new_meta2[i] = 0xbb;
+  }
+
+  fd = SPIFFS_open(FS, "foo", SPIFFS_RDONLY, 0);
+  TEST_CHECK(fd >= 0);
+  res = SPIFFS_fupdate_meta(FS, fd, new_meta2);
+  TEST_CHECK(res < 0);
+  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_WRITABLE);
+  SPIFFS_close(FS, fd);
+
+  res = SPIFFS_stat(FS, "foo", &s);
+  TEST_CHECK(res >= 0);
+  TEST_CHECK_EQ(memcmp(s.meta, new_meta, SPIFFS_OBJ_META_LEN), 0);
+
+  fd = SPIFFS_open(FS, "foo", SPIFFS_RDWR, 0);
+  TEST_CHECK(fd >= 0);
+  res = SPIFFS_fupdate_meta(FS, fd, new_meta2);
+  TEST_CHECK_EQ(res, 0);
+  SPIFFS_close(FS, fd);
+
+  res = SPIFFS_stat(FS, "foo", &s);
+  TEST_CHECK(res >= 0);
+  TEST_CHECK_EQ(memcmp(s.meta, new_meta2, SPIFFS_OBJ_META_LEN), 0);
+
+  return TEST_RES_OK;
+} TEST_END
+#endif
+
+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
+
+
+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
+
+
+TEST(write_cache)
+{
+  int res;
+  spiffs_file fd;
+  u8_t buf[1024*8];
+  u8_t fbuf[1024*8];
+  res = test_create_file("f");
+  TEST_CHECK(res >= 0);
+  fd = SPIFFS_open(FS, "f", SPIFFS_RDWR, 0);
+  TEST_CHECK(fd >= 0);
+  memrand(buf, sizeof(buf));
+  res = SPIFFS_write(FS, fd, buf, SPIFFS_CFG_LOG_PAGE_SZ(FS)/2);
+  TEST_CHECK(res >= 0);
+  res = SPIFFS_write(FS, fd, buf, SPIFFS_CFG_LOG_PAGE_SZ(FS)*2);
+  TEST_CHECK(res >= 0);
+  res = SPIFFS_close(FS, fd);
+  TEST_CHECK(res >= 0);
+
+  fd = SPIFFS_open(FS, "f", SPIFFS_RDWR, 0);
+  TEST_CHECK(fd >= 0);
+  res = SPIFFS_read(FS, fd, fbuf, SPIFFS_CFG_LOG_PAGE_SZ(FS)/2 + SPIFFS_CFG_LOG_PAGE_SZ(FS)*2);
+  TEST_CHECK(res >= 0);
+  TEST_CHECK(0 == memcmp(&buf[0], &fbuf[0], SPIFFS_CFG_LOG_PAGE_SZ(FS)/2));
+  TEST_CHECK(0 == memcmp(&buf[0], &fbuf[SPIFFS_CFG_LOG_PAGE_SZ(FS)/2], SPIFFS_CFG_LOG_PAGE_SZ(FS)*2));
+  res = SPIFFS_close(FS, fd);
+  TEST_CHECK(res >= 0);
+
+  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+
+TEST(write_big_file_chunks_page)
+{
+  int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/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
+
+
+TEST(write_big_files_chunks_page)
+{
+  char name[32];
+  int f;
+  int files = 10;
+  int res;
+  int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/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
+
+
+TEST(write_big_file_chunks_index)
+{
+  int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/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
+
+
+TEST(write_big_files_chunks_index)
+{
+  char name[32];
+  int f;
+  int files = 10;
+  int res;
+  int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/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
+
+
+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
+
+
+TEST(write_big_files_chunks_huge)
+{
+  char name[32];
+  int f;
+  int files = 10;
+  int res;
+  int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/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
+
+
+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
+
+
+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
+
+
+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
+
+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
+
+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
+
+
+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
+
+
+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
+
+
+TEST(read_chunk_huge)
+{
+  int sz = (2*SPIFFS_CFG_PHYS_SZ(FS))/3;
+  TEST_CHECK(create_and_read_back(sz, sz) == 0);
+  return TEST_RES_OK;
+}
+TEST_END
+
+
+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
+
+TEST(read_beyond2)
+{
+  char *name = "file";
+  spiffs_file fd;
+  s32_t res;
+  const s32_t size = SPIFFS_DATA_PAGE_SIZE(FS);
+
+  u8_t buf[size*2];
+  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);
+
+  int i,j;
+  for (j = 1; j <= size+1; j++) {
+    fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0);
+    CHECK(fd >= 0);
+    SPIFFS_clearerr(FS);
+    for (i = 0; i < size * 2; i += j) {
+      u8_t dst;
+      res = SPIFFS_read(FS, fd, buf, j);
+      TEST_CHECK_EQ(SPIFFS_errno(FS), i < size ? SPIFFS_OK : SPIFFS_ERR_END_OF_OBJECT);
+      TEST_CHECK_EQ(res, MIN(j, MAX(0, size - (i + j) + j)));
+    }
+    SPIFFS_close(FS, fd);
+  }
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+
+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
+
+
+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
+
+
+TEST(lseek_simple_modification) {
+  int res;
+  spiffs_file fd;
+  char *fname = "seekfile";
+  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
+
+
+TEST(lseek_modification_append) {
+  int res;
+  spiffs_file fd;
+  char *fname = "seekfile";
+  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
+
+
+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
+
+
+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
+
+
+
+TEST(lseek_oob) {
+  int res;
+  spiffs_file fd;
+  char *fname = "seekfile";
+  int len = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS);
+
+  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, -1, SPIFFS_SEEK_SET);
+  TEST_CHECK_EQ(res, SPIFFS_ERR_SEEK_BOUNDS);
+  res = SPIFFS_lseek(FS, fd, len+1, SPIFFS_SEEK_SET);
+  TEST_CHECK_EQ(res, SPIFFS_ERR_END_OF_OBJECT);
+  free(refbuf);
+  SPIFFS_close(FS, fd);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+
+TEST(gc_quick)
+{
+  char name[32];
+  int f;
+  int size = SPIFFS_DATA_PAGE_SIZE(FS);
+  int pages_per_block=SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS);
+  int files = (pages_per_block+1)/2;
+  int res;
+
+  // negative, try quick gc on clean sys
+  res = SPIFFS_gc_quick(FS, 0);
+  TEST_CHECK(res < 0);
+  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NO_DELETED_BLOCKS);
+
+  // fill block with files
+  for (f = 0; f < files; f++) {
+    sprintf(name, "file%i", f);
+    res = test_create_and_write_file(name, size, 1);
+    TEST_CHECK(res >= 0);
+  }
+  for (f = 0; f < files; f++) {
+    sprintf(name, "file%i", f);
+    res = read_and_verify(name);
+    TEST_CHECK(res >= 0);
+  }
+  // remove all files in block
+  for (f = 0; f < files; f++) {
+    sprintf(name, "file%i", f);
+    res = SPIFFS_remove(FS, name);
+    TEST_CHECK(res >= 0);
+  }
+
+  // do a quick gc
+  res = SPIFFS_gc_quick(FS, 0);
+  TEST_CHECK(res >= 0);
+
+  // fill another block with files but two pages
+  // We might have one deleted page left over from the previous gc, in case pages_per_block is odd.
+  int pages_already=2*files-pages_per_block;
+  int files2=(pages_per_block-pages_already+1)/2;
+
+  for (f = 0; f < files2 - 1; f++) {
+    sprintf(name, "file%i", f);
+    res = test_create_and_write_file(name, size, 1);
+    TEST_CHECK(res >= 0);
+  }
+  for (f = 0; f < files2 - 1; f++) {
+    sprintf(name, "file%i", f);
+    res = read_and_verify(name);
+    TEST_CHECK(res >= 0);
+  }
+  // remove all files in block leaving two free pages in block
+  for (f = 0; f < files2 - 1; f++) {
+    sprintf(name, "file%i", f);
+    res = SPIFFS_remove(FS, name);
+    TEST_CHECK(res >= 0);
+  }
+
+  // negative, try quick gc where no fully deleted blocks exist
+  res = SPIFFS_gc_quick(FS, 0);
+  TEST_CHECK(res < 0);
+  TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NO_DELETED_BLOCKS);
+
+  // positive, try quick gc where allowing two free pages
+  res = SPIFFS_gc_quick(FS, 2);
+  TEST_CHECK(res >= 0);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+
+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
+
+
+TEST(write_small_files_chunks_1)
+{
+  char name[32];
+  int f;
+  int size = 512;
+  int files = ((20*SPIFFS_CFG_PHYS_SZ(FS))/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
+
+TEST(write_big_file_chunks_1)
+{
+  int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/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
+
+TEST(write_big_files_chunks_1)
+{
+  char name[32];
+  int f;
+  int files = 10;
+  int res;
+  int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/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
+
+
+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 = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = LONG
+      },
+      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = LONG
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = LONG
+      },
+      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = LONG
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = NORMAL
+      },
+      {   .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
+
+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
+
+
+TEST(long_run_config_many_small)
+{
+  tfile_conf cfgs[] = {
+      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = LONG
+      },
+      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = LONG
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = MODIFIED,      .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = APPENDED,      .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,      .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = REWRITTEN,     .tlife = SHORT
+      },
+      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = NORMAL
+      },
+      {   .tsize = SMALL,     .ttype = UNTAMPERED,    .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = SHORT
+      },
+      {   .tsize = EMPTY,     .ttype = UNTAMPERED,    .tlife = NORMAL
+      },
+      {   .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
+
+
+TEST(long_run)
+{
+  tfile_conf cfgs[] = {
+      {   .tsize = EMPTY,     .ttype = APPENDED,      .tlife = NORMAL
+      },
+      {   .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], 20, 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
+
+#if SPIFFS_IX_MAP
+TEST(ix_map_basic)
+{
+  // create a scattered file
+  s32_t res;
+  spiffs_file fd1, fd2;
+  fd1 = SPIFFS_open(FS, "1", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0);
+  TEST_CHECK_GT(fd1, 0);
+  fd2 = SPIFFS_open(FS, "2", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0);
+  TEST_CHECK_GT(fd2, 0);
+
+  u8_t buf[SPIFFS_DATA_PAGE_SIZE(FS)];
+  int i;
+  for (i = 0; i < SPIFFS_CFG_PHYS_SZ(FS) / 4 / SPIFFS_DATA_PAGE_SIZE(FS); i++) {
+    memrand(buf, sizeof(buf));
+    res = SPIFFS_write(FS, fd1, buf, sizeof(buf));
+    TEST_CHECK_GE(res, SPIFFS_OK);
+    memrand(buf, sizeof(buf));
+    res = SPIFFS_write(FS, fd2, buf, sizeof(buf));
+    TEST_CHECK_GE(res, SPIFFS_OK);
+  }
+  res = SPIFFS_close(FS, fd1);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+  res = SPIFFS_close(FS, fd2);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+
+  res = SPIFFS_remove(FS, "2");
+  TEST_CHECK_GE(res, SPIFFS_OK);
+
+  spiffs_stat s;
+  res = SPIFFS_stat(FS, "1", &s);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+  u32_t size = s.size;
+
+  printf("file created, size: %i..\n", size);
+
+  fd1 = SPIFFS_open(FS, "1", SPIFFS_O_RDONLY, 0);
+  TEST_CHECK_GT(fd1, 0);
+  printf(".. corresponding pix entries: %i\n", SPIFFS_bytes_to_ix_map_entries(FS, size));
+
+  u8_t rd_buf[SPIFFS_CFG_LOG_PAGE_SZ(FS)];
+
+  fd1 = SPIFFS_open(FS, "1", SPIFFS_O_RDONLY, 0);
+  TEST_CHECK_GT(fd1, 0);
+
+  clear_flash_ops_log();
+
+  printf("reading file without memory mapped index\n");
+  while ((res = SPIFFS_read(FS, fd1, rd_buf, sizeof(rd_buf))) == sizeof(rd_buf));
+  TEST_CHECK_GT(res, SPIFFS_OK);
+
+  res = SPIFFS_OK;
+
+  u32_t reads_without_ixmap = get_flash_ops_log_read_bytes();
+  dump_flash_access_stats();
+
+  u32_t crc_non_map_ix = get_spiffs_file_crc_by_fd(fd1);
+
+  TEST_CHECK_EQ(SPIFFS_close(FS, fd1), SPIFFS_OK);
+
+
+  printf("reading file with memory mapped index\n");
+  spiffs_ix_map map;
+  spiffs_page_ix ixbuf[SPIFFS_bytes_to_ix_map_entries(FS, size)];
+
+  fd1 = SPIFFS_open(FS, "1", SPIFFS_O_RDONLY, 0);
+  TEST_CHECK_GT(fd1, 0);
+
+  // map index to memory
+  res = SPIFFS_ix_map(FS, fd1, &map, 0, size, ixbuf);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+
+  clear_flash_ops_log();
+
+  while ((res = SPIFFS_read(FS, fd1, rd_buf, sizeof(rd_buf))) == sizeof(rd_buf));
+  TEST_CHECK_GT(res, SPIFFS_OK);
+  u32_t reads_with_ixmap_pass1 = get_flash_ops_log_read_bytes();
+
+  dump_flash_access_stats();
+
+  u32_t crc_map_ix_pass1 = get_spiffs_file_crc_by_fd(fd1);
+
+  TEST_CHECK_LT(reads_with_ixmap_pass1, reads_without_ixmap);
+
+  TEST_CHECK_EQ(crc_non_map_ix, crc_map_ix_pass1);
+
+  spiffs_page_ix ref_ixbuf[SPIFFS_bytes_to_ix_map_entries(FS, size)];
+  memcpy(ref_ixbuf, ixbuf, sizeof(ixbuf));
+
+  // force a gc by creating small files until full, reordering the index
+  printf("forcing gc, error ERR_FULL %i expected\n", SPIFFS_ERR_FULL);
+  res = SPIFFS_OK;
+  u32_t ix = 10;
+  while (res == SPIFFS_OK) {
+    char name[32];
+    sprintf(name, "%i", ix);
+    res = test_create_and_write_file(name, SPIFFS_CFG_LOG_BLOCK_SZ(FS), SPIFFS_CFG_LOG_BLOCK_SZ(FS));
+    ix++;
+  }
+
+  TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_FULL);
+
+  // make sure the map array was altered
+  TEST_CHECK_NEQ(0, memcmp(ref_ixbuf, ixbuf, sizeof(ixbuf)));
+
+  TEST_CHECK_GE(SPIFFS_lseek(FS, fd1, 0, SPIFFS_SEEK_SET), SPIFFS_OK);
+
+  clear_flash_ops_log();
+  while ((res = SPIFFS_read(FS, fd1, rd_buf, sizeof(rd_buf))) == sizeof(rd_buf));
+  TEST_CHECK_GT(res, SPIFFS_OK);
+  u32_t reads_with_ixmap_pass2 = get_flash_ops_log_read_bytes();
+
+  TEST_CHECK_EQ(reads_with_ixmap_pass1, reads_with_ixmap_pass2);
+
+  u32_t crc_map_ix_pass2 = get_spiffs_file_crc_by_fd(fd1);
+
+  TEST_CHECK_EQ(crc_map_ix_pass1, crc_map_ix_pass2);
+
+  TEST_CHECK_EQ(SPIFFS_close(FS, fd1), SPIFFS_OK);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+TEST(ix_map_remap)
+{
+  // create a file, 10 data pages long
+  s32_t res;
+  spiffs_file fd1;
+  fd1 = SPIFFS_open(FS, "1", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0);
+  TEST_CHECK_GT(fd1, 0);
+
+  const int size_pages = 10;
+
+  u8_t buf[SPIFFS_DATA_PAGE_SIZE(FS)];
+  int i;
+  for (i = 0; i < size_pages; i++) {
+    memrand(buf, sizeof(buf));
+    res = SPIFFS_write(FS, fd1, buf, sizeof(buf));
+    TEST_CHECK_GE(res, SPIFFS_OK);
+  }
+  res = SPIFFS_close(FS, fd1);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+
+  spiffs_stat s;
+  res = SPIFFS_stat(FS, "1", &s);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+  u32_t size = s.size;
+
+  printf("file created, size: %i..\n", size);
+
+  fd1 = SPIFFS_open(FS, "1", SPIFFS_O_RDONLY, 0);
+  TEST_CHECK_GT(fd1, 0);
+  printf(".. corresponding pix entries: %i\n", SPIFFS_bytes_to_ix_map_entries(FS, size) + 1);
+  TEST_CHECK_EQ(SPIFFS_bytes_to_ix_map_entries(FS, size), size_pages + 1);
+
+  // map index to memory
+  // move around, check validity
+  const int entries = SPIFFS_bytes_to_ix_map_entries(FS, size/2);
+  spiffs_ix_map map;
+  // add one extra for stack safeguarding
+  spiffs_page_ix ixbuf[entries+1];
+  spiffs_page_ix ixbuf_ref[entries+1];
+  const spiffs_page_ix canary = (spiffs_page_ix)0x87654321;
+  memset(ixbuf, 0xee, sizeof(ixbuf));
+  ixbuf[entries] = canary;
+
+  res = SPIFFS_ix_map(FS, fd1, &map, 0, size/2, ixbuf);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+
+  for (i = 0; i < entries; i++) {
+    printf("%04x ", ixbuf[i]);
+  }
+  printf("\n");
+
+  memcpy(ixbuf_ref, ixbuf, sizeof(spiffs_page_ix) * entries);
+
+  TEST_CHECK_EQ(SPIFFS_ix_remap(FS, fd1, 0), SPIFFS_OK);
+  TEST_CHECK_EQ(canary, ixbuf[entries]);
+  for (i = 0; i < entries; i++) {
+    printf("%04x ", ixbuf[i]);
+  }
+  printf("\n");
+  TEST_CHECK_EQ(0, memcmp(ixbuf_ref, ixbuf, sizeof(spiffs_page_ix) * entries));
+
+  TEST_CHECK_EQ(SPIFFS_ix_remap(FS, fd1, SPIFFS_DATA_PAGE_SIZE(FS)), SPIFFS_OK);
+  for (i = 0; i < entries; i++) {
+    printf("%04x ", ixbuf[i]);
+  }
+  printf("\n");
+  TEST_CHECK_EQ(canary, ixbuf[entries]);
+  TEST_CHECK_EQ(0, memcmp(&ixbuf_ref[1], ixbuf, sizeof(spiffs_page_ix) * (entries-1)));
+
+
+  TEST_CHECK_EQ(SPIFFS_ix_remap(FS, fd1, 0), SPIFFS_OK);
+  for (i = 0; i < entries; i++) {
+    printf("%04x ", ixbuf[i]);
+  }
+  printf("\n");
+  TEST_CHECK_EQ(canary, ixbuf[entries]);
+  TEST_CHECK_EQ(0, memcmp(ixbuf_ref, ixbuf, sizeof(spiffs_page_ix) * entries));
+
+  TEST_CHECK_EQ(SPIFFS_ix_remap(FS, fd1, size/2), SPIFFS_OK);
+  TEST_CHECK_EQ(canary, ixbuf[entries]);
+
+  for (i = 0; i < entries; i++) {
+    printf("%04x ", ixbuf_ref[i]);
+  }
+  printf("\n");
+
+  for (i = 0; i < entries; i++) {
+    printf("%04x ", ixbuf[i]);
+  }
+  printf("\n");
+
+  int matches = 0;
+  for (i = 0; i < entries; i++) {
+    int j;
+    for (j = 0; j < entries; j++) {
+      if (ixbuf_ref[i] == ixbuf[i]) {
+        matches++;
+      }
+    }
+  }
+  TEST_CHECK_LE(matches, 1);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+TEST(ix_map_partial)
+{
+  // create a file, 10 data pages long
+  s32_t res;
+  spiffs_file fd;
+  fd = SPIFFS_open(FS, "1", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0);
+  TEST_CHECK_GT(fd, 0);
+
+  const int size_pages = 10;
+
+  u8_t buf[SPIFFS_DATA_PAGE_SIZE(FS)];
+  int i;
+  for (i = 0; i < size_pages; i++) {
+    memrand(buf, sizeof(buf));
+    res = SPIFFS_write(FS, fd, buf, sizeof(buf));
+    TEST_CHECK_GE(res, SPIFFS_OK);
+  }
+  res = SPIFFS_close(FS, fd);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+
+  spiffs_stat s;
+  res = SPIFFS_stat(FS, "1", &s);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+  u32_t size = s.size;
+
+  printf("file created, size: %i..\n", size);
+
+  const u32_t crc_unmapped = get_spiffs_file_crc("1");
+
+  fd = SPIFFS_open(FS, "1", SPIFFS_O_RDONLY, 0);
+  TEST_CHECK_GT(fd, 0);
+
+  // map index to memory
+  const int entries = SPIFFS_bytes_to_ix_map_entries(FS, size/2);
+  spiffs_ix_map map;
+  spiffs_page_ix ixbuf[entries];
+
+  printf("map 0-50%%\n");
+  res = SPIFFS_ix_map(FS, fd, &map, 0, size/2, ixbuf);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+
+  const u32_t crc_mapped_beginning = get_spiffs_file_crc_by_fd(fd);
+  TEST_CHECK_EQ(crc_mapped_beginning, crc_unmapped);
+
+  printf("map 25-75%%\n");
+  res = SPIFFS_ix_remap(FS, fd, size/4);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+
+  const u32_t crc_mapped_middle = get_spiffs_file_crc_by_fd(fd);
+  TEST_CHECK_EQ(crc_mapped_middle, crc_unmapped);
+
+  printf("map 50-100%%\n");
+  res = SPIFFS_ix_remap(FS, fd, size/2);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+
+  const u32_t crc_mapped_end = get_spiffs_file_crc_by_fd(fd);
+  TEST_CHECK_EQ(crc_mapped_end, crc_unmapped);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+TEST(ix_map_beyond)
+{
+  // create a file, 10 data pages long
+  s32_t res;
+  spiffs_file fd;
+  fd = SPIFFS_open(FS, "1", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0);
+  TEST_CHECK_GT(fd, 0);
+
+  const int size_pages = 10;
+
+  u8_t buf[SPIFFS_DATA_PAGE_SIZE(FS)];
+  int i;
+  for (i = 0; i < size_pages; i++) {
+    memrand(buf, sizeof(buf));
+    res = SPIFFS_write(FS, fd, buf, sizeof(buf));
+    TEST_CHECK_GE(res, SPIFFS_OK);
+  }
+  res = SPIFFS_close(FS, fd);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+
+  spiffs_stat s;
+  res = SPIFFS_stat(FS, "1", &s);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+  u32_t size = s.size;
+
+  printf("file created, size: %i..\n", size);
+
+  // map index to memory
+  fd = SPIFFS_open(FS, "1", SPIFFS_O_RDWR | SPIFFS_O_APPEND, 0);
+  TEST_CHECK_GT(fd, 0);
+
+  const int entries = SPIFFS_bytes_to_ix_map_entries(FS, size);
+  spiffs_ix_map map;
+  spiffs_page_ix ixbuf[entries];
+  printf("map has %i entries\n", entries);
+
+  printf("map 100-200%%\n");
+  res = SPIFFS_ix_map(FS, fd, &map, size, size, ixbuf);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+
+  printf("make sure map is empty\n");
+  for (i = 0; i < entries; i++) {
+    printf("%04x ", ixbuf[i]);
+    TEST_CHECK_EQ(ixbuf[i], 0);
+  }
+  printf("\n");
+
+  printf("elongate by 100%%\n");
+  for (i = 0; i < size_pages; i++) {
+    memrand(buf, sizeof(buf));
+    res = SPIFFS_write(FS, fd, buf, sizeof(buf));
+    TEST_CHECK_GE(res, SPIFFS_OK);
+  }
+  TEST_CHECK_GE(SPIFFS_fflush(FS, fd), SPIFFS_OK);
+
+  res = SPIFFS_stat(FS, "1", &s);
+  TEST_CHECK_GE(res, SPIFFS_OK);
+  size = s.size;
+  printf("file elongated, size: %i..\n", size);
+
+  printf("make sure map is full but for one element\n");
+  int zeroed = 0;
+  for (i = 0; i < entries; i++) {
+    printf("%04x ", ixbuf[i]);
+    if (ixbuf[i] == 0) zeroed++;
+  }
+  printf("\n");
+  TEST_CHECK_LE(zeroed, 1);
+
+  printf("remap till end\n");
+  TEST_CHECK_EQ(SPIFFS_ix_remap(FS, fd, size), SPIFFS_OK);
+
+  printf("make sure map is empty but for one element\n");
+  int nonzero = 0;
+  for (i = 0; i < entries; i++) {
+    printf("%04x ", ixbuf[i]);
+    if (ixbuf[i]) nonzero++;
+  }
+  printf("\n");
+  TEST_CHECK_LE(nonzero, 1);
+
+  printf("elongate again, by other fd\n");
+
+  spiffs_file fd2 = SPIFFS_open(FS, "1", SPIFFS_O_WRONLY | SPIFFS_O_APPEND, 0);
+  TEST_CHECK_GT(fd2, 0);
+
+  for (i = 0; i < size_pages; i++) {
+    memrand(buf, sizeof(buf));
+    res = SPIFFS_write(FS, fd2, buf, sizeof(buf));
+    TEST_CHECK_GE(res, SPIFFS_OK);
+  }
+  TEST_CHECK_GE(SPIFFS_close(FS, fd2), SPIFFS_OK);
+
+  printf("make sure map is full but for one element\n");
+  zeroed = 0;
+  for (i = 0; i < entries; i++) {
+    printf("%04x ", ixbuf[i]);
+    if (ixbuf[i] == 0) zeroed++;
+  }
+  printf("\n");
+  TEST_CHECK_LE(zeroed, 1);
+
+  return TEST_RES_OK;
+}
+TEST_END
+
+#endif // SPIFFS_IX_MAP
+
+SUITE_TESTS(hydrogen_tests)
+  ADD_TEST(info)
+#if SPIFFS_USE_MAGIC
+  ADD_TEST(magic)
+#if SPIFFS_USE_MAGIC_LENGTH
+  ADD_TEST(magic_length)
+#if SPIFFS_SINGLETON==0
+  ADD_TEST(magic_length_probe)
+#endif
+#endif
+#endif
+  ADD_TEST(missing_file)
+  ADD_TEST(bad_fd)
+  ADD_TEST(closed_fd)
+  ADD_TEST(deleted_same_fd)
+  ADD_TEST(deleted_other_fd)
+  ADD_TEST(file_by_open)
+  ADD_TEST(file_by_creat)
+  ADD_TEST(file_by_open_excl)
+#if SPIFFS_FILEHDL_OFFSET
+  ADD_TEST(open_fh_offs)
+#endif
+  ADD_TEST(list_dir)
+  ADD_TEST(open_by_dirent)
+  ADD_TEST(open_by_page)
+  ADD_TEST(user_callback_basic)
+  ADD_TEST(user_callback_gc)
+  ADD_TEST(name_too_long)
+  ADD_TEST(rename)
+#if SPIFFS_OBJ_META_LEN
+  ADD_TEST(update_meta)
+#endif
+  ADD_TEST(remove_single_by_path)
+  ADD_TEST(remove_single_by_fd)
+  ADD_TEST(write_cache)
+  ADD_TEST(write_big_file_chunks_page)
+  ADD_TEST(write_big_files_chunks_page)
+  ADD_TEST(write_big_file_chunks_index)
+  ADD_TEST(write_big_files_chunks_index)
+  ADD_TEST(write_big_file_chunks_huge)
+  ADD_TEST(write_big_files_chunks_huge)
+  ADD_TEST(truncate_big_file)
+  ADD_TEST(simultaneous_write)
+  ADD_TEST(simultaneous_write_append)
+  ADD_TEST(file_uniqueness)
+  ADD_TEST(read_chunk_1)
+  ADD_TEST(read_chunk_page)
+  ADD_TEST(read_chunk_index)
+  ADD_TEST(read_chunk_huge)
+  ADD_TEST(read_beyond)
+  ADD_TEST(read_beyond2)
+  ADD_TEST(bad_index_1)
+  ADD_TEST(bad_index_2)
+  ADD_TEST(lseek_simple_modification)
+  ADD_TEST(lseek_modification_append)
+  ADD_TEST(lseek_modification_append_multi)
+  ADD_TEST(lseek_read)
+  ADD_TEST(lseek_oob)
+  ADD_TEST(gc_quick)
+  ADD_TEST(write_small_file_chunks_1)
+  ADD_TEST(write_small_files_chunks_1)
+  ADD_TEST(write_big_file_chunks_1)
+  ADD_TEST(write_big_files_chunks_1)
+  ADD_TEST(long_run_config_many_small_one_long)
+  ADD_TEST(long_run_config_many_medium)
+  ADD_TEST(long_run_config_many_small)
+  ADD_TEST(long_run)
+#if SPIFFS_IX_MAP
+  ADD_TEST(ix_map_basic)
+  ADD_TEST(ix_map_remap)
+  ADD_TEST(ix_map_partial)
+  ADD_TEST(ix_map_beyond)
+#endif
+
+SUITE_END(hydrogen_tests)
+
diff --git a/components/spiffs/spiffs/src/test/test_spiffs.c b/components/spiffs/spiffs/src/test/test_spiffs.c
new file mode 100644
index 00000000..aa608660
--- /dev/null
+++ b/components/spiffs/spiffs/src/test/test_spiffs.c
@@ -0,0 +1,1111 @@
+/*
+ * 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>
+#include <errno.h>
+
+#define AREA(x) _area[(x) - addr_offset]
+
+static u32_t _area_sz;
+static unsigned char *_area = NULL;
+static u32_t addr_offset = 0;
+
+static int *_erases;
+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;
+static u32_t _fs_locks;
+
+spiffs __fs;
+static u8_t *_work = NULL;
+static u8_t *_fds = NULL;
+static u32_t _fds_sz;
+static u8_t *_cache = NULL;
+static u32_t _cache_sz;
+
+static int check_valid_flash = 1;
+
+#ifndef TEST_PATH
+#define TEST_PATH "/dev/shm/spiffs/test-data/"
+#endif
+
+// taken from http://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux
+// thanks Jonathan Leffler
+
+static int do_mkdir(const char *path, mode_t mode)
+{
+  struct stat st;
+  int status = 0;
+
+  if (stat(path, &st) != 0) {
+    /* Directory does not exist. EEXIST for race condition */
+    if (mkdir(path, mode) != 0 && errno != EEXIST) {
+      status = -1;
+    }
+  } else if (!S_ISDIR(st.st_mode)) {
+    errno = ENOTDIR;
+    status = -1;
+  }
+
+  return status;
+}
+
+/**
+** mkpath - ensure all directories in path exist
+** Algorithm takes the pessimistic view and works top-down to ensure
+** each directory in path exists, rather than optimistically creating
+** the last element and working backwards.
+*/
+static int mkpath(const char *path, mode_t mode) {
+  char *pp;
+  char *sp;
+  int status;
+  char *copypath = strdup(path);
+
+  status = 0;
+  pp = copypath;
+  while (status == 0 && (sp = strchr(pp, '/')) != 0) {
+    if (sp != pp)  {
+      /* Neither root nor double slash in path */
+      *sp = '\0';
+      status = do_mkdir(copypath, mode);
+      *sp = '/';
+    }
+    pp = sp + 1;
+  }
+  if (status == 0) {
+      status = do_mkdir(path, mode);
+  }
+  free(copypath);
+  return status;
+}
+
+// end take
+//
+//
+char *make_test_fname(const char *name) {
+  sprintf(_path, "%s/%s", TEST_PATH, name);
+  return _path;
+}
+
+void create_test_path(void) {
+  if (mkpath(TEST_PATH, 0755)) {
+    printf("could not create path %s\n", TEST_PATH);
+    exit(1);
+  }
+}
+
+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(
+#if SPIFFS_HAL_CALLBACK_EXTRA
+    spiffs *fs,
+#endif
+    u32_t addr, u32_t size, u8_t *dst) {
+  //printf("rd @ addr %08x => %p\n", addr, &AREA(addr));
+  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 < SPIFFS_CFG_PHYS_ADDR(&__fs)) {
+    printf("FATAL read addr too low %08x < %08x\n", addr, SPIFFS_PHYS_ADDR);
+    ERREXIT();
+    return -1;
+  }
+  if (addr + size > SPIFFS_CFG_PHYS_ADDR(&__fs) + SPIFFS_CFG_PHYS_SZ(&__fs)) {
+    printf("FATAL read addr too high %08x + %08x > %08x\n", addr, size, SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE);
+    ERREXIT();
+    return -1;
+  }
+  memcpy(dst, &AREA(addr), size);
+  return 0;
+}
+
+static s32_t _write(
+#if SPIFFS_HAL_CALLBACK_EXTRA
+    spiffs *fs,
+#endif
+    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 < SPIFFS_CFG_PHYS_ADDR(&__fs)) {
+    printf("FATAL write addr too low %08x < %08x\n", addr, SPIFFS_PHYS_ADDR);
+    ERREXIT();
+    return -1;
+  }
+  if (addr + size > SPIFFS_CFG_PHYS_ADDR(&__fs) + SPIFFS_CFG_PHYS_SZ(&__fs)) {
+    printf("FATAL write addr too high %08x + %08x > %08x\n", addr, size, SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE);
+    ERREXIT();
+    return -1;
+  }
+
+  for (i = 0; i < size; i++) {
+    if (((addr + i) & (SPIFFS_CFG_LOG_PAGE_SZ(&__fs)-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 (as part of writing %d bytes to addr %08x)\n", src[i], AREA(addr + i), addr+i, size, addr);
+        spiffs_page_ix pix = (addr + i) / SPIFFS_CFG_LOG_PAGE_SZ(&__fs);
+        dump_page(&__fs, pix);
+	ERREXIT();
+        return -1;
+      }
+    }
+    AREA(addr + i) &= src[i];
+  }
+  return 0;
+}
+static s32_t _erase(
+#if SPIFFS_HAL_CALLBACK_EXTRA
+    spiffs *fs,
+#endif
+    u32_t addr, u32_t size) {
+  if (addr & (SPIFFS_CFG_PHYS_ERASE_SZ(&__fs)-1)) {
+    printf("trying to erase at addr %08x, out of boundary\n", addr);
+    ERREXIT();
+    return -1;
+  }
+  if (size & (SPIFFS_CFG_PHYS_ERASE_SZ(&__fs)-1)) {
+    printf("trying to erase at with size %08x, out of boundary\n", size);
+    ERREXIT();
+    return -1;
+  }
+  _erases[(addr-SPIFFS_CFG_PHYS_ADDR(&__fs))/SPIFFS_CFG_PHYS_ERASE_SZ(&__fs)]++;
+  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 = SPIFFS_CFG_LOG_PAGE_SZ(fs);
+  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_set(u32_t addr, u8_t d, u32_t size) {
+  int i;
+  for (i = 0; i < size; i++) {
+    AREA(addr + i) = d;
+  }
+}
+
+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;
+  spiffs_block_ix bix_offs;
+  printf("  BLOCK     |\n");
+  printf("   AGE COUNT|\n");
+  for (bix_offs = 0; bix_offs < fs->block_count; bix_offs+=8) {
+    for (bix = bix_offs; bix < bix_offs+8 && bix < fs->block_count; bix++) {
+      printf("----%3i ----|", bix);
+    }
+    printf("\n");
+    for (bix = bix_offs; bix < bix_offs+8 && 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 int check_cb_count;
+// static u32_t old_perc = 999;
+static void spiffs_check_cb_f(
+#if SPIFFS_HAL_CALLBACK_EXTRA
+    spiffs *fs,
+#endif
+    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) {
+    check_cb_count++;
+    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_set_addr_offset(u32_t offset) {
+  addr_offset = offset;
+}
+
+void test_lock(spiffs *fs) {
+  if (_fs_locks != 0) {
+    printf("FATAL: reentrant locks. Abort.\n");
+    ERREXIT();
+    exit(-1);
+  }
+  _fs_locks++;
+}
+
+void test_unlock(spiffs *fs) {
+  if (_fs_locks != 1) {
+    printf("FATAL: unlocking unlocked. Abort.\n");
+    ERREXIT();
+    exit(-1);
+  }
+  _fs_locks--;
+}
+
+s32_t fs_mount_specific(u32_t phys_addr, u32_t phys_size,
+    u32_t phys_sector_size,
+    u32_t log_block_size, u32_t log_page_size) {
+  spiffs_config c;
+  c.hal_erase_f = _erase;
+  c.hal_read_f = _read;
+  c.hal_write_f = _write;
+#if SPIFFS_SINGLETON == 0
+  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;
+#endif
+#if SPIFFS_FILEHDL_OFFSET
+  c.fh_ix_offset = TEST_SPIFFS_FILEHDL_OFFSET;
+#endif
+  return SPIFFS_mount(&__fs, &c, _work, _fds, _fds_sz, _cache, _cache_sz, spiffs_check_cb_f);
+}
+
+static void fs_create(u32_t spiflash_size,
+    u32_t phys_sector_size,
+    u32_t log_page_size,
+    u32_t descriptors, u32_t cache_pages) {
+  _area_sz = spiflash_size;
+  _area = malloc(spiflash_size);
+  ASSERT(_area != NULL, "testbench area could not be malloced");
+
+  const u32_t erase_sz = sizeof(int) * (spiflash_size / phys_sector_size);
+  _erases = malloc(erase_sz);
+  ASSERT(_erases != NULL, "testbench erase log could not be malloced");
+  memset(_erases, 0, erase_sz);
+
+  _fds_sz = descriptors * sizeof(spiffs_fd);
+  _fds = malloc(_fds_sz);
+  ASSERT(_fds != NULL, "testbench fd buffer could not be malloced");
+  memset(_fds, 0, _fds_sz);
+
+#if SPIFFS_CACHE
+  _cache_sz = sizeof(spiffs_cache) + cache_pages * (sizeof(spiffs_cache_page) + log_page_size);
+  _cache = malloc(_cache_sz);
+  ASSERT(_cache != NULL, "testbench cache could not be malloced");
+  memset(_cache, 0, _cache_sz);
+#endif
+
+  const u32_t work_sz = log_page_size * 2;
+  _work = malloc(work_sz);
+  ASSERT(_work != NULL, "testbench work buffer could not be malloced");
+  memset(_work, 0, work_sz);
+}
+
+static void fs_free(void) {
+  if (_area) free(_area);
+  _area = NULL;
+  if (_erases) free(_erases);
+  _erases = NULL;
+  if (_fds) free(_fds);
+  _fds = NULL;
+  if (_cache) free(_cache);
+  _cache = NULL;
+  if (_work) free(_work);
+  _work = NULL;
+}
+
+/**
+ * addr_offset
+ */
+void fs_reset_specific(u32_t addr_offset, u32_t phys_addr, u32_t phys_size,
+    u32_t phys_sector_size,
+    u32_t log_block_size, u32_t log_page_size) {
+  fs_create(phys_size + phys_addr - addr_offset,
+            phys_sector_size,
+            log_page_size,
+            DEFAULT_NUM_FD,
+            DEFAULT_NUM_CACHE_PAGES);
+  fs_set_addr_offset(addr_offset);
+  memset(&AREA(addr_offset), 0xcc, _area_sz);
+  memset(&AREA(phys_addr), 0xff, phys_size);
+  memset(&__fs, 0, sizeof(__fs));
+
+  s32_t res = fs_mount_specific(phys_addr, phys_size, phys_sector_size, log_block_size, log_page_size);
+
+#if SPIFFS_USE_MAGIC
+  if (res == SPIFFS_OK) {
+    SPIFFS_unmount(&__fs);
+  }
+  res = SPIFFS_format(&__fs);
+  if (res != SPIFFS_OK) {
+    printf("format failed, %i\n", SPIFFS_errno(&__fs));
+  }
+  res = fs_mount_specific(phys_addr, phys_size, phys_sector_size, log_block_size, log_page_size);
+  if (res != SPIFFS_OK) {
+    printf("mount failed, %i\n", SPIFFS_errno(&__fs));
+  }
+#endif
+
+  clear_flash_ops_log();
+  log_flash_ops = 1;
+  fs_check_fixes = 0;
+}
+
+void fs_reset() {
+  fs_reset_specific(0, SPIFFS_PHYS_ADDR, SPIFFS_FLASH_SIZE, SECTOR_SIZE, LOG_BLOCK, LOG_PAGE);
+}
+
+void fs_store_dump(char *fname) {
+  int pfd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+  ASSERT(pfd > 0, "could not open dump file");
+  write(pfd, _area, _area_sz);
+  close(pfd);
+}
+
+void fs_load_dump(char *fname) {
+  int pfd = open(fname, O_RDONLY, S_IRUSR | S_IWUSR);
+  ASSERT(pfd > 0, "could not load dump");
+  read(pfd, _area, _area_sz);
+  close(pfd);
+}
+
+void fs_mount_dump(char *fname,
+    u32_t addr_offset, u32_t phys_addr, u32_t phys_size,
+        u32_t phys_sector_size,
+        u32_t log_block_size, u32_t log_page_size) {
+  fs_create(phys_size + phys_addr - addr_offset,
+            phys_sector_size,
+            log_page_size,
+            DEFAULT_NUM_FD,
+            DEFAULT_NUM_CACHE_PAGES);
+  fs_set_addr_offset(addr_offset);
+  memset(&AREA(addr_offset), 0xcc, _area_sz);
+  memset(&AREA(phys_addr), 0xff, phys_size);
+  memset(&__fs, 0, sizeof(__fs));
+
+  fs_load_dump(fname);
+
+  s32_t res = fs_mount_specific(phys_addr, phys_size, phys_sector_size, log_block_size, log_page_size);
+
+  ASSERT(res == SPIFFS_OK, "failed mounting dump, check settings");
+
+  clear_flash_ops_log();
+  log_flash_ops = 1;
+  fs_check_fixes = 0;
+}
+
+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) {
+  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;
+  }
+
+  off_t fsize = lseek(pfd, 0, SEEK_END);
+  if (s.size != fsize) {
+    printf("  read_and_verify: size differs, %s spiffs:%d!=fs:%ld\n", name, s.size, fsize);
+    return -1;
+  }
+  lseek(pfd, 0, SEEK_SET);
+
+  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
+  if (_area) 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);
+#if SPIFFS_OBJ_META_LEN
+  {
+    int i;
+    for (i = 0; i < SPIFFS_OBJ_META_LEN; i++) {
+      CHECK(s.meta[i] == 0xff);
+    }
+  }
+#endif
+  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 (fd < 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;
+}
+
+static u32_t crc32_tab[] = {
+  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+  0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+  0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+  0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+  0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+  0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+  0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+  0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+  0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+  0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+  0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+  0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+  0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+  0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+  0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+  0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+  0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+  0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+  0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+  0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+  0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+  0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+  0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+  0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+  0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+  0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+  0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+  0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+  0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+  0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+static u32_t crc32(u32_t crc, const void *buf, size_t size)
+{
+  const u8_t *p;
+
+  p = buf;
+  crc = crc ^ ~0U;
+
+  while (size--)
+    crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+
+  return crc ^ ~0U;
+}
+
+u32_t get_spiffs_file_crc_by_fd(spiffs_file fd) {
+  s32_t res;
+  u32_t crc = 0;
+  u8_t buf[256];
+
+  ASSERT(SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_SET) >= 0, "could not seek to start of file");
+
+  while ((res = SPIFFS_read(FS, fd, buf, sizeof(buf))) > SPIFFS_OK) {
+    crc = crc32(crc, buf, res);
+  }
+  ASSERT(SPIFFS_errno(FS) == SPIFFS_ERR_END_OF_OBJECT || SPIFFS_errno(FS) == SPIFFS_OK, "failed reading file");
+
+  return crc;
+}
+
+u32_t get_spiffs_file_crc(char *name) {
+  s32_t res;
+  spiffs_file fd;
+  fd = SPIFFS_open(FS, name, SPIFFS_O_RDONLY, 0);
+  ASSERT(fd >= 0, "Could not open file");
+  u32_t crc = get_spiffs_file_crc_by_fd(fd);
+  res = SPIFFS_close(FS, fd);
+  ASSERT(res >= SPIFFS_OK, "failing closing file");
+  return crc;
+}
+
+#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() {
+  create_test_path();
+  fs_set_validate_flashing(1);
+  test_init(test_on_stop);
+}
+
+void _setup() {
+  _fs_locks = 0;
+  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)));
+  chits_tot = 0;
+  cmiss_tot = 0;
+#endif
+#endif
+  if (_area) {
+    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 output begin\n");
+    check_cb_count = 0;
+    SPIFFS_check(FS);
+    printf("  fs consistency check output end\n");
+    if (check_cb_count) {
+      ERREXIT();
+    }
+  }
+  clear_test_path();
+  fs_free();
+  printf("  locks : %i\n", _fs_locks);
+  if (_fs_locks != 0) {
+    printf("FATAL: lock asymmetry. Abort.\n");
+    ERREXIT();
+    exit(-1);
+  }
+}
+
+u32_t tfile_get_size(tfile_size s) {
+  switch (s) {
+  case EMPTY:
+    return 0;
+  case SMALL: // half a data page
+    return SPIFFS_DATA_PAGE_SIZE(FS)/2;
+  case MEDIUM: // one block
+    return SPIFFS_DATA_PAGE_SIZE(FS) * (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS));
+  case LARGE: // third of fs
+    return SPIFFS_DATA_PAGE_SIZE(FS) * (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS)) * (FS)->block_count/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(tfile));
+        }
+
+      }
+    }
+
+    run++;
+  }
+  free(tfiles);
+  return 0;
+}
+
+int count_taken_fds(spiffs *fs) {
+  int i;
+  spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
+  int taken = 0;
+  for (i = 0; i < fs->fd_count; i++) {
+    spiffs_fd *cur_fd = &fds[i];
+    if (cur_fd->file_nbr) taken++;
+  }
+  return taken;
+}
diff --git a/components/spiffs/spiffs/src/test/test_spiffs.h b/components/spiffs/spiffs/src/test/test_spiffs.h
new file mode 100644
index 00000000..4c39bdba
--- /dev/null
+++ b/components/spiffs/spiffs/src/test/test_spiffs.h
@@ -0,0 +1,109 @@
+/*
+ * 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) \
+    (SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_PAGE_SZ(fs)- (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 = 3,
+  NORMAL = 15,
+  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 addr_offset, u32_t phys_addr, u32_t phys_size,
+    u32_t phys_sector_size,
+    u32_t log_block_size, u32_t log_page_size);
+s32_t fs_mount_specific(u32_t phys_addr, u32_t phys_size,
+    u32_t phys_sector_size,
+    u32_t log_block_size, u32_t log_page_size);
+void fs_mount_dump(char *fname,
+    u32_t addr_offset, u32_t phys_addr, u32_t phys_size,
+        u32_t phys_sector_size,
+        u32_t log_block_size, u32_t log_page_size);
+
+void fs_store_dump(char *fname);
+void fs_load_dump(char *fname);
+
+void fs_set_addr_offset(u32_t offset);
+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_set(u32_t addr, u8_t d, 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 fs_set_validate_flashing(int i);
+int get_error_count();
+int count_taken_fds(spiffs *fs);
+
+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);
+u32_t get_spiffs_file_crc_by_fd(spiffs_file fd);
+u32_t get_spiffs_file_crc(char *name);
+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);
+
+void test_lock(spiffs *fs);
+void test_unlock(spiffs *fs);
+
+#endif /* TEST_SPIFFS_H_ */
diff --git a/components/spiffs/spiffs/src/test/testrunner.c b/components/spiffs/spiffs/src/test/testrunner.c
new file mode 100644
index 00000000..27419d32
--- /dev/null
+++ b/components/spiffs/spiffs/src/test/testrunner.c
@@ -0,0 +1,238 @@
+/*
+ * 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;
+  char incl_filter[256];
+  char excl_filter[256];
+} test_main;
+
+void test_init(void (*on_stop)(test *t)) {
+  test_main.on_stop = on_stop;
+}
+
+static int abort_on_error = 0;
+static int error_count = 0;
+
+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(line)-1) == 0) {
+        free(line);
+        return 1;
+      }
+    }
+    free(line);
+    return 0;
+  } else {
+    return 1;
+  }
+}
+
+static char check_incl_filter(char *name) {
+  if (strlen(test_main.incl_filter)== 0) return 1;
+  return strstr(name, test_main.incl_filter) == 0 ? 0 : 2;
+}
+
+static char check_excl_filter(char *name) {
+  if (strlen(test_main.excl_filter)== 0) return 1;
+  return strstr(name, test_main.excl_filter) == 0 ? 1 : 0;
+}
+
+void _add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t), int non_default) {
+  if (f == 0) return;
+  if (!check_spec(name)) return;
+  if (check_incl_filter(name) <= non_default) return;
+  if (!check_excl_filter(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;
+  }
+}
+
+int get_error_count(void) {
+  return error_count;
+}
+
+void inc_error_count(void) {
+  error_count++;
+}
+
+int set_abort_on_error(int val) {
+  int old_val = abort_on_error;
+  abort_on_error = val;
+
+  return old_val;
+}
+
+int get_abort_on_error(void) {
+  return abort_on_error;
+}
+
+int run_tests(int argc, char **args) {
+  memset(&test_main, 0, sizeof(test_main));
+  int arg;
+  int incl_filter = 0;
+  int excl_filter = 0;
+  for (arg = 1; arg < argc; arg++) {
+    if (strlen(args[arg]) == 0) continue;
+    if (0 == strcmp("-f", args[arg])) {
+      incl_filter = 1;
+      continue;
+    }
+    if (0 == strcmp("-e", args[arg])) {
+      excl_filter = 1;
+      continue;
+    }
+    if (incl_filter) {
+      strcpy(test_main.incl_filter, args[arg]);
+      incl_filter = 0;
+    } else if (excl_filter) {
+      strcpy(test_main.excl_filter, args[arg]);
+      excl_filter = 0;
+    } else {
+      printf("running tests from %s\n", args[arg]);
+      FILE *fd = fopen(args[1], "r");
+      if (fd == NULL) {
+        printf("%s not found\n", args[arg]);
+        return -2;
+      }
+      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 0;
+  }
+
+  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 start_error_count = get_error_count();
+    int res = cur_t->f(cur_t);
+    if (res == TEST_RES_OK && get_error_count() != start_error_count) {
+      res = TEST_RES_FAIL;
+    }
+    cur_t->test_result = res;
+    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;
+    }
+    cur_t->teardown(cur_t);
+    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");
+    return -1;
+  } else {
+    printf("\nALL TESTS OK\n");
+    return 0;
+  }
+}
diff --git a/components/spiffs/spiffs/src/test/testrunner.h b/components/spiffs/spiffs/src/test/testrunner.h
new file mode 100644
index 00000000..697fb095
--- /dev/null
+++ b/components/spiffs/spiffs/src/test/testrunner.h
@@ -0,0 +1,165 @@
+/*
+ * testrunner.h
+ *
+ *  Created on: Jun 19, 2013
+ *      Author: petera
+ */
+
+/*
+
+file mysuite.c:
+
+SUITE(mysuite)
+
+static void setup(test *t) {}
+
+static void teardown(test *t) {}
+
+TEST(mytest) {
+  printf("mytest runs now..\n");
+  return 0;
+} TEST_END
+
+SUITE_TESTS(mysuite)
+  ADD_TEST(mytest)
+SUITE_END(mysuite)
+
+
+
+file mysuite2.c:
+
+SUITE(mysuite2)
+
+static void setup(test *t) {}
+
+static void teardown(test *t) {}
+
+TEST(mytest2a) {
+  printf("mytest2a runs now..\n");
+  return 0;
+} TEST_END
+
+TEST(mytest2b) {
+  printf("mytest2b runs now..\n");
+  return 0;
+} TEST_END
+
+SUITE_TESTS(mysuite2)
+  ADD_TEST(mytest2a)
+  ADD_TEST(mytest2b)
+SUITE_END(mysuite2)
+
+
+some other file.c:
+
+void add_suites() {
+  ADD_SUITE(mysuite);
+  ADD_SUITE(mysuite2);
+}
+ */
+
+#ifndef TESTRUNNER_H_
+#define TESTRUNNER_H_
+
+#define TEST_RES_OK   0
+#define TEST_RES_FAIL -1
+#define TEST_RES_ASSERT -2
+
+#define ERREXIT() if (get_abort_on_error()) abort(); else inc_error_count()
+
+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;
+  unsigned char test_result;
+} 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:%d\n", __FILE__, __LINE__); \
+  goto __fail_stop; \
+}
+#define TEST_CHECK_EQ(x, y) if ((x) != (y)) { \
+  printf("  TEST FAIL %s:%d, %d != %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \
+  goto __fail_stop; \
+}
+#define TEST_CHECK_NEQ(x, y) if ((x) == (y)) { \
+  printf("  TEST FAIL %s:%d, %d == %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \
+  goto __fail_stop; \
+}
+#define TEST_CHECK_GT(x, y) if ((x) <= (y)) { \
+  printf("  TEST FAIL %s:%d, %d <= %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \
+  goto __fail_stop; \
+}
+#define TEST_CHECK_LT(x, y) if ((x) >= (y)) { \
+  printf("  TEST FAIL %s:%d, %d >= %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \
+  goto __fail_stop; \
+}
+#define TEST_CHECK_GE(x, y) if ((x) < (y)) { \
+  printf("  TEST FAIL %s:%d, %d < %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \
+  goto __fail_stop; \
+}
+#define TEST_CHECK_LE(x, y) if ((x) > (y)) { \
+  printf("  TEST FAIL %s:%d, %d > %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \
+  goto __fail_stop; \
+}
+#define TEST_ASSERT(x) if (!(x)) { \
+  printf("  TEST ASSERT %s:%d\n", __FILE__, __LINE__); \
+  goto __fail_assert; \
+}
+
+#define DBGT(...) printf(__VA_ARGS__)
+
+#define str(s) #s
+
+#define SUITE(sui)
+
+#define SUITE_TESTS(sui) \
+  void _add_suite_tests_##sui(void) {
+
+#define SUITE_END(sui) \
+  }
+
+#define ADD_TEST(tf) \
+  _add_test(__test_##tf, str(tf), setup, teardown, 0);
+
+#define ADD_TEST_NON_DEFAULT(tf) \
+  _add_test(__test_##tf, str(tf), setup, teardown, 1);
+
+#define ADD_SUITE(sui) \
+  extern void _add_suite_tests_##sui(void); \
+  _add_suite_tests_##sui();
+
+#define TEST(tf) \
+  static int __test_##tf(struct test_s *t) { do
+
+#define TEST_END \
+  while(0); \
+  __fail_stop: return TEST_RES_FAIL; \
+  __fail_assert: return TEST_RES_ASSERT; \
+  }
+
+int set_abort_on_error(int val);
+int get_abort_on_error(void);
+int get_error_count(void);
+void inc_error_count(void);
+
+void add_suites(void);
+void test_init(void (*on_stop)(test *t));
+// returns 0 if all tests ok, -1 if any test failed, -2 on badness
+int run_tests(int argc, char **args);
+void _add_suite(const char *suite_name);
+void _add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t), int non_default);
+
+#endif /* TESTRUNNER_H_ */
diff --git a/components/spiffs/spiffs/src/test/testsuites.c b/components/spiffs/spiffs/src/test/testsuites.c
new file mode 100644
index 00000000..cce5cd9d
--- /dev/null
+++ b/components/spiffs/spiffs/src/test/testsuites.c
@@ -0,0 +1,15 @@
+/*
+ * testsuites.c
+ *
+ *  Created on: Jun 19, 2013
+ *      Author: petera
+ */
+
+#include "testrunner.h"
+
+void add_suites(void) {
+  //ADD_SUITE(dev_tests);
+  ADD_SUITE(check_tests);
+  ADD_SUITE(hydrogen_tests);
+  ADD_SUITE(bug_tests);
+}
diff --git a/components/spiffs/spiffs_api.c b/components/spiffs/spiffs_api.c
new file mode 100644
index 00000000..01c256d0
--- /dev/null
+++ b/components/spiffs/spiffs_api.c
@@ -0,0 +1,93 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "freertos/FreeRTOS.h"
+#include "esp_log.h"
+#include "esp_partition.h"
+#include "esp_spiffs.h"
+#include "esp_vfs.h"
+#include "spiffs_api.h"
+
+static const char* TAG = "SPIFFS";
+
+void spiffs_api_lock(spiffs *fs)
+{
+    (void) xSemaphoreTake(((esp_spiffs_t *)(fs->user_data))->lock, portMAX_DELAY);
+}
+
+void spiffs_api_unlock(spiffs *fs)
+{
+    xSemaphoreGive(((esp_spiffs_t *)(fs->user_data))->lock);
+}
+
+s32_t spiffs_api_read(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *dst)
+{
+    esp_err_t err = esp_partition_read(((esp_spiffs_t *)(fs->user_data))->partition, 
+                                        addr, dst, size);
+    if (err) {
+        ESP_LOGE(TAG, "failed to read addr %08x, size %08x, err %d", addr, size, err);
+        return -1;
+    }
+    return 0;
+}
+
+s32_t spiffs_api_write(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *src)
+{
+    esp_err_t err = esp_partition_write(((esp_spiffs_t *)(fs->user_data))->partition, 
+                                        addr, src, size);
+    if (err) {
+        ESP_LOGE(TAG, "failed to write addr %08x, size %08x, err %d", addr, size, err);
+        return -1;
+    }
+    return 0;
+}
+
+s32_t spiffs_api_erase(spiffs *fs, uint32_t addr, uint32_t size)
+{
+    esp_err_t err = esp_partition_erase_range(((esp_spiffs_t *)(fs->user_data))->partition, 
+                                        addr, size);
+    if (err) {
+        ESP_LOGE(TAG, "failed to erase addr %08x, size %08x, err %d", addr, size, err);
+        return -1;
+    }
+    return 0;
+}
+
+void spiffs_api_check(spiffs *fs, spiffs_check_type type, 
+                            spiffs_check_report report, uint32_t arg1, uint32_t arg2)
+{
+    static const char * spiffs_check_type_str[3] = {
+        "LOOKUP",
+        "INDEX",
+        "PAGE"
+    };
+
+    static const char * spiffs_check_report_str[7] = {
+        "PROGRESS",
+        "ERROR",
+        "FIX INDEX",
+        "FIX LOOKUP",
+        "DELETE ORPHANED INDEX",
+        "DELETE PAGE",
+        "DELETE BAD FILE"
+    };
+
+    if (report != SPIFFS_CHECK_PROGRESS) {
+        ESP_LOGE(TAG, "CHECK: type:%s, report:%s, %x:%x", spiffs_check_type_str[type], 
+                              spiffs_check_report_str[report], arg1, arg2);
+    } else {
+        ESP_LOGV(TAG, "CHECK PROGRESS: report:%s, %x:%x", 
+                              spiffs_check_report_str[report], arg1, arg2);
+    }
+}
diff --git a/components/spiffs/spiffs_api.h b/components/spiffs/spiffs_api.h
new file mode 100644
index 00000000..5009716a
--- /dev/null
+++ b/components/spiffs/spiffs_api.h
@@ -0,0 +1,57 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/semphr.h"
+#include "spiffs.h"
+#include "esp_vfs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief SPIFFS definition structure
+ */
+typedef struct {
+    spiffs *fs;                             /*!< Handle to the underlying SPIFFS */
+    SemaphoreHandle_t lock;                 /*!< FS lock */
+    const esp_partition_t* partition;       /*!< The partition on which SPIFFS is located */
+    char base_path[ESP_VFS_PATH_MAX+1];     /*!< Mount point */
+    bool by_label;                          /*!< Partition was mounted by label */
+    spiffs_config cfg;                      /*!< SPIFFS Mount configuration */
+    uint8_t *work;                          /*!< Work Buffer */
+    uint8_t *fds;                           /*!< File Descriptor Buffer */
+    uint32_t fds_sz;                        /*!< File Descriptor Buffer Length */
+    uint8_t *cache;                         /*!< Cache Buffer */
+    uint32_t cache_sz;                      /*!< Cache Buffer Length */
+} esp_spiffs_t;
+
+s32_t spiffs_api_read(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *dst);
+
+s32_t spiffs_api_write(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *src);
+
+s32_t spiffs_api_erase(spiffs *fs, uint32_t addr, uint32_t size);
+
+void spiffs_api_check(spiffs *fs, spiffs_check_type type,
+                            spiffs_check_report report, uint32_t arg1, uint32_t arg2);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/components/spiffs/test/CMakeLists.txt b/components/spiffs/test/CMakeLists.txt
new file mode 100644
index 00000000..860ab6df
--- /dev/null
+++ b/components/spiffs/test/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(COMPONENT_SRCDIRS ".")
+set(COMPONENT_ADD_INCLUDEDIRS ".")
+
+set(COMPONENT_REQUIRES unity spiffs)
+
+register_component()
\ No newline at end of file
diff --git a/components/spiffs/test/component.mk b/components/spiffs/test/component.mk
new file mode 100644
index 00000000..ce464a21
--- /dev/null
+++ b/components/spiffs/test/component.mk
@@ -0,0 +1 @@
+COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
diff --git a/components/spiffs/test/test_spiffs.c b/components/spiffs/test/test_spiffs.c
new file mode 100644
index 00000000..f3592132
--- /dev/null
+++ b/components/spiffs/test/test_spiffs.c
@@ -0,0 +1,652 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/unistd.h>
+#include "unity.h"
+#include "test_utils.h"
+#include "esp_log.h"
+#include "esp_system.h"
+#include "esp_vfs.h"
+#include "esp_spiffs.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "freertos/semphr.h"
+#include "esp_partition.h"
+
+const char* spiffs_test_hello_str = "Hello, World!\n";
+const char* spiffs_test_partition_label = "flash_test";
+
+void test_spiffs_create_file_with_text(const char* name, const char* text)
+{
+    FILE* f = fopen(name, "wb");
+    TEST_ASSERT_NOT_NULL(f);
+    TEST_ASSERT_TRUE(fputs(text, f) != EOF);
+    TEST_ASSERT_EQUAL(0, fclose(f));
+}
+
+void test_spiffs_overwrite_append(const char* filename)
+{
+    /* Create new file with 'aaaa' */
+    test_spiffs_create_file_with_text(filename, "aaaa");
+
+    /* Append 'bbbb' to file */
+    FILE *f_a = fopen(filename, "a");
+    TEST_ASSERT_NOT_NULL(f_a);
+    TEST_ASSERT_NOT_EQUAL(EOF, fputs("bbbb", f_a));
+    TEST_ASSERT_EQUAL(0, fclose(f_a));
+
+    /* Read back 8 bytes from file, verify it's 'aaaabbbb' */
+    char buf[10] = { 0 };
+    FILE *f_r = fopen(filename, "r");
+    TEST_ASSERT_NOT_NULL(f_r);
+    TEST_ASSERT_EQUAL(8, fread(buf, 1, 8, f_r));
+    TEST_ASSERT_EQUAL_STRING_LEN("aaaabbbb", buf, 8);
+
+    /* Be sure we're at end of file */
+    TEST_ASSERT_EQUAL(0, fread(buf, 1, 8, f_r));
+
+    TEST_ASSERT_EQUAL(0, fclose(f_r));
+
+    /* Overwrite file with 'cccc' */
+    test_spiffs_create_file_with_text(filename, "cccc");
+
+    /* Verify file now only contains 'cccc' */
+    f_r = fopen(filename, "r");
+    TEST_ASSERT_NOT_NULL(f_r);
+    bzero(buf, sizeof(buf));
+    TEST_ASSERT_EQUAL(4, fread(buf, 1, 8, f_r)); // trying to read 8 bytes, only expecting 4
+    TEST_ASSERT_EQUAL_STRING_LEN("cccc", buf, 4);
+    TEST_ASSERT_EQUAL(0, fclose(f_r));
+}
+
+void test_spiffs_read_file(const char* filename)
+{
+    FILE* f = fopen(filename, "r");
+    TEST_ASSERT_NOT_NULL(f);
+    char buf[32] = { 0 };
+    int cb = fread(buf, 1, sizeof(buf), f);
+    TEST_ASSERT_EQUAL(strlen(spiffs_test_hello_str), cb);
+    TEST_ASSERT_EQUAL(0, strcmp(spiffs_test_hello_str, buf));
+    TEST_ASSERT_EQUAL(0, fclose(f));
+}
+
+void test_spiffs_open_max_files(const char* filename_prefix, size_t files_count)
+{
+    FILE** files = calloc(files_count, sizeof(FILE*));
+    for (size_t i = 0; i < files_count; ++i) {
+        char name[32];
+        snprintf(name, sizeof(name), "%s_%d.txt", filename_prefix, i);
+        files[i] = fopen(name, "w");
+        TEST_ASSERT_NOT_NULL(files[i]);
+    }
+    /* close everything and clean up */
+    for (size_t i = 0; i < files_count; ++i) {
+        fclose(files[i]);
+    }
+    free(files);
+}
+
+void test_spiffs_lseek(const char* filename)
+{
+    FILE* f = fopen(filename, "wb+");
+    TEST_ASSERT_NOT_NULL(f);
+    TEST_ASSERT_EQUAL(11, fprintf(f, "0123456789\n"));
+    TEST_ASSERT_EQUAL(0, fseek(f, -2, SEEK_CUR));
+    TEST_ASSERT_EQUAL('9', fgetc(f));
+    TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_SET));
+    TEST_ASSERT_EQUAL('3', fgetc(f));
+    TEST_ASSERT_EQUAL(0, fseek(f, -3, SEEK_END));
+    TEST_ASSERT_EQUAL('8', fgetc(f));
+    TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
+    TEST_ASSERT_EQUAL(11, ftell(f));
+    TEST_ASSERT_EQUAL(4, fprintf(f, "abc\n"));
+    TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
+    TEST_ASSERT_EQUAL(15, ftell(f));
+    TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
+    char buf[20];
+    TEST_ASSERT_EQUAL(15, fread(buf, 1, sizeof(buf), f));
+    const char ref_buf[] = "0123456789\nabc\n";
+    TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1);
+
+    TEST_ASSERT_EQUAL(0, fclose(f));
+}
+
+void test_spiffs_stat(const char* filename)
+{
+    test_spiffs_create_file_with_text(filename, "foo\n");
+    struct stat st;
+    TEST_ASSERT_EQUAL(0, stat(filename, &st));
+    TEST_ASSERT(st.st_mode & S_IFREG);
+    TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
+}
+
+void test_spiffs_unlink(const char* filename)
+{
+    test_spiffs_create_file_with_text(filename, "unlink\n");
+
+    TEST_ASSERT_EQUAL(0, unlink(filename));
+
+    TEST_ASSERT_NULL(fopen(filename, "r"));
+}
+
+void test_spiffs_rename(const char* filename_prefix)
+{
+    char name_dst[64];
+    char name_src[64];
+    snprintf(name_dst, sizeof(name_dst), "%s_dst.txt", filename_prefix);
+    snprintf(name_src, sizeof(name_src), "%s_src.txt", filename_prefix);
+
+    unlink(name_dst);
+    unlink(name_src);
+
+    FILE* f = fopen(name_src, "w+");
+    TEST_ASSERT_NOT_NULL(f);
+    const char* str = "0123456789";
+    for (int i = 0; i < 400; ++i) {
+        TEST_ASSERT_NOT_EQUAL(EOF, fputs(str, f));
+    }
+    TEST_ASSERT_EQUAL(0, fclose(f));
+    TEST_ASSERT_EQUAL(0, rename(name_src, name_dst));
+    TEST_ASSERT_NULL(fopen(name_src, "r"));
+    FILE* fdst = fopen(name_dst, "r");
+    TEST_ASSERT_NOT_NULL(fdst);
+    TEST_ASSERT_EQUAL(0, fseek(fdst, 0, SEEK_END));
+    TEST_ASSERT_EQUAL(4000, ftell(fdst));
+    TEST_ASSERT_EQUAL(0, fclose(fdst));
+}
+
+void test_spiffs_can_opendir(const char* path)
+{
+    char name_dir_file[64];
+    const char * file_name = "test_opd.txt";
+    snprintf(name_dir_file, sizeof(name_dir_file), "%s/%s", path, file_name);
+    unlink(name_dir_file);
+    test_spiffs_create_file_with_text(name_dir_file, "test_opendir\n");
+    DIR* dir = opendir(path);
+    TEST_ASSERT_NOT_NULL(dir);
+    bool found = false;
+    while (true) {
+        struct dirent* de = readdir(dir);
+        if (!de) {
+            break;
+        }
+        if (strcasecmp(de->d_name, file_name) == 0) {
+            found = true;
+            break;
+        }
+    }
+    TEST_ASSERT_TRUE(found);
+    TEST_ASSERT_EQUAL(0, closedir(dir));
+    unlink(name_dir_file);
+}
+
+void test_spiffs_opendir_readdir_rewinddir(const char* dir_prefix)
+{
+    char name_dir_inner_file[64];
+    char name_dir_inner[64];
+    char name_dir_file3[64];
+    char name_dir_file2[64];
+    char name_dir_file1[64];
+
+    snprintf(name_dir_inner_file, sizeof(name_dir_inner_file), "%s/inner/3.txt", dir_prefix);
+    snprintf(name_dir_inner, sizeof(name_dir_inner), "%s/inner", dir_prefix);
+    snprintf(name_dir_file3, sizeof(name_dir_file2), "%s/boo.bin", dir_prefix);
+    snprintf(name_dir_file2, sizeof(name_dir_file2), "%s/2.txt", dir_prefix);
+    snprintf(name_dir_file1, sizeof(name_dir_file1), "%s/1.txt", dir_prefix);
+
+    unlink(name_dir_inner_file);
+    rmdir(name_dir_inner);
+    unlink(name_dir_file1);
+    unlink(name_dir_file2);
+    unlink(name_dir_file3);
+    rmdir(dir_prefix);
+
+    test_spiffs_create_file_with_text(name_dir_file1, "1\n");
+    test_spiffs_create_file_with_text(name_dir_file2, "2\n");
+    test_spiffs_create_file_with_text(name_dir_file3, "\01\02\03");
+    test_spiffs_create_file_with_text(name_dir_inner_file, "3\n");
+
+    DIR* dir = opendir(dir_prefix);
+    TEST_ASSERT_NOT_NULL(dir);
+    int count = 0;
+    const char* names[4];
+    while(count < 4) {
+        struct dirent* de = readdir(dir);
+        if (!de) {
+            break;
+        }
+        printf("found '%s'\n", de->d_name);
+        if (strcasecmp(de->d_name, "1.txt") == 0) {
+            TEST_ASSERT_TRUE(de->d_type == DT_REG);
+            names[count] = "1.txt";
+            ++count;
+        } else if (strcasecmp(de->d_name, "2.txt") == 0) {
+            TEST_ASSERT_TRUE(de->d_type == DT_REG);
+            names[count] = "2.txt";
+            ++count;
+        } else if (strcasecmp(de->d_name, "inner/3.txt") == 0) {
+            TEST_ASSERT_TRUE(de->d_type == DT_REG);
+            names[count] = "inner/3.txt";
+            ++count;
+        } else if (strcasecmp(de->d_name, "boo.bin") == 0) {
+            TEST_ASSERT_TRUE(de->d_type == DT_REG);
+            names[count] = "boo.bin";
+            ++count;
+        } else {
+            TEST_FAIL_MESSAGE("unexpected directory entry");
+        }
+    }
+    TEST_ASSERT_EQUAL(count, 4);
+
+    rewinddir(dir);
+    struct dirent* de = readdir(dir);
+    TEST_ASSERT_NOT_NULL(de);
+    TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0]));
+    seekdir(dir, 3);
+    de = readdir(dir);
+    TEST_ASSERT_NOT_NULL(de);
+    TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3]));
+    seekdir(dir, 1);
+    de = readdir(dir);
+    TEST_ASSERT_NOT_NULL(de);
+    TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1]));
+    seekdir(dir, 2);
+    de = readdir(dir);
+    TEST_ASSERT_NOT_NULL(de);
+    TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2]));
+
+    TEST_ASSERT_EQUAL(0, closedir(dir));
+}
+
+void test_spiffs_readdir_many_files(const char* dir_prefix)
+{
+    const int n_files = 40;
+    const int n_folders = 4;
+    unsigned char file_count[n_files * n_folders];
+    memset(file_count, 0, sizeof(file_count)/sizeof(file_count[0]));
+    char file_name[ESP_VFS_PATH_MAX + CONFIG_SPIFFS_OBJ_NAME_LEN];
+
+    /* clean stale files before the test */
+    DIR* dir = opendir(dir_prefix);
+    if (dir) {
+        while (true) {
+            struct dirent* de = readdir(dir);
+            if (!de) {
+                break;
+            }
+            int len = snprintf(file_name, sizeof(file_name), "%s/%s", dir_prefix, de->d_name);
+            assert(len < sizeof(file_name));
+            unlink(file_name);
+        }
+    }
+
+    /* create files */
+    for (int d = 0; d < n_folders; ++d) {
+        printf("filling directory %d\n", d);
+        for (int f = 0; f < n_files; ++f) {
+            snprintf(file_name, sizeof(file_name), "%s/%d/%d.txt", dir_prefix, d, f);
+            test_spiffs_create_file_with_text(file_name, file_name);
+        }
+    }
+
+    /* list files */
+    for (int d = 0; d < n_folders; ++d) {
+        printf("listing files in directory %d\n", d);
+        snprintf(file_name, sizeof(file_name), "%s/%d", dir_prefix, d);
+        dir = opendir(file_name);
+        TEST_ASSERT_NOT_NULL(dir);
+        while (true) {
+            struct dirent* de = readdir(dir);
+            if (!de) {
+                break;
+            }
+            int file_id;
+            TEST_ASSERT_EQUAL(1, sscanf(de->d_name, "%d.txt", &file_id));
+            file_count[file_id + d * n_files]++;
+        }
+        closedir(dir);
+    }
+
+    /* check that all created files have been seen */
+    for (int d = 0; d < n_folders; ++d) {
+        printf("checking that all files have been found in directory %d\n", d);
+        for (int f = 0; f < n_files; ++f) {
+            TEST_ASSERT_EQUAL(1, file_count[f + d * n_files]);
+        }
+    }
+}
+
+
+typedef struct {
+    const char* filename;
+    bool write;
+    size_t word_count;
+    int seed;
+    SemaphoreHandle_t done;
+    int result;
+} read_write_test_arg_t;
+
+#define READ_WRITE_TEST_ARG_INIT(name, seed_) \
+        { \
+            .filename = name, \
+            .seed = seed_, \
+            .word_count = 4096, \
+            .write = true, \
+            .done = xSemaphoreCreateBinary() \
+        }
+
+static void read_write_task(void* param)
+{
+    read_write_test_arg_t* args = (read_write_test_arg_t*) param;
+    FILE* f = fopen(args->filename, args->write ? "wb" : "rb");
+    if (f == NULL) {
+        args->result = ESP_ERR_NOT_FOUND;
+        goto done;
+    }
+
+    srand(args->seed);
+    for (size_t i = 0; i < args->word_count; ++i) {
+        uint32_t val = rand();
+        if (args->write) {
+            int cnt = fwrite(&val, sizeof(val), 1, f);
+            if (cnt != 1) {
+                ets_printf("E(w): i=%d, cnt=%d val=%d\n\n", i, cnt, val);
+                args->result = ESP_FAIL;
+                goto close;
+            }
+        } else {
+            uint32_t rval;
+            int cnt = fread(&rval, sizeof(rval), 1, f);
+            if (cnt != 1) {
+                ets_printf("E(r): i=%d, cnt=%d rval=%d\n\n", i, cnt, rval);
+                args->result = ESP_FAIL;
+                goto close;
+            }
+        }
+    }
+    args->result = ESP_OK;
+
+close:
+    fclose(f);
+
+done:
+    xSemaphoreGive(args->done);
+    vTaskDelay(1);
+    vTaskDelete(NULL);
+}
+
+void test_spiffs_concurrent(const char* filename_prefix)
+{
+    char names[4][64];
+    for (size_t i = 0; i < 4; ++i) {
+        snprintf(names[i], sizeof(names[i]), "%s%d", filename_prefix, i + 1);
+        unlink(names[i]);
+    }
+
+    read_write_test_arg_t args1 = READ_WRITE_TEST_ARG_INIT(names[0], 1);
+    read_write_test_arg_t args2 = READ_WRITE_TEST_ARG_INIT(names[1], 2);
+
+    printf("writing f1 and f2\n");
+    const int cpuid_0 = 0;
+    const int cpuid_1 = portNUM_PROCESSORS - 1;
+    xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, cpuid_0);
+    xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, cpuid_1);
+
+    xSemaphoreTake(args1.done, portMAX_DELAY);
+    printf("f1 done\n");
+    TEST_ASSERT_EQUAL(ESP_OK, args1.result);
+    xSemaphoreTake(args2.done, portMAX_DELAY);
+    printf("f2 done\n");
+    TEST_ASSERT_EQUAL(ESP_OK, args2.result);
+
+    args1.write = false;
+    args2.write = false;
+    read_write_test_arg_t args3 = READ_WRITE_TEST_ARG_INIT(names[2], 3);
+    read_write_test_arg_t args4 = READ_WRITE_TEST_ARG_INIT(names[3], 4);
+
+    printf("reading f1 and f2, writing f3 and f4\n");
+
+    xTaskCreatePinnedToCore(&read_write_task, "rw3", 2048, &args3, 3, NULL, cpuid_1);
+    xTaskCreatePinnedToCore(&read_write_task, "rw4", 2048, &args4, 3, NULL, cpuid_0);
+    xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, cpuid_0);
+    xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, cpuid_1);
+
+    xSemaphoreTake(args1.done, portMAX_DELAY);
+    printf("f1 done\n");
+    TEST_ASSERT_EQUAL(ESP_OK, args1.result);
+    xSemaphoreTake(args2.done, portMAX_DELAY);
+    printf("f2 done\n");
+    TEST_ASSERT_EQUAL(ESP_OK, args2.result);
+    xSemaphoreTake(args3.done, portMAX_DELAY);
+    printf("f3 done\n");
+    TEST_ASSERT_EQUAL(ESP_OK, args3.result);
+    xSemaphoreTake(args4.done, portMAX_DELAY);
+    printf("f4 done\n");
+    TEST_ASSERT_EQUAL(ESP_OK, args4.result);
+
+    vSemaphoreDelete(args1.done);
+    vSemaphoreDelete(args2.done);
+    vSemaphoreDelete(args3.done);
+    vSemaphoreDelete(args4.done);
+}
+
+
+static void test_setup()
+{
+    esp_vfs_spiffs_conf_t conf = {
+      .base_path = "/spiffs",
+      .partition_label = spiffs_test_partition_label,
+      .max_files = 5,
+      .format_if_mount_failed = true
+    };
+
+    TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
+}
+
+static void test_teardown()
+{
+    TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label));
+}
+
+TEST_CASE("can initialize SPIFFS in erased partition", "[spiffs]")
+{
+    const esp_partition_t* part = get_test_data_partition();
+    TEST_ASSERT_NOT_NULL(part);
+    TEST_ESP_OK(esp_partition_erase_range(part, 0, part->size));
+    test_setup();
+    size_t total = 0, used = 0;
+    TEST_ESP_OK(esp_spiffs_info(spiffs_test_partition_label, &total, &used));
+    printf("total: %d, used: %d\n", total, used);
+    TEST_ASSERT_EQUAL(0, used);
+    test_teardown();
+}
+
+TEST_CASE("can format mounted partition", "[spiffs]")
+{
+    // Mount SPIFFS, create file, format, check that the file does not exist.
+    const esp_partition_t* part = get_test_data_partition();
+    TEST_ASSERT_NOT_NULL(part);
+    test_setup();
+    const char* filename = "/spiffs/hello.txt";
+    test_spiffs_create_file_with_text(filename, spiffs_test_hello_str);
+    esp_spiffs_format(part->label);
+    FILE* f = fopen(filename, "r");
+    TEST_ASSERT_NULL(f);
+    test_teardown();
+}
+
+TEST_CASE("can format unmounted partition", "[spiffs]")
+{
+    // Mount SPIFFS, create file, unmount. Format. Mount again, check that
+    // the file does not exist.
+    const esp_partition_t* part = get_test_data_partition();
+    TEST_ASSERT_NOT_NULL(part);
+    test_setup();
+    const char* filename = "/spiffs/hello.txt";
+    test_spiffs_create_file_with_text(filename, spiffs_test_hello_str);
+    test_teardown();
+    esp_spiffs_format(part->label);
+    // Don't use test_setup here, need to mount without formatting
+    esp_vfs_spiffs_conf_t conf = {
+        .base_path = "/spiffs",
+        .partition_label = spiffs_test_partition_label,
+        .max_files = 5,
+        .format_if_mount_failed = false
+    };
+    TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
+    FILE* f = fopen(filename, "r");
+    TEST_ASSERT_NULL(f);
+    test_teardown();
+}
+
+TEST_CASE("can create and write file", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str);
+    test_teardown();
+}
+
+TEST_CASE("can read file", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str);
+    test_spiffs_read_file("/spiffs/hello.txt");
+    test_teardown();
+}
+
+TEST_CASE("can open maximum number of files", "[spiffs]")
+{
+    size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */
+    esp_vfs_spiffs_conf_t conf = {
+        .base_path = "/spiffs",
+        .partition_label = spiffs_test_partition_label,
+        .format_if_mount_failed = true,
+        .max_files = max_files
+    };
+    TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
+    test_spiffs_open_max_files("/spiffs/f", max_files);
+    TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label));
+}
+
+TEST_CASE("overwrite and append file", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_overwrite_append("/spiffs/hello.txt");
+    test_teardown();
+}
+
+TEST_CASE("can lseek", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_lseek("/spiffs/seek.txt");
+    test_teardown();
+}
+
+
+TEST_CASE("stat returns correct values", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_stat("/spiffs/stat.txt");
+    test_teardown();
+}
+
+TEST_CASE("unlink removes a file", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_unlink("/spiffs/unlink.txt");
+    test_teardown();
+}
+
+TEST_CASE("rename moves a file", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_rename("/spiffs/move");
+    test_teardown();
+}
+
+TEST_CASE("can opendir root directory of FS", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_can_opendir("/spiffs");
+    test_teardown();
+}
+
+TEST_CASE("opendir, readdir, rewinddir, seekdir work as expected", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_opendir_readdir_rewinddir("/spiffs/dir");
+    test_teardown();
+}
+
+TEST_CASE("readdir with large number of files", "[spiffs][timeout=30]")
+{
+    test_setup();
+    test_spiffs_readdir_many_files("/spiffs/dir2");
+    test_teardown();
+}
+
+TEST_CASE("multiple tasks can use same volume", "[spiffs]")
+{
+    test_setup();
+    test_spiffs_concurrent("/spiffs/f");
+    test_teardown();
+}
+
+#ifdef CONFIG_SPIFFS_USE_MTIME
+TEST_CASE("mtime is updated when file is opened", "[spiffs]")
+{
+    /* Open a file, check that mtime is set correctly */
+    const char* filename = "/spiffs/time";
+    test_setup();
+    time_t t_before_create = time(NULL);
+    test_spiffs_create_file_with_text(filename, "\n");
+    time_t t_after_create = time(NULL);
+
+    struct stat st;
+    TEST_ASSERT_EQUAL(0, stat(filename, &st));
+    printf("mtime=%d\n", (int) st.st_mtime);
+    TEST_ASSERT(st.st_mtime >= t_before_create
+             && st.st_mtime <= t_after_create);
+
+    /* Wait a bit, open again, check that mtime is updated */
+    vTaskDelay(2000 / portTICK_PERIOD_MS);
+    time_t t_before_open = time(NULL);
+    FILE *f = fopen(filename, "a");
+    time_t t_after_open = time(NULL);
+    TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st));
+    printf("mtime=%d\n", (int) st.st_mtime);
+    TEST_ASSERT(st.st_mtime >= t_before_open
+             && st.st_mtime <= t_after_open);
+    fclose(f);
+
+    /* Wait a bit, open for reading, check that mtime is not updated */
+    vTaskDelay(2000 / portTICK_PERIOD_MS);
+    time_t t_before_open_ro = time(NULL);
+    f = fopen(filename, "r");
+    TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st));
+    printf("mtime=%d\n", (int) st.st_mtime);
+    TEST_ASSERT(t_before_open_ro > t_after_open
+             && st.st_mtime >= t_before_open
+             && st.st_mtime <= t_after_open);
+    fclose(f);
+
+    test_teardown();
+}
+#endif // CONFIG_SPIFFS_USE_MTIME
diff --git a/components/spiffs/test_spiffs_host/Makefile b/components/spiffs/test_spiffs_host/Makefile
new file mode 100644
index 00000000..b0e694f2
--- /dev/null
+++ b/components/spiffs/test_spiffs_host/Makefile
@@ -0,0 +1,98 @@
+ifndef COMPONENT
+COMPONENT := spiffs
+endif
+
+COMPONENT_LIB := lib$(COMPONENT).a
+TEST_PROGRAM := test_$(COMPONENT)
+
+STUBS_LIB_DIR := ../../../components/spi_flash/sim/stubs
+STUBS_LIB_BUILD_DIR := $(STUBS_LIB_DIR)/build
+STUBS_LIB := libstubs.a
+
+SPI_FLASH_SIM_DIR := ../../../components/spi_flash/sim
+SPI_FLASH_SIM_BUILD_DIR := $(SPI_FLASH_SIM_DIR)/build
+SPI_FLASH_SIM_LIB := libspi_flash.a
+
+include Makefile.files
+
+all: test
+
+ifndef SDKCONFIG
+SDKCONFIG_DIR := $(dir $(realpath sdkconfig/sdkconfig.h))
+SDKCONFIG := $(SDKCONFIG_DIR)sdkconfig.h
+else
+SDKCONFIG_DIR := $(dir $(realpath $(SDKCONFIG)))
+endif
+
+INCLUDE_FLAGS := $(addprefix -I, $(INCLUDE_DIRS) $(SDKCONFIG_DIR) ../../../tools/catch)
+
+CPPFLAGS += $(INCLUDE_FLAGS) -g -m32
+CXXFLAGS += $(INCLUDE_FLAGS) -std=c++11 -g -m32
+
+# Build libraries that this component is dependent on
+$(STUBS_LIB_BUILD_DIR)/$(STUBS_LIB): force
+	$(MAKE) -C $(STUBS_LIB_DIR) lib SDKCONFIG=$(SDKCONFIG)
+
+$(SPI_FLASH_SIM_BUILD_DIR)/$(SPI_FLASH_SIM_LIB): force
+	$(MAKE) -C $(SPI_FLASH_SIM_DIR) lib SDKCONFIG=$(SDKCONFIG)
+
+# Create target for building this component as a library
+CFILES := $(filter %.c, $(SOURCE_FILES))
+CPPFILES := $(filter %.cpp, $(SOURCE_FILES))
+
+CTARGET = ${2}/$(patsubst %.c,%.o,$(notdir ${1}))
+CPPTARGET = ${2}/$(patsubst %.cpp,%.o,$(notdir ${1}))
+
+ifndef BUILD_DIR
+BUILD_DIR := build
+endif
+
+OBJ_FILES := $(addprefix $(BUILD_DIR)/, $(filter %.o, $(notdir $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o))))
+
+define COMPILE_C
+$(call CTARGET, ${1}, $(BUILD_DIR)) : ${1} $(SDKCONFIG)
+	mkdir -p $(BUILD_DIR)
+	$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(call CTARGET, ${1}, $(BUILD_DIR)) ${1}
+endef
+
+define COMPILE_CPP
+$(call CPPTARGET, ${1}, $(BUILD_DIR)) : ${1} $(SDKCONFIG)
+	mkdir -p $(BUILD_DIR) 
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $(call CPPTARGET, ${1}, $(BUILD_DIR)) ${1}
+endef
+
+$(BUILD_DIR)/$(COMPONENT_LIB): $(OBJ_FILES) $(SDKCONFIG)
+	mkdir -p $(BUILD_DIR)
+	$(AR) rcs $@ $^
+
+clean:
+	$(MAKE) -C $(STUBS_LIB_DIR) clean
+	$(MAKE) -C $(SPI_FLASH_SIM_DIR) clean
+	rm -f $(OBJ_FILES) $(TEST_OBJ_FILES) $(TEST_PROGRAM) $(COMPONENT_LIB) partition_table.bin
+
+lib: $(BUILD_DIR)/$(COMPONENT_LIB)
+
+$(foreach cfile, $(CFILES), $(eval $(call COMPILE_C, $(cfile))))
+$(foreach cxxfile, $(CPPFILES), $(eval $(call COMPILE_CPP, $(cxxfile))))
+
+# Create target for building this component as a test
+TEST_SOURCE_FILES = \
+	test_spiffs.cpp \
+	main.cpp \
+	test_utils.c
+
+TEST_OBJ_FILES = $(filter %.o, $(TEST_SOURCE_FILES:.cpp=.o) $(TEST_SOURCE_FILES:.c=.o))
+
+$(TEST_PROGRAM): lib $(TEST_OBJ_FILES) $(SPI_FLASH_SIM_BUILD_DIR)/$(SPI_FLASH_SIM_LIB) $(STUBS_LIB_BUILD_DIR)/$(STUBS_LIB) partition_table.bin $(SDKCONFIG)
+	g++ $(LDFLAGS) $(CXXFLAGS) -o $@  $(TEST_OBJ_FILES) -L$(BUILD_DIR) -l:$(COMPONENT_LIB) -L$(SPI_FLASH_SIM_BUILD_DIR) -l:$(SPI_FLASH_SIM_LIB) -L$(STUBS_LIB_BUILD_DIR) -l:$(STUBS_LIB)
+
+test: $(TEST_PROGRAM)
+	./$(TEST_PROGRAM)
+
+# Create other necessary targets
+partition_table.bin: partition_table.csv
+	python ../../../components/partition_table/gen_esp32part.py --verify $< $@
+
+force:
+
+.PHONY: all lib test clean force
diff --git a/components/spiffs/test_spiffs_host/Makefile.files b/components/spiffs/test_spiffs_host/Makefile.files
new file mode 100644
index 00000000..06858bc1
--- /dev/null
+++ b/components/spiffs/test_spiffs_host/Makefile.files
@@ -0,0 +1,33 @@
+SOURCE_FILES := \
+	../spiffs_api.c \
+	$(addprefix ../spiffs/src/, \
+	spiffs_cache.c \
+	spiffs_check.c \
+	spiffs_gc.c \
+	spiffs_hydrogen.c \
+	spiffs_nucleus.c \
+	) 
+
+INCLUDE_DIRS := \
+	. \
+	.. \
+	../spiffs/src \
+	../include \
+	$(addprefix ../../spi_flash/sim/stubs/, \
+	app_update/include \
+	driver/include \
+	esp32/include \
+	freertos/include \
+	log/include \
+	newlib/include \
+	sdmmc/include \
+	vfs/include \
+	) \
+	$(addprefix ../../../components/, \
+	soc/esp32/include \
+	esp32/include \
+	bootloader_support/include \
+	app_update/include \
+	spi_flash/include \
+	wear_levelling/include \
+	)
diff --git a/components/spiffs/test_spiffs_host/component.mk b/components/spiffs/test_spiffs_host/component.mk
new file mode 100644
index 00000000..82dea0b0
--- /dev/null
+++ b/components/spiffs/test_spiffs_host/component.mk
@@ -0,0 +1,17 @@
+include $(COMPONENT_PATH)/Makefile.files
+
+COMPONENT_OWNBUILDTARGET := 1
+COMPONENT_OWNCLEANTARGET := 1
+
+COMPONENT_ADD_INCLUDEDIRS := $(INCLUDE_DIRS)
+
+.PHONY: build
+build: $(SDKCONFIG_HEADER)
+	$(MAKE) -C $(COMPONENT_PATH) lib SDKCONFIG=$(SDKCONFIG_HEADER) BUILD_DIR=$(COMPONENT_BUILD_DIR) COMPONENT=$(COMPONENT_NAME)
+
+CLEAN_FILES := component_project_vars.mk
+.PHONY: clean
+clean:
+	$(summary) RM $(CLEAN_FILES)
+	rm -f $(CLEAN_FILES)
+	$(MAKE) -C $(COMPONENT_PATH) clean SDKCONFIG=$(SDKCONFIG_HEADER) BUILD_DIR=$(COMPONENT_BUILD_DIR) COMPONENT=$(COMPONENT_NAME)
\ No newline at end of file
diff --git a/components/spiffs/test_spiffs_host/main.cpp b/components/spiffs/test_spiffs_host/main.cpp
new file mode 100644
index 00000000..178916ea
--- /dev/null
+++ b/components/spiffs/test_spiffs_host/main.cpp
@@ -0,0 +1,3 @@
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
+
diff --git a/components/spiffs/test_spiffs_host/partition_table.csv b/components/spiffs/test_spiffs_host/partition_table.csv
new file mode 100644
index 00000000..d02771b5
--- /dev/null
+++ b/components/spiffs/test_spiffs_host/partition_table.csv
@@ -0,0 +1,6 @@
+# Name,   Type, SubType, Offset,  Size, Flags
+# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
+nvs,      data, nvs,     0x9000,  0x6000,
+phy_init, data, phy,     0xf000,  0x1000,
+factory,  app,  factory, 0x10000, 1M,
+storage,  data, spiffs,  ,        2M, 
diff --git a/components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h b/components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h
new file mode 100644
index 00000000..44966c55
--- /dev/null
+++ b/components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#define CONFIG_SPIFFS_USE_MAGIC_LENGTH 1
+#define CONFIG_SPIFFS_MAX_PARTITIONS 3
+#define CONFIG_SPIFFS_OBJ_NAME_LEN 32
+#define CONFIG_SPIFFS_PAGE_SIZE 256
+#define CONFIG_SPIFFS_GC_MAX_RUNS 10
+#define CONFIG_SPIFFS_CACHE_WR 1
+#define CONFIG_SPIFFS_CACHE 1
+#define CONFIG_SPIFFS_META_LENGTH 4
+#define CONFIG_SPIFFS_USE_MAGIC 1
+#define CONFIG_SPIFFS_PAGE_CHECK 1
+#define CONFIG_SPIFFS_USE_MTIME 1
+
+#define CONFIG_WL_SECTOR_SIZE 4096
+#define CONFIG_LOG_DEFAULT_LEVEL 3
+#define CONFIG_PARTITION_TABLE_OFFSET 0x8000
+
+#define CONFIG_ESPTOOLPY_FLASHSIZE "8MB"
diff --git a/components/spiffs/test_spiffs_host/test_spiffs.cpp b/components/spiffs/test_spiffs_host/test_spiffs.cpp
new file mode 100644
index 00000000..fa3936c2
--- /dev/null
+++ b/components/spiffs/test_spiffs_host/test_spiffs.cpp
@@ -0,0 +1,107 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "esp_partition.h"
+#include "spiffs.h"
+#include "spiffs_nucleus.h"
+#include "spiffs_api.h"
+
+#include "catch.hpp"
+
+extern "C" void init_spi_flash(const char* chip_size, size_t block_size, size_t sector_size, size_t page_size, const char* partition_bin);
+
+TEST_CASE("format disk, open file, write and read file", "[spiffs]")
+{
+    init_spi_flash(CONFIG_ESPTOOLPY_FLASHSIZE, CONFIG_WL_SECTOR_SIZE * 16, CONFIG_WL_SECTOR_SIZE, CONFIG_WL_SECTOR_SIZE, "partition_table.bin");
+
+    spiffs fs;
+    spiffs_config cfg;
+
+    const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, "storage");
+
+    // Configure objects needed by SPIFFS
+    esp_spiffs_t esp_user_data;
+    esp_user_data.partition = partition;
+    fs.user_data = (void*)&esp_user_data;
+
+    cfg.hal_erase_f = spiffs_api_erase;
+    cfg.hal_read_f = spiffs_api_read;
+    cfg.hal_write_f = spiffs_api_write;
+    cfg.log_block_size = CONFIG_WL_SECTOR_SIZE;
+    cfg.log_page_size = CONFIG_SPIFFS_PAGE_SIZE;
+    cfg.phys_addr = 0;
+    cfg.phys_erase_block = CONFIG_WL_SECTOR_SIZE;
+    cfg.phys_size = partition->size;
+
+    uint32_t max_files = 5;
+
+    uint32_t fds_sz = max_files * sizeof(spiffs_fd);
+    uint32_t work_sz = cfg.log_page_size * 2;
+    uint32_t cache_sz = sizeof(spiffs_cache) + max_files * (sizeof(spiffs_cache_page)
+                          + cfg.log_page_size);
+
+    uint8_t *work = (uint8_t*) malloc(work_sz);
+    uint8_t *fds = (uint8_t*) malloc(fds_sz); 
+    uint8_t *cache = (uint8_t*) malloc(cache_sz); 
+
+    s32_t spiffs_res;
+
+    // Special mounting procedure: mount, format, mount as per
+    // https://github.com/pellepl/spiffs/wiki/Using-spiffs
+    spiffs_res = SPIFFS_mount(&fs, &cfg, work, fds, fds_sz, 
+                            cache, cache_sz, spiffs_api_check);
+    REQUIRE(spiffs_res == SPIFFS_ERR_NOT_A_FS);    
+
+    spiffs_res = SPIFFS_format(&fs);
+    REQUIRE(spiffs_res >= SPIFFS_OK);
+
+    spiffs_res = SPIFFS_mount(&fs, &cfg, work, fds, fds_sz, 
+                            cache, cache_sz, spiffs_api_check);
+    REQUIRE(spiffs_res >= SPIFFS_OK);
+
+    // Open test file
+    spiffs_res = SPIFFS_open(&fs, "test.txt", SPIFFS_O_CREAT | SPIFFS_O_RDWR, 0);    
+    REQUIRE(spiffs_res >= SPIFFS_OK);
+
+    // Generate data
+    spiffs_file file = spiffs_res;
+
+    uint32_t data_size = 100000;
+
+    char *data = (char*) malloc(data_size);
+    char *read = (char*) malloc(data_size);
+
+    for(uint32_t i = 0; i < data_size; i += sizeof(i))
+    {
+        *((uint32_t*)(data + i)) = i;
+    }
+
+    s32_t bw;
+
+    // Write data to file
+    spiffs_res = SPIFFS_write(&fs, file, (void*)data, data_size);    
+    REQUIRE(spiffs_res >= SPIFFS_OK);
+    REQUIRE(spiffs_res == data_size);
+
+    // Set the file object pointer to the beginning
+    spiffs_res = SPIFFS_lseek(&fs, file, 0, SPIFFS_SEEK_SET);
+    REQUIRE(spiffs_res >= SPIFFS_OK);
+    
+    // Read the file
+    spiffs_res = SPIFFS_read(&fs, file, (void*)read, data_size);
+    REQUIRE(spiffs_res >= SPIFFS_OK);
+    REQUIRE(spiffs_res == data_size);
+
+    // Close the test file
+    spiffs_res = SPIFFS_close(&fs, file);
+    REQUIRE(spiffs_res >= SPIFFS_OK);
+
+    REQUIRE(memcmp(data, read, data_size) == 0);
+
+    // Unmount
+    SPIFFS_unmount(&fs);
+
+    free(read);
+    free(data);
+}
diff --git a/components/spiffs/test_spiffs_host/test_utils.c b/components/spiffs/test_spiffs_host/test_utils.c
new file mode 100644
index 00000000..3e4b0575
--- /dev/null
+++ b/components/spiffs/test_spiffs_host/test_utils.c
@@ -0,0 +1,7 @@
+#include "esp_spi_flash.h"
+#include "esp_partition.h"
+
+void init_spi_flash(const char* chip_size, size_t block_size, size_t sector_size, size_t page_size, const char* partition_bin)
+{
+    spi_flash_init(chip_size, block_size, sector_size, page_size, partition_bin);
+}