feat(storage/vfs): Add minified vfs implementation

This commit is contained in:
Tomáš Rohlínek
2024-06-27 10:38:13 +02:00
committed by BOT
parent 12398c0f25
commit b6a9acfc07
4 changed files with 611 additions and 88 deletions

View File

@@ -29,6 +29,8 @@
#include <string.h>
#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 <min_fd; max_fd).

View File

@@ -0,0 +1,276 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdarg.h>
#include <unistd.h>
#include <utime.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_err.h"
#include <sys/types.h>
#include <sys/reent.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/termios.h>
#include <sys/poll.h>
#ifdef __clang__ // TODO LLVM-330
#include <sys/dirent.h>
#else
#include <dirent.h>
#endif
#include <string.h>
#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

View File

@@ -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

View File

@@ -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