mirror of
https://github.com/espressif/esp-idf.git
synced 2026-07-19 01:13:01 +03:00
fatfs: add read_only flag to esp_vfs_fat_mount_config_t
This commit is contained in:
@@ -15,6 +15,7 @@ set(requires "wear_levelling" "esp_blockdev")
|
||||
if(${target} STREQUAL "linux")
|
||||
list(APPEND srcs "port/linux/ffsystem.c"
|
||||
"vfs/vfs_fat.c"
|
||||
"vfs/vfs_fat_spiflash.c"
|
||||
"vfs/vfs_fat_bdl.c")
|
||||
list(APPEND include_dirs "vfs")
|
||||
list(APPEND priv_requires "vfs" "linux")
|
||||
|
||||
@@ -38,6 +38,16 @@ components/fatfs/test_apps/flash_wl:
|
||||
- vfs
|
||||
- wear_leveling
|
||||
|
||||
components/fatfs/test_apps/flash_wl_readonly:
|
||||
disable_test:
|
||||
- if: IDF_TARGET not in ["esp32", "esp32c3"]
|
||||
reason: only one target per arch needed
|
||||
depends_components:
|
||||
- esp_partition
|
||||
- fatfs
|
||||
- vfs
|
||||
- wear_leveling
|
||||
|
||||
components/fatfs/test_apps/sdcard:
|
||||
disable:
|
||||
- if: SOC_GPSPI_SUPPORTED != 1
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
set(COMPONENTS main)
|
||||
set(EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_LIST_DIR}/../test_fatfs_common")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
project(test_fatfs_flash_wl_readonly)
|
||||
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS "test_fatfs_flash_wl_readonly.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES unity spi_flash fatfs vfs
|
||||
WHOLE_ARCHIVE)
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/unistd.h>
|
||||
#include "unity.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "wear_levelling.h"
|
||||
|
||||
static const char *BASE_PATH = "/spiflash";
|
||||
static wl_handle_t s_wl_handle = WL_INVALID_HANDLE;
|
||||
|
||||
static void prepare_filesystem(void)
|
||||
{
|
||||
esp_vfs_fat_mount_config_t cfg = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 5,
|
||||
};
|
||||
TEST_ESP_OK(esp_vfs_fat_spiflash_mount_rw_wl(BASE_PATH, NULL, &cfg, &s_wl_handle));
|
||||
|
||||
FILE *f = fopen("/spiflash/hello.txt", "w");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
fprintf(f, "hello");
|
||||
fclose(f);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, mkdir("/spiflash/testdir", 0755));
|
||||
|
||||
TEST_ESP_OK(esp_vfs_fat_spiflash_unmount_rw_wl(BASE_PATH, s_wl_handle));
|
||||
s_wl_handle = WL_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
static void mount_readonly(void)
|
||||
{
|
||||
esp_vfs_fat_mount_config_t cfg = {
|
||||
.format_if_mount_failed = false,
|
||||
.max_files = 5,
|
||||
.read_only = true,
|
||||
};
|
||||
TEST_ESP_OK(esp_vfs_fat_spiflash_mount_rw_wl(BASE_PATH, NULL, &cfg, &s_wl_handle));
|
||||
}
|
||||
|
||||
static void unmount(void)
|
||||
{
|
||||
TEST_ESP_OK(esp_vfs_fat_spiflash_unmount_rw_wl(BASE_PATH, s_wl_handle));
|
||||
s_wl_handle = WL_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
prepare_filesystem();
|
||||
unity_run_menu();
|
||||
}
|
||||
|
||||
TEST_CASE("read_only mount: can read existing file", "[fatfs][readonly]")
|
||||
{
|
||||
mount_readonly();
|
||||
|
||||
FILE *f = fopen("/spiflash/hello.txt", "r");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
char buf[16] = {};
|
||||
TEST_ASSERT_EQUAL(5, fread(buf, 1, sizeof(buf), f));
|
||||
TEST_ASSERT_EQUAL_STRING("hello", buf);
|
||||
fclose(f);
|
||||
|
||||
unmount();
|
||||
}
|
||||
|
||||
TEST_CASE("read_only mount: open for write fails with EROFS", "[fatfs][readonly]")
|
||||
{
|
||||
mount_readonly();
|
||||
|
||||
int fd = open("/spiflash/new.txt", O_CREAT | O_RDWR, 0666);
|
||||
TEST_ASSERT_EQUAL(-1, fd);
|
||||
TEST_ASSERT_EQUAL(EROFS, errno);
|
||||
|
||||
fd = open("/spiflash/hello.txt", O_WRONLY);
|
||||
TEST_ASSERT_EQUAL(-1, fd);
|
||||
TEST_ASSERT_EQUAL(EROFS, errno);
|
||||
|
||||
unmount();
|
||||
}
|
||||
|
||||
TEST_CASE("read_only mount: mkdir fails with EROFS", "[fatfs][readonly]")
|
||||
{
|
||||
mount_readonly();
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, mkdir("/spiflash/newdir", 0755));
|
||||
TEST_ASSERT_EQUAL(EROFS, errno);
|
||||
|
||||
unmount();
|
||||
}
|
||||
|
||||
TEST_CASE("read_only mount: unlink fails with EROFS", "[fatfs][readonly]")
|
||||
{
|
||||
mount_readonly();
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, unlink("/spiflash/hello.txt"));
|
||||
TEST_ASSERT_EQUAL(EROFS, errno);
|
||||
|
||||
unmount();
|
||||
}
|
||||
|
||||
TEST_CASE("read_only mount: rename fails with EROFS", "[fatfs][readonly]")
|
||||
{
|
||||
mount_readonly();
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, rename("/spiflash/hello.txt", "/spiflash/bye.txt"));
|
||||
TEST_ASSERT_EQUAL(EROFS, errno);
|
||||
|
||||
unmount();
|
||||
}
|
||||
|
||||
TEST_CASE("read_only mount: stat and opendir still work", "[fatfs][readonly]")
|
||||
{
|
||||
mount_readonly();
|
||||
|
||||
struct stat st;
|
||||
TEST_ASSERT_EQUAL(0, stat("/spiflash/hello.txt", &st));
|
||||
TEST_ASSERT(st.st_mode & S_IFREG);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, stat("/spiflash/testdir", &st));
|
||||
TEST_ASSERT(st.st_mode & S_IFDIR);
|
||||
|
||||
DIR *dir = opendir("/spiflash");
|
||||
TEST_ASSERT_NOT_NULL(dir);
|
||||
TEST_ASSERT_EQUAL(0, closedir(dir));
|
||||
|
||||
unmount();
|
||||
}
|
||||
|
||||
TEST_CASE("read_only mount: format_if_mount_failed and read_only are mutually exclusive", "[fatfs][readonly]")
|
||||
{
|
||||
esp_vfs_fat_mount_config_t cfg = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 5,
|
||||
.read_only = true,
|
||||
};
|
||||
wl_handle_t wl_handle = WL_INVALID_HANDLE;
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG,
|
||||
esp_vfs_fat_spiflash_mount_rw_wl(BASE_PATH, NULL, &cfg, &wl_handle));
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, fat, , 528k,
|
||||
|
@@ -0,0 +1,11 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['esp32', 'esp32c3'], indirect=['target'])
|
||||
def test_fatfs_flash_wl_readonly(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
||||
@@ -0,0 +1,14 @@
|
||||
# General options for additional checks
|
||||
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
|
||||
CONFIG_COMPILER_WARN_WRITE_STRINGS=y
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
|
||||
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
|
||||
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
|
||||
CONFIG_COMPILER_STACK_CHECK=y
|
||||
|
||||
# disable task watchdog since this app uses an interactive menu
|
||||
CONFIG_ESP_TASK_WDT_INIT=n
|
||||
|
||||
# use custom partition table
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
@@ -136,6 +136,14 @@ typedef struct {
|
||||
* may be different.
|
||||
*/
|
||||
bool use_one_fat;
|
||||
/**
|
||||
* Mount the filesystem in read-only mode.
|
||||
* When set to true, all write operations (open for write, mkdir, unlink,
|
||||
* rename, etc.) will fail with errno set to EROFS.
|
||||
* This flag is independent of the underlying storage — it can be used
|
||||
* with wear-levelled, raw, BDL, or SD card partitions.
|
||||
*/
|
||||
bool read_only;
|
||||
} esp_vfs_fat_mount_config_t;
|
||||
|
||||
#define VFS_FAT_MOUNT_DEFAULT_CONFIG() \
|
||||
@@ -145,6 +153,7 @@ typedef struct {
|
||||
.allocation_unit_size = 0, \
|
||||
.disk_status_check_enable = false, \
|
||||
.use_one_fat = false, \
|
||||
.read_only = false, \
|
||||
}
|
||||
|
||||
// Compatibility definition
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string.h>
|
||||
#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"
|
||||
@@ -17,8 +18,6 @@ static const char *TAG = "vfs_fat_bdl";
|
||||
|
||||
static vfs_fat_bdl_ctx_t *s_bdl_ctx[FF_VOLUMES] = {};
|
||||
|
||||
extern esp_err_t esp_vfs_set_readonly_flag(const char *base_path);
|
||||
|
||||
static bool get_ctx_id_by_bdl(esp_blockdev_handle_t bdl, uint32_t *out_id)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
@@ -104,6 +103,8 @@ esp_err_t esp_vfs_fat_bdl_mount(const char *base_path,
|
||||
ESP_RETURN_ON_FALSE(base_path, ESP_ERR_INVALID_ARG, TAG, "base_path is NULL");
|
||||
ESP_RETURN_ON_FALSE(bdl_handle != ESP_BLOCKDEV_HANDLE_INVALID, ESP_ERR_INVALID_ARG, TAG, "invalid BDL handle");
|
||||
ESP_RETURN_ON_FALSE(mount_config, ESP_ERR_INVALID_ARG, TAG, "mount_config is NULL");
|
||||
ESP_RETURN_ON_FALSE(!(mount_config->read_only && mount_config->format_if_mount_failed),
|
||||
ESP_ERR_INVALID_ARG, TAG, "read_only and format_if_mount_failed are mutually exclusive");
|
||||
|
||||
BYTE pdrv = 0xFF;
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK) {
|
||||
@@ -165,7 +166,7 @@ esp_err_t esp_vfs_fat_bdl_mount(const char *base_path,
|
||||
assert(ctx_id != FF_VOLUMES);
|
||||
s_bdl_ctx[ctx_id] = ctx;
|
||||
|
||||
if (bdl_handle->device_flags.read_only) {
|
||||
if (bdl_handle->device_flags.read_only || mount_config->read_only) {
|
||||
esp_vfs_set_readonly_flag(base_path);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -415,6 +415,8 @@ esp_err_t esp_vfs_fat_mount_initialized(sdmmc_card_t* card,
|
||||
if (card == NULL || base_path == NULL || mount_config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(!(mount_config->read_only && mount_config->format_if_mount_failed),
|
||||
ESP_ERR_INVALID_ARG, TAG, "read_only and format_if_mount_failed are mutually exclusive");
|
||||
|
||||
esp_err_t err;
|
||||
|
||||
@@ -436,6 +438,10 @@ esp_err_t esp_vfs_fat_mount_initialized(sdmmc_card_t* card,
|
||||
err = esp_vfs_fat_save_ctx(ldrv, mount_config, card, dup_path, fs, flags);
|
||||
CHECK_EXECUTE_RESULT(err, "esp_vfs_fat_save_ctx failed");
|
||||
|
||||
if (mount_config->read_only) {
|
||||
esp_vfs_set_readonly_flag(base_path);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
cleanup:
|
||||
free(dup_path);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string.h>
|
||||
#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"
|
||||
@@ -23,8 +24,6 @@ static const char* TAG = "vfs_fat_spiflash";
|
||||
|
||||
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 externally
|
||||
|
||||
static bool s_get_context_id_by_label(const char *label, uint32_t *out_id)
|
||||
{
|
||||
vfs_fat_spiflash_ctx_t *p_ctx = NULL;
|
||||
@@ -149,6 +148,9 @@ esp_err_t esp_vfs_fat_spiflash_mount_rw_wl(const char* base_path,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
wl_handle_t* wl_handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(!(mount_config->read_only && mount_config->format_if_mount_failed),
|
||||
ESP_ERR_INVALID_ARG, TAG, "read_only and format_if_mount_failed are mutually exclusive");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
vfs_fat_spiflash_ctx_t *ctx = NULL;
|
||||
uint32_t ctx_id = FF_VOLUMES;
|
||||
@@ -208,7 +210,7 @@ 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) {
|
||||
if (data_partition->readonly || mount_config->read_only) {
|
||||
esp_vfs_set_readonly_flag(base_path);
|
||||
}
|
||||
|
||||
@@ -350,6 +352,9 @@ esp_err_t esp_vfs_fat_spiflash_mount_ro(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(!(mount_config->read_only && mount_config->format_if_mount_failed),
|
||||
ESP_ERR_INVALID_ARG, TAG, "read_only and format_if_mount_failed are mutually exclusive");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
@@ -388,7 +393,7 @@ esp_err_t esp_vfs_fat_spiflash_mount_ro(const char* base_path,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (data_partition->readonly) {
|
||||
if (data_partition->readonly || mount_config->read_only) {
|
||||
esp_vfs_set_readonly_flag(base_path);
|
||||
}
|
||||
|
||||
|
||||
@@ -512,6 +512,19 @@ void esp_vfs_dump_fds(FILE *fp);
|
||||
*/
|
||||
void esp_vfs_dump_registered_paths(FILE *fp);
|
||||
|
||||
/**
|
||||
* @brief Set the read-only flag for a registered VFS entry
|
||||
*
|
||||
* After this call, all write operations (open for write, mkdir, unlink,
|
||||
* rename, etc.) on the given path will fail with errno set to EROFS.
|
||||
*
|
||||
* @param base_path Path prefix of the already-registered VFS entry
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if no VFS entry matches base_path
|
||||
*/
|
||||
esp_err_t esp_vfs_set_readonly_flag(const char *base_path);
|
||||
|
||||
#if !defined(__DOXYGEN__)
|
||||
#pragma pop_macro("deprecated")
|
||||
#endif
|
||||
|
||||
@@ -34,6 +34,46 @@ The general mounting workflow is:
|
||||
#. Use standard file APIs (``stdio.h`` or POSIX) on paths under the mount path.
|
||||
#. Close open files and call the matching unmount helper.
|
||||
|
||||
.. _fatfs-read-only-mount:
|
||||
|
||||
Read-Only Mount
|
||||
---------------
|
||||
|
||||
Any FAT filesystem can be mounted in read-only mode by setting ``esp_vfs_fat_mount_config_t.read_only`` to ``true``. When this flag is set, the VFS layer rejects all write operations (``open`` for write, ``mkdir``, ``unlink``, ``rename``, ``truncate``, etc.) with ``errno`` set to ``EROFS``, regardless of the underlying storage type.
|
||||
|
||||
This is useful for protecting filesystem integrity when write access is not needed — for example, when reading configuration or asset data from a wear-levelled SPI flash partition:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_vfs_fat_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = false,
|
||||
.max_files = 5,
|
||||
.read_only = true,
|
||||
};
|
||||
wl_handle_t wl_handle;
|
||||
esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl("/data", "storage",
|
||||
&mount_config, &wl_handle);
|
||||
|
||||
// Reading works normally
|
||||
FILE *f = fopen("/data/config.json", "r"); // OK
|
||||
|
||||
// All write operations are rejected at the VFS level
|
||||
FILE *w = fopen("/data/new.txt", "w"); // Returns NULL, errno == EROFS
|
||||
mkdir("/data/subdir", 0755); // Returns -1, errno == EROFS
|
||||
|
||||
The ``read_only`` flag works with all mount helpers:
|
||||
|
||||
- :cpp:func:`esp_vfs_fat_spiflash_mount_rw_wl` — wear-levelled SPI flash (read-only with WL metadata preserved).
|
||||
- :cpp:func:`esp_vfs_fat_spiflash_mount_ro` — raw read-only SPI flash.
|
||||
- :cpp:func:`esp_vfs_fat_sdmmc_mount` / :cpp:func:`esp_vfs_fat_sdspi_mount` — SD cards.
|
||||
- :cpp:func:`esp_vfs_fat_bdl_mount` — Block Device Layer.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``read_only`` flag is enforced by the VFS layer, not the FatFs library itself. It prevents POSIX/stdio write operations from reaching the filesystem, but does not modify the on-disk filesystem state or metadata. The underlying storage driver (e.g., wear leveling) remains initialized normally.
|
||||
|
||||
Mounting with ``read_only = true`` is distinct from :cpp:func:`esp_vfs_fat_spiflash_mount_ro`, which skips wear leveling entirely and accesses the raw flash partition directly. Use ``read_only = true`` with :cpp:func:`esp_vfs_fat_spiflash_mount_rw_wl` when you need the wear-leveling layer active (e.g., to ensure correct interpretation of a WL-formatted partition) but do not want the application to modify the filesystem.
|
||||
|
||||
.. _fatfs-configuration-options:
|
||||
|
||||
Configuration Options
|
||||
|
||||
@@ -34,6 +34,46 @@ FatFs 组件提供了便利的封装函数,用于通过 VFS 层挂载文件系
|
||||
#. 在挂载路径下的路径上使用标准文件 API(``stdio.h`` 或 POSIX)。
|
||||
#. 关闭打开的文件,并调用相应的卸载辅助函数。
|
||||
|
||||
.. _fatfs-read-only-mount:
|
||||
|
||||
只读挂载
|
||||
--------
|
||||
|
||||
通过将 ``esp_vfs_fat_mount_config_t.read_only`` 设置为 ``true``,可以将任何 FAT 文件系统以只读模式挂载。设置此标志后,VFS 层将拒绝所有写操作,例如 ``open`` (写模式)、``mkdir``、``unlink``、``rename``、``truncate`` 等,并将 ``errno`` 设置为 ``EROFS``,无论底层存储类型如何。
|
||||
|
||||
当不需要写入访问时,此功能可用于保护文件系统完整性。例如,从带有磨损均衡功能的 SPI flash 分区读取配置或资源数据:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_vfs_fat_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = false,
|
||||
.max_files = 5,
|
||||
.read_only = true,
|
||||
};
|
||||
wl_handle_t wl_handle;
|
||||
esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl("/data", "storage",
|
||||
&mount_config, &wl_handle);
|
||||
|
||||
// 读取操作正常
|
||||
FILE *f = fopen("/data/config.json", "r"); // 正常
|
||||
|
||||
// 所有写操作在 VFS 层被拒绝
|
||||
FILE *w = fopen("/data/new.txt", "w"); // 返回 NULL,errno == EROFS
|
||||
mkdir("/data/subdir", 0755); // 返回 -1,errno == EROFS
|
||||
|
||||
``read_only`` 标志适用于所有挂载辅助函数:
|
||||
|
||||
- :cpp:func:`esp_vfs_fat_spiflash_mount_rw_wl` — 带有磨损均衡功能的 SPI flash(只读但保留 WL 元数据)。
|
||||
- :cpp:func:`esp_vfs_fat_spiflash_mount_ro` — 原始只读 SPI flash。
|
||||
- :cpp:func:`esp_vfs_fat_sdmmc_mount` / :cpp:func:`esp_vfs_fat_sdspi_mount` — SD 卡。
|
||||
- :cpp:func:`esp_vfs_fat_bdl_mount` — 块设备层。
|
||||
|
||||
.. note::
|
||||
|
||||
``read_only`` 标志由 VFS 层强制执行,而非 FatFs 库本身。它阻止 POSIX/stdio 写操作到达文件系统,但不会修改磁盘上的文件系统状态或元数据。底层存储驱动(如磨损均衡)仍正常初始化。
|
||||
|
||||
上述通过 ``read_only`` 标志实现的只读挂载方式与 :cpp:func:`esp_vfs_fat_spiflash_mount_ro` 不同,后者完全跳过磨损均衡并直接访问原始 flash 分区。当需要磨损均衡层处于活动状态(例如确保正确解释 WL 格式的分区)但不希望应用程序修改文件系统时,请在调用 :cpp:func:`esp_vfs_fat_spiflash_mount_rw_wl` 时设置 ``read_only = true``。
|
||||
|
||||
.. _fatfs-configuration-options:
|
||||
|
||||
配置选项
|
||||
|
||||
Reference in New Issue
Block a user