diff --git a/components/fatfs/vfs/esp_vfs_fat.h b/components/fatfs/vfs/esp_vfs_fat.h index 7b87818fa87..dbe4abf4532 100644 --- a/components/fatfs/vfs/esp_vfs_fat.h +++ b/components/fatfs/vfs/esp_vfs_fat.h @@ -111,6 +111,12 @@ typedef struct { * may be different. */ bool use_one_fat; + /** + * Number of reserved root directory items. + * Effective only on FAT12/16, ignored on other file systems (by FatFS lib). + * 0 == default (maximum number of 32B entries given by the sector size). + */ + size_t rootdir_entries; } esp_vfs_fat_mount_config_t; #define VFS_FAT_MOUNT_DEFAULT_CONFIG() \ @@ -120,6 +126,7 @@ typedef struct { .allocation_unit_size = 0, \ .disk_status_check_enable = false, \ .use_one_fat = false, \ + .rootdir_entries = 0, \ } // Compatibility definition diff --git a/components/fatfs/vfs/vfs_fat_spiflash.c b/components/fatfs/vfs/vfs_fat_spiflash.c index 7144f165f6a..eed4464269e 100644 --- a/components/fatfs/vfs/vfs_fat_spiflash.c +++ b/components/fatfs/vfs/vfs_fat_spiflash.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -78,7 +78,7 @@ vfs_fat_spiflash_ctx_t* get_vfs_fat_spiflash_ctx(wl_handle_t wlhandle) return NULL; } -static esp_err_t s_f_mount_rw(FATFS *fs, const char *drv, const esp_vfs_fat_mount_config_t *mount_config, vfs_fat_x_ctx_flags_t *out_flags, size_t sec_num) +static esp_err_t s_f_mount_rw(FATFS *fs, const char *drv, const esp_vfs_fat_mount_config_t *mount_config, vfs_fat_x_ctx_flags_t *out_flags) { FRESULT fresult = f_mount(fs, drv, 1); if (fresult != FR_OK) { @@ -97,13 +97,12 @@ static esp_err_t s_f_mount_rw(FATFS *fs, const char *drv, const esp_vfs_fat_moun size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(CONFIG_WL_SECTOR_SIZE, mount_config->allocation_unit_size); ESP_LOGI(TAG, "Formatting FATFS partition, allocation unit size=%d", alloc_unit_size); - UINT root_dir_entries; - if (CONFIG_WL_SECTOR_SIZE == 512) { - root_dir_entries = 16; - } else { - root_dir_entries = 128; + UINT root_dir_entries = mount_config->rootdir_entries; + if (root_dir_entries == 0) { + root_dir_entries = CONFIG_WL_SECTOR_SIZE / 32; } - const MKFS_PARM opt = {(BYTE)(FM_ANY | FM_SFD), (mount_config->use_one_fat ? 1 : 2), 0, (sec_num <= MIN_REQ_SEC ? root_dir_entries : 0), alloc_unit_size}; + + const MKFS_PARM opt = {(BYTE)(FM_ANY | FM_SFD), (mount_config->use_one_fat ? 1 : 2), 0, root_dir_entries, alloc_unit_size}; fresult = f_mkfs(drv, &opt, workbuf, workbuf_size); free(workbuf); workbuf = NULL; @@ -121,6 +120,63 @@ static esp_err_t s_f_mount_rw(FATFS *fs, const char *drv, const esp_vfs_fat_moun *out_flags &= ~FORMATTED_DURING_LAST_MOUNT; // reset flag } } + + const char *msg = "Unknown"; + const char *note = ""; + bool recoverable = false; + + switch (fresult) { + case FR_NO_FILESYSTEM: + msg = "No filesystem detected"; + note = "(This may indicate corrupt FS, or attempt to mount read-only fatfsgen image for write)"; + recoverable = true; + break; + case FR_INT_ERR: + msg = "Assertion failed"; + recoverable = true; + break; + default: + break; + } + + if (!recoverable || !mount_config->format_if_mount_failed) { + ESP_LOGE(TAG, "f_mount failed with error: \"%s\" [%d]. %s", msg, fresult, note); + return ESP_FAIL; + } + + ESP_LOGW(TAG, "FatFS mount (f_mount) failed with error: \"%s\" [%d]. Retrying after format...", msg, fresult); + + const size_t workbuf_size = 4096; + void *workbuf = ff_memalloc(workbuf_size); + if (workbuf == NULL) { + return ESP_ERR_NO_MEM; + } + + size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(CONFIG_WL_SECTOR_SIZE, mount_config->allocation_unit_size); + ESP_LOGI(TAG, "Formatting FATFS partition, allocation unit size=%d", alloc_unit_size); + + //if root directory entries set as default, allocate whole 1 sector for this purpose (one rootdir entry == 32B) + UINT root_dir_entries = mount_config->rootdir_entries; + if (root_dir_entries == 0) { + root_dir_entries = CONFIG_WL_SECTOR_SIZE / 32; + } + const MKFS_PARM opt = {(BYTE)(FM_ANY | FM_SFD), (mount_config->use_one_fat ? 1 : 2), 0, root_dir_entries, alloc_unit_size}; + ESP_LOGD(TAG, "f_mkfs() params: root_dir_entries: %ul, alloc_unit_size=%ul", root_dir_entries, alloc_unit_size); + + fresult = f_mkfs(drv, &opt, workbuf, workbuf_size); + + free(workbuf); + workbuf = NULL; + ESP_RETURN_ON_FALSE(fresult == FR_OK, ESP_FAIL, TAG, "f_mkfs failed (%d)", fresult); + + if (out_flags) { + *out_flags |= FORMATTED_DURING_LAST_MOUNT; // set flag + } + + ESP_LOGI(TAG, "Mounting again"); + fresult = f_mount(fs, drv, 1); + ESP_RETURN_ON_FALSE(fresult == FR_OK, ESP_FAIL, TAG, "f_mount failed after formatting (%d)", fresult); + return ESP_OK; } @@ -132,11 +188,9 @@ esp_err_t esp_vfs_fat_spiflash_mount_rw_wl(const char* base_path, esp_err_t ret = ESP_OK; vfs_fat_spiflash_ctx_t *ctx = NULL; uint32_t ctx_id = FF_VOLUMES; + esp_partition_subtype_t subtype = partition_label ? ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_FAT; - esp_partition_subtype_t subtype = partition_label ? - ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_FAT; - const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, - subtype, partition_label); + const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, subtype, partition_label); ESP_RETURN_ON_FALSE(data_partition, ESP_ERR_NOT_FOUND, TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label); ESP_RETURN_ON_ERROR(wl_mount(data_partition, wl_handle), TAG, "failed to mount wear levelling layer. ret = %i", ret); @@ -167,9 +221,8 @@ esp_err_t esp_vfs_fat_spiflash_mount_rw_wl(const char* base_path, vfs_fat_x_ctx_flags_t flags = 0; - size_t sec_num = wl_size(*wl_handle) / wl_sector_size(*wl_handle); // Try to mount partition - ret = s_f_mount_rw(fs, drv, mount_config, &flags, sec_num); + ret = s_f_mount_rw(fs, drv, mount_config, &flags); if (ret != ESP_OK) { goto fail; } @@ -235,7 +288,6 @@ esp_err_t esp_vfs_fat_spiflash_format_cfg_rw_wl(const char* base_path, const cha wl_handle_t temp_handle = WL_INVALID_HANDLE; uint32_t id = FF_VOLUMES; - size_t sec_num = 0; bool found = s_get_context_id_by_label(partition_label, &id); if (!found) { @@ -251,7 +303,6 @@ esp_err_t esp_vfs_fat_spiflash_format_cfg_rw_wl(const char* base_path, const cha } ESP_RETURN_ON_ERROR(esp_vfs_fat_spiflash_mount_rw_wl(base_path, partition_label, mount_cfg, &temp_handle), TAG, "Failed to mount"); found = s_get_context_id_by_label(partition_label, &id); - sec_num = wl_size(temp_handle) / wl_sector_size(temp_handle); assert(found); if (s_ctx[id]->flags & FORMATTED_DURING_LAST_MOUNT) { ESP_LOGD(TAG, "partition was formatted during mounting, skipping another format"); @@ -264,7 +315,6 @@ esp_err_t esp_vfs_fat_spiflash_format_cfg_rw_wl(const char* base_path, const cha s_ctx[id]->mount_config = *cfg; } temp_handle = s_ctx[id]->wlhandle; - sec_num = wl_size(temp_handle) / wl_sector_size(temp_handle); } //unmount @@ -281,13 +331,14 @@ esp_err_t esp_vfs_fat_spiflash_format_cfg_rw_wl(const char* base_path, const cha } 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); - UINT root_dir_entries; - if (CONFIG_WL_SECTOR_SIZE == 512) { - root_dir_entries = 16; - } else { - root_dir_entries = 128; + + //if root directory entries set as default, allocate whole 1 sector for this purpose (one rootdir entry == 32B) + UINT root_dir_entries = s_ctx[id]->mount_config.rootdir_entries; + if (root_dir_entries == 0) { + root_dir_entries = CONFIG_WL_SECTOR_SIZE / 32; } - const MKFS_PARM opt = {(BYTE)(FM_ANY | FM_SFD), (s_ctx[id]->mount_config.use_one_fat ? 1 : 2), 0, (sec_num <= MIN_REQ_SEC ? root_dir_entries : 0), alloc_unit_size}; + const MKFS_PARM opt = {(BYTE)(FM_ANY | FM_SFD), (s_ctx[id]->mount_config.use_one_fat ? 1 : 2), 0, root_dir_entries, alloc_unit_size}; + fresult = f_mkfs(drv, &opt, workbuf, workbuf_size); free(workbuf); workbuf = NULL; @@ -295,7 +346,7 @@ esp_err_t esp_vfs_fat_spiflash_format_cfg_rw_wl(const char* base_path, const cha mount_back: if (partition_was_mounted) { - esp_err_t err = s_f_mount_rw(s_ctx[id]->fs, drv, &s_ctx[id]->mount_config, NULL, sec_num); + esp_err_t err = s_f_mount_rw(s_ctx[id]->fs, drv, &s_ctx[id]->mount_config, NULL); if (err != ESP_OK) { ESP_LOGE(TAG, "failed to mount back, go to recycle"); goto recycle;