VFS: Implement pread() and pwrite()
Closes https://github.com/espressif/esp-idf/issues/3515
This commit is contained in:
parent
1ea68e6093
commit
41062bea99
10 changed files with 307 additions and 0 deletions
|
@ -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;
|
||||
|
|
|
@ -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
21
components/newlib/pread.c
Normal 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);
|
||||
}
|
21
components/newlib/pwrite.c
Normal file
21
components/newlib/pwrite.c
Normal 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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue