Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/src/lib/libnoderawfs.js
4150 views
1
/**
2
* @license
3
* Copyright 2018 The Emscripten Authors
4
* SPDX-License-Identifier: MIT
5
*/
6
7
addToLibrary({
8
$NODERAWFS__deps: ['$ERRNO_CODES', '$FS', '$NODEFS', '$mmapAlloc', '$FS_modeStringToFlags'],
9
$NODERAWFS__postset: `
10
if (!ENVIRONMENT_IS_NODE) {
11
throw new Error("NODERAWFS is currently only supported on Node.js environment.")
12
}
13
var _wrapNodeError = function(func) {
14
return function(...args) {
15
try {
16
return func(...args)
17
} catch (e) {
18
if (e.code) {
19
throw new FS.ErrnoError(ERRNO_CODES[e.code]);
20
}
21
throw e;
22
}
23
}
24
};
25
// Use this to reference our in-memory filesystem
26
/** @suppress {partialAlias} */
27
var VFS = {...FS};
28
// Wrap the whole in-memory filesystem API with
29
// our Node.js based functions
30
for (var _key in NODERAWFS) {
31
FS[_key] = _wrapNodeError(NODERAWFS[_key]);
32
}`,
33
$NODERAWFS: {
34
lookup(parent, name) {
35
#if ASSERTIONS
36
assert(parent)
37
assert(parent.path)
38
#endif
39
return FS.lookupPath(`${parent.path}/${name}`).node;
40
},
41
lookupPath(path, opts = {}) {
42
if (opts.parent) {
43
path = PATH.dirname(path);
44
}
45
var st = fs.lstatSync(path);
46
var mode = NODEFS.getMode(path);
47
return { path, node: { id: st.ino, mode, node_ops: NODERAWFS, path }};
48
},
49
createStandardStreams() {
50
// FIXME: tty is set to true to appease isatty(), the underlying ioctl syscalls still needs to be implemented, see issue #22264.
51
FS.createStream({ nfd: 0, position: 0, path: '/dev/stdin', flags: 0, tty: true, seekable: false }, 0);
52
var paths = [,'/dev/stdout', '/dev/stderr'];
53
for (var i = 1; i < 3; i++) {
54
FS.createStream({ nfd: i, position: 0, path: paths[i], flags: {{{ cDefs.O_TRUNC | cDefs.O_CREAT | cDefs.O_WRONLY }}}, tty: true, seekable: false }, i);
55
}
56
},
57
// generic function for all node creation
58
cwd() { return process.cwd(); },
59
chdir(...args) { process.chdir(...args); },
60
mknod(path, mode) {
61
if (FS.isDir(path)) {
62
fs.mkdirSync(path, mode);
63
} else {
64
fs.writeFileSync(path, '', { mode: mode });
65
}
66
},
67
mkdir(...args) { fs.mkdirSync(...args); },
68
symlink(...args) { fs.symlinkSync(...args); },
69
rename(...args) { fs.renameSync(...args); },
70
rmdir(...args) { fs.rmdirSync(...args); },
71
readdir(...args) { return ['.', '..'].concat(fs.readdirSync(...args)); },
72
unlink(...args) { fs.unlinkSync(...args); },
73
readlink(...args) { return fs.readlinkSync(...args); },
74
stat(path, dontFollow) {
75
var stat = dontFollow ? fs.lstatSync(path) : fs.statSync(path);
76
if (NODEFS.isWindows) {
77
// Windows does not report the 'x' permission bit, so propagate read
78
// bits to execute bits.
79
stat.mode |= (stat.mode & {{{ cDefs.S_IRUGO }}}) >> 2;
80
}
81
return stat;
82
},
83
fstat(fd) {
84
var stream = FS.getStreamChecked(fd);
85
return fs.fstatSync(stream.nfd);
86
},
87
statfs(path) {
88
// Node's fs.statfsSync API doesn't provide these attributes so include
89
// some defaults.
90
var defaults = {
91
fsid: 42,
92
flags: 2,
93
namelen: 255,
94
}
95
return Object.assign(defaults, fs.statfsSync(path));
96
},
97
statfsStream(stream) {
98
return FS.statfs(stream.path);
99
},
100
chmod(path, mode, dontFollow) {
101
mode &= {{{ cDefs.S_IALLUGO }}};
102
if (NODEFS.isWindows) {
103
// Windows only supports S_IREAD / S_IWRITE (S_IRUSR / S_IWUSR)
104
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/chmod-wchmod
105
mode &= {{{ cDefs.S_IRUSR | cDefs.S_IWUSR }}};
106
}
107
if (dontFollow && fs.lstatSync(path).isSymbolicLink()) {
108
// Node (and indeed linux) does not support chmod on symlinks
109
// https://nodejs.org/api/fs.html#fslchmodsyncpath-mode
110
throw new FS.ErrnoError({{{ cDefs.EOPNOTSUPP }}});
111
}
112
fs.chmodSync(path, mode);
113
},
114
fchmod(fd, mode) {
115
var stream = FS.getStreamChecked(fd);
116
fs.fchmodSync(stream.nfd, mode);
117
},
118
chown(...args) { fs.chownSync(...args); },
119
fchown(fd, owner, group) {
120
var stream = FS.getStreamChecked(fd);
121
fs.fchownSync(stream.nfd, owner, group);
122
},
123
truncate(path, len) {
124
// See https://github.com/nodejs/node/issues/35632
125
if (len < 0) {
126
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
127
}
128
return fs.truncateSync(path, len);
129
},
130
ftruncate(fd, len) {
131
// See https://github.com/nodejs/node/issues/35632
132
if (len < 0) {
133
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
134
}
135
var stream = FS.getStreamChecked(fd);
136
fs.ftruncateSync(stream.nfd, len);
137
},
138
utime(path, atime, mtime) {
139
// null here for atime or mtime means UTIME_OMIT was passed. Since node
140
// doesn't support this concept we need to first find the existing
141
// timestamps in order to preserve them.
142
if ((atime === null) || (mtime === null)) {
143
var st = fs.statSync(path);
144
atime ||= st.atimeMs;
145
mtime ||= st.mtimeMs;
146
}
147
fs.utimesSync(path, atime/1000, mtime/1000);
148
},
149
open(path, flags, mode) {
150
if (typeof flags == "string") {
151
flags = FS_modeStringToFlags(flags)
152
}
153
var pathTruncated = path.split('/').map((s) => s.slice(0, 255)).join('/');
154
var nfd = fs.openSync(pathTruncated, NODEFS.flagsForNode(flags), mode);
155
var st = fs.fstatSync(nfd);
156
if (flags & {{{ cDefs.O_DIRECTORY }}} && !st.isDirectory()) {
157
fs.closeSync(nfd);
158
throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR);
159
}
160
var newMode = NODEFS.getMode(pathTruncated);
161
var node = { id: st.ino, mode: newMode, node_ops: NODERAWFS, path }
162
return FS.createStream({ nfd, position: 0, path, flags, node, seekable: true });
163
},
164
createStream(stream, fd) {
165
// Call the original FS.createStream
166
var rtn = VFS.createStream(stream, fd);
167
if (typeof rtn.shared.refcnt == 'undefined') {
168
rtn.shared.refcnt = 1;
169
} else {
170
rtn.shared.refcnt++;
171
}
172
return rtn;
173
},
174
close(stream) {
175
VFS.closeStream(stream.fd);
176
// Don't close stdin/stdout/stderr since they are used by node itself.
177
if (!stream.stream_ops && --stream.shared.refcnt <= 0 && stream.nfd > 2) {
178
// This stream is created by our Node.js filesystem, close the
179
// native file descriptor when its reference count drops to 0.
180
fs.closeSync(stream.nfd);
181
}
182
},
183
llseek(stream, offset, whence) {
184
if (stream.stream_ops) {
185
// this stream is created by in-memory filesystem
186
return VFS.llseek(stream, offset, whence);
187
}
188
var position = offset;
189
if (whence === {{{ cDefs.SEEK_CUR }}}) {
190
position += stream.position;
191
} else if (whence === {{{ cDefs.SEEK_END }}}) {
192
position += fs.fstatSync(stream.nfd).size;
193
} else if (whence !== {{{ cDefs.SEEK_SET }}}) {
194
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
195
}
196
197
if (position < 0) {
198
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
199
}
200
stream.position = position;
201
return position;
202
},
203
read(stream, buffer, offset, length, position) {
204
if (stream.stream_ops) {
205
// this stream is created by in-memory filesystem
206
return VFS.read(stream, buffer, offset, length, position);
207
}
208
var seeking = typeof position != 'undefined';
209
if (!seeking && stream.seekable) position = stream.position;
210
var bytesRead = fs.readSync(stream.nfd, new Int8Array(buffer.buffer, offset, length), 0, length, position);
211
// update position marker when non-seeking
212
if (!seeking) stream.position += bytesRead;
213
return bytesRead;
214
},
215
write(stream, buffer, offset, length, position) {
216
if (stream.stream_ops) {
217
// this stream is created by in-memory filesystem
218
return VFS.write(stream, buffer, offset, length, position);
219
}
220
if (stream.flags & +"{{{ cDefs.O_APPEND }}}") {
221
// seek to the end before writing in append mode
222
FS.llseek(stream, 0, +"{{{ cDefs.SEEK_END }}}");
223
}
224
var seeking = typeof position != 'undefined';
225
if (!seeking && stream.seekable) position = stream.position;
226
var bytesWritten = fs.writeSync(stream.nfd, new Int8Array(buffer.buffer, offset, length), 0, length, position);
227
// update position marker when non-seeking
228
if (!seeking) stream.position += bytesWritten;
229
return bytesWritten;
230
},
231
mmap(stream, length, position, prot, flags) {
232
if (!length) {
233
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
234
}
235
if (stream.stream_ops) {
236
// this stream is created by in-memory filesystem
237
return VFS.mmap(stream, length, position, prot, flags);
238
}
239
240
var ptr = mmapAlloc(length);
241
FS.read(stream, HEAP8, ptr, length, position);
242
return { ptr, allocated: true };
243
},
244
msync(stream, buffer, offset, length, mmapFlags) {
245
if (stream.stream_ops) {
246
// this stream is created by in-memory filesystem
247
return VFS.msync(stream, buffer, offset, length, mmapFlags);
248
}
249
250
FS.write(stream, buffer, 0, length, offset);
251
// should we check if bytesWritten and length are the same?
252
return 0;
253
},
254
ioctl() {
255
throw new FS.ErrnoError({{{ cDefs.ENOTTY }}});
256
}
257
}
258
});
259
260