mirror of
https://github.com/espressif/esp-idf.git
synced 2025-11-02 08:01:43 +01:00
VFS: Implement pread() and pwrite()
Closes https://github.com/espressif/esp-idf/issues/3515
This commit is contained in:
@@ -16,8 +16,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <utime.h>
|
||||
#include "unity.h"
|
||||
@@ -98,6 +100,88 @@ void test_fatfs_read_file_utf_8(const char* filename)
|
||||
TEST_ASSERT_EQUAL(0, fclose(f));
|
||||
}
|
||||
|
||||
void test_fatfs_pread_file(const char* filename)
|
||||
{
|
||||
char buf[32] = { 0 };
|
||||
const int fd = open(filename, O_RDONLY);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
int r = pread(fd, buf, sizeof(buf), 0); // it is a regular read() with offset==0
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str, buf));
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), r);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
r = pread(fd, buf, sizeof(buf), 1); // offset==1
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str + 1, buf));
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str) - 1, r);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
r = pread(fd, buf, sizeof(buf), 5); // offset==5
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str + 5, buf));
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str) - 5, r);
|
||||
|
||||
// regular read() should work now because pread() should not affect the current position in file
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
r = read(fd, buf, sizeof(buf)); // note that this is read() and not pread()
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str, buf));
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str), r);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
r = pread(fd, buf, sizeof(buf), 10); // offset==10
|
||||
TEST_ASSERT_EQUAL(0, strcmp(fatfs_test_hello_str + 10, buf));
|
||||
TEST_ASSERT_EQUAL(strlen(fatfs_test_hello_str) - 10, r);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
r = pread(fd, buf, sizeof(buf), strlen(fatfs_test_hello_str) + 1); // offset to EOF
|
||||
TEST_ASSERT_EQUAL(0, r);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, close(fd));
|
||||
}
|
||||
|
||||
static void test_pwrite(const char *filename, off_t offset, const char *msg)
|
||||
{
|
||||
const int fd = open(filename, O_WRONLY);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
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), offset);
|
||||
TEST_ASSERT_EQUAL(strlen(msg), r);
|
||||
|
||||
TEST_ASSERT_EQUAL(current_pos, lseek(fd, 0, SEEK_CUR)); // pwrite should not move the pointer
|
||||
|
||||
TEST_ASSERT_EQUAL(0, close(fd));
|
||||
}
|
||||
|
||||
static void test_file_content(const char *filename, const char *msg)
|
||||
{
|
||||
char buf[32] = { 0 };
|
||||
const int fd = open(filename, O_RDONLY);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
|
||||
int r = read(fd, buf, sizeof(buf));
|
||||
TEST_ASSERT_NOT_EQUAL(-1, r);
|
||||
TEST_ASSERT_EQUAL(0, strcmp(msg, buf));
|
||||
|
||||
TEST_ASSERT_EQUAL(0, close(fd));
|
||||
}
|
||||
|
||||
void test_fatfs_pwrite_file(const char *filename)
|
||||
{
|
||||
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
TEST_ASSERT_NOT_EQUAL(-1, fd);
|
||||
TEST_ASSERT_EQUAL(0, close(fd));
|
||||
|
||||
test_pwrite(filename, 0, "Hello");
|
||||
test_file_content(filename, "Hello");
|
||||
|
||||
test_pwrite(filename, strlen("Hello"), ", world!");
|
||||
test_file_content(filename, "Hello, world!");
|
||||
test_pwrite(filename, strlen("Hello, "), "Dolly");
|
||||
test_file_content(filename, "Hello, Dolly!");
|
||||
}
|
||||
|
||||
void test_fatfs_open_max_files(const char* filename_prefix, size_t files_count)
|
||||
{
|
||||
FILE** files = calloc(files_count, sizeof(FILE*));
|
||||
|
||||
@@ -41,6 +41,10 @@ void test_fatfs_read_file(const char* filename);
|
||||
|
||||
void test_fatfs_read_file_utf_8(const char* filename);
|
||||
|
||||
void test_fatfs_pread_file(const char* filename);
|
||||
|
||||
void test_fatfs_pwrite_file(const char* filename);
|
||||
|
||||
void test_fatfs_open_max_files(const char* filename_prefix, size_t files_count);
|
||||
|
||||
void test_fatfs_lseek(const char* filename);
|
||||
|
||||
@@ -86,6 +86,20 @@ TEST_CASE("(SD) can read file", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) can read file with pread()", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_create_file_with_text(test_filename, fatfs_test_hello_str);
|
||||
test_fatfs_pread_file(test_filename);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) pwrite() works well", "[fatfs][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_pwrite_file(test_filename);
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(SD) overwrite and append file", "[fatfs][sd][test_env=UT_T1_SDMODE]")
|
||||
{
|
||||
|
||||
@@ -70,6 +70,21 @@ TEST_CASE("(WL) can read file", "[fatfs][wear_levelling]")
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) can read file with pread", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_create_file_with_text("/spiflash/hello.txt", fatfs_test_hello_str);
|
||||
test_fatfs_pread_file("/spiflash/hello.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) pwrite() works well", "[fatfs][wear_levelling]")
|
||||
{
|
||||
test_setup();
|
||||
test_fatfs_pwrite_file("/spiflash/hello.txt");
|
||||
test_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("(WL) can open maximum number of files", "[fatfs][wear_levelling]")
|
||||
{
|
||||
size_t max_files = FOPEN_MAX - 3; /* account for stdin, stdout, stderr */
|
||||
|
||||
@@ -68,6 +68,8 @@ static const char* TAG = "vfs_fat";
|
||||
static ssize_t vfs_fat_write(void* p, int fd, const void * data, size_t size);
|
||||
static off_t vfs_fat_lseek(void* p, int fd, off_t size, int mode);
|
||||
static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size);
|
||||
static ssize_t vfs_fat_pread(void *ctx, int fd, void *dst, size_t size, off_t offset);
|
||||
static ssize_t vfs_fat_pwrite(void *ctx, int fd, const void *src, size_t size, off_t offset);
|
||||
static int vfs_fat_open(void* ctx, const char * path, int flags, int mode);
|
||||
static int vfs_fat_close(void* ctx, int fd);
|
||||
static int vfs_fat_fstat(void* ctx, int fd, struct stat * st);
|
||||
@@ -129,6 +131,8 @@ esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, siz
|
||||
.write_p = &vfs_fat_write,
|
||||
.lseek_p = &vfs_fat_lseek,
|
||||
.read_p = &vfs_fat_read,
|
||||
.pread_p = &vfs_fat_pread,
|
||||
.pwrite_p = &vfs_fat_pwrite,
|
||||
.open_p = &vfs_fat_open,
|
||||
.close_p = &vfs_fat_close,
|
||||
.fstat_p = &vfs_fat_fstat,
|
||||
@@ -373,6 +377,84 @@ static ssize_t vfs_fat_read(void* ctx, int fd, void * dst, size_t size)
|
||||
return read;
|
||||
}
|
||||
|
||||
static ssize_t vfs_fat_pread(void *ctx, int fd, void *dst, size_t size, off_t offset)
|
||||
{
|
||||
ssize_t ret = -1;
|
||||
vfs_fat_ctx_t *fat_ctx = (vfs_fat_ctx_t *) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
FIL *file = &fat_ctx->files[fd];
|
||||
const off_t prev_pos = f_tell(file);
|
||||
|
||||
FRESULT f_res = f_lseek(file, offset);
|
||||
if (f_res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
|
||||
errno = fresult_to_errno(f_res);
|
||||
goto pread_release;
|
||||
}
|
||||
|
||||
unsigned read = 0;
|
||||
f_res = f_read(file, dst, size, &read);
|
||||
if (f_res == FR_OK) {
|
||||
ret = read;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
|
||||
errno = fresult_to_errno(f_res);
|
||||
// No return yet - need to restore previous position
|
||||
}
|
||||
|
||||
f_res = f_lseek(file, prev_pos);
|
||||
if (f_res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
|
||||
if (ret >= 0) {
|
||||
errno = fresult_to_errno(f_res);
|
||||
} // else f_read failed so errno shouldn't be overwritten
|
||||
ret = -1; // in case the read was successful but the seek wasn't
|
||||
}
|
||||
|
||||
pread_release:
|
||||
_lock_release(&fat_ctx->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t vfs_fat_pwrite(void *ctx, int fd, const void *src, size_t size, off_t offset)
|
||||
{
|
||||
ssize_t ret = -1;
|
||||
vfs_fat_ctx_t *fat_ctx = (vfs_fat_ctx_t *) ctx;
|
||||
_lock_acquire(&fat_ctx->lock);
|
||||
FIL *file = &fat_ctx->files[fd];
|
||||
const off_t prev_pos = f_tell(file);
|
||||
|
||||
FRESULT f_res = f_lseek(file, offset);
|
||||
if (f_res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
|
||||
errno = fresult_to_errno(f_res);
|
||||
goto pwrite_release;
|
||||
}
|
||||
|
||||
unsigned wr = 0;
|
||||
f_res = f_write(file, src, size, &wr);
|
||||
if (f_res == FR_OK) {
|
||||
ret = wr;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
|
||||
errno = fresult_to_errno(f_res);
|
||||
// No return yet - need to restore previous position
|
||||
}
|
||||
|
||||
f_res = f_lseek(file, prev_pos);
|
||||
if (f_res != FR_OK) {
|
||||
ESP_LOGD(TAG, "%s: fresult=%d", __func__, f_res);
|
||||
if (ret >= 0) {
|
||||
errno = fresult_to_errno(f_res);
|
||||
} // else f_write failed so errno shouldn't be overwritten
|
||||
ret = -1; // in case the write was successful but the seek wasn't
|
||||
}
|
||||
|
||||
pwrite_release:
|
||||
_lock_release(&fat_ctx->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vfs_fat_fsync(void* ctx, int fd)
|
||||
{
|
||||
vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ctx;
|
||||
|
||||
Reference in New Issue
Block a user