Преглед изворни кода

vfs, fatfs: Add file truncate for fatfs

Renz Bagaporo пре 7 година
родитељ
комит
ea711f2ee9

+ 0 - 1
components/fatfs/src/ff.c

@@ -4694,7 +4694,6 @@ FRESULT f_truncate (
 	FATFS *fs;
 	DWORD ncl;
 
-
 	res = validate(&fp->obj, &fs);	/* Check validity of the file object */
 	if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
 	if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);	/* Check access mode */

+ 73 - 0
components/fatfs/src/vfs_fat.c

@@ -85,6 +85,7 @@ static int vfs_fat_closedir(void* ctx, DIR* pdir);
 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 vfs_fat_ctx_t* s_fat_ctxs[FF_VOLUMES] = { NULL, NULL };
 //backwards-compatibility with esp_vfs_fat_unregister()
@@ -144,6 +145,7 @@ esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, siz
         .mkdir_p = &vfs_fat_mkdir,
         .rmdir_p = &vfs_fat_rmdir,
         .access_p = &vfs_fat_access,
+        .truncate_p = &vfs_fat_truncate,
     };
     size_t ctx_size = sizeof(vfs_fat_ctx_t) + max_files * sizeof(FIL);
     vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) calloc(1, ctx_size);
@@ -751,3 +753,74 @@ static int vfs_fat_access(void* ctx, const char *path, int amode)
 
     return ret;
 }
+
+static int vfs_fat_truncate(void* ctx, const char *path, off_t length)
+{
+    FRESULT res;
+    FIL* file;
+
+    int ret = 0;
+    
+    vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
+
+    _lock_acquire(&fat_ctx->lock);
+    prepend_drive_to_path(fat_ctx, &path, NULL);
+
+    file = (FIL*) calloc(1, sizeof(FIL));
+    if (file == NULL) {
+        ESP_LOGD(TAG, "truncate alloc failed");
+        errno = ENOMEM;
+        ret = -1;
+        goto out;
+    }
+
+    res = f_open(file, path, FA_WRITE);
+
+    if (res != FR_OK) {
+        ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
+        errno = fresult_to_errno(res);
+        ret = -1;
+        goto out;
+    }
+
+    res = f_size(file);
+
+    if (res < length) {
+        ESP_LOGD(TAG, "truncate does not support extending size");
+        errno = EPERM;
+        ret = -1;
+        goto close;
+    }
+
+    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 close;
+    }
+
+    res = f_truncate(file);
+
+    if (res != FR_OK) {
+        ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
+        errno = fresult_to_errno(res);
+        ret = -1;
+    }
+
+close:
+    res = f_close(file);
+
+    if (res != FR_OK) {
+        ESP_LOGE(TAG, "closing file opened for truncate failed");
+        // Overwrite previous errors, since not being able to close
+        // an opened file is a more critical issue.
+        errno = fresult_to_errno(res);
+        ret = -1;
+    }
+
+out:
+    free(file);
+    _lock_release(&fat_ctx->lock);
+    return ret;
+}

+ 76 - 0
components/fatfs/test/test_fatfs_common.c

@@ -18,6 +18,7 @@
 #include <time.h>
 #include <sys/time.h>
 #include <sys/unistd.h>
+#include <errno.h>
 #include "unity.h"
 #include "esp_log.h"
 #include "esp_system.h"
@@ -137,6 +138,81 @@ void test_fatfs_lseek(const char* filename)
     TEST_ASSERT_EQUAL(0, fclose(f));
 }
 
