#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"
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*));
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);
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]")
{
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 */
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);
.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,
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;
"heap.c"
"locks.c"
"poll.c"
+ "pread.c"
+ "pwrite.c"
"pthread.c"
"random.c"
"reent_init.c"
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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);
+}
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);
*/
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
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)
{