浏览代码

Manual sync with esp_littlefs see commit ba48c4
Added #define CONFIG_LITTLEFS_SPIFFS_COMPAT,
when is set to 1 it is closer to SPIFFS folderless behaviour

lorol 4 年之前
父节点
当前提交
4e8cbbc786
共有 7 个文件被更改,包括 257 次插入47 次删除
  1. 1 1
      README.md
  2. 1 1
      library.json
  3. 3 3
      library.properties
  4. 251 36
      src/esp_littlefs.c
  5. 0 1
      src/esp_littlefs.h
  6. 0 5
      src/littlefs_api.c
  7. 1 0
      src/littlefs_api.h

+ 1 - 1
README.md

@@ -1,6 +1,6 @@
 # LittleFS_esp32
 
-### ***Notice: The Library is been integrated to [Arduino esp32 core esp32s2 branch](https://github.com/espressif/arduino-esp32/tree/esp32s2/ ) for future major core release.***
+### ***Notice: The Library is been integrated to [Arduino esp32 core idf-release/v4.2 branch](https://github.com/espressif/arduino-esp32/tree/idf-release/v4.2 ) for future major core release.***
 
 
 ## LittleFS library for arduino-esp32

+ 1 - 1
library.json

@@ -12,7 +12,7 @@
     "type": "git",
     "url": "https://github.com/lorol/LITTLEFS.git"
   },
-  "version": "1.0",
+  "version": "1.0.5",
   "license": "LGPL-2.0",
   "frameworks": "arduino",
   "platforms": "espressif32",

+ 3 - 3
library.properties

@@ -1,9 +1,9 @@
 name=LittleFS_esp32
-version=1.0
+version=1.0.5
 author=lorol
 maintainer=lorol
-sentence=LittleFS for esp32
-paragraph=LittleFS for esp32
+sentence=LittleFS for esp32 based on esp_littlefs IDF component. Use esp32 core-provided LITTLEFS library instead this one (when it gets available)
+paragraph=. See littlefs.c #define CONFIG_LITTLEFS_SPIFFS_COMPAT 1 and #define CONFIG_LITTLEFS_FOR_IDF_3_2 to adjust for best SPIFFS and for older core releases compatibility.
 category=Data Storage
 url=https://github.com/lorol/LITTLEFS
 architectures=esp32

+ 251 - 36
src/esp_littlefs.c

@@ -11,8 +11,9 @@
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
-//#define LOG_LOCAL_LEVEL 4
-//#define CONFIG_LITTLEFS_FOR_IDF_3_2   /* For old IDF - like in release 1.0.4 */
+//#define LOG_LOCAL_LEVEL 5
+//#define CONFIG_LITTLEFS_FOR_IDF_3_2      /* For old IDF - like in release 1.0.4 */
+#define CONFIG_LITTLEFS_SPIFFS_COMPAT 0    /* Use 1 for better drop-in replacement of SPIFFS */
 
 #include "esp_log.h"
 #include "esp_spi_flash.h"
@@ -37,6 +38,7 @@
 #include "esp_littlefs.h"
 #include "littlefs_api.h"
 
+
 static const char TAG[] = "esp_littlefs";
 
 #define CONFIG_LITTLEFS_BLOCK_SIZE 4096 /* ESP32 can only operate at 4kb */
@@ -64,7 +66,6 @@ static const char TAG[] = "esp_littlefs";
 #define CONFIG_LITTLEFS_MTIME_USE_SECONDS 1
 #endif
 
-
 /**
  * @brief littlefs DIR structure
  */
