mirror of
https://github.com/espressif/esp-idf.git
synced 2026-01-04 17:37:05 +00:00
Merge branch 'feat/vfs_linux' into 'master'
feat(vfs): Add support for linux target in VFS See merge request espressif/esp-idf!39524
This commit is contained in:
@@ -2,17 +2,19 @@ idf_build_get_property(target IDF_TARGET)
|
||||
|
||||
set(srcs "commands.c"
|
||||
"esp_console_common.c"
|
||||
"esp_console_repl_internal.c"
|
||||
"split_argv.c"
|
||||
"linenoise/linenoise.c")
|
||||
|
||||
set(requires vfs)
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
list(APPEND srcs "esp_console_repl_linux.c")
|
||||
list(APPEND srcs "esp_console_repl_linux.c" "esp_console_repl_internal.c")
|
||||
else()
|
||||
list(APPEND srcs "esp_console_repl_chip.c")
|
||||
list(APPEND requires esp_stdio)
|
||||
if(CONFIG_VFS_SUPPORT_SELECT)
|
||||
list(APPEND srcs "esp_console_repl_internal.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(argtable_srcs argtable3/arg_cmd.c
|
||||
|
||||
@@ -106,7 +106,7 @@ esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_repl_co
|
||||
linenoiseSetCompletionCallback(&esp_console_get_completion);
|
||||
linenoiseSetHintsCallback((linenoiseHintsCallback *)&esp_console_get_hint);
|
||||
|
||||
#if CONFIG_VFS_SUPPORT_SELECT
|
||||
#if CONFIG_VFS_SUPPORT_SELECT || CONFIG_IDF_TARGET_LINUX
|
||||
ret = esp_console_internal_set_event_fd(repl_com);
|
||||
if (ret != ESP_OK) {
|
||||
goto _exit;
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#include "esp_vfs_eventfd.h"
|
||||
|
||||
#if CONFIG_VFS_SUPPORT_SELECT
|
||||
|
||||
static const char *TAG = "console.repl.internal";
|
||||
|
||||
static int s_interrupt_reading_fd = -1;
|
||||
@@ -38,7 +36,7 @@ static ssize_t read_bytes(int fd, void *buf, size_t max_bytes)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (FD_ISSET(s_interrupt_reading_fd, &read_fds)) {
|
||||
if (s_interrupt_reading_fd != -1 && FD_ISSET(s_interrupt_reading_fd, &read_fds)) {
|
||||
/* read termination request happened, return */
|
||||
int buf[sizeof(s_interrupt_reading_signal)];
|
||||
nread = read(s_interrupt_reading_fd, buf, sizeof(s_interrupt_reading_signal));
|
||||
@@ -147,7 +145,6 @@ esp_err_t esp_console_common_deinit(esp_console_repl_com_t *repl_com)
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
|
||||
/* DO NOT move this function out of this file. All other definitions in this
|
||||
* file are strong definition of weak functions.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -74,6 +74,12 @@ static esp_err_t esp_console_repl_linux_delete(esp_console_repl_t *repl)
|
||||
goto _exit;
|
||||
}
|
||||
repl_com->state = CONSOLE_REPL_STATE_DEINIT;
|
||||
|
||||
ret = esp_console_common_deinit(&linux_repl->repl_com);
|
||||
if (ret != ESP_OK) {
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
esp_console_deinit();
|
||||
|
||||
free(linux_repl);
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_VFS_SUPPORT_IO=n
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_VFS_SUPPORT_IO=n
|
||||
|
||||
# enable sorted commands in the help command
|
||||
CONFIG_CONSOLE_SORTED_HELP=y
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_VFS_SUPPORT_IO=n
|
||||
@@ -56,6 +56,7 @@ else()
|
||||
if(${target} STREQUAL "linux")
|
||||
# set BUILD_DIR because partition_linux.c uses a file created in the build directory
|
||||
target_compile_definitions(${COMPONENT_LIB} PRIVATE "BUILD_DIR=\"${build_dir}\"")
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE dl)
|
||||
endif()
|
||||
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
|
||||
|
||||
@@ -21,9 +21,22 @@
|
||||
#include "esp_private/partition_linux.h"
|
||||
#include "esp_log.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
ESP_LOG_ATTR_TAG(TAG, "linux_spiflash");
|
||||
|
||||
typedef int (*ftruncate_func_t)(int fd, off_t length);
|
||||
static ftruncate_func_t __orig_ftruncate = NULL;
|
||||
|
||||
void __attribute__((constructor)) init_orig_funcs(void)
|
||||
{
|
||||
__orig_ftruncate = (ftruncate_func_t) dlsym(RTLD_NEXT, "ftruncate");
|
||||
if (__orig_ftruncate == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to load original ftruncate function: %s", dlerror());
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void *s_spiflash_mem_file_buf = NULL;
|
||||
static int s_spiflash_mem_file_fd = -1;
|
||||
static const esp_partition_mmap_handle_t s_default_partition_mmap_handle = 0;
|
||||
@@ -268,7 +281,7 @@ esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start)
|
||||
|
||||
do {
|
||||
// resize file
|
||||
if (ftruncate(s_spiflash_mem_file_fd, s_esp_partition_file_mmap_ctrl_act.flash_file_size) != 0) {
|
||||
if (__orig_ftruncate && __orig_ftruncate(s_spiflash_mem_file_fd, s_esp_partition_file_mmap_ctrl_act.flash_file_size) != 0) {
|
||||
ESP_LOGE(TAG, "Failed to set size of SPI FLASH memory emulation file %s: %s", s_esp_partition_file_mmap_ctrl_act.flash_file_name, strerror(errno));
|
||||
ret = ESP_ERR_INVALID_SIZE;
|
||||
break;
|
||||
|
||||
@@ -10,9 +10,12 @@ 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
|
||||
# for linux, we do not have support for sdmmc, for real targets, add respective sources
|
||||
if(${target} STREQUAL "linux")
|
||||
list(APPEND srcs "port/linux/ffsystem.c")
|
||||
list(APPEND srcs "port/linux/ffsystem.c"
|
||||
"vfs/vfs_fat.c")
|
||||
list(APPEND include_dirs "vfs")
|
||||
list(APPEND priv_requires "vfs" "linux")
|
||||
else()
|
||||
list(APPEND srcs "port/freertos/ffsystem.c"
|
||||
"diskio/diskio_sdmmc.c"
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
// Copyright 2017-2019 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: 2017-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -19,9 +11,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT;
|
||||
typedef unsigned char BYTE;
|
||||
typedef uint32_t DWORD;
|
||||
#include "ff.h"
|
||||
|
||||
#define FF_DRV_NOT_USED 0xFF
|
||||
|
||||
@@ -36,8 +26,8 @@ typedef uint32_t DWORD;
|
||||
typedef struct {
|
||||
DSTATUS (*init) (unsigned char pdrv); /*!< disk initialization function */
|
||||
DSTATUS (*status) (unsigned char pdrv); /*!< disk status check function */
|
||||
DRESULT (*read) (unsigned char pdrv, unsigned char* buff, uint32_t sector, unsigned count); /*!< sector read function */
|
||||
DRESULT (*write) (unsigned char pdrv, const unsigned char* buff, uint32_t sector, unsigned count); /*!< sector write function */
|
||||
DRESULT (*read) (unsigned char pdrv, unsigned char* buff, uint32_t sector, UINT count); /*!< sector read function */
|
||||
DRESULT (*write) (unsigned char pdrv, const unsigned char* buff, uint32_t sector, UINT count); /*!< sector write function */
|
||||
DRESULT (*ioctl) (unsigned char pdrv, unsigned char cmd, void* buff); /*!< function to get info about disk and do some misc operations */
|
||||
} ff_diskio_impl_t;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
idf_component_register(SRCS "test_fatfs.cpp"
|
||||
REQUIRES fatfs
|
||||
idf_component_register(SRCS "test_fatfs.cpp" "test_fatfs_vfs.cpp"
|
||||
REQUIRES fatfs vfs
|
||||
WHOLE_ARCHIVE
|
||||
)
|
||||
|
||||
|
||||
579
components/fatfs/host_test/main/test_fatfs_vfs.cpp
Normal file
579
components/fatfs/host_test/main/test_fatfs_vfs.cpp
Normal file
@@ -0,0 +1,579 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "ff.h"
|
||||
#include "esp_partition.h"
|
||||
#include "wear_levelling.h"
|
||||
#include "diskio_impl.h"
|
||||
#include "diskio_wl.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "esp_vfs.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
static const char* TAG = "linux_vfs";
|
||||
|
||||
static BYTE pdrv;
|
||||
static wl_handle_t wl_handle;
|
||||
static char drv[3];
|
||||
|
||||
static void test_setup(void)
|
||||
{
|
||||
FATFS *fs;
|
||||
esp_err_t ret = ESP_OK;
|
||||
FRESULT fr_result;
|
||||
|
||||
esp_err_t esp_result;
|
||||
|
||||
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, "storage");
|
||||
REQUIRE(partition != NULL);
|
||||
|
||||
// Mount wear-levelled partition
|
||||
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);
|
||||
|
||||
char temp_drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
strncpy(drv, temp_drv, 3);
|
||||
|
||||
// Create FAT volume on the entire disk
|
||||
LBA_t part_list[] = {100, 0, 0, 0};
|
||||
BYTE workbuf[FF_MAX_SS];
|
||||
const size_t workbuf_size = sizeof(workbuf);
|
||||
|
||||
fr_result = f_fdisk(pdrv, part_list, workbuf);
|
||||
REQUIRE(fr_result == FR_OK);
|
||||
const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, 0};
|
||||
fr_result = f_mkfs("", &opt, workbuf, workbuf_size); // Use default volume
|
||||
REQUIRE(fr_result == FR_OK);
|
||||
|
||||
esp_vfs_fat_conf_t conf = {
|
||||
.base_path = "/linux",
|
||||
.fat_drive = drv,
|
||||
.max_files = 5,
|
||||
};
|
||||
ret = esp_vfs_fat_register_cfg(&conf, &fs);
|
||||
if (ret == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (ret != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register_cfg failed 0x(%x)", ret);
|
||||
}
|
||||
REQUIRE(ret == ESP_OK);
|
||||
|
||||
//Mount the volume
|
||||
fr_result = f_mount(fs, "", 0);
|
||||
REQUIRE(fr_result == FR_OK);
|
||||
}
|
||||
|
||||
static void test_teardown(void)
|
||||
{
|
||||
esp_err_t esp_result;
|
||||
FRESULT fr_result;
|
||||
// Unmount default volume
|
||||
fr_result = f_mount(0, drv, 0);
|
||||
REQUIRE(fr_result == FR_OK);
|
||||
|
||||
ff_diskio_unregister(pdrv);
|
||||
ff_diskio_clear_pdrv_wl(wl_handle);
|
||||
esp_result = wl_unmount(wl_handle);
|
||||
esp_vfs_fat_unregister_path("/linux");
|
||||
REQUIRE(esp_result == ESP_OK);
|
||||
}
|
||||
|
||||
static void test_fatfs_create_file_with_text(const char* name, const char* text)
|
||||
{
|
||||
int fd = -1;
|
||||
fd = open(name, O_CREAT|O_RDWR, 0777);
|
||||
REQUIRE(fd != -1);
|
||||
|
||||
ssize_t sz = write(fd, text, strlen(text));
|
||||
REQUIRE(sz == strlen(text));
|
||||
|
||||
REQUIRE(0 == close(fd));
|
||||
}
|
||||
|
||||
static void test_open_read_write_file()
|
||||
{
|
||||
constexpr const char *test_str = "0123456789\n";
|
||||
const char *filename = "/linux/test.txt";
|
||||
test_fatfs_create_file_with_text(filename, test_str);
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
|
||||
char data[strlen(test_str)];
|
||||
size_t sz = read(fd, data, strlen(test_str));
|
||||
REQUIRE(sz == strlen(test_str));
|
||||
REQUIRE(0 == strncmp(data, test_str, strlen(test_str)));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST_CASE("open file, write and read back data via VFS", "[fatfs]")
|
||||
{
|
||||
test_setup();
|
||||
test_open_read_write_file();
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_lseek()
|
||||
{
|
||||
const char *test_str = "0123456789\n";
|
||||
const char *filename = "/linux/lseek.txt";
|
||||
test_fatfs_create_file_with_text(filename, test_str);
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
|
||||
off_t off = lseek(fd, 6, SEEK_CUR);
|
||||
REQUIRE(off == 6);
|
||||
off = lseek(fd, 3, SEEK_SET);
|
||||
REQUIRE(off == 3);
|
||||
off = lseek(fd, -9, SEEK_END);
|
||||
REQUIRE(off == 2);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST_CASE("lseek via VFS", "[fatfs]")
|
||||
{
|
||||
test_setup();
|
||||
test_lseek();
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_pread()
|
||||
{
|
||||
char buf[32] = { 0 };
|
||||
const char *test_str = "0123456789\n";
|
||||
const char *filename = "/linux/pread.txt";
|
||||
test_fatfs_create_file_with_text(filename, test_str);
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
|
||||
int r = pread(fd, buf, sizeof(buf), 0); // it is a regular read() with offset==0
|
||||
REQUIRE(0 == strcmp(test_str, buf));
|
||||
REQUIRE(strlen(test_str) == r);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
r = pread(fd, buf, sizeof(buf), 1); // offset==1
|
||||
REQUIRE(0 == strcmp(test_str + 1, buf));
|
||||
REQUIRE(strlen(test_str) - 1 == r);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
r = pread(fd, buf, sizeof(buf), 5); // offset==5
|
||||
REQUIRE(0 == strcmp(test_str + 5, buf));
|
||||
REQUIRE(strlen(test_str) - 5 == r);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST_CASE("can read file with pread", "[fatfs]")
|
||||
{
|
||||
test_setup();
|
||||
test_pread();
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_pwrite()
|
||||
{
|
||||
const char *test_str = "0123456789\n";
|
||||
const char *msg = "ESP";
|
||||
const char *filename = "/linux/pwrite.txt";
|
||||
test_fatfs_create_file_with_text(filename, test_str);
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
|
||||
const off_t current_pos = lseek(fd, 0, SEEK_END); // O_APPEND is not the same - jumps to the end only before write()
|
||||
|
||||
const int r = pwrite(fd, msg, strlen(msg), 0);
|
||||
REQUIRE(strlen(msg) == r);
|
||||
|
||||
REQUIRE(current_pos == lseek(fd, 0, SEEK_CUR)); // pwrite should not move the pointer
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST_CASE("pwrite() works well", "[fatfs]")
|
||||
{
|
||||
test_setup();
|
||||
test_pwrite();
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_link_unlink_rename()
|
||||
{
|
||||
const char *filename_prefix = "/linux/link";
|
||||
|
||||
char name_copy[64];
|
||||
char name_dst[64];
|
||||
char name_src[64];
|
||||
snprintf(name_copy, sizeof(name_copy), "%s_cpy.txt", filename_prefix);
|
||||
snprintf(name_dst, sizeof(name_dst), "%s_dst.txt", filename_prefix);
|
||||
snprintf(name_src, sizeof(name_src), "%s_src.txt", filename_prefix);
|
||||
|
||||
unlink(name_copy);
|
||||
unlink(name_dst);
|
||||
unlink(name_src);
|
||||
|
||||
const char *test_str = "0123456789\n";
|
||||
test_fatfs_create_file_with_text(name_src, test_str);
|
||||
|
||||
REQUIRE(0 == link(name_src, name_copy));
|
||||
REQUIRE(-1 != open(name_copy, O_RDONLY));
|
||||
|
||||
test_fatfs_create_file_with_text(name_copy, test_str);
|
||||
rename(name_copy, name_dst);
|
||||
REQUIRE(-1 == open(name_copy, O_RDONLY));
|
||||
REQUIRE(-1 != open(name_dst, O_RDONLY));
|
||||
|
||||
unlink(name_dst);
|
||||
unlink(name_src);
|
||||
|
||||
REQUIRE(-1 == open(name_src, O_RDONLY));
|
||||
REQUIRE(-1 == open(name_dst, O_RDONLY));
|
||||
}
|
||||
|
||||
TEST_CASE("link copies a file, unlink removes file, rename moves a file", "[fatfs]")
|
||||
{
|
||||
test_setup();
|
||||
test_link_unlink_rename();
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_opendir_closedir_readdir()
|
||||
{
|
||||
const char *dirname = "/linux/dir";
|
||||
|
||||
REQUIRE(0 == mkdir(dirname, 0755));
|
||||
|
||||
char name_dir_file[64];
|
||||
const char * file_name = "test_opd.txt";
|
||||
snprintf(name_dir_file, sizeof(name_dir_file), "%s/%s", dirname, file_name);
|
||||
unlink(name_dir_file);
|
||||
test_fatfs_create_file_with_text(name_dir_file, "test_opendir\n");
|
||||
DIR* dir = opendir(dirname);
|
||||
REQUIRE(dir != NULL);
|
||||
bool found = false;
|
||||
while (true) {
|
||||
struct dirent* de = readdir(dir);
|
||||
if (!de) {
|
||||
break;
|
||||
}
|
||||
if (strcasecmp(de->d_name, file_name) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
REQUIRE(found == true);
|
||||
REQUIRE(0 == closedir(dir));
|
||||
unlink(name_dir_file);
|
||||
}
|
||||
|
||||
void test_fatfs_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);
|
||||
|
||||
REQUIRE(0 == mkdir(dir_prefix, 0755));
|
||||
test_fatfs_create_file_with_text(name_dir_file1, "1\n");
|
||||
test_fatfs_create_file_with_text(name_dir_file2, "2\n");
|
||||
test_fatfs_create_file_with_text(name_dir_file3, "\01\02\03");
|
||||
REQUIRE(0 == mkdir(name_dir_inner, 0755));
|
||||
test_fatfs_create_file_with_text(name_dir_inner_file, "3\n");
|
||||
|
||||
DIR* dir = opendir(dir_prefix);
|
||||
REQUIRE(dir != NULL);
|
||||
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) {
|
||||
REQUIRE(de->d_type == DT_REG);
|
||||
names[count] = "1.txt";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "2.txt") == 0) {
|
||||
REQUIRE(de->d_type == DT_REG);
|
||||
names[count] = "2.txt";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "inner") == 0) {
|
||||
REQUIRE(de->d_type == DT_DIR);
|
||||
names[count] = "inner";
|
||||
++count;
|
||||
} else if (strcasecmp(de->d_name, "boo.bin") == 0) {
|
||||
REQUIRE(de->d_type == DT_REG);
|
||||
names[count] = "boo.bin";
|
||||
++count;
|
||||
} else {
|
||||
printf("unexpected directory entry");
|
||||
}
|
||||
}
|
||||
REQUIRE(count == 4);
|
||||
|
||||
rewinddir(dir);
|
||||
struct dirent* de = readdir(dir);
|
||||
REQUIRE(de != NULL);
|
||||
REQUIRE(0 == strcasecmp(de->d_name, names[0]));
|
||||
seekdir(dir, 3);
|
||||
de = readdir(dir);
|
||||
REQUIRE(de != NULL);
|
||||
REQUIRE(0 == strcasecmp(de->d_name, names[3]));
|
||||
seekdir(dir, 1);
|
||||
de = readdir(dir);
|
||||
REQUIRE(de != NULL);
|
||||
REQUIRE(0 == strcasecmp(de->d_name, names[1]));
|
||||
seekdir(dir, 2);
|
||||
de = readdir(dir);
|
||||
REQUIRE(de != NULL);
|
||||
REQUIRE(0 == strcasecmp(de->d_name, names[2]));
|
||||
|
||||
REQUIRE(0 == closedir(dir));
|
||||
}
|
||||
|
||||
TEST_CASE("opendir, readdir, rewinddir, seekdir work as expected", "[fatfs]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_opendir_readdir_rewinddir("/linux/dir");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("can opendir, closedir and readdir of FS", "[fatfs]")
|
||||
{
|
||||
test_setup();
|
||||
test_opendir_closedir_readdir();
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_truncate()
|
||||
{
|
||||
const char *test_str = "0123456789\n";
|
||||
const char *filename = "/linux/truncate.txt";
|
||||
test_fatfs_create_file_with_text(filename, test_str);
|
||||
|
||||
struct stat st;
|
||||
size_t size;
|
||||
REQUIRE(0 == stat(filename, &st));
|
||||
size = st.st_size;
|
||||
REQUIRE(strlen(test_str) == size);
|
||||
|
||||
size_t trunc_add = 2;
|
||||
off_t new_size = strlen(test_str) + trunc_add;
|
||||
REQUIRE(0 == truncate(filename, new_size));
|
||||
stat(filename, &st);
|
||||
size = st.st_size;
|
||||
REQUIRE(new_size == size);
|
||||
|
||||
const char truncated_1[] = "01234";
|
||||
off_t truncated_len = strlen(truncated_1);
|
||||
|
||||
REQUIRE(0 == truncate(filename, truncated_len));
|
||||
|
||||
stat(filename, &st);
|
||||
size = st.st_size;
|
||||
REQUIRE(strlen(truncated_1) == size);
|
||||
}
|
||||
|
||||
TEST_CASE("can truncate", "[fatfs]")
|
||||
{
|
||||
test_setup();
|
||||
test_truncate();
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_ftruncate()
|
||||
{
|
||||
const char *test_str = "0123456789\n";
|
||||
const char *filename = "/linux/ftrunc.txt";
|
||||
test_fatfs_create_file_with_text(filename, test_str);
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
struct stat st;
|
||||
size_t size;
|
||||
fstat(fd, &st);
|
||||
size = st.st_size;
|
||||
REQUIRE(strlen(test_str) == size);
|
||||
close(fd);
|
||||
|
||||
size_t trunc_add = 2;
|
||||
off_t new_size = strlen(test_str) + trunc_add;
|
||||
fd = open(filename, O_RDWR);
|
||||
REQUIRE(0 == ftruncate(fd, new_size));
|
||||
fstat(fd, &st);
|
||||
size = st.st_size;
|
||||
REQUIRE(new_size == size);
|
||||
close(fd);
|
||||
|
||||
const char truncated_1[] = "01234";
|
||||
off_t truncated_len = strlen(truncated_1);
|
||||
|
||||
fd = open(filename, O_RDWR);
|
||||
REQUIRE(0 == ftruncate(fd, truncated_len));
|
||||
fstat(fd, &st);
|
||||
size = st.st_size;
|
||||
REQUIRE(strlen(truncated_1) == size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST_CASE("can ftruncate", "[fatfs]")
|
||||
{
|
||||
test_setup();
|
||||
test_ftruncate();
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
void test_fatfs_utime(const char* filename, const char* root_dir)
|
||||
{
|
||||
struct stat achieved_stat;
|
||||
struct tm desired_tm;
|
||||
struct utimbuf desired_time = {
|
||||
.actime = 0, // access time is not supported
|
||||
.modtime = 0,
|
||||
};
|
||||
time_t false_now = 0;
|
||||
memset(&desired_tm, 0, sizeof(struct tm));
|
||||
|
||||
// Setting up a false actual time - used when the file is created and for modification with the current time
|
||||
desired_tm.tm_mon = 10 - 1;
|
||||
desired_tm.tm_mday = 31;
|
||||
desired_tm.tm_year = 2018 - 1900;
|
||||
desired_tm.tm_hour = 10;
|
||||
desired_tm.tm_min = 35;
|
||||
desired_tm.tm_sec = 23;
|
||||
|
||||
false_now = mktime(&desired_tm);
|
||||
|
||||
struct timeval now = { .tv_sec = false_now, .tv_usec = 0};
|
||||
settimeofday(&now, NULL);
|
||||
|
||||
test_fatfs_create_file_with_text(filename, "");
|
||||
|
||||
// 00:00:00. January 1st, 1980 - FATFS cannot handle earlier dates
|
||||
desired_tm.tm_mon = 1 - 1;
|
||||
desired_tm.tm_mday = 1;
|
||||
desired_tm.tm_year = 1980 - 1900;
|
||||
desired_tm.tm_hour = 0;
|
||||
desired_tm.tm_min = 0;
|
||||
desired_tm.tm_sec = 0;
|
||||
printf("Testing mod. time: %s", asctime(&desired_tm));
|
||||
desired_time.modtime = mktime(&desired_tm);
|
||||
REQUIRE(0 == utime(filename, &desired_time));
|
||||
REQUIRE(0 == stat(filename, &achieved_stat));
|
||||
REQUIRE(desired_time.modtime == achieved_stat.st_mtime);
|
||||
|
||||
// current time
|
||||
REQUIRE(0 == utime(filename, NULL));
|
||||
REQUIRE(0 == stat(filename, &achieved_stat));
|
||||
printf("Mod. time changed to (false actual time): %s", ctime(&achieved_stat.st_mtime));
|
||||
REQUIRE(desired_time.modtime != achieved_stat.st_mtime);
|
||||
REQUIRE(false_now - achieved_stat.st_mtime <= 2); // two seconds of tolerance are given
|
||||
|
||||
// 23:59:08. December 31st, 2037
|
||||
desired_tm.tm_mon = 12 - 1;
|
||||
desired_tm.tm_mday = 31;
|
||||
desired_tm.tm_year = 2037 - 1900;
|
||||
desired_tm.tm_hour = 23;
|
||||
desired_tm.tm_min = 59;
|
||||
desired_tm.tm_sec = 8;
|
||||
printf("Testing mod. time: %s", asctime(&desired_tm));
|
||||
desired_time.modtime = mktime(&desired_tm);
|
||||
REQUIRE(0 == utime(filename, &desired_time));
|
||||
REQUIRE(0 == stat(filename, &achieved_stat));
|
||||
REQUIRE(desired_time.modtime == achieved_stat.st_mtime);
|
||||
|
||||
//WARNING: it has the Unix Millennium bug (Y2K38)
|
||||
|
||||
// 00:00:00. January 1st, 1970 - FATFS cannot handle years before 1980
|
||||
desired_tm.tm_mon = 1 - 1;
|
||||
desired_tm.tm_mday = 1;
|
||||
desired_tm.tm_year = 1970 - 1900;
|
||||
desired_tm.tm_hour = 0;
|
||||
desired_tm.tm_min = 0;
|
||||
desired_tm.tm_sec = 0;
|
||||
printf("Testing mod. time: %s", asctime(&desired_tm));
|
||||
desired_time.modtime = mktime(&desired_tm);
|
||||
REQUIRE(-1 == utime(filename, &desired_time));
|
||||
REQUIRE(EINVAL == errno);
|
||||
}
|
||||
|
||||
TEST_CASE("utime sets modification time", "[fatfs]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_utime("/linux/utime.txt", "/linux");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_fstat()
|
||||
{
|
||||
const char *test_str = "0123456789\n";
|
||||
const char *filename = "/linux/pwrite.txt";
|
||||
test_fatfs_create_file_with_text(filename, test_str);
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
|
||||
struct stat st;
|
||||
REQUIRE(0 == fstat(fd, &st));
|
||||
|
||||
REQUIRE((st.st_mode & S_IFREG) == S_IFREG);
|
||||
REQUIRE(st.st_size == strlen(test_str));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
TEST_CASE("fstat returns correct values", "[fatfs]")
|
||||
{
|
||||
test_setup();
|
||||
test_fstat();
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
static void test_mkdir_rmdir()
|
||||
{
|
||||
const char *dir_prefix = "/linux/dir";
|
||||
|
||||
REQUIRE(0 == mkdir(dir_prefix, 0777));
|
||||
|
||||
struct stat st;
|
||||
REQUIRE(0 == stat(dir_prefix, &st));
|
||||
REQUIRE((st.st_mode & S_IFDIR) == S_IFDIR);
|
||||
REQUIRE((st.st_mode & S_IFREG) != S_IFREG);
|
||||
REQUIRE(0 == rmdir(dir_prefix));
|
||||
REQUIRE(-1 == stat(dir_prefix, &st));
|
||||
}
|
||||
|
||||
TEST_CASE("can create and remove directories", "[fatfs]")
|
||||
{
|
||||
test_setup();
|
||||
test_mkdir_rmdir();
|
||||
test_teardown();
|
||||
}
|
||||
@@ -3,5 +3,5 @@
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, fat, , 32k,
|
||||
storage, data, fat, , 1M,
|
||||
storage2, data, fat, , 32k,
|
||||
|
||||
|
@@ -47,7 +47,7 @@ typedef unsigned __int64 QWORD;
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
|
||||
#define FF_INTDEF 2
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef size_t UINT; /* int must be 16-bit or 32-bit or 64 bit (Linux) */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef uint16_t WORD; /* 16-bit unsigned */
|
||||
typedef uint32_t DWORD; /* 32-bit unsigned */
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
/* Some memory allocation functions are declared here in addition to ff.h, so that
|
||||
they can be used also by external code when LFN feature is disabled.
|
||||
*/
|
||||
void* ff_memalloc (unsigned msize);
|
||||
void* ff_memalloc (size_t msize);
|
||||
void ff_memfree(void*);
|
||||
|
||||
/* Redefine names of disk IO functions to prevent name collisions */
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
#ifndef CONFIG_IDF_TARGET_LINUX
|
||||
#include "sd_protocol_types.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
#endif
|
||||
#include "ff.h"
|
||||
#include "wear_levelling.h"
|
||||
|
||||
@@ -145,6 +147,7 @@ typedef struct {
|
||||
// Compatibility definition
|
||||
typedef esp_vfs_fat_mount_config_t esp_vfs_fat_sdmmc_mount_config_t;
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_LINUX
|
||||
/**
|
||||
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||
*
|
||||
@@ -277,6 +280,7 @@ esp_err_t esp_vfs_fat_sdcard_format_cfg(const char *base_path, sdmmc_card_t *car
|
||||
* - ESP_FAIL: fail to format it, or fail to mount back
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdcard_format(const char *base_path, sdmmc_card_t *card);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Convenience function to initialize FAT filesystem in SPI flash and register it in VFS
|
||||
@@ -410,6 +414,7 @@ esp_err_t esp_vfs_fat_spiflash_unmount_ro(const char* base_path, const char* par
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
@@ -444,6 +449,7 @@ esp_err_t esp_vfs_fat_create_contiguous_file(const char* base_path, const char*
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_test_contiguous_file(const char* base_path, const char* full_path, bool* is_contiguous);
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_LINUX
|
||||
/** @cond */
|
||||
/**
|
||||
* @deprecated Please use `esp_vfs_fat_register_cfg` instead
|
||||
@@ -481,6 +487,7 @@ esp_err_t esp_vfs_fat_rawflash_unmount(const char* base_path, const char* partit
|
||||
__attribute__((deprecated("esp_vfs_fat_rawflash_unmount is deprecated, please use esp_vfs_fat_spiflash_unmount_ro instead")));
|
||||
/** @endcond */
|
||||
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -455,7 +455,7 @@ static ssize_t vfs_fat_write(void* ctx, int fd, const void * data, size_t size)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
unsigned written = 0;
|
||||
UINT written = 0;
|
||||
res = f_write(file, data, size, &written);
|
||||
if (((written == 0) && (size != 0)) && (res == 0)) {
|
||||
errno = ENOSPC;
|
||||
@@ -490,7 +490,7 @@ static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
FIL* file = &fat_ctx->files[fd];
|
||||
unsigned read = 0;
|
||||
UINT read = 0;
|
||||
FRESULT res = f_read(file, dst, size, &read);
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
@@ -518,7 +518,7 @@ static ssize_t vfs_fat_pread(void *ctx, int fd, void *dst, size_t size, off_t of
|
||||
goto pread_release;
|
||||
}
|
||||
|
||||
unsigned read = 0;
|
||||
UINT read = 0;
|
||||
f_res = f_read(file, dst, size, &read);
|
||||
if (f_res == FR_OK) {
|
||||
ret = read;
|
||||
@@ -558,7 +558,7 @@ static ssize_t vfs_fat_pwrite(void *ctx, int fd, const void *src, size_t size, o
|
||||
goto pwrite_release;
|
||||
}
|
||||
|
||||
unsigned wr = 0;
|
||||
UINT wr = 0;
|
||||
f_res = f_write(file, src, size, &wr);
|
||||
if (((wr == 0) && (size != 0)) && (f_res == 0)) {
|
||||
errno = ENOSPC;
|
||||
@@ -714,22 +714,23 @@ static void update_stat_struct(struct stat *st, FILINFO *info)
|
||||
memset(st, 0, sizeof(*st));
|
||||
st->st_size = info->fsize;
|
||||
st->st_mode = get_stat_mode((info->fattrib & AM_DIR) != 0);
|
||||
fat_date_t fdate = { .as_int = info->fdate };
|
||||
fat_time_t ftime = { .as_int = info->ftime };
|
||||
struct tm tm = {
|
||||
.tm_mday = fdate.mday,
|
||||
.tm_mon = fdate.mon - 1, /* unlike tm_mday, tm_mon is zero-based */
|
||||
.tm_year = fdate.year + 80,
|
||||
.tm_sec = ftime.sec * 2,
|
||||
.tm_min = ftime.min,
|
||||
.tm_hour = ftime.hour,
|
||||
/* FAT doesn't keep track if the time was DST or not, ask the C library
|
||||
* to try to figure this out. Note that this may yield incorrect result
|
||||
* in the hour before the DST comes in effect, when the local time can't
|
||||
* be converted to UTC uniquely.
|
||||
*/
|
||||
.tm_isdst = -1
|
||||
};
|
||||
fat_date_t fdate = { 0 };
|
||||
fdate.as_int = info->fdate;
|
||||
fat_time_t ftime = { 0 };
|
||||
ftime.as_int = info->ftime;
|
||||
struct tm tm = { 0 };
|
||||
tm.tm_mday = fdate.mday;
|
||||
tm.tm_mon = fdate.mon - 1; /* unlike tm_mday, tm_mon is zero-based */
|
||||
tm.tm_year = fdate.year + 80;
|
||||
tm.tm_sec = ftime.sec * 2;
|
||||
tm.tm_min = ftime.min;
|
||||
tm.tm_hour = ftime.hour;
|
||||
/* FAT doesn't keep track if the time was DST or not, ask the C library
|
||||
* to try to figure this out. Note that this may yield incorrect result
|
||||
* in the hour before the DST comes in effect, when the local time can't
|
||||
* be converted to UTC uniquely.
|
||||
*/
|
||||
tm.tm_isdst = -1;
|
||||
st->st_mtime = mktime(&tm);
|
||||
st->st_atime = 0;
|
||||
st->st_ctime = 0;
|
||||
@@ -830,8 +831,8 @@ static int vfs_fat_link(void* ctx, const char* n1, const char* n2)
|
||||
|
||||
size_t size_left = f_size(pf1);
|
||||
while (size_left > 0) {
|
||||
size_t will_copy = (size_left < copy_buf_size) ? size_left : copy_buf_size;
|
||||
size_t read;
|
||||
UINT will_copy = (size_left < copy_buf_size) ? size_left : copy_buf_size;
|
||||
UINT read = 0;
|
||||
res = f_read(pf1, buf, will_copy, &read);
|
||||
if (res != FR_OK) {
|
||||
goto close_both;
|
||||
@@ -839,7 +840,7 @@ static int vfs_fat_link(void* ctx, const char* n1, const char* n2)
|
||||
res = FR_DISK_ERR;
|
||||
goto close_both;
|
||||
}
|
||||
size_t written;
|
||||
UINT written = 0;
|
||||
res = f_write(pf2, buf, will_copy, &written);
|
||||
if (res != FR_OK) {
|
||||
goto close_both;
|
||||
@@ -1345,7 +1346,7 @@ static int vfs_fat_utime(void *ctx, const char *path, const struct utimbuf *time
|
||||
FILINFO filinfo_time;
|
||||
|
||||
{
|
||||
struct tm tm_time;
|
||||
struct tm tm_time = { 0 };
|
||||
|
||||
if (times) {
|
||||
localtime_r(×->modtime, &tm_time);
|
||||
@@ -1369,7 +1370,7 @@ static int vfs_fat_utime(void *ctx, const char *path, const struct utimbuf *time
|
||||
fdate.mday = tm_time.tm_mday;
|
||||
fdate.mon = tm_time.tm_mon + 1; // January in fdate.mon is 1, and 0 in tm_time.tm_mon
|
||||
fdate.year = tm_time.tm_year - 80; // tm_time.tm_year=0 is 1900, tm_time.tm_year=0 is 1980
|
||||
ftime.sec = tm_time.tm_sec / 2, // ftime.sec counts seconds by 2
|
||||
ftime.sec = tm_time.tm_sec / 2; // ftime.sec counts seconds by 2
|
||||
ftime.min = tm_time.tm_min;
|
||||
ftime.hour = tm_time.tm_hour;
|
||||
|
||||
@@ -1550,7 +1551,7 @@ esp_err_t esp_vfs_fat_format_drive(uint8_t ldrv, const esp_vfs_fat_mount_config_
|
||||
}
|
||||
|
||||
size_t sector_size = 512; // default value
|
||||
ff_diskio_get_sector_size(ldrv, §or_size);
|
||||
ff_diskio_get_sector_size(ldrv, (UINT *)§or_size);
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(sector_size, mount_config->allocation_unit_size);
|
||||
ESP_LOGW(TAG, "formatting drive, allocation unit size=%d", alloc_unit_size);
|
||||
const MKFS_PARM opt = {(BYTE)FM_ANY, (mount_config->use_one_fat ? 1 : 2), 0, 0, alloc_unit_size};
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "diskio_impl.h"
|
||||
#include "esp_partition.h"
|
||||
#ifndef CONFIG_IDF_TARGET_LINUX
|
||||
#include "sdmmc_cmd.h"
|
||||
#endif
|
||||
#include <sys/param.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -23,15 +25,6 @@ typedef struct vfs_fat_spiflash_ctx_t {
|
||||
vfs_fat_x_ctx_flags_t flags; //Flags
|
||||
} vfs_fat_spiflash_ctx_t;
|
||||
|
||||
typedef struct vfs_fat_sd_ctx_t {
|
||||
BYTE pdrv; //Drive number that is mounted
|
||||
esp_vfs_fat_mount_config_t mount_config; //Mount configuration
|
||||
FATFS *fs; //FAT structure pointer that is registered
|
||||
sdmmc_card_t *card; //Card info
|
||||
char *base_path; //Path where partition is registered
|
||||
vfs_fat_x_ctx_flags_t flags; //Flags
|
||||
} vfs_fat_sd_ctx_t;
|
||||
|
||||
static inline size_t esp_vfs_fat_get_allocation_unit_size(
|
||||
size_t sector_size, size_t requested_size)
|
||||
{
|
||||
@@ -43,6 +36,16 @@ static inline size_t esp_vfs_fat_get_allocation_unit_size(
|
||||
return alloc_unit_size;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_LINUX
|
||||
typedef struct vfs_fat_sd_ctx_t {
|
||||
BYTE pdrv; //Drive number that is mounted
|
||||
esp_vfs_fat_mount_config_t mount_config; //Mount configuration
|
||||
FATFS *fs; //FAT structure pointer that is registered
|
||||
sdmmc_card_t *card; //Card info
|
||||
char *base_path; //Path where partition is registered
|
||||
vfs_fat_x_ctx_flags_t flags; //Flags
|
||||
} vfs_fat_sd_ctx_t;
|
||||
|
||||
vfs_fat_spiflash_ctx_t* get_vfs_fat_spiflash_ctx(wl_handle_t wlhandle);
|
||||
vfs_fat_sd_ctx_t* get_vfs_fat_get_sd_ctx(const sdmmc_card_t *card);
|
||||
|
||||
@@ -130,3 +133,4 @@ esp_err_t esp_vfs_fat_format_drive(uint8_t ldrv, const esp_vfs_fat_mount_config_
|
||||
* - ESP_FAIL if f_fdisk failed
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_partition_drive(uint8_t pdrv, const esp_vfs_fat_drive_divide_arr_t drive_divide);
|
||||
#endif
|
||||
|
||||
@@ -2,8 +2,22 @@ idf_build_get_property(target IDF_TARGET)
|
||||
|
||||
# On Linux, we only support a few features, hence this simple component registration
|
||||
if(${target} STREQUAL "linux")
|
||||
idf_component_register(SRCS "vfs_eventfd_linux.c"
|
||||
INCLUDE_DIRS "include")
|
||||
|
||||
list(APPEND inc include)
|
||||
if(CONFIG_VFS_SUPPORT_IO)
|
||||
list(APPEND inc linux_include)
|
||||
list(APPEND priv_inc private_include)
|
||||
list(APPEND srcs "vfs_linux.c" "vfs.c")
|
||||
endif()
|
||||
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
|
||||
list(APPEND srcs "vfs_eventfd_linux.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
LDFRAGMENTS "linker.lf"
|
||||
INCLUDE_DIRS ${inc}
|
||||
PRIV_INCLUDE_DIRS ${priv_inc})
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
8
components/vfs/host_test/CMakeLists.txt
Normal file
8
components/vfs/host_test/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(COMPONENTS main)
|
||||
# This test app doesn't require FreeRTOS, using mock instead
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/")
|
||||
|
||||
project(vfs_linux_test)
|
||||
10
components/vfs/host_test/main/CMakeLists.txt
Normal file
10
components/vfs/host_test/main/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
set(src "test_vfs.c" "test_vfs_linux_dev.c")
|
||||
|
||||
idf_component_register(SRCS ${src}
|
||||
INCLUDE_DIRS .
|
||||
PRIV_REQUIRES vfs unity
|
||||
WHOLE_ARCHIVE
|
||||
)
|
||||
|
||||
# dlsym is used to dynamically link native Linux platform functions during test execution
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE dl)
|
||||
265
components/vfs/host_test/main/test_vfs.c
Normal file
265
components/vfs/host_test/main/test_vfs.c
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "esp_vfs.h"
|
||||
#include "test_vfs_linux_dev.h"
|
||||
#include "unity.h"
|
||||
#include "unity_fixture.h"
|
||||
|
||||
TEST_GROUP(vfs_linux);
|
||||
|
||||
TEST_SETUP(vfs_linux)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_TEAR_DOWN(vfs_linux)
|
||||
{
|
||||
}
|
||||
|
||||
static void test_create_file_with_text(const char* name, const char* text)
|
||||
{
|
||||
int fd = -1;
|
||||
fd = open(name, O_CREAT|O_RDWR, 0777);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
ssize_t sz = write(fd, text, strlen(text));
|
||||
TEST_ASSERT_EQUAL(strlen(text), sz);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, close(fd));
|
||||
}
|
||||
|
||||
TEST(vfs_linux,test_linux_vfs_open)
|
||||
{
|
||||
linux_vfs_dev_register();
|
||||
|
||||
const char *test_str = "Espressif";
|
||||
const char *filename = "/linux/test.txt";
|
||||
test_create_file_with_text(filename, test_str);
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
int len = strlen(test_str);
|
||||
char buf[10];
|
||||
TEST_ASSERT_EQUAL(len, read(fd, buf, len));
|
||||
TEST_ASSERT_EQUAL(0, strncmp(test_str, buf, len));
|
||||
close(fd);
|
||||
unlink(filename);
|
||||
printf("Inside %s\n",__func__);
|
||||
|
||||
linux_vfs_dev_unregister();
|
||||
}
|
||||
|
||||
static void test_lseek(void)
|
||||
{
|
||||
const char *test_str = "0123456789\n";
|
||||
const char *filename = "/linux/lseek.txt";
|
||||
test_create_file_with_text(filename, test_str);
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
off_t off = lseek(fd, 6, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(6, off);
|
||||
off = lseek(fd, 3, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(3, off);
|
||||
off = lseek(fd, -9, SEEK_END);
|
||||
TEST_ASSERT_EQUAL(2, off);
|
||||
|
||||
close(fd);
|
||||
unlink(filename);
|
||||
}
|
||||
|
||||
TEST(vfs_linux, test_lseek_via_vfs)
|
||||
{
|
||||
printf("Inside %s\n",__func__);
|
||||
linux_vfs_dev_register();
|
||||
printf("Inside %s\n",__func__);
|
||||
test_lseek();
|
||||
linux_vfs_dev_unregister();
|
||||
}
|
||||
|
||||
static void test_pread_pwrite(void)
|
||||
{
|
||||
const char *test_str = "0123456789";
|
||||
const char *filename = "/linux/pread_pwrite.txt";
|
||||
test_create_file_with_text(filename, test_str);
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
// Test pwrite
|
||||
const char *write_str = "ABCD";
|
||||
ssize_t written = pwrite(fd, write_str, strlen(write_str), 2);
|
||||
TEST_ASSERT_EQUAL(strlen(write_str), written);
|
||||
|
||||
// Test pread
|
||||
char read_buf[10] = {0};
|
||||
ssize_t bytes_read = pread(fd, read_buf, 4, 2);
|
||||
TEST_ASSERT_EQUAL(4, bytes_read);
|
||||
TEST_ASSERT_EQUAL(0, strncmp(write_str, read_buf, 4));
|
||||
|
||||
close(fd);
|
||||
unlink(filename);
|
||||
}
|
||||
|
||||
TEST(vfs_linux, test_pread_pwrite_via_vfs)
|
||||
{
|
||||
linux_vfs_dev_register();
|
||||
test_pread_pwrite();
|
||||
linux_vfs_dev_unregister();
|
||||
}
|
||||
|
||||
static void test_unlink(void)
|
||||
{
|
||||
const char *filename = "/linux/unlink.txt";
|
||||
test_create_file_with_text(filename, "test");
|
||||
|
||||
// Test that file exists
|
||||
int fd = open(filename, O_RDWR);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
close(fd);
|
||||
|
||||
// Test unlink
|
||||
TEST_ASSERT_EQUAL(0, unlink(filename));
|
||||
|
||||
// Test that file no longer exists
|
||||
fd = open(filename, O_RDWR);
|
||||
TEST_ASSERT_EQUAL(-1, fd);
|
||||
TEST_ASSERT_EQUAL(ENOENT, errno);
|
||||
}
|
||||
|
||||
TEST(vfs_linux, test_unlink_via_vfs)
|
||||
{
|
||||
linux_vfs_dev_register();
|
||||
test_unlink();
|
||||
linux_vfs_dev_unregister();
|
||||
}
|
||||
|
||||
static void test_fstat(void)
|
||||
{
|
||||
const char *filename = "/linux/fstat.txt";
|
||||
test_create_file_with_text(filename, "test");
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
struct stat st;
|
||||
TEST_ASSERT_EQUAL(0, fstat(fd, &st));
|
||||
TEST_ASSERT_TRUE(S_ISREG(st.st_mode)); // Check if it's a regular file
|
||||
TEST_ASSERT_EQUAL(4, st.st_size); // Check file size
|
||||
|
||||
close(fd);
|
||||
unlink(filename);
|
||||
}
|
||||
|
||||
TEST(vfs_linux, test_fstat_via_vfs)
|
||||
{
|
||||
linux_vfs_dev_register();
|
||||
test_fstat();
|
||||
linux_vfs_dev_unregister();
|
||||
}
|
||||
|
||||
static void test_fcntl(void)
|
||||
{
|
||||
const char *filename = "/linux/fcntl.txt";
|
||||
test_create_file_with_text(filename, "test");
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
// Test F_GETFL
|
||||
int flags = fcntl(fd, F_GETFL);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, flags);
|
||||
TEST_ASSERT_EQUAL(O_RDWR, (flags & O_RDWR)); // Check if file is opened in read-write mode
|
||||
close(fd);
|
||||
unlink(filename);
|
||||
}
|
||||
|
||||
TEST(vfs_linux, test_fcntl_via_vfs)
|
||||
{
|
||||
linux_vfs_dev_register();
|
||||
test_fcntl();
|
||||
linux_vfs_dev_unregister();
|
||||
}
|
||||
|
||||
static void test_ftruncate(void)
|
||||
{
|
||||
const char *test_str = "0123456789\n";
|
||||
const char *filename = "/linux/ftruncate.txt";
|
||||
test_create_file_with_text(filename, test_str);
|
||||
|
||||
int fd = open(filename, O_RDWR);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
// Test truncating to a smaller size
|
||||
const char truncated_1[] = "01234";
|
||||
off_t truncated_len = strlen(truncated_1);
|
||||
TEST_ASSERT_EQUAL(0, ftruncate(fd, truncated_len));
|
||||
|
||||
// Verify the file size after truncation
|
||||
struct stat st;
|
||||
TEST_ASSERT_EQUAL(0, fstat(fd, &st));
|
||||
TEST_ASSERT_EQUAL(truncated_len, st.st_size);
|
||||
|
||||
// Read back the truncated content
|
||||
char buf[32] = {0};
|
||||
TEST_ASSERT_EQUAL(truncated_len, read(fd, buf, sizeof(buf)));
|
||||
TEST_ASSERT_EQUAL(0, strncmp(truncated_1, buf, truncated_len));
|
||||
|
||||
// Test truncating to a larger size
|
||||
off_t new_size = truncated_len + 5;
|
||||
TEST_ASSERT_EQUAL(0, ftruncate(fd, new_size));
|
||||
TEST_ASSERT_EQUAL(0, fstat(fd, &st));
|
||||
TEST_ASSERT_EQUAL(new_size, st.st_size);
|
||||
|
||||
// Test truncating to zero
|
||||
TEST_ASSERT_EQUAL(0, ftruncate(fd, 0));
|
||||
TEST_ASSERT_EQUAL(0, fstat(fd, &st));
|
||||
TEST_ASSERT_EQUAL(0, st.st_size);
|
||||
|
||||
// Test invalid length
|
||||
TEST_ASSERT_EQUAL(-1, ftruncate(fd, -1));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
|
||||
close(fd);
|
||||
unlink(filename);
|
||||
}
|
||||
|
||||
TEST(vfs_linux, test_ftruncate_via_vfs)
|
||||
{
|
||||
linux_vfs_dev_register();
|
||||
test_ftruncate();
|
||||
linux_vfs_dev_unregister();
|
||||
}
|
||||
|
||||
TEST_GROUP_RUNNER(vfs_linux)
|
||||
{
|
||||
RUN_TEST_CASE(vfs_linux, test_linux_vfs_open);
|
||||
RUN_TEST_CASE(vfs_linux, test_lseek_via_vfs);
|
||||
RUN_TEST_CASE(vfs_linux, test_pread_pwrite_via_vfs);
|
||||
RUN_TEST_CASE(vfs_linux, test_unlink_via_vfs);
|
||||
RUN_TEST_CASE(vfs_linux, test_fstat_via_vfs);
|
||||
RUN_TEST_CASE(vfs_linux, test_fcntl_via_vfs);
|
||||
RUN_TEST_CASE(vfs_linux, test_ftruncate_via_vfs);
|
||||
}
|
||||
|
||||
static void run_all_tests(void)
|
||||
{
|
||||
RUN_TEST_GROUP(vfs_linux);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
UNITY_MAIN_FUNC(run_all_tests);
|
||||
return 0;
|
||||
}
|
||||
152
components/vfs/host_test/main/test_vfs_linux_dev.c
Normal file
152
components/vfs/host_test/main/test_vfs_linux_dev.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include "esp_vfs.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define NONE -1
|
||||
|
||||
typedef int (*open_func_t)(const char * path, int flags, ...);
|
||||
typedef int (*close_func_t)(int s);
|
||||
typedef ssize_t (*write_func_t)(int fd, const void * data, size_t size);
|
||||
typedef ssize_t (*read_func_t)(int fd, void* data, size_t size);
|
||||
typedef ssize_t (*pread_func_t)(int fd, void *dst, size_t size, off_t offset);
|
||||
typedef ssize_t (*pwrite_func_t)(int fd, const void *src, size_t size, off_t offset);
|
||||
typedef off_t (*lseek_func_t)(int fd, off_t size, int mode);
|
||||
typedef int (*fstat_func_t)(int fd, struct stat *st);
|
||||
typedef int (*fcntl_func_t)(int fd, int cmd, int arg);
|
||||
typedef int (*fsync_func_t)(int fd);
|
||||
typedef int (*ioctl_func_t)(int fd, int cmd, va_list args);
|
||||
typedef int (*unlink_func_t)(const char *path);
|
||||
typedef int (*ftruncate_func_t)(int fd, off_t length);
|
||||
|
||||
static open_func_t __real_open = NULL;
|
||||
static close_func_t __real_close = NULL;
|
||||
static write_func_t __real_write = NULL;
|
||||
static read_func_t __real_read = NULL;
|
||||
static pread_func_t __real_pread = NULL;
|
||||
static pwrite_func_t __real_pwrite = NULL;
|
||||
static lseek_func_t __real_lseek = NULL;
|
||||
static fstat_func_t __real_fstat = NULL;
|
||||
static fcntl_func_t __real_fcntl = NULL;
|
||||
static fsync_func_t __real_fsync = NULL;
|
||||
static ioctl_func_t __real_ioctl = NULL;
|
||||
static unlink_func_t __real_unlink = NULL;
|
||||
static ftruncate_func_t __real_ftruncate = NULL;
|
||||
|
||||
void __attribute__((constructor)) init_real_funcs(void)
|
||||
{
|
||||
__real_open = (open_func_t) dlsym(RTLD_NEXT, "open");
|
||||
__real_close = (close_func_t) dlsym(RTLD_NEXT, "close");
|
||||
__real_write = (write_func_t) dlsym(RTLD_NEXT, "write");
|
||||
__real_read = (read_func_t) dlsym(RTLD_NEXT, "read");
|
||||
__real_pread = (pread_func_t) dlsym(RTLD_NEXT, "pread");
|
||||
__real_pwrite = (pwrite_func_t) dlsym(RTLD_NEXT, "pwrite");
|
||||
__real_lseek = (lseek_func_t) dlsym(RTLD_NEXT, "lseek");
|
||||
__real_fstat = (fstat_func_t) dlsym(RTLD_NEXT, "fstat");
|
||||
__real_fcntl = (fcntl_func_t) dlsym(RTLD_NEXT, "fcntl");
|
||||
__real_fsync = (fsync_func_t) dlsym(RTLD_NEXT, "fsync");
|
||||
__real_ioctl = (ioctl_func_t) dlsym(RTLD_NEXT, "ioctl");
|
||||
__real_unlink = (unlink_func_t) dlsym(RTLD_NEXT, "unlink");
|
||||
__real_ftruncate = (ftruncate_func_t) dlsym(RTLD_NEXT, "ftruncate");
|
||||
}
|
||||
|
||||
static int linux_open(const char *path, int flags, int mode)
|
||||
{
|
||||
return __real_open(path+1, flags, mode);
|
||||
}
|
||||
|
||||
static ssize_t linux_write(int fd, const void * data, size_t size)
|
||||
{
|
||||
return __real_write(fd, data, size);
|
||||
}
|
||||
|
||||
static ssize_t linux_read(int fd, void* data, size_t size)
|
||||
{
|
||||
return __real_read(fd, data, size);
|
||||
}
|
||||
|
||||
static int linux_close(int fd)
|
||||
{
|
||||
return __real_close(fd);
|
||||
}
|
||||
|
||||
static ssize_t linux_pread(int fd, void *dst, size_t size, off_t offset)
|
||||
{
|
||||
return __real_pread(fd, dst, size, offset);
|
||||
}
|
||||
|
||||
static ssize_t linux_pwrite(int fd, const void *src, size_t size, off_t offset)
|
||||
{
|
||||
return __real_pwrite(fd, src, size, offset);
|
||||
}
|
||||
|
||||
static off_t linux_lseek(int fd, off_t size, int mode)
|
||||
{
|
||||
return __real_lseek(fd, size, mode);
|
||||
}
|
||||
|
||||
static int linux_fstat(int fd, struct stat *st)
|
||||
{
|
||||
return __real_fstat(fd, st);
|
||||
}
|
||||
|
||||
static int linux_fcntl(int fd, int cmd, int arg)
|
||||
{
|
||||
return __real_fcntl(fd, cmd, arg);
|
||||
}
|
||||
|
||||
static int linux_fsync(int fd)
|
||||
{
|
||||
return __real_fsync(fd);
|
||||
}
|
||||
|
||||
static int linux_ioctl(int fd, int request, va_list args)
|
||||
{
|
||||
return __real_ioctl(fd, request, args);
|
||||
}
|
||||
|
||||
static int linux_unlink(const char *path)
|
||||
{
|
||||
return __real_unlink(path+1);
|
||||
}
|
||||
|
||||
static int linux_ftruncate(int fd, off_t length)
|
||||
{
|
||||
return __real_ftruncate(fd, length);
|
||||
}
|
||||
|
||||
static const esp_vfs_t linux_vfs = {
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
.write = &linux_write,
|
||||
.open = &linux_open,
|
||||
.close = &linux_close,
|
||||
.read = &linux_read,
|
||||
.pread = &linux_pread,
|
||||
.pwrite = &linux_pwrite,
|
||||
.lseek = &linux_lseek,
|
||||
.fstat = &linux_fstat,
|
||||
.fcntl = &linux_fcntl,
|
||||
.fsync = &linux_fsync,
|
||||
.ioctl = &linux_ioctl,
|
||||
.unlink = &linux_unlink,
|
||||
.ftruncate = &linux_ftruncate,
|
||||
};
|
||||
|
||||
void linux_vfs_dev_register(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_vfs_register("/linux", &linux_vfs, NULL));
|
||||
}
|
||||
|
||||
void linux_vfs_dev_unregister(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_vfs_unregister("/linux"));
|
||||
}
|
||||
19
components/vfs/host_test/main/test_vfs_linux_dev.h
Normal file
19
components/vfs/host_test/main/test_vfs_linux_dev.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void linux_vfs_dev_register(void);
|
||||
void linux_vfs_dev_unregister(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
5
components/vfs/host_test/sdkconfig.defaults
Normal file
5
components/vfs/host_test/sdkconfig.defaults
Normal file
@@ -0,0 +1,5 @@
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_IDF_TARGET_LINUX=y
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
|
||||
CONFIG_UNITY_ENABLE_FIXTURE=y
|
||||
@@ -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
|
||||
*/
|
||||
@@ -31,9 +31,11 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_LINUX
|
||||
#ifndef _SYS_TYPES_FD_SET
|
||||
#error "VFS should be used with FD_SETSIZE and FD_SET from sys/types.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Maximum number of (global) file descriptors.
|
||||
@@ -385,6 +387,25 @@ int esp_vfs_link(struct _reent *r, const char* n1, const char* n2);
|
||||
int esp_vfs_unlink(struct _reent *r, const char *path);
|
||||
int esp_vfs_rename(struct _reent *r, const char *src, const char *dst);
|
||||
int esp_vfs_utime(const char *path, const struct utimbuf *times);
|
||||
int esp_vfs_fsync(int fd);
|
||||
int esp_vfs_fcntl_r(struct _reent *r, int fd, int cmd, int arg);
|
||||
int esp_vfs_ioctl(int fd, int cmd, ...);
|
||||
|
||||
/* Directory related functions */
|
||||
int esp_vfs_stat(struct _reent *r, const char *path, struct stat *st);
|
||||
int esp_vfs_truncate(const char *path, off_t length);
|
||||
int esp_vfs_ftruncate(int fd, off_t length);
|
||||
int esp_vfs_access(const char *path, int amode);
|
||||
int esp_vfs_utime(const char *path, const struct utimbuf *times);
|
||||
int esp_vfs_rmdir(const char* name);
|
||||
int esp_vfs_mkdir(const char* name, mode_t mode);
|
||||
DIR* esp_vfs_opendir(const char* name);
|
||||
int esp_vfs_closedir(DIR* pdir);
|
||||
int esp_vfs_readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent);
|
||||
struct dirent* esp_vfs_readdir(DIR* pdir);
|
||||
long esp_vfs_telldir(DIR* pdir);
|
||||
void esp_vfs_seekdir(DIR* pdir, long loc);
|
||||
void esp_vfs_rewinddir(DIR* pdir);
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -27,9 +27,11 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_LINUX
|
||||
#ifndef _SYS_TYPES_FD_SET
|
||||
#error "VFS should be used with FD_SETSIZE and FD_SET from sys/types.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* @brief VFS identificator used for esp_vfs_register_with_id()
|
||||
|
||||
64
components/vfs/linux_include/dirent.h
Normal file
64
components/vfs/linux_include/dirent.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/**
|
||||
* This header file provides POSIX-compatible definitions of directory access data types.
|
||||
* The standard dirent.h cannot be used directly because we have a custom version defined in newlib component,
|
||||
* which is used by VFS. Accessing the VFS index (definition of struct DIR) requires this custom dirent.h.
|
||||
* This file is a copy of dirent.h from newlib.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Opaque directory structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t dd_vfs_idx; /*!< VFS index, not to be used by applications */
|
||||
uint16_t dd_rsv; /*!< field reserved for future extension */
|
||||
/* remaining fields are defined by VFS implementation */
|
||||
} DIR;
|
||||
|
||||
/**
|
||||
* @brief Directory entry structure
|
||||
*/
|
||||
struct dirent {
|
||||
ino_t d_ino; /*!< file number */
|
||||
uint8_t d_type; /*!< not defined in POSIX, but present in BSD and Linux */
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_REG 1
|
||||
#define DT_DIR 2
|
||||
#if __BSD_VISIBLE
|
||||
#define MAXNAMLEN 255
|
||||
char d_name[MAXNAMLEN + 1]; /*!< zero-terminated file name */
|
||||
#else
|
||||
char d_name[256];
|
||||
#endif
|
||||
};
|
||||
|
||||
DIR* opendir(const char* name);
|
||||
struct dirent* readdir(DIR* pdir);
|
||||
long telldir(DIR* pdir);
|
||||
void seekdir(DIR* pdir, long loc);
|
||||
void rewinddir(DIR* pdir);
|
||||
int closedir(DIR* pdir);
|
||||
int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent);
|
||||
int scandir(const char *dirname, struct dirent ***out_dirlist,
|
||||
int (*select_func)(const struct dirent *),
|
||||
int (*cmp_func)(const struct dirent **, const struct dirent **));
|
||||
int alphasort(const struct dirent **d1, const struct dirent **d2);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
31
components/vfs/linux_include/errno.h
Normal file
31
components/vfs/linux_include/errno.h
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _ESP_PLATFORM_ERRNO_H_
|
||||
#define _ESP_PLATFORM_ERRNO_H_
|
||||
|
||||
#include_next "errno.h"
|
||||
|
||||
//
|
||||
// Possibly define some missing errors
|
||||
//
|
||||
#ifndef ESHUTDOWN
|
||||
#define ESHUTDOWN 110 /* Cannot send after transport endpoint shutdown */
|
||||
#endif
|
||||
|
||||
#ifndef EAI_SOCKTYPE
|
||||
#define EAI_SOCKTYPE 10 /* ai_socktype not supported */
|
||||
#endif
|
||||
|
||||
#ifndef EAI_AGAIN
|
||||
#define EAI_AGAIN 2 /* temporary failure in name resolution */
|
||||
#endif
|
||||
|
||||
#ifndef EAI_BADFLAGS
|
||||
#define EAI_BADFLAGS 3 /* invalid value for ai_flags */
|
||||
#endif
|
||||
|
||||
#endif // _ESP_PLATFORM_ERRNO_H_
|
||||
16
components/vfs/linux_include/sys/reent.h
Normal file
16
components/vfs/linux_include/sys/reent.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// reent struct is not used for Linux platform. It's defined in Newlib.
|
||||
// To make it compatible with vfs.c, following dummy struct and macros are defined.
|
||||
struct _reent {
|
||||
int i;
|
||||
};
|
||||
|
||||
#define __errno_r(r) r->i
|
||||
#define __getreent() NULL
|
||||
@@ -6,21 +6,15 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/reent.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/param.h>
|
||||
#include <dirent.h>
|
||||
#include "inttypes_ext.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_private.h"
|
||||
#include "include/esp_vfs.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
// Warn about using deprecated option
|
||||
#ifdef CONFIG_LWIP_USE_ONLY_LWIP_SELECT
|
||||
@@ -47,7 +41,11 @@ static const char *TAG = "vfs";
|
||||
#define LEN_PATH_PREFIX_IGNORED SIZE_MAX /* special length value for VFS which is never recognised by open() */
|
||||
#define FD_TABLE_ENTRY_UNUSED (fd_table_t) { .permanent = false, .has_pending_close = false, .has_pending_select = false, .vfs_index = -1, .local_fd = -1 }
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_LINUX
|
||||
typedef uint16_t local_fd_t;
|
||||
#else
|
||||
typedef uint8_t local_fd_t;
|
||||
#endif
|
||||
_Static_assert((1 << (sizeof(local_fd_t)*8)) >= MAX_FDS, "file descriptor type too small");
|
||||
|
||||
typedef int8_t vfs_index_t;
|
||||
@@ -547,7 +545,7 @@ esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ct
|
||||
esp_err_t esp_vfs_register_fd_range(const esp_vfs_t *vfs, void *ctx, int min_fd, int max_fd)
|
||||
{
|
||||
if (min_fd < 0 || max_fd < 0 || min_fd > MAX_FDS || max_fd > MAX_FDS || min_fd > max_fd) {
|
||||
ESP_LOGD(TAG, "Invalid arguments: esp_vfs_register_fd_range(0x%x, 0x%x, %d, %d)", (int) vfs, (int) ctx, min_fd, max_fd);
|
||||
ESP_LOGD(TAG, "Invalid arguments: esp_vfs_register_fd_range(0x%p, 0x%p, %d, %d)", vfs, ctx, min_fd, max_fd);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
@@ -623,7 +621,9 @@ esp_err_t esp_vfs_unregister_with_id(esp_vfs_id_t vfs_id)
|
||||
|
||||
}
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_LINUX
|
||||
esp_err_t esp_vfs_unregister_fs_with_id(esp_vfs_id_t vfs_id) __attribute__((alias("esp_vfs_unregister_with_id")));
|
||||
#endif
|
||||
|
||||
esp_err_t esp_vfs_unregister(const char* base_path)
|
||||
{
|
||||
@@ -641,7 +641,9 @@ esp_err_t esp_vfs_unregister(const char* base_path)
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_LINUX
|
||||
esp_err_t esp_vfs_unregister_fs(const char* base_path) __attribute__((alias("esp_vfs_unregister")));
|
||||
#endif
|
||||
|
||||
esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd)
|
||||
{
|
||||
@@ -730,7 +732,7 @@ void esp_vfs_dump_registered_paths(FILE *fp)
|
||||
for (size_t i = 0; i < VFS_MAX_COUNT; ++i) {
|
||||
fprintf(
|
||||
fp,
|
||||
"%d:%s -> %p\n",
|
||||
"%" PRIuSIZE ": %s -> %p\n",
|
||||
i,
|
||||
s_vfs[i] ? s_vfs[i]->path_prefix : "NULL",
|
||||
s_vfs[i] ? s_vfs[i]->vfs : NULL
|
||||
@@ -1587,7 +1589,7 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds
|
||||
|
||||
// call start_select for all non-socket VFSs with has at least one FD set in readfds, writefds, or errorfds
|
||||
// note: it can point to socket VFS but item->isset will be false for that
|
||||
ESP_LOGD(TAG, "calling start_select for VFS ID %d with the following local FDs", i);
|
||||
ESP_LOGD(TAG, "calling start_select for VFS ID % " PRIuSIZE " with the following local FDs", i);
|
||||
esp_vfs_log_fd_set("readfds", &item->readfds);
|
||||
esp_vfs_log_fd_set("writefds", &item->writefds);
|
||||
esp_vfs_log_fd_set("errorfds", &item->errorfds);
|
||||
@@ -1833,6 +1835,7 @@ int tcsendbreak(int fd, int duration)
|
||||
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_LINUX
|
||||
/* Create aliases for libc syscalls
|
||||
|
||||
These functions are also available in ROM as stubs which use the syscall table, but linking them
|
||||
@@ -1905,6 +1908,7 @@ void seekdir(DIR* pdir, long loc)
|
||||
void rewinddir(DIR* pdir)
|
||||
__attribute__((alias("esp_vfs_rewinddir")));
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
#endif //CONFIG_IDF_TARGET_LINUX
|
||||
|
||||
void vfs_include_syscalls_impl(void)
|
||||
{
|
||||
|
||||
189
components/vfs/vfs_linux.c
Normal file
189
components/vfs/vfs_linux.c
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Linux target VFS syscall wrappers
|
||||
*
|
||||
* This file provides POSIX syscall wrappers that redirect to ESP-IDF VFS functions
|
||||
* when building for Linux target.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "esp_vfs.h"
|
||||
|
||||
int open(const char * path, int flags, ...)
|
||||
{
|
||||
struct _reent r;
|
||||
va_list list;
|
||||
va_start(list, flags);
|
||||
int mode = va_arg(list, int);
|
||||
va_end(list);
|
||||
return esp_vfs_open(&r, path, flags, mode);
|
||||
}
|
||||
|
||||
int close(int fd)
|
||||
{
|
||||
struct _reent r;
|
||||
return esp_vfs_close(&r, fd);
|
||||
}
|
||||
|
||||
ssize_t read(int fd, void * dst, size_t size)
|
||||
{
|
||||
struct _reent r;
|
||||
return esp_vfs_read(&r, fd, dst, size);
|
||||
}
|
||||
|
||||
ssize_t write(int fd, const void * data, size_t size)
|
||||
{
|
||||
struct _reent r;
|
||||
return esp_vfs_write(&r, fd, data, size);
|
||||
}
|
||||
|
||||
off_t lseek(int fd, off_t size, int mode)
|
||||
{
|
||||
struct _reent r;
|
||||
return esp_vfs_lseek(&r, fd, size, mode);
|
||||
}
|
||||
|
||||
ssize_t pread(int fd, void *dst, size_t size, off_t offset)
|
||||
{
|
||||
return esp_vfs_pread(fd,dst, size, offset);
|
||||
}
|
||||
|
||||
ssize_t pwrite(int fd, const void *src, size_t size, off_t offset)
|
||||
{
|
||||
return esp_vfs_pwrite(fd, src,size, offset);
|
||||
}
|
||||
|
||||
int fstat(int fd, struct stat *st)
|
||||
{
|
||||
struct _reent r;
|
||||
return esp_vfs_fstat(&r, fd, st);
|
||||
}
|
||||
|
||||
int fcntl(int fd, int cmd, ...)
|
||||
{
|
||||
struct _reent r;
|
||||
va_list list;
|
||||
va_start(list, cmd);
|
||||
int args = va_arg(list, int);
|
||||
va_end(list);
|
||||
return esp_vfs_fcntl_r(&r, fd, cmd, args);
|
||||
}
|
||||
|
||||
int ioctl(int fd, unsigned long cmd, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, cmd);
|
||||
int ret = esp_vfs_ioctl(fd, cmd, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fsync(int fd)
|
||||
{
|
||||
return esp_vfs_fsync(fd);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
|
||||
{
|
||||
return esp_vfs_select(nfds, readfds, writefds, errorfds, timeout);
|
||||
}
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
int stat(const char * path, struct stat * st)
|
||||
{
|
||||
struct _reent r;
|
||||
return esp_vfs_stat(&r, path, st);
|
||||
}
|
||||
|
||||
int link(const char* n1, const char* n2)
|
||||
{
|
||||
struct _reent r;
|
||||
return esp_vfs_link(&r, n1, n2);
|
||||
}
|
||||
|
||||
int unlink(const char *path)
|
||||
{
|
||||
struct _reent r;
|
||||
return esp_vfs_unlink(&r, path);
|
||||
}
|
||||
|
||||
int rename(const char *src, const char *dst)
|
||||
{
|
||||
struct _reent r;
|
||||
return esp_vfs_rename(&r, src, dst);
|
||||
}
|
||||
|
||||
int truncate(const char *path, off_t length)
|
||||
{
|
||||
return esp_vfs_truncate(path, length);
|
||||
}
|
||||
|
||||
int ftruncate(int fd, off_t length)
|
||||
{
|
||||
return esp_vfs_ftruncate(fd, length);
|
||||
}
|
||||
|
||||
int access(const char *path, int amode)
|
||||
{
|
||||
return esp_vfs_access(path, amode);
|
||||
}
|
||||
|
||||
int utime(const char *path, const struct utimbuf *times)
|
||||
{
|
||||
return esp_vfs_utime(path, times);
|
||||
}
|
||||
|
||||
int rmdir(const char* name)
|
||||
{
|
||||
return esp_vfs_rmdir(name);
|
||||
}
|
||||
|
||||
int mkdir(const char* name, mode_t mode)
|
||||
{
|
||||
return esp_vfs_mkdir(name, mode);
|
||||
}
|
||||
|
||||
DIR* opendir(const char* name)
|
||||
{
|
||||
return esp_vfs_opendir(name);
|
||||
}
|
||||
|
||||
int closedir(DIR* pdir)
|
||||
{
|
||||
return esp_vfs_closedir(pdir);
|
||||
}
|
||||
|
||||
int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent)
|
||||
{
|
||||
return esp_vfs_readdir_r(pdir, entry, out_dirent);
|
||||
}
|
||||
|
||||
struct dirent* readdir(DIR* pdir)
|
||||
{
|
||||
return esp_vfs_readdir(pdir);
|
||||
}
|
||||
|
||||
long telldir(DIR* pdir)
|
||||
{
|
||||
return esp_vfs_telldir(pdir);
|
||||
}
|
||||
|
||||
void seekdir(DIR* pdir, long loc)
|
||||
{
|
||||
return esp_vfs_seekdir(pdir, loc);
|
||||
}
|
||||
|
||||
void rewinddir(DIR* pdir)
|
||||
{
|
||||
return esp_vfs_rewinddir(pdir);
|
||||
}
|
||||
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
@@ -449,7 +449,6 @@ components/esp_rom/patches/esp_rom_longjmp.S
|
||||
components/esp_system/ubsan.c
|
||||
components/esp_wifi/src/mesh_event.c
|
||||
components/fatfs/diskio/diskio.c
|
||||
components/fatfs/diskio/diskio_impl.h
|
||||
components/fatfs/diskio/diskio_rawflash.h
|
||||
components/fatfs/diskio/diskio_wl.h
|
||||
components/fatfs/port/freertos/ffsystem.c
|
||||
|
||||
Reference in New Issue
Block a user