Buckets:
arudradey/fullhtml-storage / emsdk /upstream /emscripten /system /lib /wasmfs /backends /node_backend.cpp
| // Copyright 2022 The Emscripten Authors. All rights reserved. | |
| // Emscripten is available under two separate licenses, the MIT license and the | |
| // University of Illinois/NCSA Open Source License. Both these licenses can be | |
| // found in the LICENSE file. | |
| namespace wasmfs { | |
| class NodeBackend; | |
| // The state of a file on the underlying Node file system. | |
| class NodeState { | |
| // Map all separate WasmFS opens of a file to a single underlying fd. | |
| 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; } | |
| // Attempt to open the file with the given flags, returning 0 on success or an | |
| // error code. | |
| int open(oflags_t flags) { | |
| int result = 0; | |
| if (openCount == 0) { | |
| // No existing fd, so open a fresh one. | |
| switch (flags) { | |
| case O_RDONLY: | |
| result = _wasmfs_node_open(path.c_str(), "r"); | |
| break; | |
| case O_WRONLY: | |
| // TODO(sbc): Specific handling of O_WRONLY. | |
| // There is no simple way to map O_WRONLY to an fopen-style | |
| // mode string since the only two modes that are write only | |
| // are `w` and `a`. The problem with the former is that it | |
| // truncates to file. The problem with the latter is that it | |
| // opens for appending. For now simply opening in O_RDWR | |
| // mode is enough to pass all our tests. | |
| case O_RDWR: | |
| result = _wasmfs_node_open(path.c_str(), "r+"); | |
| break; | |
| default: | |
| WASMFS_UNREACHABLE("Unexpected open access mode"); | |
| } | |
| if (result < 0) { | |
| return result; | |
| } | |
| // Fall through to update our state with the new result. | |
| } else if ((openFlags == O_RDONLY && | |
| (flags == O_WRONLY || flags == O_RDWR)) || | |
| (openFlags == O_WRONLY && | |
| (flags == O_RDONLY || flags == O_RDWR))) { | |
| // We already have a file descriptor, but we need to replace it with a new | |
| // fd with more access. | |
| result = _wasmfs_node_open(path.c_str(), "r+"); | |
| if (result < 0) { | |
| return result; | |
| } | |
| // Success! Close the old fd before updating it. | |
| (void)_wasmfs_node_close(fd); | |
| // Fall through to update our state with the new result. | |
| } else { | |
| // Reuse the existing file descriptor. | |
| ++openCount; | |
| return 0; | |
| } | |
| // Update our state for the new fd. | |
| 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 { | |
| // TODO: This should really be using a 64-bit file size type. | |
| uint32_t size; | |
| if (state.isOpen()) { | |
| if (_wasmfs_node_fstat_size(state.getFD(), &size)) { | |
| // TODO: Make this fallible. | |
| return 0; | |
| } | |
| } else { | |
| if (_wasmfs_node_stat_size(state.path.c_str(), &size)) { | |
| // TODO: Make this fallible. | |
| 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>); | |
| // TODO: also retrieve and set ctime, atime, ino, etc. | |
| auto childPath = getChildPath(name); | |
| mode_t mode; | |
| if (_wasmfs_node_get_mode(childPath.c_str(), &mode)) { | |
| return nullptr; | |
| } | |
| // Allow reading from character device files too (e.g. `/dev/random`, | |
| // `/dev/urandom`) | |
| 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 { | |
| // Unrecognized file kind not made visible to WasmFS. | |
| return nullptr; | |
| } | |
| } | |
| int removeChild(const std::string& name) override { | |
| auto childPath = getChildPath(name); | |
| // Try both `unlink` and `rmdir`. | |
| 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 { | |
| // TODO: optimize this? | |
| 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 { | |
| // The underlying Node FS path of this backend's mount points. | |
| 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"); | |
| } | |
| }; | |
| // TODO: symlink | |
| 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}); | |
| } | |
| } // extern "C" | |
| } // namespace wasmfs | |
Xet Storage Details
- Size:
- 9.18 kB
- Xet hash:
- 1a2018117666fe5806a4495b4d172b61dfc7d5af78ed8506b30033e64442c2f0
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.