Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/system/lib/wasmfs/file.cpp
6174 views
1
// Copyright 2021 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
// This file defines the file object.
7
8
#include "file.h"
9
#include "wasmfs.h"
10
#include "wasmfs_internal.h"
11
#include <emscripten/threading.h>
12
13
namespace wasmfs {
14
15
//
16
// DataFile
17
//
18
19
void DataFile::Handle::preloadFromJS(int index) {
20
// TODO: Each Datafile type could have its own impl of file preloading.
21
// Create a buffer with the required file size.
22
std::vector<uint8_t> buffer(_wasmfs_get_preloaded_file_size(index));
23
24
// Ensure that files are preloaded from the main thread.
25
assert(emscripten_is_main_runtime_thread());
26
27
// Load data into the in-memory buffer.
28
_wasmfs_copy_preloaded_file_data(index, buffer.data());
29
30
write((const uint8_t*)buffer.data(), buffer.size(), 0);
31
}
32
33
//
34
// Directory
35
//
36
37
void Directory::Handle::cacheChild(const std::string& name,
38
std::shared_ptr<File> child,
39
DCacheKind kind) {
40
// Update the dcache if the backend hasn't opted out of using the dcache or if
41
// this is a mount point, in which case it is not under the control of the
42
// backend.
43
if (kind == DCacheKind::Mount || !getDir()->maintainsFileIdentity()) {
44
auto& dcache = getDir()->dcache;
45
[[maybe_unused]] auto [_, inserted] = dcache.insert({name, {kind, child}});
46
assert(inserted && "inserted child already existed!");
47
}
48
// Set the child's parent.
49
assert(child->locked().getParent() == nullptr ||
50
child->locked().getParent() == getDir());
51
child->locked().setParent(getDir());
52
}
53
54
std::shared_ptr<File> Directory::Handle::getChild(const std::string& name) {
55
// Unlinked directories must be empty, without even "." or ".."
56
if (!getParent()) {
57
return nullptr;
58
}
59
if (name == ".") {
60
return file;
61
}
62
if (name == "..") {
63
return getParent();
64
}
65
// Check whether the cache already contains this child.
66
auto& dcache = getDir()->dcache;
67
if (auto it = dcache.find(name); it != dcache.end()) {
68
return it->second.file;
69
}
70
// Otherwise check whether the backend contains an underlying file we don't
71
// know about.
72
auto child = getDir()->getChild(name);
73
if (!child) {
74
return nullptr;
75
}
76
cacheChild(name, child, DCacheKind::Normal);
77
return child;
78
}
79
80
bool Directory::Handle::mountChild(const std::string& name,
81
std::shared_ptr<File> child) {
82
assert(child);
83
// Cannot insert into an unlinked directory.
84
if (!getParent()) {
85
return false;
86
}
87
cacheChild(name, child, DCacheKind::Mount);
88
return true;
89
}
90
91
std::shared_ptr<DataFile>
92
Directory::Handle::insertDataFile(const std::string& name, mode_t mode) {
93
// Cannot insert into an unlinked directory.
94
if (!getParent()) {
95
return nullptr;
96
}
97
auto child = getDir()->insertDataFile(name, mode);
98
if (!child) {
99
return nullptr;
100
}
101
cacheChild(name, child, DCacheKind::Normal);
102
updateMTime();
103
return child;
104
}
105
106
std::shared_ptr<Directory>
107
Directory::Handle::insertDirectory(const std::string& name, mode_t mode) {
108
// Cannot insert into an unlinked directory.
109
if (!getParent()) {
110
return nullptr;
111
}
112
auto child = getDir()->insertDirectory(name, mode);
113
if (!child) {
114
return nullptr;
115
}
116
cacheChild(name, child, DCacheKind::Normal);
117
updateMTime();
118
return child;
119
}
120
121
std::shared_ptr<Symlink>
122
Directory::Handle::insertSymlink(const std::string& name,
123
const std::string& target) {
124
// Cannot insert into an unlinked directory.
125
if (!getParent()) {
126
return nullptr;
127
}
128
auto child = getDir()->insertSymlink(name, target);
129
if (!child) {
130
return nullptr;
131
}
132
cacheChild(name, child, DCacheKind::Normal);
133
updateMTime();
134
return child;
135
}
136
137
// TODO: consider moving this to be `Backend::move` to avoid asymmetry between
138
// the source and destination directories and/or taking `Directory::Handle`
139
// arguments to prove that the directories have already been locked.
140
int Directory::Handle::insertMove(const std::string& name,
141
std::shared_ptr<File> file) {
142
// Cannot insert into an unlinked directory.
143
if (!getParent()) {
144
return -EPERM;
145
}
146
147
// Look up the file in its old parent's cache.
148
auto oldParent = file->locked().getParent();
149
auto& oldCache = oldParent->dcache;
150
auto oldIt = std::find_if(oldCache.begin(), oldCache.end(), [&](auto& kv) {
151
return kv.second.file == file;
152
});
153
154
// TODO: Handle moving mount points correctly by only updating caches without
155
// involving the backend.
156
157
// Attempt the move.
158
if (auto err = getDir()->insertMove(name, file)) {
159
return err;
160
}
161
162
if (oldIt != oldCache.end()) {
163
// Do the move and update the caches.
164
auto [oldName, entry] = *oldIt;
165
assert(oldName.size());
166
// Update parent pointers and caches to reflect the successful move.
167
oldCache.erase(oldIt);
168
auto& newCache = getDir()->dcache;
169
auto [it, inserted] = newCache.insert({name, entry});
170
if (!inserted) {
171
// Update and overwrite the overwritten file.
172
it->second.file->locked().setParent(nullptr);
173
it->second = entry;
174
}
175
} else {
176
// This backend doesn't use the dcache.
177
assert(getDir()->maintainsFileIdentity());
178
}
179
180
file->locked().setParent(getDir());
181
182
// TODO: Moving mount points probably shouldn't update the mtime.
183
oldParent->locked().updateMTime();
184
updateMTime();
185
186
return 0;
187
}
188
189
int Directory::Handle::removeChild(const std::string& name) {
190
auto& dcache = getDir()->dcache;
191
auto entry = dcache.find(name);
192
// If this is a mount, we don't need to call into the backend.
193
if (entry != dcache.end() && entry->second.kind == DCacheKind::Mount) {
194
dcache.erase(entry);
195
return 0;
196
}
197
if (auto err = getDir()->removeChild(name)) {
198
assert(err < 0);
199
return err;
200
}
201
if (entry != dcache.end()) {
202
entry->second.file->locked().setParent(nullptr);
203
dcache.erase(entry);
204
}
205
updateMTime();
206
return 0;
207
}
208
209
std::string Directory::Handle::getName(std::shared_ptr<File> file) {
210
if (getDir()->maintainsFileIdentity()) {
211
return getDir()->getName(file);
212
}
213
auto& dcache = getDir()->dcache;
214
for (auto it = dcache.begin(); it != dcache.end(); ++it) {
215
if (it->second.file == file) {
216
return it->first;
217
}
218
}
219
return "";
220
}
221
222
ssize_t Directory::Handle::getNumEntries() {
223
size_t mounts = 0;
224
auto& dcache = getDir()->dcache;
225
for (auto it = dcache.begin(); it != dcache.end(); ++it) {
226
if (it->second.kind == DCacheKind::Mount) {
227
++mounts;
228
}
229
}
230
auto numReal = getDir()->getNumEntries();
231
if (numReal < 0) {
232
return numReal;
233
}
234
return numReal + mounts;
235
}
236
237
Directory::MaybeEntries Directory::Handle::getEntries() {
238
auto entries = getDir()->getEntries();
239
if (entries.getError()) {
240
return entries;
241
}
242
auto& dcache = getDir()->dcache;
243
for (auto it = dcache.begin(); it != dcache.end(); ++it) {
244
auto& [name, entry] = *it;
245
if (entry.kind == DCacheKind::Mount) {
246
entries->push_back({name, entry.file->kind, entry.file->getIno()});
247
}
248
}
249
return entries;
250
}
251
252
} // namespace wasmfs
253
254