730 lines
18 KiB
C
730 lines
18 KiB
C
|
|
/*
|
||
|
|
* Copyright 2024, Hiroyuki OYAMA
|
||
|
|
*
|
||
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
*/
|
||
|
|
#include <string.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
// #include <sys/errno.h>
|
||
|
|
// #include <sys/dirent.h>
|
||
|
|
// #include <sys/unistd.h>
|
||
|
|
// #include <pico/mutex.h>
|
||
|
|
#include "vfs.h"
|
||
|
|
|
||
|
|
|
||
|
|
typedef struct
|
||
|
|
{
|
||
|
|
const char *dir;
|
||
|
|
void *filesystem;
|
||
|
|
void *device;
|
||
|
|
} mountpoint_t;
|
||
|
|
|
||
|
|
typedef struct
|
||
|
|
{
|
||
|
|
fs_file_t *file;
|
||
|
|
filesystem_t *filesystem;
|
||
|
|
char path[PATH_MAX1 + 1];
|
||
|
|
} file_descriptor_t;
|
||
|
|
|
||
|
|
typedef struct
|
||
|
|
{
|
||
|
|
fs_dir_t *dir;
|
||
|
|
filesystem_t *filesystem;
|
||
|
|
} dir_descriptor_t;
|
||
|
|
|
||
|
|
#if !defined(PICO_VFS_MAX_MOUNTPOINT)
|
||
|
|
#define PICO_VFS_MAX_MOUNTPOINT 10
|
||
|
|
#endif
|
||
|
|
#define FS_MAX_MOUNTPOINT PICO_VFS_MAX_MOUNTPOINT
|
||
|
|
#define STDIO_FILNO_MAX 2
|
||
|
|
#define FILENO_VALUE(fd) (fd + STDIO_FILNO_MAX + 1) // Conversion to file descriptors for publication
|
||
|
|
#define FILENO_INDEX(fd) (fd - STDIO_FILNO_MAX - 1) // Conversion to file descriptors for internal use
|
||
|
|
|
||
|
|
static mountpoint_t mountpoints[FS_MAX_MOUNTPOINT] = {0}; // Mount points and file system map
|
||
|
|
static size_t max_file_descriptor = 0; // File descriptor current maximum value
|
||
|
|
static file_descriptor_t *file_descriptor = NULL; // File descriptor and file system map
|
||
|
|
static size_t max_dir_descriptor = 0; // Dir descriptor current maximum value
|
||
|
|
static dir_descriptor_t *dir_descriptor = NULL; // Dir descriptor and file system map
|
||
|
|
|
||
|
|
static int _error_remap(int err)
|
||
|
|
{
|
||
|
|
if (err >= 0)
|
||
|
|
{
|
||
|
|
errno = 0;
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
|
||
|
|
errno = -err;
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
static const char *remove_prefix(const char *str, const char *prefix)
|
||
|
|
{
|
||
|
|
size_t len_prefix = strlen(prefix);
|
||
|
|
size_t len_str = strlen(str);
|
||
|
|
|
||
|
|
if (len_str < len_prefix || strncmp(str, prefix, len_prefix) != 0)
|
||
|
|
{
|
||
|
|
return str;
|
||
|
|
}
|
||
|
|
return str + len_prefix;
|
||
|
|
}
|
||
|
|
|
||
|
|
static mountpoint_t *find_mountpoint(const char *path)
|
||
|
|
{
|
||
|
|
mountpoint_t *longest_match = NULL;
|
||
|
|
size_t longest_length = 0;
|
||
|
|
|
||
|
|
for (size_t i = 0; i < FS_MAX_MOUNTPOINT; i++)
|
||
|
|
{
|
||
|
|
if (mountpoints[i].dir == NULL)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
size_t prefix_length = strlen(mountpoints[i].dir);
|
||
|
|
if (prefix_length > longest_length && strncmp(path, mountpoints[i].dir, prefix_length) == 0)
|
||
|
|
{
|
||
|
|
longest_match = &mountpoints[i];
|
||
|
|
longest_length = prefix_length;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return longest_match;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool is_valid_file_descriptor(int fildes)
|
||
|
|
{
|
||
|
|
if (fildes <= STDIO_FILNO_MAX || (int)max_file_descriptor <= FILENO_INDEX(fildes))
|
||
|
|
return false;
|
||
|
|
else
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
int fs_format(filesystem_t *fs, blockdevice_t *device)
|
||
|
|
{
|
||
|
|
if (!device->is_initialized)
|
||
|
|
{
|
||
|
|
int err = device->init(device);
|
||
|
|
if (err != BD_ERROR_OK)
|
||
|
|
{
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return fs->format(fs, device);
|
||
|
|
}
|
||
|
|
|
||
|
|
int fs_mount(const char *dir, filesystem_t *fs, blockdevice_t *device)
|
||
|
|
{
|
||
|
|
if (!device->is_initialized)
|
||
|
|
{
|
||
|
|
int err = device->init(device);
|
||
|
|
if (err)
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
int err = fs->mount(fs, device, false);
|
||
|
|
if (err)
|
||
|
|
{
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (size_t i = 0; i < FS_MAX_MOUNTPOINT; i++)
|
||
|
|
{
|
||
|
|
if (mountpoints[i].filesystem == NULL)
|
||
|
|
{
|
||
|
|
mountpoints[i].filesystem = fs;
|
||
|
|
mountpoints[i].device = device;
|
||
|
|
mountpoints[i].dir = VFS_strdup(dir);
|
||
|
|
return _error_remap(0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return _error_remap(-EFAULT);
|
||
|
|
}
|
||
|
|
|
||
|
|
int fs_unmount(const char *path)
|
||
|
|
{
|
||
|
|
|
||
|
|
mountpoint_t *mp = find_mountpoint(path);
|
||
|
|
if (mp == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-ENOENT);
|
||
|
|
}
|
||
|
|
filesystem_t *fs = mp->filesystem;
|
||
|
|
int err = fs->unmount(fs);
|
||
|
|
if (err)
|
||
|
|
{
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
|
||
|
|
mp->filesystem = NULL;
|
||
|
|
mp->device = NULL;
|
||
|
|
VFS_free(mp->dir);
|
||
|
|
return _error_remap(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
int fs_reformat(const char *path)
|
||
|
|
{
|
||
|
|
|
||
|
|
mountpoint_t *mp = find_mountpoint(path);
|
||
|
|
if (mp == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-ENOENT);
|
||
|
|
}
|
||
|
|
filesystem_t *fs = mp->filesystem;
|
||
|
|
blockdevice_t *device = mp->device;
|
||
|
|
|
||
|
|
int err = fs->unmount(fs);
|
||
|
|
if (err)
|
||
|
|
{
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
err = fs->format(fs, device);
|
||
|
|
if (err)
|
||
|
|
{
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
err = fs->mount(fs, device, false);
|
||
|
|
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
|
||
|
|
int fs_info(const char *path, filesystem_t **fs, blockdevice_t **device)
|
||
|
|
{
|
||
|
|
(void)fs;
|
||
|
|
(void)device;
|
||
|
|
|
||
|
|
mountpoint_t *mp = find_mountpoint(path);
|
||
|
|
if (mp == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-ENOENT);
|
||
|
|
}
|
||
|
|
*fs = mp->filesystem;
|
||
|
|
*device = mp->device;
|
||
|
|
return _error_remap(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
#if 0
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
int _fstat(int fildes, struct stat *st) {
|
||
|
|
auto_init_recursive_mutex(_mutex);
|
||
|
|
recursive_mutex_enter_blocking(&_mutex);
|
||
|
|
|
||
|
|
if (fildes == STDIN_FILENO || fildes == STDOUT_FILENO || fildes == STDERR_FILENO) {
|
||
|
|
recursive_mutex_exit(&_mutex);
|
||
|
|
st->st_size = 0;
|
||
|
|
st->st_mode = S_IFCHR;
|
||
|
|
return _error_remap(0);
|
||
|
|
}
|
||
|
|
if (!is_valid_file_descriptor(fildes)) {
|
||
|
|
recursive_mutex_exit(&_mutex);
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
|
||
|
|
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||
|
|
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||
|
|
if (fs == NULL) {
|
||
|
|
recursive_mutex_exit(&_mutex);
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
|
||
|
|
off_t size = 0;
|
||
|
|
if (fs->type != FILESYSTEM_TYPE_FAT) {
|
||
|
|
off_t current = fs->file_tell(fs, file);
|
||
|
|
size = fs->file_seek(fs, file, 0, SEEK_END);
|
||
|
|
off_t err = fs->file_seek(fs, file, current, SEEK_SET);
|
||
|
|
if (current != err) {
|
||
|
|
recursive_mutex_exit(&_mutex);
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
/* NOTE: Support for different behaviour of FatFs from POSIX
|
||
|
|
*
|
||
|
|
* FatFs has a problem where f_size() reports a larger than actual file size when
|
||
|
|
* moved to a position beyond the f_lseek() file size; POSIX reports the actual size
|
||
|
|
* written to the file, not the seek position.
|
||
|
|
*/
|
||
|
|
const char *path = file_descriptor[FILENO_INDEX(fildes)].path;
|
||
|
|
mountpoint_t *mp = find_mountpoint(path);
|
||
|
|
if (mp == NULL) {
|
||
|
|
recursive_mutex_exit(&_mutex);
|
||
|
|
return _error_remap(-ENOENT);
|
||
|
|
}
|
||
|
|
const char *entity_path = remove_prefix(path, mp->dir);
|
||
|
|
filesystem_t *fs = mp->filesystem;
|
||
|
|
|
||
|
|
struct stat finfo = {0};
|
||
|
|
int err = fs->stat(fs, entity_path, &finfo);
|
||
|
|
if (err != 0) {
|
||
|
|
recursive_mutex_exit(&_mutex);
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
size = finfo.st_size;
|
||
|
|
}
|
||
|
|
recursive_mutex_exit(&_mutex);
|
||
|
|
|
||
|
|
st->st_size = size;
|
||
|
|
st->st_mode = S_IFREG;
|
||
|
|
return _error_remap(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
#endif
|
||
|
|
char *fs_strerror(int errnum)
|
||
|
|
{
|
||
|
|
if (errnum > 5000)
|
||
|
|
{
|
||
|
|
// SD blockdevice error
|
||
|
|
const char *str = "";
|
||
|
|
switch (errnum)
|
||
|
|
{
|
||
|
|
case 5001:
|
||
|
|
str = "operation would block";
|
||
|
|
break;
|
||
|
|
case 5002:
|
||
|
|
str = "unsupported operation";
|
||
|
|
break;
|
||
|
|
case 5003:
|
||
|
|
str = "invalid parameter";
|
||
|
|
break;
|
||
|
|
case 5004:
|
||
|
|
str = "uninitialized";
|
||
|
|
break;
|
||
|
|
case 5005:
|
||
|
|
str = "device is missing or not connected";
|
||
|
|
break;
|
||
|
|
case 5006:
|
||
|
|
str = "write protected";
|
||
|
|
break;
|
||
|
|
case 5007:
|
||
|
|
str = "unusable card";
|
||
|
|
break;
|
||
|
|
case 5008:
|
||
|
|
str = "No response from device";
|
||
|
|
break;
|
||
|
|
case 5009:
|
||
|
|
str = "CRC error";
|
||
|
|
break;
|
||
|
|
case 5010:
|
||
|
|
str = "Erase error: reset/sequence";
|
||
|
|
break;
|
||
|
|
case 5011:
|
||
|
|
str = "Write error: !SPI_DATA_ACCEPTED";
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
return (char *)str;
|
||
|
|
}
|
||
|
|
else if (errnum > 4000)
|
||
|
|
{
|
||
|
|
// On-board flash blockdevice error
|
||
|
|
const char *str = "";
|
||
|
|
switch (errnum)
|
||
|
|
{
|
||
|
|
case 4001:
|
||
|
|
str = "operation timeout";
|
||
|
|
break;
|
||
|
|
case 4002:
|
||
|
|
str = "safe execution is not possible";
|
||
|
|
break;
|
||
|
|
case 4003:
|
||
|
|
str = "method fails due to dynamic resource exhaustion";
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
return (char *)str;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
return strerror(errnum);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static int _assign_file_descriptor()
|
||
|
|
{
|
||
|
|
int fd = -1;
|
||
|
|
if (max_file_descriptor == 0)
|
||
|
|
{
|
||
|
|
max_file_descriptor = 2;
|
||
|
|
file_descriptor = VFS_calloc(max_file_descriptor, sizeof(file_descriptor_t));
|
||
|
|
if (file_descriptor == NULL)
|
||
|
|
{
|
||
|
|
printf("_open: Out of memory\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for (size_t i = 0; i < max_file_descriptor; i++)
|
||
|
|
{
|
||
|
|
if (file_descriptor[i].filesystem == NULL)
|
||
|
|
{
|
||
|
|
fd = i;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (fd == -1)
|
||
|
|
{
|
||
|
|
// Extend the management array
|
||
|
|
size_t last_max = max_file_descriptor;
|
||
|
|
max_file_descriptor *= 2;
|
||
|
|
file_descriptor = realloc(file_descriptor, sizeof(file_descriptor_t) * max_file_descriptor);
|
||
|
|
if (file_descriptor == NULL)
|
||
|
|
{
|
||
|
|
printf("_open: Out of memory\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
for (size_t i = last_max; i < max_file_descriptor; i++)
|
||
|
|
{
|
||
|
|
file_descriptor[i].filesystem = NULL;
|
||
|
|
file_descriptor[i].file = NULL;
|
||
|
|
}
|
||
|
|
fd = last_max;
|
||
|
|
}
|
||
|
|
return FILENO_VALUE(fd);
|
||
|
|
}
|
||
|
|
|
||
|
|
int VFS_open(const char *path, int oflags, ...)
|
||
|
|
{
|
||
|
|
|
||
|
|
mountpoint_t *mp = find_mountpoint(path);
|
||
|
|
if (mp == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-ENOENT);
|
||
|
|
}
|
||
|
|
const char *entity_path = remove_prefix(path, mp->dir);
|
||
|
|
// find file descriptor
|
||
|
|
int fd = _assign_file_descriptor();
|
||
|
|
if (fd == -1)
|
||
|
|
{
|
||
|
|
return _error_remap(-ENFILE);
|
||
|
|
}
|
||
|
|
|
||
|
|
filesystem_t *fs = mp->filesystem;
|
||
|
|
fs_file_t *file = file_descriptor[FILENO_INDEX(fd)].file = VFS_calloc(1, sizeof(fs_file_t));
|
||
|
|
if (file == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-ENOMEM);
|
||
|
|
}
|
||
|
|
int err = fs->file_open(fs, file, entity_path, oflags);
|
||
|
|
if (err < 0)
|
||
|
|
{
|
||
|
|
VFS_free(file);
|
||
|
|
file_descriptor[FILENO_INDEX(fd)].file = NULL;
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
file_descriptor[FILENO_INDEX(fd)].filesystem = fs;
|
||
|
|
strncpy(file_descriptor[FILENO_INDEX(fd)].path, path, PATH_MAX1);
|
||
|
|
return _error_remap(fd);
|
||
|
|
}
|
||
|
|
|
||
|
|
ssize_t VFS_read(int fildes, void *buf, size_t nbyte)
|
||
|
|
{
|
||
|
|
|
||
|
|
if (!is_valid_file_descriptor(fildes))
|
||
|
|
{
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||
|
|
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||
|
|
if (fs == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
ssize_t size = fs->file_read(fs, file, buf, nbyte);
|
||
|
|
|
||
|
|
return _error_remap(size);
|
||
|
|
}
|
||
|
|
|
||
|
|
ssize_t VFS_write(int fildes, const void *buf, size_t nbyte)
|
||
|
|
{
|
||
|
|
if (!is_valid_file_descriptor(fildes))
|
||
|
|
{
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||
|
|
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||
|
|
if (fs == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
ssize_t size = fs->file_write(fs, file, buf, nbyte);
|
||
|
|
return _error_remap(size);
|
||
|
|
}
|
||
|
|
|
||
|
|
int VFS_close(int fildes)
|
||
|
|
{
|
||
|
|
if (!is_valid_file_descriptor(fildes))
|
||
|
|
{
|
||
|
|
printf("_close error fildes=%d\n", fildes);
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||
|
|
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||
|
|
if (fs == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
int err = fs->file_close(fs, file);
|
||
|
|
VFS_free(file);
|
||
|
|
file_descriptor[FILENO_INDEX(fildes)].filesystem = NULL;
|
||
|
|
file_descriptor[FILENO_INDEX(fildes)].file = NULL;
|
||
|
|
file_descriptor[FILENO_INDEX(fildes)].path[0] = '\0';
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
int32_t VFS_seek(int fildes, int32_t offset, int whence)
|
||
|
|
{
|
||
|
|
if (!is_valid_file_descriptor(fildes))
|
||
|
|
{
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||
|
|
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||
|
|
if (fs == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t pos = fs->file_seek(fs, file, offset, whence);
|
||
|
|
return _error_remap(pos);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
int32_t VFS_fsize(int fildes)
|
||
|
|
{
|
||
|
|
if (!is_valid_file_descriptor(fildes))
|
||
|
|
{
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||
|
|
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||
|
|
if (fs == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
|
||
|
|
int32_t filesize = fs->file_size(fs, file);
|
||
|
|
return _error_remap(filesize);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
int VFS_stat(const char *path, struct VFS_stat *st)
|
||
|
|
{
|
||
|
|
mountpoint_t *mp = find_mountpoint(path);
|
||
|
|
if (mp == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-ENOENT);
|
||
|
|
}
|
||
|
|
const char *entity_path = remove_prefix(path, mp->dir);
|
||
|
|
filesystem_t *fs = mp->filesystem;
|
||
|
|
int err = fs->stat(fs, entity_path, st);
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
|
||
|
|
int VFS_ftruncate(int fildes, int32_t length)
|
||
|
|
{
|
||
|
|
if (!is_valid_file_descriptor(fildes))
|
||
|
|
{
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
fs_file_t *file = file_descriptor[FILENO_INDEX(fildes)].file;
|
||
|
|
filesystem_t *fs = file_descriptor[FILENO_INDEX(fildes)].filesystem;
|
||
|
|
if (fs == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
int err = fs->file_truncate(fs, file, length);
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
|
||
|
|
int VFS_mkdir(const char *path, mode_t mode)
|
||
|
|
{
|
||
|
|
mountpoint_t *mp = find_mountpoint(path);
|
||
|
|
if (mp == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-ENOENT);
|
||
|
|
}
|
||
|
|
const char *entity_path = remove_prefix(path, mp->dir);
|
||
|
|
filesystem_t *fs = mp->filesystem;
|
||
|
|
int err = fs->mkdir(fs, entity_path, mode);
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int _assign_dir_descriptor(void)
|
||
|
|
{
|
||
|
|
int fd = -1;
|
||
|
|
if (max_dir_descriptor == 0)
|
||
|
|
{
|
||
|
|
max_dir_descriptor = 2;
|
||
|
|
dir_descriptor = VFS_calloc(max_dir_descriptor, sizeof(dir_descriptor_t));
|
||
|
|
if (dir_descriptor == NULL)
|
||
|
|
{
|
||
|
|
printf("_opendir: Out of memory\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for (size_t i = 0; i < max_dir_descriptor; i++)
|
||
|
|
{
|
||
|
|
if (dir_descriptor[i].filesystem == NULL)
|
||
|
|
{
|
||
|
|
fd = i;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (fd == -1)
|
||
|
|
{
|
||
|
|
// Extend the management array
|
||
|
|
size_t last_max = max_dir_descriptor;
|
||
|
|
max_dir_descriptor *= 2;
|
||
|
|
dir_descriptor = realloc(dir_descriptor, sizeof(dir_descriptor_t) * max_dir_descriptor);
|
||
|
|
if (dir_descriptor == NULL)
|
||
|
|
{
|
||
|
|
printf("_opendir: Out of memory\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
for (size_t i = last_max; i < max_dir_descriptor; i++)
|
||
|
|
{
|
||
|
|
dir_descriptor[i].filesystem = NULL;
|
||
|
|
dir_descriptor[i].dir = NULL;
|
||
|
|
}
|
||
|
|
fd = (int)last_max;
|
||
|
|
}
|
||
|
|
return fd;
|
||
|
|
}
|
||
|
|
|
||
|
|
fs_dir_t *VFS_opendir(const char *path)
|
||
|
|
{
|
||
|
|
|
||
|
|
mountpoint_t *mp = find_mountpoint(path);
|
||
|
|
if (mp == NULL)
|
||
|
|
{
|
||
|
|
_error_remap(-ENOENT);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
const char *entity_path = remove_prefix(path, mp->dir);
|
||
|
|
// find dir descriptor
|
||
|
|
int fd = _assign_dir_descriptor();
|
||
|
|
if (fd == -1)
|
||
|
|
{
|
||
|
|
_error_remap(-ENFILE);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
fs_dir_t *dir = dir_descriptor[fd].dir = VFS_calloc(1, sizeof(fs_dir_t));
|
||
|
|
if (dir == NULL)
|
||
|
|
{
|
||
|
|
_error_remap(-ENOMEM);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
filesystem_t *fs = mp->filesystem;
|
||
|
|
int err = fs->dir_open(fs, dir, entity_path);
|
||
|
|
if (err != 0)
|
||
|
|
{
|
||
|
|
VFS_free(dir);
|
||
|
|
dir_descriptor[fd].dir = NULL;
|
||
|
|
_error_remap(err);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
dir_descriptor[fd].filesystem = fs;
|
||
|
|
dir->fd = fd;
|
||
|
|
|
||
|
|
return dir;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct dirent *VFS_readdir(fs_dir_t *dir)
|
||
|
|
{
|
||
|
|
fs_dir_t *_dir = dir_descriptor[dir->fd].dir;
|
||
|
|
filesystem_t *fs = dir_descriptor[dir->fd].filesystem;
|
||
|
|
if (fs == NULL)
|
||
|
|
{
|
||
|
|
_error_remap(-EBADF);
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
VFS_memset(&_dir->current, 0, sizeof(_dir->current));
|
||
|
|
int err = fs->dir_read(fs, _dir, &_dir->current);
|
||
|
|
if (err == 0)
|
||
|
|
{
|
||
|
|
|
||
|
|
return &_dir->current;
|
||
|
|
}
|
||
|
|
else if (err == -ENOENT)
|
||
|
|
{
|
||
|
|
VFS_memset(&_dir->current, 0, sizeof(_dir->current));
|
||
|
|
_error_remap(0);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
VFS_memset(&_dir->current, 0, sizeof(_dir->current));
|
||
|
|
_error_remap(err);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int VFS_closedir(fs_dir_t *dir)
|
||
|
|
{
|
||
|
|
int fd = dir->fd;
|
||
|
|
fs_dir_t *_dir = dir_descriptor[dir->fd].dir;
|
||
|
|
filesystem_t *fs = dir_descriptor[dir->fd].filesystem;
|
||
|
|
if (fs == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-EBADF);
|
||
|
|
}
|
||
|
|
int err = fs->dir_close(fs, _dir);
|
||
|
|
dir_descriptor[fd].filesystem = NULL;
|
||
|
|
VFS_free(dir_descriptor[fd].dir);
|
||
|
|
dir_descriptor[fd].dir = NULL;
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
|
||
|
|
int VFS_rmdir(const char *path)
|
||
|
|
{
|
||
|
|
mountpoint_t *mp = find_mountpoint(path);
|
||
|
|
if (mp == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-ENOENT);
|
||
|
|
}
|
||
|
|
const char *entity_path = remove_prefix(path, mp->dir);
|
||
|
|
printf("entity_path:%s\n", entity_path);
|
||
|
|
filesystem_t *fs = mp->filesystem;
|
||
|
|
int err = fs->rmdir(fs, entity_path);
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
|
||
|
|
int VFS_rename(const char *old, const char *new)
|
||
|
|
{
|
||
|
|
mountpoint_t *mp = find_mountpoint(old);
|
||
|
|
if (mp == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-ENOENT);
|
||
|
|
}
|
||
|
|
const char *old_entity_path = remove_prefix(old, mp->dir);
|
||
|
|
const char *new_entity_path = remove_prefix(new, mp->dir);
|
||
|
|
|
||
|
|
filesystem_t *fs = mp->filesystem;
|
||
|
|
int err = fs->rename(fs, old_entity_path, new_entity_path);
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
|
||
|
|
int VFS_unlink(const char *path)
|
||
|
|
{
|
||
|
|
|
||
|
|
mountpoint_t *mp = find_mountpoint(path);
|
||
|
|
if (mp == NULL)
|
||
|
|
{
|
||
|
|
return _error_remap(-ENOENT);
|
||
|
|
}
|
||
|
|
const char *entity_path = remove_prefix(path, mp->dir);
|
||
|
|
filesystem_t *fs = mp->filesystem;
|
||
|
|
int err = fs->remove(fs, entity_path);
|
||
|
|
return _error_remap(err);
|
||
|
|
}
|
||
|
|
|