@ai-ecoverse/slicc-kernel 1.69.0 → 1.71.0
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 +22 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +343 -80
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +5 -1
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +333 -70
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +5 -1
- package/dist/process-worker.js.map +2 -2
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -162,12 +162,12 @@ var IndexedDbMeta = class {
|
|
|
162
162
|
return [transaction, transaction.objectStore(META_STORE)];
|
|
163
163
|
}
|
|
164
164
|
async get(paths) {
|
|
165
|
-
const
|
|
166
|
-
if (
|
|
165
|
+
const missing4 = paths.filter((path) => !this.cache.has(path));
|
|
166
|
+
if (missing4.length > 0) {
|
|
167
167
|
const generation = this.generation;
|
|
168
168
|
const [transaction, store] = this.transaction("readonly");
|
|
169
169
|
const found = /* @__PURE__ */ new Map();
|
|
170
|
-
for (const path of
|
|
170
|
+
for (const path of missing4) {
|
|
171
171
|
const request2 = store.get(path);
|
|
172
172
|
request2.onsuccess = () => void found.set(path, request2.result ?? null);
|
|
173
173
|
}
|
|
@@ -353,6 +353,183 @@ async function chownAs(fs, cred, path, change) {
|
|
|
353
353
|
});
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
+
// src/fs/protect.ts
|
|
357
|
+
var PROTECT_WRITER = "slicc-kernel: protect.writer needs the process tag (#405), which this kernel does not have yet";
|
|
358
|
+
function protectedPrefixes(paths) {
|
|
359
|
+
return [...new Set(paths.map((p) => normalizePath(p)))];
|
|
360
|
+
}
|
|
361
|
+
function within(path, prefix) {
|
|
362
|
+
return prefix === "/" || path === prefix || path.startsWith(`${prefix}/`);
|
|
363
|
+
}
|
|
364
|
+
function isProtected(prefixes2, path) {
|
|
365
|
+
return prefixes2.some((prefix) => within(path, prefix));
|
|
366
|
+
}
|
|
367
|
+
function holdsProtected(prefixes2, path) {
|
|
368
|
+
return prefixes2.some((prefix) => within(prefix, path));
|
|
369
|
+
}
|
|
370
|
+
var MAX_LINKS = 40;
|
|
371
|
+
function partsOf(path) {
|
|
372
|
+
return path.split("/").filter((p) => p !== "" && p !== ".");
|
|
373
|
+
}
|
|
374
|
+
function missing(err) {
|
|
375
|
+
const code = err?.code;
|
|
376
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
377
|
+
}
|
|
378
|
+
async function resolved(fs, path, follow2) {
|
|
379
|
+
let done4 = [];
|
|
380
|
+
let todo = partsOf(path);
|
|
381
|
+
for (let hops = 0; todo.length > 0; ) {
|
|
382
|
+
const part = todo.shift();
|
|
383
|
+
if (part === "..") {
|
|
384
|
+
done4.pop();
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
if (todo.length === 0 && !follow2) {
|
|
388
|
+
done4.push(part);
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
const at = `/${[...done4, part].join("/")}`;
|
|
392
|
+
const st = await fs.lstat(at).catch((err) => {
|
|
393
|
+
if (missing(err)) return void 0;
|
|
394
|
+
throw err;
|
|
395
|
+
});
|
|
396
|
+
if (!st) {
|
|
397
|
+
return normalizePath([...done4, part, ...todo].join("/"));
|
|
398
|
+
}
|
|
399
|
+
if (!st.isSymbolicLink) {
|
|
400
|
+
done4.push(part);
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
if (++hops > MAX_LINKS) throw fsError("ELOOP", path);
|
|
404
|
+
const link2 = await fs.readlink(at);
|
|
405
|
+
if (link2.startsWith("/")) done4 = [];
|
|
406
|
+
todo = [...partsOf(link2), ...todo];
|
|
407
|
+
}
|
|
408
|
+
return `/${done4.join("/")}`;
|
|
409
|
+
}
|
|
410
|
+
var ProtectedFs = class {
|
|
411
|
+
prefixes;
|
|
412
|
+
constructor(prefixes2) {
|
|
413
|
+
this.prefixes = prefixes2;
|
|
414
|
+
}
|
|
415
|
+
async check(fs, path, follow2, whole = false) {
|
|
416
|
+
const lexical2 = normalizePath(path);
|
|
417
|
+
const real = await resolved(fs, lexical2, follow2);
|
|
418
|
+
for (const p of /* @__PURE__ */ new Set([lexical2, real])) {
|
|
419
|
+
if (isProtected(this.prefixes, p) || whole && holdsProtected(this.prefixes, p)) {
|
|
420
|
+
throw fsError("EROFS", path);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
view(fs) {
|
|
425
|
+
const check = (path, follow2, whole = false) => this.check(fs, path, follow2, whole);
|
|
426
|
+
const marked = (path, st) => !st.readonly && isProtected(this.prefixes, normalizePath(path)) ? { ...st, readonly: true } : st;
|
|
427
|
+
const own = {
|
|
428
|
+
async writeFile(...args) {
|
|
429
|
+
await check(args[0], true);
|
|
430
|
+
return fs.writeFile(...args);
|
|
431
|
+
},
|
|
432
|
+
async mkdir(...args) {
|
|
433
|
+
await check(args[0], false);
|
|
434
|
+
return fs.mkdir(...args);
|
|
435
|
+
},
|
|
436
|
+
async rm(...args) {
|
|
437
|
+
await check(args[0], false, true);
|
|
438
|
+
return fs.rm(...args);
|
|
439
|
+
},
|
|
440
|
+
async rename(from, to) {
|
|
441
|
+
await check(from, false, true);
|
|
442
|
+
await check(to, false, true);
|
|
443
|
+
return fs.rename(from, to);
|
|
444
|
+
},
|
|
445
|
+
async link(from, to, follow2) {
|
|
446
|
+
await check(from, follow2 === true);
|
|
447
|
+
await check(to, false);
|
|
448
|
+
return fs.link(from, to, follow2);
|
|
449
|
+
},
|
|
450
|
+
async symlink(...args) {
|
|
451
|
+
await check(args[1], false);
|
|
452
|
+
return fs.symlink(...args);
|
|
453
|
+
},
|
|
454
|
+
async chmod(path, mode) {
|
|
455
|
+
await check(path, true);
|
|
456
|
+
return fs.chmod(path, mode);
|
|
457
|
+
},
|
|
458
|
+
async utimes(path, atime, mtime) {
|
|
459
|
+
await check(path, true);
|
|
460
|
+
return fs.utimes(path, atime, mtime);
|
|
461
|
+
},
|
|
462
|
+
async stat(path) {
|
|
463
|
+
return marked(path, await fs.stat(path));
|
|
464
|
+
},
|
|
465
|
+
async lstat(path) {
|
|
466
|
+
return marked(path, await fs.lstat(path));
|
|
467
|
+
},
|
|
468
|
+
async readdirStat(path) {
|
|
469
|
+
const dir = normalizePath(path);
|
|
470
|
+
return (await fs.readdirStat(path)).map(([name2, st]) => [
|
|
471
|
+
name2,
|
|
472
|
+
st && marked(`${dir}/${name2}`, st)
|
|
473
|
+
]);
|
|
474
|
+
},
|
|
475
|
+
...fs.createExclusive ? {
|
|
476
|
+
async createExclusive(...args) {
|
|
477
|
+
await check(args[0], true);
|
|
478
|
+
return fs.createExclusive?.(...args);
|
|
479
|
+
}
|
|
480
|
+
} : {},
|
|
481
|
+
...fs.chown ? {
|
|
482
|
+
async chown(...args) {
|
|
483
|
+
await check(args[0], args[1].follow !== false);
|
|
484
|
+
return fs.chown?.(...args);
|
|
485
|
+
}
|
|
486
|
+
} : {},
|
|
487
|
+
...fs.lutimes ? {
|
|
488
|
+
async lutimes(path, atime, mtime) {
|
|
489
|
+
await check(path, false);
|
|
490
|
+
return fs.lutimes?.(path, atime, mtime);
|
|
491
|
+
}
|
|
492
|
+
} : {},
|
|
493
|
+
...fs.pwrite ? {
|
|
494
|
+
async pwrite(...args) {
|
|
495
|
+
await check(args[0], true);
|
|
496
|
+
return fs.pwrite?.(...args);
|
|
497
|
+
}
|
|
498
|
+
} : {},
|
|
499
|
+
...fs.truncate ? {
|
|
500
|
+
async truncate(path, size) {
|
|
501
|
+
await check(path, true);
|
|
502
|
+
return fs.truncate?.(path, size);
|
|
503
|
+
}
|
|
504
|
+
} : {},
|
|
505
|
+
...fs.exclusive ? {
|
|
506
|
+
async exclusive(path, run2, follow2) {
|
|
507
|
+
return fs.exclusive(
|
|
508
|
+
path,
|
|
509
|
+
(held) => run2({
|
|
510
|
+
...held,
|
|
511
|
+
writeFile: async (...args) => {
|
|
512
|
+
await check(args[0], true);
|
|
513
|
+
return held.writeFile(...args);
|
|
514
|
+
}
|
|
515
|
+
}),
|
|
516
|
+
follow2
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
} : {}
|
|
520
|
+
};
|
|
521
|
+
const view = {};
|
|
522
|
+
for (let at = fs; at && at !== Object.prototype; at = Object.getPrototypeOf(at)) {
|
|
523
|
+
for (const name2 of Object.getOwnPropertyNames(at)) {
|
|
524
|
+
if (name2 === "constructor" || Object.hasOwn(view, name2)) continue;
|
|
525
|
+
const value = fs[name2];
|
|
526
|
+
view[name2] = typeof value === "function" ? value.bind(fs) : value;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return Object.assign(view, own);
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
|
|
356
533
|
// src/fs/opfs.ts
|
|
357
534
|
var S_IFDIR = 16384;
|
|
358
535
|
var S_IFREG = 32768;
|
|
@@ -360,7 +537,7 @@ var S_IFLNK = 40960;
|
|
|
360
537
|
var PERM_MASK = 4095;
|
|
361
538
|
var S_ISGID2 = 1024;
|
|
362
539
|
var READ_ATTEMPTS = 3;
|
|
363
|
-
var
|
|
540
|
+
var MAX_LINKS2 = 40;
|
|
364
541
|
var CHECK_MS = 1e3;
|
|
365
542
|
var EXECUTABLE = /\/node_modules\/(?:@[^/]+\/)?[^/]+\/bin\//;
|
|
366
543
|
var ERRNO_BY_DOM_ERROR = {
|
|
@@ -397,7 +574,7 @@ function sidecar(meta) {
|
|
|
397
574
|
});
|
|
398
575
|
return Object.fromEntries(META_OPS.map((op) => [op, guarded2(op)]));
|
|
399
576
|
}
|
|
400
|
-
function
|
|
577
|
+
function missing2(err) {
|
|
401
578
|
const code = err?.code;
|
|
402
579
|
return code === "ENOENT" || code === "ENOTDIR";
|
|
403
580
|
}
|
|
@@ -414,7 +591,7 @@ function prefixes(path) {
|
|
|
414
591
|
const parts2 = split(path);
|
|
415
592
|
return parts2.map((_, i) => `/${parts2.slice(0, i + 1).join("/")}`);
|
|
416
593
|
}
|
|
417
|
-
function
|
|
594
|
+
function within2(path, root) {
|
|
418
595
|
return path === root || path.startsWith(`${root}/`);
|
|
419
596
|
}
|
|
420
597
|
var LOCKED = /* @__PURE__ */ new Set(["NotReadableError", "NoModificationAllowedError", "InvalidStateError"]);
|
|
@@ -553,14 +730,25 @@ var OpfsFs = class {
|
|
|
553
730
|
const fields = await this.newFields(path, owner, directory);
|
|
554
731
|
if (fields) await this.meta.update(path, (entry) => ({ ...entry, path, ...fields }));
|
|
555
732
|
}
|
|
733
|
+
guarded = [];
|
|
734
|
+
protect(prefixes2) {
|
|
735
|
+
this.guarded = prefixes2;
|
|
736
|
+
}
|
|
737
|
+
refuse(path, holding = false) {
|
|
738
|
+
if (isProtected(this.guarded, path) || holding && holdsProtected(this.guarded, path)) {
|
|
739
|
+
throw fsError("EROFS", path);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
556
742
|
async chown(path, change) {
|
|
557
743
|
if (!this.owners) return;
|
|
558
744
|
if (change.kind === "new") {
|
|
559
745
|
const at = resolvePath((await this.locate(parentOf3(path), true)).path, baseOf(path));
|
|
746
|
+
this.refuse(at);
|
|
560
747
|
const { uid = 0, gid = 0, mode } = change;
|
|
561
748
|
return this.stampNew(at, { uid, gid, ...mode !== void 0 ? { mode } : {} }, false);
|
|
562
749
|
}
|
|
563
750
|
const found = await this.locate(path, change.follow !== false);
|
|
751
|
+
this.refuse(found.path);
|
|
564
752
|
const [stored] = found.link ? [found.link] : await this.meta.get([found.path]);
|
|
565
753
|
const current = await this.ownerAt(found.path, stored);
|
|
566
754
|
const next2 = { uid: change.uid ?? current.uid, gid: change.gid ?? current.gid };
|
|
@@ -673,11 +861,11 @@ var OpfsFs = class {
|
|
|
673
861
|
}
|
|
674
862
|
async locate(path, follow2) {
|
|
675
863
|
let current = path;
|
|
676
|
-
for (let hops = 0; hops <=
|
|
864
|
+
for (let hops = 0; hops <= MAX_LINKS2; hops++) {
|
|
677
865
|
try {
|
|
678
866
|
return { path: current, handle: await this.handle(current) };
|
|
679
867
|
} catch (err) {
|
|
680
|
-
if (!
|
|
868
|
+
if (!missing2(err)) throw err;
|
|
681
869
|
const link2 = await this.linkAlong(current);
|
|
682
870
|
if (!link2) throw err;
|
|
683
871
|
if (link2.path === current && !follow2) return { path: current, link: link2 };
|
|
@@ -699,7 +887,7 @@ var OpfsFs = class {
|
|
|
699
887
|
const st = await this.lstat(at);
|
|
700
888
|
if (!st.isSymbolicLink && !st.isDirectory) throw fsError("ENOTDIR", at);
|
|
701
889
|
if (!st.isSymbolicLink) done4.push(part);
|
|
702
|
-
else if (++hops >
|
|
890
|
+
else if (++hops > MAX_LINKS2) throw fsError("ELOOP", path);
|
|
703
891
|
else {
|
|
704
892
|
const link2 = await this.readlink(at);
|
|
705
893
|
if (link2.startsWith("/")) done4 = [];
|
|
@@ -796,7 +984,7 @@ var OpfsFs = class {
|
|
|
796
984
|
return this.locate(path, true).then(
|
|
797
985
|
() => true,
|
|
798
986
|
(err) => {
|
|
799
|
-
if (
|
|
987
|
+
if (missing2(err)) return false;
|
|
800
988
|
throw err;
|
|
801
989
|
}
|
|
802
990
|
);
|
|
@@ -814,8 +1002,10 @@ var OpfsFs = class {
|
|
|
814
1002
|
}
|
|
815
1003
|
}
|
|
816
1004
|
}
|
|
817
|
-
async existing(path) {
|
|
818
|
-
const
|
|
1005
|
+
async existing(path, writing = false) {
|
|
1006
|
+
const found = await this.locate(path, true);
|
|
1007
|
+
if (writing) this.refuse(found.path);
|
|
1008
|
+
const handle = found.handle;
|
|
819
1009
|
if (handle.kind === "directory") throw fsError("EISDIR", path);
|
|
820
1010
|
return handle;
|
|
821
1011
|
}
|
|
@@ -840,7 +1030,7 @@ var OpfsFs = class {
|
|
|
840
1030
|
await this.retrying(path, () => positioned(handle, (io) => io.write(offset, bytes)), true);
|
|
841
1031
|
}
|
|
842
1032
|
async truncate(path, size) {
|
|
843
|
-
const handle = await this.existing(path);
|
|
1033
|
+
const handle = await this.existing(path, true);
|
|
844
1034
|
await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
|
|
845
1035
|
}
|
|
846
1036
|
async writeSession(path, truncate) {
|
|
@@ -866,10 +1056,15 @@ var OpfsFs = class {
|
|
|
866
1056
|
return new TextDecoder().decode(await this.readFileBuffer(path));
|
|
867
1057
|
}
|
|
868
1058
|
async writeTarget(path) {
|
|
1059
|
+
const [target, existed] = await this.locateWrite(path);
|
|
1060
|
+
this.refuse(target);
|
|
1061
|
+
return [target, existed];
|
|
1062
|
+
}
|
|
1063
|
+
async locateWrite(path) {
|
|
869
1064
|
try {
|
|
870
1065
|
return [(await this.locate(path, true)).path, true];
|
|
871
1066
|
} catch (err) {
|
|
872
|
-
if (!
|
|
1067
|
+
if (!missing2(err)) throw err;
|
|
873
1068
|
}
|
|
874
1069
|
const [entry] = await this.meta.get([path]);
|
|
875
1070
|
const wanted = entry?.link === void 0 ? path : resolvePath(parentOf3(path), entry.link);
|
|
@@ -938,7 +1133,7 @@ var OpfsFs = class {
|
|
|
938
1133
|
try {
|
|
939
1134
|
found = await this.locate(path, true);
|
|
940
1135
|
} catch (err) {
|
|
941
|
-
if (!
|
|
1136
|
+
if (!missing2(err)) throw err;
|
|
942
1137
|
}
|
|
943
1138
|
if (found) {
|
|
944
1139
|
const directory = found.handle?.kind === "directory";
|
|
@@ -959,6 +1154,7 @@ var OpfsFs = class {
|
|
|
959
1154
|
}
|
|
960
1155
|
const parent = await this.locate(parentOf3(path), true);
|
|
961
1156
|
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
1157
|
+
this.refuse(resolvePath(parent.path, baseOf(path)));
|
|
962
1158
|
await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
|
|
963
1159
|
const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
|
|
964
1160
|
await this.stampNew(made2, owner, true);
|
|
@@ -973,6 +1169,7 @@ var OpfsFs = class {
|
|
|
973
1169
|
if (options.force && error.code === "ENOENT") return;
|
|
974
1170
|
throw error;
|
|
975
1171
|
}
|
|
1172
|
+
this.refuse(found.path, true);
|
|
976
1173
|
if (found.handle) {
|
|
977
1174
|
const [dir, name2] = await this.parent(found.path);
|
|
978
1175
|
try {
|
|
@@ -997,13 +1194,15 @@ var OpfsFs = class {
|
|
|
997
1194
|
async rename(from, to) {
|
|
998
1195
|
if (from === to) return;
|
|
999
1196
|
const source = await this.locate(from, false);
|
|
1197
|
+
this.refuse(source.path, true);
|
|
1000
1198
|
this.forget(source.path);
|
|
1001
1199
|
const parent = await this.locate(parentOf3(to), true);
|
|
1002
1200
|
const destination = resolvePath(parent.path, baseOf(to));
|
|
1201
|
+
this.refuse(destination, true);
|
|
1003
1202
|
const sourceStat = await this.describe(source);
|
|
1004
|
-
if (sourceStat.isDirectory &&
|
|
1203
|
+
if (sourceStat.isDirectory && within2(destination, source.path)) throw fsError("EINVAL", to);
|
|
1005
1204
|
const target = await this.locate(destination, false).catch((err) => {
|
|
1006
|
-
if (
|
|
1205
|
+
if (missing2(err)) return null;
|
|
1007
1206
|
throw err;
|
|
1008
1207
|
});
|
|
1009
1208
|
const targetStat = target && await this.describe(target);
|
|
@@ -1097,9 +1296,10 @@ var OpfsFs = class {
|
|
|
1097
1296
|
const parent = await this.locate(parentOf3(path), true);
|
|
1098
1297
|
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
1099
1298
|
const at = resolvePath(parent.path, baseOf(path));
|
|
1299
|
+
this.refuse(at);
|
|
1100
1300
|
if (await this.locate(at, false).then(
|
|
1101
1301
|
() => true,
|
|
1102
|
-
(err) => !
|
|
1302
|
+
(err) => !missing2(err)
|
|
1103
1303
|
)) {
|
|
1104
1304
|
throw fsError("EEXIST", path);
|
|
1105
1305
|
}
|
|
@@ -1126,6 +1326,7 @@ var OpfsFs = class {
|
|
|
1126
1326
|
}
|
|
1127
1327
|
async chmod(path, mode) {
|
|
1128
1328
|
const found = await this.locate(path, true);
|
|
1329
|
+
this.refuse(found.path);
|
|
1129
1330
|
const ctimeMs = Date.now();
|
|
1130
1331
|
await this.meta.update(found.path, (entry) => ({
|
|
1131
1332
|
...entry,
|
|
@@ -1141,6 +1342,7 @@ var OpfsFs = class {
|
|
|
1141
1342
|
await this.setTimes(await this.locate(path, false), atime, mtime);
|
|
1142
1343
|
}
|
|
1143
1344
|
async setTimes(found, atime, mtime) {
|
|
1345
|
+
this.refuse(found.path);
|
|
1144
1346
|
const { link: link2 } = found;
|
|
1145
1347
|
if (link2) {
|
|
1146
1348
|
const ctimeMs2 = Date.now();
|
|
@@ -1345,7 +1547,7 @@ function fresh(bytes, start2 = 0, end = bytes.length) {
|
|
|
1345
1547
|
}
|
|
1346
1548
|
|
|
1347
1549
|
// src/fs/walk.ts
|
|
1348
|
-
var
|
|
1550
|
+
var MAX_LINKS3 = 40;
|
|
1349
1551
|
function parentOf4(path) {
|
|
1350
1552
|
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
1351
1553
|
}
|
|
@@ -1374,7 +1576,7 @@ async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
|
1374
1576
|
at = next2;
|
|
1375
1577
|
continue;
|
|
1376
1578
|
}
|
|
1377
|
-
if (budget.hops >=
|
|
1579
|
+
if (budget.hops >= MAX_LINKS3) throw fsError("ELOOP", dir);
|
|
1378
1580
|
budget.hops++;
|
|
1379
1581
|
queue.unshift(...link2.split("/").filter(Boolean));
|
|
1380
1582
|
if (link2.startsWith("/")) at = "/";
|
|
@@ -1391,7 +1593,7 @@ async function realTarget(fs, path) {
|
|
|
1391
1593
|
for (; ; ) {
|
|
1392
1594
|
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
1393
1595
|
if (link2 === void 0) break;
|
|
1394
|
-
if (budget.hops >=
|
|
1596
|
+
if (budget.hops >= MAX_LINKS3) throw fsError("ELOOP", path);
|
|
1395
1597
|
budget.hops++;
|
|
1396
1598
|
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
1397
1599
|
dir = await realDir(fs, parentOf4(target), budget);
|
|
@@ -1404,7 +1606,7 @@ async function realTarget(fs, path) {
|
|
|
1404
1606
|
var R_OK = 4;
|
|
1405
1607
|
var W_OK = 2;
|
|
1406
1608
|
var X_OK = 1;
|
|
1407
|
-
var
|
|
1609
|
+
var MAX_LINKS4 = 40;
|
|
1408
1610
|
var S_ISVTX = 512;
|
|
1409
1611
|
function permOf(st, link2) {
|
|
1410
1612
|
return {
|
|
@@ -1475,7 +1677,7 @@ async function walk(fs, cred, path, follow2) {
|
|
|
1475
1677
|
const perm = await perms.at(next2);
|
|
1476
1678
|
const last = queue.length === 0;
|
|
1477
1679
|
if (perm?.link !== void 0 && (!last || follow2)) {
|
|
1478
|
-
if (++hops >
|
|
1680
|
+
if (++hops > MAX_LINKS4) return { path: next2, perm: void 0 };
|
|
1479
1681
|
queue.unshift(...parts(perm.link));
|
|
1480
1682
|
if (perm.link.startsWith("/")) at = "/";
|
|
1481
1683
|
continue;
|
|
@@ -3422,7 +3624,7 @@ var UserDb = class {
|
|
|
3422
3624
|
};
|
|
3423
3625
|
|
|
3424
3626
|
// src/client/serve-client.ts
|
|
3425
|
-
var
|
|
3627
|
+
var MAX_LINKS5 = 40;
|
|
3426
3628
|
function act(session, req, signal) {
|
|
3427
3629
|
if (req.action === "write") session.write(req.bytes);
|
|
3428
3630
|
else if (req.action === "resize") session.resize(req.cols, req.rows);
|
|
@@ -3431,22 +3633,22 @@ function act(session, req, signal) {
|
|
|
3431
3633
|
}
|
|
3432
3634
|
async function realpath(fs, path) {
|
|
3433
3635
|
let parts2 = fs.resolvePath("/", path).split("/").filter(Boolean);
|
|
3434
|
-
let
|
|
3636
|
+
let resolved2 = "";
|
|
3435
3637
|
for (let hops = 0; parts2.length > 0; ) {
|
|
3436
3638
|
const [name2, ...rest2] = parts2;
|
|
3437
|
-
const next2 = `${
|
|
3639
|
+
const next2 = `${resolved2}/${name2}`;
|
|
3438
3640
|
if (!(await fs.lstat(next2)).isSymbolicLink) {
|
|
3439
|
-
|
|
3641
|
+
resolved2 = next2;
|
|
3440
3642
|
parts2 = rest2;
|
|
3441
3643
|
continue;
|
|
3442
3644
|
}
|
|
3443
|
-
if (++hops >
|
|
3645
|
+
if (++hops > MAX_LINKS5) throw fsError("ELOOP", path);
|
|
3444
3646
|
const target = await fs.readlink(next2);
|
|
3445
|
-
const base = target.startsWith("/") ? "/" :
|
|
3647
|
+
const base = target.startsWith("/") ? "/" : resolved2 || "/";
|
|
3446
3648
|
parts2 = [...fs.resolvePath(base, target).split("/").filter(Boolean), ...rest2];
|
|
3447
|
-
|
|
3649
|
+
resolved2 = "";
|
|
3448
3650
|
}
|
|
3449
|
-
return
|
|
3651
|
+
return resolved2 || "/";
|
|
3450
3652
|
}
|
|
3451
3653
|
var CLIENT_CHECKS = {
|
|
3452
3654
|
readFile: (g, at) => readable(g, "read", at(0)),
|
|
@@ -3751,7 +3953,7 @@ function serveClient(port, host) {
|
|
|
3751
3953
|
if (!FS_METHODS.includes(req.method)) {
|
|
3752
3954
|
throw new Error(`unknown file system call ${req.method}`);
|
|
3753
3955
|
}
|
|
3754
|
-
return fsCall(l.
|
|
3956
|
+
return fsCall(l.processFs, req.method, req.args, ownerFor(l, self), guardFor(l, self));
|
|
3755
3957
|
case "watch":
|
|
3756
3958
|
case "unwatch":
|
|
3757
3959
|
return watchOp(l, req, watches, send, guardFor(l, self));
|
|
@@ -4970,11 +5172,11 @@ function ipv6(host) {
|
|
|
4970
5172
|
const back = halves.length === 2 ? groups(halves[1]) : [];
|
|
4971
5173
|
if (![...head, ...back].every((g) => /^[0-9A-Fa-f]{1,4}$/.test(g))) return void 0;
|
|
4972
5174
|
const words2 = [...head, ...back].map((g) => Number.parseInt(g, 16));
|
|
4973
|
-
const
|
|
4974
|
-
if (halves.length === 2 ?
|
|
5175
|
+
const missing4 = 8 - words2.length - tail.length;
|
|
5176
|
+
if (halves.length === 2 ? missing4 < 1 : missing4 !== 0) return void 0;
|
|
4975
5177
|
const all = [
|
|
4976
5178
|
...head.map((g) => Number.parseInt(g, 16)),
|
|
4977
|
-
...new Array(halves.length === 2 ?
|
|
5179
|
+
...new Array(halves.length === 2 ? missing4 : 0).fill(0),
|
|
4978
5180
|
...back.map((g) => Number.parseInt(g, 16)),
|
|
4979
5181
|
...tail
|
|
4980
5182
|
];
|
|
@@ -5763,7 +5965,7 @@ function parsePrefix(text) {
|
|
|
5763
5965
|
if (length2 < 0 || length2 > max) return void 0;
|
|
5764
5966
|
return { ...parsed2, length: length2 };
|
|
5765
5967
|
}
|
|
5766
|
-
function
|
|
5968
|
+
function within3(prefix, addr) {
|
|
5767
5969
|
if (prefix.v6 !== addr.v6) return false;
|
|
5768
5970
|
const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
|
|
5769
5971
|
return addr.bits >> shift === prefix.bits >> shift;
|
|
@@ -5803,9 +6005,9 @@ var Routes = class {
|
|
|
5803
6005
|
if (!addr) return "unreachable";
|
|
5804
6006
|
if (addr.v6 && addr.bits === 1n) return "loopback";
|
|
5805
6007
|
if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
|
|
5806
|
-
if (RESERVED.some((p) =>
|
|
6008
|
+
if (RESERVED.some((p) => within3(p, addr))) return "reserved";
|
|
5807
6009
|
if (addr.v6 && !this.ipv6) return "unreachable";
|
|
5808
|
-
if (this.exit || this.table.some((p) =>
|
|
6010
|
+
if (this.exit || this.table.some((p) => within3(p, addr))) return "uplink";
|
|
5809
6011
|
return "unreachable";
|
|
5810
6012
|
}
|
|
5811
6013
|
};
|
|
@@ -6419,8 +6621,8 @@ function binfmtOf(binfmts, path) {
|
|
|
6419
6621
|
var S_IFDIR2 = 16384;
|
|
6420
6622
|
var S_IFREG2 = 32768;
|
|
6421
6623
|
var COMMAND = /^\/(?:usr\/)?bin\/([^/]+)$/;
|
|
6422
|
-
var
|
|
6423
|
-
function
|
|
6624
|
+
var MAX_LINKS6 = 40;
|
|
6625
|
+
function missing3(err) {
|
|
6424
6626
|
return err?.code === "ENOENT";
|
|
6425
6627
|
}
|
|
6426
6628
|
function synthetic(directory, path) {
|
|
@@ -6439,7 +6641,7 @@ function synthetic(directory, path) {
|
|
|
6439
6641
|
}
|
|
6440
6642
|
async function followLinks(fs, path) {
|
|
6441
6643
|
let current = path;
|
|
6442
|
-
for (let hops = 0; hops <
|
|
6644
|
+
for (let hops = 0; hops < MAX_LINKS6; hops++) {
|
|
6443
6645
|
const st = await fs.lstat(current).catch(() => void 0);
|
|
6444
6646
|
if (!st?.isSymbolicLink) return current;
|
|
6445
6647
|
const parent = current.slice(0, current.lastIndexOf("/")) || "/";
|
|
@@ -6486,7 +6688,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
6486
6688
|
try {
|
|
6487
6689
|
return await real(path);
|
|
6488
6690
|
} catch (err) {
|
|
6489
|
-
const found =
|
|
6691
|
+
const found = missing3(err) ? await linked(path) : void 0;
|
|
6490
6692
|
if (found) return found;
|
|
6491
6693
|
throw err;
|
|
6492
6694
|
}
|
|
@@ -6498,7 +6700,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
6498
6700
|
try {
|
|
6499
6701
|
return await fs.readFileBuffer(path);
|
|
6500
6702
|
} catch (err) {
|
|
6501
|
-
if (
|
|
6703
|
+
if (missing3(err) && (await linked(path))?.isFile) return new Uint8Array(0);
|
|
6502
6704
|
throw err;
|
|
6503
6705
|
}
|
|
6504
6706
|
},
|
|
@@ -6514,7 +6716,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
6514
6716
|
const extra = await children2(path);
|
|
6515
6717
|
if (!extra) return fs.readdir(path);
|
|
6516
6718
|
const real = await fs.readdir(path).catch((err) => {
|
|
6517
|
-
if (
|
|
6719
|
+
if (missing3(err)) return [];
|
|
6518
6720
|
throw err;
|
|
6519
6721
|
});
|
|
6520
6722
|
return [.../* @__PURE__ */ new Set([...real, ...extra])].sort();
|
|
@@ -6523,7 +6725,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
6523
6725
|
const extra = await children2(path);
|
|
6524
6726
|
if (!extra) return fs.readdirStat(path);
|
|
6525
6727
|
const real = await fs.readdirStat(path).catch((err) => {
|
|
6526
|
-
if (
|
|
6728
|
+
if (missing3(err)) return [];
|
|
6527
6729
|
throw err;
|
|
6528
6730
|
});
|
|
6529
6731
|
const seen = new Set(real.map(([name2]) => name2));
|
|
@@ -6607,7 +6809,7 @@ var UNLINKED_DIR = ".slicc-unlinked";
|
|
|
6607
6809
|
function webLocks() {
|
|
6608
6810
|
return globalThis.navigator?.locks;
|
|
6609
6811
|
}
|
|
6610
|
-
var
|
|
6812
|
+
var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
6611
6813
|
var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6612
6814
|
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
6613
6815
|
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
@@ -6707,7 +6909,7 @@ var UnlinkedKeeper = class {
|
|
|
6707
6909
|
for (const holder of this.holders) {
|
|
6708
6910
|
if (holder.owns(target)) continue;
|
|
6709
6911
|
for (const path of holder.held()) {
|
|
6710
|
-
if (!
|
|
6912
|
+
if (!within4(path, target) || holder.isKept(path)) continue;
|
|
6711
6913
|
held.set(path, [...held.get(path) ?? [], holder]);
|
|
6712
6914
|
}
|
|
6713
6915
|
}
|
|
@@ -6960,6 +7162,18 @@ function capturing(chunks, max = Number.POSITIVE_INFINITY, overflow) {
|
|
|
6960
7162
|
size += bytes.length;
|
|
6961
7163
|
};
|
|
6962
7164
|
}
|
|
7165
|
+
function captureFile(onData, control) {
|
|
7166
|
+
if (!control) return sinkFile(onData);
|
|
7167
|
+
control.opened = true;
|
|
7168
|
+
return new OpenFile({
|
|
7169
|
+
write: async (bytes) => {
|
|
7170
|
+
if (control.shut) throw new KernelError("EPIPE");
|
|
7171
|
+
onData(bytes.slice());
|
|
7172
|
+
return bytes.length;
|
|
7173
|
+
},
|
|
7174
|
+
close: () => control.ended()
|
|
7175
|
+
});
|
|
7176
|
+
}
|
|
6963
7177
|
function concat3(chunks) {
|
|
6964
7178
|
const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
|
|
6965
7179
|
let offset = 0;
|
|
@@ -6994,7 +7208,12 @@ var ChildTable = class {
|
|
|
6994
7208
|
const fds = new FdTable();
|
|
6995
7209
|
const captured = /* @__PURE__ */ new Map();
|
|
6996
7210
|
try {
|
|
6997
|
-
for (const [n, slot2] of stdio.entries())
|
|
7211
|
+
for (const [n, slot2] of stdio.entries()) {
|
|
7212
|
+
fds.installAt(
|
|
7213
|
+
n,
|
|
7214
|
+
"sameAs" in slot2 ? fds.get(slot2.sameAs).retain() : this.openSlot(slot2, n, captured)
|
|
7215
|
+
);
|
|
7216
|
+
}
|
|
6998
7217
|
for (const slot2 of inherit2) {
|
|
6999
7218
|
if (slot2.fd <= 2) continue;
|
|
7000
7219
|
if ("device" in slot2) {
|
|
@@ -7051,7 +7270,7 @@ var ChildTable = class {
|
|
|
7051
7270
|
if ("capture" in slot2) {
|
|
7052
7271
|
const chunks = [];
|
|
7053
7272
|
captured.set(n, chunks);
|
|
7054
|
-
return
|
|
7273
|
+
return captureFile(capturing(chunks, slot2.max, slot2.overflow), slot2.control);
|
|
7055
7274
|
}
|
|
7056
7275
|
return deviceFile("null");
|
|
7057
7276
|
}
|
|
@@ -7244,7 +7463,7 @@ function checkIds(options) {
|
|
|
7244
7463
|
if (!ID.test(value) || Number(value) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value}`);
|
|
7245
7464
|
}
|
|
7246
7465
|
}
|
|
7247
|
-
var
|
|
7466
|
+
var MAX_LINKS7 = 40;
|
|
7248
7467
|
var STALE_RETRIES = 3;
|
|
7249
7468
|
var SETTLE_ROUNDS = 4;
|
|
7250
7469
|
var DEFAULT_IO = 8 * 1024 * 1024;
|
|
@@ -7254,7 +7473,7 @@ function parseSize(text) {
|
|
|
7254
7473
|
if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
|
|
7255
7474
|
return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
|
|
7256
7475
|
}
|
|
7257
|
-
var
|
|
7476
|
+
var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
7258
7477
|
var HeldSet = class extends Set {
|
|
7259
7478
|
idle;
|
|
7260
7479
|
constructor(idle) {
|
|
@@ -7273,7 +7492,7 @@ var HeldSet = class extends Set {
|
|
|
7273
7492
|
}
|
|
7274
7493
|
};
|
|
7275
7494
|
function heldUnder(held, target) {
|
|
7276
|
-
for (const paths of held) for (const path of paths.keys()) if (
|
|
7495
|
+
for (const paths of held) for (const path of paths.keys()) if (within5(path, target)) return true;
|
|
7277
7496
|
return false;
|
|
7278
7497
|
}
|
|
7279
7498
|
var HeldPaths = class extends Map {
|
|
@@ -7384,24 +7603,24 @@ var HeldPaths = class extends Map {
|
|
|
7384
7603
|
return this.owning === path;
|
|
7385
7604
|
}
|
|
7386
7605
|
revoke(prefix) {
|
|
7387
|
-
for (const path of this.keys()) if (
|
|
7606
|
+
for (const path of this.keys()) if (within5(path, prefix)) this.revoked.add(path);
|
|
7388
7607
|
}
|
|
7389
7608
|
renamed(from, to) {
|
|
7390
7609
|
for (const [path, count] of [...this]) {
|
|
7391
|
-
if (!
|
|
7610
|
+
if (!within5(path, from)) continue;
|
|
7392
7611
|
const moved2 = to + path.slice(from.length);
|
|
7393
7612
|
this.delete(path);
|
|
7394
7613
|
this.set(moved2, (this.get(moved2) ?? 0) + count);
|
|
7395
7614
|
if (this.revoked.delete(path)) this.revoked.add(moved2);
|
|
7396
7615
|
}
|
|
7397
7616
|
for (const [path, count] of [...this.opens]) {
|
|
7398
|
-
if (!
|
|
7617
|
+
if (!within5(path, from)) continue;
|
|
7399
7618
|
const moved2 = to + path.slice(from.length);
|
|
7400
7619
|
this.opens.delete(path);
|
|
7401
7620
|
this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
|
|
7402
7621
|
}
|
|
7403
7622
|
for (const [id2, key] of this.handles) {
|
|
7404
|
-
if (
|
|
7623
|
+
if (within5(key, from)) this.handles.set(id2, to + key.slice(from.length));
|
|
7405
7624
|
}
|
|
7406
7625
|
}
|
|
7407
7626
|
isRevoked(path) {
|
|
@@ -7508,7 +7727,7 @@ var MountTable = class {
|
|
|
7508
7727
|
locate(path) {
|
|
7509
7728
|
let best;
|
|
7510
7729
|
for (const mount of this.mounts.values()) {
|
|
7511
|
-
if (
|
|
7730
|
+
if (within5(path, mount.target) && (!best || mount.target.length > best.target.length))
|
|
7512
7731
|
best = mount;
|
|
7513
7732
|
}
|
|
7514
7733
|
if (!best) return void 0;
|
|
@@ -7651,7 +7870,7 @@ var MountTable = class {
|
|
|
7651
7870
|
async commitUnder(prefix) {
|
|
7652
7871
|
const at = normalizePath(prefix);
|
|
7653
7872
|
await Promise.all(
|
|
7654
|
-
[...this.sessions.keys()].filter((p) =>
|
|
7873
|
+
[...this.sessions.keys()].filter((p) => within5(p, at)).map((p) => this.commit(p))
|
|
7655
7874
|
);
|
|
7656
7875
|
}
|
|
7657
7876
|
async commitOverlapping(real, offset, length2) {
|
|
@@ -7686,10 +7905,10 @@ var MountTable = class {
|
|
|
7686
7905
|
}
|
|
7687
7906
|
forget(mount, path) {
|
|
7688
7907
|
mount.epoch++;
|
|
7689
|
-
for (const key of [...mount.attrs.keys()]) if (
|
|
7908
|
+
for (const key of [...mount.attrs.keys()]) if (within5(key, path)) mount.attrs.delete(key);
|
|
7690
7909
|
mount.lists.delete(path);
|
|
7691
7910
|
mount.lists.delete(parentOf7(path));
|
|
7692
|
-
for (const key of [...mount.lists.keys()]) if (
|
|
7911
|
+
for (const key of [...mount.lists.keys()]) if (within5(key, path)) mount.lists.delete(key);
|
|
7693
7912
|
}
|
|
7694
7913
|
async getattr(mount, path, rel2, settled = false) {
|
|
7695
7914
|
for (let round = 1; ; round++) {
|
|
@@ -7762,7 +7981,7 @@ var MountFs = class {
|
|
|
7762
7981
|
throw err;
|
|
7763
7982
|
}
|
|
7764
7983
|
if (attr2.kind !== "symlink") return real;
|
|
7765
|
-
if (hops >=
|
|
7984
|
+
if (hops >= MAX_LINKS7) throw errnoError("ELOOP", path);
|
|
7766
7985
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
7767
7986
|
return this.follow(this.base.resolvePath(parentOf7(real), target), hops + 1, create);
|
|
7768
7987
|
}
|
|
@@ -10558,9 +10777,11 @@ function capLimit(maxBuffer) {
|
|
|
10558
10777
|
return Math.min(asked, RUN_CAPTURE_CAP);
|
|
10559
10778
|
}
|
|
10560
10779
|
function slot(how, n, extra) {
|
|
10780
|
+
if (how === "stdout") return { sameAs: 1 };
|
|
10781
|
+
if (typeof how === "number") return { fd: how };
|
|
10561
10782
|
if (how === "inherit") return { fd: n };
|
|
10562
10783
|
if (how === "ignore") return { none: true };
|
|
10563
|
-
return extra;
|
|
10784
|
+
return extra();
|
|
10564
10785
|
}
|
|
10565
10786
|
function frame2(outcome, out, err) {
|
|
10566
10787
|
const head = new TextEncoder().encode(JSON.stringify(outcome));
|
|
@@ -10590,12 +10811,20 @@ async function runChild(req, deps) {
|
|
|
10590
10811
|
if (failure2 !== void 0) return;
|
|
10591
10812
|
failure2 = code;
|
|
10592
10813
|
if (pid !== 0) kill();
|
|
10814
|
+
decided.resolve();
|
|
10593
10815
|
};
|
|
10594
|
-
const
|
|
10816
|
+
const controls = [];
|
|
10817
|
+
const capture = () => {
|
|
10818
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
10819
|
+
const control = { shut: false, ended: resolve, eof: promise };
|
|
10820
|
+
controls.push(control);
|
|
10821
|
+
return { capture: true, max, overflow: () => stop("ENOBUFS"), control };
|
|
10822
|
+
};
|
|
10823
|
+
const decided = Promise.withResolvers();
|
|
10595
10824
|
const stdio = [
|
|
10596
|
-
slot(req.stdin, 0, { input: req.input ?? new Uint8Array(0) }),
|
|
10597
|
-
slot(req.stdout, 1, capture
|
|
10598
|
-
slot(req.stderr, 2, capture
|
|
10825
|
+
slot(req.stdin, 0, () => ({ input: req.input ?? new Uint8Array(0) })),
|
|
10826
|
+
slot(req.stdout, 1, capture),
|
|
10827
|
+
slot(req.stderr, 2, capture)
|
|
10599
10828
|
];
|
|
10600
10829
|
try {
|
|
10601
10830
|
pid = await deps.children.spawn(
|
|
@@ -10612,8 +10841,11 @@ async function runChild(req, deps) {
|
|
|
10612
10841
|
let status;
|
|
10613
10842
|
try {
|
|
10614
10843
|
status = await reaped(deps, pid);
|
|
10844
|
+
const open = controls.filter((c) => c.opened);
|
|
10845
|
+
await Promise.race([Promise.all(open.map((c) => c.eof)), decided.promise]);
|
|
10615
10846
|
} finally {
|
|
10616
10847
|
clearTimeout(timer);
|
|
10848
|
+
for (const control of controls) control.shut = true;
|
|
10617
10849
|
}
|
|
10618
10850
|
const out = deps.children.captured(pid, 1);
|
|
10619
10851
|
const err = deps.children.captured(pid, 2);
|
|
@@ -10783,7 +11015,7 @@ function isMissing(err) {
|
|
|
10783
11015
|
if (err?.code === "ENOENT") return true;
|
|
10784
11016
|
return err instanceof Error && err.message.startsWith("ENOENT");
|
|
10785
11017
|
}
|
|
10786
|
-
function
|
|
11018
|
+
function within6(path, root) {
|
|
10787
11019
|
return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
|
|
10788
11020
|
}
|
|
10789
11021
|
var VfsNode = class {
|
|
@@ -11104,12 +11336,12 @@ var VfsNodes = class {
|
|
|
11104
11336
|
return node;
|
|
11105
11337
|
}
|
|
11106
11338
|
holds(prefix) {
|
|
11107
|
-
for (const path of this.byPath.keys()) if (
|
|
11339
|
+
for (const path of this.byPath.keys()) if (within6(path, prefix)) return true;
|
|
11108
11340
|
return false;
|
|
11109
11341
|
}
|
|
11110
11342
|
writes(prefix) {
|
|
11111
11343
|
for (const [path, node] of this.byPath)
|
|
11112
|
-
if (node.writers > 0 &&
|
|
11344
|
+
if (node.writers > 0 && within6(path, prefix)) return true;
|
|
11113
11345
|
return false;
|
|
11114
11346
|
}
|
|
11115
11347
|
closed(node, writable2 = false) {
|
|
@@ -11153,11 +11385,11 @@ var VfsNodes = class {
|
|
|
11153
11385
|
this.kept.delete(node);
|
|
11154
11386
|
}
|
|
11155
11387
|
async flush(path) {
|
|
11156
|
-
for (const [p, node] of this.byPath) if (
|
|
11388
|
+
for (const [p, node] of this.byPath) if (within6(p, path)) await node.serial(() => node.flush());
|
|
11157
11389
|
}
|
|
11158
11390
|
async unlinking(path) {
|
|
11159
11391
|
for (const [held, node] of [...this.byPath]) {
|
|
11160
|
-
if (!
|
|
11392
|
+
if (!within6(held, path)) continue;
|
|
11161
11393
|
await node.serial(async () => {
|
|
11162
11394
|
await Promise.all([this.keepsOnDisk || node.keep(), node.limit()]);
|
|
11163
11395
|
});
|
|
@@ -11165,14 +11397,14 @@ var VfsNodes = class {
|
|
|
11165
11397
|
}
|
|
11166
11398
|
revoke(prefix) {
|
|
11167
11399
|
for (const [path, node] of [...this.byPath]) {
|
|
11168
|
-
if (!
|
|
11400
|
+
if (!within6(path, prefix)) continue;
|
|
11169
11401
|
node.revoke();
|
|
11170
11402
|
this.forget(node);
|
|
11171
11403
|
}
|
|
11172
11404
|
}
|
|
11173
11405
|
unlinked(path) {
|
|
11174
11406
|
for (const [held, node] of [...this.byPath]) {
|
|
11175
|
-
if (!
|
|
11407
|
+
if (!within6(held, path) || this.byPath.get(held) !== node) continue;
|
|
11176
11408
|
if (!this.dropName(held, node)) this.orphan(node);
|
|
11177
11409
|
}
|
|
11178
11410
|
}
|
|
@@ -11180,8 +11412,8 @@ var VfsNodes = class {
|
|
|
11180
11412
|
if (from === to) return;
|
|
11181
11413
|
const moved2 = [];
|
|
11182
11414
|
for (const [p, node] of [...this.byPath]) {
|
|
11183
|
-
if (
|
|
11184
|
-
else if (
|
|
11415
|
+
if (within6(p, from)) moved2.push([p, node]);
|
|
11416
|
+
else if (within6(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
|
|
11185
11417
|
this.orphan(node);
|
|
11186
11418
|
}
|
|
11187
11419
|
}
|
|
@@ -11727,6 +11959,7 @@ var WasmProcess = class {
|
|
|
11727
11959
|
this.fds.installAt(fd, await this.procFile(req, path));
|
|
11728
11960
|
return fd;
|
|
11729
11961
|
}
|
|
11962
|
+
if (this.writesAt(req) && this.options.readonlyAt?.(path)) throw new KernelError("EROFS");
|
|
11730
11963
|
const open = { ...req, path };
|
|
11731
11964
|
if (req.exclusive) {
|
|
11732
11965
|
await nodes.createExclusive(path, this.newOwner());
|
|
@@ -11772,6 +12005,9 @@ var WasmProcess = class {
|
|
|
11772
12005
|
};
|
|
11773
12006
|
return fs.exclusive ? fs.exclusive(first.path, look) : look();
|
|
11774
12007
|
}
|
|
12008
|
+
writesAt(req) {
|
|
12009
|
+
return (req.flags & WRITE_ACCESS) !== 0 || req.truncate === true || req.create === true || req.exclusive === true;
|
|
12010
|
+
}
|
|
11775
12011
|
async mayOpen(req) {
|
|
11776
12012
|
const { guard: guard2 } = this.options;
|
|
11777
12013
|
const inherited2 = req.orphan || req.contents !== void 0 || req.handle !== void 0;
|
|
@@ -12553,6 +12789,7 @@ function processOptions(opts, header3, hooks, created3) {
|
|
|
12553
12789
|
...inherited(opts),
|
|
12554
12790
|
...opts.owners ? { owners: true } : {},
|
|
12555
12791
|
...opts.sealed ? { sealed: opts.sealed } : {},
|
|
12792
|
+
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
12556
12793
|
...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
|
|
12557
12794
|
...opts.identity ? { identity: opts.identity } : {},
|
|
12558
12795
|
...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
|
|
@@ -15279,13 +15516,13 @@ function interpreter(head) {
|
|
|
15279
15516
|
function expandDefaults(defaults, env) {
|
|
15280
15517
|
const out = {};
|
|
15281
15518
|
for (const [key, value] of Object.entries(defaults ?? {})) {
|
|
15282
|
-
let
|
|
15519
|
+
let missing4 = false;
|
|
15283
15520
|
const expanded = value.replace(ENV_REFERENCE, (_, name2) => {
|
|
15284
15521
|
const set2 = Object.hasOwn(env, name2) ? env[name2] : void 0;
|
|
15285
|
-
if (set2 === void 0)
|
|
15522
|
+
if (set2 === void 0) missing4 = true;
|
|
15286
15523
|
return set2 ?? "";
|
|
15287
15524
|
});
|
|
15288
|
-
if (!
|
|
15525
|
+
if (!missing4) out[key] = expanded;
|
|
15289
15526
|
}
|
|
15290
15527
|
return out;
|
|
15291
15528
|
}
|
|
@@ -15332,6 +15569,8 @@ function concat5(chunks) {
|
|
|
15332
15569
|
}
|
|
15333
15570
|
var Launcher = class {
|
|
15334
15571
|
fs;
|
|
15572
|
+
processFs;
|
|
15573
|
+
prefixes;
|
|
15335
15574
|
base;
|
|
15336
15575
|
createWorker;
|
|
15337
15576
|
modulesDir;
|
|
@@ -15458,6 +15697,10 @@ var Launcher = class {
|
|
|
15458
15697
|
}
|
|
15459
15698
|
);
|
|
15460
15699
|
this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
|
|
15700
|
+
if (options.protect?.writer) throw new Error(PROTECT_WRITER);
|
|
15701
|
+
this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
|
|
15702
|
+
this.processFs = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes).view(this.fs) : this.fs;
|
|
15703
|
+
options.fs.protect?.(this.prefixes);
|
|
15461
15704
|
this.users = new UserDb(this.fs, this.owners);
|
|
15462
15705
|
this.sudoer = new Sudo({
|
|
15463
15706
|
fs: this.fs,
|
|
@@ -15808,7 +16051,8 @@ var Launcher = class {
|
|
|
15808
16051
|
env: req.env,
|
|
15809
16052
|
cwd: req.cwd,
|
|
15810
16053
|
fds: req.fds,
|
|
15811
|
-
fs: this.
|
|
16054
|
+
fs: this.processFs,
|
|
16055
|
+
...this.prefixes.length > 0 ? { readonlyAt: (path) => isProtected(this.prefixes, path) } : {},
|
|
15812
16056
|
createWorker: this.createWorker,
|
|
15813
16057
|
onError: req.report,
|
|
15814
16058
|
spawner: this.spawner(pid, req.report),
|
|
@@ -16093,6 +16337,18 @@ var Launcher = class {
|
|
|
16093
16337
|
const keyed = () => this.jobs.fromKeyboard(send);
|
|
16094
16338
|
this.settling.deliver(sig, pgid, keyed, tty.reading ? void 0 : foreground);
|
|
16095
16339
|
}
|
|
16340
|
+
async resolvePrefixes() {
|
|
16341
|
+
for (const prefix of [...this.prefixes]) {
|
|
16342
|
+
const real = await this.base.realpath?.(prefix, true).catch(() => void 0);
|
|
16343
|
+
if (real !== void 0 && !this.prefixes.includes(real)) this.prefixes.push(real);
|
|
16344
|
+
}
|
|
16345
|
+
}
|
|
16346
|
+
guardMount(target) {
|
|
16347
|
+
const at = normalizePath(target);
|
|
16348
|
+
if (isProtected(this.prefixes, at) || holdsProtected(this.prefixes, at)) {
|
|
16349
|
+
throw fsError("EROFS", at);
|
|
16350
|
+
}
|
|
16351
|
+
}
|
|
16096
16352
|
umaskOf(pid) {
|
|
16097
16353
|
return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
|
|
16098
16354
|
}
|
|
@@ -16227,6 +16483,7 @@ var Launcher = class {
|
|
|
16227
16483
|
};
|
|
16228
16484
|
}
|
|
16229
16485
|
async mount(spec) {
|
|
16486
|
+
this.guardMount(spec.target);
|
|
16230
16487
|
const fsa = spec.type === "fsa" && !spec.source.startsWith("fsa:");
|
|
16231
16488
|
const entry = await this.mounts.mount(
|
|
16232
16489
|
fsa ? { ...spec, source: `fsa:${crypto.randomUUID()}` } : spec,
|
|
@@ -16245,6 +16502,7 @@ var Launcher = class {
|
|
|
16245
16502
|
[`/${UNLINKED_DIR}`, 448]
|
|
16246
16503
|
];
|
|
16247
16504
|
for (const [path, mode] of layout) {
|
|
16505
|
+
if (isProtected(this.prefixes, normalizePath(path))) continue;
|
|
16248
16506
|
const real = await this.realLayoutPath(path);
|
|
16249
16507
|
const fresh2 = real !== void 0 && !await this.base.hasOwner?.(real);
|
|
16250
16508
|
if (fresh2) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
|
|
@@ -16307,6 +16565,7 @@ var Launcher = class {
|
|
|
16307
16565
|
}
|
|
16308
16566
|
async processMount(pid, call, signal) {
|
|
16309
16567
|
const spec = mountCall(call);
|
|
16568
|
+
this.guardMount(spec.target);
|
|
16310
16569
|
await this.permit({ op: "mount", pid, ...spec, options: { ...spec.options } });
|
|
16311
16570
|
await this.mountsOwn(
|
|
16312
16571
|
this.credOf(pid),
|
|
@@ -16323,6 +16582,7 @@ var Launcher = class {
|
|
|
16323
16582
|
}
|
|
16324
16583
|
async umountAs(cred, target) {
|
|
16325
16584
|
const at = normalizePath(target);
|
|
16585
|
+
this.guardMount(at);
|
|
16326
16586
|
await this.mountsOwn(cred, at, async () => Number(this.mounts.mounted(at)?.options.uid));
|
|
16327
16587
|
this.umount(at);
|
|
16328
16588
|
}
|
|
@@ -16336,6 +16596,7 @@ var Launcher = class {
|
|
|
16336
16596
|
async processUmount(pid, target, flags2) {
|
|
16337
16597
|
const call = umountCall(target, flags2);
|
|
16338
16598
|
let mounted;
|
|
16599
|
+
this.guardMount(call.target);
|
|
16339
16600
|
for (; ; ) {
|
|
16340
16601
|
mounted = this.mounts.mounted(call.target);
|
|
16341
16602
|
await this.permit({
|
|
@@ -16350,6 +16611,7 @@ var Launcher = class {
|
|
|
16350
16611
|
this.umount(call.target, call.detach);
|
|
16351
16612
|
}
|
|
16352
16613
|
async prepare() {
|
|
16614
|
+
await this.resolvePrefixes();
|
|
16353
16615
|
for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
|
|
16354
16616
|
if (this.owners) await this.base.chmod("/tmp", 1023);
|
|
16355
16617
|
await this.provisionPnpmHome();
|
|
@@ -16406,7 +16668,7 @@ var Launcher = class {
|
|
|
16406
16668
|
const start2 = dir ?? "/";
|
|
16407
16669
|
const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
|
|
16408
16670
|
const [file = ""] = argv;
|
|
16409
|
-
await this.base.mkdir(cwd, { recursive: true });
|
|
16671
|
+
if (!await this.base.exists(cwd)) await this.processFs.mkdir(cwd, { recursive: true });
|
|
16410
16672
|
const planned = await this.plan(file, argv, cwd, scope);
|
|
16411
16673
|
await this.mayExec(credOf(account), { file, cwd, pid: 0 }, planned);
|
|
16412
16674
|
return { cwd, file, planned };
|
|
@@ -16647,6 +16909,7 @@ function hostOptions(req, port, sealed) {
|
|
|
16647
16909
|
...req.hostname !== void 0 ? { hostname: req.hostname } : {},
|
|
16648
16910
|
...req.users !== void 0 ? { users: req.users } : {},
|
|
16649
16911
|
...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {},
|
|
16912
|
+
...req.protect ? { protect: { paths: [...req.protect.paths] } } : {},
|
|
16650
16913
|
...req.sealed ? { sealed } : {}
|
|
16651
16914
|
};
|
|
16652
16915
|
}
|