diff --git a/components/bootloader_support/src/flash_partitions.c b/components/bootloader_support/src/flash_partitions.c index 51359ea6..1f4487ef 100644 --- a/components/bootloader_support/src/flash_partitions.c +++ b/components/bootloader_support/src/flash_partitions.c @@ -109,7 +109,7 @@ esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition { int md5_found = 0; int num_parts; - uint32_t chip_size = flashchip.chip_size; + uint32_t chip_size = g_rom_flashchip.chip_size; *num_partitions = 0; for (num_parts = 0; num_parts < ESP_PARTITION_TABLE_MAX_ENTRIES; num_parts++) { diff --git a/components/esp8266/include/esp8266/rom_functions.h b/components/esp8266/include/esp8266/rom_functions.h index 246622a8..cabdb2eb 100644 --- a/components/esp8266/include/esp8266/rom_functions.h +++ b/components/esp8266/include/esp8266/rom_functions.h @@ -6,16 +6,16 @@ #define ROM_FLASH_BUF_DECLARE(__name, __size) uint8_t __name[__size] __attribute__((aligned(4))) -typedef struct esp_spi_flash_chip { - uint32_t deviceId; - uint32_t chip_size; // chip size in byte - uint32_t block_size; - uint32_t sector_size; - uint32_t page_size; - uint32_t status_mask; -} esp_spi_flash_chip_t; +typedef struct { + uint32_t device_id; + uint32_t chip_size; // chip size in bytes + uint32_t block_size; + uint32_t sector_size; + uint32_t page_size; + uint32_t status_mask; +} esp_rom_spiflash_chip_t; -extern esp_spi_flash_chip_t flashchip; +extern esp_rom_spiflash_chip_t g_rom_flashchip; uint32_t Wait_SPI_Idle(); @@ -27,13 +27,13 @@ void system_soft_wdt_feed(); void Cache_Read_Enable_New(); -int SPI_page_program(esp_spi_flash_chip_t *chip, uint32_t dst_addr, void *pbuf, uint32_t len); -int SPI_read_data(esp_spi_flash_chip_t *chip, uint32_t dst_addr, void *pbuf, uint32_t len); -int SPI_write_enable(esp_spi_flash_chip_t *chip); -int SPI_sector_erase(esp_spi_flash_chip_t *chip, uint32_t sect_addr); -int SPI_write_status(esp_spi_flash_chip_t *chip, uint32_t status); -int SPI_read_status(esp_spi_flash_chip_t *chip, uint32_t *status); -int Enable_QMode(esp_spi_flash_chip_t *chip); +int SPI_page_program(esp_rom_spiflash_chip_t *chip, uint32_t dst_addr, void *pbuf, uint32_t len); +int SPI_read_data(esp_rom_spiflash_chip_t *chip, uint32_t dst_addr, void *pbuf, uint32_t len); +int SPI_write_enable(esp_rom_spiflash_chip_t *chip); +int SPI_sector_erase(esp_rom_spiflash_chip_t *chip, uint32_t sect_addr); +int SPI_write_status(esp_rom_spiflash_chip_t *chip, uint32_t status); +int SPI_read_status(esp_rom_spiflash_chip_t *chip, uint32_t *status); +int Enable_QMode(esp_rom_spiflash_chip_t *chip); int SPIWrite(uint32_t addr, const uint8_t *src, uint32_t size); int SPIRead(uint32_t addr, void *dst, uint32_t size); diff --git a/components/esp8266/include/esp_libc.h b/components/esp8266/include/esp_libc.h index e9e01828..d77ea28a 100644 --- a/components/esp8266/include/esp_libc.h +++ b/components/esp8266/include/esp_libc.h @@ -29,6 +29,7 @@ #include #include #include +#include "rom/ets_sys.h" #ifndef BOOTLOADER_BUILD #include "esp_heap_caps.h" #endif @@ -60,18 +61,6 @@ int ets_putc(int c); */ int ets_vprintf(const char *fmt, va_list ap); -/** - * @brief Printf the strings to uart or other devices, similar with printf, simple than printf. - * Can not print float point data format, or longlong data format. - * - * @param const char *fmt : See printf. - * - * @param ... : See printf. - * - * @return int : the length printed to the output device. - */ -int ets_printf(const char *fmt, ...); - #ifndef os_printf #define os_printf printf #endif diff --git a/components/esp8266/include/rom/ets_sys.h b/components/esp8266/include/rom/ets_sys.h index b5e37330..d9057d0a 100644 --- a/components/esp8266/include/rom/ets_sys.h +++ b/components/esp8266/include/rom/ets_sys.h @@ -109,6 +109,18 @@ void os_delay_us(uint16_t us); */ void ets_delay_us(uint32_t us); +/** + * @brief Printf the strings to uart or other devices, similar with printf, simple than printf. + * Can not print float point data format, or longlong data format. + * + * @param const char *fmt : See printf. + * + * @param ... : See printf. + * + * @return int : the length printed to the output device. + */ +int ets_printf(const char *fmt, ...); + /** * @brief Register the print output function. * diff --git a/components/esp8266/include/rom/spi_flash.h b/components/esp8266/include/rom/spi_flash.h new file mode 100644 index 00000000..c3d0d100 --- /dev/null +++ b/components/esp8266/include/rom/spi_flash.h @@ -0,0 +1,25 @@ +// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ROM_SPI_FLASH_H_ +#define _ROM_SPI_FLASH_H_ + +#include +#include + +#include "esp_attr.h" + +#include "esp8266/rom_functions.h" + +#endif /* _ROM_SPI_FLASH_H_ */ diff --git a/components/spi_flash/include/priv/esp_spi_flash_raw.h b/components/spi_flash/include/priv/esp_spi_flash_raw.h index c3c6931b..57b7e39c 100644 --- a/components/spi_flash/include/priv/esp_spi_flash_raw.h +++ b/components/spi_flash/include/priv/esp_spi_flash_raw.h @@ -48,21 +48,21 @@ typedef struct { uint8_t dummy_bits; } spi_cmd_t; -bool spi_user_cmd_raw(esp_spi_flash_chip_t *chip, spi_cmd_dir_t mode, spi_cmd_t *p_cmd); +bool spi_user_cmd_raw(esp_rom_spiflash_chip_t *chip, spi_cmd_dir_t mode, spi_cmd_t *p_cmd); -uint32_t spi_flash_get_id_raw(esp_spi_flash_chip_t *chip); +uint32_t spi_flash_get_id_raw(esp_rom_spiflash_chip_t *chip); -esp_err_t spi_flash_enable_qmode_raw(esp_spi_flash_chip_t *chip); +esp_err_t spi_flash_enable_qmode_raw(esp_rom_spiflash_chip_t *chip); -esp_err_t spi_flash_read_status_raw(esp_spi_flash_chip_t *chip, uint32_t *status); +esp_err_t spi_flash_read_status_raw(esp_rom_spiflash_chip_t *chip, uint32_t *status); -esp_err_t spi_flash_write_status_raw(esp_spi_flash_chip_t *chip, uint32_t status_value); +esp_err_t spi_flash_write_status_raw(esp_rom_spiflash_chip_t *chip, uint32_t status_value); -esp_err_t spi_flash_read_raw(esp_spi_flash_chip_t *chip, size_t src_addr, void *dest, size_t size); +esp_err_t spi_flash_read_raw(esp_rom_spiflash_chip_t *chip, size_t src_addr, void *dest, size_t size); -esp_err_t spi_flash_write_raw(esp_spi_flash_chip_t *chip, size_t dest_addr, const void *src, size_t size); +esp_err_t spi_flash_write_raw(esp_rom_spiflash_chip_t *chip, size_t dest_addr, const void *src, size_t size); -esp_err_t spi_flash_erase_sector_raw(esp_spi_flash_chip_t *chip, size_t sec, size_t sec_size); +esp_err_t spi_flash_erase_sector_raw(esp_rom_spiflash_chip_t *chip, size_t sec, size_t sec_size); void spi_flash_switch_to_qio_raw(void); diff --git a/components/spi_flash/port/port.c b/components/spi_flash/port/port.c index e683e1fd..527e60c6 100644 --- a/components/spi_flash/port/port.c +++ b/components/spi_flash/port/port.c @@ -69,7 +69,7 @@ static uint32_t s_v2_flash_bin_size; static uint32_t s_v2_flash_size; static sys_param_t s_sys_param; static boot_param_t s_boot_param; -static esp_spi_flash_chip_t s_flash_chip = { +static esp_rom_spiflash_chip_t s_flash_chip = { 0x1640ef, CONFIG_SPI_FLASH_SIZE, 64 * 1024, diff --git a/components/spi_flash/src/spi_flash.c b/components/spi_flash/src/spi_flash.c index 468b5376..f4c622df 100644 --- a/components/spi_flash/src/spi_flash.c +++ b/components/spi_flash/src/spi_flash.c @@ -64,7 +64,7 @@ extern void vPortEnterCritical(void); extern void vPortExitCritical(void); -esp_spi_flash_chip_t flashchip = { +esp_rom_spiflash_chip_t g_rom_flashchip = { 0x1640ef, CONFIG_SPI_FLASH_SIZE, 64 * 1024, @@ -89,7 +89,7 @@ bool spi_user_cmd(spi_cmd_dir_t mode, spi_cmd_t *p_cmd) FLASH_INTR_LOCK(c_tmp); FlashIsOnGoing = 1; - ret = spi_user_cmd_raw(&flashchip, mode, p_cmd); + ret = spi_user_cmd_raw(&g_rom_flashchip, mode, p_cmd); FlashIsOnGoing = 0; FLASH_INTR_UNLOCK(c_tmp); @@ -174,7 +174,7 @@ uint32_t spi_flash_get_id(void) FlashIsOnGoing = 1; - rdid = spi_flash_get_id_raw(&flashchip); + rdid = spi_flash_get_id_raw(&g_rom_flashchip); FlashIsOnGoing = 0; @@ -192,7 +192,7 @@ esp_err_t spi_flash_read_status(uint32_t *status) FlashIsOnGoing = 1; - ret = spi_flash_read_status_raw(&flashchip, status); + ret = spi_flash_read_status_raw(&g_rom_flashchip, status); FlashIsOnGoing = 0; @@ -209,7 +209,7 @@ esp_err_t spi_flash_write_status(uint32_t status_value) FlashIsOnGoing = 1; - spi_flash_write_status_raw(&flashchip, status_value); + spi_flash_write_status_raw(&g_rom_flashchip, status_value); FlashIsOnGoing = 0; @@ -436,7 +436,7 @@ void user_spi_flash_dio_to_qio_pre_init(void) } //ENBALE FLASH QIO 0X01H+0X00+0X02 } else { - if (spi_flash_enable_qmode_raw(&flashchip) == ESP_OK) { + if (spi_flash_enable_qmode_raw(&g_rom_flashchip) == ESP_OK) { to_qio = true; } } @@ -452,7 +452,7 @@ esp_err_t spi_flash_erase_sector(size_t sec) esp_err_t ret; - if (sec >= (flashchip.chip_size / flashchip.sector_size)) { + if (sec >= (g_rom_flashchip.chip_size / g_rom_flashchip.sector_size)) { return ESP_ERR_FLASH_OP_FAIL; } @@ -463,7 +463,7 @@ esp_err_t spi_flash_erase_sector(size_t sec) FLASH_INTR_LOCK(c_tmp); FlashIsOnGoing = 1; - ret = spi_flash_erase_sector_raw(&flashchip, sec, flashchip.sector_size); + ret = spi_flash_erase_sector_raw(&g_rom_flashchip, sec, g_rom_flashchip.sector_size); FlashIsOnGoing = 0; FLASH_INTR_UNLOCK(c_tmp); @@ -477,15 +477,15 @@ static esp_err_t spi_flash_program(uint32_t target, uint32_t *src_addr, size_t l uint32_t pgm_len, pgm_num; uint8_t i; - page_size = flashchip.page_size; + page_size = g_rom_flashchip.page_size; pgm_len = page_size - (target % page_size); if (len < pgm_len) { - if (ESP_OK != spi_flash_write_raw(&flashchip, target, src_addr, len)) { + if (ESP_OK != spi_flash_write_raw(&g_rom_flashchip, target, src_addr, len)) { return ESP_ERR_FLASH_OP_FAIL; } } else { - if (ESP_OK != spi_flash_write_raw(&flashchip, target, src_addr, pgm_len)) { + if (ESP_OK != spi_flash_write_raw(&g_rom_flashchip, target, src_addr, pgm_len)) { return ESP_ERR_FLASH_OP_FAIL; } @@ -493,7 +493,7 @@ static esp_err_t spi_flash_program(uint32_t target, uint32_t *src_addr, size_t l pgm_num = (len - pgm_len) / page_size; for (i = 0; i < pgm_num; i++) { - if (ESP_OK != spi_flash_write_raw(&flashchip, target + pgm_len, src_addr + (pgm_len >> 2), page_size)) { + if (ESP_OK != spi_flash_write_raw(&g_rom_flashchip, target + pgm_len, src_addr + (pgm_len >> 2), page_size)) { return ESP_ERR_FLASH_OP_FAIL; } @@ -501,7 +501,7 @@ static esp_err_t spi_flash_program(uint32_t target, uint32_t *src_addr, size_t l } //remain parts to program - if (ESP_OK != spi_flash_write_raw(&flashchip, target + pgm_len, src_addr + (pgm_len >> 2), len - pgm_len)) { + if (ESP_OK != spi_flash_write_raw(&g_rom_flashchip, target + pgm_len, src_addr + (pgm_len >> 2), len - pgm_len)) { return ESP_ERR_FLASH_OP_FAIL; } } @@ -556,7 +556,7 @@ esp_err_t spi_flash_write(size_t dest_addr, const void *src, size_t size) return ESP_ERR_FLASH_OP_FAIL; } - if ((dest_addr + size) > flashchip.chip_size) { + if ((dest_addr + size) > g_rom_flashchip.chip_size) { return ESP_ERR_FLASH_OP_FAIL; } @@ -619,7 +619,7 @@ static esp_err_t __spi_flash_read(size_t src_addr, void *dest, size_t size) FLASH_INTR_LOCK(c_tmp); FlashIsOnGoing = 1; - ret = spi_flash_read_raw(&flashchip, src_addr, dest, size); + ret = spi_flash_read_raw(&g_rom_flashchip, src_addr, dest, size); FlashIsOnGoing = 0; FLASH_INTR_UNLOCK(c_tmp); @@ -779,5 +779,5 @@ uintptr_t spi_flash_cache2phys(const void *cached) size_t spi_flash_get_chip_size() { - return flashchip.chip_size; + return g_rom_flashchip.chip_size; } diff --git a/components/spi_flash/src/spi_flash_raw.c b/components/spi_flash/src/spi_flash_raw.c index 9fc3afca..dee89962 100644 --- a/components/spi_flash/src/spi_flash_raw.c +++ b/components/spi_flash/src/spi_flash_raw.c @@ -35,7 +35,7 @@ void Cache_Read_Enable_2() } void Cache_Read_Enable_New(void) __attribute__((alias("Cache_Read_Enable_2"))); -uint32_t spi_flash_get_id_raw(esp_spi_flash_chip_t *chip) +uint32_t spi_flash_get_id_raw(esp_rom_spiflash_chip_t *chip) { uint32_t rdid = 0; @@ -54,7 +54,7 @@ uint32_t spi_flash_get_id_raw(esp_spi_flash_chip_t *chip) return rdid; } -esp_err_t spi_flash_read_status_raw(esp_spi_flash_chip_t *chip, uint32_t *status) +esp_err_t spi_flash_read_status_raw(esp_rom_spiflash_chip_t *chip, uint32_t *status) { esp_err_t ret; @@ -67,7 +67,7 @@ esp_err_t spi_flash_read_status_raw(esp_spi_flash_chip_t *chip, uint32_t *status return ret; } -esp_err_t spi_flash_write_status_raw(esp_spi_flash_chip_t *chip, uint32_t status_value) +esp_err_t spi_flash_write_status_raw(esp_rom_spiflash_chip_t *chip, uint32_t status_value) { Cache_Read_Disable_2(); @@ -86,7 +86,7 @@ esp_err_t spi_flash_write_status_raw(esp_spi_flash_chip_t *chip, uint32_t status return ESP_OK; } -esp_err_t spi_flash_erase_sector_raw(esp_spi_flash_chip_t *chip, size_t sec, size_t sec_size) +esp_err_t spi_flash_erase_sector_raw(esp_rom_spiflash_chip_t *chip, size_t sec, size_t sec_size) { esp_err_t ret = ESP_OK; @@ -105,7 +105,7 @@ esp_err_t spi_flash_erase_sector_raw(esp_spi_flash_chip_t *chip, size_t sec, siz return ret; } -esp_err_t spi_flash_enable_qmode_raw(esp_spi_flash_chip_t *chip) +esp_err_t spi_flash_enable_qmode_raw(esp_rom_spiflash_chip_t *chip) { esp_err_t ret; @@ -120,7 +120,7 @@ esp_err_t spi_flash_enable_qmode_raw(esp_spi_flash_chip_t *chip) return ret; } -esp_err_t spi_flash_write_raw(esp_spi_flash_chip_t *chip, size_t dest_addr, const void *src, size_t size) +esp_err_t spi_flash_write_raw(esp_rom_spiflash_chip_t *chip, size_t dest_addr, const void *src, size_t size) { esp_err_t ret; @@ -133,7 +133,7 @@ esp_err_t spi_flash_write_raw(esp_spi_flash_chip_t *chip, size_t dest_addr, cons return ret; } -esp_err_t spi_flash_read_raw(esp_spi_flash_chip_t *chip, size_t src_addr, void *dest, size_t size) +esp_err_t spi_flash_read_raw(esp_rom_spiflash_chip_t *chip, size_t src_addr, void *dest, size_t size) { esp_err_t ret; @@ -146,7 +146,7 @@ esp_err_t spi_flash_read_raw(esp_spi_flash_chip_t *chip, size_t src_addr, void * return ret; } -bool spi_user_cmd_raw(esp_spi_flash_chip_t *chip, spi_cmd_dir_t mode, spi_cmd_t *p_cmd) +bool spi_user_cmd_raw(esp_rom_spiflash_chip_t *chip, spi_cmd_dir_t mode, spi_cmd_t *p_cmd) { int idx = 0; diff --git a/components/spiffs/CMakeLists.txt b/components/spiffs/CMakeLists.txt index f2b03f79..cb18c92c 100644 --- a/components/spiffs/CMakeLists.txt +++ b/components/spiffs/CMakeLists.txt @@ -1,7 +1,17 @@ -set(COMPONENT_ADD_INCLUDEDIRS include include/spiffs) -set(COMPONENT_SRCDIRS library) +if(CONFIG_USING_SPIFFS) +set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_PRIV_INCLUDEDIRS "." "spiffs/src") +set(COMPONENT_SRCS "esp_spiffs.c" + "spiffs_api.c" + "spiffs/src/spiffs_cache.c" + "spiffs/src/spiffs_check.c" + "spiffs/src/spiffs_gc.c" + "spiffs/src/spiffs_hydrogen.c" + "spiffs/src/spiffs_nucleus.c") +endif() -set(COMPONENT_REQUIRES freertos) -set(COMPONENT_PRIV_REQUIRES esp8266) +set(COMPONENT_REQUIRES spi_flash) +set(COMPONENT_PRIV_REQUIRES bootloader_support) register_component() + diff --git a/components/spiffs/Kconfig b/components/spiffs/Kconfig new file mode 100644 index 00000000..4bbcb0fc --- /dev/null +++ b/components/spiffs/Kconfig @@ -0,0 +1,167 @@ +menuconfig USING_SPIFFS + bool "SPIFFS" + select USING_ESP_VFS + help + Select this option to enable support for the SPIFFS. + +if USING_SPIFFS + +config SPIFFS_MAX_PARTITIONS + int "Maximum Number of Partitions" + default 3 + range 1 10 + help + Define maximum number of partitions that can be mounted. + +menu "SPIFFS Cache Configuration" +config SPIFFS_CACHE + bool "Enable SPIFFS Cache" + default "y" + help + Enables/disable memory read caching of nucleus file system + operations. + +config SPIFFS_CACHE_WR + bool "Enable SPIFFS Write Caching" + default "y" + depends on SPIFFS_CACHE + help + Enables memory write caching for file descriptors in hydrogen. + +config SPIFFS_CACHE_STATS + bool "Enable SPIFFS Cache Statistics" + default "n" + depends on SPIFFS_CACHE + help + Enable/disable statistics on caching. Debug/test purpose only. + +endmenu + +config SPIFFS_PAGE_CHECK + bool "Enable SPIFFS Page Check" + default "y" + help + Always check header of each accessed page to ensure consistent state. + If enabled it will increase number of reads from flash, especially + if cache is disabled. + +config SPIFFS_GC_MAX_RUNS + int "Set Maximum GC Runs" + default 10 + range 1 255 + help + Define maximum number of GC runs to perform to reach desired free pages. + +config SPIFFS_GC_STATS + bool "Enable SPIFFS GC Statistics" + default "n" + help + Enable/disable statistics on gc. Debug/test purpose only. + +config SPIFFS_PAGE_SIZE + int "SPIFFS logical page size" + default 256 + range 256 1024 + help + Logical page size of SPIFFS partition, in bytes. Must be multiple + of flash page size (which is usually 256 bytes). + Larger page sizes reduce overhead when storing large files, and + improve filesystem performance when reading large files. + Smaller page sizes reduce overhead when storing small (< page size) + files. + +config SPIFFS_OBJ_NAME_LEN + int "Set SPIFFS Maximum Name Length" + default 32 + range 1 256 + help + Object name maximum length. Note that this length include the + zero-termination character, meaning maximum string of characters + can at most be SPIFFS_OBJ_NAME_LEN - 1. + + SPIFFS_OBJ_NAME_LEN + SPIFFS_META_LENGTH should not exceed + SPIFFS_PAGE_SIZE - 64. + +config SPIFFS_USE_MAGIC + bool "Enable SPIFFS Filesystem Magic" + default "y" + help + Enable this to have an identifiable spiffs filesystem. + This will look for a magic in all sectors to determine if this + is a valid spiffs system or not at mount time. + +config SPIFFS_USE_MAGIC_LENGTH + bool "Enable SPIFFS Filesystem Length Magic" + default "y" + depends on SPIFFS_USE_MAGIC + help + If this option is enabled, the magic will also be dependent + on the length of the filesystem. For example, a filesystem + configured and formatted for 4 megabytes will not be accepted + for mounting with a configuration defining the filesystem as 2 megabytes. + +config SPIFFS_META_LENGTH + int "Size of per-file metadata field" + default 4 + help + This option sets the number of extra bytes stored in the file header. + These bytes can be used in an application-specific manner. + Set this to at least 4 bytes to enable support for saving file + modification time. + + SPIFFS_OBJ_NAME_LEN + SPIFFS_META_LENGTH should not exceed + SPIFFS_PAGE_SIZE - 64. + +config SPIFFS_USE_MTIME + bool "Save file modification time" + default "y" + depends on SPIFFS_META_LENGTH >= 4 + help + If enabled, then the first 4 bytes of per-file metadata will be used + to store file modification time (mtime), accessible through + stat/fstat functions. + Modification time is updated when the file is opened. + +menu "Debug Configuration" + +config SPIFFS_DBG + bool "Enable general SPIFFS debug" + default "n" + help + Enabling this option will print general debug mesages to the console. + +config SPIFFS_API_DBG + bool "Enable SPIFFS API debug" + default "n" + help + Enabling this option will print API debug mesages to the console. + +config SPIFFS_GC_DBG + bool "Enable SPIFFS Garbage Cleaner debug" + default "n" + help + Enabling this option will print GC debug mesages to the console. + +config SPIFFS_CACHE_DBG + bool "Enable SPIFFS Cache debug" + default "n" + depends on SPIFFS_CACHE + help + Enabling this option will print cache debug mesages to the console. + +config SPIFFS_CHECK_DBG + bool "Enable SPIFFS Filesystem Check debug" + default "n" + help + Enabling this option will print Filesystem Check debug mesages + to the console. + +config SPIFFS_TEST_VISUALISATION + bool "Enable SPIFFS Filesystem Visualization" + default "n" + help + Enable this option to enable SPIFFS_vis function in the API. + +endmenu + +endif diff --git a/components/spiffs/component.mk b/components/spiffs/component.mk index ad45698a..916c7eb8 100644 --- a/components/spiffs/component.mk +++ b/components/spiffs/component.mk @@ -1,6 +1,9 @@ -# -# Component Makefile -# -COMPONENT_ADD_INCLUDEDIRS += include/spiffs - -COMPONENT_SRCDIRS := library +ifdef CONFIG_USING_SPIFFS +COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_PRIV_INCLUDEDIRS := . spiffs/src +COMPONENT_SRCDIRS := . spiffs/src +else +COMPONENT_ADD_INCLUDEDIRS := +COMPONENT_PRIV_INCLUDEDIRS := +COMPONENT_SRCDIRS := +endif \ No newline at end of file diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c new file mode 100644 index 00000000..853986e4 --- /dev/null +++ b/components/spiffs/esp_spiffs.c @@ -0,0 +1,746 @@ +// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp_spiffs.h" +#include "spiffs.h" +#include "spiffs_nucleus.h" +#include "esp_log.h" +#include "esp_partition.h" +#include "esp_spi_flash.h" +#include "esp_image_format.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include +#include +#include +#include +#include +#include "esp_vfs.h" +#include "esp_err.h" +#include "rom/spi_flash.h" +#include "spiffs_api.h" + +static const char* TAG = "SPIFFS"; + +#ifdef CONFIG_SPIFFS_USE_MTIME +_Static_assert(CONFIG_SPIFFS_META_LENGTH >= sizeof(time_t), + "SPIFFS_META_LENGTH size should be >= sizeof(time_t)"); +#endif //CONFIG_SPIFFS_USE_MTIME + +/** + * @brief SPIFFS DIR structure + */ +typedef struct { + DIR dir; /*!< VFS DIR struct */ + spiffs_DIR d; /*!< SPIFFS DIR struct */ + struct dirent e; /*!< Last open dirent */ + long offset; /*!< Offset of the current dirent */ + char path[SPIFFS_OBJ_NAME_LEN]; /*!< Requested directory name */ +} vfs_spiffs_dir_t; + +static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode); +static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size); +static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size); +static int vfs_spiffs_close(void* ctx, int fd); +static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode); +static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st); +static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st); +static int vfs_spiffs_unlink(void* ctx, const char *path); +static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2); +static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst); +static DIR* vfs_spiffs_opendir(void* ctx, const char* name); +static int vfs_spiffs_closedir(void* ctx, DIR* pdir); +static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir); +static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir, + struct dirent* entry, struct dirent** out_dirent); +static long vfs_spiffs_telldir(void* ctx, DIR* pdir); +static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset); +static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode); +static int vfs_spiffs_rmdir(void* ctx, const char* name); +static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file f); +static time_t vfs_spiffs_get_mtime(const spiffs_stat* s); + +static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS]; + +static void esp_spiffs_free(esp_spiffs_t ** efs) +{ + esp_spiffs_t * e = *efs; + if (*efs == NULL) { + return; + } + *efs = NULL; + + if (e->fs) { + SPIFFS_unmount(e->fs); + free(e->fs); + } + vSemaphoreDelete(e->lock); + free(e->fds); + free(e->cache); + free(e->work); + free(e); +} + +static esp_err_t esp_spiffs_by_label(const char* label, int * index){ + int i; + esp_spiffs_t * p; + for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) { + p = _efs[i]; + if (p) { + if (!label && !p->by_label) { + *index = i; + return ESP_OK; + } + if (label && p->by_label && strncmp(label, p->partition->label, 17) == 0) { + *index = i; + return ESP_OK; + } + } + } + return ESP_ERR_NOT_FOUND; +} + +static esp_err_t esp_spiffs_get_empty(int * index){ + int i; + for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) { + if (_efs[i] == NULL) { + *index = i; + return ESP_OK; + } + } + return ESP_ERR_NOT_FOUND; +} + +static esp_err_t esp_spiffs_init(const esp_vfs_spiffs_conf_t* conf) +{ + int index; + //find if such partition is already mounted + if (esp_spiffs_by_label(conf->partition_label, &index) == ESP_OK) { + return ESP_ERR_INVALID_STATE; + } + + if (esp_spiffs_get_empty(&index) != ESP_OK) { + ESP_LOGE(TAG, "max mounted partitions reached"); + return ESP_ERR_INVALID_STATE; + } + + uint32_t flash_page_size = g_rom_flashchip.page_size; + uint32_t log_page_size = CONFIG_SPIFFS_PAGE_SIZE; + if (log_page_size % flash_page_size != 0) { + ESP_LOGE(TAG, "SPIFFS_PAGE_SIZE is not multiple of flash chip page size (%d)", + flash_page_size); + return ESP_ERR_INVALID_ARG; + } + + esp_partition_subtype_t subtype = conf->partition_label ? + ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_SPIFFS; + const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, + subtype, conf->partition_label); + if (!partition) { + ESP_LOGE(TAG, "spiffs partition could not be found"); + return ESP_ERR_NOT_FOUND; + } + + if (partition->encrypted) { + ESP_LOGE(TAG, "spiffs can not run on encrypted partition"); + return ESP_ERR_INVALID_STATE; + } + + esp_spiffs_t * efs = malloc(sizeof(esp_spiffs_t)); + if (efs == NULL) { + ESP_LOGE(TAG, "esp_spiffs could not be malloced"); + return ESP_ERR_NO_MEM; + } + memset(efs, 0, sizeof(esp_spiffs_t)); + + efs->cfg.hal_erase_f = spiffs_api_erase; + efs->cfg.hal_read_f = spiffs_api_read; + efs->cfg.hal_write_f = spiffs_api_write; + efs->cfg.log_block_size = g_rom_flashchip.sector_size; + efs->cfg.log_page_size = log_page_size; + efs->cfg.phys_addr = 0; + efs->cfg.phys_erase_block = g_rom_flashchip.sector_size; + efs->cfg.phys_size = partition->size; + + efs->by_label = conf->partition_label != NULL; + + efs->lock = xSemaphoreCreateMutex(); + if (efs->lock == NULL) { + ESP_LOGE(TAG, "mutex lock could not be created"); + esp_spiffs_free(&efs); + return ESP_ERR_NO_MEM; + } + + efs->fds_sz = conf->max_files * sizeof(spiffs_fd); + efs->fds = malloc(efs->fds_sz); + if (efs->fds == NULL) { + ESP_LOGE(TAG, "fd buffer could not be malloced"); + esp_spiffs_free(&efs); + return ESP_ERR_NO_MEM; + } + memset(efs->fds, 0, efs->fds_sz); + +#if SPIFFS_CACHE + efs->cache_sz = sizeof(spiffs_cache) + conf->max_files * (sizeof(spiffs_cache_page) + + efs->cfg.log_page_size); + efs->cache = malloc(efs->cache_sz); + if (efs->cache == NULL) { + ESP_LOGE(TAG, "cache buffer could not be malloced"); + esp_spiffs_free(&efs); + return ESP_ERR_NO_MEM; + } + memset(efs->cache, 0, efs->cache_sz); +#endif + + const uint32_t work_sz = efs->cfg.log_page_size * 2; + efs->work = malloc(work_sz); + if (efs->work == NULL) { + ESP_LOGE(TAG, "work buffer could not be malloced"); + esp_spiffs_free(&efs); + return ESP_ERR_NO_MEM; + } + memset(efs->work, 0, work_sz); + + efs->fs = malloc(sizeof(spiffs)); + if (efs->fs == NULL) { + ESP_LOGE(TAG, "spiffs could not be malloced"); + esp_spiffs_free(&efs); + return ESP_ERR_NO_MEM; + } + memset(efs->fs, 0, sizeof(spiffs)); + + efs->fs->user_data = (void *)efs; + efs->partition = partition; + + s32_t res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz, + efs->cache, efs->cache_sz, spiffs_api_check); + + if (conf->format_if_mount_failed && res != SPIFFS_OK) { + ESP_LOGW(TAG, "mount failed, %i. formatting...", SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + res = SPIFFS_format(efs->fs); + if (res != SPIFFS_OK) { + ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + esp_spiffs_free(&efs); + return ESP_FAIL; + } + res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz, + efs->cache, efs->cache_sz, spiffs_api_check); + } + if (res != SPIFFS_OK) { + ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + esp_spiffs_free(&efs); + return ESP_FAIL; + } + _efs[index] = efs; + return ESP_OK; +} + +bool esp_spiffs_mounted(const char* partition_label) +{ + int index; + if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) { + return false; + } + return (SPIFFS_mounted(_efs[index]->fs)); +} + +esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes) +{ + int index; + if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) { + return ESP_ERR_INVALID_STATE; + } + SPIFFS_info(_efs[index]->fs, total_bytes, used_bytes); + return ESP_OK; +} + +esp_err_t esp_spiffs_format(const char* partition_label) +{ + bool partition_was_mounted = false; + int index; + /* If the partition is not mounted, need to create SPIFFS structures + * and mount the partition, unmount, format, delete SPIFFS structures. + * See SPIFFS wiki for the reason why. + */ + esp_err_t err = esp_spiffs_by_label(partition_label, &index); + if (err != ESP_OK) { + esp_vfs_spiffs_conf_t conf = { + .format_if_mount_failed = true, + .partition_label = partition_label, + .max_files = 1 + }; + err = esp_spiffs_init(&conf); + if (err != ESP_OK) { + return err; + } + err = esp_spiffs_by_label(partition_label, &index); + assert(err == ESP_OK && "failed to get index of the partition just mounted"); + } else if (SPIFFS_mounted(_efs[index]->fs)) { + partition_was_mounted = true; + } + + SPIFFS_unmount(_efs[index]->fs); + + s32_t res = SPIFFS_format(_efs[index]->fs); + if (res != SPIFFS_OK) { + ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(_efs[index]->fs)); + SPIFFS_clearerr(_efs[index]->fs); + /* If the partition was previously mounted, but format failed, don't + * try to mount the partition back (it will probably fail). On the + * other hand, if it was not mounted, need to clean up. + */ + if (!partition_was_mounted) { + esp_spiffs_free(&_efs[index]); + } + return ESP_FAIL; + } + + if (partition_was_mounted) { + res = SPIFFS_mount(_efs[index]->fs, &_efs[index]->cfg, _efs[index]->work, + _efs[index]->fds, _efs[index]->fds_sz, _efs[index]->cache, + _efs[index]->cache_sz, spiffs_api_check); + if (res != SPIFFS_OK) { + ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(_efs[index]->fs)); + SPIFFS_clearerr(_efs[index]->fs); + return ESP_FAIL; + } + } else { + esp_spiffs_free(&_efs[index]); + } + return ESP_OK; +} + +esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf) +{ + assert(conf->base_path); + const esp_vfs_t vfs = { + .flags = ESP_VFS_FLAG_CONTEXT_PTR, + .write_p = &vfs_spiffs_write, + .lseek_p = &vfs_spiffs_lseek, + .read_p = &vfs_spiffs_read, + .open_p = &vfs_spiffs_open, + .close_p = &vfs_spiffs_close, + .fstat_p = &vfs_spiffs_fstat, + .stat_p = &vfs_spiffs_stat, + .link_p = &vfs_spiffs_link, + .unlink_p = &vfs_spiffs_unlink, + .rename_p = &vfs_spiffs_rename, + .opendir_p = &vfs_spiffs_opendir, + .closedir_p = &vfs_spiffs_closedir, + .readdir_p = &vfs_spiffs_readdir, + .readdir_r_p = &vfs_spiffs_readdir_r, + .seekdir_p = &vfs_spiffs_seekdir, + .telldir_p = &vfs_spiffs_telldir, + .mkdir_p = &vfs_spiffs_mkdir, + .rmdir_p = &vfs_spiffs_rmdir + }; + + esp_err_t err = esp_spiffs_init(conf); + if (err != ESP_OK) { + return err; + } + + int index; + if (esp_spiffs_by_label(conf->partition_label, &index) != ESP_OK) { + return ESP_ERR_INVALID_STATE; + } + + strlcat(_efs[index]->base_path, conf->base_path, ESP_VFS_PATH_MAX + 1); + err = esp_vfs_register(conf->base_path, &vfs, _efs[index]); + if (err != ESP_OK) { + esp_spiffs_free(&_efs[index]); + return err; + } + + return ESP_OK; +} + +esp_err_t esp_vfs_spiffs_unregister(const char* partition_label) +{ + int index; + if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) { + return ESP_ERR_INVALID_STATE; + } + esp_err_t err = esp_vfs_unregister(_efs[index]->base_path); + if (err != ESP_OK) { + return err; + } + esp_spiffs_free(&_efs[index]); + return ESP_OK; +} + +static int spiffs_res_to_errno(s32_t fr) +{ + switch(fr) { + case SPIFFS_OK : + return 0; + case SPIFFS_ERR_NOT_MOUNTED : + return ENODEV; + case SPIFFS_ERR_NOT_A_FS : + return ENODEV; + case SPIFFS_ERR_FULL : + return ENOSPC; + case SPIFFS_ERR_BAD_DESCRIPTOR : + return EBADF; + case SPIFFS_ERR_MOUNTED : + return EEXIST; + case SPIFFS_ERR_FILE_EXISTS : + return EEXIST; + case SPIFFS_ERR_NOT_FOUND : + return ENOENT; + case SPIFFS_ERR_NOT_A_FILE : + return ENOENT; + case SPIFFS_ERR_DELETED : + return ENOENT; + case SPIFFS_ERR_FILE_DELETED : + return ENOENT; + case SPIFFS_ERR_NAME_TOO_LONG : + return ENAMETOOLONG; + case SPIFFS_ERR_RO_NOT_IMPL : + return EROFS; + case SPIFFS_ERR_RO_ABORTED_OPERATION : + return EROFS; + default : + return EIO; + } + return ENOTSUP; +} + +static int spiffs_mode_conv(int m) +{ + int res = 0; + int acc_mode = m & O_ACCMODE; + if (acc_mode == O_RDONLY) { + res |= SPIFFS_O_RDONLY; + } else if (acc_mode == O_WRONLY) { + res |= SPIFFS_O_WRONLY; + } else if (acc_mode == O_RDWR) { + res |= SPIFFS_O_RDWR; + } + if ((m & O_CREAT) && (m & O_EXCL)) { + res |= SPIFFS_O_CREAT | SPIFFS_O_EXCL; + } else if ((m & O_CREAT) && (m & O_TRUNC)) { + res |= SPIFFS_O_CREAT | SPIFFS_O_TRUNC; + } + if (m & O_APPEND) { + res |= SPIFFS_O_CREAT | SPIFFS_O_APPEND; + } + return res; +} + +static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode) +{ + assert(path); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + int spiffs_flags = spiffs_mode_conv(flags); + int fd = SPIFFS_open(efs->fs, path, spiffs_flags, mode); + if (fd < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + if (!(spiffs_flags & SPIFFS_RDONLY)) { + vfs_spiffs_update_mtime(efs->fs, fd); + } + return fd; +} + +static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size) +{ + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + ssize_t res = SPIFFS_write(efs->fs, fd, (void *)data, size); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size) +{ + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + ssize_t res = SPIFFS_read(efs->fs, fd, dst, size); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static int vfs_spiffs_close(void* ctx, int fd) +{ + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + int res = SPIFFS_close(efs->fs, fd); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode) +{ + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + off_t res = SPIFFS_lseek(efs->fs, fd, offset, mode); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st) +{ + assert(st); + spiffs_stat s; + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + off_t res = SPIFFS_fstat(efs->fs, fd, &s); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + st->st_size = s.size; + st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG; + st->st_mtime = vfs_spiffs_get_mtime(&s); + st->st_atime = 0; + st->st_ctime = 0; + return res; +} + +static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st) +{ + assert(path); + assert(st); + spiffs_stat s; + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + off_t res = SPIFFS_stat(efs->fs, path, &s); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + + st->st_size = s.size; + st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO; + st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG; + st->st_mtime = vfs_spiffs_get_mtime(&s); + st->st_atime = 0; + st->st_ctime = 0; + return res; +} + +static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst) +{ + assert(src); + assert(dst); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + int res = SPIFFS_rename(efs->fs, src, dst); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static int vfs_spiffs_unlink(void* ctx, const char *path) +{ + assert(path); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + int res = SPIFFS_remove(efs->fs, path); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static DIR* vfs_spiffs_opendir(void* ctx, const char* name) +{ + assert(name); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + vfs_spiffs_dir_t * dir = calloc(1, sizeof(vfs_spiffs_dir_t)); + if (!dir) { + errno = ENOMEM; + return NULL; + } + if (!SPIFFS_opendir(efs->fs, name, &dir->d)) { + free(dir); + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return NULL; + } + dir->offset = 0; + strlcpy(dir->path, name, SPIFFS_OBJ_NAME_LEN); + return (DIR*) dir; +} + +static int vfs_spiffs_closedir(void* ctx, DIR* pdir) +{ + assert(pdir); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir; + int res = SPIFFS_closedir(&dir->d); + free(dir); + if (res < 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return -1; + } + return res; +} + +static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir) +{ + assert(pdir); + vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir; + struct dirent* out_dirent; + int err = vfs_spiffs_readdir_r(ctx, pdir, &dir->e, &out_dirent); + if (err != 0) { + errno = err; + return NULL; + } + return out_dirent; +} + +static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir, struct dirent* entry, + struct dirent** out_dirent) +{ + assert(pdir); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir; + struct spiffs_dirent out; + size_t plen; + char * item_name; + do { + if (SPIFFS_readdir(&dir->d, &out) == 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + if (!errno) { + *out_dirent = NULL; + } + return errno; + } + item_name = (char *)out.name; + plen = strlen(dir->path); + + } while ((plen > 1) && (strncasecmp(dir->path, (const char*)out.name, plen) || out.name[plen] != '/' || !out.name[plen + 1])); + + if (plen > 1) { + item_name += plen + 1; + } else if (item_name[0] == '/') { + item_name++; + } + entry->d_ino = 0; + entry->d_type = out.type; + snprintf(entry->d_name, SPIFFS_OBJ_NAME_LEN, "%s", item_name); + dir->offset++; + *out_dirent = entry; + return 0; +} + +static long vfs_spiffs_telldir(void* ctx, DIR* pdir) +{ + assert(pdir); + vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir; + return dir->offset; +} + +static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset) +{ + assert(pdir); + esp_spiffs_t * efs = (esp_spiffs_t *)ctx; + vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir; + struct spiffs_dirent tmp; + if (offset < dir->offset) { + //rewind dir + SPIFFS_closedir(&dir->d); + if (!SPIFFS_opendir(efs->fs, NULL, &dir->d)) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return; + } + dir->offset = 0; + } + while (dir->offset < offset) { + if (SPIFFS_readdir(&dir->d, &tmp) == 0) { + errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs)); + SPIFFS_clearerr(efs->fs); + return; + } + size_t plen = strlen(dir->path); + if (plen > 1) { + if (strncasecmp(dir->path, (const char *)tmp.name, plen) || tmp.name[plen] != '/' || !tmp.name[plen+1]) { + continue; + } + } + dir->offset++; + } +} + +static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode) +{ + errno = ENOTSUP; + return -1; +} + +static int vfs_spiffs_rmdir(void* ctx, const char* name) +{ + errno = ENOTSUP; + return -1; +} + +static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2) +{ + errno = ENOTSUP; + return -1; +} + +static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file fd) +{ +#ifdef CONFIG_SPIFFS_USE_MTIME + time_t t = time(NULL); + spiffs_stat s; + int ret = SPIFFS_OK; + if (CONFIG_SPIFFS_META_LENGTH > sizeof(t)) { + ret = SPIFFS_fstat(fs, fd, &s); + } + if (ret == SPIFFS_OK) { + memcpy(s.meta, &t, sizeof(t)); + ret = SPIFFS_fupdate_meta(fs, fd, s.meta); + } + if (ret != SPIFFS_OK) { + ESP_LOGW(TAG, "Failed to update mtime (%d)", ret); + } +#endif //CONFIG_SPIFFS_USE_MTIME +} + +static time_t vfs_spiffs_get_mtime(const spiffs_stat* s) +{ + time_t t = 0; +#ifdef CONFIG_SPIFFS_USE_MTIME + memcpy(&t, s->meta, sizeof(t)); +#endif + return t; +} diff --git a/components/spiffs/include/esp_spiffs.h b/components/spiffs/include/esp_spiffs.h new file mode 100644 index 00000000..ae1b9ad4 --- /dev/null +++ b/components/spiffs/include/esp_spiffs.h @@ -0,0 +1,102 @@ +// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP_SPIFFS_H_ +#define _ESP_SPIFFS_H_ + +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configuration structure for esp_vfs_spiffs_register + */ +typedef struct { + const char* base_path; /*!< File path prefix associated with the filesystem. */ + const char* partition_label; /*!< Optional, label of SPIFFS partition to use. If set to NULL, first partition with subtype=spiffs will be used. */ + size_t max_files; /*!< Maximum files that could be open at the same time. */ + bool format_if_mount_failed; /*!< If true, it will format the file system if it fails to mount. */ +} esp_vfs_spiffs_conf_t; + +/** + * Register and mount SPIFFS to VFS with given path prefix. + * + * @param conf Pointer to esp_vfs_spiffs_conf_t configuration structure + * + * @return + * - ESP_OK if success + * - ESP_ERR_NO_MEM if objects could not be allocated + * - ESP_ERR_INVALID_STATE if already mounted or partition is encrypted + * - ESP_ERR_NOT_FOUND if partition for SPIFFS was not found + * - ESP_FAIL if mount or format fails + */ +esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf); + +/** + * Unregister and unmount SPIFFS from VFS + * + * @param partition_label Optional, label of the partition to unregister. + * If not specified, first partition with subtype=spiffs is used. + * + * @return + * - ESP_OK if successful + * - ESP_ERR_INVALID_STATE already unregistered + */ +esp_err_t esp_vfs_spiffs_unregister(const char* partition_label); + +/** + * Check if SPIFFS is mounted + * + * @param partition_label Optional, label of the partition to check. + * If not specified, first partition with subtype=spiffs is used. + * + * @return + * - true if mounted + * - false if not mounted + */ +bool esp_spiffs_mounted(const char* partition_label); + +/** + * Format the SPIFFS partition + * + * @param partition_label Optional, label of the partition to format. + * If not specified, first partition with subtype=spiffs is used. + * @return + * - ESP_OK if successful + * - ESP_FAIL on error + */ +esp_err_t esp_spiffs_format(const char* partition_label); + +/** + * Get information for SPIFFS + * + * @param partition_label Optional, label of the partition to get info for. + * If not specified, first partition with subtype=spiffs is used. + * @param[out] total_bytes Size of the file system + * @param[out] used_bytes Current used bytes in the file system + * + * @return + * - ESP_OK if success + * - ESP_ERR_INVALID_STATE if not mounted + */ +esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes); + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP_SPIFFS_H_ */ diff --git a/components/spiffs/include/spiffs/esp_spiffs.h b/components/spiffs/include/spiffs/esp_spiffs.h deleted file mode 100644 index d02340c8..00000000 --- a/components/spiffs/include/spiffs/esp_spiffs.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * ESPRSSIF MIT License - * - * Copyright (c) 2015 - * - * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef __ESP_SPIFFS_H__ -#define __ESP_SPIFFS_H__ - -#include "spiffs/spiffs.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** \defgroup Spiffs_APIs Spiffs APIs - * @brief Spiffs APIs - * - * More details about spiffs on https://github.com/pellepl/spiffs - * - */ - -/** @addtogroup Spiffs_APIs - * @{ - */ - -struct esp_spiffs_config { - u32_t phys_size; /**< physical size of the SPI Flash */ - u32_t phys_addr; /**< physical offset in spi flash used for spiffs, must be on block boundary */ - u32_t phys_erase_block; /**< physical size when erasing a block */ - - u32_t log_block_size; /**< logical size of a block, must be on physical block size boundary and must never be less than a physical block */ - u32_t log_page_size; /**< logical size of a page, at least log_block_size/8 */ - - u32_t fd_buf_size; /**< file descriptor memory area size */ - u32_t cache_buf_size; /**< cache buffer size */ -}; - -/** - * @brief Initialize spiffs - * - * @param struct esp_spiffs_config *config : ESP8266 spiffs configuration - * - * @return 0 : succeed (Equals SPIFFS_OK) - * @return otherwise : fail (-1 or SPIFFS_ERR_*) - */ -s32_t esp_spiffs_init(struct esp_spiffs_config *config); - -/** - * @brief Deinitialize spiffs - * - * @param uint8 format : 0, only deinit; otherwise, deinit spiffs and format. - * - * @return null - */ -void esp_spiffs_deinit(u8_t format); - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __ESP_SPIFFS_H__ */ diff --git a/components/spiffs/include/spiffs_config.h b/components/spiffs/include/spiffs_config.h new file mode 100644 index 00000000..1a36a7c6 --- /dev/null +++ b/components/spiffs/include/spiffs_config.h @@ -0,0 +1,316 @@ +/* + * spiffs_config.h + * + * Created on: Jul 3, 2013 + * Author: petera + */ + +#ifndef SPIFFS_CONFIG_H_ +#define SPIFFS_CONFIG_H_ + +// ----------- 8< ------------ +// Following includes are for the linux test build of spiffs +// These may/should/must be removed/altered/replaced in your target +#include +#include +#include +#include +#include +#include +#include +#include + +// compile time switches +#define SPIFFS_TAG "SPIFFS" + +// Set generic spiffs debug output call. +#if CONFIG_SPIFFS_DBG +#define SPIFFS_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__) +#else +#define SPIFFS_DBG(...) +#endif +#if CONFIG_SPIFFS_API_DBG +#define SPIFFS_API_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__) +#else +#define SPIFFS_API_DBG(...) +#endif +#if CONFIG_SPIFFS_DBG +#define SPIFFS_GC_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__) +#else +#define SPIFFS_GC_DBG(...) +#endif +#if CONFIG_SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__) +#else +#define SPIFFS_CACHE_DBG(...) +#endif +#if CONFIG_SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__) +#else +#define SPIFFS_CHECK_DBG(...) +#endif + +// needed types +typedef signed int s32_t; +typedef unsigned int u32_t; +typedef signed short s16_t; +typedef unsigned short u16_t; +typedef signed char s8_t; +typedef unsigned char u8_t; + +struct spiffs_t; +extern void spiffs_api_lock(struct spiffs_t *fs); +extern void spiffs_api_unlock(struct spiffs_t *fs); + +// Defines spiffs debug print formatters +// some general signed number +#define _SPIPRIi "%d" +// address +#define _SPIPRIad "%08x" +// block +#define _SPIPRIbl "%04x" +// page +#define _SPIPRIpg "%04x" +// span index +#define _SPIPRIsp "%04x" +// file descriptor +#define _SPIPRIfd "%d" +// file object id +#define _SPIPRIid "%04x" +// file flags +#define _SPIPRIfl "%02x" + + +// Enable/disable API functions to determine exact number of bytes +// for filedescriptor and cache buffers. Once decided for a configuration, +// this can be disabled to reduce flash. +#define SPIFFS_BUFFER_HELP 0 + +// Enables/disable memory read caching of nucleus file system operations. +// If enabled, memory area must be provided for cache in SPIFFS_mount. +#ifdef CONFIG_SPIFFS_CACHE +#define SPIFFS_CACHE (1) +#else +#define SPIFFS_CACHE (0) +#endif +#if SPIFFS_CACHE +// Enables memory write caching for file descriptors in hydrogen +#ifdef CONFIG_SPIFFS_CACHE_WR +#define SPIFFS_CACHE_WR (1) +#else +#define SPIFFS_CACHE_WR (0) +#endif + +// Enable/disable statistics on caching. Debug/test purpose only. +#ifdef CONFIG_SPIFFS_CACHE_STATS +#define SPIFFS_CACHE_STATS (1) +#else +#define SPIFFS_CACHE_STATS (0) +#endif +#endif + +// Always check header of each accessed page to ensure consistent state. +// If enabled it will increase number of reads, will increase flash. +#ifdef CONFIG_SPIFFS_PAGE_CHECK +#define SPIFFS_PAGE_CHECK (1) +#else +#define SPIFFS_PAGE_CHECK (0) +#endif + +// Define maximum number of gc runs to perform to reach desired free pages. +#define SPIFFS_GC_MAX_RUNS CONFIG_SPIFFS_GC_MAX_RUNS + +// Enable/disable statistics on gc. Debug/test purpose only. +#ifdef CONFIG_SPIFFS_GC_STATS +#define SPIFFS_GC_STATS (1) +#else +#define SPIFFS_GC_STATS (0) +#endif + +// Garbage collecting examines all pages in a block which and sums up +// to a block score. Deleted pages normally gives positive score and +// used pages normally gives a negative score (as these must be moved). +// To have a fair wear-leveling, the erase age is also included in score, +// whose factor normally is the most positive. +// The larger the score, the more likely it is that the block will +// picked for garbage collection. + +// Garbage collecting heuristics - weight used for deleted pages. +#define SPIFFS_GC_HEUR_W_DELET (5) +// Garbage collecting heuristics - weight used for used pages. +#define SPIFFS_GC_HEUR_W_USED (-1) +// Garbage collecting heuristics - weight used for time between +// last erased and erase of this block. +#define SPIFFS_GC_HEUR_W_ERASE_AGE (50) + +// Object name maximum length. Note that this length include the +// zero-termination character, meaning maximum string of characters +// can at most be SPIFFS_OBJ_NAME_LEN - 1. +#define SPIFFS_OBJ_NAME_LEN (CONFIG_SPIFFS_OBJ_NAME_LEN) + +// Maximum length of the metadata associated with an object. +// Setting to non-zero value enables metadata-related API but also +// changes the on-disk format, so the change is not backward-compatible. +// +// Do note: the meta length must never exceed +// logical_page_size - (SPIFFS_OBJ_NAME_LEN + SPIFFS_PAGE_EXTRA_SIZE) +// +// This is derived from following: +// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) + +// spiffs_object_ix_header fields + at least some LUT entries) +#define SPIFFS_OBJ_META_LEN (CONFIG_SPIFFS_META_LENGTH) +#define SPIFFS_PAGE_EXTRA_SIZE (64) +_Static_assert(SPIFFS_OBJ_META_LEN + SPIFFS_OBJ_NAME_LEN + SPIFFS_PAGE_EXTRA_SIZE + <= CONFIG_SPIFFS_PAGE_SIZE, "SPIFFS_OBJ_META_LEN or SPIFFS_OBJ_NAME_LEN too long"); + +// Size of buffer allocated on stack used when copying data. +// Lower value generates more read/writes. No meaning having it bigger +// than logical page size. +#define SPIFFS_COPY_BUFFER_STACK (256) + +// Enable this to have an identifiable spiffs filesystem. This will look for +// a magic in all sectors to determine if this is a valid spiffs system or +// not on mount point. If not, SPIFFS_format must be called prior to mounting +// again. +#ifdef CONFIG_SPIFFS_USE_MAGIC +#define SPIFFS_USE_MAGIC (1) +#else +#define SPIFFS_USE_MAGIC (0) +#endif + +#if SPIFFS_USE_MAGIC +// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is +// enabled, the magic will also be dependent on the length of the filesystem. +// For example, a filesystem configured and formatted for 4 megabytes will not +// be accepted for mounting with a configuration defining the filesystem as 2 +// megabytes. +#ifdef CONFIG_SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_USE_MAGIC_LENGTH (1) +#else +#define SPIFFS_USE_MAGIC_LENGTH (0) +#endif +#endif + +// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level +// These should be defined on a multithreaded system + +// define this to enter a mutex if you're running on a multithreaded system +#define SPIFFS_LOCK(fs) spiffs_api_lock(fs) +// define this to exit a mutex if you're running on a multithreaded system +#define SPIFFS_UNLOCK(fs) spiffs_api_unlock(fs) + +// Enable if only one spiffs instance with constant configuration will exist +// on the target. This will reduce calculations, flash and memory accesses. +// Parts of configuration must be defined below instead of at time of mount. +#define SPIFFS_SINGLETON 0 + +// Enable this if your target needs aligned data for index tables +#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 4 + +// Enable this if you want the HAL callbacks to be called with the spiffs struct +#define SPIFFS_HAL_CALLBACK_EXTRA 1 + +// Enable this if you want to add an integer offset to all file handles +// (spiffs_file). This is useful if running multiple instances of spiffs on +// same target, in order to recognise to what spiffs instance a file handle +// belongs. +// NB: This adds config field fh_ix_offset in the configuration struct when +// mounting, which must be defined. +#define SPIFFS_FILEHDL_OFFSET 0 + +// Enable this to compile a read only version of spiffs. +// This will reduce binary size of spiffs. All code comprising modification +// of the file system will not be compiled. Some config will be ignored. +// HAL functions for erasing and writing to spi-flash may be null. Cache +// can be disabled for even further binary size reduction (and ram savings). +// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL. +// If the file system cannot be mounted due to aborted erase operation and +// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be +// returned. +// Might be useful for e.g. bootloaders and such. +#define SPIFFS_READ_ONLY 0 + +// Enable this to add a temporal file cache using the fd buffer. +// The effects of the cache is that SPIFFS_open will find the file faster in +// certain cases. It will make it a lot easier for spiffs to find files +// opened frequently, reducing number of readings from the spi flash for +// finding those files. +// This will grow each fd by 6 bytes. If your files are opened in patterns +// with a degree of temporal locality, the system is optimized. +// Examples can be letting spiffs serve web content, where one file is the css. +// The css is accessed for each html file that is opened, meaning it is +// accessed almost every second time a file is opened. Another example could be +// a log file that is often opened, written, and closed. +// The size of the cache is number of given file descriptors, as it piggybacks +// on the fd update mechanism. The cache lives in the closed file descriptors. +// When closed, the fd know the whereabouts of the file. Instead of forgetting +// this, the temporal cache will keep handling updates to that file even if the +// fd is closed. If the file is opened again, the location of the file is found +// directly. If all available descriptors become opened, all cache memory is +// lost. +#define SPIFFS_TEMPORAL_FD_CACHE 1 + +// Temporal file cache hit score. Each time a file is opened, all cached files +// will lose one point. If the opened file is found in cache, that entry will +// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this +// value for the specific access patterns of the application. However, it must +// be between 1 (no gain for hitting a cached entry often) and 255. +#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE 4 + +// Enable to be able to map object indices to memory. +// This allows for faster and more deterministic reading if cases of reading +// large files and when changing file offset by seeking around a lot. +// When mapping a file's index, the file system will be scanned for index pages +// and the info will be put in memory provided by user. When reading, the +// memory map can be looked up instead of searching for index pages on the +// medium. This way, user can trade memory against performance. +// Whole, parts of, or future parts not being written yet can be mapped. The +// memory array will be owned by spiffs and updated accordingly during garbage +// collecting or when modifying the indices. The latter is invoked by when the +// file is modified in some way. The index buffer is tied to the file +// descriptor. +#define SPIFFS_IX_MAP 1 + +// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function +// in the api. This function will visualize all filesystem using given printf +// function. +#ifdef CONFIG_SPIFFS_TEST_VISUALISATION +#define SPIFFS_TEST_VISUALISATION 1 +#else +#define SPIFFS_TEST_VISUALISATION 0 +#endif +#if SPIFFS_TEST_VISUALISATION +#ifndef spiffs_printf +#define spiffs_printf(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__) +#endif +// spiffs_printf argument for a free page +#define SPIFFS_TEST_VIS_FREE_STR "_" +// spiffs_printf argument for a deleted page +#define SPIFFS_TEST_VIS_DELE_STR "/" +// spiffs_printf argument for an index page for given object id +#define SPIFFS_TEST_VIS_INDX_STR(id) "i" +// spiffs_printf argument for a data page for given object id +#define SPIFFS_TEST_VIS_DATA_STR(id) "d" +#endif + +// Types depending on configuration such as the amount of flash bytes +// given to spiffs file system in total (spiffs_file_system_size), +// the logical block size (log_block_size), and the logical page size +// (log_page_size) + +// Block index type. Make sure the size of this type can hold +// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size +typedef u16_t spiffs_block_ix; +// Page index type. Make sure the size of this type can hold +// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size +typedef u16_t spiffs_page_ix; +// Object id type - most significant bit is reserved for index flag. Make sure the +// size of this type can hold the highest object id on a full system, +// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2 +typedef u16_t spiffs_obj_id; +// Object span index type. Make sure the size of this type can +// hold the largest possible span index on the system - +// i.e. (spiffs_file_system_size / log_page_size) - 1 +typedef u16_t spiffs_span_ix; + +#endif /* SPIFFS_CONFIG_H_ */ diff --git a/components/spiffs/library/esp_spiffs.c b/components/spiffs/library/esp_spiffs.c deleted file mode 100644 index b5d77a89..00000000 --- a/components/spiffs/library/esp_spiffs.c +++ /dev/null @@ -1,311 +0,0 @@ -#include -#include - -#include "esp_spiffs.h" -#include "spi_flash.h" - -#define NUM_SYS_FD 3 - -static spiffs fs; - -static u8_t *spiffs_work_buf; -static u8_t *spiffs_fd_buf; -static u8_t *spiffs_cache_buf; - -#define FLASH_UNIT_SIZE 4 - -static s32_t esp_spiffs_readwrite(u32_t addr, u32_t size, u8_t *p, int write) -{ - /* - * With proper configurarion spiffs never reads or writes more than - * LOG_PAGE_SIZE - */ - - if (size > fs.cfg.log_page_size) { - printf("Invalid size provided to read/write (%d)\n\r", (int) size); - return SPIFFS_ERR_NOT_CONFIGURED; - } - - char tmp_buf[fs.cfg.log_page_size + FLASH_UNIT_SIZE * 2]; - u32_t aligned_addr = addr & (-FLASH_UNIT_SIZE); - u32_t aligned_size = - ((size + (FLASH_UNIT_SIZE - 1)) & -FLASH_UNIT_SIZE) + FLASH_UNIT_SIZE; - - int res = spi_flash_read(aligned_addr, (u32_t *) tmp_buf, aligned_size); - - if (res != 0) { - printf("spi_flash_read failed: %d (%d, %d)\n\r", res, (int) aligned_addr, - (int) aligned_size); - return res; - } - - if (!write) { - memcpy(p, tmp_buf + (addr - aligned_addr), size); - return SPIFFS_OK; - } - - memcpy(tmp_buf + (addr - aligned_addr), p, size); - - res = spi_flash_write(aligned_addr, (u32_t *) tmp_buf, aligned_size); - - if (res != 0) { -// printf("spi_flash_write failed: %d (%d, %d)\n\r", res, -// (int) aligned_addr, (int) aligned_size); - return res; - } - - return SPIFFS_OK; -} - -static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) -{ - return esp_spiffs_readwrite(addr, size, dst, 0); -} - -static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) -{ - return esp_spiffs_readwrite(addr, size, src, 1); -} - -static s32_t esp_spiffs_erase(u32_t addr, u32_t size) -{ - /* - * With proper configurarion spiffs always - * provides here sector address & sector size - */ - if (size != fs.cfg.phys_erase_block || addr % fs.cfg.phys_erase_block != 0) { - printf("Invalid size provided to esp_spiffs_erase (%d, %d)\n\r", - (int) addr, (int) size); - return SPIFFS_ERR_NOT_CONFIGURED; - } - - return spi_flash_erase_sector(addr / fs.cfg.phys_erase_block); -} - -s32_t esp_spiffs_init(struct esp_spiffs_config *config) -{ - if (SPIFFS_mounted(&fs)) { - return SPIFFS_ERR_MOUNTED; - } - - spiffs_config cfg; - s32_t ret; - - cfg.phys_size = config->phys_size; - cfg.phys_addr = config->phys_addr; - cfg.phys_erase_block = config->phys_erase_block; - cfg.log_block_size = config->log_block_size; - cfg.log_page_size = config->log_page_size; - - cfg.hal_read_f = esp_spiffs_read; - cfg.hal_write_f = esp_spiffs_write; - cfg.hal_erase_f = esp_spiffs_erase; - - if (spiffs_work_buf != NULL) { - free(spiffs_work_buf); - spiffs_work_buf = NULL; - } - spiffs_work_buf = malloc(config->log_page_size * 2); - - if (spiffs_work_buf == NULL) { - return -1; - } - - if (spiffs_fd_buf != NULL) { - free(spiffs_fd_buf); - spiffs_fd_buf = NULL; - } - spiffs_fd_buf = malloc(config->fd_buf_size); - - if (spiffs_fd_buf == NULL) { - free(spiffs_work_buf); - return -1; - } - - if (spiffs_cache_buf != NULL) { - free(spiffs_cache_buf); - spiffs_cache_buf = NULL; - } - spiffs_cache_buf = malloc(config->cache_buf_size); - - if (spiffs_cache_buf == NULL) { - free(spiffs_work_buf); - free(spiffs_fd_buf); - return -1; - } - - ret = SPIFFS_mount(&fs, &cfg, spiffs_work_buf, - spiffs_fd_buf, config->fd_buf_size, - spiffs_cache_buf, config->cache_buf_size, - 0); - - if (ret == -1) { - free(spiffs_work_buf); - free(spiffs_fd_buf); - free(spiffs_cache_buf); - } - - ret = SPIFFS_errno(&fs); - - return ret; -} - -void esp_spiffs_deinit(u8_t format) -{ - if (SPIFFS_mounted(&fs)) { - SPIFFS_unmount(&fs); - free(spiffs_work_buf); - free(spiffs_fd_buf); - free(spiffs_cache_buf); - } - if (format) { - SPIFFS_format(&fs); - } -} - -int _spiffs_open_r(struct _reent *r, const char *filename, int flags, int mode) -{ - spiffs_mode sm = 0; - int res; - int rw = (flags & 3); - - if (rw == O_RDONLY || rw == O_RDWR) { - sm |= SPIFFS_RDONLY; - } - - if (rw == O_WRONLY || rw == O_RDWR) { - sm |= SPIFFS_WRONLY; - } - - if (flags & O_CREAT) { - sm |= SPIFFS_CREAT; - } - - if (flags & O_TRUNC) { - sm |= SPIFFS_TRUNC; - } - - if (flags & O_APPEND) { - sm |= SPIFFS_APPEND; - } - - /* Supported in newer versions of SPIFFS. */ - /* if (flags && O_EXCL) sm |= SPIFFS_EXCL; */ - /* if (flags && O_DIRECT) sm |= SPIFFS_DIRECT; */ - - res = SPIFFS_open(&fs, (char *) filename, sm, 0); - - if (res >= 0) { - res += NUM_SYS_FD; - } - else { - res = SPIFFS_errno(&fs); - } - return res; -} - -_ssize_t _spiffs_read_r(struct _reent *r, int fd, void *buf, size_t len) -{ - - ssize_t res; - - if (fd < NUM_SYS_FD) { - res = -1; - } else { - res = SPIFFS_read(&fs, fd - NUM_SYS_FD, buf, len); - if(res < 0) { - res = SPIFFS_errno(&fs); - } - } - - return res; -} - -_ssize_t _spiffs_write_r(struct _reent *r, int fd, void *buf, size_t len) -{ - - if (fd < NUM_SYS_FD) { - return -1; - } - - int res = SPIFFS_write(&fs, fd - NUM_SYS_FD, (char *) buf, len); - if(res < 0){ - return SPIFFS_errno(&fs); - } - return res; -} - -_off_t _spiffs_lseek_r(struct _reent *r, int fd, _off_t where, int whence) -{ - - ssize_t res; - - if (fd < NUM_SYS_FD) { - res = -1; - } else { - res = SPIFFS_lseek(&fs, fd - NUM_SYS_FD, where, whence); - if(res < 0) { - res = SPIFFS_errno(&fs); - } - } - - return res; -} - -int _spiffs_close_r(struct _reent *r, int fd) -{ - - if (fd < NUM_SYS_FD) { - return -1; - } - - SPIFFS_close(&fs, fd - NUM_SYS_FD); - return 0; -} - -int _spiffs_rename_r(struct _reent *r, const char *from, const char *to) -{ - - int res = SPIFFS_rename(&fs, (char *) from, (char *) to); - if(res < 0) { - res = SPIFFS_errno(&fs); - } - return res; -} - -int _spiffs_unlink_r(struct _reent *r, const char *filename) -{ - - int res = SPIFFS_remove(&fs, (char *) filename); - if(res < 0) { - res = SPIFFS_errno(&fs); - } - return res; -} - -int _spiffs_fstat_r(struct _reent *r, int fd, struct stat *s) -{ - - int res; - spiffs_stat ss; - memset(s, 0, sizeof(*s)); - - if (fd < NUM_SYS_FD) { - s->st_ino = fd; - s->st_rdev = fd; - s->st_mode = S_IFCHR | 0666; - return 0; - } - - res = SPIFFS_fstat(&fs, fd - NUM_SYS_FD, &ss); - - if (res < 0) { - return SPIFFS_errno(&fs); - } - - s->st_ino = ss.obj_id; - s->st_mode = 0666; - s->st_nlink = 1; - s->st_size = ss.size; - return 0; -} diff --git a/components/spiffs/spiffs/.travis.yml b/components/spiffs/spiffs/.travis.yml new file mode 100644 index 00000000..8c36dc57 --- /dev/null +++ b/components/spiffs/spiffs/.travis.yml @@ -0,0 +1,8 @@ +language: c + +compiler: + - gcc + +before_script: + +script: make all && make clean && make test && make build-all && make clean test FLAGS=-DSPIFFS_OBJ_META_LEN=8 diff --git a/components/spiffs/spiffs/FUZZING.md b/components/spiffs/spiffs/FUZZING.md new file mode 100644 index 00000000..f5178005 --- /dev/null +++ b/components/spiffs/spiffs/FUZZING.md @@ -0,0 +1,47 @@ +# Fuzzing SPIFFS + +The SPIFFS test suite includes a test program designed for fuzzing with +[AFL](http://lcamtuf.coredump.cx/afl/). This automatically exercises the +SPIFFS API and verifies that the file system does not crash or interact incorrectly +with the flash chip. + +There are two steps to fuzzing. The first is to build the test suite with +the AFL version of gcc. The CC variable should point to your copy of afl-gcc. + +``` +make clean test CC=/usr/local/bin/afl-gcc +``` + +There is a new test `afl_test` that reads from stdin a list of commands +and arguments. These are interpreted and executed on the API. The `afltests` +directory contains a number of test cases that can be fed to the `afl_test` test. + + +The second is to run this test suite under afl as follows (where findings is +the output directory): + +``` +afl-fuzz -i afltests -o findings ./build/linux_spiffs_test -f afl_test +``` + +This run will take hours (or days) and will (hopefully) not find any crashes. +If a crash (or hang) is found, then the input file that caused the crash is +saved. This allows the specific test case to be debugged. + +## Reducing the size of the file + +AFL comes with `afl-tmin` which can reduce the size of the test input file to +make it easier to debug. + +``` +afl-tmin -i findings/crashes/ -o smalltest -- build/linux_spiffs_test -f afl_test +``` + +This will write a short version of the testcase file to `smalltest`. This can then be +fed into the test program for debugging: + +``` +build/linux_spiffs_test -f afl_test < smalltest +``` + +This should still crash, but allows it to be run under a debugger. diff --git a/components/spiffs/spiffs/LICENSE b/components/spiffs/spiffs/LICENSE new file mode 100644 index 00000000..5fb2427b --- /dev/null +++ b/components/spiffs/spiffs/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/components/spiffs/spiffs/README.md b/components/spiffs/spiffs/README.md new file mode 100644 index 00000000..64ffcc36 --- /dev/null +++ b/components/spiffs/spiffs/README.md @@ -0,0 +1,212 @@ +# SPIFFS (SPI Flash File System) +**V0.3.7** + +[![Build Status](https://travis-ci.org/pellepl/spiffs.svg?branch=master)](https://travis-ci.org/pellepl/spiffs) + +Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976 at gmail.com) + +For legal stuff, see [LICENSE](https://github.com/pellepl/spiffs/blob/master/LICENSE). Basically, you may do whatever you want with the source. Use, modify, sell, print it out, roll it and smoke it - as long as I won't be held responsible. + +Love to hear feedback though! + + +## INTRODUCTION + +Spiffs is a file system intended for SPI NOR flash devices on embedded targets. + +Spiffs is designed with following characteristics in mind: + - Small (embedded) targets, sparse RAM without heap + - Only big areas of data (blocks) can be erased + - An erase will reset all bits in block to ones + - Writing pulls one to zeroes + - Zeroes can only be pulled to ones by erase + - Wear leveling + + +## BUILDING + +`mkdir build; make` + +Otherwise, configure the `builddir` variable towards the top of `makefile` as something opposed to the default `build`. Sanity check on the host via `make test` and refer to `.travis.yml` for the official in-depth testing procedure. See the wiki for [integrating](https://github.com/pellepl/spiffs/wiki/Integrate-spiffs) spiffs into projects and [spiffsimg](https://github.com/nodemcu/nodemcu-firmware/tree/master/tools/spiffsimg) from [nodemcu](https://github.com/nodemcu) is a good example on the subject. + + +## FEATURES + +What spiffs does: + - Specifically designed for low ram usage + - Uses statically sized ram buffers, independent of number of files + - Posix-like api: open, close, read, write, seek, stat, etc + - It can run on any NOR flash, not only SPI flash - theoretically also on embedded flash of a microprocessor + - Multiple spiffs configurations can run on same target - and even on same SPI flash device + - Implements static wear leveling + - Built in file system consistency checks + - Highly configurable + +What spiffs does not: + - Presently, spiffs does not support directories. It produces a flat structure. Creating a file with path *tmp/myfile.txt* will create a file called *tmp/myfile.txt* instead of a *myfile.txt* under directory *tmp*. + - It is not a realtime stack. One write operation might last much longer than another. + - Poor scalability. Spiffs is intended for small memory devices - the normal sizes for SPI flashes. Going beyond ~128Mbyte is probably a bad idea. This is a side effect of the design goal to use as little ram as possible. + - Presently, it does not detect or handle bad blocks. + - One configuration, one binary. There's no generic spiffs binary that handles all types of configurations. + + +## MORE INFO + +See the [wiki](https://github.com/pellepl/spiffs/wiki) for [configuring](https://github.com/pellepl/spiffs/wiki/Configure-spiffs), [integrating](https://github.com/pellepl/spiffs/wiki/Integrate-spiffs), [using](https://github.com/pellepl/spiffs/wiki/Using-spiffs), and [optimizing](https://github.com/pellepl/spiffs/wiki/Performance-and-Optimizing) spiffs. + +For design, see [docs/TECH_SPEC](https://github.com/pellepl/spiffs/blob/master/docs/TECH_SPEC). + +For a generic spi flash driver, see [this](https://github.com/pellepl/spiflash_driver). + +## HISTORY + +### 0.3.7 +- fixed prevent seeking to negative offsets #158 +- fixed file descriptor offsets not updated for multiple fds on same file #157 +- fixed cache page not closed for removed files #156 +- fixed a lseek bug when seeking exactly to end of a fully indexed first level LUT #148 +- fixed wear leveling issue #145 +- fixed attempt to write out of bounds in flash #130, +- set file offset when seeking over end #121 (thanks @sensslen) +- fixed seeking in virgin files #120 (thanks @sensslen) +- Optional file metadata #128 (thanks @cesanta) +- AFL testing framework #100 #143 (thanks @pjsg) +- Testframe updates + +New API functions: +- `SPIFFS_update_meta, SPIFFS_fupdate_meta` - updates metadata for a file + +New config defines: +- `SPIFFS_OBJ_META_LEN` - enable possibility to add extra metadata to files + +### 0.3.6 +- Fix range bug in index memory mapping #98 +- Add index memory mapping #97 +- Optimize SPIFFS_read for large files #96 +- Add temporal cache for opening files #95 +- More robust gc #93 (thanks @dismirlian) +- Fixed a double write of same data in certain cache situations +- Fixed an open bug in READ_ONLY builds +- File not visible in SPIFFS_readdir #90 (thanks @benpicco-tmp) +- Cache load code cleanup #92 (thanks @niclash) +- Fixed lock/unlock asymmetry #88 #87 (thanks @JackJefferson, @dpruessner) +- Testframe updates + +New API functions: +- `SPIFFS_ix_map` - map index meta data to memory for a file +- `SPIFFS_ix_unmap` - unmaps index meta data for a file +- `SPIFFS_ix_remap` - changes file offset for index metadata map +- `SPIFFS_bytes_to_ix_map_entries` - utility, get length of needed vector for given amount of bytes +- `SPIFFS_ix_map_entries_to_bytes` - utility, get number of bytes a vector can represent given length + +New config defines: +- `SPIFFS_IX_MAP` - enable possibility to map index meta data to memory for reading faster +- `SPIFFS_TEMPORAL_FD_CACHE` - enable temporal cache for opening files faster +- `SPIFFS_TEMPORAL_CACHE_HIT_SCORE` - for tuning the temporal cache + +### 0.3.5 +- Fixed a bug in fs check +- API returns actual error codes #84) (thanks @Nails) +- Fix compiler warnings for non-gcc #83 #81 (thanks @Nails) +- Unable to recover from full fs #82 (thanks @rojer) +- Define SPIFFS_O_* flags #80 +- Problem with long filenames #79 (thanks @psjg) +- Duplicate file name bug fix #74 (thanks @igrr) +- SPIFFS_eof and SPIFFS_tell return wrong value #72 (thanks @ArtemPisarenko) +- Bunch of testframe updates #77 #78 #86 (thanks @dpreussner, @psjg a.o) + +### 0.3.4 +- Added user callback file func. +- Fixed a stat bug with obj id. +- SPIFFS_probe_fs added +- Add possibility to compile a read-only version of spiffs +- Make magic dependent on fs length, if needed (see #59 & #66) (thanks @hreintke) +- Exposed SPIFFS_open_by_page_function +- Zero-size file cannot be seek #57 (thanks @lishen2) +- Add tell and eof functions #54 (thanks @raburton) +- Make api string params const #53 (thanks @raburton) +- Preserve user_data during mount() #51 (thanks @rojer) + +New API functions: +- `SPIFFS_set_file_callback_func` - register a callback informing about file events +- `SPIFFS_probe_fs` - probe a spi flash trying to figure out size of fs +- `SPIFFS_open_by_page` - open a file by page index +- `SPIFFS_eof` - checks if end of file is reached +- `SPIFFS_tell` - returns current file offset + +New config defines: +- `SPIFFS_READ_ONLY` +- `SPIFFS_USE_MAGIC_LENGTH` + +### 0.3.3 +**Might not be compatible with 0.3.2 structures. See issue #40** +- Possibility to add integer offset to file handles +- Truncate function presumes too few free pages #49 +- Bug in truncate function #48 (thanks @PawelDefee) +- Update spiffs_gc.c - remove unnecessary parameter (thanks @PawelDefee) +- Update INTEGRATION docs (thanks @PawelDefee) +- Fix pointer truncation in 64-bit platforms (thanks @igrr) +- Zero-sized files cannot be read #44 (thanks @rojer) +- (More) correct calculation of max_id in obj_lu_find #42 #41 (thanks @lishen2) +- Check correct error code in obj_lu_find_free #41 (thanks @lishen2) +- Moar comments for SPIFFS_lseek (thanks @igrr) +- Fixed padding in spiffs_page_object_ix #40 (thanks @jmattsson @lishen2) +- Fixed gc_quick test (thanks @jmattsson) +- Add SPIFFS_EXCL flag #36 +- SPIFFS_close may fail silently if cache is enabled #37 +- User data in callbacks #34 +- Ignoring SINGLETON build in cache setup (thanks Luca) +- Compilation error fixed #32 (thanks @chotasanjiv) +- Align cand_scores (thanks @hefloryd) +- Fix build warnings when SPIFFS_CACHE is 0 (thanks @ajaybhargav) + +New config defines: +- `SPIFFS_FILEHDL_OFFSET` + +### 0.3.2 +- Limit cache size if too much cache is given (thanks pgeiem) +- New feature - Controlled erase. #23 +- SPIFFS_rename leaks file descriptors #28 (thanks benpicco) +- moved dbg print defines in test framework to params_test.h +- lseek should return the resulting offset (thanks hefloryd) +- fixed type on dbg ifdefs +- silence warning about signed/unsigned comparison when spiffs_obj_id is 32 bit (thanks benpicco) +- Possible error in test_spiffs.c #21 (thanks yihcdaso-yeskela) +- Cache might writethrough too often #16 +- even moar testrunner updates +- Test framework update and some added tests +- Some thoughts for next gen +- Test sigsevs when having too many sectors #13 (thanks alonewolfx2) +- GC might be suboptimal #11 +- Fix eternal readdir when objheader at last block, last entry + +New API functions: +- `SPIFFS_gc_quick` - call a nonintrusive gc +- `SPIFFS_gc` - call a full-scale intrusive gc + +### 0.3.1 +- Removed two return warnings, was too triggerhappy on release + +### 0.3.0 +- Added existing namecheck when creating files +- Lots of static analysis bugs #6 +- Added rename func +- Fix SPIFFS_read length when reading beyond file size +- Added reading beyond file length testcase +- Made build a bit more configurable +- Changed name in spiffs from "errno" to "err_code" due to conflicts compiling in mingw +- Improved GC checks, fixed an append bug, more robust truncate for very special case +- GC checks preempts GC, truncate even less picky +- Struct alignment needed for some targets, define in spiffs config #10 +- Spiffs filesystem magic, definable in config + +New config defines: +- `SPIFFS_USE_MAGIC` - enable or disable magic check upon mount +- `SPIFFS_ALIGNED_OBJECT_INDEX_TABLES` - alignment for certain targets + +New API functions: +- `SPIFFS_rename` - rename files +- `SPIFFS_clearerr` - clears last errno +- `SPIFFS_info` - returns info on used and total bytes in fs +- `SPIFFS_format` - formats the filesystem +- `SPIFFS_mounted` - checks if filesystem is mounted diff --git a/components/spiffs/spiffs/afltests/100 b/components/spiffs/spiffs/afltests/100 new file mode 100644 index 00000000..6bb22391 --- /dev/null +++ b/components/spiffs/spiffs/afltests/100 @@ -0,0 +1,15 @@ +‰5S-C4 +d5rh +OlWkR#C4 +d5rh +O4W4R4O4W4êC4#d5rh +O4d5rh +OlWkRh +O4Y5rh +OlWkR4C44R45ŠË +O4W4ê4C4C4 +O4O4W4R4O4W4êC4#d5rh +O4d5rh +W4R45rË +O4W4ê4#d5rh +rz diff --git a/components/spiffs/spiffs/afltests/200 b/components/spiffs/spiffs/afltests/200 new file mode 100644 index 00000000..90143128 Binary files /dev/null and b/components/spiffs/spiffs/afltests/200 differ diff --git a/components/spiffs/spiffs/afltests/a b/components/spiffs/spiffs/afltests/a new file mode 100644 index 00000000..24e3a212 --- /dev/null +++ b/components/spiffs/spiffs/afltests/a @@ -0,0 +1,18 @@ + b55 +O4W4R4C4D4 +b45 +d5rh +O4W4R4f4C4 +baaU +d5rh +OaWaRafaCa +cd5rh +OaWaRafaCa +O4S4W4R4C4 +d5rh +O4W4S4R4C4 +d5rh +O4W4R4S4C4 +d5rh +O4W4R4C4 +d5rh diff --git a/components/spiffs/spiffs/afltests/b b/components/spiffs/spiffs/afltests/b new file mode 100644 index 00000000..1f957748 --- /dev/null +++ b/components/spiffs/spiffs/afltests/b @@ -0,0 +1,15 @@ +b55 +O4 +W?W?W?W?W?f4 +WW:W;f4 +C4 +b45 +d5rh +O4W?R4f4C4 +baa +d5rh +OaWaRafaCa +d5rh +OaWaRafaCa +O4W?R4C4 +d5rh diff --git a/components/spiffs/spiffs/docs/TECH_SPEC b/components/spiffs/spiffs/docs/TECH_SPEC new file mode 100644 index 00000000..b4755a6d --- /dev/null +++ b/components/spiffs/spiffs/docs/TECH_SPEC @@ -0,0 +1,239 @@ +* USING SPIFFS + +TODO + + +* SPIFFS DESIGN + +Spiffs is inspired by YAFFS. However, YAFFS is designed for NAND flashes, and +for bigger targets with much more ram. Nevertheless, many wise thoughts have +been borrowed from YAFFS when writing spiffs. Kudos! + +The main complication writing spiffs was that it cannot be assumed the target +has a heap. Spiffs must go along only with the work ram buffer given to it. +This forces extra implementation on many areas of spiffs. + + +** SPI flash devices using NOR technology + +Below is a small description of how SPI flashes work internally. This is to +give an understanding of the design choices made in spiffs. + +SPI flash devices are physically divided in blocks. On some SPI flash devices, +blocks are further divided into sectors. Datasheets sometimes name blocks as +sectors and vice versa. + +Common memory capacaties for SPI flashes are 512kB up to 8MB of data, where +blocks may be 64kB. Sectors can be e.g. 4kB, if supported. Many SPI flashes +have uniform block sizes, whereas others have non-uniform - the latter meaning +that e.g. the first 16 blocks are 4kB big, and the rest are 64kB. + +The entire memory is linear and can be read and written in random access. +Erasing can only be done block- or sectorwise; or by mass erase. + +SPI flashes can normally be erased from 100.000 up to 1.000.000 cycles before +they fail. + +A clean SPI flash from factory have all bits in entire memory set to one. A +mass erase will reset the device to this state. Block or sector erasing will +put the all bits in the area given by the sector or block to ones. Writing to a +NOR flash pulls ones to zeroes. Writing 0xFF to an address is simply a no-op. + +Writing 0b10101010 to a flash address holding 0b00001111 will yield 0b00001010. + +This way of "write by nand" is used considerably in spiffs. + +Common characteristics of NOR flashes are quick reads, but slow writes. + +And finally, unlike NAND flashes, NOR flashes seem to not need any error +correction. They always write correctly I gather. + + +** Spiffs logical structure + +Some terminology before proceeding. Physical blocks/sectors means sizes stated +in the datasheet. Logical blocks and pages is something the integrator choose. + + +** Blocks and pages + +Spiffs is allocated to a part or all of the memory of the SPI flash device. +This area is divided into logical blocks, which in turn are divided into +logical pages. The boundary of a logical block must coincide with one or more +physical blocks. The sizes for logical blocks and logical pages always remain +the same, they are uniform. + +Example: non-uniform flash mapped to spiffs with 128kB logical blocks + +PHYSICAL FLASH BLOCKS SPIFFS LOGICAL BLOCKS: 128kB + ++-----------------------+ - - - +-----------------------+ +| Block 1 : 16kB | | Block 1 : 128kB | ++-----------------------+ | | +| Block 2 : 16kB | | | ++-----------------------+ | | +| Block 3 : 16kB | | | ++-----------------------+ | | +| Block 4 : 16kB | | | ++-----------------------+ | | +| Block 5 : 64kB | | | ++-----------------------+ - - - +-----------------------+ +| Block 6 : 64kB | | Block 2 : 128kB | ++-----------------------+ | | +| Block 7 : 64kB | | | ++-----------------------+ - - - +-----------------------+ +| Block 8 : 64kB | | Block 3 : 128kB | ++-----------------------+ | | +| Block 9 : 64kB | | | ++-----------------------+ - - - +-----------------------+ +| ... | | ... | + +A logical block is divided further into a number of logical pages. A page +defines the smallest data holding element known to spiffs. Hence, if a file +is created being one byte big, it will occupy one page for index and one page +for data - it will occupy 2 x size of a logical page on flash. +So it seems it is good to select a small page size. + +Each page has a metadata header being normally 5 to 9 bytes. This said, a very +small page size will make metadata occupy a lot of the memory on the flash. A +page size of 64 bytes will waste 8-14% on metadata, while 256 bytes 2-4%. +So it seems it is good to select a big page size. + +Also, spiffs uses a ram buffer being two times the page size. This ram buffer +is used for loading and manipulating pages, but it is also used for algorithms +to find free file ids, scanning the file system, etc. Having too small a page +size means less work buffer for spiffs, ending up in more reads operations and +eventually gives a slower file system. + +Choosing the page size for the system involves many factors: + - How big is the logical block size + - What is the normal size of most files + - How much ram can be spent + - How much data (vs metadata) must be crammed into the file system + - How fast must spiffs be + - Other things impossible to find out + +So, chosing the Optimal Page Size (tm) seems tricky, to say the least. Don't +fret - there is no optimal page size. This varies from how the target will use +spiffs. Use the golden rule: + + ~~~ Logical Page Size = Logical Block Size / 256 ~~~ + +This is a good starting point. The final page size can then be derived through +heuristical experimenting for us non-analytical minds. + + +** Objects, indices and look-ups + +A file, or an object as called in spiffs, is identified by an object id. +Another YAFFS rip-off. This object id is a part of the page header. So, all +pages know to which object/file they belong - not counting the free pages. + +An object is made up of two types of pages: object index pages and data pages. +Data pages contain the data written by user. Index pages contain metadata about +the object, more specifically what data pages are part of the object. + +The page header also includes something called a span index. Let's say a file +is written covering three data pages. The first data page will then have span +index 0, the second span index 1, and the last data page will have span index +2. Simple as that. + +Finally, each page header contain flags, telling if the page is used, +deleted, finalized, holds index or data, and more. + +Object indices also have span indices, where an object index with span index 0 +is referred to as the object index header. This page does not only contain +references to data pages, but also extra info such as object name, object size +in bytes, flags for file or directory, etc. + +If one were to create a file covering three data pages, named e.g. +"spandex-joke.txt", given object id 12, it could look like this: + +PAGE 0 + +PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA] + + +PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA] + + +PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA] + + +PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA] + + +PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX] + obj ix header: [name:spandex-joke.txt size:600 bytes flags:FILE] + obj ix: [1 2 4] + +Looking in detail at page 5, the object index header page, the object index +array refers to each data page in order, as mentioned before. The index of the +object index array correlates with the data page span index. + + entry ix: 0 1 2 + obj ix: [1 2 4] + | | | + PAGE 1, DATA, SPAN_IX 0 --------/ | | + PAGE 2, DATA, SPAN_IX 1 --------/ | + PAGE 4, DATA, SPAN_IX 2 --------/ + +Things to be unveiled in page 0 - well.. Spiffs is designed for systems low on +ram. We cannot keep a dynamic list on the whereabouts of each object index +header so we can find a file fast. There might not even be a heap! But, we do +not want to scan all page headers on the flash to find the object index header. + +The first page(s) of each block contains the so called object look-up. These +are not normal pages, they do not have a header. Instead, they are arrays +pointing out what object-id the rest of all pages in the block belongs to. + +By this look-up, only the first page(s) in each block must to scanned to find +the actual page which contains the object index header of the desired object. + +The object lookup is redundant metadata. The assumption is that it presents +less overhead reading a full page of data to memory from each block and search +that, instead of reading a small amount of data from each page (i.e. the page +header) in all blocks. Each read operation from SPI flash normally contains +extra data as the read command itself and the flash address. Also, depending on +the underlying implementation, other criterions may need to be passed for each +read transaction, like mutexes and such. + +The veiled example unveiled would look like this, with some extra pages: + +PAGE 0 [ 12 12 545 12 12 34 34 4 0 0 0 0 ...] +PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA] ... +PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA] ... +PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA] ... +PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA] ... +PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX] ... +PAGE 6 page header: [obj_id:34 span_ix:0 flags:USED|DATA] ... +PAGE 7 page header: [obj_id:34 span_ix:1 flags:USED|DATA] ... +PAGE 8 page header: [obj_id:4 span_ix:1 flags:USED|INDEX] ... +PAGE 9 page header: [obj_id:23 span_ix:0 flags:DELETED|INDEX] ... +PAGE 10 page header: [obj_id:23 span_ix:0 flags:DELETED|DATA] ... +PAGE 11 page header: [obj_id:23 span_ix:1 flags:DELETED|DATA] ... +PAGE 12 page header: [obj_id:23 span_ix:2 flags:DELETED|DATA] ... +... + +Ok, so why are page 9 to 12 marked as 0 when they belong to object id 23? These +pages are deleted, so this is marked both in page header flags and in the look +up. This is an example where spiffs uses NOR flashes "nand-way" of writing. + +As a matter of fact, there are two object id's which are special: + +obj id 0 (all bits zeroes) - indicates a deleted page in object look up +obj id 0xff.. (all bits ones) - indicates a free page in object look up + +Actually, the object id's have another quirk: if the most significant bit is +set, this indicates an object index page. If the most significant bit is zero, +this indicates a data page. So to be fully correct, page 0 in above example +would look like this: + +PAGE 0 [ 12 12 545 12 *12 34 34 *4 0 0 0 0 ...] + +where the asterisk means the msb of the object id is set. + +This is another way to speed up the searches when looking for object indices. +By looking on the object id's msb in the object lookup, it is also possible +to find out whether the page is an object index page or a data page. + diff --git a/components/spiffs/spiffs/docs/TODO b/components/spiffs/spiffs/docs/TODO new file mode 100644 index 00000000..c947316a --- /dev/null +++ b/components/spiffs/spiffs/docs/TODO @@ -0,0 +1,15 @@ +* When mending lost pages, also see if they fit into length specified in object index header + +SPIFFS2 thoughts + +* Instead of exact object id:s in the object lookup tables, use a hash of span index and object id. + Eg. object id xor:ed with bit-reversed span index. + This should decrease number of actual pages that needs to be visited when looking thru the obj lut. + +* Logical number of each block. When moving stuff in a garbage collected page, the free + page is assigned the same number as the garbage collected. Thus, object index pages do not have to + be rewritten. + +* Steal one page, use as a bit parity page. When starting an fs modification operation, write one bit + as zero. When ending, write another bit as zero. On mount, if number of zeroes in page is uneven, a + check is automatically run. \ No newline at end of file diff --git a/components/spiffs/spiffs/files.mk b/components/spiffs/spiffs/files.mk new file mode 100644 index 00000000..631ec7e1 --- /dev/null +++ b/components/spiffs/spiffs/files.mk @@ -0,0 +1,12 @@ +ifndef spiffs +$(warn defaulting path to generic spiffs module, spiffs variable not set) +spiffs = ../generic/spiffs +endif +FLAGS += -DCONFIG_BUILD_SPIFFS +INC += -I${spiffs}/src +CPATH += ${spiffs}/src +CFILES += spiffs_nucleus.c +CFILES += spiffs_gc.c +CFILES += spiffs_hydrogen.c +CFILES += spiffs_cache.c +CFILES += spiffs_check.c diff --git a/components/spiffs/spiffs/makefile b/components/spiffs/spiffs/makefile new file mode 100644 index 00000000..355b4679 --- /dev/null +++ b/components/spiffs/spiffs/makefile @@ -0,0 +1,163 @@ +BINARY = linux_spiffs_test + +############ +# +# Paths +# +############ + +sourcedir = src +builddir = build + + +############# +# +# Build tools +# +############# + +CC ?= gcc +LD ?= ld +GDB ?= gdb +OBJCOPY ?= objcopy +OBJDUMP ?= objdump +MKDIR ?= mkdir -p + +############### +# +# Files and libs +# +############### + +NO_TEST ?= 0 +CFLAGS = $(FLAGS) +ifeq (1, $(strip $(NO_TEST))) +CFILES_TEST = main.c +CFLAGS += -DNO_TEST -Werror +else +CFILES_TEST = main.c \ + test_spiffs.c \ + test_dev.c \ + test_check.c \ + test_hydrogen.c \ + test_bugreports.c \ + testsuites.c \ + testrunner.c +CFLAGS += -D_SPIFFS_TEST +endif +include files.mk +INCLUDE_DIRECTIVES = -I./${sourcedir} -I./${sourcedir}/default -I./${sourcedir}/test +COMPILEROPTIONS = $(INCLUDE_DIRECTIVES) + +COMPILEROPTIONS_APP = $(INCLUDE_DIRECTIVES) \ +-Wall -Wno-format-y2k -W -Wstrict-prototypes -Wmissing-prototypes \ +-Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch \ +-Wshadow -Wcast-align -Wchar-subscripts -Winline -Wnested-externs\ +-Wredundant-decls + +############ +# +# Tasks +# +############ + +vpath %.c ${sourcedir} ${sourcedir}/default ${sourcedir}/test + +OBJFILES = $(CFILES:%.c=${builddir}/%.o) +OBJFILES_TEST = $(CFILES_TEST:%.c=${builddir}/%.o) + +DEPFILES = $(CFILES:%.c=${builddir}/%.d) $(CFILES_TEST:%.c=${builddir}/%.d) + +ALLOBJFILES += $(OBJFILES) $(OBJFILES_TEST) + +DEPENDENCIES = $(DEPFILES) + +# link object files, create binary +$(BINARY): $(ALLOBJFILES) + @echo "... linking" + @${CC} $(LINKEROPTIONS) -o ${builddir}/$(BINARY) $(ALLOBJFILES) $(LIBS) +ifeq (1, $(strip $(NO_TEST))) + @echo "size: `du -b ${builddir}/${BINARY} | sed 's/\([0-9]*\).*/\1/g '` bytes" +endif + + +-include $(DEPENDENCIES) + +# compile c files +$(OBJFILES) : ${builddir}/%.o:%.c + @echo "... compile $@" + @${CC} $(COMPILEROPTIONS_APP) $(CFLAGS) -g -c -o $@ $< + +$(OBJFILES_TEST) : ${builddir}/%.o:%.c + @echo "... compile $@" + @${CC} ${COMPILEROPTIONS} $(CFLAGS) -g -c -o $@ $< + +# make dependencies +# @echo "... depend $@"; +$(DEPFILES) : ${builddir}/%.d:%.c + @rm -f $@; \ + ${CC} $(COMPILEROPTIONS) -M $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*, ${builddir}/\1.o $@ : ,g' < $@.$$$$ > $@; \ + rm -f $@.$$$$ + +all: mkdirs $(BINARY) + +mkdirs: + -@${MKDIR} ${builddir} + -@${MKDIR} test_data + +FILTER ?= + +test: $(BINARY) +ifdef $(FILTER) + ./build/$(BINARY) +else + ./build/$(BINARY) -f $(FILTER) +endif + +test_failed: $(BINARY) + ./build/$(BINARY) _tests_fail + +clean: + @echo ... removing build files in ${builddir} + @rm -f ${builddir}/*.o + @rm -f ${builddir}/*.d + @rm -f ${builddir}/*.elf + +ONOFF = 1 0 +OFFON = 0 1 +build-all: + @for rdonly in $(ONOFF); do \ + for singleton in $(ONOFF); do \ + for hal_cb_xtra in $(OFFON); do \ + for cache in $(OFFON); do \ + for magic in $(OFFON); do \ + for temporal_cache in $(OFFON); do \ + for ix_map in $(OFFON); do \ + echo; \ + echo ============================================================; \ + echo SPIFFS_READ_ONLY=$$rdonly; \ + echo SPIFFS_SINGLETON=$$singleton; \ + echo SPIFFS_HAL_CALLBACK_EXTRA=$$hal_cb_xtra; \ + echo SPIFFS_CACHE, SPIFFS_CACHE_WR=$$cache; \ + echo SPIFFS_USE_MAGIC, SPIFFS_USE_MAGIC_LENGTH=$$magic; \ + echo SPIFFS_TEMPORAL_FD_CACHE=$$temporal_cache; \ + echo SPIFFS_IX_MAP=$$ix_map; \ + $(MAKE) clean && $(MAKE) FLAGS="\ + -DSPIFFS_HAL_CALLBACK_EXTRA=$$hal_cb_xtra \ + -DSPIFFS_SINGLETON=$$singleton \ + -DSPIFFS_CACHE=$$cache \ + -DSPIFFS_CACHE_WR=$$cache \ + -DSPIFFS_READ_ONLY=$$rdonly \ + -DSPIFFS_USE_MAGIC=$$magic \ + -DSPIFFS_USE_MAGIC_LENGTH=$$magic \ + -DSPIFFS_TEMPORAL_FD_CACHE=$$temporal_cache \ + -DSPIFFS_IX_MAP=$$ix_map \ + " NO_TEST=1; \ + done || exit 1; \ + done \ + done \ + done \ + done \ + done \ + done diff --git a/components/spiffs/include/spiffs/spiffs_config.h b/components/spiffs/spiffs/src/default/spiffs_config.h similarity index 52% rename from components/spiffs/include/spiffs/spiffs_config.h rename to components/spiffs/spiffs/src/default/spiffs_config.h index 5e826a65..ce562bfa 100644 --- a/components/spiffs/include/spiffs/spiffs_config.h +++ b/components/spiffs/spiffs/src/default/spiffs_config.h @@ -11,42 +11,76 @@ // ----------- 8< ------------ // Following includes are for the linux test build of spiffs // These may/should/must be removed/altered/replaced in your target +#include "params_test.h" #include #include #include #include -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" - +#include +#ifdef _SPIFFS_TEST +#include "testrunner.h" +#endif // ----------- >8 ------------ -typedef uint8_t u8_t; -typedef int8_t s8_t; -typedef uint16_t u16_t; -typedef int16_t s16_t; -typedef uint32_t u32_t; -typedef int32_t s32_t; - // compile time switches // Set generic spiffs debug output call. #ifndef SPIFFS_DBG -#define SPIFFS_DBG(...) //printf(__VA_ARGS__) +#define SPIFFS_DBG(_f, ...) //printf(_f, ## __VA_ARGS__) #endif // Set spiffs debug output call for garbage collecting. #ifndef SPIFFS_GC_DBG -#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__) +#define SPIFFS_GC_DBG(_f, ...) //printf(_f, ## __VA_ARGS__) #endif // Set spiffs debug output call for caching. #ifndef SPIFFS_CACHE_DBG -#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__) +#define SPIFFS_CACHE_DBG(_f, ...) //printf(_f, ## __VA_ARGS__) #endif // Set spiffs debug output call for system consistency checks. #ifndef SPIFFS_CHECK_DBG -#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__) +#define SPIFFS_CHECK_DBG(_f, ...) //printf(_f, ## __VA_ARGS__) #endif +// Set spiffs debug output call for all api invocations. +#ifndef SPIFFS_API_DBG +#define SPIFFS_API_DBG(_f, ...) //printf(_f, ## __VA_ARGS__) +#endif + + + +// Defines spiffs debug print formatters +// some general signed number +#ifndef _SPIPRIi +#define _SPIPRIi "%d" +#endif +// address +#ifndef _SPIPRIad +#define _SPIPRIad "%08x" +#endif +// block +#ifndef _SPIPRIbl +#define _SPIPRIbl "%04x" +#endif +// page +#ifndef _SPIPRIpg +#define _SPIPRIpg "%04x" +#endif +// span index +#ifndef _SPIPRIsp +#define _SPIPRIsp "%04x" +#endif +// file descriptor +#ifndef _SPIPRIfd +#define _SPIPRIfd "%d" +#endif +// file object id +#ifndef _SPIPRIid +#define _SPIPRIid "%04x" +#endif +// file flags +#ifndef _SPIPRIfl +#define _SPIPRIfl "%02x" +#endif + // Enable/disable API functions to determine exact number of bytes // for filedescriptor and cache buffers. Once decided for a configuration, @@ -60,7 +94,6 @@ typedef int32_t s32_t; #ifndef SPIFFS_CACHE #define SPIFFS_CACHE 1 #endif - #if SPIFFS_CACHE // Enables memory write caching for file descriptors in hydrogen #ifndef SPIFFS_CACHE_WR @@ -111,11 +144,27 @@ typedef int32_t s32_t; #define SPIFFS_GC_HEUR_W_ERASE_AGE (50) #endif -// Object name maximum length. +// Object name maximum length. Note that this length include the +// zero-termination character, meaning maximum string of characters +// can at most be SPIFFS_OBJ_NAME_LEN - 1. #ifndef SPIFFS_OBJ_NAME_LEN #define SPIFFS_OBJ_NAME_LEN (32) #endif +// Maximum length of the metadata associated with an object. +// Setting to non-zero value enables metadata-related API but also +// changes the on-disk format, so the change is not backward-compatible. +// +// Do note: the meta length must never exceed +// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64) +// +// This is derived from following: +// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) + +// spiffs_object_ix_header fields + at least some LUT entries) +#ifndef SPIFFS_OBJ_META_LEN +#define SPIFFS_OBJ_META_LEN (0) +#endif + // Size of buffer allocated on stack used when copying data. // Lower value generates more read/writes. No meaning having it bigger // than logical page size. @@ -131,6 +180,17 @@ typedef int32_t s32_t; #define SPIFFS_USE_MAGIC (0) #endif +#if SPIFFS_USE_MAGIC +// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is +// enabled, the magic will also be dependent on the length of the filesystem. +// For example, a filesystem configured and formatted for 4 megabytes will not +// be accepted for mounting with a configuration defining the filesystem as 2 +// megabytes. +#ifndef SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_USE_MAGIC_LENGTH (0) +#endif +#endif + // SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level // These should be defined on a multithreaded system @@ -143,7 +203,6 @@ typedef int32_t s32_t; #define SPIFFS_UNLOCK(fs) #endif - // Enable if only one spiffs instance with constant configuration will exist // on the target. This will reduce calculations, flash and memory accesses. // Parts of configuration must be defined below instead of at time of mount. @@ -173,14 +232,90 @@ typedef int32_t s32_t; // Enable this if your target needs aligned data for index tables #ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES -#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1 +#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 0 +#endif + +// Enable this if you want the HAL callbacks to be called with the spiffs struct +#ifndef SPIFFS_HAL_CALLBACK_EXTRA +#define SPIFFS_HAL_CALLBACK_EXTRA 0 +#endif + +// Enable this if you want to add an integer offset to all file handles +// (spiffs_file). This is useful if running multiple instances of spiffs on +// same target, in order to recognise to what spiffs instance a file handle +// belongs. +// NB: This adds config field fh_ix_offset in the configuration struct when +// mounting, which must be defined. +#ifndef SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FILEHDL_OFFSET 0 +#endif + +// Enable this to compile a read only version of spiffs. +// This will reduce binary size of spiffs. All code comprising modification +// of the file system will not be compiled. Some config will be ignored. +// HAL functions for erasing and writing to spi-flash may be null. Cache +// can be disabled for even further binary size reduction (and ram savings). +// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL. +// If the file system cannot be mounted due to aborted erase operation and +// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be +// returned. +// Might be useful for e.g. bootloaders and such. +#ifndef SPIFFS_READ_ONLY +#define SPIFFS_READ_ONLY 0 +#endif + +// Enable this to add a temporal file cache using the fd buffer. +// The effects of the cache is that SPIFFS_open will find the file faster in +// certain cases. It will make it a lot easier for spiffs to find files +// opened frequently, reducing number of readings from the spi flash for +// finding those files. +// This will grow each fd by 6 bytes. If your files are opened in patterns +// with a degree of temporal locality, the system is optimized. +// Examples can be letting spiffs serve web content, where one file is the css. +// The css is accessed for each html file that is opened, meaning it is +// accessed almost every second time a file is opened. Another example could be +// a log file that is often opened, written, and closed. +// The size of the cache is number of given file descriptors, as it piggybacks +// on the fd update mechanism. The cache lives in the closed file descriptors. +// When closed, the fd know the whereabouts of the file. Instead of forgetting +// this, the temporal cache will keep handling updates to that file even if the +// fd is closed. If the file is opened again, the location of the file is found +// directly. If all available descriptors become opened, all cache memory is +// lost. +#ifndef SPIFFS_TEMPORAL_FD_CACHE +#define SPIFFS_TEMPORAL_FD_CACHE 1 +#endif + +// Temporal file cache hit score. Each time a file is opened, all cached files +// will lose one point. If the opened file is found in cache, that entry will +// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this +// value for the specific access patterns of the application. However, it must +// be between 1 (no gain for hitting a cached entry often) and 255. +#ifndef SPIFFS_TEMPORAL_CACHE_HIT_SCORE +#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE 4 +#endif + +// Enable to be able to map object indices to memory. +// This allows for faster and more deterministic reading if cases of reading +// large files and when changing file offset by seeking around a lot. +// When mapping a file's index, the file system will be scanned for index pages +// and the info will be put in memory provided by user. When reading, the +// memory map can be looked up instead of searching for index pages on the +// medium. This way, user can trade memory against performance. +// Whole, parts of, or future parts not being written yet can be mapped. The +// memory array will be owned by spiffs and updated accordingly during garbage +// collecting or when modifying the indices. The latter is invoked by when the +// file is modified in some way. The index buffer is tied to the file +// descriptor. +#ifndef SPIFFS_IX_MAP +#define SPIFFS_IX_MAP 1 #endif // Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function // in the api. This function will visualize all filesystem using given printf // function. #ifndef SPIFFS_TEST_VISUALISATION -#define SPIFFS_TEST_VISUALISATION 0 +#define SPIFFS_TEST_VISUALISATION 1 #endif #if SPIFFS_TEST_VISUALISATION #ifndef spiffs_printf diff --git a/components/spiffs/include/spiffs/spiffs.h b/components/spiffs/spiffs/src/spiffs.h similarity index 59% rename from components/spiffs/include/spiffs/spiffs.h rename to components/spiffs/spiffs/src/spiffs.h index 2bf0a6e9..534c3df8 100644 --- a/components/spiffs/include/spiffs/spiffs.h +++ b/components/spiffs/spiffs/src/spiffs.h @@ -5,8 +5,6 @@ * Author: petera */ - - #ifndef SPIFFS_H_ #define SPIFFS_H_ #if defined(__cplusplus) @@ -49,6 +47,22 @@ extern "C" { #define SPIFFS_ERR_NO_DELETED_BLOCKS -10029 +#define SPIFFS_ERR_FILE_EXISTS -10030 + +#define SPIFFS_ERR_NOT_A_FILE -10031 +#define SPIFFS_ERR_RO_NOT_IMPL -10032 +#define SPIFFS_ERR_RO_ABORTED_OPERATION -10033 +#define SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS -10034 +#define SPIFFS_ERR_PROBE_NOT_A_FS -10035 +#define SPIFFS_ERR_NAME_TOO_LONG -10036 + +#define SPIFFS_ERR_IX_MAP_UNMAPPED -10037 +#define SPIFFS_ERR_IX_MAP_MAPPED -10038 +#define SPIFFS_ERR_IX_MAP_BAD_RANGE -10039 + +#define SPIFFS_ERR_SEEK_BOUNDS -10040 + + #define SPIFFS_ERR_INTERNAL -10050 #define SPIFFS_ERR_TEST -10100 @@ -63,12 +77,26 @@ typedef u16_t spiffs_mode; // object type typedef u8_t spiffs_obj_type; +struct spiffs_t; + +#if SPIFFS_HAL_CALLBACK_EXTRA + +/* spi read call function type */ +typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst); +/* spi write call function type */ +typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src); +/* spi erase call function type */ +typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size); + +#else // SPIFFS_HAL_CALLBACK_EXTRA + /* spi read call function type */ typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst); /* spi write call function type */ typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src); /* spi erase call function type */ typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); +#endif // SPIFFS_HAL_CALLBACK_EXTRA /* file system check callback report operation */ typedef enum { @@ -85,16 +113,34 @@ typedef enum { SPIFFS_CHECK_FIX_LOOKUP, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, SPIFFS_CHECK_DELETE_PAGE, - SPIFFS_CHECK_DELETE_BAD_FILE, + SPIFFS_CHECK_DELETE_BAD_FILE } spiffs_check_report; /* file system check callback function */ +#if SPIFFS_HAL_CALLBACK_EXTRA +typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report, + u32_t arg1, u32_t arg2); +#else // SPIFFS_HAL_CALLBACK_EXTRA typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report, u32_t arg1, u32_t arg2); +#endif // SPIFFS_HAL_CALLBACK_EXTRA + +/* file system listener callback operation */ +typedef enum { + /* the file has been created */ + SPIFFS_CB_CREATED = 0, + /* the file has been updated or moved to another page */ + SPIFFS_CB_UPDATED, + /* the file has been deleted */ + SPIFFS_CB_DELETED +} spiffs_fileop_type; + +/* file system listener callback function */ +typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix); #ifndef SPIFFS_DBG #define SPIFFS_DBG(...) \ - print(__VA_ARGS__) + printf(__VA_ARGS__) #endif #ifndef SPIFFS_GC_DBG #define SPIFFS_GC_DBG(...) printf(__VA_ARGS__) @@ -108,18 +154,28 @@ typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_repor /* Any write to the filehandle is appended to end of the file */ #define SPIFFS_APPEND (1<<0) +#define SPIFFS_O_APPEND SPIFFS_APPEND /* If the opened file exists, it will be truncated to zero length before opened */ #define SPIFFS_TRUNC (1<<1) +#define SPIFFS_O_TRUNC SPIFFS_TRUNC /* If the opened file does not exist, it will be created before opened */ #define SPIFFS_CREAT (1<<2) +#define SPIFFS_O_CREAT SPIFFS_CREAT /* The opened file may only be read */ #define SPIFFS_RDONLY (1<<3) -/* The opened file may only be writted */ +#define SPIFFS_O_RDONLY SPIFFS_RDONLY +/* The opened file may only be written */ #define SPIFFS_WRONLY (1<<4) -/* The opened file may be both read and writted */ +#define SPIFFS_O_WRONLY SPIFFS_WRONLY +/* The opened file may be both read and written */ #define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY) -/* Any writes to the filehandle will never be cached */ +#define SPIFFS_O_RDWR SPIFFS_RDWR +/* Any writes to the filehandle will never be cached but flushed directly */ #define SPIFFS_DIRECT (1<<5) +#define SPIFFS_O_DIRECT SPIFFS_DIRECT +/* If SPIFFS_O_CREAT and SPIFFS_O_EXCL are set, SPIFFS_open() shall fail if the file exists */ +#define SPIFFS_EXCL (1<<6) +#define SPIFFS_O_EXCL SPIFFS_EXCL #define SPIFFS_SEEK_SET (0) #define SPIFFS_SEEK_CUR (1) @@ -164,10 +220,15 @@ typedef struct { // logical size of a page, must be at least // log_block_size / 8 u32_t log_page_size; + +#endif +#if SPIFFS_FILEHDL_OFFSET + // an integer offset added to each file handle + u16_t fh_ix_offset; #endif } spiffs_config; -typedef struct { +typedef struct spiffs_t { // file system configuration spiffs_config cfg; // number of logical blocks @@ -222,9 +283,12 @@ typedef struct { // check callback function spiffs_check_callback check_cb_f; - + // file callback function + spiffs_file_callback file_cb_f; // mounted flag u8_t mounted; + // user data + void *user_data; // config magic u32_t config_magic; } spiffs; @@ -234,7 +298,11 @@ typedef struct { spiffs_obj_id obj_id; u32_t size; spiffs_obj_type type; + spiffs_page_ix pix; u8_t name[SPIFFS_OBJ_NAME_LEN]; +#if SPIFFS_OBJ_META_LEN + u8_t meta[SPIFFS_OBJ_META_LEN]; +#endif } spiffs_stat; struct spiffs_dirent { @@ -243,6 +311,9 @@ struct spiffs_dirent { spiffs_obj_type type; u32_t size; spiffs_page_ix pix; +#if SPIFFS_OBJ_META_LEN + u8_t meta[SPIFFS_OBJ_META_LEN]; +#endif }; typedef struct { @@ -251,8 +322,57 @@ typedef struct { int entry; } spiffs_DIR; +#if SPIFFS_IX_MAP + +typedef struct { + // buffer with looked up data pixes + spiffs_page_ix *map_buf; + // precise file byte offset + u32_t offset; + // start data span index of lookup buffer + spiffs_span_ix start_spix; + // end data span index of lookup buffer + spiffs_span_ix end_spix; +} spiffs_ix_map; + +#endif + // functions +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 +/** + * Special function. This takes a spiffs config struct and returns the number + * of blocks this file system was formatted with. This function relies on + * that following info is set correctly in given config struct: + * + * phys_addr, log_page_size, and log_block_size. + * + * Also, hal_read_f must be set in the config struct. + * + * One must be sure of the correct page size and that the physical address is + * correct in the probed file system when calling this function. It is not + * checked if the phys_addr actually points to the start of the file system, + * so one might get a false positive if entering a phys_addr somewhere in the + * middle of the file system at block boundary. In addition, it is not checked + * if the page size is actually correct. If it is not, weird file system sizes + * will be returned. + * + * If this function detects a file system it returns the assumed file system + * size, which can be used to set the phys_size. + * + * Otherwise, it returns an error indicating why it is not regarded as a file + * system. + * + * Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK + * macros. It returns the error code directly, instead of as read by + * SPIFFS_errno. + * + * @param config essential parts of the physical and logical + * configuration of the file system. + */ +s32_t SPIFFS_probe_fs(spiffs_config *config); +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + /** * Initializes the file system dynamic parameters and mounts the filesystem. * If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS @@ -286,19 +406,18 @@ void SPIFFS_unmount(spiffs *fs); * @param path the path of the new file * @param mode ignored, for posix compliance */ -s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode); +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode); /** * Opens/creates a file. * @param fs the file system struct * @param path the path of the new file * @param flags the flags for the open command, can be combinations of - * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, - * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT + * SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY, + * SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL * @param mode ignored, for posix compliance */ -spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode mode); - +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode); /** * Opens a file by given dir entry. @@ -306,7 +425,7 @@ spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode * a normal SPIFFS_open would need to traverse the filesystem again to find * the file, whilst SPIFFS_open_by_dirent already knows where the file resides. * @param fs the file system struct - * @param path the dir entry to the file + * @param e the dir entry to the file * @param flags the flags for the open command, can be combinations of * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. @@ -315,6 +434,22 @@ spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode */ spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode); +/** + * Opens a file by given page index. + * Optimization purposes, opens a file by directly pointing to the page + * index in the spi flash. + * If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE + * is returned. + * @param fs the file system struct + * @param page_ix the page index + * @param flags the flags for the open command, can be combinations of + * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, + * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. + * SPIFFS_CREAT will have no effect in this case. + * @param mode ignored, for posix compliance + */ +spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode); + /** * Reads from given filehandle. * @param fs the file system struct @@ -336,13 +471,14 @@ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len); s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len); /** - * Moves the read/write file offset + * Moves the read/write file offset. Resulting offset is returned or negative if error. + * lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset. * @param fs the file system struct * @param fh the filehandle * @param offs how much/where to move the offset * @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes * if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset - * if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offset + * if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative */ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence); @@ -351,7 +487,7 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence); * @param fs the file system struct * @param path the path of the file to remove */ -s32_t SPIFFS_remove(spiffs *fs, char *path); +s32_t SPIFFS_remove(spiffs *fs, const char *path); /** * Removes a file by filehandle @@ -366,7 +502,7 @@ s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh); * @param path the path of the file to stat * @param s the stat struct to populate */ -s32_t SPIFFS_stat(spiffs *fs, char *path, spiffs_stat *s); +s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s); /** * Gets file status by filehandle @@ -388,7 +524,7 @@ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh); * @param fs the file system struct * @param fh the filehandle of the file to close */ -void SPIFFS_close(spiffs *fs, spiffs_file fh); +s32_t SPIFFS_close(spiffs *fs, spiffs_file fh); /** * Renames a file @@ -396,7 +532,25 @@ void SPIFFS_close(spiffs *fs, spiffs_file fh); * @param old path of file to rename * @param newPath new path of file */ -s32_t SPIFFS_rename(spiffs *fs, char *old, char *newPath); +s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath); + +#if SPIFFS_OBJ_META_LEN +/** + * Updates file's metadata + * @param fs the file system struct + * @param path path to the file + * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. + */ +s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta); + +/** + * Updates file's metadata + * @param fs the file system struct + * @param fh file handle of the file + * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. + */ +s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta); +#endif /** * Returns last error of last file operation. @@ -419,7 +573,7 @@ void SPIFFS_clearerr(spiffs *fs); * @param name the name of the directory * @param d pointer the directory stream to be populated */ -spiffs_DIR *SPIFFS_opendir(spiffs *fs, char *name, spiffs_DIR *d); +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d); /** * Closes a directory stream @@ -441,13 +595,6 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); */ s32_t SPIFFS_check(spiffs *fs); -/** - * Searches for a block with only deleted entries. If found, it is erased. - * @param fs the file system struct - */ -s32_t SPIFFS_erase_deleted_block(spiffs *fs); - - /** * Returns number of total bytes available and number of used bytes. * This is an estimation, and depends on if there a many files with little @@ -527,6 +674,115 @@ s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages); */ s32_t SPIFFS_gc(spiffs *fs, u32_t size); +/** + * Check if EOF reached. + * @param fs the file system struct + * @param fh the filehandle of the file to check + */ +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); + +/** + * Get position in file. + * @param fs the file system struct + * @param fh the filehandle of the file to check + */ +s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); + +/** + * Registers a callback function that keeps track on operations on file + * headers. Do note, that this callback is called from within internal spiffs + * mechanisms. Any operations on the actual file system being callbacked from + * in this callback will mess things up for sure - do not do this. + * This can be used to track where files are and move around during garbage + * collection, which in turn can be used to build location tables in ram. + * Used in conjuction with SPIFFS_open_by_page this may improve performance + * when opening a lot of files. + * Must be invoked after mount. + * + * @param fs the file system struct + * @param cb_func the callback on file operations + */ +s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func); + +#if SPIFFS_IX_MAP + +/** + * Maps the first level index lookup to a given memory map. + * This will make reading big files faster, as the memory map will be used for + * looking up data pages instead of searching for the indices on the physical + * medium. When mapping, all affected indicies are found and the information is + * copied to the array. + * Whole file or only parts of it may be mapped. The index map will cover file + * contents from argument offset until and including arguments (offset+len). + * It is valid to map a longer range than the current file size. The map will + * then be populated when the file grows. + * On garbage collections and file data page movements, the map array will be + * automatically updated. Do not tamper with the map array, as this contains + * the references to the data pages. Modifying it from outside will corrupt any + * future readings using this file descriptor. + * The map will no longer be used when the file descriptor closed or the file + * is unmapped. + * This can be useful to get faster and more deterministic timing when reading + * large files, or when seeking and reading a lot within a file. + * @param fs the file system struct + * @param fh the file handle of the file to map + * @param map a spiffs_ix_map struct, describing the index map + * @param offset absolute file offset where to start the index map + * @param len length of the mapping in actual file bytes + * @param map_buf the array buffer for the look up data - number of required + * elements in the array can be derived from function + * SPIFFS_bytes_to_ix_map_entries given the length + */ +s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, + u32_t offset, u32_t len, spiffs_page_ix *map_buf); + +/** + * Unmaps the index lookup from this filehandle. All future readings will + * proceed as normal, requiring reading of the first level indices from + * physical media. + * The map and map buffer given in function SPIFFS_ix_map will no longer be + * referenced by spiffs. + * It is not strictly necessary to unmap a file before closing it, as closing + * a file will automatically unmap it. + * @param fs the file system struct + * @param fh the file handle of the file to unmap + */ +s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh); + +/** + * Moves the offset for the index map given in function SPIFFS_ix_map. Parts or + * all of the map buffer will repopulated. + * @param fs the file system struct + * @param fh the mapped file handle of the file to remap + * @param offset new absolute file offset where to start the index map + */ +s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offs); + +/** + * Utility function to get number of spiffs_page_ix entries a map buffer must + * contain on order to map given amount of file data in bytes. + * See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes. + * @param fs the file system struct + * @param bytes number of file data bytes to map + * @return needed number of elements in a spiffs_page_ix array needed to + * map given amount of bytes in a file + */ +s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes); + +/** + * Utility function to amount of file data bytes that can be mapped when + * mapping a file with buffer having given number of spiffs_page_ix entries. + * See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries. + * @param fs the file system struct + * @param map_page_ix_entries number of entries in a spiffs_page_ix array + * @return amount of file data in bytes that can be mapped given a map + * buffer having given amount of spiffs_page_ix entries + */ +s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries); + +#endif // SPIFFS_IX_MAP + + #if SPIFFS_TEST_VISUALISATION /** * Prints out a visualization of the filesystem. diff --git a/components/spiffs/library/spiffs_cache.c b/components/spiffs/spiffs/src/spiffs_cache.c similarity index 76% rename from components/spiffs/library/spiffs_cache.c rename to components/spiffs/spiffs/src/spiffs_cache.c index 4fde4d36..e7cd4b73 100644 --- a/components/spiffs/library/spiffs_cache.c +++ b/components/spiffs/spiffs/src/spiffs_cache.c @@ -20,12 +20,12 @@ static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) if ((cache->cpage_use_map & (1<flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && cp->pix == pix ) { - SPIFFS_CACHE_DBG("CACHE_GET: have cache page %i for %04x\n", i, pix); + //SPIFFS_CACHE_DBG("CACHE_GET: have cache page "_SPIPRIi" for "_SPIPRIpg"\n", i, pix); cp->last_access = cache->last_access; return cp; } } - //SPIFFS_CACHE_DBG("CACHE_GET: no cache for %04x\n", pix); + //SPIFFS_CACHE_DBG("CACHE_GET: no cache for "_SPIPRIpg"\n", pix); return 0; } @@ -39,17 +39,20 @@ static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) { (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) { u8_t *mem = spiffs_get_cache_page(fs, cache, ix); - res = fs->cfg.hal_write_f(SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); + SPIFFS_CACHE_DBG("CACHE_FREE: write cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); + res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); } - cp->flags = 0; - cache->cpage_use_map &= ~(1 << ix); - +#if SPIFFS_CACHE_WR if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) { - SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i objid %04x\n", ix, cp->obj_id); - } else { - SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i pix %04x\n", ix, cp->pix); + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" objid "_SPIPRIid"\n", ix, cp->obj_id); + } else +#endif + { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix); } + cache->cpage_use_map &= ~(1 << ix); + cp->flags = 0; } return res; @@ -98,7 +101,7 @@ static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) { spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); cache->cpage_use_map |= (1<last_access = cache->last_access; - SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page %i\n", i); + //SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i); return cp; } } @@ -130,38 +133,50 @@ s32_t spiffs_phys_rd( spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr)); cache->last_access++; if (cp) { + // we've already got one, you see #if SPIFFS_CACHE_STATS fs->cache_hits++; #endif cp->last_access = cache->last_access; + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); } else { if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) { // for second layer lookup functions, we do not cache in order to prevent shredding - return fs->cfg.hal_read_f( - addr , - len, - dst); + return SPIFFS_HAL_READ(fs, addr, len, dst); } #if SPIFFS_CACHE_STATS fs->cache_misses++; #endif + // this operation will always free one cache page (unless all already free), + // the result code stems from the write operation of the possibly freed cache page res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + cp = spiffs_cache_page_allocate(fs); if (cp) { cp->flags = SPIFFS_CACHE_FLAG_WRTHRU; cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr); - } + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for pix "_SPIPRIpg "\n", cp->ix, cp->pix); - s32_t res2 = fs->cfg.hal_read_f( - addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr), - SPIFFS_CFG_LOG_PAGE_SZ(fs), - spiffs_get_cache_page(fs, cache, cp->ix)); - if (res2 != SPIFFS_OK) { - res = res2; + s32_t res2 = SPIFFS_HAL_READ(fs, + addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + spiffs_get_cache_page(fs, cache, cp->ix)); + if (res2 != SPIFFS_OK) { + // honor read failure before possible write failure (bad idea?) + res = res2; + } + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + } else { + // this will never happen, last resort for sake of symmetry + s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst); + if (res2 != SPIFFS_OK) { + // honor read failure before possible write failure (bad idea?) + res = res2; + } } } - u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); - memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); return res; } @@ -186,24 +201,24 @@ s32_t spiffs_phys_wr( (op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) { // page is being deleted, wipe from cache - unless it is a lookup page spiffs_cache_page_free(fs, cp->ix, 0); - return fs->cfg.hal_write_f(addr, len, src); + return SPIFFS_HAL_WRITE(fs, addr, len, src); } u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); - memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); + _SPIFFS_MEMCPY(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); cache->last_access++; cp->last_access = cache->last_access; if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) { // page is being updated, no write-cache, just pass thru - return fs->cfg.hal_write_f(addr, len, src); + return SPIFFS_HAL_WRITE(fs, addr, len, src); } else { return SPIFFS_OK; } } else { // no cache page, no write cache - just write thru - return fs->cfg.hal_write_f(addr, len, src); + return SPIFFS_HAL_WRITE(fs, addr, len, src); } } @@ -245,6 +260,7 @@ spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) { cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR; cp->obj_id = fd->obj_id; fd->cache_page = cp; + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for fd "_SPIPRIfd ":"_SPIPRIid "\n", cp->ix, fd->file_nbr, fd->obj_id); return cp; } @@ -288,7 +304,7 @@ void spiffs_cache_init(spiffs *fs) { cache.cpage_use_map = 0xffffffff; cache.cpage_use_mask = cache_mask; - memcpy(fs->cache, &cache, sizeof(spiffs_cache)); + _SPIFFS_MEMCPY(fs->cache, &cache, sizeof(spiffs_cache)); spiffs_cache *c = spiffs_get_cache(fs); diff --git a/components/spiffs/library/spiffs_check.c b/components/spiffs/spiffs/src/spiffs_check.c similarity index 83% rename from components/spiffs/library/spiffs_check.c rename to components/spiffs/spiffs/src/spiffs_check.c index 2180a2a1..dde85eff 100644 --- a/components/spiffs/library/spiffs_check.c +++ b/components/spiffs/spiffs/src/spiffs_check.c @@ -19,9 +19,24 @@ * Author: petera */ + #include "spiffs.h" #include "spiffs_nucleus.h" +#if !SPIFFS_READ_ONLY + +#if SPIFFS_HAL_CALLBACK_EXTRA +#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ + do { \ + if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \ + } while (0) +#else +#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ + do { \ + if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \ + } while (0) +#endif + //--------------------------------------- // Look up consistency @@ -93,6 +108,7 @@ static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ } else { // calc entry in index entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix); + } // load index res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, @@ -166,7 +182,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) || ((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) { // look up entry deleted / free but used in page header - SPIFFS_CHECK_DBG("LU: pix %04x deleted/free in lu but not on page\n", cur_pix); + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" deleted/free in lu but not on page\n", cur_pix); *reload_lu = 1; delete_page = 1; if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { @@ -183,20 +199,20 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s // copy page to new place and re-write the object index to new place spiffs_page_ix new_pix; res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); - SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting %04x to new page %04x\n", cur_pix, new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix); SPIFFS_CHECK_RES(res); *reload_lu = 1; - SPIFFS_CHECK_DBG("LU: FIXUP: %04x rewritten to %04x, affected objix_pix %04x\n", cur_pix, new_pix, objix_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: "_SPIPRIpg" rewritten to "_SPIPRIpg", affected objix_pix "_SPIPRIpg"\n", cur_pix, new_pix, objix_pix); res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("LU: FIXUP: index bad %i, cannot mend!\n", res); + SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); res = spiffs_page_delete(fs, new_pix); SPIFFS_CHECK_RES(res); res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); } else { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix); } SPIFFS_CHECK_RES(res); } @@ -213,10 +229,10 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s // got a data page also, assume lu corruption only, rewrite to new page spiffs_page_ix new_pix; res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); - SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting %04x to new page %04x\n", cur_pix, new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix); SPIFFS_CHECK_RES(res); *reload_lu = 1; - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); } } else { SPIFFS_CHECK_RES(res); @@ -226,7 +242,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) { // look up entry used if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) { - SPIFFS_CHECK_DBG("LU: pix %04x differ in obj_id lu:%04x ph:%04x\n", cur_pix, lu_obj_id, p_hdr->obj_id); + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" differ in obj_id lu:"_SPIPRIid" ph:"_SPIPRIid"\n", cur_pix, lu_obj_id, p_hdr->obj_id); delete_page = 1; if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 || (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) || @@ -249,12 +265,12 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("LU: FIXUP: index bad %i, cannot mend!\n", res); + SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); res = spiffs_page_delete(fs, new_pix); SPIFFS_CHECK_RES(res); res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); *reload_lu = 1; - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); } SPIFFS_CHECK_RES(res); } @@ -305,8 +321,8 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s // rewrite as obj_id_ph new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG; res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); - SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page %04x as %04x to pix %04x\n", cur_pix, new_ph.obj_id, new_pix); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid" to pix "_SPIPRIpg"\n", cur_pix, new_ph.obj_id, new_pix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); SPIFFS_CHECK_RES(res); *reload_lu = 1; } else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) || @@ -314,8 +330,8 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s // got a data page for look up obj id // rewrite as obj_id_lu new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; - SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page %04x as %04x\n", cur_pix, new_ph.obj_id); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid"\n", cur_pix, new_ph.obj_id); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); SPIFFS_CHECK_RES(res); *reload_lu = 1; @@ -328,7 +344,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s } } else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) || ((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) { - SPIFFS_CHECK_DBG("LU: %04x lu/page index marking differ\n", cur_pix); + SPIFFS_CHECK_DBG("LU: "_SPIPRIpg" lu/page index marking differ\n", cur_pix); spiffs_page_ix data_pix, objix_pix_d; // see if other data page exists for given obj id and span index res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix); @@ -353,7 +369,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s // if only data page exists, make this page index if (data_pix && objix_pix_d == 0) { SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n"); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix); spiffs_page_header new_ph; spiffs_page_ix new_pix; new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); @@ -369,7 +385,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s // if only index exists, make data page if (data_pix == 0 && objix_pix_d) { SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n"); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix); spiffs_page_header new_ph; spiffs_page_ix new_pix; new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); @@ -386,10 +402,10 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s } } else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) { - SPIFFS_CHECK_DBG("LU: pix %04x busy in lu but deleted on page\n", cur_pix); + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy in lu but deleted on page\n", cur_pix); delete_page = 1; } else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) { - SPIFFS_CHECK_DBG("LU: pix %04x busy but not final\n", cur_pix); + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy but not final\n", cur_pix); // page can be removed if not referenced by object index *reload_lu = 1; res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); @@ -406,7 +422,7 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s // page referenced by object index but not final // just finalize SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n"); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL; res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), @@ -417,8 +433,8 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s } if (delete_page) { - SPIFFS_CHECK_DBG("LU: FIXUP: deleting page %04x\n", cur_pix); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + SPIFFS_CHECK_DBG("LU: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); res = spiffs_page_delete(fs, cur_pix); SPIFFS_CHECK_RES(res); } @@ -427,14 +443,14 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s } static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry, - u32_t user_data, void *user_p) { - (void)user_data; - (void)user_p; + const void *user_const_p, void *user_var_p) { + (void)user_const_p; + (void)user_var_p; s32_t res = SPIFFS_OK; spiffs_page_header p_hdr; spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, (cur_block * 256)/fs->block_count, 0); // load header @@ -460,7 +476,7 @@ s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { (void)check_all_objects; s32_t res = SPIFFS_OK; - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0); @@ -469,10 +485,10 @@ s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { } if (res != SPIFFS_OK) { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); } - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); return res; } @@ -506,14 +522,17 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { spiffs_block_ix cur_block = 0; // build consistency bitmap for id range traversing all blocks while (!restart && cur_block < fs->block_count) { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, (pix_offset*256)/(SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) + ((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count), 0); - // traverse each page except for lookup pages spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block; while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) { + //if ((cur_pix & 0xff) == 0) + // SPIFFS_CHECK_DBG("PA: processing pix "_SPIPRIpg", block "_SPIPRIbl" of pix "_SPIPRIpg", block "_SPIPRIbl"\n", + // cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count); + // read header spiffs_page_header p_hdr; res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, @@ -570,7 +589,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { || (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) { // bad reference - SPIFFS_CHECK_DBG("PA: pix %04x bad pix / LU referenced from page %04x\n", + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg"x bad pix / LU referenced from page "_SPIPRIpg"\n", rpix, cur_pix); // check for data page elsewhere spiffs_page_ix data_pix; @@ -589,20 +608,20 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { new_ph.span_ix = data_spix_offset + i; res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix); SPIFFS_CHECK_RES(res); - SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ %04x\n", data_pix); + SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ "_SPIPRIpg"\n", data_pix); } // remap index - SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix %04x\n", cur_pix); + SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix "_SPIPRIpg"\n", cur_pix); res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i, data_pix, cur_pix); if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend - delete object\n", res); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); + SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend - delete object\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); // delete file res = spiffs_page_delete(fs, cur_pix); } else { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix); } SPIFFS_CHECK_RES(res); restart = 1; @@ -621,7 +640,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { rp_hdr.span_ix != data_spix_offset + i || (rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) != (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) { - SPIFFS_CHECK_DBG("PA: pix %04x has inconsistent page header ix id/span:%04x/%04x, ref id/span:%04x/%04x flags:%02x\n", + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" has inconsistent page header ix id/span:"_SPIPRIid"/"_SPIPRIsp", ref id/span:"_SPIPRIid"/"_SPIPRIsp" flags:"_SPIPRIfl"\n", rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i, rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags); // try finding correct page @@ -635,23 +654,23 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { SPIFFS_CHECK_RES(res); if (data_pix == 0) { // not found, this index is badly borked - SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id %04x\n", p_hdr.obj_id); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id "_SPIPRIid"\n", p_hdr.obj_id); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); SPIFFS_CHECK_RES(res); break; } else { // found it, so rewrite index - SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix %04x, rewrite ix pix %04x id %04x\n", + SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix "_SPIPRIpg", rewrite ix pix "_SPIPRIpg" id "_SPIPRIid"\n", data_pix, cur_pix, p_hdr.obj_id); res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix); if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); } else { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); } SPIFFS_CHECK_RES(res); restart = 1; @@ -662,14 +681,14 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { const u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits); const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits; if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) { - SPIFFS_CHECK_DBG("PA: pix %04x multiple referenced from page %04x\n", + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" multiple referenced from page "_SPIPRIpg"\n", rpix, cur_pix); // Here, we should have fixed all broken references - getting this means there // must be multiple files with same object id. Only solution is to delete // the object which is referring to this page - SPIFFS_CHECK_DBG("PA: FIXUP: removing object %04x and page %04x\n", + SPIFFS_CHECK_DBG("PA: FIXUP: removing object "_SPIPRIid" and page "_SPIPRIpg"\n", p_hdr.obj_id, cur_pix); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); SPIFFS_CHECK_RES(res); // extra precaution, delete this page also @@ -706,7 +725,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { if (bitmask == 0x1) { // 001 - SPIFFS_CHECK_DBG("PA: pix %04x USED, UNREFERENCED, not index\n", cur_pix); + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, UNREFERENCED, not index\n", cur_pix); u8_t rewrite_ix_to_this = 0; u8_t delete_page = 0; @@ -722,7 +741,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { if (((rpix == (spiffs_page_ix)-1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) { // pointing to a bad page altogether, rewrite index to this rewrite_ix_to_this = 1; - SPIFFS_CHECK_DBG("PA: corresponding ref is bad: %04x, rewrite to this %04x\n", rpix, cur_pix); + SPIFFS_CHECK_DBG("PA: corresponding ref is bad: "_SPIPRIpg", rewrite to this "_SPIPRIpg"\n", rpix, cur_pix); } else { // pointing to something else, check what spiffs_page_header rp_hdr; @@ -733,12 +752,12 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { ((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) == (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) { // pointing to something else valid, just delete this page then - SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: %04x, delete this %04x\n", rpix, cur_pix); + SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: "_SPIPRIpg", delete this "_SPIPRIpg"\n", rpix, cur_pix); delete_page = 1; } else { // pointing to something weird, update index to point to this page instead if (rpix != cur_pix) { - SPIFFS_CHECK_DBG("PA: corresponding ref is weird: %04x %s%s%s%s, rewrite this %04x\n", rpix, + SPIFFS_CHECK_DBG("PA: corresponding ref is weird: "_SPIPRIpg" %s%s%s%s, rewrite this "_SPIPRIpg"\n", rpix, (rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ", (rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ", (rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "", @@ -751,32 +770,32 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { } } } else if (res == SPIFFS_ERR_NOT_FOUND) { - SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete %04x\n", cur_pix); + SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete "_SPIPRIpg"\n", cur_pix); delete_page = 1; res = SPIFFS_OK; } if (rewrite_ix_to_this) { // if pointing to invalid page, redirect index to this page - SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id %04x data spix %04x to point to this pix: %04x\n", + SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id "_SPIPRIid" data spix "_SPIPRIsp" to point to this pix: "_SPIPRIpg"\n", p_hdr.obj_id, p_hdr.span_ix, cur_pix); res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix); if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { // index bad also, cannot mend this file - SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); res = spiffs_page_delete(fs, cur_pix); SPIFFS_CHECK_RES(res); res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); } else { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); } SPIFFS_CHECK_RES(res); restart = 1; continue; } else if (delete_page) { - SPIFFS_CHECK_DBG("PA: FIXUP: deleting page %04x\n", cur_pix); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + SPIFFS_CHECK_DBG("PA: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); res = spiffs_page_delete(fs, cur_pix); } SPIFFS_CHECK_RES(res); @@ -784,7 +803,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { if (bitmask == 0x2) { // 010 - SPIFFS_CHECK_DBG("PA: pix %04x FREE, REFERENCED, not index\n", cur_pix); + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, not index\n", cur_pix); // no op, this should be taken care of when checking valid references } @@ -794,7 +813,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { if (bitmask == 0x4) { // 100 - SPIFFS_CHECK_DBG("PA: pix %04x FREE, unreferenced, INDEX\n", cur_pix); + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, unreferenced, INDEX\n", cur_pix); // this should never happen, major fubar } @@ -804,20 +823,22 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { if (bitmask == 0x6) { // 110 - SPIFFS_CHECK_DBG("PA: pix %04x FREE, REFERENCED, INDEX\n", cur_pix); + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, INDEX\n", cur_pix); // no op, this should be taken care of when checking valid references } if (bitmask == 0x7) { // 111 - SPIFFS_CHECK_DBG("PA: pix %04x USED, REFERENCED, INDEX\n", cur_pix); + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, REFERENCED, INDEX\n", cur_pix); // no op, this should be taken care of when checking valid references } } } } + + SPIFFS_CHECK_DBG("PA: processed "_SPIPRIpg", restart "_SPIPRIi"\n", pix_offset, restart); // next page range if (!restart) { pix_offset += pages_per_scan; @@ -828,12 +849,12 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { // Checks consistency amongst all pages and fixes irregularities s32_t spiffs_page_consistency_check(spiffs *fs) { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0); s32_t res = spiffs_page_consistency_check_i(fs); if (res != SPIFFS_OK) { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0); } - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0); return res; } @@ -855,14 +876,14 @@ static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) { } static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, - int cur_entry, u32_t user_data, void *user_p) { - (void)user_data; + int cur_entry, const void *user_const_p, void *user_var_p) { + (void)user_const_p; s32_t res_c = SPIFFS_VIS_COUNTINUE; s32_t res = SPIFFS_OK; - u32_t *log_ix = (u32_t *)user_p; + u32_t *log_ix = (u32_t*)user_var_p; spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, (cur_block * 256)/fs->block_count, 0); if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { @@ -877,9 +898,9 @@ static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id o if (p_hdr.span_ix == 0 && (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == (SPIFFS_PH_FLAG_DELET)) { - SPIFFS_CHECK_DBG("IX: pix %04x, obj id:%04x spix:%04x header not fully deleted - deleting\n", + SPIFFS_CHECK_DBG("IX: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" header not fully deleted - deleting\n", cur_pix, obj_id, p_hdr.span_ix); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id); res = spiffs_page_delete(fs, cur_pix); SPIFFS_CHECK_RES(res); return res_c; @@ -933,9 +954,9 @@ static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id o } if (delete) { - SPIFFS_CHECK_DBG("IX: FIXUP: pix %04x, obj id:%04x spix:%04x is orphan index - deleting\n", + SPIFFS_CHECK_DBG("IX: FIXUP: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" is orphan index - deleting\n", cur_pix, obj_id, p_hdr.span_ix); - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id); res = spiffs_page_delete(fs, cur_pix); SPIFFS_CHECK_RES(res); } @@ -958,16 +979,17 @@ s32_t spiffs_object_index_consistency_check(spiffs *fs) { // a reachable/unreachable object id. memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); u32_t obj_id_log_ix = 0; - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0); res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix, - 0, 0); + 0, 0); if (res == SPIFFS_VIS_END) { res = SPIFFS_OK; } if (res != SPIFFS_OK) { - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0); } - if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0); return res; } +#endif // !SPIFFS_READ_ONLY diff --git a/components/spiffs/library/spiffs_gc.c b/components/spiffs/spiffs/src/spiffs_gc.c similarity index 75% rename from components/spiffs/library/spiffs_gc.c rename to components/spiffs/spiffs/src/spiffs_gc.c index 5752e708..db1af4cc 100644 --- a/components/spiffs/library/spiffs_gc.c +++ b/components/spiffs/spiffs/src/spiffs_gc.c @@ -1,6 +1,8 @@ #include "spiffs.h" #include "spiffs_nucleus.h" +#if !SPIFFS_READ_ONLY + // Erases a logical block and updates the erase counter. // If cache is enabled, all pages that might be cached in this block // is dropped. @@ -9,7 +11,7 @@ static s32_t spiffs_gc_erase_block( spiffs_block_ix bix) { s32_t res; - SPIFFS_GC_DBG("gc: erase block %i\n", bix); + SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix); res = spiffs_erase_block(fs, bix); SPIFFS_CHECK_RES(res); @@ -36,7 +38,7 @@ s32_t spiffs_gc_quick( int cur_entry = 0; spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; - SPIFFS_GC_DBG("gc_quick: running\n", cur_block); + SPIFFS_GC_DBG("gc_quick: running\n"); #if SPIFFS_GC_STATS fs->stats_gc_runs++; #endif @@ -120,19 +122,19 @@ s32_t spiffs_gc_check( u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs); // if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { -// SPIFFS_GC_DBG("gc: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); +// SPIFFS_GC_DBG("gc: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); // return SPIFFS_ERR_FULL; // } if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) { - SPIFFS_GC_DBG("gc_check: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); + SPIFFS_GC_DBG("gc_check: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); return SPIFFS_ERR_FULL; } do { - SPIFFS_GC_DBG("\ngc_check #%i: run gc free_blocks:%i pfree:%i pallo:%i pdele:%i [%i] len:%i of %i\n", + SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\n", tries, fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted), - len, free_pages*SPIFFS_DATA_PAGE_SIZE(fs)); + len, (u32_t)(free_pages*SPIFFS_DATA_PAGE_SIZE(fs))); spiffs_block_ix *cands; int count; @@ -150,13 +152,13 @@ s32_t spiffs_gc_check( #endif cand = cands[0]; fs->cleaning = 1; - //printf("gcing: cleaning block %i\n", cand); + //SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand); res = spiffs_gc_clean(fs, cand); fs->cleaning = 0; if (res < 0) { - SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res); + SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); } else { - SPIFFS_GC_DBG("gc_check: cleaning block %i, result %i\n", cand, res); + SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); } SPIFFS_CHECK_RES(res); @@ -186,7 +188,7 @@ s32_t spiffs_gc_check( res = SPIFFS_ERR_FULL; } - SPIFFS_GC_DBG("gc_check: finished, %i dirty, blocks %i free, %i pages free, %i tries, res %i\n", + SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\n", fs->stats_p_allocated + fs->stats_p_deleted, fs->free_blocks, free_pages, tries, res); @@ -224,7 +226,7 @@ s32_t spiffs_gc_erase_page_stats( } // per entry obj_lookup_page++; } // per object lookup page - SPIFFS_GC_DBG("gc_check: wipe pallo:%i pdele:%i\n", allo, dele); + SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi" pdele:"_SPIPRIi"\n", allo, dele); fs->stats_p_allocated -= allo; fs->stats_p_deleted -= dele; return res; @@ -249,10 +251,12 @@ s32_t spiffs_gc_find_candidate( memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); // divide up work area into block indices and scores - // todo alignment? spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work; s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix)); + // align cand_scores on s32_t boundary + cand_scores = (s32_t*)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1)); + *block_candidates = cand_blocks; int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); @@ -290,7 +294,7 @@ s32_t spiffs_gc_find_candidate( // calculate score and insert into candidate table // stoneage sort, but probably not so many blocks - if (res == SPIFFS_OK && deleted_pages_in_block > 0) { + if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) { // read erase count spiffs_obj_id erase_count; res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, @@ -310,7 +314,7 @@ s32_t spiffs_gc_find_candidate( used_pages_in_block * SPIFFS_GC_HEUR_W_USED + erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE); int cand_ix = 0; - SPIFFS_GC_DBG("gc_check: bix:%i del:%i use:%i score:%i\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); + SPIFFS_GC_DBG("gc_check: bix:"_SPIPRIbl" del:"_SPIPRIi" use:"_SPIPRIi" score:"_SPIPRIi"\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); while (cand_ix < max_candidates) { if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) { cand_blocks[cand_ix] = cur_block; @@ -352,6 +356,7 @@ typedef struct { spiffs_obj_id cur_obj_id; spiffs_span_ix cur_objix_spix; spiffs_page_ix cur_objix_pix; + spiffs_page_ix cur_data_pix; int stored_scan_entry_index; u8_t obj_id_found; } spiffs_gc; @@ -371,15 +376,16 @@ typedef struct { // s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { s32_t res = SPIFFS_OK; - int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + // this is the global localizer being pushed and popped int cur_entry = 0; spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; - spiffs_gc gc; + spiffs_gc gc; // our stack frame/state spiffs_page_ix cur_pix = 0; spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; - SPIFFS_GC_DBG("gc_clean: cleaning block %i\n", bix); + SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix); memset(&gc, 0, sizeof(spiffs_gc)); gc.state = FIND_OBJ_DATA; @@ -388,12 +394,12 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { // move free cursor to next block, cannot use free pages from the block we want to clean fs->free_cursor_block_ix = (bix+1)%fs->block_count; fs->free_cursor_obj_lu_entry = 0; - SPIFFS_GC_DBG("gc_clean: move free cursor to block %i\n", fs->free_cursor_block_ix); + SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix); } while (res == SPIFFS_OK && gc.state != FINISHED) { - SPIFFS_GC_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry); - gc.obj_id_found = 0; + SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry); + gc.obj_id_found = 0; // reset (to no found data page) // scan through lookup pages int obj_lookup_page = cur_entry / entries_per_page; @@ -404,7 +410,7 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); - // check each entry + // check each object lookup entry while (scan && res == SPIFFS_OK && cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; @@ -413,21 +419,26 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { // act upon object id depending on gc state switch (gc.state) { case FIND_OBJ_DATA: + // find a data page if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) { - SPIFFS_GC_DBG("gc_clean: FIND_DATA state:%i - found obj id %04x\n", gc.state, obj_id); + // found a data page, stop scanning and handle in switch case below + SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id); gc.obj_id_found = 1; gc.cur_obj_id = obj_id; + gc.cur_data_pix = cur_pix; scan = 0; } break; case MOVE_OBJ_DATA: + // evacuate found data pages for corresponding object index we have in memory, + // update memory representation if (obj_id == gc.cur_obj_id) { spiffs_page_header p_hdr; res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); SPIFFS_CHECK_RES(res); - SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page %04x:%04x @ %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) { SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n"); } else { @@ -435,7 +446,7 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { // move page res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix); - SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix %04x:%04x page %04x to %04x\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix); SPIFFS_CHECK_RES(res); // move wipes obj_lu, reload it res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, @@ -443,8 +454,10 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); SPIFFS_CHECK_RES(res); } else { - // page is deleted but not deleted in lookup, scrap it - SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix); + // page is deleted but not deleted in lookup, scrap it - + // might seem unnecessary as we will erase this block, but + // we might get aborted + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); res = spiffs_page_delete(fs, cur_pix); SPIFFS_CHECK_RES(res); new_data_pix = SPIFFS_OBJ_ID_FREE; @@ -453,16 +466,17 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { if (gc.cur_objix_spix == 0) { // update object index header page ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix; - SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); } else { // update object index page ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix; - SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); } } } break; case MOVE_OBJ_IX: + // find and evacuate object index pages if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { // found an index object id @@ -475,20 +489,24 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { // move page res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix); - SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix %04x:%04x page %04x to %04x\n", obj_id, p_hdr.span_ix, cur_pix, new_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix); SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, obj_id, p_hdr.span_ix, new_pix, 0); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr, + SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0); // move wipes obj_lu, reload it res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); SPIFFS_CHECK_RES(res); } else { - // page is deleted but not deleted in lookup, scrap it - SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix %04x:%04x page %04x\n", obj_id, p_hdr.span_ix, cur_pix); + // page is deleted but not deleted in lookup, scrap it - + // might seem unnecessary as we will erase this block, but + // we might get aborted + SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); res = spiffs_page_delete(fs, cur_pix); if (res == SPIFFS_OK) { - spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0); } } SPIFFS_CHECK_RES(res); @@ -497,72 +515,92 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { default: scan = 0; break; - } + } // switch gc state cur_entry++; } // per entry - obj_lookup_page++; + obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop } // per object lookup page - if (res != SPIFFS_OK) break; // state finalization and switch switch (gc.state) { case FIND_OBJ_DATA: if (gc.obj_id_found) { + // handle found data page - // find out corresponding obj ix page and load it to memory spiffs_page_header p_hdr; spiffs_page_ix objix_pix; - gc.stored_scan_entry_index = cur_entry; - cur_entry = 0; + gc.stored_scan_entry_index = cur_entry; // push cursor + cur_entry = 0; // restart scan from start gc.state = MOVE_OBJ_DATA; res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr); SPIFFS_CHECK_RES(res); gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix); - SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:%04x\n", gc.cur_objix_spix); + SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix); res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // on borked systems we might get an ERR_NOT_FOUND here - + // this is handled by simply deleting the page as it is not referenced + // from anywhere + SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix); + res = spiffs_page_delete(fs, gc.cur_data_pix); + SPIFFS_CHECK_RES(res); + // then we restore states and continue scanning for data pages + cur_entry = gc.stored_scan_entry_index; // pop cursor + gc.state = FIND_OBJ_DATA; + break; // done + } SPIFFS_CHECK_RES(res); - SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page %04x\n", objix_pix); + SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix); res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); SPIFFS_CHECK_RES(res); + // cannot allow a gc if the presumed index in fact is no index, a + // check must run or lot of data may be lost SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix); gc.cur_objix_pix = objix_pix; } else { + // no more data pages found, passed thru all block, start evacuating object indices gc.state = MOVE_OBJ_IX; cur_entry = 0; // restart entry scan index } break; case MOVE_OBJ_DATA: { - // store modified objix (hdr) page + // store modified objix (hdr) page residing in memory now that all + // data pages belonging to this object index and residing in the block + // we want to evacuate spiffs_page_ix new_objix_pix; gc.state = FIND_OBJ_DATA; - cur_entry = gc.stored_scan_entry_index; + cur_entry = gc.stored_scan_entry_index; // pop cursor if (gc.cur_objix_spix == 0) { // store object index header page - res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, &new_objix_pix); - SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, %04x:%04x\n", new_objix_pix, 0); + res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0); SPIFFS_CHECK_RES(res); } else { // store object index page res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix); - SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, %04x:%04x\n", new_objix_pix, objix->p_hdr.span_ix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix); SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); } } break; case MOVE_OBJ_IX: + // scanned thru all block, no more object indices found - our work here is done gc.state = FINISHED; break; default: cur_entry = 0; break; - } - SPIFFS_GC_DBG("gc_clean: state-> %i\n", gc.state); + } // switch gc.state + SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state); } // while state != FINISHED return res; } +#endif // !SPIFFS_READ_ONLY diff --git a/components/spiffs/library/spiffs_hydrogen.c b/components/spiffs/spiffs/src/spiffs_hydrogen.c similarity index 52% rename from components/spiffs/library/spiffs_hydrogen.c rename to components/spiffs/spiffs/src/spiffs_hydrogen.c index 977c0039..235aaaa6 100644 --- a/components/spiffs/library/spiffs_hydrogen.c +++ b/components/spiffs/spiffs/src/spiffs_hydrogen.c @@ -8,7 +8,9 @@ #include "spiffs.h" #include "spiffs_nucleus.h" +#if SPIFFS_CACHE == 1 static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh); +#endif #if SPIFFS_BUFFER_HELP u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) { @@ -26,6 +28,10 @@ u8_t SPIFFS_mounted(spiffs *fs) { } s32_t SPIFFS_format(spiffs *fs) { +#if SPIFFS_READ_ONLY + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; +#else SPIFFS_API_CHECK_CFG(fs); if (SPIFFS_CHECK_MOUNT(fs)) { fs->err_code = SPIFFS_ERR_MOUNTED; @@ -49,25 +55,48 @@ s32_t SPIFFS_format(spiffs *fs) { SPIFFS_UNLOCK(fs); return 0; +#endif // SPIFFS_READ_ONLY } +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +s32_t SPIFFS_probe_fs(spiffs_config *config) { + SPIFFS_API_DBG("%s\n", __func__); + s32_t res = spiffs_probe(config); + return res; +} + +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, u8_t *fd_space, u32_t fd_space_size, void *cache, u32_t cache_size, spiffs_check_callback check_cb_f) { + SPIFFS_API_DBG("%s " + " sz:"_SPIPRIi " logpgsz:"_SPIPRIi " logblksz:"_SPIPRIi " perasz:"_SPIPRIi + " addr:"_SPIPRIad + " fdsz:"_SPIPRIi " cachesz:"_SPIPRIi + "\n", + __func__, + SPIFFS_CFG_PHYS_SZ(fs), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + SPIFFS_CFG_LOG_BLOCK_SZ(fs), + SPIFFS_CFG_PHYS_ERASE_SZ(fs), + SPIFFS_CFG_PHYS_ADDR(fs), + fd_space_size, cache_size); + void *user_data; SPIFFS_LOCK(fs); + user_data = fs->user_data; memset(fs, 0, sizeof(spiffs)); - memcpy(&fs->cfg, config, sizeof(spiffs_config)); + _SPIFFS_MEMCPY(&fs->cfg, config, sizeof(spiffs_config)); + fs->user_data = user_data; fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); fs->work = &work[0]; fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; memset(fd_space, 0, fd_space_size); - // align fd_space pointer to pointer size byte boundary, below is safe + // align fd_space pointer to pointer size byte boundary u8_t ptr_size = sizeof(void*); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpointer-to-int-cast" - u8_t addr_lsb = ((u8_t)fd_space) & (ptr_size-1); -#pragma GCC diagnostic pop + u8_t addr_lsb = ((u8_t)(intptr_t)fd_space) & (ptr_size-1); if (addr_lsb) { fd_space += (ptr_size-addr_lsb); fd_space_size -= (ptr_size-addr_lsb); @@ -75,11 +104,8 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, fs->fd_space = fd_space; fs->fd_count = (fd_space_size/sizeof(spiffs_fd)); - // align cache pointer to 4 byte boundary, below is safe -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpointer-to-int-cast" - addr_lsb = ((u8_t)cache) & (ptr_size-1); -#pragma GCC diagnostic pop + // align cache pointer to 4 byte boundary + addr_lsb = ((u8_t)(intptr_t)cache) & (ptr_size-1); if (addr_lsb) { u8_t *cache_8 = (u8_t *)cache; cache_8 += (ptr_size-addr_lsb); @@ -89,9 +115,10 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, if (cache_size & (ptr_size-1)) { cache_size -= (cache_size & (ptr_size-1)); } + #if SPIFFS_CACHE fs->cache = cache; - fs->cache_size = (cache_size > (config->log_page_size*32)) ? config->log_page_size*32 : cache_size; + fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs)*32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs)*32 : cache_size; spiffs_cache_init(fs); #endif @@ -107,14 +134,14 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, res = spiffs_obj_lu_scan(fs); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - SPIFFS_DBG("page index byte len: %i\n", SPIFFS_CFG_LOG_PAGE_SZ(fs)); - SPIFFS_DBG("object lookup pages: %i\n", SPIFFS_OBJ_LOOKUP_PAGES(fs)); - SPIFFS_DBG("page pages per block: %i\n", SPIFFS_PAGES_PER_BLOCK(fs)); - SPIFFS_DBG("page header length: %i\n", sizeof(spiffs_page_header)); - SPIFFS_DBG("object header index entries: %i\n", SPIFFS_OBJ_HDR_IX_LEN(fs)); - SPIFFS_DBG("object index entries: %i\n", SPIFFS_OBJ_IX_LEN(fs)); - SPIFFS_DBG("available file descriptors: %i\n", fs->fd_count); - SPIFFS_DBG("free blocks: %i\n", fs->free_blocks); + SPIFFS_DBG("page index byte len: "_SPIPRIi"\n", (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)); + SPIFFS_DBG("object lookup pages: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_LOOKUP_PAGES(fs)); + SPIFFS_DBG("page pages per block: "_SPIPRIi"\n", (u32_t)SPIFFS_PAGES_PER_BLOCK(fs)); + SPIFFS_DBG("page header length: "_SPIPRIi"\n", (u32_t)sizeof(spiffs_page_header)); + SPIFFS_DBG("object header index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_HDR_IX_LEN(fs)); + SPIFFS_DBG("object index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_IX_LEN(fs)); + SPIFFS_DBG("available file descriptors: "_SPIPRIi"\n", (u32_t)fs->fd_count); + SPIFFS_DBG("free blocks: "_SPIPRIi"\n", (u32_t)fs->free_blocks); fs->check_cb_f = check_cb_f; @@ -126,6 +153,7 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, } void SPIFFS_unmount(spiffs *fs) { + SPIFFS_API_DBG("%s\n", __func__); if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return; SPIFFS_LOCK(fs); u32_t i; @@ -149,46 +177,74 @@ s32_t SPIFFS_errno(spiffs *fs) { } void SPIFFS_clearerr(spiffs *fs) { + SPIFFS_API_DBG("%s\n", __func__); fs->err_code = SPIFFS_OK; } -s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode) { +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); +#if SPIFFS_READ_ONLY + (void)fs; (void)path; (void)mode; + return SPIFFS_ERR_RO_NOT_IMPL; +#else (void)mode; SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } SPIFFS_LOCK(fs); spiffs_obj_id obj_id; s32_t res; - res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (u8_t *)path); + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t*)path); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_create(fs, obj_id, (u8_t *)path, SPIFFS_TYPE_FILE, 0); + res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, 0); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); SPIFFS_UNLOCK(fs); return 0; +#endif // SPIFFS_READ_ONLY } -spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode mode) { +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_DBG("%s '%s' "_SPIPRIfl "\n", __func__, path, flags); (void)mode; SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } SPIFFS_LOCK(fs); spiffs_fd *fd; spiffs_page_ix pix; - s32_t res = spiffs_fd_find_new(fs, &fd); +#if SPIFFS_READ_ONLY + // not valid flags in read only mode + flags &= ~(SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC); +#endif // SPIFFS_READ_ONLY + + s32_t res = spiffs_fd_find_new(fs, &fd, path); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)path, &pix); - if ((flags & SPIFFS_CREAT) == 0) { + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); + if ((flags & SPIFFS_O_CREAT) == 0) { if (res < SPIFFS_OK) { spiffs_fd_return(fs, fd->file_nbr); } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } - if ((flags & SPIFFS_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { + if (res == SPIFFS_OK && + (flags & (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) == (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) { + // creat and excl and file exists - fail + res = SPIFFS_ERR_FILE_EXISTS; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((flags & SPIFFS_O_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { +#if !SPIFFS_READ_ONLY spiffs_obj_id obj_id; // no need to enter conflicting name here, already looked for it above res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, 0); @@ -196,12 +252,13 @@ spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode spiffs_fd_return(fs, fd->file_nbr); } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_create(fs, obj_id, (u8_t*)path, SPIFFS_TYPE_FILE, &pix); + res = spiffs_object_create(fs, obj_id, (const u8_t*)path, 0, SPIFFS_TYPE_FILE, &pix); if (res < SPIFFS_OK) { spiffs_fd_return(fs, fd->file_nbr); } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - flags &= ~SPIFFS_TRUNC; + flags &= ~SPIFFS_O_TRUNC; +#endif // !SPIFFS_READ_ONLY } else { if (res < SPIFFS_OK) { spiffs_fd_return(fs, fd->file_nbr); @@ -213,29 +270,32 @@ spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode spiffs_fd_return(fs, fd->file_nbr); } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if (flags & SPIFFS_TRUNC) { +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { res = spiffs_object_truncate(fd, 0, 0); if (res < SPIFFS_OK) { spiffs_fd_return(fs, fd->file_nbr); } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } +#endif // !SPIFFS_READ_ONLY fd->fdoffset = 0; SPIFFS_UNLOCK(fs); - return fd->file_nbr; + return SPIFFS_FH_OFFS(fs, fd->file_nbr); } spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_DBG("%s '%s':"_SPIPRIid " "_SPIPRIfl "\n", __func__, e->name, e->obj_id, flags); SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); spiffs_fd *fd; - s32_t res = spiffs_fd_find_new(fs, &fd); + s32_t res = spiffs_fd_find_new(fs, &fd, 0); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode); @@ -243,22 +303,71 @@ spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_fl spiffs_fd_return(fs, fd->file_nbr); } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if (flags & SPIFFS_TRUNC) { +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { res = spiffs_object_truncate(fd, 0, 0); if (res < SPIFFS_OK) { spiffs_fd_return(fs, fd->file_nbr); } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } +#endif // !SPIFFS_READ_ONLY fd->fdoffset = 0; SPIFFS_UNLOCK(fs); - return fd->file_nbr; + return SPIFFS_FH_OFFS(fs, fd->file_nbr); } -s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { +spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_DBG("%s "_SPIPRIpg " "_SPIPRIfl "\n", __func__, page_ix, flags); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (SPIFFS_IS_LOOKUP_PAGE(fs, page_ix)) { + res = SPIFFS_ERR_NOT_A_FILE; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + res = spiffs_object_open_by_page(fs, page_ix, fd, flags, mode); + if (res == SPIFFS_ERR_IS_FREE || + res == SPIFFS_ERR_DELETED || + res == SPIFFS_ERR_NOT_FINALIZED || + res == SPIFFS_ERR_NOT_INDEX || + res == SPIFFS_ERR_INDEX_SPAN_MISMATCH) { + res = SPIFFS_ERR_NOT_A_FILE; + } + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +static s32_t spiffs_hydro_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -266,14 +375,21 @@ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { spiffs_fd *fd; s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); res = spiffs_fd_get(fs, fh, &fd); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if ((fd->flags & SPIFFS_RDONLY) == 0) { + if ((fd->flags & SPIFFS_O_RDONLY) == 0) { res = SPIFFS_ERR_NOT_READABLE; SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } + if (fd->size == SPIFFS_UNDEFINED_LEN && len > 0) { + // special case for zero sized files + res = SPIFFS_ERR_END_OF_OBJECT; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + #if SPIFFS_CACHE_WR spiffs_fflush_cache(fs, fh); #endif @@ -305,6 +421,17 @@ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { return len; } +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len); + s32_t res = spiffs_hydro_read(fs, fh, buf, len); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + res = 0; + } + return res; +} + + +#if !SPIFFS_READ_ONLY static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) { (void)fs; s32_t res = SPIFFS_OK; @@ -326,8 +453,14 @@ static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offs return len; } +#endif // !SPIFFS_READ_ONLY s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len); +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; (void)buf; (void)len; + return SPIFFS_ERR_RO_NOT_IMPL; +#else SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -336,14 +469,18 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { s32_t res; u32_t offset; + fh = SPIFFS_FH_UNOFFS(fs, fh); res = spiffs_fd_get(fs, fh, &fd); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if ((fd->flags & SPIFFS_WRONLY) == 0) { + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { res = SPIFFS_ERR_NOT_WRITABLE; SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } + if ((fd->flags & SPIFFS_O_APPEND)) { + fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + } offset = fd->fdoffset; #if SPIFFS_CACHE_WR @@ -352,7 +489,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); } #endif - if (fd->flags & SPIFFS_APPEND) { + if (fd->flags & SPIFFS_O_APPEND) { if (fd->size == SPIFFS_UNDEFINED_LEN) { offset = 0; } else { @@ -366,7 +503,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { } #if SPIFFS_CACHE_WR - if ((fd->flags & SPIFFS_DIRECT) == 0) { + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) { // small write, try to cache it u8_t alloc_cpage = 1; @@ -377,13 +514,13 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page { // boundary violation, write back cache first and allocate new - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, boundary viol, offs:%i size:%i\n", + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", boundary viol, offs:"_SPIPRIi" size:"_SPIPRIi"\n", fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); res = spiffs_hydro_write(fs, fd, spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), fd->cache_page->offset, fd->cache_page->size); spiffs_cache_fd_release(fs, fd->cache_page); - SPIFFS_API_CHECK_RES(fs, res); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } else { // writing within cache alloc_cpage = 0; @@ -395,26 +532,37 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { if (fd->cache_page) { fd->cache_page->offset = offset; fd->cache_page->size = 0; - SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page %i for fd %i:%04x\n", + SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid"\n", fd->cache_page->ix, fd->file_nbr, fd->obj_id); } } if (fd->cache_page) { u32_t offset_in_cpage = offset - fd->cache_page->offset; - SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page %i for fd %i:%04x, offs %i:%i len %i\n", + SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", offs "_SPIPRIi":"_SPIPRIi" len "_SPIPRIi"\n", fd->cache_page->ix, fd->file_nbr, fd->obj_id, offset, offset_in_cpage, len); spiffs_cache *cache = spiffs_get_cache(fs); u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix); - memcpy(&cpage_data[offset_in_cpage], buf, len); +#ifdef _SPIFFS_TEST + { + intptr_t __a1 = (u8_t*)&cpage_data[offset_in_cpage]-(u8_t*)cache; + intptr_t __a2 = (u8_t*)&cpage_data[offset_in_cpage]+len-(u8_t*)cache; + intptr_t __b = sizeof(spiffs_cache) + cache->cpage_count * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); + if (__a1 > __b || __a2 > __b) { + printf("FATAL OOB: CACHE_WR: memcpy to cache buffer ixs:%4ld..%4ld of %4ld\n", __a1, __a2, __b); + ERREXIT(); + } + } +#endif + _SPIFFS_MEMCPY(&cpage_data[offset_in_cpage], buf, len); fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len); fd->fdoffset += len; SPIFFS_UNLOCK(fs); return len; } else { res = spiffs_hydro_write(fs, fd, buf, offset, len); - SPIFFS_API_CHECK_RES(fs, res); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); fd->fdoffset += len; SPIFFS_UNLOCK(fs); return res; @@ -423,58 +571,65 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { // big write, no need to cache it - but first check if there is a cached write already if (fd->cache_page) { // write back cache first - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, big write, offs:%i size:%i\n", + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", big write, offs:"_SPIPRIi" size:"_SPIPRIi"\n", fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); res = spiffs_hydro_write(fs, fd, spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), fd->cache_page->offset, fd->cache_page->size); spiffs_cache_fd_release(fs, fd->cache_page); - SPIFFS_API_CHECK_RES(fs, res); - res = spiffs_hydro_write(fs, fd, buf, offset, len); - SPIFFS_API_CHECK_RES(fs, res); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + // data written below } } } #endif res = spiffs_hydro_write(fs, fd, buf, offset, len); - SPIFFS_API_CHECK_RES(fs, res); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); fd->fdoffset += len; SPIFFS_UNLOCK(fs); return res; +#endif // SPIFFS_READ_ONLY } s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " %s\n", __func__, fh, offs, (const char* []){"SET","CUR","END","???"}[MIN(whence,3)]); SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); spiffs_fd *fd; s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); res = spiffs_fd_get(fs, fh, &fd); - SPIFFS_API_CHECK_RES(fs, res); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); #if SPIFFS_CACHE_WR spiffs_fflush_cache(fs, fh); #endif + s32_t file_size = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + switch (whence) { case SPIFFS_SEEK_CUR: offs = fd->fdoffset+offs; break; case SPIFFS_SEEK_END: - offs = (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size) + offs; + offs = file_size + offs; break; } - - if (offs > (s32_t)fd->size) { + if (offs < 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_SEEK_BOUNDS); + } + if (offs > file_size) { + fd->fdoffset = file_size; res = SPIFFS_ERR_END_OF_OBJECT; } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - spiffs_span_ix data_spix = offs / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_span_ix data_spix = (offs > 0 ? (offs-1) : 0) / SPIFFS_DATA_PAGE_SIZE(fs); spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); if (fd->cursor_objix_spix != objix_spix) { spiffs_page_ix pix; @@ -491,19 +646,27 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { return offs; } -s32_t SPIFFS_remove(spiffs *fs, char *path) { +s32_t SPIFFS_remove(spiffs *fs, const char *path) { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); +#if SPIFFS_READ_ONLY + (void)fs; (void)path; + return SPIFFS_ERR_RO_NOT_IMPL; +#else SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } SPIFFS_LOCK(fs); spiffs_fd *fd; spiffs_page_ix pix; s32_t res; - res = spiffs_fd_find_new(fs, &fd); + res = spiffs_fd_find_new(fs, &fd, 0); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_find_object_index_header_by_name(fs, (u8_t *)path, &pix); + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); if (res != SPIFFS_OK) { spiffs_fd_return(fs, fd->file_nbr); } @@ -523,19 +686,26 @@ s32_t SPIFFS_remove(spiffs *fs, char *path) { SPIFFS_UNLOCK(fs); return 0; +#endif // SPIFFS_READ_ONLY } s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; + return SPIFFS_ERR_RO_NOT_IMPL; +#else SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); spiffs_fd *fd; s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); res = spiffs_fd_get(fs, fh, &fd); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - if ((fd->flags & SPIFFS_WRONLY) == 0) { + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { res = SPIFFS_ERR_NOT_WRITABLE; SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } @@ -551,9 +721,11 @@ s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { SPIFFS_UNLOCK(fs); return 0; +#endif // SPIFFS_READ_ONLY } static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) { + (void)fh; spiffs_page_object_ix_header objix_hdr; spiffs_obj_id obj_id; s32_t res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, @@ -566,23 +738,31 @@ static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spi obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id); SPIFFS_API_CHECK_RES(fs, res); - s->obj_id = obj_id; + s->obj_id = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; s->type = objix_hdr.type; s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + s->pix = pix; strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN); +#if SPIFFS_OBJ_META_LEN + _SPIFFS_MEMCPY(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); +#endif return res; } -s32_t SPIFFS_stat(spiffs *fs, char *path, spiffs_stat *s) { +s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } SPIFFS_LOCK(fs); s32_t res; spiffs_page_ix pix; - res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)path, &pix); + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)path, &pix); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); res = spiffs_stat_pix(fs, pix, 0, s); @@ -593,6 +773,7 @@ s32_t SPIFFS_stat(spiffs *fs, char *path, spiffs_stat *s) { } s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_LOCK(fs); @@ -600,6 +781,7 @@ s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { spiffs_fd *fd; s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); res = spiffs_fd_get(fs, fh, &fd); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); @@ -616,21 +798,24 @@ s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { // Checks if there are any cached writes for the object id associated with // given filehandle. If so, these writes are flushed. +#if SPIFFS_CACHE == 1 static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { + (void)fs; + (void)fh; s32_t res = SPIFFS_OK; -#if SPIFFS_CACHE_WR +#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR spiffs_fd *fd; res = spiffs_fd_get(fs, fh, &fd); SPIFFS_API_CHECK_RES(fs, res); - if ((fd->flags & SPIFFS_DIRECT) == 0) { + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { if (fd->cache_page == 0) { // see if object id is associated with cache already fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); } if (fd->cache_page) { - SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %i for fd %i:%04x, flush, offs:%i size:%i\n", + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", flush, offs:"_SPIPRIi" size:"_SPIPRIi"\n", fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); res = spiffs_hydro_write(fs, fd, spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), @@ -645,13 +830,17 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { return res; } +#endif s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + (void)fh; SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); s32_t res = SPIFFS_OK; -#if SPIFFS_CACHE_WR +#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR SPIFFS_LOCK(fs); + fh = SPIFFS_FH_UNOFFS(fs, fh); res = spiffs_fflush_cache(fs, fh); SPIFFS_API_CHECK_RES_UNLOCK(fs,res); SPIFFS_UNLOCK(fs); @@ -660,38 +849,48 @@ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { return res; } -void SPIFFS_close(spiffs *fs, spiffs_file fh) { - if (!SPIFFS_CHECK_CFG((fs))) { - (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; - return; - } - - if (!SPIFFS_CHECK_MOUNT(fs)) { - fs->err_code = SPIFFS_ERR_NOT_MOUNTED; - return; - } - SPIFFS_LOCK(fs); - -#if SPIFFS_CACHE - spiffs_fflush_cache(fs, fh); -#endif - spiffs_fd_return(fs, fh); - - SPIFFS_UNLOCK(fs); -} - -s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) { +s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); + + s32_t res = SPIFFS_OK; + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); +#if SPIFFS_CACHE + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + res = spiffs_fd_return(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) { + SPIFFS_API_DBG("%s %s %s\n", __func__, old_path, new_path); +#if SPIFFS_READ_ONLY + (void)fs; (void)old_path; (void)new_path; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(new_path) > SPIFFS_OBJ_NAME_LEN - 1 || + strlen(old_path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } SPIFFS_LOCK(fs); spiffs_page_ix pix_old, pix_dummy; spiffs_fd *fd; - s32_t res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)old, &pix_old); + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)old_path, &pix_old); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)new, &pix_dummy); + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)new_path, &pix_dummy); if (res == SPIFFS_ERR_NOT_FOUND) { res = SPIFFS_OK; } else if (res == SPIFFS_OK) { @@ -699,7 +898,7 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) { } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_fd_find_new(fs, &fd); + res = spiffs_fd_find_new(fs, &fd, 0); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0); @@ -708,7 +907,50 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) { } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (u8_t*)new, + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t*)new_path, + 0, 0, &pix_dummy); +#if SPIFFS_TEMPORAL_FD_CACHE + if (res == SPIFFS_OK) { + spiffs_fd_temporal_cache_rehash(fs, old_path, new_path); + } +#endif + + spiffs_fd_return(fs, fd->file_nbr); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +#if SPIFFS_OBJ_META_LEN +s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) { +#if SPIFFS_READ_ONLY + (void)fs; (void)name; (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_page_ix pix, pix_dummy; + spiffs_fd *fd; + + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t*)name, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, 0, &pix_dummy); spiffs_fd_return(fs, fd->file_nbr); @@ -718,9 +960,45 @@ s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) { SPIFFS_UNLOCK(fs); return res; +#endif // SPIFFS_READ_ONLY } -spiffs_DIR *SPIFFS_opendir(spiffs *fs, char *name, spiffs_DIR *d) { +s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) { +#if SPIFFS_READ_ONLY + (void)fs; (void)fh; (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + s32_t res; + spiffs_fd *fd; + spiffs_page_ix pix_dummy; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} +#endif // SPIFFS_OBJ_META_LEN + +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) { + SPIFFS_API_DBG("%s\n", __func__); (void)name; if (!SPIFFS_CHECK_CFG((fs))) { @@ -744,9 +1022,9 @@ static s32_t spiffs_read_dir_v( spiffs_obj_id obj_id, spiffs_block_ix bix, int ix_entry, - u32_t user_data, - void *user_p) { - (void)user_data; + const void *user_const_p, + void *user_var_p) { + (void)user_const_p; s32_t res; spiffs_page_object_ix_header objix_hdr; if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || @@ -760,26 +1038,29 @@ static s32_t spiffs_read_dir_v( if (res != SPIFFS_OK) return res; if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && objix_hdr.p_hdr.span_ix == 0 && - (objix_hdr.p_hdr.flags& (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { - struct spiffs_dirent *e = (struct spiffs_dirent *)user_p; + struct spiffs_dirent *e = (struct spiffs_dirent*)user_var_p; e->obj_id = obj_id; strcpy((char *)e->name, (char *)objix_hdr.name); e->type = objix_hdr.type; e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; e->pix = pix; +#if SPIFFS_OBJ_META_LEN + _SPIFFS_MEMCPY(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); +#endif return SPIFFS_OK; } - return SPIFFS_VIS_COUNTINUE; } struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { + SPIFFS_API_DBG("%s\n", __func__); if (!SPIFFS_CHECK_MOUNT(d->fs)) { d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED; return 0; } - SPIFFS_LOCK(fs); + SPIFFS_LOCK(d->fs); spiffs_block_ix bix; int entry; @@ -799,21 +1080,28 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { if (res == SPIFFS_OK) { d->block = bix; d->entry = entry + 1; + e->obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; ret = e; } else { d->fs->err_code = res; } - SPIFFS_UNLOCK(fs); + SPIFFS_UNLOCK(d->fs); return ret; } s32_t SPIFFS_closedir(spiffs_DIR *d) { + SPIFFS_API_DBG("%s\n", __func__); SPIFFS_API_CHECK_CFG(d->fs); SPIFFS_API_CHECK_MOUNT(d->fs); return 0; } s32_t SPIFFS_check(spiffs *fs) { + SPIFFS_API_DBG("%s\n", __func__); +#if SPIFFS_READ_ONLY + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; +#else s32_t res; SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); @@ -829,9 +1117,11 @@ s32_t SPIFFS_check(spiffs *fs) { SPIFFS_UNLOCK(fs); return res; +#endif // SPIFFS_READ_ONLY } s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { + SPIFFS_API_DBG("%s\n", __func__); s32_t res = SPIFFS_OK; SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); @@ -856,6 +1146,11 @@ s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { } s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) { + SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, max_free_pages); +#if SPIFFS_READ_ONLY + (void)fs; (void)max_free_pages; + return SPIFFS_ERR_RO_NOT_IMPL; +#else s32_t res; SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); @@ -866,10 +1161,16 @@ s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) { SPIFFS_API_CHECK_RES_UNLOCK(fs, res); SPIFFS_UNLOCK(fs); return 0; +#endif // SPIFFS_READ_ONLY } s32_t SPIFFS_gc(spiffs *fs, u32_t size) { + SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, size); +#if SPIFFS_READ_ONLY + (void)fs; (void)size; + return SPIFFS_ERR_RO_NOT_IMPL; +#else s32_t res; SPIFFS_API_CHECK_CFG(fs); SPIFFS_API_CHECK_MOUNT(fs); @@ -880,8 +1181,199 @@ s32_t SPIFFS_gc(spiffs *fs, u32_t size) { SPIFFS_API_CHECK_RES_UNLOCK(fs, res); SPIFFS_UNLOCK(fs); return 0; +#endif // SPIFFS_READ_ONLY } +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + res = (fd->fdoffset >= (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size)); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + res = fd->fdoffset; + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) { + SPIFFS_API_DBG("%s\n", __func__); + SPIFFS_LOCK(fs); + fs->file_cb_f = cb_func; + SPIFFS_UNLOCK(fs); + return 0; +} + +#if SPIFFS_IX_MAP + +s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, + u32_t offset, u32_t len, spiffs_page_ix *map_buf) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " "_SPIPRIi "\n", __func__, fh, offset, len); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_MAPPED); + } + + map->map_buf = map_buf; + map->offset = offset; + // nb: spix range includes last + map->start_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + map->end_spix = (offset + len) / SPIFFS_DATA_PAGE_SIZE(fs); + memset(map_buf, 0, sizeof(spiffs_page_ix) * (map->end_spix - map->start_spix + 1)); + fd->ix_map = map; + + // scan for pixes + res = spiffs_populate_ix_map(fs, fd, 0, map->end_spix - map->start_spix + 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + fd->ix_map = 0; + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, offset); + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + spiffs_ix_map *map = fd->ix_map; + + s32_t spix_diff = offset / SPIFFS_DATA_PAGE_SIZE(fs) - map->start_spix; + map->offset = offset; + + // move existing pixes if within map offs + if (spix_diff != 0) { + // move vector + int i; + const s32_t vec_len = map->end_spix - map->start_spix + 1; // spix range includes last + map->start_spix += spix_diff; + map->end_spix += spix_diff; + if (spix_diff >= vec_len) { + // moving beyond range + memset(&map->map_buf, 0, vec_len * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, vec_len-1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else if (spix_diff > 0) { + // diff positive + for (i = 0; i < vec_len - spix_diff; i++) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[vec_len - spix_diff], 0, spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, vec_len - spix_diff, vec_len-1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // diff negative + for (i = vec_len - 1; i >= -spix_diff; i--) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[0], 0, -spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, -spix_diff - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + } + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes) { + SPIFFS_API_CHECK_CFG(fs); + // always add one extra page, the offset might change to the middle of a page + return (bytes + SPIFFS_DATA_PAGE_SIZE(fs) ) / SPIFFS_DATA_PAGE_SIZE(fs); +} + +s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries) { + SPIFFS_API_CHECK_CFG(fs); + return map_page_ix_entries * SPIFFS_DATA_PAGE_SIZE(fs); +} + +#endif // SPIFFS_IX_MAP #if SPIFFS_TEST_VISUALISATION s32_t SPIFFS_vis(spiffs *fs) { @@ -908,7 +1400,7 @@ s32_t SPIFFS_vis(spiffs *fs) { cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))) { spiffs_obj_id obj_id = obj_lu_buf[cur_entry-entry_offset]; if (cur_entry == 0) { - spiffs_printf("%4i ", bix); + spiffs_printf(_SPIPRIbl" ", bix); } else if ((cur_entry & 0x3f) == 0) { spiffs_printf(" "); } @@ -936,7 +1428,7 @@ s32_t SPIFFS_vis(spiffs *fs) { SPIFFS_CHECK_RES(res); if (erase_count != (spiffs_obj_id)-1) { - spiffs_printf("\tera_cnt: %i\n", erase_count); + spiffs_printf("\tera_cnt: "_SPIPRIi"\n", erase_count); } else { spiffs_printf("\tera_cnt: N/A\n"); } @@ -944,17 +1436,16 @@ s32_t SPIFFS_vis(spiffs *fs) { bix++; } // per block - spiffs_printf("era_cnt_max: %i\n", fs->max_erase_count); - spiffs_printf("last_errno: %i\n", fs->err_code); - spiffs_printf("blocks: %i\n", fs->block_count); - spiffs_printf("free_blocks: %i\n", fs->free_blocks); - spiffs_printf("page_alloc: %i\n", fs->stats_p_allocated); - spiffs_printf("page_delet: %i\n", fs->stats_p_deleted); + spiffs_printf("era_cnt_max: "_SPIPRIi"\n", fs->max_erase_count); + spiffs_printf("last_errno: "_SPIPRIi"\n", fs->err_code); + spiffs_printf("blocks: "_SPIPRIi"\n", fs->block_count); + spiffs_printf("free_blocks: "_SPIPRIi"\n", fs->free_blocks); + spiffs_printf("page_alloc: "_SPIPRIi"\n", fs->stats_p_allocated); + spiffs_printf("page_delet: "_SPIPRIi"\n", fs->stats_p_deleted); + SPIFFS_UNLOCK(fs); u32_t total, used; SPIFFS_info(fs, &total, &used); - spiffs_printf("used: %i of %i\n", used, total); - - SPIFFS_UNLOCK(fs); + spiffs_printf("used: "_SPIPRIi" of "_SPIPRIi"\n", used, total); return res; } #endif diff --git a/components/spiffs/library/spiffs_nucleus.c b/components/spiffs/spiffs/src/spiffs_nucleus.c similarity index 66% rename from components/spiffs/library/spiffs_nucleus.c rename to components/spiffs/spiffs/src/spiffs_nucleus.c index bfd028fc..27ecdff2 100644 --- a/components/spiffs/library/spiffs_nucleus.c +++ b/components/spiffs/spiffs/src/spiffs_nucleus.c @@ -29,6 +29,7 @@ static s32_t spiffs_page_data_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pi return res; } +#if !SPIFFS_READ_ONLY static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { s32_t res = SPIFFS_OK; if (pix == (spiffs_page_ix)-1) { @@ -56,6 +57,7 @@ static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix p #endif return res; } +#endif // !SPIFFS_READ_ONLY #if !SPIFFS_CACHE @@ -64,7 +66,7 @@ s32_t spiffs_phys_rd( u32_t addr, u32_t len, u8_t *dst) { - return fs->cfg.hal_read_f(addr, len, dst); + return SPIFFS_HAL_READ(fs, addr, len, dst); } s32_t spiffs_phys_wr( @@ -72,17 +74,19 @@ s32_t spiffs_phys_wr( u32_t addr, u32_t len, u8_t *src) { - return fs->cfg.hal_write_f(addr, len, src); + return SPIFFS_HAL_WRITE(fs, addr, len, src); } #endif +#if !SPIFFS_READ_ONLY s32_t spiffs_phys_cpy( spiffs *fs, spiffs_file fh, u32_t dst, u32_t src, u32_t len) { + (void)fh; s32_t res; u8_t b[SPIFFS_COPY_BUFFER_STACK]; while (len > 0) { @@ -97,10 +101,11 @@ s32_t spiffs_phys_cpy( } return SPIFFS_OK; } +#endif // !SPIFFS_READ_ONLY // Find object lookup entry containing given id with visitor. // Iterate over object lookup pages in each block until a given object id entry is found. -// When found, the visitor function is called with block index, entry index and user_data. +// When found, the visitor function is called with block index, entry index and user data. // If visitor returns SPIFFS_VIS_CONTINUE, the search goes on. Otherwise, the search will be // ended and visitor's return code is returned to caller. // If no visitor is given (0) the search returns on first entry with matching object id. @@ -112,8 +117,8 @@ s32_t spiffs_phys_cpy( // SPIFFS_VIS_NO_WRAP // @param obj_id argument object id // @param v visitor callback function -// @param user_data any data, passed to the callback visitor function -// @param user_p any pointer, passed to the callback visitor function +// @param user_const_p any const pointer, passed to the callback visitor function +// @param user_var_p any pointer, passed to the callback visitor function // @param block_ix reported block index where match was found // @param lu_entry reported look up index where match was found s32_t spiffs_obj_lu_find_entry_visitor( @@ -123,8 +128,8 @@ s32_t spiffs_obj_lu_find_entry_visitor( u8_t flags, spiffs_obj_id obj_id, spiffs_visitor_f v, - u32_t user_data, - void *user_p, + const void *user_const_p, + void *user_var_p, spiffs_block_ix *block_ix, int *lu_entry) { s32_t res = SPIFFS_OK; @@ -137,7 +142,7 @@ s32_t spiffs_obj_lu_find_entry_visitor( int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); // wrap initial - if (cur_entry >= (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { + if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { cur_entry = 0; cur_block++; cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); @@ -174,8 +179,8 @@ s32_t spiffs_obj_lu_find_entry_visitor( (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry-entry_offset], cur_block, cur_entry, - user_data, - user_p); + user_const_p, + user_var_p); if (res == SPIFFS_VIS_COUNTINUE || res == SPIFFS_VIS_COUNTINUE_RELOAD) { if (res == SPIFFS_VIS_COUNTINUE_RELOAD) { res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, @@ -217,6 +222,7 @@ s32_t spiffs_obj_lu_find_entry_visitor( return SPIFFS_VIS_END; } +#if !SPIFFS_READ_ONLY s32_t spiffs_erase_block( spiffs *fs, spiffs_block_ix bix) { @@ -226,8 +232,9 @@ s32_t spiffs_erase_block( // here we ignore res, just try erasing the block while (size > 0) { - SPIFFS_DBG("erase %08x:%08x\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); - (void)fs->cfg.hal_erase_f(addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + SPIFFS_DBG("erase "_SPIPRIad":"_SPIPRIi"\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + SPIFFS_HAL_ERASE(fs, addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs); size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs); } @@ -241,7 +248,7 @@ s32_t spiffs_erase_block( #if SPIFFS_USE_MAGIC // finally, write magic - spiffs_obj_id magic = SPIFFS_MAGIC(fs); + spiffs_obj_id magic = SPIFFS_MAGIC(fs, bix); res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, SPIFFS_MAGIC_PADDR(fs, bix), sizeof(spiffs_obj_id), (u8_t *)&magic); @@ -255,6 +262,59 @@ s32_t spiffs_erase_block( return res; } +#endif // !SPIFFS_READ_ONLY + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 +s32_t spiffs_probe( + spiffs_config *cfg) { + s32_t res; + u32_t paddr; + spiffs dummy_fs; // create a dummy fs struct just to be able to use macros + _SPIFFS_MEMCPY(&dummy_fs.cfg, cfg, sizeof(spiffs_config)); + dummy_fs.block_count = 0; + + // Read three magics, as one block may be in an aborted erase state. + // At least two of these must contain magic and be in decreasing order. + spiffs_obj_id magic[3]; + spiffs_obj_id bix_count[3]; + + spiffs_block_ix bix; + for (bix = 0; bix < 3; bix++) { + paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix); +#if SPIFFS_HAL_CALLBACK_EXTRA + // not any proper fs to report here, so callback with null + // (cross fingers that no-one gets angry) + res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); +#else + res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); +#endif + bix_count[bix] = magic[bix] ^ SPIFFS_MAGIC(&dummy_fs, 0); + SPIFFS_CHECK_RES(res); + } + + // check that we have sane number of blocks + if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; + // check that the order is correct, take aborted erases in calculation + // first block aborted erase + if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) { + return (bix_count[1]+1) * cfg->log_block_size; + } + // second block aborted erase + if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) { + return bix_count[0] * cfg->log_block_size; + } + // third block aborted erase + if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) { + return bix_count[0] * cfg->log_block_size; + } + // no block has aborted erase + if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) { + return bix_count[0] * cfg->log_block_size; + } + + return SPIFFS_ERR_PROBE_NOT_A_FS; +} +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 static s32_t spiffs_obj_lu_scan_v( @@ -262,11 +322,11 @@ static s32_t spiffs_obj_lu_scan_v( spiffs_obj_id obj_id, spiffs_block_ix bix, int ix_entry, - u32_t user_data, - void *user_p) { + const void *user_const_p, + void *user_var_p) { (void)bix; - (void)user_data; - (void)user_p; + (void)user_const_p; + (void)user_var_p; if (obj_id == SPIFFS_OBJ_ID_FREE) { if (ix_entry == 0) { fs->free_blocks++; @@ -309,7 +369,7 @@ s32_t spiffs_obj_lu_scan( sizeof(spiffs_obj_id), (u8_t *)&magic); SPIFFS_CHECK_RES(res); - if (magic != SPIFFS_MAGIC(fs)) { + if (magic != SPIFFS_MAGIC(fs, bix)) { if (unerased_bix == (spiffs_block_ix)-1) { // allow one unerased block as it might be powered down during an erase unerased_bix = bix; @@ -347,8 +407,12 @@ s32_t spiffs_obj_lu_scan( #if SPIFFS_USE_MAGIC if (unerased_bix != (spiffs_block_ix)-1) { // found one unerased block, remedy - SPIFFS_DBG("mount: erase block %i\n", bix); + SPIFFS_DBG("mount: erase block "_SPIPRIbl"\n", bix); +#if SPIFFS_READ_ONLY + res = SPIFFS_ERR_RO_ABORTED_OPERATION; +#else res = spiffs_erase_block(fs, unerased_bix); +#endif // SPIFFS_READ_ONLY SPIFFS_CHECK_RES(res); } #endif @@ -379,6 +443,7 @@ s32_t spiffs_obj_lu_scan( return res; } +#if !SPIFFS_READ_ONLY // Find free object lookup entry // Iterate over object lookup pages in each block until a free object id entry is found s32_t spiffs_obj_lu_find_free( @@ -402,17 +467,18 @@ s32_t spiffs_obj_lu_find_free( SPIFFS_OBJ_ID_FREE, block_ix, lu_entry); if (res == SPIFFS_OK) { fs->free_cursor_block_ix = *block_ix; - fs->free_cursor_obj_lu_entry = *lu_entry; + fs->free_cursor_obj_lu_entry = (*lu_entry) + 1; if (*lu_entry == 0) { fs->free_blocks--; } } - if (res == SPIFFS_VIS_END) { + if (res == SPIFFS_ERR_FULL) { SPIFFS_DBG("fs full\n"); } - return res == SPIFFS_VIS_END ? SPIFFS_ERR_FULL : res; + return res; } +#endif // !SPIFFS_READ_ONLY // Find object lookup entry containing given id // Iterate over object lookup pages in each block until a given object id entry is found @@ -437,8 +503,8 @@ static s32_t spiffs_obj_lu_find_id_and_span_v( spiffs_obj_id obj_id, spiffs_block_ix bix, int ix_entry, - u32_t user_data, - void *user_p) { + const void *user_const_p, + void *user_var_p) { s32_t res; spiffs_page_header ph; spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); @@ -446,10 +512,10 @@ static s32_t spiffs_obj_lu_find_id_and_span_v( SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph); SPIFFS_CHECK_RES(res); if (ph.obj_id == obj_id && - ph.span_ix == (spiffs_span_ix)user_data && + ph.span_ix == *((spiffs_span_ix*)user_var_p) && (ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET && !((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (ph.flags & SPIFFS_PH_FLAG_IXDELE) == 0 && ph.span_ix == 0) && - (user_p == 0 || *((spiffs_page_ix *)user_p) != pix)) { + (user_const_p == 0 || *((const spiffs_page_ix*)user_const_p) != pix)) { return SPIFFS_OK; } else { return SPIFFS_VIS_COUNTINUE; @@ -474,8 +540,8 @@ s32_t spiffs_obj_lu_find_id_and_span( SPIFFS_VIS_CHECK_ID, obj_id, spiffs_obj_lu_find_id_and_span_v, - (u32_t)spix, exclusion_pix ? &exclusion_pix : 0, + &spix, &bix, &entry); @@ -513,8 +579,8 @@ s32_t spiffs_obj_lu_find_id_and_span_by_phdr( SPIFFS_VIS_CHECK_PH, obj_id, spiffs_obj_lu_find_id_and_span_v, - (u32_t)spix, exclusion_pix ? &exclusion_pix : 0, + &spix, &bix, &entry); @@ -534,6 +600,153 @@ s32_t spiffs_obj_lu_find_id_and_span_by_phdr( return res; } +#if SPIFFS_IX_MAP + +// update index map of given fd with given object index data +static void spiffs_update_ix_map(spiffs *fs, + spiffs_fd *fd, spiffs_span_ix objix_spix, spiffs_page_object_ix *objix) { +#if SPIFFS_SINGLETON + (void)fs; +#endif + spiffs_ix_map *map = fd->ix_map; + spiffs_span_ix map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix); + spiffs_span_ix map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->end_spix); + + // check if updated ix is within map range + if (objix_spix < map_objix_start_spix || objix_spix > map_objix_end_spix) { + return; + } + + // update memory mapped page index buffer to new pages + + // get range of updated object index map data span indices + spiffs_span_ix objix_data_spix_start = + SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, objix_spix); + spiffs_span_ix objix_data_spix_end = objix_data_spix_start + + (objix_spix == 0 ? SPIFFS_OBJ_HDR_IX_LEN(fs) : SPIFFS_OBJ_IX_LEN(fs)); + + // calc union of object index range and index map range array + spiffs_span_ix map_spix = MAX(map->start_spix, objix_data_spix_start); + spiffs_span_ix map_spix_end = MIN(map->end_spix + 1, objix_data_spix_end); + + while (map_spix < map_spix_end) { + spiffs_page_ix objix_data_pix; + if (objix_spix == 0) { + // get data page from object index header page + objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix_header)))[map_spix]; + } else { + // get data page from object index page + objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, map_spix)]; + } + + if (objix_data_pix == (spiffs_page_ix)-1) { + // reached end of object, abort + break; + } + + map->map_buf[map_spix - map->start_spix] = objix_data_pix; + SPIFFS_DBG("map "_SPIPRIid":"_SPIPRIsp" ("_SPIPRIsp"--"_SPIPRIsp") objix.spix:"_SPIPRIsp" to pix "_SPIPRIpg"\n", + fd->obj_id, map_spix - map->start_spix, + map->start_spix, map->end_spix, + objix->p_hdr.span_ix, + objix_data_pix); + + map_spix++; + } +} + +typedef struct { + spiffs_fd *fd; + u32_t remaining_objix_pages_to_visit; + spiffs_span_ix map_objix_start_spix; + spiffs_span_ix map_objix_end_spix; +} spiffs_ix_map_populate_state; + +static s32_t spiffs_populate_ix_map_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_const_p; + s32_t res; + spiffs_ix_map_populate_state *state = (spiffs_ix_map_populate_state *)user_var_p; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + + // load header to check it + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix), (u8_t *)objix); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, obj_id, objix->p_hdr.span_ix); + + // check if hdr is ok, and if objix range overlap with ix map range + if ((objix->p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE) && + objix->p_hdr.span_ix >= state->map_objix_start_spix && + objix->p_hdr.span_ix <= state->map_objix_end_spix) { + // ok, load rest of object index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_object_ix), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix), + (u8_t *)objix + sizeof(spiffs_page_object_ix)); + SPIFFS_CHECK_RES(res); + + spiffs_update_ix_map(fs, state->fd, objix->p_hdr.span_ix, objix); + + state->remaining_objix_pages_to_visit--; + SPIFFS_DBG("map "_SPIPRIid" ("_SPIPRIsp"--"_SPIPRIsp") remaining objix pages "_SPIPRIi"\n", + state->fd->obj_id, + state->fd->ix_map->start_spix, state->fd->ix_map->end_spix, + state->remaining_objix_pages_to_visit); + } + + if (res == SPIFFS_OK) { + res = state->remaining_objix_pages_to_visit ? SPIFFS_VIS_COUNTINUE : SPIFFS_VIS_END; + } + return res; +} + +// populates index map, from vector entry start to vector entry end, inclusive +s32_t spiffs_populate_ix_map(spiffs *fs, spiffs_fd *fd, u32_t vec_entry_start, u32_t vec_entry_end) { + s32_t res; + spiffs_ix_map *map = fd->ix_map; + spiffs_ix_map_populate_state state; + vec_entry_start = MIN((u32_t)(map->end_spix - map->start_spix), vec_entry_start); + vec_entry_end = MAX((u32_t)(map->end_spix - map->start_spix), vec_entry_end); + if (vec_entry_start > vec_entry_end) { + return SPIFFS_ERR_IX_MAP_BAD_RANGE; + } + state.map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_start); + state.map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_end); + state.remaining_objix_pages_to_visit = + state.map_objix_end_spix - state.map_objix_start_spix + 1; + state.fd = fd; + + res = spiffs_obj_lu_find_entry_visitor( + fs, + SPIFFS_BLOCK_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_VIS_CHECK_ID, + fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + spiffs_populate_ix_map_v, + 0, + &state, + 0, + 0); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + return res; +} + +#endif + + +#if !SPIFFS_READ_ONLY // Allocates a free defined page with given obj_id // Occupies object lookup entry and page // data may be NULL; where only page header is stored, len and page_offs is ignored @@ -591,7 +804,9 @@ s32_t spiffs_page_allocate_data( return res; } +#endif // !SPIFFS_READ_ONLY +#if !SPIFFS_READ_ONLY // Moves a page from src to a free page and finalizes it. Updates page index. Page data is given in param page. // If page data is null, provided header is used for metainfo and page data is physically copied. s32_t spiffs_page_move( @@ -654,7 +869,9 @@ s32_t spiffs_page_move( res = spiffs_page_delete(fs, src_pix); return res; } +#endif // !SPIFFS_READ_ONLY +#if !SPIFFS_READ_ONLY // Deletes a page and removes it from object lookup. s32_t spiffs_page_delete( spiffs *fs, @@ -683,12 +900,15 @@ s32_t spiffs_page_delete( return res; } +#endif // !SPIFFS_READ_ONLY +#if !SPIFFS_READ_ONLY // Create an object index header page with empty index and undefined length s32_t spiffs_object_create( spiffs *fs, spiffs_obj_id obj_id, - u8_t name[SPIFFS_OBJ_NAME_LEN], + const u8_t name[], + const u8_t meta[], spiffs_obj_type type, spiffs_page_ix *objix_hdr_pix) { s32_t res = SPIFFS_OK; @@ -704,7 +924,7 @@ s32_t spiffs_object_create( // find free entry res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); SPIFFS_CHECK_RES(res); - SPIFFS_DBG("create: found free page @ %04x bix:%i entry:%i\n", SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); + SPIFFS_DBG("create: found free page @ "_SPIPRIpg" bix:"_SPIPRIbl" entry:"_SPIPRIsp"\n", (spiffs_page_ix)SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); // occupy page in object lookup res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, @@ -719,15 +939,24 @@ s32_t spiffs_object_create( oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED); oix_hdr.type = type; oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page - strncpy((char *)&oix_hdr.name, (char *)name, SPIFFS_OBJ_NAME_LEN); - + strncpy((char*)oix_hdr.name, (const char*)name, SPIFFS_OBJ_NAME_LEN); +#if SPIFFS_OBJ_META_LEN + if (meta) { + _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); + } else { + memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN); + } +#else + (void) meta; +#endif // update page res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&oix_hdr); SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&oix_hdr, + SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN); if (objix_hdr_pix) { *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); @@ -735,7 +964,9 @@ s32_t spiffs_object_create( return res; } +#endif // !SPIFFS_READ_ONLY +#if !SPIFFS_READ_ONLY // update object index header with any combination of name/size/index // new_objix_hdr_data may be null, if so the object index header page is loaded // name may be null, if so name is not changed @@ -746,7 +977,8 @@ s32_t spiffs_object_update_index_hdr( spiffs_obj_id obj_id, spiffs_page_ix objix_hdr_pix, u8_t *new_objix_hdr_data, - u8_t name[SPIFFS_OBJ_NAME_LEN], + const u8_t name[], + const u8_t meta[], u32_t size, spiffs_page_ix *new_pix) { s32_t res = SPIFFS_OK; @@ -770,8 +1002,15 @@ s32_t spiffs_object_update_index_hdr( // change name if (name) { - strncpy((char *)objix_hdr->name, (char *)name, SPIFFS_OBJ_NAME_LEN); + strncpy((char*)objix_hdr->name, (const char*)name, SPIFFS_OBJ_NAME_LEN); } +#if SPIFFS_OBJ_META_LEN + if (meta) { + _SPIFFS_MEMCPY(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN); + } +#else + (void) meta; +#endif if (size) { objix_hdr->size = size; } @@ -784,49 +1023,125 @@ s32_t spiffs_object_update_index_hdr( *new_pix = new_objix_hdr_pix; } // callback on object index update - spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + new_objix_hdr_data ? SPIFFS_EV_IX_UPD : SPIFFS_EV_IX_UPD_HDR, + obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size); if (fd) fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster } return res; } +#endif // !SPIFFS_READ_ONLY void spiffs_cb_object_event( spiffs *fs, - spiffs_fd *fd, + spiffs_page_object_ix *objix, int ev, - spiffs_obj_id obj_id, + spiffs_obj_id obj_id_raw, spiffs_span_ix spix, spiffs_page_ix new_pix, u32_t new_size) { - (void)fd; +#if SPIFFS_IX_MAP == 0 + (void)objix; +#endif // update index caches in all file descriptors - obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG; u32_t i; spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + SPIFFS_DBG(" CALLBACK %s obj_id:"_SPIPRIid" spix:"_SPIPRIsp" npix:"_SPIPRIpg" nsz:"_SPIPRIi"\n", (const char *[]){"UPD", "NEW", "DEL", "MOV", "HUP","???"}[MIN(ev,5)], + obj_id_raw, spix, new_pix, new_size); for (i = 0; i < fs->fd_count; i++) { spiffs_fd *cur_fd = &fds[i]; - if (cur_fd->file_nbr == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; - if (spix == 0) { - if (ev == SPIFFS_EV_IX_NEW || ev == SPIFFS_EV_IX_UPD) { - SPIFFS_DBG(" callback: setting fd %i:%04x objix_hdr_pix to %04x, size:%i\n", cur_fd->file_nbr, cur_fd->obj_id, new_pix, new_size); + if ((cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; // fd not related to updated file +#if !SPIFFS_TEMPORAL_FD_CACHE + if (cur_fd->file_nbr == 0) continue; // fd closed +#endif + if (spix == 0) { // object index header update + if (ev != SPIFFS_EV_IX_DEL) { +#if SPIFFS_TEMPORAL_FD_CACHE + if (cur_fd->score == 0) continue; // never used fd +#endif + SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid"(fdoffs:"_SPIPRIi" offs:"_SPIPRIi") objix_hdr_pix to "_SPIPRIpg", size:"_SPIPRIi"\n", + SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, cur_fd->fdoffset, cur_fd->offset, new_pix, new_size); cur_fd->objix_hdr_pix = new_pix; if (new_size != 0) { + // update size and offsets for fds to this file cur_fd->size = new_size; + u32_t act_new_size = new_size == SPIFFS_UNDEFINED_LEN ? 0 : new_size; +#if SPIFFS_CACHE_WR + if (act_new_size > 0 && cur_fd->cache_page) { + act_new_size = MAX(act_new_size, cur_fd->cache_page->offset + cur_fd->cache_page->size); + } +#endif + if (cur_fd->offset > act_new_size) { + cur_fd->offset = act_new_size; + } + if (cur_fd->fdoffset > act_new_size) { + cur_fd->fdoffset = act_new_size; + } +#if SPIFFS_CACHE_WR + if (cur_fd->cache_page && cur_fd->cache_page->offset > act_new_size+1) { + SPIFFS_CACHE_DBG("CACHE_DROP: file trunced, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix); + spiffs_cache_fd_release(fs, cur_fd->cache_page); + } +#endif } - } else if (ev == SPIFFS_EV_IX_DEL) { + } else { + // removing file +#if SPIFFS_CACHE_WR + if (cur_fd->file_nbr && cur_fd->cache_page) { + SPIFFS_CACHE_DBG("CACHE_DROP: file deleted, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix); + spiffs_cache_fd_release(fs, cur_fd->cache_page); + } +#endif + SPIFFS_DBG(" callback: release fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); cur_fd->file_nbr = 0; cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED; } - } + } // object index header update if (cur_fd->cursor_objix_spix == spix) { - if (ev == SPIFFS_EV_IX_NEW || ev == SPIFFS_EV_IX_UPD) { - SPIFFS_DBG(" callback: setting fd %i:%04x span:%04x objix_pix to %04x\n", cur_fd->file_nbr, cur_fd->obj_id, spix, new_pix); + if (ev != SPIFFS_EV_IX_DEL) { + SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); cur_fd->cursor_objix_pix = new_pix; } else { cur_fd->cursor_objix_pix = 0; } } + } // fd update loop + +#if SPIFFS_IX_MAP + + // update index maps + if (ev == SPIFFS_EV_IX_UPD || ev == SPIFFS_EV_IX_NEW) { + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + // check fd opened, having ix map, match obj id + if (cur_fd->file_nbr == 0 || + cur_fd->ix_map == 0 || + (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; + SPIFFS_DBG(" callback: map ix update fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix); + spiffs_update_ix_map(fs, cur_fd, spix, objix); + } + } + +#endif + + // callback to user if object index header + if (fs->file_cb_f && spix == 0 && (obj_id_raw & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_fileop_type op; + if (ev == SPIFFS_EV_IX_NEW) { + op = SPIFFS_CB_CREATED; + } else if (ev == SPIFFS_EV_IX_UPD || + ev == SPIFFS_EV_IX_MOV || + ev == SPIFFS_EV_IX_UPD_HDR) { + op = SPIFFS_CB_UPDATED; + } else if (ev == SPIFFS_EV_IX_DEL) { + op = SPIFFS_CB_DELETED; + } else { + SPIFFS_DBG(" callback: WARNING unknown callback event "_SPIPRIi"\n", ev); + return; // bail out + } + fs->file_cb_f(fs, op, obj_id, new_pix); } } @@ -881,11 +1196,12 @@ s32_t spiffs_object_open_by_page( SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); - SPIFFS_DBG("open: fd %i is obj id %04x\n", fd->file_nbr, fd->obj_id); + SPIFFS_DBG("open: fd "_SPIPRIfd" is obj id "_SPIPRIid"\n", SPIFFS_FH_OFFS(fs, fd->file_nbr), fd->obj_id); return res; } +#if !SPIFFS_READ_ONLY // Append to object // keep current object index (header) page in fs->work buffer s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { @@ -893,7 +1209,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { s32_t res = SPIFFS_OK; u32_t written = 0; - SPIFFS_DBG("append: %i bytes @ offs %i of size %i\n", len, offset, fd->size); + SPIFFS_DBG("append: "_SPIPRIi" bytes @ offs "_SPIPRIi" of size "_SPIPRIi"\n", len, offset, fd->size); if (offset > fd->size) { SPIFFS_DBG("append: offset reversed to size\n"); @@ -902,7 +1218,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta if (res != SPIFFS_OK) { - SPIFFS_DBG("append: gc check fail %i\n", res); + SPIFFS_DBG("append: gc check fail "_SPIPRIi"\n", res); } SPIFFS_CHECK_RES(res); @@ -930,7 +1246,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // within this clause we return directly if something fails, object index mess-up if (written > 0) { // store previous object index page, unless first pass - SPIFFS_DBG("append: %04x store objix %04x:%04x, written %i\n", fd->obj_id, + SPIFFS_DBG("append: "_SPIPRIid" store objix "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, cur_objix_pix, prev_objix_spix, written); if (prev_objix_spix == 0) { // this is an update to object index header page @@ -945,9 +1261,9 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { } else { // was a nonempty object, update to new page res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, fs->work, 0, offset+written, &new_objix_hdr_page); + fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page); SPIFFS_CHECK_RES(res); - SPIFFS_DBG("append: %04x store new objix_hdr, %04x:%04x, written %i\n", fd->obj_id, + SPIFFS_DBG("append: "_SPIPRIid" store new objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, new_objix_hdr_page, 0, written); } } else { @@ -958,12 +1274,13 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD,fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD,fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); // update length in object index header page res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page); + fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page); SPIFFS_CHECK_RES(res); - SPIFFS_DBG("append: %04x store new size I %i in objix_hdr, %04x:%04x, written %i\n", fd->obj_id, + SPIFFS_DBG("append: "_SPIPRIid" store new size I "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, offset+written, new_objix_hdr_page, 0, written); } fd->size = offset+written; @@ -973,7 +1290,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // create or load new object index page if (cur_objix_spix == 0) { // load object index header page, must always exist - SPIFFS_DBG("append: %04x load objixhdr page %04x:%04x\n", fd->obj_id, cur_objix_pix, cur_objix_spix); + SPIFFS_DBG("append: "_SPIPRIid" load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", fd->obj_id, cur_objix_pix, cur_objix_spix); res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); SPIFFS_CHECK_RES(res); @@ -988,23 +1305,24 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { res = spiffs_page_allocate_data(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, &p_hdr, 0, 0, 0, 1, &cur_objix_pix); SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); // quick "load" of new object index page memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - memcpy(fs->work, &p_hdr, sizeof(spiffs_page_header)); - SPIFFS_DBG("append: %04x create objix page, %04x:%04x, written %i\n", fd->obj_id + _SPIFFS_MEMCPY(fs->work, &p_hdr, sizeof(spiffs_page_header)); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); + SPIFFS_DBG("append: "_SPIPRIid" create objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id , cur_objix_pix, cur_objix_spix, written); } else { // on first pass, we load existing object index page spiffs_page_ix pix; - SPIFFS_DBG("append: %04x find objix span_ix:%04x\n", fd->obj_id, cur_objix_spix); + SPIFFS_DBG("append: "_SPIPRIid" find objix span_ix:"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); if (fd->cursor_objix_spix == cur_objix_spix) { pix = fd->cursor_objix_pix; } else { res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); SPIFFS_CHECK_RES(res); } - SPIFFS_DBG("append: %04x found object index at page %04x [fd size %i]\n", fd->obj_id, pix, fd->size); + SPIFFS_DBG("append: "_SPIPRIid" found object index at page "_SPIPRIpg" [fd size "_SPIPRIi"]\n", fd->obj_id, pix, fd->size); res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); SPIFFS_CHECK_RES(res); @@ -1028,7 +1346,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, &p_hdr, &data[written], to_write, page_offs, 1, &data_page); - SPIFFS_DBG("append: %04x store new data page, %04x:%04x offset:%i, len %i, written %i\n", fd->obj_id, + SPIFFS_DBG("append: "_SPIPRIid" store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id, data_page, data_spix, page_offs, to_write, written); } else { // append to existing page, fill out free data in existing page @@ -1045,7 +1363,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); - SPIFFS_DBG("append: %04x store to existing data page, %04x:%04x offset:%i, len %i, written %i\n", fd->obj_id + SPIFFS_DBG("append: "_SPIPRIid" store to existing data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id , data_page, data_spix, page_offs, to_write, written); } @@ -1055,14 +1373,14 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { if (cur_objix_spix == 0) { // update object index header page ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page; - SPIFFS_DBG("append: %04x wrote page %04x to objix_hdr entry %02x in mem\n", fd->obj_id + SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", fd->obj_id , data_page, data_spix); objix_hdr->size = offset+written; } else { // update object index page ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page; - SPIFFS_DBG("append: %04x wrote page %04x to objix entry %02x in mem\n", fd->obj_id - , data_page, SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", fd->obj_id + , data_page, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); } // update internals @@ -1081,7 +1399,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { if (cur_objix_spix != 0) { // wrote beyond object index header page // write last modified object index page, unless object header index page - SPIFFS_DBG("append: %04x store objix page, %04x:%04x, written %i\n", fd->obj_id, + SPIFFS_DBG("append: "_SPIPRIid" store objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, cur_objix_pix, cur_objix_spix, written); res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); @@ -1090,12 +1408,13 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); SPIFFS_CHECK_RES(res2); - spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); // update size in object header index page res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page); - SPIFFS_DBG("append: %04x store new size II %i in objix_hdr, %04x:%04x, written %i, res %i\n", fd->obj_id + fd->objix_hdr_pix, 0, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_DBG("append: "_SPIPRIid" store new size II "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi", res "_SPIPRIi"\n", fd->obj_id , offset+written, new_objix_hdr_page, 0, written, res2); SPIFFS_CHECK_RES(res2); } else { @@ -1103,7 +1422,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { if (offset == 0) { // wrote to empty object - simply update size and write whole page objix_hdr->size = offset+written; - SPIFFS_DBG("append: %04x store fresh objix_hdr page, %04x:%04x, written %i\n", fd->obj_id + SPIFFS_DBG("append: "_SPIPRIid" store fresh objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id , cur_objix_pix, cur_objix_spix, written); res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); @@ -1113,20 +1432,23 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); SPIFFS_CHECK_RES(res2); // callback on object index update - spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD_HDR, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size); } else { // modifying object index header page, update size and make new copy res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, fs->work, 0, offset+written, &new_objix_hdr_page); - SPIFFS_DBG("append: %04x store modified objix_hdr page, %04x:%04x, written %i\n", fd->obj_id + fd->objix_hdr_pix, fs->work, 0, 0, offset+written, &new_objix_hdr_page); + SPIFFS_DBG("append: "_SPIPRIid" store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id , new_objix_hdr_page, 0, written); SPIFFS_CHECK_RES(res2); } } return res; -} +} // spiffs_object_append +#endif // !SPIFFS_READ_ONLY +#if !SPIFFS_READ_ONLY // Modify object // keep current object index (header) page in fs->work buffer s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { @@ -1165,8 +1487,8 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { if (prev_objix_spix == 0) { // store previous object index header page res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, fs->work, 0, 0, &new_objix_hdr_pix); - SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %i\n", new_objix_hdr_pix, 0, written); + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); SPIFFS_CHECK_RES(res); } else { // store new version of previous object index page @@ -1176,16 +1498,17 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { SPIFFS_CHECK_RES(res); res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); - SPIFFS_DBG("modify: store previous modified objix page, %04x:%04x, written %i\n", new_objix_pix, objix->p_hdr.span_ix, written); + SPIFFS_DBG("modify: store previous modified objix page, "_SPIPRIid":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, objix->p_hdr.span_ix, written); SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); } } // load next object index page if (cur_objix_spix == 0) { // load object index header page, must exist - SPIFFS_DBG("modify: load objixhdr page %04x:%04x\n", cur_objix_pix, cur_objix_spix); + SPIFFS_DBG("modify: load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", cur_objix_pix, cur_objix_spix); res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); SPIFFS_CHECK_RES(res); @@ -1193,14 +1516,14 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { } else { // load existing object index page on first pass spiffs_page_ix pix; - SPIFFS_DBG("modify: find objix span_ix:%04x\n", cur_objix_spix); + SPIFFS_DBG("modify: find objix span_ix:"_SPIPRIsp"\n", cur_objix_spix); if (fd->cursor_objix_spix == cur_objix_spix) { pix = fd->cursor_objix_pix; } else { res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); SPIFFS_CHECK_RES(res); } - SPIFFS_DBG("modify: found object index at page %04x\n", pix); + SPIFFS_DBG("modify: found object index at page "_SPIPRIpg"\n", pix); res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); SPIFFS_CHECK_RES(res); @@ -1231,7 +1554,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // a full page, allocate and write a new page of data res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, &p_hdr, &data[written], to_write, page_offs, 1, &data_pix); - SPIFFS_DBG("modify: store new data page, %04x:%04x offset:%i, len %i, written %i\n", data_pix, data_spix, page_offs, to_write, written); + SPIFFS_DBG("modify: store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", data_pix, data_spix, page_offs, to_write, written); } else { // write to existing page, allocate new and copy unmodified data @@ -1272,7 +1595,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { (u8_t *)&p_hdr.flags); if (res != SPIFFS_OK) break; - SPIFFS_DBG("modify: store to existing data page, src:%04x, dst:%04x:%04x offset:%i, len %i, written %i\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); + SPIFFS_DBG("modify: store to existing data page, src:"_SPIPRIpg", dst:"_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); } // delete original data page @@ -1282,11 +1605,11 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { if (cur_objix_spix == 0) { // update object index header page ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix; - SPIFFS_DBG("modify: wrote page %04x to objix_hdr entry %02x in mem\n", data_pix, data_spix); + SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", data_pix, data_spix); } else { // update object index page ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix; - SPIFFS_DBG("modify: wrote page %04x to objix entry %02x in mem\n", data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); } // update internals @@ -1311,31 +1634,33 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { SPIFFS_CHECK_RES(res2); res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); - SPIFFS_DBG("modify: store modified objix page, %04x:%04x, written %i\n", new_objix_pix, cur_objix_spix, written); + SPIFFS_DBG("modify: store modified objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, cur_objix_spix, written); fd->cursor_objix_pix = new_objix_pix; fd->cursor_objix_spix = cur_objix_spix; SPIFFS_CHECK_RES(res2); - spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); } else { // wrote within object index header page res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, fs->work, 0, 0, &new_objix_hdr_pix); - SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %i\n", new_objix_hdr_pix, 0, written); + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); SPIFFS_CHECK_RES(res2); } return res; -} +} // spiffs_object_modify +#endif // !SPIFFS_READ_ONLY static s32_t spiffs_object_find_object_index_header_by_name_v( spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix bix, int ix_entry, - u32_t user_data, - void *user_p) { - (void)user_data; + const void *user_const_p, + void *user_var_p) { + (void)user_var_p; s32_t res; spiffs_page_object_ix_header objix_hdr; spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); @@ -1349,7 +1674,7 @@ static s32_t spiffs_object_find_object_index_header_by_name_v( if (objix_hdr.p_hdr.span_ix == 0 && (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { - if (strcmp((char *)user_p, (char *)objix_hdr.name) == 0) { + if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) { return SPIFFS_OK; } } @@ -1360,7 +1685,7 @@ static s32_t spiffs_object_find_object_index_header_by_name_v( // Finds object index header page by name s32_t spiffs_object_find_object_index_header_by_name( spiffs *fs, - u8_t name[SPIFFS_OBJ_NAME_LEN], + const u8_t name[SPIFFS_OBJ_NAME_LEN], spiffs_page_ix *pix) { s32_t res; spiffs_block_ix bix; @@ -1372,8 +1697,8 @@ s32_t spiffs_object_find_object_index_header_by_name( 0, 0, spiffs_object_find_object_index_header_by_name_v, - 0, name, + 0, &bix, &entry); @@ -1392,16 +1717,25 @@ s32_t spiffs_object_find_object_index_header_by_name( return res; } +#if !SPIFFS_READ_ONLY // Truncates object to new size. If new size is null, object may be removed totally s32_t spiffs_object_truncate( spiffs_fd *fd, u32_t new_size, - u8_t remove) { + u8_t remove_full) { s32_t res = SPIFFS_OK; spiffs *fs = fd->fs; - res = spiffs_gc_check(fs, remove ? 0 : SPIFFS_DATA_PAGE_SIZE(fs)); - SPIFFS_CHECK_RES(res); + if ((fd->size == SPIFFS_UNDEFINED_LEN || fd->size == 0) && !remove_full) { + // no op + return res; + } + + // need 2 pages if not removing: object index page + possibly chopped data page + if (remove_full == 0) { + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs) * 2); + SPIFFS_CHECK_RES(res); + } spiffs_page_ix objix_pix = fd->objix_hdr_pix; spiffs_span_ix data_spix = (fd->size > 0 ? fd->size-1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs); @@ -1414,7 +1748,7 @@ s32_t spiffs_object_truncate( spiffs_page_ix new_objix_hdr_pix; // before truncating, check if object is to be fully removed and mark this - if (remove && new_size == 0) { + if (remove_full && new_size == 0) { u8_t flags = ~( SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE); res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix) + offsetof(spiffs_page_header, flags), @@ -1431,20 +1765,28 @@ s32_t spiffs_object_truncate( if (prev_objix_spix != cur_objix_spix) { if (prev_objix_spix != (spiffs_span_ix)-1) { // remove previous object index page - SPIFFS_DBG("truncate: delete objix page %04x:%04x\n", objix_pix, prev_objix_spix); + SPIFFS_DBG("truncate: delete objix page "_SPIPRIpg":"_SPIPRIsp"\n", objix_pix, prev_objix_spix); res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix); SPIFFS_CHECK_RES(res); res = spiffs_page_delete(fs, objix_pix); SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0); if (prev_objix_spix > 0) { - // update object index header page - SPIFFS_DBG("truncate: update objix hdr page %04x:%04x to size %i\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, 0, 0, cur_size, &new_objix_hdr_pix); - SPIFFS_CHECK_RES(res); + // Update object index header page, unless we totally want to remove the file. + // If fully removing, we're not keeping consistency as good as when storing the header between chunks, + // would we be aborted. But when removing full files, a crammed system may otherwise + // report ERR_FULL a la windows. We cannot have that. + // Hence, take the risk - if aborted, a file check would free the lost pages and mend things + // as the file is marked as fully deleted in the beginning. + if (remove_full == 0) { + SPIFFS_DBG("truncate: update objix hdr page "_SPIPRIpg":"_SPIPRIsp" to size "_SPIPRIi"\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } fd->size = cur_size; } } @@ -1456,7 +1798,7 @@ s32_t spiffs_object_truncate( SPIFFS_CHECK_RES(res); } - SPIFFS_DBG("truncate: load objix page %04x:%04x for data spix:%04x\n", objix_pix, cur_objix_spix, data_spix); + SPIFFS_DBG("truncate: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); SPIFFS_CHECK_RES(res); @@ -1478,20 +1820,20 @@ s32_t spiffs_object_truncate( ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; } - SPIFFS_DBG("truncate: got data pix %04x\n", data_pix); + SPIFFS_DBG("truncate: got data pix "_SPIPRIpg"\n", data_pix); - if (cur_size - SPIFFS_DATA_PAGE_SIZE(fs) >= new_size) { + if (new_size == 0 || remove_full || cur_size - new_size >= SPIFFS_DATA_PAGE_SIZE(fs)) { // delete full data page res = spiffs_page_data_check(fs, fd, data_pix, data_spix); if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) { - SPIFFS_DBG("truncate: err validating data pix %i\n", res); + SPIFFS_DBG("truncate: err validating data pix "_SPIPRIi"\n", res); break; } if (res == SPIFFS_OK) { res = spiffs_page_delete(fs, data_pix); if (res != SPIFFS_OK) { - SPIFFS_DBG("truncate: err deleting data pix %i\n", res); + SPIFFS_DBG("truncate: err deleting data pix "_SPIPRIi"\n", res); break; } } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) { @@ -1506,13 +1848,13 @@ s32_t spiffs_object_truncate( } fd->size = cur_size; fd->offset = cur_size; - SPIFFS_DBG("truncate: delete data page %04x for data spix:%04x, cur_size:%i\n", data_pix, data_spix, cur_size); + SPIFFS_DBG("truncate: delete data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", data_pix, data_spix, cur_size); } else { // delete last page, partially spiffs_page_header p_hdr; spiffs_page_ix new_data_pix; u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs)); - SPIFFS_DBG("truncate: delete %i bytes from data page %04x for data spix:%04x, cur_size:%i\n", bytes_to_remove, data_pix, data_spix, cur_size); + SPIFFS_DBG("truncate: delete "_SPIPRIi" bytes from data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", bytes_to_remove, data_pix, data_spix, cur_size); res = spiffs_page_data_check(fs, fd, data_pix, data_spix); if (res != SPIFFS_OK) break; @@ -1544,11 +1886,11 @@ s32_t spiffs_object_truncate( if (cur_objix_spix == 0) { // update object index header page ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; - SPIFFS_DBG("truncate: wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); } else { // update object index page ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; - SPIFFS_DBG("truncate: wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); } cur_size = new_size; fd->size = new_size; @@ -1562,30 +1904,31 @@ s32_t spiffs_object_truncate( if (cur_objix_spix == 0) { // update object index header page if (cur_size == 0) { - if (remove) { + if (remove_full) { // remove object altogether - SPIFFS_DBG("truncate: remove object index header page %04x\n", objix_pix); + SPIFFS_DBG("truncate: remove object index header page "_SPIPRIpg"\n", objix_pix); res = spiffs_page_index_check(fs, fd, objix_pix, 0); SPIFFS_CHECK_RES(res); res = spiffs_page_delete(fs, objix_pix); SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0); } else { // make uninitialized object - SPIFFS_DBG("truncate: reset objix_hdr page %04x\n", objix_pix); + SPIFFS_DBG("truncate: reset objix_hdr page "_SPIPRIpg"\n", objix_pix); memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - objix_pix, fs->work, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); + objix_pix, fs->work, 0, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); SPIFFS_CHECK_RES(res); } } else { // update object index header page SPIFFS_DBG("truncate: update object index header page with indices and size\n"); res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - objix_pix, fs->work, 0, cur_size, &new_objix_hdr_pix); + objix_pix, fs->work, 0, 0, cur_size, &new_objix_hdr_pix); SPIFFS_CHECK_RES(res); } } else { @@ -1598,20 +1941,22 @@ s32_t spiffs_object_truncate( // move and update object index page res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix_hdr, fd->obj_id, 0, objix_pix, &new_objix_pix); SPIFFS_CHECK_RES(res); - spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); - SPIFFS_DBG("truncate: store modified objix page, %04x:%04x\n", new_objix_pix, cur_objix_spix); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + SPIFFS_DBG("truncate: store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, cur_objix_spix); fd->cursor_objix_pix = new_objix_pix; fd->cursor_objix_spix = cur_objix_spix; fd->offset = cur_size; // update object index header page with new size res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, - fd->objix_hdr_pix, 0, 0, cur_size, &new_objix_hdr_pix); + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); SPIFFS_CHECK_RES(res); } fd->size = cur_size; return res; -} +} // spiffs_object_truncate +#endif // !SPIFFS_READ_ONLY s32_t spiffs_object_read( spiffs_fd *fd, @@ -1630,45 +1975,59 @@ s32_t spiffs_object_read( spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; while (cur_offset < offset + len) { - cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); - if (prev_objix_spix != cur_objix_spix) { - // load current object index (header) page - if (cur_objix_spix == 0) { - objix_pix = fd->objix_hdr_pix; - } else { - SPIFFS_DBG("read: find objix %04x:%04x\n", fd->obj_id, cur_objix_spix); - res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); - SPIFFS_CHECK_RES(res); - } - SPIFFS_DBG("read: load objix page %04x:%04x for data spix:%04x\n", objix_pix, cur_objix_spix, data_spix); - res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); - SPIFFS_CHECK_RES(res); - SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix); - - fd->offset = cur_offset; - fd->cursor_objix_pix = objix_pix; - fd->cursor_objix_spix = cur_objix_spix; - - prev_objix_spix = cur_objix_spix; - } - - if (cur_objix_spix == 0) { - // get data page from object index header page - data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; +#if SPIFFS_IX_MAP + // check if we have a memory, index map and if so, if we're within index map's range + // and if so, if the entry is populated + if (fd->ix_map && data_spix >= fd->ix_map->start_spix && data_spix <= fd->ix_map->end_spix + && fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]) { + data_pix = fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]; } else { - // get data page from object index page - data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; - } +#endif + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (prev_objix_spix != cur_objix_spix) { + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + SPIFFS_DBG("read: find objix "_SPIPRIid":"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + objix_pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + } + SPIFFS_DBG("read: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix); + fd->offset = cur_offset; + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } +#if SPIFFS_IX_MAP + } +#endif // all remaining data u32_t len_to_read = offset + len - cur_offset; // remaining data in page len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); // remaining data in file len_to_read = MIN(len_to_read, fd->size); - SPIFFS_DBG("read: offset:%i rd:%i data spix:%04x is data_pix:%04x addr:%08x\n", cur_offset, len_to_read, data_spix, data_pix, - SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); + SPIFFS_DBG("read: offset:"_SPIPRIi" rd:"_SPIPRIi" data spix:"_SPIPRIsp" is data_pix:"_SPIPRIpg" addr:"_SPIPRIad"\n", cur_offset, len_to_read, data_spix, data_pix, + (u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)))); if (len_to_read <= 0) { res = SPIFFS_ERR_END_OF_OBJECT; break; @@ -1691,6 +2050,7 @@ s32_t spiffs_object_read( return res; } +#if !SPIFFS_READ_ONLY typedef struct { spiffs_obj_id min_obj_id; spiffs_obj_id max_obj_id; @@ -1699,10 +2059,10 @@ typedef struct { } spiffs_free_obj_id_state; static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, - u32_t user_data, void *user_p) { + const void *user_const_p, void *user_var_p) { if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED) { - spiffs_obj_id min_obj_id = user_data; - u8_t *conflicting_name = (u8_t *)user_p; + spiffs_obj_id min_obj_id = *((spiffs_obj_id*)user_var_p); + const u8_t *conflicting_name = (const u8_t*)user_const_p; // if conflicting name parameter is given, also check if this name is found in object index hdrs if (conflicting_name && (id & SPIFFS_OBJ_ID_IX_FLAG)) { @@ -1715,7 +2075,7 @@ static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id i if (objix_hdr.p_hdr.span_ix == 0 && (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { - if (strcmp((char *)user_p, (char *)objix_hdr.name) == 0) { + if (strcmp((const char*)user_const_p, (char*)objix_hdr.name) == 0) { return SPIFFS_ERR_CONFLICTING_NAME; } } @@ -1732,11 +2092,11 @@ static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id i } static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, - u32_t user_data, void *user_p) { - (void)user_data; + const void *user_const_p, void *user_var_p) { + (void)user_var_p; if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED && (id & SPIFFS_OBJ_ID_IX_FLAG)) { s32_t res; - spiffs_free_obj_id_state *state = (spiffs_free_obj_id_state *)user_p; + const spiffs_free_obj_id_state *state = (const spiffs_free_obj_id_state*)user_const_p; spiffs_page_object_ix_header objix_hdr; res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, @@ -1753,7 +2113,7 @@ static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id if (id >= state->min_obj_id && id <= state->max_obj_id) { u8_t *map = (u8_t *)fs->work; int ix = (id - state->min_obj_id) / state->compaction; - //SPIFFS_DBG("free_obj_id: add ix %i for id %04x min:%04x max%04x comp:%i\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); + //SPIFFS_DBG("free_obj_id: add ix "_SPIPRIi" for id "_SPIPRIid" min"_SPIPRIid" max"_SPIPRIid" comp:"_SPIPRIi"\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); map[ix]++; } } @@ -1764,10 +2124,10 @@ static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id // Scans thru all object lookup for object index header pages. If total possible number of // object ids cannot fit into a work buffer, these are grouped. When a group containing free // object ids is found, the object lu is again scanned for object ids within group and bitmasked. -// Finally, the bitmasked is searched for a free id -s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *conflicting_name) { +// Finally, the bitmask is searched for a free id +s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, const u8_t *conflicting_name) { s32_t res = SPIFFS_OK; - u32_t max_objects = (SPIFFS_CFG_PHYS_SZ(fs) / (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) / 2; + u32_t max_objects = (fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) / 2; spiffs_free_obj_id_state state; spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE; state.min_obj_id = 1; @@ -1781,11 +2141,11 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co if (state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8) { // possible to represent in bitmap u32_t i, j; - SPIFFS_DBG("free_obj_id: BITM min:%04x max:%04x\n", state.min_obj_id, state.max_obj_id); + SPIFFS_DBG("free_obj_id: BITM min:"_SPIPRIid" max:"_SPIPRIid"\n", state.min_obj_id, state.max_obj_id); memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, state.min_obj_id, - conflicting_name, 0, 0); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, + conflicting_name, &state.min_obj_id, 0, 0); if (res == SPIFFS_VIS_END) res = SPIFFS_OK; SPIFFS_CHECK_RES(res); // traverse bitmask until found free obj_id @@ -1826,14 +2186,14 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co return SPIFFS_ERR_FULL; } - SPIFFS_DBG("free_obj_id: COMP select index:%i min_count:%i min:%04x max:%04x compact:%i\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); + SPIFFS_DBG("free_obj_id: COMP select index:"_SPIPRIi" min_count:"_SPIPRIi" min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); if (min_count == 0) { // no id in this range, skip compacting and use directly *obj_id = min_i * state.compaction + state.min_obj_id; return SPIFFS_OK; } else { - SPIFFS_DBG("free_obj_id: COMP SEL chunk:%04x min:%04x -> %04x\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction); + SPIFFS_DBG("free_obj_id: COMP SEL chunk:"_SPIPRIi" min:"_SPIPRIid" -> "_SPIPRIid"\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction); state.min_obj_id += min_i * state.compaction; state.max_obj_id = state.min_obj_id + state.compaction; // decrease compaction @@ -1846,10 +2206,10 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co // in a work memory of log_page_size bytes, we may fit in log_page_size ids // todo what if compaction is > 255 - then we cannot fit it in a byte state.compaction = (state.max_obj_id-state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t))); - SPIFFS_DBG("free_obj_id: COMP min:%04x max:%04x compact:%i\n", state.min_obj_id, state.max_obj_id, state.compaction); + SPIFFS_DBG("free_obj_id: COMP min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", state.min_obj_id, state.max_obj_id, state.compaction); memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); - res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, 0, &state, 0, 0); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, &state, 0, 0, 0); if (res == SPIFFS_VIS_END) res = SPIFFS_OK; SPIFFS_CHECK_RES(res); state.conflicting_name = 0; // searched for conflicting name once, no need to do it again @@ -1858,8 +2218,86 @@ s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *co return res; } +#endif // !SPIFFS_READ_ONLY -s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd) { +#if SPIFFS_TEMPORAL_FD_CACHE +// djb2 hash +static u32_t spiffs_hash(spiffs *fs, const u8_t *name) { + (void)fs; + u32_t hash = 5381; + u8_t c; + int i = 0; + while ((c = name[i++]) && i < SPIFFS_OBJ_NAME_LEN) { + hash = (hash * 33) ^ c; + } + return hash; +} +#endif + +s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd, const char *name) { +#if SPIFFS_TEMPORAL_FD_CACHE + u32_t i; + u16_t min_score = 0xffff; + u32_t cand_ix = (u32_t)-1; + u32_t name_hash = name ? spiffs_hash(fs, (const u8_t *)name) : 0; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + + if (name) { + // first, decrease score of all closed descriptors + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + if (cur_fd->score > 1) { // score == 0 indicates never used fd + cur_fd->score--; + } + } + } + } + + // find the free fd with least score or name match + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + if (name && cur_fd->name_hash == name_hash) { + cand_ix = i; + break; + } + if (cur_fd->score < min_score) { + min_score = cur_fd->score; + cand_ix = i; + } + } + } + + if (cand_ix != (u32_t)-1) { + spiffs_fd *cur_fd = &fds[cand_ix]; + if (name) { + if (cur_fd->name_hash == name_hash && cur_fd->score > 0) { + // opened an fd with same name hash, assume same file + // set search point to saved obj index page and hope we have a correct match directly + // when start searching - if not, we will just keep searching until it is found + fs->cursor_block_ix = SPIFFS_BLOCK_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + fs->cursor_obj_lu_entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + // update score + if (cur_fd->score < 0xffff-SPIFFS_TEMPORAL_CACHE_HIT_SCORE) { + cur_fd->score += SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + } else { + cur_fd->score = 0xffff; + } + } else { + // no hash hit, restore this fd to initial state + cur_fd->score = SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + cur_fd->name_hash = name_hash; + } + } + cur_fd->file_nbr = cand_ix+1; + *fd = cur_fd; + return SPIFFS_OK; + } else { + return SPIFFS_ERR_OUT_OF_FILE_DESCS; + } +#else + (void)name; u32_t i; spiffs_fd *fds = (spiffs_fd *)fs->fd_space; for (i = 0; i < fs->fd_count; i++) { @@ -1871,6 +2309,7 @@ s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd) { } } return SPIFFS_ERR_OUT_OF_FILE_DESCS; +#endif } s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) { @@ -1883,6 +2322,9 @@ s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) { return SPIFFS_ERR_FILE_CLOSED; } fd->file_nbr = 0; +#if SPIFFS_IX_MAP + fd->ix_map = 0; +#endif return SPIFFS_OK; } @@ -1897,3 +2339,21 @@ s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) { } return SPIFFS_OK; } + +#if SPIFFS_TEMPORAL_FD_CACHE +void spiffs_fd_temporal_cache_rehash( + spiffs *fs, + const char *old_path, + const char *new_path) { + u32_t i; + u32_t old_hash = spiffs_hash(fs, (const u8_t *)old_path); + u32_t new_hash = spiffs_hash(fs, (const u8_t *)new_path); + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->score > 0 && cur_fd->name_hash == old_hash) { + cur_fd->name_hash = new_hash; + } + } +} +#endif diff --git a/components/spiffs/include/spiffs/spiffs_nucleus.h b/components/spiffs/spiffs/src/spiffs_nucleus.h similarity index 81% rename from components/spiffs/include/spiffs/spiffs_nucleus.h rename to components/spiffs/spiffs/src/spiffs_nucleus.h index 80cc1cff..dd1c414b 100644 --- a/components/spiffs/include/spiffs/spiffs_nucleus.h +++ b/components/spiffs/spiffs/src/spiffs_nucleus.h @@ -116,13 +116,23 @@ #define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3) #define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4) +// visitor result, continue searching #define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20) +// visitor result, continue searching after reloading lu buffer #define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21) +// visitor result, stop searching #define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22) -#define SPIFFS_EV_IX_UPD 0 -#define SPIFFS_EV_IX_NEW 1 -#define SPIFFS_EV_IX_DEL 2 +// updating an object index contents +#define SPIFFS_EV_IX_UPD (0) +// creating a new object index +#define SPIFFS_EV_IX_NEW (1) +// deleting an object index +#define SPIFFS_EV_IX_DEL (2) +// moving an object index without updating contents +#define SPIFFS_EV_IX_MOV (3) +// updating an object index header data only, not the table itself +#define SPIFFS_EV_IX_UPD_HDR (4) #define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1))) @@ -131,7 +141,31 @@ #define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0) #define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) -#define SPIFFS_MAGIC(fs) ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) + + +#if defined(__GNUC__) || defined(__clang__) + /* For GCC and clang */ +#define SPIFFS_PACKED __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +#define SPIFFS_PACKED + +#else + /* Unknown compiler */ +#define SPIFFS_PACKED +#endif + + + +#if SPIFFS_USE_MAGIC +#if !SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_MAGIC(fs, bix) \ + ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) +#else // SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_MAGIC(fs, bix) \ + ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix)))) +#endif // SPIFFS_USE_MAGIC_LENGTH +#endif // SPIFFS_USE_MAGIC #define SPIFFS_CONFIG_MAGIC (0x20090315) @@ -220,6 +254,17 @@ // object index span index number for given data span index or entry #define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \ ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs))) +// get data span index for object index span index +#define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \ + ( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) ) + +#if SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0) +#define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0) +#else +#define SPIFFS_FH_OFFS(fs, fh) (fh) +#define SPIFFS_FH_UNOFFS(fs, fh) (fh) +#endif #define SPIFFS_OP_T_OBJ_LU (0<<0) @@ -264,26 +309,26 @@ #define SPIFFS_API_CHECK_MOUNT(fs) \ if (!SPIFFS_CHECK_MOUNT((fs))) { \ (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \ - return -1; \ + return SPIFFS_ERR_NOT_MOUNTED; \ } #define SPIFFS_API_CHECK_CFG(fs) \ if (!SPIFFS_CHECK_CFG((fs))) { \ (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \ - return -1; \ + return SPIFFS_ERR_NOT_CONFIGURED; \ } #define SPIFFS_API_CHECK_RES(fs, res) \ if ((res) < SPIFFS_OK) { \ (fs)->err_code = (res); \ - return -1; \ + return (res); \ } #define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ if ((res) < SPIFFS_OK) { \ (fs)->err_code = (res); \ SPIFFS_UNLOCK(fs); \ - return -1; \ + return (res); \ } #define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \ @@ -304,13 +349,33 @@ if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH; -// check id +// check id, only visit matching objec ids #define SPIFFS_VIS_CHECK_ID (1<<0) // report argument object id to visitor - else object lookup id is reported #define SPIFFS_VIS_CHECK_PH (1<<1) // stop searching at end of all look up pages #define SPIFFS_VIS_NO_WRAP (1<<2) +#if SPIFFS_HAL_CALLBACK_EXTRA + +#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ + (_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src)) +#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ + (_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst)) +#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ + (_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len)) + +#else // SPIFFS_HAL_CALLBACK_EXTRA + +#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ + (_fs)->cfg.hal_write_f((_paddr), (_len), (_src)) +#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ + (_fs)->cfg.hal_read_f((_paddr), (_len), (_dst)) +#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ + (_fs)->cfg.hal_erase_f((_paddr), (_len)) + +#endif // SPIFFS_HAL_CALLBACK_EXTRA + #if SPIFFS_CACHE #define SPIFFS_CACHE_FLAG_DIRTY (1<<0) @@ -390,13 +455,23 @@ typedef struct { spiffs_span_ix cursor_objix_spix; // current absolute offset u32_t offset; - // current file descriptor offset + // current file descriptor offset (cached) u32_t fdoffset; // fd flags spiffs_flags flags; #if SPIFFS_CACHE_WR spiffs_cache_page *cache_page; #endif +#if SPIFFS_TEMPORAL_FD_CACHE + // djb2 hash of filename + u32_t name_hash; + // hit score (score == 0 indicates never used fd) + u16_t score; +#endif +#if SPIFFS_IX_MAP + // spiffs index map, if 0 it means unmapped + spiffs_ix_map *ix_map; +#endif } spiffs_fd; @@ -405,7 +480,7 @@ typedef struct { // page header, part of each page except object lookup pages // NB: this is always aligned when the data page is an object index, // as in this case struct spiffs_page_object_ix is used -typedef struct __attribute(( packed )) { +typedef struct SPIFFS_PACKED { // object id spiffs_obj_id obj_id; // object span index @@ -415,7 +490,7 @@ typedef struct __attribute(( packed )) { } spiffs_page_header; // object index header page header -typedef struct __attribute(( packed )) +typedef struct SPIFFS_PACKED #if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES __attribute(( aligned(sizeof(spiffs_page_ix)) )) #endif @@ -423,24 +498,28 @@ typedef struct __attribute(( packed )) // common page header spiffs_page_header p_hdr; // alignment - u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)]; + u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; // size of object u32_t size; // type of object spiffs_obj_type type; // name of object u8_t name[SPIFFS_OBJ_NAME_LEN]; +#if SPIFFS_OBJ_META_LEN + // metadata. not interpreted by SPIFFS in any way. + u8_t meta[SPIFFS_OBJ_META_LEN]; +#endif } spiffs_page_object_ix_header; // object index page header -typedef struct __attribute(( packed )) { +typedef struct SPIFFS_PACKED { spiffs_page_header p_hdr; - u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)]; + u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; } spiffs_page_object_ix; // callback func for object lookup visitor typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, - u32_t user_data, void *user_p); + const void *user_const_p, void *user_var_p); #if SPIFFS_CACHE @@ -501,8 +580,8 @@ s32_t spiffs_obj_lu_find_entry_visitor( u8_t flags, spiffs_obj_id obj_id, spiffs_visitor_f v, - u32_t user_data, - void *user_p, + const void *user_const_p, + void *user_var_p, spiffs_block_ix *block_ix, int *lu_entry); @@ -510,6 +589,11 @@ s32_t spiffs_erase_block( spiffs *fs, spiffs_block_ix bix); +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH +s32_t spiffs_probe( + spiffs_config *cfg); +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH + // --------------- s32_t spiffs_obj_lu_scan( @@ -518,7 +602,7 @@ s32_t spiffs_obj_lu_scan( s32_t spiffs_obj_lu_find_free_obj_id( spiffs *fs, spiffs_obj_id *obj_id, - u8_t *conflicting_name); + const u8_t *conflicting_name); s32_t spiffs_obj_lu_find_free( spiffs *fs, @@ -579,7 +663,8 @@ s32_t spiffs_page_delete( s32_t spiffs_object_create( spiffs *fs, spiffs_obj_id obj_id, - u8_t name[SPIFFS_OBJ_NAME_LEN], + const u8_t name[], + const u8_t meta[], spiffs_obj_type type, spiffs_page_ix *objix_hdr_pix); @@ -589,13 +674,24 @@ s32_t spiffs_object_update_index_hdr( spiffs_obj_id obj_id, spiffs_page_ix objix_hdr_pix, u8_t *new_objix_hdr_data, - u8_t name[SPIFFS_OBJ_NAME_LEN], + const u8_t name[], + const u8_t meta[], u32_t size, spiffs_page_ix *new_pix); -void spiffs_cb_object_event( +#if SPIFFS_IX_MAP + +s32_t spiffs_populate_ix_map( spiffs *fs, spiffs_fd *fd, + u32_t vec_entry_start, + u32_t vec_entry_end); + +#endif + +void spiffs_cb_object_event( + spiffs *fs, + spiffs_page_object_ix *objix, int ev, spiffs_obj_id obj_id, spiffs_span_ix spix, @@ -641,7 +737,7 @@ s32_t spiffs_object_truncate( s32_t spiffs_object_find_object_index_header_by_name( spiffs *fs, - u8_t name[SPIFFS_OBJ_NAME_LEN], + const u8_t name[SPIFFS_OBJ_NAME_LEN], spiffs_page_ix *pix); // --------------- @@ -671,7 +767,8 @@ s32_t spiffs_gc_quick( s32_t spiffs_fd_find_new( spiffs *fs, - spiffs_fd **fd); + spiffs_fd **fd, + const char *name); s32_t spiffs_fd_return( spiffs *fs, @@ -682,6 +779,13 @@ s32_t spiffs_fd_get( spiffs_file f, spiffs_fd **fd); +#if SPIFFS_TEMPORAL_FD_CACHE +void spiffs_fd_temporal_cache_rehash( + spiffs *fs, + const char *old_path, + const char *new_path); +#endif + #if SPIFFS_CACHE void spiffs_cache_init( spiffs *fs); @@ -715,4 +819,24 @@ s32_t spiffs_page_consistency_check( s32_t spiffs_object_index_consistency_check( spiffs *fs); +// memcpy macro, +// checked in test builds, otherwise plain memcpy (unless already defined) +#ifdef _SPIFFS_TEST +#define _SPIFFS_MEMCPY(__d, __s, __l) do { \ + intptr_t __a1 = (intptr_t)((u8_t*)(__s)); \ + intptr_t __a2 = (intptr_t)((u8_t*)(__s)+(__l)); \ + intptr_t __b1 = (intptr_t)((u8_t*)(__d)); \ + intptr_t __b2 = (intptr_t)((u8_t*)(__d)+(__l)); \ + if (__a1 <= __b2 && __b1 <= __a2) { \ + printf("FATAL OVERLAP: memcpy from %lx..%lx to %lx..%lx\n", __a1, __a2, __b1, __b2); \ + ERREXIT(); \ + } \ + memcpy((__d),(__s),(__l)); \ +} while (0) +#else +#ifndef _SPIFFS_MEMCPY +#define _SPIFFS_MEMCPY(__d, __s, __l) do{memcpy((__d),(__s),(__l));}while(0) +#endif +#endif //_SPIFFS_TEST + #endif /* SPIFFS_NUCLEUS_H_ */ diff --git a/components/spiffs/spiffs/src/test/main.c b/components/spiffs/spiffs/src/test/main.c new file mode 100644 index 00000000..4d363d59 --- /dev/null +++ b/components/spiffs/spiffs/src/test/main.c @@ -0,0 +1,12 @@ +#include + +#ifndef NO_TEST +#include "testrunner.h" +#endif + +int main(int argc, char **args) { +#ifndef NO_TEST + run_tests(argc, args); +#endif + exit(EXIT_SUCCESS); +} diff --git a/components/spiffs/spiffs/src/test/params_test.h b/components/spiffs/spiffs/src/test/params_test.h new file mode 100644 index 00000000..74f553ff --- /dev/null +++ b/components/spiffs/spiffs/src/test/params_test.h @@ -0,0 +1,84 @@ +/* + * params_test.h + * + * Created on: May 26, 2013 + * Author: petera + */ + +#ifndef PARAMS_TEST_H_ +#define PARAMS_TEST_H_ + +//////////////// TEST PARAMS //////////////// + +// default test total emulated spi flash size +#define PHYS_FLASH_SIZE (16*1024*1024) +// default test spiffs file system size +#define SPIFFS_FLASH_SIZE (2*1024*1024) +// default test spiffs file system offset in emulated spi flash +#define SPIFFS_PHYS_ADDR (4*1024*1024) +// default test sector size +#define SECTOR_SIZE 65536 +// default test logical block size +#define LOG_BLOCK (SECTOR_SIZE*2) +// default test logical page size +#define LOG_PAGE (SECTOR_SIZE/256) +// default test number of filedescs +#define DEFAULT_NUM_FD 16 +// default test number of cache pages +#define DEFAULT_NUM_CACHE_PAGES 8 + +// When testing, test bench create reference files for comparison on +// the actual hard drive. By default, put these on ram drive for speed. +#define TEST_PATH "/dev/shm/spiffs/test-data/" + +#define ASSERT(c, m) real_assert((c),(m), __FILE__, __LINE__); +void real_assert(int c, const char *n, const char *file, int l); + +/////////// SPIFFS BUILD CONFIG //////////// + +// test using filesystem magic +#ifndef SPIFFS_USE_MAGIC +#define SPIFFS_USE_MAGIC 1 +#endif +// test using filesystem magic length +#ifndef SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_USE_MAGIC_LENGTH 1 +#endif +// test using extra param in callback +#ifndef SPIFFS_HAL_CALLBACK_EXTRA +#define SPIFFS_HAL_CALLBACK_EXTRA 1 +#endif +// test using filehandle offset +#ifndef SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FILEHDL_OFFSET 1 +// use this offset +#define TEST_SPIFFS_FILEHDL_OFFSET 0x1000 +#endif + +#ifdef NO_TEST +#define SPIFFS_LOCK(fs) +#define SPIFFS_UNLOCK(fs) +#else +struct spiffs_t; +extern void test_lock(struct spiffs_t *fs); +extern void test_unlock(struct spiffs_t *fs); +#define SPIFFS_LOCK(fs) test_lock(fs) +#define SPIFFS_UNLOCK(fs) test_unlock(fs) +#endif + +// dbg output +#define SPIFFS_DBG(_f, ...) //printf("\x1b[32m" _f "\x1b[0m", ## __VA_ARGS__) +#define SPIFFS_API_DBG(_f, ...) //printf("\n\x1b[1m\x1b[7m" _f "\x1b[0m", ## __VA_ARGS__) +#define SPIFFS_GC_DBG(_f, ...) //printf("\x1b[36m" _f "\x1b[0m", ## __VA_ARGS__) +#define SPIFFS_CACHE_DBG(_f, ...) //printf("\x1b[33m" _f "\x1b[0m", ## __VA_ARGS__) +#define SPIFFS_CHECK_DBG(_f, ...) //printf("\x1b[31m" _f "\x1b[0m", ## __VA_ARGS__) + +// needed types +typedef signed int s32_t; +typedef unsigned int u32_t; +typedef signed short s16_t; +typedef unsigned short u16_t; +typedef signed char s8_t; +typedef unsigned char u8_t; + +#endif /* PARAMS_TEST_H_ */ diff --git a/components/spiffs/spiffs/src/test/test_bugreports.c b/components/spiffs/spiffs/src/test/test_bugreports.c new file mode 100644 index 00000000..e9b59d18 --- /dev/null +++ b/components/spiffs/spiffs/src/test/test_bugreports.c @@ -0,0 +1,1266 @@ +/* + * test_bugreports.c + * + * Created on: Mar 8, 2015 + * Author: petera + */ + + + +#include "testrunner.h" +#include "test_spiffs.h" +#include "spiffs_nucleus.h" +#include "spiffs.h" +#include +#include +#include +#include +#include + +/* The follow defines control details of how the fuzzer can exercise the API. If you + * undef any of these, then the fuzzer is less brutal. FOr example, if you undef + * HAVE_REMOVE_OPEN, then the fuzzer will not attempt to remove (or rename) an open file + */ +#define HAVE_REMOVE_OPEN +#define HAVE_MULTIPLE_OPEN +#define NO_FORCE_CHECK + +SUITE(bug_tests) +static void setup() { + _setup_test_only(); +} +static void teardown() { + _teardown(); +} + +TEST(nodemcu_full_fs_1) { + fs_reset_specific(0, 0, 4096*20, 4096, 4096, 256); + + int res; + spiffs_file fd; + + printf(" fill up system by writing one byte a lot\n"); + fd = SPIFFS_open(FS, "test1.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(fd > 0); + int i; + spiffs_stat s; + res = SPIFFS_OK; + for (i = 0; i < 100*1000; i++) { + u8_t buf = 'x'; + res = SPIFFS_write(FS, fd, &buf, 1); + } + + int errno = SPIFFS_errno(FS); + int res2 = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res2 == SPIFFS_OK); + printf(" >>> file %s size: %i\n", s.name, s.size); + + TEST_CHECK(errno == SPIFFS_ERR_FULL); + SPIFFS_close(FS, fd); + + printf(" remove big file\n"); + res = SPIFFS_remove(FS, "test1.txt"); + + printf("res:%i errno:%i\n",res, SPIFFS_errno(FS)); + + TEST_CHECK(res == SPIFFS_OK); + res2 = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res2 < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res2 = SPIFFS_stat(FS, "test1.txt", &s); + TEST_CHECK(res2 < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + printf(" create small file\n"); + fd = SPIFFS_open(FS, "test2.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(fd > 0); + res = SPIFFS_OK; + for (i = 0; res >= 0 && i < 1000; i++) { + u8_t buf = 'x'; + res = SPIFFS_write(FS, fd, &buf, 1); + } + TEST_CHECK(res >= SPIFFS_OK); + + res2 = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res2 == SPIFFS_OK); + printf(" >>> file %s size: %i\n", s.name, s.size); + + TEST_CHECK(s.size == 1000); + SPIFFS_close(FS, fd); + + return TEST_RES_OK; + +} TEST_END + +TEST(nodemcu_full_fs_2) { + fs_reset_specific(0, 0, 4096*22, 4096, 4096, 256); + + int res; + spiffs_file fd; + + printf(" fill up system by writing one byte a lot\n"); + fd = SPIFFS_open(FS, "test1.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(fd > 0); + int i; + spiffs_stat s; + res = SPIFFS_OK; + for (i = 0; i < 100*1000; i++) { + u8_t buf = 'x'; + res = SPIFFS_write(FS, fd, &buf, 1); + } + + int errno = SPIFFS_errno(FS); + int res2 = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res2 == SPIFFS_OK); + printf(" >>> file %s size: %i\n", s.name, s.size); + + TEST_CHECK(errno == SPIFFS_ERR_FULL); + SPIFFS_close(FS, fd); + + res2 = SPIFFS_stat(FS, "test1.txt", &s); + TEST_CHECK(res2 == SPIFFS_OK); + + SPIFFS_clearerr(FS); + printf(" create small file\n"); + fd = SPIFFS_open(FS, "test2.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); +#if 0 + // before gc in v3.1 + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK); + TEST_CHECK(fd > 0); + + for (i = 0; i < 1000; i++) { + u8_t buf = 'x'; + res = SPIFFS_write(FS, fd, &buf, 1); + } + + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FULL); + res2 = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res2 == SPIFFS_OK); + printf(" >>> file %s size: %i\n", s.name, s.size); + TEST_CHECK(s.size == 0); + SPIFFS_clearerr(FS); +#else + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FULL); + SPIFFS_clearerr(FS); +#endif + printf(" remove files\n"); + res = SPIFFS_remove(FS, "test1.txt"); + TEST_CHECK(res == SPIFFS_OK); +#if 0 + res = SPIFFS_remove(FS, "test2.txt"); + TEST_CHECK(res == SPIFFS_OK); +#endif + + printf(" create medium file\n"); + fd = SPIFFS_open(FS, "test3.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK); + TEST_CHECK(fd > 0); + + for (i = 0; i < 20*1000; i++) { + u8_t buf = 'x'; + res = SPIFFS_write(FS, fd, &buf, 1); + } + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK); + + res2 = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res2 == SPIFFS_OK); + printf(" >>> file %s size: %i\n", s.name, s.size); + TEST_CHECK(s.size == 20*1000); + + return TEST_RES_OK; + +} TEST_END + +TEST(magic_test) { + // this test only works on default sizes + TEST_ASSERT(sizeof(spiffs_obj_id) == sizeof(u16_t)); + + // one obj lu page, not full + fs_reset_specific(0, 0, 4096*16, 4096, 4096*1, 128); + TEST_CHECK(SPIFFS_CHECK_MAGIC_POSSIBLE(FS)); + // one obj lu page, full + fs_reset_specific(0, 0, 4096*16, 4096, 4096*2, 128); + TEST_CHECK(!SPIFFS_CHECK_MAGIC_POSSIBLE(FS)); + // two obj lu pages, not full + fs_reset_specific(0, 0, 4096*16, 4096, 4096*4, 128); + TEST_CHECK(SPIFFS_CHECK_MAGIC_POSSIBLE(FS)); + + return TEST_RES_OK; + +} TEST_END + +TEST(nodemcu_309) { + fs_reset_specific(0, 0, 4096*20, 4096, 4096, 256); + + int res; + spiffs_file fd; + int j; + + for (j = 1; j <= 3; j++) { + char fname[32]; + sprintf(fname, "20K%i.txt", j); + fd = SPIFFS_open(FS, fname, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_DIRECT, 0); + TEST_CHECK(fd > 0); + int i; + res = SPIFFS_OK; + u8_t err = 0; + for (i = 1; i <= 1280; i++) { + char *buf = "0123456789ABCDE\n"; + res = SPIFFS_write(FS, fd, buf, strlen(buf)); + if (!err && res < 0) { + printf("err @ %i,%i\n", i, j); + err = 1; + } + } + } + + int errno = SPIFFS_errno(FS); + TEST_CHECK(errno == SPIFFS_ERR_FULL); + + u32_t total; + u32_t used; + + SPIFFS_info(FS, &total, &used); + printf("total:%i\nused:%i\nremain:%i\nerrno:%i\n", total, used, total-used, errno); + //TEST_CHECK(total-used < 11000); // disabled, depends on too many variables + + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + + SPIFFS_opendir(FS, "/", &d); + int spoon_guard = 0; + while ((pe = SPIFFS_readdir(&d, pe))) { + printf("%s [%04x] size:%i\n", pe->name, pe->obj_id, pe->size); + TEST_CHECK(spoon_guard++ < 3); + } + TEST_CHECK(spoon_guard == 3); + SPIFFS_closedir(&d); + + return TEST_RES_OK; + +} TEST_END + + +TEST(robert) { + // create a clean file system starting at address 0, 2 megabytes big, + // sector size 65536, block size 65536, page size 256 + fs_reset_specific(0, 0, 1024*1024*2, 65536, 65536, 256); + + int res; + spiffs_file fd; + char fname[32]; + + sprintf(fname, "test.txt"); + fd = SPIFFS_open(FS, fname, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(fd > 0); + res = SPIFFS_OK; + char buf[500]; + memset(buf, 0xaa, 500); + res = SPIFFS_write(FS, fd, buf, 500); + TEST_CHECK(res >= SPIFFS_OK); + SPIFFS_close(FS, fd); + + int errno = SPIFFS_errno(FS); + TEST_CHECK(errno == SPIFFS_OK); + + //SPIFFS_vis(FS); + // unmount + SPIFFS_unmount(FS); + + // remount + res = fs_mount_specific(0, 1024*1024*2, 65536, 65536, 256); + TEST_CHECK(res== SPIFFS_OK); + + //SPIFFS_vis(FS); + + spiffs_stat s; + TEST_CHECK(SPIFFS_stat(FS, fname, &s) == SPIFFS_OK); + printf("file %s stat size %i\n", s.name, s.size); + TEST_CHECK(s.size == 500); + + return TEST_RES_OK; + +} TEST_END + + +TEST(spiffs_12) { + fs_reset_specific(0x4024c000, 0x4024c000 + 0, 192*1024, 4096, 4096*2, 256); + + int res; + spiffs_file fd; + int j = 1; + + while (1) { + char fname[32]; + sprintf(fname, "file%i.txt", j); + fd = SPIFFS_open(FS, fname, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_DIRECT, 0); + if (fd <=0) break; + + int i; + res = SPIFFS_OK; + for (i = 1; i <= 100; i++) { + char *buf = "0123456789ABCDE\n"; + res = SPIFFS_write(FS, fd, buf, strlen(buf)); + if (res < 0) break; + } + SPIFFS_close(FS, fd); + j++; + } + + int errno = SPIFFS_errno(FS); + TEST_CHECK(errno == SPIFFS_ERR_FULL); + + u32_t total; + u32_t used; + + SPIFFS_info(FS, &total, &used); + printf("total:%i (%iK)\nused:%i (%iK)\nremain:%i (%iK)\nerrno:%i\n", total, total/1024, used, used/1024, total-used, (total-used)/1024, errno); + + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + + SPIFFS_opendir(FS, "/", &d); + while ((pe = SPIFFS_readdir(&d, pe))) { + printf("%s [%04x] size:%i\n", pe->name, pe->obj_id, pe->size); + } + SPIFFS_closedir(&d); + + //SPIFFS_vis(FS); + + //dump_page(FS, 0); + //dump_page(FS, 1); + + return TEST_RES_OK; + +} TEST_END + + +TEST(zero_sized_file_44) { + fs_reset(); + + spiffs_file fd = SPIFFS_open(FS, "zero", SPIFFS_RDWR | SPIFFS_CREAT, 0); + TEST_CHECK_GE(fd, 0); + + int res = SPIFFS_close(FS, fd); + TEST_CHECK_GE(res, 0); + + fd = SPIFFS_open(FS, "zero", SPIFFS_RDWR, 0); + TEST_CHECK_GE(fd, 0); + + u8_t buf[8]; + res = SPIFFS_read(FS, fd, buf, 8); + TEST_CHECK_EQ(res, 0); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_END_OF_OBJECT); + + res = SPIFFS_read(FS, fd, buf, 0); + TEST_CHECK_GE(res, 0); + + res = SPIFFS_read(FS, fd, buf, 0); + TEST_CHECK_GE(res, 0); + + buf[0] = 1; + buf[1] = 2; + + res = SPIFFS_write(FS, fd, buf, 2); + TEST_CHECK_EQ(res, 2); + + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_SET); + TEST_CHECK_GE(res, 0); + + u8_t b; + res = SPIFFS_read(FS, fd, &b, 1); + TEST_CHECK_EQ(res, 1); + TEST_CHECK_EQ(b, 1); + + res = SPIFFS_read(FS, fd, &b, 1); + TEST_CHECK_EQ(res, 1); + TEST_CHECK_EQ(b, 2); + + res = SPIFFS_read(FS, fd, buf, 8); + TEST_CHECK_EQ(res, 0); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_END_OF_OBJECT); + + return TEST_RES_OK; +} TEST_END + +#if !SPIFFS_READ_ONLY +TEST(truncate_48) { + fs_reset(); + + u32_t len = SPIFFS_DATA_PAGE_SIZE(FS)/2; + + s32_t res = test_create_and_write_file("small", len, len); + TEST_CHECK_GE(res, 0); + + spiffs_file fd = SPIFFS_open(FS, "small", SPIFFS_RDWR, 0); + TEST_CHECK_GE(fd, 0); + + spiffs_fd *desc; +#if SPIFFS_FILEHDL_OFFSET + res = spiffs_fd_get(FS, fd - TEST_SPIFFS_FILEHDL_OFFSET, &desc); +#else + res = spiffs_fd_get(FS, fd, &desc); +#endif + + TEST_CHECK_GE(res, 0); + + TEST_CHECK_EQ(desc->size, len); + + u32_t new_len = len/2; + res = spiffs_object_truncate(desc, new_len, 0); + TEST_CHECK_GE(res, 0); + + TEST_CHECK_EQ(desc->size, new_len); + + res = SPIFFS_close(FS, fd); + TEST_CHECK_GE(res, 0); + + spiffs_stat s; + res = SPIFFS_stat(FS, "small", &s); + TEST_CHECK_GE(res, 0); + TEST_CHECK_EQ(s.size, new_len); + + res = SPIFFS_remove(FS, "small"); + TEST_CHECK_GE(res, 0); + + fd = SPIFFS_open(FS, "small", SPIFFS_RDWR, 0); + TEST_CHECK_LT(fd, 0); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_FOUND); + + return TEST_RES_OK; +} TEST_END +#endif + +TEST(eof_tell_72) { + fs_reset(); + + s32_t res; + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_CREAT | SPIFFS_RDWR | SPIFFS_APPEND, 0); + TEST_CHECK_GT(fd, 0); + TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 1); + TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 0); + + res = SPIFFS_write(FS, fd, "test", 4); + TEST_CHECK_EQ(res, 4); + TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 1); + TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 4); + + res = SPIFFS_fflush(FS, fd); + TEST_CHECK_EQ(res, SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 1); + TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 4); + + res = SPIFFS_lseek(FS, fd, 2, SPIFFS_SEEK_SET); + TEST_CHECK_EQ(res, 2); + TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 0); + TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 2); + + res = SPIFFS_write(FS, fd, "test", 4); + TEST_CHECK_EQ(res, 4); + TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 1); + TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 8); + + res = SPIFFS_fflush(FS, fd); + TEST_CHECK_EQ(res, SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 1); + TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 8); + + res = SPIFFS_close(FS, fd); + TEST_CHECK_EQ(res, SPIFFS_OK); + TEST_CHECK_LT(SPIFFS_eof(FS, fd), SPIFFS_OK); + TEST_CHECK_LT(SPIFFS_tell(FS, fd), SPIFFS_OK); + + fd = SPIFFS_open(FS, "file", SPIFFS_RDWR, 0); + TEST_CHECK_GT(fd, 0); + TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 0); + TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 0); + + res = SPIFFS_lseek(FS, fd, 2, SPIFFS_SEEK_SET); + TEST_CHECK_EQ(res, 2); + TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 0); + TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 2); + + res = SPIFFS_write(FS, fd, "test", 4); + TEST_CHECK_EQ(res, 4); + TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 0); + TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 6); + + res = SPIFFS_fflush(FS, fd); + TEST_CHECK_EQ(res, SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 0); + TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 6); + + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_END); + TEST_CHECK_EQ(res, 8); + TEST_CHECK_EQ(SPIFFS_eof(FS, fd), 1); + TEST_CHECK_EQ(SPIFFS_tell(FS, fd), 8); + + return TEST_RES_OK; +} TEST_END + +TEST(spiffs_dup_file_74) { + fs_reset_specific(0, 0, 64*1024, 4096, 4096*2, 256); + { + spiffs_file fd = SPIFFS_open(FS, "/config", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_WRONLY, 0); + TEST_CHECK(fd >= 0); + char buf[5]; + strncpy(buf, "test", sizeof(buf)); + SPIFFS_write(FS, fd, buf, 4); + SPIFFS_close(FS, fd); + } + { + spiffs_file fd = SPIFFS_open(FS, "/data", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_WRONLY, 0); + TEST_CHECK(fd >= 0); + SPIFFS_close(FS, fd); + } + { + spiffs_file fd = SPIFFS_open(FS, "/config", SPIFFS_RDONLY, 0); + TEST_CHECK(fd >= 0); + char buf[5]; + int cb = SPIFFS_read(FS, fd, buf, sizeof(buf)); + TEST_CHECK(cb > 0 && cb < sizeof(buf)); + TEST_CHECK(strncmp("test", buf, cb) == 0); + SPIFFS_close(FS, fd); + } + { + spiffs_file fd = SPIFFS_open(FS, "/data", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_WRONLY, 0); + TEST_CHECK(fd >= 0); + spiffs_stat stat; + SPIFFS_fstat(FS, fd, &stat); + if (strcmp((const char*) stat.name, "/data") != 0) { + // oops! lets check the list of files... + spiffs_DIR dir; + SPIFFS_opendir(FS, "/", &dir); + struct spiffs_dirent dirent; + while (SPIFFS_readdir(&dir, &dirent)) { + printf("%s\n", dirent.name); + } + // this will print "/config" two times + TEST_CHECK(0); + } + SPIFFS_close(FS, fd); + } + return TEST_RES_OK; +} TEST_END + +TEST(temporal_fd_cache) { + fs_reset_specific(0, 0, 1024*1024, 4096, 2*4096, 256); + (FS)->fd_count = 4; + + char *fcss = "blaha.css"; + + char *fhtml[] = { + "index.html", "cykel.html", "bloja.html", "drivmedel.html", "smorgasbord.html", + "ombudsman.html", "fubbick.html", "paragrod.html" + }; + + const int hit_probabilities[] = { + 25, 20, 16, 12, 10, 8, 5, 4 + }; + + const int runs = 10000; + + // create our webserver files + TEST_CHECK_EQ(test_create_and_write_file(fcss, 2000, 256), SPIFFS_OK); + TEST_CHECK_EQ(test_create_and_write_file(fhtml[0], 4000, 256), SPIFFS_OK); + TEST_CHECK_EQ(test_create_and_write_file(fhtml[1], 3000, 256), SPIFFS_OK); + TEST_CHECK_EQ(test_create_and_write_file(fhtml[2], 2000, 256), SPIFFS_OK); + TEST_CHECK_EQ(test_create_and_write_file(fhtml[3], 1000, 256), SPIFFS_OK); + TEST_CHECK_EQ(test_create_and_write_file(fhtml[4], 1500, 256), SPIFFS_OK); + TEST_CHECK_EQ(test_create_and_write_file(fhtml[5], 3000, 256), SPIFFS_OK); + TEST_CHECK_EQ(test_create_and_write_file(fhtml[6], 2000, 256), SPIFFS_OK); + TEST_CHECK_EQ(test_create_and_write_file(fhtml[7], 3500, 256), SPIFFS_OK); + + clear_flash_ops_log(); + + int run = 0; + do { + // open & read an html + int dice = rand() % 100; + int probability = 0; + int html_ix = 0; + do { + probability += hit_probabilities[html_ix]; + if (dice <= probability) { + break; + } + html_ix++; + } while(probability < 100); + + TEST_CHECK_EQ(read_and_verify(fhtml[html_ix]), SPIFFS_OK); + + // open & read css + TEST_CHECK_EQ(read_and_verify(fcss), SPIFFS_OK); + } while (run ++ < runs); + + return TEST_RES_OK; +} TEST_END + +static int run_fuzz_test(FILE *f, int maxfds, int debuglog) { + // There are a bunch of arbitrary constants in this test case. Changing them will + // almost certainly change the effets of an input file. It *may* be worth + // making some of these constants to come from the input file. + int setup = fgetc(f); + + int page_size = 128 << (setup & 3); + setup >>= 2; + int erase_size = 4096 << (setup & 3); + setup >>= 2; + int block_size = erase_size << (setup & 1); + setup >>= 1; + int blocks = 4 + (setup & 7); + fs_reset_specific(0, 0, blocks * block_size, erase_size, block_size, page_size); + int res; + (FS)->fd_count = 4; + + int c; + + spiffs_file fd[4]; + memset(fd, -1, sizeof(fd)); + int openindex[4]; + memset(openindex, -1, sizeof(openindex)); + char *filename[8]; + + int i; + + for (i = 0; i < 8; i++) { + char buff[128]; + sprintf(buff, "%dfile%d.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxasdasdasdadxxxxxxxxxxxxxxxxxxx", i, i); + buff[9 + 2 * i] = 0; + filename[i] = strdup(buff); + } + + // The list of 8 modes that are chosen. SPIFFS_EXCL is not present -- it probably ought to be. + int modes[8] = {SPIFFS_RDONLY, SPIFFS_RDWR, SPIFFS_RDWR|SPIFFS_TRUNC, SPIFFS_RDWR|SPIFFS_CREAT, SPIFFS_RDWR|SPIFFS_CREAT|SPIFFS_TRUNC, + SPIFFS_WRONLY|SPIFFS_CREAT|SPIFFS_TRUNC, SPIFFS_RDWR|SPIFFS_CREAT|SPIFFS_TRUNC|SPIFFS_DIRECT, SPIFFS_WRONLY}; + + char buff[2048]; + for (i = 0; i < sizeof(buff); i++) { + buff[i] = i * 19; + } + +#define LOGOP if (debuglog) printf + + while ((c = fgetc(f)) >= 0) { + int add; + char rbuff[2048]; + if (c <= ' ') { + continue; + } + int arg = fgetc(f); + if (arg < 0) { + break; + } + int fdn = ((arg >> 6) & 3) % maxfds; + int rc; + switch(c) { + case 'O': + if (fd[fdn] >= 0) { + LOGOP(" close(%d)\n", fd[fdn]); + SPIFFS_close(FS, fd[fdn]); + openindex[fdn] = -1; + fd[fdn] = -1; + } +#ifndef HAVE_MULTIPLE_OPEN + { + int index = (arg >> 3) & 7; + for (i = 0; i < sizeof(openindex) / sizeof(openindex[0]); i++) { + if (openindex[i] == index) { + break; + } + } + if (i < sizeof(openindex) / sizeof(openindex[0])) { + break; + } + } +#endif + LOGOP(" open(\"%s\", 0x%x)", filename[(arg>>3) & 7], modes[arg & 7]); + fd[fdn] = SPIFFS_open(FS, filename[(arg>>3) & 7], modes[arg & 7], 0); + if (fd[fdn] >= 0) { + openindex[fdn] = (arg >> 3) & 7; + } + LOGOP(" -> %d\n", fd[fdn]); + break; + + case 'S': + if (fd[fdn] >= 0) { + int offset = (14 << (arg & 7)) + arg; + if (arg & 16) { + offset = -offset; + } + int whence = (arg & 63) % 3; + LOGOP(" lseek(%d, %d, %d)\n", fd[fdn], offset, whence); + SPIFFS_lseek(FS, fd[fdn], offset, whence); + } + break; + + case 'R': + if (fd[fdn] >= 0) { + LOGOP(" read(%d, , %d)", fd[fdn], (15 << (arg & 7)) + (arg & 127)); + int rlen = SPIFFS_read(FS, fd[fdn], rbuff, (15 << (arg & 7)) + (arg & 127)); + LOGOP(" -> %d\n", rlen); + } + break; + + case 'W': + if (fd[fdn] >= 0) { + LOGOP(" write(%d, , %d)", fd[fdn], (15 << (arg & 7)) + (arg & 127)); + rc = SPIFFS_write(FS, fd[fdn], buff, (15 << (arg & 7)) + (arg & 127)); + LOGOP(" -> %d\n", rc); + } + break; + + case 'C': + if (fd[fdn] >= 0) { + LOGOP(" close(%d)\n", fd[fdn]); + SPIFFS_close(FS, fd[fdn]); + } + fd[fdn] = -1; + openindex[fdn] = -1; + break; + + case 'b': + add = fgetc(f); + for (i = 0; i < sizeof(buff); i++) { + buff[i] = add + i * arg; + } + break; + + case 'f': + if (fd[fdn] >= 0) { + LOGOP(" fflush(%d)\n", fd[fdn]); + SPIFFS_fflush(FS, fd[fdn]); + } + break; + +#ifdef HAVE_REMOVE_OPEN + case 'D': + if (fd[fdn] >= 0) { + LOGOP(" fremove(%d)\n", fd[fdn]); + SPIFFS_fremove(FS, fd[fdn]); + } + break; +#endif + + case 'd': +#ifndef HAVE_REMOVE_OPEN + { + int index = arg & 7; + for (i = 0; i < sizeof(openindex) / sizeof(openindex[0]); i++) { + if (openindex[i] == index) { + break; + } + } + if (i < sizeof(openindex) / sizeof(openindex[0])) { + break; + } + } +#endif + LOGOP(" remove(\"%s\")", filename[arg & 7]); + rc = SPIFFS_remove(FS, filename[arg & 7]); + LOGOP(" -> %d\n", rc); + break; + + case 'r': +#ifndef HAVE_REMOVE_OPEN + { + int index = arg & 7; + for (i = 0; i < sizeof(openindex) / sizeof(openindex[0]); i++) { + if (openindex[i] == index) { + break; + } + } + if (i < sizeof(openindex) / sizeof(openindex[0])) { + break; + } + } +#endif + LOGOP(" rename(\"%s\", \"%s\")", filename[arg & 7], filename[(arg >> 3) & 7]); + rc = SPIFFS_rename(FS, filename[arg & 7], filename[(arg >> 3) & 7]); + LOGOP(" -> %d\n", rc); + break; + + case 'U': + ungetc(arg, f); + for (i = 0; i < 4; i++) { + fd[i] = -1; + } + { +#ifdef DO_UNMOUNT + LOGOP(" unmount\n"); + SPIFFS_unmount(FS); +#endif + char *tmpfile = strdup("/tmp/fsdump.XXXXXX"); + LOGOP(" cycle and remount\n"); + close(mkstemp(tmpfile)); + fs_store_dump(tmpfile); + fs_mount_dump(tmpfile, 0, 0, blocks * block_size, erase_size, block_size, page_size); + unlink(tmpfile); + free(tmpfile); +#ifndef NO_FORCE_CHECK + LOGOP(" forcecheck()"); + rc = SPIFFS_check(FS); + LOGOP(" -> %d\n", rc); +#endif + } + break; + + case 'c': + { + LOGOP(" check()"); + rc = SPIFFS_check(FS); + LOGOP(" -> %d\n", rc); + ungetc(arg, f); + break; + } + + default: + ungetc(arg, f); + break; + } + } + + for (i = 0; i < 4; i++) { + if (fd[i] >= 0) { + LOGOP(" close(%d)\n", fd[i]); + SPIFFS_close(FS, fd[i]); + } + } + + return TEST_RES_OK; +} + +#define FMEMARGS(x) x, sizeof(x) - 1 + +TEST(fuzzer_found_1) { + return run_fuzz_test(fmemopen(FMEMARGS("\021OlWkd5O4W4W0O5OlWkO5OlW0O5O4W0"), "r"), 4, 1); +} TEST_END + +TEST(fuzzer_found_2) { + return run_fuzz_test(fmemopen(FMEMARGS("bO4W6W0d\036O4W6"), "r"), 4, 1); +} TEST_END + +TEST(fuzzer_found_3) { + return run_fuzz_test(fmemopen(FMEMARGS("\264O4OqWeWWWWW@O4WWW\027"), "r"), 4, 1); +} TEST_END + +TEST(fuzzer_found_4) { + unsigned char smalltest[] = { + 0x62, 0x4f, 0x24, 0x57, 0x3f, 0x57, 0x3f, 0x57, 0x3f, 0x57, 0x3f, 0x57, + 0x3f, 0x4f, 0x34, 0x57, 0x3f, 0x55, 0x4f, 0x61, 0x57, 0x61, 0x4f, 0x61, + 0x57, 0x65, 0x43, 0x61, 0x4f, 0x24, 0x57, 0x30 + }; + unsigned int smalltest_len = 32; + + return run_fuzz_test(fmemopen(smalltest, smalltest_len, "r"), 4, 1); +} TEST_END + +TEST(fuzzer_found_single_1) { + return run_fuzz_test(fmemopen(FMEMARGS("\000O\004Odr4d\356Okr0WWUO;WWWWd\035W4"), "r"), 1, 1); +} TEST_END + +TEST(log_afl_test) { + u32_t old_val = set_abort_on_error(1); + int rc = run_fuzz_test(stdin, 4, 1); + set_abort_on_error(old_val); + return rc; +} TEST_END + +TEST(afl_test) { + u32_t old_val = set_abort_on_error(1); + int rc = run_fuzz_test(stdin, 4, 0); + set_abort_on_error(old_val); + return rc; +} TEST_END + +TEST(afl_single) { + u32_t old_val = set_abort_on_error(1); + int rc = run_fuzz_test(stdin, 1, 0); + set_abort_on_error(old_val); + return rc; +} TEST_END + +TEST(small_free_space) { + fs_reset_specific(0, 0, 400*1024, 4096, 2*4096, 256); + spiffs_file fd; + int res; + (FS)->fd_count = 4; + + int tfd = SPIFFS_open(FS, "testfile", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(tfd > 0); + char *tbuf = "some data"; + res = SPIFFS_write(FS, tfd, tbuf, strlen(tbuf)); + + TEST_CHECK(res == strlen(tbuf)); + + res = SPIFFS_fflush(FS, tfd); + TEST_CHECK(res >= SPIFFS_OK); + + SPIFFS_close(FS, tfd); + + const int runs = 1000; + + int fileCurrNumber = 0; + int fileDelNumber = 1; + + int run = 0; + do { + u8_t buf[1000]; + + sprintf(buf, "%d", fileCurrNumber); + int i; + for (i = 0; i < 100; i++) { + strcat(buf, " azzaaax"); + } + + int maxFileNr = 500; + char *filename = "awyn"; + char *fileext = ".dat"; + + u32_t total; + u32_t used; + + SPIFFS_info(FS, &total, &used); + + if (total - used < 20000) { + maxFileNr = 1; + } + + fileCurrNumber++; + int fileCntr = fileCurrNumber + 1 - fileDelNumber; + + char fileCurrName[64]; + sprintf(fileCurrName, "%s%d%s", filename, fileCurrNumber, fileext); + + fd = SPIFFS_open(FS, fileCurrName, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(fd > 0); + + //printf("About to write to %s\n", fileCurrName); + res = SPIFFS_write(FS, fd, buf, strlen(buf)); + + TEST_CHECK(res == strlen(buf)); + + res = SPIFFS_fflush(FS, fd); + TEST_CHECK_EQ(res, SPIFFS_OK); + + SPIFFS_close(FS, fd); + + if (fileCntr > maxFileNr) { + char fileDelName[64]; + sprintf(fileDelName, "%s%d%s", filename, fileDelNumber, fileext); + //printf("Deleting %s (free space %d)\n", fileDelName, total - used); + + res = SPIFFS_remove(FS, fileDelName); + + TEST_CHECK(res == SPIFFS_OK); + fileDelNumber++; + } + } while (run ++ < runs); + + tfd = SPIFFS_open(FS, "testfile", SPIFFS_RDONLY, 0); + TEST_CHECK(tfd > 0); + char rbuf[32]; + res = SPIFFS_read(FS, tfd, rbuf, sizeof(rbuf)); + + TEST_CHECK(res == strlen(tbuf)); + + SPIFFS_close(FS, tfd); + + TEST_CHECK(memcmp(rbuf, tbuf, strlen(tbuf)) == 0); + + return TEST_RES_OK; +} TEST_END + +TEST(lots_of_overwrite) { + fs_reset_specific(0, 0, 3000*1024, 4096, 2*4096, 256); + spiffs_file fd; + int res; + (FS)->fd_count = 4; + + int i; + + for (i = 0; i < 5; i++) { + + char filename[64]; + sprintf(filename, "%d-tstfile", i); + int tfd = SPIFFS_open(FS, filename, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(tfd > 0); + char tbuf[1024]; + memset(tbuf, 'a', 700); + tbuf[700] = 0; + res = SPIFFS_write(FS, tfd, tbuf, strlen(tbuf)); + + TEST_CHECK(res == strlen(tbuf)); + + res = SPIFFS_fflush(FS, tfd); + TEST_CHECK(res >= SPIFFS_OK); + + SPIFFS_close(FS, tfd); + } + + const int runs = 100000; + + int run = 0; + for (run = 0; run < runs; run++) { + u8_t buf[2000]; + + sprintf(buf, "%d", run); + int i; + for (i = 0; i < 100 + (run % 100); i++) { + strcat(buf, " azzaaax"); + } + + int tfd = SPIFFS_open(FS, "file.dat", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(tfd > 0); + res = SPIFFS_write(FS, tfd, buf, strlen(buf)); + + TEST_CHECK(res == strlen(buf)); + + res = SPIFFS_fflush(FS, tfd); + TEST_CHECK(res >= SPIFFS_OK); + + SPIFFS_close(FS, tfd); + + tfd = SPIFFS_open(FS, "file.dat", SPIFFS_RDONLY, 0); + TEST_CHECK(tfd > 0); + char rbuf[2000]; + res = SPIFFS_read(FS, tfd, rbuf, sizeof(rbuf)); + + TEST_CHECK(res == strlen(buf)); + + SPIFFS_close(FS, tfd); + + TEST_CHECK(memcmp(rbuf, buf, strlen(buf)) == 0); + + char filename[64]; + sprintf(filename, "%d-tstfile", run % 5); + tfd = SPIFFS_open(FS, filename, SPIFFS_RDONLY, 0); + TEST_CHECK(tfd > 0); + char tbuf[1024]; + memset(tbuf, 'a', 700); + tbuf[700] = 0; + res = SPIFFS_read(FS, tfd, rbuf, sizeof(rbuf)); + + TEST_CHECK(res == strlen(tbuf)); + + SPIFFS_close(FS, tfd); + TEST_CHECK(memcmp(rbuf, tbuf, strlen(tbuf)) == 0); + } + + return TEST_RES_OK; +} TEST_END + + +#if 0 +TEST(spiffs_hidden_file_90) { + fs_mount_dump("imgs/90.hidden_file.spiffs", 0, 0, 1*1024*1024, 4096, 4096, 128); + + SPIFFS_vis(FS); + + dump_page(FS, 1); + dump_page(FS, 0x8fe); + dump_page(FS, 0x8ff); + + { + spiffs_DIR dir; + SPIFFS_opendir(FS, "/", &dir); + struct spiffs_dirent dirent; + while (SPIFFS_readdir(&dir, &dirent)) { + printf("%-32s sz:%-7i obj_id:%08x pix:%08x\n", dirent.name, dirent.size, dirent.obj_id, dirent.pix); + } + } + + printf("remove cli.bin res %i\n", SPIFFS_remove(FS, "cli.bin")); + + { + spiffs_DIR dir; + SPIFFS_opendir(FS, "/", &dir); + struct spiffs_dirent dirent; + while (SPIFFS_readdir(&dir, &dirent)) { + printf("%-32s sz:%-7i obj_id:%08x pix:%08x\n", dirent.name, dirent.size, dirent.obj_id, dirent.pix); + } + } + return TEST_RES_OK; + +} TEST_END +#endif +#if 0 +TEST(null_deref_check_93) { + fs_mount_dump("imgs/93.dump.bin", 0, 0, 2*1024*1024, 4096, 4096, 256); + + //int res = SPIFFS_open(FS, "d43.fw", SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_WRONLY, 0); + //TEST_CHECK_GE(res, SPIFFS_OK); + + SPIFFS_vis(FS); + + printf("\n\n-------------------------------------------------\n\n"); + + SPIFFS_check(FS); + //fs_store_dump("imgs/93.dump.checked.bin"); + + SPIFFS_vis(FS); + + printf("\n\n-------------------------------------------------\n\n"); + + SPIFFS_check(FS); + + SPIFFS_vis(FS); + printf("\n\n-------------------------------------------------\n\n"); + + + + return TEST_RES_OK; +} TEST_END +#endif + +TEST(spiffs_145) { + int res; + fs_reset_specific(0, 0, 1024*1024, 65536, 65536, 1024); + { + spiffs_file fd = SPIFFS_open(FS, "biggie", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_WRONLY, 0); + TEST_CHECK(fd >= 0); + char buf[1024*512]; + memset(buf, 0xee, sizeof(buf)); + TEST_CHECK_GT(SPIFFS_write(FS, fd, buf, sizeof(buf)), 0); + TEST_CHECK_EQ(SPIFFS_close(FS, fd), SPIFFS_OK); + } + const int runs = 1000; + int run = 0; + while (run++ < runs) { + spiffs_file fd = SPIFFS_open(FS, "clobber", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_WRONLY, 0); + TEST_CHECK(fd >= 0); + char buf[8192]; + memset(buf, 0xee, sizeof(buf)); + TEST_CHECK_GT(SPIFFS_write(FS, fd, buf, sizeof(buf)), 0); + TEST_CHECK_EQ(SPIFFS_close(FS, fd), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_remove(FS, "clobber"), SPIFFS_OK); + } + + // below stolen from SPIFFS_vis + spiffs *fs = FS; + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_block_ix bix = 0; + + while (bix < fs->block_count) { + // check each object lookup page + spiffs_obj_id erase_count; + TEST_CHECK_EQ(_spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&erase_count), SPIFFS_OK); + TEST_CHECK_NEQ(erase_count, (spiffs_obj_id)-1); + TEST_CHECK_NEQ(erase_count, 0); + bix++; + } // per block + + return TEST_RES_OK; +} TEST_END + + +TEST(seek_bug_148) { + int res; +#define MAGIC_SIZE_THAT_FAILS 26355 // happens to be SPIFFS_DATA_PAGE_SIZE(FS) * SPIFFS_OBJ_HDR_IX_LEN(FS) + fs_reset_specific(0, 0, 64*1024, 4096, 4096, 256); + u8_t buf[MAGIC_SIZE_THAT_FAILS]; + spiffs_file fd = SPIFFS_open(FS, "EVENT", SPIFFS_O_CREAT | SPIFFS_O_RDWR, 0); + TEST_CHECK_GT(fd, 0); + TEST_CHECK_EQ(SPIFFS_write(FS, fd, &buf, sizeof(buf)), sizeof(buf)); + TEST_CHECK_EQ(SPIFFS_close(FS, fd), SPIFFS_OK); + fd = SPIFFS_open(FS, "EVENT", SPIFFS_O_RDONLY, 0); + TEST_CHECK_GT(fd, 0); + TEST_CHECK_EQ(SPIFFS_lseek(FS, fd, 0, SEEK_END), MAGIC_SIZE_THAT_FAILS); + return TEST_RES_OK; +} TEST_END + + +TEST(remove_release_fd_152) { + int res; + fs_reset_specific(0, 0, 64*1024, 4096, 4096, 256); + u8_t buf[1024]; + memrand(buf, sizeof(buf)); + TEST_CHECK_EQ(count_taken_fds(FS), 0); + spiffs_file fd1 = SPIFFS_open(FS, "removemeandloseafd", SPIFFS_O_CREAT | SPIFFS_O_RDWR, 0); + TEST_CHECK_GT(fd1, 0); + TEST_CHECK_EQ(count_taken_fds(FS), 1); + TEST_CHECK_EQ(SPIFFS_write(FS, fd1, &buf, sizeof(buf)), sizeof(buf)); + TEST_CHECK_EQ(SPIFFS_close(FS, fd1), SPIFFS_OK); + TEST_CHECK_EQ(count_taken_fds(FS), 0); + spiffs_file fd2 = SPIFFS_open(FS, "removemeandloseafd", SPIFFS_O_RDWR, 0); + TEST_CHECK_GT(fd2, 0); + TEST_CHECK_EQ(count_taken_fds(FS), 1); + spiffs_file fd3 = SPIFFS_open(FS, "removemeandloseafd", SPIFFS_O_RDWR, 0); + TEST_CHECK_GT(fd3, 0); + TEST_CHECK_EQ(count_taken_fds(FS), 2); + TEST_CHECK_EQ(SPIFFS_remove(FS, "removemeandloseafd"), SPIFFS_OK); + TEST_CHECK_EQ(count_taken_fds(FS), 0); + return TEST_RES_OK; +} TEST_END + +TEST(certain_file_size_fail_165) { + fs_reset_specific(0, 0, 512*1024, 4*1024, 64*1024, 256); + const int NUM = 134; + const int SIZ = 200; + u8_t buf[SIZ]; + + TEST_CHECK_EQ(SPIFFS_creat(FS, "test", 0), SPIFFS_OK); + spiffs_file fd = SPIFFS_open(FS, "test", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0); + TEST_CHECK_GT(fd, 0); + + int i; + for (i = 0; i < NUM; i++) { + TEST_CHECK_EQ(SPIFFS_write(FS, fd, buf, SIZ), SIZ); + } + TEST_CHECK_EQ(SPIFFS_close(FS, fd), SPIFFS_OK); + fd = SPIFFS_open(FS, "test", SPIFFS_O_RDONLY, 0); + TEST_CHECK_GT(fd, 0); + + spiffs_stat s; + TEST_CHECK_EQ(SPIFFS_fstat(FS, fd, &s), SPIFFS_OK); + TEST_CHECK_EQ(s.size, NUM*SIZ); + + int size = 0; + for (i = 0; i < NUM; i++) { + size += SPIFFS_read(FS, fd, buf, SIZ); + } + TEST_CHECK_EQ(size, NUM*SIZ); + + return TEST_RES_OK; +} TEST_END + + +SUITE_TESTS(bug_tests) + ADD_TEST(nodemcu_full_fs_1) + ADD_TEST(nodemcu_full_fs_2) + ADD_TEST(magic_test) + ADD_TEST(nodemcu_309) + ADD_TEST(robert) + ADD_TEST(spiffs_12) + ADD_TEST(zero_sized_file_44) + ADD_TEST(truncate_48) + ADD_TEST(eof_tell_72) + ADD_TEST(spiffs_dup_file_74) + ADD_TEST(temporal_fd_cache) + ADD_TEST(spiffs_145) + ADD_TEST(seek_bug_148) + //ADD_TEST(small_free_space) + ADD_TEST(lots_of_overwrite) + ADD_TEST(fuzzer_found_1) + ADD_TEST(fuzzer_found_2) + ADD_TEST(fuzzer_found_3) + ADD_TEST(fuzzer_found_4) + ADD_TEST(remove_release_fd_152) + ADD_TEST(certain_file_size_fail_165) + ADD_TEST_NON_DEFAULT(fuzzer_found_single_1) + ADD_TEST_NON_DEFAULT(log_afl_test) + ADD_TEST_NON_DEFAULT(afl_test) + ADD_TEST_NON_DEFAULT(afl_single) +#if 0 + ADD_TEST(spiffs_hidden_file_90) +#endif +#if 0 + ADD_TEST(null_deref_check_93) +#endif +SUITE_END(bug_tests) diff --git a/components/spiffs/spiffs/src/test/test_check.c b/components/spiffs/spiffs/src/test/test_check.c new file mode 100644 index 00000000..15865c47 --- /dev/null +++ b/components/spiffs/spiffs/src/test/test_check.c @@ -0,0 +1,427 @@ +/* + * test_dev.c + * + * Created on: Jul 14, 2013 + * Author: petera + */ + + +#include "testrunner.h" +#include "test_spiffs.h" +#include "spiffs_nucleus.h" +#include "spiffs.h" +#include +#include +#include +#include +#include + + +SUITE(check_tests) +static void setup() { + _setup(); +} +static void teardown() { + _teardown(); +} + +TEST(evil_write) { + fs_set_validate_flashing(0); + printf("writing corruption to block 1 data range (leaving lu intact)\n"); + u32_t data_range = SPIFFS_CFG_LOG_BLOCK_SZ(FS) - + SPIFFS_CFG_LOG_PAGE_SZ(FS) * (SPIFFS_OBJ_LOOKUP_PAGES(FS)); + u8_t *corruption = malloc(data_range); + memrand(corruption, data_range); + u32_t addr = 0 * SPIFFS_CFG_LOG_PAGE_SZ(FS) * SPIFFS_OBJ_LOOKUP_PAGES(FS); + area_write(addr, corruption, data_range); + free(corruption); + + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + + printf("CHECK1-----------------\n"); + SPIFFS_check(FS); + printf("CHECK2-----------------\n"); + SPIFFS_check(FS); + printf("CHECK3-----------------\n"); + SPIFFS_check(FS); + + res = test_create_and_write_file("file2", size, size); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END + + +TEST(lu_check1) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify lu entry data page index 1 + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix); + TEST_CHECK(res >= 0); + + // reset lu entry to being erased, but keep page data + spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED; + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix); + u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry*sizeof(spiffs_obj_id); + + area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id)); + +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + SPIFFS_check(FS); + + return TEST_RES_OK; +} TEST_END + + +TEST(page_cons1) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify object index, find object index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + // set object index entry 2 to a bad page + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 0 * sizeof(spiffs_page_ix); + spiffs_page_ix bad_pix_ref = 0x55; + area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + area_write(addr + sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + + // delete all cache +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END + + +TEST(page_cons2) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify object index, find object index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + // find data page span index 0 + spiffs_page_ix dpix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &dpix); + TEST_CHECK(res >= 0); + + // set object index entry 1+2 to a data page 0 + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix); + spiffs_page_ix bad_pix_ref = dpix; + area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + + // delete all cache +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END + + + +TEST(page_cons3) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify object index, find object index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + // set object index entry 1+2 lookup page + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix); + spiffs_page_ix bad_pix_ref = SPIFFS_PAGES_PER_BLOCK(FS) * (*FS.block_count - 2); + area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + + // delete all cache +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END + + +TEST(page_cons_final) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify page header, make unfinalized + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix); + TEST_CHECK(res >= 0); + + // set page span ix 1 as unfinalized + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags); + u8_t flags; + area_read(addr, (u8_t*)&flags, 1); + flags |= SPIFFS_PH_FLAG_FINAL; + area_write(addr, (u8_t*)&flags, 1); + + // delete all cache +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END + + +TEST(index_cons1) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS); + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify lu entry data page index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + printf(" deleting lu entry pix %04x\n", pix); + // reset lu entry to being erased, but keep page data + spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED; + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix); + u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id); + + area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id)); + +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END + + +TEST(index_cons2) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS); + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify lu entry data page index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + printf(" writing lu entry for index page, ix %04x, as data page\n", pix); + spiffs_obj_id obj_id = 0x1234; + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix); + u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id); + + area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id)); + +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END + + +TEST(index_cons3) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS); + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify lu entry data page index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + printf(" setting lu entry pix %04x to another index page\n", pix); + // reset lu entry to being erased, but keep page data + spiffs_obj_id obj_id = 1234 | SPIFFS_OBJ_ID_IX_FLAG; + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix); + u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id); + + area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id)); + +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + SPIFFS_check(FS); + + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} TEST_END + +TEST(index_cons4) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS); + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify lu entry data page index header, flags + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + printf(" cue objix hdr deletion in page %04x\n", pix); + // set flags as deleting ix header + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags); + u8_t flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE); + + area_write(addr, (u8_t*)&flags, 1); + +#if SPIFFS_CACHE + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + SPIFFS_check(FS); + + return TEST_RES_OK; +} TEST_END + +SUITE_TESTS(check_tests) + ADD_TEST(evil_write) + ADD_TEST(lu_check1) + ADD_TEST(page_cons1) + ADD_TEST(page_cons2) + ADD_TEST(page_cons3) + ADD_TEST(page_cons_final) + ADD_TEST(index_cons1) + ADD_TEST(index_cons2) + ADD_TEST(index_cons3) + ADD_TEST(index_cons4) +SUITE_END(check_tests) diff --git a/components/spiffs/spiffs/src/test/test_dev.c b/components/spiffs/spiffs/src/test/test_dev.c new file mode 100644 index 00000000..552e9d48 --- /dev/null +++ b/components/spiffs/spiffs/src/test/test_dev.c @@ -0,0 +1,122 @@ +/* + * test_dev.c + * + * Created on: Jul 14, 2013 + * Author: petera + */ + + +#include "testrunner.h" +#include "test_spiffs.h" +#include "spiffs_nucleus.h" +#include "spiffs.h" +#include +#include +#include +#include +#include + + +SUITE(dev_tests) +static void setup() { + _setup(); +} +static void teardown() { + _teardown(); +} + +TEST(interrupted_write) { + char *name = "interrupt"; + char *name2 = "interrupt2"; + int res; + spiffs_file fd; + + const u32_t sz = SPIFFS_CFG_LOG_PAGE_SZ(FS)*8; + u8_t *buf = malloc(sz); + memrand(buf, sz); + + printf(" create reference file\n"); + fd = SPIFFS_open(FS, name, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(fd > 0); + clear_flash_ops_log(); + res = SPIFFS_write(FS, fd, buf, sz); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + u32_t written = get_flash_ops_log_write_bytes(); + printf(" written bytes: %i\n", written); + + + printf(" create error file\n"); + fd = SPIFFS_open(FS, name2, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0); + TEST_CHECK(fd > 0); + clear_flash_ops_log(); + invoke_error_after_write_bytes(written/2, 0); + res = SPIFFS_write(FS, fd, buf, sz); + SPIFFS_close(FS, fd); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_TEST); + + clear_flash_ops_log(); + +#if SPIFFS_CACHE + // delete all cache + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + + printf(" read error file\n"); + fd = SPIFFS_open(FS, name2, SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + printf(" file size: %i\n", s.size); + + if (s.size > 0) { + u8_t *buf2 = malloc(s.size); + res = SPIFFS_read(FS, fd, buf2, s.size); + TEST_CHECK(res >= 0); + + u32_t ix = 0; + for (ix = 0; ix < s.size; ix += 16) { + int i; + printf(" "); + for (i = 0; i < 16; i++) { + printf("%02x", buf[ix+i]); + } + printf(" "); + for (i = 0; i < 16; i++) { + printf("%02x", buf2[ix+i]); + } + printf("\n"); + } + free(buf2); + } + SPIFFS_close(FS, fd); + + + printf(" FS check\n"); + SPIFFS_check(FS); + + printf(" read error file again\n"); + fd = SPIFFS_open(FS, name2, SPIFFS_APPEND | SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + printf(" file size: %i\n", s.size); + printf(" write file\n"); + res = SPIFFS_write(FS, fd, buf, sz); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + free(buf); + + return TEST_RES_OK; + +} TEST_END + +SUITE_TESTS(dev_tests) + ADD_TEST(interrupted_write) +SUITE_END(dev_tests) diff --git a/components/spiffs/spiffs/src/test/test_hydrogen.c b/components/spiffs/spiffs/src/test/test_hydrogen.c new file mode 100644 index 00000000..52126b2a --- /dev/null +++ b/components/spiffs/spiffs/src/test/test_hydrogen.c @@ -0,0 +1,2507 @@ +/* + * test_suites.c + * + * Created on: Jun 19, 2013 + * Author: petera + */ + + +#include "testrunner.h" +#include "test_spiffs.h" +#include "spiffs_nucleus.h" +#include "spiffs.h" +#include +#include +#include +#include +#include + +SUITE(hydrogen_tests) +static void setup() { + _setup(); +} +static void teardown() { + _teardown(); +} + +TEST(info) +{ + u32_t used, total; + int res = SPIFFS_info(FS, &total, &used); + TEST_CHECK(res == SPIFFS_OK); + TEST_CHECK(used == 0); + TEST_CHECK(total < SPIFFS_CFG_PHYS_SZ(&__fs)); + return TEST_RES_OK; +} +TEST_END + +#if SPIFFS_USE_MAGIC +TEST(magic) +{ + fs_reset_specific(0, 0, 65536*16, 65536, 65536, 256); + SPIFFS_unmount(FS); + + TEST_CHECK_EQ(fs_mount_specific(0, 65536*16, 65536, 65536, 256), SPIFFS_OK); + SPIFFS_unmount(FS); + + TEST_CHECK_NEQ(fs_mount_specific(0, 65536*16, 65536, 65536, 128), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FS); + + TEST_CHECK_NEQ(fs_mount_specific(4, 65536*16, 65536, 65536, 256), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FS); + + return TEST_RES_OK; +} +TEST_END + + +#if SPIFFS_USE_MAGIC_LENGTH +TEST(magic_length) +{ + fs_reset_specific(0, 0, 65536*16, 65536, 65536, 256); + SPIFFS_unmount(FS); + + TEST_CHECK_EQ(fs_mount_specific(0, 65536*16, 65536, 65536, 256), SPIFFS_OK); + SPIFFS_unmount(FS); + + TEST_CHECK_NEQ(fs_mount_specific(0, 65536*8, 65536, 65536, 256), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FS); + + TEST_CHECK_NEQ(fs_mount_specific(0, 65536*15, 65536, 65536, 256), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FS); + + TEST_CHECK_NEQ(fs_mount_specific(0, 65536*17, 65536, 65536, 256), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FS); + + TEST_CHECK_NEQ(fs_mount_specific(0, 65536*256, 65536, 65536, 256), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FS); + + return TEST_RES_OK; +} +TEST_END + +#if SPIFFS_SINGLETON==0 +TEST(magic_length_probe) +{ + fs_reset_specific(0, 0, 65536*16, 65536, 65536, 256); + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 65536*16); + + fs_reset_specific(0, 0, 65536*24, 65536, 65536, 256); + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 65536*24); + + fs_reset_specific(0, 0, 32768*16, 32768, 32768, 128); + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 32768*16); + + fs_reset_specific(0, 0, 16384*37, 16384, 16384, 128); + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 16384*37); + + fs_reset_specific(0, 0, 4096*11, 4096, 4096, 256); + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 4096*11); + + fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256); + __fs.cfg.log_page_size = 128; + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS); + __fs.cfg.log_page_size = 512; + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS); + __fs.cfg.log_page_size = 256; + __fs.cfg.log_block_size = 8192; + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS); + __fs.cfg.log_block_size = 2048; + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS); + __fs.cfg.log_block_size = 4096; + __fs.cfg.phys_addr += 2; + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS); + + fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256); + __fs.cfg.phys_addr += 4096*6; + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS); + + fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256); + area_set(4096*0, 0xff, 4096); // "erase" block 0 + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 4096*8); + + fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256); + area_set(4096*0, 0xff, 4096); // "erase" block 1 + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 4096*8); + + fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256); + area_set(4096*0, 0xff, 4096); // "erase" block 2 + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), 4096*8); + + fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256); + area_set(4096*0, 0xff, 4096*2); // "erase" block 0 & 1 + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS); + + fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256); + area_set(4096*0, 0xff, 4096*2); // "erase" block 0 + area_set(4096*0, 0xff, 4096); // "erase" block 2 + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS); + + fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256); + area_set(4096*1, 0xff, 4096*2); // "erase" block 1 & 2 + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS); + + fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256); + area_set(4096*0, 0xff, 4096*8); // "erase" all + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS); + + fs_reset_specific(0, 0, 4096*8, 4096, 4096, 256); + area_set(4096*0, 0xdd, 4096*8); // garble all + TEST_CHECK_EQ(SPIFFS_probe_fs(&__fs.cfg), SPIFFS_ERR_PROBE_NOT_A_FS); + + return TEST_RES_OK; +} +TEST_END + +#endif // SPIFFS_SINGLETON==0 + +#endif // SPIFFS_USE_MAGIC_LENGTH + +#endif // SPIFFS_USE_MAGIC + +TEST(missing_file) +{ + spiffs_file fd = SPIFFS_open(FS, "this_wont_exist", SPIFFS_RDONLY, 0); + TEST_CHECK(fd < 0); + return TEST_RES_OK; +} +TEST_END + + +TEST(bad_fd) +{ + int res; + spiffs_stat s; + spiffs_file fd = SPIFFS_open(FS, "this_wont_exist", SPIFFS_RDONLY, 0); + TEST_CHECK(fd < 0); + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + res = SPIFFS_read(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); + res = SPIFFS_write(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); +#if SPIFFS_OBJ_META_LEN + u8_t new_meta[SPIFFS_OBJ_META_LEN] = {0}; + res = SPIFFS_fupdate_meta(FS, fd, new_meta); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR); +#endif + return TEST_RES_OK; +} +TEST_END + + +TEST(closed_fd) +{ + int res; + spiffs_stat s; + res = test_create_file("file"); + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd >= 0); + SPIFFS_close(FS, fd); + + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_read(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_write(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); +#if SPIFFS_OBJ_META_LEN + u8_t new_meta[SPIFFS_OBJ_META_LEN] = {0}; + res = SPIFFS_fupdate_meta(FS, fd, new_meta); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); +#endif + return TEST_RES_OK; +} +TEST_END + + +TEST(deleted_same_fd) +{ + int res; + spiffs_stat s; + spiffs_file fd; + res = test_create_file("remove"); + fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res >= 0); + + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_read(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_write(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + + return TEST_RES_OK; +} +TEST_END + + +TEST(deleted_other_fd) +{ + int res; + spiffs_stat s; + spiffs_file fd, fd_orig; + res = test_create_file("remove"); + fd_orig = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0); + TEST_CHECK(fd_orig >= 0); + fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fremove(FS, fd_orig); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd_orig); + + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_read(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + res = SPIFFS_write(FS, fd, 0, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED); + + return TEST_RES_OK; +} +TEST_END + + +TEST(file_by_open) +{ + int res; + spiffs_stat s; + spiffs_file fd = SPIFFS_open(FS, "filebopen", SPIFFS_CREAT, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp((char*)s.name, "filebopen") == 0); + TEST_CHECK(s.size == 0); + SPIFFS_close(FS, fd); + + fd = SPIFFS_open(FS, "filebopen", SPIFFS_RDONLY, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp((char*)s.name, "filebopen") == 0); + TEST_CHECK(s.size == 0); + SPIFFS_close(FS, fd); + return TEST_RES_OK; +} +TEST_END + + +TEST(file_by_creat) +{ + int res; + res = test_create_file("filebcreat"); + TEST_CHECK(res >= 0); + res = SPIFFS_creat(FS, "filebcreat", 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS)==SPIFFS_ERR_CONFLICTING_NAME); + return TEST_RES_OK; +} +TEST_END + +TEST(file_by_open_excl) +{ + int res; + spiffs_stat s; + spiffs_file fd = SPIFFS_open(FS, "filebexcl", SPIFFS_CREAT | SPIFFS_EXCL, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp((char*)s.name, "filebexcl") == 0); + TEST_CHECK(s.size == 0); + SPIFFS_close(FS, fd); + + fd = SPIFFS_open(FS, "filebexcl", SPIFFS_CREAT | SPIFFS_EXCL, 0); + TEST_CHECK(fd < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_EXISTS); + + return TEST_RES_OK; +} +TEST_END + +#if SPIFFS_FILEHDL_OFFSET +TEST(open_fh_offs) +{ + spiffs_file fd1, fd2, fd3; + fd1 = SPIFFS_open(FS, "1", SPIFFS_CREAT | SPIFFS_EXCL, 0); + fd2 = SPIFFS_open(FS, "2", SPIFFS_CREAT | SPIFFS_EXCL, 0); + fd3 = SPIFFS_open(FS, "3", SPIFFS_CREAT | SPIFFS_EXCL, 0); + TEST_CHECK(fd1 >= TEST_SPIFFS_FILEHDL_OFFSET); + TEST_CHECK(fd2 >= TEST_SPIFFS_FILEHDL_OFFSET); + TEST_CHECK(fd3 >= TEST_SPIFFS_FILEHDL_OFFSET); + SPIFFS_close(FS, fd1); + fd1 = SPIFFS_open(FS, "2", SPIFFS_RDONLY, 0); + TEST_CHECK(fd1 >= TEST_SPIFFS_FILEHDL_OFFSET); + SPIFFS_close(FS, fd2); + fd2 = SPIFFS_open(FS, "3", SPIFFS_RDONLY, 0); + TEST_CHECK(fd2 >= TEST_SPIFFS_FILEHDL_OFFSET); + SPIFFS_close(FS, fd3); + fd3 = SPIFFS_open(FS, "1", SPIFFS_RDONLY, 0); + TEST_CHECK(fd3 >= TEST_SPIFFS_FILEHDL_OFFSET); + SPIFFS_close(FS, fd1); + SPIFFS_close(FS, fd2); + SPIFFS_close(FS, fd3); + fd1 = SPIFFS_open(FS, "3", SPIFFS_RDONLY, 0); + TEST_CHECK(fd1 >= TEST_SPIFFS_FILEHDL_OFFSET); + SPIFFS_close(FS, fd1); + fd1 = SPIFFS_open(FS, "foo", SPIFFS_RDONLY, 0); + TEST_CHECK(fd1 < TEST_SPIFFS_FILEHDL_OFFSET); + + return TEST_RES_OK; +} +TEST_END + +#endif //SPIFFS_FILEHDL_OFFSET + +TEST(list_dir) +{ + int res; + + char *files[4] = { + "file1", + "file2", + "file3", + "file4" + }; + int file_cnt = sizeof(files)/sizeof(char *); + + int i; + + for (i = 0; i < file_cnt; i++) { + res = test_create_file(files[i]); + TEST_CHECK(res >= 0); + } + + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + + SPIFFS_opendir(FS, "/", &d); + int found = 0; + while ((pe = SPIFFS_readdir(&d, pe))) { + printf(" %s [%04x] size:%i\n", pe->name, pe->obj_id, pe->size); + for (i = 0; i < file_cnt; i++) { + if (strcmp(files[i], (char *)pe->name) == 0) { + found++; + break; + } + } + { + spiffs_stat s; + TEST_CHECK_EQ(SPIFFS_stat(FS, pe->name, &s), 0); + TEST_CHECK_EQ(pe->obj_id, s.obj_id); + TEST_CHECK_EQ(pe->size, s.size); + TEST_CHECK_EQ(pe->type, s.type); + TEST_CHECK_EQ(pe->pix, s.pix); +#if SPIFFS_OBJ_META_LEN + TEST_CHECK_EQ(memcmp(pe->meta, s.meta, SPIFFS_OBJ_META_LEN), 0); +#endif + } + } + SPIFFS_closedir(&d); + + TEST_CHECK(found == file_cnt); + + return TEST_RES_OK; +} +TEST_END + + +TEST(open_by_dirent) { + int res; + + char *files[4] = { + "file1", + "file2", + "file3", + "file4" + }; + int file_cnt = sizeof(files)/sizeof(char *); + + int i; + int size = SPIFFS_DATA_PAGE_SIZE(FS); + + for (i = 0; i < file_cnt; i++) { + res = test_create_and_write_file(files[i], size, size); + TEST_CHECK(res >= 0); + } + + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + + int found = 0; + SPIFFS_opendir(FS, "/", &d); + while ((pe = SPIFFS_readdir(&d, pe))) { + spiffs_file fd = SPIFFS_open_by_dirent(FS, pe, SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = read_and_verify_fd(fd, (char *)pe->name); + TEST_CHECK(res == SPIFFS_OK); + fd = SPIFFS_open_by_dirent(FS, pe, SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res == SPIFFS_OK); + SPIFFS_close(FS, fd); + found++; + } + SPIFFS_closedir(&d); + + TEST_CHECK(found == file_cnt); + + found = 0; + SPIFFS_opendir(FS, "/", &d); + while ((pe = SPIFFS_readdir(&d, pe))) { + found++; + } + SPIFFS_closedir(&d); + + TEST_CHECK(found == 0); + + return TEST_RES_OK; + +} TEST_END + + +TEST(open_by_page) { + int res; + + char *files[4] = { + "file1", + "file2", + "file3", + "file4" + }; + int file_cnt = sizeof(files)/sizeof(char *); + + int i; + int size = SPIFFS_DATA_PAGE_SIZE(FS); + + for (i = 0; i < file_cnt; i++) { + res = test_create_and_write_file(files[i], size, size); + TEST_CHECK(res >= 0); + } + + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + + int found = 0; + SPIFFS_opendir(FS, "/", &d); + while ((pe = SPIFFS_readdir(&d, pe))) { + spiffs_file fd = SPIFFS_open_by_dirent(FS, pe, SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = read_and_verify_fd(fd, (char *)pe->name); + TEST_CHECK(res == SPIFFS_OK); + fd = SPIFFS_open_by_page(FS, pe->pix, SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res == SPIFFS_OK); + SPIFFS_close(FS, fd); + found++; + } + SPIFFS_closedir(&d); + + TEST_CHECK(found == file_cnt); + + found = 0; + SPIFFS_opendir(FS, "/", &d); + while ((pe = SPIFFS_readdir(&d, pe))) { + found++; + } + SPIFFS_closedir(&d); + + TEST_CHECK(found == 0); + + spiffs_file fd; + // test opening a lookup page + fd = SPIFFS_open_by_page(FS, 0, SPIFFS_RDWR, 0); + TEST_CHECK_LT(fd, 0); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FILE); + // test opening a proper page but not being object index + fd = SPIFFS_open_by_page(FS, SPIFFS_OBJ_LOOKUP_PAGES(FS)+1, SPIFFS_RDWR, 0); + TEST_CHECK_LT(fd, 0); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NOT_A_FILE); + + return TEST_RES_OK; +} TEST_END + + +static struct { + u32_t calls; + spiffs_fileop_type op; + spiffs_obj_id obj_id; + spiffs_page_ix pix; +} ucb; + +void test_cb(spiffs *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix) { + ucb.calls++; + ucb.op = op; + ucb.obj_id = obj_id; + ucb.pix = pix; + //printf("%4i op:%i objid:%04x pix:%04x\n", ucb.calls, ucb.op, ucb.obj_id, ucb.pix); +} + +TEST(user_callback_basic) { + SPIFFS_set_file_callback_func(FS, test_cb); + int res; + memset(&ucb, 0, sizeof(ucb)); + spiffs_file fd = SPIFFS_open(FS, "foo", SPIFFS_CREAT | SPIFFS_APPEND | SPIFFS_RDWR, 0); + TEST_CHECK_GE(fd, 0); + TEST_CHECK_EQ(ucb.calls, 1); + TEST_CHECK_EQ(ucb.op, SPIFFS_CB_CREATED); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK_GE(res, 0); + TEST_CHECK_EQ(ucb.obj_id, s.obj_id); + TEST_CHECK_EQ(ucb.pix, s.pix); + + res = SPIFFS_write(FS, fd, "howdy partner", 14); + TEST_CHECK_GE(res, 0); + res = SPIFFS_fflush(FS, fd); + TEST_CHECK_GE(res, 0); + TEST_CHECK_EQ(ucb.calls, 2); + TEST_CHECK_EQ(ucb.op, SPIFFS_CB_UPDATED); + TEST_CHECK_EQ(ucb.obj_id, s.obj_id); + TEST_CHECK_EQ(ucb.pix, s.pix); + + res = SPIFFS_fremove(FS, fd); + TEST_CHECK_GE(res, 0); + TEST_CHECK_EQ(ucb.calls, 3); + TEST_CHECK_EQ(ucb.op, SPIFFS_CB_DELETED); + TEST_CHECK_EQ(ucb.obj_id, s.obj_id); + TEST_CHECK_EQ(ucb.pix, s.pix); + + return TEST_RES_OK; +} TEST_END + + +TEST(user_callback_gc) { + SPIFFS_set_file_callback_func(FS, test_cb); + + char name[32]; + int f; + int size = SPIFFS_DATA_PAGE_SIZE(FS); + int pages_per_block = SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS); + int files = (pages_per_block-1)/2; + int res; + + // fill block with files + for (f = 0; f < files; f++) { + sprintf(name, "file%i", f); + res = test_create_and_write_file(name, size, 1); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "file%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + // remove all files in block + for (f = 0; f < files; f++) { + sprintf(name, "file%i", f); + res = SPIFFS_remove(FS, name); + TEST_CHECK(res >= 0); + } + + memset(&ucb, 0, sizeof(ucb)); + spiffs_file fd = SPIFFS_open(FS, "foo", SPIFFS_CREAT | SPIFFS_APPEND | SPIFFS_RDWR, 0); + TEST_CHECK_GE(fd, 0); + TEST_CHECK_EQ(ucb.calls, 1); + TEST_CHECK_EQ(ucb.op, SPIFFS_CB_CREATED); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK_GE(res, 0); + TEST_CHECK_EQ(ucb.obj_id, s.obj_id); + TEST_CHECK_EQ(ucb.pix, s.pix); + + res = SPIFFS_write(FS, fd, "howdy partner", 14); + TEST_CHECK_GE(res, 0); + res = SPIFFS_fflush(FS, fd); + TEST_CHECK_GE(res, 0); + TEST_CHECK_EQ(ucb.calls, 2); + TEST_CHECK_EQ(ucb.op, SPIFFS_CB_UPDATED); + TEST_CHECK_EQ(ucb.obj_id, s.obj_id); + TEST_CHECK_EQ(ucb.pix, s.pix); + + u32_t tot, us; + SPIFFS_info(FS, &tot, &us); + + // do a hard gc, should move our file + res = SPIFFS_gc(FS, tot-us*2); + TEST_CHECK_GE(res, 0); + + TEST_CHECK_GE(res, 0); + TEST_CHECK_EQ(ucb.calls, 3); + TEST_CHECK_EQ(ucb.op, SPIFFS_CB_UPDATED); + TEST_CHECK_EQ(ucb.obj_id, s.obj_id); + TEST_CHECK_NEQ(ucb.pix, s.pix); + + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK_GE(res, 0); + TEST_CHECK_EQ(ucb.pix, s.pix); + + res = SPIFFS_fremove(FS, fd); + TEST_CHECK_GE(res, 0); + TEST_CHECK_EQ(ucb.calls, 4); + TEST_CHECK_EQ(ucb.op, SPIFFS_CB_DELETED); + TEST_CHECK_EQ(ucb.obj_id, s.obj_id); + TEST_CHECK_EQ(ucb.pix, s.pix); + + return TEST_RES_OK; +} TEST_END + + +TEST(name_too_long) { + char name[SPIFFS_OBJ_NAME_LEN*2]; + memset(name, 0, sizeof(name)); + int i; + for (i = 0; i < SPIFFS_OBJ_NAME_LEN; i++) { + name[i] = 'A'; + } + + TEST_CHECK_LT(SPIFFS_creat(FS, name, 0), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NAME_TOO_LONG); + + TEST_CHECK_LT(SPIFFS_open(FS, name, SPIFFS_CREAT | SPIFFS_TRUNC, 0), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NAME_TOO_LONG); + + TEST_CHECK_LT(SPIFFS_remove(FS, name), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NAME_TOO_LONG); + + spiffs_stat s; + TEST_CHECK_LT(SPIFFS_stat(FS, name, &s), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NAME_TOO_LONG); + + TEST_CHECK_LT(SPIFFS_rename(FS, name, "a"), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NAME_TOO_LONG); + + TEST_CHECK_LT(SPIFFS_rename(FS, "a", name), SPIFFS_OK); + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_NAME_TOO_LONG); + + return TEST_RES_OK; +} TEST_END + + +TEST(rename) { + int res; + + char *src_name = "baah"; + char *dst_name = "booh"; + char *dst_name2 = "beeh"; + int size = SPIFFS_DATA_PAGE_SIZE(FS); + + res = test_create_and_write_file(src_name, size, size); + TEST_CHECK(res >= 0); + + res = SPIFFS_rename(FS, src_name, dst_name); + TEST_CHECK(res >= 0); + + res = SPIFFS_rename(FS, dst_name, dst_name); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_CONFLICTING_NAME); + + res = SPIFFS_rename(FS, src_name, dst_name2); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + return TEST_RES_OK; +} TEST_END + +#if SPIFFS_OBJ_META_LEN +TEST(update_meta) { + s32_t i, res, fd; + spiffs_stat s; + u8_t new_meta[SPIFFS_OBJ_META_LEN], new_meta2[SPIFFS_OBJ_META_LEN]; + + res = test_create_file("foo"); + TEST_CHECK(res >= 0); + + for (i = 0; i < SPIFFS_OBJ_META_LEN; i++) { + new_meta[i] = 0xaa; + } + res = SPIFFS_update_meta(FS, "foo", new_meta); + TEST_CHECK(res >= 0); + + res = SPIFFS_stat(FS, "foo", &s); + TEST_CHECK(res >= 0); + TEST_CHECK_EQ(memcmp(s.meta, new_meta, SPIFFS_OBJ_META_LEN), 0); + + for (i = 0; i < SPIFFS_OBJ_META_LEN; i++) { + new_meta2[i] = 0xbb; + } + + fd = SPIFFS_open(FS, "foo", SPIFFS_RDONLY, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fupdate_meta(FS, fd, new_meta2); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_WRITABLE); + SPIFFS_close(FS, fd); + + res = SPIFFS_stat(FS, "foo", &s); + TEST_CHECK(res >= 0); + TEST_CHECK_EQ(memcmp(s.meta, new_meta, SPIFFS_OBJ_META_LEN), 0); + + fd = SPIFFS_open(FS, "foo", SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fupdate_meta(FS, fd, new_meta2); + TEST_CHECK_EQ(res, 0); + SPIFFS_close(FS, fd); + + res = SPIFFS_stat(FS, "foo", &s); + TEST_CHECK(res >= 0); + TEST_CHECK_EQ(memcmp(s.meta, new_meta2, SPIFFS_OBJ_META_LEN), 0); + + return TEST_RES_OK; +} TEST_END +#endif + +TEST(remove_single_by_path) +{ + int res; + spiffs_file fd; + res = test_create_file("remove"); + TEST_CHECK(res >= 0); + res = SPIFFS_remove(FS, "remove"); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, "remove", SPIFFS_RDONLY, 0); + TEST_CHECK(fd < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + return TEST_RES_OK; +} +TEST_END + + +TEST(remove_single_by_fd) +{ + int res; + spiffs_file fd; + res = test_create_file("remove"); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + fd = SPIFFS_open(FS, "remove", SPIFFS_RDONLY, 0); + TEST_CHECK(fd < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + return TEST_RES_OK; +} +TEST_END + + +TEST(write_cache) +{ + int res; + spiffs_file fd; + u8_t buf[1024*8]; + u8_t fbuf[1024*8]; + res = test_create_file("f"); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, "f", SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + memrand(buf, sizeof(buf)); + res = SPIFFS_write(FS, fd, buf, SPIFFS_CFG_LOG_PAGE_SZ(FS)/2); + TEST_CHECK(res >= 0); + res = SPIFFS_write(FS, fd, buf, SPIFFS_CFG_LOG_PAGE_SZ(FS)*2); + TEST_CHECK(res >= 0); + res = SPIFFS_close(FS, fd); + TEST_CHECK(res >= 0); + + fd = SPIFFS_open(FS, "f", SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_read(FS, fd, fbuf, SPIFFS_CFG_LOG_PAGE_SZ(FS)/2 + SPIFFS_CFG_LOG_PAGE_SZ(FS)*2); + TEST_CHECK(res >= 0); + TEST_CHECK(0 == memcmp(&buf[0], &fbuf[0], SPIFFS_CFG_LOG_PAGE_SZ(FS)/2)); + TEST_CHECK(0 == memcmp(&buf[0], &fbuf[SPIFFS_CFG_LOG_PAGE_SZ(FS)/2], SPIFFS_CFG_LOG_PAGE_SZ(FS)*2)); + res = SPIFFS_close(FS, fd); + TEST_CHECK(res >= 0); + + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK); + + return TEST_RES_OK; +} +TEST_END + + +TEST(write_big_file_chunks_page) +{ + int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/100); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, SPIFFS_DATA_PAGE_SIZE(FS)); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END + + +TEST(write_big_files_chunks_page) +{ + char name[32]; + int f; + int files = 10; + int res; + int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/100)/files; + printf(" filesize %i\n", size); + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = test_create_and_write_file(name, size, SPIFFS_DATA_PAGE_SIZE(FS)); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END + + +TEST(write_big_file_chunks_index) +{ + int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/100); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, SPIFFS_DATA_PAGE_SIZE(FS) * SPIFFS_OBJ_HDR_IX_LEN(FS)); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END + + +TEST(write_big_files_chunks_index) +{ + char name[32]; + int f; + int files = 10; + int res; + int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/100)/files; + printf(" filesize %i\n", size); + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = test_create_and_write_file(name, size, SPIFFS_DATA_PAGE_SIZE(FS) * SPIFFS_OBJ_HDR_IX_LEN(FS)); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END + + +TEST(write_big_file_chunks_huge) +{ + int size = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END + + +TEST(write_big_files_chunks_huge) +{ + char name[32]; + int f; + int files = 10; + int res; + int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/100)/files; + printf(" filesize %i\n", size); + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = test_create_and_write_file(name, size, size); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END + + +TEST(truncate_big_file) +{ + int size = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + spiffs_file fd = SPIFFS_open(FS, "bigfile", SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + res = SPIFFS_fremove(FS, fd); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + fd = SPIFFS_open(FS, "bigfile", SPIFFS_RDWR, 0); + TEST_CHECK(fd < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND); + + return TEST_RES_OK; +} +TEST_END + + +TEST(simultaneous_write) { + int res = SPIFFS_creat(FS, "simul1", 0); + TEST_CHECK(res >= 0); + + spiffs_file fd1 = SPIFFS_open(FS, "simul1", SPIFFS_RDWR, 0); + TEST_CHECK(fd1 > 0); + spiffs_file fd2 = SPIFFS_open(FS, "simul1", SPIFFS_RDWR, 0); + TEST_CHECK(fd2 > 0); + spiffs_file fd3 = SPIFFS_open(FS, "simul1", SPIFFS_RDWR, 0); + TEST_CHECK(fd3 > 0); + + u8_t data1 = 1; + u8_t data2 = 2; + u8_t data3 = 3; + + res = SPIFFS_write(FS, fd1, &data1, 1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd1); + res = SPIFFS_write(FS, fd2, &data2, 1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd2); + res = SPIFFS_write(FS, fd3, &data3, 1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd3); + + spiffs_stat s; + res = SPIFFS_stat(FS, "simul1", &s); + TEST_CHECK(res >= 0); + + TEST_CHECK(s.size == 1); + + u8_t rdata; + spiffs_file fd = SPIFFS_open(FS, "simul1", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + res = SPIFFS_read(FS, fd, &rdata, 1); + TEST_CHECK(res >= 0); + + TEST_CHECK(rdata == data3); + + return TEST_RES_OK; +} +TEST_END + + +TEST(simultaneous_write_append) { + int res = SPIFFS_creat(FS, "simul2", 0); + TEST_CHECK(res >= 0); + + spiffs_file fd1 = SPIFFS_open(FS, "simul2", SPIFFS_RDWR | SPIFFS_APPEND, 0); + TEST_CHECK(fd1 > 0); + spiffs_file fd2 = SPIFFS_open(FS, "simul2", SPIFFS_RDWR | SPIFFS_APPEND, 0); + TEST_CHECK(fd2 > 0); + spiffs_file fd3 = SPIFFS_open(FS, "simul2", SPIFFS_RDWR | SPIFFS_APPEND, 0); + TEST_CHECK(fd3 > 0); + + u8_t data1 = 1; + u8_t data2 = 2; + u8_t data3 = 3; + + res = SPIFFS_write(FS, fd1, &data1, 1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd1); + res = SPIFFS_write(FS, fd2, &data2, 1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd2); + res = SPIFFS_write(FS, fd3, &data3, 1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd3); + + spiffs_stat s; + res = SPIFFS_stat(FS, "simul2", &s); + TEST_CHECK(res >= 0); + + TEST_CHECK(s.size == 3); + + u8_t rdata[3]; + spiffs_file fd = SPIFFS_open(FS, "simul2", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + res = SPIFFS_read(FS, fd, &rdata, 3); + TEST_CHECK(res >= 0); + + TEST_CHECK(rdata[0] == data1); + TEST_CHECK(rdata[1] == data2); + TEST_CHECK(rdata[2] == data3); + + return TEST_RES_OK; +} +TEST_END + +TEST(file_uniqueness) +{ + int res; + spiffs_file fd; + char fname[32]; + int files = ((SPIFFS_CFG_PHYS_SZ(FS) * 75) / 100) / 2 / SPIFFS_CFG_LOG_PAGE_SZ(FS); + //(FS_PURE_DATA_PAGES(FS) / 2) - SPIFFS_PAGES_PER_BLOCK(FS)*8; + int i; + printf(" creating %i files\n", files); + for (i = 0; i < files; i++) { + char content[20]; + sprintf(fname, "file%i", i); + sprintf(content, "%i", i); + res = test_create_file(fname); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, fname, SPIFFS_APPEND | SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_write(FS, fd, content, strlen(content)+1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + } + printf(" checking %i files\n", files); + for (i = 0; i < files; i++) { + char content[20]; + char ref_content[20]; + sprintf(fname, "file%i", i); + sprintf(content, "%i", i); + fd = SPIFFS_open(FS, fname, SPIFFS_RDONLY, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_read(FS, fd, ref_content, strlen(content)+1); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp(ref_content, content) == 0); + SPIFFS_close(FS, fd); + } + printf(" removing %i files\n", files/2); + for (i = 0; i < files; i += 2) { + sprintf(fname, "file%i", i); + res = SPIFFS_remove(FS, fname); + TEST_CHECK(res >= 0); + } + printf(" creating %i files\n", files/2); + for (i = 0; i < files; i += 2) { + char content[20]; + sprintf(fname, "file%i", i); + sprintf(content, "new%i", i); + res = test_create_file(fname); + TEST_CHECK(res >= 0); + fd = SPIFFS_open(FS, fname, SPIFFS_APPEND | SPIFFS_RDWR, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_write(FS, fd, content, strlen(content)+1); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + } + printf(" checking %i files\n", files); + for (i = 0; i < files; i++) { + char content[20]; + char ref_content[20]; + sprintf(fname, "file%i", i); + if ((i & 1) == 0) { + sprintf(content, "new%i", i); + } else { + sprintf(content, "%i", i); + } + fd = SPIFFS_open(FS, fname, SPIFFS_RDONLY, 0); + TEST_CHECK(fd >= 0); + res = SPIFFS_read(FS, fd, ref_content, strlen(content)+1); + TEST_CHECK(res >= 0); + TEST_CHECK(strcmp(ref_content, content) == 0); + SPIFFS_close(FS, fd); + } + + return TEST_RES_OK; +} +TEST_END + +int create_and_read_back(int size, int chunk) { + char *name = "file"; + spiffs_file fd; + s32_t res; + + u8_t *buf = malloc(size); + memrand(buf, size); + + res = test_create_file(name); + CHECK(res >= 0); + fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0); + CHECK(fd >= 0); + res = SPIFFS_write(FS, fd, buf, size); + CHECK(res >= 0); + + spiffs_stat stat; + res = SPIFFS_fstat(FS, fd, &stat); + CHECK(res >= 0); + CHECK(stat.size == size); + + SPIFFS_close(FS, fd); + + fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0); + CHECK(fd >= 0); + + u8_t *rbuf = malloc(size); + int offs = 0; + while (offs < size) { + int len = MIN(size - offs, chunk); + res = SPIFFS_read(FS, fd, &rbuf[offs], len); + CHECK(res >= 0); + CHECK(memcmp(&rbuf[offs], &buf[offs], len) == 0); + + offs += chunk; + } + + CHECK(memcmp(&rbuf[0], &buf[0], size) == 0); + + SPIFFS_close(FS, fd); + + free(rbuf); + free(buf); + + return 0; +} + +TEST(read_chunk_1) +{ + TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*8, 1) == 0); + return TEST_RES_OK; +} +TEST_END + + +TEST(read_chunk_page) +{ + TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))*2, + SPIFFS_DATA_PAGE_SIZE(FS)) == 0); + return TEST_RES_OK; +} +TEST_END + + +TEST(read_chunk_index) +{ + TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))*4, + SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))) == 0); + return TEST_RES_OK; +} +TEST_END + + +TEST(read_chunk_huge) +{ + int sz = (2*SPIFFS_CFG_PHYS_SZ(FS))/3; + TEST_CHECK(create_and_read_back(sz, sz) == 0); + return TEST_RES_OK; +} +TEST_END + + +TEST(read_beyond) +{ + char *name = "file"; + spiffs_file fd; + s32_t res; + u32_t size = SPIFFS_DATA_PAGE_SIZE(FS)*2; + + u8_t *buf = malloc(size); + memrand(buf, size); + + res = test_create_file(name); + CHECK(res >= 0); + fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0); + CHECK(fd >= 0); + res = SPIFFS_write(FS, fd, buf, size); + CHECK(res >= 0); + + spiffs_stat stat; + res = SPIFFS_fstat(FS, fd, &stat); + CHECK(res >= 0); + CHECK(stat.size == size); + + SPIFFS_close(FS, fd); + + fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0); + CHECK(fd >= 0); + + u8_t *rbuf = malloc(size+10); + res = SPIFFS_read(FS, fd, rbuf, size+10); + + SPIFFS_close(FS, fd); + + free(rbuf); + free(buf); + + TEST_CHECK(res == size); + + return TEST_RES_OK; +} +TEST_END + +TEST(read_beyond2) +{ + char *name = "file"; + spiffs_file fd; + s32_t res; + const s32_t size = SPIFFS_DATA_PAGE_SIZE(FS); + + u8_t buf[size*2]; + memrand(buf, size); + + res = test_create_file(name); + CHECK(res >= 0); + fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0); + CHECK(fd >= 0); + res = SPIFFS_write(FS, fd, buf, size); + CHECK(res >= 0); + + spiffs_stat stat; + res = SPIFFS_fstat(FS, fd, &stat); + CHECK(res >= 0); + CHECK(stat.size == size); + + SPIFFS_close(FS, fd); + + int i,j; + for (j = 1; j <= size+1; j++) { + fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0); + CHECK(fd >= 0); + SPIFFS_clearerr(FS); + for (i = 0; i < size * 2; i += j) { + u8_t dst; + res = SPIFFS_read(FS, fd, buf, j); + TEST_CHECK_EQ(SPIFFS_errno(FS), i < size ? SPIFFS_OK : SPIFFS_ERR_END_OF_OBJECT); + TEST_CHECK_EQ(res, MIN(j, MAX(0, size - (i + j) + j))); + } + SPIFFS_close(FS, fd); + } + + return TEST_RES_OK; +} +TEST_END + + +TEST(bad_index_1) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify object index, find object index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + // set object index entry 2 to a bad page, free + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 2 * sizeof(spiffs_page_ix); + spiffs_page_ix bad_pix_ref = (spiffs_page_ix)-1; + area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + +#if SPIFFS_CACHE + // delete all cache + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + res = read_and_verify("file"); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_INDEX_REF_FREE); + + return TEST_RES_OK; +} TEST_END + + +TEST(bad_index_2) { + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + int res = test_create_and_write_file("file", size, size); + TEST_CHECK(res >= 0); + res = read_and_verify("file"); + TEST_CHECK(res >= 0); + + spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0); + TEST_CHECK(fd > 0); + spiffs_stat s; + res = SPIFFS_fstat(FS, fd, &s); + TEST_CHECK(res >= 0); + SPIFFS_close(FS, fd); + + // modify object index, find object index header + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + TEST_CHECK(res >= 0); + + // set object index entry 2 to a bad page, lu + u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 2 * sizeof(spiffs_page_ix); + spiffs_page_ix bad_pix_ref = SPIFFS_OBJ_LOOKUP_PAGES(FS)-1; + area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix)); + +#if SPIFFS_CACHE + // delete all cache + spiffs_cache *cache = spiffs_get_cache(FS); + cache->cpage_use_map = 0; +#endif + + res = read_and_verify("file"); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_INDEX_REF_LU); + + return TEST_RES_OK; +} TEST_END + + +TEST(lseek_simple_modification) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int len = 4096; + fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + u8_t *buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + res = SPIFFS_lseek(FS, fd, len/2, SPIFFS_SEEK_SET); + TEST_CHECK(res >= 0); + lseek(pfd, len/2, SEEK_SET); + len = len/4; + buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + SPIFFS_close(FS, fd); + close(pfd); + + return TEST_RES_OK; +} +TEST_END + + +TEST(lseek_modification_append) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int len = 4096; + fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + u8_t *buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + res = SPIFFS_lseek(FS, fd, len/2, SPIFFS_SEEK_SET); + TEST_CHECK(res >= 0); + lseek(pfd, len/2, SEEK_SET); + + buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + SPIFFS_close(FS, fd); + close(pfd); + + return TEST_RES_OK; +} +TEST_END + + +TEST(lseek_modification_append_multi) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int len = 1024; + int runs = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS) / (len/2); + + fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + u8_t *buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + + while (runs--) { + res = SPIFFS_lseek(FS, fd, -len/2, SPIFFS_SEEK_END); + TEST_CHECK(res >= 0); + lseek(pfd, -len/2, SEEK_END); + + buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + TEST_CHECK(res >= 0); + write(pfd, buf, len); + free(buf); + + res = read_and_verify(fname); + TEST_CHECK(res >= 0); + } + + SPIFFS_close(FS, fd); + close(pfd); + + return TEST_RES_OK; +} +TEST_END + + +TEST(lseek_read) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int len = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS); + int runs = 100000; + + fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + u8_t *refbuf = malloc(len); + memrand(refbuf, len); + res = SPIFFS_write(FS, fd, refbuf, len); + TEST_CHECK(res >= 0); + + int offs = 0; + res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_SET); + TEST_CHECK(res >= 0); + + while (runs--) { + int i; + u8_t buf[64]; + if (offs + 41 + sizeof(buf) >= len) { + offs = (offs + 41 + sizeof(buf)) % len; + res = SPIFFS_lseek(FS, fd, offs, SPIFFS_SEEK_SET); + TEST_CHECK(res >= 0); + } + res = SPIFFS_lseek(FS, fd, 41, SPIFFS_SEEK_CUR); + TEST_CHECK(res >= 0); + offs += 41; + res = SPIFFS_read(FS, fd, buf, sizeof(buf)); + TEST_CHECK(res >= 0); + for (i = 0; i < sizeof(buf); i++) { + if (buf[i] != refbuf[offs+i]) { + printf(" mismatch at offs %i\n", offs); + } + TEST_CHECK(buf[i] == refbuf[offs+i]); + } + offs += sizeof(buf); + + res = SPIFFS_lseek(FS, fd, -((u32_t)sizeof(buf)+11), SPIFFS_SEEK_CUR); + TEST_CHECK(res >= 0); + offs -= (sizeof(buf)+11); + res = SPIFFS_read(FS, fd, buf, sizeof(buf)); + TEST_CHECK(res >= 0); + for (i = 0; i < sizeof(buf); i++) { + if (buf[i] != refbuf[offs+i]) { + printf(" mismatch at offs %i\n", offs); + } + TEST_CHECK(buf[i] == refbuf[offs+i]); + } + offs += sizeof(buf); + } + + free(refbuf); + SPIFFS_close(FS, fd); + + return TEST_RES_OK; +} +TEST_END + + + +TEST(lseek_oob) { + int res; + spiffs_file fd; + char *fname = "seekfile"; + int len = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS); + + fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + TEST_CHECK(fd > 0); + u8_t *refbuf = malloc(len); + memrand(refbuf, len); + res = SPIFFS_write(FS, fd, refbuf, len); + TEST_CHECK(res >= 0); + + int offs = 0; + res = SPIFFS_lseek(FS, fd, -1, SPIFFS_SEEK_SET); + TEST_CHECK_EQ(res, SPIFFS_ERR_SEEK_BOUNDS); + res = SPIFFS_lseek(FS, fd, len+1, SPIFFS_SEEK_SET); + TEST_CHECK_EQ(res, SPIFFS_ERR_END_OF_OBJECT); + free(refbuf); + SPIFFS_close(FS, fd); + + return TEST_RES_OK; +} +TEST_END + + +TEST(gc_quick) +{ + char name[32]; + int f; + int size = SPIFFS_DATA_PAGE_SIZE(FS); + int pages_per_block=SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS); + int files = (pages_per_block+1)/2; + int res; + + // negative, try quick gc on clean sys + res = SPIFFS_gc_quick(FS, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NO_DELETED_BLOCKS); + + // fill block with files + for (f = 0; f < files; f++) { + sprintf(name, "file%i", f); + res = test_create_and_write_file(name, size, 1); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "file%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + // remove all files in block + for (f = 0; f < files; f++) { + sprintf(name, "file%i", f); + res = SPIFFS_remove(FS, name); + TEST_CHECK(res >= 0); + } + + // do a quick gc + res = SPIFFS_gc_quick(FS, 0); + TEST_CHECK(res >= 0); + + // fill another block with files but two pages + // We might have one deleted page left over from the previous gc, in case pages_per_block is odd. + int pages_already=2*files-pages_per_block; + int files2=(pages_per_block-pages_already+1)/2; + + for (f = 0; f < files2 - 1; f++) { + sprintf(name, "file%i", f); + res = test_create_and_write_file(name, size, 1); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files2 - 1; f++) { + sprintf(name, "file%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + // remove all files in block leaving two free pages in block + for (f = 0; f < files2 - 1; f++) { + sprintf(name, "file%i", f); + res = SPIFFS_remove(FS, name); + TEST_CHECK(res >= 0); + } + + // negative, try quick gc where no fully deleted blocks exist + res = SPIFFS_gc_quick(FS, 0); + TEST_CHECK(res < 0); + TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NO_DELETED_BLOCKS); + + // positive, try quick gc where allowing two free pages + res = SPIFFS_gc_quick(FS, 2); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END + + +TEST(write_small_file_chunks_1) +{ + int res = test_create_and_write_file("smallfile", 256, 1); + TEST_CHECK(res >= 0); + res = read_and_verify("smallfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END + + +TEST(write_small_files_chunks_1) +{ + char name[32]; + int f; + int size = 512; + int files = ((20*SPIFFS_CFG_PHYS_SZ(FS))/100)/size; + int res; + for (f = 0; f < files; f++) { + sprintf(name, "smallfile%i", f); + res = test_create_and_write_file(name, size, 1); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "smallfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END + +TEST(write_big_file_chunks_1) +{ + int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/100); + printf(" filesize %i\n", size); + int res = test_create_and_write_file("bigfile", size, 1); + TEST_CHECK(res >= 0); + res = read_and_verify("bigfile"); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END + +TEST(write_big_files_chunks_1) +{ + char name[32]; + int f; + int files = 10; + int res; + int size = ((50*SPIFFS_CFG_PHYS_SZ(FS))/100)/files; + printf(" filesize %i\n", size); + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = test_create_and_write_file(name, size, 1); + TEST_CHECK(res >= 0); + } + for (f = 0; f < files; f++) { + sprintf(name, "bigfile%i", f); + res = read_and_verify(name); + TEST_CHECK(res >= 0); + } + + return TEST_RES_OK; +} +TEST_END + + +TEST(long_run_config_many_small_one_long) +{ + tfile_conf cfgs[] = { + { .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = LONG + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = LONG + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = LONG + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = LONG + }, + }; + + int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 206, 5, 0); + TEST_CHECK(res >= 0); + return TEST_RES_OK; +} +TEST_END + +TEST(long_run_config_many_medium) +{ + tfile_conf cfgs[] = { + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG + }, + }; + + int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 305, 5, 0); + TEST_CHECK(res >= 0); + return TEST_RES_OK; +} +TEST_END + + +TEST(long_run_config_many_small) +{ + tfile_conf cfgs[] = { + { .tsize = SMALL, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = LONG + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = NORMAL + }, + { .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT + }, + }; + + int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 115, 6, 0); + TEST_CHECK(res >= 0); + return TEST_RES_OK; +} +TEST_END + + +TEST(long_run) +{ + tfile_conf cfgs[] = { + { .tsize = EMPTY, .ttype = APPENDED, .tlife = NORMAL + }, + { .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT + }, + { .tsize = MEDIUM, .ttype = MODIFIED, .tlife = SHORT + }, + { .tsize = MEDIUM, .ttype = APPENDED, .tlife = SHORT + }, + }; + + int macro_runs = 500; + printf(" "); + u32_t clob_size = SPIFFS_CFG_PHYS_SZ(FS)/4; + int res = test_create_and_write_file("long_clobber", clob_size, clob_size); + TEST_CHECK(res >= 0); + + res = read_and_verify("long_clobber"); + TEST_CHECK(res >= 0); + + while (macro_runs--) { + //printf(" ---- run %i ----\n", macro_runs); + if ((macro_runs % 20) == 0) { + printf("."); + fflush(stdout); + } + res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 20, 2, 0); + TEST_CHECK(res >= 0); + } + printf("\n"); + + res = read_and_verify("long_clobber"); + TEST_CHECK(res >= 0); + + res = SPIFFS_check(FS); + TEST_CHECK(res >= 0); + + return TEST_RES_OK; +} +TEST_END + +#if SPIFFS_IX_MAP +TEST(ix_map_basic) +{ + // create a scattered file + s32_t res; + spiffs_file fd1, fd2; + fd1 = SPIFFS_open(FS, "1", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0); + TEST_CHECK_GT(fd1, 0); + fd2 = SPIFFS_open(FS, "2", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0); + TEST_CHECK_GT(fd2, 0); + + u8_t buf[SPIFFS_DATA_PAGE_SIZE(FS)]; + int i; + for (i = 0; i < SPIFFS_CFG_PHYS_SZ(FS) / 4 / SPIFFS_DATA_PAGE_SIZE(FS); i++) { + memrand(buf, sizeof(buf)); + res = SPIFFS_write(FS, fd1, buf, sizeof(buf)); + TEST_CHECK_GE(res, SPIFFS_OK); + memrand(buf, sizeof(buf)); + res = SPIFFS_write(FS, fd2, buf, sizeof(buf)); + TEST_CHECK_GE(res, SPIFFS_OK); + } + res = SPIFFS_close(FS, fd1); + TEST_CHECK_GE(res, SPIFFS_OK); + res = SPIFFS_close(FS, fd2); + TEST_CHECK_GE(res, SPIFFS_OK); + + res = SPIFFS_remove(FS, "2"); + TEST_CHECK_GE(res, SPIFFS_OK); + + spiffs_stat s; + res = SPIFFS_stat(FS, "1", &s); + TEST_CHECK_GE(res, SPIFFS_OK); + u32_t size = s.size; + + printf("file created, size: %i..\n", size); + + fd1 = SPIFFS_open(FS, "1", SPIFFS_O_RDONLY, 0); + TEST_CHECK_GT(fd1, 0); + printf(".. corresponding pix entries: %i\n", SPIFFS_bytes_to_ix_map_entries(FS, size)); + + u8_t rd_buf[SPIFFS_CFG_LOG_PAGE_SZ(FS)]; + + fd1 = SPIFFS_open(FS, "1", SPIFFS_O_RDONLY, 0); + TEST_CHECK_GT(fd1, 0); + + clear_flash_ops_log(); + + printf("reading file without memory mapped index\n"); + while ((res = SPIFFS_read(FS, fd1, rd_buf, sizeof(rd_buf))) == sizeof(rd_buf)); + TEST_CHECK_GT(res, SPIFFS_OK); + + res = SPIFFS_OK; + + u32_t reads_without_ixmap = get_flash_ops_log_read_bytes(); + dump_flash_access_stats(); + + u32_t crc_non_map_ix = get_spiffs_file_crc_by_fd(fd1); + + TEST_CHECK_EQ(SPIFFS_close(FS, fd1), SPIFFS_OK); + + + printf("reading file with memory mapped index\n"); + spiffs_ix_map map; + spiffs_page_ix ixbuf[SPIFFS_bytes_to_ix_map_entries(FS, size)]; + + fd1 = SPIFFS_open(FS, "1", SPIFFS_O_RDONLY, 0); + TEST_CHECK_GT(fd1, 0); + + // map index to memory + res = SPIFFS_ix_map(FS, fd1, &map, 0, size, ixbuf); + TEST_CHECK_GE(res, SPIFFS_OK); + + clear_flash_ops_log(); + + while ((res = SPIFFS_read(FS, fd1, rd_buf, sizeof(rd_buf))) == sizeof(rd_buf)); + TEST_CHECK_GT(res, SPIFFS_OK); + u32_t reads_with_ixmap_pass1 = get_flash_ops_log_read_bytes(); + + dump_flash_access_stats(); + + u32_t crc_map_ix_pass1 = get_spiffs_file_crc_by_fd(fd1); + + TEST_CHECK_LT(reads_with_ixmap_pass1, reads_without_ixmap); + + TEST_CHECK_EQ(crc_non_map_ix, crc_map_ix_pass1); + + spiffs_page_ix ref_ixbuf[SPIFFS_bytes_to_ix_map_entries(FS, size)]; + memcpy(ref_ixbuf, ixbuf, sizeof(ixbuf)); + + // force a gc by creating small files until full, reordering the index + printf("forcing gc, error ERR_FULL %i expected\n", SPIFFS_ERR_FULL); + res = SPIFFS_OK; + u32_t ix = 10; + while (res == SPIFFS_OK) { + char name[32]; + sprintf(name, "%i", ix); + res = test_create_and_write_file(name, SPIFFS_CFG_LOG_BLOCK_SZ(FS), SPIFFS_CFG_LOG_BLOCK_SZ(FS)); + ix++; + } + + TEST_CHECK_EQ(SPIFFS_errno(FS), SPIFFS_ERR_FULL); + + // make sure the map array was altered + TEST_CHECK_NEQ(0, memcmp(ref_ixbuf, ixbuf, sizeof(ixbuf))); + + TEST_CHECK_GE(SPIFFS_lseek(FS, fd1, 0, SPIFFS_SEEK_SET), SPIFFS_OK); + + clear_flash_ops_log(); + while ((res = SPIFFS_read(FS, fd1, rd_buf, sizeof(rd_buf))) == sizeof(rd_buf)); + TEST_CHECK_GT(res, SPIFFS_OK); + u32_t reads_with_ixmap_pass2 = get_flash_ops_log_read_bytes(); + + TEST_CHECK_EQ(reads_with_ixmap_pass1, reads_with_ixmap_pass2); + + u32_t crc_map_ix_pass2 = get_spiffs_file_crc_by_fd(fd1); + + TEST_CHECK_EQ(crc_map_ix_pass1, crc_map_ix_pass2); + + TEST_CHECK_EQ(SPIFFS_close(FS, fd1), SPIFFS_OK); + + return TEST_RES_OK; +} +TEST_END + +TEST(ix_map_remap) +{ + // create a file, 10 data pages long + s32_t res; + spiffs_file fd1; + fd1 = SPIFFS_open(FS, "1", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0); + TEST_CHECK_GT(fd1, 0); + + const int size_pages = 10; + + u8_t buf[SPIFFS_DATA_PAGE_SIZE(FS)]; + int i; + for (i = 0; i < size_pages; i++) { + memrand(buf, sizeof(buf)); + res = SPIFFS_write(FS, fd1, buf, sizeof(buf)); + TEST_CHECK_GE(res, SPIFFS_OK); + } + res = SPIFFS_close(FS, fd1); + TEST_CHECK_GE(res, SPIFFS_OK); + + spiffs_stat s; + res = SPIFFS_stat(FS, "1", &s); + TEST_CHECK_GE(res, SPIFFS_OK); + u32_t size = s.size; + + printf("file created, size: %i..\n", size); + + fd1 = SPIFFS_open(FS, "1", SPIFFS_O_RDONLY, 0); + TEST_CHECK_GT(fd1, 0); + printf(".. corresponding pix entries: %i\n", SPIFFS_bytes_to_ix_map_entries(FS, size) + 1); + TEST_CHECK_EQ(SPIFFS_bytes_to_ix_map_entries(FS, size), size_pages + 1); + + // map index to memory + // move around, check validity + const int entries = SPIFFS_bytes_to_ix_map_entries(FS, size/2); + spiffs_ix_map map; + // add one extra for stack safeguarding + spiffs_page_ix ixbuf[entries+1]; + spiffs_page_ix ixbuf_ref[entries+1]; + const spiffs_page_ix canary = (spiffs_page_ix)0x87654321; + memset(ixbuf, 0xee, sizeof(ixbuf)); + ixbuf[entries] = canary; + + res = SPIFFS_ix_map(FS, fd1, &map, 0, size/2, ixbuf); + TEST_CHECK_GE(res, SPIFFS_OK); + + for (i = 0; i < entries; i++) { + printf("%04x ", ixbuf[i]); + } + printf("\n"); + + memcpy(ixbuf_ref, ixbuf, sizeof(spiffs_page_ix) * entries); + + TEST_CHECK_EQ(SPIFFS_ix_remap(FS, fd1, 0), SPIFFS_OK); + TEST_CHECK_EQ(canary, ixbuf[entries]); + for (i = 0; i < entries; i++) { + printf("%04x ", ixbuf[i]); + } + printf("\n"); + TEST_CHECK_EQ(0, memcmp(ixbuf_ref, ixbuf, sizeof(spiffs_page_ix) * entries)); + + TEST_CHECK_EQ(SPIFFS_ix_remap(FS, fd1, SPIFFS_DATA_PAGE_SIZE(FS)), SPIFFS_OK); + for (i = 0; i < entries; i++) { + printf("%04x ", ixbuf[i]); + } + printf("\n"); + TEST_CHECK_EQ(canary, ixbuf[entries]); + TEST_CHECK_EQ(0, memcmp(&ixbuf_ref[1], ixbuf, sizeof(spiffs_page_ix) * (entries-1))); + + + TEST_CHECK_EQ(SPIFFS_ix_remap(FS, fd1, 0), SPIFFS_OK); + for (i = 0; i < entries; i++) { + printf("%04x ", ixbuf[i]); + } + printf("\n"); + TEST_CHECK_EQ(canary, ixbuf[entries]); + TEST_CHECK_EQ(0, memcmp(ixbuf_ref, ixbuf, sizeof(spiffs_page_ix) * entries)); + + TEST_CHECK_EQ(SPIFFS_ix_remap(FS, fd1, size/2), SPIFFS_OK); + TEST_CHECK_EQ(canary, ixbuf[entries]); + + for (i = 0; i < entries; i++) { + printf("%04x ", ixbuf_ref[i]); + } + printf("\n"); + + for (i = 0; i < entries; i++) { + printf("%04x ", ixbuf[i]); + } + printf("\n"); + + int matches = 0; + for (i = 0; i < entries; i++) { + int j; + for (j = 0; j < entries; j++) { + if (ixbuf_ref[i] == ixbuf[i]) { + matches++; + } + } + } + TEST_CHECK_LE(matches, 1); + + return TEST_RES_OK; +} +TEST_END + +TEST(ix_map_partial) +{ + // create a file, 10 data pages long + s32_t res; + spiffs_file fd; + fd = SPIFFS_open(FS, "1", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0); + TEST_CHECK_GT(fd, 0); + + const int size_pages = 10; + + u8_t buf[SPIFFS_DATA_PAGE_SIZE(FS)]; + int i; + for (i = 0; i < size_pages; i++) { + memrand(buf, sizeof(buf)); + res = SPIFFS_write(FS, fd, buf, sizeof(buf)); + TEST_CHECK_GE(res, SPIFFS_OK); + } + res = SPIFFS_close(FS, fd); + TEST_CHECK_GE(res, SPIFFS_OK); + + spiffs_stat s; + res = SPIFFS_stat(FS, "1", &s); + TEST_CHECK_GE(res, SPIFFS_OK); + u32_t size = s.size; + + printf("file created, size: %i..\n", size); + + const u32_t crc_unmapped = get_spiffs_file_crc("1"); + + fd = SPIFFS_open(FS, "1", SPIFFS_O_RDONLY, 0); + TEST_CHECK_GT(fd, 0); + + // map index to memory + const int entries = SPIFFS_bytes_to_ix_map_entries(FS, size/2); + spiffs_ix_map map; + spiffs_page_ix ixbuf[entries]; + + printf("map 0-50%%\n"); + res = SPIFFS_ix_map(FS, fd, &map, 0, size/2, ixbuf); + TEST_CHECK_GE(res, SPIFFS_OK); + + const u32_t crc_mapped_beginning = get_spiffs_file_crc_by_fd(fd); + TEST_CHECK_EQ(crc_mapped_beginning, crc_unmapped); + + printf("map 25-75%%\n"); + res = SPIFFS_ix_remap(FS, fd, size/4); + TEST_CHECK_GE(res, SPIFFS_OK); + + const u32_t crc_mapped_middle = get_spiffs_file_crc_by_fd(fd); + TEST_CHECK_EQ(crc_mapped_middle, crc_unmapped); + + printf("map 50-100%%\n"); + res = SPIFFS_ix_remap(FS, fd, size/2); + TEST_CHECK_GE(res, SPIFFS_OK); + + const u32_t crc_mapped_end = get_spiffs_file_crc_by_fd(fd); + TEST_CHECK_EQ(crc_mapped_end, crc_unmapped); + + return TEST_RES_OK; +} +TEST_END + +TEST(ix_map_beyond) +{ + // create a file, 10 data pages long + s32_t res; + spiffs_file fd; + fd = SPIFFS_open(FS, "1", SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0); + TEST_CHECK_GT(fd, 0); + + const int size_pages = 10; + + u8_t buf[SPIFFS_DATA_PAGE_SIZE(FS)]; + int i; + for (i = 0; i < size_pages; i++) { + memrand(buf, sizeof(buf)); + res = SPIFFS_write(FS, fd, buf, sizeof(buf)); + TEST_CHECK_GE(res, SPIFFS_OK); + } + res = SPIFFS_close(FS, fd); + TEST_CHECK_GE(res, SPIFFS_OK); + + spiffs_stat s; + res = SPIFFS_stat(FS, "1", &s); + TEST_CHECK_GE(res, SPIFFS_OK); + u32_t size = s.size; + + printf("file created, size: %i..\n", size); + + // map index to memory + fd = SPIFFS_open(FS, "1", SPIFFS_O_RDWR | SPIFFS_O_APPEND, 0); + TEST_CHECK_GT(fd, 0); + + const int entries = SPIFFS_bytes_to_ix_map_entries(FS, size); + spiffs_ix_map map; + spiffs_page_ix ixbuf[entries]; + printf("map has %i entries\n", entries); + + printf("map 100-200%%\n"); + res = SPIFFS_ix_map(FS, fd, &map, size, size, ixbuf); + TEST_CHECK_GE(res, SPIFFS_OK); + + printf("make sure map is empty\n"); + for (i = 0; i < entries; i++) { + printf("%04x ", ixbuf[i]); + TEST_CHECK_EQ(ixbuf[i], 0); + } + printf("\n"); + + printf("elongate by 100%%\n"); + for (i = 0; i < size_pages; i++) { + memrand(buf, sizeof(buf)); + res = SPIFFS_write(FS, fd, buf, sizeof(buf)); + TEST_CHECK_GE(res, SPIFFS_OK); + } + TEST_CHECK_GE(SPIFFS_fflush(FS, fd), SPIFFS_OK); + + res = SPIFFS_stat(FS, "1", &s); + TEST_CHECK_GE(res, SPIFFS_OK); + size = s.size; + printf("file elongated, size: %i..\n", size); + + printf("make sure map is full but for one element\n"); + int zeroed = 0; + for (i = 0; i < entries; i++) { + printf("%04x ", ixbuf[i]); + if (ixbuf[i] == 0) zeroed++; + } + printf("\n"); + TEST_CHECK_LE(zeroed, 1); + + printf("remap till end\n"); + TEST_CHECK_EQ(SPIFFS_ix_remap(FS, fd, size), SPIFFS_OK); + + printf("make sure map is empty but for one element\n"); + int nonzero = 0; + for (i = 0; i < entries; i++) { + printf("%04x ", ixbuf[i]); + if (ixbuf[i]) nonzero++; + } + printf("\n"); + TEST_CHECK_LE(nonzero, 1); + + printf("elongate again, by other fd\n"); + + spiffs_file fd2 = SPIFFS_open(FS, "1", SPIFFS_O_WRONLY | SPIFFS_O_APPEND, 0); + TEST_CHECK_GT(fd2, 0); + + for (i = 0; i < size_pages; i++) { + memrand(buf, sizeof(buf)); + res = SPIFFS_write(FS, fd2, buf, sizeof(buf)); + TEST_CHECK_GE(res, SPIFFS_OK); + } + TEST_CHECK_GE(SPIFFS_close(FS, fd2), SPIFFS_OK); + + printf("make sure map is full but for one element\n"); + zeroed = 0; + for (i = 0; i < entries; i++) { + printf("%04x ", ixbuf[i]); + if (ixbuf[i] == 0) zeroed++; + } + printf("\n"); + TEST_CHECK_LE(zeroed, 1); + + return TEST_RES_OK; +} +TEST_END + +#endif // SPIFFS_IX_MAP + +SUITE_TESTS(hydrogen_tests) + ADD_TEST(info) +#if SPIFFS_USE_MAGIC + ADD_TEST(magic) +#if SPIFFS_USE_MAGIC_LENGTH + ADD_TEST(magic_length) +#if SPIFFS_SINGLETON==0 + ADD_TEST(magic_length_probe) +#endif +#endif +#endif + ADD_TEST(missing_file) + ADD_TEST(bad_fd) + ADD_TEST(closed_fd) + ADD_TEST(deleted_same_fd) + ADD_TEST(deleted_other_fd) + ADD_TEST(file_by_open) + ADD_TEST(file_by_creat) + ADD_TEST(file_by_open_excl) +#if SPIFFS_FILEHDL_OFFSET + ADD_TEST(open_fh_offs) +#endif + ADD_TEST(list_dir) + ADD_TEST(open_by_dirent) + ADD_TEST(open_by_page) + ADD_TEST(user_callback_basic) + ADD_TEST(user_callback_gc) + ADD_TEST(name_too_long) + ADD_TEST(rename) +#if SPIFFS_OBJ_META_LEN + ADD_TEST(update_meta) +#endif + ADD_TEST(remove_single_by_path) + ADD_TEST(remove_single_by_fd) + ADD_TEST(write_cache) + ADD_TEST(write_big_file_chunks_page) + ADD_TEST(write_big_files_chunks_page) + ADD_TEST(write_big_file_chunks_index) + ADD_TEST(write_big_files_chunks_index) + ADD_TEST(write_big_file_chunks_huge) + ADD_TEST(write_big_files_chunks_huge) + ADD_TEST(truncate_big_file) + ADD_TEST(simultaneous_write) + ADD_TEST(simultaneous_write_append) + ADD_TEST(file_uniqueness) + ADD_TEST(read_chunk_1) + ADD_TEST(read_chunk_page) + ADD_TEST(read_chunk_index) + ADD_TEST(read_chunk_huge) + ADD_TEST(read_beyond) + ADD_TEST(read_beyond2) + ADD_TEST(bad_index_1) + ADD_TEST(bad_index_2) + ADD_TEST(lseek_simple_modification) + ADD_TEST(lseek_modification_append) + ADD_TEST(lseek_modification_append_multi) + ADD_TEST(lseek_read) + ADD_TEST(lseek_oob) + ADD_TEST(gc_quick) + ADD_TEST(write_small_file_chunks_1) + ADD_TEST(write_small_files_chunks_1) + ADD_TEST(write_big_file_chunks_1) + ADD_TEST(write_big_files_chunks_1) + ADD_TEST(long_run_config_many_small_one_long) + ADD_TEST(long_run_config_many_medium) + ADD_TEST(long_run_config_many_small) + ADD_TEST(long_run) +#if SPIFFS_IX_MAP + ADD_TEST(ix_map_basic) + ADD_TEST(ix_map_remap) + ADD_TEST(ix_map_partial) + ADD_TEST(ix_map_beyond) +#endif + +SUITE_END(hydrogen_tests) + diff --git a/components/spiffs/spiffs/src/test/test_spiffs.c b/components/spiffs/spiffs/src/test/test_spiffs.c new file mode 100644 index 00000000..aa608660 --- /dev/null +++ b/components/spiffs/spiffs/src/test/test_spiffs.c @@ -0,0 +1,1111 @@ +/* + * test_spiffs.c + * + * Created on: Jun 19, 2013 + * Author: petera + */ + + +#include +#include +#include + +#include "params_test.h" +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#include "testrunner.h" + +#include "test_spiffs.h" + +#include +#include +#include +#include +#include +#include + +#define AREA(x) _area[(x) - addr_offset] + +static u32_t _area_sz; +static unsigned char *_area = NULL; +static u32_t addr_offset = 0; + +static int *_erases; +static char _path[256]; +static u32_t bytes_rd = 0; +static u32_t bytes_wr = 0; +static u32_t reads = 0; +static u32_t writes = 0; +static u32_t error_after_bytes_written = 0; +static u32_t error_after_bytes_read = 0; +static char error_after_bytes_written_once_only = 0; +static char error_after_bytes_read_once_only = 0; +static char log_flash_ops = 1; +static u32_t fs_check_fixes = 0; +static u32_t _fs_locks; + +spiffs __fs; +static u8_t *_work = NULL; +static u8_t *_fds = NULL; +static u32_t _fds_sz; +static u8_t *_cache = NULL; +static u32_t _cache_sz; + +static int check_valid_flash = 1; + +#ifndef TEST_PATH +#define TEST_PATH "/dev/shm/spiffs/test-data/" +#endif + +// taken from http://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux +// thanks Jonathan Leffler + +static int do_mkdir(const char *path, mode_t mode) +{ + struct stat st; + int status = 0; + + if (stat(path, &st) != 0) { + /* Directory does not exist. EEXIST for race condition */ + if (mkdir(path, mode) != 0 && errno != EEXIST) { + status = -1; + } + } else if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + status = -1; + } + + return status; +} + +/** +** mkpath - ensure all directories in path exist +** Algorithm takes the pessimistic view and works top-down to ensure +** each directory in path exists, rather than optimistically creating +** the last element and working backwards. +*/ +static int mkpath(const char *path, mode_t mode) { + char *pp; + char *sp; + int status; + char *copypath = strdup(path); + + status = 0; + pp = copypath; + while (status == 0 && (sp = strchr(pp, '/')) != 0) { + if (sp != pp) { + /* Neither root nor double slash in path */ + *sp = '\0'; + status = do_mkdir(copypath, mode); + *sp = '/'; + } + pp = sp + 1; + } + if (status == 0) { + status = do_mkdir(path, mode); + } + free(copypath); + return status; +} + +// end take +// +// +char *make_test_fname(const char *name) { + sprintf(_path, "%s/%s", TEST_PATH, name); + return _path; +} + +void create_test_path(void) { + if (mkpath(TEST_PATH, 0755)) { + printf("could not create path %s\n", TEST_PATH); + exit(1); + } +} + +void clear_test_path() { + DIR *dp; + struct dirent *ep; + dp = opendir(TEST_PATH); + + if (dp != NULL) { + while ((ep = readdir(dp))) { + if (ep->d_name[0] != '.') { + sprintf(_path, "%s/%s", TEST_PATH, ep->d_name); + remove(_path); + } + } + closedir(dp); + } +} + +static s32_t _read( +#if SPIFFS_HAL_CALLBACK_EXTRA + spiffs *fs, +#endif + u32_t addr, u32_t size, u8_t *dst) { + //printf("rd @ addr %08x => %p\n", addr, &AREA(addr)); + if (log_flash_ops) { + bytes_rd += size; + reads++; + if (error_after_bytes_read > 0 && bytes_rd >= error_after_bytes_read) { + if (error_after_bytes_read_once_only) { + error_after_bytes_read = 0; + } + return SPIFFS_ERR_TEST; + } + } + if (addr < SPIFFS_CFG_PHYS_ADDR(&__fs)) { + printf("FATAL read addr too low %08x < %08x\n", addr, SPIFFS_PHYS_ADDR); + ERREXIT(); + return -1; + } + if (addr + size > SPIFFS_CFG_PHYS_ADDR(&__fs) + SPIFFS_CFG_PHYS_SZ(&__fs)) { + printf("FATAL read addr too high %08x + %08x > %08x\n", addr, size, SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE); + ERREXIT(); + return -1; + } + memcpy(dst, &AREA(addr), size); + return 0; +} + +static s32_t _write( +#if SPIFFS_HAL_CALLBACK_EXTRA + spiffs *fs, +#endif + u32_t addr, u32_t size, u8_t *src) { + int i; + //printf("wr %08x %i\n", addr, size); + if (log_flash_ops) { + bytes_wr += size; + writes++; + if (error_after_bytes_written > 0 && bytes_wr >= error_after_bytes_written) { + if (error_after_bytes_written_once_only) { + error_after_bytes_written = 0; + } + return SPIFFS_ERR_TEST; + } + } + + if (addr < SPIFFS_CFG_PHYS_ADDR(&__fs)) { + printf("FATAL write addr too low %08x < %08x\n", addr, SPIFFS_PHYS_ADDR); + ERREXIT(); + return -1; + } + if (addr + size > SPIFFS_CFG_PHYS_ADDR(&__fs) + SPIFFS_CFG_PHYS_SZ(&__fs)) { + printf("FATAL write addr too high %08x + %08x > %08x\n", addr, size, SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE); + ERREXIT(); + return -1; + } + + for (i = 0; i < size; i++) { + if (((addr + i) & (SPIFFS_CFG_LOG_PAGE_SZ(&__fs)-1)) != offsetof(spiffs_page_header, flags)) { + if (check_valid_flash && ((AREA(addr + i) ^ src[i]) & src[i])) { + printf("trying to write %02x to %02x at addr %08x (as part of writing %d bytes to addr %08x)\n", src[i], AREA(addr + i), addr+i, size, addr); + spiffs_page_ix pix = (addr + i) / SPIFFS_CFG_LOG_PAGE_SZ(&__fs); + dump_page(&__fs, pix); + ERREXIT(); + return -1; + } + } + AREA(addr + i) &= src[i]; + } + return 0; +} +static s32_t _erase( +#if SPIFFS_HAL_CALLBACK_EXTRA + spiffs *fs, +#endif + u32_t addr, u32_t size) { + if (addr & (SPIFFS_CFG_PHYS_ERASE_SZ(&__fs)-1)) { + printf("trying to erase at addr %08x, out of boundary\n", addr); + ERREXIT(); + return -1; + } + if (size & (SPIFFS_CFG_PHYS_ERASE_SZ(&__fs)-1)) { + printf("trying to erase at with size %08x, out of boundary\n", size); + ERREXIT(); + return -1; + } + _erases[(addr-SPIFFS_CFG_PHYS_ADDR(&__fs))/SPIFFS_CFG_PHYS_ERASE_SZ(&__fs)]++; + memset(&AREA(addr), 0xff, size); + return 0; +} + +void hexdump_mem(u8_t *b, u32_t len) { + while (len--) { + if ((((intptr_t)b)&0x1f) == 0) { + printf("\n"); + } + printf("%02x", *b++); + } + printf("\n"); +} + +void hexdump(u32_t addr, u32_t len) { + int remainder = (addr % 32) == 0 ? 0 : 32 - (addr % 32); + u32_t a; + for (a = addr - remainder; a < addr+len; a++) { + if ((a & 0x1f) == 0) { + if (a != addr) { + printf(" "); + int j; + for (j = 0; j < 32; j++) { + if (a-32+j < addr) + printf(" "); + else { + printf("%c", (AREA(a-32+j) < 32 || AREA(a-32+j) >= 0x7f) ? '.' : AREA(a-32+j)); + } + } + } + printf("%s %08x: ", a<=addr ? "":"\n", a); + } + if (a < addr) { + printf(" "); + } else { + printf("%02x", AREA(a)); + } + } + int j; + printf(" "); + for (j = 0; j < 32; j++) { + if (a-32+j < addr) + printf(" "); + else { + printf("%c", (AREA(a-32+j) < 32 || AREA(a-32+j) >= 0x7f) ? '.' : AREA(a-32+j)); + } + } + printf("\n"); +} + +void dump_page(spiffs *fs, spiffs_page_ix p) { + printf("page %04x ", p); + u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, p); + if (p % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // obj lu page + printf("OBJ_LU"); + } else { + u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , p)) + + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, p) * sizeof(spiffs_obj_id); + spiffs_obj_id obj_id = *((spiffs_obj_id *)&AREA(obj_id_addr)); + // data page + spiffs_page_header *ph = (spiffs_page_header *)&AREA(addr); + printf("DATA %04x:%04x ", obj_id, ph->span_ix); + printf("%s", ((ph->flags & SPIFFS_PH_FLAG_FINAL) == 0) ? "FIN " : "fin "); + printf("%s", ((ph->flags & SPIFFS_PH_FLAG_DELET) == 0) ? "DEL " : "del "); + printf("%s", ((ph->flags & SPIFFS_PH_FLAG_INDEX) == 0) ? "IDX " : "idx "); + printf("%s", ((ph->flags & SPIFFS_PH_FLAG_USED) == 0) ? "USD " : "usd "); + printf("%s ", ((ph->flags & SPIFFS_PH_FLAG_IXDELE) == 0) ? "IDL " : "idl "); + if (obj_id & SPIFFS_OBJ_ID_IX_FLAG) { + // object index + printf("OBJ_IX"); + if (ph->span_ix == 0) { + printf("_HDR "); + spiffs_page_object_ix_header *oix_hdr = (spiffs_page_object_ix_header *)&AREA(addr); + printf("'%s' %i bytes type:%02x", oix_hdr->name, oix_hdr->size, oix_hdr->type); + } + } else { + // data page + printf("CONTENT"); + } + } + printf("\n"); + u32_t len = SPIFFS_CFG_LOG_PAGE_SZ(fs); + hexdump(addr, len); +} + +void area_write(u32_t addr, u8_t *buf, u32_t size) { + int i; + for (i = 0; i < size; i++) { + AREA(addr + i) = *buf++; + } +} + +void area_set(u32_t addr, u8_t d, u32_t size) { + int i; + for (i = 0; i < size; i++) { + AREA(addr + i) = d; + } +} + +void area_read(u32_t addr, u8_t *buf, u32_t size) { + int i; + for (i = 0; i < size; i++) { + *buf++ = AREA(addr + i); + } +} + +void dump_erase_counts(spiffs *fs) { + spiffs_block_ix bix; + spiffs_block_ix bix_offs; + printf(" BLOCK |\n"); + printf(" AGE COUNT|\n"); + for (bix_offs = 0; bix_offs < fs->block_count; bix_offs+=8) { + for (bix = bix_offs; bix < bix_offs+8 && bix < fs->block_count; bix++) { + printf("----%3i ----|", bix); + } + printf("\n"); + for (bix = bix_offs; bix < bix_offs+8 && bix < fs->block_count; bix++) { + spiffs_obj_id erase_mark; + _spiffs_rd(fs, 0, 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix), sizeof(spiffs_obj_id), (u8_t *)&erase_mark); + if (_erases[bix] == 0) { + printf(" |"); + } else { + printf("%7i %4i|", (fs->max_erase_count - erase_mark), _erases[bix]); + } + } + printf("\n"); + } +} + +void dump_flash_access_stats() { + printf(" RD: %10i reads %10i bytes %10i avg bytes/read\n", reads, bytes_rd, reads == 0 ? 0 : (bytes_rd / reads)); + printf(" WR: %10i writes %10i bytes %10i avg bytes/write\n", writes, bytes_wr, writes == 0 ? 0 : (bytes_wr / writes)); +} + + +static int check_cb_count; +// static u32_t old_perc = 999; +static void spiffs_check_cb_f( +#if SPIFFS_HAL_CALLBACK_EXTRA + spiffs *fs, +#endif + spiffs_check_type type, spiffs_check_report report, + u32_t arg1, u32_t arg2) { +/* if (report == SPIFFS_CHECK_PROGRESS && old_perc != arg1) { + old_perc = arg1; + printf("CHECK REPORT: "); + switch(type) { + case SPIFFS_CHECK_LOOKUP: + printf("LU "); break; + case SPIFFS_CHECK_INDEX: + printf("IX "); break; + case SPIFFS_CHECK_PAGE: + printf("PA "); break; + } + printf("%i%%\n", arg1 * 100 / 256); + }*/ + if (report != SPIFFS_CHECK_PROGRESS) { + check_cb_count++; + if (report != SPIFFS_CHECK_ERROR) fs_check_fixes++; + printf(" check: "); + switch (type) { + case SPIFFS_CHECK_INDEX: + printf("INDEX "); break; + case SPIFFS_CHECK_LOOKUP: + printf("LOOKUP "); break; + case SPIFFS_CHECK_PAGE: + printf("PAGE "); break; + default: + printf("???? "); break; + } + if (report == SPIFFS_CHECK_ERROR) { + printf("ERROR %i", arg1); + } else if (report == SPIFFS_CHECK_DELETE_BAD_FILE) { + printf("DELETE BAD FILE %04x", arg1); + } else if (report == SPIFFS_CHECK_DELETE_ORPHANED_INDEX) { + printf("DELETE ORPHANED INDEX %04x", arg1); + } else if (report == SPIFFS_CHECK_DELETE_PAGE) { + printf("DELETE PAGE %04x", arg1); + } else if (report == SPIFFS_CHECK_FIX_INDEX) { + printf("FIX INDEX %04x:%04x", arg1, arg2); + } else if (report == SPIFFS_CHECK_FIX_LOOKUP) { + printf("FIX INDEX %04x:%04x", arg1, arg2); + } else { + printf("??"); + } + printf("\n"); + } +} + +void fs_set_addr_offset(u32_t offset) { + addr_offset = offset; +} + +void test_lock(spiffs *fs) { + if (_fs_locks != 0) { + printf("FATAL: reentrant locks. Abort.\n"); + ERREXIT(); + exit(-1); + } + _fs_locks++; +} + +void test_unlock(spiffs *fs) { + if (_fs_locks != 1) { + printf("FATAL: unlocking unlocked. Abort.\n"); + ERREXIT(); + exit(-1); + } + _fs_locks--; +} + +s32_t fs_mount_specific(u32_t phys_addr, u32_t phys_size, + u32_t phys_sector_size, + u32_t log_block_size, u32_t log_page_size) { + spiffs_config c; + c.hal_erase_f = _erase; + c.hal_read_f = _read; + c.hal_write_f = _write; +#if SPIFFS_SINGLETON == 0 + c.log_block_size = log_block_size; + c.log_page_size = log_page_size; + c.phys_addr = phys_addr; + c.phys_erase_block = phys_sector_size; + c.phys_size = phys_size; +#endif +#if SPIFFS_FILEHDL_OFFSET + c.fh_ix_offset = TEST_SPIFFS_FILEHDL_OFFSET; +#endif + return SPIFFS_mount(&__fs, &c, _work, _fds, _fds_sz, _cache, _cache_sz, spiffs_check_cb_f); +} + +static void fs_create(u32_t spiflash_size, + u32_t phys_sector_size, + u32_t log_page_size, + u32_t descriptors, u32_t cache_pages) { + _area_sz = spiflash_size; + _area = malloc(spiflash_size); + ASSERT(_area != NULL, "testbench area could not be malloced"); + + const u32_t erase_sz = sizeof(int) * (spiflash_size / phys_sector_size); + _erases = malloc(erase_sz); + ASSERT(_erases != NULL, "testbench erase log could not be malloced"); + memset(_erases, 0, erase_sz); + + _fds_sz = descriptors * sizeof(spiffs_fd); + _fds = malloc(_fds_sz); + ASSERT(_fds != NULL, "testbench fd buffer could not be malloced"); + memset(_fds, 0, _fds_sz); + +#if SPIFFS_CACHE + _cache_sz = sizeof(spiffs_cache) + cache_pages * (sizeof(spiffs_cache_page) + log_page_size); + _cache = malloc(_cache_sz); + ASSERT(_cache != NULL, "testbench cache could not be malloced"); + memset(_cache, 0, _cache_sz); +#endif + + const u32_t work_sz = log_page_size * 2; + _work = malloc(work_sz); + ASSERT(_work != NULL, "testbench work buffer could not be malloced"); + memset(_work, 0, work_sz); +} + +static void fs_free(void) { + if (_area) free(_area); + _area = NULL; + if (_erases) free(_erases); + _erases = NULL; + if (_fds) free(_fds); + _fds = NULL; + if (_cache) free(_cache); + _cache = NULL; + if (_work) free(_work); + _work = NULL; +} + +/** + * addr_offset + */ +void fs_reset_specific(u32_t addr_offset, u32_t phys_addr, u32_t phys_size, + u32_t phys_sector_size, + u32_t log_block_size, u32_t log_page_size) { + fs_create(phys_size + phys_addr - addr_offset, + phys_sector_size, + log_page_size, + DEFAULT_NUM_FD, + DEFAULT_NUM_CACHE_PAGES); + fs_set_addr_offset(addr_offset); + memset(&AREA(addr_offset), 0xcc, _area_sz); + memset(&AREA(phys_addr), 0xff, phys_size); + memset(&__fs, 0, sizeof(__fs)); + + s32_t res = fs_mount_specific(phys_addr, phys_size, phys_sector_size, log_block_size, log_page_size); + +#if SPIFFS_USE_MAGIC + if (res == SPIFFS_OK) { + SPIFFS_unmount(&__fs); + } + res = SPIFFS_format(&__fs); + if (res != SPIFFS_OK) { + printf("format failed, %i\n", SPIFFS_errno(&__fs)); + } + res = fs_mount_specific(phys_addr, phys_size, phys_sector_size, log_block_size, log_page_size); + if (res != SPIFFS_OK) { + printf("mount failed, %i\n", SPIFFS_errno(&__fs)); + } +#endif + + clear_flash_ops_log(); + log_flash_ops = 1; + fs_check_fixes = 0; +} + +void fs_reset() { + fs_reset_specific(0, SPIFFS_PHYS_ADDR, SPIFFS_FLASH_SIZE, SECTOR_SIZE, LOG_BLOCK, LOG_PAGE); +} + +void fs_store_dump(char *fname) { + int pfd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + ASSERT(pfd > 0, "could not open dump file"); + write(pfd, _area, _area_sz); + close(pfd); +} + +void fs_load_dump(char *fname) { + int pfd = open(fname, O_RDONLY, S_IRUSR | S_IWUSR); + ASSERT(pfd > 0, "could not load dump"); + read(pfd, _area, _area_sz); + close(pfd); +} + +void fs_mount_dump(char *fname, + u32_t addr_offset, u32_t phys_addr, u32_t phys_size, + u32_t phys_sector_size, + u32_t log_block_size, u32_t log_page_size) { + fs_create(phys_size + phys_addr - addr_offset, + phys_sector_size, + log_page_size, + DEFAULT_NUM_FD, + DEFAULT_NUM_CACHE_PAGES); + fs_set_addr_offset(addr_offset); + memset(&AREA(addr_offset), 0xcc, _area_sz); + memset(&AREA(phys_addr), 0xff, phys_size); + memset(&__fs, 0, sizeof(__fs)); + + fs_load_dump(fname); + + s32_t res = fs_mount_specific(phys_addr, phys_size, phys_sector_size, log_block_size, log_page_size); + + ASSERT(res == SPIFFS_OK, "failed mounting dump, check settings"); + + clear_flash_ops_log(); + log_flash_ops = 1; + fs_check_fixes = 0; +} + +void set_flash_ops_log(int enable) { + log_flash_ops = enable; +} + +void clear_flash_ops_log() { + bytes_rd = 0; + bytes_wr = 0; + reads = 0; + writes = 0; + error_after_bytes_read = 0; + error_after_bytes_written = 0; +} + +u32_t get_flash_ops_log_read_bytes() { + return bytes_rd; +} + +u32_t get_flash_ops_log_write_bytes() { + return bytes_wr; +} + +void invoke_error_after_read_bytes(u32_t b, char once_only) { + error_after_bytes_read = b; + error_after_bytes_read_once_only = once_only; +} +void invoke_error_after_write_bytes(u32_t b, char once_only) { + error_after_bytes_written = b; + error_after_bytes_written_once_only = once_only; +} + +void fs_set_validate_flashing(int i) { + check_valid_flash = i; +} + +void real_assert(int c, const char *n, const char *file, int l) { + if (c == 0) { + printf("ASSERT: %s %s @ %i\n", (n ? n : ""), file, l); + printf("fs errno:%i\n", __fs.err_code); + exit(0); + } +} + +int read_and_verify(char *name) { + int fd = SPIFFS_open(&__fs, name, SPIFFS_RDONLY, 0); + if (fd < 0) { + printf(" read_and_verify: could not open file %s\n", name); + return fd; + } + return read_and_verify_fd(fd, name); +} + +int read_and_verify_fd(spiffs_file fd, char *name) { + s32_t res; + int pfd = open(make_test_fname(name), O_RDONLY); + spiffs_stat s; + res = SPIFFS_fstat(&__fs, fd, &s); + if (res < 0) { + printf(" read_and_verify: could not stat file %s\n", name); + return res; + } + + off_t fsize = lseek(pfd, 0, SEEK_END); + if (s.size != fsize) { + printf(" read_and_verify: size differs, %s spiffs:%d!=fs:%ld\n", name, s.size, fsize); + return -1; + } + lseek(pfd, 0, SEEK_SET); + + if (s.size == 0) { + SPIFFS_close(&__fs, fd); + close(pfd); + return 0; + } + + //printf("verifying %s, len %i\n", name, s.size); + int offs = 0; + u8_t buf_d[256]; + u8_t buf_v[256]; + while (offs < s.size) { + int read_len = MIN(s.size - offs, sizeof(buf_d)); + res = SPIFFS_read(&__fs, fd, buf_d, read_len); + if (res < 0) { + printf(" read_and_verify: could not read file %s offs:%i len:%i filelen:%i\n", name, offs, read_len, s.size); + return res; + } + int pres = read(pfd, buf_v, read_len); + (void)pres; + //printf("reading offs:%i len:%i spiffs_res:%i posix_res:%i\n", offs, read_len, res, pres); + int i; + int veri_ok = 1; + for (i = 0; veri_ok && i < read_len; i++) { + if (buf_d[i] != buf_v[i]) { + printf("file verification mismatch @ %i, %02x %c != %02x %c\n", offs+i, buf_d[i], buf_d[i], buf_v[i], buf_v[i]); + int j = MAX(0, i-16); + int k = MIN(sizeof(buf_d), i+16); + k = MIN(s.size-offs, k); + int l; + for (l = j; l < k; l++) { + printf("%c", buf_d[l] > 31 ? buf_d[l] : '.'); + } + printf("\n"); + for (l = j; l < k; l++) { + printf("%c", buf_v[l] > 31 ? buf_v[l] : '.'); + } + printf("\n"); + veri_ok = 0; + } + } + if (!veri_ok) { + SPIFFS_close(&__fs, fd); + close(pfd); + printf("data mismatch\n"); + return -1; + } + + offs += read_len; + } + + SPIFFS_close(&__fs, fd); + close(pfd); + + return 0; +} + +static void test_on_stop(test *t) { + printf(" spiffs errno:%i\n", SPIFFS_errno(&__fs)); +#if SPIFFS_TEST_VISUALISATION + if (_area) SPIFFS_vis(FS); +#endif + +} + +void memrand(u8_t *b, int len) { + int i; + for (i = 0; i < len; i++) { + b[i] = rand(); + } +} + +int test_create_file(char *name) { + spiffs_stat s; + spiffs_file fd; + int res = SPIFFS_creat(FS, name, 0); + CHECK_RES(res); + fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0); + CHECK(fd >= 0); + res = SPIFFS_fstat(FS, fd, &s); + CHECK_RES(res); + CHECK(strcmp((char*)s.name, name) == 0); + CHECK(s.size == 0); +#if SPIFFS_OBJ_META_LEN + { + int i; + for (i = 0; i < SPIFFS_OBJ_META_LEN; i++) { + CHECK(s.meta[i] == 0xff); + } + } +#endif + SPIFFS_close(FS, fd); + return 0; +} + +int test_create_and_write_file(char *name, int size, int chunk_size) { + int res; + spiffs_file fd; + printf(" create and write %s", name); + res = test_create_file(name); + if (res < 0) { + printf(" failed creation, %i\n",res); + } + CHECK(res >= 0); + fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0); + if (fd < 0) { + printf(" failed open, %i\n",res); + } + CHECK(fd >= 0); + int pfd = open(make_test_fname(name), O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + int offset = 0; + int mark = 0; + while (offset < size) { + int len = MIN(size-offset, chunk_size); + if (offset > mark) { + mark += size/16; + printf("."); + fflush(stdout); + } + u8_t *buf = malloc(len); + memrand(buf, len); + res = SPIFFS_write(FS, fd, buf, len); + write(pfd, buf, len); + free(buf); + if (res < 0) { + printf("\n error @ offset %i, res %i\n", offset, res); + } + offset += len; + CHECK(res >= 0); + } + printf("\n"); + close(pfd); + + spiffs_stat stat; + res = SPIFFS_fstat(FS, fd, &stat); + if (res < 0) { + printf(" failed fstat, %i\n",res); + } + CHECK(res >= 0); + if (stat.size != size) { + printf(" failed size, %i != %i\n", stat.size, size); + } + CHECK(stat.size == size); + + SPIFFS_close(FS, fd); + return 0; +} + +static u32_t crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +static u32_t crc32(u32_t crc, const void *buf, size_t size) +{ + const u8_t *p; + + p = buf; + crc = crc ^ ~0U; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc ^ ~0U; +} + +u32_t get_spiffs_file_crc_by_fd(spiffs_file fd) { + s32_t res; + u32_t crc = 0; + u8_t buf[256]; + + ASSERT(SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_SET) >= 0, "could not seek to start of file"); + + while ((res = SPIFFS_read(FS, fd, buf, sizeof(buf))) > SPIFFS_OK) { + crc = crc32(crc, buf, res); + } + ASSERT(SPIFFS_errno(FS) == SPIFFS_ERR_END_OF_OBJECT || SPIFFS_errno(FS) == SPIFFS_OK, "failed reading file"); + + return crc; +} + +u32_t get_spiffs_file_crc(char *name) { + s32_t res; + spiffs_file fd; + fd = SPIFFS_open(FS, name, SPIFFS_O_RDONLY, 0); + ASSERT(fd >= 0, "Could not open file"); + u32_t crc = get_spiffs_file_crc_by_fd(fd); + res = SPIFFS_close(FS, fd); + ASSERT(res >= SPIFFS_OK, "failing closing file"); + return crc; +} + +#if SPIFFS_CACHE +#if SPIFFS_CACHE_STATS +static u32_t chits_tot = 0; +static u32_t cmiss_tot = 0; +#endif +#endif + +void _setup_test_only() { + create_test_path(); + fs_set_validate_flashing(1); + test_init(test_on_stop); +} + +void _setup() { + _fs_locks = 0; + fs_reset(); + _setup_test_only(); +} + +void _teardown() { + printf(" free blocks : %i of %i\n", (FS)->free_blocks, (FS)->block_count); + printf(" pages allocated : %i\n", (FS)->stats_p_allocated); + printf(" pages deleted : %i\n", (FS)->stats_p_deleted); +#if SPIFFS_GC_STATS + printf(" gc runs : %i\n", (FS)->stats_gc_runs); +#endif +#if SPIFFS_CACHE +#if SPIFFS_CACHE_STATS + chits_tot += (FS)->cache_hits; + cmiss_tot += (FS)->cache_misses; + printf(" cache hits : %i (sum %i)\n", (FS)->cache_hits, chits_tot); + printf(" cache misses : %i (sum %i)\n", (FS)->cache_misses, cmiss_tot); + printf(" cache utiliz : %f\n", ((float)chits_tot/(float)(chits_tot + cmiss_tot))); + chits_tot = 0; + cmiss_tot = 0; +#endif +#endif + if (_area) { + dump_flash_access_stats(); + clear_flash_ops_log(); +#if SPIFFS_GC_STATS + if ((FS)->stats_gc_runs > 0) +#endif + dump_erase_counts(FS); + printf(" fs consistency check output begin\n"); + check_cb_count = 0; + SPIFFS_check(FS); + printf(" fs consistency check output end\n"); + if (check_cb_count) { + ERREXIT(); + } + } + clear_test_path(); + fs_free(); + printf(" locks : %i\n", _fs_locks); + if (_fs_locks != 0) { + printf("FATAL: lock asymmetry. Abort.\n"); + ERREXIT(); + exit(-1); + } +} + +u32_t tfile_get_size(tfile_size s) { + switch (s) { + case EMPTY: + return 0; + case SMALL: // half a data page + return SPIFFS_DATA_PAGE_SIZE(FS)/2; + case MEDIUM: // one block + return SPIFFS_DATA_PAGE_SIZE(FS) * (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS)); + case LARGE: // third of fs + return SPIFFS_DATA_PAGE_SIZE(FS) * (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS)) * (FS)->block_count/3; + } + return 0; +} + +int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files, int dbg) { + int res; + tfile *tfiles = malloc(sizeof(tfile) * max_concurrent_files); + memset(tfiles, 0, sizeof(tfile) * max_concurrent_files); + int run = 0; + int cur_config_ix = 0; + char name[32]; + while (run < max_runs) { + if (dbg) printf(" run %i/%i\n", run, max_runs); + int i; + for (i = 0; i < max_concurrent_files; i++) { + sprintf(name, "file%i_%i", (1+run), i); + tfile *tf = &tfiles[i]; + if (tf->state == 0 && cur_config_ix < cfg_count) { +// create a new file + strcpy(tf->name, name); + tf->state = 1; + tf->cfg = cfgs[cur_config_ix]; + int size = tfile_get_size(tf->cfg.tsize); + if (dbg) printf(" create new %s with cfg %i/%i, size %i\n", name, (1+cur_config_ix), cfg_count, size); + + if (tf->cfg.tsize == EMPTY) { + res = SPIFFS_creat(FS, name, 0); + CHECK_RES(res); + int pfd = open(make_test_fname(name), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + close(pfd); + int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0; + spiffs_file fd = SPIFFS_open(FS, name, extra_flags | SPIFFS_RDWR, 0); + CHECK(fd > 0); + tf->fd = fd; + } else { + int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0; + spiffs_file fd = SPIFFS_open(FS, name, extra_flags | SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + CHECK(fd > 0); + extra_flags = tf->cfg.ttype == APPENDED ? O_APPEND : 0; + int pfd = open(make_test_fname(name), extra_flags | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + tf->fd = fd; + u8_t *buf = malloc(size); + memrand(buf, size); + res = SPIFFS_write(FS, fd, buf, size); + CHECK_RES(res); + write(pfd, buf, size); + close(pfd); + free(buf); + res = read_and_verify(name); + CHECK_RES(res); + } + + cur_config_ix++; + } else if (tf->state > 0) { +// hande file lifecycle + switch (tf->cfg.ttype) { + case UNTAMPERED: { + break; + } + case APPENDED: { + if (dbg) printf(" appending %s\n", tf->name); + int size = SPIFFS_DATA_PAGE_SIZE(FS)*3; + u8_t *buf = malloc(size); + memrand(buf, size); + res = SPIFFS_write(FS, tf->fd, buf, size); + CHECK_RES(res); + int pfd = open(make_test_fname(tf->name), O_APPEND | O_RDWR); + write(pfd, buf, size); + close(pfd); + free(buf); + res = read_and_verify(tf->name); + CHECK_RES(res); + break; + } + case MODIFIED: { + if (dbg) printf(" modify %s\n", tf->name); + spiffs_stat stat; + res = SPIFFS_fstat(FS, tf->fd, &stat); + CHECK_RES(res); + int size = stat.size / tf->cfg.tlife + SPIFFS_DATA_PAGE_SIZE(FS)/3; + int offs = (stat.size / tf->cfg.tlife) * tf->state; + res = SPIFFS_lseek(FS, tf->fd, offs, SPIFFS_SEEK_SET); + CHECK_RES(res); + u8_t *buf = malloc(size); + memrand(buf, size); + res = SPIFFS_write(FS, tf->fd, buf, size); + CHECK_RES(res); + int pfd = open(make_test_fname(tf->name), O_RDWR); + lseek(pfd, offs, SEEK_SET); + write(pfd, buf, size); + close(pfd); + free(buf); + res = read_and_verify(tf->name); + CHECK_RES(res); + break; + } + case REWRITTEN: { + if (tf->fd > 0) { + SPIFFS_close(FS, tf->fd); + } + if (dbg) printf(" rewriting %s\n", tf->name); + spiffs_file fd = SPIFFS_open(FS, tf->name, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0); + CHECK(fd > 0); + int pfd = open(make_test_fname(tf->name), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + tf->fd = fd; + int size = tfile_get_size(tf->cfg.tsize); + u8_t *buf = malloc(size); + memrand(buf, size); + res = SPIFFS_write(FS, fd, buf, size); + CHECK_RES(res); + write(pfd, buf, size); + close(pfd); + free(buf); + res = read_and_verify(tf->name); + CHECK_RES(res); + break; + } + } + tf->state++; + if (tf->state > tf->cfg.tlife) { +// file outlived its time, kill it + if (tf->fd > 0) { + SPIFFS_close(FS, tf->fd); + } + if (dbg) printf(" removing %s\n", tf->name); + res = read_and_verify(tf->name); + CHECK_RES(res); + res = SPIFFS_remove(FS, tf->name); + CHECK_RES(res); + remove(make_test_fname(tf->name)); + memset(tf, 0, sizeof(tfile)); + } + + } + } + + run++; + } + free(tfiles); + return 0; +} + +int count_taken_fds(spiffs *fs) { + int i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + int taken = 0; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr) taken++; + } + return taken; +} diff --git a/components/spiffs/spiffs/src/test/test_spiffs.h b/components/spiffs/spiffs/src/test/test_spiffs.h new file mode 100644 index 00000000..4c39bdba --- /dev/null +++ b/components/spiffs/spiffs/src/test/test_spiffs.h @@ -0,0 +1,109 @@ +/* + * test_spiffs.h + * + * Created on: Jun 19, 2013 + * Author: petera + */ + +#ifndef TEST_SPIFFS_H_ +#define TEST_SPIFFS_H_ + +#include "spiffs.h" + +#define FS &__fs + +extern spiffs __fs; + + +#define CHECK(r) if (!(r)) return -1; +#define CHECK_RES(r) if (r < 0) return -1; +#define FS_PURE_DATA_PAGES(fs) \ + (SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_PAGE_SZ(fs)- (fs)->block_count * SPIFFS_OBJ_LOOKUP_PAGES(fs)) +#define FS_PURE_DATA_SIZE(fs) \ + FS_PURE_DATA_PAGES(fs) * SPIFFS_DATA_PAGE_SIZE(fs) + +typedef enum { + EMPTY, + SMALL, + MEDIUM, + LARGE, +} tfile_size; + +typedef enum { + UNTAMPERED, + APPENDED, + MODIFIED, + REWRITTEN, +} tfile_type; + +typedef enum { + SHORT = 3, + NORMAL = 15, + LONG = 100, +} tfile_life; + +typedef struct { + tfile_size tsize; + tfile_type ttype; + tfile_life tlife; +} tfile_conf; + +typedef struct { + int state; + spiffs_file fd; + tfile_conf cfg; + char name[32]; +} tfile; + +void fs_reset(); +void fs_reset_specific(u32_t addr_offset, u32_t phys_addr, u32_t phys_size, + u32_t phys_sector_size, + u32_t log_block_size, u32_t log_page_size); +s32_t fs_mount_specific(u32_t phys_addr, u32_t phys_size, + u32_t phys_sector_size, + u32_t log_block_size, u32_t log_page_size); +void fs_mount_dump(char *fname, + u32_t addr_offset, u32_t phys_addr, u32_t phys_size, + u32_t phys_sector_size, + u32_t log_block_size, u32_t log_page_size); + +void fs_store_dump(char *fname); +void fs_load_dump(char *fname); + +void fs_set_addr_offset(u32_t offset); +int read_and_verify(char *name); +int read_and_verify_fd(spiffs_file fd, char *name); +void dump_page(spiffs *fs, spiffs_page_ix p); +void hexdump(u32_t addr, u32_t len); +char *make_test_fname(const char *name); +void clear_test_path(); +void area_write(u32_t addr, u8_t *buf, u32_t size); +void area_set(u32_t addr, u8_t d, u32_t size); +void area_read(u32_t addr, u8_t *buf, u32_t size); +void dump_erase_counts(spiffs *fs); +void dump_flash_access_stats(); +void set_flash_ops_log(int enable); +void clear_flash_ops_log(); +u32_t get_flash_ops_log_read_bytes(); +u32_t get_flash_ops_log_write_bytes(); +void invoke_error_after_read_bytes(u32_t b, char once_only); +void invoke_error_after_write_bytes(u32_t b, char once_only); +void fs_set_validate_flashing(int i); +int get_error_count(); +int count_taken_fds(spiffs *fs); + +void memrand(u8_t *b, int len); +int test_create_file(char *name); +int test_create_and_write_file(char *name, int size, int chunk_size); +u32_t get_spiffs_file_crc_by_fd(spiffs_file fd); +u32_t get_spiffs_file_crc(char *name); +void _setup(); +void _setup_test_only(); +void _teardown(); +u32_t tfile_get_size(tfile_size s); +int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files, int dbg); + +void test_lock(spiffs *fs); +void test_unlock(spiffs *fs); + +#endif /* TEST_SPIFFS_H_ */ diff --git a/components/spiffs/spiffs/src/test/testrunner.c b/components/spiffs/spiffs/src/test/testrunner.c new file mode 100644 index 00000000..27419d32 --- /dev/null +++ b/components/spiffs/spiffs/src/test/testrunner.c @@ -0,0 +1,238 @@ +/* + * testrunner.c + * + * Created on: Jun 18, 2013 + * Author: petera + */ + + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "testrunner.h" + +static struct { + test *tests; + test *_last_test; + int test_count; + void (*on_stop)(test *t); + test_res *failed; + test_res *failed_last; + test_res *stopped; + test_res *stopped_last; + FILE *spec; + char incl_filter[256]; + char excl_filter[256]; +} test_main; + +void test_init(void (*on_stop)(test *t)) { + test_main.on_stop = on_stop; +} + +static int abort_on_error = 0; +static int error_count = 0; + +static char check_spec(char *name) { + if (test_main.spec) { + fseek(test_main.spec, 0, SEEK_SET); + char *line = NULL; + size_t sz; + ssize_t read; + while ((read = getline(&line, &sz, test_main.spec)) != -1) { + if (strncmp(line, name, strlen(line)-1) == 0) { + free(line); + return 1; + } + } + free(line); + return 0; + } else { + return 1; + } +} + +static char check_incl_filter(char *name) { + if (strlen(test_main.incl_filter)== 0) return 1; + return strstr(name, test_main.incl_filter) == 0 ? 0 : 2; +} + +static char check_excl_filter(char *name) { + if (strlen(test_main.excl_filter)== 0) return 1; + return strstr(name, test_main.excl_filter) == 0 ? 1 : 0; +} + +void _add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t), int non_default) { + if (f == 0) return; + if (!check_spec(name)) return; + if (check_incl_filter(name) <= non_default) return; + if (!check_excl_filter(name)) return; + DBGT("adding test %s\n", name); + test *t = malloc(sizeof(test)); + memset(t, 0, sizeof(test)); + t->f = f; + strcpy(t->name, name); + t->setup = setup; + t->teardown = teardown; + if (test_main.tests == 0) { + test_main.tests = t; + } else { + test_main._last_test->_next = t; + } + test_main._last_test = t; + test_main.test_count++; +} + +static void add_res(test *t, test_res **head, test_res **last) { + test_res *tr = malloc(sizeof(test_res)); + memset(tr,0,sizeof(test_res)); + strcpy(tr->name, t->name); + if (*head == 0) { + *head = tr; + } else { + (*last)->_next = tr; + } + *last = tr; +} + +static void dump_res(test_res **head) { + test_res *tr = (*head); + while (tr) { + test_res *next_tr = tr->_next; + printf(" %s\n", tr->name); + free(tr); + tr = next_tr; + } +} + +int get_error_count(void) { + return error_count; +} + +void inc_error_count(void) { + error_count++; +} + +int set_abort_on_error(int val) { + int old_val = abort_on_error; + abort_on_error = val; + + return old_val; +} + +int get_abort_on_error(void) { + return abort_on_error; +} + +int run_tests(int argc, char **args) { + memset(&test_main, 0, sizeof(test_main)); + int arg; + int incl_filter = 0; + int excl_filter = 0; + for (arg = 1; arg < argc; arg++) { + if (strlen(args[arg]) == 0) continue; + if (0 == strcmp("-f", args[arg])) { + incl_filter = 1; + continue; + } + if (0 == strcmp("-e", args[arg])) { + excl_filter = 1; + continue; + } + if (incl_filter) { + strcpy(test_main.incl_filter, args[arg]); + incl_filter = 0; + } else if (excl_filter) { + strcpy(test_main.excl_filter, args[arg]); + excl_filter = 0; + } else { + printf("running tests from %s\n", args[arg]); + FILE *fd = fopen(args[1], "r"); + if (fd == NULL) { + printf("%s not found\n", args[arg]); + return -2; + } + test_main.spec = fd; + } + } + + DBGT("adding suites...\n"); + add_suites(); + DBGT("%i tests added\n", test_main.test_count); + if (test_main.spec) { + fclose(test_main.spec); + } + + if (test_main.test_count == 0) { + printf("No tests to run\n"); + return 0; + } + + int fd_success = open("_tests_ok", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + int fd_bad = open("_tests_fail", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + + DBGT("running tests...\n"); + int ok = 0; + int failed = 0; + int stopped = 0; + test *cur_t = test_main.tests; + int i = 1; + while (cur_t) { + cur_t->setup(cur_t); + test *next_test = cur_t->_next; + DBGT("TEST %i/%i : running test %s\n", i, test_main.test_count, cur_t->name); + i++; + int start_error_count = get_error_count(); + int res = cur_t->f(cur_t); + if (res == TEST_RES_OK && get_error_count() != start_error_count) { + res = TEST_RES_FAIL; + } + cur_t->test_result = res; + int fd = res == TEST_RES_OK ? fd_success : fd_bad; + write(fd, cur_t->name, strlen(cur_t->name)); + write(fd, "\n", 1); + switch (res) { + case TEST_RES_OK: + ok++; + printf(" .. ok\n"); + break; + case TEST_RES_FAIL: + failed++; + printf(" .. FAILED\n"); + if (test_main.on_stop) test_main.on_stop(cur_t); + add_res(cur_t, &test_main.failed, &test_main.failed_last); + break; + case TEST_RES_ASSERT: + stopped++; + printf(" .. ABORTED\n"); + if (test_main.on_stop) test_main.on_stop(cur_t); + add_res(cur_t, &test_main.stopped, &test_main.stopped_last); + break; + } + cur_t->teardown(cur_t); + free(cur_t); + cur_t = next_test; + } + close(fd_success); + close(fd_bad); + DBGT("ran %i tests\n", test_main.test_count); + printf("Test report, %i tests\n", test_main.test_count); + printf("%i succeeded\n", ok); + printf("%i failed\n", failed); + dump_res(&test_main.failed); + printf("%i stopped\n", stopped); + dump_res(&test_main.stopped); + if (ok < test_main.test_count) { + printf("\nFAILED\n"); + return -1; + } else { + printf("\nALL TESTS OK\n"); + return 0; + } +} diff --git a/components/spiffs/spiffs/src/test/testrunner.h b/components/spiffs/spiffs/src/test/testrunner.h new file mode 100644 index 00000000..697fb095 --- /dev/null +++ b/components/spiffs/spiffs/src/test/testrunner.h @@ -0,0 +1,165 @@ +/* + * testrunner.h + * + * Created on: Jun 19, 2013 + * Author: petera + */ + +/* + +file mysuite.c: + +SUITE(mysuite) + +static void setup(test *t) {} + +static void teardown(test *t) {} + +TEST(mytest) { + printf("mytest runs now..\n"); + return 0; +} TEST_END + +SUITE_TESTS(mysuite) + ADD_TEST(mytest) +SUITE_END(mysuite) + + + +file mysuite2.c: + +SUITE(mysuite2) + +static void setup(test *t) {} + +static void teardown(test *t) {} + +TEST(mytest2a) { + printf("mytest2a runs now..\n"); + return 0; +} TEST_END + +TEST(mytest2b) { + printf("mytest2b runs now..\n"); + return 0; +} TEST_END + +SUITE_TESTS(mysuite2) + ADD_TEST(mytest2a) + ADD_TEST(mytest2b) +SUITE_END(mysuite2) + + +some other file.c: + +void add_suites() { + ADD_SUITE(mysuite); + ADD_SUITE(mysuite2); +} + */ + +#ifndef TESTRUNNER_H_ +#define TESTRUNNER_H_ + +#define TEST_RES_OK 0 +#define TEST_RES_FAIL -1 +#define TEST_RES_ASSERT -2 + +#define ERREXIT() if (get_abort_on_error()) abort(); else inc_error_count() + +struct test_s; + +typedef int (*test_f)(struct test_s *t); + +typedef struct test_s { + test_f f; + char name[256]; + void *data; + void (*setup)(struct test_s *t); + void (*teardown)(struct test_s *t); + struct test_s *_next; + unsigned char test_result; +} test; + +typedef struct test_res_s { + char name[256]; + struct test_res_s *_next; +} test_res; + +#define TEST_CHECK(x) if (!(x)) { \ + printf(" TEST FAIL %s:%d\n", __FILE__, __LINE__); \ + goto __fail_stop; \ +} +#define TEST_CHECK_EQ(x, y) if ((x) != (y)) { \ + printf(" TEST FAIL %s:%d, %d != %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \ + goto __fail_stop; \ +} +#define TEST_CHECK_NEQ(x, y) if ((x) == (y)) { \ + printf(" TEST FAIL %s:%d, %d == %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \ + goto __fail_stop; \ +} +#define TEST_CHECK_GT(x, y) if ((x) <= (y)) { \ + printf(" TEST FAIL %s:%d, %d <= %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \ + goto __fail_stop; \ +} +#define TEST_CHECK_LT(x, y) if ((x) >= (y)) { \ + printf(" TEST FAIL %s:%d, %d >= %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \ + goto __fail_stop; \ +} +#define TEST_CHECK_GE(x, y) if ((x) < (y)) { \ + printf(" TEST FAIL %s:%d, %d < %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \ + goto __fail_stop; \ +} +#define TEST_CHECK_LE(x, y) if ((x) > (y)) { \ + printf(" TEST FAIL %s:%d, %d > %d\n", __FILE__, __LINE__, (int)(x), (int)(y)); \ + goto __fail_stop; \ +} +#define TEST_ASSERT(x) if (!(x)) { \ + printf(" TEST ASSERT %s:%d\n", __FILE__, __LINE__); \ + goto __fail_assert; \ +} + +#define DBGT(...) printf(__VA_ARGS__) + +#define str(s) #s + +#define SUITE(sui) + +#define SUITE_TESTS(sui) \ + void _add_suite_tests_##sui(void) { + +#define SUITE_END(sui) \ + } + +#define ADD_TEST(tf) \ + _add_test(__test_##tf, str(tf), setup, teardown, 0); + +#define ADD_TEST_NON_DEFAULT(tf) \ + _add_test(__test_##tf, str(tf), setup, teardown, 1); + +#define ADD_SUITE(sui) \ + extern void _add_suite_tests_##sui(void); \ + _add_suite_tests_##sui(); + +#define TEST(tf) \ + static int __test_##tf(struct test_s *t) { do + +#define TEST_END \ + while(0); \ + __fail_stop: return TEST_RES_FAIL; \ + __fail_assert: return TEST_RES_ASSERT; \ + } + +int set_abort_on_error(int val); +int get_abort_on_error(void); +int get_error_count(void); +void inc_error_count(void); + +void add_suites(void); +void test_init(void (*on_stop)(test *t)); +// returns 0 if all tests ok, -1 if any test failed, -2 on badness +int run_tests(int argc, char **args); +void _add_suite(const char *suite_name); +void _add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t), int non_default); + +#endif /* TESTRUNNER_H_ */ diff --git a/components/spiffs/spiffs/src/test/testsuites.c b/components/spiffs/spiffs/src/test/testsuites.c new file mode 100644 index 00000000..cce5cd9d --- /dev/null +++ b/components/spiffs/spiffs/src/test/testsuites.c @@ -0,0 +1,15 @@ +/* + * testsuites.c + * + * Created on: Jun 19, 2013 + * Author: petera + */ + +#include "testrunner.h" + +void add_suites(void) { + //ADD_SUITE(dev_tests); + ADD_SUITE(check_tests); + ADD_SUITE(hydrogen_tests); + ADD_SUITE(bug_tests); +} diff --git a/components/spiffs/spiffs_api.c b/components/spiffs/spiffs_api.c new file mode 100644 index 00000000..01c256d0 --- /dev/null +++ b/components/spiffs/spiffs_api.c @@ -0,0 +1,93 @@ +// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "freertos/FreeRTOS.h" +#include "esp_log.h" +#include "esp_partition.h" +#include "esp_spiffs.h" +#include "esp_vfs.h" +#include "spiffs_api.h" + +static const char* TAG = "SPIFFS"; + +void spiffs_api_lock(spiffs *fs) +{ + (void) xSemaphoreTake(((esp_spiffs_t *)(fs->user_data))->lock, portMAX_DELAY); +} + +void spiffs_api_unlock(spiffs *fs) +{ + xSemaphoreGive(((esp_spiffs_t *)(fs->user_data))->lock); +} + +s32_t spiffs_api_read(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *dst) +{ + esp_err_t err = esp_partition_read(((esp_spiffs_t *)(fs->user_data))->partition, + addr, dst, size); + if (err) { + ESP_LOGE(TAG, "failed to read addr %08x, size %08x, err %d", addr, size, err); + return -1; + } + return 0; +} + +s32_t spiffs_api_write(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *src) +{ + esp_err_t err = esp_partition_write(((esp_spiffs_t *)(fs->user_data))->partition, + addr, src, size); + if (err) { + ESP_LOGE(TAG, "failed to write addr %08x, size %08x, err %d", addr, size, err); + return -1; + } + return 0; +} + +s32_t spiffs_api_erase(spiffs *fs, uint32_t addr, uint32_t size) +{ + esp_err_t err = esp_partition_erase_range(((esp_spiffs_t *)(fs->user_data))->partition, + addr, size); + if (err) { + ESP_LOGE(TAG, "failed to erase addr %08x, size %08x, err %d", addr, size, err); + return -1; + } + return 0; +} + +void spiffs_api_check(spiffs *fs, spiffs_check_type type, + spiffs_check_report report, uint32_t arg1, uint32_t arg2) +{ + static const char * spiffs_check_type_str[3] = { + "LOOKUP", + "INDEX", + "PAGE" + }; + + static const char * spiffs_check_report_str[7] = { + "PROGRESS", + "ERROR", + "FIX INDEX", + "FIX LOOKUP", + "DELETE ORPHANED INDEX", + "DELETE PAGE", + "DELETE BAD FILE" + }; + + if (report != SPIFFS_CHECK_PROGRESS) { + ESP_LOGE(TAG, "CHECK: type:%s, report:%s, %x:%x", spiffs_check_type_str[type], + spiffs_check_report_str[report], arg1, arg2); + } else { + ESP_LOGV(TAG, "CHECK PROGRESS: report:%s, %x:%x", + spiffs_check_report_str[report], arg1, arg2); + } +} diff --git a/components/spiffs/spiffs_api.h b/components/spiffs/spiffs_api.h new file mode 100644 index 00000000..5009716a --- /dev/null +++ b/components/spiffs/spiffs_api.h @@ -0,0 +1,57 @@ +// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "spiffs.h" +#include "esp_vfs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief SPIFFS definition structure + */ +typedef struct { + spiffs *fs; /*!< Handle to the underlying SPIFFS */ + SemaphoreHandle_t lock; /*!< FS lock */ + const esp_partition_t* partition; /*!< The partition on which SPIFFS is located */ + char base_path[ESP_VFS_PATH_MAX+1]; /*!< Mount point */ + bool by_label; /*!< Partition was mounted by label */ + spiffs_config cfg; /*!< SPIFFS Mount configuration */ + uint8_t *work; /*!< Work Buffer */ + uint8_t *fds; /*!< File Descriptor Buffer */ + uint32_t fds_sz; /*!< File Descriptor Buffer Length */ + uint8_t *cache; /*!< Cache Buffer */ + uint32_t cache_sz; /*!< Cache Buffer Length */ +} esp_spiffs_t; + +s32_t spiffs_api_read(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *dst); + +s32_t spiffs_api_write(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *src); + +s32_t spiffs_api_erase(spiffs *fs, uint32_t addr, uint32_t size); + +void spiffs_api_check(spiffs *fs, spiffs_check_type type, + spiffs_check_report report, uint32_t arg1, uint32_t arg2); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/spiffs/test/CMakeLists.txt b/components/spiffs/test/CMakeLists.txt new file mode 100644 index 00000000..860ab6df --- /dev/null +++ b/components/spiffs/test/CMakeLists.txt @@ -0,0 +1,6 @@ +set(COMPONENT_SRCDIRS ".") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +set(COMPONENT_REQUIRES unity spiffs) + +register_component() \ No newline at end of file diff --git a/components/spiffs/test/component.mk b/components/spiffs/test/component.mk new file mode 100644 index 00000000..ce464a21 --- /dev/null +++ b/components/spiffs/test/component.mk @@ -0,0 +1 @@ +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/spiffs/test/test_spiffs.c b/components/spiffs/test/test_spiffs.c new file mode 100644 index 00000000..f3592132 --- /dev/null +++ b/components/spiffs/test/test_spiffs.c @@ -0,0 +1,652 @@ +// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include "unity.h" +#include "test_utils.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_vfs.h" +#include "esp_spiffs.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "esp_partition.h" + +const char* spiffs_test_hello_str = "Hello, World!\n"; +const char* spiffs_test_partition_label = "flash_test"; + +void test_spiffs_create_file_with_text(const char* name, const char* text) +{ + FILE* f = fopen(name, "wb"); + TEST_ASSERT_NOT_NULL(f); + TEST_ASSERT_TRUE(fputs(text, f) != EOF); + TEST_ASSERT_EQUAL(0, fclose(f)); +} + +void test_spiffs_overwrite_append(const char* filename) +{ + /* Create new file with 'aaaa' */ + test_spiffs_create_file_with_text(filename, "aaaa"); + + /* Append 'bbbb' to file */ + FILE *f_a = fopen(filename, "a"); + TEST_ASSERT_NOT_NULL(f_a); + TEST_ASSERT_NOT_EQUAL(EOF, fputs("bbbb", f_a)); + TEST_ASSERT_EQUAL(0, fclose(f_a)); + + /* Read back 8 bytes from file, verify it's 'aaaabbbb' */ + char buf[10] = { 0 }; + FILE *f_r = fopen(filename, "r"); + TEST_ASSERT_NOT_NULL(f_r); + TEST_ASSERT_EQUAL(8, fread(buf, 1, 8, f_r)); + TEST_ASSERT_EQUAL_STRING_LEN("aaaabbbb", buf, 8); + + /* Be sure we're at end of file */ + TEST_ASSERT_EQUAL(0, fread(buf, 1, 8, f_r)); + + TEST_ASSERT_EQUAL(0, fclose(f_r)); + + /* Overwrite file with 'cccc' */ + test_spiffs_create_file_with_text(filename, "cccc"); + + /* Verify file now only contains 'cccc' */ + f_r = fopen(filename, "r"); + TEST_ASSERT_NOT_NULL(f_r); + bzero(buf, sizeof(buf)); + TEST_ASSERT_EQUAL(4, fread(buf, 1, 8, f_r)); // trying to read 8 bytes, only expecting 4 + TEST_ASSERT_EQUAL_STRING_LEN("cccc", buf, 4); + TEST_ASSERT_EQUAL(0, fclose(f_r)); +} + +void test_spiffs_read_file(const char* filename) +{ + FILE* f = fopen(filename, "r"); + TEST_ASSERT_NOT_NULL(f); + char buf[32] = { 0 }; + int cb = fread(buf, 1, sizeof(buf), f); + TEST_ASSERT_EQUAL(strlen(spiffs_test_hello_str), cb); + TEST_ASSERT_EQUAL(0, strcmp(spiffs_test_hello_str, buf)); + TEST_ASSERT_EQUAL(0, fclose(f)); +} + +void test_spiffs_open_max_files(const char* filename_prefix, size_t files_count) +{ + FILE** files = calloc(files_count, sizeof(FILE*)); + for (size_t i = 0; i < files_count; ++i) { + char name[32]; + snprintf(name, sizeof(name), "%s_%d.txt", filename_prefix, i); + files[i] = fopen(name, "w"); + TEST_ASSERT_NOT_NULL(files[i]); + } + /* close everything and clean up */ + for (size_t i = 0; i < files_count; ++i) { + fclose(files[i]); + } + free(files); +} + +void test_spiffs_lseek(const char* filename) +{ + FILE* f = fopen(filename, "wb+"); + TEST_ASSERT_NOT_NULL(f); + TEST_ASSERT_EQUAL(11, fprintf(f, "0123456789\n")); + TEST_ASSERT_EQUAL(0, fseek(f, -2, SEEK_CUR)); + TEST_ASSERT_EQUAL('9', fgetc(f)); + TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_SET)); + TEST_ASSERT_EQUAL('3', fgetc(f)); + TEST_ASSERT_EQUAL(0, fseek(f, -3, SEEK_END)); + TEST_ASSERT_EQUAL('8', fgetc(f)); + TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END)); + TEST_ASSERT_EQUAL(11, ftell(f)); + TEST_ASSERT_EQUAL(4, fprintf(f, "abc\n")); + TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END)); + TEST_ASSERT_EQUAL(15, ftell(f)); + TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET)); + char buf[20]; + TEST_ASSERT_EQUAL(15, fread(buf, 1, sizeof(buf), f)); + const char ref_buf[] = "0123456789\nabc\n"; + TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1); + + TEST_ASSERT_EQUAL(0, fclose(f)); +} + +void test_spiffs_stat(const char* filename) +{ + test_spiffs_create_file_with_text(filename, "foo\n"); + struct stat st; + TEST_ASSERT_EQUAL(0, stat(filename, &st)); + TEST_ASSERT(st.st_mode & S_IFREG); + TEST_ASSERT_FALSE(st.st_mode & S_IFDIR); +} + +void test_spiffs_unlink(const char* filename) +{ + test_spiffs_create_file_with_text(filename, "unlink\n"); + + TEST_ASSERT_EQUAL(0, unlink(filename)); + + TEST_ASSERT_NULL(fopen(filename, "r")); +} + +void test_spiffs_rename(const char* filename_prefix) +{ + char name_dst[64]; + char name_src[64]; + snprintf(name_dst, sizeof(name_dst), "%s_dst.txt", filename_prefix); + snprintf(name_src, sizeof(name_src), "%s_src.txt", filename_prefix); + + unlink(name_dst); + unlink(name_src); + + FILE* f = fopen(name_src, "w+"); + TEST_ASSERT_NOT_NULL(f); + const char* str = "0123456789"; + for (int i = 0; i < 400; ++i) { + TEST_ASSERT_NOT_EQUAL(EOF, fputs(str, f)); + } + TEST_ASSERT_EQUAL(0, fclose(f)); + TEST_ASSERT_EQUAL(0, rename(name_src, name_dst)); + TEST_ASSERT_NULL(fopen(name_src, "r")); + FILE* fdst = fopen(name_dst, "r"); + TEST_ASSERT_NOT_NULL(fdst); + TEST_ASSERT_EQUAL(0, fseek(fdst, 0, SEEK_END)); + TEST_ASSERT_EQUAL(4000, ftell(fdst)); + TEST_ASSERT_EQUAL(0, fclose(fdst)); +} + +void test_spiffs_can_opendir(const char* path) +{ + char name_dir_file[64]; + const char * file_name = "test_opd.txt"; + snprintf(name_dir_file, sizeof(name_dir_file), "%s/%s", path, file_name); + unlink(name_dir_file); + test_spiffs_create_file_with_text(name_dir_file, "test_opendir\n"); + DIR* dir = opendir(path); + TEST_ASSERT_NOT_NULL(dir); + bool found = false; + while (true) { + struct dirent* de = readdir(dir); + if (!de) { + break; + } + if (strcasecmp(de->d_name, file_name) == 0) { + found = true; + break; + } + } + TEST_ASSERT_TRUE(found); + TEST_ASSERT_EQUAL(0, closedir(dir)); + unlink(name_dir_file); +} + +void test_spiffs_opendir_readdir_rewinddir(const char* dir_prefix) +{ + char name_dir_inner_file[64]; + char name_dir_inner[64]; + char name_dir_file3[64]; + char name_dir_file2[64]; + char name_dir_file1[64]; + + snprintf(name_dir_inner_file, sizeof(name_dir_inner_file), "%s/inner/3.txt", dir_prefix); + snprintf(name_dir_inner, sizeof(name_dir_inner), "%s/inner", dir_prefix); + snprintf(name_dir_file3, sizeof(name_dir_file2), "%s/boo.bin", dir_prefix); + snprintf(name_dir_file2, sizeof(name_dir_file2), "%s/2.txt", dir_prefix); + snprintf(name_dir_file1, sizeof(name_dir_file1), "%s/1.txt", dir_prefix); + + unlink(name_dir_inner_file); + rmdir(name_dir_inner); + unlink(name_dir_file1); + unlink(name_dir_file2); + unlink(name_dir_file3); + rmdir(dir_prefix); + + test_spiffs_create_file_with_text(name_dir_file1, "1\n"); + test_spiffs_create_file_with_text(name_dir_file2, "2\n"); + test_spiffs_create_file_with_text(name_dir_file3, "\01\02\03"); + test_spiffs_create_file_with_text(name_dir_inner_file, "3\n"); + + DIR* dir = opendir(dir_prefix); + TEST_ASSERT_NOT_NULL(dir); + int count = 0; + const char* names[4]; + while(count < 4) { + struct dirent* de = readdir(dir); + if (!de) { + break; + } + printf("found '%s'\n", de->d_name); + if (strcasecmp(de->d_name, "1.txt") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "1.txt"; + ++count; + } else if (strcasecmp(de->d_name, "2.txt") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "2.txt"; + ++count; + } else if (strcasecmp(de->d_name, "inner/3.txt") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "inner/3.txt"; + ++count; + } else if (strcasecmp(de->d_name, "boo.bin") == 0) { + TEST_ASSERT_TRUE(de->d_type == DT_REG); + names[count] = "boo.bin"; + ++count; + } else { + TEST_FAIL_MESSAGE("unexpected directory entry"); + } + } + TEST_ASSERT_EQUAL(count, 4); + + rewinddir(dir); + struct dirent* de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0])); + seekdir(dir, 3); + de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3])); + seekdir(dir, 1); + de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1])); + seekdir(dir, 2); + de = readdir(dir); + TEST_ASSERT_NOT_NULL(de); + TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2])); + + TEST_ASSERT_EQUAL(0, closedir(dir)); +} + +void test_spiffs_readdir_many_files(const char* dir_prefix) +{ + const int n_files = 40; + const int n_folders = 4; + unsigned char file_count[n_files * n_folders]; + memset(file_count, 0, sizeof(file_count)/sizeof(file_count[0])); + char file_name[ESP_VFS_PATH_MAX + CONFIG_SPIFFS_OBJ_NAME_LEN]; + + /* clean stale files before the test */ + DIR* dir = opendir(dir_prefix); + if (dir) { + while (true) { + struct dirent* de = readdir(dir); + if (!de) { + break; + } + int len = snprintf(file_name, sizeof(file_name), "%s/%s", dir_prefix, de->d_name); + assert(len < sizeof(file_name)); + unlink(file_name); + } + } + + /* create files */ + for (int d = 0; d < n_folders; ++d) { + printf("filling directory %d\n", d); + for (int f = 0; f < n_files; ++f) { + snprintf(file_name, sizeof(file_name), "%s/%d/%d.txt", dir_prefix, d, f); + test_spiffs_create_file_with_text(file_name, file_name); + } + } + + /* list files */ + for (int d = 0; d < n_folders; ++d) { + printf("listing files in directory %d\n", d); + snprintf(file_name, sizeof(file_name), "%s/%d", dir_prefix, d); + dir = opendir(file_name); + TEST_ASSERT_NOT_NULL(dir); + while (true) { + struct dirent* de = readdir(dir); + if (!de) { + break; + } + int file_id; + TEST_ASSERT_EQUAL(1, sscanf(de->d_name, "%d.txt", &file_id)); + file_count[file_id + d * n_files]++; + } + closedir(dir); + } + + /* check that all created files have been seen */ + for (int d = 0; d < n_folders; ++d) { + printf("checking that all files have been found in directory %d\n", d); + for (int f = 0; f < n_files; ++f) { + TEST_ASSERT_EQUAL(1, file_count[f + d * n_files]); + } + } +} + + +typedef struct { + const char* filename; + bool write; + size_t word_count; + int seed; + SemaphoreHandle_t done; + int result; +} read_write_test_arg_t; + +#define READ_WRITE_TEST_ARG_INIT(name, seed_) \ + { \ + .filename = name, \ + .seed = seed_, \ + .word_count = 4096, \ + .write = true, \ + .done = xSemaphoreCreateBinary() \ + } + +static void read_write_task(void* param) +{ + read_write_test_arg_t* args = (read_write_test_arg_t*) param; + FILE* f = fopen(args->filename, args->write ? "wb" : "rb"); + if (f == NULL) { + args->result = ESP_ERR_NOT_FOUND; + goto done; + } + + srand(args->seed); + for (size_t i = 0; i < args->word_count; ++i) { + uint32_t val = rand(); + if (args->write) { + int cnt = fwrite(&val, sizeof(val), 1, f); + if (cnt != 1) { + ets_printf("E(w): i=%d, cnt=%d val=%d\n\n", i, cnt, val); + args->result = ESP_FAIL; + goto close; + } + } else { + uint32_t rval; + int cnt = fread(&rval, sizeof(rval), 1, f); + if (cnt != 1) { + ets_printf("E(r): i=%d, cnt=%d rval=%d\n\n", i, cnt, rval); + args->result = ESP_FAIL; + goto close; + } + } + } + args->result = ESP_OK; + +close: + fclose(f); + +done: + xSemaphoreGive(args->done); + vTaskDelay(1); + vTaskDelete(NULL); +} + +void test_spiffs_concurrent(const char* filename_prefix) +{ + char names[4][64]; + for (size_t i = 0; i < 4; ++i) { + snprintf(names[i], sizeof(names[i]), "%s%d", filename_prefix, i + 1); + unlink(names[i]); + } + + read_write_test_arg_t args1 = READ_WRITE_TEST_ARG_INIT(names[0], 1); + read_write_test_arg_t args2 = READ_WRITE_TEST_ARG_INIT(names[1], 2); + + printf("writing f1 and f2\n"); + const int cpuid_0 = 0; + const int cpuid_1 = portNUM_PROCESSORS - 1; + xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, cpuid_0); + xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, cpuid_1); + + xSemaphoreTake(args1.done, portMAX_DELAY); + printf("f1 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args1.result); + xSemaphoreTake(args2.done, portMAX_DELAY); + printf("f2 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args2.result); + + args1.write = false; + args2.write = false; + read_write_test_arg_t args3 = READ_WRITE_TEST_ARG_INIT(names[2], 3); + read_write_test_arg_t args4 = READ_WRITE_TEST_ARG_INIT(names[3], 4); + + printf("reading f1 and f2, writing f3 and f4\n"); + + xTaskCreatePinnedToCore(&read_write_task, "rw3", 2048, &args3, 3, NULL, cpuid_1); + xTaskCreatePinnedToCore(&read_write_task, "rw4", 2048, &args4, 3, NULL, cpuid_0); + xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, cpuid_0); + xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, cpuid_1); + + xSemaphoreTake(args1.done, portMAX_DELAY); + printf("f1 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args1.result); + xSemaphoreTake(args2.done, portMAX_DELAY); + printf("f2 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args2.result); + xSemaphoreTake(args3.done, portMAX_DELAY); + printf("f3 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args3.result); + xSemaphoreTake(args4.done, portMAX_DELAY); + printf("f4 done\n"); + TEST_ASSERT_EQUAL(ESP_OK, args4.result); + + vSemaphoreDelete(args1.done); + vSemaphoreDelete(args2.done); + vSemaphoreDelete(args3.done); + vSemaphoreDelete(args4.done); +} + + +static void test_setup() +{ + esp_vfs_spiffs_conf_t conf = { + .base_path = "/spiffs", + .partition_label = spiffs_test_partition_label, + .max_files = 5, + .format_if_mount_failed = true + }; + + TEST_ESP_OK(esp_vfs_spiffs_register(&conf)); +} + +static void test_teardown() +{ + TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label)); +} + +TEST_CASE("can initialize SPIFFS in erased partition", "[spiffs]") +{ + const esp_partition_t* part = get_test_data_partition(); + TEST_ASSERT_NOT_NULL(part); + TEST_ESP_OK(esp_partition_erase_range(part, 0, part->size)); + test_setup(); + size_t total = 0, used = 0; + TEST_ESP_OK(esp_spiffs_info(spiffs_test_partition_label, &total, &used)); + printf("total: %d, used: %d\n", total, used); + TEST_ASSERT_EQUAL(0, used); + test_teardown(); +} + +TEST_CASE("can format mounted partition", "[spiffs]") +{ + // Mount SPIFFS, create file, format, check that the file does not exist. + const esp_partition_t* part = get_test_data_partition(); + TEST_ASSERT_NOT_NULL(part); + test_setup(); + const char* filename = "/spiffs/hello.txt"; + test_spiffs_create_file_with_text(filename, spiffs_test_hello_str); + esp_spiffs_format(part->label); + FILE* f = fopen(filename, "r"); + TEST_ASSERT_NULL(f); + test_teardown(); +} + +TEST_CASE("can format unmounted partition", "[spiffs]") +{ + // Mount SPIFFS, create file, unmount. Format. Mount again, check that + // the file does not exist. + const esp_partition_t* part = get_test_data_partition(); + TEST_ASSERT_NOT_NULL(part); + test_setup(); + const char* filename = "/spiffs/hello.txt"; + test_spiffs_create_file_with_text(filename, spiffs_test_hello_str); + test_teardown(); + esp_spiffs_format(part->label); + // Don't use test_setup here, need to mount without formatting + esp_vfs_spiffs_conf_t conf = { + .base_path = "/spiffs", + .partition_label = spiffs_test_partition_label, + .max_files = 5, + .format_if_mount_failed = false + }; + TEST_ESP_OK(esp_vfs_spiffs_register(&conf)); + FILE* f = fopen(filename, "r"); + TEST_ASSERT_NULL(f); + test_teardown(); +} + +TEST_CASE("can create and write file", "[spiffs]") +{ + test_setup(); + test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str); + test_teardown(); +} + +TEST_CASE("can read file", "[spiffs]") +{ + test_setup(); + test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str); + test_spiffs_read_file("/spiffs/hello.txt"); + test_teardown(); +} + +TEST_CASE("can open maximum number of files", "[spiffs]") +{ + size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */ + esp_vfs_spiffs_conf_t conf = { + .base_path = "/spiffs", + .partition_label = spiffs_test_partition_label, + .format_if_mount_failed = true, + .max_files = max_files + }; + TEST_ESP_OK(esp_vfs_spiffs_register(&conf)); + test_spiffs_open_max_files("/spiffs/f", max_files); + TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label)); +} + +TEST_CASE("overwrite and append file", "[spiffs]") +{ + test_setup(); + test_spiffs_overwrite_append("/spiffs/hello.txt"); + test_teardown(); +} + +TEST_CASE("can lseek", "[spiffs]") +{ + test_setup(); + test_spiffs_lseek("/spiffs/seek.txt"); + test_teardown(); +} + + +TEST_CASE("stat returns correct values", "[spiffs]") +{ + test_setup(); + test_spiffs_stat("/spiffs/stat.txt"); + test_teardown(); +} + +TEST_CASE("unlink removes a file", "[spiffs]") +{ + test_setup(); + test_spiffs_unlink("/spiffs/unlink.txt"); + test_teardown(); +} + +TEST_CASE("rename moves a file", "[spiffs]") +{ + test_setup(); + test_spiffs_rename("/spiffs/move"); + test_teardown(); +} + +TEST_CASE("can opendir root directory of FS", "[spiffs]") +{ + test_setup(); + test_spiffs_can_opendir("/spiffs"); + test_teardown(); +} + +TEST_CASE("opendir, readdir, rewinddir, seekdir work as expected", "[spiffs]") +{ + test_setup(); + test_spiffs_opendir_readdir_rewinddir("/spiffs/dir"); + test_teardown(); +} + +TEST_CASE("readdir with large number of files", "[spiffs][timeout=30]") +{ + test_setup(); + test_spiffs_readdir_many_files("/spiffs/dir2"); + test_teardown(); +} + +TEST_CASE("multiple tasks can use same volume", "[spiffs]") +{ + test_setup(); + test_spiffs_concurrent("/spiffs/f"); + test_teardown(); +} + +#ifdef CONFIG_SPIFFS_USE_MTIME +TEST_CASE("mtime is updated when file is opened", "[spiffs]") +{ + /* Open a file, check that mtime is set correctly */ + const char* filename = "/spiffs/time"; + test_setup(); + time_t t_before_create = time(NULL); + test_spiffs_create_file_with_text(filename, "\n"); + time_t t_after_create = time(NULL); + + struct stat st; + TEST_ASSERT_EQUAL(0, stat(filename, &st)); + printf("mtime=%d\n", (int) st.st_mtime); + TEST_ASSERT(st.st_mtime >= t_before_create + && st.st_mtime <= t_after_create); + + /* Wait a bit, open again, check that mtime is updated */ + vTaskDelay(2000 / portTICK_PERIOD_MS); + time_t t_before_open = time(NULL); + FILE *f = fopen(filename, "a"); + time_t t_after_open = time(NULL); + TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st)); + printf("mtime=%d\n", (int) st.st_mtime); + TEST_ASSERT(st.st_mtime >= t_before_open + && st.st_mtime <= t_after_open); + fclose(f); + + /* Wait a bit, open for reading, check that mtime is not updated */ + vTaskDelay(2000 / portTICK_PERIOD_MS); + time_t t_before_open_ro = time(NULL); + f = fopen(filename, "r"); + TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st)); + printf("mtime=%d\n", (int) st.st_mtime); + TEST_ASSERT(t_before_open_ro > t_after_open + && st.st_mtime >= t_before_open + && st.st_mtime <= t_after_open); + fclose(f); + + test_teardown(); +} +#endif // CONFIG_SPIFFS_USE_MTIME diff --git a/components/spiffs/test_spiffs_host/Makefile b/components/spiffs/test_spiffs_host/Makefile new file mode 100644 index 00000000..b0e694f2 --- /dev/null +++ b/components/spiffs/test_spiffs_host/Makefile @@ -0,0 +1,98 @@ +ifndef COMPONENT +COMPONENT := spiffs +endif + +COMPONENT_LIB := lib$(COMPONENT).a +TEST_PROGRAM := test_$(COMPONENT) + +STUBS_LIB_DIR := ../../../components/spi_flash/sim/stubs +STUBS_LIB_BUILD_DIR := $(STUBS_LIB_DIR)/build +STUBS_LIB := libstubs.a + +SPI_FLASH_SIM_DIR := ../../../components/spi_flash/sim +SPI_FLASH_SIM_BUILD_DIR := $(SPI_FLASH_SIM_DIR)/build +SPI_FLASH_SIM_LIB := libspi_flash.a + +include Makefile.files + +all: test + +ifndef SDKCONFIG +SDKCONFIG_DIR := $(dir $(realpath sdkconfig/sdkconfig.h)) +SDKCONFIG := $(SDKCONFIG_DIR)sdkconfig.h +else +SDKCONFIG_DIR := $(dir $(realpath $(SDKCONFIG))) +endif + +INCLUDE_FLAGS := $(addprefix -I, $(INCLUDE_DIRS) $(SDKCONFIG_DIR) ../../../tools/catch) + +CPPFLAGS += $(INCLUDE_FLAGS) -g -m32 +CXXFLAGS += $(INCLUDE_FLAGS) -std=c++11 -g -m32 + +# Build libraries that this component is dependent on +$(STUBS_LIB_BUILD_DIR)/$(STUBS_LIB): force + $(MAKE) -C $(STUBS_LIB_DIR) lib SDKCONFIG=$(SDKCONFIG) + +$(SPI_FLASH_SIM_BUILD_DIR)/$(SPI_FLASH_SIM_LIB): force + $(MAKE) -C $(SPI_FLASH_SIM_DIR) lib SDKCONFIG=$(SDKCONFIG) + +# Create target for building this component as a library +CFILES := $(filter %.c, $(SOURCE_FILES)) +CPPFILES := $(filter %.cpp, $(SOURCE_FILES)) + +CTARGET = ${2}/$(patsubst %.c,%.o,$(notdir ${1})) +CPPTARGET = ${2}/$(patsubst %.cpp,%.o,$(notdir ${1})) + +ifndef BUILD_DIR +BUILD_DIR := build +endif + +OBJ_FILES := $(addprefix $(BUILD_DIR)/, $(filter %.o, $(notdir $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o)))) + +define COMPILE_C +$(call CTARGET, ${1}, $(BUILD_DIR)) : ${1} $(SDKCONFIG) + mkdir -p $(BUILD_DIR) + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(call CTARGET, ${1}, $(BUILD_DIR)) ${1} +endef + +define COMPILE_CPP +$(call CPPTARGET, ${1}, $(BUILD_DIR)) : ${1} $(SDKCONFIG) + mkdir -p $(BUILD_DIR) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $(call CPPTARGET, ${1}, $(BUILD_DIR)) ${1} +endef + +$(BUILD_DIR)/$(COMPONENT_LIB): $(OBJ_FILES) $(SDKCONFIG) + mkdir -p $(BUILD_DIR) + $(AR) rcs $@ $^ + +clean: + $(MAKE) -C $(STUBS_LIB_DIR) clean + $(MAKE) -C $(SPI_FLASH_SIM_DIR) clean + rm -f $(OBJ_FILES) $(TEST_OBJ_FILES) $(TEST_PROGRAM) $(COMPONENT_LIB) partition_table.bin + +lib: $(BUILD_DIR)/$(COMPONENT_LIB) + +$(foreach cfile, $(CFILES), $(eval $(call COMPILE_C, $(cfile)))) +$(foreach cxxfile, $(CPPFILES), $(eval $(call COMPILE_CPP, $(cxxfile)))) + +# Create target for building this component as a test +TEST_SOURCE_FILES = \ + test_spiffs.cpp \ + main.cpp \ + test_utils.c + +TEST_OBJ_FILES = $(filter %.o, $(TEST_SOURCE_FILES:.cpp=.o) $(TEST_SOURCE_FILES:.c=.o)) + +$(TEST_PROGRAM): lib $(TEST_OBJ_FILES) $(SPI_FLASH_SIM_BUILD_DIR)/$(SPI_FLASH_SIM_LIB) $(STUBS_LIB_BUILD_DIR)/$(STUBS_LIB) partition_table.bin $(SDKCONFIG) + g++ $(LDFLAGS) $(CXXFLAGS) -o $@ $(TEST_OBJ_FILES) -L$(BUILD_DIR) -l:$(COMPONENT_LIB) -L$(SPI_FLASH_SIM_BUILD_DIR) -l:$(SPI_FLASH_SIM_LIB) -L$(STUBS_LIB_BUILD_DIR) -l:$(STUBS_LIB) + +test: $(TEST_PROGRAM) + ./$(TEST_PROGRAM) + +# Create other necessary targets +partition_table.bin: partition_table.csv + python ../../../components/partition_table/gen_esp32part.py --verify $< $@ + +force: + +.PHONY: all lib test clean force diff --git a/components/spiffs/test_spiffs_host/Makefile.files b/components/spiffs/test_spiffs_host/Makefile.files new file mode 100644 index 00000000..06858bc1 --- /dev/null +++ b/components/spiffs/test_spiffs_host/Makefile.files @@ -0,0 +1,33 @@ +SOURCE_FILES := \ + ../spiffs_api.c \ + $(addprefix ../spiffs/src/, \ + spiffs_cache.c \ + spiffs_check.c \ + spiffs_gc.c \ + spiffs_hydrogen.c \ + spiffs_nucleus.c \ + ) + +INCLUDE_DIRS := \ + . \ + .. \ + ../spiffs/src \ + ../include \ + $(addprefix ../../spi_flash/sim/stubs/, \ + app_update/include \ + driver/include \ + esp32/include \ + freertos/include \ + log/include \ + newlib/include \ + sdmmc/include \ + vfs/include \ + ) \ + $(addprefix ../../../components/, \ + soc/esp32/include \ + esp32/include \ + bootloader_support/include \ + app_update/include \ + spi_flash/include \ + wear_levelling/include \ + ) diff --git a/components/spiffs/test_spiffs_host/component.mk b/components/spiffs/test_spiffs_host/component.mk new file mode 100644 index 00000000..82dea0b0 --- /dev/null +++ b/components/spiffs/test_spiffs_host/component.mk @@ -0,0 +1,17 @@ +include $(COMPONENT_PATH)/Makefile.files + +COMPONENT_OWNBUILDTARGET := 1 +COMPONENT_OWNCLEANTARGET := 1 + +COMPONENT_ADD_INCLUDEDIRS := $(INCLUDE_DIRS) + +.PHONY: build +build: $(SDKCONFIG_HEADER) + $(MAKE) -C $(COMPONENT_PATH) lib SDKCONFIG=$(SDKCONFIG_HEADER) BUILD_DIR=$(COMPONENT_BUILD_DIR) COMPONENT=$(COMPONENT_NAME) + +CLEAN_FILES := component_project_vars.mk +.PHONY: clean +clean: + $(summary) RM $(CLEAN_FILES) + rm -f $(CLEAN_FILES) + $(MAKE) -C $(COMPONENT_PATH) clean SDKCONFIG=$(SDKCONFIG_HEADER) BUILD_DIR=$(COMPONENT_BUILD_DIR) COMPONENT=$(COMPONENT_NAME) \ No newline at end of file diff --git a/components/spiffs/test_spiffs_host/main.cpp b/components/spiffs/test_spiffs_host/main.cpp new file mode 100644 index 00000000..178916ea --- /dev/null +++ b/components/spiffs/test_spiffs_host/main.cpp @@ -0,0 +1,3 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + diff --git a/components/spiffs/test_spiffs_host/partition_table.csv b/components/spiffs/test_spiffs_host/partition_table.csv new file mode 100644 index 00000000..d02771b5 --- /dev/null +++ b/components/spiffs/test_spiffs_host/partition_table.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, +storage, data, spiffs, , 2M, diff --git a/components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h b/components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h new file mode 100644 index 00000000..44966c55 --- /dev/null +++ b/components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h @@ -0,0 +1,19 @@ +#pragma once + +#define CONFIG_SPIFFS_USE_MAGIC_LENGTH 1 +#define CONFIG_SPIFFS_MAX_PARTITIONS 3 +#define CONFIG_SPIFFS_OBJ_NAME_LEN 32 +#define CONFIG_SPIFFS_PAGE_SIZE 256 +#define CONFIG_SPIFFS_GC_MAX_RUNS 10 +#define CONFIG_SPIFFS_CACHE_WR 1 +#define CONFIG_SPIFFS_CACHE 1 +#define CONFIG_SPIFFS_META_LENGTH 4 +#define CONFIG_SPIFFS_USE_MAGIC 1 +#define CONFIG_SPIFFS_PAGE_CHECK 1 +#define CONFIG_SPIFFS_USE_MTIME 1 + +#define CONFIG_WL_SECTOR_SIZE 4096 +#define CONFIG_LOG_DEFAULT_LEVEL 3 +#define CONFIG_PARTITION_TABLE_OFFSET 0x8000 + +#define CONFIG_ESPTOOLPY_FLASHSIZE "8MB" diff --git a/components/spiffs/test_spiffs_host/test_spiffs.cpp b/components/spiffs/test_spiffs_host/test_spiffs.cpp new file mode 100644 index 00000000..fa3936c2 --- /dev/null +++ b/components/spiffs/test_spiffs_host/test_spiffs.cpp @@ -0,0 +1,107 @@ +#include +#include +#include + +#include "esp_partition.h" +#include "spiffs.h" +#include "spiffs_nucleus.h" +#include "spiffs_api.h" + +#include "catch.hpp" + +extern "C" void init_spi_flash(const char* chip_size, size_t block_size, size_t sector_size, size_t page_size, const char* partition_bin); + +TEST_CASE("format disk, open file, write and read file", "[spiffs]") +{ + init_spi_flash(CONFIG_ESPTOOLPY_FLASHSIZE, CONFIG_WL_SECTOR_SIZE * 16, CONFIG_WL_SECTOR_SIZE, CONFIG_WL_SECTOR_SIZE, "partition_table.bin"); + + spiffs fs; + spiffs_config cfg; + + const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, "storage"); + + // Configure objects needed by SPIFFS + esp_spiffs_t esp_user_data; + esp_user_data.partition = partition; + fs.user_data = (void*)&esp_user_data; + + cfg.hal_erase_f = spiffs_api_erase; + cfg.hal_read_f = spiffs_api_read; + cfg.hal_write_f = spiffs_api_write; + cfg.log_block_size = CONFIG_WL_SECTOR_SIZE; + cfg.log_page_size = CONFIG_SPIFFS_PAGE_SIZE; + cfg.phys_addr = 0; + cfg.phys_erase_block = CONFIG_WL_SECTOR_SIZE; + cfg.phys_size = partition->size; + + uint32_t max_files = 5; + + uint32_t fds_sz = max_files * sizeof(spiffs_fd); + uint32_t work_sz = cfg.log_page_size * 2; + uint32_t cache_sz = sizeof(spiffs_cache) + max_files * (sizeof(spiffs_cache_page) + + cfg.log_page_size); + + uint8_t *work = (uint8_t*) malloc(work_sz); + uint8_t *fds = (uint8_t*) malloc(fds_sz); + uint8_t *cache = (uint8_t*) malloc(cache_sz); + + s32_t spiffs_res; + + // Special mounting procedure: mount, format, mount as per + // https://github.com/pellepl/spiffs/wiki/Using-spiffs + spiffs_res = SPIFFS_mount(&fs, &cfg, work, fds, fds_sz, + cache, cache_sz, spiffs_api_check); + REQUIRE(spiffs_res == SPIFFS_ERR_NOT_A_FS); + + spiffs_res = SPIFFS_format(&fs); + REQUIRE(spiffs_res >= SPIFFS_OK); + + spiffs_res = SPIFFS_mount(&fs, &cfg, work, fds, fds_sz, + cache, cache_sz, spiffs_api_check); + REQUIRE(spiffs_res >= SPIFFS_OK); + + // Open test file + spiffs_res = SPIFFS_open(&fs, "test.txt", SPIFFS_O_CREAT | SPIFFS_O_RDWR, 0); + REQUIRE(spiffs_res >= SPIFFS_OK); + + // Generate data + spiffs_file file = spiffs_res; + + uint32_t data_size = 100000; + + char *data = (char*) malloc(data_size); + char *read = (char*) malloc(data_size); + + for(uint32_t i = 0; i < data_size; i += sizeof(i)) + { + *((uint32_t*)(data + i)) = i; + } + + s32_t bw; + + // Write data to file + spiffs_res = SPIFFS_write(&fs, file, (void*)data, data_size); + REQUIRE(spiffs_res >= SPIFFS_OK); + REQUIRE(spiffs_res == data_size); + + // Set the file object pointer to the beginning + spiffs_res = SPIFFS_lseek(&fs, file, 0, SPIFFS_SEEK_SET); + REQUIRE(spiffs_res >= SPIFFS_OK); + + // Read the file + spiffs_res = SPIFFS_read(&fs, file, (void*)read, data_size); + REQUIRE(spiffs_res >= SPIFFS_OK); + REQUIRE(spiffs_res == data_size); + + // Close the test file + spiffs_res = SPIFFS_close(&fs, file); + REQUIRE(spiffs_res >= SPIFFS_OK); + + REQUIRE(memcmp(data, read, data_size) == 0); + + // Unmount + SPIFFS_unmount(&fs); + + free(read); + free(data); +} diff --git a/components/spiffs/test_spiffs_host/test_utils.c b/components/spiffs/test_spiffs_host/test_utils.c new file mode 100644 index 00000000..3e4b0575 --- /dev/null +++ b/components/spiffs/test_spiffs_host/test_utils.c @@ -0,0 +1,7 @@ +#include "esp_spi_flash.h" +#include "esp_partition.h" + +void init_spi_flash(const char* chip_size, size_t block_size, size_t sector_size, size_t page_size, const char* partition_bin) +{ + spi_flash_init(chip_size, block_size, sector_size, page_size, partition_bin); +} diff --git a/tools/unit-test-app/components/unity/test_utils.c b/tools/unit-test-app/components/unity/test_utils.c index 36aae4c2..7834e697 100644 --- a/tools/unit-test-app/components/unity/test_utils.c +++ b/tools/unit-test-app/components/unity/test_utils.c @@ -13,6 +13,7 @@ // limitations under the License. #include +#include #include "unity.h" #include "test_utils.h" #include "rom/ets_sys.h" diff --git a/tools/unit-test-app/sdkconfig.defaults b/tools/unit-test-app/sdkconfig.defaults index ab100149..8f274aa1 100644 --- a/tools/unit-test-app/sdkconfig.defaults +++ b/tools/unit-test-app/sdkconfig.defaults @@ -5,3 +5,4 @@ CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_TASK_WDT= CONFIG_ENABLE_PTHREAD=y +CONFIG_USING_SPIFFS=y