Files
earlephilhower_arduino-pico/libraries/VFS/src/VFS.cpp
T
Earle F. Philhower, III 16d9609ac9 Add VFS to enable POSIX file I/O operations (#2333)
* Add VFS to enable POSIX file I/O operations

Enables use of FILE * operations on internal and external storage.  fopen,
fclose, fseek, fprintf, fscanf, etc. supported.

* Add FS/File::stat and support POSIX stat/fstat
2024-08-16 13:37:23 -07:00

187 lines
4.6 KiB
C++

/*
VFS wrapper to allow POSIX FILE operations
Copyright (c) 2024 Earle F. Philhower, III <earlephilhower@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <list>
#include <map>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cerrno>
#include <FSImpl.h>
#include <FS.h>
#include "VFS.h"
// Global static to allow non-class POSIX calls to use this info
typedef struct {
const char *path;
FS *fs;
} Entry;
static FS *root = nullptr;
static std::list<Entry> mounts;
static std::map<int, File> files;
static int fd = 3;
VFSClass::VFSClass() {
}
void VFSClass::root(FS &fs) {
::root = &fs;
}
void VFSClass::map(const char *path, FS &fs) {
Entry e = { strdup(path), &fs };
mounts.push_back(e);
}
static FS *pathToFS(const char **name) {
const char *nm = *name;
for (auto a : mounts) {
if (!strncmp(a.path, nm, strlen(a.path))) {
*name += strlen(a.path);
return a.fs;
}
}
return ::root;
}
extern "C" int _open(char *file, int flags, int mode) {
(void) mode; // No mode RWX here
const char *nm = file;
auto fs = pathToFS(&nm);
if (!fs) {
return -1;
}
const char *md = "r";
// Taken from table at https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html
flags &= O_RDONLY | O_WRONLY | O_CREAT | O_TRUNC | O_APPEND | O_RDWR;
if (flags == O_RDONLY) {
md = "r";
} else if (flags == (O_WRONLY | O_CREAT | O_TRUNC)) {
md = "w";
} else if (flags == (O_WRONLY | O_CREAT | O_APPEND)) {
md = "a";
} else if (flags == O_RDWR) {
md = "r+";
} else if (flags == (O_RDWR | O_CREAT | O_TRUNC)) {
md = "w+";
} else if (flags == (O_RDWR | O_CREAT | O_APPEND)) {
md = "a+";
}
File f = fs->open(nm, md);
if (!f) {
return -1;
}
files.insert({fd, f});
return fd++;
}
extern "C" ssize_t _write(int fd, const void *buf, size_t count) {
#if defined DEBUG_RP2040_PORT
if (fd < 3) {
return DEBUG_RP2040_PORT.write((const char *)buf, count);
}
#endif
auto f = files.find(fd);
if (f == files.end()) {
return 0; // FD not found
}
return f->second.write((const char *)buf, count);
}
extern "C" int _close(int fd) {
auto f = files.find(fd);
if (f == files.end()) {
return -1;
}
f->second.close();
files.erase(f);
return 0;
}
extern "C" int _lseek(int fd, int ptr, int dir) {
auto f = files.find(fd);
if (f == files.end()) {
return -1;
}
SeekMode d = SeekSet;
if (dir == SEEK_CUR) {
d = SeekCur;
} else if (dir == SEEK_END) {
d = SeekEnd;
}
return f->second.seek(ptr, d) ? 0 : 1;
}
extern "C" int _read(int fd, char *buf, int size) {
auto f = files.find(fd);
if (f == files.end()) {
return -1; // FD not found
}
return f->second.read((uint8_t *)buf, size);
}
extern "C" int _unlink(char *name) {
auto f = pathToFS((const char **)&name);
if (f) {
return f->remove(name) ? 0 : -1;
}
return -1;
}
extern "C" int _stat(const char *name, struct stat *st) {
auto f = pathToFS((const char **)&name);
if (f) {
fs::FSStat s;
if (!f->stat(name, &s)) {
return -1;
}
bzero(st, sizeof(*st));
st->st_size = s.size;
st->st_blksize = s.blocksize;
st->st_ctim.tv_sec = s.ctime;
st->st_atim.tv_sec = s.atime;
st->st_mode = s.isDir ? S_IFDIR : S_IFREG;
return 0;
}
return -1;
}
extern "C" int _fstat(int fd, struct stat *st) {
auto f = files.find(fd);
if (f == files.end()) {
return -1; // FD not found
}
fs::FSStat s;
if (!f->second.stat(&s)) {
return -1;
}
bzero(st, sizeof(*st));
st->st_size = s.size;
st->st_blksize = s.blocksize;
st->st_ctim.tv_sec = s.ctime;
st->st_ctim.tv_sec = s.ctime;
st->st_mode = s.isDir ? S_IFDIR : S_IFREG;
return 0;
}
VFSClass VFS;