Merge branch 'feature/fs_truncate_support' into 'master'

FATFS truncate support

See merge request idf/esp-idf!2541
This commit is contained in:
Angus Gratton 2018-06-14 13:56:39 +08:00
commit c77f84e788
9 changed files with 214 additions and 2 deletions

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -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();

View File

@ -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 */

View File

@ -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 */

View File

@ -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) {