diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index 655bbf5790..42c6a1199a 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -29,6 +29,8 @@ #include #include "sdkconfig.h" +#include "esp_vfs_minified.h" + #ifdef __cplusplus extern "C" { #endif @@ -62,20 +64,11 @@ extern "C" { */ #define ESP_VFS_FLAG_READONLY_FS (1 << 2) -/* - * @brief VFS identificator used for esp_vfs_register_with_id() - */ -typedef int esp_vfs_id_t; - /** - * @brief VFS semaphore type for select() - * + * Flag which indicates that VFS structure should be freed upon unregistering. + * @note Free if false, do not free if true */ -typedef struct -{ - bool is_sem_local; /*!< type of "sem" is SemaphoreHandle_t when true, defined by socket driver otherwise */ - void *sem; /*!< semaphore instance */ -} esp_vfs_select_sem_t; +#define ESP_VFS_FLAG_STATIC (1 << 3) /** * @brief VFS definition structure @@ -259,6 +252,8 @@ typedef struct #endif // CONFIG_VFS_SUPPORT_SELECT || defined __DOXYGEN__ } esp_vfs_t; + + /** * Register a virtual filesystem for given path prefix. * @@ -284,7 +279,6 @@ typedef struct */ esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx); - /** * Special case function for registering a VFS that uses a method other than * open() to open new file descriptors from the interval +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp_err.h" +#include +#include +#include +#include +#include +#include +#ifdef __clang__ // TODO LLVM-330 +#include +#else +#include +#endif +#include +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _SYS_TYPES_FD_SET +#error "VFS should be used with FD_SETSIZE and FD_SET from sys/types.h" +#endif + +/* + * @brief VFS identificator used for esp_vfs_register_with_id() + */ +typedef int esp_vfs_id_t; + +/** + * @brief VFS semaphore type for select() + * + */ +typedef struct +{ + bool is_sem_local; /*!< type of "sem" is SemaphoreHandle_t when true, defined by socket driver otherwise */ + void *sem; /*!< semaphore instance */ +} esp_vfs_select_sem_t; + +#ifdef CONFIG_VFS_SUPPORT_DIR + +typedef struct { + union { + int (*stat_p)(void* ctx, const char * path, struct stat * st); /*!< stat with context pointer */ + int (*stat)(const char * path, struct stat * st); /*!< stat without context pointer */ + }; + union { + int (*link_p)(void* ctx, const char* n1, const char* n2); /*!< link with context pointer */ + int (*link)(const char* n1, const char* n2); /*!< link without context pointer */ + }; + union { + int (*unlink_p)(void* ctx, const char *path); /*!< unlink with context pointer */ + int (*unlink)(const char *path); /*!< unlink without context pointer */ + }; + union { + int (*rename_p)(void* ctx, const char *src, const char *dst); /*!< rename with context pointer */ + int (*rename)(const char *src, const char *dst); /*!< rename without context pointer */ + }; + union { + DIR* (*opendir_p)(void* ctx, const char* name); /*!< opendir with context pointer */ + DIR* (*opendir)(const char* name); /*!< opendir without context pointer */ + }; + union { + struct dirent* (*readdir_p)(void* ctx, DIR* pdir); /*!< readdir with context pointer */ + struct dirent* (*readdir)(DIR* pdir); /*!< readdir without context pointer */ + }; + union { + int (*readdir_r_p)(void* ctx, DIR* pdir, struct dirent* entry, struct dirent** out_dirent); /*!< readdir_r with context pointer */ + int (*readdir_r)(DIR* pdir, struct dirent* entry, struct dirent** out_dirent); /*!< readdir_r without context pointer */ + }; + union { + long (*telldir_p)(void* ctx, DIR* pdir); /*!< telldir with context pointer */ + long (*telldir)(DIR* pdir); /*!< telldir without context pointer */ + }; + union { + void (*seekdir_p)(void* ctx, DIR* pdir, long offset); /*!< seekdir with context pointer */ + void (*seekdir)(DIR* pdir, long offset); /*!< seekdir without context pointer */ + }; + union { + int (*closedir_p)(void* ctx, DIR* pdir); /*!< closedir with context pointer */ + int (*closedir)(DIR* pdir); /*!< closedir without context pointer */ + }; + union { + int (*mkdir_p)(void* ctx, const char* name, mode_t mode); /*!< mkdir with context pointer */ + int (*mkdir)(const char* name, mode_t mode); /*!< mkdir without context pointer */ + }; + union { + int (*rmdir_p)(void* ctx, const char* name); /*!< rmdir with context pointer */ + int (*rmdir)(const char* name); /*!< rmdir without context pointer */ + }; + union { + int (*access_p)(void* ctx, const char *path, int amode); /*!< access with context pointer */ + int (*access)(const char *path, int amode); /*!< access without context pointer */ + }; + union { + int (*truncate_p)(void* ctx, const char *path, off_t length); /*!< truncate with context pointer */ + int (*truncate)(const char *path, off_t length); /*!< truncate without context pointer */ + }; + union { + int (*ftruncate_p)(void* ctx, int fd, off_t length); /*!< ftruncate with context pointer */ + int (*ftruncate)(int fd, off_t length); /*!< ftruncate without context pointer */ + }; + union { + int (*utime_p)(void* ctx, const char *path, const struct utimbuf *times); /*!< utime with context pointer */ + int (*utime)(const char *path, const struct utimbuf *times); /*!< utime without context pointer */ + }; +} esp_vfs_dir_t; + +#endif // CONFIG_VFS_SUPPORT_DIR + +#ifdef CONFIG_VFS_SUPPORT_TERMIOS + +typedef struct { + union { + int (*tcsetattr_p)(void *ctx, int fd, int optional_actions, const struct termios *p); /*!< tcsetattr with context pointer */ + int (*tcsetattr)(int fd, int optional_actions, const struct termios *p); /*!< tcsetattr without context pointer */ + }; + union { + int (*tcgetattr_p)(void *ctx, int fd, struct termios *p); /*!< tcgetattr with context pointer */ + int (*tcgetattr)(int fd, struct termios *p); /*!< tcgetattr without context pointer */ + }; + union { + int (*tcdrain_p)(void *ctx, int fd); /*!< tcdrain with context pointer */ + int (*tcdrain)(int fd); /*!< tcdrain without context pointer */ + }; + union { + int (*tcflush_p)(void *ctx, int fd, int select); /*!< tcflush with context pointer */ + int (*tcflush)(int fd, int select); /*!< tcflush without context pointer */ + }; + union { + int (*tcflow_p)(void *ctx, int fd, int action); /*!< tcflow with context pointer */ + int (*tcflow)(int fd, int action); /*!< tcflow without context pointer */ + }; + union { + pid_t (*tcgetsid_p)(void *ctx, int fd); /*!< tcgetsid with context pointer */ + pid_t (*tcgetsid)(int fd); /*!< tcgetsid without context pointer */ + }; + union { + int (*tcsendbreak_p)(void *ctx, int fd, int duration); /*!< tcsendbreak with context pointer */ + int (*tcsendbreak)(int fd, int duration); /*!< tcsendbreak without context pointer */ + }; +} esp_vfs_termios_t; + +#endif // CONFIG_VFS_SUPPORT_TERMIOS + +#ifdef CONFIG_VFS_SUPPORT_SELECT + +typedef struct { + /** start_select is called for setting up synchronous I/O multiplexing of the desired file descriptors in the given VFS */ + esp_err_t (*start_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, esp_vfs_select_sem_t sem, void **end_select_args); + + /** socket select function for socket FDs with the functionality of POSIX select(); this should be set only for the socket VFS */ + int (*socket_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout); + + /** called by VFS to interrupt the socket_select call when select is activated from a non-socket VFS driver; set only for the socket driver */ + void (*stop_socket_select)(void *sem); + + /** stop_socket_select which can be called from ISR; set only for the socket driver */ + void (*stop_socket_select_isr)(void *sem, BaseType_t *woken); + + /** end_select is called to stop the I/O multiplexing and deinitialize the environment created by start_select for the given VFS */ + void* (*get_socket_select_semaphore)(void); + + /** get_socket_select_semaphore returns semaphore allocated in the socket driver; set only for the socket driver */ + esp_err_t (*end_select)(void *end_select_args); +} esp_vfs_select_t; + +#endif // CONFIG_VFS_SUPPORT_SELECT + +typedef struct { + union { + ssize_t (*write_p)(void* p, int fd, const void * data, size_t size); /*!< Write with context pointer */ + ssize_t (*write)(int fd, const void * data, size_t size); /*!< Write without context pointer */ + }; + union { + off_t (*lseek_p)(void* p, int fd, off_t size, int mode); /*!< Seek with context pointer */ + off_t (*lseek)(int fd, off_t size, int mode); /*!< Seek without context pointer */ + }; + union { + ssize_t (*read_p)(void* ctx, int fd, void * dst, size_t size); /*!< Read with context pointer */ + ssize_t (*read)(int fd, void * dst, size_t size); /*!< Read without context pointer */ + }; + union { + ssize_t (*pread_p)(void *ctx, int fd, void * dst, size_t size, off_t offset); /*!< pread with context pointer */ + ssize_t (*pread)(int fd, void * dst, size_t size, off_t offset); /*!< pread without context pointer */ + }; + union { + ssize_t (*pwrite_p)(void *ctx, int fd, const void *src, size_t size, off_t offset); /*!< pwrite with context pointer */ + ssize_t (*pwrite)(int fd, const void *src, size_t size, off_t offset); /*!< pwrite without context pointer */ + }; + union { + int (*open_p)(void* ctx, const char * path, int flags, int mode); /*!< open with context pointer */ + int (*open)(const char * path, int flags, int mode); /*!< open without context pointer */ + }; + union { + int (*close_p)(void* ctx, int fd); /*!< close with context pointer */ + int (*close)(int fd); /*!< close without context pointer */ + }; + union { + int (*fstat_p)(void* ctx, int fd, struct stat * st); /*!< fstat with context pointer */ + int (*fstat)(int fd, struct stat * st); /*!< fstat without context pointer */ + }; + union { + int (*fcntl_p)(void* ctx, int fd, int cmd, int arg); /*!< fcntl with context pointer */ + int (*fcntl)(int fd, int cmd, int arg); /*!< fcntl without context pointer */ + }; + union { + int (*ioctl_p)(void* ctx, int fd, int cmd, va_list args); /*!< ioctl with context pointer */ + int (*ioctl)(int fd, int cmd, va_list args); /*!< ioctl without context pointer */ + }; + union { + int (*fsync_p)(void* ctx, int fd); /*!< fsync with context pointer */ + int (*fsync)(int fd); /*!< fsync without context pointer */ + }; + +#ifdef CONFIG_VFS_SUPPORT_DIR + esp_vfs_dir_t *dir; +#endif + +#ifdef CONFIG_VFS_SUPPORT_TERMIOS + esp_vfs_termios_t *termios; +#endif + +#if CONFIG_VFS_SUPPORT_SELECT || defined __DOXYGEN__ + esp_vfs_select_t *select; +#endif + +} esp_vfs_minified_t; + +/** + * Register a virtual filesystem for given path prefix. + * + * @param base_path file path prefix associated with the filesystem. + * Must be a zero-terminated C string, may be empty. + * If not empty, must be up to ESP_VFS_PATH_MAX + * characters long, and at least 2 characters long. + * Name must start with a "/" and must not end with "/". + * For example, "/data" or "/dev/spi" are valid. + * These VFSes would then be called to handle file paths such as + * "/data/myfile.txt" or "/dev/spi/0". + * In the special case of an empty base_path, a "fallback" + * VFS is registered. Such VFS will handle paths which are not + * matched by any other registered VFS. + * @param vfs Pointer to esp_vfs_t, a structure which maps syscalls to + * the filesystem driver functions. VFS component doesn't + * assume ownership of this pointer. + * @param ctx If vfs->flags has ESP_VFS_FLAG_CONTEXT_PTR set, a pointer + * which should be passed to VFS functions. Otherwise, NULL. + * + * @return ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are + * registered. + */ +esp_err_t esp_vfs_register_minified(const char* base_path, const esp_vfs_minified_t* vfs, int flags, void* ctx); + +esp_err_t esp_vfs_register_minified_with_id(const esp_vfs_minified_t* vfs, int flags, void* ctx, int* id); + +esp_err_t esp_vfs_unregister_minified(const char* base_path); + +esp_err_t esp_vfs_unregister_minified_with_id(esp_vfs_id_t id); + +#ifdef __cplusplus +} +#endif diff --git a/components/vfs/private_include/esp_vfs_private.h b/components/vfs/private_include/esp_vfs_private.h index fda3e9712b..b211f842a2 100644 --- a/components/vfs/private_include/esp_vfs_private.h +++ b/components/vfs/private_include/esp_vfs_private.h @@ -19,7 +19,8 @@ extern "C" { #endif typedef struct vfs_entry_ { - esp_vfs_t vfs; // contains pointers to VFS functions + int flags; /*!< ESP_VFS_FLAG_CONTEXT_PTR and/or ESP_VFS_FLAG_READONLY_FS or ESP_VFS_FLAG_DEFAULT */ + const esp_vfs_minified_t *vfs; // contains pointers to VFS functions char path_prefix[ESP_VFS_PATH_MAX]; // path prefix mapped to this VFS size_t path_prefix_len; // micro-optimization to avoid doing extra strlen void* ctx; // optional pointer which can be passed to VFS diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index 906e8684d9..7e494b90ef 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -19,6 +19,7 @@ #include "freertos/semphr.h" #include "esp_vfs.h" #include "esp_vfs_private.h" +#include "include/esp_vfs.h" #include "sdkconfig.h" // Warn about using deprecated option @@ -75,8 +76,189 @@ static size_t s_vfs_count = 0; static fd_table_t s_fd_table[MAX_FDS] = { [0 ... MAX_FDS-1] = FD_TABLE_ENTRY_UNUSED }; static _lock_t s_fd_table_lock; -esp_err_t esp_vfs_register_common(const char* base_path, size_t len, const esp_vfs_t* vfs, void* ctx, int *vfs_index) +static ssize_t esp_get_free_index(void) { + for (ssize_t i = 0; i < MAX_FDS; i++) { + if (s_vfs[i] == NULL) { + return i; + } + } + return -1; +} + +static void esp_free_minified_vfs(esp_vfs_minified_t *vfs) { +#ifdef CONFIG_VFS_SUPPORT_TERMIOS + free(vfs->termios); +#endif + +#ifdef CONFIG_VFS_SUPPORT_DIR + free(vfs->dir); +#endif + +#ifdef CONFIG_VFS_SUPPORT_SELECT + free(vfs->select); +#endif + + free(vfs); +} + +static esp_vfs_minified_t *esp_alloc_minified_vfs(void) { + esp_vfs_minified_t *main = (esp_vfs_minified_t*) heap_caps_malloc(sizeof(esp_vfs_minified_t), VFS_MALLOC_FLAGS); + if (main == NULL) { + return NULL; + } + + // Initialize all fields to NULL + memset(main, 0, sizeof(esp_vfs_minified_t)); + +#ifdef CONFIG_VFS_SUPPORT_DIR + main->dir = (esp_vfs_dir_t*) heap_caps_malloc(sizeof(esp_vfs_dir_t), VFS_MALLOC_FLAGS); + if (main->dir == NULL) { + goto fail; + } +#endif + +#ifdef CONFIG_VFS_SUPPORT_TERMIOS + main->termios = (esp_vfs_termios_t*) heap_caps_malloc(sizeof(esp_vfs_termios_t), VFS_MALLOC_FLAGS); + if (main->termios == NULL) { + goto fail; + } +#endif + +#ifdef CONFIG_VFS_SUPPORT_SELECT + main->select = (esp_vfs_select_t*) heap_caps_malloc(sizeof(esp_vfs_select_t), VFS_MALLOC_FLAGS); + if (main->select == NULL) { + goto fail; + } +#endif + + return main; + +#if defined(CONFIG_VFS_SUPPORT_SELECT) || defined(CONFIG_VFS_SUPPORT_TERMIOS) || defined(CONFIG_VFS_SUPPORT_DIR) +fail: + + esp_free_minified_vfs(main); + return NULL; +#endif +} + +static void esp_vfs_free_entry(vfs_entry_t *entry) { + if (entry == NULL) { // Necessary because of the following flags check + return; + } + + if (!(entry->flags & ESP_VFS_FLAG_STATIC)) { + esp_free_minified_vfs((esp_vfs_minified_t*)entry->vfs); // const cast, but we know it's not static from the flag + } + + free(entry); +} + +static void esp_minify_vfs(const esp_vfs_t * const vfs, esp_vfs_minified_t *min) { + if (vfs == NULL) { + ESP_LOGE(TAG, "Cannot minify NULL VFS"); + assert(vfs != NULL); // Show stack trace + abort(); // Ensure we don't continue with a NULL VFS when assert is disabled + } + + if (min == NULL) { + ESP_LOGE(TAG, "Cannot minify VFS to NULL"); + assert(min != NULL); // Show stack trace + abort(); // Ensure we don't continue with a NULL VFS when assert is disabled + } + + *min = (esp_vfs_minified_t) { + .write = vfs->write, + .lseek = vfs->lseek, + .read = vfs->read, + .pread = vfs->pread, + .pwrite = vfs->pwrite, + .open = vfs->open, + .close = vfs->close, + .fstat = vfs->fstat, + .fcntl = vfs->fcntl, + .ioctl = vfs->ioctl, + .fsync = vfs->fsync, +#ifdef CONFIG_VFS_SUPPORT_DIR + .dir = min->dir, +#endif +#ifdef CONFIG_VFS_SUPPORT_TERMIOS + .termios = min->termios, +#endif +#ifdef CONFIG_VFS_SUPPORT_SELECT + .select = min->select, +#endif + }; + +#ifdef CONFIG_VFS_SUPPORT_DIR + if (min->dir == NULL) { + ESP_LOGE(TAG, "Dir subcomponent is not allocated"); + assert(min->dir != NULL); // Show stack trace + abort(); // Ensure we don't continue with a NULL VFS when assert is disabled + } + *(min->dir) = (esp_vfs_dir_t) { + .stat = vfs->stat, + .link = vfs->link, + .unlink = vfs->unlink, + .rename = vfs->rename, + .opendir = vfs->opendir, + .readdir = vfs->readdir, + .readdir_r = vfs->readdir_r, + .telldir = vfs->telldir, + .seekdir = vfs->seekdir, + .closedir = vfs->closedir, + .mkdir = vfs->mkdir, + .rmdir = vfs->rmdir, + .access = vfs->access, + .truncate = vfs->truncate, + .ftruncate = vfs->ftruncate, + .utime = vfs->utime, + }; +#endif // CONFIG_VFS_SUPPORT_DIR + +#ifdef CONFIG_VFS_SUPPORT_TERMIOS + if (min->termios == NULL) { + ESP_LOGE(TAG, "Termios subcomponent is not allocated"); + assert(min->termios != NULL); // Show stack trace + abort(); // Ensure we don't continue with a NULL VFS when assert is disabled + } + + *(min->termios) = (esp_vfs_termios_t) { + .tcsetattr = vfs->tcsetattr, + .tcgetattr = vfs->tcgetattr, + .tcdrain = vfs->tcdrain, + .tcflush = vfs->tcflush, + .tcflow = vfs->tcflow, + .tcgetsid = vfs->tcgetsid, + .tcsendbreak = vfs->tcsendbreak, + }; +#endif // CONFIG_VFS_SUPPORT_TERMIOS + +#ifdef CONFIG_VFS_SUPPORT_SELECT + if (min->select == NULL) { + ESP_LOGE(TAG, "Select subcomponent is not allocated"); + assert(min->select != NULL); // Show stack trace + abort(); // Ensure we don't continue with a NULL VFS when assert is disabled + } + + *(min->select) = (esp_vfs_select_t) { + .start_select = vfs->start_select, + .socket_select = vfs->socket_select, + .stop_socket_select = vfs->stop_socket_select, + .stop_socket_select_isr = vfs->stop_socket_select_isr, + .get_socket_select_semaphore = vfs->get_socket_select_semaphore, + .end_select = vfs->end_select, + }; +#endif // CONFIG_VFS_SUPPORT_SELECT + +} + +static esp_err_t esp_vfs_register_minified_common(const char* base_path, size_t len, const esp_vfs_minified_t* vfs, int flags, void* ctx, int *vfs_index) { + if (vfs == NULL) { + ESP_LOGE(TAG, "VFS is NULL"); + return ESP_ERR_INVALID_ARG; + } + if (len != LEN_PATH_PREFIX_IGNORED) { /* empty prefix is allowed, "/" is not allowed */ if ((len == 1) || (len > ESP_VFS_PATH_MAX)) { @@ -87,33 +269,36 @@ esp_err_t esp_vfs_register_common(const char* base_path, size_t len, const esp_v return ESP_ERR_INVALID_ARG; } } + + ssize_t index = esp_get_free_index(); + if (index < 0) { + return ESP_ERR_NO_MEM; + } + + if (s_vfs[index] != NULL) { + return ESP_ERR_INVALID_STATE; + } + + if (index == s_vfs_count) { + s_vfs_count++; + } + vfs_entry_t *entry = (vfs_entry_t*) heap_caps_malloc(sizeof(vfs_entry_t), VFS_MALLOC_FLAGS); if (entry == NULL) { return ESP_ERR_NO_MEM; } - size_t index; - for (index = 0; index < s_vfs_count; ++index) { - if (s_vfs[index] == NULL) { - break; - } - } - if (index == s_vfs_count) { - if (s_vfs_count >= VFS_MAX_COUNT) { - free(entry); - return ESP_ERR_NO_MEM; - } - ++s_vfs_count; - } + s_vfs[index] = entry; if (len != LEN_PATH_PREFIX_IGNORED) { strcpy(entry->path_prefix, base_path); // we have already verified argument length } else { bzero(entry->path_prefix, sizeof(entry->path_prefix)); } - memcpy(&entry->vfs, vfs, sizeof(esp_vfs_t)); - entry->path_prefix_len = len; + entry->path_prefix_len = strlen(base_path); + entry->vfs = vfs; entry->ctx = ctx; entry->offset = index; + entry->flags = flags; if (vfs_index) { *vfs_index = index; @@ -122,6 +307,58 @@ esp_err_t esp_vfs_register_common(const char* base_path, size_t len, const esp_v return ESP_OK; } +esp_err_t esp_vfs_register_minified(const char* base_path, const esp_vfs_minified_t* vfs, int flags, void* ctx) +{ + if (vfs == NULL) { + ESP_LOGE(TAG, "VFS is NULL"); + return ESP_ERR_INVALID_ARG; + } + + if ((flags & ESP_VFS_FLAG_STATIC)) { + return esp_vfs_register_minified_common(base_path, strlen(base_path), vfs, flags, ctx, NULL); + } + + esp_vfs_minified_t *_vfs = esp_alloc_minified_vfs(); + if (_vfs == NULL) { + return ESP_ERR_NO_MEM; + } + + esp_err_t ret = esp_vfs_register_minified_common(base_path, strlen(base_path), _vfs, flags, ctx, NULL); + if (ret != ESP_OK) { + esp_free_minified_vfs(_vfs); + } + + return ESP_OK; +} + +esp_err_t esp_vfs_register_common(const char* base_path, size_t len, const esp_vfs_t* vfs, void* ctx, int *vfs_index) +{ + if (vfs == NULL) { + ESP_LOGE(TAG, "VFS is NULL"); + return ESP_ERR_INVALID_ARG; + } + + if (vfs->flags & ESP_VFS_FLAG_STATIC) { + ESP_LOGE(TAG, "ESP_VFS_FLAG_STATIC is not supported for esp_vfs_t, use esp_vfs_register_minified instead"); + return ESP_ERR_INVALID_ARG; + } + + esp_vfs_minified_t *_vfs = esp_alloc_minified_vfs(); + if (_vfs == NULL) { + return ESP_ERR_NO_MEM; + } + + // Minify the VFS + esp_minify_vfs(vfs, _vfs); + + esp_err_t ret = esp_vfs_register_minified_common(base_path, len, _vfs, vfs->flags, ctx, vfs_index); + if (ret != ESP_OK) { + esp_free_minified_vfs(_vfs); + } + + return ESP_OK; +} + esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx) { return esp_vfs_register_common(base_path, strlen(base_path), vfs, ctx, NULL); @@ -134,7 +371,7 @@ esp_err_t esp_vfs_register_fd_range(const esp_vfs_t *vfs, void *ctx, int min_fd, return ESP_ERR_INVALID_ARG; } - int index = -1; + int index = 0; esp_err_t ret = esp_vfs_register_common("", LEN_PATH_PREFIX_IGNORED, vfs, ctx, &index); if (ret == ESP_OK) { @@ -164,6 +401,16 @@ esp_err_t esp_vfs_register_fd_range(const esp_vfs_t *vfs, void *ctx, int min_fd, return ret; } +esp_err_t esp_vfs_register_minified_with_id(const esp_vfs_minified_t *vfs, int flags, void *ctx, esp_vfs_id_t *vfs_id) +{ + if (vfs_id == NULL) { + return ESP_ERR_INVALID_ARG; + } + + *vfs_id = -1; + return esp_vfs_register_minified_common("", LEN_PATH_PREFIX_IGNORED, vfs, flags, ctx, vfs_id); +} + esp_err_t esp_vfs_register_with_id(const esp_vfs_t *vfs, void *ctx, esp_vfs_id_t *vfs_id) { if (vfs_id == NULL) { @@ -180,7 +427,7 @@ esp_err_t esp_vfs_unregister_with_id(esp_vfs_id_t vfs_id) return ESP_ERR_INVALID_ARG; } vfs_entry_t* vfs = s_vfs[vfs_id]; - free(vfs); + esp_vfs_free_entry(vfs); s_vfs[vfs_id] = NULL; _lock_acquire(&s_fd_table_lock); @@ -193,8 +440,11 @@ esp_err_t esp_vfs_unregister_with_id(esp_vfs_id_t vfs_id) _lock_release(&s_fd_table_lock); return ESP_OK; + } +esp_err_t esp_vfs_unregister_minified_with_id(esp_vfs_id_t vfs_id) __attribute__((alias("esp_vfs_unregister_with_id"))); + esp_err_t esp_vfs_unregister(const char* base_path) { const size_t base_path_len = strlen(base_path); @@ -211,6 +461,8 @@ esp_err_t esp_vfs_unregister(const char* base_path) return ESP_ERR_INVALID_STATE; } +esp_err_t esp_vfs_unregister_minified(const char* base_path) __attribute__((alias("esp_vfs_unregister"))); + esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd) { return esp_vfs_register_fd_with_local_fd(vfs_id, -1, true, fd); @@ -306,7 +558,7 @@ esp_err_t esp_vfs_set_readonly_flag(const char* base_path) } if (base_path_len == vfs->path_prefix_len && memcmp(base_path, vfs->path_prefix, vfs->path_prefix_len) == 0) { - vfs->vfs.flags |= ESP_VFS_FLAG_READONLY_FS; + vfs->flags |= ESP_VFS_FLAG_READONLY_FS; return ESP_OK; } } @@ -411,37 +663,37 @@ const vfs_entry_t* get_vfs_for_path(const char* path) * It is enough to check just one of them for NULL, as both variants are part of a union. */ #define CHECK_AND_CALL(ret, r, pvfs, func, ...) \ - if (pvfs->vfs.func == NULL) { \ + if (pvfs->vfs->func == NULL) { \ __errno_r(r) = ENOSYS; \ return -1; \ } \ - if (pvfs->vfs.flags & ESP_VFS_FLAG_CONTEXT_PTR) { \ - ret = (*pvfs->vfs.func ## _p)(pvfs->ctx, __VA_ARGS__); \ + if (pvfs->flags & ESP_VFS_FLAG_CONTEXT_PTR) { \ + ret = (*pvfs->vfs->func ## _p)(pvfs->ctx, __VA_ARGS__); \ } else { \ - ret = (*pvfs->vfs.func)(__VA_ARGS__);\ + ret = (*pvfs->vfs->func)(__VA_ARGS__);\ } #define CHECK_AND_CALLV(r, pvfs, func, ...) \ - if (pvfs->vfs.func == NULL) { \ + if (pvfs->vfs->func == NULL) { \ __errno_r(r) = ENOSYS; \ return; \ } \ - if (pvfs->vfs.flags & ESP_VFS_FLAG_CONTEXT_PTR) { \ - (*pvfs->vfs.func ## _p)(pvfs->ctx, __VA_ARGS__); \ + if (pvfs->flags & ESP_VFS_FLAG_CONTEXT_PTR) { \ + (*pvfs->vfs->func ## _p)(pvfs->ctx, __VA_ARGS__); \ } else { \ - (*pvfs->vfs.func)(__VA_ARGS__);\ + (*pvfs->vfs->func)(__VA_ARGS__);\ } #define CHECK_AND_CALLP(ret, r, pvfs, func, ...) \ - if (pvfs->vfs.func == NULL) { \ + if (pvfs->vfs->func == NULL) { \ __errno_r(r) = ENOSYS; \ return NULL; \ } \ - if (pvfs->vfs.flags & ESP_VFS_FLAG_CONTEXT_PTR) { \ - ret = (*pvfs->vfs.func ## _p)(pvfs->ctx, __VA_ARGS__); \ + if (pvfs->flags & ESP_VFS_FLAG_CONTEXT_PTR) { \ + ret = (*pvfs->vfs->func ## _p)(pvfs->ctx, __VA_ARGS__); \ } else { \ - ret = (*pvfs->vfs.func)(__VA_ARGS__);\ + ret = (*pvfs->vfs->func)(__VA_ARGS__);\ } #define CHECK_VFS_READONLY_FLAG(flags) \ @@ -459,7 +711,7 @@ int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode) } int acc_mode = flags & O_ACCMODE; - int ro_filesystem = vfs->vfs.flags & ESP_VFS_FLAG_READONLY_FS; + int ro_filesystem = vfs->flags & ESP_VFS_FLAG_READONLY_FS; if (acc_mode != O_RDONLY && ro_filesystem) { __errno_r(r) = EROFS; return -1; @@ -648,7 +900,7 @@ int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st) } const char* path_within_vfs = translate_path(vfs, path); int ret; - CHECK_AND_CALL(ret, r, vfs, stat, path_within_vfs, st); + CHECK_AND_CALL(ret, r, vfs, dir->stat, path_within_vfs, st); return ret; } @@ -662,7 +914,7 @@ int esp_vfs_utime(const char *path, const struct utimbuf *times) return -1; } const char* path_within_vfs = translate_path(vfs, path); - CHECK_AND_CALL(ret, r, vfs, utime, path_within_vfs, times); + CHECK_AND_CALL(ret, r, vfs, dir->utime, path_within_vfs, times); return ret; } @@ -679,12 +931,12 @@ int esp_vfs_link(struct _reent *r, const char* n1, const char* n2) return -1; } - CHECK_VFS_READONLY_FLAG(vfs2->vfs.flags); + CHECK_VFS_READONLY_FLAG(vfs2->flags); const char* path1_within_vfs = translate_path(vfs, n1); const char* path2_within_vfs = translate_path(vfs, n2); int ret; - CHECK_AND_CALL(ret, r, vfs, link, path1_within_vfs, path2_within_vfs); + CHECK_AND_CALL(ret, r, vfs, dir->link, path1_within_vfs, path2_within_vfs); return ret; } @@ -696,11 +948,11 @@ int esp_vfs_unlink(struct _reent *r, const char *path) return -1; } - CHECK_VFS_READONLY_FLAG(vfs->vfs.flags); + CHECK_VFS_READONLY_FLAG(vfs->flags); const char* path_within_vfs = translate_path(vfs, path); int ret; - CHECK_AND_CALL(ret, r, vfs, unlink, path_within_vfs); + CHECK_AND_CALL(ret, r, vfs, dir->unlink, path_within_vfs); return ret; } @@ -712,7 +964,7 @@ int esp_vfs_rename(struct _reent *r, const char *src, const char *dst) return -1; } - CHECK_VFS_READONLY_FLAG(vfs->vfs.flags); + CHECK_VFS_READONLY_FLAG(vfs->flags); const vfs_entry_t* vfs_dst = get_vfs_for_path(dst); if (vfs != vfs_dst) { @@ -720,12 +972,12 @@ int esp_vfs_rename(struct _reent *r, const char *src, const char *dst) return -1; } - CHECK_VFS_READONLY_FLAG(vfs_dst->vfs.flags); + CHECK_VFS_READONLY_FLAG(vfs_dst->flags); const char* src_within_vfs = translate_path(vfs, src); const char* dst_within_vfs = translate_path(vfs, dst); int ret; - CHECK_AND_CALL(ret, r, vfs, rename, src_within_vfs, dst_within_vfs); + CHECK_AND_CALL(ret, r, vfs, dir->rename, src_within_vfs, dst_within_vfs); return ret; } @@ -739,7 +991,7 @@ DIR* esp_vfs_opendir(const char* name) } const char* path_within_vfs = translate_path(vfs, name); DIR* ret; - CHECK_AND_CALLP(ret, r, vfs, opendir, path_within_vfs); + CHECK_AND_CALLP(ret, r, vfs, dir->opendir, path_within_vfs); if (ret != NULL) { ret->dd_vfs_idx = vfs->offset; } @@ -755,7 +1007,7 @@ struct dirent* esp_vfs_readdir(DIR* pdir) return NULL; } struct dirent* ret; - CHECK_AND_CALLP(ret, r, vfs, readdir, pdir); + CHECK_AND_CALLP(ret, r, vfs, dir->readdir, pdir); return ret; } @@ -768,7 +1020,7 @@ int esp_vfs_readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_diren return -1; } int ret; - CHECK_AND_CALL(ret, r, vfs, readdir_r, pdir, entry, out_dirent); + CHECK_AND_CALL(ret, r, vfs, dir->readdir_r, pdir, entry, out_dirent); return ret; } @@ -781,7 +1033,7 @@ long esp_vfs_telldir(DIR* pdir) return -1; } long ret; - CHECK_AND_CALL(ret, r, vfs, telldir, pdir); + CHECK_AND_CALL(ret, r, vfs, dir->telldir, pdir); return ret; } @@ -793,7 +1045,7 @@ void esp_vfs_seekdir(DIR* pdir, long loc) errno = EBADF; return; } - CHECK_AND_CALLV(r, vfs, seekdir, pdir, loc); + CHECK_AND_CALLV(r, vfs, dir->seekdir, pdir, loc); } void esp_vfs_rewinddir(DIR* pdir) @@ -810,7 +1062,7 @@ int esp_vfs_closedir(DIR* pdir) return -1; } int ret; - CHECK_AND_CALL(ret, r, vfs, closedir, pdir); + CHECK_AND_CALL(ret, r, vfs, dir->closedir, pdir); return ret; } @@ -823,11 +1075,11 @@ int esp_vfs_mkdir(const char* name, mode_t mode) return -1; } - CHECK_VFS_READONLY_FLAG(vfs->vfs.flags); + CHECK_VFS_READONLY_FLAG(vfs->flags); const char* path_within_vfs = translate_path(vfs, name); int ret; - CHECK_AND_CALL(ret, r, vfs, mkdir, path_within_vfs, mode); + CHECK_AND_CALL(ret, r, vfs, dir->mkdir, path_within_vfs, mode); return ret; } @@ -840,11 +1092,11 @@ int esp_vfs_rmdir(const char* name) return -1; } - CHECK_VFS_READONLY_FLAG(vfs->vfs.flags); + CHECK_VFS_READONLY_FLAG(vfs->flags); const char* path_within_vfs = translate_path(vfs, name); int ret; - CHECK_AND_CALL(ret, r, vfs, rmdir, path_within_vfs); + CHECK_AND_CALL(ret, r, vfs, dir->rmdir, path_within_vfs); return ret; } @@ -858,7 +1110,7 @@ int esp_vfs_access(const char *path, int amode) return -1; } const char* path_within_vfs = translate_path(vfs, path); - CHECK_AND_CALL(ret, r, vfs, access, path_within_vfs, amode); + CHECK_AND_CALL(ret, r, vfs, dir->access, path_within_vfs, amode); return ret; } @@ -872,10 +1124,10 @@ int esp_vfs_truncate(const char *path, off_t length) return -1; } - CHECK_VFS_READONLY_FLAG(vfs->vfs.flags); + CHECK_VFS_READONLY_FLAG(vfs->flags); const char* path_within_vfs = translate_path(vfs, path); - CHECK_AND_CALL(ret, r, vfs, truncate, path_within_vfs, length); + CHECK_AND_CALL(ret, r, vfs, dir->truncate, path_within_vfs, length); return ret; } @@ -889,10 +1141,10 @@ int esp_vfs_ftruncate(int fd, off_t length) return -1; } - CHECK_VFS_READONLY_FLAG(vfs->vfs.flags); + CHECK_VFS_READONLY_FLAG(vfs->flags); int ret; - CHECK_AND_CALL(ret, r, vfs, ftruncate, local_fd, length); + CHECK_AND_CALL(ret, r, vfs, dir->ftruncate, local_fd, length); return ret; } @@ -905,8 +1157,8 @@ static void call_end_selects(int end_index, const fds_triple_t *vfs_fds_triple, for (int i = 0; i < end_index; ++i) { const vfs_entry_t *vfs = get_vfs_for_index(i); const fds_triple_t *item = &vfs_fds_triple[i]; - if (vfs && vfs->vfs.end_select && item->isset) { - esp_err_t err = vfs->vfs.end_select(driver_args[i]); + if (vfs && vfs->vfs->select->end_select && item->isset) { + esp_err_t err = vfs->vfs->select->end_select(driver_args[i]); if (err != ESP_OK) { ESP_LOGD(TAG, "end_select failed: %s", esp_err_to_name(err)); } @@ -1023,8 +1275,8 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds esp_vfs_safe_fd_isset(fd, writefds) || esp_vfs_safe_fd_isset(fd, errorfds)) { const vfs_entry_t *vfs = s_vfs[vfs_index]; - socket_select = vfs->vfs.socket_select; - sel_sem.sem = vfs->vfs.get_socket_select_semaphore(); + socket_select = vfs->vfs->select->socket_select; + sel_sem.sem = vfs->vfs->select->get_socket_select_semaphore(); } } continue; @@ -1080,16 +1332,16 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds const vfs_entry_t *vfs = get_vfs_for_index(i); fds_triple_t *item = &vfs_fds_triple[i]; - if (vfs && !vfs->vfs.start_select) { + if (vfs && !vfs->vfs->select->start_select) { ESP_LOGD(TAG, "start_select function callback for this vfs (s_vfs[%d]) is not defined", vfs->offset); - } else if (vfs && vfs->vfs.start_select && item->isset) { + } else if (vfs && vfs->vfs->select->start_select && item->isset) { // call start_select for all non-socket VFSs with has at least one FD set in readfds, writefds, or errorfds // note: it can point to socket VFS but item->isset will be false for that ESP_LOGD(TAG, "calling start_select for VFS ID %d with the following local FDs", i); esp_vfs_log_fd_set("readfds", &item->readfds); esp_vfs_log_fd_set("writefds", &item->writefds); esp_vfs_log_fd_set("errorfds", &item->errorfds); - esp_err_t err = vfs->vfs.start_select(nfds, &item->readfds, &item->writefds, &item->errorfds, sel_sem, + esp_err_t err = vfs->vfs->select->start_select(nfds, &item->readfds, &item->writefds, &item->errorfds, sel_sem, driver_args + i); if (err != ESP_OK) { @@ -1193,8 +1445,8 @@ void esp_vfs_select_triggered(esp_vfs_select_sem_t sem) // Note: s_vfs_count could have changed since the start of vfs_select() call. However, that change doesn't // matter here stop_socket_select() will be called for only valid VFS drivers. const vfs_entry_t *vfs = s_vfs[i]; - if (vfs != NULL && vfs->vfs.stop_socket_select != NULL) { - vfs->vfs.stop_socket_select(sem.sem); + if (vfs != NULL && vfs->vfs->select->stop_socket_select != NULL) { + vfs->vfs->select->stop_socket_select(sem.sem); break; } } @@ -1213,9 +1465,9 @@ void esp_vfs_select_triggered_isr(esp_vfs_select_sem_t sem, BaseType_t *woken) // Note: s_vfs_count could have changed since the start of vfs_select() call. However, that change doesn't // matter here stop_socket_select() will be called for only valid VFS drivers. const vfs_entry_t *vfs = s_vfs[i]; - if (vfs != NULL && vfs->vfs.stop_socket_select_isr != NULL) { + if (vfs != NULL && vfs->vfs->select->stop_socket_select_isr != NULL) { // Note: If the UART ISR resides in IRAM, the function referenced by stop_socket_select_isr should also be placed in IRAM. - vfs->vfs.stop_socket_select_isr(sem.sem, woken); + vfs->vfs->select->stop_socket_select_isr(sem.sem, woken); break; } } @@ -1236,7 +1488,7 @@ int tcgetattr(int fd, struct termios *p) return -1; } int ret; - CHECK_AND_CALL(ret, r, vfs, tcgetattr, local_fd, p); + CHECK_AND_CALL(ret, r, vfs, termios->tcgetattr, local_fd, p); return ret; } @@ -1250,7 +1502,7 @@ int tcsetattr(int fd, int optional_actions, const struct termios *p) return -1; } int ret; - CHECK_AND_CALL(ret, r, vfs, tcsetattr, local_fd, optional_actions, p); + CHECK_AND_CALL(ret, r, vfs, termios->tcsetattr, local_fd, optional_actions, p); return ret; } @@ -1264,7 +1516,7 @@ int tcdrain(int fd) return -1; } int ret; - CHECK_AND_CALL(ret, r, vfs, tcdrain, local_fd); + CHECK_AND_CALL(ret, r, vfs, termios->tcdrain, local_fd); return ret; } @@ -1278,7 +1530,7 @@ int tcflush(int fd, int select) return -1; } int ret; - CHECK_AND_CALL(ret, r, vfs, tcflush, local_fd, select); + CHECK_AND_CALL(ret, r, vfs, termios->tcflush, local_fd, select); return ret; } @@ -1292,7 +1544,7 @@ int tcflow(int fd, int action) return -1; } int ret; - CHECK_AND_CALL(ret, r, vfs, tcflow, local_fd, action); + CHECK_AND_CALL(ret, r, vfs, termios->tcflow, local_fd, action); return ret; } @@ -1306,7 +1558,7 @@ pid_t tcgetsid(int fd) return -1; } int ret; - CHECK_AND_CALL(ret, r, vfs, tcgetsid, local_fd); + CHECK_AND_CALL(ret, r, vfs, termios->tcgetsid, local_fd); return ret; } @@ -1320,7 +1572,7 @@ int tcsendbreak(int fd, int duration) return -1; } int ret; - CHECK_AND_CALL(ret, r, vfs, tcsendbreak, local_fd, duration); + CHECK_AND_CALL(ret, r, vfs, termios->tcsendbreak, local_fd, duration); return ret; } #endif // CONFIG_VFS_SUPPORT_TERMIOS