+void test_fatfs_truncate_file(const char* filename)
+{
+    int read = 0;
+    int truncated_len = 0;
+
+    const char input[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+    char output[sizeof(input)];
+
+    FILE* f = fopen(filename, "wb");
+
+    TEST_ASSERT_NOT_NULL(f);
+    TEST_ASSERT_EQUAL(strlen(input), fprintf(f, input));
+
+    TEST_ASSERT_EQUAL(0, fclose(f));
+
+
+    // Extending file beyond size is not supported
+    TEST_ASSERT_EQUAL(-1, truncate(filename, strlen(input) + 1));
+    TEST_ASSERT_EQUAL(errno, EPERM);
+
+    TEST_ASSERT_EQUAL(-1, truncate(filename, -1));
+    TEST_ASSERT_EQUAL(errno, EPERM);
+
+
+    // Truncating should succeed
+    const char truncated_1[] = "ABCDEFGHIJ";
+    truncated_len = strlen(truncated_1);
+
+    TEST_ASSERT_EQUAL(0, truncate(filename, truncated_len));
+
+    f = fopen(filename, "rb");
+    TEST_ASSERT_NOT_NULL(f);
+    
+    memset(output, 0, sizeof(output));
+    read = fread(output, 1, sizeof(output), f);
+    
+    TEST_ASSERT_EQUAL(truncated_len, read);
+    TEST_ASSERT_EQUAL_STRING_LEN(truncated_1, output, truncated_len);
+
+    TEST_ASSERT_EQUAL(0, fclose(f));
+
+
+    // Once truncated, the new file size should be the basis 
+    // whether truncation should succeed or not
+    TEST_ASSERT_EQUAL(-1, truncate(filename, truncated_len + 1));
+    TEST_ASSERT_EQUAL(EPERM, errno);
+
+    TEST_ASSERT_EQUAL(-1, truncate(filename, strlen(input)));
+    TEST_ASSERT_EQUAL(EPERM, errno);
+
+    TEST_ASSERT_EQUAL(-1, truncate(filename, strlen(input) + 1));
+    TEST_ASSERT_EQUAL(EPERM, errno);
+
+    TEST_ASSERT_EQUAL(-1, truncate(filename, -1));
+    TEST_ASSERT_EQUAL(EPERM, errno);
+
+
+    // Truncating a truncated file should succeed
+    const char truncated_2[] = "ABCDE";
+    truncated_len = strlen(truncated_2);
+
+    TEST_ASSERT_EQUAL(0, truncate(filename, truncated_len));
+
+    f = fopen(filename, "rb");
+    TEST_ASSERT_NOT_NULL(f);
+    
+    memset(output, 0, sizeof(output));
+    read = fread(output, 1, sizeof(output), f);
+    
+    TEST_ASSERT_EQUAL(truncated_len, read);
+    TEST_ASSERT_EQUAL_STRING_LEN(truncated_2, output, truncated_len);
+
+    TEST_ASSERT_EQUAL(0, fclose(f));
+}
+
 void test_fatfs_stat(const char* filename, const char* root_dir)
 {
     struct tm tm;

+ 3 - 0
components/fatfs/test/test_fatfs_common.h

@@ -46,6 +46,8 @@ void test_fatfs_open_max_files(const char* filename_prefix, size_t files_count);
 
 void test_fatfs_lseek(const char* filename);
 
+void test_fatfs_truncate_file(const char* path);
+
 void test_fatfs_stat(const char* filename, const char* root_dir);
 
 void test_fatfs_unlink(const char* filename);
@@ -63,3 +65,4 @@ void test_fatfs_opendir_readdir_rewinddir(const char* dir_prefix);
 void test_fatfs_opendir_readdir_rewinddir_utf_8(const char* dir_prefix);
 
 void test_fatfs_rw_speed(const char* filename, void* buf, size_t buf_size, size_t file_size, bool write);
+

+ 7 - 1
components/fatfs/test/test_fatfs_sdmmc.c

@@ -95,7 +95,6 @@ TEST_CASE("(SD) overwrite and append file", "[fatfs][sd][test_env=UT_T1_SDMODE]"
     test_teardown();
 }
 
-
 TEST_CASE("(SD) can lseek", "[fatfs][sd][test_env=UT_T1_SDMODE]")
 {
     test_setup();
@@ -103,6 +102,13 @@ TEST_CASE("(SD) can lseek", "[fatfs][sd][test_env=UT_T1_SDMODE]")
     test_teardown();
 }
 
+TEST_CASE("(SD) can truncate", "[fatfs][sd][test_env=UT_T1_SDMODE]")
+{
+    test_setup();
+    test_fatfs_truncate_file("/sdcard/truncate.txt");
+    test_teardown();
+}
+
 TEST_CASE("(SD) stat returns correct values", "[fatfs][test_env=UT_T1_SDMODE]")
 {
     test_setup();

+ 7 - 0
components/fatfs/test/test_fatfs_spiflash.c

@@ -96,6 +96,13 @@ TEST_CASE("(WL) can lseek", "[fatfs][wear_levelling]")
     test_teardown();
 }
 
+TEST_CASE("(WL) can truncate", "[fatfs][wear_levelling]")
+{
+    test_setup();
+    test_fatfs_truncate_file("/spiflash/truncate.txt");
+    test_teardown();
+}
+
 TEST_CASE("(WL) stat returns correct values", "[fatfs][wear_levelling]")
 {
     test_setup();

+ 30 - 0
components/newlib/platform_include/sys/unistd.h

@@ -0,0 +1,30 @@
+// Copyright 2018 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.
+
+
+#ifndef _ESP_SYS_UNISTD_H
+#define _ESP_SYS_UNISTD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include_next <sys/unistd.h>
+
+int     _EXFUN(truncate, (const char *, off_t __length));
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _SYS_UNISTD_H */

+ 4 - 0
components/vfs/include/esp_vfs.h

@@ -174,6 +174,10 @@ typedef struct
         int (*access_p)(void* ctx, const char *path, int amode);
         int (*access)(const char *path, int amode);
     };
+    union {
+        int (*truncate_p)(void* ctx, const char *path, off_t length);
+        int (*truncate)(const char *path, off_t length);
+    };
     /** start_select is called for setting up synchronous I/O multiplexing of the desired file descriptors in the given VFS */
     esp_err_t (*start_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, SemaphoreHandle_t *signal_sem);
     /** socket select function for socket FDs with the functionality of POSIX select(); this should be set only for the socket VFS */

+ 14 - 0
components/vfs/vfs.c

@@ -717,6 +717,20 @@ int access(const char *path, int amode)
     return ret;
 }
 
+int truncate(const char *path, off_t length)
+{
+    int ret;
+    const vfs_entry_t* vfs = get_vfs_for_path(path);
+    struct _reent* r = __getreent();
+    if (vfs == NULL) {
+        __errno_r(r) = ENOENT;
+        return -1;
+    }
+    const char* path_within_vfs = translate_path(vfs, path);
+    CHECK_AND_CALL(ret, r, vfs, truncate, path_within_vfs, length);
+    return ret;
+}
+
 static void call_end_selects(int end_index, const fds_triple_t *vfs_fds_triple)
 {
     for (int i = 0; i < end_index; ++i) {