From 6e73f1a22edddf19a2813c3fdfe67664e04629b3 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 9 Apr 2024 11:04:42 +1000 Subject: [PATCH] Upgrade fatfs component version --- lib/fatfs/CMakeLists.txt | 42 ++- lib/fatfs/Kconfig | 9 + lib/fatfs/diskio/diskio.c | 2 + lib/fatfs/diskio/diskio_rawflash.c | 12 +- lib/fatfs/diskio/diskio_sdmmc.c | 3 + lib/fatfs/diskio/diskio_wl.c | 8 +- lib/fatfs/fatfs_utils/entry.py | 2 +- lib/fatfs/fatfsgen.py | 4 +- lib/fatfs/fatfsparse.py | 2 +- lib/fatfs/host_test/CMakeLists.txt | 10 + lib/fatfs/host_test/README.md | 2 + lib/fatfs/host_test/main/CMakeLists.txt | 6 + lib/fatfs/host_test/main/main.cpp | 7 + lib/fatfs/host_test/main/test_fatfs.cpp | 285 ++++++++++++++++++ lib/fatfs/host_test/partition_table.csv | 7 + lib/fatfs/host_test/pytest_fatfs_linux.py | 10 + lib/fatfs/host_test/sdkconfig.defaults | 12 + lib/fatfs/sbom.yml | 5 + lib/fatfs/src/ffconf.h | 4 +- lib/fatfs/test_apps/.build-test-rules.yml | 11 + lib/fatfs/test_apps/flash_ro/README.md | 4 +- lib/fatfs/test_apps/flash_wl/README.md | 4 +- .../flash_wl/main/test_fatfs_flash_wl.c | 42 ++- lib/fatfs/test_apps/flash_wl/partitions.csv | 3 +- .../flash_wl/pytest_fatfs_flash_wl.py | 6 +- .../flash_wl/sdkconfig.ci.auto_fsync | 1 + .../test_apps/sdcard/main/test_fatfs_sdmmc.c | 2 +- .../test_apps/sdcard/main/test_fatfs_sdspi.c | 2 +- .../test_apps/sdcard/pytest_fatfs_sdcard.py | 8 +- .../test_fatfs_common/test_fatfs_common.c | 36 ++- .../test_fatfs_common/test_fatfs_common.h | 2 + lib/fatfs/vfs/esp_vfs_fat.h | 45 ++- lib/fatfs/vfs/vfs_fat.c | 59 +++- lib/fatfs/vfs/vfs_fat_sdmmc.c | 19 +- lib/fatfs/vfs/vfs_fat_spiflash.c | 23 +- 35 files changed, 628 insertions(+), 71 deletions(-) create mode 100644 lib/fatfs/host_test/CMakeLists.txt create mode 100644 lib/fatfs/host_test/README.md create mode 100644 lib/fatfs/host_test/main/CMakeLists.txt create mode 100644 lib/fatfs/host_test/main/main.cpp create mode 100644 lib/fatfs/host_test/main/test_fatfs.cpp create mode 100644 lib/fatfs/host_test/partition_table.csv create mode 100644 lib/fatfs/host_test/pytest_fatfs_linux.py create mode 100644 lib/fatfs/host_test/sdkconfig.defaults create mode 100644 lib/fatfs/sbom.yml create mode 100644 lib/fatfs/test_apps/.build-test-rules.yml create mode 100644 lib/fatfs/test_apps/flash_wl/sdkconfig.ci.auto_fsync diff --git a/lib/fatfs/CMakeLists.txt b/lib/fatfs/CMakeLists.txt index 9ff1d792..b6a288e0 100644 --- a/lib/fatfs/CMakeLists.txt +++ b/lib/fatfs/CMakeLists.txt @@ -1,16 +1,34 @@ +idf_build_get_property(target IDF_TARGET) + set(srcs "diskio/diskio.c" - "diskio/diskio_rawflash.c" - "diskio/diskio_sdmmc.c" - "diskio/diskio_wl.c" - "src/ff.c" - "port/freertos/ffsystem.c" - "src/ffunicode.c" - "vfs/vfs_fat.c" - "vfs/vfs_fat_sdmmc.c" - "vfs/vfs_fat_spiflash.c") + "diskio/diskio_rawflash.c" + "diskio/diskio_wl.c" + "src/ff.c" + "src/ffunicode.c") + +set(include_dirs "diskio" "src") + +set(requires "wear_levelling") + +# for linux, we do not have support for vfs and sdmmc, for real targets, add respective sources +if(${target} STREQUAL "linux") + list(APPEND srcs "port/linux/ffsystem.c") +else() + list(APPEND srcs "port/freertos/ffsystem.c" + "diskio/diskio_sdmmc.c" + "vfs/vfs_fat.c" + "vfs/vfs_fat_sdmmc.c" + "vfs/vfs_fat_spiflash.c") + + list(APPEND include_dirs "vfs") + + list(APPEND requires "sdmmc") + + list(APPEND priv_requires "vfs") +endif() idf_component_register(SRCS ${srcs} - INCLUDE_DIRS diskio vfs src - REQUIRES wear_levelling sdmmc - PRIV_REQUIRES vfs + INCLUDE_DIRS ${include_dirs} + REQUIRES ${requires} + PRIV_REQUIRES ${priv_requires} ) diff --git a/lib/fatfs/Kconfig b/lib/fatfs/Kconfig index 3d62a06b..f2c737a6 100644 --- a/lib/fatfs/Kconfig +++ b/lib/fatfs/Kconfig @@ -230,4 +230,13 @@ menu "FAT Filesystem support" accessing target media for given file descriptor! See 'Improving I/O performance' section of 'Maximizing Execution Speed' documentation page for more details. + + config FATFS_IMMEDIATE_FSYNC + bool "Enable automatic f_sync" + default n + help + Enables automatic calling of f_sync() to flush recent file changes after each call of vfs_fat_write(), + vfs_fat_pwrite(), vfs_fat_link(), vfs_fat_truncate() and vfs_fat_ftruncate() functions. + This feature improves file-consistency and size reporting accuracy for the FatFS, + at a price on decreased performance due to frequent disk operations endmenu diff --git a/lib/fatfs/diskio/diskio.c b/lib/fatfs/diskio/diskio.c index 70143279..4bafa7ea 100644 --- a/lib/fatfs/diskio/diskio.c +++ b/lib/fatfs/diskio/diskio.c @@ -21,7 +21,9 @@ static ff_diskio_impl_t * s_impls[FF_VOLUMES] = { NULL }; #if FF_MULTI_PARTITION /* Multiple partition configuration */ const PARTITION VolToPart[FF_VOLUMES] = { {0, 0}, /* Logical drive 0 ==> Physical drive 0, auto detection */ +#if FF_VOLUMES > 1 {1, 0}, /* Logical drive 1 ==> Physical drive 1, auto detection */ +#endif #if FF_VOLUMES > 2 {2, 0}, /* Logical drive 2 ==> Physical drive 2, auto detection */ #endif diff --git a/lib/fatfs/diskio/diskio_rawflash.c b/lib/fatfs/diskio/diskio_rawflash.c index cbd03e0f..313aacec 100644 --- a/lib/fatfs/diskio/diskio_rawflash.c +++ b/lib/fatfs/diskio/diskio_rawflash.c @@ -18,6 +18,7 @@ static const esp_partition_t* s_ff_raw_handles[FF_VOLUMES]; // Determine the sector size and sector count by parsing the boot sector static size_t s_sector_size[FF_VOLUMES]; static size_t s_sectors_count[FF_VOLUMES]; +static uint8_t s_initialized[FF_VOLUMES]; #define BPB_BytsPerSec 11 #define BPB_TotSec16 19 @@ -56,12 +57,17 @@ DSTATUS ff_raw_initialize (BYTE pdrv) s_sectors_count[pdrv] = sectors_count_tmp_32; } - return 0; + s_initialized[pdrv] = true; + return STA_PROTECT; } DSTATUS ff_raw_status (BYTE pdrv) { - return 0; + DSTATUS status = STA_PROTECT; + if (!s_initialized[pdrv]) { + status |= STA_NOINIT | STA_NODISK; + } + return status; } DRESULT ff_raw_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count) @@ -80,7 +86,7 @@ DRESULT ff_raw_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count) DRESULT ff_raw_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count) { - return RES_ERROR; + return RES_WRPRT; } DRESULT ff_raw_ioctl (BYTE pdrv, BYTE cmd, void *buff) diff --git a/lib/fatfs/diskio/diskio_sdmmc.c b/lib/fatfs/diskio/diskio_sdmmc.c index 751729d9..3e61eb00 100644 --- a/lib/fatfs/diskio/diskio_sdmmc.c +++ b/lib/fatfs/diskio/diskio_sdmmc.c @@ -105,6 +105,9 @@ DRESULT ff_sdmmc_ioctl (BYTE pdrv, BYTE cmd, void* buff) return RES_ERROR; #if FF_USE_TRIM case CTRL_TRIM: + if (sdmmc_can_trim(card) != ESP_OK) { + return RES_PARERR; + } return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector (*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count #endif //FF_USE_TRIM diff --git a/lib/fatfs/diskio/diskio_wl.c b/lib/fatfs/diskio/diskio_wl.c index 31bb7d25..8e20290e 100644 --- a/lib/fatfs/diskio/diskio_wl.c +++ b/lib/fatfs/diskio/diskio_wl.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -31,7 +31,7 @@ DSTATUS ff_wl_status (BYTE pdrv) DRESULT ff_wl_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count) { - ESP_LOGV(TAG, "ff_wl_read - pdrv=%i, sector=%i, count=%i\n", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count); + ESP_LOGV(TAG, "ff_wl_read - pdrv=%i, sector=%i, count=%i", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count); wl_handle_t wl_handle = ff_wl_handles[pdrv]; assert(wl_handle + 1); esp_err_t err = wl_read(wl_handle, sector * wl_sector_size(wl_handle), buff, count * wl_sector_size(wl_handle)); @@ -44,7 +44,7 @@ DRESULT ff_wl_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count) DRESULT ff_wl_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count) { - ESP_LOGV(TAG, "ff_wl_write - pdrv=%i, sector=%i, count=%i\n", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count); + ESP_LOGV(TAG, "ff_wl_write - pdrv=%i, sector=%i, count=%i", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count); wl_handle_t wl_handle = ff_wl_handles[pdrv]; assert(wl_handle + 1); esp_err_t err = wl_erase_range(wl_handle, sector * wl_sector_size(wl_handle), count * wl_sector_size(wl_handle)); @@ -63,7 +63,7 @@ DRESULT ff_wl_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count) DRESULT ff_wl_ioctl (BYTE pdrv, BYTE cmd, void *buff) { wl_handle_t wl_handle = ff_wl_handles[pdrv]; - ESP_LOGV(TAG, "ff_wl_ioctl: cmd=%i\n", cmd); + ESP_LOGV(TAG, "ff_wl_ioctl: cmd=%i", cmd); assert(wl_handle + 1); switch (cmd) { case CTRL_SYNC: diff --git a/lib/fatfs/fatfs_utils/entry.py b/lib/fatfs/fatfs_utils/entry.py index ec986a39..bb5d9f8f 100644 --- a/lib/fatfs/fatfs_utils/entry.py +++ b/lib/fatfs/fatfs_utils/entry.py @@ -50,7 +50,7 @@ class Entry: 'DIR_Name' / PaddedString(MAX_NAME_SIZE, SHORT_NAMES_ENCODING), 'DIR_Name_ext' / PaddedString(MAX_EXT_SIZE, SHORT_NAMES_ENCODING), 'DIR_Attr' / Int8ul, - 'DIR_NTRes' / Int8ul, # this tagged for lfn (0x00 for lfn prefix, 0x18 for short name in lfn) + 'DIR_NTRes' / Int8ul, # this tagged for lfn (0x00 for short entry in lfn, 0x18 for short name) 'DIR_CrtTimeTenth' / Const(EMPTY_BYTE), # ignored by esp-idf fatfs library 'DIR_CrtTime' / Int16ul, # ignored by esp-idf fatfs library 'DIR_CrtDate' / Int16ul, # ignored by esp-idf fatfs library diff --git a/lib/fatfs/fatfsgen.py b/lib/fatfs/fatfsgen.py index 7518b010..199916ef 100755 --- a/lib/fatfs/fatfsgen.py +++ b/lib/fatfs/fatfsgen.py @@ -85,8 +85,8 @@ class FATFS: object_timestamp_: datetime = FATFS_INCEPTION, is_empty: bool = False) -> None: """ - Root directory recursively finds the parent directory of the new file, allocates cluster, - entry and appends a new file into the parent directory. + This method allocates necessary clusters and creates a new file record in the directory required. + The directory must exists. When path_from_root is None the dir is root. diff --git a/lib/fatfs/fatfsparse.py b/lib/fatfs/fatfsparse.py index c5d3716c..de50f2fe 100755 --- a/lib/fatfs/fatfsparse.py +++ b/lib/fatfs/fatfsparse.py @@ -57,7 +57,7 @@ def traverse_folder_tree(directory_bytes_: bytes, try: obj_: dict = Entry.ENTRY_FORMAT_SHORT_NAME.parse( directory_bytes_[obj_address_: obj_address_ + FATDefaults.ENTRY_SIZE]) - except (construct.core.ConstError, UnicodeDecodeError): + except (construct.core.ConstError, UnicodeDecodeError, construct.core.StringError): args.long_name_support = True continue diff --git a/lib/fatfs/host_test/CMakeLists.txt b/lib/fatfs/host_test/CMakeLists.txt new file mode 100644 index 00000000..48267da8 --- /dev/null +++ b/lib/fatfs/host_test/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(COMPONENTS main) +# Freertos is included via common components. However, CATCH isn't compatible with the FreeRTOS component yet, hence +# using the FreeRTOS mock component. +# target. +list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/") + +project(fatfs_host_test) diff --git a/lib/fatfs/host_test/README.md b/lib/fatfs/host_test/README.md new file mode 100644 index 00000000..37c142df --- /dev/null +++ b/lib/fatfs/host_test/README.md @@ -0,0 +1,2 @@ +| Supported Targets | Linux | +| ----------------- | ----- | diff --git a/lib/fatfs/host_test/main/CMakeLists.txt b/lib/fatfs/host_test/main/CMakeLists.txt new file mode 100644 index 00000000..ef8aeb43 --- /dev/null +++ b/lib/fatfs/host_test/main/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register(SRCS "main.cpp" + "test_fatfs.cpp" + INCLUDE_DIRS "$ENV{IDF_PATH}/tools/catch" + REQUIRES fatfs + WHOLE_ARCHIVE + ) diff --git a/lib/fatfs/host_test/main/main.cpp b/lib/fatfs/host_test/main/main.cpp new file mode 100644 index 00000000..cd66dc30 --- /dev/null +++ b/lib/fatfs/host_test/main/main.cpp @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" diff --git a/lib/fatfs/host_test/main/test_fatfs.cpp b/lib/fatfs/host_test/main/test_fatfs.cpp new file mode 100644 index 00000000..77af2e70 --- /dev/null +++ b/lib/fatfs/host_test/main/test_fatfs.cpp @@ -0,0 +1,285 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#include "ff.h" +#include "esp_partition.h" +#include "wear_levelling.h" +#include "diskio_impl.h" +#include "diskio_wl.h" + +#include "catch.hpp" + +TEST_CASE("Create volume, open file, write and read back data", "[fatfs]") +{ + FRESULT fr_result; + BYTE pdrv; + FATFS fs; + FIL file; + UINT bw; + + esp_err_t esp_result; + + const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, "storage"); + + // Mount wear-levelled partition + wl_handle_t wl_handle; + esp_result = wl_mount(partition, &wl_handle); + REQUIRE(esp_result == ESP_OK); + + // Get a physical drive + esp_result = ff_diskio_get_drive(&pdrv); + REQUIRE(esp_result == ESP_OK); + + // Register physical drive as wear-levelled partition + esp_result = ff_diskio_register_wl_partition(pdrv, wl_handle); + + // Create FAT volume on the entire disk + LBA_t part_list[] = {100, 0, 0, 0}; + BYTE work_area[FF_MAX_SS]; + + fr_result = f_fdisk(pdrv, part_list, work_area); + REQUIRE(fr_result == FR_OK); + const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, 0}; + fr_result = f_mkfs("", &opt, work_area, sizeof(work_area)); // Use default volume + + // Mount the volume + fr_result = f_mount(&fs, "", 0); + REQUIRE(fr_result == FR_OK); + + // Open, write and read data + fr_result = f_open(&file, "test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE); + REQUIRE(fr_result == FR_OK); + + // Generate data + 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; + } + + // Write generated data + fr_result = f_write(&file, data, data_size, &bw); + REQUIRE(fr_result == FR_OK); + REQUIRE(bw == data_size); + + // Move to beginning of file + fr_result = f_lseek(&file, 0); + REQUIRE(fr_result == FR_OK); + + // Read written data + fr_result = f_read(&file, read, data_size, &bw); + REQUIRE(fr_result == FR_OK); + REQUIRE(bw == data_size); + + REQUIRE(memcmp(data, read, data_size) == 0); + + // Close file + fr_result = f_close(&file); + REQUIRE(fr_result == FR_OK); + + // Unmount default volume + fr_result = f_mount(0, "", 0); + REQUIRE(fr_result == FR_OK); + + // Clear + free(read); + free(data); + ff_diskio_unregister(pdrv); + ff_diskio_clear_pdrv_wl(wl_handle); + esp_result = wl_unmount(wl_handle); + REQUIRE(esp_result == ESP_OK); +} + +static void prepare_fatfs(const char* partition_label, const esp_partition_t** partition, wl_handle_t* wl_handle, BYTE* pdrv) +{ + FRESULT fr_result; + esp_err_t esp_result; + + *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label); + REQUIRE(partition != NULL); + printf("partition address=0x%x\n", (*partition)->address); + printf("partition size=0x%x\n", (*partition)->size); + + // Mount wear-levelled partition + esp_result = wl_mount(*partition, wl_handle); + REQUIRE(esp_result == ESP_OK); + + // Get a physical drive + BYTE _pdrv; + esp_result = ff_diskio_get_drive(&_pdrv); + REQUIRE(esp_result == ESP_OK); + printf("using pdrv=%i\n", _pdrv); + char drv[3] = {(char)('0' + _pdrv), ':', 0}; + *pdrv = _pdrv; + + // Register physical drive as wear-levelled partition + esp_result = ff_diskio_register_wl_partition(_pdrv, *wl_handle); + + // Create FAT volume on the entire disk + LBA_t part_list[] = {100, 0, 0, 0}; + BYTE work_area[FF_MAX_SS]; + + fr_result = f_fdisk(_pdrv, part_list, work_area); + REQUIRE(fr_result == FR_OK); + const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, 0}; + fr_result = f_mkfs(drv, &opt, work_area, sizeof(work_area)); // Use default volume + REQUIRE(fr_result == FR_OK); +} + +/* + * This just tests formatting from FATFS library itself, not directly VFS FATFS (SPIFLASH) API + * like `esp_vfs_fat_spiflash_format_rw_wl` function, since `vfs` is not buildable on linux host + * at the time of writing this - therefore there also is a device test_apps test in + * `components/fatfs/test_apps/flash_wl/main/test_fatfs_flash_wl.c` which tests our VFS FATFS SPIFLASH API. + */ +TEST_CASE("Test mounting 2 volumes, writing data and formating the 2nd one, reading data", "[fatfs]") +{ + FRESULT fr_result; + esp_err_t esp_result; + + const char* partition_label0 = "storage"; + const esp_partition_t *partition0 = NULL; + BYTE pdrv0 = UINT8_MAX; + FATFS fs0; + wl_handle_t wl_handle0 = WL_INVALID_HANDLE; + + const char* partition_label1 = "storage2"; + const esp_partition_t *partition1 = NULL; + BYTE pdrv1 = UINT8_MAX; + FATFS fs1; + wl_handle_t wl_handle1 = WL_INVALID_HANDLE; + + size_t allocation_unit_size = CONFIG_WL_SECTOR_SIZE; + size_t data_size = 10; + + + // Mount the volume 0 + prepare_fatfs(partition_label0, &partition0, &wl_handle0, &pdrv0); + REQUIRE(partition0 != NULL); + REQUIRE(wl_handle0 != WL_INVALID_HANDLE); + REQUIRE(pdrv0 == 0); + char drv0[3] = {(char)('0' + pdrv0), ':', 0}; + fr_result = f_mount(&fs0, drv0, 0); + REQUIRE(fr_result == FR_OK); + + // Open file and write data + FIL file0; + UINT bw0; + fr_result = f_open(&file0, "0:/test0.txt", FA_OPEN_ALWAYS | FA_WRITE); + REQUIRE(fr_result == FR_OK); + // Write data + const char *data0 = "123456789"; + char read0[10] = {0}; + fr_result = f_write(&file0, data0, data_size, &bw0); + REQUIRE(fr_result == FR_OK); + REQUIRE(bw0 == data_size); + // Close file + fr_result = f_close(&file0); + REQUIRE(fr_result == FR_OK); + + // Unmount volume 0 + fr_result = f_mount(0, drv0, 0); + REQUIRE(fr_result == FR_OK); + + + // Mount the volume 1 + prepare_fatfs(partition_label1, &partition1, &wl_handle1, &pdrv1); + REQUIRE(partition1 != NULL); + REQUIRE(wl_handle1 != WL_INVALID_HANDLE); + REQUIRE(pdrv1 == 1); + char drv1[3] = {(char)('0' + pdrv1), ':', 0}; + fr_result = f_mount(&fs1, drv1, 0); + REQUIRE(fr_result == FR_OK); + + // Open file and write data + FIL file1; + UINT bw1; + fr_result = f_open(&file1, "1:/test1.txt", FA_OPEN_ALWAYS | FA_WRITE); + REQUIRE(fr_result == FR_OK); + // Write data + const char* data1 = "987654321"; + char read1[10] = {0}; + fr_result = f_write(&file1, data1, data_size, &bw1); + REQUIRE(fr_result == FR_OK); + REQUIRE(bw1 == data_size); + // Close file + fr_result = f_close(&file1); + REQUIRE(fr_result == FR_OK); + + // Unmount volume 1 + fr_result = f_mount(0, drv1, 0); + REQUIRE(fr_result == FR_OK); + + // Format the volume 1 + const size_t workbuf_size = 4096; + void *workbuf = ff_memalloc(workbuf_size); + REQUIRE(workbuf != NULL); + const MKFS_PARM opt = {(BYTE)(FM_ANY | FM_SFD), 0, 0, 0, CONFIG_WL_SECTOR_SIZE}; + fr_result = f_mkfs(drv1, &opt, workbuf, workbuf_size); + free(workbuf); + workbuf = NULL; + REQUIRE(fr_result == FR_OK); + printf("partition1 formatted\n"); + + + // Remount the volume 1 + fr_result = f_mount(&fs1, drv1, 1); + REQUIRE(fr_result == FR_OK); + // Open file and read data from file1 + fr_result = f_open(&file1, "1:/test1.txt", FA_OPEN_ALWAYS | FA_READ); + REQUIRE(fr_result == FR_OK); + // Read written data from file1 + fr_result = f_read(&file1, read1, data_size, &bw1); + REQUIRE(fr_result == FR_OK); + REQUIRE(bw1 != data_size); + // Comapre data + printf("data1=%s, read1=%s\n", data1, read1); + REQUIRE(strncmp(data1, read1, data_size-1) != 0); // 987654321 should be ersead due to formatting + // Close file from file1 + fr_result = f_close(&file1); + REQUIRE(fr_result == FR_OK); + + + // Remount the volume 0 + fr_result = f_mount(&fs0, drv0, 1); + REQUIRE(fr_result == FR_OK); + // Open file and read data from file0 + fr_result = f_open(&file0, "0:/test0.txt", FA_OPEN_ALWAYS | FA_READ); + REQUIRE(fr_result == FR_OK); + // Read written data from file0 + fr_result = f_read(&file0, read0, data_size, &bw0); + REQUIRE(fr_result == FR_OK); + REQUIRE(bw0 == data_size); + // Comapre data + printf("data0=%s, read0=%s\n", data0, read0); + REQUIRE(strncmp(data0, read0, data_size-1) == 0); // should match since the partition was not formatted + // Close file from file0 + fr_result = f_close(&file0); + REQUIRE(fr_result == FR_OK); + + + // Unmount both volumes + fr_result = f_mount(0, drv0, 0); + REQUIRE(fr_result == FR_OK); + fr_result = f_mount(0, drv1, 0); + REQUIRE(fr_result == FR_OK); + + // Clear + ff_diskio_unregister(pdrv0); + ff_diskio_unregister(pdrv1); + ff_diskio_clear_pdrv_wl(wl_handle0); + ff_diskio_clear_pdrv_wl(wl_handle1); + esp_result = wl_unmount(wl_handle0); + REQUIRE(esp_result == ESP_OK); + esp_result = wl_unmount(wl_handle1); + REQUIRE(esp_result == ESP_OK); +} diff --git a/lib/fatfs/host_test/partition_table.csv b/lib/fatfs/host_test/partition_table.csv new file mode 100644 index 00000000..30d2d909 --- /dev/null +++ b/lib/fatfs/host_test/partition_table.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, +storage, data, fat, , 1M, +storage2, data, fat, , 1M, diff --git a/lib/fatfs/host_test/pytest_fatfs_linux.py b/lib/fatfs/host_test/pytest_fatfs_linux.py new file mode 100644 index 00000000..7b12c361 --- /dev/null +++ b/lib/fatfs/host_test/pytest_fatfs_linux.py @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 +import pytest +from pytest_embedded import Dut + + +@pytest.mark.linux +@pytest.mark.host_test +def test_fatfs_linux(dut: Dut) -> None: + dut.expect_exact('All tests passed', timeout=120) diff --git a/lib/fatfs/host_test/sdkconfig.defaults b/lib/fatfs/host_test/sdkconfig.defaults new file mode 100644 index 00000000..e0d9a692 --- /dev/null +++ b/lib/fatfs/host_test/sdkconfig.defaults @@ -0,0 +1,12 @@ +CONFIG_IDF_TARGET="linux" +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n +CONFIG_WL_SECTOR_SIZE=4096 +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table.csv" +CONFIG_ESPTOOLPY_FLASHSIZE="8MB" +CONFIG_MMU_PAGE_SIZE=0X10000 +CONFIG_ESP_PARTITION_ENABLE_STATS=y +CONFIG_FATFS_VOLUME_COUNT=3 diff --git a/lib/fatfs/sbom.yml b/lib/fatfs/sbom.yml new file mode 100644 index 00000000..d9e03707 --- /dev/null +++ b/lib/fatfs/sbom.yml @@ -0,0 +1,5 @@ +name: 'FatFs' +version: 'R0.15' +supplier: 'Organization: Espressif Systems (Shanghai) CO LTD' +originator: 'Person: ChaN' +description: 'Generic FAT Filesystem Module for embedded systems.' diff --git a/lib/fatfs/src/ffconf.h b/lib/fatfs/src/ffconf.h index cc28510a..77bd5981 100644 --- a/lib/fatfs/src/ffconf.h +++ b/lib/fatfs/src/ffconf.h @@ -36,7 +36,7 @@ /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ -#define FF_USE_FASTSEEK 1 +#define FF_USE_FASTSEEK CONFIG_FATFS_USE_FASTSEEK /* This option switches fast seek function. (0:Disable or 1:Enable) */ @@ -296,7 +296,7 @@ #define FF_FS_REENTRANT 1 -#define FF_FS_TIMEOUT (portMAX_DELAY) +#define FF_FS_TIMEOUT (CONFIG_FATFS_TIMEOUT_MS / portTICK_PERIOD_MS) /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs / module itself. Note that regardless of this option, file access to different / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() diff --git a/lib/fatfs/test_apps/.build-test-rules.yml b/lib/fatfs/test_apps/.build-test-rules.yml new file mode 100644 index 00000000..d8277804 --- /dev/null +++ b/lib/fatfs/test_apps/.build-test-rules.yml @@ -0,0 +1,11 @@ +# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps + +components/fatfs/test_apps/sdcard: + disable_test: + - if: IDF_TARGET in ["esp32s3", "esp32c2", "esp32c6", "esp32h2"] + temporary: true + reason: No sdspi runners for these targets + disable: + - if: IDF_TARGET == "esp32p4" + temporary: true + reason: target esp32p4 is not supported yet # TODO: IDF-7501 diff --git a/lib/fatfs/test_apps/flash_ro/README.md b/lib/fatfs/test_apps/flash_ro/README.md index 2dcdd216..4d341fbc 100644 --- a/lib/fatfs/test_apps/flash_ro/README.md +++ b/lib/fatfs/test_apps/flash_ro/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | This test app runs a few FATFS test cases in a read-only FAT partition. diff --git a/lib/fatfs/test_apps/flash_wl/README.md b/lib/fatfs/test_apps/flash_wl/README.md index 051b4766..9112db39 100644 --- a/lib/fatfs/test_apps/flash_wl/README.md +++ b/lib/fatfs/test_apps/flash_wl/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | This test app runs a few FATFS test cases in a wear levelling FAT partition. diff --git a/lib/fatfs/test_apps/flash_wl/main/test_fatfs_flash_wl.c b/lib/fatfs/test_apps/flash_wl/main/test_fatfs_flash_wl.c index a56b7895..1af50fe6 100644 --- a/lib/fatfs/test_apps/flash_wl/main/test_fatfs_flash_wl.c +++ b/lib/fatfs/test_apps/flash_wl/main/test_fatfs_flash_wl.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,6 +10,7 @@ #include #include #include +#include #include "unity.h" #include "esp_partition.h" #include "esp_log.h" @@ -44,14 +45,14 @@ static void test_teardown(void) TEST_ESP_OK(esp_vfs_fat_spiflash_unmount_rw_wl("/spiflash", s_test_wl_handle)); } -TEST_CASE("(WL) can format partition", "[fatfs][wear_levelling]") +TEST_CASE("(WL) can format partition", "[fatfs][wear_levelling][timeout=180]") { TEST_ESP_OK(esp_vfs_fat_spiflash_format_rw_wl("/spiflash", NULL)); test_setup(); test_teardown(); } -TEST_CASE("(WL) can format when the FAT is mounted already", "[fatfs][wear_levelling]") +TEST_CASE("(WL) can format when the FAT is mounted already", "[fatfs][wear_levelling][timeout=180]") { test_setup(); TEST_ESP_OK(esp_vfs_fat_spiflash_format_rw_wl("/spiflash", NULL)); @@ -60,6 +61,30 @@ TEST_CASE("(WL) can format when the FAT is mounted already", "[fatfs][wear_level test_teardown(); } +TEST_CASE("(WL) can format specified FAT when more are mounted", "[fatfs][wear_levelling][timeout=180]") +{ + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5, + }; + wl_handle_t s_test_wl_handle1; + wl_handle_t s_test_wl_handle2; + TEST_ESP_OK(esp_vfs_fat_spiflash_mount_rw_wl("/spiflash1", "storage", &mount_config, &s_test_wl_handle1)); + TEST_ESP_OK(esp_vfs_fat_spiflash_mount_rw_wl("/spiflash2", "storage2", &mount_config, &s_test_wl_handle2)); + + test_fatfs_create_file_with_text("/spiflash1/hello.txt", fatfs_test_hello_str); + test_fatfs_create_file_with_text("/spiflash2/hello.txt", fatfs_test_hello_str); + + TEST_ESP_OK(esp_vfs_fat_spiflash_format_rw_wl("/spiflash2", "storage2")); + + FILE* f = fopen("/spiflash2/hello.txt", "r"); + TEST_ASSERT_NULL(f); // File is erased on the formatted FAT + test_fatfs_pread_file("/spiflash1/hello.txt"); // File is still readable on the other FAT + + TEST_ESP_OK(esp_vfs_fat_spiflash_unmount_rw_wl("/spiflash1", s_test_wl_handle1)); + TEST_ESP_OK(esp_vfs_fat_spiflash_unmount_rw_wl("/spiflash2", s_test_wl_handle2)); +} + TEST_CASE("(WL) can create and write file", "[fatfs][wear_levelling]") { test_setup(); @@ -274,3 +299,14 @@ TEST_CASE("FATFS prefers SPI RAM for allocations", "[fatfs]") test_teardown(); } #endif // CONFIG_SPIRAM + +#if CONFIG_FATFS_IMMEDIATE_FSYNC + +TEST_CASE("Size is correct after write when immediate fsync is enabled", "[fatfs]") +{ + test_setup(); + test_fatfs_size("/spiflash/size.txt", "random text\n preferably something relatively long\n"); + test_teardown(); +} + +#endif // CONFIG_FATFS_IMMEDIATE_FSYNC diff --git a/lib/fatfs/test_apps/flash_wl/partitions.csv b/lib/fatfs/test_apps/flash_wl/partitions.csv index a9299711..d1dcbae6 100644 --- a/lib/fatfs/test_apps/flash_wl/partitions.csv +++ b/lib/fatfs/test_apps/flash_wl/partitions.csv @@ -1,3 +1,4 @@ # Name, Type, SubType, Offset, Size, Flags -factory, app, factory, 0x10000, 1M, +factory, app, factory, 0x10000, 768k, storage, data, fat, , 528k, +storage2, data, fat, , 528k, diff --git a/lib/fatfs/test_apps/flash_wl/pytest_fatfs_flash_wl.py b/lib/fatfs/test_apps/flash_wl/pytest_fatfs_flash_wl.py index 70b46cbf..af8c5db6 100644 --- a/lib/fatfs/test_apps/flash_wl/pytest_fatfs_flash_wl.py +++ b/lib/fatfs/test_apps/flash_wl/pytest_fatfs_flash_wl.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: CC0-1.0 import pytest @@ -20,7 +20,7 @@ def test_fatfs_flash_wl_generic(dut: Dut) -> None: dut.write('') dut.expect_exact('Enter test for running.') dut.write('*') - dut.expect_unity_test_output(timeout=120) + dut.expect_unity_test_output(timeout=180) @pytest.mark.supported_targets @@ -36,4 +36,4 @@ def test_fatfs_flash_wl_psram(dut: Dut) -> None: dut.write('') dut.expect_exact('Enter test for running.') dut.write('*') - dut.expect_unity_test_output(timeout=120) + dut.expect_unity_test_output(timeout=180) diff --git a/lib/fatfs/test_apps/flash_wl/sdkconfig.ci.auto_fsync b/lib/fatfs/test_apps/flash_wl/sdkconfig.ci.auto_fsync new file mode 100644 index 00000000..b74d5124 --- /dev/null +++ b/lib/fatfs/test_apps/flash_wl/sdkconfig.ci.auto_fsync @@ -0,0 +1 @@ +CONFIG_FATFS_IMMEDIATE_FSYNC=y diff --git a/lib/fatfs/test_apps/sdcard/main/test_fatfs_sdmmc.c b/lib/fatfs/test_apps/sdcard/main/test_fatfs_sdmmc.c index 96b691e8..4504a5d6 100644 --- a/lib/fatfs/test_apps/sdcard/main/test_fatfs_sdmmc.c +++ b/lib/fatfs/test_apps/sdcard/main/test_fatfs_sdmmc.c @@ -92,7 +92,7 @@ TEST_CASE("Mount fails cleanly without card inserted", "[fatfs][ignore]") HEAP_SIZE_CHECK(heap_size, 0); } -TEST_CASE("(SD) can format partition", "[fatfs][sdmmc]") +TEST_CASE("(SD) can format partition", "[fatfs][sdmmc][timeout=180]") { sdmmc_card_t *card = NULL; test_setup_sdmmc(&card); diff --git a/lib/fatfs/test_apps/sdcard/main/test_fatfs_sdspi.c b/lib/fatfs/test_apps/sdcard/main/test_fatfs_sdspi.c index 86c74d8b..c1d567c0 100644 --- a/lib/fatfs/test_apps/sdcard/main/test_fatfs_sdspi.c +++ b/lib/fatfs/test_apps/sdcard/main/test_fatfs_sdspi.c @@ -167,7 +167,7 @@ TEST_CASE("(SDSPI) can get partition info", "[fatfs][sdspi]") test_teardown_sdspi(&mem); } -TEST_CASE("(SDSPI) can format card", "[fatfs][sdspi]") +TEST_CASE("(SDSPI) can format card", "[fatfs][sdspi][timeout=180]") { sdspi_mem_t mem; test_setup_sdspi(&mem); diff --git a/lib/fatfs/test_apps/sdcard/pytest_fatfs_sdcard.py b/lib/fatfs/test_apps/sdcard/pytest_fatfs_sdcard.py index 61b97c4c..fe720a02 100644 --- a/lib/fatfs/test_apps/sdcard/pytest_fatfs_sdcard.py +++ b/lib/fatfs/test_apps/sdcard/pytest_fatfs_sdcard.py @@ -19,7 +19,7 @@ def test_fatfs_sdcard_generic_sdmmc(dut: Dut) -> None: dut.write('') dut.expect_exact('Enter test for running.') dut.write('[sdmmc]') - dut.expect_unity_test_output(timeout=120) + dut.expect_unity_test_output(timeout=180) @pytest.mark.esp32 @@ -38,7 +38,7 @@ def test_fatfs_sdcard_generic_sdspi(dut: Dut) -> None: dut.write('') dut.expect_exact('Enter test for running.') dut.write('[sdspi]') - dut.expect_unity_test_output(timeout=120) + dut.expect_unity_test_output(timeout=180) @pytest.mark.esp32 @@ -55,7 +55,7 @@ def test_fatfs_sdcard_psram_sdmmc(dut: Dut) -> None: dut.write('') dut.expect_exact('Enter test for running.') dut.write('[sdmmc]') - dut.expect_unity_test_output(timeout=120) + dut.expect_unity_test_output(timeout=180) @pytest.mark.esp32 @@ -72,4 +72,4 @@ def test_fatfs_sdcard_psram_sdspi(dut: Dut) -> None: dut.write('') dut.expect_exact('Enter test for running.') dut.write('[sdspi]') - dut.expect_unity_test_output(timeout=120) + dut.expect_unity_test_output(timeout=180) diff --git a/lib/fatfs/test_apps/test_fatfs_common/test_fatfs_common.c b/lib/fatfs/test_apps/test_fatfs_common/test_fatfs_common.c index 1bc54c7a..77372fbb 100644 --- a/lib/fatfs/test_apps/test_fatfs_common/test_fatfs_common.c +++ b/lib/fatfs/test_apps/test_fatfs_common/test_fatfs_common.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -434,6 +434,40 @@ void test_fatfs_stat(const char* filename, const char* root_dir) TEST_ASSERT_FALSE(st.st_mode & S_IFREG); } +void test_fatfs_size(const char* filename, const char* content) { + size_t expected_size = strlen(content); + + int fd = open(filename, O_CREAT | O_WRONLY); + TEST_ASSERT_NOT_EQUAL(-1, fd); + + ssize_t wr = write(fd, content, expected_size); + TEST_ASSERT_NOT_EQUAL(-1, wr); + + struct stat st; + TEST_ASSERT_EQUAL(0, stat(filename, &st)); + TEST_ASSERT_EQUAL(wr, st.st_size); + + ssize_t wr2 = pwrite(fd, content, expected_size, expected_size); + TEST_ASSERT_NOT_EQUAL(-1, wr2); + + TEST_ASSERT_EQUAL(0, stat(filename, &st)); + TEST_ASSERT_EQUAL(wr + wr2, st.st_size); + + TEST_ASSERT_EQUAL(0, ftruncate(fd, wr)); + + TEST_ASSERT_EQUAL(0, stat(filename, &st)); + TEST_ASSERT_EQUAL(wr, st.st_size); + + TEST_ASSERT_EQUAL(0, close(fd)); + + wr /= 2; + + TEST_ASSERT_EQUAL(0, truncate(filename, wr)); + + TEST_ASSERT_EQUAL(0, stat(filename, &st)); + TEST_ASSERT_EQUAL(wr, st.st_size); +} + void test_fatfs_mtime_dst(const char* filename, const char* root_dir) { struct timeval tv = { 1653638041, 0 }; diff --git a/lib/fatfs/test_apps/test_fatfs_common/test_fatfs_common.h b/lib/fatfs/test_apps/test_fatfs_common/test_fatfs_common.h index dd28211d..dad352e2 100644 --- a/lib/fatfs/test_apps/test_fatfs_common/test_fatfs_common.h +++ b/lib/fatfs/test_apps/test_fatfs_common/test_fatfs_common.h @@ -51,6 +51,8 @@ void test_fatfs_ftruncate_file(const char* path); void test_fatfs_stat(const char* filename, const char* root_dir); +void test_fatfs_size(const char* filename, const char* content); + void test_fatfs_mtime_dst(const char* filename, const char* root_dir); void test_fatfs_utime(const char* filename, const char* root_dir); diff --git a/lib/fatfs/vfs/esp_vfs_fat.h b/lib/fatfs/vfs/esp_vfs_fat.h index c84a2055..3e7b165b 100644 --- a/lib/fatfs/vfs/esp_vfs_fat.h +++ b/lib/fatfs/vfs/esp_vfs_fat.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -316,7 +316,7 @@ esp_err_t esp_vfs_fat_spiflash_mount_ro(const char* base_path, * @brief Unmount FAT filesystem and release resources acquired using esp_vfs_fat_spiflash_mount_ro * * @param base_path path where partition should be registered (e.g. "/spiflash") - * @param partition_label label of partition to be unmounted + * @param partition_label label of partition to be unmounted * * @return * - ESP_OK on success @@ -324,32 +324,49 @@ esp_err_t esp_vfs_fat_spiflash_mount_ro(const char* base_path, */ esp_err_t esp_vfs_fat_spiflash_unmount_ro(const char* base_path, const char* partition_label); +/** + * @brief Get information for FATFS partition + * + * @param base_path Base path of the partition examined (e.g. "/spiflash") + * @param[out] out_total_bytes Size of the file system + * @param[out] out_free_bytes Free bytes available in the file system + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if partition not found + * - ESP_FAIL if another FRESULT error (saved in errno) + */ +esp_err_t esp_vfs_fat_info(const char* base_path, uint64_t* out_total_bytes, uint64_t* out_free_bytes); + +/** @cond */ +/** + * @deprecated Please use `esp_vfs_fat_spiflash_mount_rw_wl` instead + */ esp_err_t esp_vfs_fat_spiflash_mount(const char* base_path, const char* partition_label, const esp_vfs_fat_mount_config_t* mount_config, wl_handle_t* wl_handle) __attribute__((deprecated("esp_vfs_fat_spiflash_mount is deprecated, please use esp_vfs_fat_spiflash_mount_rw_wl instead"))); + +/** + * @deprecated Please use `esp_vfs_fat_spiflash_unmount_rw_wl` instead + */ esp_err_t esp_vfs_fat_spiflash_unmount(const char* base_path, wl_handle_t wl_handle) __attribute__((deprecated("esp_vfs_fat_spiflash_unmount is deprecated, please use esp_vfs_fat_spiflash_unmount_rw_wl instead"))); + +/** + * @deprecated Please use `esp_vfs_fat_spiflash_mount_ro` instead + */ esp_err_t esp_vfs_fat_rawflash_mount(const char* base_path, const char* partition_label, const esp_vfs_fat_mount_config_t* mount_config) __attribute__((deprecated("esp_vfs_fat_rawflash_mount is deprecated, please use esp_vfs_fat_spiflash_mount_ro instead"))); -esp_err_t esp_vfs_fat_rawflash_unmount(const char* base_path, const char* partition_label) - __attribute__((deprecated("esp_vfs_fat_rawflash_unmount is deprecated, please use esp_vfs_fat_spiflash_unmount_ro instead"))); /** - * @brief Get information for FATFS partition - * - * @param base_path Path where partition should be registered (e.g. "/spiflash") - * @param[out] out_total_bytes Size of the file system - * @param[out] out_free_bytes Current used bytes in the file system - * @return - * - ESP_OK on success - * - ESP_ERR_INVALID_STATE if partition not found - * - ESP_FAIL if another FRESULT error (saved in errno) + * @deprecated Please use `esp_vfs_fat_spiflash_unmount_ro` instead */ -esp_err_t esp_vfs_fat_info(const char* base_path, uint64_t* out_total_bytes, uint64_t* out_free_bytes); +esp_err_t esp_vfs_fat_rawflash_unmount(const char* base_path, const char* partition_label) + __attribute__((deprecated("esp_vfs_fat_rawflash_unmount is deprecated, please use esp_vfs_fat_spiflash_unmount_ro instead"))); +/** @endcond */ #ifdef __cplusplus } diff --git a/lib/fatfs/vfs/vfs_fat.c b/lib/fatfs/vfs/vfs_fat.c index eff73ec3..4ae04c3d 100644 --- a/lib/fatfs/vfs/vfs_fat.c +++ b/lib/fatfs/vfs/vfs_fat.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -416,6 +416,21 @@ static ssize_t vfs_fat_write(void* ctx, int fd, const void * data, size_t size) return -1; } } + +#if CONFIG_FATFS_IMMEDIATE_FSYNC + _lock_acquire(&fat_ctx->lock); + if (written > 0) { + res = f_sync(file); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + _lock_release(&fat_ctx->lock); + return -1; + } + } + _lock_release(&fat_ctx->lock); +#endif + return written; } @@ -514,6 +529,18 @@ static ssize_t vfs_fat_pwrite(void *ctx, int fd, const void *src, size_t size, o ret = -1; // in case the write was successful but the seek wasn't } +#if CONFIG_FATFS_IMMEDIATE_FSYNC + if (wr > 0) { + FRESULT f_res2 = f_sync(file); // We need new result to check whether we can overwrite errno + if (f_res2 != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res2); + if (f_res == FR_OK) + errno = fresult_to_errno(f_res2); + ret = -1; + } + } +#endif + pwrite_release: _lock_release(&fat_ctx->lock); return ret; @@ -727,6 +754,13 @@ static int vfs_fat_link(void* ctx, const char* n1, const char* n2) } size_left -= will_copy; } + +#if CONFIG_FATFS_IMMEDIATE_FSYNC + _lock_acquire(&fat_ctx->lock); + res = f_sync(pf2); + _lock_release(&fat_ctx->lock); +#endif + fail3: f_close(pf2); fail2: @@ -985,13 +1019,24 @@ static int vfs_fat_truncate(void* ctx, const char *path, off_t length) } res = f_truncate(file); - _lock_release(&fat_ctx->lock); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + ret = -1; + goto close; + } + +#if CONFIG_FATFS_IMMEDIATE_FSYNC + res = f_sync(file); if (res != FR_OK) { ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); errno = fresult_to_errno(res); ret = -1; } +#endif + + _lock_release(&fat_ctx->lock); close: res = f_close(file); @@ -1051,11 +1096,21 @@ static int vfs_fat_ftruncate(void* ctx, int fd, off_t length) res = f_truncate(file); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + ret = -1; + goto out; + } + +#if CONFIG_FATFS_IMMEDIATE_FSYNC + res = f_sync(file); if (res != FR_OK) { ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); errno = fresult_to_errno(res); ret = -1; } +#endif out: _lock_release(&fat_ctx->lock); diff --git a/lib/fatfs/vfs/vfs_fat_sdmmc.c b/lib/fatfs/vfs/vfs_fat_sdmmc.c index f3625a2f..f62d6e7c 100644 --- a/lib/fatfs/vfs/vfs_fat_sdmmc.c +++ b/lib/fatfs/vfs/vfs_fat_sdmmc.c @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include +#include "esp_check.h" #include "esp_log.h" #include "esp_vfs.h" #include "esp_vfs_fat.h" @@ -439,6 +440,8 @@ esp_err_t esp_vfs_fat_sdcard_unmount(const char *base_path, sdmmc_card_t *card) if (!found) { return ESP_ERR_INVALID_ARG; } + free(s_ctx[id]->base_path); + s_ctx[id]->base_path = NULL; free(s_ctx[id]); s_ctx[id] = NULL; @@ -461,16 +464,20 @@ esp_err_t esp_vfs_fat_sdcard_format(const char *base_path, sdmmc_card_t *card) return ESP_ERR_INVALID_STATE; } + //unmount + char drv[3] = {(char)('0' + pdrv), ':', 0}; + FRESULT res = f_mount(0, drv, 0); + if (res != FR_OK) { + ESP_LOGE(TAG, "f_mount unmount failed (%d)", res); + return ESP_FAIL; + } + const size_t workbuf_size = 4096; void *workbuf = ff_memalloc(workbuf_size); if (workbuf == NULL) { return ESP_ERR_NO_MEM; } - //unmount - char drv[3] = {(char)('0' + pdrv), ':', 0}; - f_mount(0, drv, 0); - //format uint32_t id = FF_VOLUMES; bool found = s_get_context_id_by_card(card, &id); @@ -480,7 +487,7 @@ esp_err_t esp_vfs_fat_sdcard_format(const char *base_path, sdmmc_card_t *card) s_ctx[id]->mount_config.allocation_unit_size); ESP_LOGI(TAG, "Formatting card, allocation unit size=%d", alloc_unit_size); const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size}; - FRESULT res = f_mkfs(drv, &opt, workbuf, workbuf_size); + res = f_mkfs(drv, &opt, workbuf, workbuf_size); free(workbuf); if (res != FR_OK) { ret = ESP_FAIL; diff --git a/lib/fatfs/vfs/vfs_fat_spiflash.c b/lib/fatfs/vfs/vfs_fat_spiflash.c index d227de82..98e4160f 100644 --- a/lib/fatfs/vfs/vfs_fat_spiflash.c +++ b/lib/fatfs/vfs/vfs_fat_spiflash.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,7 +8,6 @@ #include #include "esp_check.h" #include "esp_log.h" -#include "esp_vfs.h" #include "esp_vfs_fat.h" #include "vfs_fat_internal.h" #include "diskio_impl.h" @@ -29,6 +28,8 @@ typedef struct vfs_fat_spiflash_ctx_t { static vfs_fat_spiflash_ctx_t *s_ctx[FF_VOLUMES] = {}; +extern esp_err_t esp_vfs_set_readonly_flag(const char* base_path); // from vfs/vfs.c to set readonly flag in esp_vfs_t struct externally + static bool s_get_context_id_by_label(const char *label, uint32_t *out_id) { vfs_fat_spiflash_ctx_t *p_ctx = NULL; @@ -160,6 +161,10 @@ esp_err_t esp_vfs_fat_spiflash_mount_rw_wl(const char* base_path, assert(ctx_id != FF_VOLUMES); s_ctx[ctx_id] = ctx; + if (data_partition->readonly) { + esp_vfs_set_readonly_flag(base_path); + } + return ESP_OK; fail: @@ -203,7 +208,6 @@ esp_err_t esp_vfs_fat_spiflash_format_rw_wl(const char* base_path, const char* p wl_handle_t temp_handle = WL_INVALID_HANDLE; uint32_t id = FF_VOLUMES; - char drv[3] = {0, ':', 0}; bool found = s_get_context_id_by_label(partition_label, &id); if (!found) { @@ -219,8 +223,10 @@ esp_err_t esp_vfs_fat_spiflash_format_rw_wl(const char* base_path, const char* p } //unmount - drv[1] = (char)('0' + s_ctx[id]->pdrv); - f_mount(0, drv, 0); + char drv[3] = {(char)('0' + s_ctx[id]->pdrv), ':', 0}; + FRESULT fresult = f_mount(0, drv, 0); + ESP_RETURN_ON_FALSE(fresult != FR_INVALID_DRIVE, ESP_FAIL, TAG, "f_mount unmount failed (%d) - the logical drive number is invalid", fresult); + ESP_GOTO_ON_FALSE(fresult == FR_OK, ESP_FAIL, recycle, TAG, "f_mount unmount failed (%d), go to recycle", fresult); const size_t workbuf_size = 4096; void *workbuf = ff_memalloc(workbuf_size); @@ -231,7 +237,7 @@ esp_err_t esp_vfs_fat_spiflash_format_rw_wl(const char* base_path, const char* p size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(CONFIG_WL_SECTOR_SIZE, s_ctx[id]->mount_config.allocation_unit_size); ESP_LOGI(TAG, "Formatting FATFS partition, allocation unit size=%d", alloc_unit_size); const MKFS_PARM opt = {(BYTE)(FM_ANY | FM_SFD), 0, 0, 0, alloc_unit_size}; - FRESULT fresult = f_mkfs(drv, &opt, workbuf, workbuf_size); + fresult = f_mkfs(drv, &opt, workbuf, workbuf_size); free(workbuf); workbuf = NULL; ESP_GOTO_ON_FALSE(fresult == FR_OK, ESP_FAIL, mount_back, TAG, "f_mkfs failed (%d)", fresult); @@ -296,6 +302,11 @@ esp_err_t esp_vfs_fat_spiflash_mount_ro(const char* base_path, ret = ESP_FAIL; goto fail; } + + if (data_partition->readonly) { + esp_vfs_set_readonly_flag(base_path); + } + return ESP_OK; fail: