blob: 45a5544d25281147bca523c17458725e9071f5cd [file]
// 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 memory file backend.
#include "memory_backend.h"
#include "backend.h"
#include "special_files.h"
#include "wasmfs.h"
namespace wasmfs {
ssize_t MemoryDataFile::write(const uint8_t* buf, size_t len, off_t offset) {
if (offset + len > buffer.size()) {
if (offset + len > buffer.max_size()) {
// Overflow: the necessary size fits in an off_t, but cannot fit in the
// container.
return -EIO;
}
buffer.resize(offset + len);
}
std::memcpy(&buffer[offset], buf, len);
return len;
}
ssize_t MemoryDataFile::read(uint8_t* buf, size_t len, off_t offset) {
if (offset >= buffer.size()) {
len = 0;
} else if (offset + len >= buffer.size()) {
len = buffer.size() - offset;
}
std::memcpy(buf, &buffer[offset], len);
return len;
}
std::vector<MemoryDirectory::ChildEntry>::iterator
MemoryDirectory::findEntry(const std::string& name) {
return std::find_if(entries.begin(), entries.end(), [&](const auto& entry) {
return entry.name == name;
});
}
std::shared_ptr<File> MemoryDirectory::getChild(const std::string& name) {
if (auto entry = findEntry(name); entry != entries.end()) {
return entry->child;
}
return nullptr;
}
int MemoryDirectory::removeChild(const std::string& name) {
auto entry = findEntry(name);
if (entry != entries.end()) {
entry->child->locked().setParent(nullptr);
entries.erase(entry);
}
return 0;
}
Directory::MaybeEntries MemoryDirectory::getEntries() {
std::vector<Directory::Entry> result;
result.reserve(entries.size());
for (auto& [name, child] : entries) {
result.push_back({name, child->kind, child->getIno()});
}
return {result};
}
int MemoryDirectory::insertMove(const std::string& name,
std::shared_ptr<File> file) {
auto& oldEntries =
std::static_pointer_cast<MemoryDirectory>(file->locked().getParent())
->entries;
for (auto it = oldEntries.begin(); it != oldEntries.end(); ++it) {
if (it->child == file) {
oldEntries.erase(it);
break;
}
}
(void)removeChild(name);
insertChild(name, file);
return 0;
}
std::string MemoryDirectory::getName(std::shared_ptr<File> file) {
auto it =
std::find_if(entries.begin(), entries.end(), [&](const auto& entry) {
return entry.child == file;
});
if (it != entries.end()) {
return it->name;
}
return "";
}
class MemoryBackend : public Backend {
public:
std::shared_ptr<DataFile> createFile(mode_t mode) override {
return std::make_shared<MemoryDataFile>(mode, this);
}
std::shared_ptr<Directory> createDirectory(mode_t mode) override {
return std::make_shared<MemoryDirectory>(mode, this);
}
std::shared_ptr<Symlink> createSymlink(std::string target) override {
return std::make_shared<MemorySymlink>(target, this);
}
// Initializes default directories including /dev/stdin, /dev/stdout,
// /dev/stderr. Refers to the same std streams in the open file table.
void populateRoot(Directory::Handle& lockedRoot) override {
// If the /dev/ directory does not already exist, create it. (It may already
// exist if those files have been preloaded.)
auto devDir = lockedRoot.insertDirectory("dev", S_IRUGO | S_IXUGO);
if (devDir) {
auto lockedDev = devDir->locked();
lockedDev.mountChild("null", SpecialFiles::getNull());
lockedDev.mountChild("stdin", SpecialFiles::getStdin());
lockedDev.mountChild("stdout", SpecialFiles::getStdout());
lockedDev.mountChild("stderr", SpecialFiles::getStderr());
lockedDev.mountChild("random", SpecialFiles::getRandom());
lockedDev.mountChild("urandom", SpecialFiles::getURandom());
}
// As with the /dev/ directory, it is not an error for /tmp/ to already
// exist.
lockedRoot.insertDirectory("tmp", S_IRWXUGO);
}
};
backend_t createMemoryBackend() {
return wasmFS.addBackend(std::make_unique<MemoryBackend>());
}
extern "C" {
backend_t wasmfs_create_memory_backend() { return createMemoryBackend(); }
} // extern "C"
} // namespace wasmfs