@volter/browser-wali 0.1.15 → 0.1.17
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/dist/filesystem-host.d.ts.map +1 -1
- package/dist/filesystem-host.js +4 -2
- package/dist/filesystem-host.js.map +1 -1
- package/dist/filesystem-remote.d.ts +13 -1
- package/dist/filesystem-remote.d.ts.map +1 -1
- package/dist/filesystem-remote.js +72 -6
- package/dist/filesystem-remote.js.map +1 -1
- package/dist/filesystem.d.ts +27 -0
- package/dist/filesystem.d.ts.map +1 -1
- package/dist/filesystem.js +247 -7
- package/dist/filesystem.js.map +1 -1
- package/dist/network-worker.js +18 -11
- package/dist/network-worker.js.map +1 -1
- package/dist/network.d.ts +1 -0
- package/dist/network.d.ts.map +1 -1
- package/dist/network.js +4 -2
- package/dist/network.js.map +1 -1
- package/dist/private-tree.d.ts +63 -0
- package/dist/private-tree.d.ts.map +1 -0
- package/dist/private-tree.js +134 -0
- package/dist/private-tree.js.map +1 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +38 -9
- package/dist/runtime.js.map +1 -1
- package/dist/signals.d.ts +31 -2
- package/dist/signals.d.ts.map +1 -1
- package/dist/signals.js +35 -2
- package/dist/signals.js.map +1 -1
- package/dist/snapshot-filesystem.d.ts +9 -0
- package/dist/snapshot-filesystem.d.ts.map +1 -1
- package/dist/snapshot-filesystem.js +20 -0
- package/dist/snapshot-filesystem.js.map +1 -1
- package/dist/symlink.d.ts.map +1 -1
- package/dist/symlink.js +13 -3
- package/dist/symlink.js.map +1 -1
- package/dist/thread-worker.js +17 -5
- package/dist/thread-worker.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/filesystem.js
CHANGED
|
@@ -3,6 +3,7 @@ import { WaliDescriptionRegistry, WaliProcessDescriptorTable } from "./descripto
|
|
|
3
3
|
import { createProcessFilesystemMutationGuard } from "@volter/browser-runtime/browser-process-filesystem-policy.js";
|
|
4
4
|
import { BufferedWaliStdin } from "./stdin.js";
|
|
5
5
|
import { readWaliSymlink, resolveWaliSymlinkPath, writeWaliSymlink } from "./symlink.js";
|
|
6
|
+
import { PR_WALI, PR_WALI_TREE_ADOPT, PR_WALI_TREE_CONFIRM, PR_WALI_TREE_DECLINE, PR_WALI_TREE_RELEASE, PRIVATE_TREE_INODES, PRIVATE_TREE_WINDOW_BASE, PRIVATE_TREE_WINDOW_SIZE, decodePrivateTree, encodePrivateTree, inPrivateTreeWindow, } from "./private-tree.js";
|
|
6
7
|
let reportedDroppedEnvironment = false;
|
|
7
8
|
const AT_FDCWD = -100;
|
|
8
9
|
const O_ACCMODE = 3;
|
|
@@ -58,6 +59,9 @@ export class WaliFileDescriptorTable {
|
|
|
58
59
|
cwd;
|
|
59
60
|
guestFilesystem;
|
|
60
61
|
assertWritable;
|
|
62
|
+
/** The private root the program's libc holds, while it holds it (ADR-0047). */
|
|
63
|
+
privateTree;
|
|
64
|
+
privateTreeDeclined = false;
|
|
61
65
|
constructor(options) {
|
|
62
66
|
this.options = options;
|
|
63
67
|
this.descriptors = options.descriptors ?? new WaliProcessDescriptorTable(new WaliDescriptionRegistry());
|
|
@@ -93,6 +97,8 @@ export class WaliFileDescriptorTable {
|
|
|
93
97
|
renameSync: (from, to) => fs.renameSync(this.assertWritable(from), this.assertWritable(to)),
|
|
94
98
|
...(fs.chmodSync ? { chmodSync: (path, mode) => fs.chmodSync(this.assertWritable(path), mode) } : {}),
|
|
95
99
|
...(fs.utimesSync ? { utimesSync: (path, atime, mtime) => fs.utimesSync(this.assertWritable(path), atime, mtime) } : {}),
|
|
100
|
+
...(fs.reserveInodes ? { reserveInodes: fs.reserveInodes.bind(fs) } : {}),
|
|
101
|
+
...(fs.restoreNodeSync ? { restoreNodeSync: (path, node) => fs.restoreNodeSync(this.assertWritable(path), node) } : {}),
|
|
96
102
|
} : fs;
|
|
97
103
|
this.stdin = "read" in options.stdin ? options.stdin : new BufferedWaliStdin(options.stdin);
|
|
98
104
|
if (!options.descriptors) {
|
|
@@ -166,6 +172,9 @@ export class WaliFileDescriptorTable {
|
|
|
166
172
|
SYS_fsync: (fd) => this.hasFd(Number(fd)) ? 0n : -9n,
|
|
167
173
|
SYS_fdatasync: (fd) => this.hasFd(Number(fd)) ? 0n : -9n,
|
|
168
174
|
SYS_flock: (fd, operation) => this.flock(Number(fd), Number(operation)),
|
|
175
|
+
// ADR-0047's handshake; every other prctl answers 0, as before.
|
|
176
|
+
SYS_prctl: (option, operation, pointer, length) => Number(option) === PR_WALI
|
|
177
|
+
? this.privateTreeCall(Number(operation), Number(pointer), Number(length)) : 0n,
|
|
169
178
|
};
|
|
170
179
|
}
|
|
171
180
|
writeEnvironmentFile(env, pid) {
|
|
@@ -462,6 +471,11 @@ export class WaliFileDescriptorTable {
|
|
|
462
471
|
allocateFd(minimum = 3) {
|
|
463
472
|
const maximum = this.options.descriptorMaximum ?? 65_535;
|
|
464
473
|
for (let fd = Math.max(minimum, 0); fd <= maximum; fd += 1) {
|
|
474
|
+
// A private tree's numbers are its libc's, held or given back (ADR-0047).
|
|
475
|
+
if (inPrivateTreeWindow(fd)) {
|
|
476
|
+
fd = PRIVATE_TREE_WINDOW_BASE + PRIVATE_TREE_WINDOW_SIZE - 1;
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
465
479
|
// `stdioFd` covers 0-2, this table's own aliases and the shared registry.
|
|
466
480
|
if (this.descriptors.get(fd) || this.files.has(fd) || this.specialDescriptors.has(fd)
|
|
467
481
|
|| this.stdioFd(fd) !== undefined)
|
|
@@ -695,7 +709,8 @@ export class WaliFileDescriptorTable {
|
|
|
695
709
|
throw errno("EINVAL");
|
|
696
710
|
const base = whence === SEEK_SET ? 0 : whence === SEEK_CUR ? file.position
|
|
697
711
|
: whence === SEEK_END ? this.fs().statSync(file.path).size : undefined;
|
|
698
|
-
|
|
712
|
+
// An offset or a position past 2^53 - 1 is not one a double holds (musl 0005's lseek says the same).
|
|
713
|
+
if (base === undefined || !Number.isSafeInteger(offset) || base + offset < 0 || !Number.isSafeInteger(base + offset))
|
|
699
714
|
throw errno("EINVAL");
|
|
700
715
|
file.position = base + offset;
|
|
701
716
|
return BigInt(file.position);
|
|
@@ -935,7 +950,36 @@ export class WaliFileDescriptorTable {
|
|
|
935
950
|
}
|
|
936
951
|
}
|
|
937
952
|
rename(from, to) {
|
|
938
|
-
return this.mutate(() =>
|
|
953
|
+
return this.mutate(() => {
|
|
954
|
+
const source = this.resolveFilesystemPath(this.resolve(from), false);
|
|
955
|
+
const destination = this.resolveFilesystemPath(this.resolve(to), false);
|
|
956
|
+
// Moving a directory the held tree is under moves the tree: the libc's to release first (ADR-0047).
|
|
957
|
+
if (this.privateTree && (underRoot(this.privateTree.root, source) || underRoot(this.privateTree.root, destination)))
|
|
958
|
+
throw errno("EREMOTE");
|
|
959
|
+
const fs = this.fs();
|
|
960
|
+
// Linux's rules, before the store's move: a file never replaces a
|
|
961
|
+
// directory nor a directory a file, a directory only an empty one, and
|
|
962
|
+
// never into itself. The store's move sets the source over whatever the
|
|
963
|
+
// destination held and leaves that directory's children named under a
|
|
964
|
+
// file, a tree no later snapshot can be laid out from.
|
|
965
|
+
if (fs.existsSync(source)) {
|
|
966
|
+
if (source === destination)
|
|
967
|
+
return;
|
|
968
|
+
const directory = fs.statSync(source).isDirectory();
|
|
969
|
+
if (directory && destination.startsWith(`${source}/`))
|
|
970
|
+
throw errno("EINVAL");
|
|
971
|
+
if (fs.existsSync(destination)) {
|
|
972
|
+
const into = fs.statSync(destination).isDirectory();
|
|
973
|
+
if (directory && !into)
|
|
974
|
+
throw errno("ENOTDIR");
|
|
975
|
+
if (!directory && into)
|
|
976
|
+
throw errno("EISDIR");
|
|
977
|
+
if (directory && fs.readdirSync(destination).length > 0)
|
|
978
|
+
throw errno("ENOTEMPTY");
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
fs.renameSync(source, destination);
|
|
982
|
+
});
|
|
939
983
|
}
|
|
940
984
|
utimensat(dirfd, pathPointer, timesPointer, flags) {
|
|
941
985
|
try {
|
|
@@ -984,6 +1028,8 @@ export class WaliFileDescriptorTable {
|
|
|
984
1028
|
if (!Number.isSafeInteger(size) || size < 0)
|
|
985
1029
|
throw errno("EINVAL");
|
|
986
1030
|
const resolved = this.resolveAt(dirfd, path);
|
|
1031
|
+
if (this.privateTree && underRoot(resolved, this.privateTree.root))
|
|
1032
|
+
throw errno("EREMOTE");
|
|
987
1033
|
let target;
|
|
988
1034
|
if (resolved === "/proc/self/exe")
|
|
989
1035
|
target = this.options.executablePath ?? "/usr/bin/wali-program";
|
|
@@ -1018,7 +1064,13 @@ export class WaliFileDescriptorTable {
|
|
|
1018
1064
|
}
|
|
1019
1065
|
}
|
|
1020
1066
|
resolveFilesystemPath(path, followFinal = true) {
|
|
1021
|
-
|
|
1067
|
+
const resolved = resolveWaliSymlinkPath(this.fs(), path, followFinal);
|
|
1068
|
+
// A held private tree is the libc's: a path that reaches it here (a symlink
|
|
1069
|
+
// elsewhere, a directory descriptor) is EREMOTE, and the libc releases the
|
|
1070
|
+
// tree and asks again (ADR-0047 §3).
|
|
1071
|
+
if (this.privateTree && underRoot(resolved, this.privateTree.root))
|
|
1072
|
+
throw errno("EREMOTE");
|
|
1073
|
+
return resolved;
|
|
1022
1074
|
}
|
|
1023
1075
|
readSymlink(path) {
|
|
1024
1076
|
return readWaliSymlink(this.fs(), path);
|
|
@@ -1028,7 +1080,7 @@ export class WaliFileDescriptorTable {
|
|
|
1028
1080
|
const stdio = this.stdioFd(fd);
|
|
1029
1081
|
if (stdio !== undefined) {
|
|
1030
1082
|
const next = target ?? this.allocateFd(minimum);
|
|
1031
|
-
if (next < 0 || next > maximum)
|
|
1083
|
+
if (next < 0 || (next > maximum && !inPrivateTreeWindow(next)))
|
|
1032
1084
|
return -9n;
|
|
1033
1085
|
if (next === fd)
|
|
1034
1086
|
return BigInt(next);
|
|
@@ -1040,7 +1092,7 @@ export class WaliFileDescriptorTable {
|
|
|
1040
1092
|
if (!this.descriptors.get(fd))
|
|
1041
1093
|
return -9n;
|
|
1042
1094
|
const next = target ?? this.allocateFd(minimum);
|
|
1043
|
-
if (next < 0 || next > maximum)
|
|
1095
|
+
if (next < 0 || (next > maximum && !inPrivateTreeWindow(next)))
|
|
1044
1096
|
return -9n;
|
|
1045
1097
|
if (next === fd)
|
|
1046
1098
|
return BigInt(next);
|
|
@@ -1283,6 +1335,194 @@ export class WaliFileDescriptorTable {
|
|
|
1283
1335
|
return negativeErrno(error);
|
|
1284
1336
|
}
|
|
1285
1337
|
}
|
|
1338
|
+
privateTreeCall(operation, pointer, length) {
|
|
1339
|
+
try {
|
|
1340
|
+
if (operation === PR_WALI_TREE_ADOPT)
|
|
1341
|
+
return this.adoptPrivateTree(pointer, length);
|
|
1342
|
+
if (operation === PR_WALI_TREE_RELEASE)
|
|
1343
|
+
return this.releasePrivateTree(pointer, length);
|
|
1344
|
+
if (operation === PR_WALI_TREE_CONFIRM)
|
|
1345
|
+
return this.confirmPrivateTree();
|
|
1346
|
+
if (operation === PR_WALI_TREE_DECLINE) {
|
|
1347
|
+
// The libc could not take the tree: it stays the kernel's, as it was,
|
|
1348
|
+
// with anything a CONFIRM that failed part-way had removed put back.
|
|
1349
|
+
const handed = this.privateTree?.handed;
|
|
1350
|
+
if (!handed)
|
|
1351
|
+
return -22n;
|
|
1352
|
+
const fs = this.fs();
|
|
1353
|
+
for (const node of handed)
|
|
1354
|
+
if (!fs.existsSync(node.path)) {
|
|
1355
|
+
if (fs.restoreNodeSync)
|
|
1356
|
+
fs.restoreNodeSync(node.path, node);
|
|
1357
|
+
else {
|
|
1358
|
+
if (node.directory)
|
|
1359
|
+
fs.mkdirSync(node.path, { recursive: true });
|
|
1360
|
+
else
|
|
1361
|
+
fs.writeFileSync(node.path, node.content);
|
|
1362
|
+
fs.chmodSync?.(node.path, node.mode & 0o7777);
|
|
1363
|
+
fs.utimesSync?.(node.path, node.atimeMs, node.mtimeMs);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
this.privateTree = undefined;
|
|
1367
|
+
return 0n;
|
|
1368
|
+
}
|
|
1369
|
+
return -22n;
|
|
1370
|
+
}
|
|
1371
|
+
catch (error) {
|
|
1372
|
+
return negativeErrno(error);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* ADOPT (ADR-0047 §3): the first private root, offered only while nothing
|
|
1377
|
+
* else can see it. The reply is the window, the inode range, the root, the
|
|
1378
|
+
* working directory, and what the kernel's tree already held there (a run's
|
|
1379
|
+
* environment file), which leaves the kernel's tree. 0 declines, as an
|
|
1380
|
+
* older kernel's answer to every prctl does.
|
|
1381
|
+
*/
|
|
1382
|
+
adoptPrivateTree(pointer, length) {
|
|
1383
|
+
const decline = (why) => {
|
|
1384
|
+
if (!this.privateTreeDeclined) {
|
|
1385
|
+
this.privateTreeDeclined = true;
|
|
1386
|
+
this.options.onDiagnostic?.(`[WALI] the private tree is not the program's (ADR-0047): ${why}; its file calls cross into the kernel.`);
|
|
1387
|
+
}
|
|
1388
|
+
return 0n;
|
|
1389
|
+
};
|
|
1390
|
+
if (this.privateTree)
|
|
1391
|
+
return decline("it is held already");
|
|
1392
|
+
const root = this.options.privateRoots?.[0];
|
|
1393
|
+
if (!root)
|
|
1394
|
+
return decline("the run names no private root");
|
|
1395
|
+
if (root === "/")
|
|
1396
|
+
return decline("the root is the whole filesystem");
|
|
1397
|
+
if (this.options.privateTreeAdoptable && !this.options.privateTreeAdoptable())
|
|
1398
|
+
return decline("a forked process shares the filesystem");
|
|
1399
|
+
const fs = this.fs();
|
|
1400
|
+
for (let fd = 0; fd < this.descriptors.entries.length; fd += 1) {
|
|
1401
|
+
const file = this.files.get(fd);
|
|
1402
|
+
if (file && underRoot(file.path, root))
|
|
1403
|
+
return decline(`a descriptor is open under ${root}`);
|
|
1404
|
+
}
|
|
1405
|
+
try {
|
|
1406
|
+
this.assertWritable(`${root}/.wali-private-tree`);
|
|
1407
|
+
}
|
|
1408
|
+
catch {
|
|
1409
|
+
return decline(`the run's filesystem grant does not allow writes under ${root}`);
|
|
1410
|
+
}
|
|
1411
|
+
if (fs.existsSync(root) && (!fs.statSync(root).isDirectory() || readWaliSymlink(fs, root) !== undefined))
|
|
1412
|
+
return decline(`${root} is not a directory`);
|
|
1413
|
+
// Made only once nothing declines: the offer is the one change.
|
|
1414
|
+
if (!fs.existsSync(root))
|
|
1415
|
+
fs.mkdirSync(root, { recursive: true });
|
|
1416
|
+
const nodes = [];
|
|
1417
|
+
const walk = (path) => {
|
|
1418
|
+
const stat = fs.statSync(path);
|
|
1419
|
+
const directory = stat.isDirectory();
|
|
1420
|
+
nodes.push({ path, directory, mode: stat.mode ?? (directory ? 0o755 : 0o644), atimeMs: stat.atimeMs ?? stat.mtimeMs ?? 0, mtimeMs: stat.mtimeMs ?? 0, ino: stat.ino ?? 1, content: directory ? new Uint8Array() : fs.readFileSync(path) });
|
|
1421
|
+
if (directory)
|
|
1422
|
+
for (const name of fs.readdirSync(path))
|
|
1423
|
+
walk(`${path === "/" ? "" : path}/${name}`);
|
|
1424
|
+
};
|
|
1425
|
+
walk(root);
|
|
1426
|
+
const tree = encodePrivateTree(nodes);
|
|
1427
|
+
const encoder = new TextEncoder();
|
|
1428
|
+
const rootBytes = encoder.encode(root);
|
|
1429
|
+
const cwdBytes = encoder.encode(this.cwd);
|
|
1430
|
+
const size = 4 + 4 + 8 + 4 + rootBytes.byteLength + 4 + cwdBytes.byteLength + tree.byteLength;
|
|
1431
|
+
if (size > length)
|
|
1432
|
+
return decline(`what ${root} already holds is larger than the libc's ${length}-byte reply`);
|
|
1433
|
+
const inodes = fs.reserveInodes?.(PRIVATE_TREE_INODES) ?? 2 ** 40;
|
|
1434
|
+
const reply = new Uint8Array(size);
|
|
1435
|
+
const view = new DataView(reply.buffer);
|
|
1436
|
+
let at = 0;
|
|
1437
|
+
view.setUint32(at, PRIVATE_TREE_WINDOW_BASE, true);
|
|
1438
|
+
at += 4;
|
|
1439
|
+
view.setUint32(at, PRIVATE_TREE_WINDOW_SIZE, true);
|
|
1440
|
+
at += 4;
|
|
1441
|
+
view.setBigUint64(at, BigInt(inodes), true);
|
|
1442
|
+
at += 8;
|
|
1443
|
+
view.setUint32(at, rootBytes.byteLength, true);
|
|
1444
|
+
at += 4;
|
|
1445
|
+
reply.set(rootBytes, at);
|
|
1446
|
+
at += rootBytes.byteLength;
|
|
1447
|
+
view.setUint32(at, cwdBytes.byteLength, true);
|
|
1448
|
+
at += 4;
|
|
1449
|
+
reply.set(cwdBytes, at);
|
|
1450
|
+
at += cwdBytes.byteLength;
|
|
1451
|
+
reply.set(tree, at);
|
|
1452
|
+
this.copyIn(pointer, reply);
|
|
1453
|
+
// Held from here; the kernel's copy stays until the libc says it took it
|
|
1454
|
+
// (CONFIRM), so a libc that could not (DECLINE) loses nothing.
|
|
1455
|
+
this.privateTree = { root, handed: nodes };
|
|
1456
|
+
return BigInt(PRIVATE_TREE_WINDOW_BASE);
|
|
1457
|
+
}
|
|
1458
|
+
/** CONFIRM: the libc holds what ADOPT handed it, and the kernel's copy empties. */
|
|
1459
|
+
confirmPrivateTree() {
|
|
1460
|
+
const held = this.privateTree;
|
|
1461
|
+
if (!held?.handed)
|
|
1462
|
+
return -22n;
|
|
1463
|
+
const fs = this.fs();
|
|
1464
|
+
for (const node of held.handed.slice(1).reverse()) {
|
|
1465
|
+
if (node.directory)
|
|
1466
|
+
fs.rmdirSync(node.path);
|
|
1467
|
+
else
|
|
1468
|
+
fs.unlinkSync(node.path);
|
|
1469
|
+
}
|
|
1470
|
+
held.handed = undefined;
|
|
1471
|
+
return 0n;
|
|
1472
|
+
}
|
|
1473
|
+
/**
|
|
1474
|
+
* RELEASE (ADR-0047 §4): the tree and every descriptor on it, installed in
|
|
1475
|
+
* this one call: the same paths, modes, times and inodes, the same
|
|
1476
|
+
* descriptor numbers, positions and flags, and a dup's descriptors sharing
|
|
1477
|
+
* one description again. From here every call on it is this kernel's.
|
|
1478
|
+
*/
|
|
1479
|
+
releasePrivateTree(pointer, length) {
|
|
1480
|
+
const held = this.privateTree;
|
|
1481
|
+
if (!held || held.handed)
|
|
1482
|
+
return -22n;
|
|
1483
|
+
assertRange(this.options.memory(), pointer, length);
|
|
1484
|
+
const { nodes, descriptors } = decodePrivateTree(this.copyOut(pointer, length), held.root);
|
|
1485
|
+
const fs = this.fs();
|
|
1486
|
+
for (const node of nodes) {
|
|
1487
|
+
// Made in the tree, so made by this process, as the kernel's own open
|
|
1488
|
+
// records: the first lock on it must not pull the page's (absent) copy
|
|
1489
|
+
// over it (WaliCoherentFileLockClient).
|
|
1490
|
+
if (!node.directory)
|
|
1491
|
+
this.options.fileLocks?.created?.(node.path);
|
|
1492
|
+
if (fs.restoreNodeSync)
|
|
1493
|
+
fs.restoreNodeSync(node.path, node);
|
|
1494
|
+
else {
|
|
1495
|
+
if (node.directory)
|
|
1496
|
+
fs.mkdirSync(node.path, { recursive: true });
|
|
1497
|
+
else
|
|
1498
|
+
fs.writeFileSync(node.path, node.content);
|
|
1499
|
+
fs.chmodSync?.(node.path, node.mode & 0o7777);
|
|
1500
|
+
fs.utimesSync?.(node.path, node.atimeMs, node.mtimeMs);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
for (const descriptor of descriptors) {
|
|
1504
|
+
if (this.descriptors.get(descriptor.fd))
|
|
1505
|
+
this.descriptors.close(descriptor.fd);
|
|
1506
|
+
if (descriptor.group === descriptor.fd) {
|
|
1507
|
+
this.files.set(descriptor.fd, {
|
|
1508
|
+
path: descriptor.path, flags: descriptor.flags, position: descriptor.position,
|
|
1509
|
+
directory: descriptor.directory, description: this.nextDescription++,
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
else {
|
|
1513
|
+
const shared = this.descriptors.get(descriptor.group);
|
|
1514
|
+
if (!shared)
|
|
1515
|
+
throw errno("EINVAL");
|
|
1516
|
+
this.descriptors.set(descriptor.fd, shared);
|
|
1517
|
+
}
|
|
1518
|
+
this.descriptors.entries[descriptor.fd].flags = descriptor.closeOnExec ? 1 : 0;
|
|
1519
|
+
}
|
|
1520
|
+
this.privateTree = undefined;
|
|
1521
|
+
return 0n;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
function underRoot(path, root) {
|
|
1525
|
+
return path === root || path.startsWith(root === "/" ? "/" : `${root}/`);
|
|
1286
1526
|
}
|
|
1287
1527
|
function normalizePath(cwd, path) {
|
|
1288
1528
|
if (!path)
|
|
@@ -1437,10 +1677,10 @@ function errno(errnoName) {
|
|
|
1437
1677
|
return error;
|
|
1438
1678
|
}
|
|
1439
1679
|
const ERRNO_NUMBERS = {
|
|
1440
|
-
EPERM: 1n, ENOENT: 2n, EIO: 5n, EAGAIN: 11n, ENOMEM: 12n, EACCES: 13n,
|
|
1680
|
+
EPERM: 1n, ENOENT: 2n, EIO: 5n, EBADF: 9n, EAGAIN: 11n, ENOMEM: 12n, EACCES: 13n,
|
|
1441
1681
|
EFAULT: 14n, EBUSY: 16n, EEXIST: 17n, EXDEV: 18n, ENOTDIR: 20n, EISDIR: 21n,
|
|
1442
1682
|
EINVAL: 22n, EMFILE: 24n, ENOSPC: 28n, ESPIPE: 29n, EROFS: 30n, ERANGE: 34n,
|
|
1443
|
-
ENOSYS: 38n, ENOTEMPTY: 39n, ELOOP: 40n, ENAMETOOLONG: 36n,
|
|
1683
|
+
ENOSYS: 38n, ENOTEMPTY: 39n, ELOOP: 40n, ENAMETOOLONG: 36n, EREMOTE: 66n,
|
|
1444
1684
|
};
|
|
1445
1685
|
function negativeErrno(error) {
|
|
1446
1686
|
// The errno's own name first, and the message parsed only for an error that has none.
|