diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c index 98bd975bf6..2996fbef57 100644 --- a/components/spiffs/esp_spiffs.c +++ b/components/spiffs/esp_spiffs.c @@ -1,16 +1,8 @@ -// 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. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include "esp_spiffs.h" #include "spiffs.h" @@ -66,6 +58,7 @@ typedef struct { char path[SPIFFS_OBJ_NAME_LEN]; /*!< Requested directory name */ } vfs_spiffs_dir_t; +static int spiffs_res_to_errno(s32_t fr); 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); @@ -321,6 +314,22 @@ esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size return ESP_OK; } +esp_err_t esp_spiffs_check(const char* partition_label) +{ + int index; + if (esp_spiffs_by_label(partition_label, &index) != ESP_OK) { + return ESP_ERR_INVALID_STATE; + } + if (SPIFFS_check(_efs[index]->fs) != SPIFFS_OK) { + int spiffs_res = SPIFFS_errno(_efs[index]->fs); + ESP_LOGE(TAG, "SPIFFS_check failed (%d)", spiffs_res); + errno = spiffs_res_to_errno(SPIFFS_errno(_efs[index]->fs)); + SPIFFS_clearerr(_efs[index]->fs); + return ESP_FAIL; + } + return ESP_OK; +} + esp_err_t esp_spiffs_format(const char* partition_label) { bool partition_was_mounted = false; diff --git a/components/spiffs/include/esp_spiffs.h b/components/spiffs/include/esp_spiffs.h index 9cc120895d..eb1582d151 100644 --- a/components/spiffs/include/esp_spiffs.h +++ b/components/spiffs/include/esp_spiffs.h @@ -92,6 +92,17 @@ esp_err_t esp_spiffs_format(const char* partition_label); */ esp_err_t esp_spiffs_info(const char* partition_label, size_t *total_bytes, size_t *used_bytes); +/** + * Check integrity of SPIFFS + * + * @param partition_label Same label as passed to esp_vfs_spiffs_register + * @return + * - ESP_OK if successful + * - ESP_ERR_INVALID_STATE if not mounted + * - ESP_FAIL on error + */ +esp_err_t esp_spiffs_check(const char* partition_label); + #ifdef __cplusplus } #endif diff --git a/docs/en/api-reference/storage/spiffs.rst b/docs/en/api-reference/storage/spiffs.rst index 7156afcfb4..524c7d726f 100644 --- a/docs/en/api-reference/storage/spiffs.rst +++ b/docs/en/api-reference/storage/spiffs.rst @@ -17,6 +17,7 @@ Notes - SPIFFS is able to reliably utilize only around 75% of assigned partition space. - When the filesystem is running out of space, the garbage collector is trying to find free space by scanning the filesystem multiple times, which can take up to several seconds per write function call, depending on required space. This is caused by the SPIFFS design and the issue has been reported multiple times (e.g. `here `_) and in the official `SPIFFS github repository `_. The issue can be partially mitigated by the `SPIFFS configuration `_. - Deleting a file does not always remove the whole file, which leaves unusable sections throughout the filesystem. + - When ESP32 experiences a power loss during a file system operation it could result in SPIFFS corruption. However the file system still might be recovered via ``esp_spiffs_check`` function. More details in the official SPIFFS `FAQ `. Tools ----- diff --git a/docs/zh_CN/api-reference/storage/spiffs.rst b/docs/zh_CN/api-reference/storage/spiffs.rst index 63118b3d0b..9e98e610ab 100644 --- a/docs/zh_CN/api-reference/storage/spiffs.rst +++ b/docs/zh_CN/api-reference/storage/spiffs.rst @@ -17,6 +17,7 @@ SPIFFS 是一个用于 SPI NOR flash 设备的嵌入式文件系统,支持磨 - SPIFFS 只能稳定地使用约 75% 的指定分区容量。 - 当文件系统空间不足时,垃圾收集器会尝试多次扫描文件系统来寻找可用空间。根据所需空间的不同,写操作会被调用多次,每次函数调用将花费几秒。同一操作可能会花费不同时长的问题缘于 SPIFFS 的设计,且已在官方的 `SPIFFS github 仓库 `_ 或是 `_ 中被多次报告。这个问题可以通过 `SPIFFS 配置 `_ 部分缓解。 - 被删除文件通常不会被完全清除,会在文件系统中遗留下无法使用的部分。 + - 如果 ESP32 在文件系统操作期间断电,可能会导致 SPIFFS 损坏。但是仍可通过 ``esp_spiffs_check`` 函数恢复文件系统。详情请参阅官方 SPIFFS `FAQ `。 工具 ----- diff --git a/examples/storage/spiffs/README.md b/examples/storage/spiffs/README.md index 0216e44957..6ceb34634e 100644 --- a/examples/storage/spiffs/README.md +++ b/examples/storage/spiffs/README.md @@ -20,6 +20,11 @@ SPIFFS partition size is set in partitions_example.csv file. See [Partition Tabl This example does not require any special hardware, and can be run on any common development board. +### Configure the project + +* Open the project configuration menu (`idf.py menuconfig`) +* Configure SPIFFS settings under "SPIFFS Example menu". See note about `esp_spiffs_check` function on [SPIFFS Filesystem](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/spiffs.html) page. + ### Build and flash Replace PORT with serial port name: diff --git a/examples/storage/spiffs/main/Kconfig.projbuild b/examples/storage/spiffs/main/Kconfig.projbuild new file mode 100644 index 0000000000..eadd67909a --- /dev/null +++ b/examples/storage/spiffs/main/Kconfig.projbuild @@ -0,0 +1,9 @@ +menu "SPIFFS Example menu" + + config EXAMPLE_SPIFFS_CHECK_ON_START + bool "Run SPIFFS_check on every start-up" + default y + help + If this config item is set, esp_spiffs_check() will be run on every start-up. + Slow on large flash sizes. +endmenu diff --git a/examples/storage/spiffs/main/spiffs_example_main.c b/examples/storage/spiffs/main/spiffs_example_main.c index d0fc23235b..32cc176860 100644 --- a/examples/storage/spiffs/main/spiffs_example_main.c +++ b/examples/storage/spiffs/main/spiffs_example_main.c @@ -42,14 +42,42 @@ void app_main(void) return; } +#ifdef CONFIG_EXAMPLE_SPIFFS_CHECK_ON_START + ESP_LOGI(TAG, "Performing SPIFFS_check()."); + ret = esp_spiffs_check(conf.partition_label); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret)); + return; + } else { + ESP_LOGI(TAG, "SPIFFS_check() successful"); + } +#endif + size_t total = 0, used = 0; ret = esp_spiffs_info(conf.partition_label, &total, &used); if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret)); + ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s). Formatting...", esp_err_to_name(ret)); + esp_spiffs_format(conf.partition_label); + return; } else { ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); } +# + // Check consistency of reported partiton size info. + if (used > total) { + ESP_LOGW(TAG, "Number of used bytes cannot be larger than total. Performing SPIFFS_check()."); + ret = esp_spiffs_check(conf.partition_label); + // Could be also used to mend broken files, to clean unreferenced pages, etc. + // More info at https://github.com/pellepl/spiffs/wiki/FAQ#powerlosses-contd-when-should-i-run-spiffs_check + if (ret != ESP_OK) { + ESP_LOGE(TAG, "SPIFFS_check() failed (%s)", esp_err_to_name(ret)); + return; + } else { + ESP_LOGI(TAG, "SPIFFS_check() successful"); + } + } + // Use POSIX and C standard library functions to work with files. // First create a file. ESP_LOGI(TAG, "Opening file");