mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-06-12 17:27:13 +08:00
feat(spiffs): Bring from esp-idf
Commit ID: 13018449
This commit is contained in:
components/spiffs
CMakeLists.txtKconfigcomponent.mkesp_spiffs.c
include
library
spiffs
spiffs_api.cspiffs_api.htest
test_spiffs_host
@ -1,7 +1,15 @@
|
|||||||
set(COMPONENT_ADD_INCLUDEDIRS include include/spiffs)
|
set(COMPONENT_ADD_INCLUDEDIRS "include")
|
||||||
set(COMPONENT_SRCDIRS library)
|
set(COMPONENT_PRIV_INCLUDEDIRS "." "spiffs/src")
|
||||||
|
set(COMPONENT_SRCS "esp_spiffs.c"
|
||||||
|
"spiffs_api.c"
|
||||||
|
"spiffs/src/spiffs_cache.c"
|
||||||
|
"spiffs/src/spiffs_check.c"
|
||||||
|
"spiffs/src/spiffs_gc.c"
|
||||||
|
"spiffs/src/spiffs_hydrogen.c"
|
||||||
|
"spiffs/src/spiffs_nucleus.c")
|
||||||
|
|
||||||
set(COMPONENT_REQUIRES freertos)
|
set(COMPONENT_REQUIRES spi_flash)
|
||||||
set(COMPONENT_PRIV_REQUIRES esp8266)
|
set(COMPONENT_PRIV_REQUIRES bootloader_support)
|
||||||
|
|
||||||
register_component()
|
register_component()
|
||||||
|
|
||||||
|
161
components/spiffs/Kconfig
Normal file
161
components/spiffs/Kconfig
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
menu "SPIFFS Configuration"
|
||||||
|
|
||||||
|
config SPIFFS_MAX_PARTITIONS
|
||||||
|
int "Maximum Number of Partitions"
|
||||||
|
default 3
|
||||||
|
range 1 10
|
||||||
|
help
|
||||||
|
Define maximum number of partitions that can be mounted.
|
||||||
|
|
||||||
|
menu "SPIFFS Cache Configuration"
|
||||||
|
config SPIFFS_CACHE
|
||||||
|
bool "Enable SPIFFS Cache"
|
||||||
|
default "y"
|
||||||
|
help
|
||||||
|
Enables/disable memory read caching of nucleus file system
|
||||||
|
operations.
|
||||||
|
|
||||||
|
config SPIFFS_CACHE_WR
|
||||||
|
bool "Enable SPIFFS Write Caching"
|
||||||
|
default "y"
|
||||||
|
depends on SPIFFS_CACHE
|
||||||
|
help
|
||||||
|
Enables memory write caching for file descriptors in hydrogen.
|
||||||
|
|
||||||
|
config SPIFFS_CACHE_STATS
|
||||||
|
bool "Enable SPIFFS Cache Statistics"
|
||||||
|
default "n"
|
||||||
|
depends on SPIFFS_CACHE
|
||||||
|
help
|
||||||
|
Enable/disable statistics on caching. Debug/test purpose only.
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
config SPIFFS_PAGE_CHECK
|
||||||
|
bool "Enable SPIFFS Page Check"
|
||||||
|
default "y"
|
||||||
|
help
|
||||||
|
Always check header of each accessed page to ensure consistent state.
|
||||||
|
If enabled it will increase number of reads from flash, especially
|
||||||
|
if cache is disabled.
|
||||||
|
|
||||||
|
config SPIFFS_GC_MAX_RUNS
|
||||||
|
int "Set Maximum GC Runs"
|
||||||
|
default 10
|
||||||
|
range 1 255
|
||||||
|
help
|
||||||
|
Define maximum number of GC runs to perform to reach desired free pages.
|
||||||
|
|
||||||
|
config SPIFFS_GC_STATS
|
||||||
|
bool "Enable SPIFFS GC Statistics"
|
||||||
|
default "n"
|
||||||
|
help
|
||||||
|
Enable/disable statistics on gc. Debug/test purpose only.
|
||||||
|
|
||||||
|
config SPIFFS_PAGE_SIZE
|
||||||
|
int "SPIFFS logical page size"
|
||||||
|
default 256
|
||||||
|
range 256 1024
|
||||||
|
help
|
||||||
|
Logical page size of SPIFFS partition, in bytes. Must be multiple
|
||||||
|
of flash page size (which is usually 256 bytes).
|
||||||
|
Larger page sizes reduce overhead when storing large files, and
|
||||||
|
improve filesystem performance when reading large files.
|
||||||
|
Smaller page sizes reduce overhead when storing small (< page size)
|
||||||
|
files.
|
||||||
|
|
||||||
|
config SPIFFS_OBJ_NAME_LEN
|
||||||
|
int "Set SPIFFS Maximum Name Length"
|
||||||
|
default 32
|
||||||
|
range 1 256
|
||||||
|
help
|
||||||
|
Object name maximum length. Note that this length include the
|
||||||
|
zero-termination character, meaning maximum string of characters
|
||||||
|
can at most be SPIFFS_OBJ_NAME_LEN - 1.
|
||||||
|
|
||||||
|
SPIFFS_OBJ_NAME_LEN + SPIFFS_META_LENGTH should not exceed
|
||||||
|
SPIFFS_PAGE_SIZE - 64.
|
||||||
|
|
||||||
|
config SPIFFS_USE_MAGIC
|
||||||
|
bool "Enable SPIFFS Filesystem Magic"
|
||||||
|
default "y"
|
||||||
|
help
|
||||||
|
Enable this to have an identifiable spiffs filesystem.
|
||||||
|
This will look for a magic in all sectors to determine if this
|
||||||
|
is a valid spiffs system or not at mount time.
|
||||||
|
|
||||||
|
config SPIFFS_USE_MAGIC_LENGTH
|
||||||
|
bool "Enable SPIFFS Filesystem Length Magic"
|
||||||
|
default "y"
|
||||||
|
depends on SPIFFS_USE_MAGIC
|
||||||
|
help
|
||||||
|
If this option is enabled, the magic will also be dependent
|
||||||
|
on the length of the filesystem. For example, a filesystem
|
||||||
|
configured and formatted for 4 megabytes will not be accepted
|
||||||
|
for mounting with a configuration defining the filesystem as 2 megabytes.
|
||||||
|
|
||||||
|
config SPIFFS_META_LENGTH
|
||||||
|
int "Size of per-file metadata field"
|
||||||
|
default 4
|
||||||
|
help
|
||||||
|
This option sets the number of extra bytes stored in the file header.
|
||||||
|
These bytes can be used in an application-specific manner.
|
||||||
|
Set this to at least 4 bytes to enable support for saving file
|
||||||
|
modification time.
|
||||||
|
|
||||||
|
SPIFFS_OBJ_NAME_LEN + SPIFFS_META_LENGTH should not exceed
|
||||||
|
SPIFFS_PAGE_SIZE - 64.
|
||||||
|
|
||||||
|
config SPIFFS_USE_MTIME
|
||||||
|
bool "Save file modification time"
|
||||||
|
default "y"
|
||||||
|
depends on SPIFFS_META_LENGTH >= 4
|
||||||
|
help
|
||||||
|
If enabled, then the first 4 bytes of per-file metadata will be used
|
||||||
|
to store file modification time (mtime), accessible through
|
||||||
|
stat/fstat functions.
|
||||||
|
Modification time is updated when the file is opened.
|
||||||
|
|
||||||
|
menu "Debug Configuration"
|
||||||
|
|
||||||
|
config SPIFFS_DBG
|
||||||
|
bool "Enable general SPIFFS debug"
|
||||||
|
default "n"
|
||||||
|
help
|
||||||
|
Enabling this option will print general debug mesages to the console.
|
||||||
|
|
||||||
|
config SPIFFS_API_DBG
|
||||||
|
bool "Enable SPIFFS API debug"
|
||||||
|
default "n"
|
||||||
|
help
|
||||||
|
Enabling this option will print API debug mesages to the console.
|
||||||
|
|
||||||
|
config SPIFFS_GC_DBG
|
||||||
|
bool "Enable SPIFFS Garbage Cleaner debug"
|
||||||
|
default "n"
|
||||||
|
help
|
||||||
|
Enabling this option will print GC debug mesages to the console.
|
||||||
|
|
||||||
|
config SPIFFS_CACHE_DBG
|
||||||
|
bool "Enable SPIFFS Cache debug"
|
||||||
|
default "n"
|
||||||
|
depends on SPIFFS_CACHE
|
||||||
|
help
|
||||||
|
Enabling this option will print cache debug mesages to the console.
|
||||||
|
|
||||||
|
config SPIFFS_CHECK_DBG
|
||||||
|
bool "Enable SPIFFS Filesystem Check debug"
|
||||||
|
default "n"
|
||||||
|
help
|
||||||
|
Enabling this option will print Filesystem Check debug mesages
|
||||||
|
to the console.
|
||||||
|
|
||||||
|
config SPIFFS_TEST_VISUALISATION
|
||||||
|
bool "Enable SPIFFS Filesystem Visualization"
|
||||||
|
default "n"
|
||||||
|
help
|
||||||
|
Enable this option to enable SPIFFS_vis function in the API.
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
endmenu
|
@ -1,6 +1,5 @@
|
|||||||
#
|
COMPONENT_ADD_INCLUDEDIRS := include
|
||||||
# Component Makefile
|
COMPONENT_PRIV_INCLUDEDIRS := . spiffs/src
|
||||||
#
|
COMPONENT_SRCDIRS := . spiffs/src
|
||||||
COMPONENT_ADD_INCLUDEDIRS += include/spiffs
|
|
||||||
|
|
||||||
COMPONENT_SRCDIRS := library
|
COMPONENT_SUBMODULES := spiffs
|
||||||
|
746
components/spiffs/esp_spiffs.c
Normal file
746
components/spiffs/esp_spiffs.c
Normal file
@ -0,0 +1,746 @@
|
|||||||
|
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "esp_spiffs.h"
|
||||||
|
#include "spiffs.h"
|
||||||
|
#include "spiffs_nucleus.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_partition.h"
|
||||||
|
#include "esp_spi_flash.h"
|
||||||
|
#include "esp_image_format.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <sys/fcntl.h>
|
||||||
|
#include <sys/lock.h>
|
||||||
|
#include "esp_vfs.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "rom/spi_flash.h"
|
||||||
|
#include "spiffs_api.h"
|
||||||
|
|
||||||
|
static const char* TAG = "SPIFFS";
|
||||||
|
|
||||||
|
#ifdef CONFIG_SPIFFS_USE_MTIME
|
||||||
|
_Static_assert(CONFIG_SPIFFS_META_LENGTH >= sizeof(time_t),
|
||||||
|
"SPIFFS_META_LENGTH size should be >= sizeof(time_t)");
|
||||||
|
#endif //CONFIG_SPIFFS_USE_MTIME
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SPIFFS DIR structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
DIR dir; /*!< VFS DIR struct */
|
||||||
|
spiffs_DIR d; /*!< SPIFFS DIR struct */
|
||||||
|
struct dirent e; /*!< Last open dirent */
|
||||||
|
long offset; /*!< Offset of the current dirent */
|
||||||
|
char path[SPIFFS_OBJ_NAME_LEN]; /*!< Requested directory name */
|
||||||
|
} vfs_spiffs_dir_t;
|
||||||
|
|
||||||
|
static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode);
|
||||||
|
static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size);
|
||||||
|
static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size);
|
||||||
|
static int vfs_spiffs_close(void* ctx, int fd);
|
||||||
|
static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode);
|
||||||
|
static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st);
|
||||||
|
static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st);
|
||||||
|
static int vfs_spiffs_unlink(void* ctx, const char *path);
|
||||||
|
static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2);
|
||||||
|
static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst);
|
||||||
|
static DIR* vfs_spiffs_opendir(void* ctx, const char* name);
|
||||||
|
static int vfs_spiffs_closedir(void* ctx, DIR* pdir);
|
||||||
|
static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir);
|
||||||
|
static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir,
|
||||||
|
struct dirent* entry, struct dirent** out_dirent);
|
||||||
|
static long vfs_spiffs_telldir(void* ctx, DIR* pdir);
|
||||||
|
static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset);
|
||||||
|
static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode);
|
||||||
|
static int vfs_spiffs_rmdir(void* ctx, const char* name);
|
||||||
|
static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file f);
|
||||||
|
static time_t vfs_spiffs_get_mtime(const spiffs_stat* s);
|
||||||
|
|
||||||
|
static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS];
|
||||||
|
|
||||||
|
static void esp_spiffs_free(esp_spiffs_t ** efs)
|
||||||
|
{
|
||||||
|
esp_spiffs_t * e = *efs;
|
||||||
|
if (*efs == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*efs = NULL;
|
||||||
|
|
||||||
|
if (e->fs) {
|
||||||
|
SPIFFS_unmount(e->fs);
|
||||||
|
free(e->fs);
|
||||||
|
}
|
||||||
|
vSemaphoreDelete(e->lock);
|
||||||
|
free(e->fds);
|
||||||
|
free(e->cache);
|
||||||
|
free(e->work);
|
||||||
|
free(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_spiffs_by_label(const char* label, int * index){
|
||||||
|
int i;
|
||||||
|
esp_spiffs_t * p;
|
||||||
|
for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) {
|
||||||
|
p = _efs[i];
|
||||||
|
if (p) {
|
||||||
|
if (!label && !p->by_label) {
|
||||||
|
*index = i;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
if (label && p->by_label && strncmp(label, p->partition->label, 17) == 0) {
|
||||||
|
*index = i;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_spiffs_get_empty(int * index){
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < CONFIG_SPIFFS_MAX_PARTITIONS; i++) {
|
||||||
|
if (_efs[i] == NULL) {
|
||||||
|
*index = i;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_spiffs_init(const esp_vfs_spiffs_conf_t* conf)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
//find if such partition is already mounted
|
||||||
|
if (esp_spiffs_by_label(conf->partition_label, &index) == ESP_OK) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_spiffs_get_empty(&index) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "max mounted partitions reached");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t flash_page_size = g_rom_flashchip.page_size;
|
||||||
|
uint32_t log_page_size = CONFIG_SPIFFS_PAGE_SIZE;
|
||||||
|
if (log_page_size % flash_page_size != 0) {
|
||||||
|
ESP_LOGE(TAG, "SPIFFS_PAGE_SIZE is not multiple of flash chip page size (%d)",
|
||||||
|
flash_page_size);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_partition_subtype_t subtype = conf->partition_label ?
|
||||||
|
ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_SPIFFS;
|
||||||
|
const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||||
|
subtype, conf->partition_label);
|
||||||
|
if (!partition) {
|
||||||
|
ESP_LOGE(TAG, "spiffs partition could not be found");
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partition->encrypted) {
|
||||||
|
ESP_LOGE(TAG, "spiffs can not run on encrypted partition");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_spiffs_t * efs = malloc(sizeof(esp_spiffs_t));
|
||||||
|
if (efs == NULL) {
|
||||||
|
ESP_LOGE(TAG, "esp_spiffs could not be malloced");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
memset(efs, 0, sizeof(esp_spiffs_t));
|
||||||
|
|
||||||
|
efs->cfg.hal_erase_f = spiffs_api_erase;
|
||||||
|
efs->cfg.hal_read_f = spiffs_api_read;
|
||||||
|
efs->cfg.hal_write_f = spiffs_api_write;
|
||||||
|
efs->cfg.log_block_size = g_rom_flashchip.sector_size;
|
||||||
|
efs->cfg.log_page_size = log_page_size;
|
||||||
|
efs->cfg.phys_addr = 0;
|
||||||
|
efs->cfg.phys_erase_block = g_rom_flashchip.sector_size;
|
||||||
|
efs->cfg.phys_size = partition->size;
|
||||||
|
|
||||||
|
efs->by_label = conf->partition_label != NULL;
|
||||||
|
|
||||||
|
efs->lock = xSemaphoreCreateMutex();
|
||||||
|
if (efs->lock == NULL) {
|
||||||
|
ESP_LOGE(TAG, "mutex lock could not be created");
|
||||||
|
esp_spiffs_free(&efs);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
efs->fds_sz = conf->max_files * sizeof(spiffs_fd);
|
||||||
|
efs->fds = malloc(efs->fds_sz);
|
||||||
|
if (efs->fds == NULL) {
|
||||||
|
ESP_LOGE(TAG, "fd buffer could not be malloced");
|
||||||
|
esp_spiffs_free(&efs);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
memset(efs->fds, 0, efs->fds_sz);
|
||||||
|
|
||||||
|
#if SPIFFS_CACHE
|
||||||
|
efs->cache_sz = sizeof(spiffs_cache) + conf->max_files * (sizeof(spiffs_cache_page)
|
||||||
|
+ efs->cfg.log_page_size);
|
||||||
|
efs->cache = malloc(efs->cache_sz);
|
||||||
|
if (efs->cache == NULL) {
|
||||||
|
ESP_LOGE(TAG, "cache buffer could not be malloced");
|
||||||
|
esp_spiffs_free(&efs);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
memset(efs->cache, 0, efs->cache_sz);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const uint32_t work_sz = efs->cfg.log_page_size * 2;
|
||||||
|
efs->work = malloc(work_sz);
|
||||||
|
if (efs->work == NULL) {
|
||||||
|
ESP_LOGE(TAG, "work buffer could not be malloced");
|
||||||
|
esp_spiffs_free(&efs);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
memset(efs->work, 0, work_sz);
|
||||||
|
|
||||||
|
efs->fs = malloc(sizeof(spiffs));
|
||||||
|
if (efs->fs == NULL) {
|
||||||
|
ESP_LOGE(TAG, "spiffs could not be malloced");
|
||||||
|
esp_spiffs_free(&efs);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
memset(efs->fs, 0, sizeof(spiffs));
|
||||||
|
|
||||||
|
efs->fs->user_data = (void *)efs;
|
||||||
|
efs->partition = partition;
|
||||||
|
|
||||||
|
s32_t res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz,
|
||||||
|
efs->cache, efs->cache_sz, spiffs_api_check);
|
||||||
|
|
||||||
|
if (conf->format_if_mount_failed && res != SPIFFS_OK) {
|
||||||
|
ESP_LOGW(TAG, "mount failed, %i. formatting...", SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
res = SPIFFS_format(efs->fs);
|
||||||
|
if (res != SPIFFS_OK) {
|
||||||
|
ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
esp_spiffs_free(&efs);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
res = SPIFFS_mount(efs->fs, &efs->cfg, efs->work, efs->fds, efs->fds_sz,
|
||||||
|
efs->cache, efs->cache_sz, spiffs_api_check);
|
||||||
|
}
|
||||||
|
if (res != SPIFFS_OK) {
|
||||||
|
ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
esp_spiffs_free(&efs);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
_efs[index] = efs;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool esp_spiffs_mounted(const char* partition_label)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (SPIFFS_mounted(_efs[index]->fs));
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
SPIFFS_info(_efs[index]->fs, total_bytes, used_bytes);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_spiffs_format(const char* partition_label)
|
||||||
|
{
|
||||||
|
bool partition_was_mounted = false;
|
||||||
|
int index;
|
||||||
|
/* If the partition is not mounted, need to create SPIFFS structures
|
||||||
|
* and mount the partition, unmount, format, delete SPIFFS structures.
|
||||||
|
* See SPIFFS wiki for the reason why.
|
||||||
|
*/
|
||||||
|
esp_err_t err = esp_spiffs_by_label(partition_label, &index);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
esp_vfs_spiffs_conf_t conf = {
|
||||||
|
.format_if_mount_failed = true,
|
||||||
|
.partition_label = partition_label,
|
||||||
|
.max_files = 1
|
||||||
|
};
|
||||||
|
err = esp_spiffs_init(&conf);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
err = esp_spiffs_by_label(partition_label, &index);
|
||||||
|
assert(err == ESP_OK && "failed to get index of the partition just mounted");
|
||||||
|
} else if (SPIFFS_mounted(_efs[index]->fs)) {
|
||||||
|
partition_was_mounted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPIFFS_unmount(_efs[index]->fs);
|
||||||
|
|
||||||
|
s32_t res = SPIFFS_format(_efs[index]->fs);
|
||||||
|
if (res != SPIFFS_OK) {
|
||||||
|
ESP_LOGE(TAG, "format failed, %i", SPIFFS_errno(_efs[index]->fs));
|
||||||
|
SPIFFS_clearerr(_efs[index]->fs);
|
||||||
|
/* If the partition was previously mounted, but format failed, don't
|
||||||
|
* try to mount the partition back (it will probably fail). On the
|
||||||
|
* other hand, if it was not mounted, need to clean up.
|
||||||
|
*/
|
||||||
|
if (!partition_was_mounted) {
|
||||||
|
esp_spiffs_free(&_efs[index]);
|
||||||
|
}
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partition_was_mounted) {
|
||||||
|
res = SPIFFS_mount(_efs[index]->fs, &_efs[index]->cfg, _efs[index]->work,
|
||||||
|
_efs[index]->fds, _efs[index]->fds_sz, _efs[index]->cache,
|
||||||
|
_efs[index]->cache_sz, spiffs_api_check);
|
||||||
|
if (res != SPIFFS_OK) {
|
||||||
|
ESP_LOGE(TAG, "mount failed, %i", SPIFFS_errno(_efs[index]->fs));
|
||||||
|
SPIFFS_clearerr(_efs[index]->fs);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
esp_spiffs_free(&_efs[index]);
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf)
|
||||||
|
{
|
||||||
|
assert(conf->base_path);
|
||||||
|
const esp_vfs_t vfs = {
|
||||||
|
.flags = ESP_VFS_FLAG_CONTEXT_PTR,
|
||||||
|
.write_p = &vfs_spiffs_write,
|
||||||
|
.lseek_p = &vfs_spiffs_lseek,
|
||||||
|
.read_p = &vfs_spiffs_read,
|
||||||
|
.open_p = &vfs_spiffs_open,
|
||||||
|
.close_p = &vfs_spiffs_close,
|
||||||
|
.fstat_p = &vfs_spiffs_fstat,
|
||||||
|
.stat_p = &vfs_spiffs_stat,
|
||||||
|
.link_p = &vfs_spiffs_link,
|
||||||
|
.unlink_p = &vfs_spiffs_unlink,
|
||||||
|
.rename_p = &vfs_spiffs_rename,
|
||||||
|
.opendir_p = &vfs_spiffs_opendir,
|
||||||
|
.closedir_p = &vfs_spiffs_closedir,
|
||||||
|
.readdir_p = &vfs_spiffs_readdir,
|
||||||
|
.readdir_r_p = &vfs_spiffs_readdir_r,
|
||||||
|
.seekdir_p = &vfs_spiffs_seekdir,
|
||||||
|
.telldir_p = &vfs_spiffs_telldir,
|
||||||
|
.mkdir_p = &vfs_spiffs_mkdir,
|
||||||
|
.rmdir_p = &vfs_spiffs_rmdir
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t err = esp_spiffs_init(conf);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index;
|
||||||
|
if (esp_spiffs_by_label(conf->partition_label, &index) != ESP_OK) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcat(_efs[index]->base_path, conf->base_path, ESP_VFS_PATH_MAX + 1);
|
||||||
|
err = esp_vfs_register(conf->base_path, &vfs, _efs[index]);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
esp_spiffs_free(&_efs[index]);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_vfs_spiffs_unregister(const char* partition_label)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
esp_err_t err = esp_vfs_unregister(_efs[index]->base_path);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
esp_spiffs_free(&_efs[index]);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spiffs_res_to_errno(s32_t fr)
|
||||||
|
{
|
||||||
|
switch(fr) {
|
||||||
|
case SPIFFS_OK :
|
||||||
|
return 0;
|
||||||
|
case SPIFFS_ERR_NOT_MOUNTED :
|
||||||
|
return ENODEV;
|
||||||
|
case SPIFFS_ERR_NOT_A_FS :
|
||||||
|
return ENODEV;
|
||||||
|
case SPIFFS_ERR_FULL :
|
||||||
|
return ENOSPC;
|
||||||
|
case SPIFFS_ERR_BAD_DESCRIPTOR :
|
||||||
|
return EBADF;
|
||||||
|
case SPIFFS_ERR_MOUNTED :
|
||||||
|
return EEXIST;
|
||||||
|
case SPIFFS_ERR_FILE_EXISTS :
|
||||||
|
return EEXIST;
|
||||||
|
case SPIFFS_ERR_NOT_FOUND :
|
||||||
|
return ENOENT;
|
||||||
|
case SPIFFS_ERR_NOT_A_FILE :
|
||||||
|
return ENOENT;
|
||||||
|
case SPIFFS_ERR_DELETED :
|
||||||
|
return ENOENT;
|
||||||
|
case SPIFFS_ERR_FILE_DELETED :
|
||||||
|
return ENOENT;
|
||||||
|
case SPIFFS_ERR_NAME_TOO_LONG :
|
||||||
|
return ENAMETOOLONG;
|
||||||
|
case SPIFFS_ERR_RO_NOT_IMPL :
|
||||||
|
return EROFS;
|
||||||
|
case SPIFFS_ERR_RO_ABORTED_OPERATION :
|
||||||
|
return EROFS;
|
||||||
|
default :
|
||||||
|
return EIO;
|
||||||
|
}
|
||||||
|
return ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spiffs_mode_conv(int m)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
int acc_mode = m & O_ACCMODE;
|
||||||
|
if (acc_mode == O_RDONLY) {
|
||||||
|
res |= SPIFFS_O_RDONLY;
|
||||||
|
} else if (acc_mode == O_WRONLY) {
|
||||||
|
res |= SPIFFS_O_WRONLY;
|
||||||
|
} else if (acc_mode == O_RDWR) {
|
||||||
|
res |= SPIFFS_O_RDWR;
|
||||||
|
}
|
||||||
|
if ((m & O_CREAT) && (m & O_EXCL)) {
|
||||||
|
res |= SPIFFS_O_CREAT | SPIFFS_O_EXCL;
|
||||||
|
} else if ((m & O_CREAT) && (m & O_TRUNC)) {
|
||||||
|
res |= SPIFFS_O_CREAT | SPIFFS_O_TRUNC;
|
||||||
|
}
|
||||||
|
if (m & O_APPEND) {
|
||||||
|
res |= SPIFFS_O_CREAT | SPIFFS_O_APPEND;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vfs_spiffs_open(void* ctx, const char * path, int flags, int mode)
|
||||||
|
{
|
||||||
|
assert(path);
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
int spiffs_flags = spiffs_mode_conv(flags);
|
||||||
|
int fd = SPIFFS_open(efs->fs, path, spiffs_flags, mode);
|
||||||
|
if (fd < 0) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!(spiffs_flags & SPIFFS_RDONLY)) {
|
||||||
|
vfs_spiffs_update_mtime(efs->fs, fd);
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t vfs_spiffs_write(void* ctx, int fd, const void * data, size_t size)
|
||||||
|
{
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
ssize_t res = SPIFFS_write(efs->fs, fd, (void *)data, size);
|
||||||
|
if (res < 0) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size)
|
||||||
|
{
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
ssize_t res = SPIFFS_read(efs->fs, fd, dst, size);
|
||||||
|
if (res < 0) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vfs_spiffs_close(void* ctx, int fd)
|
||||||
|
{
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
int res = SPIFFS_close(efs->fs, fd);
|
||||||
|
if (res < 0) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode)
|
||||||
|
{
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
off_t res = SPIFFS_lseek(efs->fs, fd, offset, mode);
|
||||||
|
if (res < 0) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st)
|
||||||
|
{
|
||||||
|
assert(st);
|
||||||
|
spiffs_stat s;
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
off_t res = SPIFFS_fstat(efs->fs, fd, &s);
|
||||||
|
if (res < 0) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
st->st_size = s.size;
|
||||||
|
st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO | S_IFREG;
|
||||||
|
st->st_mtime = vfs_spiffs_get_mtime(&s);
|
||||||
|
st->st_atime = 0;
|
||||||
|
st->st_ctime = 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st)
|
||||||
|
{
|
||||||
|
assert(path);
|
||||||
|
assert(st);
|
||||||
|
spiffs_stat s;
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
off_t res = SPIFFS_stat(efs->fs, path, &s);
|
||||||
|
if (res < 0) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
st->st_size = s.size;
|
||||||
|
st->st_mode = S_IRWXU | S_IRWXG | S_IRWXO;
|
||||||
|
st->st_mode |= (s.type == SPIFFS_TYPE_DIR)?S_IFDIR:S_IFREG;
|
||||||
|
st->st_mtime = vfs_spiffs_get_mtime(&s);
|
||||||
|
st->st_atime = 0;
|
||||||
|
st->st_ctime = 0;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vfs_spiffs_rename(void* ctx, const char *src, const char *dst)
|
||||||
|
{
|
||||||
|
assert(src);
|
||||||
|
assert(dst);
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
int res = SPIFFS_rename(efs->fs, src, dst);
|
||||||
|
if (res < 0) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vfs_spiffs_unlink(void* ctx, const char *path)
|
||||||
|
{
|
||||||
|
assert(path);
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
int res = SPIFFS_remove(efs->fs, path);
|
||||||
|
if (res < 0) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DIR* vfs_spiffs_opendir(void* ctx, const char* name)
|
||||||
|
{
|
||||||
|
assert(name);
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
vfs_spiffs_dir_t * dir = calloc(1, sizeof(vfs_spiffs_dir_t));
|
||||||
|
if (!dir) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!SPIFFS_opendir(efs->fs, name, &dir->d)) {
|
||||||
|
free(dir);
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
dir->offset = 0;
|
||||||
|
strlcpy(dir->path, name, SPIFFS_OBJ_NAME_LEN);
|
||||||
|
return (DIR*) dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vfs_spiffs_closedir(void* ctx, DIR* pdir)
|
||||||
|
{
|
||||||
|
assert(pdir);
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
|
||||||
|
int res = SPIFFS_closedir(&dir->d);
|
||||||
|
free(dir);
|
||||||
|
if (res < 0) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dirent* vfs_spiffs_readdir(void* ctx, DIR* pdir)
|
||||||
|
{
|
||||||
|
assert(pdir);
|
||||||
|
vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
|
||||||
|
struct dirent* out_dirent;
|
||||||
|
int err = vfs_spiffs_readdir_r(ctx, pdir, &dir->e, &out_dirent);
|
||||||
|
if (err != 0) {
|
||||||
|
errno = err;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return out_dirent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vfs_spiffs_readdir_r(void* ctx, DIR* pdir, struct dirent* entry,
|
||||||
|
struct dirent** out_dirent)
|
||||||
|
{
|
||||||
|
assert(pdir);
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
|
||||||
|
struct spiffs_dirent out;
|
||||||
|
size_t plen;
|
||||||
|
char * item_name;
|
||||||
|
do {
|
||||||
|
if (SPIFFS_readdir(&dir->d, &out) == 0) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
if (!errno) {
|
||||||
|
*out_dirent = NULL;
|
||||||
|
}
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
item_name = (char *)out.name;
|
||||||
|
plen = strlen(dir->path);
|
||||||
|
|
||||||
|
} while ((plen > 1) && (strncasecmp(dir->path, (const char*)out.name, plen) || out.name[plen] != '/' || !out.name[plen + 1]));
|
||||||
|
|
||||||
|
if (plen > 1) {
|
||||||
|
item_name += plen + 1;
|
||||||
|
} else if (item_name[0] == '/') {
|
||||||
|
item_name++;
|
||||||
|
}
|
||||||
|
entry->d_ino = 0;
|
||||||
|
entry->d_type = out.type;
|
||||||
|
snprintf(entry->d_name, SPIFFS_OBJ_NAME_LEN, "%s", item_name);
|
||||||
|
dir->offset++;
|
||||||
|
*out_dirent = entry;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long vfs_spiffs_telldir(void* ctx, DIR* pdir)
|
||||||
|
{
|
||||||
|
assert(pdir);
|
||||||
|
vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
|
||||||
|
return dir->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset)
|
||||||
|
{
|
||||||
|
assert(pdir);
|
||||||
|
esp_spiffs_t * efs = (esp_spiffs_t *)ctx;
|
||||||
|
vfs_spiffs_dir_t * dir = (vfs_spiffs_dir_t *)pdir;
|
||||||
|
struct spiffs_dirent tmp;
|
||||||
|
if (offset < dir->offset) {
|
||||||
|
//rewind dir
|
||||||
|
SPIFFS_closedir(&dir->d);
|
||||||
|
if (!SPIFFS_opendir(efs->fs, NULL, &dir->d)) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dir->offset = 0;
|
||||||
|
}
|
||||||
|
while (dir->offset < offset) {
|
||||||
|
if (SPIFFS_readdir(&dir->d, &tmp) == 0) {
|
||||||
|
errno = spiffs_res_to_errno(SPIFFS_errno(efs->fs));
|
||||||
|
SPIFFS_clearerr(efs->fs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t plen = strlen(dir->path);
|
||||||
|
if (plen > 1) {
|
||||||
|
if (strncasecmp(dir->path, (const char *)tmp.name, plen) || tmp.name[plen] != '/' || !tmp.name[plen+1]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dir->offset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode)
|
||||||
|
{
|
||||||
|
errno = ENOTSUP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vfs_spiffs_rmdir(void* ctx, const char* name)
|
||||||
|
{
|
||||||
|
errno = ENOTSUP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2)
|
||||||
|
{
|
||||||
|
errno = ENOTSUP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file fd)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SPIFFS_USE_MTIME
|
||||||
|
time_t t = time(NULL);
|
||||||
|
spiffs_stat s;
|
||||||
|
int ret = SPIFFS_OK;
|
||||||
|
if (CONFIG_SPIFFS_META_LENGTH > sizeof(t)) {
|
||||||
|
ret = SPIFFS_fstat(fs, fd, &s);
|
||||||
|
}
|
||||||
|
if (ret == SPIFFS_OK) {
|
||||||
|
memcpy(s.meta, &t, sizeof(t));
|
||||||
|
ret = SPIFFS_fupdate_meta(fs, fd, s.meta);
|
||||||
|
}
|
||||||
|
if (ret != SPIFFS_OK) {
|
||||||
|
ESP_LOGW(TAG, "Failed to update mtime (%d)", ret);
|
||||||
|
}
|
||||||
|
#endif //CONFIG_SPIFFS_USE_MTIME
|
||||||
|
}
|
||||||
|
|
||||||
|
static time_t vfs_spiffs_get_mtime(const spiffs_stat* s)
|
||||||
|
{
|
||||||
|
time_t t = 0;
|
||||||
|
#ifdef CONFIG_SPIFFS_USE_MTIME
|
||||||
|
memcpy(&t, s->meta, sizeof(t));
|
||||||
|
#endif
|
||||||
|
return t;
|
||||||
|
}
|
102
components/spiffs/include/esp_spiffs.h
Normal file
102
components/spiffs/include/esp_spiffs.h
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef _ESP_SPIFFS_H_
|
||||||
|
#define _ESP_SPIFFS_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configuration structure for esp_vfs_spiffs_register
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
const char* base_path; /*!< File path prefix associated with the filesystem. */
|
||||||
|
const char* partition_label; /*!< Optional, label of SPIFFS partition to use. If set to NULL, first partition with subtype=spiffs will be used. */
|
||||||
|
size_t max_files; /*!< Maximum files that could be open at the same time. */
|
||||||
|
bool format_if_mount_failed; /*!< If true, it will format the file system if it fails to mount. */
|
||||||
|
} esp_vfs_spiffs_conf_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register and mount SPIFFS to VFS with given path prefix.
|
||||||
|
*
|
||||||
|
* @param conf Pointer to esp_vfs_spiffs_conf_t configuration structure
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK if success
|
||||||
|
* - ESP_ERR_NO_MEM if objects could not be allocated
|
||||||
|
* - ESP_ERR_INVALID_STATE if already mounted or partition is encrypted
|
||||||
|
* - ESP_ERR_NOT_FOUND if partition for SPIFFS was not found
|
||||||
|
* - ESP_FAIL if mount or format fails
|
||||||
|
*/
|
||||||
|
esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister and unmount SPIFFS from VFS
|
||||||
|
*
|
||||||
|
* @param partition_label Optional, label of the partition to unregister.
|
||||||
|
* If not specified, first partition with subtype=spiffs is used.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK if successful
|
||||||
|
* - ESP_ERR_INVALID_STATE already unregistered
|
||||||
|
*/
|
||||||
|
esp_err_t esp_vfs_spiffs_unregister(const char* partition_label);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if SPIFFS is mounted
|
||||||
|
*
|
||||||
|
* @param partition_label Optional, label of the partition to check.
|
||||||
|
* If not specified, first partition with subtype=spiffs is used.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - true if mounted
|
||||||
|
* - false if not mounted
|
||||||
|
*/
|
||||||
|
bool esp_spiffs_mounted(const char* partition_label);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format the SPIFFS partition
|
||||||
|
*
|
||||||
|
* @param partition_label Optional, label of the partition to format.
|
||||||
|
* If not specified, first partition with subtype=spiffs is used.
|
||||||
|
* @return
|
||||||
|
* - ESP_OK if successful
|
||||||
|
* - ESP_FAIL on error
|
||||||
|
*/
|
||||||
|
esp_err_t esp_spiffs_format(const char* partition_label);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get information for SPIFFS
|
||||||
|
*
|
||||||
|
* @param partition_label Optional, label of the partition to get info for.
|
||||||
|
* If not specified, first partition with subtype=spiffs is used.
|
||||||
|
* @param[out] total_bytes Size of the file system
|
||||||
|
* @param[out] used_bytes Current used bytes in the file system
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK if success
|
||||||
|
* - ESP_ERR_INVALID_STATE if not mounted
|
||||||
|
*/
|
||||||
|
esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _ESP_SPIFFS_H_ */
|
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* ESPRSSIF MIT License
|
|
||||||
*
|
|
||||||
* Copyright (c) 2015 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
|
|
||||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
|
||||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
|
||||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
|
||||||
* to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all copies or
|
|
||||||
* substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __ESP_SPIFFS_H__
|
|
||||||
#define __ESP_SPIFFS_H__
|
|
||||||
|
|
||||||
#include "spiffs/spiffs.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** \defgroup Spiffs_APIs Spiffs APIs
|
|
||||||
* @brief Spiffs APIs
|
|
||||||
*
|
|
||||||
* More details about spiffs on https://github.com/pellepl/spiffs
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @addtogroup Spiffs_APIs
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct esp_spiffs_config {
|
|
||||||
u32_t phys_size; /**< physical size of the SPI Flash */
|
|
||||||
u32_t phys_addr; /**< physical offset in spi flash used for spiffs, must be on block boundary */
|
|
||||||
u32_t phys_erase_block; /**< physical size when erasing a block */
|
|
||||||
|
|
||||||
u32_t log_block_size; /**< logical size of a block, must be on physical block size boundary and must never be less than a physical block */
|
|
||||||
u32_t log_page_size; /**< logical size of a page, at least log_block_size/8 */
|
|
||||||
|
|
||||||
u32_t fd_buf_size; /**< file descriptor memory area size */
|
|
||||||
u32_t cache_buf_size; /**< cache buffer size */
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize spiffs
|
|
||||||
*
|
|
||||||
* @param struct esp_spiffs_config *config : ESP8266 spiffs configuration
|
|
||||||
*
|
|
||||||
* @return 0 : succeed (Equals SPIFFS_OK)
|
|
||||||
* @return otherwise : fail (-1 or SPIFFS_ERR_*)
|
|
||||||
*/
|
|
||||||
s32_t esp_spiffs_init(struct esp_spiffs_config *config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Deinitialize spiffs
|
|
||||||
*
|
|
||||||
* @param uint8 format : 0, only deinit; otherwise, deinit spiffs and format.
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
void esp_spiffs_deinit(u8_t format);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __ESP_SPIFFS_H__ */
|
|
316
components/spiffs/include/spiffs_config.h
Normal file
316
components/spiffs/include/spiffs_config.h
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
/*
|
||||||
|
* spiffs_config.h
|
||||||
|
*
|
||||||
|
* Created on: Jul 3, 2013
|
||||||
|
* Author: petera
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SPIFFS_CONFIG_H_
|
||||||
|
#define SPIFFS_CONFIG_H_
|
||||||
|
|
||||||
|
// ----------- 8< ------------
|
||||||
|
// Following includes are for the linux test build of spiffs
|
||||||
|
// These may/should/must be removed/altered/replaced in your target
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sdkconfig.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
// compile time switches
|
||||||
|
#define SPIFFS_TAG "SPIFFS"
|
||||||
|
|
||||||
|
// Set generic spiffs debug output call.
|
||||||
|
#if CONFIG_SPIFFS_DBG
|
||||||
|
#define SPIFFS_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPIFFS_DBG(...)
|
||||||
|
#endif
|
||||||
|
#if CONFIG_SPIFFS_API_DBG
|
||||||
|
#define SPIFFS_API_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPIFFS_API_DBG(...)
|
||||||
|
#endif
|
||||||
|
#if CONFIG_SPIFFS_DBG
|
||||||
|
#define SPIFFS_GC_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPIFFS_GC_DBG(...)
|
||||||
|
#endif
|
||||||
|
#if CONFIG_SPIFFS_CACHE_DBG
|
||||||
|
#define SPIFFS_CACHE_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPIFFS_CACHE_DBG(...)
|
||||||
|
#endif
|
||||||
|
#if CONFIG_SPIFFS_CHECK_DBG
|
||||||
|
#define SPIFFS_CHECK_DBG(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPIFFS_CHECK_DBG(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// needed types
|
||||||
|
typedef signed int s32_t;
|
||||||
|
typedef unsigned int u32_t;
|
||||||
|
typedef signed short s16_t;
|
||||||
|
typedef unsigned short u16_t;
|
||||||
|
typedef signed char s8_t;
|
||||||
|
typedef unsigned char u8_t;
|
||||||
|
|
||||||
|
struct spiffs_t;
|
||||||
|
extern void spiffs_api_lock(struct spiffs_t *fs);
|
||||||
|
extern void spiffs_api_unlock(struct spiffs_t *fs);
|
||||||
|
|
||||||
|
// Defines spiffs debug print formatters
|
||||||
|
// some general signed number
|
||||||
|
#define _SPIPRIi "%d"
|
||||||
|
// address
|
||||||
|
#define _SPIPRIad "%08x"
|
||||||
|
// block
|
||||||
|
#define _SPIPRIbl "%04x"
|
||||||
|
// page
|
||||||
|
#define _SPIPRIpg "%04x"
|
||||||
|
// span index
|
||||||
|
#define _SPIPRIsp "%04x"
|
||||||
|
// file descriptor
|
||||||
|
#define _SPIPRIfd "%d"
|
||||||
|
// file object id
|
||||||
|
#define _SPIPRIid "%04x"
|
||||||
|
// file flags
|
||||||
|
#define _SPIPRIfl "%02x"
|
||||||
|
|
||||||
|
|
||||||
|
// Enable/disable API functions to determine exact number of bytes
|
||||||
|
// for filedescriptor and cache buffers. Once decided for a configuration,
|
||||||
|
// this can be disabled to reduce flash.
|
||||||
|
#define SPIFFS_BUFFER_HELP 0
|
||||||
|
|
||||||
|
// Enables/disable memory read caching of nucleus file system operations.
|
||||||
|
// If enabled, memory area must be provided for cache in SPIFFS_mount.
|
||||||
|
#ifdef CONFIG_SPIFFS_CACHE
|
||||||
|
#define SPIFFS_CACHE (1)
|
||||||
|
#else
|
||||||
|
#define SPIFFS_CACHE (0)
|
||||||
|
#endif
|
||||||
|
#if SPIFFS_CACHE
|
||||||
|
// Enables memory write caching for file descriptors in hydrogen
|
||||||
|
#ifdef CONFIG_SPIFFS_CACHE_WR
|
||||||
|
#define SPIFFS_CACHE_WR (1)
|
||||||
|
#else
|
||||||
|
#define SPIFFS_CACHE_WR (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Enable/disable statistics on caching. Debug/test purpose only.
|
||||||
|
#ifdef CONFIG_SPIFFS_CACHE_STATS
|
||||||
|
#define SPIFFS_CACHE_STATS (1)
|
||||||
|
#else
|
||||||
|
#define SPIFFS_CACHE_STATS (0)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Always check header of each accessed page to ensure consistent state.
|
||||||
|
// If enabled it will increase number of reads, will increase flash.
|
||||||
|
#ifdef CONFIG_SPIFFS_PAGE_CHECK
|
||||||
|
#define SPIFFS_PAGE_CHECK (1)
|
||||||
|
#else
|
||||||
|
#define SPIFFS_PAGE_CHECK (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Define maximum number of gc runs to perform to reach desired free pages.
|
||||||
|
#define SPIFFS_GC_MAX_RUNS CONFIG_SPIFFS_GC_MAX_RUNS
|
||||||
|
|
||||||
|
// Enable/disable statistics on gc. Debug/test purpose only.
|
||||||
|
#ifdef CONFIG_SPIFFS_GC_STATS
|
||||||
|
#define SPIFFS_GC_STATS (1)
|
||||||
|
#else
|
||||||
|
#define SPIFFS_GC_STATS (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Garbage collecting examines all pages in a block which and sums up
|
||||||
|
// to a block score. Deleted pages normally gives positive score and
|
||||||
|
// used pages normally gives a negative score (as these must be moved).
|
||||||
|
// To have a fair wear-leveling, the erase age is also included in score,
|
||||||
|
// whose factor normally is the most positive.
|
||||||
|
// The larger the score, the more likely it is that the block will
|
||||||
|
// picked for garbage collection.
|
||||||
|
|
||||||
|
// Garbage collecting heuristics - weight used for deleted pages.
|
||||||
|
#define SPIFFS_GC_HEUR_W_DELET (5)
|
||||||
|
// Garbage collecting heuristics - weight used for used pages.
|
||||||
|
#define SPIFFS_GC_HEUR_W_USED (-1)
|
||||||
|
// Garbage collecting heuristics - weight used for time between
|
||||||
|
// last erased and erase of this block.
|
||||||
|
#define SPIFFS_GC_HEUR_W_ERASE_AGE (50)
|
||||||
|
|
||||||
|
// Object name maximum length. Note that this length include the
|
||||||
|
// zero-termination character, meaning maximum string of characters
|
||||||
|
// can at most be SPIFFS_OBJ_NAME_LEN - 1.
|
||||||
|
#define SPIFFS_OBJ_NAME_LEN (CONFIG_SPIFFS_OBJ_NAME_LEN)
|
||||||
|
|
||||||
|
// Maximum length of the metadata associated with an object.
|
||||||
|
// Setting to non-zero value enables metadata-related API but also
|
||||||
|
// changes the on-disk format, so the change is not backward-compatible.
|
||||||
|
//
|
||||||
|
// Do note: the meta length must never exceed
|
||||||
|
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + SPIFFS_PAGE_EXTRA_SIZE)
|
||||||
|
//
|
||||||
|
// This is derived from following:
|
||||||
|
// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) +
|
||||||
|
// spiffs_object_ix_header fields + at least some LUT entries)
|
||||||
|
#define SPIFFS_OBJ_META_LEN (CONFIG_SPIFFS_META_LENGTH)
|
||||||
|
#define SPIFFS_PAGE_EXTRA_SIZE (64)
|
||||||
|
_Static_assert(SPIFFS_OBJ_META_LEN + SPIFFS_OBJ_NAME_LEN + SPIFFS_PAGE_EXTRA_SIZE
|
||||||
|
<= CONFIG_SPIFFS_PAGE_SIZE, "SPIFFS_OBJ_META_LEN or SPIFFS_OBJ_NAME_LEN too long");
|
||||||
|
|
||||||
|
// Size of buffer allocated on stack used when copying data.
|
||||||
|
// Lower value generates more read/writes. No meaning having it bigger
|
||||||
|
// than logical page size.
|
||||||
|
#define SPIFFS_COPY_BUFFER_STACK (256)
|
||||||
|
|
||||||
|
// Enable this to have an identifiable spiffs filesystem. This will look for
|
||||||
|
// a magic in all sectors to determine if this is a valid spiffs system or
|
||||||
|
// not on mount point. If not, SPIFFS_format must be called prior to mounting
|
||||||
|
// again.
|
||||||
|
#ifdef CONFIG_SPIFFS_USE_MAGIC
|
||||||
|
#define SPIFFS_USE_MAGIC (1)
|
||||||
|
#else
|
||||||
|
#define SPIFFS_USE_MAGIC (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPIFFS_USE_MAGIC
|
||||||
|
// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is
|
||||||
|
// enabled, the magic will also be dependent on the length of the filesystem.
|
||||||
|
// For example, a filesystem configured and formatted for 4 megabytes will not
|
||||||
|
// be accepted for mounting with a configuration defining the filesystem as 2
|
||||||
|
// megabytes.
|
||||||
|
#ifdef CONFIG_SPIFFS_USE_MAGIC_LENGTH
|
||||||
|
#define SPIFFS_USE_MAGIC_LENGTH (1)
|
||||||
|
#else
|
||||||
|
#define SPIFFS_USE_MAGIC_LENGTH (0)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level
|
||||||
|
// These should be defined on a multithreaded system
|
||||||
|
|
||||||
|
// define this to enter a mutex if you're running on a multithreaded system
|
||||||
|
#define SPIFFS_LOCK(fs) spiffs_api_lock(fs)
|
||||||
|
// define this to exit a mutex if you're running on a multithreaded system
|
||||||
|
#define SPIFFS_UNLOCK(fs) spiffs_api_unlock(fs)
|
||||||
|
|
||||||
|
// Enable if only one spiffs instance with constant configuration will exist
|
||||||
|
// on the target. This will reduce calculations, flash and memory accesses.
|
||||||
|
// Parts of configuration must be defined below instead of at time of mount.
|
||||||
|
#define SPIFFS_SINGLETON 0
|
||||||
|
|
||||||
|
// Enable this if your target needs aligned data for index tables
|
||||||
|
#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 0
|
||||||
|
|
||||||
|
// Enable this if you want the HAL callbacks to be called with the spiffs struct
|
||||||
|
#define SPIFFS_HAL_CALLBACK_EXTRA 1
|
||||||
|
|
||||||
|
// Enable this if you want to add an integer offset to all file handles
|
||||||
|
// (spiffs_file). This is useful if running multiple instances of spiffs on
|
||||||
|
// same target, in order to recognise to what spiffs instance a file handle
|
||||||
|
// belongs.
|
||||||
|
// NB: This adds config field fh_ix_offset in the configuration struct when
|
||||||
|
// mounting, which must be defined.
|
||||||
|
#define SPIFFS_FILEHDL_OFFSET 0
|
||||||
|
|
||||||
|
// Enable this to compile a read only version of spiffs.
|
||||||
|
// This will reduce binary size of spiffs. All code comprising modification
|
||||||
|
// of the file system will not be compiled. Some config will be ignored.
|
||||||
|
// HAL functions for erasing and writing to spi-flash may be null. Cache
|
||||||
|
// can be disabled for even further binary size reduction (and ram savings).
|
||||||
|
// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL.
|
||||||
|
// If the file system cannot be mounted due to aborted erase operation and
|
||||||
|
// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be
|
||||||
|
// returned.
|
||||||
|
// Might be useful for e.g. bootloaders and such.
|
||||||
|
#define SPIFFS_READ_ONLY 0
|
||||||
|
|
||||||
|
// Enable this to add a temporal file cache using the fd buffer.
|
||||||
|
// The effects of the cache is that SPIFFS_open will find the file faster in
|
||||||
|
// certain cases. It will make it a lot easier for spiffs to find files
|
||||||
|
// opened frequently, reducing number of readings from the spi flash for
|
||||||
|
// finding those files.
|
||||||
|
// This will grow each fd by 6 bytes. If your files are opened in patterns
|
||||||
|
// with a degree of temporal locality, the system is optimized.
|
||||||
|
// Examples can be letting spiffs serve web content, where one file is the css.
|
||||||
|
// The css is accessed for each html file that is opened, meaning it is
|
||||||
|
// accessed almost every second time a file is opened. Another example could be
|
||||||
|
// a log file that is often opened, written, and closed.
|
||||||
|
// The size of the cache is number of given file descriptors, as it piggybacks
|
||||||
|
// on the fd update mechanism. The cache lives in the closed file descriptors.
|
||||||
|
// When closed, the fd know the whereabouts of the file. Instead of forgetting
|
||||||
|
// this, the temporal cache will keep handling updates to that file even if the
|
||||||
|
// fd is closed. If the file is opened again, the location of the file is found
|
||||||
|
// directly. If all available descriptors become opened, all cache memory is
|
||||||
|
// lost.
|
||||||
|
#define SPIFFS_TEMPORAL_FD_CACHE 1
|
||||||
|
|
||||||
|
// Temporal file cache hit score. Each time a file is opened, all cached files
|
||||||
|
// will lose one point. If the opened file is found in cache, that entry will
|
||||||
|
// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this
|
||||||
|
// value for the specific access patterns of the application. However, it must
|
||||||
|
// be between 1 (no gain for hitting a cached entry often) and 255.
|
||||||
|
#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE 4
|
||||||
|
|
||||||
|
// Enable to be able to map object indices to memory.
|
||||||
|
// This allows for faster and more deterministic reading if cases of reading
|
||||||
|
// large files and when changing file offset by seeking around a lot.
|
||||||
|
// When mapping a file's index, the file system will be scanned for index pages
|
||||||
|
// and the info will be put in memory provided by user. When reading, the
|
||||||
|
// memory map can be looked up instead of searching for index pages on the
|
||||||
|
// medium. This way, user can trade memory against performance.
|
||||||
|
// Whole, parts of, or future parts not being written yet can be mapped. The
|
||||||
|
// memory array will be owned by spiffs and updated accordingly during garbage
|
||||||
|
// collecting or when modifying the indices. The latter is invoked by when the
|
||||||
|
// file is modified in some way. The index buffer is tied to the file
|
||||||
|
// descriptor.
|
||||||
|
#define SPIFFS_IX_MAP 1
|
||||||
|
|
||||||
|
// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
|
||||||
|
// in the api. This function will visualize all filesystem using given printf
|
||||||
|
// function.
|
||||||
|
#ifdef CONFIG_SPIFFS_TEST_VISUALISATION
|
||||||
|
#define SPIFFS_TEST_VISUALISATION 1
|
||||||
|
#else
|
||||||
|
#define SPIFFS_TEST_VISUALISATION 0
|
||||||
|
#endif
|
||||||
|
#if SPIFFS_TEST_VISUALISATION
|
||||||
|
#ifndef spiffs_printf
|
||||||
|
#define spiffs_printf(...) ESP_LOGD(SPIFFS_TAG, __VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
// spiffs_printf argument for a free page
|
||||||
|
#define SPIFFS_TEST_VIS_FREE_STR "_"
|
||||||
|
// spiffs_printf argument for a deleted page
|
||||||
|
#define SPIFFS_TEST_VIS_DELE_STR "/"
|
||||||
|
// spiffs_printf argument for an index page for given object id
|
||||||
|
#define SPIFFS_TEST_VIS_INDX_STR(id) "i"
|
||||||
|
// spiffs_printf argument for a data page for given object id
|
||||||
|
#define SPIFFS_TEST_VIS_DATA_STR(id) "d"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Types depending on configuration such as the amount of flash bytes
|
||||||
|
// given to spiffs file system in total (spiffs_file_system_size),
|
||||||
|
// the logical block size (log_block_size), and the logical page size
|
||||||
|
// (log_page_size)
|
||||||
|
|
||||||
|
// Block index type. Make sure the size of this type can hold
|
||||||
|
// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size
|
||||||
|
typedef u16_t spiffs_block_ix;
|
||||||
|
// Page index type. Make sure the size of this type can hold
|
||||||
|
// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size
|
||||||
|
typedef u16_t spiffs_page_ix;
|
||||||
|
// Object id type - most significant bit is reserved for index flag. Make sure the
|
||||||
|
// size of this type can hold the highest object id on a full system,
|
||||||
|
// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2
|
||||||
|
typedef u16_t spiffs_obj_id;
|
||||||
|
// Object span index type. Make sure the size of this type can
|
||||||
|
// hold the largest possible span index on the system -
|
||||||
|
// i.e. (spiffs_file_system_size / log_page_size) - 1
|
||||||
|
typedef u16_t spiffs_span_ix;
|
||||||
|
|
||||||
|
#endif /* SPIFFS_CONFIG_H_ */
|
@ -1,311 +0,0 @@
|
|||||||
#include <fcntl.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "esp_spiffs.h"
|
|
||||||
#include "spi_flash.h"
|
|
||||||
|
|
||||||
#define NUM_SYS_FD 3
|
|
||||||
|
|
||||||
static spiffs fs;
|
|
||||||
|
|
||||||
static u8_t *spiffs_work_buf;
|
|
||||||
static u8_t *spiffs_fd_buf;
|
|
||||||
static u8_t *spiffs_cache_buf;
|
|
||||||
|
|
||||||
#define FLASH_UNIT_SIZE 4
|
|
||||||
|
|
||||||
static s32_t esp_spiffs_readwrite(u32_t addr, u32_t size, u8_t *p, int write)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* With proper configurarion spiffs never reads or writes more than
|
|
||||||
* LOG_PAGE_SIZE
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (size > fs.cfg.log_page_size) {
|
|
||||||
printf("Invalid size provided to read/write (%d)\n\r", (int) size);
|
|
||||||
return SPIFFS_ERR_NOT_CONFIGURED;
|
|
||||||
}
|
|
||||||
|
|
||||||
char tmp_buf[fs.cfg.log_page_size + FLASH_UNIT_SIZE * 2];
|
|
||||||
u32_t aligned_addr = addr & (-FLASH_UNIT_SIZE);
|
|
||||||
u32_t aligned_size =
|
|
||||||
((size + (FLASH_UNIT_SIZE - 1)) & -FLASH_UNIT_SIZE) + FLASH_UNIT_SIZE;
|
|
||||||
|
|
||||||
int res = spi_flash_read(aligned_addr, (u32_t *) tmp_buf, aligned_size);
|
|
||||||
|
|
||||||
if (res != 0) {
|
|
||||||
printf("spi_flash_read failed: %d (%d, %d)\n\r", res, (int) aligned_addr,
|
|
||||||
(int) aligned_size);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!write) {
|
|
||||||
memcpy(p, tmp_buf + (addr - aligned_addr), size);
|
|
||||||
return SPIFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(tmp_buf + (addr - aligned_addr), p, size);
|
|
||||||
|
|
||||||
res = spi_flash_write(aligned_addr, (u32_t *) tmp_buf, aligned_size);
|
|
||||||
|
|
||||||
if (res != 0) {
|
|
||||||
// printf("spi_flash_write failed: %d (%d, %d)\n\r", res,
|
|
||||||
// (int) aligned_addr, (int) aligned_size);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SPIFFS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst)
|
|
||||||
{
|
|
||||||
return esp_spiffs_readwrite(addr, size, dst, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src)
|
|
||||||
{
|
|
||||||
return esp_spiffs_readwrite(addr, size, src, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static s32_t esp_spiffs_erase(u32_t addr, u32_t size)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* With proper configurarion spiffs always
|
|
||||||
* provides here sector address & sector size
|
|
||||||
*/
|
|
||||||
if (size != fs.cfg.phys_erase_block || addr % fs.cfg.phys_erase_block != 0) {
|
|
||||||
printf("Invalid size provided to esp_spiffs_erase (%d, %d)\n\r",
|
|
||||||
(int) addr, (int) size);
|
|
||||||
return SPIFFS_ERR_NOT_CONFIGURED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return spi_flash_erase_sector(addr / fs.cfg.phys_erase_block);
|
|
||||||
}
|
|
||||||
|
|
||||||
s32_t esp_spiffs_init(struct esp_spiffs_config *config)
|
|
||||||
{
|
|
||||||
if (SPIFFS_mounted(&fs)) {
|
|
||||||
return SPIFFS_ERR_MOUNTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
spiffs_config cfg;
|
|
||||||
s32_t ret;
|
|
||||||
|
|
||||||
cfg.phys_size = config->phys_size;
|
|
||||||
cfg.phys_addr = config->phys_addr;
|
|
||||||
cfg.phys_erase_block = config->phys_erase_block;
|
|
||||||
cfg.log_block_size = config->log_block_size;
|
|
||||||
cfg.log_page_size = config->log_page_size;
|
|
||||||
|
|
||||||
cfg.hal_read_f = esp_spiffs_read;
|
|
||||||
cfg.hal_write_f = esp_spiffs_write;
|
|
||||||
cfg.hal_erase_f = esp_spiffs_erase;
|
|
||||||
|
|
||||||
if (spiffs_work_buf != NULL) {
|
|
||||||
free(spiffs_work_buf);
|
|
||||||
spiffs_work_buf = NULL;
|
|
||||||
}
|
|
||||||
spiffs_work_buf = malloc(config->log_page_size * 2);
|
|
||||||
|
|
||||||
if (spiffs_work_buf == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spiffs_fd_buf != NULL) {
|
|
||||||
free(spiffs_fd_buf);
|
|
||||||
spiffs_fd_buf = NULL;
|
|
||||||
}
|
|
||||||
spiffs_fd_buf = malloc(config->fd_buf_size);
|
|
||||||
|
|
||||||
if (spiffs_fd_buf == NULL) {
|
|
||||||
free(spiffs_work_buf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spiffs_cache_buf != NULL) {
|
|
||||||
free(spiffs_cache_buf);
|
|
||||||
spiffs_cache_buf = NULL;
|
|
||||||
}
|
|
||||||
spiffs_cache_buf = malloc(config->cache_buf_size);
|
|
||||||
|
|
||||||
if (spiffs_cache_buf == NULL) {
|
|
||||||
free(spiffs_work_buf);
|
|
||||||
free(spiffs_fd_buf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = SPIFFS_mount(&fs, &cfg, spiffs_work_buf,
|
|
||||||
spiffs_fd_buf, config->fd_buf_size,
|
|
||||||
spiffs_cache_buf, config->cache_buf_size,
|
|
||||||
0);
|
|
||||||
|
|
||||||
if (ret == -1) {
|
|
||||||
free(spiffs_work_buf);
|
|
||||||
free(spiffs_fd_buf);
|
|
||||||
free(spiffs_cache_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = SPIFFS_errno(&fs);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void esp_spiffs_deinit(u8_t format)
|
|
||||||
{
|
|
||||||
if (SPIFFS_mounted(&fs)) {
|
|
||||||
SPIFFS_unmount(&fs);
|
|
||||||
free(spiffs_work_buf);
|
|
||||||
free(spiffs_fd_buf);
|
|
||||||
free(spiffs_cache_buf);
|
|
||||||
}
|
|
||||||
if (format) {
|
|
||||||
SPIFFS_format(&fs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int _spiffs_open_r(struct _reent *r, const char *filename, int flags, int mode)
|
|
||||||
{
|
|
||||||
spiffs_mode sm = 0;
|
|
||||||
int res;
|
|
||||||
int rw = (flags & 3);
|
|
||||||
|
|
||||||
if (rw == O_RDONLY || rw == O_RDWR) {
|
|
||||||
sm |= SPIFFS_RDONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rw == O_WRONLY || rw == O_RDWR) {
|
|
||||||
sm |= SPIFFS_WRONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & O_CREAT) {
|
|
||||||
sm |= SPIFFS_CREAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & O_TRUNC) {
|
|
||||||
sm |= SPIFFS_TRUNC;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & O_APPEND) {
|
|
||||||
sm |= SPIFFS_APPEND;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Supported in newer versions of SPIFFS. */
|
|
||||||
/* if (flags && O_EXCL) sm |= SPIFFS_EXCL; */
|
|
||||||
/* if (flags && O_DIRECT) sm |= SPIFFS_DIRECT; */
|
|
||||||
|
|
||||||
res = SPIFFS_open(&fs, (char *) filename, sm, 0);
|
|
||||||
|
|
||||||
if (res >= 0) {
|
|
||||||
res += NUM_SYS_FD;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
res = SPIFFS_errno(&fs);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ssize_t _spiffs_read_r(struct _reent *r, int fd, void *buf, size_t len)
|
|
||||||
{
|
|
||||||
|
|
||||||
ssize_t res;
|
|
||||||
|
|
||||||
if (fd < NUM_SYS_FD) {
|
|
||||||
res = -1;
|
|
||||||
} else {
|
|
||||||
res = SPIFFS_read(&fs, fd - NUM_SYS_FD, buf, len);
|
|
||||||
if(res < 0) {
|
|
||||||
res = SPIFFS_errno(&fs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ssize_t _spiffs_write_r(struct _reent *r, int fd, void *buf, size_t len)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (fd < NUM_SYS_FD) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int res = SPIFFS_write(&fs, fd - NUM_SYS_FD, (char *) buf, len);
|
|
||||||
if(res < 0){
|
|
||||||
return SPIFFS_errno(&fs);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
_off_t _spiffs_lseek_r(struct _reent *r, int fd, _off_t where, int whence)
|
|
||||||
{
|
|
||||||
|
|
||||||
ssize_t res;
|
|
||||||
|
|
||||||
if (fd < NUM_SYS_FD) {
|
|
||||||
res = -1;
|
|
||||||
} else {
|
|
||||||
res = SPIFFS_lseek(&fs, fd - NUM_SYS_FD, where, whence);
|
|
||||||
if(res < 0) {
|
|
||||||
res = SPIFFS_errno(&fs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _spiffs_close_r(struct _reent *r, int fd)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (fd < NUM_SYS_FD) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPIFFS_close(&fs, fd - NUM_SYS_FD);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _spiffs_rename_r(struct _reent *r, const char *from, const char *to)
|
|
||||||
{
|
|
||||||
|
|
||||||
int res = SPIFFS_rename(&fs, (char *) from, (char *) to);
|
|
||||||
if(res < 0) {
|
|
||||||
res = SPIFFS_errno(&fs);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _spiffs_unlink_r(struct _reent *r, const char *filename)
|
|
||||||
{
|
|
||||||
|
|
||||||
int res = SPIFFS_remove(&fs, (char *) filename);
|
|
||||||
if(res < 0) {
|
|
||||||
res = SPIFFS_errno(&fs);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _spiffs_fstat_r(struct _reent *r, int fd, struct stat *s)
|
|
||||||
{
|
|
||||||
|
|
||||||
int res;
|
|
||||||
spiffs_stat ss;
|
|
||||||
memset(s, 0, sizeof(*s));
|
|
||||||
|
|
||||||
if (fd < NUM_SYS_FD) {
|
|
||||||
s->st_ino = fd;
|
|
||||||
s->st_rdev = fd;
|
|
||||||
s->st_mode = S_IFCHR | 0666;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = SPIFFS_fstat(&fs, fd - NUM_SYS_FD, &ss);
|
|
||||||
|
|
||||||
if (res < 0) {
|
|
||||||
return SPIFFS_errno(&fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
s->st_ino = ss.obj_id;
|
|
||||||
s->st_mode = 0666;
|
|
||||||
s->st_nlink = 1;
|
|
||||||
s->st_size = ss.size;
|
|
||||||
return 0;
|
|
||||||
}
|
|
8
components/spiffs/spiffs/.travis.yml
Normal file
8
components/spiffs/spiffs/.travis.yml
Normal file
@ -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
|
47
components/spiffs/spiffs/FUZZING.md
Normal file
47
components/spiffs/spiffs/FUZZING.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Fuzzing SPIFFS
|
||||||
|
|
||||||
|
The SPIFFS test suite includes a test program designed for fuzzing with
|
||||||
|
[AFL](http://lcamtuf.coredump.cx/afl/). This automatically exercises the
|
||||||
|
SPIFFS API and verifies that the file system does not crash or interact incorrectly
|
||||||
|
with the flash chip.
|
||||||
|
|
||||||
|
There are two steps to fuzzing. The first is to build the test suite with
|
||||||
|
the AFL version of gcc. The CC variable should point to your copy of afl-gcc.
|
||||||
|
|
||||||
|
```
|
||||||
|
make clean test CC=/usr/local/bin/afl-gcc
|
||||||
|
```
|
||||||
|
|
||||||
|
There is a new test `afl_test` that reads from stdin a list of commands
|
||||||
|
and arguments. These are interpreted and executed on the API. The `afltests`
|
||||||
|
directory contains a number of test cases that can be fed to the `afl_test` test.
|
||||||
|
|
||||||
|
|
||||||
|
The second is to run this test suite under afl as follows (where findings is
|
||||||
|
the output directory):
|
||||||
|
|
||||||
|
```
|
||||||
|
afl-fuzz -i afltests -o findings ./build/linux_spiffs_test -f afl_test
|
||||||
|
```
|
||||||
|
|
||||||
|
This run will take hours (or days) and will (hopefully) not find any crashes.
|
||||||
|
If a crash (or hang) is found, then the input file that caused the crash is
|
||||||
|
saved. This allows the specific test case to be debugged.
|
||||||
|
|
||||||
|
## Reducing the size of the file
|
||||||
|
|
||||||
|
AFL comes with `afl-tmin` which can reduce the size of the test input file to
|
||||||
|
make it easier to debug.
|
||||||
|
|
||||||
|
```
|
||||||
|
afl-tmin -i findings/crashes/<somefile> -o smalltest -- build/linux_spiffs_test -f afl_test
|
||||||
|
```
|
||||||
|
|
||||||
|
This will write a short version of the testcase file to `smalltest`. This can then be
|
||||||
|
fed into the test program for debugging:
|
||||||
|
|
||||||
|
```
|
||||||
|
build/linux_spiffs_test -f afl_test < smalltest
|
||||||
|
```
|
||||||
|
|
||||||
|
This should still crash, but allows it to be run under a debugger.
|
20
components/spiffs/spiffs/LICENSE
Normal file
20
components/spiffs/spiffs/LICENSE
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976<at>gmail.com)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
212
components/spiffs/spiffs/README.md
Normal file
212
components/spiffs/spiffs/README.md
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
# SPIFFS (SPI Flash File System)
|
||||||
|
**V0.3.7**
|
||||||
|
|
||||||
|
[](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
|
15
components/spiffs/spiffs/afltests/100
Normal file
15
components/spiffs/spiffs/afltests/100
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<EFBFBD>5S-C4
|
||||||
|
d5rh
|
||||||
|
OlWkR#C4
|
||||||
|
d5rh
|
||||||
|
O4W4R4O4W4<EFBFBD>C4#d5rh
|
||||||
|
O4d5rh
|
||||||
|
OlWkRh
|
||||||
|
O4Y5rh
|
||||||
|
OlWkR4C44R45<EFBFBD><EFBFBD>
|
||||||
|
O4W4<EFBFBD>4C4C4
|
||||||
|
O4O4W4R4O4W4<EFBFBD>C4#d5rh
|
||||||
|
O4d5rh
|
||||||
|
W4R45r<EFBFBD>
|
||||||
|
O4W4<EFBFBD>4#d5rh
|
||||||
|
rz
|
BIN
components/spiffs/spiffs/afltests/200
Normal file
BIN
components/spiffs/spiffs/afltests/200
Normal file
Binary file not shown.
18
components/spiffs/spiffs/afltests/a
Normal file
18
components/spiffs/spiffs/afltests/a
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
b55
|
||||||
|
O4W4R4C4D4
|
||||||
|
b45
|
||||||
|
d5rh
|
||||||
|
O4W4R4f4C4
|
||||||
|
baaU
|
||||||
|
d5rh
|
||||||
|
OaWaRafaCa
|
||||||
|
cd5rh
|
||||||
|
OaWaRafaCa
|
||||||
|
O4S4W4R4C4
|
||||||
|
d5rh
|
||||||
|
O4W4S4R4C4
|
||||||
|
d5rh
|
||||||
|
O4W4R4S4C4
|
||||||
|
d5rh
|
||||||
|
O4W4R4C4
|
||||||
|
d5rh
|
15
components/spiffs/spiffs/afltests/b
Normal file
15
components/spiffs/spiffs/afltests/b
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
b55
|
||||||
|
O4
|
||||||
|
W?W?W?W?W?f4
|
||||||
|
W<W=W>W:W;f4
|
||||||
|
C4
|
||||||
|
b45
|
||||||
|
d5rh
|
||||||
|
O4W?R4f4C4
|
||||||
|
baa
|
||||||
|
d5rh
|
||||||
|
OaWaRafaCa
|
||||||
|
d5rh
|
||||||
|
OaWaRafaCa
|
||||||
|
O4W?R4C4
|
||||||
|
d5rh
|
239
components/spiffs/spiffs/docs/TECH_SPEC
Normal file
239
components/spiffs/spiffs/docs/TECH_SPEC
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
* USING SPIFFS
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
|
||||||
|
* SPIFFS DESIGN
|
||||||
|
|
||||||
|
Spiffs is inspired by YAFFS. However, YAFFS is designed for NAND flashes, and
|
||||||
|
for bigger targets with much more ram. Nevertheless, many wise thoughts have
|
||||||
|
been borrowed from YAFFS when writing spiffs. Kudos!
|
||||||
|
|
||||||
|
The main complication writing spiffs was that it cannot be assumed the target
|
||||||
|
has a heap. Spiffs must go along only with the work ram buffer given to it.
|
||||||
|
This forces extra implementation on many areas of spiffs.
|
||||||
|
|
||||||
|
|
||||||
|
** SPI flash devices using NOR technology
|
||||||
|
|
||||||
|
Below is a small description of how SPI flashes work internally. This is to
|
||||||
|
give an understanding of the design choices made in spiffs.
|
||||||
|
|
||||||
|
SPI flash devices are physically divided in blocks. On some SPI flash devices,
|
||||||
|
blocks are further divided into sectors. Datasheets sometimes name blocks as
|
||||||
|
sectors and vice versa.
|
||||||
|
|
||||||
|
Common memory capacaties for SPI flashes are 512kB up to 8MB of data, where
|
||||||
|
blocks may be 64kB. Sectors can be e.g. 4kB, if supported. Many SPI flashes
|
||||||
|
have uniform block sizes, whereas others have non-uniform - the latter meaning
|
||||||
|
that e.g. the first 16 blocks are 4kB big, and the rest are 64kB.
|
||||||
|
|
||||||
|
The entire memory is linear and can be read and written in random access.
|
||||||
|
Erasing can only be done block- or sectorwise; or by mass erase.
|
||||||
|
|
||||||
|
SPI flashes can normally be erased from 100.000 up to 1.000.000 cycles before
|
||||||
|
they fail.
|
||||||
|
|
||||||
|
A clean SPI flash from factory have all bits in entire memory set to one. A
|
||||||
|
mass erase will reset the device to this state. Block or sector erasing will
|
||||||
|
put the all bits in the area given by the sector or block to ones. Writing to a
|
||||||
|
NOR flash pulls ones to zeroes. Writing 0xFF to an address is simply a no-op.
|
||||||
|
|
||||||
|
Writing 0b10101010 to a flash address holding 0b00001111 will yield 0b00001010.
|
||||||
|
|
||||||
|
This way of "write by nand" is used considerably in spiffs.
|
||||||
|
|
||||||
|
Common characteristics of NOR flashes are quick reads, but slow writes.
|
||||||
|
|
||||||
|
And finally, unlike NAND flashes, NOR flashes seem to not need any error
|
||||||
|
correction. They always write correctly I gather.
|
||||||
|
|
||||||
|
|
||||||
|
** Spiffs logical structure
|
||||||
|
|
||||||
|
Some terminology before proceeding. Physical blocks/sectors means sizes stated
|
||||||
|
in the datasheet. Logical blocks and pages is something the integrator choose.
|
||||||
|
|
||||||
|
|
||||||
|
** Blocks and pages
|
||||||
|
|
||||||
|
Spiffs is allocated to a part or all of the memory of the SPI flash device.
|
||||||
|
This area is divided into logical blocks, which in turn are divided into
|
||||||
|
logical pages. The boundary of a logical block must coincide with one or more
|
||||||
|
physical blocks. The sizes for logical blocks and logical pages always remain
|
||||||
|
the same, they are uniform.
|
||||||
|
|
||||||
|
Example: non-uniform flash mapped to spiffs with 128kB logical blocks
|
||||||
|
|
||||||
|
PHYSICAL FLASH BLOCKS SPIFFS LOGICAL BLOCKS: 128kB
|
||||||
|
|
||||||
|
+-----------------------+ - - - +-----------------------+
|
||||||
|
| Block 1 : 16kB | | Block 1 : 128kB |
|
||||||
|
+-----------------------+ | |
|
||||||
|
| Block 2 : 16kB | | |
|
||||||
|
+-----------------------+ | |
|
||||||
|
| Block 3 : 16kB | | |
|
||||||
|
+-----------------------+ | |
|
||||||
|
| Block 4 : 16kB | | |
|
||||||
|
+-----------------------+ | |
|
||||||
|
| Block 5 : 64kB | | |
|
||||||
|
+-----------------------+ - - - +-----------------------+
|
||||||
|
| Block 6 : 64kB | | Block 2 : 128kB |
|
||||||
|
+-----------------------+ | |
|
||||||
|
| Block 7 : 64kB | | |
|
||||||
|
+-----------------------+ - - - +-----------------------+
|
||||||
|
| Block 8 : 64kB | | Block 3 : 128kB |
|
||||||
|
+-----------------------+ | |
|
||||||
|
| Block 9 : 64kB | | |
|
||||||
|
+-----------------------+ - - - +-----------------------+
|
||||||
|
| ... | | ... |
|
||||||
|
|
||||||
|
A logical block is divided further into a number of logical pages. A page
|
||||||
|
defines the smallest data holding element known to spiffs. Hence, if a file
|
||||||
|
is created being one byte big, it will occupy one page for index and one page
|
||||||
|
for data - it will occupy 2 x size of a logical page on flash.
|
||||||
|
So it seems it is good to select a small page size.
|
||||||
|
|
||||||
|
Each page has a metadata header being normally 5 to 9 bytes. This said, a very
|
||||||
|
small page size will make metadata occupy a lot of the memory on the flash. A
|
||||||
|
page size of 64 bytes will waste 8-14% on metadata, while 256 bytes 2-4%.
|
||||||
|
So it seems it is good to select a big page size.
|
||||||
|
|
||||||
|
Also, spiffs uses a ram buffer being two times the page size. This ram buffer
|
||||||
|
is used for loading and manipulating pages, but it is also used for algorithms
|
||||||
|
to find free file ids, scanning the file system, etc. Having too small a page
|
||||||
|
size means less work buffer for spiffs, ending up in more reads operations and
|
||||||
|
eventually gives a slower file system.
|
||||||
|
|
||||||
|
Choosing the page size for the system involves many factors:
|
||||||
|
- How big is the logical block size
|
||||||
|
- What is the normal size of most files
|
||||||
|
- How much ram can be spent
|
||||||
|
- How much data (vs metadata) must be crammed into the file system
|
||||||
|
- How fast must spiffs be
|
||||||
|
- Other things impossible to find out
|
||||||
|
|
||||||
|
So, chosing the Optimal Page Size (tm) seems tricky, to say the least. Don't
|
||||||
|
fret - there is no optimal page size. This varies from how the target will use
|
||||||
|
spiffs. Use the golden rule:
|
||||||
|
|
||||||
|
~~~ Logical Page Size = Logical Block Size / 256 ~~~
|
||||||
|
|
||||||
|
This is a good starting point. The final page size can then be derived through
|
||||||
|
heuristical experimenting for us non-analytical minds.
|
||||||
|
|
||||||
|
|
||||||
|
** Objects, indices and look-ups
|
||||||
|
|
||||||
|
A file, or an object as called in spiffs, is identified by an object id.
|
||||||
|
Another YAFFS rip-off. This object id is a part of the page header. So, all
|
||||||
|
pages know to which object/file they belong - not counting the free pages.
|
||||||
|
|
||||||
|
An object is made up of two types of pages: object index pages and data pages.
|
||||||
|
Data pages contain the data written by user. Index pages contain metadata about
|
||||||
|
the object, more specifically what data pages are part of the object.
|
||||||
|
|
||||||
|
The page header also includes something called a span index. Let's say a file
|
||||||
|
is written covering three data pages. The first data page will then have span
|
||||||
|
index 0, the second span index 1, and the last data page will have span index
|
||||||
|
2. Simple as that.
|
||||||
|
|
||||||
|
Finally, each page header contain flags, telling if the page is used,
|
||||||
|
deleted, finalized, holds index or data, and more.
|
||||||
|
|
||||||
|
Object indices also have span indices, where an object index with span index 0
|
||||||
|
is referred to as the object index header. This page does not only contain
|
||||||
|
references to data pages, but also extra info such as object name, object size
|
||||||
|
in bytes, flags for file or directory, etc.
|
||||||
|
|
||||||
|
If one were to create a file covering three data pages, named e.g.
|
||||||
|
"spandex-joke.txt", given object id 12, it could look like this:
|
||||||
|
|
||||||
|
PAGE 0 <things to be unveiled soon>
|
||||||
|
|
||||||
|
PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA]
|
||||||
|
<first data page of joke>
|
||||||
|
|
||||||
|
PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA]
|
||||||
|
<second data page of joke>
|
||||||
|
|
||||||
|
PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA]
|
||||||
|
<some data belonging to object 545, probably not very amusing>
|
||||||
|
|
||||||
|
PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA]
|
||||||
|
<third data page of joke>
|
||||||
|
|
||||||
|
PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX]
|
||||||
|
obj ix header: [name:spandex-joke.txt size:600 bytes flags:FILE]
|
||||||
|
obj ix: [1 2 4]
|
||||||
|
|
||||||
|
Looking in detail at page 5, the object index header page, the object index
|
||||||
|
array refers to each data page in order, as mentioned before. The index of the
|
||||||
|
object index array correlates with the data page span index.
|
||||||
|
|
||||||
|
entry ix: 0 1 2
|
||||||
|
obj ix: [1 2 4]
|
||||||
|
| | |
|
||||||
|
PAGE 1, DATA, SPAN_IX 0 --------/ | |
|
||||||
|
PAGE 2, DATA, SPAN_IX 1 --------/ |
|
||||||
|
PAGE 4, DATA, SPAN_IX 2 --------/
|
||||||
|
|
||||||
|
Things to be unveiled in page 0 - well.. Spiffs is designed for systems low on
|
||||||
|
ram. We cannot keep a dynamic list on the whereabouts of each object index
|
||||||
|
header so we can find a file fast. There might not even be a heap! But, we do
|
||||||
|
not want to scan all page headers on the flash to find the object index header.
|
||||||
|
|
||||||
|
The first page(s) of each block contains the so called object look-up. These
|
||||||
|
are not normal pages, they do not have a header. Instead, they are arrays
|
||||||
|
pointing out what object-id the rest of all pages in the block belongs to.
|
||||||
|
|
||||||
|
By this look-up, only the first page(s) in each block must to scanned to find
|
||||||
|
the actual page which contains the object index header of the desired object.
|
||||||
|
|
||||||
|
The object lookup is redundant metadata. The assumption is that it presents
|
||||||
|
less overhead reading a full page of data to memory from each block and search
|
||||||
|
that, instead of reading a small amount of data from each page (i.e. the page
|
||||||
|
header) in all blocks. Each read operation from SPI flash normally contains
|
||||||
|
extra data as the read command itself and the flash address. Also, depending on
|
||||||
|
the underlying implementation, other criterions may need to be passed for each
|
||||||
|
read transaction, like mutexes and such.
|
||||||
|
|
||||||
|
The veiled example unveiled would look like this, with some extra pages:
|
||||||
|
|
||||||
|
PAGE 0 [ 12 12 545 12 12 34 34 4 0 0 0 0 ...]
|
||||||
|
PAGE 1 page header: [obj_id:12 span_ix:0 flags:USED|DATA] ...
|
||||||
|
PAGE 2 page header: [obj_id:12 span_ix:1 flags:USED|DATA] ...
|
||||||
|
PAGE 3 page header: [obj_id:545 span_ix:13 flags:USED|DATA] ...
|
||||||
|
PAGE 4 page header: [obj_id:12 span_ix:2 flags:USED|DATA] ...
|
||||||
|
PAGE 5 page header: [obj_id:12 span_ix:0 flags:USED|INDEX] ...
|
||||||
|
PAGE 6 page header: [obj_id:34 span_ix:0 flags:USED|DATA] ...
|
||||||
|
PAGE 7 page header: [obj_id:34 span_ix:1 flags:USED|DATA] ...
|
||||||
|
PAGE 8 page header: [obj_id:4 span_ix:1 flags:USED|INDEX] ...
|
||||||
|
PAGE 9 page header: [obj_id:23 span_ix:0 flags:DELETED|INDEX] ...
|
||||||
|
PAGE 10 page header: [obj_id:23 span_ix:0 flags:DELETED|DATA] ...
|
||||||
|
PAGE 11 page header: [obj_id:23 span_ix:1 flags:DELETED|DATA] ...
|
||||||
|
PAGE 12 page header: [obj_id:23 span_ix:2 flags:DELETED|DATA] ...
|
||||||
|
...
|
||||||
|
|
||||||
|
Ok, so why are page 9 to 12 marked as 0 when they belong to object id 23? These
|
||||||
|
pages are deleted, so this is marked both in page header flags and in the look
|
||||||
|
up. This is an example where spiffs uses NOR flashes "nand-way" of writing.
|
||||||
|
|
||||||
|
As a matter of fact, there are two object id's which are special:
|
||||||
|
|
||||||
|
obj id 0 (all bits zeroes) - indicates a deleted page in object look up
|
||||||
|
obj id 0xff.. (all bits ones) - indicates a free page in object look up
|
||||||
|
|
||||||
|
Actually, the object id's have another quirk: if the most significant bit is
|
||||||
|
set, this indicates an object index page. If the most significant bit is zero,
|
||||||
|
this indicates a data page. So to be fully correct, page 0 in above example
|
||||||
|
would look like this:
|
||||||
|
|
||||||
|
PAGE 0 [ 12 12 545 12 *12 34 34 *4 0 0 0 0 ...]
|
||||||
|
|
||||||
|
where the asterisk means the msb of the object id is set.
|
||||||
|
|
||||||
|
This is another way to speed up the searches when looking for object indices.
|
||||||
|
By looking on the object id's msb in the object lookup, it is also possible
|
||||||
|
to find out whether the page is an object index page or a data page.
|
||||||
|
|
15
components/spiffs/spiffs/docs/TODO
Normal file
15
components/spiffs/spiffs/docs/TODO
Normal file
@ -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.
|
12
components/spiffs/spiffs/files.mk
Normal file
12
components/spiffs/spiffs/files.mk
Normal file
@ -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
|
163
components/spiffs/spiffs/makefile
Normal file
163
components/spiffs/spiffs/makefile
Normal file
@ -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
|
@ -11,42 +11,76 @@
|
|||||||
// ----------- 8< ------------
|
// ----------- 8< ------------
|
||||||
// Following includes are for the linux test build of spiffs
|
// Following includes are for the linux test build of spiffs
|
||||||
// These may/should/must be removed/altered/replaced in your target
|
// These may/should/must be removed/altered/replaced in your target
|
||||||
|
#include "params_test.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <unistd.h>
|
||||||
|
#ifdef _SPIFFS_TEST
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "testrunner.h"
|
||||||
#include "freertos/task.h"
|
#endif
|
||||||
|
|
||||||
// ----------- >8 ------------
|
// ----------- >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
|
// compile time switches
|
||||||
|
|
||||||
// Set generic spiffs debug output call.
|
// Set generic spiffs debug output call.
|
||||||
#ifndef SPIFFS_DBG
|
#ifndef SPIFFS_DBG
|
||||||
#define SPIFFS_DBG(...) //printf(__VA_ARGS__)
|
#define SPIFFS_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
// Set spiffs debug output call for garbage collecting.
|
// Set spiffs debug output call for garbage collecting.
|
||||||
#ifndef SPIFFS_GC_DBG
|
#ifndef SPIFFS_GC_DBG
|
||||||
#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__)
|
#define SPIFFS_GC_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
// Set spiffs debug output call for caching.
|
// Set spiffs debug output call for caching.
|
||||||
#ifndef SPIFFS_CACHE_DBG
|
#ifndef SPIFFS_CACHE_DBG
|
||||||
#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__)
|
#define SPIFFS_CACHE_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
// Set spiffs debug output call for system consistency checks.
|
// Set spiffs debug output call for system consistency checks.
|
||||||
#ifndef SPIFFS_CHECK_DBG
|
#ifndef SPIFFS_CHECK_DBG
|
||||||
#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__)
|
#define SPIFFS_CHECK_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
|
||||||
#endif
|
#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
|
// Enable/disable API functions to determine exact number of bytes
|
||||||
// for filedescriptor and cache buffers. Once decided for a configuration,
|
// for filedescriptor and cache buffers. Once decided for a configuration,
|
||||||
@ -60,7 +94,6 @@ typedef int32_t s32_t;
|
|||||||
#ifndef SPIFFS_CACHE
|
#ifndef SPIFFS_CACHE
|
||||||
#define SPIFFS_CACHE 1
|
#define SPIFFS_CACHE 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if SPIFFS_CACHE
|
#if SPIFFS_CACHE
|
||||||
// Enables memory write caching for file descriptors in hydrogen
|
// Enables memory write caching for file descriptors in hydrogen
|
||||||
#ifndef SPIFFS_CACHE_WR
|
#ifndef SPIFFS_CACHE_WR
|
||||||
@ -111,11 +144,27 @@ typedef int32_t s32_t;
|
|||||||
#define SPIFFS_GC_HEUR_W_ERASE_AGE (50)
|
#define SPIFFS_GC_HEUR_W_ERASE_AGE (50)
|
||||||
#endif
|
#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
|
#ifndef SPIFFS_OBJ_NAME_LEN
|
||||||
#define SPIFFS_OBJ_NAME_LEN (32)
|
#define SPIFFS_OBJ_NAME_LEN (32)
|
||||||
#endif
|
#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.
|
// Size of buffer allocated on stack used when copying data.
|
||||||
// Lower value generates more read/writes. No meaning having it bigger
|
// Lower value generates more read/writes. No meaning having it bigger
|
||||||
// than logical page size.
|
// than logical page size.
|
||||||
@ -131,6 +180,17 @@ typedef int32_t s32_t;
|
|||||||
#define SPIFFS_USE_MAGIC (0)
|
#define SPIFFS_USE_MAGIC (0)
|
||||||
#endif
|
#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
|
// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level
|
||||||
// These should be defined on a multithreaded system
|
// These should be defined on a multithreaded system
|
||||||
|
|
||||||
@ -143,7 +203,6 @@ typedef int32_t s32_t;
|
|||||||
#define SPIFFS_UNLOCK(fs)
|
#define SPIFFS_UNLOCK(fs)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Enable if only one spiffs instance with constant configuration will exist
|
// Enable if only one spiffs instance with constant configuration will exist
|
||||||
// on the target. This will reduce calculations, flash and memory accesses.
|
// on the target. This will reduce calculations, flash and memory accesses.
|
||||||
// Parts of configuration must be defined below instead of at time of mount.
|
// 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
|
// Enable this if your target needs aligned data for index tables
|
||||||
#ifndef SPIFFS_ALIGNED_OBJECT_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
|
#endif
|
||||||
|
|
||||||
// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
|
// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
|
||||||
// in the api. This function will visualize all filesystem using given printf
|
// in the api. This function will visualize all filesystem using given printf
|
||||||
// function.
|
// function.
|
||||||
#ifndef SPIFFS_TEST_VISUALISATION
|
#ifndef SPIFFS_TEST_VISUALISATION
|
||||||
#define SPIFFS_TEST_VISUALISATION 0
|
#define SPIFFS_TEST_VISUALISATION 1
|
||||||
#endif
|
#endif
|
||||||
#if SPIFFS_TEST_VISUALISATION
|
#if SPIFFS_TEST_VISUALISATION
|
||||||
#ifndef spiffs_printf
|
#ifndef spiffs_printf
|
@ -5,8 +5,6 @@
|
|||||||
* Author: petera
|
* Author: petera
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef SPIFFS_H_
|
#ifndef SPIFFS_H_
|
||||||
#define SPIFFS_H_
|
#define SPIFFS_H_
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
@ -49,6 +47,22 @@ extern "C" {
|
|||||||
|
|
||||||
#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029
|
#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_INTERNAL -10050
|
||||||
|
|
||||||
#define SPIFFS_ERR_TEST -10100
|
#define SPIFFS_ERR_TEST -10100
|
||||||
@ -63,12 +77,26 @@ typedef u16_t spiffs_mode;
|
|||||||
// object type
|
// object type
|
||||||
typedef u8_t spiffs_obj_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 */
|
/* spi read call function type */
|
||||||
typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst);
|
typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst);
|
||||||
/* spi write call function type */
|
/* spi write call function type */
|
||||||
typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src);
|
typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src);
|
||||||
/* spi erase call function type */
|
/* spi erase call function type */
|
||||||
typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size);
|
typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size);
|
||||||
|
#endif // SPIFFS_HAL_CALLBACK_EXTRA
|
||||||
|
|
||||||
/* file system check callback report operation */
|
/* file system check callback report operation */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -85,16 +113,34 @@ typedef enum {
|
|||||||
SPIFFS_CHECK_FIX_LOOKUP,
|
SPIFFS_CHECK_FIX_LOOKUP,
|
||||||
SPIFFS_CHECK_DELETE_ORPHANED_INDEX,
|
SPIFFS_CHECK_DELETE_ORPHANED_INDEX,
|
||||||
SPIFFS_CHECK_DELETE_PAGE,
|
SPIFFS_CHECK_DELETE_PAGE,
|
||||||
SPIFFS_CHECK_DELETE_BAD_FILE,
|
SPIFFS_CHECK_DELETE_BAD_FILE
|
||||||
} spiffs_check_report;
|
} spiffs_check_report;
|
||||||
|
|
||||||
/* file system check callback function */
|
/* 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,
|
typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report,
|
||||||
u32_t arg1, u32_t arg2);
|
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
|
#ifndef SPIFFS_DBG
|
||||||
#define SPIFFS_DBG(...) \
|
#define SPIFFS_DBG(...) \
|
||||||
print(__VA_ARGS__)
|
printf(__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
#ifndef SPIFFS_GC_DBG
|
#ifndef SPIFFS_GC_DBG
|
||||||
#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__)
|
#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 */
|
/* Any write to the filehandle is appended to end of the file */
|
||||||
#define SPIFFS_APPEND (1<<0)
|
#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 */
|
/* If the opened file exists, it will be truncated to zero length before opened */
|
||||||
#define SPIFFS_TRUNC (1<<1)
|
#define SPIFFS_TRUNC (1<<1)
|
||||||
|
#define SPIFFS_O_TRUNC SPIFFS_TRUNC
|
||||||
/* If the opened file does not exist, it will be created before opened */
|
/* If the opened file does not exist, it will be created before opened */
|
||||||
#define SPIFFS_CREAT (1<<2)
|
#define SPIFFS_CREAT (1<<2)
|
||||||
|
#define SPIFFS_O_CREAT SPIFFS_CREAT
|
||||||
/* The opened file may only be read */
|
/* The opened file may only be read */
|
||||||
#define SPIFFS_RDONLY (1<<3)
|
#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)
|
#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)
|
#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_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_SET (0)
|
||||||
#define SPIFFS_SEEK_CUR (1)
|
#define SPIFFS_SEEK_CUR (1)
|
||||||
@ -164,10 +220,15 @@ typedef struct {
|
|||||||
// logical size of a page, must be at least
|
// logical size of a page, must be at least
|
||||||
// log_block_size / 8
|
// log_block_size / 8
|
||||||
u32_t log_page_size;
|
u32_t log_page_size;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#if SPIFFS_FILEHDL_OFFSET
|
||||||
|
// an integer offset added to each file handle
|
||||||
|
u16_t fh_ix_offset;
|
||||||
#endif
|
#endif
|
||||||
} spiffs_config;
|
} spiffs_config;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct spiffs_t {
|
||||||
// file system configuration
|
// file system configuration
|
||||||
spiffs_config cfg;
|
spiffs_config cfg;
|
||||||
// number of logical blocks
|
// number of logical blocks
|
||||||
@ -222,9 +283,12 @@ typedef struct {
|
|||||||
|
|
||||||
// check callback function
|
// check callback function
|
||||||
spiffs_check_callback check_cb_f;
|
spiffs_check_callback check_cb_f;
|
||||||
|
// file callback function
|
||||||
|
spiffs_file_callback file_cb_f;
|
||||||
// mounted flag
|
// mounted flag
|
||||||
u8_t mounted;
|
u8_t mounted;
|
||||||
|
// user data
|
||||||
|
void *user_data;
|
||||||
// config magic
|
// config magic
|
||||||
u32_t config_magic;
|
u32_t config_magic;
|
||||||
} spiffs;
|
} spiffs;
|
||||||
@ -234,7 +298,11 @@ typedef struct {
|
|||||||
spiffs_obj_id obj_id;
|
spiffs_obj_id obj_id;
|
||||||
u32_t size;
|
u32_t size;
|
||||||
spiffs_obj_type type;
|
spiffs_obj_type type;
|
||||||
|
spiffs_page_ix pix;
|
||||||
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
||||||
|
#if SPIFFS_OBJ_META_LEN
|
||||||
|
u8_t meta[SPIFFS_OBJ_META_LEN];
|
||||||
|
#endif
|
||||||
} spiffs_stat;
|
} spiffs_stat;
|
||||||
|
|
||||||
struct spiffs_dirent {
|
struct spiffs_dirent {
|
||||||
@ -243,6 +311,9 @@ struct spiffs_dirent {
|
|||||||
spiffs_obj_type type;
|
spiffs_obj_type type;
|
||||||
u32_t size;
|
u32_t size;
|
||||||
spiffs_page_ix pix;
|
spiffs_page_ix pix;
|
||||||
|
#if SPIFFS_OBJ_META_LEN
|
||||||
|
u8_t meta[SPIFFS_OBJ_META_LEN];
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -251,8 +322,57 @@ typedef struct {
|
|||||||
int entry;
|
int entry;
|
||||||
} spiffs_DIR;
|
} 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
|
// 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.
|
* Initializes the file system dynamic parameters and mounts the filesystem.
|
||||||
* If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS
|
* If 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 path the path of the new file
|
||||||
* @param mode ignored, for posix compliance
|
* @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.
|
* Opens/creates a file.
|
||||||
* @param fs the file system struct
|
* @param fs the file system struct
|
||||||
* @param path the path of the new file
|
* @param path the path of the new file
|
||||||
* @param flags the flags for the open command, can be combinations of
|
* @param flags the flags for the open command, can be combinations of
|
||||||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
* SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY,
|
||||||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT
|
* SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL
|
||||||
* @param mode ignored, for posix compliance
|
* @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.
|
* 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
|
* 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.
|
* the file, whilst SPIFFS_open_by_dirent already knows where the file resides.
|
||||||
* @param fs the file system struct
|
* @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
|
* @param flags the flags for the open command, can be combinations of
|
||||||
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
* SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY,
|
||||||
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT.
|
* 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);
|
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.
|
* Reads from given filehandle.
|
||||||
* @param fs the file system struct
|
* @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);
|
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 fs the file system struct
|
||||||
* @param fh the filehandle
|
* @param fh the filehandle
|
||||||
* @param offs how much/where to move the offset
|
* @param offs how much/where to move the offset
|
||||||
* @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes
|
* @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_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);
|
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 fs the file system struct
|
||||||
* @param path the path of the file to remove
|
* @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
|
* 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 path the path of the file to stat
|
||||||
* @param s the stat struct to populate
|
* @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
|
* 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 fs the file system struct
|
||||||
* @param fh the filehandle of the file to close
|
* @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
|
* Renames a file
|
||||||
@ -396,7 +532,25 @@ void SPIFFS_close(spiffs *fs, spiffs_file fh);
|
|||||||
* @param old path of file to rename
|
* @param old path of file to rename
|
||||||
* @param newPath new path of file
|
* @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.
|
* Returns last error of last file operation.
|
||||||
@ -419,7 +573,7 @@ void SPIFFS_clearerr(spiffs *fs);
|
|||||||
* @param name the name of the directory
|
* @param name the name of the directory
|
||||||
* @param d pointer the directory stream to be populated
|
* @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
|
* 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);
|
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.
|
* 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
|
* 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);
|
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
|
#if SPIFFS_TEST_VISUALISATION
|
||||||
/**
|
/**
|
||||||
* Prints out a visualization of the filesystem.
|
* Prints out a visualization of the filesystem.
|
@ -20,12 +20,12 @@ static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix)
|
|||||||
if ((cache->cpage_use_map & (1<<i)) &&
|
if ((cache->cpage_use_map & (1<<i)) &&
|
||||||
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
|
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
|
||||||
cp->pix == pix ) {
|
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;
|
cp->last_access = cache->last_access;
|
||||||
return cp;
|
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;
|
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_TYPE_WR) == 0 &&
|
||||||
(cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) {
|
(cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) {
|
||||||
u8_t *mem = spiffs_get_cache_page(fs, cache, ix);
|
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;
|
#if SPIFFS_CACHE_WR
|
||||||
cache->cpage_use_map &= ~(1 << ix);
|
|
||||||
|
|
||||||
if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_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);
|
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" objid "_SPIPRIid"\n", ix, cp->obj_id);
|
||||||
} else {
|
} else
|
||||||
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page %i pix %04x\n", ix, cp->pix);
|
#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;
|
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);
|
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
|
||||||
cache->cpage_use_map |= (1<<i);
|
cache->cpage_use_map |= (1<<i);
|
||||||
cp->last_access = cache->last_access;
|
cp->last_access = cache->last_access;
|
||||||
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page %i\n", i);
|
//SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i);
|
||||||
return cp;
|
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));
|
spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr));
|
||||||
cache->last_access++;
|
cache->last_access++;
|
||||||
if (cp) {
|
if (cp) {
|
||||||
|
// we've already got one, you see
|
||||||
#if SPIFFS_CACHE_STATS
|
#if SPIFFS_CACHE_STATS
|
||||||
fs->cache_hits++;
|
fs->cache_hits++;
|
||||||
#endif
|
#endif
|
||||||
cp->last_access = cache->last_access;
|
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 {
|
} else {
|
||||||
if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) {
|
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
|
// for second layer lookup functions, we do not cache in order to prevent shredding
|
||||||
return fs->cfg.hal_read_f(
|
return SPIFFS_HAL_READ(fs, addr, len, dst);
|
||||||
addr ,
|
|
||||||
len,
|
|
||||||
dst);
|
|
||||||
}
|
}
|
||||||
#if SPIFFS_CACHE_STATS
|
#if SPIFFS_CACHE_STATS
|
||||||
fs->cache_misses++;
|
fs->cache_misses++;
|
||||||
#endif
|
#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);
|
res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0);
|
||||||
|
|
||||||
cp = spiffs_cache_page_allocate(fs);
|
cp = spiffs_cache_page_allocate(fs);
|
||||||
if (cp) {
|
if (cp) {
|
||||||
cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
|
cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
|
||||||
cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
|
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(
|
s32_t res2 = SPIFFS_HAL_READ(fs,
|
||||||
addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
|
addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
|
||||||
SPIFFS_CFG_LOG_PAGE_SZ(fs),
|
SPIFFS_CFG_LOG_PAGE_SZ(fs),
|
||||||
spiffs_get_cache_page(fs, cache, cp->ix));
|
spiffs_get_cache_page(fs, cache, cp->ix));
|
||||||
if (res2 != SPIFFS_OK) {
|
if (res2 != SPIFFS_OK) {
|
||||||
res = res2;
|
// 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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,24 +201,24 @@ s32_t spiffs_phys_wr(
|
|||||||
(op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) {
|
(op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) {
|
||||||
// page is being deleted, wipe from cache - unless it is a lookup page
|
// page is being deleted, wipe from cache - unless it is a lookup page
|
||||||
spiffs_cache_page_free(fs, cp->ix, 0);
|
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);
|
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++;
|
cache->last_access++;
|
||||||
cp->last_access = cache->last_access;
|
cp->last_access = cache->last_access;
|
||||||
|
|
||||||
if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) {
|
if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) {
|
||||||
// page is being updated, no write-cache, just pass thru
|
// 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 {
|
} else {
|
||||||
return SPIFFS_OK;
|
return SPIFFS_OK;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// no cache page, no write cache - just write thru
|
// 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->flags = SPIFFS_CACHE_FLAG_TYPE_WR;
|
||||||
cp->obj_id = fd->obj_id;
|
cp->obj_id = fd->obj_id;
|
||||||
fd->cache_page = cp;
|
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;
|
return cp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +304,7 @@ void spiffs_cache_init(spiffs *fs) {
|
|||||||
|
|
||||||
cache.cpage_use_map = 0xffffffff;
|
cache.cpage_use_map = 0xffffffff;
|
||||||
cache.cpage_use_mask = cache_mask;
|
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);
|
spiffs_cache *c = spiffs_get_cache(fs);
|
||||||
|
|
@ -19,9 +19,24 @@
|
|||||||
* Author: petera
|
* Author: petera
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "spiffs.h"
|
#include "spiffs.h"
|
||||||
#include "spiffs_nucleus.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
|
// Look up consistency
|
||||||
|
|
||||||
@ -93,6 +108,7 @@ static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_
|
|||||||
} else {
|
} else {
|
||||||
// calc entry in index
|
// calc entry in index
|
||||||
entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix);
|
entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix);
|
||||||
|
|
||||||
}
|
}
|
||||||
// load index
|
// load index
|
||||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
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)) ||
|
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)) {
|
((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
|
// 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;
|
*reload_lu = 1;
|
||||||
delete_page = 1;
|
delete_page = 1;
|
||||||
if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) {
|
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
|
// copy page to new place and re-write the object index to new place
|
||||||
spiffs_page_ix new_pix;
|
spiffs_page_ix new_pix;
|
||||||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &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);
|
SPIFFS_CHECK_RES(res);
|
||||||
*reload_lu = 1;
|
*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);
|
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) {
|
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||||
// index bad also, cannot mend this file
|
// 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);
|
res = spiffs_page_delete(fs, new_pix);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
|
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 {
|
} 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);
|
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
|
// got a data page also, assume lu corruption only, rewrite to new page
|
||||||
spiffs_page_ix new_pix;
|
spiffs_page_ix new_pix;
|
||||||
res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &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);
|
SPIFFS_CHECK_RES(res);
|
||||||
*reload_lu = 1;
|
*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 {
|
} else {
|
||||||
SPIFFS_CHECK_RES(res);
|
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) {
|
if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) {
|
||||||
// look up entry used
|
// look up entry used
|
||||||
if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) {
|
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;
|
delete_page = 1;
|
||||||
if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 ||
|
if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 ||
|
||||||
(p_hdr->flags & SPIFFS_PH_FLAG_FINAL) ||
|
(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);
|
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) {
|
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||||
// index bad also, cannot mend this file
|
// 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);
|
res = spiffs_page_delete(fs, new_pix);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
|
res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id);
|
||||||
*reload_lu = 1;
|
*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);
|
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
|
// rewrite as obj_id_ph
|
||||||
new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
||||||
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
|
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);
|
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid" to pix "_SPIPRIpg"\n", cur_pix, new_ph.obj_id, new_pix);
|
||||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
*reload_lu = 1;
|
*reload_lu = 1;
|
||||||
} else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) ||
|
} 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
|
// got a data page for look up obj id
|
||||||
// rewrite as obj_id_lu
|
// rewrite as obj_id_lu
|
||||||
new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG;
|
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);
|
SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid"\n", cur_pix, new_ph.obj_id);
|
||||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
|
||||||
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
|
res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
*reload_lu = 1;
|
*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)) ||
|
} 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)) {
|
((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;
|
spiffs_page_ix data_pix, objix_pix_d;
|
||||||
// see if other data page exists for given obj id and span index
|
// 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);
|
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 only data page exists, make this page index
|
||||||
if (data_pix && objix_pix_d == 0) {
|
if (data_pix && objix_pix_d == 0) {
|
||||||
SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n");
|
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_header new_ph;
|
||||||
spiffs_page_ix new_pix;
|
spiffs_page_ix new_pix;
|
||||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX);
|
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 only index exists, make data page
|
||||||
if (data_pix == 0 && objix_pix_d) {
|
if (data_pix == 0 && objix_pix_d) {
|
||||||
SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n");
|
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_header new_ph;
|
||||||
spiffs_page_ix new_pix;
|
spiffs_page_ix new_pix;
|
||||||
new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL);
|
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) {
|
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;
|
delete_page = 1;
|
||||||
} else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) {
|
} 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
|
// page can be removed if not referenced by object index
|
||||||
*reload_lu = 1;
|
*reload_lu = 1;
|
||||||
res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix);
|
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
|
// page referenced by object index but not final
|
||||||
// just finalize
|
// just finalize
|
||||||
SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n");
|
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;
|
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL;
|
||||||
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
|
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),
|
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) {
|
if (delete_page) {
|
||||||
SPIFFS_CHECK_DBG("LU: FIXUP: deleting page %04x\n", cur_pix);
|
SPIFFS_CHECK_DBG("LU: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix);
|
||||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
|
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
|
||||||
res = spiffs_page_delete(fs, cur_pix);
|
res = spiffs_page_delete(fs, cur_pix);
|
||||||
SPIFFS_CHECK_RES(res);
|
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,
|
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) {
|
const void *user_const_p, void *user_var_p) {
|
||||||
(void)user_data;
|
(void)user_const_p;
|
||||||
(void)user_p;
|
(void)user_var_p;
|
||||||
s32_t res = SPIFFS_OK;
|
s32_t res = SPIFFS_OK;
|
||||||
spiffs_page_header p_hdr;
|
spiffs_page_header p_hdr;
|
||||||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry);
|
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);
|
(cur_block * 256)/fs->block_count, 0);
|
||||||
|
|
||||||
// load header
|
// load header
|
||||||
@ -460,7 +476,7 @@ s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) {
|
|||||||
(void)check_all_objects;
|
(void)check_all_objects;
|
||||||
s32_t res = SPIFFS_OK;
|
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);
|
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 (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;
|
return res;
|
||||||
}
|
}
|
||||||
@ -506,14 +522,17 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
|
|||||||
spiffs_block_ix cur_block = 0;
|
spiffs_block_ix cur_block = 0;
|
||||||
// build consistency bitmap for id range traversing all blocks
|
// build consistency bitmap for id range traversing all blocks
|
||||||
while (!restart && cur_block < fs->block_count) {
|
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) +
|
(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),
|
((((cur_block * pages_per_scan * 256)/ (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count),
|
||||||
0);
|
0);
|
||||||
|
|
||||||
// traverse each page except for lookup pages
|
// traverse each page except for lookup pages
|
||||||
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block;
|
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)) {
|
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
|
// read header
|
||||||
spiffs_page_header p_hdr;
|
spiffs_page_header p_hdr;
|
||||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
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))) {
|
|| (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) {
|
||||||
|
|
||||||
// bad reference
|
// 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);
|
rpix, cur_pix);
|
||||||
// check for data page elsewhere
|
// check for data page elsewhere
|
||||||
spiffs_page_ix data_pix;
|
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;
|
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);
|
res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix);
|
||||||
SPIFFS_CHECK_RES(res);
|
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
|
// 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,
|
res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG,
|
||||||
data_spix_offset + i, data_pix, cur_pix);
|
data_spix_offset + i, data_pix, cur_pix);
|
||||||
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||||
// index bad also, cannot mend this file
|
// index bad also, cannot mend this file
|
||||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend - delete object\n", res);
|
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend - delete object\n", res);
|
||||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0);
|
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0);
|
||||||
// delete file
|
// delete file
|
||||||
res = spiffs_page_delete(fs, cur_pix);
|
res = spiffs_page_delete(fs, cur_pix);
|
||||||
} else {
|
} 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);
|
SPIFFS_CHECK_RES(res);
|
||||||
restart = 1;
|
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.span_ix != data_spix_offset + i ||
|
||||||
(rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) !=
|
(rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) !=
|
||||||
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) {
|
(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,
|
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);
|
rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags);
|
||||||
// try finding correct page
|
// try finding correct page
|
||||||
@ -635,23 +654,23 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
|
|||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
if (data_pix == 0) {
|
if (data_pix == 0) {
|
||||||
// not found, this index is badly borked
|
// not found, this index is badly borked
|
||||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id %04x\n", p_hdr.obj_id);
|
SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id "_SPIPRIid"\n", p_hdr.obj_id);
|
||||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
// found it, so rewrite index
|
// 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);
|
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);
|
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) {
|
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||||
// index bad also, cannot mend this file
|
// index bad also, cannot mend this file
|
||||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res);
|
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
|
||||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||||
} else {
|
} 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);
|
SPIFFS_CHECK_RES(res);
|
||||||
restart = 1;
|
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 u32_t rpix_byte_ix = (rpix - pix_offset) / (8/bits);
|
||||||
const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits;
|
const u8_t rpix_bit_ix = (rpix & ((8/bits)-1)) * bits;
|
||||||
if (fs->work[rpix_byte_ix] & (1<<(rpix_bit_ix + 1))) {
|
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);
|
rpix, cur_pix);
|
||||||
// Here, we should have fixed all broken references - getting this means there
|
// 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
|
// must be multiple files with same object id. Only solution is to delete
|
||||||
// the object which is referring to this page
|
// 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);
|
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);
|
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
// extra precaution, delete this page also
|
// extra precaution, delete this page also
|
||||||
@ -706,7 +725,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
|
|||||||
if (bitmask == 0x1) {
|
if (bitmask == 0x1) {
|
||||||
|
|
||||||
// 001
|
// 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 rewrite_ix_to_this = 0;
|
||||||
u8_t delete_page = 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)))) {
|
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
|
// pointing to a bad page altogether, rewrite index to this
|
||||||
rewrite_ix_to_this = 1;
|
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 {
|
} else {
|
||||||
// pointing to something else, check what
|
// pointing to something else, check what
|
||||||
spiffs_page_header rp_hdr;
|
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)) ==
|
((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))) {
|
(SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) {
|
||||||
// pointing to something else valid, just delete this page then
|
// 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;
|
delete_page = 1;
|
||||||
} else {
|
} else {
|
||||||
// pointing to something weird, update index to point to this page instead
|
// pointing to something weird, update index to point to this page instead
|
||||||
if (rpix != cur_pix) {
|
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_INDEX) ? "" : "INDEX ",
|
||||||
(rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ",
|
(rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ",
|
||||||
(rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "",
|
(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) {
|
} 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;
|
delete_page = 1;
|
||||||
res = SPIFFS_OK;
|
res = SPIFFS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rewrite_ix_to_this) {
|
if (rewrite_ix_to_this) {
|
||||||
// if pointing to invalid page, redirect index to this page
|
// 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);
|
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);
|
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) {
|
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
|
||||||
// index bad also, cannot mend this file
|
// index bad also, cannot mend this file
|
||||||
SPIFFS_CHECK_DBG("PA: FIXUP: index bad %i, cannot mend!\n", res);
|
SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res);
|
||||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
|
||||||
res = spiffs_page_delete(fs, cur_pix);
|
res = spiffs_page_delete(fs, cur_pix);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id);
|
||||||
} else {
|
} 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);
|
SPIFFS_CHECK_RES(res);
|
||||||
restart = 1;
|
restart = 1;
|
||||||
continue;
|
continue;
|
||||||
} else if (delete_page) {
|
} else if (delete_page) {
|
||||||
SPIFFS_CHECK_DBG("PA: FIXUP: deleting page %04x\n", cur_pix);
|
SPIFFS_CHECK_DBG("PA: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix);
|
||||||
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
|
CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0);
|
||||||
res = spiffs_page_delete(fs, cur_pix);
|
res = spiffs_page_delete(fs, cur_pix);
|
||||||
}
|
}
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
@ -784,7 +803,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
|
|||||||
if (bitmask == 0x2) {
|
if (bitmask == 0x2) {
|
||||||
|
|
||||||
// 010
|
// 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
|
// 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) {
|
if (bitmask == 0x4) {
|
||||||
|
|
||||||
// 100
|
// 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
|
// this should never happen, major fubar
|
||||||
}
|
}
|
||||||
@ -804,20 +823,22 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
|
|||||||
if (bitmask == 0x6) {
|
if (bitmask == 0x6) {
|
||||||
|
|
||||||
// 110
|
// 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
|
// no op, this should be taken care of when checking valid references
|
||||||
}
|
}
|
||||||
if (bitmask == 0x7) {
|
if (bitmask == 0x7) {
|
||||||
|
|
||||||
// 111
|
// 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
|
// 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
|
// next page range
|
||||||
if (!restart) {
|
if (!restart) {
|
||||||
pix_offset += pages_per_scan;
|
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
|
// Checks consistency amongst all pages and fixes irregularities
|
||||||
s32_t spiffs_page_consistency_check(spiffs *fs) {
|
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);
|
s32_t res = spiffs_page_consistency_check_i(fs);
|
||||||
if (res != SPIFFS_OK) {
|
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;
|
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,
|
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) {
|
int cur_entry, const void *user_const_p, void *user_var_p) {
|
||||||
(void)user_data;
|
(void)user_const_p;
|
||||||
s32_t res_c = SPIFFS_VIS_COUNTINUE;
|
s32_t res_c = SPIFFS_VIS_COUNTINUE;
|
||||||
s32_t res = SPIFFS_OK;
|
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;
|
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);
|
(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)) {
|
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 &&
|
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)) ==
|
(p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) ==
|
||||||
(SPIFFS_PH_FLAG_DELET)) {
|
(SPIFFS_PH_FLAG_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);
|
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);
|
res = spiffs_page_delete(fs, cur_pix);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
return res_c;
|
return res_c;
|
||||||
@ -933,9 +954,9 @@ static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id o
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (delete) {
|
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);
|
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);
|
res = spiffs_page_delete(fs, cur_pix);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
}
|
}
|
||||||
@ -958,16 +979,17 @@ s32_t spiffs_object_index_consistency_check(spiffs *fs) {
|
|||||||
// a reachable/unreachable object id.
|
// a reachable/unreachable object id.
|
||||||
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||||
u32_t obj_id_log_ix = 0;
|
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,
|
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) {
|
if (res == SPIFFS_VIS_END) {
|
||||||
res = SPIFFS_OK;
|
res = SPIFFS_OK;
|
||||||
}
|
}
|
||||||
if (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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // !SPIFFS_READ_ONLY
|
@ -1,6 +1,8 @@
|
|||||||
#include "spiffs.h"
|
#include "spiffs.h"
|
||||||
#include "spiffs_nucleus.h"
|
#include "spiffs_nucleus.h"
|
||||||
|
|
||||||
|
#if !SPIFFS_READ_ONLY
|
||||||
|
|
||||||
// Erases a logical block and updates the erase counter.
|
// Erases a logical block and updates the erase counter.
|
||||||
// If cache is enabled, all pages that might be cached in this block
|
// If cache is enabled, all pages that might be cached in this block
|
||||||
// is dropped.
|
// is dropped.
|
||||||
@ -9,7 +11,7 @@ static s32_t spiffs_gc_erase_block(
|
|||||||
spiffs_block_ix bix) {
|
spiffs_block_ix bix) {
|
||||||
s32_t res;
|
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);
|
res = spiffs_erase_block(fs, bix);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
|
|
||||||
@ -36,7 +38,7 @@ s32_t spiffs_gc_quick(
|
|||||||
int cur_entry = 0;
|
int cur_entry = 0;
|
||||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
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
|
#if SPIFFS_GC_STATS
|
||||||
fs->stats_gc_runs++;
|
fs->stats_gc_runs++;
|
||||||
#endif
|
#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);
|
u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
|
||||||
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
|
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
|
||||||
// SPIFFS_GC_DBG("gc: full freeblk:%i needed:%i free:%i dele:%i\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
|
// 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;
|
// return SPIFFS_ERR_FULL;
|
||||||
// }
|
// }
|
||||||
if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) {
|
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;
|
return SPIFFS_ERR_FULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
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,
|
tries,
|
||||||
fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages+fs->stats_p_allocated+fs->stats_p_deleted),
|
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;
|
spiffs_block_ix *cands;
|
||||||
int count;
|
int count;
|
||||||
@ -150,13 +152,13 @@ s32_t spiffs_gc_check(
|
|||||||
#endif
|
#endif
|
||||||
cand = cands[0];
|
cand = cands[0];
|
||||||
fs->cleaning = 1;
|
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);
|
res = spiffs_gc_clean(fs, cand);
|
||||||
fs->cleaning = 0;
|
fs->cleaning = 0;
|
||||||
if (res < 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 {
|
} 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);
|
SPIFFS_CHECK_RES(res);
|
||||||
|
|
||||||
@ -186,7 +188,7 @@ s32_t spiffs_gc_check(
|
|||||||
res = SPIFFS_ERR_FULL;
|
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->stats_p_allocated + fs->stats_p_deleted,
|
||||||
fs->free_blocks, free_pages, tries, res);
|
fs->free_blocks, free_pages, tries, res);
|
||||||
|
|
||||||
@ -224,7 +226,7 @@ s32_t spiffs_gc_erase_page_stats(
|
|||||||
} // per entry
|
} // per entry
|
||||||
obj_lookup_page++;
|
obj_lookup_page++;
|
||||||
} // per object 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_allocated -= allo;
|
||||||
fs->stats_p_deleted -= dele;
|
fs->stats_p_deleted -= dele;
|
||||||
return res;
|
return res;
|
||||||
@ -249,10 +251,12 @@ s32_t spiffs_gc_find_candidate(
|
|||||||
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
|
||||||
|
|
||||||
// divide up work area into block indices and scores
|
// divide up work area into block indices and scores
|
||||||
// todo alignment?
|
|
||||||
spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
|
spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work;
|
||||||
s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix));
|
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;
|
*block_candidates = cand_blocks;
|
||||||
|
|
||||||
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
|
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
|
// calculate score and insert into candidate table
|
||||||
// stoneage sort, but probably not so many blocks
|
// 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
|
// read erase count
|
||||||
spiffs_obj_id erase_count;
|
spiffs_obj_id erase_count;
|
||||||
res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
|
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 +
|
used_pages_in_block * SPIFFS_GC_HEUR_W_USED +
|
||||||
erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE);
|
erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE);
|
||||||
int cand_ix = 0;
|
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) {
|
while (cand_ix < max_candidates) {
|
||||||
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
|
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
|
||||||
cand_blocks[cand_ix] = cur_block;
|
cand_blocks[cand_ix] = cur_block;
|
||||||
@ -352,6 +356,7 @@ typedef struct {
|
|||||||
spiffs_obj_id cur_obj_id;
|
spiffs_obj_id cur_obj_id;
|
||||||
spiffs_span_ix cur_objix_spix;
|
spiffs_span_ix cur_objix_spix;
|
||||||
spiffs_page_ix cur_objix_pix;
|
spiffs_page_ix cur_objix_pix;
|
||||||
|
spiffs_page_ix cur_data_pix;
|
||||||
int stored_scan_entry_index;
|
int stored_scan_entry_index;
|
||||||
u8_t obj_id_found;
|
u8_t obj_id_found;
|
||||||
} spiffs_gc;
|
} spiffs_gc;
|
||||||
@ -371,15 +376,16 @@ typedef struct {
|
|||||||
//
|
//
|
||||||
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
||||||
s32_t res = SPIFFS_OK;
|
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;
|
int cur_entry = 0;
|
||||||
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
|
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_ix cur_pix = 0;
|
||||||
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
|
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
|
||||||
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
|
spiffs_page_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));
|
memset(&gc, 0, sizeof(spiffs_gc));
|
||||||
gc.state = FIND_OBJ_DATA;
|
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
|
// 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_block_ix = (bix+1)%fs->block_count;
|
||||||
fs->free_cursor_obj_lu_entry = 0;
|
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) {
|
while (res == SPIFFS_OK && gc.state != FINISHED) {
|
||||||
SPIFFS_GC_DBG("gc_clean: state = %i entry:%i\n", gc.state, cur_entry);
|
SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry);
|
||||||
gc.obj_id_found = 0;
|
gc.obj_id_found = 0; // reset (to no found data page)
|
||||||
|
|
||||||
// scan through lookup pages
|
// scan through lookup pages
|
||||||
int obj_lookup_page = cur_entry / entries_per_page;
|
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,
|
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),
|
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_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||||
// check each entry
|
// check each object lookup entry
|
||||||
while (scan && res == SPIFFS_OK &&
|
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))) {
|
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];
|
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
|
// act upon object id depending on gc state
|
||||||
switch (gc.state) {
|
switch (gc.state) {
|
||||||
case FIND_OBJ_DATA:
|
case FIND_OBJ_DATA:
|
||||||
|
// find a data page
|
||||||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
|
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
|
||||||
((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) {
|
((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.obj_id_found = 1;
|
||||||
gc.cur_obj_id = obj_id;
|
gc.cur_obj_id = obj_id;
|
||||||
|
gc.cur_data_pix = cur_pix;
|
||||||
scan = 0;
|
scan = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MOVE_OBJ_DATA:
|
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) {
|
if (obj_id == gc.cur_obj_id) {
|
||||||
spiffs_page_header p_hdr;
|
spiffs_page_header p_hdr;
|
||||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
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);
|
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||||
SPIFFS_CHECK_RES(res);
|
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) {
|
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");
|
SPIFFS_GC_DBG("gc_clean: MOVE_DATA no objix spix match, take in another run\n");
|
||||||
} else {
|
} else {
|
||||||
@ -435,7 +446,7 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
|||||||
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||||
// move page
|
// move page
|
||||||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix);
|
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);
|
SPIFFS_CHECK_RES(res);
|
||||||
// move wipes obj_lu, reload it
|
// move wipes obj_lu, reload it
|
||||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
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_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
} else {
|
} else {
|
||||||
// page is deleted but not deleted in lookup, scrap it
|
// 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);
|
// 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);
|
res = spiffs_page_delete(fs, cur_pix);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
new_data_pix = SPIFFS_OBJ_ID_FREE;
|
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) {
|
if (gc.cur_objix_spix == 0) {
|
||||||
// update object index header page
|
// 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_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 {
|
} else {
|
||||||
// update object index page
|
// 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_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;
|
break;
|
||||||
case MOVE_OBJ_IX:
|
case MOVE_OBJ_IX:
|
||||||
|
// find and evacuate object index pages
|
||||||
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
|
if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE &&
|
||||||
(obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
|
(obj_id & SPIFFS_OBJ_ID_IX_FLAG)) {
|
||||||
// found an index object id
|
// 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) {
|
if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) {
|
||||||
// move page
|
// move page
|
||||||
res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix);
|
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_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
|
// move wipes obj_lu, reload it
|
||||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
|
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),
|
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_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
} else {
|
} else {
|
||||||
// page is deleted but not deleted in lookup, scrap it
|
// 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);
|
// 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);
|
res = spiffs_page_delete(fs, cur_pix);
|
||||||
if (res == SPIFFS_OK) {
|
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);
|
SPIFFS_CHECK_RES(res);
|
||||||
@ -497,72 +515,92 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
|
|||||||
default:
|
default:
|
||||||
scan = 0;
|
scan = 0;
|
||||||
break;
|
break;
|
||||||
}
|
} // switch gc state
|
||||||
cur_entry++;
|
cur_entry++;
|
||||||
} // per 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
|
} // per object lookup page
|
||||||
|
|
||||||
if (res != SPIFFS_OK) break;
|
if (res != SPIFFS_OK) break;
|
||||||
|
|
||||||
// state finalization and switch
|
// state finalization and switch
|
||||||
switch (gc.state) {
|
switch (gc.state) {
|
||||||
case FIND_OBJ_DATA:
|
case FIND_OBJ_DATA:
|
||||||
if (gc.obj_id_found) {
|
if (gc.obj_id_found) {
|
||||||
|
// handle found data page -
|
||||||
// find out corresponding obj ix page and load it to memory
|
// find out corresponding obj ix page and load it to memory
|
||||||
spiffs_page_header p_hdr;
|
spiffs_page_header p_hdr;
|
||||||
spiffs_page_ix objix_pix;
|
spiffs_page_ix objix_pix;
|
||||||
gc.stored_scan_entry_index = cur_entry;
|
gc.stored_scan_entry_index = cur_entry; // push cursor
|
||||||
cur_entry = 0;
|
cur_entry = 0; // restart scan from start
|
||||||
gc.state = MOVE_OBJ_DATA;
|
gc.state = MOVE_OBJ_DATA;
|
||||||
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
|
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);
|
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t*)&p_hdr);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix);
|
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);
|
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_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,
|
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);
|
0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
|
||||||
SPIFFS_CHECK_RES(res);
|
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);
|
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;
|
gc.cur_objix_pix = objix_pix;
|
||||||
} else {
|
} else {
|
||||||
|
// no more data pages found, passed thru all block, start evacuating object indices
|
||||||
gc.state = MOVE_OBJ_IX;
|
gc.state = MOVE_OBJ_IX;
|
||||||
cur_entry = 0; // restart entry scan index
|
cur_entry = 0; // restart entry scan index
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MOVE_OBJ_DATA: {
|
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;
|
spiffs_page_ix new_objix_pix;
|
||||||
gc.state = FIND_OBJ_DATA;
|
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) {
|
if (gc.cur_objix_spix == 0) {
|
||||||
// store object index header page
|
// 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);
|
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, %04x:%04x\n", new_objix_pix, 0);
|
SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0);
|
||||||
SPIFFS_CHECK_RES(res);
|
SPIFFS_CHECK_RES(res);
|
||||||
} else {
|
} else {
|
||||||
// store object index page
|
// 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);
|
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_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;
|
break;
|
||||||
case MOVE_OBJ_IX:
|
case MOVE_OBJ_IX:
|
||||||
|
// scanned thru all block, no more object indices found - our work here is done
|
||||||
gc.state = FINISHED;
|
gc.state = FINISHED;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
cur_entry = 0;
|
cur_entry = 0;
|
||||||
break;
|
break;
|
||||||
}
|
} // switch gc.state
|
||||||
SPIFFS_GC_DBG("gc_clean: state-> %i\n", gc.state);
|
SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state);
|
||||||
} // while state != FINISHED
|
} // while state != FINISHED
|
||||||
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // !SPIFFS_READ_ONLY
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
172
components/spiffs/include/spiffs/spiffs_nucleus.h → components/spiffs/spiffs/src/spiffs_nucleus.h
172
components/spiffs/include/spiffs/spiffs_nucleus.h → 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_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3)
|
||||||
#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4)
|
#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4)
|
||||||
|
|
||||||
|
// visitor result, continue searching
|
||||||
#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20)
|
#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)
|
#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21)
|
||||||
|
// visitor result, stop searching
|
||||||
#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22)
|
#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22)
|
||||||
|
|
||||||
#define SPIFFS_EV_IX_UPD 0
|
// updating an object index contents
|
||||||
#define SPIFFS_EV_IX_NEW 1
|
#define SPIFFS_EV_IX_UPD (0)
|
||||||
#define SPIFFS_EV_IX_DEL 2
|
// creating a new object index
|
||||||
|
#define SPIFFS_EV_IX_NEW (1)
|
||||||
|
// deleting an object index
|
||||||
|
#define SPIFFS_EV_IX_DEL (2)
|
||||||
|
// moving an object index without updating contents
|
||||||
|
#define SPIFFS_EV_IX_MOV (3)
|
||||||
|
// updating an object index header data only, not the table itself
|
||||||
|
#define SPIFFS_EV_IX_UPD_HDR (4)
|
||||||
|
|
||||||
#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1)))
|
#define SPIFFS_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_DELETED ((spiffs_obj_id)0)
|
||||||
#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1)
|
#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)
|
#define SPIFFS_CONFIG_MAGIC (0x20090315)
|
||||||
|
|
||||||
@ -220,6 +254,17 @@
|
|||||||
// object index span index number for given data span index or entry
|
// object index span index number for given data span index or entry
|
||||||
#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \
|
#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)))
|
((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)
|
#define SPIFFS_OP_T_OBJ_LU (0<<0)
|
||||||
@ -264,26 +309,26 @@
|
|||||||
#define SPIFFS_API_CHECK_MOUNT(fs) \
|
#define SPIFFS_API_CHECK_MOUNT(fs) \
|
||||||
if (!SPIFFS_CHECK_MOUNT((fs))) { \
|
if (!SPIFFS_CHECK_MOUNT((fs))) { \
|
||||||
(fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \
|
(fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \
|
||||||
return -1; \
|
return SPIFFS_ERR_NOT_MOUNTED; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SPIFFS_API_CHECK_CFG(fs) \
|
#define SPIFFS_API_CHECK_CFG(fs) \
|
||||||
if (!SPIFFS_CHECK_CFG((fs))) { \
|
if (!SPIFFS_CHECK_CFG((fs))) { \
|
||||||
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \
|
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \
|
||||||
return -1; \
|
return SPIFFS_ERR_NOT_CONFIGURED; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SPIFFS_API_CHECK_RES(fs, res) \
|
#define SPIFFS_API_CHECK_RES(fs, res) \
|
||||||
if ((res) < SPIFFS_OK) { \
|
if ((res) < SPIFFS_OK) { \
|
||||||
(fs)->err_code = (res); \
|
(fs)->err_code = (res); \
|
||||||
return -1; \
|
return (res); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
|
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
|
||||||
if ((res) < SPIFFS_OK) { \
|
if ((res) < SPIFFS_OK) { \
|
||||||
(fs)->err_code = (res); \
|
(fs)->err_code = (res); \
|
||||||
SPIFFS_UNLOCK(fs); \
|
SPIFFS_UNLOCK(fs); \
|
||||||
return -1; \
|
return (res); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \
|
#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \
|
||||||
@ -304,13 +349,33 @@
|
|||||||
if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH;
|
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)
|
#define SPIFFS_VIS_CHECK_ID (1<<0)
|
||||||
// report argument object id to visitor - else object lookup id is reported
|
// report argument object id to visitor - else object lookup id is reported
|
||||||
#define SPIFFS_VIS_CHECK_PH (1<<1)
|
#define SPIFFS_VIS_CHECK_PH (1<<1)
|
||||||
// stop searching at end of all look up pages
|
// stop searching at end of all look up pages
|
||||||
#define SPIFFS_VIS_NO_WRAP (1<<2)
|
#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
|
#if SPIFFS_CACHE
|
||||||
|
|
||||||
#define SPIFFS_CACHE_FLAG_DIRTY (1<<0)
|
#define SPIFFS_CACHE_FLAG_DIRTY (1<<0)
|
||||||
@ -390,13 +455,23 @@ typedef struct {
|
|||||||
spiffs_span_ix cursor_objix_spix;
|
spiffs_span_ix cursor_objix_spix;
|
||||||
// current absolute offset
|
// current absolute offset
|
||||||
u32_t offset;
|
u32_t offset;
|
||||||
// current file descriptor offset
|
// current file descriptor offset (cached)
|
||||||
u32_t fdoffset;
|
u32_t fdoffset;
|
||||||
// fd flags
|
// fd flags
|
||||||
spiffs_flags flags;
|
spiffs_flags flags;
|
||||||
#if SPIFFS_CACHE_WR
|
#if SPIFFS_CACHE_WR
|
||||||
spiffs_cache_page *cache_page;
|
spiffs_cache_page *cache_page;
|
||||||
#endif
|
#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;
|
} spiffs_fd;
|
||||||
|
|
||||||
|
|
||||||
@ -405,7 +480,7 @@ typedef struct {
|
|||||||
// page header, part of each page except object lookup pages
|
// page header, part of each page except object lookup pages
|
||||||
// NB: this is always aligned when the data page is an object index,
|
// NB: this is always aligned when the data page is an object index,
|
||||||
// as in this case struct spiffs_page_object_ix is used
|
// as in this case struct spiffs_page_object_ix is used
|
||||||
typedef struct __attribute(( packed )) {
|
typedef struct SPIFFS_PACKED {
|
||||||
// object id
|
// object id
|
||||||
spiffs_obj_id obj_id;
|
spiffs_obj_id obj_id;
|
||||||
// object span index
|
// object span index
|
||||||
@ -415,7 +490,7 @@ typedef struct __attribute(( packed )) {
|
|||||||
} spiffs_page_header;
|
} spiffs_page_header;
|
||||||
|
|
||||||
// object index header page header
|
// object index header page header
|
||||||
typedef struct __attribute(( packed ))
|
typedef struct SPIFFS_PACKED
|
||||||
#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
|
#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
|
||||||
__attribute(( aligned(sizeof(spiffs_page_ix)) ))
|
__attribute(( aligned(sizeof(spiffs_page_ix)) ))
|
||||||
#endif
|
#endif
|
||||||
@ -423,24 +498,28 @@ typedef struct __attribute(( packed ))
|
|||||||
// common page header
|
// common page header
|
||||||
spiffs_page_header p_hdr;
|
spiffs_page_header p_hdr;
|
||||||
// alignment
|
// 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
|
// size of object
|
||||||
u32_t size;
|
u32_t size;
|
||||||
// type of object
|
// type of object
|
||||||
spiffs_obj_type type;
|
spiffs_obj_type type;
|
||||||
// name of object
|
// name of object
|
||||||
u8_t name[SPIFFS_OBJ_NAME_LEN];
|
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;
|
} spiffs_page_object_ix_header;
|
||||||
|
|
||||||
// object index page header
|
// object index page header
|
||||||
typedef struct __attribute(( packed )) {
|
typedef struct SPIFFS_PACKED {
|
||||||
spiffs_page_header p_hdr;
|
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;
|
} spiffs_page_object_ix;
|
||||||
|
|
||||||
// callback func for object lookup visitor
|
// 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,
|
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
|
#if SPIFFS_CACHE
|
||||||
@ -501,8 +580,8 @@ s32_t spiffs_obj_lu_find_entry_visitor(
|
|||||||
u8_t flags,
|
u8_t flags,
|
||||||
spiffs_obj_id obj_id,
|
spiffs_obj_id obj_id,
|
||||||
spiffs_visitor_f v,
|
spiffs_visitor_f v,
|
||||||
u32_t user_data,
|
const void *user_const_p,
|
||||||
void *user_p,
|
void *user_var_p,
|
||||||
spiffs_block_ix *block_ix,
|
spiffs_block_ix *block_ix,
|
||||||
int *lu_entry);
|
int *lu_entry);
|
||||||
|
|
||||||
@ -510,6 +589,11 @@ s32_t spiffs_erase_block(
|
|||||||
spiffs *fs,
|
spiffs *fs,
|
||||||
spiffs_block_ix bix);
|
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(
|
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(
|
s32_t spiffs_obj_lu_find_free_obj_id(
|
||||||
spiffs *fs,
|
spiffs *fs,
|
||||||
spiffs_obj_id *obj_id,
|
spiffs_obj_id *obj_id,
|
||||||
u8_t *conflicting_name);
|
const u8_t *conflicting_name);
|
||||||
|
|
||||||
s32_t spiffs_obj_lu_find_free(
|
s32_t spiffs_obj_lu_find_free(
|
||||||
spiffs *fs,
|
spiffs *fs,
|
||||||
@ -579,7 +663,8 @@ s32_t spiffs_page_delete(
|
|||||||
s32_t spiffs_object_create(
|
s32_t spiffs_object_create(
|
||||||
spiffs *fs,
|
spiffs *fs,
|
||||||
spiffs_obj_id obj_id,
|
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_obj_type type,
|
||||||
spiffs_page_ix *objix_hdr_pix);
|
spiffs_page_ix *objix_hdr_pix);
|
||||||
|
|
||||||
@ -589,13 +674,24 @@ s32_t spiffs_object_update_index_hdr(
|
|||||||
spiffs_obj_id obj_id,
|
spiffs_obj_id obj_id,
|
||||||
spiffs_page_ix objix_hdr_pix,
|
spiffs_page_ix objix_hdr_pix,
|
||||||
u8_t *new_objix_hdr_data,
|
u8_t *new_objix_hdr_data,
|
||||||
u8_t name[SPIFFS_OBJ_NAME_LEN],
|
const u8_t name[],
|
||||||
|
const u8_t meta[],
|
||||||
u32_t size,
|
u32_t size,
|
||||||
spiffs_page_ix *new_pix);
|
spiffs_page_ix *new_pix);
|
||||||
|
|
||||||
void spiffs_cb_object_event(
|
#if SPIFFS_IX_MAP
|
||||||
|
|
||||||
|
s32_t spiffs_populate_ix_map(
|
||||||
spiffs *fs,
|
spiffs *fs,
|
||||||
spiffs_fd *fd,
|
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,
|
int ev,
|
||||||
spiffs_obj_id obj_id,
|
spiffs_obj_id obj_id,
|
||||||
spiffs_span_ix spix,
|
spiffs_span_ix spix,
|
||||||
@ -641,7 +737,7 @@ s32_t spiffs_object_truncate(
|
|||||||
|
|
||||||
s32_t spiffs_object_find_object_index_header_by_name(
|
s32_t spiffs_object_find_object_index_header_by_name(
|
||||||
spiffs *fs,
|
spiffs *fs,
|
||||||
u8_t name[SPIFFS_OBJ_NAME_LEN],
|
const u8_t name[SPIFFS_OBJ_NAME_LEN],
|
||||||
spiffs_page_ix *pix);
|
spiffs_page_ix *pix);
|
||||||
|
|
||||||
// ---------------
|
// ---------------
|
||||||
@ -671,7 +767,8 @@ s32_t spiffs_gc_quick(
|
|||||||
|
|
||||||
s32_t spiffs_fd_find_new(
|
s32_t spiffs_fd_find_new(
|
||||||
spiffs *fs,
|
spiffs *fs,
|
||||||
spiffs_fd **fd);
|
spiffs_fd **fd,
|
||||||
|
const char *name);
|
||||||
|
|
||||||
s32_t spiffs_fd_return(
|
s32_t spiffs_fd_return(
|
||||||
spiffs *fs,
|
spiffs *fs,
|
||||||
@ -682,6 +779,13 @@ s32_t spiffs_fd_get(
|
|||||||
spiffs_file f,
|
spiffs_file f,
|
||||||
spiffs_fd **fd);
|
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
|
#if SPIFFS_CACHE
|
||||||
void spiffs_cache_init(
|
void spiffs_cache_init(
|
||||||
spiffs *fs);
|
spiffs *fs);
|
||||||
@ -715,4 +819,24 @@ s32_t spiffs_page_consistency_check(
|
|||||||
s32_t spiffs_object_index_consistency_check(
|
s32_t spiffs_object_index_consistency_check(
|
||||||
spiffs *fs);
|
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_ */
|
#endif /* SPIFFS_NUCLEUS_H_ */
|
12
components/spiffs/spiffs/src/test/main.c
Normal file
12
components/spiffs/spiffs/src/test/main.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef NO_TEST
|
||||||
|
#include "testrunner.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main(int argc, char **args) {
|
||||||
|
#ifndef NO_TEST
|
||||||
|
run_tests(argc, args);
|
||||||
|
#endif
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
84
components/spiffs/spiffs/src/test/params_test.h
Normal file
84
components/spiffs/spiffs/src/test/params_test.h
Normal file
@ -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_ */
|
1266
components/spiffs/spiffs/src/test/test_bugreports.c
Normal file
1266
components/spiffs/spiffs/src/test/test_bugreports.c
Normal file
File diff suppressed because it is too large
Load Diff
427
components/spiffs/spiffs/src/test/test_check.c
Normal file
427
components/spiffs/spiffs/src/test/test_check.c
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
/*
|
||||||
|
* test_dev.c
|
||||||
|
*
|
||||||
|
* Created on: Jul 14, 2013
|
||||||
|
* Author: petera
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "testrunner.h"
|
||||||
|
#include "test_spiffs.h"
|
||||||
|
#include "spiffs_nucleus.h"
|
||||||
|
#include "spiffs.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
SUITE(check_tests)
|
||||||
|
static void setup() {
|
||||||
|
_setup();
|
||||||
|
}
|
||||||
|
static void teardown() {
|
||||||
|
_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(evil_write) {
|
||||||
|
fs_set_validate_flashing(0);
|
||||||
|
printf("writing corruption to block 1 data range (leaving lu intact)\n");
|
||||||
|
u32_t data_range = SPIFFS_CFG_LOG_BLOCK_SZ(FS) -
|
||||||
|
SPIFFS_CFG_LOG_PAGE_SZ(FS) * (SPIFFS_OBJ_LOOKUP_PAGES(FS));
|
||||||
|
u8_t *corruption = malloc(data_range);
|
||||||
|
memrand(corruption, data_range);
|
||||||
|
u32_t addr = 0 * SPIFFS_CFG_LOG_PAGE_SZ(FS) * SPIFFS_OBJ_LOOKUP_PAGES(FS);
|
||||||
|
area_write(addr, corruption, data_range);
|
||||||
|
free(corruption);
|
||||||
|
|
||||||
|
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||||
|
int res = test_create_and_write_file("file", size, size);
|
||||||
|
|
||||||
|
printf("CHECK1-----------------\n");
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
printf("CHECK2-----------------\n");
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
printf("CHECK3-----------------\n");
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
|
||||||
|
res = test_create_and_write_file("file2", size, size);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
return TEST_RES_OK;
|
||||||
|
} TEST_END
|
||||||
|
|
||||||
|
|
||||||
|
TEST(lu_check1) {
|
||||||
|
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||||
|
int res = test_create_and_write_file("file", size, size);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
spiffs_stat s;
|
||||||
|
res = SPIFFS_fstat(FS, fd, &s);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
|
||||||
|
// modify lu entry data page index 1
|
||||||
|
spiffs_page_ix pix;
|
||||||
|
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
// reset lu entry to being erased, but keep page data
|
||||||
|
spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED;
|
||||||
|
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
|
||||||
|
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
|
||||||
|
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry*sizeof(spiffs_obj_id);
|
||||||
|
|
||||||
|
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
|
||||||
|
|
||||||
|
#if SPIFFS_CACHE
|
||||||
|
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||||
|
cache->cpage_use_map = 0;
|
||||||
|
#endif
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
|
||||||
|
return TEST_RES_OK;
|
||||||
|
} TEST_END
|
||||||
|
|
||||||
|
|
||||||
|
TEST(page_cons1) {
|
||||||
|
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||||
|
int res = test_create_and_write_file("file", size, size);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
spiffs_stat s;
|
||||||
|
res = SPIFFS_fstat(FS, fd, &s);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
|
||||||
|
// modify object index, find object index header
|
||||||
|
spiffs_page_ix pix;
|
||||||
|
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
// set object index entry 2 to a bad page
|
||||||
|
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 0 * sizeof(spiffs_page_ix);
|
||||||
|
spiffs_page_ix bad_pix_ref = 0x55;
|
||||||
|
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
|
||||||
|
area_write(addr + sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
|
||||||
|
|
||||||
|
// delete all cache
|
||||||
|
#if SPIFFS_CACHE
|
||||||
|
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||||
|
cache->cpage_use_map = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
return TEST_RES_OK;
|
||||||
|
} TEST_END
|
||||||
|
|
||||||
|
|
||||||
|
TEST(page_cons2) {
|
||||||
|
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||||
|
int res = test_create_and_write_file("file", size, size);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
spiffs_stat s;
|
||||||
|
res = SPIFFS_fstat(FS, fd, &s);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
|
||||||
|
// modify object index, find object index header
|
||||||
|
spiffs_page_ix pix;
|
||||||
|
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
// find data page span index 0
|
||||||
|
spiffs_page_ix dpix;
|
||||||
|
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &dpix);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
// set object index entry 1+2 to a data page 0
|
||||||
|
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix);
|
||||||
|
spiffs_page_ix bad_pix_ref = dpix;
|
||||||
|
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
|
||||||
|
area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
|
||||||
|
|
||||||
|
// delete all cache
|
||||||
|
#if SPIFFS_CACHE
|
||||||
|
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||||
|
cache->cpage_use_map = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
return TEST_RES_OK;
|
||||||
|
} TEST_END
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TEST(page_cons3) {
|
||||||
|
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||||
|
int res = test_create_and_write_file("file", size, size);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
spiffs_stat s;
|
||||||
|
res = SPIFFS_fstat(FS, fd, &s);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
|
||||||
|
// modify object index, find object index header
|
||||||
|
spiffs_page_ix pix;
|
||||||
|
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
// set object index entry 1+2 lookup page
|
||||||
|
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix);
|
||||||
|
spiffs_page_ix bad_pix_ref = SPIFFS_PAGES_PER_BLOCK(FS) * (*FS.block_count - 2);
|
||||||
|
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
|
||||||
|
area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
|
||||||
|
|
||||||
|
// delete all cache
|
||||||
|
#if SPIFFS_CACHE
|
||||||
|
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||||
|
cache->cpage_use_map = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
return TEST_RES_OK;
|
||||||
|
} TEST_END
|
||||||
|
|
||||||
|
|
||||||
|
TEST(page_cons_final) {
|
||||||
|
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
|
||||||
|
int res = test_create_and_write_file("file", size, size);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
spiffs_stat s;
|
||||||
|
res = SPIFFS_fstat(FS, fd, &s);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
|
||||||
|
// modify page header, make unfinalized
|
||||||
|
spiffs_page_ix pix;
|
||||||
|
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
// set page span ix 1 as unfinalized
|
||||||
|
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags);
|
||||||
|
u8_t flags;
|
||||||
|
area_read(addr, (u8_t*)&flags, 1);
|
||||||
|
flags |= SPIFFS_PH_FLAG_FINAL;
|
||||||
|
area_write(addr, (u8_t*)&flags, 1);
|
||||||
|
|
||||||
|
// delete all cache
|
||||||
|
#if SPIFFS_CACHE
|
||||||
|
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||||
|
cache->cpage_use_map = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
return TEST_RES_OK;
|
||||||
|
} TEST_END
|
||||||
|
|
||||||
|
|
||||||
|
TEST(index_cons1) {
|
||||||
|
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
|
||||||
|
int res = test_create_and_write_file("file", size, size);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
spiffs_stat s;
|
||||||
|
res = SPIFFS_fstat(FS, fd, &s);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
|
||||||
|
// modify lu entry data page index header
|
||||||
|
spiffs_page_ix pix;
|
||||||
|
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
printf(" deleting lu entry pix %04x\n", pix);
|
||||||
|
// reset lu entry to being erased, but keep page data
|
||||||
|
spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED;
|
||||||
|
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
|
||||||
|
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
|
||||||
|
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
|
||||||
|
|
||||||
|
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
|
||||||
|
|
||||||
|
#if SPIFFS_CACHE
|
||||||
|
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||||
|
cache->cpage_use_map = 0;
|
||||||
|
#endif
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
return TEST_RES_OK;
|
||||||
|
} TEST_END
|
||||||
|
|
||||||
|
|
||||||
|
TEST(index_cons2) {
|
||||||
|
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
|
||||||
|
int res = test_create_and_write_file("file", size, size);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
spiffs_stat s;
|
||||||
|
res = SPIFFS_fstat(FS, fd, &s);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
|
||||||
|
// modify lu entry data page index header
|
||||||
|
spiffs_page_ix pix;
|
||||||
|
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
printf(" writing lu entry for index page, ix %04x, as data page\n", pix);
|
||||||
|
spiffs_obj_id obj_id = 0x1234;
|
||||||
|
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
|
||||||
|
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
|
||||||
|
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
|
||||||
|
|
||||||
|
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
|
||||||
|
|
||||||
|
#if SPIFFS_CACHE
|
||||||
|
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||||
|
cache->cpage_use_map = 0;
|
||||||
|
#endif
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
return TEST_RES_OK;
|
||||||
|
} TEST_END
|
||||||
|
|
||||||
|
|
||||||
|
TEST(index_cons3) {
|
||||||
|
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
|
||||||
|
int res = test_create_and_write_file("file", size, size);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
spiffs_stat s;
|
||||||
|
res = SPIFFS_fstat(FS, fd, &s);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
|
||||||
|
// modify lu entry data page index header
|
||||||
|
spiffs_page_ix pix;
|
||||||
|
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
printf(" setting lu entry pix %04x to another index page\n", pix);
|
||||||
|
// reset lu entry to being erased, but keep page data
|
||||||
|
spiffs_obj_id obj_id = 1234 | SPIFFS_OBJ_ID_IX_FLAG;
|
||||||
|
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
|
||||||
|
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
|
||||||
|
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
|
||||||
|
|
||||||
|
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
|
||||||
|
|
||||||
|
#if SPIFFS_CACHE
|
||||||
|
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||||
|
cache->cpage_use_map = 0;
|
||||||
|
#endif
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
return TEST_RES_OK;
|
||||||
|
} TEST_END
|
||||||
|
|
||||||
|
TEST(index_cons4) {
|
||||||
|
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
|
||||||
|
int res = test_create_and_write_file("file", size, size);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
res = read_and_verify("file");
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
spiffs_stat s;
|
||||||
|
res = SPIFFS_fstat(FS, fd, &s);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
|
||||||
|
// modify lu entry data page index header, flags
|
||||||
|
spiffs_page_ix pix;
|
||||||
|
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
printf(" cue objix hdr deletion in page %04x\n", pix);
|
||||||
|
// set flags as deleting ix header
|
||||||
|
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags);
|
||||||
|
u8_t flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE);
|
||||||
|
|
||||||
|
area_write(addr, (u8_t*)&flags, 1);
|
||||||
|
|
||||||
|
#if SPIFFS_CACHE
|
||||||
|
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||||
|
cache->cpage_use_map = 0;
|
||||||
|
#endif
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
|
||||||
|
return TEST_RES_OK;
|
||||||
|
} TEST_END
|
||||||
|
|
||||||
|
SUITE_TESTS(check_tests)
|
||||||
|
ADD_TEST(evil_write)
|
||||||
|
ADD_TEST(lu_check1)
|
||||||
|
ADD_TEST(page_cons1)
|
||||||
|
ADD_TEST(page_cons2)
|
||||||
|
ADD_TEST(page_cons3)
|
||||||
|
ADD_TEST(page_cons_final)
|
||||||
|
ADD_TEST(index_cons1)
|
||||||
|
ADD_TEST(index_cons2)
|
||||||
|
ADD_TEST(index_cons3)
|
||||||
|
ADD_TEST(index_cons4)
|
||||||
|
SUITE_END(check_tests)
|
122
components/spiffs/spiffs/src/test/test_dev.c
Normal file
122
components/spiffs/spiffs/src/test/test_dev.c
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* test_dev.c
|
||||||
|
*
|
||||||
|
* Created on: Jul 14, 2013
|
||||||
|
* Author: petera
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "testrunner.h"
|
||||||
|
#include "test_spiffs.h"
|
||||||
|
#include "spiffs_nucleus.h"
|
||||||
|
#include "spiffs.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
SUITE(dev_tests)
|
||||||
|
static void setup() {
|
||||||
|
_setup();
|
||||||
|
}
|
||||||
|
static void teardown() {
|
||||||
|
_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(interrupted_write) {
|
||||||
|
char *name = "interrupt";
|
||||||
|
char *name2 = "interrupt2";
|
||||||
|
int res;
|
||||||
|
spiffs_file fd;
|
||||||
|
|
||||||
|
const u32_t sz = SPIFFS_CFG_LOG_PAGE_SZ(FS)*8;
|
||||||
|
u8_t *buf = malloc(sz);
|
||||||
|
memrand(buf, sz);
|
||||||
|
|
||||||
|
printf(" create reference file\n");
|
||||||
|
fd = SPIFFS_open(FS, name, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
clear_flash_ops_log();
|
||||||
|
res = SPIFFS_write(FS, fd, buf, sz);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
|
||||||
|
u32_t written = get_flash_ops_log_write_bytes();
|
||||||
|
printf(" written bytes: %i\n", written);
|
||||||
|
|
||||||
|
|
||||||
|
printf(" create error file\n");
|
||||||
|
fd = SPIFFS_open(FS, name2, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
clear_flash_ops_log();
|
||||||
|
invoke_error_after_write_bytes(written/2, 0);
|
||||||
|
res = SPIFFS_write(FS, fd, buf, sz);
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_TEST);
|
||||||
|
|
||||||
|
clear_flash_ops_log();
|
||||||
|
|
||||||
|
#if SPIFFS_CACHE
|
||||||
|
// delete all cache
|
||||||
|
spiffs_cache *cache = spiffs_get_cache(FS);
|
||||||
|
cache->cpage_use_map = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
printf(" read error file\n");
|
||||||
|
fd = SPIFFS_open(FS, name2, SPIFFS_RDONLY, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
|
||||||
|
spiffs_stat s;
|
||||||
|
res = SPIFFS_fstat(FS, fd, &s);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
printf(" file size: %i\n", s.size);
|
||||||
|
|
||||||
|
if (s.size > 0) {
|
||||||
|
u8_t *buf2 = malloc(s.size);
|
||||||
|
res = SPIFFS_read(FS, fd, buf2, s.size);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
|
||||||
|
u32_t ix = 0;
|
||||||
|
for (ix = 0; ix < s.size; ix += 16) {
|
||||||
|
int i;
|
||||||
|
printf(" ");
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
printf("%02x", buf[ix+i]);
|
||||||
|
}
|
||||||
|
printf(" ");
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
printf("%02x", buf2[ix+i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
free(buf2);
|
||||||
|
}
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
|
||||||
|
|
||||||
|
printf(" FS check\n");
|
||||||
|
SPIFFS_check(FS);
|
||||||
|
|
||||||
|
printf(" read error file again\n");
|
||||||
|
fd = SPIFFS_open(FS, name2, SPIFFS_APPEND | SPIFFS_RDWR, 0);
|
||||||
|
TEST_CHECK(fd > 0);
|
||||||
|
res = SPIFFS_fstat(FS, fd, &s);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
printf(" file size: %i\n", s.size);
|
||||||
|
printf(" write file\n");
|
||||||
|
res = SPIFFS_write(FS, fd, buf, sz);
|
||||||
|
TEST_CHECK(res >= 0);
|
||||||
|
SPIFFS_close(FS, fd);
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return TEST_RES_OK;
|
||||||
|
|
||||||
|
} TEST_END
|
||||||
|
|
||||||
|
SUITE_TESTS(dev_tests)
|
||||||
|
ADD_TEST(interrupted_write)
|
||||||
|
SUITE_END(dev_tests)
|
2507
components/spiffs/spiffs/src/test/test_hydrogen.c
Normal file
2507
components/spiffs/spiffs/src/test/test_hydrogen.c
Normal file
File diff suppressed because it is too large
Load Diff
1111
components/spiffs/spiffs/src/test/test_spiffs.c
Normal file
1111
components/spiffs/spiffs/src/test/test_spiffs.c
Normal file
File diff suppressed because it is too large
Load Diff
109
components/spiffs/spiffs/src/test/test_spiffs.h
Normal file
109
components/spiffs/spiffs/src/test/test_spiffs.h
Normal file
@ -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_ */
|
238
components/spiffs/spiffs/src/test/testrunner.c
Normal file
238
components/spiffs/spiffs/src/test/testrunner.c
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
/*
|
||||||
|
* testrunner.c
|
||||||
|
*
|
||||||
|
* Created on: Jun 18, 2013
|
||||||
|
* Author: petera
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "testrunner.h"
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
test *tests;
|
||||||
|
test *_last_test;
|
||||||
|
int test_count;
|
||||||
|
void (*on_stop)(test *t);
|
||||||
|
test_res *failed;
|
||||||
|
test_res *failed_last;
|
||||||
|
test_res *stopped;
|
||||||
|
test_res *stopped_last;
|
||||||
|
FILE *spec;
|
||||||
|
char incl_filter[256];
|
||||||
|
char excl_filter[256];
|
||||||
|
} test_main;
|
||||||
|
|
||||||
|
void test_init(void (*on_stop)(test *t)) {
|
||||||
|
test_main.on_stop = on_stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int abort_on_error = 0;
|
||||||
|
static int error_count = 0;
|
||||||
|
|
||||||
|
static char check_spec(char *name) {
|
||||||
|
if (test_main.spec) {
|
||||||
|
fseek(test_main.spec, 0, SEEK_SET);
|
||||||
|
char *line = NULL;
|
||||||
|
size_t sz;
|
||||||
|
ssize_t read;
|
||||||
|
while ((read = getline(&line, &sz, test_main.spec)) != -1) {
|
||||||
|
if (strncmp(line, name, strlen(line)-1) == 0) {
|
||||||
|
free(line);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char check_incl_filter(char *name) {
|
||||||
|
if (strlen(test_main.incl_filter)== 0) return 1;
|
||||||
|
return strstr(name, test_main.incl_filter) == 0 ? 0 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char check_excl_filter(char *name) {
|
||||||
|
if (strlen(test_main.excl_filter)== 0) return 1;
|
||||||
|
return strstr(name, test_main.excl_filter) == 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t), int non_default) {
|
||||||
|
if (f == 0) return;
|
||||||
|
if (!check_spec(name)) return;
|
||||||
|
if (check_incl_filter(name) <= non_default) return;
|
||||||
|
if (!check_excl_filter(name)) return;
|
||||||
|
DBGT("adding test %s\n", name);
|
||||||
|
test *t = malloc(sizeof(test));
|
||||||
|
memset(t, 0, sizeof(test));
|
||||||
|
t->f = f;
|
||||||
|
strcpy(t->name, name);
|
||||||
|
t->setup = setup;
|
||||||
|
t->teardown = teardown;
|
||||||
|
if (test_main.tests == 0) {
|
||||||
|
test_main.tests = t;
|
||||||
|
} else {
|
||||||
|
test_main._last_test->_next = t;
|
||||||
|
}
|
||||||
|
test_main._last_test = t;
|
||||||
|
test_main.test_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_res(test *t, test_res **head, test_res **last) {
|
||||||
|
test_res *tr = malloc(sizeof(test_res));
|
||||||
|
memset(tr,0,sizeof(test_res));
|
||||||
|
strcpy(tr->name, t->name);
|
||||||
|
if (*head == 0) {
|
||||||
|
*head = tr;
|
||||||
|
} else {
|
||||||
|
(*last)->_next = tr;
|
||||||
|
}
|
||||||
|
*last = tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_res(test_res **head) {
|
||||||
|
test_res *tr = (*head);
|
||||||
|
while (tr) {
|
||||||
|
test_res *next_tr = tr->_next;
|
||||||
|
printf(" %s\n", tr->name);
|
||||||
|
free(tr);
|
||||||
|
tr = next_tr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_error_count(void) {
|
||||||
|
return error_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void inc_error_count(void) {
|
||||||
|
error_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int set_abort_on_error(int val) {
|
||||||
|
int old_val = abort_on_error;
|
||||||
|
abort_on_error = val;
|
||||||
|
|
||||||
|
return old_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_abort_on_error(void) {
|
||||||
|
return abort_on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_tests(int argc, char **args) {
|
||||||
|
memset(&test_main, 0, sizeof(test_main));
|
||||||
|
int arg;
|
||||||
|
int incl_filter = 0;
|
||||||
|
int excl_filter = 0;
|
||||||
|
for (arg = 1; arg < argc; arg++) {
|
||||||
|
if (strlen(args[arg]) == 0) continue;
|
||||||
|
if (0 == strcmp("-f", args[arg])) {
|
||||||
|
incl_filter = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == strcmp("-e", args[arg])) {
|
||||||
|
excl_filter = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (incl_filter) {
|
||||||
|
strcpy(test_main.incl_filter, args[arg]);
|
||||||
|
incl_filter = 0;
|
||||||
|
} else if (excl_filter) {
|
||||||
|
strcpy(test_main.excl_filter, args[arg]);
|
||||||
|
excl_filter = 0;
|
||||||
|
} else {
|
||||||
|
printf("running tests from %s\n", args[arg]);
|
||||||
|
FILE *fd = fopen(args[1], "r");
|
||||||
|
if (fd == NULL) {
|
||||||
|
printf("%s not found\n", args[arg]);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
test_main.spec = fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBGT("adding suites...\n");
|
||||||
|
add_suites();
|
||||||
|
DBGT("%i tests added\n", test_main.test_count);
|
||||||
|
if (test_main.spec) {
|
||||||
|
fclose(test_main.spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_main.test_count == 0) {
|
||||||
|
printf("No tests to run\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd_success = open("_tests_ok", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||||
|
int fd_bad = open("_tests_fail", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||||
|
|
||||||
|
DBGT("running tests...\n");
|
||||||
|
int ok = 0;
|
||||||
|
int failed = 0;
|
||||||
|
int stopped = 0;
|
||||||
|
test *cur_t = test_main.tests;
|
||||||
|
int i = 1;
|
||||||
|
while (cur_t) {
|
||||||
|
cur_t->setup(cur_t);
|
||||||
|
test *next_test = cur_t->_next;
|
||||||
|
DBGT("TEST %i/%i : running test %s\n", i, test_main.test_count, cur_t->name);
|
||||||
|
i++;
|
||||||
|
int start_error_count = get_error_count();
|
||||||
|
int res = cur_t->f(cur_t);
|
||||||
|
if (res == TEST_RES_OK && get_error_count() != start_error_count) {
|
||||||
|
res = TEST_RES_FAIL;
|
||||||
|
}
|
||||||
|
cur_t->test_result = res;
|
||||||
|
int fd = res == TEST_RES_OK ? fd_success : fd_bad;
|
||||||
|
write(fd, cur_t->name, strlen(cur_t->name));
|
||||||
|
write(fd, "\n", 1);
|
||||||
|
switch (res) {
|
||||||
|
case TEST_RES_OK:
|
||||||
|
ok++;
|
||||||
|
printf(" .. ok\n");
|
||||||
|
break;
|
||||||
|
case TEST_RES_FAIL:
|
||||||
|
failed++;
|
||||||
|
printf(" .. FAILED\n");
|
||||||
|
if (test_main.on_stop) test_main.on_stop(cur_t);
|
||||||
|
add_res(cur_t, &test_main.failed, &test_main.failed_last);
|
||||||
|
break;
|
||||||
|
case TEST_RES_ASSERT:
|
||||||
|
stopped++;
|
||||||
|
printf(" .. ABORTED\n");
|
||||||
|
if (test_main.on_stop) test_main.on_stop(cur_t);
|
||||||
|
add_res(cur_t, &test_main.stopped, &test_main.stopped_last);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cur_t->teardown(cur_t);
|
||||||
|
free(cur_t);
|
||||||
|
cur_t = next_test;
|
||||||
|
}
|
||||||
|
close(fd_success);
|
||||||
|
close(fd_bad);
|
||||||
|
DBGT("ran %i tests\n", test_main.test_count);
|
||||||
|
printf("Test report, %i tests\n", test_main.test_count);
|
||||||
|
printf("%i succeeded\n", ok);
|
||||||
|
printf("%i failed\n", failed);
|
||||||
|
dump_res(&test_main.failed);
|
||||||
|
printf("%i stopped\n", stopped);
|
||||||
|
dump_res(&test_main.stopped);
|
||||||
|
if (ok < test_main.test_count) {
|
||||||
|
printf("\nFAILED\n");
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
printf("\nALL TESTS OK\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
165
components/spiffs/spiffs/src/test/testrunner.h
Normal file
165
components/spiffs/spiffs/src/test/testrunner.h
Normal file
@ -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_ */
|
15
components/spiffs/spiffs/src/test/testsuites.c
Normal file
15
components/spiffs/spiffs/src/test/testsuites.c
Normal file
@ -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);
|
||||||
|
}
|
93
components/spiffs/spiffs_api.c
Normal file
93
components/spiffs/spiffs_api.c
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
57
components/spiffs/spiffs_api.h
Normal file
57
components/spiffs/spiffs_api.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include "spiffs.h"
|
||||||
|
#include "esp_vfs.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SPIFFS definition structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
spiffs *fs; /*!< Handle to the underlying SPIFFS */
|
||||||
|
SemaphoreHandle_t lock; /*!< FS lock */
|
||||||
|
const esp_partition_t* partition; /*!< The partition on which SPIFFS is located */
|
||||||
|
char base_path[ESP_VFS_PATH_MAX+1]; /*!< Mount point */
|
||||||
|
bool by_label; /*!< Partition was mounted by label */
|
||||||
|
spiffs_config cfg; /*!< SPIFFS Mount configuration */
|
||||||
|
uint8_t *work; /*!< Work Buffer */
|
||||||
|
uint8_t *fds; /*!< File Descriptor Buffer */
|
||||||
|
uint32_t fds_sz; /*!< File Descriptor Buffer Length */
|
||||||
|
uint8_t *cache; /*!< Cache Buffer */
|
||||||
|
uint32_t cache_sz; /*!< Cache Buffer Length */
|
||||||
|
} esp_spiffs_t;
|
||||||
|
|
||||||
|
s32_t spiffs_api_read(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *dst);
|
||||||
|
|
||||||
|
s32_t spiffs_api_write(spiffs *fs, uint32_t addr, uint32_t size, uint8_t *src);
|
||||||
|
|
||||||
|
s32_t spiffs_api_erase(spiffs *fs, uint32_t addr, uint32_t size);
|
||||||
|
|
||||||
|
void spiffs_api_check(spiffs *fs, spiffs_check_type type,
|
||||||
|
spiffs_check_report report, uint32_t arg1, uint32_t arg2);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
6
components/spiffs/test/CMakeLists.txt
Normal file
6
components/spiffs/test/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
set(COMPONENT_SRCDIRS ".")
|
||||||
|
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||||
|
|
||||||
|
set(COMPONENT_REQUIRES unity spiffs)
|
||||||
|
|
||||||
|
register_component()
|
1
components/spiffs/test/component.mk
Normal file
1
components/spiffs/test/component.mk
Normal file
@ -0,0 +1 @@
|
|||||||
|
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
652
components/spiffs/test/test_spiffs.c
Normal file
652
components/spiffs/test/test_spiffs.c
Normal file
@ -0,0 +1,652 @@
|
|||||||
|
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
|
#include "unity.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
#include "esp_vfs.h"
|
||||||
|
#include "esp_spiffs.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include "esp_partition.h"
|
||||||
|
|
||||||
|
const char* spiffs_test_hello_str = "Hello, World!\n";
|
||||||
|
const char* spiffs_test_partition_label = "flash_test";
|
||||||
|
|
||||||
|
void test_spiffs_create_file_with_text(const char* name, const char* text)
|
||||||
|
{
|
||||||
|
FILE* f = fopen(name, "wb");
|
||||||
|
TEST_ASSERT_NOT_NULL(f);
|
||||||
|
TEST_ASSERT_TRUE(fputs(text, f) != EOF);
|
||||||
|
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_spiffs_overwrite_append(const char* filename)
|
||||||
|
{
|
||||||
|
/* Create new file with 'aaaa' */
|
||||||
|
test_spiffs_create_file_with_text(filename, "aaaa");
|
||||||
|
|
||||||
|
/* Append 'bbbb' to file */
|
||||||
|
FILE *f_a = fopen(filename, "a");
|
||||||
|
TEST_ASSERT_NOT_NULL(f_a);
|
||||||
|
TEST_ASSERT_NOT_EQUAL(EOF, fputs("bbbb", f_a));
|
||||||
|
TEST_ASSERT_EQUAL(0, fclose(f_a));
|
||||||
|
|
||||||
|
/* Read back 8 bytes from file, verify it's 'aaaabbbb' */
|
||||||
|
char buf[10] = { 0 };
|
||||||
|
FILE *f_r = fopen(filename, "r");
|
||||||
|
TEST_ASSERT_NOT_NULL(f_r);
|
||||||
|
TEST_ASSERT_EQUAL(8, fread(buf, 1, 8, f_r));
|
||||||
|
TEST_ASSERT_EQUAL_STRING_LEN("aaaabbbb", buf, 8);
|
||||||
|
|
||||||
|
/* Be sure we're at end of file */
|
||||||
|
TEST_ASSERT_EQUAL(0, fread(buf, 1, 8, f_r));
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(0, fclose(f_r));
|
||||||
|
|
||||||
|
/* Overwrite file with 'cccc' */
|
||||||
|
test_spiffs_create_file_with_text(filename, "cccc");
|
||||||
|
|
||||||
|
/* Verify file now only contains 'cccc' */
|
||||||
|
f_r = fopen(filename, "r");
|
||||||
|
TEST_ASSERT_NOT_NULL(f_r);
|
||||||
|
bzero(buf, sizeof(buf));
|
||||||
|
TEST_ASSERT_EQUAL(4, fread(buf, 1, 8, f_r)); // trying to read 8 bytes, only expecting 4
|
||||||
|
TEST_ASSERT_EQUAL_STRING_LEN("cccc", buf, 4);
|
||||||
|
TEST_ASSERT_EQUAL(0, fclose(f_r));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_spiffs_read_file(const char* filename)
|
||||||
|
{
|
||||||
|
FILE* f = fopen(filename, "r");
|
||||||
|
TEST_ASSERT_NOT_NULL(f);
|
||||||
|
char buf[32] = { 0 };
|
||||||
|
int cb = fread(buf, 1, sizeof(buf), f);
|
||||||
|
TEST_ASSERT_EQUAL(strlen(spiffs_test_hello_str), cb);
|
||||||
|
TEST_ASSERT_EQUAL(0, strcmp(spiffs_test_hello_str, buf));
|
||||||
|
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_spiffs_open_max_files(const char* filename_prefix, size_t files_count)
|
||||||
|
{
|
||||||
|
FILE** files = calloc(files_count, sizeof(FILE*));
|
||||||
|
for (size_t i = 0; i < files_count; ++i) {
|
||||||
|
char name[32];
|
||||||
|
snprintf(name, sizeof(name), "%s_%d.txt", filename_prefix, i);
|
||||||
|
files[i] = fopen(name, "w");
|
||||||
|
TEST_ASSERT_NOT_NULL(files[i]);
|
||||||
|
}
|
||||||
|
/* close everything and clean up */
|
||||||
|
for (size_t i = 0; i < files_count; ++i) {
|
||||||
|
fclose(files[i]);
|
||||||
|
}
|
||||||
|
free(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_spiffs_lseek(const char* filename)
|
||||||
|
{
|
||||||
|
FILE* f = fopen(filename, "wb+");
|
||||||
|
TEST_ASSERT_NOT_NULL(f);
|
||||||
|
TEST_ASSERT_EQUAL(11, fprintf(f, "0123456789\n"));
|
||||||
|
TEST_ASSERT_EQUAL(0, fseek(f, -2, SEEK_CUR));
|
||||||
|
TEST_ASSERT_EQUAL('9', fgetc(f));
|
||||||
|
TEST_ASSERT_EQUAL(0, fseek(f, 3, SEEK_SET));
|
||||||
|
TEST_ASSERT_EQUAL('3', fgetc(f));
|
||||||
|
TEST_ASSERT_EQUAL(0, fseek(f, -3, SEEK_END));
|
||||||
|
TEST_ASSERT_EQUAL('8', fgetc(f));
|
||||||
|
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
|
||||||
|
TEST_ASSERT_EQUAL(11, ftell(f));
|
||||||
|
TEST_ASSERT_EQUAL(4, fprintf(f, "abc\n"));
|
||||||
|
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_END));
|
||||||
|
TEST_ASSERT_EQUAL(15, ftell(f));
|
||||||
|
TEST_ASSERT_EQUAL(0, fseek(f, 0, SEEK_SET));
|
||||||
|
char buf[20];
|
||||||
|
TEST_ASSERT_EQUAL(15, fread(buf, 1, sizeof(buf), f));
|
||||||
|
const char ref_buf[] = "0123456789\nabc\n";
|
||||||
|
TEST_ASSERT_EQUAL_INT8_ARRAY(ref_buf, buf, sizeof(ref_buf) - 1);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_spiffs_stat(const char* filename)
|
||||||
|
{
|
||||||
|
test_spiffs_create_file_with_text(filename, "foo\n");
|
||||||
|
struct stat st;
|
||||||
|
TEST_ASSERT_EQUAL(0, stat(filename, &st));
|
||||||
|
TEST_ASSERT(st.st_mode & S_IFREG);
|
||||||
|
TEST_ASSERT_FALSE(st.st_mode & S_IFDIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_spiffs_unlink(const char* filename)
|
||||||
|
{
|
||||||
|
test_spiffs_create_file_with_text(filename, "unlink\n");
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(0, unlink(filename));
|
||||||
|
|
||||||
|
TEST_ASSERT_NULL(fopen(filename, "r"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_spiffs_rename(const char* filename_prefix)
|
||||||
|
{
|
||||||
|
char name_dst[64];
|
||||||
|
char name_src[64];
|
||||||
|
snprintf(name_dst, sizeof(name_dst), "%s_dst.txt", filename_prefix);
|
||||||
|
snprintf(name_src, sizeof(name_src), "%s_src.txt", filename_prefix);
|
||||||
|
|
||||||
|
unlink(name_dst);
|
||||||
|
unlink(name_src);
|
||||||
|
|
||||||
|
FILE* f = fopen(name_src, "w+");
|
||||||
|
TEST_ASSERT_NOT_NULL(f);
|
||||||
|
const char* str = "0123456789";
|
||||||
|
for (int i = 0; i < 400; ++i) {
|
||||||
|
TEST_ASSERT_NOT_EQUAL(EOF, fputs(str, f));
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||||
|
TEST_ASSERT_EQUAL(0, rename(name_src, name_dst));
|
||||||
|
TEST_ASSERT_NULL(fopen(name_src, "r"));
|
||||||
|
FILE* fdst = fopen(name_dst, "r");
|
||||||
|
TEST_ASSERT_NOT_NULL(fdst);
|
||||||
|
TEST_ASSERT_EQUAL(0, fseek(fdst, 0, SEEK_END));
|
||||||
|
TEST_ASSERT_EQUAL(4000, ftell(fdst));
|
||||||
|
TEST_ASSERT_EQUAL(0, fclose(fdst));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_spiffs_can_opendir(const char* path)
|
||||||
|
{
|
||||||
|
char name_dir_file[64];
|
||||||
|
const char * file_name = "test_opd.txt";
|
||||||
|
snprintf(name_dir_file, sizeof(name_dir_file), "%s/%s", path, file_name);
|
||||||
|
unlink(name_dir_file);
|
||||||
|
test_spiffs_create_file_with_text(name_dir_file, "test_opendir\n");
|
||||||
|
DIR* dir = opendir(path);
|
||||||
|
TEST_ASSERT_NOT_NULL(dir);
|
||||||
|
bool found = false;
|
||||||
|
while (true) {
|
||||||
|
struct dirent* de = readdir(dir);
|
||||||
|
if (!de) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (strcasecmp(de->d_name, file_name) == 0) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST_ASSERT_TRUE(found);
|
||||||
|
TEST_ASSERT_EQUAL(0, closedir(dir));
|
||||||
|
unlink(name_dir_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_spiffs_opendir_readdir_rewinddir(const char* dir_prefix)
|
||||||
|
{
|
||||||
|
char name_dir_inner_file[64];
|
||||||
|
char name_dir_inner[64];
|
||||||
|
char name_dir_file3[64];
|
||||||
|
char name_dir_file2[64];
|
||||||
|
char name_dir_file1[64];
|
||||||
|
|
||||||
|
snprintf(name_dir_inner_file, sizeof(name_dir_inner_file), "%s/inner/3.txt", dir_prefix);
|
||||||
|
snprintf(name_dir_inner, sizeof(name_dir_inner), "%s/inner", dir_prefix);
|
||||||
|
snprintf(name_dir_file3, sizeof(name_dir_file2), "%s/boo.bin", dir_prefix);
|
||||||
|
snprintf(name_dir_file2, sizeof(name_dir_file2), "%s/2.txt", dir_prefix);
|
||||||
|
snprintf(name_dir_file1, sizeof(name_dir_file1), "%s/1.txt", dir_prefix);
|
||||||
|
|
||||||
|
unlink(name_dir_inner_file);
|
||||||
|
rmdir(name_dir_inner);
|
||||||
|
unlink(name_dir_file1);
|
||||||
|
unlink(name_dir_file2);
|
||||||
|
unlink(name_dir_file3);
|
||||||
|
rmdir(dir_prefix);
|
||||||
|
|
||||||
|
test_spiffs_create_file_with_text(name_dir_file1, "1\n");
|
||||||
|
test_spiffs_create_file_with_text(name_dir_file2, "2\n");
|
||||||
|
test_spiffs_create_file_with_text(name_dir_file3, "\01\02\03");
|
||||||
|
test_spiffs_create_file_with_text(name_dir_inner_file, "3\n");
|
||||||
|
|
||||||
|
DIR* dir = opendir(dir_prefix);
|
||||||
|
TEST_ASSERT_NOT_NULL(dir);
|
||||||
|
int count = 0;
|
||||||
|
const char* names[4];
|
||||||
|
while(count < 4) {
|
||||||
|
struct dirent* de = readdir(dir);
|
||||||
|
if (!de) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
printf("found '%s'\n", de->d_name);
|
||||||
|
if (strcasecmp(de->d_name, "1.txt") == 0) {
|
||||||
|
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||||
|
names[count] = "1.txt";
|
||||||
|
++count;
|
||||||
|
} else if (strcasecmp(de->d_name, "2.txt") == 0) {
|
||||||
|
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||||
|
names[count] = "2.txt";
|
||||||
|
++count;
|
||||||
|
} else if (strcasecmp(de->d_name, "inner/3.txt") == 0) {
|
||||||
|
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||||
|
names[count] = "inner/3.txt";
|
||||||
|
++count;
|
||||||
|
} else if (strcasecmp(de->d_name, "boo.bin") == 0) {
|
||||||
|
TEST_ASSERT_TRUE(de->d_type == DT_REG);
|
||||||
|
names[count] = "boo.bin";
|
||||||
|
++count;
|
||||||
|
} else {
|
||||||
|
TEST_FAIL_MESSAGE("unexpected directory entry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL(count, 4);
|
||||||
|
|
||||||
|
rewinddir(dir);
|
||||||
|
struct dirent* de = readdir(dir);
|
||||||
|
TEST_ASSERT_NOT_NULL(de);
|
||||||
|
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[0]));
|
||||||
|
seekdir(dir, 3);
|
||||||
|
de = readdir(dir);
|
||||||
|
TEST_ASSERT_NOT_NULL(de);
|
||||||
|
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[3]));
|
||||||
|
seekdir(dir, 1);
|
||||||
|
de = readdir(dir);
|
||||||
|
TEST_ASSERT_NOT_NULL(de);
|
||||||
|
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[1]));
|
||||||
|
seekdir(dir, 2);
|
||||||
|
de = readdir(dir);
|
||||||
|
TEST_ASSERT_NOT_NULL(de);
|
||||||
|
TEST_ASSERT_EQUAL(0, strcasecmp(de->d_name, names[2]));
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(0, closedir(dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_spiffs_readdir_many_files(const char* dir_prefix)
|
||||||
|
{
|
||||||
|
const int n_files = 40;
|
||||||
|
const int n_folders = 4;
|
||||||
|
unsigned char file_count[n_files * n_folders];
|
||||||
|
memset(file_count, 0, sizeof(file_count)/sizeof(file_count[0]));
|
||||||
|
char file_name[ESP_VFS_PATH_MAX + CONFIG_SPIFFS_OBJ_NAME_LEN];
|
||||||
|
|
||||||
|
/* clean stale files before the test */
|
||||||
|
DIR* dir = opendir(dir_prefix);
|
||||||
|
if (dir) {
|
||||||
|
while (true) {
|
||||||
|
struct dirent* de = readdir(dir);
|
||||||
|
if (!de) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int len = snprintf(file_name, sizeof(file_name), "%s/%s", dir_prefix, de->d_name);
|
||||||
|
assert(len < sizeof(file_name));
|
||||||
|
unlink(file_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create files */
|
||||||
|
for (int d = 0; d < n_folders; ++d) {
|
||||||
|
printf("filling directory %d\n", d);
|
||||||
|
for (int f = 0; f < n_files; ++f) {
|
||||||
|
snprintf(file_name, sizeof(file_name), "%s/%d/%d.txt", dir_prefix, d, f);
|
||||||
|
test_spiffs_create_file_with_text(file_name, file_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* list files */
|
||||||
|
for (int d = 0; d < n_folders; ++d) {
|
||||||
|
printf("listing files in directory %d\n", d);
|
||||||
|
snprintf(file_name, sizeof(file_name), "%s/%d", dir_prefix, d);
|
||||||
|
dir = opendir(file_name);
|
||||||
|
TEST_ASSERT_NOT_NULL(dir);
|
||||||
|
while (true) {
|
||||||
|
struct dirent* de = readdir(dir);
|
||||||
|
if (!de) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int file_id;
|
||||||
|
TEST_ASSERT_EQUAL(1, sscanf(de->d_name, "%d.txt", &file_id));
|
||||||
|
file_count[file_id + d * n_files]++;
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that all created files have been seen */
|
||||||
|
for (int d = 0; d < n_folders; ++d) {
|
||||||
|
printf("checking that all files have been found in directory %d\n", d);
|
||||||
|
for (int f = 0; f < n_files; ++f) {
|
||||||
|
TEST_ASSERT_EQUAL(1, file_count[f + d * n_files]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* filename;
|
||||||
|
bool write;
|
||||||
|
size_t word_count;
|
||||||
|
int seed;
|
||||||
|
SemaphoreHandle_t done;
|
||||||
|
int result;
|
||||||
|
} read_write_test_arg_t;
|
||||||
|
|
||||||
|
#define READ_WRITE_TEST_ARG_INIT(name, seed_) \
|
||||||
|
{ \
|
||||||
|
.filename = name, \
|
||||||
|
.seed = seed_, \
|
||||||
|
.word_count = 4096, \
|
||||||
|
.write = true, \
|
||||||
|
.done = xSemaphoreCreateBinary() \
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_write_task(void* param)
|
||||||
|
{
|
||||||
|
read_write_test_arg_t* args = (read_write_test_arg_t*) param;
|
||||||
|
FILE* f = fopen(args->filename, args->write ? "wb" : "rb");
|
||||||
|
if (f == NULL) {
|
||||||
|
args->result = ESP_ERR_NOT_FOUND;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
srand(args->seed);
|
||||||
|
for (size_t i = 0; i < args->word_count; ++i) {
|
||||||
|
uint32_t val = rand();
|
||||||
|
if (args->write) {
|
||||||
|
int cnt = fwrite(&val, sizeof(val), 1, f);
|
||||||
|
if (cnt != 1) {
|
||||||
|
ets_printf("E(w): i=%d, cnt=%d val=%d\n\n", i, cnt, val);
|
||||||
|
args->result = ESP_FAIL;
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint32_t rval;
|
||||||
|
int cnt = fread(&rval, sizeof(rval), 1, f);
|
||||||
|
if (cnt != 1) {
|
||||||
|
ets_printf("E(r): i=%d, cnt=%d rval=%d\n\n", i, cnt, rval);
|
||||||
|
args->result = ESP_FAIL;
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args->result = ESP_OK;
|
||||||
|
|
||||||
|
close:
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
done:
|
||||||
|
xSemaphoreGive(args->done);
|
||||||
|
vTaskDelay(1);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_spiffs_concurrent(const char* filename_prefix)
|
||||||
|
{
|
||||||
|
char names[4][64];
|
||||||
|
for (size_t i = 0; i < 4; ++i) {
|
||||||
|
snprintf(names[i], sizeof(names[i]), "%s%d", filename_prefix, i + 1);
|
||||||
|
unlink(names[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
read_write_test_arg_t args1 = READ_WRITE_TEST_ARG_INIT(names[0], 1);
|
||||||
|
read_write_test_arg_t args2 = READ_WRITE_TEST_ARG_INIT(names[1], 2);
|
||||||
|
|
||||||
|
printf("writing f1 and f2\n");
|
||||||
|
const int cpuid_0 = 0;
|
||||||
|
const int cpuid_1 = portNUM_PROCESSORS - 1;
|
||||||
|
xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, cpuid_0);
|
||||||
|
xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, cpuid_1);
|
||||||
|
|
||||||
|
xSemaphoreTake(args1.done, portMAX_DELAY);
|
||||||
|
printf("f1 done\n");
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, args1.result);
|
||||||
|
xSemaphoreTake(args2.done, portMAX_DELAY);
|
||||||
|
printf("f2 done\n");
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, args2.result);
|
||||||
|
|
||||||
|
args1.write = false;
|
||||||
|
args2.write = false;
|
||||||
|
read_write_test_arg_t args3 = READ_WRITE_TEST_ARG_INIT(names[2], 3);
|
||||||
|
read_write_test_arg_t args4 = READ_WRITE_TEST_ARG_INIT(names[3], 4);
|
||||||
|
|
||||||
|
printf("reading f1 and f2, writing f3 and f4\n");
|
||||||
|
|
||||||
|
xTaskCreatePinnedToCore(&read_write_task, "rw3", 2048, &args3, 3, NULL, cpuid_1);
|
||||||
|
xTaskCreatePinnedToCore(&read_write_task, "rw4", 2048, &args4, 3, NULL, cpuid_0);
|
||||||
|
xTaskCreatePinnedToCore(&read_write_task, "rw1", 2048, &args1, 3, NULL, cpuid_0);
|
||||||
|
xTaskCreatePinnedToCore(&read_write_task, "rw2", 2048, &args2, 3, NULL, cpuid_1);
|
||||||
|
|
||||||
|
xSemaphoreTake(args1.done, portMAX_DELAY);
|
||||||
|
printf("f1 done\n");
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, args1.result);
|
||||||
|
xSemaphoreTake(args2.done, portMAX_DELAY);
|
||||||
|
printf("f2 done\n");
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, args2.result);
|
||||||
|
xSemaphoreTake(args3.done, portMAX_DELAY);
|
||||||
|
printf("f3 done\n");
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, args3.result);
|
||||||
|
xSemaphoreTake(args4.done, portMAX_DELAY);
|
||||||
|
printf("f4 done\n");
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, args4.result);
|
||||||
|
|
||||||
|
vSemaphoreDelete(args1.done);
|
||||||
|
vSemaphoreDelete(args2.done);
|
||||||
|
vSemaphoreDelete(args3.done);
|
||||||
|
vSemaphoreDelete(args4.done);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void test_setup()
|
||||||
|
{
|
||||||
|
esp_vfs_spiffs_conf_t conf = {
|
||||||
|
.base_path = "/spiffs",
|
||||||
|
.partition_label = spiffs_test_partition_label,
|
||||||
|
.max_files = 5,
|
||||||
|
.format_if_mount_failed = true
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_teardown()
|
||||||
|
{
|
||||||
|
TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("can initialize SPIFFS in erased partition", "[spiffs]")
|
||||||
|
{
|
||||||
|
const esp_partition_t* part = get_test_data_partition();
|
||||||
|
TEST_ASSERT_NOT_NULL(part);
|
||||||
|
TEST_ESP_OK(esp_partition_erase_range(part, 0, part->size));
|
||||||
|
test_setup();
|
||||||
|
size_t total = 0, used = 0;
|
||||||
|
TEST_ESP_OK(esp_spiffs_info(spiffs_test_partition_label, &total, &used));
|
||||||
|
printf("total: %d, used: %d\n", total, used);
|
||||||
|
TEST_ASSERT_EQUAL(0, used);
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("can format mounted partition", "[spiffs]")
|
||||||
|
{
|
||||||
|
// Mount SPIFFS, create file, format, check that the file does not exist.
|
||||||
|
const esp_partition_t* part = get_test_data_partition();
|
||||||
|
TEST_ASSERT_NOT_NULL(part);
|
||||||
|
test_setup();
|
||||||
|
const char* filename = "/spiffs/hello.txt";
|
||||||
|
test_spiffs_create_file_with_text(filename, spiffs_test_hello_str);
|
||||||
|
esp_spiffs_format(part->label);
|
||||||
|
FILE* f = fopen(filename, "r");
|
||||||
|
TEST_ASSERT_NULL(f);
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("can format unmounted partition", "[spiffs]")
|
||||||
|
{
|
||||||
|
// Mount SPIFFS, create file, unmount. Format. Mount again, check that
|
||||||
|
// the file does not exist.
|
||||||
|
const esp_partition_t* part = get_test_data_partition();
|
||||||
|
TEST_ASSERT_NOT_NULL(part);
|
||||||
|
test_setup();
|
||||||
|
const char* filename = "/spiffs/hello.txt";
|
||||||
|
test_spiffs_create_file_with_text(filename, spiffs_test_hello_str);
|
||||||
|
test_teardown();
|
||||||
|
esp_spiffs_format(part->label);
|
||||||
|
// Don't use test_setup here, need to mount without formatting
|
||||||
|
esp_vfs_spiffs_conf_t conf = {
|
||||||
|
.base_path = "/spiffs",
|
||||||
|
.partition_label = spiffs_test_partition_label,
|
||||||
|
.max_files = 5,
|
||||||
|
.format_if_mount_failed = false
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
|
||||||
|
FILE* f = fopen(filename, "r");
|
||||||
|
TEST_ASSERT_NULL(f);
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("can create and write file", "[spiffs]")
|
||||||
|
{
|
||||||
|
test_setup();
|
||||||
|
test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str);
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("can read file", "[spiffs]")
|
||||||
|
{
|
||||||
|
test_setup();
|
||||||
|
test_spiffs_create_file_with_text("/spiffs/hello.txt", spiffs_test_hello_str);
|
||||||
|
test_spiffs_read_file("/spiffs/hello.txt");
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("can open maximum number of files", "[spiffs]")
|
||||||
|
{
|
||||||
|
size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */
|
||||||
|
esp_vfs_spiffs_conf_t conf = {
|
||||||
|
.base_path = "/spiffs",
|
||||||
|
.partition_label = spiffs_test_partition_label,
|
||||||
|
.format_if_mount_failed = true,
|
||||||
|
.max_files = max_files
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(esp_vfs_spiffs_register(&conf));
|
||||||
|
test_spiffs_open_max_files("/spiffs/f", max_files);
|
||||||
|
TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_test_partition_label));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("overwrite and append file", "[spiffs]")
|
||||||
|
{
|
||||||
|
test_setup();
|
||||||
|
test_spiffs_overwrite_append("/spiffs/hello.txt");
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("can lseek", "[spiffs]")
|
||||||
|
{
|
||||||
|
test_setup();
|
||||||
|
test_spiffs_lseek("/spiffs/seek.txt");
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("stat returns correct values", "[spiffs]")
|
||||||
|
{
|
||||||
|
test_setup();
|
||||||
|
test_spiffs_stat("/spiffs/stat.txt");
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("unlink removes a file", "[spiffs]")
|
||||||
|
{
|
||||||
|
test_setup();
|
||||||
|
test_spiffs_unlink("/spiffs/unlink.txt");
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("rename moves a file", "[spiffs]")
|
||||||
|
{
|
||||||
|
test_setup();
|
||||||
|
test_spiffs_rename("/spiffs/move");
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("can opendir root directory of FS", "[spiffs]")
|
||||||
|
{
|
||||||
|
test_setup();
|
||||||
|
test_spiffs_can_opendir("/spiffs");
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("opendir, readdir, rewinddir, seekdir work as expected", "[spiffs]")
|
||||||
|
{
|
||||||
|
test_setup();
|
||||||
|
test_spiffs_opendir_readdir_rewinddir("/spiffs/dir");
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("readdir with large number of files", "[spiffs][timeout=30]")
|
||||||
|
{
|
||||||
|
test_setup();
|
||||||
|
test_spiffs_readdir_many_files("/spiffs/dir2");
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("multiple tasks can use same volume", "[spiffs]")
|
||||||
|
{
|
||||||
|
test_setup();
|
||||||
|
test_spiffs_concurrent("/spiffs/f");
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_SPIFFS_USE_MTIME
|
||||||
|
TEST_CASE("mtime is updated when file is opened", "[spiffs]")
|
||||||
|
{
|
||||||
|
/* Open a file, check that mtime is set correctly */
|
||||||
|
const char* filename = "/spiffs/time";
|
||||||
|
test_setup();
|
||||||
|
time_t t_before_create = time(NULL);
|
||||||
|
test_spiffs_create_file_with_text(filename, "\n");
|
||||||
|
time_t t_after_create = time(NULL);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
TEST_ASSERT_EQUAL(0, stat(filename, &st));
|
||||||
|
printf("mtime=%d\n", (int) st.st_mtime);
|
||||||
|
TEST_ASSERT(st.st_mtime >= t_before_create
|
||||||
|
&& st.st_mtime <= t_after_create);
|
||||||
|
|
||||||
|
/* Wait a bit, open again, check that mtime is updated */
|
||||||
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
|
time_t t_before_open = time(NULL);
|
||||||
|
FILE *f = fopen(filename, "a");
|
||||||
|
time_t t_after_open = time(NULL);
|
||||||
|
TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st));
|
||||||
|
printf("mtime=%d\n", (int) st.st_mtime);
|
||||||
|
TEST_ASSERT(st.st_mtime >= t_before_open
|
||||||
|
&& st.st_mtime <= t_after_open);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
/* Wait a bit, open for reading, check that mtime is not updated */
|
||||||
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
|
time_t t_before_open_ro = time(NULL);
|
||||||
|
f = fopen(filename, "r");
|
||||||
|
TEST_ASSERT_EQUAL(0, fstat(fileno(f), &st));
|
||||||
|
printf("mtime=%d\n", (int) st.st_mtime);
|
||||||
|
TEST_ASSERT(t_before_open_ro > t_after_open
|
||||||
|
&& st.st_mtime >= t_before_open
|
||||||
|
&& st.st_mtime <= t_after_open);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
test_teardown();
|
||||||
|
}
|
||||||
|
#endif // CONFIG_SPIFFS_USE_MTIME
|
98
components/spiffs/test_spiffs_host/Makefile
Normal file
98
components/spiffs/test_spiffs_host/Makefile
Normal file
@ -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
|
33
components/spiffs/test_spiffs_host/Makefile.files
Normal file
33
components/spiffs/test_spiffs_host/Makefile.files
Normal file
@ -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 \
|
||||||
|
)
|
17
components/spiffs/test_spiffs_host/component.mk
Normal file
17
components/spiffs/test_spiffs_host/component.mk
Normal file
@ -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)
|
3
components/spiffs/test_spiffs_host/main.cpp
Normal file
3
components/spiffs/test_spiffs_host/main.cpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include "catch.hpp"
|
||||||
|
|
6
components/spiffs/test_spiffs_host/partition_table.csv
Normal file
6
components/spiffs/test_spiffs_host/partition_table.csv
Normal file
@ -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,
|
|
19
components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h
Normal file
19
components/spiffs/test_spiffs_host/sdkconfig/sdkconfig.h
Normal file
@ -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"
|
107
components/spiffs/test_spiffs_host/test_spiffs.cpp
Normal file
107
components/spiffs/test_spiffs_host/test_spiffs.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "esp_partition.h"
|
||||||
|
#include "spiffs.h"
|
||||||
|
#include "spiffs_nucleus.h"
|
||||||
|
#include "spiffs_api.h"
|
||||||
|
|
||||||
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
extern "C" void init_spi_flash(const char* chip_size, size_t block_size, size_t sector_size, size_t page_size, const char* partition_bin);
|
||||||
|
|
||||||
|
TEST_CASE("format disk, open file, write and read file", "[spiffs]")
|
||||||
|
{
|
||||||
|
init_spi_flash(CONFIG_ESPTOOLPY_FLASHSIZE, CONFIG_WL_SECTOR_SIZE * 16, CONFIG_WL_SECTOR_SIZE, CONFIG_WL_SECTOR_SIZE, "partition_table.bin");
|
||||||
|
|
||||||
|
spiffs fs;
|
||||||
|
spiffs_config cfg;
|
||||||
|
|
||||||
|
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, "storage");
|
||||||
|
|
||||||
|
// Configure objects needed by SPIFFS
|
||||||
|
esp_spiffs_t esp_user_data;
|
||||||
|
esp_user_data.partition = partition;
|
||||||
|
fs.user_data = (void*)&esp_user_data;
|
||||||
|
|
||||||
|
cfg.hal_erase_f = spiffs_api_erase;
|
||||||
|
cfg.hal_read_f = spiffs_api_read;
|
||||||
|
cfg.hal_write_f = spiffs_api_write;
|
||||||
|
cfg.log_block_size = CONFIG_WL_SECTOR_SIZE;
|
||||||
|
cfg.log_page_size = CONFIG_SPIFFS_PAGE_SIZE;
|
||||||
|
cfg.phys_addr = 0;
|
||||||
|
cfg.phys_erase_block = CONFIG_WL_SECTOR_SIZE;
|
||||||
|
cfg.phys_size = partition->size;
|
||||||
|
|
||||||
|
uint32_t max_files = 5;
|
||||||
|
|
||||||
|
uint32_t fds_sz = max_files * sizeof(spiffs_fd);
|
||||||
|
uint32_t work_sz = cfg.log_page_size * 2;
|
||||||
|
uint32_t cache_sz = sizeof(spiffs_cache) + max_files * (sizeof(spiffs_cache_page)
|
||||||
|
+ cfg.log_page_size);
|
||||||
|
|
||||||
|
uint8_t *work = (uint8_t*) malloc(work_sz);
|
||||||
|
uint8_t *fds = (uint8_t*) malloc(fds_sz);
|
||||||
|
uint8_t *cache = (uint8_t*) malloc(cache_sz);
|
||||||
|
|
||||||
|
s32_t spiffs_res;
|
||||||
|
|
||||||
|
// Special mounting procedure: mount, format, mount as per
|
||||||
|
// https://github.com/pellepl/spiffs/wiki/Using-spiffs
|
||||||
|
spiffs_res = SPIFFS_mount(&fs, &cfg, work, fds, fds_sz,
|
||||||
|
cache, cache_sz, spiffs_api_check);
|
||||||
|
REQUIRE(spiffs_res == SPIFFS_ERR_NOT_A_FS);
|
||||||
|
|
||||||
|
spiffs_res = SPIFFS_format(&fs);
|
||||||
|
REQUIRE(spiffs_res >= SPIFFS_OK);
|
||||||
|
|
||||||
|
spiffs_res = SPIFFS_mount(&fs, &cfg, work, fds, fds_sz,
|
||||||
|
cache, cache_sz, spiffs_api_check);
|
||||||
|
REQUIRE(spiffs_res >= SPIFFS_OK);
|
||||||
|
|
||||||
|
// Open test file
|
||||||
|
spiffs_res = SPIFFS_open(&fs, "test.txt", SPIFFS_O_CREAT | SPIFFS_O_RDWR, 0);
|
||||||
|
REQUIRE(spiffs_res >= SPIFFS_OK);
|
||||||
|
|
||||||
|
// Generate data
|
||||||
|
spiffs_file file = spiffs_res;
|
||||||
|
|
||||||
|
uint32_t data_size = 100000;
|
||||||
|
|
||||||
|
char *data = (char*) malloc(data_size);
|
||||||
|
char *read = (char*) malloc(data_size);
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < data_size; i += sizeof(i))
|
||||||
|
{
|
||||||
|
*((uint32_t*)(data + i)) = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32_t bw;
|
||||||
|
|
||||||
|
// Write data to file
|
||||||
|
spiffs_res = SPIFFS_write(&fs, file, (void*)data, data_size);
|
||||||
|
REQUIRE(spiffs_res >= SPIFFS_OK);
|
||||||
|
REQUIRE(spiffs_res == data_size);
|
||||||
|
|
||||||
|
// Set the file object pointer to the beginning
|
||||||
|
spiffs_res = SPIFFS_lseek(&fs, file, 0, SPIFFS_SEEK_SET);
|
||||||
|
REQUIRE(spiffs_res >= SPIFFS_OK);
|
||||||
|
|
||||||
|
// Read the file
|
||||||
|
spiffs_res = SPIFFS_read(&fs, file, (void*)read, data_size);
|
||||||
|
REQUIRE(spiffs_res >= SPIFFS_OK);
|
||||||
|
REQUIRE(spiffs_res == data_size);
|
||||||
|
|
||||||
|
// Close the test file
|
||||||
|
spiffs_res = SPIFFS_close(&fs, file);
|
||||||
|
REQUIRE(spiffs_res >= SPIFFS_OK);
|
||||||
|
|
||||||
|
REQUIRE(memcmp(data, read, data_size) == 0);
|
||||||
|
|
||||||
|
// Unmount
|
||||||
|
SPIFFS_unmount(&fs);
|
||||||
|
|
||||||
|
free(read);
|
||||||
|
free(data);
|
||||||
|
}
|
7
components/spiffs/test_spiffs_host/test_utils.c
Normal file
7
components/spiffs/test_spiffs_host/test_utils.c
Normal file
@ -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);
|
||||||
|
}
|
Reference in New Issue
Block a user