Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/system/lib/wasmfs/paths.cpp
6174 views
1
// Copyright 2022 The Emscripten Authors. All rights reserved.
2
// Emscripten is available under two separate licenses, the MIT license and the
3
// University of Illinois/NCSA Open Source License. Both these licenses can be
4
// found in the LICENSE file.
5
6
#include <string_view>
7
8
#include "file.h"
9
#include "paths.h"
10
#include "wasmfs.h"
11
12
namespace wasmfs::path {
13
14
namespace {
15
16
static inline constexpr size_t MAX_RECURSIONS = 40;
17
18
ParsedFile doParseFile(std::string_view path,
19
std::shared_ptr<Directory> base,
20
LinkBehavior links,
21
size_t& recursions);
22
23
ParsedFile getBaseDir(__wasi_fd_t basefd) {
24
if (basefd == AT_FDCWD) {
25
return {wasmFS.getCWD()};
26
}
27
auto openFile = wasmFS.getFileTable().locked().getEntry(basefd);
28
if (!openFile) {
29
return -EBADF;
30
}
31
if (auto baseDir = openFile->locked().getFile()->dynCast<Directory>()) {
32
return {baseDir};
33
}
34
return -ENOTDIR;
35
}
36
37
ParsedFile getChild(std::shared_ptr<Directory> dir,
38
std::string_view name,
39
LinkBehavior links,
40
size_t& recursions) {
41
auto child = dir->locked().getChild(std::string(name));
42
if (!child) {
43
return -ENOENT;
44
}
45
if (links != NoFollowLinks) {
46
while (auto link = child->dynCast<Symlink>()) {
47
if (++recursions > MAX_RECURSIONS) {
48
return -ELOOP;
49
}
50
auto target = link->getTarget();
51
if (target.empty()) {
52
return -ENOENT;
53
}
54
auto parsed = doParseFile(target, dir, FollowLinks, recursions);
55
if (auto err = parsed.getError()) {
56
return err;
57
}
58
child = parsed.getFile();
59
}
60
}
61
return child;
62
}
63
64
ParsedParent doParseParent(std::string_view path,
65
std::shared_ptr<Directory> curr,
66
size_t& recursions) {
67
// Empty paths never exist.
68
if (path.empty()) {
69
return {-ENOENT};
70
}
71
72
// Handle absolute paths.
73
if (path.front() == '/') {
74
curr = wasmFS.getRootDirectory();
75
path.remove_prefix(1);
76
}
77
78
// Ignore trailing '/'.
79
while (!path.empty() && path.back() == '/') {
80
path.remove_suffix(1);
81
}
82
83
// An empty path here means that the path was equivalent to "/" and does not
84
// contain a child segment for us to return. The root is its own parent, so we
85
// can handle this by returning (root, ".").
86
if (path.empty()) {
87
return {std::make_pair(std::move(curr), std::string_view("."))};
88
}
89
90
while (true) {
91
// Skip any leading '/' for each segment.
92
while (!path.empty() && path.front() == '/') {
93
path.remove_prefix(1);
94
}
95
96
// If this is the leaf segment, return.
97
size_t segment_end = path.find_first_of('/');
98
if (segment_end == std::string_view::npos) {
99
return {std::make_pair(std::move(curr), path)};
100
}
101
102
// Try to descend into the child segment.
103
// TODO: Check permissions on intermediate directories.
104
auto segment = path.substr(0, segment_end);
105
auto child = getChild(curr, segment, FollowLinks, recursions);
106
if (auto err = child.getError()) {
107
return err;
108
}
109
curr = child.getFile()->dynCast<Directory>();
110
if (!curr) {
111
return -ENOTDIR;
112
}
113
path.remove_prefix(segment_end);
114
}
115
}
116
117
ParsedFile doParseFile(std::string_view path,
118
std::shared_ptr<Directory> base,
119
LinkBehavior links,
120
size_t& recursions) {
121
auto parsed = doParseParent(path, base, recursions);
122
if (auto err = parsed.getError()) {
123
return {err};
124
}
125
auto& [parent, child] = parsed.getParentChild();
126
return getChild(parent, child, links, recursions);
127
}
128
129
} // anonymous namespace
130
131
ParsedParent parseParent(std::string_view path, __wasi_fd_t basefd) {
132
auto base = getBaseDir(basefd);
133
if (auto err = base.getError()) {
134
return err;
135
}
136
size_t recursions = 0;
137
auto baseDir = base.getFile()->cast<Directory>();
138
return doParseParent(path, baseDir, recursions);
139
}
140
141
ParsedFile
142
parseFile(std::string_view path, __wasi_fd_t basefd, LinkBehavior links) {
143
auto base = getBaseDir(basefd);
144
if (auto err = base.getError()) {
145
return err;
146
}
147
size_t recursions = 0;
148
auto baseDir = base.getFile()->cast<Directory>();
149
return doParseFile(path, baseDir, links, recursions);
150
}
151
152
ParsedFile getFileAt(__wasi_fd_t fd, std::string_view path, int flags) {
153
if ((flags & AT_EMPTY_PATH) && path.size() == 0) {
154
// Don't parse a path, just use `dirfd` directly.
155
if (fd == AT_FDCWD) {
156
return {wasmFS.getCWD()};
157
}
158
auto openFile = wasmFS.getFileTable().locked().getEntry(fd);
159
if (!openFile) {
160
return {-EBADF};
161
}
162
return {openFile->locked().getFile()};
163
}
164
auto links = (flags & AT_SYMLINK_NOFOLLOW) ? NoFollowLinks : FollowLinks;
165
return path::parseFile(path, fd, links);
166
}
167
168
ParsedFile getFileFrom(std::shared_ptr<Directory> base, std::string_view path) {
169
size_t recursions = 0;
170
return doParseFile(path, base, FollowLinks, recursions);
171
}
172
173
} // namespace wasmfs::path
174
175