#include "file.h"
#include "wasmfs.h"
#include "wasmfs_internal.h"
#include <emscripten/threading.h>
namespace wasmfs {
void DataFile::Handle::preloadFromJS(int index) {
std::vector<uint8_t> buffer(_wasmfs_get_preloaded_file_size(index));
assert(emscripten_is_main_runtime_thread());
_wasmfs_copy_preloaded_file_data(index, buffer.data());
write((const uint8_t*)buffer.data(), buffer.size(), 0);
}
void Directory::Handle::cacheChild(const std::string& name,
std::shared_ptr<File> child,
DCacheKind kind) {
if (kind == DCacheKind::Mount || !getDir()->maintainsFileIdentity()) {
auto& dcache = getDir()->dcache;
[[maybe_unused]] auto [_, inserted] = dcache.insert({name, {kind, child}});
assert(inserted && "inserted child already existed!");
}
assert(child->locked().getParent() == nullptr ||
child->locked().getParent() == getDir());
child->locked().setParent(getDir());
}
std::shared_ptr<File> Directory::Handle::getChild(const std::string& name) {
if (!getParent()) {
return nullptr;
}
if (name == ".") {
return file;
}
if (name == "..") {
return getParent();
}
auto& dcache = getDir()->dcache;
if (auto it = dcache.find(name); it != dcache.end()) {
return it->second.file;
}
auto child = getDir()->getChild(name);
if (!child) {
return nullptr;
}
cacheChild(name, child, DCacheKind::Normal);
return child;
}
bool Directory::Handle::mountChild(const std::string& name,
std::shared_ptr<File> child) {
assert(child);
if (!getParent()) {
return false;
}
cacheChild(name, child, DCacheKind::Mount);
return true;
}
std::shared_ptr<DataFile>
Directory::Handle::insertDataFile(const std::string& name, mode_t mode) {
if (!getParent()) {
return nullptr;
}
auto child = getDir()->insertDataFile(name, mode);
if (!child) {
return nullptr;
}
cacheChild(name, child, DCacheKind::Normal);
updateMTime();
return child;
}
std::shared_ptr<Directory>
Directory::Handle::insertDirectory(const std::string& name, mode_t mode) {
if (!getParent()) {
return nullptr;
}
auto child = getDir()->insertDirectory(name, mode);
if (!child) {
return nullptr;
}
cacheChild(name, child, DCacheKind::Normal);
updateMTime();
return child;
}
std::shared_ptr<Symlink>
Directory::Handle::insertSymlink(const std::string& name,
const std::string& target) {
if (!getParent()) {
return nullptr;
}
auto child = getDir()->insertSymlink(name, target);
if (!child) {
return nullptr;
}
cacheChild(name, child, DCacheKind::Normal);
updateMTime();
return child;
}
int Directory::Handle::insertMove(const std::string& name,
std::shared_ptr<File> file) {
if (!getParent()) {
return -EPERM;
}
auto oldParent = file->locked().getParent();
auto& oldCache = oldParent->dcache;
auto oldIt = std::find_if(oldCache.begin(), oldCache.end(), [&](auto& kv) {
return kv.second.file == file;
});
if (auto err = getDir()->insertMove(name, file)) {
return err;
}
if (oldIt != oldCache.end()) {
auto [oldName, entry] = *oldIt;
assert(oldName.size());
oldCache.erase(oldIt);
auto& newCache = getDir()->dcache;
auto [it, inserted] = newCache.insert({name, entry});
if (!inserted) {
it->second.file->locked().setParent(nullptr);
it->second = entry;
}
} else {
assert(getDir()->maintainsFileIdentity());
}
file->locked().setParent(getDir());
oldParent->locked().updateMTime();
updateMTime();
return 0;
}
int Directory::Handle::removeChild(const std::string& name) {
auto& dcache = getDir()->dcache;
auto entry = dcache.find(name);
if (entry != dcache.end() && entry->second.kind == DCacheKind::Mount) {
dcache.erase(entry);
return 0;
}
if (auto err = getDir()->removeChild(name)) {
assert(err < 0);
return err;
}
if (entry != dcache.end()) {
entry->second.file->locked().setParent(nullptr);
dcache.erase(entry);
}
updateMTime();
return 0;
}
std::string Directory::Handle::getName(std::shared_ptr<File> file) {
if (getDir()->maintainsFileIdentity()) {
return getDir()->getName(file);
}
auto& dcache = getDir()->dcache;
for (auto it = dcache.begin(); it != dcache.end(); ++it) {
if (it->second.file == file) {
return it->first;
}
}
return "";
}
ssize_t Directory::Handle::getNumEntries() {
size_t mounts = 0;
auto& dcache = getDir()->dcache;
for (auto it = dcache.begin(); it != dcache.end(); ++it) {
if (it->second.kind == DCacheKind::Mount) {
++mounts;
}
}
auto numReal = getDir()->getNumEntries();
if (numReal < 0) {
return numReal;
}
return numReal + mounts;
}
Directory::MaybeEntries Directory::Handle::getEntries() {
auto entries = getDir()->getEntries();
if (entries.getError()) {
return entries;
}
auto& dcache = getDir()->dcache;
for (auto it = dcache.begin(); it != dcache.end(); ++it) {
auto& [name, entry] = *it;
if (entry.kind == DCacheKind::Mount) {
entries->push_back({name, entry.file->kind, entry.file->getIno()});
}
}
return entries;
}
}