Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libnodefs.js
4150 views
1
/**
2
* @license
3
* Copyright 2013 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*/
6
7
addToLibrary({
8
#if WASMFS
9
$NODEFS__deps: ['$stringToUTF8OnStack', 'wasmfs_create_node_backend'],
10
$NODEFS: {
11
createBackend(opts) {
12
return _wasmfs_create_node_backend(stringToUTF8OnStack(opts.root));
13
}
14
}
15
#else
16
$NODEFS__deps: ['$FS', '$PATH', '$ERRNO_CODES', '$mmapAlloc'],
17
$NODEFS__postset: 'if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit(); }',
18
$NODEFS: {
19
isWindows: false,
20
staticInit() {
21
NODEFS.isWindows = !!process.platform.match(/^win/);
22
var flags = process.binding("constants")["fs"];
23
NODEFS.flagsForNodeMap = {
24
"{{{ cDefs.O_APPEND }}}": flags["O_APPEND"],
25
"{{{ cDefs.O_CREAT }}}": flags["O_CREAT"],
26
"{{{ cDefs.O_EXCL }}}": flags["O_EXCL"],
27
"{{{ cDefs.O_NOCTTY }}}": flags["O_NOCTTY"],
28
"{{{ cDefs.O_RDONLY }}}": flags["O_RDONLY"],
29
"{{{ cDefs.O_RDWR }}}": flags["O_RDWR"],
30
"{{{ cDefs.O_DSYNC }}}": flags["O_SYNC"],
31
"{{{ cDefs.O_TRUNC }}}": flags["O_TRUNC"],
32
"{{{ cDefs.O_WRONLY }}}": flags["O_WRONLY"],
33
"{{{ cDefs.O_NOFOLLOW }}}": flags["O_NOFOLLOW"],
34
};
35
#if ASSERTIONS
36
// The 0 define must match on both sides, as otherwise we would not
37
// know to add it.
38
assert(NODEFS.flagsForNodeMap["0"] === 0);
39
#endif
40
},
41
convertNodeCode(e) {
42
var code = e.code;
43
#if ASSERTIONS
44
assert(code in ERRNO_CODES, `unexpected node error code: ${code} (${e})`);
45
#endif
46
return ERRNO_CODES[code];
47
},
48
tryFSOperation(f) {
49
try {
50
return f();
51
} catch (e) {
52
if (!e.code) throw e;
53
// node under windows can return code 'UNKNOWN' here:
54
// https://github.com/emscripten-core/emscripten/issues/15468
55
if (e.code === 'UNKNOWN') throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
56
throw new FS.ErrnoError(NODEFS.convertNodeCode(e));
57
}
58
},
59
mount(mount) {
60
#if ASSERTIONS
61
assert(ENVIRONMENT_IS_NODE);
62
#endif
63
return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0);
64
},
65
createNode(parent, name, mode, dev) {
66
if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) {
67
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
68
}
69
var node = FS.createNode(parent, name, mode);
70
node.node_ops = NODEFS.node_ops;
71
node.stream_ops = NODEFS.stream_ops;
72
return node;
73
},
74
getMode(path) {
75
return NODEFS.tryFSOperation(() => {
76
var mode = fs.lstatSync(path).mode;
77
if (NODEFS.isWindows) {
78
// Windows does not report the 'x' permission bit, so propagate read
79
// bits to execute bits.
80
mode |= (mode & {{{ cDefs.S_IRUGO }}}) >> 2;
81
}
82
return mode;
83
});
84
},
85
realPath(node) {
86
var parts = [];
87
while (node.parent !== node) {
88
parts.push(node.name);
89
node = node.parent;
90
}
91
parts.push(node.mount.opts.root);
92
parts.reverse();
93
return PATH.join(...parts);
94
},
95
// This maps the integer permission modes from http://linux.die.net/man/3/open
96
// to node.js-specific file open permission strings at http://nodejs.org/api/fs.html#fs_fs_open_path_flags_mode_callback
97
flagsForNode(flags) {
98
flags &= ~{{{ cDefs.O_PATH }}}; // Ignore this flag from musl, otherwise node.js fails to open the file.
99
flags &= ~{{{ cDefs.O_NONBLOCK }}}; // Ignore this flag from musl, otherwise node.js fails to open the file.
100
flags &= ~{{{ cDefs.O_LARGEFILE }}}; // Ignore this flag from musl, otherwise node.js fails to open the file.
101
flags &= ~{{{ cDefs.O_CLOEXEC }}}; // Some applications may pass it; it makes no sense for a single process.
102
flags &= ~{{{ cDefs.O_DIRECTORY }}}; // Node.js doesn't need this passed in, it errors.
103
var newFlags = 0;
104
for (var k in NODEFS.flagsForNodeMap) {
105
if (flags & k) {
106
newFlags |= NODEFS.flagsForNodeMap[k];
107
flags ^= k;
108
}
109
}
110
if (flags) {
111
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
112
}
113
return newFlags;
114
},
115
getattr(func, node) {
116
var stat = NODEFS.tryFSOperation(func);
117
if (NODEFS.isWindows) {
118
// node.js v0.10.20 doesn't report blksize and blocks on Windows. Fake
119
// them with default blksize of 4096.
120
// See http://support.microsoft.com/kb/140365
121
if (!stat.blksize) {
122
stat.blksize = 4096;
123
}
124
if (!stat.blocks) {
125
stat.blocks = (stat.size+stat.blksize-1)/stat.blksize|0;
126
}
127
// Windows does not report the 'x' permission bit, so propagate read
128
// bits to execute bits.
129
stat.mode |= (stat.mode & {{{ cDefs.S_IRUGO }}}) >> 2;
130
}
131
return {
132
dev: stat.dev,
133
ino: node.id,
134
mode: stat.mode,
135
nlink: stat.nlink,
136
uid: stat.uid,
137
gid: stat.gid,
138
rdev: stat.rdev,
139
size: stat.size,
140
atime: stat.atime,
141
mtime: stat.mtime,
142
ctime: stat.ctime,
143
blksize: stat.blksize,
144
blocks: stat.blocks
145
};
146
},
147
// Common code for both node and stream setattr
148
// For node getatrr:
149
// - arg is a native path
150
// - chmod, utimes, truncate are fs.chmodSync, fs.utimesSync, fs.truncateSync
151
// For stream getatrr:
152
// - arg is a native file descriptor
153
// - chmod, utimes, truncate are fs.fchmodSync, fs.futimesSync, fs.ftruncateSync
154
setattr(arg, node, attr, chmod, utimes, truncate, stat) {
155
NODEFS.tryFSOperation(() => {
156
if (attr.mode !== undefined) {
157
var mode = attr.mode;
158
if (NODEFS.isWindows) {
159
// Windows only supports S_IREAD / S_IWRITE (S_IRUSR / S_IWUSR)
160
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/chmod-wchmod
161
mode &= {{{ cDefs.S_IRUSR | cDefs.S_IWUSR }}};
162
}
163
chmod(arg, mode);
164
// update the common node structure mode as well
165
node.mode = attr.mode;
166
}
167
if (typeof (attr.atime ?? attr.mtime) === "number") {
168
// Unfortunately, we have to stat the current value if we don't want
169
// to change it. On top of that, since the times don't round trip
170
// this will only keep the value nearly unchanged not exactly
171
// unchanged. See:
172
// https://github.com/nodejs/node/issues/56492
173
var atime = new Date(attr.atime ?? stat(arg).atime);
174
var mtime = new Date(attr.mtime ?? stat(arg).mtime);
175
utimes(arg, atime, mtime);
176
}
177
if (attr.size !== undefined) {
178
truncate(arg, attr.size);
179
}
180
});
181
},
182
node_ops: {
183
getattr(node) {
184
var path = NODEFS.realPath(node);
185
return NODEFS.getattr(() => fs.lstatSync(path), node);
186
},
187
setattr(node, attr) {
188
var path = NODEFS.realPath(node);
189
if (attr.mode != null && attr.dontFollow) {
190
throw new FS.ErrnoError({{{ cDefs.ENOSYS }}});
191
}
192
NODEFS.setattr(path, node, attr, fs.chmodSync, fs.utimesSync, fs.truncateSync, fs.lstatSync);
193
},
194
lookup(parent, name) {
195
var path = PATH.join2(NODEFS.realPath(parent), name);
196
var mode = NODEFS.getMode(path);
197
return NODEFS.createNode(parent, name, mode);
198
},
199
mknod(parent, name, mode, dev) {
200
var node = NODEFS.createNode(parent, name, mode, dev);
201
// create the backing node for this in the fs root as well
202
var path = NODEFS.realPath(node);
203
NODEFS.tryFSOperation(() => {
204
if (FS.isDir(node.mode)) {
205
fs.mkdirSync(path, node.mode);
206
} else {
207
fs.writeFileSync(path, '', { mode: node.mode });
208
}
209
});
210
return node;
211
},
212
rename(oldNode, newDir, newName) {
213
var oldPath = NODEFS.realPath(oldNode);
214
var newPath = PATH.join2(NODEFS.realPath(newDir), newName);
215
try {
216
FS.unlink(newPath);
217
} catch(e) {}
218
NODEFS.tryFSOperation(() => fs.renameSync(oldPath, newPath));
219
oldNode.name = newName;
220
},
221
unlink(parent, name) {
222
var path = PATH.join2(NODEFS.realPath(parent), name);
223
NODEFS.tryFSOperation(() => fs.unlinkSync(path));
224
},
225
rmdir(parent, name) {
226
var path = PATH.join2(NODEFS.realPath(parent), name);
227
NODEFS.tryFSOperation(() => fs.rmdirSync(path));
228
},
229
readdir(node) {
230
var path = NODEFS.realPath(node);
231
return NODEFS.tryFSOperation(() => fs.readdirSync(path));
232
},
233
symlink(parent, newName, oldPath) {
234
var newPath = PATH.join2(NODEFS.realPath(parent), newName);
235
NODEFS.tryFSOperation(() => fs.symlinkSync(oldPath, newPath));
236
},
237
readlink(node) {
238
var path = NODEFS.realPath(node);
239
return NODEFS.tryFSOperation(() => fs.readlinkSync(path));
240
},
241
statfs(path) {
242
var stats = NODEFS.tryFSOperation(() => fs.statfsSync(path));
243
// Node.js doesn't provide frsize (fragment size). Set it to bsize (block size)
244
// as they're often the same in many file systems. May not be accurate for all.
245
stats.frsize = stats.bsize;
246
return stats;
247
}
248
},
249
stream_ops: {
250
getattr(stream) {
251
return NODEFS.getattr(() => fs.fstatSync(stream.nfd), stream.node);
252
},
253
setattr(stream, attr) {
254
NODEFS.setattr(stream.nfd, stream.node, attr, fs.fchmodSync, fs.futimesSync, fs.ftruncateSync, fs.fstatSync);
255
},
256
open(stream) {
257
var path = NODEFS.realPath(stream.node);
258
NODEFS.tryFSOperation(() => {
259
stream.shared.refcount = 1;
260
stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags));
261
});
262
},
263
close(stream) {
264
NODEFS.tryFSOperation(() => {
265
if (stream.nfd && --stream.shared.refcount === 0) {
266
fs.closeSync(stream.nfd);
267
}
268
});
269
},
270
dup(stream) {
271
stream.shared.refcount++;
272
},
273
read(stream, buffer, offset, length, position) {
274
return NODEFS.tryFSOperation(() =>
275
fs.readSync(stream.nfd, new Int8Array(buffer.buffer, offset, length), 0, length, position)
276
);
277
},
278
write(stream, buffer, offset, length, position) {
279
return NODEFS.tryFSOperation(() =>
280
fs.writeSync(stream.nfd, new Int8Array(buffer.buffer, offset, length), 0, length, position)
281
);
282
},
283
llseek(stream, offset, whence) {
284
var position = offset;
285
if (whence === {{{ cDefs.SEEK_CUR }}}) {
286
position += stream.position;
287
} else if (whence === {{{ cDefs.SEEK_END }}}) {
288
if (FS.isFile(stream.node.mode)) {
289
NODEFS.tryFSOperation(() => {
290
var stat = fs.fstatSync(stream.nfd);
291
position += stat.size;
292
});
293
}
294
}
295
296
if (position < 0) {
297
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
298
}
299
300
return position;
301
},
302
mmap(stream, length, position, prot, flags) {
303
if (!FS.isFile(stream.node.mode)) {
304
throw new FS.ErrnoError({{{ cDefs.ENODEV }}});
305
}
306
307
var ptr = mmapAlloc(length);
308
309
NODEFS.stream_ops.read(stream, HEAP8, ptr, length, position);
310
return { ptr, allocated: true };
311
},
312
msync(stream, buffer, offset, length, mmapFlags) {
313
NODEFS.stream_ops.write(stream, buffer, 0, length, offset, false);
314
// should we check if bytesWritten and length are the same?
315
return 0;
316
}
317
}
318
}
319
#endif
320
});
321
322