]> granicus.if.org Git - esp-idf/commitdiff
VFS: Implement pread() and pwrite()
authorRoland Dobai <dobai.roland@gmail.com>
Mon, 1 Jul 2019 09:08:57 +0000 (11:08 +0200)
committerbot <bot@espressif.com>
Thu, 11 Jul 2019 06:20:21 +0000 (06:20 +0000)
Closes https://github.com/espressif/esp-idf/issues/3515

components/fatfs/test/test_fatfs_common.c
components/fatfs/test/test_fatfs_common.h
components/fatfs/test/test_fatfs_sdmmc.c
components/fatfs/test/test_fatfs_spiflash.c
components/fatfs/vfs/vfs_fat.c
components/newlib/CMakeLists.txt
components/newlib/pread.c [new file with mode: 0644]
components/newlib/pwrite.c [new file with mode: 0644]
components/vfs/include/esp_vfs.h
components/vfs/vfs.c

index 77e53707c522f53347c16af700b0fcaa45fb28fa..09e29382681e5cccb48dd33ebcd63f61c8f7da73 100644 (file)
 #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*));
index ba330b6b6b719f0235cf46b38c02665b1a2de52d..0a792434b1c187c137cb0caefa19e0bae3878959 100644 (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);
index b2df8f0105b299792977d39faa2e0bd85c9b0188..50e1eb5a08f340d04393157b226999139e132de3 100644 (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]")
 {
index 6c33559395e9ecc7560654f00c56a1161b2fe330..f848c31ca85434ae1468475d42fb6e193eb545e5 100644 (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 */
index 1ee269590364e3eb8f39d3fe6fca762e9bafeb64..511be8c56e06c0a0af1e4b71080abd5c1b535ee9 100644 (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;
index 47b7c368b3424f3d6fa12509c1ad9db9ad565ab7..ccd5a766935360870521f34d8dd585e9531e98a2 100644 (file)
@@ -2,6 +2,8 @@ set(srcs
     "heap.c"
     "locks.c"
     "poll.c"
+    "pread.c"
+    "pwrite.c"
     "pthread.c"
     "random.c"
     "reent_init.c"
diff --git a/components/newlib/pread.c b/components/newlib/pread.c
new file mode 100644 (file)
index 0000000..d14bd72
--- /dev/null
@@ -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);
+}
diff --git a/components/newlib/pwrite.c b/components/newlib/pwrite.c
new file mode 100644 (file)
index 0000000..78fd0b8
--- /dev/null
@@ -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);
+}
index 3e711f25406ab5f8f5d4de079a294fcd822a7d88..4d9396c1c4581946201b8147777e41cda442b71e 100644 (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
index ecbaf6acf13f223f981906159008f2e517f76f64..0fc5aead31bb823ba581ff9a50879246e97ae6dd 100644 (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)
 {