aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMitch Bradley <wmb@firmworks.com>2017-05-25 15:09:40 -1000
committerMitch Bradley <wmb@firmworks.com>2017-05-25 15:09:40 -1000
commitbaa58e60fa72ecd6c7832d0160e84a12c51d6ef3 (patch)
treec64018e135b64388288bfd3ee14ef4564e1f06a5
parent24cf091ad85463a3cb6fb03390c285375700111e (diff)
downloadcforth-baa58e60fa72ecd6c7832d0160e84a12c51d6ef3.tar.gz
esp32 - add SPIFFS filesystem
-rw-r--r--build/esp32/sdk_build/components/README-spiffs.txt23
-rw-r--r--build/esp32/sdk_build/components/spiffs/Kconfig18
-rw-r--r--build/esp32/sdk_build/components/spiffs/component.mk7
-rw-r--r--build/esp32/sdk_build/components/spiffs/esp_spiffs.c138
-rw-r--r--build/esp32/sdk_build/components/spiffs/esp_spiffs.h43
-rw-r--r--build/esp32/sdk_build/components/spiffs/list.c251
-rw-r--r--build/esp32/sdk_build/components/spiffs/list.h60
-rw-r--r--build/esp32/sdk_build/components/spiffs/mutex.c102
-rw-r--r--build/esp32/sdk_build/components/spiffs/mutex.h53
-rw-r--r--build/esp32/sdk_build/components/spiffs/spiffs.h813
-rw-r--r--build/esp32/sdk_build/components/spiffs/spiffs_cache.c314
-rw-r--r--build/esp32/sdk_build/components/spiffs/spiffs_check.c995
-rw-r--r--build/esp32/sdk_build/components/spiffs/spiffs_config.h361
-rw-r--r--build/esp32/sdk_build/components/spiffs/spiffs_gc.c606
-rw-r--r--build/esp32/sdk_build/components/spiffs/spiffs_hydrogen.c1405
-rw-r--r--build/esp32/sdk_build/components/spiffs/spiffs_nucleus.c2327
-rw-r--r--build/esp32/sdk_build/components/spiffs/spiffs_nucleus.h797
-rw-r--r--build/esp32/sdk_build/components/spiffs/spiffs_vfs.c866
-rw-r--r--build/esp32/sdk_build/components/spiffs/spiffs_vfs.h20
-rw-r--r--build/esp32/sdk_build/main/interface.c71
-rw-r--r--build/esp32/sdk_build/partitions.csv6
-rw-r--r--build/esp32/sdk_build/sdkconfig15
-rw-r--r--src/app/esp32/app.fth20
-rw-r--r--src/app/esp32/consio.c2
-rw-r--r--src/app/esp32/fileio.c5
-rw-r--r--src/app/esp32/files.fth52
-rw-r--r--src/app/esp32/interface.h10
-rw-r--r--src/app/esp32/textend.c18
28 files changed, 9390 insertions, 8 deletions
diff --git a/build/esp32/sdk_build/components/README-spiffs.txt b/build/esp32/sdk_build/components/README-spiffs.txt
new file mode 100644
index 0000000..cb1ed57
--- /dev/null
+++ b/build/esp32/sdk_build/components/README-spiffs.txt
@@ -0,0 +1,23 @@
+The spiffs code was cloned from
+
+https://github.com/loboris/ESP32_spiffs_example.git
+
+The code therein includes a verbatim copy of upstream
+spiffs code https://github.com/pellepl/spiffs.git
+as of the 5/25/2017, plus configuration files and VFS
+interfaces suitable for ESPS32.
+
+The verbatim SPIFFS files are, in components/spiffs/:
+
+spiffs.h
+spiffs_cache.c
+spiffs_check.c
+spiffs_gc.c
+spiffs_hydrogen.c
+spiffs_nucleus.c
+spiffs_nucleus.h
+
+The rest of the files in that directory are ESP32-specific.
+
+../partitions.csv includes a spiffs partition and ../sdkconfig
+is configured to use it, via CONFIG_PARTITION_TABLE_CUSTOM .
diff --git a/build/esp32/sdk_build/components/spiffs/Kconfig b/build/esp32/sdk_build/components/spiffs/Kconfig
new file mode 100644
index 0000000..4655f40
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/Kconfig
@@ -0,0 +1,18 @@
+menu "SPIffs Example Configuration"
+
+config SPIFFS_LOG_BLOCK_SIZE
+ int "Logical block size"
+ range 4098 65536
+ default 8192
+
+config SPIFFS_BASE_ADDR
+ hex "Base address"
+ range 100000 1FFE000
+ default 180000
+
+config SPIFFS_SIZE
+ int "Size"
+ range 262144 2097152
+ default 1048576
+
+endmenu
diff --git a/build/esp32/sdk_build/components/spiffs/component.mk b/build/esp32/sdk_build/components/spiffs/component.mk
new file mode 100644
index 0000000..3d6fbce
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/component.mk
@@ -0,0 +1,7 @@
+#
+# Component Makefile
+#
+
+COMPONENT_SRCDIRS := .
+COMPONENT_ADD_INCLUDEDIRS := .
+COMPONENT_PRIV_INCLUDEDIRS := \ No newline at end of file
diff --git a/build/esp32/sdk_build/components/spiffs/esp_spiffs.c b/build/esp32/sdk_build/components/spiffs/esp_spiffs.c
new file mode 100644
index 0000000..088480c
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/esp_spiffs.c
@@ -0,0 +1,138 @@
+/*
+ * Lua RTOS, SPIFFS low access
+ *
+ * Copyright (C) 2015 - 2017
+ * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
+ *
+ * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
+ *
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * and its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and this
+ * permission notice and warranty disclaimer appear in supporting
+ * documentation, and that the name of the author not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * The author disclaim all warranties with regard to this
+ * software, including all implied warranties of merchantability
+ * and fitness. In no event shall the author be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether
+ * in an action of contract, negligence or other tortious action,
+ * arising out of or in connection with the use or performance of
+ * this software.
+ */
+
+#include <stdlib.h>
+
+#include "esp_spiffs.h"
+#include "esp_attr.h"
+
+#include "spiffs.h"
+
+#include <esp_spi_flash.h>
+
+s32_t esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst) {
+ u32_t aaddr;
+ u8_t *buff = NULL;
+ u8_t *abuff = NULL;
+ u32_t asize;
+
+ asize = size;
+
+ // Align address to 4 byte
+ aaddr = (addr + (4 - 1)) & (u32_t)-4;
+ if (aaddr != addr) {
+ aaddr -= 4;
+ asize += (addr - aaddr);
+ }
+
+ // Align size to 4 byte
+ asize = (asize + (4 - 1)) & (u32_t)-4;
+
+ if ((aaddr != addr) || (asize != size)) {
+ // Align buffer
+ buff = malloc(asize + 4);
+ if (!buff) {
+ return SPIFFS_ERR_INTERNAL;
+ }
+
+ abuff = (u8_t *)(((ptrdiff_t)buff + (4 - 1)) & (u32_t)-4);
+
+ if (spi_flash_read(aaddr, (void *)abuff, asize) != 0) {
+ free(buff);
+ return SPIFFS_ERR_INTERNAL;
+ }
+
+ memcpy(dst, abuff + (addr - aaddr), size);
+
+ free(buff);
+ } else {
+ if (spi_flash_read(addr, (void *)dst, size) != 0) {
+ return SPIFFS_ERR_INTERNAL;
+ }
+ }
+
+ return SPIFFS_OK;
+}
+
+s32_t esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src) {
+ u32_t aaddr;
+ u8_t *buff = NULL;
+ u8_t *abuff = NULL;
+ u32_t asize;
+
+ asize = size;
+
+ // Align address to 4 byte
+ aaddr = (addr + (4 - 1)) & -4;
+ if (aaddr != addr) {
+ aaddr -= 4;
+ asize += (addr - aaddr);
+ }
+
+ // Align size to 4 byte
+ asize = (asize + (4 - 1)) & -4;
+
+ if ((aaddr != addr) || (asize != size)) {
+ // Align buffer
+ buff = malloc(asize + 4);
+ if (!buff) {
+ return SPIFFS_ERR_INTERNAL;
+ }
+
+ abuff = (u8_t *)(((ptrdiff_t)buff + (4 - 1)) & -4);
+
+ if (spi_flash_read(aaddr, (void *)abuff, asize) != 0) {
+ free(buff);
+ return SPIFFS_ERR_INTERNAL;
+ }
+
+ memcpy(abuff + (addr - aaddr), src, size);
+
+ if (spi_flash_write(aaddr, (uint32_t *)abuff, asize) != 0) {
+ free(buff);
+ return SPIFFS_ERR_INTERNAL;
+ }
+
+ free(buff);
+ } else {
+ if (spi_flash_write(addr, (uint32_t *)src, size) != 0) {
+ return SPIFFS_ERR_INTERNAL;
+ }
+ }
+
+ return SPIFFS_OK;
+}
+
+s32_t IRAM_ATTR esp32_spi_flash_erase(u32_t addr, u32_t size) {
+ if (spi_flash_erase_sector(addr >> 12) != 0) {
+ return SPIFFS_ERR_INTERNAL;
+ }
+
+ return SPIFFS_OK;
+}
diff --git a/build/esp32/sdk_build/components/spiffs/esp_spiffs.h b/build/esp32/sdk_build/components/spiffs/esp_spiffs.h
new file mode 100644
index 0000000..c68e652
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/esp_spiffs.h
@@ -0,0 +1,43 @@
+/*
+ * Lua RTOS, write syscall implementation
+ *
+ * Copyright (C) 2015 - 2017
+ * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
+ *
+ * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
+ *
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * and its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and this
+ * permission notice and warranty disclaimer appear in supporting
+ * documentation, and that the name of the author not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * The author disclaim all warranties with regard to this
+ * software, including all implied warranties of merchantability
+ * and fitness. In no event shall the author be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether
+ * in an action of contract, negligence or other tortious action,
+ * arising out of or in connection with the use or performance of
+ * this software.
+ */
+
+#ifndef __ESP_SPIFFS_H__
+#define __ESP_SPIFFS_H__
+
+#include "spiffs.h"
+
+s32_t esp32_spi_flash_read(u32_t addr, u32_t size, u8_t *dst);
+s32_t esp32_spi_flash_write(u32_t addr, u32_t size, const u8_t *src);
+s32_t esp32_spi_flash_erase(u32_t addr, u32_t size);
+
+#define low_spiffs_read (spiffs_read *)esp32_spi_flash_read
+#define low_spiffs_write (spiffs_write *)esp32_spi_flash_write
+#define low_spiffs_erase (spiffs_erase *)esp32_spi_flash_erase
+
+#endif // __ESP_SPIFFS_H__
diff --git a/build/esp32/sdk_build/components/spiffs/list.c b/build/esp32/sdk_build/components/spiffs/list.c
new file mode 100644
index 0000000..adf2fcb
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/list.c
@@ -0,0 +1,251 @@
+/*
+ * Lua RTOS, list data structure
+ *
+ * Copyright (C) 2015 - 2017
+ * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
+ *
+ * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
+ *
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * and its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and this
+ * permission notice and warranty disclaimer appear in supporting
+ * documentation, and that the name of the author not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * The author disclaim all warranties with regard to this
+ * software, including all implied warranties of merchantability
+ * and fitness. In no event shall the author be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether
+ * in an action of contract, negligence or other tortious action,
+ * arising out of or in connection with the use or performance of
+ * this software.
+ */
+
+#include "esp_attr.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "list.h"
+#include "mutex.h"
+
+void list_init(struct list *list, int first_index) {
+ // Create the mutex
+ mtx_init(&list->mutex, NULL, NULL, 0);
+
+ mtx_lock(&list->mutex);
+
+ list->indexes = 0;
+ list->free = NULL;
+ list->index = NULL;
+ list->first_index = first_index;
+
+ mtx_unlock(&list->mutex);
+}
+
+int list_add(struct list *list, void *item, int *item_index) {
+ struct list_index *index = NULL;
+ struct list_index *indexa = NULL;
+ int grow = 0;
+
+ mtx_lock(&list->mutex);
+
+ // Get an index
+ if (list->free) {
+ // Get first free element
+ index = list->free;
+ list->free = index->next;
+ } else {
+ // Must grow index array
+ grow = 1;
+ }
+
+ if (grow) {
+ // Increment index count
+ list->indexes++;
+
+ // Create a new index array for allocate new index
+ indexa = (struct list_index *)malloc(sizeof(struct list_index) * list->indexes);
+ if (!indexa) {
+ mtx_unlock(&list->mutex);
+ return ENOMEM;
+ }
+
+ if (list->index) {
+ // Copy current index array to new created
+ bcopy(list->index, indexa, sizeof(struct list_index) * (list->indexes - 1));
+
+ // Free current index array
+ free(list->index);
+ }
+
+ // Store new index array
+ list->index = indexa;
+
+ // Current index
+ index = list->index + list->indexes - 1;
+
+ // Initialize new index
+ index->index = list->indexes - 1;
+
+ }
+
+ index->next = NULL;
+ index->item = item;
+ index->deleted = 0;
+
+ // Return index
+ *item_index = index->index + list->first_index;
+
+ mtx_unlock(&list->mutex);
+
+ return 0;
+}
+
+int IRAM_ATTR list_get(struct list *list, int index, void **item) {
+ struct list_index *cindex = NULL;
+ int iindex;
+
+ mtx_lock(&list->mutex);
+
+ if (!list->indexes) {
+ mtx_unlock(&list->mutex);
+ return EINVAL;
+ }
+
+ // Check index
+ if (index < list->first_index) {
+ mtx_unlock(&list->mutex);
+ return EINVAL;
+ }
+
+ // Get new internal index
+ iindex = index - list->first_index;
+
+ // Test for a valid index
+ if (iindex > list->indexes) {
+ mtx_unlock(&list->mutex);
+ return EINVAL;
+ }
+
+ cindex = list->index + iindex;
+
+ if (cindex->deleted) {
+ mtx_unlock(&list->mutex);
+ return EINVAL;
+ }
+
+ *item = cindex->item;
+
+ mtx_unlock(&list->mutex);
+
+ return 0;
+}
+
+int list_remove(struct list *list, int index, int destroy) {
+ struct list_index *cindex = NULL;
+ int iindex;
+
+ mtx_lock(&list->mutex);
+
+ // Check index
+ if (index < list->first_index) {
+ mtx_unlock(&list->mutex);
+ return EINVAL;
+ }
+
+ // Get new internal index
+ iindex = index - list->first_index;
+
+ // Test for a valid index
+ if ((iindex < 0) || (iindex > list->indexes)) {
+ mtx_unlock(&list->mutex);
+ return EINVAL;
+ }
+
+ cindex = &list->index[iindex];
+
+ if (destroy) {
+ free(cindex->item);
+ }
+
+ cindex->next = list->free;
+ cindex->deleted = 1;
+ list->free = cindex;
+
+ mtx_unlock(&list->mutex);
+
+ return 0;
+}
+
+int IRAM_ATTR list_first(struct list *list) {
+ int index;
+ int res = -1;
+
+ mtx_lock(&list->mutex);
+
+ for(index=0;index < list->indexes;index++) {
+ if (!list->index[index].deleted) {
+ res = index + list->first_index;
+ break;
+ }
+ }
+
+ mtx_unlock(&list->mutex);
+
+ return res;
+}
+
+int IRAM_ATTR list_next(struct list *list, int index) {
+ int res = -1;
+ int iindex;
+
+ mtx_lock(&list->mutex);
+
+ // Check index
+ if (index < list->first_index) {
+ mtx_unlock(&list->mutex);
+ return -1;
+ }
+
+ // Get new internal index
+ iindex = index - list->first_index + 1;
+
+ // Get next non deleted item on list
+ for(;iindex < list->indexes;iindex++) {
+ if (!list->index[iindex].deleted) {
+ res = iindex + list->first_index;
+ break;
+ }
+ }
+
+ mtx_unlock(&list->mutex);
+
+ return res;
+}
+
+void list_destroy(struct list *list, int items) {
+ int index;
+
+ mtx_lock(&list->mutex);
+
+ if (items) {
+ for(index=0;index < list->indexes;index++) {
+ if (!list->index[index].deleted) {
+ free(list->index[index].item);
+ }
+ }
+ }
+
+ free(list->index);
+
+ mtx_unlock(&list->mutex);
+ mtx_destroy(&list->mutex);
+}
diff --git a/build/esp32/sdk_build/components/spiffs/list.h b/build/esp32/sdk_build/components/spiffs/list.h
new file mode 100644
index 0000000..4f9c5b1
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/list.h
@@ -0,0 +1,60 @@
+/*
+ * Lua RTOS, list data structure
+ *
+ * Copyright (C) 2015 - 2017
+ * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
+ *
+ * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
+ *
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * and its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and this
+ * permission notice and warranty disclaimer appear in supporting
+ * documentation, and that the name of the author not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * The author disclaim all warranties with regard to this
+ * software, including all implied warranties of merchantability
+ * and fitness. In no event shall the author be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether
+ * in an action of contract, negligence or other tortious action,
+ * arising out of or in connection with the use or performance of
+ * this software.
+ */
+
+#ifndef _LIST_H
+#define _LIST_H
+
+#include <stdint.h>
+#include "mutex.h"
+
+struct list {
+ struct mtx mutex;
+ struct list_index *index;
+ struct list_index *free;
+ uint8_t indexes;
+ uint8_t first_index;
+};
+
+struct list_index {
+ void *item;
+ uint8_t index;
+ uint8_t deleted;
+ struct list_index *next;
+};
+
+void list_init(struct list *list, int first_index);
+int list_add(struct list *list, void *item, int *item_index);
+int list_get(struct list *list, int index, void **item);
+int list_remove(struct list *list, int index, int destroy);
+int list_first(struct list *list);
+int list_next(struct list *list, int index);
+void list_destroy(struct list *list, int items);
+
+#endif /* LIST_H */
+
diff --git a/build/esp32/sdk_build/components/spiffs/mutex.c b/build/esp32/sdk_build/components/spiffs/mutex.c
new file mode 100644
index 0000000..84aea9c
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/mutex.c
@@ -0,0 +1,102 @@
+/*
+ * Lua RTOS, mutex api implementation over FreeRTOS
+ *
+ * Copyright (C) 2015 - 2017
+ * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
+ *
+ * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
+ *
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * and its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and this
+ * permission notice and warranty disclaimer appear in supporting
+ * documentation, and that the name of the author not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * The author disclaim all warranties with regard to this
+ * software, including all implied warranties of merchantability
+ * and fitness. In no event shall the author be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether
+ * in an action of contract, negligence or other tortious action,
+ * arising out of or in connection with the use or performance of
+ * this software.
+ *
+ * Modified by: LoBo (loboris@gmail.com / https://github.com/loboris)
+ *
+ */
+
+#include "freertos/FreeRTOS.h"
+#include "esp_attr.h"
+#include "mutex.h"
+
+
+#define portEND_SWITCHING_ISR(xSwitchRequired) \
+if (xSwitchRequired) { \
+ _frxt_setup_switch(); \
+}
+
+extern unsigned port_interruptNesting[portNUM_PROCESSORS];
+
+void _mtx_init() {
+}
+
+void mtx_init(struct mtx *mutex, const char *name, const char *type, int opts) {
+ mutex->sem = xSemaphoreCreateBinary();
+
+ if (mutex->sem) {
+ if (port_interruptNesting[xPortGetCoreID()] != 0) {
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ xSemaphoreGiveFromISR( mutex->sem, &xHigherPriorityTaskWoken);
+ portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
+ } else {
+ xSemaphoreGive( mutex->sem );
+ }
+ }
+}
+
+void IRAM_ATTR mtx_lock(struct mtx *mutex) {
+ if (port_interruptNesting[xPortGetCoreID()] != 0) {
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ xSemaphoreTakeFromISR( mutex->sem, &xHigherPriorityTaskWoken );
+ portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
+ } else {
+ xSemaphoreTake( mutex->sem, portMAX_DELAY );
+ }
+}
+
+int mtx_trylock(struct mtx *mutex) {
+ if (xSemaphoreTake( mutex->sem, 0 ) == pdTRUE) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void IRAM_ATTR mtx_unlock(struct mtx *mutex) {
+ if (port_interruptNesting[xPortGetCoreID()] != 0) {
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ xSemaphoreGiveFromISR( mutex->sem, &xHigherPriorityTaskWoken );
+ portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
+ } else {
+ xSemaphoreGive( mutex->sem );
+ }
+}
+
+void mtx_destroy(struct mtx *mutex) {
+ if (port_interruptNesting[xPortGetCoreID()] != 0) {
+ BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+ xSemaphoreGiveFromISR( mutex->sem, &xHigherPriorityTaskWoken );
+ portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
+ } else {
+ xSemaphoreGive( mutex->sem );
+ }
+
+ vSemaphoreDelete( mutex->sem );
+
+ mutex->sem = 0;
+}
diff --git a/build/esp32/sdk_build/components/spiffs/mutex.h b/build/esp32/sdk_build/components/spiffs/mutex.h
new file mode 100644
index 0000000..0871e23
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/mutex.h
@@ -0,0 +1,53 @@
+/*
+ * Lua RTOS, mutex api implementation over FreeRTOS
+ *
+ * Copyright (C) 2015 - 2017
+ * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
+ *
+ * Author: Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
+ *
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * and its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and this
+ * permission notice and warranty disclaimer appear in supporting
+ * documentation, and that the name of the author not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * The author disclaim all warranties with regard to this
+ * software, including all implied warranties of merchantability
+ * and fitness. In no event shall the author be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether
+ * in an action of contract, negligence or other tortious action,
+ * arising out of or in connection with the use or performance of
+ * this software.
+ *
+ * Modified by: LoBo (loboris@gmail.com / https://github.com/loboris)
+ *
+ */
+
+#ifndef MUTEX_H_H
+#define MUTEX_H_H
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/semphr.h"
+
+#define MUTEX_INITIALIZER {.sem = 0}
+
+struct mtx {
+ SemaphoreHandle_t sem;
+};
+
+
+void mtx_init(struct mtx *mutex, const char *name, const char *type, int opts);
+void mtx_lock(struct mtx *mutex);
+int mtx_trylock(struct mtx *mutex);
+void mtx_unlock(struct mtx *mutex);
+void mtx_destroy(struct mtx *mutex);
+
+#endif /* MUTEX_H_H */
+
diff --git a/build/esp32/sdk_build/components/spiffs/spiffs.h b/build/esp32/sdk_build/components/spiffs/spiffs.h
new file mode 100644
index 0000000..d87422d
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/spiffs.h
@@ -0,0 +1,813 @@
+/*
+ * spiffs.h
+ *
+ * Created on: May 26, 2013
+ * Author: petera
+ */
+
+#ifndef SPIFFS_H_
+#define SPIFFS_H_
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "spiffs_config.h"
+
+#define SPIFFS_OK 0
+#define SPIFFS_ERR_NOT_MOUNTED -10000
+#define SPIFFS_ERR_FULL -10001
+#define SPIFFS_ERR_NOT_FOUND -10002
+#define SPIFFS_ERR_END_OF_OBJECT -10003
+#define SPIFFS_ERR_DELETED -10004
+#define SPIFFS_ERR_NOT_FINALIZED -10005
+#define SPIFFS_ERR_NOT_INDEX -10006
+#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007
+#define SPIFFS_ERR_FILE_CLOSED -10008
+#define SPIFFS_ERR_FILE_DELETED -10009
+#define SPIFFS_ERR_BAD_DESCRIPTOR -10010
+#define SPIFFS_ERR_IS_INDEX -10011
+#define SPIFFS_ERR_IS_FREE -10012
+#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013
+#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014
+#define SPIFFS_ERR_INDEX_REF_FREE -10015
+#define SPIFFS_ERR_INDEX_REF_LU -10016
+#define SPIFFS_ERR_INDEX_REF_INVALID -10017
+#define SPIFFS_ERR_INDEX_FREE -10018
+#define SPIFFS_ERR_INDEX_LU -10019
+#define SPIFFS_ERR_INDEX_INVALID -10020
+#define SPIFFS_ERR_NOT_WRITABLE -10021
+#define SPIFFS_ERR_NOT_READABLE -10022
+#define SPIFFS_ERR_CONFLICTING_NAME -10023
+#define SPIFFS_ERR_NOT_CONFIGURED -10024
+
+#define SPIFFS_ERR_NOT_A_FS -10025
+#define SPIFFS_ERR_MOUNTED -10026
+#define SPIFFS_ERR_ERASE_FAIL -10027
+#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028
+
+#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029
+
+#define SPIFFS_ERR_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_INTERNAL -10050
+
+#define SPIFFS_ERR_TEST -10100
+
+
+// spiffs file descriptor index type. must be signed
+typedef s16_t spiffs_file;
+// spiffs file descriptor flags
+typedef u16_t spiffs_flags;
+// spiffs file mode
+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 {
+ SPIFFS_CHECK_LOOKUP = 0,
+ SPIFFS_CHECK_INDEX,
+ SPIFFS_CHECK_PAGE
+} spiffs_check_type;
+
+/* file system check callback report type */
+typedef enum {
+ SPIFFS_CHECK_PROGRESS = 0,
+ SPIFFS_CHECK_ERROR,
+ SPIFFS_CHECK_FIX_INDEX,
+ SPIFFS_CHECK_FIX_LOOKUP,
+ SPIFFS_CHECK_DELETE_ORPHANED_INDEX,
+ SPIFFS_CHECK_DELETE_PAGE,
+ 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(...) \
+ printf(__VA_ARGS__)
+#endif
+#ifndef SPIFFS_GC_DBG
+#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__)
+#endif
+#ifndef SPIFFS_CACHE_DBG
+#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__)
+#endif
+#ifndef SPIFFS_CHECK_DBG
+#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__)
+#endif
+
+/* 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)
+#define SPIFFS_O_RDONLY SPIFFS_RDONLY
+/* The opened file may only be written */
+#define SPIFFS_WRONLY (1<<4)
+#define SPIFFS_O_WRONLY SPIFFS_WRONLY
+/* The opened file may be both read and written */
+#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY)
+#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)
+#define SPIFFS_SEEK_END (2)
+
+#define SPIFFS_TYPE_FILE (1)
+#define SPIFFS_TYPE_DIR (2)
+#define SPIFFS_TYPE_HARD_LINK (3)
+#define SPIFFS_TYPE_SOFT_LINK (4)
+
+#ifndef SPIFFS_LOCK
+#define SPIFFS_LOCK(fs)
+#endif
+
+#ifndef SPIFFS_UNLOCK
+#define SPIFFS_UNLOCK(fs)
+#endif
+
+// phys structs
+
+// spiffs spi configuration struct
+typedef struct {
+ // physical read function
+ spiffs_read hal_read_f;
+ // physical write function
+ spiffs_write hal_write_f;
+ // physical erase function
+ spiffs_erase hal_erase_f;
+#if SPIFFS_SINGLETON == 0
+ // physical size of the spi flash
+ u32_t phys_size;
+ // physical offset in spi flash used for spiffs,
+ // must be on block boundary
+ u32_t phys_addr;
+ // physical size when erasing a block
+ u32_t phys_erase_block;
+
+ // logical size of a block, must be on physical
+ // block size boundary and must never be less than
+ // a physical block
+ u32_t log_block_size;
+ // 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 spiffs_t {
+ // file system configuration
+ spiffs_config cfg;
+ // number of logical blocks
+ u32_t block_count;
+
+ // cursor for free blocks, block index
+ spiffs_block_ix free_cursor_block_ix;
+ // cursor for free blocks, entry index
+ int free_cursor_obj_lu_entry;
+ // cursor when searching, block index
+ spiffs_block_ix cursor_block_ix;
+ // cursor when searching, entry index
+ int cursor_obj_lu_entry;
+
+ // primary work buffer, size of a logical page
+ u8_t *lu_work;
+ // secondary work buffer, size of a logical page
+ u8_t *work;
+ // file descriptor memory area
+ u8_t *fd_space;
+ // available file descriptors
+ u32_t fd_count;
+
+ // last error
+ s32_t err_code;
+
+ // current number of free blocks
+ u32_t free_blocks;
+ // current number of busy pages
+ u32_t stats_p_allocated;
+ // current number of deleted pages
+ u32_t stats_p_deleted;
+ // flag indicating that garbage collector is cleaning
+ u8_t cleaning;
+ // max erase count amongst all blocks
+ spiffs_obj_id max_erase_count;
+
+#if SPIFFS_GC_STATS
+ u32_t stats_gc_runs;
+#endif
+
+#if SPIFFS_CACHE
+ // cache memory
+ void *cache;
+ // cache size
+ u32_t cache_size;
+#if SPIFFS_CACHE_STATS
+ u32_t cache_hits;
+ u32_t cache_misses;
+#endif
+#endif
+
+ // 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;
+
+/* spiffs file status struct */
+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 {
+ spiffs_obj_id obj_id;
+ u8_t name[SPIFFS_OBJ_NAME_LEN];
+ 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 {
+ spiffs *fs;
+ spiffs_block_ix block;
+ 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
+ * if the flash does not contain a recognizable file system.
+ * In this case, SPIFFS_format must be called prior to remounting.
+ * @param fs the file system struct
+ * @param config the physical and logical configuration of the file system
+ * @param work a memory work buffer comprising 2*config->log_page_size
+ * bytes used throughout all file system operations
+ * @param fd_space memory for file descriptors
+ * @param fd_space_size memory size of file descriptors
+ * @param cache memory for cache, may be null
+ * @param cache_size memory size of cache
+ * @param check_cb_f callback function for reporting during consistency checks
+ */
+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);
+
+/**
+ * Unmounts the file system. All file handles will be flushed of any
+ * cached writes and closed.
+ * @param fs the file system struct
+ */
+void SPIFFS_unmount(spiffs *fs);
+
+/**
+ * Creates a new file.
+ * @param fs the file system struct
+ * @param path the path of the new file
+ * @param mode ignored, for posix compliance
+ */
+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_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, const char *path, spiffs_flags flags, spiffs_mode mode);
+
+/**
+ * Opens a file by given dir entry.
+ * Optimization purposes, when traversing a file system with SPIFFS_readdir
+ * 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 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.
+ * SPIFFS_CREAT will have no effect in this case.
+ * @param mode ignored, for posix compliance
+ */
+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
+ * @param fh the filehandle
+ * @param buf where to put read data
+ * @param len how much to read
+ * @returns number of bytes read, or -1 if error
+ */
+s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
+
+/**
+ * Writes to given filehandle.
+ * @param fs the file system struct
+ * @param fh the filehandle
+ * @param buf the data to write
+ * @param len how much to write
+ * @returns number of bytes written, or -1 if error
+ */
+s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
+
+/**
+ * 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 offse, which should be negative
+ */
+s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence);
+
+/**
+ * Removes a file by path
+ * @param fs the file system struct
+ * @param path the path of the file to remove
+ */
+s32_t SPIFFS_remove(spiffs *fs, const char *path);
+
+/**
+ * Removes a file by filehandle
+ * @param fs the file system struct
+ * @param fh the filehandle of the file to remove
+ */
+s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh);
+
+/**
+ * Gets file status by path
+ * @param fs the file system struct
+ * @param path the path of the file to stat
+ * @param s the stat struct to populate
+ */
+s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s);
+
+/**
+ * Gets file status by filehandle
+ * @param fs the file system struct
+ * @param fh the filehandle of the file to stat
+ * @param s the stat struct to populate
+ */
+s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s);
+
+/**
+ * Flushes all pending write operations from cache for given file
+ * @param fs the file system struct
+ * @param fh the filehandle of the file to flush
+ */
+s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh);
+
+/**
+ * Closes a filehandle. If there are pending write operations, these are finalized before closing.
+ * @param fs the file system struct
+ * @param fh the filehandle of the file to close
+ */
+s32_t SPIFFS_close(spiffs *fs, spiffs_file fh);
+
+/**
+ * Renames a file
+ * @param fs the file system struct
+ * @param old path of file to rename
+ * @param newPath new path of file
+ */
+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.
+ * @param fs the file system struct
+ */
+s32_t SPIFFS_errno(spiffs *fs);
+
+/**
+ * Clears last error.
+ * @param fs the file system struct
+ */
+void SPIFFS_clearerr(spiffs *fs);
+
+/**
+ * Opens a directory stream corresponding to the given name.
+ * The stream is positioned at the first entry in the directory.
+ * On hydrogen builds the name argument is ignored as hydrogen builds always correspond
+ * to a flat file structure - no directories.
+ * @param fs the file system struct
+ * @param name the name of the directory
+ * @param d pointer the directory stream to be populated
+ */
+spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d);
+
+/**
+ * Closes a directory stream
+ * @param d the directory stream to close
+ */
+s32_t SPIFFS_closedir(spiffs_DIR *d);
+
+/**
+ * Reads a directory into given spifs_dirent struct.
+ * @param d pointer to the directory stream
+ * @param e the dirent struct to be populated
+ * @returns null if error or end of stream, else given dirent is returned
+ */
+struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e);
+
+/**
+ * Runs a consistency check on given filesystem.
+ * @param fs the file system struct
+ */
+s32_t SPIFFS_check(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
+ * data or few files with much data.
+ * NB: If used number of bytes exceeds total bytes, a SPIFFS_check should
+ * run. This indicates a power loss in midst of things. In worst case
+ * (repeated powerlosses in mending or gc) you might have to delete some files.
+ *
+ * @param fs the file system struct
+ * @param total total number of bytes in filesystem
+ * @param used used number of bytes in filesystem
+ */
+s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used);
+
+/**
+ * Formats the entire file system. All data will be lost.
+ * The filesystem must not be mounted when calling this.
+ *
+ * NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount
+ * MUST be called prior to formatting in order to configure the filesystem.
+ * If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling
+ * SPIFFS_format.
+ * If SPIFFS_mount fails, SPIFFS_format can be called directly without calling
+ * SPIFFS_unmount first.
+ *
+ * @param fs the file system struct
+ */
+s32_t SPIFFS_format(spiffs *fs);
+
+/**
+ * Returns nonzero if spiffs is mounted, or zero if unmounted.
+ * @param fs the file system struct
+ */
+u8_t SPIFFS_mounted(spiffs *fs);
+
+/**
+ * Tries to find a block where most or all pages are deleted, and erase that
+ * block if found. Does not care for wear levelling. Will not move pages
+ * around.
+ * If parameter max_free_pages are set to 0, only blocks with only deleted
+ * pages will be selected.
+ *
+ * NB: the garbage collector is automatically called when spiffs needs free
+ * pages. The reason for this function is to give possibility to do background
+ * tidying when user knows the system is idle.
+ *
+ * Use with care.
+ *
+ * Setting max_free_pages to anything larger than zero will eventually wear
+ * flash more as a block containing free pages can be erased.
+ *
+ * Will set err_no to SPIFFS_OK if a block was found and erased,
+ * SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found,
+ * or other error.
+ *
+ * @param fs the file system struct
+ * @param max_free_pages maximum number allowed free pages in block
+ */
+s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages);
+
+/**
+ * Will try to make room for given amount of bytes in the filesystem by moving
+ * pages and erasing blocks.
+ * If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If
+ * there already is this amount (or more) of free space, SPIFFS_gc will
+ * silently return. It is recommended to call SPIFFS_info before invoking
+ * this method in order to determine what amount of bytes to give.
+ *
+ * NB: the garbage collector is automatically called when spiffs needs free
+ * pages. The reason for this function is to give possibility to do background
+ * tidying when user knows the system is idle.
+ *
+ * Use with care.
+ *
+ * @param fs the file system struct
+ * @param size amount of bytes that should be freed
+ */
+s32_t SPIFFS_gc(spiffs *fs, u32_t size);
+
+/**
+ * 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.
+ * @param fs the file system struct
+ */
+s32_t SPIFFS_vis(spiffs *fs);
+#endif
+
+#if SPIFFS_BUFFER_HELP
+/**
+ * Returns number of bytes needed for the filedescriptor buffer given
+ * amount of file descriptors.
+ */
+u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs);
+
+#if SPIFFS_CACHE
+/**
+ * Returns number of bytes needed for the cache buffer given
+ * amount of cache pages.
+ */
+u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages);
+#endif
+#endif
+
+#if SPIFFS_CACHE
+#endif
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* SPIFFS_H_ */
diff --git a/build/esp32/sdk_build/components/spiffs/spiffs_cache.c b/build/esp32/sdk_build/components/spiffs/spiffs_cache.c
new file mode 100644
index 0000000..018f763
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/spiffs_cache.c
@@ -0,0 +1,314 @@
+/*
+ * spiffs_cache.c
+ *
+ * Created on: Jun 23, 2013
+ * Author: petera
+ */
+
+#include "spiffs.h"
+#include "spiffs_nucleus.h"
+
+#if SPIFFS_CACHE
+
+// returns cached page for give page index, or null if no such cached page
+static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) {
+ spiffs_cache *cache = spiffs_get_cache(fs);
+ if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0;
+ int i;
+ for (i = 0; i < cache->cpage_count; i++) {
+ spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
+ 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 "_SPIPRIi" for "_SPIPRIpg"\n", i, pix);
+ cp->last_access = cache->last_access;
+ return cp;
+ }
+ }
+ //SPIFFS_CACHE_DBG("CACHE_GET: no cache for "_SPIPRIpg"\n", pix);
+ return 0;
+}
+
+// frees cached page
+static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) {
+ s32_t res = SPIFFS_OK;
+ spiffs_cache *cache = spiffs_get_cache(fs);
+ spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix);
+ if (cache->cpage_use_map & (1<<ix)) {
+ if (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 = 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 (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) {
+ SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" objid "_SPIPRIid"\n", ix, cp->obj_id);
+ } else {
+ SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix);
+ }
+ }
+
+ return res;
+}
+
+// removes the oldest accessed cached page
+static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) {
+ s32_t res = SPIFFS_OK;
+ spiffs_cache *cache = spiffs_get_cache(fs);
+
+ if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) {
+ // at least one free cpage
+ return SPIFFS_OK;
+ }
+
+ // all busy, scan thru all to find the cpage which has oldest access
+ int i;
+ int cand_ix = -1;
+ u32_t oldest_val = 0;
+ for (i = 0; i < cache->cpage_count; i++) {
+ spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
+ if ((cache->last_access - cp->last_access) > oldest_val &&
+ (cp->flags & flag_mask) == flags) {
+ oldest_val = cache->last_access - cp->last_access;
+ cand_ix = i;
+ }
+ }
+
+ if (cand_ix >= 0) {
+ res = spiffs_cache_page_free(fs, cand_ix, 1);
+ }
+
+ return res;
+}
+
+// allocates a new cached page and returns it, or null if all cache pages are busy
+static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) {
+ spiffs_cache *cache = spiffs_get_cache(fs);
+ if (cache->cpage_use_map == 0xffffffff) {
+ // out of cache memory
+ return 0;
+ }
+ int i;
+ for (i = 0; i < cache->cpage_count; i++) {
+ if ((cache->cpage_use_map & (1<<i)) == 0) {
+ 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 "_SPIPRIi"\n", i);
+ return cp;
+ }
+ }
+ // out of cache entries
+ return 0;
+}
+
+// drops the cache page for give page index
+void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) {
+ spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix);
+ if (cp) {
+ spiffs_cache_page_free(fs, cp->ix, 0);
+ }
+}
+
+// ------------------------------
+
+// reads from spi flash or the cache
+s32_t spiffs_phys_rd(
+ spiffs *fs,
+ u8_t op,
+ spiffs_file fh,
+ u32_t addr,
+ u32_t len,
+ u8_t *dst) {
+ (void)fh;
+ s32_t res = SPIFFS_OK;
+ spiffs_cache *cache = spiffs_get_cache(fs);
+ 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);
+ 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 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);
+
+ 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);
+ 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;
+ }
+ }
+ }
+ return res;
+}
+
+// writes to spi flash and/or the cache
+s32_t spiffs_phys_wr(
+ spiffs *fs,
+ u8_t op,
+ spiffs_file fh,
+ u32_t addr,
+ u32_t len,
+ u8_t *src) {
+ (void)fh;
+ spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
+ spiffs_cache *cache = spiffs_get_cache(fs);
+ spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix);
+
+ if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) {
+ // have a cache page
+ // copy in data to cache page
+
+ if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE &&
+ (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 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);
+
+ 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 SPIFFS_HAL_WRITE(fs, addr, len, src);
+ } else {
+ return SPIFFS_OK;
+ }
+ } else {
+ // no cache page, no write cache - just write thru
+ return SPIFFS_HAL_WRITE(fs, addr, len, src);
+ }
+}
+
+#if SPIFFS_CACHE_WR
+// returns the cache page that this fd refers, or null if no cache page
+spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) {
+ spiffs_cache *cache = spiffs_get_cache(fs);
+
+ if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) {
+ // all cpages free, no cpage cannot be assigned to obj_id
+ return 0;
+ }
+
+ int i;
+ for (i = 0; i < cache->cpage_count; i++) {
+ spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
+ if ((cache->cpage_use_map & (1<<i)) &&
+ (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) &&
+ cp->obj_id == fd->obj_id) {
+ return cp;
+ }
+ }
+
+ return 0;
+}
+
+// allocates a new cache page and refers this to given fd - flushes an old cache
+// page if all cache is busy
+spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) {
+ // before this function is called, it is ensured that there is no already existing
+ // cache page with same object id
+ spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
+ spiffs_cache_page *cp = spiffs_cache_page_allocate(fs);
+ if (cp == 0) {
+ // could not get cache page
+ return 0;
+ }
+
+ cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR;
+ cp->obj_id = fd->obj_id;
+ fd->cache_page = cp;
+ return cp;
+}
+
+// unrefers all fds that this cache page refers to and releases the cache page
+void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) {
+ if (cp == 0) return;
+ u32_t i;
+ 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->file_nbr != 0 && cur_fd->cache_page == cp) {
+ cur_fd->cache_page = 0;
+ }
+ }
+ spiffs_cache_page_free(fs, cp->ix, 0);
+
+ cp->obj_id = 0;
+}
+
+#endif
+
+// initializes the cache
+void spiffs_cache_init(spiffs *fs) {
+ if (fs->cache == 0) return;
+ u32_t sz = fs->cache_size;
+ u32_t cache_mask = 0;
+ int i;
+ int cache_entries =
+ (sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs));
+ if (cache_entries <= 0) return;
+
+ for (i = 0; i < cache_entries; i++) {
+ cache_mask <<= 1;
+ cache_mask |= 1;
+ }
+
+ spiffs_cache cache;
+ memset(&cache, 0, sizeof(spiffs_cache));
+ cache.cpage_count = cache_entries;
+ cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache));
+
+ cache.cpage_use_map = 0xffffffff;
+ cache.cpage_use_mask = cache_mask;
+ memcpy(fs->cache, &cache, sizeof(spiffs_cache));
+
+ spiffs_cache *c = spiffs_get_cache(fs);
+
+ memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs));
+
+ c->cpage_use_map &= ~(c->cpage_use_mask);
+ for (i = 0; i < cache.cpage_count; i++) {
+ spiffs_get_cache_page_hdr(fs, c, i)->ix = i;
+ }
+}
+
+#endif // SPIFFS_CACHE
diff --git a/build/esp32/sdk_build/components/spiffs/spiffs_check.c b/build/esp32/sdk_build/components/spiffs/spiffs_check.c
new file mode 100644
index 0000000..dde85ef
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/spiffs_check.c
@@ -0,0 +1,995 @@
+/*
+ * spiffs_check.c
+ *
+ * Contains functionality for checking file system consistency
+ * and mending problems.
+ * Three levels of consistency checks are implemented:
+ *
+ * Look up consistency
+ * Checks if indices in lookup pages are coherent with page headers
+ * Object index consistency
+ * Checks if there are any orphaned object indices (missing object index headers).
+ * If an object index is found but not its header, the object index is deleted.
+ * This is critical for the following page consistency check.
+ * Page consistency
+ * Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed
+ *
+ *
+ * Created on: Jul 7, 2013
+ * 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
+
+// searches in the object indices and returns the referenced page index given
+// the object id and the data span index
+// destroys fs->lu_work
+static s32_t spiffs_object_get_data_page_index_reference(
+ spiffs *fs,
+ spiffs_obj_id obj_id,
+ spiffs_span_ix data_spix,
+ spiffs_page_ix *pix,
+ spiffs_page_ix *objix_pix) {
+ s32_t res;
+
+ // calculate object index span index for given data page span index
+ spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
+
+ // find obj index for obj id and span index
+ res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix);
+ SPIFFS_CHECK_RES(res);
+
+ // load obj index entry
+ u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix);
+ if (objix_spix == 0) {
+ // get referenced page from object index header
+ addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix);
+ } else {
+ // get referenced page from object index
+ addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix);
+ }
+
+ res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix);
+
+ return res;
+}
+
+// copies page contents to a new page
+static s32_t spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) {
+ s32_t res;
+ res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0,0,0,0, new_pix);
+ SPIFFS_CHECK_RES(res);
+ res = spiffs_phys_cpy(fs, 0,
+ SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header),
+ SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
+ SPIFFS_DATA_PAGE_SIZE(fs));
+ SPIFFS_CHECK_RES(res);
+ return res;
+}
+
+// rewrites the object index for given object id and replaces the
+// data page index to a new page index
+static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) {
+ s32_t res;
+ spiffs_block_ix bix;
+ int entry;
+ spiffs_page_ix free_pix;
+ obj_id |= SPIFFS_OBJ_ID_IX_FLAG;
+
+ // 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);
+ free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
+
+ // calculate object index span index for given data page span index
+ spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
+ if (objix_spix == 0) {
+ // calc index in index header
+ entry = data_spix;
+ } 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,
+ 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
+ SPIFFS_CHECK_RES(res);
+ spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work;
+
+ // be ultra safe, double check header against provided data
+ if (objix_p_hdr->obj_id != obj_id) {
+ spiffs_page_delete(fs, free_pix);
+ return SPIFFS_ERR_CHECK_OBJ_ID_MISM;
+ }
+ if (objix_p_hdr->span_ix != objix_spix) {
+ spiffs_page_delete(fs, free_pix);
+ return SPIFFS_ERR_CHECK_SPIX_MISM;
+ }
+ if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX |
+ SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) !=
+ (SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) {
+ spiffs_page_delete(fs, free_pix);
+ return SPIFFS_ERR_CHECK_FLAGS_BAD;
+ }
+
+ // rewrite in mem
+ if (objix_spix == 0) {
+ ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix;
+ } else {
+ ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix;
+ }
+
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
+ 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
+ SPIFFS_CHECK_RES(res);
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
+ 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix),
+ sizeof(spiffs_obj_id),
+ (u8_t *)&obj_id);
+ SPIFFS_CHECK_RES(res);
+ res = spiffs_page_delete(fs, objix_pix);
+
+ return res;
+}
+
+// deletes an object just by marking object index header as deleted
+static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) {
+ spiffs_page_ix objix_hdr_pix;
+ s32_t res;
+ res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ return SPIFFS_OK;
+ }
+ SPIFFS_CHECK_RES(res);
+ u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE;
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
+ 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags),
+ sizeof(u8_t),
+ (u8_t *)&flags);
+ return res;
+}
+
+// validates the given look up entry
+static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr,
+ spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) {
+ (void)cur_block;
+ (void)cur_entry;
+ u8_t delete_page = 0;
+ s32_t res = SPIFFS_OK;
+ spiffs_page_ix objix_pix;
+ spiffs_page_ix ref_pix;
+ // check validity, take actions
+ 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 "_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) {
+ // header says data page
+ // data page can be removed if not referenced by some object index
+ res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ // no object with this id, so remove page safely
+ res = SPIFFS_OK;
+ } else {
+ SPIFFS_CHECK_RES(res);
+ if (ref_pix == cur_pix) {
+ // data page referenced by object index but deleted in lu
+ // 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 "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix);
+ SPIFFS_CHECK_RES(res);
+ *reload_lu = 1;
+ 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 "_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);
+ CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
+ } else {
+ CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix);
+ }
+ SPIFFS_CHECK_RES(res);
+ }
+ }
+ } else {
+ // header says index page
+ // index page can be removed if other index with same obj_id and spanix is found
+ res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ // no such index page found, check for a data page amongst page headers
+ // lu cannot be trusted
+ res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0);
+ if (res == SPIFFS_OK) { // ignore other errors
+ // 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 "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix);
+ SPIFFS_CHECK_RES(res);
+ *reload_lu = 1;
+ CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
+ }
+ } else {
+ SPIFFS_CHECK_RES(res);
+ }
+ }
+ }
+ 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 "_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) ||
+ (p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) {
+ // page deleted or not finalized, just remove it
+ } else {
+ if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) {
+ // if data page, check for reference to this page
+ res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ // no object with this id, so remove page safely
+ res = SPIFFS_OK;
+ } else {
+ SPIFFS_CHECK_RES(res);
+ // if found, rewrite page with object id, update index, and delete current
+ if (ref_pix == cur_pix) {
+ spiffs_page_ix new_pix;
+ res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix);
+ SPIFFS_CHECK_RES(res);
+ 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 "_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;
+ CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0);
+ }
+ SPIFFS_CHECK_RES(res);
+ }
+ }
+ } else {
+ // else if index, check for other pages with both obj_id's and spanix
+ spiffs_page_ix objix_pix_lu, objix_pix_ph;
+ // see if other object index page exists for lookup 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, 0, &objix_pix_lu);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ res = SPIFFS_OK;
+ objix_pix_lu = 0;
+ }
+ SPIFFS_CHECK_RES(res);
+ // see if other object index exists for page header obj id and span index
+ res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ res = SPIFFS_OK;
+ objix_pix_ph = 0;
+ }
+ SPIFFS_CHECK_RES(res);
+ // if both obj_id's found, just delete current
+ if (objix_pix_ph == 0 || objix_pix_lu == 0) {
+ // otherwise try finding first corresponding data pages
+ spiffs_page_ix data_pix_lu, data_pix_ph;
+ // see if other data page exists for look up obj id and span index
+ res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ res = SPIFFS_OK;
+ objix_pix_lu = 0;
+ }
+ SPIFFS_CHECK_RES(res);
+ // see if other data page exists for page header obj id and span index
+ res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ res = SPIFFS_OK;
+ objix_pix_ph = 0;
+ }
+ SPIFFS_CHECK_RES(res);
+
+ spiffs_page_header new_ph;
+ new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL);
+ new_ph.span_ix = p_hdr->span_ix;
+ spiffs_page_ix new_pix;
+ if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) ||
+ (objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) {
+ // got a data page for page header obj id
+ // 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 "_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) ||
+ (objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) {
+ // 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 "_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;
+ } else {
+ // cannot safely do anything
+ SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n");
+ }
+ }
+ }
+ }
+ } 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: "_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);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ res = SPIFFS_OK;
+ data_pix = 0;
+ }
+ SPIFFS_CHECK_RES(res);
+ // see if other object index 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, &objix_pix_d);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ res = SPIFFS_OK;
+ objix_pix_d = 0;
+ }
+ SPIFFS_CHECK_RES(res);
+
+ delete_page = 1;
+ // if other data page exists and object index exists, just delete page
+ if (data_pix && objix_pix_d) {
+ SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n");
+ } else
+ // 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");
+ 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);
+ new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG;
+ new_ph.span_ix = p_hdr->span_ix;
+ res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix);
+ SPIFFS_CHECK_RES(res);
+ res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header),
+ SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
+ SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header));
+ SPIFFS_CHECK_RES(res);
+ } else
+ // 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");
+ 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);
+ new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
+ new_ph.span_ix = p_hdr->span_ix;
+ res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix);
+ SPIFFS_CHECK_RES(res);
+ res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header),
+ SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header),
+ SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header));
+ SPIFFS_CHECK_RES(res);
+ } else {
+ // if nothing exists, we cannot safely make a decision - delete
+ }
+ }
+ else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) {
+ 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 "_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);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ // no object with this id, so remove page safely
+ res = SPIFFS_OK;
+ delete_page = 1;
+ } else {
+ SPIFFS_CHECK_RES(res);
+ if (ref_pix != cur_pix) {
+ SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n");
+ delete_page = 1;
+ } else {
+ // page referenced by object index but not final
+ // just finalize
+ SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n");
+ 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),
+ sizeof(u8_t), (u8_t*)&flags);
+ }
+ }
+ }
+ }
+
+ if (delete_page) {
+ 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);
+ }
+
+ return res;
+}
+
+static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry,
+ 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);
+
+ CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS,
+ (cur_block * 256)/fs->block_count, 0);
+
+ // load header
+ 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);
+
+ int reload_lu = 0;
+
+ res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu);
+ SPIFFS_CHECK_RES(res);
+
+ if (res == SPIFFS_OK) {
+ return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE;
+ }
+ return res;
+}
+
+
+// Scans all object look up. For each entry, corresponding page header is checked for validity.
+// If an object index header page is found, this is also checked
+s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) {
+ (void)check_all_objects;
+ s32_t res = SPIFFS_OK;
+
+ 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);
+
+ if (res == SPIFFS_VIS_END) {
+ res = SPIFFS_OK;
+ }
+
+ if (res != SPIFFS_OK) {
+ CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0);
+ }
+
+ CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0);
+
+ return res;
+}
+
+//---------------------------------------
+// Page consistency
+
+// Scans all pages (except lu pages), reserves 4 bits in working memory for each page
+// bit 0: 0 == FREE|DELETED, 1 == USED
+// bit 1: 0 == UNREFERENCED, 1 == REFERENCED
+// bit 2: 0 == NOT_INDEX, 1 == INDEX
+// bit 3: unused
+// A consistent file system will have only pages being
+// * x000 free, unreferenced, not index
+// * x011 used, referenced only once, not index
+// * x101 used, unreferenced, index
+// The working memory might not fit all pages so several scans might be needed
+static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
+ const u32_t bits = 4;
+ const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits;
+
+ s32_t res = SPIFFS_OK;
+ spiffs_page_ix pix_offset = 0;
+
+ // for each range of pages fitting into work memory
+ while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) {
+ // set this flag to abort all checks and rescan the page range
+ u8_t restart = 0;
+ memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
+
+ spiffs_block_ix cur_block = 0;
+ // build consistency bitmap for id range traversing all blocks
+ while (!restart && cur_block < fs->block_count) {
+ 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,
+ 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
+ SPIFFS_CHECK_RES(res);
+
+ u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan);
+ const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8/bits);
+ const u8_t pix_bit_ix = (cur_pix & ((8/bits)-1)) * bits;
+
+ if (within_range &&
+ (p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) {
+ // used
+ fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 0));
+ }
+ if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) &&
+ (p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) &&
+ (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) {
+ // found non-deleted index
+ if (within_range) {
+ fs->work[pix_byte_ix] |= (1<<(pix_bit_ix + 2));
+ }
+
+ // load non-deleted index
+ res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
+ 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
+ SPIFFS_CHECK_RES(res);
+
+ // traverse index for referenced pages
+ spiffs_page_ix *object_page_index;
+ spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work;
+
+ int entries;
+ int i;
+ spiffs_span_ix data_spix_offset;
+ if (p_hdr.span_ix == 0) {
+ // object header page index
+ entries = SPIFFS_OBJ_HDR_IX_LEN(fs);
+ data_spix_offset = 0;
+ object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header));
+ } else {
+ // object page index
+ entries = SPIFFS_OBJ_IX_LEN(fs);
+ data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1);
+ object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix));
+ }
+
+ // for all entries in index
+ for (i = 0; !restart && i < entries; i++) {
+ spiffs_page_ix rpix = object_page_index[i];
+ u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan;
+
+ if ((rpix != (spiffs_page_ix)-1 && rpix > SPIFFS_MAX_PAGES(fs))
+ || (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) {
+
+ // bad reference
+ 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;
+ res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
+ data_spix_offset + i, 0, &data_pix);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ res = SPIFFS_OK;
+ data_pix = 0;
+ }
+ SPIFFS_CHECK_RES(res);
+ if (data_pix == 0) {
+ // if not, allocate free page
+ spiffs_page_header new_ph;
+ new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL);
+ new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
+ 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 @ "_SPIPRIpg"\n", data_pix);
+ }
+ // remap index
+ 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 "_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 {
+ 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;
+
+ } else if (rpix_within_range) {
+
+ // valid reference
+ // read referenced page header
+ spiffs_page_header rp_hdr;
+ res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
+ 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr);
+ SPIFFS_CHECK_RES(res);
+
+ // cross reference page header check
+ if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) ||
+ 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 "_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
+ spiffs_page_ix data_pix;
+ res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
+ data_spix_offset + i, rpix, &data_pix);
+ if (res == SPIFFS_ERR_NOT_FOUND) {
+ res = SPIFFS_OK;
+ data_pix = 0;
+ }
+ 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 "_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 "_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 "_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 {
+ CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix);
+ }
+ SPIFFS_CHECK_RES(res);
+ restart = 1;
+ }
+ }
+ else {
+ // mark rpix as referenced
+ 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 "_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 "_SPIPRIid" and page "_SPIPRIpg"\n",
+ p_hdr.obj_id, cur_pix);
+ 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
+ res = spiffs_page_delete(fs, cur_pix);
+ SPIFFS_CHECK_RES(res);
+ restart = 1;
+ }
+ fs->work[rpix_byte_ix] |= (1<<(rpix_bit_ix + 1));
+ }
+ }
+ } // for all index entries
+ } // found index
+
+ // next page
+ cur_pix++;
+ }
+ // next block
+ cur_block++;
+ }
+ // check consistency bitmap
+ if (!restart) {
+ spiffs_page_ix objix_pix;
+ spiffs_page_ix rpix;
+
+ u32_t byte_ix;
+ u8_t bit_ix;
+ for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) {
+ for (bit_ix = 0; !restart && bit_ix < 8/bits; bit_ix ++) {
+ u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7;
+ spiffs_page_ix cur_pix = pix_offset + byte_ix * (8/bits) + bit_ix;
+
+ // 000 ok - free, unreferenced, not index
+
+ if (bitmask == 0x1) {
+
+ // 001
+ 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;
+ // check corresponding object index entry
+ 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);
+
+ res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix,
+ &rpix, &objix_pix);
+ if (res == SPIFFS_OK) {
+ 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: "_SPIPRIpg", rewrite to this "_SPIPRIpg"\n", rpix, cur_pix);
+ } else {
+ // pointing to something else, check what
+ spiffs_page_header rp_hdr;
+ res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
+ 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t*)&rp_hdr);
+ SPIFFS_CHECK_RES(res);
+ if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) &&
+ ((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: "_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: "_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 " : "",
+ (rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "",
+ cur_pix);
+ rewrite_ix_to_this = 1;
+ } else {
+ // should not happen, destined for fubar
+ }
+ }
+ }
+ } else if (res == SPIFFS_ERR_NOT_FOUND) {
+ 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 "_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 "_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 {
+ 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 "_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);
+ }
+ if (bitmask == 0x2) {
+
+ // 010
+ 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
+ }
+
+ // 011 ok - busy, referenced, not index
+
+ if (bitmask == 0x4) {
+
+ // 100
+ SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, unreferenced, INDEX\n", cur_pix);
+
+ // this should never happen, major fubar
+ }
+
+ // 101 ok - busy, unreferenced, index
+
+ if (bitmask == 0x6) {
+
+ // 110
+ 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 "_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;
+ }
+ } // while page range not reached end
+ return res;
+}
+
+// Checks consistency amongst all pages and fixes irregularities
+s32_t spiffs_page_consistency_check(spiffs *fs) {
+ CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0);
+ s32_t res = spiffs_page_consistency_check_i(fs);
+ if (res != SPIFFS_OK) {
+ CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0);
+ }
+ CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0);
+ return res;
+}
+
+//---------------------------------------
+// Object index consistency
+
+// searches for given object id in temporary object id index,
+// returns the index or -1
+static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) {
+ u32_t i;
+ spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work;
+ obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG;
+ for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) {
+ if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block,
+ 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_var_p;
+ spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work;
+
+ 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)) {
+ spiffs_page_header p_hdr;
+ spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
+
+ // load header
+ 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);
+
+ 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 "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" header not fully deleted - deleting\n",
+ cur_pix, obj_id, p_hdr.span_ix);
+ 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;
+ }
+
+ if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) ==
+ (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
+ return res_c;
+ }
+
+ if (p_hdr.span_ix == 0) {
+ // objix header page, register objid as reachable
+ int r = spiffs_object_index_search(fs, obj_id);
+ if (r == -1) {
+ // not registered, do it
+ obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
+ (*log_ix)++;
+ if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) {
+ *log_ix = 0;
+ }
+ }
+ } else { // span index
+ // objix page, see if header can be found
+ int r = spiffs_object_index_search(fs, obj_id);
+ u8_t delete = 0;
+ if (r == -1) {
+ // not in temporary index, try finding it
+ spiffs_page_ix objix_hdr_pix;
+ res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix);
+ res_c = SPIFFS_VIS_COUNTINUE_RELOAD;
+ if (res == SPIFFS_OK) {
+ // found, register as reachable
+ obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
+ } else if (res == SPIFFS_ERR_NOT_FOUND) {
+ // not found, register as unreachable
+ delete = 1;
+ obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG;
+ } else {
+ SPIFFS_CHECK_RES(res);
+ }
+ (*log_ix)++;
+ if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) {
+ *log_ix = 0;
+ }
+ } else {
+ // in temporary index, check reachable flag
+ if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) {
+ // registered as unreachable
+ delete = 1;
+ }
+ }
+
+ if (delete) {
+ 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);
+ 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);
+ }
+ } // span index
+ } // valid object index id
+
+ return res_c;
+}
+
+// Removes orphaned and partially deleted index pages.
+// Scans for index pages. When an index page is found, corresponding index header is searched for.
+// If no such page exists, the index page cannot be reached as no index header exists and must be
+// deleted.
+s32_t spiffs_object_index_consistency_check(spiffs *fs) {
+ s32_t res = SPIFFS_OK;
+ // impl note:
+ // fs->work is used for a temporary object index memory, listing found object ids and
+ // indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit.
+ // In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate
+ // a reachable/unreachable object id.
+ memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
+ u32_t obj_id_log_ix = 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);
+ if (res == SPIFFS_VIS_END) {
+ res = SPIFFS_OK;
+ }
+ if (res != SPIFFS_OK) {
+ CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0);
+ }
+ CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0);
+ return res;
+}
+
+#endif // !SPIFFS_READ_ONLY
diff --git a/build/esp32/sdk_build/components/spiffs/spiffs_config.h b/build/esp32/sdk_build/components/spiffs/spiffs_config.h
new file mode 100644
index 0000000..5042cbe
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/spiffs_config.h
@@ -0,0 +1,361 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <ctype.h>
+// ----------- >8 ------------
+
+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;
+
+// compile time switches
+
+// Set generic spiffs debug output call.
+#ifndef SPIFFS_DBG
+#define SPIFFS_DBG(...) //printf(__VA_ARGS__)
+#endif
+// Set spiffs debug output call for garbage collecting.
+#ifndef SPIFFS_GC_DBG
+#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__)
+#endif
+// Set spiffs debug output call for caching.
+#ifndef SPIFFS_CACHE_DBG
+#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__)
+#endif
+// Set spiffs debug output call for system consistency checks.
+#ifndef SPIFFS_CHECK_DBG
+#define SPIFFS_CHECK_DBG(...) //printf(__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,
+// this can be disabled to reduce flash.
+#ifndef SPIFFS_BUFFER_HELP
+#define SPIFFS_BUFFER_HELP 0
+#endif
+
+// Enables/disable memory read caching of nucleus file system operations.
+// If enabled, memory area must be provided for cache in SPIFFS_mount.
+#ifndef SPIFFS_CACHE
+#define SPIFFS_CACHE 1
+#endif
+#if SPIFFS_CACHE
+// Enables memory write caching for file descriptors in hydrogen
+#ifndef SPIFFS_CACHE_WR
+#define SPIFFS_CACHE_WR 1
+#endif
+
+// Enable/disable statistics on caching. Debug/test purpose only.
+#ifndef SPIFFS_CACHE_STATS
+#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.
+#ifndef SPIFFS_PAGE_CHECK
+#define SPIFFS_PAGE_CHECK 1
+#endif
+
+// Define maximum number of gc runs to perform to reach desired free pages.
+#ifndef SPIFFS_GC_MAX_RUNS
+#define SPIFFS_GC_MAX_RUNS 5
+#endif
+
+// Enable/disable statistics on gc. Debug/test purpose only.
+#ifndef SPIFFS_GC_STATS
+#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.
+#ifndef SPIFFS_GC_HEUR_W_DELET
+#define SPIFFS_GC_HEUR_W_DELET (5)
+#endif
+// Garbage collecting heuristics - weight used for used pages.
+#ifndef SPIFFS_GC_HEUR_W_USED
+#define SPIFFS_GC_HEUR_W_USED (-1)
+#endif
+// Garbage collecting heuristics - weight used for time between
+// last erased and erase of this block.
+#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE
+#define SPIFFS_GC_HEUR_W_ERASE_AGE (50)
+#endif
+
+// 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 (64)
+#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 (64)
+#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.
+#ifndef SPIFFS_COPY_BUFFER_STACK
+#define SPIFFS_COPY_BUFFER_STACK (64)
+#endif
+
+// Enable this to have an identifiable spiffs filesystem. This will look for
+// a magic in all sectors to determine if this is a valid spiffs system or
+// not on mount point. If not, SPIFFS_format must be called prior to mounting
+// again.
+#ifndef SPIFFS_USE_MAGIC
+#define SPIFFS_USE_MAGIC (1)
+#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 (1)
+#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
+#ifndef SPIFFS_LOCK
+#define SPIFFS_LOCK(fs)
+#endif
+// define this to exit a mutex if you're running on a multithreaded system
+#ifndef SPIFFS_UNLOCK
+#define SPIFFS_UNLOCK(fs)
+#endif
+
+// Enable if only one spiffs instance with constant configuration will exist
+// on the target. This will reduce calculations, flash and memory accesses.
+// Parts of configuration must be defined below instead of at time of mount.
+#ifndef SPIFFS_SINGLETON
+#define SPIFFS_SINGLETON 0
+#endif
+
+#if SPIFFS_SINGLETON
+// Instead of giving parameters in config struct, singleton build must
+// give parameters in defines below.
+#ifndef SPIFFS_CFG_PHYS_SZ
+#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*1024*2)
+#endif
+#ifndef SPIFFS_CFG_PHYS_ERASE_SZ
+#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (65536)
+#endif
+#ifndef SPIFFS_CFG_PHYS_ADDR
+#define SPIFFS_CFG_PHYS_ADDR(ignore) (0)
+#endif
+#ifndef SPIFFS_CFG_LOG_PAGE_SZ
+#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256)
+#endif
+#ifndef SPIFFS_CFG_LOG_BLOCK_SZ
+#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (65536)
+#endif
+#endif
+
+// Enable this if your target needs aligned data for index tables
+#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
+#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1
+#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
+#endif
+#if SPIFFS_TEST_VISUALISATION
+#ifndef spiffs_printf
+#define spiffs_printf(...) printf(__VA_ARGS__)
+#endif
+// spiffs_printf argument for a free page
+#ifndef SPIFFS_TEST_VIS_FREE_STR
+#define SPIFFS_TEST_VIS_FREE_STR "_"
+#endif
+// spiffs_printf argument for a deleted page
+#ifndef SPIFFS_TEST_VIS_DELE_STR
+#define SPIFFS_TEST_VIS_DELE_STR "/"
+#endif
+// spiffs_printf argument for an index page for given object id
+#ifndef SPIFFS_TEST_VIS_INDX_STR
+#define SPIFFS_TEST_VIS_INDX_STR(id) "i"
+#endif
+// spiffs_printf argument for a data page for given object id
+#ifndef SPIFFS_TEST_VIS_DATA_STR
+#define SPIFFS_TEST_VIS_DATA_STR(id) "d"
+#endif
+#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/build/esp32/sdk_build/components/spiffs/spiffs_gc.c b/build/esp32/sdk_build/components/spiffs/spiffs_gc.c
new file mode 100644
index 0000000..db1af4c
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/spiffs_gc.c
@@ -0,0 +1,606 @@
+#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.
+static s32_t spiffs_gc_erase_block(
+ spiffs *fs,
+ spiffs_block_ix bix) {
+ s32_t res;
+
+ SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix);
+ res = spiffs_erase_block(fs, bix);
+ SPIFFS_CHECK_RES(res);
+
+#if SPIFFS_CACHE
+ {
+ u32_t i;
+ for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) {
+ spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i);
+ }
+ }
+#endif
+ return res;
+}
+
+// Searches for blocks where all entries are deleted - if one is found,
+// the block is erased. Compared to the non-quick gc, the quick one ensures
+// that no updates are needed on existing objects on pages that are erased.
+s32_t spiffs_gc_quick(
+ spiffs *fs, u16_t max_free_pages) {
+ s32_t res = SPIFFS_OK;
+ u32_t blocks = fs->block_count;
+ spiffs_block_ix cur_block = 0;
+ u32_t cur_block_addr = 0;
+ int cur_entry = 0;
+ spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
+
+ SPIFFS_GC_DBG("gc_quick: running\n");
+#if SPIFFS_GC_STATS
+ fs->stats_gc_runs++;
+#endif
+
+ int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
+
+ // find fully deleted blocks
+ // check each block
+ while (res == SPIFFS_OK && blocks--) {
+ u16_t deleted_pages_in_block = 0;
+ u16_t free_pages_in_block = 0;
+
+ int obj_lookup_page = 0;
+ // check each object lookup page
+ while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
+ int entry_offset = obj_lookup_page * entries_per_page;
+ res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
+ 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
+ // check each entry
+ while (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];
+ if (obj_id == SPIFFS_OBJ_ID_DELETED) {
+ deleted_pages_in_block++;
+ } else if (obj_id == SPIFFS_OBJ_ID_FREE) {
+ // kill scan, go for next block
+ free_pages_in_block++;
+ if (free_pages_in_block > max_free_pages) {
+ obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
+ res = 1; // kill object lu loop
+ break;
+ }
+ } else {
+ // kill scan, go for next block
+ obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs);
+ res = 1; // kill object lu loop
+ break;
+ }
+ cur_entry++;
+ } // per entry
+ obj_lookup_page++;
+ } // per object lookup page
+ if (res == 1) res = SPIFFS_OK;
+
+ if (res == SPIFFS_OK &&
+ deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs) &&
+ free_pages_in_block <= max_free_pages) {
+ // found a fully deleted block
+ fs->stats_p_deleted -= deleted_pages_in_block;
+ res = spiffs_gc_erase_block(fs, cur_block);
+ return res;
+ }
+
+ cur_entry = 0;
+ cur_block++;
+ cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
+ } // per block
+
+ if (res == SPIFFS_OK) {
+ res = SPIFFS_ERR_NO_DELETED_BLOCKS;
+ }
+ return res;
+}
+
+// Checks if garbage collecting is necessary. If so a candidate block is found,
+// cleansed and erased
+s32_t spiffs_gc_check(
+ spiffs *fs,
+ u32_t len) {
+ s32_t res;
+ s32_t free_pages =
+ (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2)
+ - fs->stats_p_allocated - fs->stats_p_deleted;
+ int tries = 0;
+
+ if (fs->free_blocks > 3 &&
+ (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
+ return SPIFFS_OK;
+ }
+
+ 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:"_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:"_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 #"_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, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs)));
+
+ spiffs_block_ix *cands;
+ int count;
+ spiffs_block_ix cand;
+ s32_t prev_free_pages = free_pages;
+ // if the fs is crammed, ignore block age when selecting candidate - kind of a bad state
+ res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0);
+ SPIFFS_CHECK_RES(res);
+ if (count == 0) {
+ SPIFFS_GC_DBG("gc_check: no candidates, return\n");
+ return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL;
+ }
+#if SPIFFS_GC_STATS
+ fs->stats_gc_runs++;
+#endif
+ cand = cands[0];
+ fs->cleaning = 1;
+ //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 "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
+ } else {
+ SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res);
+ }
+ SPIFFS_CHECK_RES(res);
+
+ res = spiffs_gc_erase_page_stats(fs, cand);
+ SPIFFS_CHECK_RES(res);
+
+ res = spiffs_gc_erase_block(fs, cand);
+ SPIFFS_CHECK_RES(res);
+
+ free_pages =
+ (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
+ - fs->stats_p_allocated - fs->stats_p_deleted;
+
+ if (prev_free_pages <= 0 && prev_free_pages == free_pages) {
+ // abort early to reduce wear, at least tried once
+ SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n");
+ break;
+ }
+
+ } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
+ (s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));
+
+ free_pages =
+ (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
+ - fs->stats_p_allocated - fs->stats_p_deleted;
+ if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
+ res = SPIFFS_ERR_FULL;
+ }
+
+ 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);
+
+ return res;
+}
+
+// Updates page statistics for a block that is about to be erased
+s32_t spiffs_gc_erase_page_stats(
+ spiffs *fs,
+ spiffs_block_ix bix) {
+ s32_t res = SPIFFS_OK;
+ int obj_lookup_page = 0;
+ 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;
+ int cur_entry = 0;
+ u32_t dele = 0;
+ u32_t allo = 0;
+
+ // check each object lookup page
+ while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
+ int entry_offset = obj_lookup_page * entries_per_page;
+ 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
+ while (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];
+ if (obj_id == SPIFFS_OBJ_ID_FREE) {
+ } else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
+ dele++;
+ } else {
+ allo++;
+ }
+ cur_entry++;
+ } // per entry
+ obj_lookup_page++;
+ } // per object lookup page
+ 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;
+}
+
+// Finds block candidates to erase
+s32_t spiffs_gc_find_candidate(
+ spiffs *fs,
+ spiffs_block_ix **block_candidates,
+ int *candidate_count,
+ char fs_crammed) {
+ s32_t res = SPIFFS_OK;
+ u32_t blocks = fs->block_count;
+ spiffs_block_ix cur_block = 0;
+ u32_t cur_block_addr = 0;
+ spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
+ int cur_entry = 0;
+
+ // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score
+ int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs)-8)/(sizeof(spiffs_block_ix) + sizeof(s32_t)));
+ *candidate_count = 0;
+ memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
+
+ // divide up work area into block indices and scores
+ 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));
+
+ // check each block
+ while (res == SPIFFS_OK && blocks--) {
+ u16_t deleted_pages_in_block = 0;
+ u16_t used_pages_in_block = 0;
+
+ int obj_lookup_page = 0;
+ // check each object lookup page
+ while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
+ int entry_offset = obj_lookup_page * entries_per_page;
+ res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
+ 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
+ // check each entry
+ while (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];
+ if (obj_id == SPIFFS_OBJ_ID_FREE) {
+ // when a free entry is encountered, scan logic ensures that all following entries are free also
+ res = 1; // kill object lu loop
+ break;
+ } else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
+ deleted_pages_in_block++;
+ } else {
+ used_pages_in_block++;
+ }
+ cur_entry++;
+ } // per entry
+ obj_lookup_page++;
+ } // per object lookup page
+ if (res == 1) res = SPIFFS_OK;
+
+ // calculate score and insert into candidate table
+ // stoneage sort, but probably not so many blocks
+ 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,
+ SPIFFS_ERASE_COUNT_PADDR(fs, cur_block),
+ sizeof(spiffs_obj_id), (u8_t *)&erase_count);
+ SPIFFS_CHECK_RES(res);
+
+ spiffs_obj_id erase_age;
+ if (fs->max_erase_count > erase_count) {
+ erase_age = fs->max_erase_count - erase_count;
+ } else {
+ erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count);
+ }
+
+ s32_t score =
+ deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET +
+ 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:"_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;
+ cand_scores[cand_ix] = score;
+ break;
+ } else if (cand_scores[cand_ix] < score) {
+ int reorder_cand_ix = max_candidates - 2;
+ while (reorder_cand_ix >= cand_ix) {
+ cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix];
+ cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix];
+ reorder_cand_ix--;
+ }
+ cand_blocks[cand_ix] = cur_block;
+ cand_scores[cand_ix] = score;
+ break;
+ }
+ cand_ix++;
+ }
+ (*candidate_count)++;
+ }
+
+ cur_entry = 0;
+ cur_block++;
+ cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
+ } // per block
+
+ return res;
+}
+
+typedef enum {
+ FIND_OBJ_DATA,
+ MOVE_OBJ_DATA,
+ MOVE_OBJ_IX,
+ FINISHED
+} spiffs_gc_clean_state;
+
+typedef struct {
+ spiffs_gc_clean_state state;
+ 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;
+
+// Empties given block by moving all data into free pages of another block
+// Strategy:
+// loop:
+// scan object lookup for object data pages
+// for first found id, check spix and load corresponding object index page to memory
+// push object scan lookup entry index
+// rescan object lookup, find data pages with same id and referenced by same object index
+// move data page, update object index in memory
+// when reached end of lookup, store updated object index
+// pop object scan lookup entry index
+// repeat loop until end of object lookup
+// scan object lookup again for remaining object index pages, move to new page in other block
+//
+s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
+ s32_t res = SPIFFS_OK;
+ 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; // 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 "_SPIPRIbl"\n", bix);
+
+ memset(&gc, 0, sizeof(spiffs_gc));
+ gc.state = FIND_OBJ_DATA;
+
+ if (fs->free_cursor_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 "_SPIPRIbl"\n", fs->free_cursor_block_ix);
+ }
+
+ while (res == SPIFFS_OK && gc.state != FINISHED) {
+ 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;
+ u8_t scan = 1;
+ // check each object lookup page
+ while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
+ int entry_offset = obj_lookup_page * entries_per_page;
+ 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 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];
+ cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry);
+
+ // 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)) {
+ // 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 "_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 {
+ spiffs_page_ix new_data_pix;
+ 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 "_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,
+ 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 -
+ // 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;
+ }
+ // update memory representation of object index page with new data page
+ 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 "_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 "_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
+ spiffs_page_header p_hdr;
+ spiffs_page_ix new_pix;
+ // load header
+ 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);
+ 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 "_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, (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 -
+ // 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, (spiffs_page_object_ix *)0,
+ SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0);
+ }
+ }
+ SPIFFS_CHECK_RES(res);
+ }
+ break;
+ default:
+ scan = 0;
+ break;
+ } // switch gc state
+ cur_entry++;
+ } // per entry
+ 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; // 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:"_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 "_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 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; // 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, 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, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix);
+ SPIFFS_CHECK_RES(res);
+ 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;
+ } // 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/build/esp32/sdk_build/components/spiffs/spiffs_hydrogen.c b/build/esp32/sdk_build/components/spiffs/spiffs_hydrogen.c
new file mode 100644
index 0000000..9ff3e7a
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/spiffs_hydrogen.c
@@ -0,0 +1,1405 @@
+/*
+ * spiffs_hydrogen.c
+ *
+ * Created on: Jun 16, 2013
+ * Author: petera
+ */
+
+#include "spiffs.h"
+#include "spiffs_nucleus.h"
+
+#if SPIFFS_FILEHDL_OFFSET
+#define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0)
+#define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0)
+#else
+#define SPIFFS_FH_OFFS(fs, fh) (fh)
+#define SPIFFS_FH_UNOFFS(fs, fh) (fh)
+#endif
+
+#if SPIFFS_CACHE == 1
+static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh);
+#endif
+
+#if SPIFFS_BUFFER_HELP
+u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) {
+ return num_descs * sizeof(spiffs_fd);
+}
+#if SPIFFS_CACHE
+u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) {
+ return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs));
+}
+#endif
+#endif
+
+u8_t SPIFFS_mounted(spiffs *fs) {
+ return SPIFFS_CHECK_MOUNT(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;
+ return -1;
+ }
+
+ s32_t res;
+ SPIFFS_LOCK(fs);
+
+ spiffs_block_ix bix = 0;
+ while (bix < fs->block_count) {
+ fs->max_erase_count = 0;
+ res = spiffs_erase_block(fs, bix);
+ if (res != SPIFFS_OK) {
+ res = SPIFFS_ERR_ERASE_FAIL;
+ }
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+ bix++;
+ }
+
+ SPIFFS_UNLOCK(fs);
+
+ return 0;
+#endif // SPIFFS_READ_ONLY
+}
+
+#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
+
+s32_t SPIFFS_probe_fs(spiffs_config *config) {
+ s32_t res = spiffs_probe(config);
+ return res;
+}
+
+#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
+
+s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
+ u8_t *fd_space, u32_t fd_space_size,
+ void *cache, u32_t cache_size,
+ spiffs_check_callback check_cb_f) {
+ void *user_data;
+ SPIFFS_LOCK(fs);
+ user_data = fs->user_data;
+ memset(fs, 0, sizeof(spiffs));
+ memcpy(&fs->cfg, config, sizeof(spiffs_config));
+ fs->user_data = user_data;
+ fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs);
+ fs->work = &work[0];
+ fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)];
+ memset(fd_space, 0, fd_space_size);
+ // align fd_space pointer to pointer size byte boundary
+ u8_t ptr_size = sizeof(void*);
+ 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);
+ }
+ fs->fd_space = fd_space;
+ fs->fd_count = (fd_space_size/sizeof(spiffs_fd));
+
+ // 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);
+ cache = cache_8;
+ cache_size -= (ptr_size-addr_lsb);
+ }
+ if (cache_size & (ptr_size-1)) {
+ cache_size -= (cache_size & (ptr_size-1));
+ }
+
+#if SPIFFS_CACHE
+ fs->cache = cache;
+ 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
+
+ s32_t res;
+
+#if SPIFFS_USE_MAGIC
+ res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE;
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+#endif
+
+ fs->config_magic = SPIFFS_CONFIG_MAGIC;
+
+ res = spiffs_obj_lu_scan(fs);
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+ SPIFFS_DBG("page index byte len: "_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;
+
+ fs->mounted = 1;
+
+ SPIFFS_UNLOCK(fs);
+
+ return 0;
+}
+
+void SPIFFS_unmount(spiffs *fs) {
+ if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return;
+ SPIFFS_LOCK(fs);
+ u32_t i;
+ 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->file_nbr != 0) {
+#if SPIFFS_CACHE
+ (void)spiffs_fflush_cache(fs, cur_fd->file_nbr);
+#endif
+ spiffs_fd_return(fs, cur_fd->file_nbr);
+ }
+ }
+ fs->mounted = 0;
+
+ SPIFFS_UNLOCK(fs);
+}
+
+s32_t SPIFFS_errno(spiffs *fs) {
+ return fs->err_code;
+}
+
+void SPIFFS_clearerr(spiffs *fs) {
+ fs->err_code = SPIFFS_OK;
+}
+
+s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) {
+#if SPIFFS_READ_ONLY
+ (void)fs; (void)path; (void)mode;
+ return SPIFFS_ERR_RO_NOT_IMPL;
+#else
+ (void)mode;
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) {
+ SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG);
+ }
+ SPIFFS_LOCK(fs);
+ spiffs_obj_id obj_id;
+ s32_t res;
+
+ res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t*)path);
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+ 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, const char *path, spiffs_flags flags, spiffs_mode mode) {
+ (void)mode;
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) {
+ SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG);
+ }
+ SPIFFS_LOCK(fs);
+
+ spiffs_fd *fd;
+ spiffs_page_ix pix;
+
+#if SPIFFS_READ_ONLY
+ // not valid flags in read only mode
+ flags &= ~(SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC);
+#endif // SPIFFS_READ_ONLY
+
+ s32_t res = spiffs_fd_find_new(fs, &fd, path);
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+ 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 (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);
+ if (res < SPIFFS_OK) {
+ spiffs_fd_return(fs, fd->file_nbr);
+ }
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+ 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_O_TRUNC;
+#endif // !SPIFFS_READ_ONLY
+ } else {
+ if (res < SPIFFS_OK) {
+ spiffs_fd_return(fs, fd->file_nbr);
+ }
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+ }
+ res = spiffs_object_open_by_page(fs, pix, fd, flags, mode);
+ 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);
+}
+
+spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) {
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ SPIFFS_LOCK(fs);
+
+ spiffs_fd *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);
+ 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);
+}
+
+spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) {
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ SPIFFS_LOCK(fs);
+
+ spiffs_fd *fd;
+
+ s32_t res = spiffs_fd_find_new(fs, &fd, 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);
+
+ 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_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
+
+ if (fd->fdoffset + len >= fd->size) {
+ // reading beyond file size
+ s32_t avail = fd->size - fd->fdoffset;
+ if (avail <= 0) {
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT);
+ }
+ res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t*)buf);
+ if (res == SPIFFS_ERR_END_OF_OBJECT) {
+ fd->fdoffset += avail;
+ SPIFFS_UNLOCK(fs);
+ return avail;
+ } else {
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+ len = avail;
+ }
+ } else {
+ // reading within file size
+ res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t*)buf);
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+ }
+ fd->fdoffset += len;
+
+ SPIFFS_UNLOCK(fs);
+
+ return len;
+}
+
+s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t 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;
+ s32_t remaining = len;
+ if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) {
+ s32_t m_len = MIN((s32_t)(fd->size - offset), len);
+ res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len);
+ SPIFFS_CHECK_RES(res);
+ remaining -= m_len;
+ u8_t *buf_8 = (u8_t *)buf;
+ buf_8 += m_len;
+ buf = buf_8;
+ offset += m_len;
+ }
+ if (remaining > 0) {
+ res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining);
+ SPIFFS_CHECK_RES(res);
+ }
+ return len;
+
+}
+#endif // !SPIFFS_READ_ONLY
+
+s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
+#if SPIFFS_READ_ONLY
+ (void)fs; (void)fh; (void)buf; (void)len;
+ return SPIFFS_ERR_RO_NOT_IMPL;
+#else
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ SPIFFS_LOCK(fs);
+
+ spiffs_fd *fd;
+ 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_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
+ 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);
+ }
+#endif
+ if (fd->flags & SPIFFS_O_APPEND) {
+ if (fd->size == SPIFFS_UNDEFINED_LEN) {
+ offset = 0;
+ } else {
+ offset = fd->size;
+ }
+#if SPIFFS_CACHE_WR
+ if (fd->cache_page) {
+ offset = MAX(offset, fd->cache_page->offset + fd->cache_page->size);
+ }
+#endif
+ }
+
+#if SPIFFS_CACHE_WR
+ 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;
+ if (fd->cache_page) {
+ // have a cached page for this fd already, check cache page boundaries
+ if (offset < fd->cache_page->offset || // writing before cache
+ offset > fd->cache_page->offset + fd->cache_page->size || // writing after cache
+ 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 "_SPIPRIpg" 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_UNLOCK(fs, res);
+ } else {
+ // writing within cache
+ alloc_cpage = 0;
+ }
+ }
+
+ if (alloc_cpage) {
+ fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd);
+ if (fd->cache_page) {
+ fd->cache_page->offset = offset;
+ fd->cache_page->size = 0;
+ SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIpg" 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 "_SPIPRIpg" 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);
+ 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_UNLOCK(fs, res);
+ fd->fdoffset += len;
+ SPIFFS_UNLOCK(fs);
+ return res;
+ }
+ } else {
+ // 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 "_SPIPRIpg" 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_UNLOCK(fs, res);
+ // data written below
+ }
+ }
+ }
+#endif
+
+ res = spiffs_hydro_write(fs, fd, buf, offset, len);
+ 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_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 SPIFFS_CACHE_WR
+ spiffs_fflush_cache(fs, fh);
+#endif
+
+ s32_t fileSize = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size;
+
+ switch (whence) {
+ case SPIFFS_SEEK_CUR:
+ offs = fd->fdoffset+offs;
+ break;
+ case SPIFFS_SEEK_END:
+ offs = fileSize + offs;
+ break;
+ }
+
+ if ((offs > fileSize)) {
+ fd->fdoffset = fileSize;
+ 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 objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
+ if (fd->cursor_objix_spix != objix_spix) {
+ spiffs_page_ix pix;
+ res = spiffs_obj_lu_find_id_and_span(
+ fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix);
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+ fd->cursor_objix_spix = objix_spix;
+ fd->cursor_objix_pix = pix;
+ }
+ fd->fdoffset = offs;
+
+ SPIFFS_UNLOCK(fs);
+
+ return offs;
+}
+
+s32_t SPIFFS_remove(spiffs *fs, const char *path) {
+#if SPIFFS_READ_ONLY
+ (void)fs; (void)path;
+ return SPIFFS_ERR_RO_NOT_IMPL;
+#else
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) {
+ SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG);
+ }
+ SPIFFS_LOCK(fs);
+
+ spiffs_fd *fd;
+ spiffs_page_ix pix;
+ s32_t res;
+
+ 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, (const u8_t*)path, &pix);
+ if (res != SPIFFS_OK) {
+ spiffs_fd_return(fs, fd->file_nbr);
+ }
+ 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_truncate(fd, 0, 1);
+ if (res != SPIFFS_OK) {
+ spiffs_fd_return(fs, fd->file_nbr);
+ }
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+ SPIFFS_UNLOCK(fs);
+ return 0;
+#endif // SPIFFS_READ_ONLY
+}
+
+s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) {
+#if SPIFFS_READ_ONLY
+ (void)fs; (void)fh;
+ return SPIFFS_ERR_RO_NOT_IMPL;
+#else
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ SPIFFS_LOCK(fs);
+
+ spiffs_fd *fd;
+ s32_t res;
+ fh = SPIFFS_FH_UNOFFS(fs, fh);
+ res = spiffs_fd_get(fs, fh, &fd);
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+ if ((fd->flags & SPIFFS_O_WRONLY) == 0) {
+ res = SPIFFS_ERR_NOT_WRITABLE;
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+ }
+
+#if SPIFFS_CACHE_WR
+ spiffs_cache_fd_release(fs, fd->cache_page);
+#endif
+
+ res = spiffs_object_truncate(fd, 0, 1);
+
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+ 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,
+ SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr);
+ SPIFFS_API_CHECK_RES(fs, res);
+
+ u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , pix)) +
+ SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_obj_id);
+ res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, fh,
+ obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id);
+ SPIFFS_API_CHECK_RES(fs, res);
+
+ 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
+ memcpy(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN);
+#endif
+
+ return res;
+}
+
+s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) {
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) {
+ SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG);
+ }
+ SPIFFS_LOCK(fs);
+
+ s32_t res;
+ spiffs_page_ix pix;
+
+ res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix);
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+ res = spiffs_stat_pix(fs, pix, 0, s);
+
+ SPIFFS_UNLOCK(fs);
+
+ return res;
+}
+
+s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) {
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ SPIFFS_LOCK(fs);
+
+ 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 SPIFFS_CACHE_WR
+ spiffs_fflush_cache(fs, fh);
+#endif
+
+ res = spiffs_stat_pix(fs, fd->objix_hdr_pix, fh, s);
+
+ SPIFFS_UNLOCK(fs);
+
+ return res;
+}
+
+// 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_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_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 "_SPIPRIpg" 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),
+ fd->cache_page->offset, fd->cache_page->size);
+ if (res < SPIFFS_OK) {
+ fs->err_code = res;
+ }
+ spiffs_cache_fd_release(fs, fd->cache_page);
+ }
+ }
+#endif
+
+ return res;
+}
+#endif
+
+s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) {
+ (void)fh;
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ s32_t res = SPIFFS_OK;
+#if !SPIFFS_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);
+#endif
+
+ return res;
+}
+
+s32_t SPIFFS_close(spiffs *fs, spiffs_file 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) {
+#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, (const u8_t*)old_path, &pix_old);
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+ 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) {
+ res = SPIFFS_ERR_CONFLICTING_NAME;
+ }
+ 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_old, 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, (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);
+
+ SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
+
+ SPIFFS_UNLOCK(fs);
+
+ return res;
+#endif // SPIFFS_READ_ONLY
+}
+
+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) {
+ (void)name;
+
+ if (!SPIFFS_CHECK_CFG((fs))) {
+ (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED;
+ return 0;
+ }
+
+ if (!SPIFFS_CHECK_MOUNT(fs)) {
+ fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
+ return 0;
+ }
+
+ d->fs = fs;
+ d->block = 0;
+ d->entry = 0;
+ return d;
+}
+
+static s32_t spiffs_read_dir_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_page_object_ix_header objix_hdr;
+ if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED ||
+ (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) {
+ return SPIFFS_VIS_COUNTINUE;
+ }
+
+ spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry);
+ 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_header), (u8_t *)&objix_hdr);
+ 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)) ==
+ (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
+ 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
+ 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) {
+ if (!SPIFFS_CHECK_MOUNT(d->fs)) {
+ d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
+ return 0;
+ }
+ SPIFFS_LOCK(d->fs);
+
+ spiffs_block_ix bix;
+ int entry;
+ s32_t res;
+ struct spiffs_dirent *ret = 0;
+
+ res = spiffs_obj_lu_find_entry_visitor(d->fs,
+ d->block,
+ d->entry,
+ SPIFFS_VIS_NO_WRAP,
+ 0,
+ spiffs_read_dir_v,
+ 0,
+ e,
+ &bix,
+ &entry);
+ 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(d->fs);
+ return ret;
+}
+
+s32_t SPIFFS_closedir(spiffs_DIR *d) {
+ SPIFFS_API_CHECK_CFG(d->fs);
+ SPIFFS_API_CHECK_MOUNT(d->fs);
+ return 0;
+}
+
+s32_t SPIFFS_check(spiffs *fs) {
+#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);
+ SPIFFS_LOCK(fs);
+
+ res = spiffs_lookup_consistency_check(fs, 0);
+
+ res = spiffs_object_index_consistency_check(fs);
+
+ res = spiffs_page_consistency_check(fs);
+
+ res = spiffs_obj_lu_scan(fs);
+
+ SPIFFS_UNLOCK(fs);
+ return res;
+#endif // SPIFFS_READ_ONLY
+}
+
+s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) {
+ s32_t res = SPIFFS_OK;
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ SPIFFS_LOCK(fs);
+
+ u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs);
+ u32_t blocks = fs->block_count;
+ u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs);
+ u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs);
+ u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page
+
+ if (total) {
+ *total = total_data_pages * data_page_size;
+ }
+
+ if (used) {
+ *used = fs->stats_p_allocated * data_page_size;
+ }
+
+ SPIFFS_UNLOCK(fs);
+ return res;
+}
+
+s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) {
+#if SPIFFS_READ_ONLY
+ (void)fs; (void)max_free_pages;
+ return SPIFFS_ERR_RO_NOT_IMPL;
+#else
+ s32_t res;
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ SPIFFS_LOCK(fs);
+
+ res = spiffs_gc_quick(fs, 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) {
+#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);
+ SPIFFS_LOCK(fs);
+
+ res = spiffs_gc_check(fs, 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) {
+ 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) {
+ 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_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) {
+ 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) {
+ 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) {
+ 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) {
+ s32_t res = SPIFFS_OK;
+ SPIFFS_API_CHECK_CFG(fs);
+ SPIFFS_API_CHECK_MOUNT(fs);
+ SPIFFS_LOCK(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
+ int obj_lookup_page = 0;
+ int cur_entry = 0;
+
+ while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
+ int entry_offset = obj_lookup_page * entries_per_page;
+ 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
+ while (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];
+ if (cur_entry == 0) {
+ spiffs_printf(_SPIPRIbl" ", bix);
+ } else if ((cur_entry & 0x3f) == 0) {
+ spiffs_printf(" ");
+ }
+ if (obj_id == SPIFFS_OBJ_ID_FREE) {
+ spiffs_printf(SPIFFS_TEST_VIS_FREE_STR);
+ } else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
+ spiffs_printf(SPIFFS_TEST_VIS_DELE_STR);
+ } else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG){
+ spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id));
+ } else {
+ spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id));
+ }
+ cur_entry++;
+ if ((cur_entry & 0x3f) == 0) {
+ spiffs_printf("\n");
+ }
+ } // per entry
+ obj_lookup_page++;
+ } // per object lookup page
+
+ spiffs_obj_id erase_count;
+ res = _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_CHECK_RES(res);
+
+ if (erase_count != (spiffs_obj_id)-1) {
+ spiffs_printf("\tera_cnt: "_SPIPRIi"\n", erase_count);
+ } else {
+ spiffs_printf("\tera_cnt: N/A\n");
+ }
+
+ bix++;
+ } // per block
+
+ 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: "_SPIPRIi" of "_SPIPRIi"\n", used, total);
+ return res;
+}
+#endif
diff --git a/build/esp32/sdk_build/components/spiffs/spiffs_nucleus.c b/build/esp32/sdk_build/components/spiffs/spiffs_nucleus.c
new file mode 100644
index 0000000..44ba711
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/spiffs_nucleus.c
@@ -0,0 +1,2327 @@
+#include "spiffs.h"
+#include "spiffs_nucleus.h"
+
+static s32_t spiffs_page_data_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) {
+ // referring to page 0xffff...., bad object index
+ return SPIFFS_ERR_INDEX_REF_FREE;
+ }
+ if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
+ // referring to an object lookup page, bad object index
+ return SPIFFS_ERR_INDEX_REF_LU;
+ }
+ if (pix > SPIFFS_MAX_PAGES(fs)) {
+ // referring to a bad page
+ return SPIFFS_ERR_INDEX_REF_INVALID;
+ }
+#if SPIFFS_PAGE_CHECK
+ spiffs_page_header ph;
+ res = _spiffs_rd(
+ fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ,
+ fd->file_nbr,
+ SPIFFS_PAGE_TO_PADDR(fs, pix),
+ sizeof(spiffs_page_header),
+ (u8_t *)&ph);
+ SPIFFS_CHECK_RES(res);
+ SPIFFS_VALIDATE_DATA(ph, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, spix);
+#endif
+ 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) {
+ // referring to page 0xffff...., bad object index
+ return SPIFFS_ERR_INDEX_FREE;
+ }
+ if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
+ // referring to an object lookup page, bad object index
+ return SPIFFS_ERR_INDEX_LU;
+ }
+ if (pix > SPIFFS_MAX_PAGES(fs)) {
+ // referring to a bad page
+ return SPIFFS_ERR_INDEX_INVALID;
+ }
+#if SPIFFS_PAGE_CHECK
+ spiffs_page_header ph;
+ res = _spiffs_rd(
+ fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
+ fd->file_nbr,
+ SPIFFS_PAGE_TO_PADDR(fs, pix),
+ sizeof(spiffs_page_header),
+ (u8_t *)&ph);
+ SPIFFS_CHECK_RES(res);
+ SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix);
+#endif
+ return res;
+}
+#endif // !SPIFFS_READ_ONLY
+
+#if !SPIFFS_CACHE
+
+s32_t spiffs_phys_rd(
+ spiffs *fs,
+ u32_t addr,
+ u32_t len,
+ u8_t *dst) {
+ return SPIFFS_HAL_READ(fs, addr, len, dst);
+}
+
+s32_t spiffs_phys_wr(
+ spiffs *fs,
+ u32_t addr,
+ u32_t len,
+ u8_t *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) {
+ u32_t chunk_size = MIN(SPIFFS_COPY_BUFFER_STACK, len);
+ res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS, fh, src, chunk_size, b);
+ SPIFFS_CHECK_RES(res);
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD, fh, dst, chunk_size, b);
+ SPIFFS_CHECK_RES(res);
+ len -= chunk_size;
+ src += chunk_size;
+ dst += chunk_size;
+ }
+ 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.
+// 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.
+// If no match is found in all look up, SPIFFS_VIS_END is returned.
+// @param fs the file system
+// @param starting_block the starting block to start search in
+// @param starting_lu_entry the look up index entry to start search in
+// @param flags ored combination of SPIFFS_VIS_CHECK_ID, SPIFFS_VIS_CHECK_PH,
+// SPIFFS_VIS_NO_WRAP
+// @param obj_id argument object id
+// @param v visitor callback 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(
+ spiffs *fs,
+ spiffs_block_ix starting_block,
+ int starting_lu_entry,
+ u8_t flags,
+ spiffs_obj_id obj_id,
+ spiffs_visitor_f v,
+ const void *user_const_p,
+ void *user_var_p,
+ spiffs_block_ix *block_ix,
+ int *lu_entry) {
+ s32_t res = SPIFFS_OK;
+ s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs);
+ spiffs_block_ix cur_block = starting_block;
+ u32_t cur_block_addr = starting_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs);
+
+ spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
+ int cur_entry = starting_lu_entry;
+ 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) {
+ cur_entry = 0;
+ cur_block++;
+ cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs);
+ if (cur_block >= fs->block_count) {
+ if (flags & SPIFFS_VIS_NO_WRAP) {
+ return SPIFFS_VIS_END;
+ } else {
+ // block wrap
+ cur_block = 0;
+ cur_block_addr = 0;
+ }
+ }
+ }
+
+ // check each block
+ while (res == SPIFFS_OK && entry_count > 0) {
+ int obj_lookup_page = cur_entry / entries_per_page;
+ // check each object lookup page
+ while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
+ int entry_offset = obj_lookup_page * entries_per_page;
+ res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
+ 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
+ // check each entry
+ while (res == SPIFFS_OK &&
+ cur_entry - entry_offset < entries_per_page && // for non-last obj lookup pages
+ cur_entry < (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) // for last obj lookup page
+ {
+ if ((flags & SPIFFS_VIS_CHECK_ID) == 0 || obj_lu_buf[cur_entry-entry_offset] == obj_id) {
+ if (block_ix) *block_ix = cur_block;
+ if (lu_entry) *lu_entry = cur_entry;
+ if (v) {
+ res = v(
+ fs,
+ (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry-entry_offset],
+ cur_block,
+ cur_entry,
+ 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,
+ 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
+ SPIFFS_CHECK_RES(res);
+ }
+ res = SPIFFS_OK;
+ cur_entry++;
+ entry_count--;
+ continue;
+ } else {
+ return res;
+ }
+ } else {
+ return SPIFFS_OK;
+ }
+ }
+ entry_count--;
+ cur_entry++;
+ } // per entry
+ obj_lookup_page++;
+ } // per object lookup page
+ cur_entry = 0;
+ cur_block++;
+ cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
+ if (cur_block >= fs->block_count) {
+ if (flags & SPIFFS_VIS_NO_WRAP) {
+ return SPIFFS_VIS_END;
+ } else {
+ // block wrap
+ cur_block = 0;
+ cur_block_addr = 0;
+ }
+ }
+ } // per block
+
+ SPIFFS_CHECK_RES(res);
+
+ return SPIFFS_VIS_END;
+}
+
+#if !SPIFFS_READ_ONLY
+s32_t spiffs_erase_block(
+ spiffs *fs,
+ spiffs_block_ix bix) {
+ s32_t res;
+ u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix);
+ s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs);
+
+ // here we ignore res, just try erasing the block
+ while (size > 0) {
+ SPIFFS_DBG("erase "_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);
+ }
+ fs->free_blocks++;
+
+ // register erase count for this block
+ res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0,
+ SPIFFS_ERASE_COUNT_PADDR(fs, bix),
+ sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count);
+ SPIFFS_CHECK_RES(res);
+
+#if SPIFFS_USE_MAGIC
+ // finally, write magic
+ spiffs_obj_id magic = SPIFFS_MAGIC(fs, 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);
+ SPIFFS_CHECK_RES(res);
+#endif
+
+ fs->max_erase_count++;
+ if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) {
+ fs->max_erase_count = 0;
+ }
+
+ return res;
+}
+#endif // !SPIFFS_READ_ONLY
+
+#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
+s32_t spiffs_probe(
+ spiffs_config *cfg) {
+ s32_t res;
+ u32_t paddr;
+ spiffs dummy_fs; // create a dummy fs struct just to be able to use macros
+ memcpy(&dummy_fs.cfg, cfg, sizeof(spiffs_config));
+ dummy_fs.block_count = 0;
+
+ // Read three magics, as one block may be in an aborted erase state.
+ // At least two of these must contain magic and be in decreasing order.
+ spiffs_obj_id magic[3];
+ spiffs_obj_id bix_count[3];
+
+ spiffs_block_ix bix;
+ for (bix = 0; bix < 3; bix++) {
+ paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix);
+#if SPIFFS_HAL_CALLBACK_EXTRA
+ // not any proper fs to report here, so callback with null
+ // (cross fingers that no-one gets angry)
+ res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]);
+#else
+ res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]);
+#endif
+ bix_count[bix] = magic[bix] ^ SPIFFS_MAGIC(&dummy_fs, 0);
+ SPIFFS_CHECK_RES(res);
+ }
+
+ // check that we have sane number of blocks
+ if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS;
+ // check that the order is correct, take aborted erases in calculation
+ // first block aborted erase
+ if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) {
+ return (bix_count[1]+1) * cfg->log_block_size;
+ }
+ // second block aborted erase
+ if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) {
+ return bix_count[0] * cfg->log_block_size;
+ }
+ // third block aborted erase
+ if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) {
+ return bix_count[0] * cfg->log_block_size;
+ }
+ // no block has aborted erase
+ if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) {
+ return bix_count[0] * cfg->log_block_size;
+ }
+
+ return SPIFFS_ERR_PROBE_NOT_A_FS;
+}
+#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
+
+
+static s32_t spiffs_obj_lu_scan_v(
+ spiffs *fs,
+ spiffs_obj_id obj_id,
+ spiffs_block_ix bix,
+ int ix_entry,
+ const void *user_const_p,
+ void *user_var_p) {
+ (void)bix;
+ (void)user_const_p;
+ (void)user_var_p;
+ if (obj_id == SPIFFS_OBJ_ID_FREE) {
+ if (ix_entry == 0) {
+ fs->free_blocks++;
+ // todo optimize further, return SPIFFS_NEXT_BLOCK
+ }
+ } else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
+ fs->stats_p_deleted++;
+ } else {
+ fs->stats_p_allocated++;
+ }
+
+ return SPIFFS_VIS_COUNTINUE;
+}
+
+
+// Scans thru all obj lu and counts free, deleted and used pages
+// Find the maximum block erase count
+// Checks magic if enabled
+s32_t spiffs_obj_lu_scan(
+ spiffs *fs) {
+ s32_t res;
+ spiffs_block_ix bix;
+ int entry;
+#if SPIFFS_USE_MAGIC
+ spiffs_block_ix unerased_bix = (spiffs_block_ix)-1;
+#endif
+
+ // find out erase count
+ // if enabled, check magic
+ bix = 0;
+ spiffs_obj_id erase_count_final;
+ spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE;
+ spiffs_obj_id erase_count_max = 0;
+ while (bix < fs->block_count) {
+#if SPIFFS_USE_MAGIC
+ spiffs_obj_id magic;
+ res = _spiffs_rd(fs,
+ SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
+ 0, SPIFFS_MAGIC_PADDR(fs, bix) ,
+ sizeof(spiffs_obj_id), (u8_t *)&magic);
+
+ SPIFFS_CHECK_RES(res);
+ if (magic != SPIFFS_MAGIC(fs, bix)) {
+ if (unerased_bix == (spiffs_block_ix)-1) {
+ // allow one unerased block as it might be powered down during an erase
+ unerased_bix = bix;
+ } else {
+ // more than one unerased block, bail out
+ SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS);
+ }
+ }
+#endif
+ spiffs_obj_id erase_count;
+ res = _spiffs_rd(fs,
+ SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
+ 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix) ,
+ sizeof(spiffs_obj_id), (u8_t *)&erase_count);
+ SPIFFS_CHECK_RES(res);
+ if (erase_count != SPIFFS_OBJ_ID_FREE) {
+ erase_count_min = MIN(erase_count_min, erase_count);
+ erase_count_max = MAX(erase_count_max, erase_count);
+ }
+ bix++;
+ }
+
+ if (erase_count_min == 0 && erase_count_max == SPIFFS_OBJ_ID_FREE) {
+ // clean system, set counter to zero
+ erase_count_final = 0;
+ } else if (erase_count_max - erase_count_min > (SPIFFS_OBJ_ID_FREE)/2) {
+ // wrap, take min
+ erase_count_final = erase_count_min+1;
+ } else {
+ erase_count_final = erase_count_max+1;
+ }
+
+ fs->max_erase_count = erase_count_final;
+
+#if SPIFFS_USE_MAGIC
+ if (unerased_bix != (spiffs_block_ix)-1) {
+ // found one unerased block, remedy
+ SPIFFS_DBG("mount: erase block "_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
+
+ // count blocks
+
+ fs->free_blocks = 0;
+ fs->stats_p_allocated = 0;
+ fs->stats_p_deleted = 0;
+
+ res = spiffs_obj_lu_find_entry_visitor(fs,
+ 0,
+ 0,
+ 0,
+ 0,
+ spiffs_obj_lu_scan_v,
+ 0,
+ 0,
+ &bix,
+ &entry);
+
+ if (res == SPIFFS_VIS_END) {
+ res = SPIFFS_OK;
+ }
+
+ SPIFFS_CHECK_RES(res);
+
+ return res;
+}
+
+#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(
+ spiffs *fs,
+ spiffs_block_ix starting_block,
+ int starting_lu_entry,
+ spiffs_block_ix *block_ix,
+ int *lu_entry) {
+ s32_t res;
+ if (!fs->cleaning && fs->free_blocks < 2) {
+ res = spiffs_gc_quick(fs, 0);
+ if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) {
+ res = SPIFFS_OK;
+ }
+ SPIFFS_CHECK_RES(res);
+ if (fs->free_blocks < 2) {
+ return SPIFFS_ERR_FULL;
+ }
+ }
+ res = spiffs_obj_lu_find_id(fs, starting_block, starting_lu_entry,
+ 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) + 1;
+ if (*lu_entry == 0) {
+ fs->free_blocks--;
+ }
+ }
+ if (res == SPIFFS_ERR_FULL) {
+ SPIFFS_DBG("fs full\n");
+ }
+
+ 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
+s32_t spiffs_obj_lu_find_id(
+ spiffs *fs,
+ spiffs_block_ix starting_block,
+ int starting_lu_entry,
+ spiffs_obj_id obj_id,
+ spiffs_block_ix *block_ix,
+ int *lu_entry) {
+ s32_t res = spiffs_obj_lu_find_entry_visitor(
+ fs, starting_block, starting_lu_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, lu_entry);
+ if (res == SPIFFS_VIS_END) {
+ res = SPIFFS_ERR_NOT_FOUND;
+ }
+ return res;
+}
+
+
+static s32_t spiffs_obj_lu_find_id_and_span_v(
+ spiffs *fs,
+ spiffs_obj_id obj_id,
+ spiffs_block_ix bix,
+ int ix_entry,
+ 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);
+ res = _spiffs_rd(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
+ 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_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_const_p == 0 || *((const spiffs_page_ix*)user_const_p) != pix)) {
+ return SPIFFS_OK;
+ } else {
+ return SPIFFS_VIS_COUNTINUE;
+ }
+}
+
+// Find object lookup entry containing given id and span index
+// Iterate over object lookup pages in each block until a given object id entry is found
+s32_t spiffs_obj_lu_find_id_and_span(
+ spiffs *fs,
+ spiffs_obj_id obj_id,
+ spiffs_span_ix spix,
+ spiffs_page_ix exclusion_pix,
+ spiffs_page_ix *pix) {
+ s32_t res;
+ spiffs_block_ix bix;
+ int entry;
+
+ res = spiffs_obj_lu_find_entry_visitor(fs,
+ fs->cursor_block_ix,
+ fs->cursor_obj_lu_entry,
+ SPIFFS_VIS_CHECK_ID,
+ obj_id,
+ spiffs_obj_lu_find_id_and_span_v,
+ exclusion_pix ? &exclusion_pix : 0,
+ &spix,
+ &bix,
+ &entry);
+
+ if (res == SPIFFS_VIS_END) {
+ res = SPIFFS_ERR_NOT_FOUND;
+ }
+
+ SPIFFS_CHECK_RES(res);
+
+ if (pix) {
+ *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
+ }
+
+ fs->cursor_block_ix = bix;
+ fs->cursor_obj_lu_entry = entry;
+
+ return res;
+}
+
+// Find object lookup entry containing given id and span index in page headers only
+// Iterate over object lookup pages in each block until a given object id entry is found
+s32_t spiffs_obj_lu_find_id_and_span_by_phdr(
+ spiffs *fs,
+ spiffs_obj_id obj_id,
+ spiffs_span_ix spix,
+ spiffs_page_ix exclusion_pix,
+ spiffs_page_ix *pix) {
+ s32_t res;
+ spiffs_block_ix bix;
+ int entry;
+
+ res = spiffs_obj_lu_find_entry_visitor(fs,
+ fs->cursor_block_ix,
+ fs->cursor_obj_lu_entry,
+ SPIFFS_VIS_CHECK_PH,
+ obj_id,
+ spiffs_obj_lu_find_id_and_span_v,
+ exclusion_pix ? &exclusion_pix : 0,
+ &spix,
+ &bix,
+ &entry);
+
+ if (res == SPIFFS_VIS_END) {
+ res = SPIFFS_ERR_NOT_FOUND;
+ }
+
+ SPIFFS_CHECK_RES(res);
+
+ if (pix) {
+ *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
+ }
+
+ fs->cursor_block_ix = bix;
+ fs->cursor_obj_lu_entry = entry;
+
+ 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((map->end_spix - map->start_spix + 1) - 1, (s32_t)vec_entry_start);
+ vec_entry_end = MAX((map->end_spix - map->start_spix + 1) - 1, (s32_t)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
+s32_t spiffs_page_allocate_data(
+ spiffs *fs,
+ spiffs_obj_id obj_id,
+ spiffs_page_header *ph,
+ u8_t *data,
+ u32_t len,
+ u32_t page_offs,
+ u8_t finalize,
+ spiffs_page_ix *pix) {
+ s32_t res = SPIFFS_OK;
+ spiffs_block_ix bix;
+ int entry;
+
+ // 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);
+
+ // occupy page in object lookup
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
+ 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id);
+ SPIFFS_CHECK_RES(res);
+
+ fs->stats_p_allocated++;
+
+ // write page header
+ ph->flags &= ~SPIFFS_PH_FLAG_USED;
+ 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_header), (u8_t*)ph);
+ SPIFFS_CHECK_RES(res);
+
+ // write page data
+ if (data) {
+ 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_header) + page_offs, len, data);
+ SPIFFS_CHECK_RES(res);
+ }
+
+ // finalize header if necessary
+ if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) {
+ ph->flags &= ~SPIFFS_PH_FLAG_FINAL;
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
+ 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + offsetof(spiffs_page_header, flags),
+ sizeof(u8_t),
+ (u8_t *)&ph->flags);
+ SPIFFS_CHECK_RES(res);
+ }
+
+ // return written page
+ if (pix) {
+ *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
+ }
+
+ 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(
+ spiffs *fs,
+ spiffs_file fh,
+ u8_t *page_data,
+ spiffs_obj_id obj_id,
+ spiffs_page_header *page_hdr,
+ spiffs_page_ix src_pix,
+ spiffs_page_ix *dst_pix) {
+ s32_t res;
+ u8_t was_final = 0;
+ spiffs_page_header *p_hdr;
+ spiffs_block_ix bix;
+ int entry;
+ spiffs_page_ix free_pix;
+
+ // 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);
+ free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
+
+ if (dst_pix) *dst_pix = free_pix;
+
+ p_hdr = page_data ? (spiffs_page_header *)page_data : page_hdr;
+ if (page_data) {
+ // got page data
+ was_final = (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) == 0;
+ // write unfinalized page
+ p_hdr->flags |= SPIFFS_PH_FLAG_FINAL;
+ p_hdr->flags &= ~SPIFFS_PH_FLAG_USED;
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
+ 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), page_data);
+ } else {
+ // copy page data
+ res = spiffs_phys_cpy(fs, fh, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_PAGE_TO_PADDR(fs, src_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs));
+ }
+ SPIFFS_CHECK_RES(res);
+
+ // mark entry in destination object lookup
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
+ 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix),
+ sizeof(spiffs_obj_id),
+ (u8_t *)&obj_id);
+ SPIFFS_CHECK_RES(res);
+
+ fs->stats_p_allocated++;
+
+ if (was_final) {
+ // mark finalized in destination page
+ p_hdr->flags &= ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED);
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
+ fh,
+ SPIFFS_PAGE_TO_PADDR(fs, free_pix) + offsetof(spiffs_page_header, flags),
+ sizeof(u8_t),
+ (u8_t *)&p_hdr->flags);
+ SPIFFS_CHECK_RES(res);
+ }
+ // mark source deleted
+ 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,
+ spiffs_page_ix pix) {
+ s32_t res;
+ spiffs_page_header hdr;
+ hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED);
+ // mark deleted entry in source object lookup
+ spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED;
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE,
+ 0,
+ SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_page_ix),
+ sizeof(spiffs_obj_id),
+ (u8_t *)&d_obj_id);
+ SPIFFS_CHECK_RES(res);
+
+ fs->stats_p_deleted++;
+ fs->stats_p_allocated--;
+
+ // mark deleted in source page
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE,
+ 0,
+ SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags),
+ sizeof(u8_t),
+ (u8_t *)&hdr.flags);
+
+ 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,
+ const u8_t name[],
+ const u8_t meta[],
+ spiffs_obj_type type,
+ spiffs_page_ix *objix_hdr_pix) {
+ s32_t res = SPIFFS_OK;
+ spiffs_block_ix bix;
+ spiffs_page_object_ix_header oix_hdr;
+ int entry;
+
+ res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs));
+ SPIFFS_CHECK_RES(res);
+
+ obj_id |= SPIFFS_OBJ_ID_IX_FLAG;
+
+ // 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 @ "_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,
+ 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id);
+ SPIFFS_CHECK_RES(res);
+
+ fs->stats_p_allocated++;
+
+ // write empty object index page
+ oix_hdr.p_hdr.obj_id = obj_id;
+ oix_hdr.p_hdr.span_ix = 0;
+ 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, (const char*)name, SPIFFS_OBJ_NAME_LEN);
+#if SPIFFS_OBJ_META_LEN
+ if (meta) {
+ 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, (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);
+ }
+
+ 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
+// size may be null, if so size is not changed
+s32_t spiffs_object_update_index_hdr(
+ spiffs *fs,
+ spiffs_fd *fd,
+ spiffs_obj_id obj_id,
+ spiffs_page_ix objix_hdr_pix,
+ u8_t *new_objix_hdr_data,
+ const u8_t name[],
+ const u8_t meta[],
+ u32_t size,
+ spiffs_page_ix *new_pix) {
+ s32_t res = SPIFFS_OK;
+ spiffs_page_object_ix_header *objix_hdr;
+ spiffs_page_ix new_objix_hdr_pix;
+
+ obj_id |= SPIFFS_OBJ_ID_IX_FLAG;
+
+ if (new_objix_hdr_data) {
+ // object index header page already given to us, no need to load it
+ objix_hdr = (spiffs_page_object_ix_header *)new_objix_hdr_data;
+ } else {
+ // read object index header page
+ res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
+ fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
+ SPIFFS_CHECK_RES(res);
+ objix_hdr = (spiffs_page_object_ix_header *)fs->work;
+ }
+
+ SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, obj_id, 0);
+
+ // change name
+ if (name) {
+ strncpy((char*)objix_hdr->name, (const char*)name, SPIFFS_OBJ_NAME_LEN);
+ }
+#if SPIFFS_OBJ_META_LEN
+ if (meta) {
+ memcpy(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN);
+ }
+#else
+ (void) meta;
+#endif
+ if (size) {
+ objix_hdr->size = size;
+ }
+
+ // move and update page
+ res = spiffs_page_move(fs, fd == 0 ? 0 : fd->file_nbr, (u8_t*)objix_hdr, obj_id, 0, objix_hdr_pix, &new_objix_hdr_pix);
+
+ if (res == SPIFFS_OK) {
+ if (new_pix) {
+ *new_pix = new_objix_hdr_pix;
+ }
+ // callback on object index update
+ 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_page_object_ix *objix,
+ int ev,
+ spiffs_obj_id obj_id_raw,
+ spiffs_span_ix spix,
+ spiffs_page_ix new_pix,
+ u32_t new_size) {
+#if SPIFFS_IX_MAP == 0
+ (void)objix;
+#endif
+ // update index caches in all file descriptors
+ spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG;
+ u32_t i;
+ spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
+ for (i = 0; i < fs->fd_count; i++) {
+ spiffs_fd *cur_fd = &fds[i];
+#if SPIFFS_TEMPORAL_FD_CACHE
+ if (cur_fd->score == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue;
+#else
+ if (cur_fd->file_nbr == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue;
+#endif
+ if (spix == 0) {
+ if (ev != SPIFFS_EV_IX_DEL) {
+ SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" objix_hdr_pix to "_SPIPRIpg", size:"_SPIPRIi"\n", cur_fd->file_nbr, cur_fd->obj_id, new_pix, new_size);
+ cur_fd->objix_hdr_pix = new_pix;
+ if (new_size != 0) {
+ cur_fd->size = new_size;
+ }
+ } else {
+ cur_fd->file_nbr = 0;
+ cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED;
+ }
+ }
+ if (cur_fd->cursor_objix_spix == spix) {
+ if (ev != SPIFFS_EV_IX_DEL) {
+ SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", 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;
+ }
+ }
+ }
+
+#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", 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);
+ }
+}
+
+// Open object by id
+s32_t spiffs_object_open_by_id(
+ spiffs *fs,
+ spiffs_obj_id obj_id,
+ spiffs_fd *fd,
+ spiffs_flags flags,
+ spiffs_mode mode) {
+ s32_t res = SPIFFS_OK;
+ spiffs_page_ix pix;
+
+ res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
+ SPIFFS_CHECK_RES(res);
+
+ res = spiffs_object_open_by_page(fs, pix, fd, flags, mode);
+
+ return res;
+}
+
+// Open object by page index
+s32_t spiffs_object_open_by_page(
+ spiffs *fs,
+ spiffs_page_ix pix,
+ spiffs_fd *fd,
+ spiffs_flags flags,
+ spiffs_mode mode) {
+ (void)mode;
+ s32_t res = SPIFFS_OK;
+ spiffs_page_object_ix_header oix_hdr;
+ spiffs_obj_id obj_id;
+
+ res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
+ fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr);
+ SPIFFS_CHECK_RES(res);
+
+ spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(fs, pix);
+ int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix);
+
+ res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
+ 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id);
+
+ fd->fs = fs;
+ fd->objix_hdr_pix = pix;
+ fd->size = oix_hdr.size;
+ fd->offset = 0;
+ fd->cursor_objix_pix = pix;
+ fd->cursor_objix_spix = 0;
+ fd->obj_id = obj_id;
+ fd->flags = flags;
+
+ SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0);
+
+ SPIFFS_DBG("open: fd "_SPIPRIfd" is obj id "_SPIPRIid"\n", 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) {
+ spiffs *fs = fd->fs;
+ s32_t res = SPIFFS_OK;
+ u32_t written = 0;
+
+ 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");
+ offset = fd->size;
+ }
+
+ 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 "_SPIPRIi"\n", res);
+ }
+ SPIFFS_CHECK_RES(res);
+
+ 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_page_header p_hdr;
+
+ spiffs_span_ix cur_objix_spix = 0;
+ spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1;
+ spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix;
+ spiffs_page_ix new_objix_hdr_page;
+
+ spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs);
+ spiffs_page_ix data_page;
+ u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs);
+
+ // write all data
+ while (res == SPIFFS_OK && written < len) {
+ // calculate object index page span index
+ cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
+
+ // handle storing and loading of object indices
+ if (cur_objix_spix != prev_objix_spix) {
+ // new object index page
+ // 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: "_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
+ objix_hdr->size = offset+written;
+ if (offset == 0) {
+ // was an empty object, update same page (size was 0xffffffff)
+ res = spiffs_page_index_check(fs, fd, cur_objix_pix, 0);
+ SPIFFS_CHECK_RES(res);
+ 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);
+ } 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, 0, offset+written, &new_objix_hdr_page);
+ SPIFFS_CHECK_RES(res);
+ SPIFFS_DBG("append: "_SPIPRIid" store new objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id,
+ new_objix_hdr_page, 0, written);
+ }
+ } else {
+ // this is an update to an object index page
+ res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix);
+ SPIFFS_CHECK_RES(res);
+
+ 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, (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, 0, offset+written, &new_objix_hdr_page);
+ SPIFFS_CHECK_RES(res);
+ 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;
+ fd->offset = offset+written;
+ }
+
+ // create or load new object index page
+ if (cur_objix_spix == 0) {
+ // load object index header page, must always exist
+ 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);
+ SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix);
+ } else {
+ spiffs_span_ix len_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, (fd->size-1)/SPIFFS_DATA_PAGE_SIZE(fs));
+ // on subsequent passes, create a new object index page
+ if (written > 0 || cur_objix_spix > len_objix_spix) {
+ p_hdr.obj_id = fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG;
+ p_hdr.span_ix = cur_objix_spix;
+ p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX);
+ 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);
+ // 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_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: "_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: "_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);
+ SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix);
+ cur_objix_pix = pix;
+ }
+ fd->cursor_objix_pix = cur_objix_pix;
+ fd->cursor_objix_spix = cur_objix_spix;
+ fd->offset = offset+written;
+ fd->size = offset+written;
+ }
+ prev_objix_spix = cur_objix_spix;
+ }
+
+ // write data
+ u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs);
+ if (page_offs == 0) {
+ // at beginning of a page, allocate and write a new page of data
+ p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
+ p_hdr.span_ix = data_spix;
+ 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: "_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
+ if (cur_objix_spix == 0) {
+ // get data page from object index header page
+ data_page = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix];
+ } else {
+ // get data page from object index page
+ data_page = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)];
+ }
+
+ res = spiffs_page_data_check(fs, fd, data_page, data_spix);
+ SPIFFS_CHECK_RES(res);
+
+ 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: "_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);
+ }
+
+ if (res != SPIFFS_OK) break;
+
+ // update memory representation of object index page with new data page
+ 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: "_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: "_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
+ page_offs = 0;
+ data_spix++;
+ written += to_write;
+ } // while all data
+
+ fd->size = offset+written;
+ fd->offset = offset+written;
+ fd->cursor_objix_pix = cur_objix_pix;
+ fd->cursor_objix_spix = cur_objix_spix;
+
+ // finalize updated object indices
+ s32_t res2 = SPIFFS_OK;
+ 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: "_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);
+ SPIFFS_CHECK_RES(res2);
+
+ 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, (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, 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 {
+ // wrote within object index header page
+ if (offset == 0) {
+ // wrote to empty object - simply update size and write whole page
+ objix_hdr->size = offset+written;
+ 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);
+ SPIFFS_CHECK_RES(res2);
+
+ 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);
+ // callback on object index update
+ 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, 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) {
+ spiffs *fs = fd->fs;
+ s32_t res = SPIFFS_OK;
+ u32_t written = 0;
+
+ res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs));
+ SPIFFS_CHECK_RES(res);
+
+ 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_page_header p_hdr;
+
+ spiffs_span_ix cur_objix_spix = 0;
+ spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1;
+ spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix;
+ spiffs_page_ix new_objix_hdr_pix;
+
+ spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs);
+ spiffs_page_ix data_pix;
+ u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs);
+
+
+ // write all data
+ while (res == SPIFFS_OK && written < len) {
+ // calculate object index page span index
+ cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
+
+ // handle storing and loading of object indices
+ if (cur_objix_spix != prev_objix_spix) {
+ // new object index page
+ // within this clause we return directly if something fails, object index mess-up
+ if (written > 0) {
+ // store previous object index (header) page, unless first pass
+ 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, 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
+ spiffs_page_ix new_objix_pix;
+
+ res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix);
+ 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, "_SPIPRIid":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, objix->p_hdr.span_ix, written);
+ SPIFFS_CHECK_RES(res);
+ 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 "_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);
+ SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix);
+ } else {
+ // load existing object index page on first pass
+ spiffs_page_ix pix;
+ 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 "_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);
+ SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix);
+ cur_objix_pix = pix;
+ }
+ fd->cursor_objix_pix = cur_objix_pix;
+ fd->cursor_objix_spix = cur_objix_spix;
+ fd->offset = offset+written;
+ prev_objix_spix = cur_objix_spix;
+ }
+
+ // write partial data
+ u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs);
+ spiffs_page_ix orig_data_pix;
+ if (cur_objix_spix == 0) {
+ // get data page from object index header page
+ orig_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
+ orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)];
+ }
+
+ p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
+ p_hdr.span_ix = data_spix;
+ p_hdr.flags = 0xff;
+ if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs)) {
+ // 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, "_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
+
+ res = spiffs_page_data_check(fs, fd, orig_data_pix, data_spix);
+ SPIFFS_CHECK_RES(res);
+
+ res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
+ &p_hdr, 0, 0, 0, 0, &data_pix);
+ if (res != SPIFFS_OK) break;
+
+ // copy unmodified data
+ if (page_offs > 0) {
+ // before modification
+ res = spiffs_phys_cpy(fs, fd->file_nbr,
+ SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header),
+ SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header),
+ page_offs);
+ if (res != SPIFFS_OK) break;
+ }
+ if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs)) {
+ // after modification
+ res = spiffs_phys_cpy(fs, fd->file_nbr,
+ SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs + to_write,
+ SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header) + page_offs + to_write,
+ SPIFFS_DATA_PAGE_SIZE(fs) - (page_offs + to_write));
+ if (res != SPIFFS_OK) break;
+ }
+
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
+ fd->file_nbr,
+ SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]);
+ if (res != SPIFFS_OK) break;
+ p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL;
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
+ fd->file_nbr,
+ SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags),
+ sizeof(u8_t),
+ (u8_t *)&p_hdr.flags);
+ if (res != SPIFFS_OK) break;
+
+ 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
+ res = spiffs_page_delete(fs, orig_data_pix);
+ if (res != SPIFFS_OK) break;
+ // update memory representation of object index page with new data page
+ 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 "_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 "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
+ }
+
+ // update internals
+ page_offs = 0;
+ data_spix++;
+ written += to_write;
+ } // while all data
+
+ fd->offset = offset+written;
+ fd->cursor_objix_pix = cur_objix_pix;
+ fd->cursor_objix_spix = cur_objix_spix;
+
+ // finalize updated object indices
+ s32_t res2 = SPIFFS_OK;
+ if (cur_objix_spix != 0) {
+ // wrote beyond object index header page
+ // write last modified object index page
+ // move and update page
+ spiffs_page_ix new_objix_pix;
+
+ res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix);
+ 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, "_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, (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, 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,
+ 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);
+ if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED ||
+ (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) {
+ return SPIFFS_VIS_COUNTINUE;
+ }
+ 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_header), (u8_t *)&objix_hdr);
+ SPIFFS_CHECK_RES(res);
+ 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((const char*)user_const_p, (char*)objix_hdr.name) == 0) {
+ return SPIFFS_OK;
+ }
+ }
+
+ return SPIFFS_VIS_COUNTINUE;
+}
+
+// Finds object index header page by name
+s32_t spiffs_object_find_object_index_header_by_name(
+ spiffs *fs,
+ const u8_t name[SPIFFS_OBJ_NAME_LEN],
+ spiffs_page_ix *pix) {
+ s32_t res;
+ spiffs_block_ix bix;
+ int entry;
+
+ res = spiffs_obj_lu_find_entry_visitor(fs,
+ fs->cursor_block_ix,
+ fs->cursor_obj_lu_entry,
+ 0,
+ 0,
+ spiffs_object_find_object_index_header_by_name_v,
+ name,
+ 0,
+ &bix,
+ &entry);
+
+ if (res == SPIFFS_VIS_END) {
+ res = SPIFFS_ERR_NOT_FOUND;
+ }
+ SPIFFS_CHECK_RES(res);
+
+ if (pix) {
+ *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
+ }
+
+ fs->cursor_block_ix = bix;
+ fs->cursor_obj_lu_entry = entry;
+
+ 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_full) {
+ s32_t res = SPIFFS_OK;
+ spiffs *fs = fd->fs;
+
+ 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);
+ u32_t cur_size = fd->size == (u32_t)SPIFFS_UNDEFINED_LEN ? 0 : fd->size ;
+ spiffs_span_ix cur_objix_spix = 0;
+ spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1;
+ 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_page_ix data_pix;
+ spiffs_page_ix new_objix_hdr_pix;
+
+ // before truncating, check if object is to be fully removed and mark this
+ 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),
+ sizeof(u8_t),
+ (u8_t *)&flags);
+ SPIFFS_CHECK_RES(res);
+ }
+
+ // delete from end of object until desired len is reached
+ while (cur_size > new_size) {
+ cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
+
+ // put object index for current data span index in work buffer
+ 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 "_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, (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, 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;
+ }
+ }
+ // load current object index (header) page
+ if (cur_objix_spix == 0) {
+ objix_pix = fd->objix_hdr_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("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);
+ SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix);
+ fd->cursor_objix_pix = objix_pix;
+ fd->cursor_objix_spix = cur_objix_spix;
+ fd->offset = cur_size;
+
+ 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];
+ ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = SPIFFS_OBJ_ID_FREE;
+ } 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)];
+ ((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 "_SPIPRIpg"\n", data_pix);
+
+ 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 "_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 "_SPIPRIi"\n", res);
+ break;
+ }
+ } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) {
+ res = SPIFFS_OK;
+ }
+
+ // update current size
+ if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) {
+ cur_size -= SPIFFS_DATA_PAGE_SIZE(fs);
+ } else {
+ cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs);
+ }
+ fd->size = cur_size;
+ fd->offset = 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 "_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;
+
+ p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
+ p_hdr.span_ix = data_spix;
+ p_hdr.flags = 0xff;
+ // allocate new page and copy unmodified data
+ res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
+ &p_hdr, 0, 0, 0, 0, &new_data_pix);
+ if (res != SPIFFS_OK) break;
+ res = spiffs_phys_cpy(fs, 0,
+ SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + sizeof(spiffs_page_header),
+ SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header),
+ SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove);
+ if (res != SPIFFS_OK) break;
+ // delete original data page
+ res = spiffs_page_delete(fs, data_pix);
+ if (res != SPIFFS_OK) break;
+ p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL;
+ res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
+ fd->file_nbr,
+ SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags),
+ sizeof(u8_t),
+ (u8_t *)&p_hdr.flags);
+ if (res != SPIFFS_OK) break;
+
+ // update memory representation of object index page with new data page
+ 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 "_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 "_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;
+ fd->offset = cur_size;
+ break;
+ }
+ data_spix--;
+ } // while all data
+
+ // update object indices
+ if (cur_objix_spix == 0) {
+ // update object index header page
+ if (cur_size == 0) {
+ if (remove_full) {
+ // remove object altogether
+ 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, (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 "_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, 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, 0, cur_size, &new_objix_hdr_pix);
+ SPIFFS_CHECK_RES(res);
+ }
+ } else {
+ // update both current object index page and object index header page
+ spiffs_page_ix new_objix_pix;
+
+ res = spiffs_page_index_check(fs, fd, objix_pix, cur_objix_spix);
+ SPIFFS_CHECK_RES(res);
+
+ // 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, (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, 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,
+ u32_t offset,
+ u32_t len,
+ u8_t *dst) {
+ s32_t res = SPIFFS_OK;
+ spiffs *fs = fd->fs;
+ spiffs_page_ix objix_pix;
+ spiffs_page_ix data_pix;
+ spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs);
+ u32_t cur_offset = offset;
+ spiffs_span_ix cur_objix_spix;
+ spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1;
+ 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;
+
+ while (cur_offset < offset + len) {
+#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 {
+#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:"_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;
+ }
+ res = spiffs_page_data_check(fs, fd, data_pix, data_spix);
+ SPIFFS_CHECK_RES(res);
+ res = _spiffs_rd(
+ fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ,
+ fd->file_nbr,
+ SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)),
+ len_to_read,
+ dst);
+ SPIFFS_CHECK_RES(res);
+ dst += len_to_read;
+ cur_offset += len_to_read;
+ fd->offset = cur_offset;
+ data_spix++;
+ }
+
+ return res;
+}
+
+#if !SPIFFS_READ_ONLY
+typedef struct {
+ spiffs_obj_id min_obj_id;
+ spiffs_obj_id max_obj_id;
+ u32_t compaction;
+ const u8_t *conflicting_name;
+} 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,
+ 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 = *((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)) {
+ spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry);
+ int res;
+ spiffs_page_object_ix_header objix_hdr;
+ 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_header), (u8_t *)&objix_hdr);
+ SPIFFS_CHECK_RES(res);
+ 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((const char*)user_const_p, (char*)objix_hdr.name) == 0) {
+ return SPIFFS_ERR_CONFLICTING_NAME;
+ }
+ }
+ }
+
+ id &= ~SPIFFS_OBJ_ID_IX_FLAG;
+ u32_t bit_ix = (id-min_obj_id) & 7;
+ int byte_ix = (id-min_obj_id) >> 3;
+ if (byte_ix >= 0 && (u32_t)byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs)) {
+ fs->work[byte_ix] |= (1<<bit_ix);
+ }
+ }
+ return SPIFFS_VIS_COUNTINUE;
+}
+
+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,
+ 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;
+ 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,
+ 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, ix_entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&objix_hdr);
+ if (res == SPIFFS_OK && objix_hdr.p_hdr.span_ix == 0 &&
+ ((objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) ==
+ (SPIFFS_PH_FLAG_DELET))) {
+ // ok object look up entry
+ if (state->conflicting_name && strcmp((const char *)state->conflicting_name, (char *)objix_hdr.name) == 0) {
+ return SPIFFS_ERR_CONFLICTING_NAME;
+ }
+
+ id &= ~SPIFFS_OBJ_ID_IX_FLAG;
+ 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 "_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]++;
+ }
+ }
+ }
+ return SPIFFS_VIS_COUNTINUE;
+}
+
+// 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 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 = (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;
+ state.max_obj_id = max_objects + 1;
+ if (state.max_obj_id & SPIFFS_OBJ_ID_IX_FLAG) {
+ state.max_obj_id = ((spiffs_obj_id)-1) & ~SPIFFS_OBJ_ID_IX_FLAG;
+ }
+ state.compaction = 0;
+ state.conflicting_name = conflicting_name;
+ while (res == SPIFFS_OK && free_obj_id == SPIFFS_OBJ_ID_FREE) {
+ 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:"_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,
+ 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
+ for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) {
+ u8_t mask = fs->work[i];
+ if (mask == 0xff) {
+ continue;
+ }
+ for (j = 0; j < 8; j++) {
+ if ((mask & (1<<j)) == 0) {
+ *obj_id = (i<<3)+j+state.min_obj_id;
+ return SPIFFS_OK;
+ }
+ }
+ }
+ return SPIFFS_ERR_FULL;
+ } else {
+ // not possible to represent all ids in range in a bitmap, compact and count
+ if (state.compaction != 0) {
+ // select element in compacted table, decrease range and recompact
+ u32_t i, min_i = 0;
+ u8_t *map = (u8_t *)fs->work;
+ u8_t min_count = 0xff;
+
+ for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(u8_t); i++) {
+ if (map[i] < min_count) {
+ min_count = map[i];
+ min_i = i;
+ if (min_count == 0) {
+ break;
+ }
+ }
+ }
+
+ if (min_count == state.compaction) {
+ // there are no free objids!
+ SPIFFS_DBG("free_obj_id: compacted table is full\n");
+ return SPIFFS_ERR_FULL;
+ }
+
+ 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:"_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
+ }
+ if ((state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8)) {
+ // no need for compacting, use bitmap
+ continue;
+ }
+ }
+ // 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:"_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, &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
+ }
+ }
+
+ return res;
+}
+#endif // !SPIFFS_READ_ONLY
+
+#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
+ 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++) {
+ spiffs_fd *cur_fd = &fds[i];
+ if (cur_fd->file_nbr == 0) {
+ cur_fd->file_nbr = i+1;
+ *fd = cur_fd;
+ return SPIFFS_OK;
+ }
+ }
+ return SPIFFS_ERR_OUT_OF_FILE_DESCS;
+#endif
+}
+
+s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) {
+ if (f <= 0 || f > (s16_t)fs->fd_count) {
+ return SPIFFS_ERR_BAD_DESCRIPTOR;
+ }
+ spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
+ spiffs_fd *fd = &fds[f-1];
+ if (fd->file_nbr == 0) {
+ return SPIFFS_ERR_FILE_CLOSED;
+ }
+ fd->file_nbr = 0;
+#if SPIFFS_IX_MAP
+ fd->ix_map = 0;
+#endif
+ return SPIFFS_OK;
+}
+
+s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) {
+ if (f <= 0 || f > (s16_t)fs->fd_count) {
+ return SPIFFS_ERR_BAD_DESCRIPTOR;
+ }
+ spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
+ *fd = &fds[f-1];
+ if ((*fd)->file_nbr == 0) {
+ return SPIFFS_ERR_FILE_CLOSED;
+ }
+ 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/build/esp32/sdk_build/components/spiffs/spiffs_nucleus.h b/build/esp32/sdk_build/components/spiffs/spiffs_nucleus.h
new file mode 100644
index 0000000..7d676ee
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/spiffs_nucleus.h
@@ -0,0 +1,797 @@
+/*
+ * spiffs_nucleus.h
+ *
+ * Created on: Jun 15, 2013
+ * Author: petera
+ */
+
+/* SPIFFS layout
+ *
+ * spiffs is designed for following spi flash characteristics:
+ * - only big areas of data (blocks) can be erased
+ * - erasing resets all bits in a block to ones
+ * - writing pulls ones to zeroes
+ * - zeroes cannot be pulled to ones, without erase
+ * - wear leveling
+ *
+ * spiffs is also meant to be run on embedded, memory constraint devices.
+ *
+ * Entire area is divided in blocks. Entire area is also divided in pages.
+ * Each block contains same number of pages. A page cannot be erased, but a
+ * block can be erased.
+ *
+ * Entire area must be block_size * x
+ * page_size must be block_size / (2^y) where y > 2
+ *
+ * ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes
+ *
+ * BLOCK 0 PAGE 0 object lookup 1
+ * PAGE 1 object lookup 2
+ * ...
+ * PAGE n-1 object lookup n
+ * PAGE n object data 1
+ * PAGE n+1 object data 2
+ * ...
+ * PAGE n+m-1 object data m
+ *
+ * BLOCK 1 PAGE n+m object lookup 1
+ * PAGE n+m+1 object lookup 2
+ * ...
+ * PAGE 2n+m-1 object lookup n
+ * PAGE 2n+m object data 1
+ * PAGE 2n+m object data 2
+ * ...
+ * PAGE 2n+2m-1 object data m
+ * ...
+ *
+ * n is number of object lookup pages, which is number of pages needed to index all pages
+ * in a block by object id
+ * : block_size / page_size * sizeof(obj_id) / page_size
+ * m is number data pages, which is number of pages in block minus number of lookup pages
+ * : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size
+ * thus, n+m is total number of pages in a block
+ * : block_size / page_size
+ *
+ * ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256
+ *
+ * Object lookup pages contain object id entries. Each entry represent the corresponding
+ * data page.
+ * Assuming a 16 bit object id, an object id being 0xffff represents a free page.
+ * An object id being 0x0000 represents a deleted page.
+ *
+ * ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff ..
+ * page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff ..
+ * page 2 : data : data for object id 0008
+ * page 3 : data : data for object id 0001
+ * page 4 : data : data for object id 0aaa
+ * ...
+ *
+ *
+ * Object data pages can be either object index pages or object content.
+ * All object data pages contains a data page header, containing object id and span index.
+ * The span index denotes the object page ordering amongst data pages with same object id.
+ * This applies to both object index pages (when index spans more than one page of entries),
+ * and object data pages.
+ * An object index page contains page entries pointing to object content page. The entry index
+ * in a object index page correlates to the span index in the actual object data page.
+ * The first object index page (span index 0) is called object index header page, and also
+ * contains object flags (directory/file), size, object name etc.
+ *
+ * ex:
+ * BLOCK 1
+ * PAGE 256: objectl lookup page 1
+ * [*123] [ 123] [ 123] [ 123]
+ * [ 123] [*123] [ 123] [ 123]
+ * [free] [free] [free] [free] ...
+ * PAGE 257: objectl lookup page 2
+ * [free] [free] [free] [free] ...
+ * PAGE 258: object index page (header)
+ * obj.id:0123 span.ix:0000 flags:INDEX
+ * size:1600 name:ex.txt type:file
+ * [259] [260] [261] [262]
+ * PAGE 259: object data page
+ * obj.id:0123 span.ix:0000 flags:DATA
+ * PAGE 260: object data page
+ * obj.id:0123 span.ix:0001 flags:DATA
+ * PAGE 261: object data page
+ * obj.id:0123 span.ix:0002 flags:DATA
+ * PAGE 262: object data page
+ * obj.id:0123 span.ix:0003 flags:DATA
+ * PAGE 263: object index page
+ * obj.id:0123 span.ix:0001 flags:INDEX
+ * [264] [265] [fre] [fre]
+ * [fre] [fre] [fre] [fre]
+ * PAGE 264: object data page
+ * obj.id:0123 span.ix:0004 flags:DATA
+ * PAGE 265: object data page
+ * obj.id:0123 span.ix:0005 flags:DATA
+ *
+ */
+#ifndef SPIFFS_NUCLEUS_H_
+#define SPIFFS_NUCLEUS_H_
+
+#define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1)
+#define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1)
+#define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2)
+#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)
+
+// 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)))
+
+#define SPIFFS_UNDEFINED_LEN (u32_t)(-1)
+
+#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0)
+#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1)
+
+#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)
+
+#if SPIFFS_SINGLETON == 0
+#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \
+ ((fs)->cfg.log_page_size)
+#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \
+ ((fs)->cfg.log_block_size)
+#define SPIFFS_CFG_PHYS_SZ(fs) \
+ ((fs)->cfg.phys_size)
+#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \
+ ((fs)->cfg.phys_erase_block)
+#define SPIFFS_CFG_PHYS_ADDR(fs) \
+ ((fs)->cfg.phys_addr)
+#endif
+
+// total number of pages
+#define SPIFFS_MAX_PAGES(fs) \
+ ( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) )
+// total number of pages per block, including object lookup pages
+#define SPIFFS_PAGES_PER_BLOCK(fs) \
+ ( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) )
+// number of object lookup pages per block
+#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \
+ (MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) )
+// checks if page index belongs to object lookup
+#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \
+ (((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs))
+// number of object lookup entries in all object lookup pages
+#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \
+ (SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))
+// converts a block to physical address
+#define SPIFFS_BLOCK_TO_PADDR(fs, block) \
+ ( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) )
+// converts a object lookup entry to page index
+#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \
+ ((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry))
+// converts a object lookup entry to physical address of corresponding page
+#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \
+ (SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) )
+// converts a page to physical address
+#define SPIFFS_PAGE_TO_PADDR(fs, page) \
+ ( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) )
+// converts a physical address to page
+#define SPIFFS_PADDR_TO_PAGE(fs, addr) \
+ ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) )
+// gives index in page for a physical address
+#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \
+ ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) )
+// returns containing block for given page
+#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \
+ ( (page) / SPIFFS_PAGES_PER_BLOCK(fs) )
+// returns starting page for block
+#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \
+ ( (block) * SPIFFS_PAGES_PER_BLOCK(fs) )
+// converts page to entry in object lookup page
+#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \
+ ( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) )
+// returns data size in a data page
+#define SPIFFS_DATA_PAGE_SIZE(fs) \
+ ( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) )
+// returns physical address for block's erase count,
+// always in the physical last entry of the last object lookup page
+#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \
+ ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) )
+// returns physical address for block's magic,
+// always in the physical second last entry of the last object lookup page
+#define SPIFFS_MAGIC_PADDR(fs, bix) \
+ ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 )
+// checks if there is any room for magic in the object luts
+#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \
+ ( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \
+ <= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) )
+
+// define helpers object
+
+// entries in an object header page index
+#define SPIFFS_OBJ_HDR_IX_LEN(fs) \
+ ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix))
+// entries in an object page index
+#define SPIFFS_OBJ_IX_LEN(fs) \
+ ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix))
+// object index entry for given data span index
+#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \
+ ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs)))
+// 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))) )
+
+#define SPIFFS_OP_T_OBJ_LU (0<<0)
+#define SPIFFS_OP_T_OBJ_LU2 (1<<0)
+#define SPIFFS_OP_T_OBJ_IX (2<<0)
+#define SPIFFS_OP_T_OBJ_DA (3<<0)
+#define SPIFFS_OP_C_DELE (0<<2)
+#define SPIFFS_OP_C_UPDT (1<<2)
+#define SPIFFS_OP_C_MOVS (2<<2)
+#define SPIFFS_OP_C_MOVD (3<<2)
+#define SPIFFS_OP_C_FLSH (4<<2)
+#define SPIFFS_OP_C_READ (5<<2)
+#define SPIFFS_OP_C_WRTHRU (6<<2)
+
+#define SPIFFS_OP_TYPE_MASK (3<<0)
+#define SPIFFS_OP_COM_MASK (7<<2)
+
+
+// if 0, this page is written to, else clean
+#define SPIFFS_PH_FLAG_USED (1<<0)
+// if 0, writing is finalized, else under modification
+#define SPIFFS_PH_FLAG_FINAL (1<<1)
+// if 0, this is an index page, else a data page
+#define SPIFFS_PH_FLAG_INDEX (1<<2)
+// if 0, page is deleted, else valid
+#define SPIFFS_PH_FLAG_DELET (1<<7)
+// if 0, this index header is being deleted
+#define SPIFFS_PH_FLAG_IXDELE (1<<6)
+
+
+#define SPIFFS_CHECK_MOUNT(fs) \
+ ((fs)->mounted != 0)
+
+#define SPIFFS_CHECK_CFG(fs) \
+ ((fs)->config_magic == SPIFFS_CONFIG_MAGIC)
+
+#define SPIFFS_CHECK_RES(res) \
+ do { \
+ if ((res) < SPIFFS_OK) return (res); \
+ } while (0);
+
+#define SPIFFS_API_CHECK_MOUNT(fs) \
+ if (!SPIFFS_CHECK_MOUNT((fs))) { \
+ (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \
+ return SPIFFS_ERR_NOT_MOUNTED; \
+ }
+
+#define SPIFFS_API_CHECK_CFG(fs) \
+ if (!SPIFFS_CHECK_CFG((fs))) { \
+ (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \
+ return SPIFFS_ERR_NOT_CONFIGURED; \
+ }
+
+#define SPIFFS_API_CHECK_RES(fs, res) \
+ if ((res) < SPIFFS_OK) { \
+ (fs)->err_code = (res); \
+ return (res); \
+ }
+
+#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
+ if ((res) < SPIFFS_OK) { \
+ (fs)->err_code = (res); \
+ SPIFFS_UNLOCK(fs); \
+ return (res); \
+ }
+
+#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \
+ if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
+ if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
+ if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
+ if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \
+ if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \
+ if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH;
+ //if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED;
+
+#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \
+ if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
+ if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
+ if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
+ if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \
+ if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \
+ if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH;
+
+
+// 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)
+#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1)
+#define SPIFFS_CACHE_FLAG_OBJLU (1<<2)
+#define SPIFFS_CACHE_FLAG_OBJIX (1<<3)
+#define SPIFFS_CACHE_FLAG_DATA (1<<4)
+#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7)
+
+#define SPIFFS_CACHE_PAGE_SIZE(fs) \
+ (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs))
+
+#define spiffs_get_cache(fs) \
+ ((spiffs_cache *)((fs)->cache))
+
+#define spiffs_get_cache_page_hdr(fs, c, ix) \
+ ((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])))
+
+#define spiffs_get_cache_page(fs, c, ix) \
+ ((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page))
+
+// cache page struct
+typedef struct {
+ // cache flags
+ u8_t flags;
+ // cache page index
+ u8_t ix;
+ // last access of this cache page
+ u32_t last_access;
+ union {
+ // type read cache
+ struct {
+ // read cache page index
+ spiffs_page_ix pix;
+ };
+#if SPIFFS_CACHE_WR
+ // type write cache
+ struct {
+ // write cache
+ spiffs_obj_id obj_id;
+ // offset in cache page
+ u32_t offset;
+ // size of cache page
+ u16_t size;
+ };
+#endif
+ };
+} spiffs_cache_page;
+
+// cache struct
+typedef struct {
+ u8_t cpage_count;
+ u32_t last_access;
+ u32_t cpage_use_map;
+ u32_t cpage_use_mask;
+ u8_t *cpages;
+} spiffs_cache;
+
+#endif
+
+
+// spiffs nucleus file descriptor
+typedef struct {
+ // the filesystem of this descriptor
+ spiffs *fs;
+ // number of file descriptor - if 0, the file descriptor is closed
+ spiffs_file file_nbr;
+ // object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted
+ spiffs_obj_id obj_id;
+ // size of the file
+ u32_t size;
+ // cached object index header page index
+ spiffs_page_ix objix_hdr_pix;
+ // cached offset object index page index
+ spiffs_page_ix cursor_objix_pix;
+ // cached offset object index span index
+ spiffs_span_ix cursor_objix_spix;
+ // current absolute offset
+ u32_t offset;
+ // current file descriptor offset
+ 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;
+
+
+// object structs
+
+// page header, part of each page except object lookup pages
+// NB: this is always aligned when the data page is an object index,
+// as in this case struct spiffs_page_object_ix is used
+typedef struct __attribute(( packed )) {
+ // object id
+ spiffs_obj_id obj_id;
+ // object span index
+ spiffs_span_ix span_ix;
+ // flags
+ u8_t flags;
+} spiffs_page_header;
+
+// object index header page header
+typedef struct __attribute(( packed ))
+#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
+ __attribute(( aligned(sizeof(spiffs_page_ix)) ))
+#endif
+{
+ // common page header
+ spiffs_page_header p_hdr;
+ // alignment
+ u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
+ // size of object
+ u32_t size;
+ // type of object
+ 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 )) {
+ spiffs_page_header p_hdr;
+ u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
+} spiffs_page_object_ix;
+
+// callback func for object lookup visitor
+typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
+ const void *user_const_p, void *user_var_p);
+
+
+#if SPIFFS_CACHE
+#define _spiffs_rd(fs, op, fh, addr, len, dst) \
+ spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst))
+#define _spiffs_wr(fs, op, fh, addr, len, src) \
+ spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src))
+#else
+#define _spiffs_rd(fs, op, fh, addr, len, dst) \
+ spiffs_phys_rd((fs), (addr), (len), (dst))
+#define _spiffs_wr(fs, op, fh, addr, len, src) \
+ spiffs_phys_wr((fs), (addr), (len), (src))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+// ---------------
+
+s32_t spiffs_phys_rd(
+ spiffs *fs,
+#if SPIFFS_CACHE
+ u8_t op,
+ spiffs_file fh,
+#endif
+ u32_t addr,
+ u32_t len,
+ u8_t *dst);
+
+s32_t spiffs_phys_wr(
+ spiffs *fs,
+#if SPIFFS_CACHE
+ u8_t op,
+ spiffs_file fh,
+#endif
+ u32_t addr,
+ u32_t len,
+ u8_t *src);
+
+s32_t spiffs_phys_cpy(
+ spiffs *fs,
+ spiffs_file fh,
+ u32_t dst,
+ u32_t src,
+ u32_t len);
+
+s32_t spiffs_phys_count_free_blocks(
+ spiffs *fs);
+
+s32_t spiffs_obj_lu_find_entry_visitor(
+ spiffs *fs,
+ spiffs_block_ix starting_block,
+ int starting_lu_entry,
+ u8_t flags,
+ spiffs_obj_id obj_id,
+ spiffs_visitor_f v,
+ const void *user_const_p,
+ void *user_var_p,
+ spiffs_block_ix *block_ix,
+ int *lu_entry);
+
+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(
+ spiffs *fs);
+
+s32_t spiffs_obj_lu_find_free_obj_id(
+ spiffs *fs,
+ spiffs_obj_id *obj_id,
+ const u8_t *conflicting_name);
+
+s32_t spiffs_obj_lu_find_free(
+ spiffs *fs,
+ spiffs_block_ix starting_block,
+ int starting_lu_entry,
+ spiffs_block_ix *block_ix,
+ int *lu_entry);
+
+s32_t spiffs_obj_lu_find_id(
+ spiffs *fs,
+ spiffs_block_ix starting_block,
+ int starting_lu_entry,
+ spiffs_obj_id obj_id,
+ spiffs_block_ix *block_ix,
+ int *lu_entry);
+
+s32_t spiffs_obj_lu_find_id_and_span(
+ spiffs *fs,
+ spiffs_obj_id obj_id,
+ spiffs_span_ix spix,
+ spiffs_page_ix exclusion_pix,
+ spiffs_page_ix *pix);
+
+s32_t spiffs_obj_lu_find_id_and_span_by_phdr(
+ spiffs *fs,
+ spiffs_obj_id obj_id,
+ spiffs_span_ix spix,
+ spiffs_page_ix exclusion_pix,
+ spiffs_page_ix *pix);
+
+// ---------------
+
+s32_t spiffs_page_allocate_data(
+ spiffs *fs,
+ spiffs_obj_id obj_id,
+ spiffs_page_header *ph,
+ u8_t *data,
+ u32_t len,
+ u32_t page_offs,
+ u8_t finalize,
+ spiffs_page_ix *pix);
+
+s32_t spiffs_page_move(
+ spiffs *fs,
+ spiffs_file fh,
+ u8_t *page_data,
+ spiffs_obj_id obj_id,
+ spiffs_page_header *page_hdr,
+ spiffs_page_ix src_pix,
+ spiffs_page_ix *dst_pix);
+
+s32_t spiffs_page_delete(
+ spiffs *fs,
+ spiffs_page_ix pix);
+
+// ---------------
+
+s32_t spiffs_object_create(
+ spiffs *fs,
+ spiffs_obj_id obj_id,
+ const u8_t name[],
+ const u8_t meta[],
+ spiffs_obj_type type,
+ spiffs_page_ix *objix_hdr_pix);
+
+s32_t spiffs_object_update_index_hdr(
+ spiffs *fs,
+ spiffs_fd *fd,
+ spiffs_obj_id obj_id,
+ spiffs_page_ix objix_hdr_pix,
+ u8_t *new_objix_hdr_data,
+ const u8_t name[],
+ const u8_t meta[],
+ u32_t size,
+ spiffs_page_ix *new_pix);
+
+#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,
+ spiffs_page_ix new_pix,
+ u32_t new_size);
+
+s32_t spiffs_object_open_by_id(
+ spiffs *fs,
+ spiffs_obj_id obj_id,
+ spiffs_fd *f,
+ spiffs_flags flags,
+ spiffs_mode mode);
+
+s32_t spiffs_object_open_by_page(
+ spiffs *fs,
+ spiffs_page_ix pix,
+ spiffs_fd *f,
+ spiffs_flags flags,
+ spiffs_mode mode);
+
+s32_t spiffs_object_append(
+ spiffs_fd *fd,
+ u32_t offset,
+ u8_t *data,
+ u32_t len);
+
+s32_t spiffs_object_modify(
+ spiffs_fd *fd,
+ u32_t offset,
+ u8_t *data,
+ u32_t len);
+
+s32_t spiffs_object_read(
+ spiffs_fd *fd,
+ u32_t offset,
+ u32_t len,
+ u8_t *dst);
+
+s32_t spiffs_object_truncate(
+ spiffs_fd *fd,
+ u32_t new_len,
+ u8_t remove_object);
+
+s32_t spiffs_object_find_object_index_header_by_name(
+ spiffs *fs,
+ const u8_t name[SPIFFS_OBJ_NAME_LEN],
+ spiffs_page_ix *pix);
+
+// ---------------
+
+s32_t spiffs_gc_check(
+ spiffs *fs,
+ u32_t len);
+
+s32_t spiffs_gc_erase_page_stats(
+ spiffs *fs,
+ spiffs_block_ix bix);
+
+s32_t spiffs_gc_find_candidate(
+ spiffs *fs,
+ spiffs_block_ix **block_candidate,
+ int *candidate_count,
+ char fs_crammed);
+
+s32_t spiffs_gc_clean(
+ spiffs *fs,
+ spiffs_block_ix bix);
+
+s32_t spiffs_gc_quick(
+ spiffs *fs, u16_t max_free_pages);
+
+// ---------------
+
+s32_t spiffs_fd_find_new(
+ spiffs *fs,
+ spiffs_fd **fd,
+ const char *name);
+
+s32_t spiffs_fd_return(
+ spiffs *fs,
+ spiffs_file f);
+
+s32_t spiffs_fd_get(
+ spiffs *fs,
+ 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);
+
+void spiffs_cache_drop_page(
+ spiffs *fs,
+ spiffs_page_ix pix);
+
+#if SPIFFS_CACHE_WR
+spiffs_cache_page *spiffs_cache_page_allocate_by_fd(
+ spiffs *fs,
+ spiffs_fd *fd);
+
+void spiffs_cache_fd_release(
+ spiffs *fs,
+ spiffs_cache_page *cp);
+
+spiffs_cache_page *spiffs_cache_page_get_by_fd(
+ spiffs *fs,
+ spiffs_fd *fd);
+#endif
+#endif
+
+s32_t spiffs_lookup_consistency_check(
+ spiffs *fs,
+ u8_t check_all_objects);
+
+s32_t spiffs_page_consistency_check(
+ spiffs *fs);
+
+s32_t spiffs_object_index_consistency_check(
+ spiffs *fs);
+
+#endif /* SPIFFS_NUCLEUS_H_ */
diff --git a/build/esp32/sdk_build/components/spiffs/spiffs_vfs.c b/build/esp32/sdk_build/components/spiffs/spiffs_vfs.c
new file mode 100644
index 0000000..b47d4a0
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/spiffs_vfs.c
@@ -0,0 +1,866 @@
+/*
+ * spiffs VFS operations
+ *
+ * Author: LoBo (loboris@gmail.com / https://github.com/loboris)
+ *
+ * Part of this code is copied from or inspired by LUA-RTOS_ESP32 project:
+ *
+ * https://github.com/whitecatboard/Lua-RTOS-ESP32
+ * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
+ * Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
+ *
+ */
+
+
+#include <freertos/FreeRTOS.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include "esp_log.h"
+
+#include <sys/stat.h>
+
+#include "esp_vfs.h"
+#include "esp_attr.h"
+#include <errno.h>
+
+#include <spiffs.h>
+#include <esp_spiffs.h>
+#include <spiffs_nucleus.h>
+#include "list.h"
+#include <sys/fcntl.h>
+#include <sys/dirent.h>
+#include "sdkconfig.h"
+
+
+#ifdef PATH_MAX
+#undef PATH_MAX
+#endif
+#define PATH_MAX MAXNAMLEN+8
+
+#define SPIFFS_ERASE_SIZE 4096
+#define SPIFFS_LOG_PAGE_SIZE 256
+
+int spiffs_is_registered = 0;
+int spiffs_is_mounted = 0;
+
+static int IRAM_ATTR vfs_spiffs_open(const char *path, int flags, int mode);
+static size_t IRAM_ATTR vfs_spiffs_write(int fd, const void *data, size_t size);
+static ssize_t IRAM_ATTR vfs_spiffs_read(int fd, void * dst, size_t size);
+static int IRAM_ATTR vfs_spiffs_fstat(int fd, struct stat * st);
+static int IRAM_ATTR vfs_spiffs_close(int fd);
+static off_t IRAM_ATTR vfs_spiffs_lseek(int fd, off_t size, int mode);
+
+typedef struct {
+ DIR dir;
+ spiffs_DIR spiffs_dir;
+ char path[MAXNAMLEN + 1];
+ struct dirent ent;
+ uint8_t read_mount;
+} vfs_spiffs_dir_t;
+
+typedef struct {
+ spiffs_file spiffs_file;
+ char path[MAXNAMLEN + 1];
+ uint8_t is_dir;
+} vfs_spiffs_file_t;
+
+typedef struct {
+ time_t mtime;
+ time_t ctime;
+ time_t atime;
+ uint8_t spare[SPIFFS_OBJ_META_LEN - (sizeof(time_t)*3)];
+} spiffs_metadata_t;
+
+static spiffs fs;
+static struct list files;
+
+static u8_t *my_spiffs_work_buf;
+static u8_t *my_spiffs_fds;
+static u8_t *my_spiffs_cache;
+
+
+/*
+ * ########################################
+ * file names/paths passed to the functions
+ * do not contain '/spiffs' prefix
+ * ########################################
+ */
+
+//----------------------------------------------------
+void spiffs_fs_stat(uint32_t *total, uint32_t *used) {
+ if (SPIFFS_info(&fs, total, used) != SPIFFS_OK) {
+ *total = 0;
+ *used = 0;
+ }
+}
+
+/*
+ * Test if path corresponds to a directory. Return 0 if is not a directory,
+ * 1 if it's a directory.
+ *
+ */
+//-----------------------------------
+static int is_dir(const char *path) {
+ spiffs_DIR d;
+ char npath[PATH_MAX + 1];
+ int res = 0;
+
+ struct spiffs_dirent e;
+
+ // Add /. to path
+ strlcpy(npath, path, PATH_MAX);
+ if (strcmp(path,"/") != 0) {
+ strlcat(npath,"/.", PATH_MAX);
+ } else {
+ strlcat(npath,".", PATH_MAX);
+ }
+
+ SPIFFS_opendir(&fs, "/", &d);
+ while (SPIFFS_readdir(&d, &e)) {
+ if (strncmp(npath, (const char *)e.name, strlen(npath)) == 0) {
+ res = 1;
+ break;
+ }
+ }
+
+ SPIFFS_closedir(&d);
+
+ return res;
+}
+
+/*
+ * This function translate error codes from SPIFFS to errno error codes
+ *
+ */
+//-------------------------------
+static int spiffs_result(int res) {
+ switch (res) {
+ case SPIFFS_OK:
+ case SPIFFS_ERR_END_OF_OBJECT:
+ return 0;
+
+ case SPIFFS_ERR_NOT_FOUND:
+ case SPIFFS_ERR_CONFLICTING_NAME:
+ return ENOENT;
+
+ case SPIFFS_ERR_NOT_WRITABLE:
+ case SPIFFS_ERR_NOT_READABLE:
+ return EACCES;
+
+ case SPIFFS_ERR_FILE_EXISTS:
+ return EEXIST;
+
+ default:
+ return res;
+ }
+}
+
+//-----------------------------------------------------------------------------------------------------
+static int IRAM_ATTR vfs_spiffs_getstat(spiffs_file fd, spiffs_stat *st, spiffs_metadata_t *metadata) {
+ int res = SPIFFS_fstat(&fs, fd, st);
+ if (res == SPIFFS_OK) {
+ // Get file's time information from metadata
+ memcpy(metadata, st->meta, sizeof(spiffs_metadata_t));
+ }
+ return res;
+}
+
+// ## path does not contain '/spiffs' prefix !
+//---------------------------------------------------------------------------
+static int IRAM_ATTR vfs_spiffs_open(const char *path, int flags, int mode) {
+ int fd, result = 0, exists = 0;
+ spiffs_stat stat;
+ spiffs_metadata_t meta;
+
+ // Allocate new file
+ vfs_spiffs_file_t *file = calloc(1, sizeof(vfs_spiffs_file_t));
+ if (!file) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ // Add file to file list. List index is file descriptor.
+ int res = list_add(&files, file, &fd);
+ if (res) {
+ free(file);
+ errno = res;
+ return -1;
+ }
+
+ // Check if file exists
+ if (SPIFFS_stat(&fs, path, &stat) == SPIFFS_OK) exists = 1;
+
+ // Make a copy of path
+ strlcpy(file->path, path, MAXNAMLEN);
+
+ // Open file
+ spiffs_flags spiffs_mode = 0;
+
+ // Translate flags to SPIFFS flags
+ if (flags == O_RDONLY)
+ spiffs_mode |= SPIFFS_RDONLY;
+
+ if (flags & O_WRONLY)
+ spiffs_mode |= SPIFFS_WRONLY;
+
+ if (flags & O_RDWR)
+ spiffs_mode = SPIFFS_RDWR;
+
+ if (flags & O_EXCL)
+ spiffs_mode |= SPIFFS_EXCL;
+
+ if (flags & O_CREAT)
+ spiffs_mode |= SPIFFS_CREAT;
+
+ if (flags & O_TRUNC)
+ spiffs_mode |= SPIFFS_TRUNC;
+
+ if (is_dir(path)) {
+ char npath[PATH_MAX + 1];
+
+ // Add /. to path
+ strlcpy(npath, path, PATH_MAX);
+ if (strcmp(path,"/") != 0) {
+ strlcat(npath,"/.", PATH_MAX);
+ } else {
+ strlcat(npath,".", PATH_MAX);
+ }
+
+ // Open SPIFFS file
+ file->spiffs_file = SPIFFS_open(&fs, npath, spiffs_mode, 0);
+ if (file->spiffs_file < 0) {
+ result = spiffs_result(fs.err_code);
+ }
+
+ file->is_dir = 1;
+ } else {
+ // Open SPIFFS file
+ file->spiffs_file = SPIFFS_open(&fs, path, spiffs_mode, 0);
+ if (file->spiffs_file < 0) {
+ result = spiffs_result(fs.err_code);
+ }
+ }
+
+ if (result != 0) {
+ list_remove(&files, fd, 1);
+ errno = result;
+ return -1;
+ }
+
+ res = vfs_spiffs_getstat(file->spiffs_file, &stat, &meta);
+ if (res == SPIFFS_OK) {
+ // update file's time information
+ meta.atime = time(NULL); // Get the system time to access time
+ if (!exists) meta.ctime = meta.atime;
+ if (spiffs_mode != SPIFFS_RDONLY) meta.mtime = meta.atime;
+ SPIFFS_fupdate_meta(&fs, file->spiffs_file, &meta);
+ }
+
+ return fd;
+}
+
+//-------------------------------------------------------------------------------
+static size_t IRAM_ATTR vfs_spiffs_write(int fd, const void *data, size_t size) {
+ vfs_spiffs_file_t *file;
+ int res;
+
+ res = list_get(&files, fd, (void **)&file);
+ if (res) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (file->is_dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ // Write SPIFFS file
+ res = SPIFFS_write(&fs, file->spiffs_file, (void *)data, size);
+ if (res >= 0) {
+ return res;
+ } else {
+ res = spiffs_result(fs.err_code);
+ if (res != 0) {
+ errno = res;
+ return -1;
+ }
+ }
+
+ return -1;
+}
+
+//-------------------------------------------------------------------------
+static ssize_t IRAM_ATTR vfs_spiffs_read(int fd, void * dst, size_t size) {
+ vfs_spiffs_file_t *file;
+ int res;
+
+ res = list_get(&files, fd, (void **)&file);
+ if (res) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (file->is_dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ // Read SPIFFS file
+ res = SPIFFS_read(&fs, file->spiffs_file, dst, size);
+ if (res >= 0) {
+ return res;
+ } else {
+ res = spiffs_result(fs.err_code);
+ if (res != 0) {
+ errno = res;
+ return -1;
+ }
+
+ // EOF
+ return 0;
+ }
+
+ return -1;
+}
+
+//---------------------------------------------------------------
+static int IRAM_ATTR vfs_spiffs_fstat(int fd, struct stat * st) {
+ vfs_spiffs_file_t *file;
+ spiffs_stat stat;
+ int res;
+ spiffs_metadata_t meta;
+
+ res = list_get(&files, fd, (void **)&file);
+ if (res) {
+ errno = EBADF;
+ return -1;
+ }
+
+ // Set block size for this file system
+ st->st_blksize = SPIFFS_LOG_PAGE_SIZE;
+
+ // Get file/directory statistics
+ res = vfs_spiffs_getstat(file->spiffs_file, &stat, &meta);
+ if (res == SPIFFS_OK) {
+ // Set file's time information from metadata
+ st->st_mtime = meta.mtime;
+ st->st_ctime = meta.ctime;
+ st->st_atime = meta.atime;
+
+ st->st_size = stat.size;
+
+ } else {
+ st->st_mtime = 0;
+ st->st_ctime = 0;
+ st->st_atime = 0;
+ st->st_size = 0;
+ errno = spiffs_result(fs.err_code);
+ //printf("SPIFFS_STAT: error %d\r\n", res);
+ return -1;
+ }
+
+ // Test if it's a directory entry
+ if (file->is_dir) st->st_mode = S_IFDIR;
+ else st->st_mode = S_IFREG;
+
+ return 0;
+}
+
+//---------------------------------------------
+static int IRAM_ATTR vfs_spiffs_close(int fd) {
+ vfs_spiffs_file_t *file;
+ int res;
+
+ res = list_get(&files, fd, (void **)&file);
+ if (res) {
+ errno = EBADF;
+ return -1;
+ }
+
+ res = SPIFFS_close(&fs, file->spiffs_file);
+ if (res) {
+ res = spiffs_result(fs.err_code);
+ }
+
+ if (res < 0) {
+ errno = res;
+ return -1;
+ }
+
+ list_remove(&files, fd, 1);
+
+ return 0;
+}
+
+//---------------------------------------------------------------------
+static off_t IRAM_ATTR vfs_spiffs_lseek(int fd, off_t size, int mode) {
+ vfs_spiffs_file_t *file;
+ int res;
+
+ res = list_get(&files, fd, (void **)&file);
+ if (res) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (file->is_dir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ int whence = SPIFFS_SEEK_CUR;
+
+ switch (mode) {
+ case SEEK_SET: whence = SPIFFS_SEEK_SET;break;
+ case SEEK_CUR: whence = SPIFFS_SEEK_CUR;break;
+ case SEEK_END: whence = SPIFFS_SEEK_END;break;
+ }
+
+ res = SPIFFS_lseek(&fs, file->spiffs_file, size, whence);
+ if (res < 0) {
+ res = spiffs_result(fs.err_code);
+ errno = res;
+ return -1;
+ }
+
+ return res;
+}
+
+//-------------------------------------------------------------------------
+static int IRAM_ATTR vfs_spiffs_stat(const char * path, struct stat * st) {
+ int fd;
+ int res;
+ fd = vfs_spiffs_open(path, 0, 0);
+ res = vfs_spiffs_fstat(fd, st);
+ vfs_spiffs_close(fd);
+
+ return res;
+}
+
+//--------------------------------------------------------
+static int IRAM_ATTR vfs_spiffs_unlink(const char *path) {
+ char npath[PATH_MAX + 1];
+
+ strlcpy(npath, path, PATH_MAX);
+
+ if (is_dir(path)) {
+ // Check if directory is empty
+ int nument = 0;
+ sprintf(npath, "/spiffs");
+ strlcat(npath, path, PATH_MAX);
+
+ DIR *dir = opendir(npath);
+ if (dir) {
+ struct dirent *ent;
+ // Read directory entries
+ while ((ent = readdir(dir)) != NULL) {
+ nument++;
+ }
+ }
+ else {
+ errno = ENOTEMPTY;
+ return -1;
+ }
+ closedir(dir);
+
+ if (nument > 0) {
+ // Directory not empty, cannot remove
+ errno = ENOTEMPTY;
+ return -1;
+ }
+
+ strlcpy(npath, path, PATH_MAX);
+ // Add /. to path
+ if (strcmp(path,"/") != 0) {
+ strlcat(npath,"/.", PATH_MAX);
+ }
+ }
+
+ // Open SPIFFS file
+ spiffs_file FP = SPIFFS_open(&fs, npath, SPIFFS_RDWR, 0);
+ if (FP < 0) {
+ errno = spiffs_result(fs.err_code);
+ return -1;
+ }
+
+ // Remove SPIFSS file
+ if (SPIFFS_fremove(&fs, FP) < 0) {
+ errno = spiffs_result(fs.err_code);
+ SPIFFS_close(&fs, FP);
+ return -1;
+ }
+
+ SPIFFS_close(&fs, FP);
+
+ return 0;
+}
+
+//------------------------------------------------------------------------
+static int IRAM_ATTR vfs_spiffs_rename(const char *src, const char *dst) {
+ if (SPIFFS_rename(&fs, src, dst) < 0) {
+ errno = spiffs_result(fs.err_code);
+ return -1;
+ }
+
+ return 0;
+}
+
+//------------------------------------------------
+static DIR* vfs_spiffs_opendir(const char* name) {
+ struct stat st;
+
+ if (strcmp(name, "/") != 0) {
+ // Not on root
+ if (vfs_spiffs_stat(name, &st)) {
+ // Not found
+ errno = ENOENT;
+ return NULL;
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ // Not a directory
+ errno = ENOTDIR;
+ return NULL;
+ }
+ }
+
+ vfs_spiffs_dir_t *dir = calloc(1, sizeof(vfs_spiffs_dir_t));
+
+ if (!dir) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ if (!SPIFFS_opendir(&fs, name, &dir->spiffs_dir)) {
+ free(dir);
+ errno = spiffs_result(fs.err_code);
+ return NULL;
+ }
+
+ strlcpy(dir->path, name, MAXNAMLEN);
+
+ return (DIR *)dir;
+}
+
+//---------------------------------------------------
+static struct dirent* vfs_spiffs_readdir(DIR* pdir) {
+ int res = 0, len = 0, entries = 0;
+ vfs_spiffs_dir_t* dir = (vfs_spiffs_dir_t*) pdir;
+
+ struct spiffs_dirent e;
+ struct spiffs_dirent *pe = &e;
+
+ struct dirent *ent = &dir->ent;
+
+ char *fn;
+
+ // Clear current dirent
+ memset(ent,0,sizeof(struct dirent));
+
+ // If this is the first call to readdir for pdir, and
+ // directory is the root path, return the mounted point if any
+ if (!dir->read_mount) {
+ if (strcmp(dir->path,"/") == 0) {
+ strlcpy(ent->d_name, "/spiffs", PATH_MAX);
+ ent->d_type = DT_DIR;
+ dir->read_mount = 1;
+
+ return ent;
+ }
+
+ dir->read_mount = 1;
+ }
+
+ // Search for next entry
+ for(;;) {
+ // Read directory
+ pe = SPIFFS_readdir(&dir->spiffs_dir, pe);
+ if (!pe) {
+ res = spiffs_result(fs.err_code);
+ errno = res;
+ break;
+ }
+
+ // Break condition
+ if (pe->name[0] == 0) break;
+
+ // Get name and length
+ fn = (char *)pe->name;
+ len = strlen(fn);
+
+ // Get entry type and size
+ ent->d_type = DT_REG;
+
+ if (len >= 2) {
+ if (fn[len - 1] == '.') {
+ if (fn[len - 2] == '/') {
+ ent->d_type = DT_DIR;
+
+ fn[len - 2] = '\0';
+
+ len = strlen(fn);
+
+ // Skip root dir
+ if (len == 0) {
+ continue;
+ }
+ }
+ }
+ }
+
+ // Skip entries not belonged to path
+ if (strncmp(fn, dir->path, strlen(dir->path)) != 0) {
+ continue;
+ }
+
+ if (strlen(dir->path) > 1) {
+ if (*(fn + strlen(dir->path)) != '/') {
+ continue;
+ }
+ }
+
+ // Skip root directory
+ fn = fn + strlen(dir->path);
+ len = strlen(fn);
+ if (len == 0) {
+ continue;
+ }
+
+ // Skip initial /
+ if (len > 1) {
+ if (*fn == '/') {
+ fn = fn + 1;
+ len--;
+ }
+ }
+
+ // Skip subdirectories
+ if (strchr(fn,'/')) {
+ continue;
+ }
+
+ //ent->d_fsize = pe->size;
+
+ strlcpy(ent->d_name, fn, MAXNAMLEN);
+
+ entries++;
+
+ break;
+ }
+
+ if (entries > 0) {
+ return ent;
+ } else {
+ return NULL;
+ }
+}
+
+//--------------------------------------------------
+static int IRAM_ATTR vfs_piffs_closedir(DIR* pdir) {
+ vfs_spiffs_dir_t* dir = (vfs_spiffs_dir_t*) pdir;
+ int res;
+
+ if (!pdir) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if ((res = SPIFFS_closedir(&dir->spiffs_dir)) < 0) {
+ errno = spiffs_result(fs.err_code);;
+ return -1;
+ }
+
+ free(dir);
+
+ return 0;
+}
+
+//--------------------------------------------------------------------
+static int IRAM_ATTR vfs_spiffs_mkdir(const char *path, mode_t mode) {
+ char npath[PATH_MAX + 1];
+ int res;
+
+ // Add /. to path
+ strlcpy(npath, path, PATH_MAX);
+ if ((strcmp(path,"/") != 0) && (strcmp(path,"/.") != 0)) {
+ strlcat(npath,"/.", PATH_MAX);
+ }
+
+ spiffs_file fd = SPIFFS_open(&fs, npath, SPIFFS_CREAT, 0);
+ if (fd < 0) {
+ res = spiffs_result(fs.err_code);
+ errno = res;
+ return -1;
+ }
+
+ if (SPIFFS_close(&fs, fd) < 0) {
+ res = spiffs_result(fs.err_code);
+ errno = res;
+ return -1;
+ }
+
+ spiffs_metadata_t meta;
+ meta.atime = time(NULL); // Get the system time to access time
+ meta.ctime = meta.atime;
+ meta.mtime = meta.atime;
+ SPIFFS_update_meta(&fs, npath, &meta);
+
+ return 0;
+}
+
+
+static const char tag[] = "[SPIFFS]";
+
+//==================
+int spiffs_mount() {
+
+ if (!spiffs_is_registered) return 0;
+ if (spiffs_is_mounted) return 1;
+
+ spiffs_config cfg;
+ int res = 0;
+ int retries = 0;
+ int err = 0;
+
+ ESP_LOGI(tag, "Mounting SPIFFS files system");
+
+ cfg.phys_addr = CONFIG_SPIFFS_BASE_ADDR;
+ cfg.phys_size = CONFIG_SPIFFS_SIZE;
+ cfg.phys_erase_block = SPIFFS_ERASE_SIZE;
+ cfg.log_page_size = SPIFFS_LOG_PAGE_SIZE;
+ cfg.log_block_size = CONFIG_SPIFFS_LOG_BLOCK_SIZE;
+
+ ESP_LOGI(tag, "Start address: 0x%x; Size %d KB", cfg.phys_addr, cfg.phys_size / 1024);
+
+ cfg.hal_read_f = (spiffs_read)low_spiffs_read;
+ cfg.hal_write_f = (spiffs_write)low_spiffs_write;
+ cfg.hal_erase_f = (spiffs_erase)low_spiffs_erase;
+
+ my_spiffs_work_buf = malloc(cfg.log_page_size * 2);
+ if (!my_spiffs_work_buf) {
+ err = 1;
+ goto err_exit;
+ }
+
+ int fds_len = sizeof(spiffs_fd) * 5;
+ my_spiffs_fds = malloc(fds_len);
+ if (!my_spiffs_fds) {
+ free(my_spiffs_work_buf);
+ err = 2;
+ goto err_exit;
+ }
+
+ int cache_len = cfg.log_page_size * 5;
+ my_spiffs_cache = malloc(cache_len);
+ if (!my_spiffs_cache) {
+ free(my_spiffs_work_buf);
+ free(my_spiffs_fds);
+ err = 3;
+ goto err_exit;
+ }
+
+ while (retries < 2) {
+ res = SPIFFS_mount(
+ &fs, &cfg, my_spiffs_work_buf, my_spiffs_fds,
+ fds_len, my_spiffs_cache, cache_len, NULL
+ );
+
+ if (res < 0) {
+ if (fs.err_code == SPIFFS_ERR_NOT_A_FS) {
+ ESP_LOGW(tag, "No file system detected, formating...");
+ SPIFFS_unmount(&fs);
+ res = SPIFFS_format(&fs);
+ if (res < 0) {
+ free(my_spiffs_work_buf);
+ free(my_spiffs_fds);
+ free(my_spiffs_cache);
+ ESP_LOGE(tag, "Format error");
+ goto exit;
+ }
+ }
+ else {
+ free(my_spiffs_work_buf);
+ free(my_spiffs_fds);
+ free(my_spiffs_cache);
+ ESP_LOGE(tag, "Error mounting fs (%d)", res);
+ goto exit;
+ }
+ }
+ else break;
+ retries++;
+ }
+
+ if (retries > 1) {
+ free(my_spiffs_work_buf);
+ free(my_spiffs_fds);
+ free(my_spiffs_cache);
+ ESP_LOGE(tag, "Can't mount");
+ goto exit;
+ }
+
+ list_init(&files, 0);
+
+ ESP_LOGI(tag, "Mounted");
+
+ spiffs_is_mounted = 1;
+ return 1;
+
+err_exit:
+ ESP_LOGE(tag, "Error allocating fs structures (%d)", err);
+exit:
+ esp_vfs_unregister("/spiffs");
+ spiffs_is_registered = 0;
+ return 0;
+}
+
+//==========================
+void vfs_spiffs_register() {
+
+ if (spiffs_is_registered) return;
+
+ esp_vfs_t vfs = {
+ .fd_offset = 0,
+ .flags = ESP_VFS_FLAG_DEFAULT,
+ .write = &vfs_spiffs_write,
+ .open = &vfs_spiffs_open,
+ .fstat = &vfs_spiffs_fstat,
+ .close = &vfs_spiffs_close,
+ .read = &vfs_spiffs_read,
+ .lseek = &vfs_spiffs_lseek,
+ .stat = &vfs_spiffs_stat,
+ .link = NULL,
+ .unlink = &vfs_spiffs_unlink,
+ .rename = &vfs_spiffs_rename,
+ .mkdir = &vfs_spiffs_mkdir,
+ .opendir = &vfs_spiffs_opendir,
+ .readdir = &vfs_spiffs_readdir,
+ .closedir = &vfs_piffs_closedir,
+ };
+
+ ESP_LOGI(tag, "Registering SPIFFS file system");
+ esp_err_t res = esp_vfs_register("/spiffs", &vfs, NULL);
+ if (res != ESP_OK) {
+ ESP_LOGI(tag, "Error, SPIFFS file system not registered");
+ return;
+ }
+ spiffs_is_registered = 1;
+
+ spiffs_mount();
+}
+
+//=============================
+int spiffs_unmount(int unreg) {
+
+ if (!spiffs_is_mounted) return 0;
+
+ SPIFFS_unmount(&fs);
+ spiffs_is_mounted = 0;
+
+ if (unreg) {
+ esp_vfs_unregister("/spiffs");
+ spiffs_is_registered = 0;
+ }
+ return 1;
+}
diff --git a/build/esp32/sdk_build/components/spiffs/spiffs_vfs.h b/build/esp32/sdk_build/components/spiffs/spiffs_vfs.h
new file mode 100644
index 0000000..c21f827
--- /dev/null
+++ b/build/esp32/sdk_build/components/spiffs/spiffs_vfs.h
@@ -0,0 +1,20 @@
+/*
+ * spiffs VFS public function
+ *
+ * Author: LoBo (loboris@gmail.com / https://github.com/loboris)
+ *
+ * Part of this code is copied from or inspired by LUA-RTOS_ESP32 project:
+ *
+ * https://github.com/whitecatboard/Lua-RTOS-ESP32
+ * IBEROXARXA SERVICIOS INTEGRALES, S.L. & CSS IBÉRICA, S.L.
+ * Jaume Olivé (jolive@iberoxarxa.com / jolive@whitecatboard.org)
+ *
+ */
+
+int spiffs_is_registered;
+int spiffs_is_mounted;
+
+void vfs_spiffs_register();
+int spiffs_mount();
+int spiffs_unmount(int unreg);
+void spiffs_fs_stat(uint32_t *total, uint32_t *used);
diff --git a/build/esp32/sdk_build/main/interface.c b/build/esp32/sdk_build/main/interface.c
index 5c8a0dd..053df1f 100644
--- a/build/esp32/sdk_build/main/interface.c
+++ b/build/esp32/sdk_build/main/interface.c
@@ -402,3 +402,74 @@ cell my_lwip_read(cell handle, cell len, void *adr)
{
return (cell)lwip_read_r((int)handle, adr, (size_t)len);
}
+
+#include <errno.h>
+#include <sys/fcntl.h>
+#include "esp_vfs.h"
+#include "esp_vfs_fat.h"
+#include "esp_log.h"
+#include "spiffs_vfs.h"
+
+void init_filesystem(void)
+{
+ vfs_spiffs_register();
+}
+
+char *expand_path(char *name)
+{
+ static char path[256];
+ strcpy(path, "/spiffs/");
+ strncat(path, name, 256 - strlen("/spiffs/"));
+ return path;
+}
+
+void *open_dir(void)
+{
+ return opendir(expand_path(""));
+}
+
+void *next_file(void *dir)
+{
+ struct dirent *ent;
+
+ while ((ent = readdir((DIR *)dir)) != NULL) {
+ if (ent->d_type == DT_REG) {
+ return ent;
+ }
+ }
+ return NULL;
+}
+
+char *dirent_name(void *ent)
+{
+ return ((struct dirent *)ent)->d_name;
+}
+
+cell dirent_size(void *ent)
+{
+ struct stat statbuf;
+ if (stat(expand_path(((struct dirent *)ent)->d_name), &statbuf)) {
+ return -1;
+ }
+ return statbuf.st_size;
+}
+
+void rename_file(char *new, char *old)
+{
+ static char path[256];
+ strcpy(path, "/spiffs/");
+ strncat(path, new, 256 - strlen("/spiffs/"));
+
+ rename(expand_path(old), path);
+}
+cell fs_avail(void)
+{
+ u32_t total, used;
+ spiffs_fs_stat(&total, &used);
+ return (cell)(total - used);
+}
+
+void delete_file(char *name)
+{
+ remove(expand_path(name));
+}
diff --git a/build/esp32/sdk_build/partitions.csv b/build/esp32/sdk_build/partitions.csv
new file mode 100644
index 0000000..aceacab
--- /dev/null
+++ b/build/esp32/sdk_build/partitions.csv
@@ -0,0 +1,6 @@
+# Name, Type, SubType, Offset, Size
+# 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, , 1M
diff --git a/build/esp32/sdk_build/sdkconfig b/build/esp32/sdk_build/sdkconfig
index edfbbc2..33fb7c3 100644
--- a/build/esp32/sdk_build/sdkconfig
+++ b/build/esp32/sdk_build/sdkconfig
@@ -74,12 +74,12 @@ CONFIG_MONITOR_BAUD=115200
#
# Partition Table
#
-CONFIG_PARTITION_TABLE_SINGLE_APP=y
+# CONFIG_PARTITION_TABLE_SINGLE_APP is not set
# CONFIG_PARTITION_TABLE_TWO_OTA is not set
-# CONFIG_PARTITION_TABLE_CUSTOM is not set
+CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000
-CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv"
+CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_APP_OFFSET=0x10000
CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
@@ -127,7 +127,7 @@ CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y
CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4
CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048
-CONFIG_MAIN_TASK_STACK_SIZE=4096
+CONFIG_MAIN_TASK_STACK_SIZE=8192
# CONFIG_NEWLIB_STDOUT_ADDCR is not set
# CONFIG_NEWLIB_NANO_FORMAT is not set
# CONFIG_CONSOLE_UART_DEFAULT is not set
@@ -290,3 +290,10 @@ CONFIG_OPENSSL_ASSERT_DO_NOTHING=y
#
# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set
CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y
+
+#
+# SPIffs Example Configuration
+#
+CONFIG_SPIFFS_LOG_BLOCK_SIZE=8192
+CONFIG_SPIFFS_BASE_ADDR=0x110000
+CONFIG_SPIFFS_SIZE=1048576
diff --git a/src/app/esp32/app.fth b/src/app/esp32/app.fth
index fe9d1a3..32d8f74 100644
--- a/src/app/esp32/app.fth
+++ b/src/app/esp32/app.fth
@@ -20,6 +20,26 @@ warning !
cr
;
+\ m-emit is defined in textend.c
+alias m-key key
+alias m-init noop
+
+: m-avail? ( -- false | char true )
+ key? if key true exit then
+ false
+;
+alias get-ticks get-msecs
+: ms>ticks ( ms -- ticks ) ;
+
+fl ../esp8266/xmifce.fth
+fl ../../lib/crc16.fth
+fl ../../lib/xmodem.fth
+also modem
+: rx ( -- ) pad unused pad here - - (receive) #100 ms ;
+previous
+
+fl files.fth
+
0 [if]
\ Replace 'quit' to make CForth auto-run some application code
\ instead of just going interactive.
diff --git a/src/app/esp32/consio.c b/src/app/esp32/consio.c
index b7d3497..a1e1e95 100644
--- a/src/app/esp32/consio.c
+++ b/src/app/esp32/consio.c
@@ -9,6 +9,7 @@
extern void uart_write_bytes(int, char *, int);
extern int uart_read_bytes(int, char *, int, int);
extern void init_uart(void);
+extern void init_filesystem(void);
int isinteractive() { return (1); }
int isstandalone() { return (1); }
@@ -52,6 +53,7 @@ void init_io(int argc, char **argv, cell *up)
{
key_is_avail = 0;
init_uart();
+ init_filesystem();
}
int caccept(char *addr, cell count, cell *up)
diff --git a/src/app/esp32/fileio.c b/src/app/esp32/fileio.c
index da03d58..7f4f92a 100644
--- a/src/app/esp32/fileio.c
+++ b/src/app/esp32/fileio.c
@@ -35,7 +35,6 @@ extern FILE *fopen();
cell
freadline(cell f, cell *sp, cell *up) // Returns IO result, actual and more? on stack
{
- printf("In freadline\n");
// Stack: adr len -- actual more?
u_char *adr = (u_char *)sp[1];
@@ -109,8 +108,8 @@ expand_name(char *name)
static char fullname[PATH_MAX];
int ndx;
- fullp = fullname;
- fullname[0] = '\0';
+ strcpy(fullname, "/spiffs/");
+ fullp = fullname + strlen(fullname);
fnamep = name;
diff --git a/src/app/esp32/files.fth b/src/app/esp32/files.fth
new file mode 100644
index 0000000..0b91ab1
--- /dev/null
+++ b/src/app/esp32/files.fth
@@ -0,0 +1,52 @@
+\ File tools for ESP32 CForth
+
+: dir ( -- )
+ open-dir ?dup 0= if exit then ( dirp )
+ begin dup next-file ?dup while ( dirp dirent )
+ dup file-bytes ( dirp dirent )
+ push-decimal 6 u.r pop-base space ( dirp dirent )
+ file-name cscount type cr ( dirp )
+ repeat ( dirp )
+ close-dir ( )
+;
+alias ls dir
+
+0 value fid
+: close-fid ( -- ) fid close-file drop ;
+: create-fid ( filename$ -- ) w/o create-file abort" Can't create file" to fid ;
+: write-fid ( adr len -- ) fid write-file abort" File write error" ;
+: open-fid ( filename$ -- ) r/o open-file abort" Can't open file" to fid ;
+: read-fid ( adr len -- actual ) fid read-file abort" File read error" ;
+: read-line-fid ( adr len -- len more? ) fid read-line abort" File read error" ;
+
+: xmodem-to-file: ( "filename" -- )
+ rx ( adr len )
+ safe-parse-word create-fid write-fid close-fid
+;
+alias rf xmodem-to-file:
+
+: $print-file ( filename$ -- )
+ open-fid
+ begin pad #100 read-line-fid while ( len )
+ pad over type ( len )
+ \ If the buffer is full the end-of-line has not yet been read
+ #100 < if cr then ( )
+ repeat ( 0 )
+ drop ( )
+ close-fid
+;
+: cat ( "filename" -- ) safe-parse-word $print-file ;
+
+: rm* ( -- )
+ open-dir ( dirp )
+ begin dup next-file ?dup while ( dirp dirent )
+ file-name cscount delete-file ( dirp )
+ repeat ( dirp )
+ close-dir
+;
+\needs $= : $= ( $1 $2 -- same? ) compare 0= ;
+: rm ( "filename" -- )
+ safe-parse-word ( name$ )
+ 2dup " *" $= if 2drop rm* else delete-file then
+;
+
diff --git a/src/app/esp32/interface.h b/src/app/esp32/interface.h
index e78fc93..8e6f4b7 100644
--- a/src/app/esp32/interface.h
+++ b/src/app/esp32/interface.h
@@ -40,3 +40,13 @@ cell my_lwip_read(cell handle, cell len, void *adr);
cell my_select(cell maxfdp1, void *reads, void *writes, void *excepts, cell milliseconds);
cell tcpip_adapter_get_ip_info(cell ifce, void *info);
+
+void *open_dir(void);
+//void *readdir(void *dir);
+void *next_file(void *dir);
+void closedir(void *dir);
+cell dirent_size(void *ent);
+char *dirent_name(void *ent);
+void rename_file(char *new, char *old);
+void delete_file(char *path);
+cell fs_avail(void);
diff --git a/src/app/esp32/textend.c b/src/app/esp32/textend.c
index 023288e..b0b965a 100644
--- a/src/app/esp32/textend.c
+++ b/src/app/esp32/textend.c
@@ -39,10 +39,14 @@ extern void adc1_config_channel_atten(void);
extern void adc1_get_voltage(void);
extern void hall_sensor_read(void);
+int xTaskGetTickCount(void);
+void raw_emit(char c);
+
cell ((* const ccalls[])()) = {
C(build_date_adr) //c 'build-date { -- a.value }
C(version_adr) //c 'version { -- a.value }
C(ms) //c ms { i.ms -- }
+ C(xTaskGetTickCount) //c get-msecs { -- i.ms }
C(software_reset) //c restart { -- }
C(adc1_config_width) //c adc-width! { i.width -- }
@@ -93,5 +97,17 @@ cell ((* const ccalls[])()) = {
C(ip_info) //c ip-info { a.info -- }
C(my_select) //c lwip-select { i.sec a.exc a.wr a.rd i.n -- i.cnt }
- C(tcpip_adapter_get_ip_info) //c ip-info@ { a.buf i.adapter# -- i.error }
+ C(tcpip_adapter_get_ip_info) //c ip-info@ { a.buf i.adapter# -- i.error }
+
+ C(open_dir) //c open-dir { -- a.dir }
+ C(closedir) //c close-dir { a.dir -- }
+ C(next_file) //c next-file { a.dir -- a.dirent }
+ C(dirent_size) //c file-bytes { a.dir -- i.size }
+ C(dirent_name) //c file-name { a.dir -- a.name }
+ C(rename_file) //c rename-file { $.old $.new -- }
+ C(delete_file) //c delete-file { $.name -- }
+ C(fs_avail) //c fs-avail { -- i.bytes }
+
+ C(raw_emit) //c m-emit { i.char -- }
};
+