// Copyright 2021 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. // This file defines the open file table of the new file system. // Current Status: Work in Progress. // See https://github.com/emscripten-core/emscripten/issues/15041. #pragma once #include "file.h" #include #include #include #include #include #include namespace wasmfs { static_assert(std::is_same::value, "size_t should be the same as __wasi_size_t"); static_assert(std::is_same::value, "off_t should be the same as __wasi_filedelta_t"); // Overflow and underflow behaviour are only defined for unsigned types. template bool addWillOverFlow(T a, T b) { if (a > 0 && b > std::numeric_limits::max() - a) { return true; } return false; } class FileTable; class OpenFileState : public std::enable_shared_from_this { std::shared_ptr file; off_t position = 0; oflags_t flags; // RD_ONLY, WR_ONLY, RDWR // An OpenFileState needs a mutex if there are concurrent accesses on one open // file descriptor. This could occur if there are multiple seeks on the same // open file descriptor. std::recursive_mutex mutex; // The number of times this OpenFileState appears in the table. Use this // instead of shared_ptr::use_count to avoid accidentally counting temporary // objects. int uses = 0; // We can't make the constructor private because std::make_shared needs to be // able to call it, but we can make it unusable publicly. struct private_key { explicit private_key(int) {} }; // `uses` is protected by the FileTable lock and can be accessed directly by // `FileTable::Handle. friend FileTable; public: // Cache directory entries at the moment the directory is opened so that // subsequent getdents calls have a stable view of the contents. Including // files removed after the open and excluding files added after the open is // allowed, and trying to recalculate the directory contents on each getdents // call could lead to missed directory entries if there are concurrent // deletions that effectively move entries back past the current read position // in the open directory. const std::vector dirents; OpenFileState(private_key, oflags_t flags, std::shared_ptr file, std::vector&& dirents) : file(file), flags(flags), dirents(std::move(dirents)) {} [[nodiscard]] static int create(std::shared_ptr file, oflags_t flags, std::shared_ptr& out); class Handle { std::shared_ptr openFileState; std::unique_lock lock; public: Handle(std::shared_ptr openFileState) : openFileState(openFileState), lock(openFileState->mutex) {} std::shared_ptr& getFile() { return openFileState->file; }; off_t getPosition() const { return openFileState->position; }; void setPosition(off_t pos) { openFileState->position = pos; }; oflags_t getFlags() const { return openFileState->flags; }; void setFlags(oflags_t flags) { openFileState->flags = flags; }; }; Handle locked() { return Handle(shared_from_this()); } }; class FileTable { // Allow WasmFS to construct the FileTable singleton. friend class WasmFS; std::vector> entries; std::recursive_mutex mutex; FileTable(); public: // Access to the FileTable must go through a Handle, which holds its lock. class Handle { FileTable& fileTable; std::unique_lock lock; public: Handle(FileTable& fileTable) : fileTable(fileTable), lock(fileTable.mutex) {} std::shared_ptr getEntry(__wasi_fd_t fd); // Set the table slot at `fd` to the given file. If this overwrites the last // reference to an OpenFileState for a data file in the table, return the // file so it can be closed by the caller. Do not close the file directly in // this method so it can be closed later while the FileTable lock is not // held. [[nodiscard]] std::shared_ptr setEntry(__wasi_fd_t fd, std::shared_ptr openFile); __wasi_fd_t addEntry(std::shared_ptr openFileState); }; Handle locked() { return Handle(*this); } }; } // namespace wasmfs