From ea9f7cc3705ab70736504f92f6100a1b721c1841 Mon Sep 17 00:00:00 2001 From: Vamshi Gajjela Date: Tue, 8 Mar 2022 00:01:48 +0530 Subject: [PATCH] fatfs: Add ftruncate api support for VFS and FAT-VFS Unit test cases added to verify ftruncate within fatfs tests. Closes https://github.com/espressif/esp-idf/issues/8279 --- components/fatfs/test/test_fatfs_common.c | 91 +++++++++++++++++++---- components/fatfs/test/test_fatfs_common.h | 2 + components/fatfs/test/test_fatfs_sdmmc.c | 7 ++ components/fatfs/vfs/vfs_fat.c | 55 ++++++++++++++ components/vfs/include/esp_vfs.h | 4 + components/vfs/vfs.c | 16 ++++ tools/ci/check_copyright_ignore.txt | 1 - 7 files changed, 162 insertions(+), 14 deletions(-) diff --git a/components/fatfs/test/test_fatfs_common.c b/components/fatfs/test/test_fatfs_common.c index 953b8d4ad5..6b44bf03ce 100644 --- a/components/fatfs/test/test_fatfs_common.c +++ b/components/fatfs/test/test_fatfs_common.c @@ -1,16 +1,8 @@ -// Copyright 2015-2017 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: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include #include @@ -315,6 +307,79 @@ void test_fatfs_truncate_file(const char* filename) TEST_ASSERT_EQUAL(0, fclose(f)); } +void test_fatfs_ftruncate_file(const char* filename) +{ + int truncated_len = 0; + + const char input[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + char output[sizeof(input)]; + + int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC); + TEST_ASSERT_NOT_EQUAL(-1, fd); + + TEST_ASSERT_EQUAL(strlen(input), write(fd, input, strlen(input))); + + // Extending file beyond size is not supported + TEST_ASSERT_EQUAL(-1, ftruncate(fd, strlen(input) + 1)); + TEST_ASSERT_EQUAL(errno, EPERM); + + TEST_ASSERT_EQUAL(-1, ftruncate(fd, -1)); + TEST_ASSERT_EQUAL(errno, EINVAL); + + // Truncating should succeed + const char truncated_1[] = "ABCDEFGHIJ"; + truncated_len = strlen(truncated_1); + TEST_ASSERT_EQUAL(0, ftruncate(fd, truncated_len)); + TEST_ASSERT_EQUAL(0, close(fd)); + + // open file for reading and validate the content + fd = open(filename, O_RDONLY); + TEST_ASSERT_NOT_EQUAL(-1, fd); + + memset(output, 0, sizeof(output)); + + TEST_ASSERT_EQUAL(truncated_len, read(fd, output, sizeof(output))); + + TEST_ASSERT_EQUAL_STRING_LEN(truncated_1, output, truncated_len); + + TEST_ASSERT_EQUAL(0, close(fd)); + + // further truncate the file + fd = open(filename, O_WRONLY); + TEST_ASSERT_NOT_EQUAL(-1, fd); + // Once truncated, the new file size should be the basis + // whether truncation should succeed or not + TEST_ASSERT_EQUAL(-1, ftruncate(fd, truncated_len + 1)); + TEST_ASSERT_EQUAL(EPERM, errno); + + TEST_ASSERT_EQUAL(-1, ftruncate(fd, strlen(input))); + TEST_ASSERT_EQUAL(EPERM, errno); + + TEST_ASSERT_EQUAL(-1, ftruncate(fd, strlen(input) + 1)); + TEST_ASSERT_EQUAL(EPERM, errno); + + TEST_ASSERT_EQUAL(-1, ftruncate(fd, -1)); + TEST_ASSERT_EQUAL(EINVAL, errno); + + // Truncating a truncated file should succeed + const char truncated_2[] = "ABCDE"; + truncated_len = strlen(truncated_2); + + TEST_ASSERT_EQUAL(0, ftruncate(fd, truncated_len)); + TEST_ASSERT_EQUAL(0, close(fd)); + + // open file for reading and validate the content + fd = open(filename, O_RDONLY); + TEST_ASSERT_NOT_EQUAL(-1, fd); + + memset(output, 0, sizeof(output)); + + TEST_ASSERT_EQUAL(truncated_len, read(fd, output, sizeof(output))); + TEST_ASSERT_EQUAL_STRING_LEN(truncated_2, output, truncated_len); + + TEST_ASSERT_EQUAL(0, close(fd)); +} + void test_fatfs_stat(const char* filename, const char* root_dir) { struct tm tm; diff --git a/components/fatfs/test/test_fatfs_common.h b/components/fatfs/test/test_fatfs_common.h index 46cf3b8909..b66078b1b2 100644 --- a/components/fatfs/test/test_fatfs_common.h +++ b/components/fatfs/test/test_fatfs_common.h @@ -43,6 +43,8 @@ void test_fatfs_lseek(const char* filename); void test_fatfs_truncate_file(const char* path); +void test_fatfs_ftruncate_file(const char* path); + void test_fatfs_stat(const char* filename, const char* root_dir); void test_fatfs_utime(const char* filename, const char* root_dir); diff --git a/components/fatfs/test/test_fatfs_sdmmc.c b/components/fatfs/test/test_fatfs_sdmmc.c index 2c5c937049..62b77d922c 100644 --- a/components/fatfs/test/test_fatfs_sdmmc.c +++ b/components/fatfs/test/test_fatfs_sdmmc.c @@ -143,6 +143,13 @@ TEST_CASE("(SD) can truncate", "[fatfs][sd][test_env=UT_T1_SDMODE][timeout=60]") test_teardown(); } +TEST_CASE("(SD) can ftruncate", "[fatfs][sd][test_env=UT_T1_SDMODE][timeout=60]") +{ + test_setup(); + test_fatfs_ftruncate_file("/sdcard/ftrunc.txt"); + test_teardown(); +} + TEST_CASE("(SD) stat returns correct values", "[fatfs][test_env=UT_T1_SDMODE][timeout=60]") { test_setup(); diff --git a/components/fatfs/vfs/vfs_fat.c b/components/fatfs/vfs/vfs_fat.c index e797244a09..e8bda64fbf 100644 --- a/components/fatfs/vfs/vfs_fat.c +++ b/components/fatfs/vfs/vfs_fat.c @@ -81,6 +81,7 @@ static int vfs_fat_mkdir(void* ctx, const char* name, mode_t mode); static int vfs_fat_rmdir(void* ctx, const char* name); static int vfs_fat_access(void* ctx, const char *path, int amode); static int vfs_fat_truncate(void* ctx, const char *path, off_t length); +static int vfs_fat_ftruncate(void* ctx, int fd, off_t length); static int vfs_fat_utime(void* ctx, const char *path, const struct utimbuf *times); #endif // CONFIG_VFS_SUPPORT_DIR @@ -146,6 +147,7 @@ esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, siz .rmdir_p = &vfs_fat_rmdir, .access_p = &vfs_fat_access, .truncate_p = &vfs_fat_truncate, + .ftruncate_p = &vfs_fat_ftruncate, .utime_p = &vfs_fat_utime, #endif // CONFIG_VFS_SUPPORT_DIR }; @@ -952,6 +954,59 @@ out: return ret; } +static int vfs_fat_ftruncate(void* ctx, int fd, off_t length) +{ + FRESULT res; + FIL* file = NULL; + + int ret = 0; + + vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx; + + if (length < 0) { + errno = EINVAL; + ret = -1; + return ret; + } + + _lock_acquire(&fat_ctx->lock); + file = &fat_ctx->files[fd]; + if (file == NULL) { + ESP_LOGD(TAG, "ftruncate NULL file pointer"); + errno = EINVAL; + ret = -1; + goto out; + } + + long sz = f_size(file); + if (sz < length) { + ESP_LOGD(TAG, "ftruncate does not support extending size"); + errno = EPERM; + ret = -1; + goto out; + } + + res = f_lseek(file, length); + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + ret = -1; + goto out; + } + + res = f_truncate(file); + + if (res != FR_OK) { + ESP_LOGD(TAG, "%s: fresult=%d", __func__, res); + errno = fresult_to_errno(res); + ret = -1; + } + +out: + _lock_release(&fat_ctx->lock); + return ret; +} + static int vfs_fat_utime(void *ctx, const char *path, const struct utimbuf *times) { FILINFO filinfo_time; diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index 0498a3aa59..d2634670d4 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -195,6 +195,10 @@ typedef struct int (*truncate_p)(void* ctx, const char *path, off_t length); /*!< truncate with context pointer */ int (*truncate)(const char *path, off_t length); /*!< truncate without context pointer */ }; + union { + int (*ftruncate_p)(void* ctx, int fd, off_t length); /*!< ftruncate with context pointer */ + int (*ftruncate)(int fd, off_t length); /*!< ftruncate without context pointer */ + }; union { int (*utime_p)(void* ctx, const char *path, const struct utimbuf *times); /*!< utime with context pointer */ int (*utime)(const char *path, const struct utimbuf *times); /*!< utime without context pointer */ diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index 38ddb91aff..a0e42cff78 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -792,6 +792,20 @@ int esp_vfs_truncate(const char *path, off_t length) return ret; } +int esp_vfs_ftruncate(int fd, off_t length) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + int local_fd = get_local_fd(vfs, fd); + struct _reent* r = __getreent(); + if (vfs == NULL || local_fd < 0) { + __errno_r(r) = EBADF; + return -1; + } + int ret; + CHECK_AND_CALL(ret, r, vfs, ftruncate, local_fd, length); + return ret; +} + #endif // CONFIG_VFS_SUPPORT_DIR #ifdef CONFIG_VFS_SUPPORT_SELECT @@ -1256,6 +1270,8 @@ int _rename_r(struct _reent *r, const char *src, const char *dst) __attribute__((alias("esp_vfs_rename"))); int truncate(const char *path, off_t length) __attribute__((alias("esp_vfs_truncate"))); +int ftruncate(int fd, off_t length) + __attribute__((alias("esp_vfs_ftruncate"))); int access(const char *path, int amode) __attribute__((alias("esp_vfs_access"))); int utime(const char *path, const struct utimbuf *times) diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index aca4310a29..ee572b824c 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -789,7 +789,6 @@ components/fatfs/src/ff.h components/fatfs/src/ffconf.h components/fatfs/src/ffsystem.c components/fatfs/src/ffunicode.c -components/fatfs/test/test_fatfs_common.c components/fatfs/test/test_fatfs_rawflash.c components/fatfs/test_fatfs_host/main.cpp components/fatfs/test_fatfs_host/sdkconfig/sdkconfig.h