blob: 274f714814db92f3a0e2f48b10ff80384a85a899 [file] [edit]
// 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 <assert.h>
#include <fcntl.h>
#include <mutex>
#include <utility>
#include <vector>
#include <wasi/api.h>
namespace wasmfs {
static_assert(std::is_same<size_t, __wasi_size_t>::value,
"size_t should be the same as __wasi_size_t");
static_assert(std::is_same<off_t, __wasi_filedelta_t>::value,
"off_t should be the same as __wasi_filedelta_t");
// Overflow and underflow behaviour are only defined for unsigned types.
template<typename T> bool addWillOverFlow(T a, T b) {
if (a > 0 && b > std::numeric_limits<T>::max() - a) {
return true;
}
return false;
}
class FileTable;
class OpenFileState : public std::enable_shared_from_this<OpenFileState> {
std::shared_ptr<File> 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<Directory::Entry> dirents;
OpenFileState(private_key,
oflags_t flags,
std::shared_ptr<File> file,
std::vector<Directory::Entry>&& dirents)
: file(file), flags(flags), dirents(std::move(dirents)) {}
[[nodiscard]] static int create(std::shared_ptr<File> file,
oflags_t flags,
std::shared_ptr<OpenFileState>& out);
class Handle {
std::shared_ptr<OpenFileState> openFileState;
std::unique_lock<std::recursive_mutex> lock;
public:
Handle(std::shared_ptr<OpenFileState> openFileState)
: openFileState(openFileState), lock(openFileState->mutex) {}
std::shared_ptr<File>& 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<std::shared_ptr<OpenFileState>> 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<std::recursive_mutex> lock;
public:
Handle(FileTable& fileTable)
: fileTable(fileTable), lock(fileTable.mutex) {}
std::shared_ptr<OpenFileState> 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<DataFile>
setEntry(__wasi_fd_t fd, std::shared_ptr<OpenFileState> openFile);
__wasi_fd_t addEntry(std::shared_ptr<OpenFileState> openFileState);
};
Handle locked() { return Handle(*this); }
};
} // namespace wasmfs