mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/fatfs_expand_files_api' into 'master'
feat(fatfs): Allow expanding files with seeking and truncating functions Closes IDFGH-12035 See merge request espressif/esp-idf!28945
This commit is contained in:
commit
b76123af06
@ -40,7 +40,7 @@
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_EXPAND 0
|
||||
#define FF_USE_EXPAND 1
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
|
@ -190,10 +190,26 @@ TEST_CASE("(WL) can lseek", "[fatfs][wear_levelling]")
|
||||
TEST_CASE("(WL) can truncate", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_truncate_file("/spiflash/truncate.txt");
|
||||
test_fatfs_truncate_file("/spiflash/truncate.txt", true);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) can ftruncate", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_ftruncate_file("/spiflash/ftrunc.txt", true);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
#if FF_USE_EXPAND
|
||||
TEST_CASE("(WL) can esp_vfs_fat_create_contiguous_file", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_create_contiguous_file("/spiflash", "/spiflash/expand.txt");
|
||||
test_teardown();
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("(WL) stat returns correct values", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
|
@ -186,7 +186,7 @@ TEST_CASE("(SD) can truncate", "[fatfs][sdmmc]")
|
||||
{
|
||||
sdmmc_card_t *card = NULL;
|
||||
test_setup_sdmmc(&card);
|
||||
test_fatfs_truncate_file("/sdcard/truncate.txt");
|
||||
test_fatfs_truncate_file("/sdcard/truncate.txt", true);
|
||||
test_teardown_sdmmc(card);
|
||||
}
|
||||
|
||||
@ -194,10 +194,20 @@ TEST_CASE("(SD) can ftruncate", "[fatfs][sdmmc]")
|
||||
{
|
||||
sdmmc_card_t *card = NULL;
|
||||
test_setup_sdmmc(&card);
|
||||
test_fatfs_ftruncate_file("/sdcard/ftrunc.txt");
|
||||
test_fatfs_ftruncate_file("/sdcard/ftrunc.txt", true);
|
||||
test_teardown_sdmmc(card);
|
||||
}
|
||||
|
||||
#if FF_USE_EXPAND
|
||||
TEST_CASE("(SD) can esp_vfs_fat_create_contiguous_file", "[fatfs][sdmmc]")
|
||||
{
|
||||
sdmmc_card_t *card = NULL;
|
||||
test_setup_sdmmc(&card);
|
||||
test_fatfs_create_contiguous_file("/sdcard", "/sdcard/expand.txt");
|
||||
test_teardown_sdmmc(card);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("(SD) stat returns correct values", "[fatfs][sdmmc]")
|
||||
{
|
||||
sdmmc_card_t *card = NULL;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -21,6 +21,7 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "test_fatfs_common.h"
|
||||
#include "ff.h"
|
||||
|
||||
const char* fatfs_test_hello_str = "Hello, World!\n";
|
||||
const char* fatfs_test_hello_str_utf = "世界,你好!\n";
|
||||
@ -253,7 +254,7 @@ void test_fatfs_lseek(const char* filename)
|
||||
|
||||
}
|
||||
|
||||
void test_fatfs_truncate_file(const char* filename)
|
||||
void test_fatfs_truncate_file(const char* filename, bool allow_expanding_files)
|
||||
{
|
||||
int read = 0;
|
||||
int truncated_len = 0;
|
||||
@ -268,14 +269,44 @@ void test_fatfs_truncate_file(const char* filename)
|
||||
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
|
||||
struct stat st;
|
||||
size_t size;
|
||||
|
||||
// Extending file beyond size is not supported
|
||||
TEST_ASSERT_EQUAL(-1, truncate(filename, strlen(input) + 1));
|
||||
TEST_ASSERT_EQUAL(errno, EPERM);
|
||||
stat(filename, &st);
|
||||
size = st.st_size;
|
||||
TEST_ASSERT_EQUAL(strlen(input), size);
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, truncate(filename, -1));
|
||||
TEST_ASSERT_EQUAL(errno, EINVAL);
|
||||
if (allow_expanding_files) {
|
||||
size_t trunc_add = 2;
|
||||
size_t new_size = strlen(input) + trunc_add;
|
||||
TEST_ASSERT_EQUAL(0, truncate(filename, new_size));
|
||||
|
||||
stat(filename, &st);
|
||||
size = st.st_size;
|
||||
TEST_ASSERT_EQUAL(new_size, size);
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
|
||||
char expanded_output[sizeof(input) + trunc_add];
|
||||
memset(expanded_output, 42, sizeof(expanded_output)); // set to something else than 0 (42)
|
||||
|
||||
read = fread(expanded_output, 1, sizeof(input) + trunc_add, f);
|
||||
TEST_ASSERT_EQUAL(new_size, read);
|
||||
|
||||
TEST_ASSERT_EQUAL('Z', expanded_output[strlen(input) - 1]); // 'Z' character
|
||||
TEST_ASSERT_EQUAL('\0', expanded_output[sizeof(input) + trunc_add - 3]); // zeroed expanded space
|
||||
TEST_ASSERT_EQUAL('\0', expanded_output[sizeof(input) + trunc_add - 2]); // zeroed expanded space
|
||||
TEST_ASSERT_EQUAL(42, expanded_output[sizeof(input) + trunc_add - 1]); // 42 set with memset, end of the array
|
||||
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
} else {
|
||||
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, EINVAL);
|
||||
}
|
||||
|
||||
// Truncating should succeed
|
||||
const char truncated_1[] = "ABCDEFGHIJ";
|
||||
@ -283,6 +314,10 @@ void test_fatfs_truncate_file(const char* filename)
|
||||
|
||||
TEST_ASSERT_EQUAL(0, truncate(filename, truncated_len));
|
||||
|
||||
stat(filename, &st);
|
||||
size = st.st_size;
|
||||
TEST_ASSERT_EQUAL(strlen(truncated_1), size);
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
|
||||
@ -294,28 +329,34 @@ void test_fatfs_truncate_file(const char* filename)
|
||||
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
|
||||
if (allow_expanding_files) {
|
||||
TEST_ASSERT_EQUAL(0, truncate(filename, truncated_len + 1));
|
||||
} else {
|
||||
// Once truncated, the new file size should be the basis
|
||||
// whether truncation should succeed or not when `allow_expanding_files == false`
|
||||
TEST_ASSERT_EQUAL(-1, truncate(filename, truncated_len + 1));
|
||||
TEST_ASSERT_EQUAL(EPERM, errno);
|
||||
|
||||
// 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)));
|
||||
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, strlen(input) + 1));
|
||||
TEST_ASSERT_EQUAL(EPERM, errno);
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, truncate(filename, -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, truncate(filename, truncated_len));
|
||||
|
||||
stat(filename, &st);
|
||||
size = st.st_size;
|
||||
TEST_ASSERT_EQUAL(strlen(truncated_2), size);
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
|
||||
@ -328,29 +369,63 @@ void test_fatfs_truncate_file(const char* filename)
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
}
|
||||
|
||||
void test_fatfs_ftruncate_file(const char* filename)
|
||||
void test_fatfs_ftruncate_file(const char* filename, bool allow_expanding_files)
|
||||
{
|
||||
int truncated_len = 0;
|
||||
|
||||
const char input[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
char output[sizeof(input)];
|
||||
|
||||
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
int fd = open(filename, O_RDWR | 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);
|
||||
struct stat st;
|
||||
size_t size;
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, ftruncate(fd, -1));
|
||||
TEST_ASSERT_EQUAL(errno, EINVAL);
|
||||
fstat(fd, &st);
|
||||
size = st.st_size;
|
||||
TEST_ASSERT_EQUAL(strlen(input), size);
|
||||
|
||||
if (allow_expanding_files) {
|
||||
size_t trunc_add = 2;
|
||||
size_t new_size = strlen(input) + trunc_add;
|
||||
TEST_ASSERT_EQUAL(0, ftruncate(fd, new_size));
|
||||
|
||||
fstat(fd, &st);
|
||||
size = st.st_size;
|
||||
TEST_ASSERT_EQUAL(new_size, size);
|
||||
|
||||
char expanded_output[sizeof(input) + trunc_add];
|
||||
memset(expanded_output, 42, sizeof(expanded_output)); // set to something else than 0 (42)
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
int r = read(fd, expanded_output, sizeof(input) + trunc_add);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, r);
|
||||
TEST_ASSERT_EQUAL(new_size, r);
|
||||
|
||||
TEST_ASSERT_EQUAL('Z', expanded_output[strlen(input) - 1]); // 'Z' character
|
||||
TEST_ASSERT_EQUAL('\0', expanded_output[sizeof(input) + trunc_add - 3]); // zeroed expanded space
|
||||
TEST_ASSERT_EQUAL('\0', expanded_output[sizeof(input) + trunc_add - 2]); // zeroed expanded space
|
||||
TEST_ASSERT_EQUAL(42, expanded_output[sizeof(input) + trunc_add - 1]); // 42 set with memset, end of the array
|
||||
} else {
|
||||
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));
|
||||
|
||||
fstat(fd, &st);
|
||||
size = st.st_size;
|
||||
TEST_ASSERT_EQUAL(truncated_len, size);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, close(fd));
|
||||
|
||||
// open file for reading and validate the content
|
||||
@ -368,25 +443,35 @@ void test_fatfs_ftruncate_file(const char* filename)
|
||||
// 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);
|
||||
if (allow_expanding_files) {
|
||||
TEST_ASSERT_EQUAL(0, ftruncate(fd, truncated_len + 1));
|
||||
} else {
|
||||
// Once truncated, the new file size should be the basis
|
||||
// whether truncation should succeed or not when `allow_expanding_files == false`
|
||||
TEST_ASSERT_EQUAL(-1, ftruncate(fd, truncated_len + 1));
|
||||
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, strlen(input)));
|
||||
TEST_ASSERT_EQUAL(EPERM, errno);
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, ftruncate(fd, -1));
|
||||
TEST_ASSERT_EQUAL(EINVAL, 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));
|
||||
|
||||
fstat(fd, &st);
|
||||
size = st.st_size;
|
||||
TEST_ASSERT_EQUAL(truncated_len, size);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, close(fd));
|
||||
|
||||
// open file for reading and validate the content
|
||||
@ -1012,3 +1097,28 @@ void test_fatfs_info(const char* base_path, const char* filepath)
|
||||
ESP_LOGD("fatfs info", "total_bytes=%llu, free_bytes_after_delete=%llu", total_bytes, free_bytes_new);
|
||||
TEST_ASSERT_EQUAL(free_bytes, free_bytes_new);
|
||||
}
|
||||
|
||||
#if FF_USE_EXPAND
|
||||
void test_fatfs_create_contiguous_file(const char* base_path, const char* full_path)
|
||||
{
|
||||
size_t desired_file_size = 64;
|
||||
|
||||
// Don't check for errors, file may not exist at first
|
||||
remove(full_path); // esp_vfs_fat_create_contiguous_file will fail if the file already exists
|
||||
|
||||
esp_err_t err = esp_vfs_fat_create_contiguous_file(base_path, full_path, desired_file_size, true);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, err);
|
||||
|
||||
struct stat st;
|
||||
size_t size;
|
||||
|
||||
stat(full_path, &st);
|
||||
size = st.st_size;
|
||||
TEST_ASSERT_EQUAL(desired_file_size, size);
|
||||
|
||||
bool is_contiguous = false;
|
||||
err = esp_vfs_fat_test_contiguous_file(base_path, full_path, &is_contiguous);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, err);
|
||||
TEST_ASSERT_TRUE(is_contiguous);
|
||||
}
|
||||
#endif
|
||||
|
@ -45,9 +45,9 @@ 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_truncate_file(const char* path, bool allow_expanding_files);
|
||||
|
||||
void test_fatfs_ftruncate_file(const char* path);
|
||||
void test_fatfs_ftruncate_file(const char* path, bool allow_expanding_files);
|
||||
|
||||
void test_fatfs_stat(const char* filename, const char* root_dir);
|
||||
|
||||
@ -76,3 +76,7 @@ void test_leading_spaces(void);
|
||||
void test_fatfs_rw_speed(const char* filename, void* buf, size_t buf_size, size_t file_size, bool write);
|
||||
|
||||
void test_fatfs_info(const char* base_path, const char* filepath);
|
||||
|
||||
#if FF_USE_EXPAND
|
||||
void test_fatfs_create_contiguous_file(const char* base_path, const char* full_path);
|
||||
#endif
|
||||
|
@ -401,6 +401,38 @@ esp_err_t esp_vfs_fat_spiflash_unmount_ro(const char* base_path, const char* par
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_info(const char* base_path, uint64_t* out_total_bytes, uint64_t* out_free_bytes);
|
||||
|
||||
/**
|
||||
* @brief Create a file with contiguous space at given path
|
||||
*
|
||||
* @note The file cannot exist before calling this function (or the file size has to be 0)
|
||||
* For more information see documentation for `f_expand` from FATFS library
|
||||
*
|
||||
* @param base_path Base path of the partition examined (e.g. "/spiflash")
|
||||
* @param full_path Full path of the file (e.g. "/spiflash/ABC.TXT")
|
||||
* @param size File size expanded to, number of bytes in size to prepare or allocate for the file
|
||||
* @param alloc_now True == allocate space now, false == prepare to allocate -- see `f_expand` from FATFS
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if invalid arguments (e.g. any of arguments are NULL or size lower or equal to 0)
|
||||
* - ESP_ERR_INVALID_STATE if partition not found
|
||||
* - ESP_FAIL if another FRESULT error (saved in errno)
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_create_contiguous_file(const char* base_path, const char* full_path, uint64_t size, bool alloc_now);
|
||||
|
||||
/**
|
||||
* @brief Test if a file is contiguous in the FAT filesystem
|
||||
*
|
||||
* @param base_path Base path of the partition examined (e.g. "/spiflash")
|
||||
* @param full_path Full path of the file (e.g. "/spiflash/ABC.TXT")
|
||||
* @param[out] is_contiguous True == allocate space now, false == prepare to allocate -- see `f_expand` from FATFS
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if invalid arguments (e.g. any of arguments are NULL)
|
||||
* - ESP_ERR_INVALID_STATE if partition not found
|
||||
* - ESP_FAIL if another FRESULT error (saved in errno)
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_test_contiguous_file(const char* base_path, const char* full_path, bool* is_contiguous);
|
||||
|
||||
/** @cond */
|
||||
/**
|
||||
* @deprecated Please use `esp_vfs_fat_register_cfg` instead
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/errno.h>
|
||||
@ -17,6 +18,8 @@
|
||||
#include "ff.h"
|
||||
#include "diskio_impl.h"
|
||||
|
||||
#define F_WRITE_MALLOC_ZEROING_BUF_SIZE_LIMIT 512
|
||||
|
||||
typedef struct {
|
||||
char fat_drive[8]; /* FAT drive name */
|
||||
char base_path[ESP_VFS_PATH_MAX]; /* base path in VFS where partition is registered */
|
||||
@ -993,6 +996,49 @@ static int vfs_fat_access(void* ctx, const char *path, int amode)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static FRESULT f_write_zero_mem(FIL* fp, FSIZE_t data_size, FSIZE_t buf_size, UINT* bytes_written)
|
||||
{
|
||||
if (fp == NULL || data_size <= 0 || buf_size <= 0) {
|
||||
return FR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buf = ff_memalloc(buf_size);
|
||||
if (buf == NULL) {
|
||||
return FR_DISK_ERR;
|
||||
}
|
||||
memset(buf, 0, buf_size);
|
||||
|
||||
FRESULT res = FR_OK;
|
||||
UINT bw = 0;
|
||||
FSIZE_t i = 0;
|
||||
if (bytes_written != NULL) {
|
||||
*bytes_written = 0;
|
||||
}
|
||||
|
||||
if (data_size > buf_size) { // prevent unsigned underflow
|
||||
for (; i < (data_size - buf_size); i += buf_size) { // write chunks of buf_size
|
||||
res = f_write(fp, buf, (UINT) buf_size, &bw);
|
||||
if (res != FR_OK) {
|
||||
goto out;
|
||||
}
|
||||
if (bytes_written != NULL) {
|
||||
*bytes_written += bw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i < data_size) { // write the remaining data
|
||||
res = f_write(fp, buf, (UINT) (data_size - i), &bw);
|
||||
if (res == FR_OK && bytes_written != NULL) {
|
||||
*bytes_written += bw;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
ff_memfree(buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int vfs_fat_truncate(void* ctx, const char *path, off_t length)
|
||||
{
|
||||
FRESULT res;
|
||||
@ -1031,31 +1077,55 @@ static int vfs_fat_truncate(void* ctx, const char *path, off_t length)
|
||||
goto out;
|
||||
}
|
||||
|
||||
long sz = f_size(file);
|
||||
if (sz < length) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "truncate does not support extending size");
|
||||
errno = EPERM;
|
||||
ret = -1;
|
||||
goto close;
|
||||
}
|
||||
FSIZE_t seek_ptr_pos = (FSIZE_t) f_tell(file); // current seek pointer position
|
||||
FSIZE_t sz = (FSIZE_t) f_size(file); // current file size (end of file position)
|
||||
|
||||
res = f_lseek(file, length);
|
||||
if (res != FR_OK) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
ret = -1;
|
||||
goto close;
|
||||
if (res != FR_OK || f_tell(file) != length) {
|
||||
goto lseek_or_write_fail;
|
||||
}
|
||||
|
||||
res = f_truncate(file);
|
||||
if (sz < length) {
|
||||
res = f_lseek(file, sz); // go to the previous end of file
|
||||
if (res != FR_OK) {
|
||||
goto lseek_or_write_fail;
|
||||
}
|
||||
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
ret = -1;
|
||||
goto close;
|
||||
FSIZE_t new_free_space = ((FSIZE_t) length) - sz;
|
||||
UINT written;
|
||||
|
||||
if (new_free_space > UINT32_MAX) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGE(TAG, "%s: Cannot extend the file more than 4GB at once", __func__);
|
||||
ret = -1;
|
||||
goto close;
|
||||
}
|
||||
|
||||
FSIZE_t buf_size_limit = F_WRITE_MALLOC_ZEROING_BUF_SIZE_LIMIT;
|
||||
FSIZE_t buf_size = new_free_space < buf_size_limit ? new_free_space : buf_size_limit;
|
||||
res = f_write_zero_mem(file, new_free_space, buf_size, &written);
|
||||
|
||||
if (res != FR_OK) {
|
||||
goto lseek_or_write_fail;
|
||||
} else if (written != (UINT) new_free_space) {
|
||||
res = FR_DISK_ERR;
|
||||
goto lseek_or_write_fail;
|
||||
}
|
||||
|
||||
res = f_lseek(file, seek_ptr_pos); // return to the original position
|
||||
if (res != FR_OK) {
|
||||
goto lseek_or_write_fail;
|
||||
}
|
||||
} else {
|
||||
res = f_truncate(file);
|
||||
|
||||
if (res != FR_OK) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
ret = -1;
|
||||
goto close;
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_FATFS_IMMEDIATE_FSYNC
|
||||
@ -1083,6 +1153,13 @@ close:
|
||||
out:
|
||||
free(file);
|
||||
return ret;
|
||||
|
||||
lseek_or_write_fail:
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
ret = -1;
|
||||
goto close;
|
||||
}
|
||||
|
||||
static int vfs_fat_ftruncate(void* ctx, int fd, off_t length)
|
||||
@ -1109,29 +1186,50 @@ static int vfs_fat_ftruncate(void* ctx, int fd, off_t length)
|
||||
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;
|
||||
}
|
||||
FSIZE_t seek_ptr_pos = (FSIZE_t) f_tell(file); // current seek pointer position
|
||||
FSIZE_t sz = (FSIZE_t) f_size(file); // current file size (end of file position)
|
||||
|
||||
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;
|
||||
if (res != FR_OK || f_tell(file) != length) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = f_truncate(file);
|
||||
if (sz < length) {
|
||||
res = f_lseek(file, sz); // go to the previous end of file
|
||||
if (res != FR_OK) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
ret = -1;
|
||||
goto out;
|
||||
FSIZE_t new_free_space = ((FSIZE_t) length) - sz;
|
||||
UINT written;
|
||||
|
||||
if (new_free_space > UINT32_MAX) {
|
||||
ESP_LOGE(TAG, "%s: Cannot extend the file more than 4GB at once", __func__);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
FSIZE_t buf_size_limit = F_WRITE_MALLOC_ZEROING_BUF_SIZE_LIMIT;
|
||||
FSIZE_t buf_size = new_free_space < buf_size_limit ? new_free_space : buf_size_limit;
|
||||
res = f_write_zero_mem(file, new_free_space, buf_size, &written);
|
||||
|
||||
if (res != FR_OK) {
|
||||
goto fail;
|
||||
} else if (written != (UINT) new_free_space) {
|
||||
res = FR_DISK_ERR;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = f_lseek(file, seek_ptr_pos); // return to the original position
|
||||
if (res != FR_OK) {
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
res = f_truncate(file);
|
||||
|
||||
if (res != FR_OK) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_FATFS_IMMEDIATE_FSYNC
|
||||
@ -1146,6 +1244,12 @@ static int vfs_fat_ftruncate(void* ctx, int fd, off_t length)
|
||||
out:
|
||||
_lock_release(&fat_ctx->lock);
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int vfs_fat_utime(void *ctx, const char *path, const struct utimbuf *times)
|
||||
@ -1201,3 +1305,138 @@ static int vfs_fat_utime(void *ctx, const char *path, const struct utimbuf *time
|
||||
}
|
||||
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
|
||||
esp_err_t esp_vfs_fat_create_contiguous_file(const char* base_path, const char* full_path, uint64_t size, bool alloc_now)
|
||||
{
|
||||
if (base_path == NULL || full_path == NULL || size <= 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
size_t ctx = find_context_index_by_path(base_path);
|
||||
if (ctx == FF_VOLUMES) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
vfs_fat_ctx_t* fat_ctx = s_fat_ctxs[ctx];
|
||||
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
const char* file_path = full_path + strlen(base_path); // shift the pointer and omit the base_path
|
||||
prepend_drive_to_path(fat_ctx, &file_path, NULL);
|
||||
|
||||
FIL* file = (FIL*) ff_memalloc(sizeof(FIL));
|
||||
if (file == NULL) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_create_contiguous_file alloc failed");
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
memset(file, 0, sizeof(*file));
|
||||
|
||||
FRESULT res = f_open(file, file_path, FA_WRITE | FA_OPEN_ALWAYS);
|
||||
if (res != FR_OK) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = f_expand(file, size, alloc_now ? 1 : 0);
|
||||
if (res != FR_OK) {
|
||||
f_close(file);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = f_close(file);
|
||||
if (res != FR_OK) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
_lock_release(&fat_ctx->lock);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static FRESULT test_contiguous_file( // From FATFS examples
|
||||
FIL* fp, /* [IN] Open file object to be checked */
|
||||
int* cont /* [OUT] 1:Contiguous, 0:Fragmented or zero-length */
|
||||
) {
|
||||
DWORD clst, clsz, step;
|
||||
FSIZE_t fsz;
|
||||
FRESULT fr;
|
||||
|
||||
*cont = 0;
|
||||
fr = f_rewind(fp); /* Validates and prepares the file */
|
||||
if (fr != FR_OK) return fr;
|
||||
|
||||
#if FF_MAX_SS == FF_MIN_SS
|
||||
clsz = (DWORD)fp->obj.fs->csize * FF_MAX_SS; /* Cluster size */
|
||||
#else
|
||||
clsz = (DWORD)fp->obj.fs->csize * fp->obj.fs->ssize;
|
||||
#endif
|
||||
fsz = f_size(fp);
|
||||
if (fsz > 0) {
|
||||
clst = fp->obj.sclust - 1; /* A cluster leading the first cluster for first test */
|
||||
while (fsz) {
|
||||
step = (fsz >= clsz) ? clsz : (DWORD)fsz;
|
||||
fr = f_lseek(fp, f_tell(fp) + step); /* Advances file pointer a cluster */
|
||||
if (fr != FR_OK) return fr;
|
||||
if (clst + 1 != fp->clust) break; /* Is not the cluster next to previous one? */
|
||||
clst = fp->clust; fsz -= step; /* Get current cluster for next test */
|
||||
}
|
||||
if (fsz == 0) *cont = 1; /* All done without fail? */
|
||||
}
|
||||
|
||||
return FR_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_test_contiguous_file(const char* base_path, const char* full_path, bool* is_contiguous)
|
||||
{
|
||||
if (base_path == NULL || full_path == NULL || is_contiguous == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
size_t ctx = find_context_index_by_path(base_path);
|
||||
if (ctx == FF_VOLUMES) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
vfs_fat_ctx_t* fat_ctx = s_fat_ctxs[ctx];
|
||||
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
const char* file_path = full_path + strlen(base_path); // shift the pointer and omit the base_path
|
||||
prepend_drive_to_path(fat_ctx, &file_path, NULL);
|
||||
|
||||
FIL* file = (FIL*) ff_memalloc(sizeof(FIL));
|
||||
if (file == NULL) {
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_test_contiguous_file alloc failed");
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
memset(file, 0, sizeof(*file));
|
||||
|
||||
FRESULT res = f_open(file, file_path, FA_WRITE | FA_OPEN_ALWAYS);
|
||||
if (res != FR_OK) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = test_contiguous_file(file, (int*) is_contiguous);
|
||||
if (res != FR_OK) {
|
||||
f_close(file);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = f_close(file);
|
||||
if (res != FR_OK) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
_lock_release(&fat_ctx->lock);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
_lock_release(&fat_ctx->lock);
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, res);
|
||||
errno = fresult_to_errno(res);
|
||||
return -1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user