|
|
@@ -19,33 +19,27 @@
|
|
|
#include <sys/fcntl.h>
|
|
|
#include <sys/ioctl.h>
|
|
|
#include <sys/unistd.h>
|
|
|
+#include <sys/lock.h>
|
|
|
#include <dirent.h>
|
|
|
#include "esp_vfs.h"
|
|
|
#include "esp_log.h"
|
|
|
|
|
|
-/*
|
|
|
- * File descriptors visible by the applications are composed of two parts.
|
|
|
- * Lower CONFIG_MAX_FD_BITS bits are used for the actual file descriptor.
|
|
|
- * Next (16 - CONFIG_MAX_FD_BITS - 1) bits are used to identify the VFS this
|
|
|
- * descriptor corresponds to.
|
|
|
- * Highest bit is zero.
|
|
|
- * We can only use 16 bits because newlib stores file descriptor as short int.
|
|
|
- */
|
|
|
+#define VFS_MAX_COUNT 8 /* max number of VFS entries (registered filesystems) */
|
|
|
+#define LEN_PATH_PREFIX_IGNORED SIZE_MAX /* special length value for VFS which is never recognised by open() */
|
|
|
+#define FD_TABLE_ENTRY_UNUSED (fd_table_t) { .permanent = false, .vfs_index = -1, .local_fd = -1 }
|
|
|
|
|
|
-#ifndef CONFIG_MAX_FD_BITS
|
|
|
-#define CONFIG_MAX_FD_BITS 12
|
|
|
-#endif
|
|
|
+typedef uint8_t local_fd_t;
|
|
|
+_Static_assert((1 << (sizeof(local_fd_t)*8)) >= MAX_FDS, "file descriptor type too small");
|
|
|
|
|
|
-#define MAX_VFS_ID_BITS (16 - CONFIG_MAX_FD_BITS - 1)
|
|
|
-// mask of actual file descriptor (e.g. 0x00000fff)
|
|
|
-#define VFS_FD_MASK ((1 << CONFIG_MAX_FD_BITS) - 1)
|
|
|
-// max number of VFS entries
|
|
|
-#define VFS_MAX_COUNT ((1 << MAX_VFS_ID_BITS) - 1)
|
|
|
-// mask of VFS id (e.g. 0x00007000)
|
|
|
-#define VFS_INDEX_MASK (VFS_MAX_COUNT << CONFIG_MAX_FD_BITS)
|
|
|
-#define VFS_INDEX_S CONFIG_MAX_FD_BITS
|
|
|
+typedef int8_t vfs_index_t;
|
|
|
+_Static_assert((1 << (sizeof(vfs_index_t)*8)) >= VFS_MAX_COUNT, "VFS index type too small");
|
|
|
+_Static_assert(((vfs_index_t) -1) < 0, "vfs_index_t must be a signed type");
|
|
|
|
|
|
-#define LEN_PATH_PREFIX_IGNORED SIZE_MAX /* special length value for VFS which is never recognised by open() */
|
|
|
+typedef struct {
|
|
|
+ bool permanent;
|
|
|
+ vfs_index_t vfs_index;
|
|
|
+ local_fd_t local_fd;
|
|
|
+} fd_table_t;
|
|
|
|
|
|
typedef struct vfs_entry_ {
|
|
|
esp_vfs_t vfs; // contains pointers to VFS functions
|
|
|
@@ -58,7 +52,10 @@ typedef struct vfs_entry_ {
|
|
|
static vfs_entry_t* s_vfs[VFS_MAX_COUNT] = { 0 };
|
|
|
static size_t s_vfs_count = 0;
|
|
|
|
|
|
-static esp_err_t esp_vfs_register_common(const char* base_path, size_t len, const esp_vfs_t* vfs, void* ctx, int *p_minimum_fd, int *p_maximum_fd)
|
|
|
+static fd_table_t s_fd_table[MAX_FDS] = { [0 ... MAX_FDS-1] = FD_TABLE_ENTRY_UNUSED };
|
|
|
+static _lock_t s_fd_table_lock;
|
|
|
+
|
|
|
+static 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 (len != LEN_PATH_PREFIX_IGNORED) {
|
|
|
if ((len != 0 && len < 2) || (len > ESP_VFS_PATH_MAX)) {
|
|
|
@@ -96,11 +93,8 @@ static esp_err_t esp_vfs_register_common(const char* base_path, size_t len, cons
|
|
|
entry->ctx = ctx;
|
|
|
entry->offset = index;
|
|
|
|
|
|
- if (p_minimum_fd != NULL) {
|
|
|
- *p_minimum_fd = index << VFS_INDEX_S;
|
|
|
- }
|
|
|
- if (p_maximum_fd != NULL) {
|
|
|
- *p_maximum_fd = (index + 1) << VFS_INDEX_S;
|
|
|
+ if (vfs_index) {
|
|
|
+ *vfs_index = index;
|
|
|
}
|
|
|
|
|
|
return ESP_OK;
|
|
|
@@ -108,12 +102,40 @@ static esp_err_t esp_vfs_register_common(const char* base_path, size_t len, cons
|
|
|
|
|
|
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, NULL);
|
|
|
+ return esp_vfs_register_common(base_path, strlen(base_path), vfs, ctx, NULL);
|
|
|
}
|
|
|
|
|
|
-esp_err_t esp_vfs_register_socket_space(const esp_vfs_t *vfs, void *ctx, int *p_min_fd, int *p_max_fd)
|
|
|
+esp_err_t esp_vfs_register_fd_range(const esp_vfs_t *vfs, void *ctx, int min_fd, int max_fd)
|
|
|
{
|
|
|
- return esp_vfs_register_common("", LEN_PATH_PREFIX_IGNORED, vfs, ctx, p_min_fd, p_max_fd);
|
|
|
+ if (min_fd < 0 || max_fd < 0 || min_fd > MAX_FDS || max_fd > MAX_FDS || min_fd > max_fd) {
|
|
|
+ return ESP_ERR_INVALID_ARG;
|
|
|
+ }
|
|
|
+
|
|
|
+ int index = -1;
|
|
|
+ esp_err_t ret = esp_vfs_register_common("", LEN_PATH_PREFIX_IGNORED, vfs, ctx, &index);
|
|
|
+
|
|
|
+ if (ret == ESP_OK) {
|
|
|
+ _lock_acquire(&s_fd_table_lock);
|
|
|
+ for (int i = min_fd; i < max_fd; ++i) {
|
|
|
+ if (s_fd_table[i].vfs_index != -1) {
|
|
|
+ free(s_vfs[i]);
|
|
|
+ s_vfs[i] = NULL;
|
|
|
+ for (int j = min_fd; j < i; ++j) {
|
|
|
+ if (s_fd_table[j].vfs_index == index) {
|
|
|
+ s_fd_table[j] = FD_TABLE_ENTRY_UNUSED;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ _lock_release(&s_fd_table_lock);
|
|
|
+ return ESP_ERR_INVALID_ARG;
|
|
|
+ }
|
|
|
+ s_fd_table[i].permanent = true;
|
|
|
+ s_fd_table[i].vfs_index = index;
|
|
|
+ s_fd_table[i].local_fd = i;
|
|
|
+ }
|
|
|
+ _lock_release(&s_fd_table_lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
esp_err_t esp_vfs_unregister(const char* base_path)
|
|
|
@@ -126,28 +148,55 @@ esp_err_t esp_vfs_unregister(const char* base_path)
|
|
|
if (memcmp(base_path, vfs->path_prefix, vfs->path_prefix_len) == 0) {
|
|
|
free(vfs);
|
|
|
s_vfs[i] = NULL;
|
|
|
+
|
|
|
+ _lock_acquire(&s_fd_table_lock);
|
|
|
+ // Delete all references from the FD lookup-table
|
|
|
+ for (int j = 0; j < MAX_FDS; ++j) {
|
|
|
+ if (s_fd_table[j].vfs_index == i) {
|
|
|
+ s_fd_table[j] = FD_TABLE_ENTRY_UNUSED;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ _lock_release(&s_fd_table_lock);
|
|
|
+
|
|
|
return ESP_OK;
|
|
|
}
|
|
|
}
|
|
|
return ESP_ERR_INVALID_STATE;
|
|
|
}
|
|
|
|
|
|
-static const vfs_entry_t* get_vfs_for_fd(int fd)
|
|
|
+static inline const vfs_entry_t *get_vfs_for_index(int index)
|
|
|
{
|
|
|
- int index = ((fd & VFS_INDEX_MASK) >> VFS_INDEX_S);
|
|
|
- if (index >= s_vfs_count) {
|
|
|
+ if (index < 0 || index >= s_vfs_count) {
|
|
|
return NULL;
|
|
|
+ } else {
|
|
|
+ return s_vfs[index];
|
|
|
}
|
|
|
- return s_vfs[index];
|
|
|
}
|
|
|
|
|
|
-static int translate_fd(const vfs_entry_t* vfs, int fd)
|
|
|
+static inline bool fd_valid(int fd)
|
|
|
{
|
|
|
- if (vfs->vfs.flags & ESP_VFS_FLAG_SHARED_FD_SPACE) {
|
|
|
- return fd;
|
|
|
- } else {
|
|
|
- return fd & VFS_FD_MASK;
|
|
|
+ return (fd < MAX_FDS) && (fd >= 0);
|
|
|
+}
|
|
|
+
|
|
|
+static const vfs_entry_t *get_vfs_for_fd(int fd)
|
|
|
+{
|
|
|
+ const vfs_entry_t *vfs = NULL;
|
|
|
+ if (fd_valid(fd)) {
|
|
|
+ const int index = s_fd_table[fd].vfs_index; // single read -> no locking is required
|
|
|
+ vfs = get_vfs_for_index(index);
|
|
|
+ }
|
|
|
+ return vfs;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int get_local_fd(const vfs_entry_t *vfs, int fd)
|
|
|
+{
|
|
|
+ int local_fd = -1;
|
|
|
+
|
|
|
+ if (vfs && fd_valid(fd)) {
|
|
|
+ local_fd = s_fd_table[fd].local_fd; // single read -> no locking is required
|
|
|
}
|
|
|
+
|
|
|
+ return local_fd;
|
|
|
}
|
|
|
|
|
|
static const char* translate_path(const vfs_entry_t* vfs, const char* src_path)
|
|
|
@@ -248,28 +297,44 @@ static const vfs_entry_t* get_vfs_for_path(const char* path)
|
|
|
|
|
|
int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode)
|
|
|
{
|
|
|
- const vfs_entry_t* vfs = get_vfs_for_path(path);
|
|
|
+ const vfs_entry_t *vfs = get_vfs_for_path(path);
|
|
|
if (vfs == NULL) {
|
|
|
__errno_r(r) = ENOENT;
|
|
|
return -1;
|
|
|
}
|
|
|
- const char* path_within_vfs = translate_path(vfs, path);
|
|
|
- int ret;
|
|
|
- CHECK_AND_CALL(ret, r, vfs, open, path_within_vfs, flags, mode);
|
|
|
- if (ret < 0) {
|
|
|
- return ret;
|
|
|
+ const char *path_within_vfs = translate_path(vfs, path);
|
|
|
+ int fd_within_vfs;
|
|
|
+ CHECK_AND_CALL(fd_within_vfs, r, vfs, open, path_within_vfs, flags, mode);
|
|
|
+ if (fd_within_vfs >= 0) {
|
|
|
+ _lock_acquire(&s_fd_table_lock);
|
|
|
+ for (int i = 0; i < MAX_FDS; ++i) {
|
|
|
+ if (s_fd_table[i].vfs_index == -1) {
|
|
|
+ s_fd_table[i].permanent = false;
|
|
|
+ s_fd_table[i].vfs_index = vfs->offset;
|
|
|
+ s_fd_table[i].local_fd = fd_within_vfs;
|
|
|
+ _lock_release(&s_fd_table_lock);
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ _lock_release(&s_fd_table_lock);
|
|
|
+ int ret;
|
|
|
+ CHECK_AND_CALL(ret, r, vfs, close, fd_within_vfs);
|
|
|
+ (void) ret; // remove "set but not used" warning
|
|
|
+ __errno_r(r) = ENOMEM;
|
|
|
+ return -1;
|
|
|
}
|
|
|
- return ret + (vfs->offset << VFS_INDEX_S);
|
|
|
+ __errno_r(r) = ENOENT;
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size)
|
|
|
{
|
|
|
const vfs_entry_t* vfs = get_vfs_for_fd(fd);
|
|
|
- if (vfs == NULL) {
|
|
|
+ const int local_fd = get_local_fd(vfs, fd);
|
|
|
+ if (vfs == NULL || local_fd < 0) {
|
|
|
__errno_r(r) = EBADF;
|
|
|
return -1;
|
|
|
}
|
|
|
- int local_fd = translate_fd(vfs, fd);
|
|
|
ssize_t ret;
|
|
|
CHECK_AND_CALL(ret, r, vfs, write, local_fd, data, size);
|
|
|
return ret;
|
|
|
@@ -278,11 +343,11 @@ ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size)
|
|
|
off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode)
|
|
|
{
|
|
|
const vfs_entry_t* vfs = get_vfs_for_fd(fd);
|
|
|
- if (vfs == NULL) {
|
|
|
+ const int local_fd = get_local_fd(vfs, fd);
|
|
|
+ if (vfs == NULL || local_fd < 0) {
|
|
|
__errno_r(r) = EBADF;
|
|
|
return -1;
|
|
|
}
|
|
|
- int local_fd = translate_fd(vfs, fd);
|
|
|
off_t ret;
|
|
|
CHECK_AND_CALL(ret, r, vfs, lseek, local_fd, size, mode);
|
|
|
return ret;
|
|
|
@@ -291,11 +356,11 @@ off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode)
|
|
|
ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size)
|
|
|
{
|
|
|
const vfs_entry_t* vfs = get_vfs_for_fd(fd);
|
|
|
- if (vfs == NULL) {
|
|
|
+ const int local_fd = get_local_fd(vfs, fd);
|
|
|
+ if (vfs == NULL || local_fd < 0) {
|
|
|
__errno_r(r) = EBADF;
|
|
|
return -1;
|
|
|
}
|
|
|
- int local_fd = translate_fd(vfs, fd);
|
|
|
ssize_t ret;
|
|
|
CHECK_AND_CALL(ret, r, vfs, read, local_fd, dst, size);
|
|
|
return ret;
|
|
|
@@ -305,24 +370,30 @@ ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size)
|
|
|
int esp_vfs_close(struct _reent *r, int fd)
|
|
|
{
|
|
|
const vfs_entry_t* vfs = get_vfs_for_fd(fd);
|
|
|
- if (vfs == NULL) {
|
|
|
+ const int local_fd = get_local_fd(vfs, fd);
|
|
|
+ if (vfs == NULL || local_fd < 0) {
|
|
|
__errno_r(r) = EBADF;
|
|
|
return -1;
|
|
|
}
|
|
|
- int local_fd = translate_fd(vfs, fd);
|
|
|
int ret;
|
|
|
CHECK_AND_CALL(ret, r, vfs, close, local_fd);
|
|
|
+
|
|
|
+ _lock_acquire(&s_fd_table_lock);
|
|
|
+ if (!s_fd_table[fd].permanent) {
|
|
|
+ s_fd_table[fd] = FD_TABLE_ENTRY_UNUSED;
|
|
|
+ }
|
|
|
+ _lock_release(&s_fd_table_lock);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
int esp_vfs_fstat(struct _reent *r, int fd, struct stat * st)
|
|
|
{
|
|
|
const vfs_entry_t* vfs = get_vfs_for_fd(fd);
|
|
|
- if (vfs == NULL) {
|
|
|
+ const int local_fd = get_local_fd(vfs, fd);
|
|
|
+ if (vfs == NULL || local_fd < 0) {
|
|
|
__errno_r(r) = EBADF;
|
|
|
return -1;
|
|
|
}
|
|
|
- int local_fd = translate_fd(vfs, fd);
|
|
|
int ret;
|
|
|
CHECK_AND_CALL(ret, r, vfs, fstat, local_fd, st);
|
|
|
return ret;
|
|
|
@@ -404,14 +475,14 @@ DIR* opendir(const char* name)
|
|
|
DIR* ret;
|
|
|
CHECK_AND_CALLP(ret, r, vfs, opendir, path_within_vfs);
|
|
|
if (ret != NULL) {
|
|
|
- ret->dd_vfs_idx = vfs->offset << VFS_INDEX_S;
|
|
|
+ ret->dd_vfs_idx = vfs->offset;
|
|
|
}
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
struct dirent* readdir(DIR* pdir)
|
|
|
{
|
|
|
- const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx);
|
|
|
+ const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
|
|
|
struct _reent* r = __getreent();
|
|
|
if (vfs == NULL) {
|
|
|
__errno_r(r) = EBADF;
|
|
|
@@ -424,7 +495,7 @@ struct dirent* readdir(DIR* pdir)
|
|
|
|
|
|
int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent)
|
|
|
{
|
|
|
- const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx);
|
|
|
+ const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
|
|
|
struct _reent* r = __getreent();
|
|
|
if (vfs == NULL) {
|
|
|
errno = EBADF;
|
|
|
@@ -437,7 +508,7 @@ int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent)
|
|
|
|
|
|
long telldir(DIR* pdir)
|
|
|
{
|
|
|
- const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx);
|
|
|
+ const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
|
|
|
struct _reent* r = __getreent();
|
|
|
if (vfs == NULL) {
|
|
|
errno = EBADF;
|
|
|
@@ -450,7 +521,7 @@ long telldir(DIR* pdir)
|
|
|
|
|
|
void seekdir(DIR* pdir, long loc)
|
|
|
{
|
|
|
- const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx);
|
|
|
+ const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
|
|
|
struct _reent* r = __getreent();
|
|
|
if (vfs == NULL) {
|
|
|
errno = EBADF;
|
|
|
@@ -466,7 +537,7 @@ void rewinddir(DIR* pdir)
|
|
|
|
|
|
int closedir(DIR* pdir)
|
|
|
{
|
|
|
- const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx);
|
|
|
+ const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
|
|
|
struct _reent* r = __getreent();
|
|
|
if (vfs == NULL) {
|
|
|
errno = EBADF;
|
|
|
@@ -508,12 +579,12 @@ int rmdir(const char* name)
|
|
|
int fcntl(int fd, int cmd, ...)
|
|
|
{
|
|
|
const vfs_entry_t* vfs = get_vfs_for_fd(fd);
|
|
|
+ const int local_fd = get_local_fd(vfs, fd);
|
|
|
struct _reent* r = __getreent();
|
|
|
- if (vfs == NULL) {
|
|
|
+ if (vfs == NULL || local_fd < 0) {
|
|
|
__errno_r(r) = EBADF;
|
|
|
return -1;
|
|
|
}
|
|
|
- int local_fd = translate_fd(vfs, fd);
|
|
|
int ret;
|
|
|
va_list args;
|
|
|
va_start(args, cmd);
|
|
|
@@ -525,12 +596,12 @@ int fcntl(int fd, int cmd, ...)
|
|
|
int ioctl(int fd, int cmd, ...)
|
|
|
{
|
|
|
const vfs_entry_t* vfs = get_vfs_for_fd(fd);
|
|
|
+ const int local_fd = get_local_fd(vfs, fd);
|
|
|
struct _reent* r = __getreent();
|
|
|
- if (vfs == NULL) {
|
|
|
+ if (vfs == NULL || local_fd < 0) {
|
|
|
__errno_r(r) = EBADF;
|
|
|
return -1;
|
|
|
}
|
|
|
- int local_fd = translate_fd(vfs, fd);
|
|
|
int ret;
|
|
|
va_list args;
|
|
|
va_start(args, cmd);
|
|
|
@@ -542,12 +613,12 @@ int ioctl(int fd, int cmd, ...)
|
|
|
int fsync(int fd)
|
|
|
{
|
|
|
const vfs_entry_t* vfs = get_vfs_for_fd(fd);
|
|
|
+ const int local_fd = get_local_fd(vfs, fd);
|
|
|
struct _reent* r = __getreent();
|
|
|
- if (vfs == NULL) {
|
|
|
+ if (vfs == NULL || local_fd < 0) {
|
|
|
__errno_r(r) = EBADF;
|
|
|
return -1;
|
|
|
}
|
|
|
- int local_fd = translate_fd(vfs, fd);
|
|
|
int ret;
|
|
|
CHECK_AND_CALL(ret, r, vfs, fsync, local_fd);
|
|
|
return ret;
|