@bytecodealliance/preview2-shim 0.17.2 → 0.17.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -0
- package/lib/browser/cli.js +76 -103
- package/lib/browser/clocks.js +30 -29
- package/lib/browser/config.js +6 -0
- package/lib/browser/environment.js +29 -0
- package/lib/browser/filesystem.js +299 -255
- package/lib/browser/http.js +129 -128
- package/lib/browser/index.js +8 -16
- package/lib/browser/io.js +143 -135
- package/lib/browser/random.js +44 -42
- package/lib/browser/sockets.js +68 -166
- package/lib/common/instantiation.js +134 -0
- package/lib/io/calls.js +8 -5
- package/lib/io/worker-http.js +179 -157
- package/lib/io/worker-io.js +402 -386
- package/lib/io/worker-socket-tcp.js +271 -219
- package/lib/io/worker-socket-udp.js +494 -429
- package/lib/io/worker-sockets.js +255 -241
- package/lib/io/worker-thread.js +837 -754
- package/lib/nodejs/cli.js +64 -63
- package/lib/nodejs/clocks.js +51 -45
- package/lib/nodejs/filesystem.js +785 -651
- package/lib/nodejs/http.js +697 -617
- package/lib/nodejs/index.js +8 -16
- package/lib/nodejs/random.js +32 -28
- package/lib/nodejs/sockets.js +538 -474
- package/lib/synckit/index.js +94 -85
- package/package.json +11 -5
- package/types/index.d.ts +0 -1
- package/types/instantiation.d.ts +136 -0
package/lib/nodejs/filesystem.js
CHANGED
|
@@ -1,60 +1,68 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from
|
|
8
|
-
import { INPUT_STREAM_CREATE, OUTPUT_STREAM_CREATE } from
|
|
9
|
-
import { FILE } from
|
|
2
|
+
earlyDispose,
|
|
3
|
+
inputStreamCreate,
|
|
4
|
+
ioCall,
|
|
5
|
+
outputStreamCreate,
|
|
6
|
+
registerDispose,
|
|
7
|
+
} from '../io/worker-io.js';
|
|
8
|
+
import { INPUT_STREAM_CREATE, OUTPUT_STREAM_CREATE } from '../io/calls.js';
|
|
9
|
+
import { FILE } from '../io/calls.js';
|
|
10
10
|
import nodeFs, {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} from
|
|
33
|
-
import { platform } from
|
|
11
|
+
closeSync,
|
|
12
|
+
constants,
|
|
13
|
+
fdatasyncSync,
|
|
14
|
+
fstatSync,
|
|
15
|
+
fsyncSync,
|
|
16
|
+
ftruncateSync,
|
|
17
|
+
futimesSync,
|
|
18
|
+
linkSync,
|
|
19
|
+
lstatSync,
|
|
20
|
+
mkdirSync,
|
|
21
|
+
opendirSync,
|
|
22
|
+
openSync,
|
|
23
|
+
readlinkSync,
|
|
24
|
+
readSync,
|
|
25
|
+
renameSync,
|
|
26
|
+
rmdirSync,
|
|
27
|
+
statSync,
|
|
28
|
+
symlinkSync,
|
|
29
|
+
unlinkSync,
|
|
30
|
+
utimesSync,
|
|
31
|
+
writeSync,
|
|
32
|
+
} from 'node:fs';
|
|
33
|
+
import { platform } from 'node:process';
|
|
34
34
|
|
|
35
35
|
const lutimesSync = nodeFs.lutimesSync;
|
|
36
36
|
|
|
37
|
-
const symbolDispose = Symbol.dispose || Symbol.for(
|
|
37
|
+
const symbolDispose = Symbol.dispose || Symbol.for('dispose');
|
|
38
38
|
|
|
39
|
-
const isWindows = platform ===
|
|
40
|
-
const isMac = platform ===
|
|
39
|
+
const isWindows = platform === 'win32';
|
|
40
|
+
const isMac = platform === 'darwin';
|
|
41
41
|
|
|
42
42
|
const nsMagnitude = 1_000_000_000_000n;
|
|
43
43
|
function nsToDateTime(ns) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
const seconds = ns / nsMagnitude;
|
|
45
|
+
const nanoseconds = Number(ns % nsMagnitude);
|
|
46
|
+
return { seconds, nanoseconds };
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function lookupType(obj) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
if (obj.isFile()) {
|
|
51
|
+
return 'regular-file';
|
|
52
|
+
} else if (obj.isSocket()) {
|
|
53
|
+
return 'socket';
|
|
54
|
+
} else if (obj.isSymbolicLink()) {
|
|
55
|
+
return 'symbolic-link';
|
|
56
|
+
} else if (obj.isFIFO()) {
|
|
57
|
+
return 'fifo';
|
|
58
|
+
} else if (obj.isDirectory()) {
|
|
59
|
+
return 'directory';
|
|
60
|
+
} else if (obj.isCharacterDevice()) {
|
|
61
|
+
return 'character-device';
|
|
62
|
+
} else if (obj.isBlockDevice()) {
|
|
63
|
+
return 'block-device';
|
|
64
|
+
}
|
|
65
|
+
return 'unknown';
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
// Note: This should implement per-segment semantics of openAt, but we cannot
|
|
@@ -65,482 +73,608 @@ function lookupType(obj) {
|
|
|
65
73
|
* @implements {DescriptorProps}
|
|
66
74
|
*/
|
|
67
75
|
class Descriptor {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
setSize(size) {
|
|
151
|
-
if (this.#hostPreopen) throw "is-directory";
|
|
152
|
-
try {
|
|
153
|
-
ftruncateSync(this.#fd, Number(size));
|
|
154
|
-
} catch (e) {
|
|
155
|
-
if (isWindows && e.code === 'EPERM')
|
|
156
|
-
throw 'access';
|
|
157
|
-
throw convertFsError(e);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
setTimes(dataAccessTimestamp, dataModificationTimestamp) {
|
|
162
|
-
if (this.#hostPreopen) throw "invalid";
|
|
163
|
-
let stats;
|
|
164
|
-
if (
|
|
165
|
-
dataAccessTimestamp.tag === "no-change" ||
|
|
166
|
-
dataModificationTimestamp.tag === "no-change"
|
|
167
|
-
)
|
|
168
|
-
stats = this.stat();
|
|
169
|
-
const atime = this.#getNewTimestamp(
|
|
170
|
-
dataAccessTimestamp,
|
|
171
|
-
dataAccessTimestamp.tag === "no-change" && stats.dataAccessTimestamp
|
|
172
|
-
);
|
|
173
|
-
const mtime = this.#getNewTimestamp(
|
|
174
|
-
dataModificationTimestamp,
|
|
175
|
-
dataModificationTimestamp.tag === "no-change" &&
|
|
176
|
-
stats.dataModificationTimestamp
|
|
177
|
-
);
|
|
178
|
-
try {
|
|
179
|
-
futimesSync(this.#fd, atime, mtime);
|
|
180
|
-
} catch (e) {
|
|
181
|
-
throw convertFsError(e);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
#getNewTimestamp(newTimestamp, maybeNow) {
|
|
186
|
-
switch (newTimestamp.tag) {
|
|
187
|
-
case "no-change":
|
|
188
|
-
return timestampToMs(maybeNow);
|
|
189
|
-
case "now":
|
|
190
|
-
return Math.floor(Date.now() / 1e3);
|
|
191
|
-
case "timestamp":
|
|
192
|
-
return timestampToMs(newTimestamp.val);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
read(length, offset) {
|
|
197
|
-
if (!this.#fullPath) throw "bad-descriptor";
|
|
198
|
-
const buf = new Uint8Array(Number(length));
|
|
199
|
-
const bytesRead = readSync(
|
|
200
|
-
this.#fd,
|
|
201
|
-
buf,
|
|
202
|
-
0,
|
|
203
|
-
Number(length),
|
|
204
|
-
Number(offset)
|
|
205
|
-
);
|
|
206
|
-
const out = new Uint8Array(buf.buffer, 0, bytesRead);
|
|
207
|
-
return [out, bytesRead === 0 ? "ended" : "open"];
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
write(buffer, offset) {
|
|
211
|
-
if (!this.#fullPath) throw "bad-descriptor";
|
|
212
|
-
return BigInt(
|
|
213
|
-
writeSync(this.#fd, buffer, 0, buffer.byteLength, Number(offset))
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
readDirectory() {
|
|
218
|
-
if (!this.#fullPath) throw "bad-descriptor";
|
|
219
|
-
try {
|
|
220
|
-
const dir = opendirSync(this.#fullPath);
|
|
221
|
-
return directoryEntryStreamCreate(dir);
|
|
222
|
-
} catch (e) {
|
|
223
|
-
throw convertFsError(e);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
sync() {
|
|
228
|
-
if (this.#hostPreopen) throw "invalid";
|
|
229
|
-
try {
|
|
230
|
-
fsyncSync(this.#fd);
|
|
231
|
-
} catch (e) {
|
|
232
|
-
if (e.code === "EPERM") return;
|
|
233
|
-
throw convertFsError(e);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
createDirectoryAt(path) {
|
|
238
|
-
const fullPath = this.#getFullPath(path);
|
|
239
|
-
try {
|
|
240
|
-
mkdirSync(fullPath);
|
|
241
|
-
} catch (e) {
|
|
242
|
-
throw convertFsError(e);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
stat() {
|
|
247
|
-
if (this.#hostPreopen) throw "invalid";
|
|
248
|
-
let stats;
|
|
249
|
-
try {
|
|
250
|
-
stats = fstatSync(this.#fd, { bigint: true });
|
|
251
|
-
} catch (e) {
|
|
252
|
-
throw convertFsError(e);
|
|
253
|
-
}
|
|
254
|
-
const type = lookupType(stats);
|
|
255
|
-
return {
|
|
256
|
-
type,
|
|
257
|
-
linkCount: stats.nlink,
|
|
258
|
-
size: stats.size,
|
|
259
|
-
dataAccessTimestamp: nsToDateTime(stats.atimeNs),
|
|
260
|
-
dataModificationTimestamp: nsToDateTime(stats.mtimeNs),
|
|
261
|
-
statusChangeTimestamp: nsToDateTime(stats.ctimeNs),
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
statAt(pathFlags, path) {
|
|
266
|
-
const fullPath = this.#getFullPath(path, false);
|
|
267
|
-
let stats;
|
|
268
|
-
try {
|
|
269
|
-
stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(fullPath, {
|
|
270
|
-
bigint: true,
|
|
271
|
-
});
|
|
272
|
-
} catch (e) {
|
|
273
|
-
throw convertFsError(e);
|
|
274
|
-
}
|
|
275
|
-
const type = lookupType(stats);
|
|
276
|
-
return {
|
|
277
|
-
type,
|
|
278
|
-
linkCount: stats.nlink,
|
|
279
|
-
size: stats.size,
|
|
280
|
-
dataAccessTimestamp: nsToDateTime(stats.atimeNs),
|
|
281
|
-
dataModificationTimestamp: nsToDateTime(stats.mtimeNs),
|
|
282
|
-
statusChangeTimestamp: nsToDateTime(stats.ctimeNs),
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
setTimesAt(pathFlags, path, dataAccessTimestamp, dataModificationTimestamp) {
|
|
287
|
-
const fullPath = this.#getFullPath(path, false);
|
|
288
|
-
let stats;
|
|
289
|
-
if (
|
|
290
|
-
dataAccessTimestamp.tag === "no-change" ||
|
|
291
|
-
dataModificationTimestamp.tag === "no-change"
|
|
292
|
-
)
|
|
293
|
-
stats = this.stat();
|
|
294
|
-
const atime = this.#getNewTimestamp(
|
|
295
|
-
dataAccessTimestamp,
|
|
296
|
-
dataAccessTimestamp.tag === "no-change" && stats.dataAccessTimestamp
|
|
297
|
-
);
|
|
298
|
-
const mtime = this.#getNewTimestamp(
|
|
299
|
-
dataModificationTimestamp,
|
|
300
|
-
dataModificationTimestamp.tag === "no-change" &&
|
|
301
|
-
stats.dataModificationTimestamp
|
|
302
|
-
);
|
|
303
|
-
if (!pathFlags.symlinkFollow && !lutimesSync){
|
|
304
|
-
throw new Error("Changing the timestamps of symlinks isn't supported");
|
|
305
|
-
}
|
|
306
|
-
try {
|
|
307
|
-
(pathFlags.symlinkFollow ? utimesSync : lutimesSync)(
|
|
308
|
-
fullPath,
|
|
309
|
-
atime,
|
|
310
|
-
mtime
|
|
311
|
-
);
|
|
312
|
-
} catch (e) {
|
|
313
|
-
throw convertFsError(e);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
linkAt(oldPathFlags, oldPath, newDescriptor, newPath) {
|
|
318
|
-
const oldFullPath = this.#getFullPath(oldPath, oldPathFlags.symlinkFollow);
|
|
319
|
-
const newFullPath = newDescriptor.#getFullPath(newPath, false);
|
|
320
|
-
// Windows doesn't automatically fail on trailing slashes
|
|
321
|
-
if (isWindows && newFullPath.endsWith("/")) throw "no-entry";
|
|
322
|
-
try {
|
|
323
|
-
linkSync(oldFullPath, newFullPath);
|
|
324
|
-
} catch (e) {
|
|
325
|
-
throw convertFsError(e);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
openAt(pathFlags, path, openFlags, descriptorFlags) {
|
|
330
|
-
if (preopenEntries.length === 0)
|
|
331
|
-
throw "access";
|
|
332
|
-
const fullPath = this.#getFullPath(path, pathFlags.symlinkFollow);
|
|
333
|
-
let fsOpenFlags = 0x0;
|
|
334
|
-
if (openFlags.create) fsOpenFlags |= constants.O_CREAT;
|
|
335
|
-
if (openFlags.directory) fsOpenFlags |= constants.O_DIRECTORY;
|
|
336
|
-
if (openFlags.exclusive) fsOpenFlags |= constants.O_EXCL;
|
|
337
|
-
if (openFlags.truncate) fsOpenFlags |= constants.O_TRUNC;
|
|
338
|
-
if (descriptorFlags.read && descriptorFlags.write)
|
|
339
|
-
fsOpenFlags |= constants.O_RDWR;
|
|
340
|
-
else if (descriptorFlags.write) fsOpenFlags |= constants.O_WRONLY;
|
|
341
|
-
else if (descriptorFlags.read) fsOpenFlags |= constants.O_RDONLY;
|
|
342
|
-
if (descriptorFlags.fileIntegritySync) fsOpenFlags |= constants.O_SYNC;
|
|
343
|
-
if (descriptorFlags.dataIntegritySync) fsOpenFlags |= constants.O_DSYNC;
|
|
344
|
-
if (!pathFlags.symlinkFollow) fsOpenFlags |= constants.O_NOFOLLOW;
|
|
345
|
-
if (descriptorFlags.requestedWriteSync || descriptorFlags.mutateDirectory)
|
|
346
|
-
throw "unsupported";
|
|
347
|
-
// Currently throw to match Wasmtime
|
|
348
|
-
if (descriptorFlags.fileIntegritySync || descriptorFlags.dataIntegritySync)
|
|
349
|
-
throw "unsupported";
|
|
350
|
-
if (isWindows) {
|
|
351
|
-
if (!pathFlags.symlinkFollow && !openFlags.create) {
|
|
352
|
-
let isSymlink = false;
|
|
76
|
+
#hostPreopen;
|
|
77
|
+
#fd;
|
|
78
|
+
#finalizer;
|
|
79
|
+
#mode;
|
|
80
|
+
#fullPath;
|
|
81
|
+
|
|
82
|
+
static _createPreopen(hostPreopen) {
|
|
83
|
+
const descriptor = new Descriptor();
|
|
84
|
+
descriptor.#hostPreopen = hostPreopen.endsWith('/')
|
|
85
|
+
? hostPreopen.slice(0, -1) || '/'
|
|
86
|
+
: hostPreopen;
|
|
87
|
+
// Windows requires UNC paths at minimum
|
|
88
|
+
if (isWindows) {
|
|
89
|
+
descriptor.#hostPreopen = descriptor.#hostPreopen.replace(
|
|
90
|
+
/\\/g,
|
|
91
|
+
'/'
|
|
92
|
+
);
|
|
93
|
+
if (descriptor.#hostPreopen === '/') {
|
|
94
|
+
descriptor.#hostPreopen = '//';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return descriptor;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static _create(fd, mode, fullPath) {
|
|
101
|
+
const descriptor = new Descriptor();
|
|
102
|
+
descriptor.#fd = fd;
|
|
103
|
+
descriptor.#finalizer = registerDispose(
|
|
104
|
+
descriptor,
|
|
105
|
+
null,
|
|
106
|
+
fd,
|
|
107
|
+
closeSync
|
|
108
|
+
);
|
|
109
|
+
descriptor.#mode = mode;
|
|
110
|
+
descriptor.#fullPath = fullPath;
|
|
111
|
+
return descriptor;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
[symbolDispose]() {
|
|
115
|
+
if (this.#finalizer) {
|
|
116
|
+
earlyDispose(this.#finalizer);
|
|
117
|
+
this.#finalizer = null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
readViaStream(offset) {
|
|
122
|
+
if (this.#hostPreopen) {
|
|
123
|
+
throw 'is-directory';
|
|
124
|
+
}
|
|
125
|
+
return inputStreamCreate(
|
|
126
|
+
FILE,
|
|
127
|
+
ioCall(INPUT_STREAM_CREATE | FILE, null, {
|
|
128
|
+
fd: this.#fd,
|
|
129
|
+
offset,
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
writeViaStream(offset) {
|
|
135
|
+
if (this.#hostPreopen) {
|
|
136
|
+
throw 'is-directory';
|
|
137
|
+
}
|
|
138
|
+
return outputStreamCreate(
|
|
139
|
+
FILE,
|
|
140
|
+
ioCall(OUTPUT_STREAM_CREATE | FILE, null, { fd: this.#fd, offset })
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
appendViaStream() {
|
|
145
|
+
return this.writeViaStream(this.stat().size);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
advise(_offset, _length, _advice) {
|
|
149
|
+
if (this.getType() === 'directory') {
|
|
150
|
+
throw 'bad-descriptor';
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
syncData() {
|
|
155
|
+
if (this.#hostPreopen) {
|
|
156
|
+
throw 'invalid';
|
|
157
|
+
}
|
|
353
158
|
try {
|
|
354
|
-
|
|
355
|
-
} catch {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
159
|
+
fdatasyncSync(this.#fd);
|
|
160
|
+
} catch (e) {
|
|
161
|
+
if (e.code === 'EPERM') {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
throw convertFsError(e);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
getFlags() {
|
|
169
|
+
return this.#mode;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
getType() {
|
|
173
|
+
if (this.#hostPreopen) {
|
|
174
|
+
return 'directory';
|
|
175
|
+
}
|
|
176
|
+
const stats = fstatSync(this.#fd);
|
|
177
|
+
return lookupType(stats);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
setSize(size) {
|
|
181
|
+
if (this.#hostPreopen) {
|
|
182
|
+
throw 'is-directory';
|
|
183
|
+
}
|
|
362
184
|
try {
|
|
363
|
-
|
|
364
|
-
} catch {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
try {
|
|
393
|
-
return readlinkSync(fullPath);
|
|
394
|
-
} catch (e) {
|
|
395
|
-
throw convertFsError(e);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
removeDirectoryAt(path) {
|
|
400
|
-
const fullPath = this.#getFullPath(path, false);
|
|
401
|
-
try {
|
|
402
|
-
rmdirSync(fullPath);
|
|
403
|
-
} catch (e) {
|
|
404
|
-
if (isWindows && e.code === "ENOENT") throw "not-directory";
|
|
405
|
-
throw convertFsError(e);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
renameAt(oldPath, newDescriptor, newPath) {
|
|
410
|
-
const oldFullPath = this.#getFullPath(oldPath, false);
|
|
411
|
-
const newFullPath = newDescriptor.#getFullPath(newPath, false);
|
|
412
|
-
try {
|
|
413
|
-
renameSync(oldFullPath, newFullPath);
|
|
414
|
-
} catch (e) {
|
|
415
|
-
if (isWindows && e.code === "EPERM") throw "access";
|
|
416
|
-
throw convertFsError(e);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
symlinkAt(target, path) {
|
|
421
|
-
const fullPath = this.#getFullPath(path, false);
|
|
422
|
-
if (target.startsWith("/")) throw "not-permitted";
|
|
423
|
-
try {
|
|
424
|
-
symlinkSync(target, fullPath);
|
|
425
|
-
} catch (e) {
|
|
426
|
-
if (fullPath.endsWith("/") && e.code === "EEXIST") {
|
|
427
|
-
let isDir = false;
|
|
185
|
+
ftruncateSync(this.#fd, Number(size));
|
|
186
|
+
} catch (e) {
|
|
187
|
+
if (isWindows && e.code === 'EPERM') {
|
|
188
|
+
throw 'access';
|
|
189
|
+
}
|
|
190
|
+
throw convertFsError(e);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
setTimes(dataAccessTimestamp, dataModificationTimestamp) {
|
|
195
|
+
if (this.#hostPreopen) {
|
|
196
|
+
throw 'invalid';
|
|
197
|
+
}
|
|
198
|
+
let stats;
|
|
199
|
+
if (
|
|
200
|
+
dataAccessTimestamp.tag === 'no-change' ||
|
|
201
|
+
dataModificationTimestamp.tag === 'no-change'
|
|
202
|
+
) {
|
|
203
|
+
stats = this.stat();
|
|
204
|
+
}
|
|
205
|
+
const atime = this.#getNewTimestamp(
|
|
206
|
+
dataAccessTimestamp,
|
|
207
|
+
dataAccessTimestamp.tag === 'no-change' && stats.dataAccessTimestamp
|
|
208
|
+
);
|
|
209
|
+
const mtime = this.#getNewTimestamp(
|
|
210
|
+
dataModificationTimestamp,
|
|
211
|
+
dataModificationTimestamp.tag === 'no-change' &&
|
|
212
|
+
stats.dataModificationTimestamp
|
|
213
|
+
);
|
|
428
214
|
try {
|
|
429
|
-
|
|
430
|
-
} catch {
|
|
431
|
-
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
215
|
+
futimesSync(this.#fd, atime, mtime);
|
|
216
|
+
} catch (e) {
|
|
217
|
+
throw convertFsError(e);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
#getNewTimestamp(newTimestamp, maybeNow) {
|
|
222
|
+
switch (newTimestamp.tag) {
|
|
223
|
+
case 'no-change':
|
|
224
|
+
return timestampToMs(maybeNow);
|
|
225
|
+
case 'now':
|
|
226
|
+
return Math.floor(Date.now() / 1e3);
|
|
227
|
+
case 'timestamp':
|
|
228
|
+
return timestampToMs(newTimestamp.val);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
read(length, offset) {
|
|
233
|
+
if (!this.#fullPath) {
|
|
234
|
+
throw 'bad-descriptor';
|
|
235
|
+
}
|
|
236
|
+
const buf = new Uint8Array(Number(length));
|
|
237
|
+
const bytesRead = readSync(
|
|
238
|
+
this.#fd,
|
|
239
|
+
buf,
|
|
240
|
+
0,
|
|
241
|
+
Number(length),
|
|
242
|
+
Number(offset)
|
|
243
|
+
);
|
|
244
|
+
const out = new Uint8Array(buf.buffer, 0, bytesRead);
|
|
245
|
+
return [out, bytesRead === 0 ? 'ended' : 'open'];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
write(buffer, offset) {
|
|
249
|
+
if (!this.#fullPath) {
|
|
250
|
+
throw 'bad-descriptor';
|
|
251
|
+
}
|
|
252
|
+
return BigInt(
|
|
253
|
+
writeSync(this.#fd, buffer, 0, buffer.byteLength, Number(offset))
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
readDirectory() {
|
|
258
|
+
if (!this.#fullPath) {
|
|
259
|
+
throw 'bad-descriptor';
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
const dir = opendirSync(this.#fullPath);
|
|
263
|
+
return directoryEntryStreamCreate(dir);
|
|
264
|
+
} catch (e) {
|
|
265
|
+
throw convertFsError(e);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
sync() {
|
|
270
|
+
if (this.#hostPreopen) {
|
|
271
|
+
throw 'invalid';
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
fsyncSync(this.#fd);
|
|
275
|
+
} catch (e) {
|
|
276
|
+
if (e.code === 'EPERM') {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
throw convertFsError(e);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
createDirectoryAt(path) {
|
|
284
|
+
const fullPath = this.#getFullPath(path);
|
|
285
|
+
try {
|
|
286
|
+
mkdirSync(fullPath);
|
|
287
|
+
} catch (e) {
|
|
288
|
+
throw convertFsError(e);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
stat() {
|
|
293
|
+
if (this.#hostPreopen) {
|
|
294
|
+
throw 'invalid';
|
|
295
|
+
}
|
|
296
|
+
let stats;
|
|
297
|
+
try {
|
|
298
|
+
stats = fstatSync(this.#fd, { bigint: true });
|
|
299
|
+
} catch (e) {
|
|
300
|
+
throw convertFsError(e);
|
|
301
|
+
}
|
|
302
|
+
const type = lookupType(stats);
|
|
303
|
+
return {
|
|
304
|
+
type,
|
|
305
|
+
linkCount: stats.nlink,
|
|
306
|
+
size: stats.size,
|
|
307
|
+
dataAccessTimestamp: nsToDateTime(stats.atimeNs),
|
|
308
|
+
dataModificationTimestamp: nsToDateTime(stats.mtimeNs),
|
|
309
|
+
statusChangeTimestamp: nsToDateTime(stats.ctimeNs),
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
statAt(pathFlags, path) {
|
|
314
|
+
const fullPath = this.#getFullPath(path, false);
|
|
315
|
+
let stats;
|
|
447
316
|
try {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
metadataHashAt(pathFlags, path) {
|
|
476
|
-
const fullPath = this.#getFullPath(path, false);
|
|
477
|
-
try {
|
|
478
|
-
const stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(fullPath, {
|
|
479
|
-
bigint: true,
|
|
480
|
-
});
|
|
481
|
-
return { upper: stats.mtimeNs, lower: stats.ino };
|
|
482
|
-
} catch (e) {
|
|
483
|
-
throw convertFsError(e);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// TODO: support followSymlinks
|
|
488
|
-
#getFullPath(subpath, _followSymlinks) {
|
|
489
|
-
let descriptor = this;
|
|
490
|
-
if (subpath.indexOf("\\") !== -1) subpath = subpath.replace(/\\/g, "/");
|
|
491
|
-
if (subpath.indexOf("//") !== -1) subpath = subpath.replace(/\/\/+/g, "/");
|
|
492
|
-
if (subpath[0] === "/") throw "not-permitted";
|
|
493
|
-
|
|
494
|
-
// segment resolution
|
|
495
|
-
const segments = [];
|
|
496
|
-
let segmentIndex = -1;
|
|
497
|
-
for (let i = 0; i < subpath.length; i++) {
|
|
498
|
-
// busy reading a segment - only terminate on '/'
|
|
499
|
-
if (segmentIndex !== -1) {
|
|
500
|
-
if (subpath[i] === "/") {
|
|
501
|
-
segments.push(subpath.slice(segmentIndex, i + 1));
|
|
502
|
-
segmentIndex = -1;
|
|
503
|
-
}
|
|
504
|
-
continue;
|
|
505
|
-
}
|
|
506
|
-
// new segment - check if it is relative
|
|
507
|
-
else if (subpath[i] === ".") {
|
|
508
|
-
// ../ segment
|
|
317
|
+
stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(fullPath, {
|
|
318
|
+
bigint: true,
|
|
319
|
+
});
|
|
320
|
+
} catch (e) {
|
|
321
|
+
throw convertFsError(e);
|
|
322
|
+
}
|
|
323
|
+
const type = lookupType(stats);
|
|
324
|
+
return {
|
|
325
|
+
type,
|
|
326
|
+
linkCount: stats.nlink,
|
|
327
|
+
size: stats.size,
|
|
328
|
+
dataAccessTimestamp: nsToDateTime(stats.atimeNs),
|
|
329
|
+
dataModificationTimestamp: nsToDateTime(stats.mtimeNs),
|
|
330
|
+
statusChangeTimestamp: nsToDateTime(stats.ctimeNs),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
setTimesAt(
|
|
335
|
+
pathFlags,
|
|
336
|
+
path,
|
|
337
|
+
dataAccessTimestamp,
|
|
338
|
+
dataModificationTimestamp
|
|
339
|
+
) {
|
|
340
|
+
const fullPath = this.#getFullPath(path, false);
|
|
341
|
+
let stats;
|
|
509
342
|
if (
|
|
510
|
-
|
|
511
|
-
|
|
343
|
+
dataAccessTimestamp.tag === 'no-change' ||
|
|
344
|
+
dataModificationTimestamp.tag === 'no-change'
|
|
512
345
|
) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
(
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
346
|
+
stats = this.stat();
|
|
347
|
+
}
|
|
348
|
+
const atime = this.#getNewTimestamp(
|
|
349
|
+
dataAccessTimestamp,
|
|
350
|
+
dataAccessTimestamp.tag === 'no-change' && stats.dataAccessTimestamp
|
|
351
|
+
);
|
|
352
|
+
const mtime = this.#getNewTimestamp(
|
|
353
|
+
dataModificationTimestamp,
|
|
354
|
+
dataModificationTimestamp.tag === 'no-change' &&
|
|
355
|
+
stats.dataModificationTimestamp
|
|
356
|
+
);
|
|
357
|
+
if (!pathFlags.symlinkFollow && !lutimesSync) {
|
|
358
|
+
throw new Error(
|
|
359
|
+
"Changing the timestamps of symlinks isn't supported"
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
try {
|
|
363
|
+
(pathFlags.symlinkFollow ? utimesSync : lutimesSync)(
|
|
364
|
+
fullPath,
|
|
365
|
+
atime,
|
|
366
|
+
mtime
|
|
367
|
+
);
|
|
368
|
+
} catch (e) {
|
|
369
|
+
throw convertFsError(e);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
linkAt(oldPathFlags, oldPath, newDescriptor, newPath) {
|
|
374
|
+
const oldFullPath = this.#getFullPath(
|
|
375
|
+
oldPath,
|
|
376
|
+
oldPathFlags.symlinkFollow
|
|
377
|
+
);
|
|
378
|
+
const newFullPath = newDescriptor.#getFullPath(newPath, false);
|
|
379
|
+
// Windows doesn't automatically fail on trailing slashes
|
|
380
|
+
if (isWindows && newFullPath.endsWith('/')) {
|
|
381
|
+
throw 'no-entry';
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
linkSync(oldFullPath, newFullPath);
|
|
385
|
+
} catch (e) {
|
|
386
|
+
throw convertFsError(e);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
openAt(pathFlags, path, openFlags, descriptorFlags) {
|
|
391
|
+
if (preopenEntries.length === 0) {
|
|
392
|
+
throw 'access';
|
|
393
|
+
}
|
|
394
|
+
const fullPath = this.#getFullPath(path, pathFlags.symlinkFollow);
|
|
395
|
+
let fsOpenFlags = 0x0;
|
|
396
|
+
if (openFlags.create) {
|
|
397
|
+
fsOpenFlags |= constants.O_CREAT;
|
|
398
|
+
}
|
|
399
|
+
if (openFlags.directory) {
|
|
400
|
+
fsOpenFlags |= constants.O_DIRECTORY;
|
|
401
|
+
}
|
|
402
|
+
if (openFlags.exclusive) {
|
|
403
|
+
fsOpenFlags |= constants.O_EXCL;
|
|
404
|
+
}
|
|
405
|
+
if (openFlags.truncate) {
|
|
406
|
+
fsOpenFlags |= constants.O_TRUNC;
|
|
407
|
+
}
|
|
408
|
+
if (descriptorFlags.read && descriptorFlags.write) {
|
|
409
|
+
fsOpenFlags |= constants.O_RDWR;
|
|
410
|
+
} else if (descriptorFlags.write) {
|
|
411
|
+
fsOpenFlags |= constants.O_WRONLY;
|
|
412
|
+
} else if (descriptorFlags.read) {
|
|
413
|
+
fsOpenFlags |= constants.O_RDONLY;
|
|
414
|
+
}
|
|
415
|
+
if (descriptorFlags.fileIntegritySync) {
|
|
416
|
+
fsOpenFlags |= constants.O_SYNC;
|
|
417
|
+
}
|
|
418
|
+
if (descriptorFlags.dataIntegritySync) {
|
|
419
|
+
fsOpenFlags |= constants.O_DSYNC;
|
|
420
|
+
}
|
|
421
|
+
if (!pathFlags.symlinkFollow) {
|
|
422
|
+
fsOpenFlags |= constants.O_NOFOLLOW;
|
|
423
|
+
}
|
|
424
|
+
if (
|
|
425
|
+
descriptorFlags.requestedWriteSync ||
|
|
426
|
+
descriptorFlags.mutateDirectory
|
|
427
|
+
) {
|
|
428
|
+
throw 'unsupported';
|
|
429
|
+
}
|
|
430
|
+
// Currently throw to match Wasmtime
|
|
431
|
+
if (
|
|
432
|
+
descriptorFlags.fileIntegritySync ||
|
|
433
|
+
descriptorFlags.dataIntegritySync
|
|
434
|
+
) {
|
|
435
|
+
throw 'unsupported';
|
|
436
|
+
}
|
|
437
|
+
if (isWindows) {
|
|
438
|
+
if (!pathFlags.symlinkFollow && !openFlags.create) {
|
|
439
|
+
let isSymlink = false;
|
|
440
|
+
try {
|
|
441
|
+
isSymlink = lstatSync(fullPath).isSymbolicLink();
|
|
442
|
+
} catch {
|
|
443
|
+
//
|
|
444
|
+
}
|
|
445
|
+
if (isSymlink) {
|
|
446
|
+
throw openFlags.directory ? 'not-directory' : 'loop';
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (pathFlags.symlinkFollow && openFlags.directory) {
|
|
450
|
+
let isFile = false;
|
|
451
|
+
try {
|
|
452
|
+
isFile = !statSync(fullPath).isDirectory();
|
|
453
|
+
} catch {
|
|
454
|
+
//
|
|
455
|
+
}
|
|
456
|
+
if (isFile) {
|
|
457
|
+
throw 'not-directory';
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
try {
|
|
462
|
+
const fd = openSync(
|
|
463
|
+
fullPath.endsWith('/') ? fullPath.slice(0, -1) : fullPath,
|
|
464
|
+
fsOpenFlags
|
|
465
|
+
);
|
|
466
|
+
const descriptor = descriptorCreate(
|
|
467
|
+
fd,
|
|
468
|
+
descriptorFlags,
|
|
469
|
+
fullPath,
|
|
470
|
+
preopenEntries
|
|
471
|
+
);
|
|
472
|
+
if (
|
|
473
|
+
fullPath.endsWith('/') &&
|
|
474
|
+
descriptor.getType() !== 'directory'
|
|
475
|
+
) {
|
|
476
|
+
descriptor[symbolDispose]();
|
|
477
|
+
throw 'not-directory';
|
|
478
|
+
}
|
|
479
|
+
return descriptor;
|
|
480
|
+
} catch (e) {
|
|
481
|
+
if (e.code === 'ERR_INVALID_ARG_VALUE') {
|
|
482
|
+
throw isWindows ? 'no-entry' : 'invalid';
|
|
483
|
+
}
|
|
484
|
+
throw convertFsError(e);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
readlinkAt(path) {
|
|
489
|
+
const fullPath = this.#getFullPath(path, false);
|
|
490
|
+
try {
|
|
491
|
+
return readlinkSync(fullPath);
|
|
492
|
+
} catch (e) {
|
|
493
|
+
throw convertFsError(e);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
removeDirectoryAt(path) {
|
|
498
|
+
const fullPath = this.#getFullPath(path, false);
|
|
499
|
+
try {
|
|
500
|
+
rmdirSync(fullPath);
|
|
501
|
+
} catch (e) {
|
|
502
|
+
if (isWindows && e.code === 'ENOENT') {
|
|
503
|
+
throw 'not-directory';
|
|
504
|
+
}
|
|
505
|
+
throw convertFsError(e);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
renameAt(oldPath, newDescriptor, newPath) {
|
|
510
|
+
const oldFullPath = this.#getFullPath(oldPath, false);
|
|
511
|
+
const newFullPath = newDescriptor.#getFullPath(newPath, false);
|
|
512
|
+
try {
|
|
513
|
+
renameSync(oldFullPath, newFullPath);
|
|
514
|
+
} catch (e) {
|
|
515
|
+
if (isWindows && e.code === 'EPERM') {
|
|
516
|
+
throw 'access';
|
|
517
|
+
}
|
|
518
|
+
throw convertFsError(e);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
symlinkAt(target, path) {
|
|
523
|
+
const fullPath = this.#getFullPath(path, false);
|
|
524
|
+
if (target.startsWith('/')) {
|
|
525
|
+
throw 'not-permitted';
|
|
526
|
+
}
|
|
527
|
+
try {
|
|
528
|
+
symlinkSync(target, fullPath);
|
|
529
|
+
} catch (e) {
|
|
530
|
+
if (fullPath.endsWith('/') && e.code === 'EEXIST') {
|
|
531
|
+
let isDir = false;
|
|
532
|
+
try {
|
|
533
|
+
isDir = statSync(fullPath).isDirectory();
|
|
534
|
+
} catch {
|
|
535
|
+
//
|
|
536
|
+
}
|
|
537
|
+
if (!isDir) {
|
|
538
|
+
throw isWindows ? 'no-entry' : 'not-directory';
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (isWindows) {
|
|
542
|
+
if (e.code === 'EPERM' || e.code === 'EEXIST') {
|
|
543
|
+
throw 'no-entry';
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
throw convertFsError(e);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
unlinkFileAt(path) {
|
|
551
|
+
const fullPath = this.#getFullPath(path, false);
|
|
552
|
+
try {
|
|
553
|
+
if (fullPath.endsWith('/')) {
|
|
554
|
+
let isDir = false;
|
|
555
|
+
try {
|
|
556
|
+
isDir = statSync(fullPath).isDirectory();
|
|
557
|
+
} catch {
|
|
558
|
+
//
|
|
559
|
+
}
|
|
560
|
+
throw isDir
|
|
561
|
+
? isWindows
|
|
562
|
+
? 'access'
|
|
563
|
+
: isMac
|
|
564
|
+
? 'not-permitted'
|
|
565
|
+
: 'is-directory'
|
|
566
|
+
: 'not-directory';
|
|
567
|
+
}
|
|
568
|
+
unlinkSync(fullPath);
|
|
569
|
+
} catch (e) {
|
|
570
|
+
if (isWindows && e.code === 'EPERM') {
|
|
571
|
+
throw 'access';
|
|
572
|
+
}
|
|
573
|
+
throw convertFsError(e);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
isSameObject(other) {
|
|
578
|
+
return other === this;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
metadataHash() {
|
|
582
|
+
if (this.#hostPreopen) {
|
|
583
|
+
return { upper: 0n, lower: BigInt(this._id) };
|
|
584
|
+
}
|
|
585
|
+
try {
|
|
586
|
+
const stats = fstatSync(this.#fd, { bigint: true });
|
|
587
|
+
return { upper: stats.mtimeNs, lower: stats.ino };
|
|
588
|
+
} catch (e) {
|
|
589
|
+
throw convertFsError(e);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
metadataHashAt(pathFlags, path) {
|
|
594
|
+
const fullPath = this.#getFullPath(path, false);
|
|
595
|
+
try {
|
|
596
|
+
const stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(
|
|
597
|
+
fullPath,
|
|
598
|
+
{
|
|
599
|
+
bigint: true,
|
|
600
|
+
}
|
|
601
|
+
);
|
|
602
|
+
return { upper: stats.mtimeNs, lower: stats.ino };
|
|
603
|
+
} catch (e) {
|
|
604
|
+
throw convertFsError(e);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// TODO: support followSymlinks
|
|
609
|
+
#getFullPath(subpath, _followSymlinks) {
|
|
610
|
+
let descriptor = this;
|
|
611
|
+
if (subpath.indexOf('\\') !== -1) {
|
|
612
|
+
subpath = subpath.replace(/\\/g, '/');
|
|
613
|
+
}
|
|
614
|
+
if (subpath.indexOf('//') !== -1) {
|
|
615
|
+
subpath = subpath.replace(/\/\/+/g, '/');
|
|
616
|
+
}
|
|
617
|
+
if (subpath[0] === '/') {
|
|
618
|
+
throw 'not-permitted';
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// segment resolution
|
|
622
|
+
const segments = [];
|
|
623
|
+
let segmentIndex = -1;
|
|
624
|
+
for (let i = 0; i < subpath.length; i++) {
|
|
625
|
+
// busy reading a segment - only terminate on '/'
|
|
626
|
+
if (segmentIndex !== -1) {
|
|
627
|
+
if (subpath[i] === '/') {
|
|
628
|
+
segments.push(subpath.slice(segmentIndex, i + 1));
|
|
629
|
+
segmentIndex = -1;
|
|
630
|
+
}
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
// new segment - check if it is relative
|
|
634
|
+
else if (subpath[i] === '.') {
|
|
635
|
+
// ../ segment
|
|
636
|
+
if (
|
|
637
|
+
subpath[i + 1] === '.' &&
|
|
638
|
+
(subpath[i + 2] === '/' || i + 2 === subpath.length)
|
|
639
|
+
) {
|
|
640
|
+
if (segments.pop() === undefined) {
|
|
641
|
+
throw 'not-permitted';
|
|
642
|
+
}
|
|
643
|
+
i += 2;
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
// ./ segment
|
|
647
|
+
else if (subpath[i + 1] === '/' || i + 1 === subpath.length) {
|
|
648
|
+
i += 1;
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
// it is the start of a new segment
|
|
653
|
+
while (subpath[i] === '/') {
|
|
654
|
+
i++;
|
|
655
|
+
}
|
|
656
|
+
segmentIndex = i;
|
|
657
|
+
}
|
|
658
|
+
// finish reading out the last segment
|
|
659
|
+
if (segmentIndex !== -1) {
|
|
660
|
+
segments.push(subpath.slice(segmentIndex));
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
subpath = segments.join('');
|
|
664
|
+
|
|
665
|
+
if (descriptor.#hostPreopen) {
|
|
666
|
+
return (
|
|
667
|
+
descriptor.#hostPreopen +
|
|
668
|
+
(descriptor.#hostPreopen.endsWith('/')
|
|
669
|
+
? ''
|
|
670
|
+
: subpath.length > 0
|
|
671
|
+
? '/'
|
|
672
|
+
: '') +
|
|
673
|
+
subpath
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
return descriptor.#fullPath + (subpath.length > 0 ? '/' : '') + subpath;
|
|
677
|
+
}
|
|
544
678
|
}
|
|
545
679
|
const descriptorCreatePreopen = Descriptor._createPreopen;
|
|
546
680
|
delete Descriptor._createPreopen;
|
|
@@ -548,39 +682,39 @@ const descriptorCreate = Descriptor._create;
|
|
|
548
682
|
delete Descriptor._create;
|
|
549
683
|
|
|
550
684
|
class DirectoryEntryStream {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
685
|
+
#dir;
|
|
686
|
+
#finalizer;
|
|
687
|
+
readDirectoryEntry() {
|
|
688
|
+
let entry;
|
|
689
|
+
try {
|
|
690
|
+
entry = this.#dir.readSync();
|
|
691
|
+
} catch (e) {
|
|
692
|
+
throw convertFsError(e);
|
|
693
|
+
}
|
|
694
|
+
if (entry === null) {
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
const name = entry.name;
|
|
698
|
+
const type = lookupType(entry);
|
|
699
|
+
return { name, type };
|
|
700
|
+
}
|
|
701
|
+
static _create(dir) {
|
|
702
|
+
const dirStream = new DirectoryEntryStream();
|
|
703
|
+
dirStream.#finalizer = registerDispose(
|
|
704
|
+
dirStream,
|
|
705
|
+
null,
|
|
706
|
+
null,
|
|
707
|
+
dir.closeSync.bind(dir)
|
|
708
|
+
);
|
|
709
|
+
dirStream.#dir = dir;
|
|
710
|
+
return dirStream;
|
|
711
|
+
}
|
|
712
|
+
[symbolDispose]() {
|
|
713
|
+
if (this.#finalizer) {
|
|
714
|
+
earlyDispose(this.#finalizer);
|
|
715
|
+
this.#finalizer = null;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
584
718
|
}
|
|
585
719
|
const directoryEntryStreamCreate = DirectoryEntryStream._create;
|
|
586
720
|
delete DirectoryEntryStream._create;
|
|
@@ -588,128 +722,128 @@ delete DirectoryEntryStream._create;
|
|
|
588
722
|
let preopenEntries = [];
|
|
589
723
|
|
|
590
724
|
export const preopens = {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
725
|
+
Descriptor,
|
|
726
|
+
getDirectories() {
|
|
727
|
+
return preopenEntries;
|
|
728
|
+
},
|
|
595
729
|
};
|
|
596
730
|
|
|
597
|
-
_addPreopen(
|
|
731
|
+
_addPreopen('/', isWindows ? '//' : '/');
|
|
598
732
|
|
|
599
733
|
export const types = {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
734
|
+
Descriptor,
|
|
735
|
+
DirectoryEntryStream,
|
|
736
|
+
filesystemErrorCode(err) {
|
|
737
|
+
return convertFsError(err.payload);
|
|
738
|
+
},
|
|
605
739
|
};
|
|
606
740
|
|
|
607
741
|
export function _setPreopens(preopens) {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
742
|
+
preopenEntries = [];
|
|
743
|
+
for (const [virtualPath, hostPreopen] of Object.entries(preopens)) {
|
|
744
|
+
_addPreopen(virtualPath, hostPreopen);
|
|
745
|
+
}
|
|
612
746
|
}
|
|
613
747
|
|
|
614
748
|
export function _addPreopen(virtualPath, hostPreopen) {
|
|
615
|
-
|
|
616
|
-
|
|
749
|
+
const preopenEntry = [descriptorCreatePreopen(hostPreopen), virtualPath];
|
|
750
|
+
preopenEntries.push(preopenEntry);
|
|
617
751
|
}
|
|
618
752
|
|
|
619
753
|
function convertFsError(e) {
|
|
620
|
-
|
|
621
|
-
case
|
|
622
|
-
|
|
623
|
-
case
|
|
624
|
-
case
|
|
625
|
-
|
|
626
|
-
case
|
|
627
|
-
|
|
628
|
-
case
|
|
629
|
-
|
|
630
|
-
case
|
|
631
|
-
|
|
632
|
-
case
|
|
633
|
-
|
|
634
|
-
case
|
|
635
|
-
|
|
636
|
-
case
|
|
637
|
-
|
|
638
|
-
case
|
|
639
|
-
|
|
640
|
-
case
|
|
641
|
-
|
|
642
|
-
case
|
|
643
|
-
|
|
644
|
-
case
|
|
645
|
-
|
|
646
|
-
case
|
|
647
|
-
|
|
648
|
-
case
|
|
649
|
-
|
|
650
|
-
case
|
|
651
|
-
|
|
652
|
-
case
|
|
653
|
-
|
|
654
|
-
case
|
|
655
|
-
|
|
656
|
-
case
|
|
657
|
-
|
|
658
|
-
case
|
|
659
|
-
|
|
660
|
-
case
|
|
661
|
-
|
|
662
|
-
case
|
|
663
|
-
|
|
664
|
-
case
|
|
665
|
-
|
|
666
|
-
case
|
|
667
|
-
|
|
668
|
-
case
|
|
669
|
-
|
|
670
|
-
case
|
|
754
|
+
switch (e.code) {
|
|
755
|
+
case 'EACCES':
|
|
756
|
+
return 'access';
|
|
757
|
+
case 'EAGAIN':
|
|
758
|
+
case 'EWOULDBLOCK':
|
|
759
|
+
return 'would-block';
|
|
760
|
+
case 'EALREADY':
|
|
761
|
+
return 'already';
|
|
762
|
+
case 'EBADF':
|
|
763
|
+
return 'bad-descriptor';
|
|
764
|
+
case 'EBUSY':
|
|
765
|
+
return 'busy';
|
|
766
|
+
case 'EDEADLK':
|
|
767
|
+
return 'deadlock';
|
|
768
|
+
case 'EDQUOT':
|
|
769
|
+
return 'quota';
|
|
770
|
+
case 'EEXIST':
|
|
771
|
+
return 'exist';
|
|
772
|
+
case 'EFBIG':
|
|
773
|
+
return 'file-too-large';
|
|
774
|
+
case 'EILSEQ':
|
|
775
|
+
return 'illegal-byte-sequence';
|
|
776
|
+
case 'EINPROGRESS':
|
|
777
|
+
return 'in-progress';
|
|
778
|
+
case 'EINTR':
|
|
779
|
+
return 'interrupted';
|
|
780
|
+
case 'EINVAL':
|
|
781
|
+
return 'invalid';
|
|
782
|
+
case 'EIO':
|
|
783
|
+
return 'io';
|
|
784
|
+
case 'EISDIR':
|
|
785
|
+
return 'is-directory';
|
|
786
|
+
case 'ELOOP':
|
|
787
|
+
return 'loop';
|
|
788
|
+
case 'EMLINK':
|
|
789
|
+
return 'too-many-links';
|
|
790
|
+
case 'EMSGSIZE':
|
|
791
|
+
return 'message-size';
|
|
792
|
+
case 'ENAMETOOLONG':
|
|
793
|
+
return 'name-too-long';
|
|
794
|
+
case 'ENODEV':
|
|
795
|
+
return 'no-device';
|
|
796
|
+
case 'ENOENT':
|
|
797
|
+
return 'no-entry';
|
|
798
|
+
case 'ENOLCK':
|
|
799
|
+
return 'no-lock';
|
|
800
|
+
case 'ENOMEM':
|
|
801
|
+
return 'insufficient-memory';
|
|
802
|
+
case 'ENOSPC':
|
|
803
|
+
return 'insufficient-space';
|
|
804
|
+
case 'ENOTDIR':
|
|
671
805
|
case 'ERR_FS_EISDIR':
|
|
672
|
-
|
|
673
|
-
case
|
|
674
|
-
|
|
675
|
-
case
|
|
676
|
-
|
|
677
|
-
case
|
|
678
|
-
|
|
679
|
-
case
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
806
|
+
return 'not-directory';
|
|
807
|
+
case 'ENOTEMPTY':
|
|
808
|
+
return 'not-empty';
|
|
809
|
+
case 'ENOTRECOVERABLE':
|
|
810
|
+
return 'not-recoverable';
|
|
811
|
+
case 'ENOTSUP':
|
|
812
|
+
return 'unsupported';
|
|
813
|
+
case 'ENOTTY':
|
|
814
|
+
return 'no-tty';
|
|
815
|
+
// windows gives this error for badly structured `//` reads
|
|
816
|
+
// this seems like a slightly better error than unknown given
|
|
817
|
+
// that it's a common footgun
|
|
684
818
|
case -4094:
|
|
685
|
-
case
|
|
686
|
-
|
|
687
|
-
case
|
|
688
|
-
|
|
689
|
-
case
|
|
690
|
-
|
|
691
|
-
case
|
|
692
|
-
|
|
693
|
-
case
|
|
694
|
-
|
|
695
|
-
case
|
|
696
|
-
|
|
697
|
-
case
|
|
698
|
-
|
|
699
|
-
case
|
|
700
|
-
|
|
701
|
-
case
|
|
702
|
-
|
|
819
|
+
case 'ENXIO':
|
|
820
|
+
return 'no-such-device';
|
|
821
|
+
case 'EOVERFLOW':
|
|
822
|
+
return 'overflow';
|
|
823
|
+
case 'EPERM':
|
|
824
|
+
return 'not-permitted';
|
|
825
|
+
case 'EPIPE':
|
|
826
|
+
return 'pipe';
|
|
827
|
+
case 'EROFS':
|
|
828
|
+
return 'read-only';
|
|
829
|
+
case 'ESPIPE':
|
|
830
|
+
return 'invalid-seek';
|
|
831
|
+
case 'ETXTBSY':
|
|
832
|
+
return 'text-file-busy';
|
|
833
|
+
case 'EXDEV':
|
|
834
|
+
return 'cross-device';
|
|
835
|
+
case 'UNKNOWN':
|
|
836
|
+
switch (e.errno) {
|
|
703
837
|
case -4094:
|
|
704
|
-
|
|
838
|
+
return 'no-such-device';
|
|
705
839
|
default:
|
|
706
|
-
|
|
707
|
-
|
|
840
|
+
throw e;
|
|
841
|
+
}
|
|
708
842
|
default:
|
|
709
|
-
|
|
710
|
-
|
|
843
|
+
throw e;
|
|
844
|
+
}
|
|
711
845
|
}
|
|
712
846
|
|
|
713
847
|
function timestampToMs(timestamp) {
|
|
714
|
-
|
|
848
|
+
return Number(timestamp.seconds) * 1000 + timestamp.nanoseconds / 1e9;
|
|
715
849
|
}
|