diff --git a/components/console/CMakeLists.txt b/components/console/CMakeLists.txt index bba6418e00..a8ce979281 100644 --- a/components/console/CMakeLists.txt +++ b/components/console/CMakeLists.txt @@ -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 diff --git a/components/console/esp_console_common.c b/components/console/esp_console_common.c index 2ba7ce275c..b8c304d1b9 100644 --- a/components/console/esp_console_common.c +++ b/components/console/esp_console_common.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; diff --git a/components/console/esp_console_repl_internal.c b/components/console/esp_console_repl_internal.c index f4bf639052..fbfd29b4dc 100644 --- a/components/console/esp_console_repl_internal.c +++ b/components/console/esp_console_repl_internal.c @@ -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. diff --git a/components/console/esp_console_repl_linux.c b/components/console/esp_console_repl_linux.c index 9a427c9bb8..abc3fbbbdd 100644 --- a/components/console/esp_console_repl_linux.c +++ b/components/console/esp_console_repl_linux.c @@ -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); diff --git a/components/console/test_apps/console/sdkconfig.ci.defaults.linux b/components/console/test_apps/console/sdkconfig.ci.defaults.linux index 9b39f10b99..fe79be5f4b 100644 --- a/components/console/test_apps/console/sdkconfig.ci.defaults.linux +++ b/components/console/test_apps/console/sdkconfig.ci.defaults.linux @@ -1 +1,2 @@ CONFIG_IDF_TARGET="linux" +CONFIG_VFS_SUPPORT_IO=n diff --git a/components/console/test_apps/console/sdkconfig.ci.sorted.linux b/components/console/test_apps/console/sdkconfig.ci.sorted.linux index c672cea1ca..7bfeba908a 100644 --- a/components/console/test_apps/console/sdkconfig.ci.sorted.linux +++ b/components/console/test_apps/console/sdkconfig.ci.sorted.linux @@ -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 diff --git a/components/console/test_apps/console/sdkconfig.defaults.linux b/components/console/test_apps/console/sdkconfig.defaults.linux new file mode 100644 index 0000000000..dcd2b2ec4a --- /dev/null +++ b/components/console/test_apps/console/sdkconfig.defaults.linux @@ -0,0 +1 @@ +CONFIG_VFS_SUPPORT_IO=n diff --git a/components/esp_partition/CMakeLists.txt b/components/esp_partition/CMakeLists.txt index 977f7cc679..5dc13db76f 100644 --- a/components/esp_partition/CMakeLists.txt +++ b/components/esp_partition/CMakeLists.txt @@ -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") diff --git a/components/esp_partition/partition_linux.c b/components/esp_partition/partition_linux.c index 5d18a0d5b6..7dd8aab9c9 100644 --- a/components/esp_partition/partition_linux.c +++ b/components/esp_partition/partition_linux.c @@ -21,9 +21,22 @@ #include "esp_private/partition_linux.h" #include "esp_log.h" #include "spi_flash_mmap.h" +#include 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; diff --git a/components/fatfs/CMakeLists.txt b/components/fatfs/CMakeLists.txt index dddcb8905f..67423287af 100644 --- a/components/fatfs/CMakeLists.txt +++ b/components/fatfs/CMakeLists.txt @@ -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" diff --git a/components/fatfs/diskio/diskio_impl.h b/components/fatfs/diskio/diskio_impl.h index 9d7e012481..12dbcb31d8 100644 --- a/components/fatfs/diskio/diskio_impl.h +++ b/components/fatfs/diskio/diskio_impl.h @@ -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 -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; diff --git a/components/fatfs/host_test/main/CMakeLists.txt b/components/fatfs/host_test/main/CMakeLists.txt index 046433c130..bc1b10714c 100644 --- a/components/fatfs/host_test/main/CMakeLists.txt +++ b/components/fatfs/host_test/main/CMakeLists.txt @@ -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 ) diff --git a/components/fatfs/host_test/main/test_fatfs_vfs.cpp b/components/fatfs/host_test/main/test_fatfs_vfs.cpp new file mode 100644 index 0000000000..516a2864b8 --- /dev/null +++ b/components/fatfs/host_test/main/test_fatfs_vfs.cpp @@ -0,0 +1,579 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include + +#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 + +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(); +} diff --git a/components/fatfs/host_test/partition_table.csv b/components/fatfs/host_test/partition_table.csv index 777a3e67e9..e1155663bf 100644 --- a/components/fatfs/host_test/partition_table.csv +++ b/components/fatfs/host_test/partition_table.csv @@ -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, diff --git a/components/fatfs/src/ff.h b/components/fatfs/src/ff.h index 095d3590a0..d10b7c4d35 100644 --- a/components/fatfs/src/ff.h +++ b/components/fatfs/src/ff.h @@ -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 -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 */ diff --git a/components/fatfs/src/ffconf.h b/components/fatfs/src/ffconf.h index 0ee0d91a6b..b8ae97bb63 100644 --- a/components/fatfs/src/ffconf.h +++ b/components/fatfs/src/ffconf.h @@ -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 */ diff --git a/components/fatfs/vfs/esp_vfs_fat.h b/components/fatfs/vfs/esp_vfs_fat.h index 332a131c80..0a0070cae8 100644 --- a/components/fatfs/vfs/esp_vfs_fat.h +++ b/components/fatfs/vfs/esp_vfs_fat.h @@ -7,8 +7,10 @@ #pragma once #include #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 diff --git a/components/fatfs/vfs/vfs_fat.c b/components/fatfs/vfs/vfs_fat.c index aeab037f57..8a8d2cd3f7 100644 --- a/components/fatfs/vfs/vfs_fat.c +++ b/components/fatfs/vfs/vfs_fat.c @@ -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}; diff --git a/components/fatfs/vfs/vfs_fat_internal.h b/components/fatfs/vfs/vfs_fat_internal.h index d20f64c438..e2f36a05f7 100644 --- a/components/fatfs/vfs/vfs_fat_internal.h +++ b/components/fatfs/vfs/vfs_fat_internal.h @@ -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 #include @@ -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 diff --git a/components/vfs/CMakeLists.txt b/components/vfs/CMakeLists.txt index 57f966359f..10fb7ac53d 100644 --- a/components/vfs/CMakeLists.txt +++ b/components/vfs/CMakeLists.txt @@ -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() diff --git a/components/vfs/host_test/CMakeLists.txt b/components/vfs/host_test/CMakeLists.txt new file mode 100644 index 0000000000..c5c8aec317 --- /dev/null +++ b/components/vfs/host_test/CMakeLists.txt @@ -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) diff --git a/components/vfs/host_test/main/CMakeLists.txt b/components/vfs/host_test/main/CMakeLists.txt new file mode 100644 index 0000000000..68b929022b --- /dev/null +++ b/components/vfs/host_test/main/CMakeLists.txt @@ -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) diff --git a/components/vfs/host_test/main/test_vfs.c b/components/vfs/host_test/main/test_vfs.c new file mode 100644 index 0000000000..99bd25cf1e --- /dev/null +++ b/components/vfs/host_test/main/test_vfs.c @@ -0,0 +1,265 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/components/vfs/host_test/main/test_vfs_linux_dev.c b/components/vfs/host_test/main/test_vfs_linux_dev.c new file mode 100644 index 0000000000..0a88cc5b7f --- /dev/null +++ b/components/vfs/host_test/main/test_vfs_linux_dev.c @@ -0,0 +1,152 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#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")); +} diff --git a/components/vfs/host_test/main/test_vfs_linux_dev.h b/components/vfs/host_test/main/test_vfs_linux_dev.h new file mode 100644 index 0000000000..a229431f9d --- /dev/null +++ b/components/vfs/host_test/main/test_vfs_linux_dev.h @@ -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 diff --git a/components/vfs/host_test/sdkconfig.defaults b/components/vfs/host_test/sdkconfig.defaults new file mode 100644 index 0000000000..1a48292ba7 --- /dev/null +++ b/components/vfs/host_test/sdkconfig.defaults @@ -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 diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index 7ad766c028..4f515eb488 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -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); /**@}*/ /** diff --git a/components/vfs/include/esp_vfs_ops.h b/components/vfs/include/esp_vfs_ops.h index d04753f082..ed43ca3d14 100644 --- a/components/vfs/include/esp_vfs_ops.h +++ b/components/vfs/include/esp_vfs_ops.h @@ -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() diff --git a/components/vfs/linux_include/dirent.h b/components/vfs/linux_include/dirent.h new file mode 100644 index 0000000000..a4c9c64803 --- /dev/null +++ b/components/vfs/linux_include/dirent.h @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include + +/** + * 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 diff --git a/components/vfs/linux_include/errno.h b/components/vfs/linux_include/errno.h new file mode 100644 index 0000000000..a72f80ed27 --- /dev/null +++ b/components/vfs/linux_include/errno.h @@ -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_ diff --git a/components/vfs/linux_include/sys/reent.h b/components/vfs/linux_include/sys/reent.h new file mode 100644 index 0000000000..886ad52d9a --- /dev/null +++ b/components/vfs/linux_include/sys/reent.h @@ -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 diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index 020a2312eb..332bfeabc7 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -6,21 +6,15 @@ #include #include -#include #include #include -#include #include -#include #include -#include #include +#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) { diff --git a/components/vfs/vfs_linux.c b/components/vfs/vfs_linux.c new file mode 100644 index 0000000000..e59502d1e8 --- /dev/null +++ b/components/vfs/vfs_linux.c @@ -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 +#include +#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 diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index c7576b2120..17f3ad58e8 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -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