Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/system/lib/wasmfs/js_api.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 <dirent.h>
7
#include <syscall_arch.h>
8
#include <unistd.h>
9
#include <emscripten/wasmfs.h>
10
11
#include "backend.h"
12
#include "file.h"
13
#include "paths.h"
14
15
// Some APIs return data using a thread-local allocation that is never freed.
16
// This is simpler and more efficient as it avoids the JS caller needing to free
17
// the allocation (which would have both the overhead of free, and also of a
18
// call back into wasm), but on the other hand it does mean more memory may be
19
// used. This seems a reasonable tradeoff as heavy workloads should ideally
20
// avoid the JS API anyhow.
21
22
using namespace wasmfs;
23
24
extern "C" {
25
26
// Copy the file specified by the pathname into JS.
27
// Return zero on success, errno on failure.
28
// Output point and length are written to `out_buf` and `out_size` params.
29
int _wasmfs_read_file(const char* path, uint8_t** out_buf, off_t* out_size) {
30
static_assert(sizeof(off_t) == 8, "File offset type must be 64-bit");
31
32
struct stat file;
33
int err = 0;
34
err = stat(path, &file);
35
if (err < 0) {
36
return errno;
37
}
38
39
off_t size = file.st_size;
40
41
static thread_local uint8_t* buffer = nullptr;
42
buffer = (uint8_t*)realloc(buffer, size);
43
44
int fd = open(path, O_RDONLY);
45
if (fd < 0) {
46
return errno;
47
}
48
ssize_t numRead = read(fd, buffer, size);
49
if (numRead < 0) {
50
return errno;
51
}
52
// TODO: Generalize this so that it is thread-proof.
53
// Must guarantee that the file size has not changed by the time it is read.
54
assert(numRead == size);
55
err = close(fd);
56
if (err < 0) {
57
return errno;
58
}
59
60
*out_size = size;
61
*out_buf = buffer;
62
return 0;
63
}
64
65
// Writes to a file, possibly creating it, and returns the number of bytes
66
// written successfully. If the file already exists, appends to it.
67
int _wasmfs_write_file(const char* pathname, char* data, size_t data_size) {
68
auto parsedParent = path::parseParent(pathname);
69
if (parsedParent.getError()) {
70
return 0;
71
}
72
auto& [parent, childNameView] = parsedParent.getParentChild();
73
std::string childName(childNameView);
74
75
std::shared_ptr<File> child;
76
{
77
auto lockedParent = parent->locked();
78
child = lockedParent.getChild(childName);
79
if (!child) {
80
// Lookup failed; try creating the file.
81
child = lockedParent.insertDataFile(childName, 0777);
82
if (!child) {
83
// File creation failed; nothing else to do.
84
return 0;
85
}
86
}
87
}
88
89
auto dataFile = child->dynCast<DataFile>();
90
if (!dataFile) {
91
// There is something here but it isn't a data file.
92
return 0;
93
}
94
95
auto lockedFile = dataFile->locked();
96
int err = lockedFile.open(O_WRONLY);
97
if (err < 0) {
98
emscripten_err("Fatal error in FS.writeFile");
99
abort();
100
}
101
102
auto offset = lockedFile.getSize();
103
auto result = lockedFile.write((uint8_t*)data, data_size, offset);
104
if (result != __WASI_ERRNO_SUCCESS) {
105
return 0;
106
}
107
108
err = lockedFile.close();
109
if (err < 0) {
110
emscripten_err("Fatal error in FS.writeFile");
111
abort();
112
}
113
114
return data_size;
115
}
116
117
int _wasmfs_mkdir(const char* path, int mode) {
118
return __syscall_mkdirat(AT_FDCWD, (intptr_t)path, mode);
119
}
120
121
int _wasmfs_rmdir(const char* path) {
122
return __syscall_unlinkat(AT_FDCWD, (intptr_t)path, AT_REMOVEDIR);
123
}
124
125
int _wasmfs_open(const char* path, int flags, mode_t mode) {
126
return __syscall_openat(AT_FDCWD, (intptr_t)path, flags, mode);
127
}
128
129
int _wasmfs_mknod(const char* path, mode_t mode, dev_t dev) {
130
return __syscall_mknodat(AT_FDCWD, (intptr_t)path, mode, dev);
131
}
132
133
int _wasmfs_unlink(const char* path) {
134
return __syscall_unlinkat(AT_FDCWD, (intptr_t)path, 0);
135
}
136
137
int _wasmfs_chdir(const char* path) { return __syscall_chdir((intptr_t)path); }
138
139
int _wasmfs_symlink(const char* old_path, const char* new_path) {
140
return __syscall_symlinkat((intptr_t)old_path, AT_FDCWD, (intptr_t)new_path);
141
}
142
143
int _wasmfs_readlink(const char* path, char** out_ptr) {
144
static thread_local char* readBuf = (char*)malloc(PATH_MAX);
145
int bytes =
146
__syscall_readlinkat(AT_FDCWD, (intptr_t)path, (intptr_t)readBuf, PATH_MAX);
147
if (bytes < 0) {
148
return bytes;
149
}
150
readBuf[bytes] = '\0';
151
*out_ptr = readBuf;
152
return 0;
153
}
154
155
int _wasmfs_write(int fd, void* buf, size_t count) {
156
__wasi_ciovec_t iovs[1];
157
iovs[0].buf = (uint8_t*)buf;
158
iovs[0].buf_len = count;
159
160
__wasi_size_t numBytes;
161
__wasi_errno_t err = __wasi_fd_write(fd, iovs, 1, &numBytes);
162
if (err) {
163
return -err;
164
}
165
return numBytes;
166
}
167
168
int _wasmfs_pwrite(int fd, void* buf, size_t count, off_t offset) {
169
__wasi_ciovec_t iovs[1];
170
iovs[0].buf = (uint8_t*)buf;
171
iovs[0].buf_len = count;
172
173
__wasi_size_t numBytes;
174
__wasi_errno_t err = __wasi_fd_pwrite(fd, iovs, 1, offset, &numBytes);
175
if (err) {
176
return -err;
177
}
178
return numBytes;
179
}
180
181
int _wasmfs_chmod(const char* path, mode_t mode) {
182
return __syscall_chmod((intptr_t)path, mode);
183
}
184
185
int _wasmfs_fchmod(int fd, mode_t mode) { return __syscall_fchmod(fd, mode); }
186
187
int _wasmfs_lchmod(const char* path, mode_t mode) {
188
return __syscall_fchmodat2(
189
AT_FDCWD, (intptr_t)path, mode, AT_SYMLINK_NOFOLLOW);
190
}
191
192
int _wasmfs_llseek(int fd, off_t offset, int whence) {
193
__wasi_filesize_t newOffset;
194
int err = __wasi_fd_seek(fd, offset, whence, &newOffset);
195
if (err > 0) {
196
return -err;
197
}
198
return newOffset;
199
}
200
201
int _wasmfs_rename(const char* oldpath, const char* newpath) {
202
return __syscall_renameat(
203
AT_FDCWD, (intptr_t)oldpath, AT_FDCWD, (intptr_t)newpath);
204
}
205
206
int _wasmfs_read(int fd, void* buf, size_t count) {
207
__wasi_iovec_t iovs[1];
208
iovs[0].buf = (uint8_t*)buf;
209
iovs[0].buf_len = count;
210
211
__wasi_size_t numBytes;
212
__wasi_errno_t err = __wasi_fd_read(fd, iovs, 1, &numBytes);
213
if (err) {
214
return -err;
215
}
216
return numBytes;
217
}
218
219
int _wasmfs_pread(int fd, void* buf, size_t count, off_t offset) {
220
__wasi_iovec_t iovs[1];
221
iovs[0].buf = (uint8_t*)buf;
222
iovs[0].buf_len = count;
223
224
__wasi_size_t numBytes;
225
__wasi_errno_t err = __wasi_fd_pread(fd, iovs, 1, offset, &numBytes);
226
if (err) {
227
return -err;
228
}
229
return numBytes;
230
}
231
232
int _wasmfs_truncate(const char* path, off_t length) {
233
return __syscall_truncate64((intptr_t)path, length);
234
}
235
236
int _wasmfs_ftruncate(int fd, off_t length) {
237
return __syscall_ftruncate64(fd, length);
238
}
239
240
int _wasmfs_close(int fd) { return __wasi_fd_close(fd); }
241
242
void* _wasmfs_mmap(size_t length, int prot, int flags, int fd, off_t offset) {
243
return (void*)__syscall_mmap2(0, length, prot, flags, fd, offset);
244
}
245
246
int _wasmfs_msync(void* addr, size_t length, int flags) {
247
return __syscall_msync((intptr_t)addr, length, flags);
248
}
249
250
int _wasmfs_munmap(void* addr, size_t length) {
251
return __syscall_munmap((intptr_t)addr, length);
252
}
253
254
int _wasmfs_utime(const char* path, double atime_ms, double mtime_ms) {
255
struct timespec times[2];
256
times[0].tv_sec = (long)atime_ms / 1000;
257
times[0].tv_nsec = ((long)atime_ms % 1000) * 1000000;
258
times[1].tv_sec = (long)mtime_ms / 1000;
259
times[1].tv_nsec = ((long)mtime_ms % 1000) * 1000000;
260
261
return __syscall_utimensat(AT_FDCWD, (intptr_t)path, (intptr_t)times, 0);
262
}
263
264
int _wasmfs_stat(const char* path, struct stat* statBuf) {
265
return __syscall_stat64((intptr_t)path, (intptr_t)statBuf);
266
}
267
268
int _wasmfs_lstat(const char* path, struct stat* statBuf) {
269
return __syscall_lstat64((intptr_t)path, (intptr_t)statBuf);
270
}
271
272
// The legacy JS API requires a mountpoint to already exist, so WasmFS will
273
// attempt to remove the target directory if it exists before replacing it with
274
// a mounted directory.
275
int _wasmfs_mount(const char* path, ::backend_t created_backend) {
276
int err = __syscall_rmdir((intptr_t)path);
277
278
// The legacy JS API mount requires the directory to already exist, but we
279
// will also allow it to be missing.
280
if (err && err != -ENOENT) {
281
return err;
282
}
283
284
return wasmfs_create_directory(path, 0777, created_backend);
285
}
286
287
// Helper method that identifies what a path is:
288
// ENOENT - if nothing exists there
289
// EISDIR - if it is a directory
290
// EEXIST - if it is a normal file
291
int _wasmfs_identify(const char* path) {
292
struct stat file;
293
int err = 0;
294
err = stat(path, &file);
295
if (err < 0) {
296
return ENOENT;
297
}
298
if (S_ISDIR(file.st_mode)) {
299
return EISDIR;
300
}
301
return EEXIST;
302
}
303
304
struct wasmfs_readdir_state {
305
int i;
306
int nentries;
307
struct dirent** entries;
308
};
309
310
struct wasmfs_readdir_state* _wasmfs_readdir_start(const char* path) {
311
struct dirent** entries;
312
int nentries = scandir(path, &entries, NULL, alphasort);
313
if (nentries == -1) {
314
return NULL;
315
}
316
struct wasmfs_readdir_state* state =
317
(struct wasmfs_readdir_state*)malloc(sizeof(*state));
318
if (state == NULL) {
319
return NULL;
320
}
321
state->i = 0;
322
state->nentries = nentries;
323
state->entries = entries;
324
return state;
325
}
326
327
const char* _wasmfs_readdir_get(struct wasmfs_readdir_state* state) {
328
if (state->i < state->nentries) {
329
return state->entries[state->i++]->d_name;
330
}
331
return NULL;
332
}
333
334
void _wasmfs_readdir_finish(struct wasmfs_readdir_state* state) {
335
for (int i = 0; i < state->nentries; i++) {
336
free(state->entries[i]);
337
}
338
free(state->entries);
339
free(state);
340
}
341
342
char* _wasmfs_get_cwd(void) {
343
// TODO: PATH_MAX is 4K atm, so it might be good to reduce this somehow.
344
static thread_local char* path = (char*)malloc(PATH_MAX);
345
return getcwd(path, PATH_MAX);
346
}
347
348
} // extern "C"
349
350