Path: blob/main/system/lib/wasmfs/backends/node_backend.cpp
6175 views
// Copyright 2022 The Emscripten Authors. All rights reserved.1// Emscripten is available under two separate licenses, the MIT license and the2// University of Illinois/NCSA Open Source License. Both these licenses can be3// found in the LICENSE file.45#include <memory>67#include "backend.h"8#include "file.h"9#include "node_backend.h"10#include "support.h"11#include "wasmfs.h"1213namespace wasmfs {1415class NodeBackend;1617// The state of a file on the underlying Node file system.18class NodeState {19// Map all separate WasmFS opens of a file to a single underlying fd.20size_t openCount = 0;21oflags_t openFlags = 0;22int fd = -1;2324public:25std::string path;2627NodeState(std::string path) : path(path) {}2829int getFD() {30assert(openCount > 0);31return fd;32}3334bool isOpen() { return openCount > 0; }3536// Attempt to open the file with the given flags, returning 0 on success or an37// error code.38int open(oflags_t flags) {39int result = 0;40if (openCount == 0) {41// No existing fd, so open a fresh one.42switch (flags) {43case O_RDONLY:44result = _wasmfs_node_open(path.c_str(), "r");45break;46case O_WRONLY:47// TODO(sbc): Specific handling of O_WRONLY.48// There is no simple way to map O_WRONLY to an fopen-style49// mode string since the only two modes that are write only50// are `w` and `a`. The problem with the former is that it51// truncates to file. The problem with the latter is that it52// opens for appending. For now simply opening in O_RDWR53// mode is enough to pass all our tests.54case O_RDWR:55result = _wasmfs_node_open(path.c_str(), "r+");56break;57default:58WASMFS_UNREACHABLE("Unexpected open access mode");59}60if (result < 0) {61return result;62}63// Fall through to update our state with the new result.64} else if ((openFlags == O_RDONLY &&65(flags == O_WRONLY || flags == O_RDWR)) ||66(openFlags == O_WRONLY &&67(flags == O_RDONLY || flags == O_RDWR))) {68// We already have a file descriptor, but we need to replace it with a new69// fd with more access.70result = _wasmfs_node_open(path.c_str(), "r+");71if (result < 0) {72return result;73}74// Success! Close the old fd before updating it.75(void)_wasmfs_node_close(fd);76// Fall through to update our state with the new result.77} else {78// Reuse the existing file descriptor.79++openCount;80return 0;81}82// Update our state for the new fd.83fd = result;84openFlags = flags;85++openCount;86return 0;87}8889int close() {90int ret = 0;91if (--openCount == 0) {92ret = _wasmfs_node_close(fd);93*this = NodeState(path);94}95return ret;96}97};9899class NodeFile : public DataFile {100public:101NodeState state;102103NodeFile(mode_t mode, backend_t backend, std::string path)104: DataFile(mode, backend), state(path) {}105106private:107off_t getSize() override {108// TODO: This should really be using a 64-bit file size type.109uint32_t size;110if (state.isOpen()) {111if (_wasmfs_node_fstat_size(state.getFD(), &size)) {112// TODO: Make this fallible.113return 0;114}115} else {116if (_wasmfs_node_stat_size(state.path.c_str(), &size)) {117// TODO: Make this fallible.118return 0;119}120}121return off_t(size);122}123124int setSize(off_t size) override {125if (state.isOpen()) {126return _wasmfs_node_ftruncate(state.getFD(), size);127}128return _wasmfs_node_truncate(state.path.c_str(), size);129}130131int open(oflags_t flags) override { return state.open(flags); }132133int close() override { return state.close(); }134135ssize_t read(uint8_t* buf, size_t len, off_t offset) override {136uint32_t nread;137if (auto err = _wasmfs_node_read(state.getFD(), buf, len, offset, &nread)) {138return -err;139}140return nread;141}142143ssize_t write(const uint8_t* buf, size_t len, off_t offset) override {144uint32_t nwritten;145if (auto err =146_wasmfs_node_write(state.getFD(), buf, len, offset, &nwritten)) {147return -err;148}149return nwritten;150}151152int flush() override {153WASMFS_UNREACHABLE("TODO: implement NodeFile::flush");154}155};156157class NodeSymlink : public Symlink {158public:159std::string path;160161NodeSymlink(backend_t backend, std::string path)162: Symlink(backend), path(path) {}163164virtual std::string getTarget() const {165char buf[PATH_MAX];166if (_wasmfs_node_readlink(path.c_str(), buf, PATH_MAX) < 0) {167WASMFS_UNREACHABLE("getTarget cannot fail");168}169return std::string(buf);170}171};172173class NodeDirectory : public Directory {174public:175NodeState state;176177NodeDirectory(mode_t mode, backend_t backend, std::string path)178: Directory(mode, backend), state(path) {}179180private:181std::string getChildPath(const std::string& name) {182return state.path + '/' + name;183}184185std::shared_ptr<File> getChild(const std::string& name) override {186static_assert(std::is_same_v<mode_t, unsigned int>);187// TODO: also retrieve and set ctime, atime, ino, etc.188auto childPath = getChildPath(name);189mode_t mode;190if (_wasmfs_node_get_mode(childPath.c_str(), &mode)) {191return nullptr;192}193if (S_ISREG(mode)) {194return std::make_shared<NodeFile>(mode, getBackend(), childPath);195} else if (S_ISDIR(mode)) {196return std::make_shared<NodeDirectory>(mode, getBackend(), childPath);197} else if (S_ISLNK(mode)) {198return std::make_shared<NodeSymlink>(getBackend(), childPath);199} else {200// Unrecognized file kind not made visible to WasmFS.201return nullptr;202}203}204205int removeChild(const std::string& name) override {206auto childPath = getChildPath(name);207// Try both `unlink` and `rmdir`.208if (auto err = _wasmfs_node_unlink(childPath.c_str())) {209if (err == EISDIR) {210err = _wasmfs_node_rmdir(childPath.c_str());211}212return -err;213}214return 0;215}216217std::shared_ptr<DataFile> insertDataFile(const std::string& name,218mode_t mode) override {219auto childPath = getChildPath(name);220if (_wasmfs_node_insert_file(childPath.c_str(), mode)) {221return nullptr;222}223return std::make_shared<NodeFile>(mode, getBackend(), childPath);224}225226std::shared_ptr<Directory> insertDirectory(const std::string& name,227mode_t mode) override {228auto childPath = getChildPath(name);229if (_wasmfs_node_insert_directory(childPath.c_str(), mode)) {230return nullptr;231}232return std::make_shared<NodeDirectory>(mode, getBackend(), childPath);233}234235std::shared_ptr<Symlink> insertSymlink(const std::string& name,236const std::string& target) override {237auto childPath = getChildPath(name);238if (_wasmfs_node_symlink(target.c_str(), childPath.c_str())) {239return nullptr;240}241return std::make_shared<NodeSymlink>(getBackend(), childPath);242}243244int insertMove(const std::string& name, std::shared_ptr<File> file) override {245std::string fromPath;246247if (file->is<DataFile>()) {248auto nodeFile = std::static_pointer_cast<NodeFile>(file);249fromPath = nodeFile->state.path;250} else {251auto nodeDir = std::static_pointer_cast<NodeDirectory>(file);252fromPath = nodeDir->state.path;253}254255auto childPath = getChildPath(name);256return _wasmfs_node_rename(fromPath.c_str(), childPath.c_str());257}258259ssize_t getNumEntries() override {260// TODO: optimize this?261auto entries = getEntries();262if (int err = entries.getError()) {263return err;264}265return entries->size();266}267268Directory::MaybeEntries getEntries() override {269std::vector<Directory::Entry> entries;270int err = _wasmfs_node_readdir(state.path.c_str(), &entries);271if (err) {272return {-err};273}274return {entries};275}276};277278class NodeBackend : public Backend {279// The underlying Node FS path of this backend's mount points.280std::string mountPath;281282public:283NodeBackend(const std::string& mountPath) : mountPath(mountPath) {}284285std::shared_ptr<DataFile> createFile(mode_t mode) override {286return std::make_shared<NodeFile>(mode, this, mountPath);287}288289std::shared_ptr<Directory> createDirectory(mode_t mode) override {290return std::make_shared<NodeDirectory>(mode, this, mountPath);291}292293std::shared_ptr<Symlink> createSymlink(std::string target) override {294WASMFS_UNREACHABLE("TODO: implement NodeBackend::createSymlink");295}296};297298// TODO: symlink299300extern "C" {301302backend_t wasmfs_create_node_backend(const char* root) {303return wasmFS.addBackend(std::make_unique<NodeBackend>(root));304}305306void EMSCRIPTEN_KEEPALIVE _wasmfs_node_record_dirent(307std::vector<Directory::Entry>* entries, const char* name, int type) {308entries->push_back({name, File::FileKind(type), 0});309}310311} // extern "C"312313} // namespace wasmfs314315316