VFS: Implement pread() and pwrite()

Closes https://github.com/espressif/esp-idf/issues/3515
This commit is contained in:
Roland Dobai 2019-07-01 11:08:57 +02:00 committed by bot
parent 1ea68e6093
commit 41062bea99
10 changed files with 307 additions and 0 deletions

View file

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

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

View file

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

View file

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

View file

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

View file

@ -2,6 +2,8 @@ set(srcs
"heap.c"
"locks.c"
"poll.c"
"pread.c"
"pwrite.c"
"pthread.c"
"random.c"
"reent_init.c"

21
components/newlib/pread.c Normal file
View file

@ -0,0 +1,21 @@
// Copyright 2019 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.
#include <unistd.h>
#include "esp_vfs.h"
ssize_t pread(int fd, void *dst, size_t size, off_t offset)
{
return esp_vfs_pread(fd, dst, size, offset);
}

View file

@ -0,0 +1,21 @@
// Copyright 2019 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.
#include <unistd.h>
#include "esp_vfs.h"
ssize_t pwrite(int fd, const void *src, size_t size, off_t offset)
{
return esp_vfs_pwrite(fd, src, size, offset);
}

View file

@ -112,6 +112,14 @@ typedef struct
ssize_t (*read_p)(void* ctx, int fd, void * dst, size_t size);
ssize_t (*read)(int fd, void * dst, size_t size);
};
union {
ssize_t (*pread_p)(void *ctx, int fd, void * dst, size_t size, off_t offset);
ssize_t (*pread)(int fd, void * dst, size_t size, off_t offset);
};
union {
ssize_t (*pwrite_p)(void *ctx, int fd, const void *src, size_t size, off_t offset);
ssize_t (*pwrite)(int fd, const void *src, size_t size, off_t offset);
};
union {
int (*open_p)(void* ctx, const char * path, int flags, int mode);
int (*open)(const char * path, int flags, int mode);
@ -414,6 +422,35 @@ void esp_vfs_select_triggered_isr(esp_vfs_select_sem_t sem, BaseType_t *woken);
*/
int esp_vfs_poll(struct pollfd *fds, nfds_t nfds, int timeout);
/**
*
* @brief Implements the VFS layer of POSIX pread()
*
* @param fd File descriptor used for read
* @param dst Pointer to the buffer where the output will be written
* @param size Number of bytes to be read
* @param offset Starting offset of the read
*
* @return A positive return value indicates the number of bytes read. -1 is return on failure and errno is
* set accordingly.
*/
ssize_t esp_vfs_pread(int fd, void *dst, size_t size, off_t offset);
/**
*
* @brief Implements the VFS layer of POSIX pwrite()
*
* @param fd File descriptor used for write
* @param src Pointer to the buffer from where the output will be read
* @param size Number of bytes to write
* @param offset Starting offset of the write
*
* @return A positive return value indicates the number of bytes written. -1 is return on failure and errno is
* set accordingly.
*/
ssize_t esp_vfs_pwrite(int fd, const void *src, size_t size, off_t offset);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -447,6 +447,33 @@ ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size)
return ret;
}
ssize_t esp_vfs_pread(int fd, void *dst, size_t size, off_t offset)
{
struct _reent *r = __getreent();
const vfs_entry_t* vfs = get_vfs_for_fd(fd);
const int local_fd = get_local_fd(vfs, fd);
if (vfs == NULL || local_fd < 0) {
__errno_r(r) = EBADF;
return -1;
}
ssize_t ret;
CHECK_AND_CALL(ret, r, vfs, pread, local_fd, dst, size, offset);
return ret;
}
ssize_t esp_vfs_pwrite(int fd, const void *src, size_t size, off_t offset)
{
struct _reent *r = __getreent();
const vfs_entry_t* vfs = get_vfs_for_fd(fd);
const int local_fd = get_local_fd(vfs, fd);
if (vfs == NULL || local_fd < 0) {
__errno_r(r) = EBADF;
return -1;
}
ssize_t ret;
CHECK_AND_CALL(ret, r, vfs, pwrite, local_fd, src, size, offset);
return ret;
}
int esp_vfs_close(struct _reent *r, int fd)
{