#include <string_view>
#include "file.h"
#include "paths.h"
#include "wasmfs.h"
namespace wasmfs::path {
namespace {
static inline constexpr size_t MAX_RECURSIONS = 40;
ParsedFile doParseFile(std::string_view path,
std::shared_ptr<Directory> base,
LinkBehavior links,
size_t& recursions);
ParsedFile getBaseDir(__wasi_fd_t basefd) {
if (basefd == AT_FDCWD) {
return {wasmFS.getCWD()};
}
auto openFile = wasmFS.getFileTable().locked().getEntry(basefd);
if (!openFile) {
return -EBADF;
}
if (auto baseDir = openFile->locked().getFile()->dynCast<Directory>()) {
return {baseDir};
}
return -ENOTDIR;
}
ParsedFile getChild(std::shared_ptr<Directory> dir,
std::string_view name,
LinkBehavior links,
size_t& recursions) {
auto child = dir->locked().getChild(std::string(name));
if (!child) {
return -ENOENT;
}
if (links != NoFollowLinks) {
while (auto link = child->dynCast<Symlink>()) {
if (++recursions > MAX_RECURSIONS) {
return -ELOOP;
}
auto target = link->getTarget();
if (target.empty()) {
return -ENOENT;
}
auto parsed = doParseFile(target, dir, FollowLinks, recursions);
if (auto err = parsed.getError()) {
return err;
}
child = parsed.getFile();
}
}
return child;
}
ParsedParent doParseParent(std::string_view path,
std::shared_ptr<Directory> curr,
size_t& recursions) {
if (path.empty()) {
return {-ENOENT};
}
if (path.front() == '/') {
curr = wasmFS.getRootDirectory();
path.remove_prefix(1);
}
while (!path.empty() && path.back() == '/') {
path.remove_suffix(1);
}
if (path.empty()) {
return {std::make_pair(std::move(curr), std::string_view("."))};
}
while (true) {
while (!path.empty() && path.front() == '/') {
path.remove_prefix(1);
}
size_t segment_end = path.find_first_of('/');
if (segment_end == std::string_view::npos) {
return {std::make_pair(std::move(curr), path)};
}
auto segment = path.substr(0, segment_end);
auto child = getChild(curr, segment, FollowLinks, recursions);
if (auto err = child.getError()) {
return err;
}
curr = child.getFile()->dynCast<Directory>();
if (!curr) {
return -ENOTDIR;
}
path.remove_prefix(segment_end);
}
}
ParsedFile doParseFile(std::string_view path,
std::shared_ptr<Directory> base,
LinkBehavior links,
size_t& recursions) {
auto parsed = doParseParent(path, base, recursions);
if (auto err = parsed.getError()) {
return {err};
}
auto& [parent, child] = parsed.getParentChild();
return getChild(parent, child, links, recursions);
}
}
ParsedParent parseParent(std::string_view path, __wasi_fd_t basefd) {
auto base = getBaseDir(basefd);
if (auto err = base.getError()) {
return err;
}
size_t recursions = 0;
auto baseDir = base.getFile()->cast<Directory>();
return doParseParent(path, baseDir, recursions);
}
ParsedFile
parseFile(std::string_view path, __wasi_fd_t basefd, LinkBehavior links) {
auto base = getBaseDir(basefd);
if (auto err = base.getError()) {
return err;
}
size_t recursions = 0;
auto baseDir = base.getFile()->cast<Directory>();
return doParseFile(path, baseDir, links, recursions);
}
ParsedFile getFileAt(__wasi_fd_t fd, std::string_view path, int flags) {
if ((flags & AT_EMPTY_PATH) && path.size() == 0) {
if (fd == AT_FDCWD) {
return {wasmFS.getCWD()};
}
auto openFile = wasmFS.getFileTable().locked().getEntry(fd);
if (!openFile) {
return {-EBADF};
}
return {openFile->locked().getFile()};
}
auto links = (flags & AT_SYMLINK_NOFOLLOW) ? NoFollowLinks : FollowLinks;
return path::parseFile(path, fd, links);
}
ParsedFile getFileFrom(std::shared_ptr<Directory> base, std::string_view path) {
size_t recursions = 0;
return doParseFile(path, base, FollowLinks, recursions);
}
}