// 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. // Utilities for creating virtual backends that forward operations to underlying // "real" files. Virtual backends must wrap all underlying files, even if the // wrapper is a no-op, so that no part of the system observes mixed backends. To // the backend-independent code, the root directory of the virtual backend and // all of its descendants must appear to have the same virtual backend. To the // underlying "real" backend, all of its files must appear to have parents of // the same real backend. Without these invariants, renames would not work // correctly and without wrapping all files, these invariants would not be // upheld. #pragma once #include "file.h" namespace wasmfs { static inline std::shared_ptr devirtualize(std::shared_ptr); class VirtualDataFile : public DataFile { protected: std::shared_ptr real; public: VirtualDataFile(std::shared_ptr real, backend_t backend) : DataFile(real->locked().getMode(), backend), real(real) {} protected: virtual off_t getSize() override { return real->locked().getSize(); } virtual int open(oflags_t flags) override { return real->locked().open(flags); } virtual int close() override { return real->locked().close(); } virtual ssize_t read(uint8_t* buf, size_t len, off_t offset) override { return real->locked().read(buf, len, offset); } virtual ssize_t write(const uint8_t* buf, size_t len, off_t offset) override { return real->locked().write(buf, len, offset); } virtual int setSize(off_t size) override { return real->locked().setSize(size); } virtual int flush() override { return real->locked().flush(); } friend std::shared_ptr devirtualize(std::shared_ptr); }; // Forward operations through Directory::Handle to get the proper dcache logic. class VirtualDirectory : public Directory { protected: std::shared_ptr real; public: VirtualDirectory(std::shared_ptr real, backend_t backend) : Directory(real->locked().getMode(), backend), real(real) {} protected: virtual std::shared_ptr getChild(const std::string& name) override { return real->locked().getChild(name); } virtual std::shared_ptr insertDataFile(const std::string& name, mode_t mode) override { return real->locked().insertDataFile(name, mode); } virtual std::shared_ptr insertDirectory(const std::string& name, mode_t mode) override { return real->locked().insertDirectory(name, mode); } virtual std::shared_ptr insertSymlink(const std::string& name, const std::string& target) override { return real->locked().insertSymlink(name, target); } virtual int insertMove(const std::string& name, std::shared_ptr file) override { return real->locked().insertMove(name, file); } virtual int removeChild(const std::string& name) override { return real->locked().removeChild(name); } virtual ssize_t getNumEntries() override { return real->locked().getNumEntries(); } virtual MaybeEntries getEntries() override { return real->locked().getEntries(); } virtual std::string getName(std::shared_ptr file) override { return real->locked().getName(file); } friend std::shared_ptr devirtualize(std::shared_ptr); }; class VirtualSymlink : public Symlink { protected: std::shared_ptr real; public: VirtualSymlink(std::shared_ptr real, backend_t backend) : Symlink(backend), real(real) {} protected: virtual std::string getTarget() const override { return real->getTarget(); } friend std::shared_ptr devirtualize(std::shared_ptr); }; // Unwrap a virtual file and get the underlying real file of the same kind. inline std::shared_ptr devirtualize(std::shared_ptr file) { if (file->is()) { return std::static_pointer_cast(file)->real; } else if (file->is()) { return std::static_pointer_cast(file)->real; } else if (file->is()) { return std::static_pointer_cast(file)->real; } WASMFS_UNREACHABLE("unexpected file kind"); } } // namespace wasmfs