blob: a3ba14dae618eeb5f85bc23b36a344f5d0e0564a [file] [edit]
// 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.
#include <string_view>
#include "paths.h"
#include "file.h"
#include "wasmfs.h"
namespace wasmfs::path {
static ParsedFile getChild(std::shared_ptr<Directory> dir,
std::string_view name) {
auto child = dir->locked().getChild(std::string(name));
if (!child) {
return -ENOENT;
}
// TODO: Follow symlinks here.
return child;
}
ParsedParent parseParent(std::string_view path, __wasi_fd_t basefd) {
// Empty paths never exist.
if (path.empty()) {
return {-ENOENT};
}
// Initialize the starting directory.
std::shared_ptr<Directory> curr;
if (path.front() == '/') {
curr = wasmFS.getRootDirectory();
path.remove_prefix(1);
} else if (basefd == AT_FDCWD) {
curr = wasmFS.getCWD();
} else {
auto openFile = wasmFS.getFileTable().locked().getEntry(basefd);
if (!openFile) {
return -EBADF;
}
curr = openFile->locked().getFile()->dynCast<Directory>();
if (!curr) {
return {-ENOTDIR};
}
}
// Ignore trailing '/'.
while (!path.empty() && path.back() == '/') {
path.remove_suffix(1);
}
// An empty path here means that the path was equivalent to "/" and does not
// contain a child segment for us to return. The root is its own parent, so we
// can handle this by returning (root, ".").
if (path.empty()) {
return {std::make_pair(std::move(curr), std::string_view("."))};
}
while (true) {
// Skip any leading '/' for each segment.
while (!path.empty() && path.front() == '/') {
path.remove_prefix(1);
}
// If this is the leaf segment, return.
size_t segment_end = path.find_first_of('/');
if (segment_end == std::string_view::npos) {
return {std::make_pair(std::move(curr), path)};
}
// Try to descend into the child segment.
// TODO: Check permissions on intermediate directories.
auto segment = path.substr(0, segment_end);
auto child = getChild(curr, segment);
if (auto err = child.getError()) {
return err;
}
curr = child.getFile()->dynCast<Directory>();
if (!curr) {
return -ENOTDIR;
}
path.remove_prefix(segment_end);
}
}
ParsedFile parseFile(std::string_view path, __wasi_fd_t basefd) {
auto parsed = parseParent(path, basefd);
if (auto err = parsed.getError()) {
return {err};
}
auto& [parent, child] = parsed.getParentChild();
return getChild(parent, child);
}
ParsedFile getFileAt(__wasi_fd_t fd, std::string_view path, int flags) {
if ((flags & AT_EMPTY_PATH) && path.size() == 0) {
// Don't parse a path, just use `dirfd` directly.
if (fd == AT_FDCWD) {
return {wasmFS.getCWD()};
}
auto openFile = wasmFS.getFileTable().locked().getEntry(fd);
if (!openFile) {
return {-EBADF};
}
return {openFile->locked().getFile()};
}
return path::parseFile(path, fd);
}
} // namespace wasmfs::path