@@ -79,6 +80,8 @@ typedef struct {
 static int     vfs_littlefs_open(void* ctx, const char * path, int flags, int mode);
 static ssize_t vfs_littlefs_write(void* ctx, int fd, const void * data, size_t size);
 static ssize_t vfs_littlefs_read(void* ctx, int fd, void * dst, size_t size);
+//static ssize_t vfs_littlefs_pwrite(void *ctx, int fd, const void *src, size_t size, off_t offset);
+//static ssize_t vfs_littlefs_pread(void *ctx, int fd, void *dst, size_t size, off_t offset);
 static int     vfs_littlefs_close(void* ctx, int fd);
 static off_t   vfs_littlefs_lseek(void* ctx, int fd, off_t offset, int mode);
 static int     vfs_littlefs_stat(void* ctx, const char * path, struct stat * st);
@@ -115,6 +118,11 @@ static time_t    vfs_littlefs_get_mtime(esp_littlefs_t *efs, const char *path);
 static int     vfs_littlefs_fstat(void* ctx, int fd, struct stat * st);
 #endif
 
+#if CONFIG_LITTLEFS_SPIFFS_COMPAT
+static void mkdirs(esp_littlefs_t * efs, const char *dir);
+static void rmdirs(esp_littlefs_t * efs, const char *dir);
+#endif  // CONFIG_LITTLEFS_SPIFFS_COMPAT
+
 static int sem_take(esp_littlefs_t *efs);
 static int sem_give(esp_littlefs_t *efs);
 
@@ -171,8 +179,10 @@ esp_err_t esp_vfs_littlefs_register(const esp_vfs_littlefs_conf_t * conf)
     const esp_vfs_t vfs = {
         .flags       = ESP_VFS_FLAG_CONTEXT_PTR,
         .write_p     = &vfs_littlefs_write,
+//        .pwrite_p    = &vfs_littlefs_pwrite,
         .lseek_p     = &vfs_littlefs_lseek,
         .read_p      = &vfs_littlefs_read,
+//        .pread_p     = &vfs_littlefs_pread,
         .open_p      = &vfs_littlefs_open,
         .close_p     = &vfs_littlefs_close,
 #ifndef CONFIG_LITTLEFS_USE_ONLY_HASH
@@ -730,6 +740,8 @@ static int esp_littlefs_allocate_fd(esp_littlefs_t *efs, vfs_littlefs_file_t **
     */
     (*file)->path = (char*)(*file) + sizeof(**file);
 #endif
+
+    (*file)->open_count = 1;
  
     /* Now find a free place in cache */
     for(i=0; i < efs->cache_size; i++) {
@@ -885,33 +897,54 @@ static int vfs_littlefs_open(void* ctx, const char * path, int flags, int mode)
 
     /* Get a FD */
     sem_take(efs);
-    fd = esp_littlefs_allocate_fd(efs, &file
+
+    if((fd = esp_littlefs_get_fd_by_name(efs, path)) >= 0) {
+        /* FD is already open, increase the reference counter*/
+        efs->cache[fd]->open_count++;
+    }
+    else {
+        /* Need to allocate a new FD */
+        fd = esp_littlefs_allocate_fd(efs, &file
 #ifndef CONFIG_LITTLEFS_USE_ONLY_HASH
-    , path_len
+        , path_len
 #endif
-    );
-    if(fd < 0) {
-        errno = -fd;
-        sem_give(efs);
-        ESP_LOGV(TAG, "Error obtaining FD");
-        return LFS_ERR_INVAL;
-    }
-    /* Open File */
-    res = lfs_file_open(efs->fs, &file->file, path, lfs_flags);
+        );
 
-    if( res < 0 ) {
-        errno = -res;
-        esp_littlefs_free_fd(efs, fd);
-        sem_give(efs);
-        ESP_LOGV(TAG, "Failed to open file. Error %s (%d)",
-                esp_littlefs_errno(res), res);
-        return LFS_ERR_INVAL;
-    }
+        if(fd < 0) {
+            errno = -fd;
+            sem_give(efs);
+            ESP_LOGV(TAG, "Error obtaining FD");
+            return LFS_ERR_INVAL;
+        }
+
+#if CONFIG_LITTLEFS_SPIFFS_COMPAT
+        /* Create all parent directories (if necessary) */
+        ESP_LOGV(TAG, "LITTLEFS_SPIFFS_COMPAT attempting to create all directories for %s", path);
+        mkdirs(efs, path);
+#endif  // CONFIG_LITTLEFS_SPIFFS_COMPAT
+
+        /* Open File */
+        res = lfs_file_open(efs->fs, &file->file, path, lfs_flags);
 
-    file->hash = compute_hash(path);
+        if( res < 0 ) {
+            errno = -res;
+            esp_littlefs_free_fd(efs, fd);
+            sem_give(efs);
+#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH
+            ESP_LOGV(TAG, "Failed to open file %s. Error %s (%d)",
+                    path, esp_littlefs_errno(res), res);
+#else
+            ESP_LOGV(TAG, "Failed to open file. Error %s (%d)",
+                    esp_littlefs_errno(res), res);
+#endif
+            return LFS_ERR_INVAL;
+        }
+
+        file->hash = compute_hash(path);
 #ifndef CONFIG_LITTLEFS_USE_ONLY_HASH
-    memcpy(file->path, path, path_len);
+        memcpy(file->path, path, path_len);
 #endif
+    }
 
 #if CONFIG_LITTLEFS_USE_MTIME
     if (lfs_flags != LFS_O_RDONLY) {
@@ -986,33 +1019,159 @@ static ssize_t vfs_littlefs_read(void* ctx, int fd, void * dst, size_t size) {
     return res;
 }
 
+#if 0 //disabled
+
+static ssize_t vfs_littlefs_pwrite(void *ctx, int fd, const void *src, size_t size, off_t offset)
+{
+    esp_littlefs_t *efs = (esp_littlefs_t *)ctx;
+    ssize_t res, save_res;
+    vfs_littlefs_file_t *file = NULL;
+
+    sem_take(efs);
+    if ((uint32_t)fd > efs->cache_size)
+    {
+        sem_give(efs);
+        ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size);
+        return LFS_ERR_BADF;
+    }
+    file = efs->cache[fd];
+
+    off_t old_offset = lfs_file_seek(efs->fs, &file->file, 0, SEEK_CUR);
+    if (old_offset < (off_t)0)
+    {
+        res = old_offset;
+        goto exit;
+    }
+
+    /* Set to wanted position.  */
+    res = lfs_file_seek(efs->fs, &file->file, offset, SEEK_SET);
+    if (res < (off_t)0)
+        goto exit;
+
+    /* Write out the data.  */
+    res = lfs_file_write(efs->fs, &file->file, src, size);
+
+    /* Now we have to restore the position.  If this fails we have to
+     return this as an error. But if the writing also failed we
+     return writing error.  */
+    save_res = lfs_file_seek(efs->fs, &file->file, old_offset, SEEK_SET);
+    if (res >= (ssize_t)0 && save_res < (off_t)0)
+    {
+            res = save_res;
+    }
+    sem_give(efs);
+
+exit:
+    if (res < 0)
+    {
+        errno = -res;
+#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH
+        ESP_LOGV(TAG, "Failed to write FD %d; path \"%s\". Error %s (%d)",
+                fd, file->path, esp_littlefs_errno(res), res);
+#else
+        ESP_LOGV(TAG, "Failed to write FD %d. Error %s (%d)",
+                fd, esp_littlefs_errno(res), res);
+#endif
+        return -1;
+    }
+
+    return res;
+}
+
+static ssize_t vfs_littlefs_pread(void *ctx, int fd, void *dst, size_t size, off_t offset)
+{
+    esp_littlefs_t *efs = (esp_littlefs_t *)ctx;
+    ssize_t res, save_res;
+    vfs_littlefs_file_t *file = NULL;
+
+    sem_take(efs);
+    if ((uint32_t)fd > efs->cache_size)
+    {
+        sem_give(efs);
+        ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size);
+        return LFS_ERR_BADF;
+    }
+    file = efs->cache[fd];
+
+    off_t old_offset = lfs_file_seek(efs->fs, &file->file, 0, SEEK_CUR);
+    if (old_offset < (off_t)0)
+    {
+        res = old_offset;
+        goto exit;
+    }
+
+    /* Set to wanted position.  */
+    res = lfs_file_seek(efs->fs, &file->file, offset, SEEK_SET);
+    if (res < (off_t)0)
+        goto exit;
+
+    /* Read the data.  */
+    res = lfs_file_read(efs->fs, &file->file, dst, size);
+
+    /* Now we have to restore the position.  If this fails we have to
+     return this as an error. But if the reading also failed we
+     return reading error.  */
+    save_res = lfs_file_seek(efs->fs, &file->file, old_offset, SEEK_SET);
+    if (res >= (ssize_t)0 && save_res < (off_t)0)
+    {
+            res = save_res;
+    }
+    sem_give(efs);
+
+exit:
+    if (res < 0)
+    {
+        errno = -res;
+#ifndef CONFIG_LITTLEFS_USE_ONLY_HASH
+        ESP_LOGV(TAG, "Failed to read file \"%s\". Error %s (%d)",
+                 file->path, esp_littlefs_errno(res), res);
+#else
+        ESP_LOGV(TAG, "Failed to read FD %d. Error %s (%d)",
+                 fd, esp_littlefs_errno(res), res);
+#endif
+        return -1;
+    }
+
+    return res;
+}
+
+#endif //disabled
+
 static int vfs_littlefs_close(void* ctx, int fd) {
     // TODO update mtime on close? SPIFFS doesn't do this
     esp_littlefs_t * efs = (esp_littlefs_t *)ctx;
-    int res;
+    int res = ESP_OK;
     vfs_littlefs_file_t *file = NULL;
 
     sem_take(efs);
+
     if((uint32_t)fd > efs->cache_size) {
         sem_give(efs);
         ESP_LOGE(TAG, "FD %d must be <%d.", fd, efs->cache_size);
         return LFS_ERR_BADF;
     }
     file = efs->cache[fd];
-    res = lfs_file_close(efs->fs, &file->file);
-    if(res < 0){
-        errno = -res;
-        sem_give(efs);
+    assert(file->open_count > 0);
+    file->open_count--;
+
+    if(file->open_count == 0) {
+        /* Actually close the file and release the descriptor */
+        res = lfs_file_close(efs->fs, &file->file);
+        if(res < 0){
+            errno = -res;
+            sem_give(efs);
 #ifndef CONFIG_LITTLEFS_USE_ONLY_HASH
-        ESP_LOGV(TAG, "Failed to close file \"%s\". Error %s (%d)",
-                file->path, esp_littlefs_errno(res), res);
+            ESP_LOGV(TAG, "Failed to close file \"%s\". Error %s (%d)",
+                    file->path, esp_littlefs_errno(res), res);
 #else
-        ESP_LOGV(TAG, "Failed to close Fd %d. Error %s (%d)",
-                fd, esp_littlefs_errno(res), res);
+            ESP_LOGV(TAG, "Failed to close Fd %d. Error %s (%d)",
+                    fd, esp_littlefs_errno(res), res);
 #endif
-        return res;
+            return res;
+        }
+        esp_littlefs_free_fd(efs, fd);
     }
-    esp_littlefs_free_fd(efs, fd);
+
     sem_give(efs);
     return res;
 }
@@ -1194,6 +1353,11 @@ static int vfs_littlefs_unlink(void* ctx, const char *path) {
         return res;
     }
 
+#if CONFIG_LITTLEFS_SPIFFS_COMPAT
+    /* Attempt to delete all parent directories that are empty */
+    rmdirs(efs, path);
+#endif  // CONFIG_LITTLEFS_SPIFFS_COMPAT
+
     sem_give(efs);
 
     return 0;
@@ -1426,7 +1590,7 @@ static int vfs_littlefs_rmdir(void* ctx, const char* name) {
     }
 
     /* Unlink the dir */
-    res = lfs_remove(efs->fs, name);  
+    res = lfs_remove(efs->fs, name);
     sem_give(efs);
     if ( res < 0) {
         errno = -res;
@@ -1511,3 +1675,54 @@ static time_t vfs_littlefs_get_mtime(esp_littlefs_t *efs, const char *path)
     return t;
 }
 #endif //CONFIG_LITTLEFS_USE_MTIME
+
+#if CONFIG_LITTLEFS_SPIFFS_COMPAT
+/**
+ * @brief Recursively make all parent directories for a file.
+ * @param[in] dir Path of directories to make up to. The last element
+ * of the path is assumed to be the file and IS NOT created.
+ *   e.g.
+ *       "foo/bar/baz"
+ *   will create directories "foo" and "bar"
+ */
+static void mkdirs(esp_littlefs_t * efs, const char *dir) {
+    char tmp[CONFIG_LITTLEFS_OBJ_NAME_LEN];
+    char *p = NULL;
+
+    strlcpy(tmp, dir, sizeof(tmp));
+    for(p = tmp + 1; *p; p++) {
+        if(*p == '/') {
+            *p = '\0';
+            vfs_littlefs_mkdir((void*)efs, tmp, S_IRWXU);
+            *p = '/';
+        }
+    }
+}
+
+/**
+ * @brief Recursively attempt to delete all empty directories for a file.
+ * @param[in] dir Path of directories to delete. The last element of the path
+ * is assumed to be the file and IS NOT deleted.
+ *   e.g.
+ *       "foo/bar/baz"
+ *   will attempt to delete directories (in order):
+ *       1. "foo/bar/baz"
+ *       2. "foo/bar"
+ *       3. "foo"
+ */
+
+static void rmdirs(esp_littlefs_t * efs, const char *dir) {
+    char tmp[CONFIG_LITTLEFS_OBJ_NAME_LEN];
+    char *p = NULL;
+
+    strlcpy(tmp, dir, sizeof(tmp));
+    for(p = tmp + strlen(tmp) - 1; p != tmp; p--) {
+        if(*p == '/') {
+            *p = '\0';
+            vfs_littlefs_rmdir((void*)efs, tmp);
+            *p = '/';
+        }
+    }
+}
+
+#endif  // CONFIG_LITTLEFS_SPIFFS_COMPAT

+ 0 - 1
src/esp_littlefs.h

@@ -13,7 +13,6 @@
 #include <sys/reent.h>
 #include <sys/stat.h>
 #include <sys/time.h>
-//#include <sys/termios.h>
 #include <sys/poll.h>
 #include <dirent.h>
 #include <string.h>

+ 0 - 5
src/littlefs_api.c

@@ -2,11 +2,6 @@
  * @file littlefs_api.c
  * @brief Maps the HAL of esp_partition <-> littlefs
  * @author Brian Pugh
- *  
- * Copyright 2020 Brian Pugh
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
- * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
 #define ESP_LOCAL_LOG_LEVEL ESP_LOG_INFO

+ 1 - 0
src/littlefs_api.h

@@ -34,6 +34,7 @@ typedef struct _vfs_littlefs_file_t {
     lfs_file_t file;
     uint32_t   hash;
     struct _vfs_littlefs_file_t * next;       /*!< Pointer to next file in Singly Linked List */
+    uint8_t open_count;  /*!< Number of times this file has been opened */
 #ifndef CONFIG_LITTLEFS_USE_ONLY_HASH
     char     * path;
 #endif