Skip to content

Commit 9194ea9

Browse files
authored
[WasmFS] Return errors from getEntries and getNumEntries (#17807)
Make the return type of `getEntries` a variant that can be either a vector of entries as before or a negative error code. Update the return type of `getNumEntries` to be a ssize_t so it can return negative error codes as well. This caps the number of directory entries that can be reported at around two billion, but that should be sufficient. I have manually tested via an injected error that the error handling works correctly for the OPFS backend, although I was not able to construct a test case for this.
1 parent 48459c6 commit 9194ea9

File tree

8 files changed

+87
-43
lines changed

8 files changed

+87
-43
lines changed

src/library_wasmfs_opfs.js

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -149,21 +149,25 @@ mergeInto(LibraryManager.library, {
149149
},
150150

151151
_wasmfs_opfs_get_entries__deps: [],
152-
_wasmfs_opfs_get_entries: async function(ctx, dirID, entries) {
152+
_wasmfs_opfs_get_entries: async function(ctx, dirID, entriesPtr, errPtr) {
153153
let dirHandle = wasmfsOPFSDirectoryHandles.get(dirID);
154154

155155
// TODO: Use 'for await' once Acorn supports that.
156-
// TODO: Error handling.
157-
let iter = dirHandle.entries();
158-
for (let entry; entry = await iter.next(), !entry.done;) {
159-
let [name, child] = entry.value;
160-
withStackSave(() => {
161-
let namePtr = allocateUTF8OnStack(name);
162-
let type = child.kind == "file" ?
163-
{{{ cDefine('File::DataFileKind') }}} :
164-
{{{ cDefine('File::DirectoryKind') }}};
165-
__wasmfs_opfs_record_entry(entries, namePtr, type)
166-
});
156+
try {
157+
let iter = dirHandle.entries();
158+
for (let entry; entry = await iter.next(), !entry.done;) {
159+
let [name, child] = entry.value;
160+
withStackSave(() => {
161+
let namePtr = allocateUTF8OnStack(name);
162+
let type = child.kind == "file" ?
163+
{{{ cDefine('File::DataFileKind') }}} :
164+
{{{ cDefine('File::DirectoryKind') }}};
165+
__wasmfs_opfs_record_entry(entriesPtr, namePtr, type)
166+
});
167+
}
168+
} catch {
169+
let err = -{{{ cDefine('EIO') }}};
170+
{{{ makeSetValue('errPtr', 0, 'err', 'i32') }}};
167171
}
168172
_emscripten_proxy_finish(ctx);
169173
},

system/lib/wasmfs/backends/memory_backend.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,13 @@ int MemoryDirectory::removeChild(const std::string& name) {
5454
return 0;
5555
}
5656

57-
std::vector<Directory::Entry> MemoryDirectory::getEntries() {
57+
Directory::MaybeEntries MemoryDirectory::getEntries() {
5858
std::vector<Directory::Entry> result;
5959
result.reserve(entries.size());
6060
for (auto& [name, child] : entries) {
6161
result.push_back({name, child->kind, child->getIno()});
6262
}
63-
return result;
63+
return {result};
6464
}
6565

6666
int MemoryDirectory::insertMove(const std::string& name,

system/lib/wasmfs/backends/node_backend.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -258,21 +258,22 @@ class NodeDirectory : public Directory {
258258
abort();
259259
}
260260

261-
size_t getNumEntries() override {
261+
ssize_t getNumEntries() override {
262262
// TODO: optimize this?
263-
return getEntries().size();
263+
auto entries = getEntries();
264+
if (int err = entries.getError()) {
265+
return err;
266+
}
267+
return entries->size();
264268
}
265269

266-
std::vector<Directory::Entry> getEntries() override {
270+
Directory::MaybeEntries getEntries() override {
267271
std::vector<Directory::Entry> entries;
268272
int err = _wasmfs_node_readdir(state.path.c_str(), &entries);
269-
// TODO: Make this fallible. We actually depend on suppressing the error
270-
// here to pass test_unlink_wasmfs_node because the File stored in the
271-
// file table is not the same File that had its parent pointer reset
272-
// during the unlink. Fixing this may require caching Files at some
273-
// layer to ensure they are the same.
274-
(void)err;
275-
return entries;
273+
if (err) {
274+
return {-err};
275+
}
276+
return {entries};
276277
}
277278
};
278279

system/lib/wasmfs/backends/opfs_backend.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ void _wasmfs_opfs_remove_child(em_proxying_ctx* ctx,
5252

5353
void _wasmfs_opfs_get_entries(em_proxying_ctx* ctx,
5454
int dirID,
55-
std::vector<Directory::Entry>* entries);
55+
std::vector<Directory::Entry>* entries,
56+
int* err);
5657

5758
void _wasmfs_opfs_open_access(em_proxying_ctx* ctx,
5859
int file_id,
@@ -420,13 +421,25 @@ class OPFSDirectory : public Directory {
420421
return err;
421422
}
422423

423-
size_t getNumEntries() override { return getEntries().size(); }
424+
ssize_t getNumEntries() override {
425+
auto entries = getEntries();
426+
if (int err = entries.getError()) {
427+
return err;
428+
}
429+
return entries->size();
430+
}
424431

425-
std::vector<Directory::Entry> getEntries() override {
432+
Directory::MaybeEntries getEntries() override {
426433
std::vector<Directory::Entry> entries;
427-
proxy(
428-
[&](auto ctx) { _wasmfs_opfs_get_entries(ctx.ctx, dirID, &entries); });
429-
return entries;
434+
int err = 0;
435+
proxy([&](auto ctx) {
436+
_wasmfs_opfs_get_entries(ctx.ctx, dirID, &entries, &err);
437+
});
438+
if (err) {
439+
assert(err < 0);
440+
return {err};
441+
}
442+
return {entries};
430443
}
431444
};
432445

system/lib/wasmfs/file.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,24 +225,31 @@ std::string Directory::Handle::getName(std::shared_ptr<File> file) {
225225
return "";
226226
}
227227

228-
size_t Directory::Handle::getNumEntries() {
228+
ssize_t Directory::Handle::getNumEntries() {
229229
size_t mounts = 0;
230230
auto& dcache = getDir()->dcache;
231231
for (auto it = dcache.begin(); it != dcache.end(); ++it) {
232232
if (it->second.kind == DCacheKind::Mount) {
233233
++mounts;
234234
}
235235
}
236-
return getDir()->getNumEntries() + mounts;
236+
auto numReal = getDir()->getNumEntries();
237+
if (numReal < 0) {
238+
return numReal;
239+
}
240+
return numReal + mounts;
237241
}
238242

239-
std::vector<Directory::Entry> Directory::Handle::getEntries() {
243+
Directory::MaybeEntries Directory::Handle::getEntries() {
240244
auto entries = getDir()->getEntries();
245+
if (entries.getError()) {
246+
return entries;
247+
}
241248
auto& dcache = getDir()->dcache;
242249
for (auto it = dcache.begin(); it != dcache.end(); ++it) {
243250
auto& [name, entry] = *it;
244251
if (entry.kind == DCacheKind::Mount) {
245-
entries.push_back({name, entry.file->kind, entry.file->getIno()});
252+
entries->push_back({name, entry.file->kind, entry.file->getIno()});
246253
}
247254
}
248255
return entries;

system/lib/wasmfs/file.h

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <mutex>
1717
#include <optional>
1818
#include <sys/stat.h>
19+
#include <variant>
1920
#include <vector>
2021
#include <wasi/api.h>
2122

@@ -171,6 +172,20 @@ class Directory : public File {
171172
ino_t ino;
172173
};
173174

175+
struct MaybeEntries : std::variant<std::vector<Entry>, int> {
176+
int getError() {
177+
if (int* err = std::get_if<int>(this)) {
178+
assert(*err < 0);
179+
return *err;
180+
}
181+
return 0;
182+
}
183+
184+
std::vector<Entry>* operator->() {
185+
return std::get_if<std::vector<Entry>>(this);
186+
}
187+
};
188+
174189
private:
175190
// The directory cache, or `dcache`, stores `File` objects for the children of
176191
// each directory so that subsequent lookups do not need to query the backend.
@@ -213,11 +228,12 @@ class Directory : public File {
213228
// if the child cannot be removed.
214229
virtual int removeChild(const std::string& name) = 0;
215230

216-
// The number of entries in this directory.
217-
virtual size_t getNumEntries() = 0;
231+
// The number of entries in this directory. Returns the number of entries or a
232+
// negative error code.
233+
virtual ssize_t getNumEntries() = 0;
218234

219-
// The list of entries in this directory.
220-
virtual std::vector<Directory::Entry> getEntries() = 0;
235+
// The list of entries in this directory or a negative error code.
236+
virtual MaybeEntries getEntries() = 0;
221237

222238
// Only backends that maintain file identity themselves (see below) need to
223239
// implement this.
@@ -384,8 +400,8 @@ class Directory::Handle : public File::Handle {
384400

385401
std::string getName(std::shared_ptr<File> file);
386402

387-
size_t getNumEntries();
388-
std::vector<Directory::Entry> getEntries();
403+
[[nodiscard]] ssize_t getNumEntries();
404+
[[nodiscard]] MaybeEntries getEntries();
389405
};
390406

391407
inline File::Handle File::locked() { return Handle(shared_from_this()); }

system/lib/wasmfs/memory_backend.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ class MemoryDirectory : public Directory {
8888

8989
int insertMove(const std::string& name, std::shared_ptr<File> file) override;
9090

91-
size_t getNumEntries() override { return entries.size(); }
92-
std::vector<Directory::Entry> getEntries() override;
91+
ssize_t getNumEntries() override { return entries.size(); }
92+
Directory::MaybeEntries getEntries() override;
9393

9494
std::string getName(std::shared_ptr<File> file) override;
9595

system/lib/wasmfs/syscalls.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,10 @@ int __syscall_getdents64(int fd, intptr_t dirp, size_t count) {
879879
{".", File::DirectoryKind, dir->getIno()},
880880
{"..", File::DirectoryKind, parent->getIno()}};
881881
auto dirEntries = lockedDir.getEntries();
882-
entries.insert(entries.end(), dirEntries.begin(), dirEntries.end());
882+
if (int err = dirEntries.getError()) {
883+
return err;
884+
}
885+
entries.insert(entries.end(), dirEntries->begin(), dirEntries->end());
883886

884887
off_t bytesRead = 0;
885888
for (; index < entries.size() && bytesRead + sizeof(dirent) <= count;

0 commit comments

Comments
 (0)