| |
| |
| |
| |
|
|
| #include <memory> |
|
|
| #include "backend.h" |
| #include "file.h" |
| #include "node_backend.h" |
| #include "support.h" |
| #include "wasmfs.h" |
|
|
| namespace wasmfs { |
|
|
| class NodeBackend; |
|
|
| |
| class NodeState { |
| |
| size_t openCount = 0; |
| oflags_t openFlags = 0; |
| int fd = -1; |
|
|
| public: |
| std::string path; |
|
|
| NodeState(std::string path) : path(path) {} |
|
|
| int getFD() { |
| assert(openCount > 0); |
| return fd; |
| } |
|
|
| bool isOpen() { return openCount > 0; } |
|
|
| |
| |
| int open(oflags_t flags) { |
| int result = 0; |
| if (openCount == 0) { |
| |
| switch (flags) { |
| case O_RDONLY: |
| result = _wasmfs_node_open(path.c_str(), "r"); |
| break; |
| case O_WRONLY: |
| |
| |
| |
| |
| |
| |
| |
| case O_RDWR: |
| result = _wasmfs_node_open(path.c_str(), "r+"); |
| break; |
| default: |
| WASMFS_UNREACHABLE("Unexpected open access mode"); |
| } |
| if (result < 0) { |
| return result; |
| } |
| |
| } else if ((openFlags == O_RDONLY && |
| (flags == O_WRONLY || flags == O_RDWR)) || |
| (openFlags == O_WRONLY && |
| (flags == O_RDONLY || flags == O_RDWR))) { |
| |
| |
| result = _wasmfs_node_open(path.c_str(), "r+"); |
| if (result < 0) { |
| return result; |
| } |
| |
| (void)_wasmfs_node_close(fd); |
| |
| } else { |
| |
| ++openCount; |
| return 0; |
| } |
| |
| fd = result; |
| openFlags = flags; |
| ++openCount; |
| return 0; |
| } |
|
|
| int close() { |
| int ret = 0; |
| if (--openCount == 0) { |
| ret = _wasmfs_node_close(fd); |
| *this = NodeState(path); |
| } |
| return ret; |
| } |
| }; |
|
|
| class NodeFile : public DataFile { |
| public: |
| NodeState state; |
|
|
| NodeFile(mode_t mode, backend_t backend, std::string path) |
| : DataFile(mode, backend), state(path) {} |
|
|
| private: |
| off_t getSize() override { |
| |
| uint32_t size; |
| if (state.isOpen()) { |
| if (_wasmfs_node_fstat_size(state.getFD(), &size)) { |
| |
| return 0; |
| } |
| } else { |
| if (_wasmfs_node_stat_size(state.path.c_str(), &size)) { |
| |
| return 0; |
| } |
| } |
| return off_t(size); |
| } |
|
|
| int setSize(off_t size) override { |
| if (state.isOpen()) { |
| return -_wasmfs_node_ftruncate(state.getFD(), size); |
| } |
| return -_wasmfs_node_truncate(state.path.c_str(), size); |
| } |
|
|
| int open(oflags_t flags) override { return state.open(flags); } |
|
|
| int close() override { return state.close(); } |
|
|
| ssize_t read(uint8_t* buf, size_t len, off_t offset) override { |
| uint32_t nread; |
| if (auto err = _wasmfs_node_read(state.getFD(), buf, len, offset, &nread)) { |
| return -err; |
| } |
| return nread; |
| } |
|
|
| ssize_t write(const uint8_t* buf, size_t len, off_t offset) override { |
| uint32_t nwritten; |
| if (auto err = |
| _wasmfs_node_write(state.getFD(), buf, len, offset, &nwritten)) { |
| return -err; |
| } |
| return nwritten; |
| } |
|
|
| int flush() override { |
| WASMFS_UNREACHABLE("TODO: implement NodeFile::flush"); |
| } |
| }; |
|
|
| class NodeSymlink : public Symlink { |
| public: |
| std::string path; |
|
|
| NodeSymlink(backend_t backend, std::string path) |
| : Symlink(backend), path(path) {} |
|
|
| virtual std::string getTarget() const { |
| char buf[PATH_MAX]; |
| if (_wasmfs_node_readlink(path.c_str(), buf, PATH_MAX) < 0) { |
| WASMFS_UNREACHABLE("getTarget cannot fail"); |
| } |
| return std::string(buf); |
| } |
| }; |
|
|
| class NodeDirectory : public Directory { |
| public: |
| NodeState state; |
|
|
| NodeDirectory(mode_t mode, backend_t backend, std::string path) |
| : Directory(mode, backend), state(path) {} |
|
|
| private: |
| std::string getChildPath(const std::string& name) { |
| return state.path + '/' + name; |
| } |
|
|
| std::shared_ptr<File> getChild(const std::string& name) override { |
| static_assert(std::is_same_v<mode_t, unsigned int>); |
| |
| auto childPath = getChildPath(name); |
| mode_t mode; |
| if (_wasmfs_node_get_mode(childPath.c_str(), &mode)) { |
| return nullptr; |
| } |
| |
| |
| if (S_ISREG(mode) || S_ISCHR(mode)) { |
| return std::make_shared<NodeFile>(mode, getBackend(), childPath); |
| } else if (S_ISDIR(mode)) { |
| return std::make_shared<NodeDirectory>(mode, getBackend(), childPath); |
| } else if (S_ISLNK(mode)) { |
| return std::make_shared<NodeSymlink>(getBackend(), childPath); |
| } else { |
| |
| return nullptr; |
| } |
| } |
|
|
| int removeChild(const std::string& name) override { |
| auto childPath = getChildPath(name); |
| |
| if (auto err = _wasmfs_node_unlink(childPath.c_str())) { |
| if (err == EISDIR) { |
| err = _wasmfs_node_rmdir(childPath.c_str()); |
| } |
| return -err; |
| } |
| return 0; |
| } |
|
|
| std::shared_ptr<DataFile> insertDataFile(const std::string& name, |
| mode_t mode) override { |
| auto childPath = getChildPath(name); |
| if (_wasmfs_node_insert_file(childPath.c_str(), mode)) { |
| return nullptr; |
| } |
| return std::make_shared<NodeFile>(mode, getBackend(), childPath); |
| } |
|
|
| std::shared_ptr<Directory> insertDirectory(const std::string& name, |
| mode_t mode) override { |
| auto childPath = getChildPath(name); |
| if (_wasmfs_node_insert_directory(childPath.c_str(), mode)) { |
| return nullptr; |
| } |
| return std::make_shared<NodeDirectory>(mode, getBackend(), childPath); |
| } |
|
|
| std::shared_ptr<Symlink> insertSymlink(const std::string& name, |
| const std::string& target) override { |
| auto childPath = getChildPath(name); |
| if (_wasmfs_node_symlink(target.c_str(), childPath.c_str())) { |
| return nullptr; |
| } |
| return std::make_shared<NodeSymlink>(getBackend(), childPath); |
| } |
|
|
| int insertMove(const std::string& name, std::shared_ptr<File> file) override { |
| std::string fromPath; |
|
|
| if (file->is<DataFile>()) { |
| auto nodeFile = std::static_pointer_cast<NodeFile>(file); |
| fromPath = nodeFile->state.path; |
| } else { |
| auto nodeDir = std::static_pointer_cast<NodeDirectory>(file); |
| fromPath = nodeDir->state.path; |
| } |
|
|
| auto childPath = getChildPath(name); |
| return -_wasmfs_node_rename(fromPath.c_str(), childPath.c_str()); |
| } |
|
|
| ssize_t getNumEntries() override { |
| |
| auto entries = getEntries(); |
| if (int err = entries.getError()) { |
| return err; |
| } |
| return entries->size(); |
| } |
|
|
| Directory::MaybeEntries getEntries() override { |
| std::vector<Directory::Entry> entries; |
| int err = _wasmfs_node_readdir(state.path.c_str(), &entries); |
| if (err) { |
| return {-err}; |
| } |
| return {entries}; |
| } |
| }; |
|
|
| class NodeBackend : public Backend { |
| |
| std::string mountPath; |
|
|
| public: |
| NodeBackend(const std::string& mountPath) : mountPath(mountPath) {} |
|
|
| std::shared_ptr<DataFile> createFile(mode_t mode) override { |
| return std::make_shared<NodeFile>(mode, this, mountPath); |
| } |
|
|
| std::shared_ptr<Directory> createDirectory(mode_t mode) override { |
| return std::make_shared<NodeDirectory>(mode, this, mountPath); |
| } |
|
|
| std::shared_ptr<Symlink> createSymlink(std::string target) override { |
| WASMFS_UNREACHABLE("TODO: implement NodeBackend::createSymlink"); |
| } |
| }; |
|
|
| |
|
|
| extern "C" { |
|
|
| backend_t wasmfs_create_node_backend(const char* root) { |
| return wasmFS.addBackend(std::make_unique<NodeBackend>(root)); |
| } |
|
|
| void EMSCRIPTEN_KEEPALIVE _wasmfs_node_record_dirent( |
| std::vector<Directory::Entry>* entries, const char* name, int type) { |
| entries->push_back({name, File::FileKind(type), 0}); |
| } |
|
|
| } |
|
|
| } |
|
|