@ai-ecoverse/slicc-kernel 1.70.0 → 1.71.1
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 +24 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +314 -74
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +6 -1
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +304 -64
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +6 -1
- package/dist/process-worker.js.map +2 -2
- package/package.json +2 -2
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 {
|
|
@@ -991,19 +1188,28 @@ var OpfsFs = class {
|
|
|
991
1188
|
links() {
|
|
992
1189
|
return false;
|
|
993
1190
|
}
|
|
994
|
-
async link(from) {
|
|
1191
|
+
async link(from, to, follow2) {
|
|
1192
|
+
await (follow2 ? this.stat(from) : this.lstat(from));
|
|
1193
|
+
if (!(await this.stat(parentOf3(to))).isDirectory) throw fsError("ENOTDIR", to);
|
|
1194
|
+
if (await this.lstat(to).then(
|
|
1195
|
+
() => true,
|
|
1196
|
+
() => false
|
|
1197
|
+
))
|
|
1198
|
+
throw fsError("EEXIST", to);
|
|
995
1199
|
throw fsError("EPERM", `${from}: this file system has no hard links`);
|
|
996
1200
|
}
|
|
997
1201
|
async rename(from, to) {
|
|
998
1202
|
if (from === to) return;
|
|
999
1203
|
const source = await this.locate(from, false);
|
|
1204
|
+
this.refuse(source.path, true);
|
|
1000
1205
|
this.forget(source.path);
|
|
1001
1206
|
const parent = await this.locate(parentOf3(to), true);
|
|
1002
1207
|
const destination = resolvePath(parent.path, baseOf(to));
|
|
1208
|
+
this.refuse(destination, true);
|
|
1003
1209
|
const sourceStat = await this.describe(source);
|
|
1004
|
-
if (sourceStat.isDirectory &&
|
|
1210
|
+
if (sourceStat.isDirectory && within2(destination, source.path)) throw fsError("EINVAL", to);
|
|
1005
1211
|
const target = await this.locate(destination, false).catch((err) => {
|
|
1006
|
-
if (
|
|
1212
|
+
if (missing2(err)) return null;
|
|
1007
1213
|
throw err;
|
|
1008
1214
|
});
|
|
1009
1215
|
const targetStat = target && await this.describe(target);
|
|
@@ -1097,9 +1303,10 @@ var OpfsFs = class {
|
|
|
1097
1303
|
const parent = await this.locate(parentOf3(path), true);
|
|
1098
1304
|
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
1099
1305
|
const at = resolvePath(parent.path, baseOf(path));
|
|
1306
|
+
this.refuse(at);
|
|
1100
1307
|
if (await this.locate(at, false).then(
|
|
1101
1308
|
() => true,
|
|
1102
|
-
(err) => !
|
|
1309
|
+
(err) => !missing2(err)
|
|
1103
1310
|
)) {
|
|
1104
1311
|
throw fsError("EEXIST", path);
|
|
1105
1312
|
}
|
|
@@ -1126,6 +1333,7 @@ var OpfsFs = class {
|
|
|
1126
1333
|
}
|
|
1127
1334
|
async chmod(path, mode) {
|
|
1128
1335
|
const found = await this.locate(path, true);
|
|
1336
|
+
this.refuse(found.path);
|
|
1129
1337
|
const ctimeMs = Date.now();
|
|
1130
1338
|
await this.meta.update(found.path, (entry) => ({
|
|
1131
1339
|
...entry,
|
|
@@ -1141,6 +1349,7 @@ var OpfsFs = class {
|
|
|
1141
1349
|
await this.setTimes(await this.locate(path, false), atime, mtime);
|
|
1142
1350
|
}
|
|
1143
1351
|
async setTimes(found, atime, mtime) {
|
|
1352
|
+
this.refuse(found.path);
|
|
1144
1353
|
const { link: link2 } = found;
|
|
1145
1354
|
if (link2) {
|
|
1146
1355
|
const ctimeMs2 = Date.now();
|
|
@@ -1345,7 +1554,7 @@ function fresh(bytes, start2 = 0, end = bytes.length) {
|
|
|
1345
1554
|
}
|
|
1346
1555
|
|
|
1347
1556
|
// src/fs/walk.ts
|
|
1348
|
-
var
|
|
1557
|
+
var MAX_LINKS3 = 40;
|
|
1349
1558
|
function parentOf4(path) {
|
|
1350
1559
|
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
1351
1560
|
}
|
|
@@ -1374,7 +1583,7 @@ async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
|
1374
1583
|
at = next2;
|
|
1375
1584
|
continue;
|
|
1376
1585
|
}
|
|
1377
|
-
if (budget.hops >=
|
|
1586
|
+
if (budget.hops >= MAX_LINKS3) throw fsError("ELOOP", dir);
|
|
1378
1587
|
budget.hops++;
|
|
1379
1588
|
queue.unshift(...link2.split("/").filter(Boolean));
|
|
1380
1589
|
if (link2.startsWith("/")) at = "/";
|
|
@@ -1391,7 +1600,7 @@ async function realTarget(fs, path) {
|
|
|
1391
1600
|
for (; ; ) {
|
|
1392
1601
|
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
1393
1602
|
if (link2 === void 0) break;
|
|
1394
|
-
if (budget.hops >=
|
|
1603
|
+
if (budget.hops >= MAX_LINKS3) throw fsError("ELOOP", path);
|
|
1395
1604
|
budget.hops++;
|
|
1396
1605
|
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
1397
1606
|
dir = await realDir(fs, parentOf4(target), budget);
|
|
@@ -1404,7 +1613,7 @@ async function realTarget(fs, path) {
|
|
|
1404
1613
|
var R_OK = 4;
|
|
1405
1614
|
var W_OK = 2;
|
|
1406
1615
|
var X_OK = 1;
|
|
1407
|
-
var
|
|
1616
|
+
var MAX_LINKS4 = 40;
|
|
1408
1617
|
var S_ISVTX = 512;
|
|
1409
1618
|
function permOf(st, link2) {
|
|
1410
1619
|
return {
|
|
@@ -1475,7 +1684,7 @@ async function walk(fs, cred, path, follow2) {
|
|
|
1475
1684
|
const perm = await perms.at(next2);
|
|
1476
1685
|
const last = queue.length === 0;
|
|
1477
1686
|
if (perm?.link !== void 0 && (!last || follow2)) {
|
|
1478
|
-
if (++hops >
|
|
1687
|
+
if (++hops > MAX_LINKS4) return { path: next2, perm: void 0 };
|
|
1479
1688
|
queue.unshift(...parts(perm.link));
|
|
1480
1689
|
if (perm.link.startsWith("/")) at = "/";
|
|
1481
1690
|
continue;
|
|
@@ -3422,7 +3631,7 @@ var UserDb = class {
|
|
|
3422
3631
|
};
|
|
3423
3632
|
|
|
3424
3633
|
// src/client/serve-client.ts
|
|
3425
|
-
var
|
|
3634
|
+
var MAX_LINKS5 = 40;
|
|
3426
3635
|
function act(session, req, signal) {
|
|
3427
3636
|
if (req.action === "write") session.write(req.bytes);
|
|
3428
3637
|
else if (req.action === "resize") session.resize(req.cols, req.rows);
|
|
@@ -3431,22 +3640,22 @@ function act(session, req, signal) {
|
|
|
3431
3640
|
}
|
|
3432
3641
|
async function realpath(fs, path) {
|
|
3433
3642
|
let parts2 = fs.resolvePath("/", path).split("/").filter(Boolean);
|
|
3434
|
-
let
|
|
3643
|
+
let resolved2 = "";
|
|
3435
3644
|
for (let hops = 0; parts2.length > 0; ) {
|
|
3436
3645
|
const [name2, ...rest2] = parts2;
|
|
3437
|
-
const next2 = `${
|
|
3646
|
+
const next2 = `${resolved2}/${name2}`;
|
|
3438
3647
|
if (!(await fs.lstat(next2)).isSymbolicLink) {
|
|
3439
|
-
|
|
3648
|
+
resolved2 = next2;
|
|
3440
3649
|
parts2 = rest2;
|
|
3441
3650
|
continue;
|
|
3442
3651
|
}
|
|
3443
|
-
if (++hops >
|
|
3652
|
+
if (++hops > MAX_LINKS5) throw fsError("ELOOP", path);
|
|
3444
3653
|
const target = await fs.readlink(next2);
|
|
3445
|
-
const base = target.startsWith("/") ? "/" :
|
|
3654
|
+
const base = target.startsWith("/") ? "/" : resolved2 || "/";
|
|
3446
3655
|
parts2 = [...fs.resolvePath(base, target).split("/").filter(Boolean), ...rest2];
|
|
3447
|
-
|
|
3656
|
+
resolved2 = "";
|
|
3448
3657
|
}
|
|
3449
|
-
return
|
|
3658
|
+
return resolved2 || "/";
|
|
3450
3659
|
}
|
|
3451
3660
|
var CLIENT_CHECKS = {
|
|
3452
3661
|
readFile: (g, at) => readable(g, "read", at(0)),
|
|
@@ -3751,7 +3960,7 @@ function serveClient(port, host) {
|
|
|
3751
3960
|
if (!FS_METHODS.includes(req.method)) {
|
|
3752
3961
|
throw new Error(`unknown file system call ${req.method}`);
|
|
3753
3962
|
}
|
|
3754
|
-
return fsCall(l.
|
|
3963
|
+
return fsCall(l.processFs, req.method, req.args, ownerFor(l, self), guardFor(l, self));
|
|
3755
3964
|
case "watch":
|
|
3756
3965
|
case "unwatch":
|
|
3757
3966
|
return watchOp(l, req, watches, send, guardFor(l, self));
|
|
@@ -4970,11 +5179,11 @@ function ipv6(host) {
|
|
|
4970
5179
|
const back = halves.length === 2 ? groups(halves[1]) : [];
|
|
4971
5180
|
if (![...head, ...back].every((g) => /^[0-9A-Fa-f]{1,4}$/.test(g))) return void 0;
|
|
4972
5181
|
const words2 = [...head, ...back].map((g) => Number.parseInt(g, 16));
|
|
4973
|
-
const
|
|
4974
|
-
if (halves.length === 2 ?
|
|
5182
|
+
const missing4 = 8 - words2.length - tail.length;
|
|
5183
|
+
if (halves.length === 2 ? missing4 < 1 : missing4 !== 0) return void 0;
|
|
4975
5184
|
const all = [
|
|
4976
5185
|
...head.map((g) => Number.parseInt(g, 16)),
|
|
4977
|
-
...new Array(halves.length === 2 ?
|
|
5186
|
+
...new Array(halves.length === 2 ? missing4 : 0).fill(0),
|
|
4978
5187
|
...back.map((g) => Number.parseInt(g, 16)),
|
|
4979
5188
|
...tail
|
|
4980
5189
|
];
|
|
@@ -5763,7 +5972,7 @@ function parsePrefix(text) {
|
|
|
5763
5972
|
if (length2 < 0 || length2 > max) return void 0;
|
|
5764
5973
|
return { ...parsed2, length: length2 };
|
|
5765
5974
|
}
|
|
5766
|
-
function
|
|
5975
|
+
function within3(prefix, addr) {
|
|
5767
5976
|
if (prefix.v6 !== addr.v6) return false;
|
|
5768
5977
|
const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
|
|
5769
5978
|
return addr.bits >> shift === prefix.bits >> shift;
|
|
@@ -5803,9 +6012,9 @@ var Routes = class {
|
|
|
5803
6012
|
if (!addr) return "unreachable";
|
|
5804
6013
|
if (addr.v6 && addr.bits === 1n) return "loopback";
|
|
5805
6014
|
if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
|
|
5806
|
-
if (RESERVED.some((p) =>
|
|
6015
|
+
if (RESERVED.some((p) => within3(p, addr))) return "reserved";
|
|
5807
6016
|
if (addr.v6 && !this.ipv6) return "unreachable";
|
|
5808
|
-
if (this.exit || this.table.some((p) =>
|
|
6017
|
+
if (this.exit || this.table.some((p) => within3(p, addr))) return "uplink";
|
|
5809
6018
|
return "unreachable";
|
|
5810
6019
|
}
|
|
5811
6020
|
};
|
|
@@ -6419,8 +6628,8 @@ function binfmtOf(binfmts, path) {
|
|
|
6419
6628
|
var S_IFDIR2 = 16384;
|
|
6420
6629
|
var S_IFREG2 = 32768;
|
|
6421
6630
|
var COMMAND = /^\/(?:usr\/)?bin\/([^/]+)$/;
|
|
6422
|
-
var
|
|
6423
|
-
function
|
|
6631
|
+
var MAX_LINKS6 = 40;
|
|
6632
|
+
function missing3(err) {
|
|
6424
6633
|
return err?.code === "ENOENT";
|
|
6425
6634
|
}
|
|
6426
6635
|
function synthetic(directory, path) {
|
|
@@ -6439,7 +6648,7 @@ function synthetic(directory, path) {
|
|
|
6439
6648
|
}
|
|
6440
6649
|
async function followLinks(fs, path) {
|
|
6441
6650
|
let current = path;
|
|
6442
|
-
for (let hops = 0; hops <
|
|
6651
|
+
for (let hops = 0; hops < MAX_LINKS6; hops++) {
|
|
6443
6652
|
const st = await fs.lstat(current).catch(() => void 0);
|
|
6444
6653
|
if (!st?.isSymbolicLink) return current;
|
|
6445
6654
|
const parent = current.slice(0, current.lastIndexOf("/")) || "/";
|
|
@@ -6486,7 +6695,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
6486
6695
|
try {
|
|
6487
6696
|
return await real(path);
|
|
6488
6697
|
} catch (err) {
|
|
6489
|
-
const found =
|
|
6698
|
+
const found = missing3(err) ? await linked(path) : void 0;
|
|
6490
6699
|
if (found) return found;
|
|
6491
6700
|
throw err;
|
|
6492
6701
|
}
|
|
@@ -6498,7 +6707,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
6498
6707
|
try {
|
|
6499
6708
|
return await fs.readFileBuffer(path);
|
|
6500
6709
|
} catch (err) {
|
|
6501
|
-
if (
|
|
6710
|
+
if (missing3(err) && (await linked(path))?.isFile) return new Uint8Array(0);
|
|
6502
6711
|
throw err;
|
|
6503
6712
|
}
|
|
6504
6713
|
},
|
|
@@ -6514,7 +6723,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
6514
6723
|
const extra = await children2(path);
|
|
6515
6724
|
if (!extra) return fs.readdir(path);
|
|
6516
6725
|
const real = await fs.readdir(path).catch((err) => {
|
|
6517
|
-
if (
|
|
6726
|
+
if (missing3(err)) return [];
|
|
6518
6727
|
throw err;
|
|
6519
6728
|
});
|
|
6520
6729
|
return [.../* @__PURE__ */ new Set([...real, ...extra])].sort();
|
|
@@ -6523,7 +6732,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
6523
6732
|
const extra = await children2(path);
|
|
6524
6733
|
if (!extra) return fs.readdirStat(path);
|
|
6525
6734
|
const real = await fs.readdirStat(path).catch((err) => {
|
|
6526
|
-
if (
|
|
6735
|
+
if (missing3(err)) return [];
|
|
6527
6736
|
throw err;
|
|
6528
6737
|
});
|
|
6529
6738
|
const seen = new Set(real.map(([name2]) => name2));
|
|
@@ -6607,7 +6816,7 @@ var UNLINKED_DIR = ".slicc-unlinked";
|
|
|
6607
6816
|
function webLocks() {
|
|
6608
6817
|
return globalThis.navigator?.locks;
|
|
6609
6818
|
}
|
|
6610
|
-
var
|
|
6819
|
+
var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
6611
6820
|
var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6612
6821
|
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
6613
6822
|
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
@@ -6707,7 +6916,7 @@ var UnlinkedKeeper = class {
|
|
|
6707
6916
|
for (const holder of this.holders) {
|
|
6708
6917
|
if (holder.owns(target)) continue;
|
|
6709
6918
|
for (const path of holder.held()) {
|
|
6710
|
-
if (!
|
|
6919
|
+
if (!within4(path, target) || holder.isKept(path)) continue;
|
|
6711
6920
|
held.set(path, [...held.get(path) ?? [], holder]);
|
|
6712
6921
|
}
|
|
6713
6922
|
}
|
|
@@ -7261,7 +7470,7 @@ function checkIds(options) {
|
|
|
7261
7470
|
if (!ID.test(value) || Number(value) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value}`);
|
|
7262
7471
|
}
|
|
7263
7472
|
}
|
|
7264
|
-
var
|
|
7473
|
+
var MAX_LINKS7 = 40;
|
|
7265
7474
|
var STALE_RETRIES = 3;
|
|
7266
7475
|
var SETTLE_ROUNDS = 4;
|
|
7267
7476
|
var DEFAULT_IO = 8 * 1024 * 1024;
|
|
@@ -7271,7 +7480,7 @@ function parseSize(text) {
|
|
|
7271
7480
|
if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
|
|
7272
7481
|
return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
|
|
7273
7482
|
}
|
|
7274
|
-
var
|
|
7483
|
+
var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
7275
7484
|
var HeldSet = class extends Set {
|
|
7276
7485
|
idle;
|
|
7277
7486
|
constructor(idle) {
|
|
@@ -7290,7 +7499,7 @@ var HeldSet = class extends Set {
|
|
|
7290
7499
|
}
|
|
7291
7500
|
};
|
|
7292
7501
|
function heldUnder(held, target) {
|
|
7293
|
-
for (const paths of held) for (const path of paths.keys()) if (
|
|
7502
|
+
for (const paths of held) for (const path of paths.keys()) if (within5(path, target)) return true;
|
|
7294
7503
|
return false;
|
|
7295
7504
|
}
|
|
7296
7505
|
var HeldPaths = class extends Map {
|
|
@@ -7401,24 +7610,24 @@ var HeldPaths = class extends Map {
|
|
|
7401
7610
|
return this.owning === path;
|
|
7402
7611
|
}
|
|
7403
7612
|
revoke(prefix) {
|
|
7404
|
-
for (const path of this.keys()) if (
|
|
7613
|
+
for (const path of this.keys()) if (within5(path, prefix)) this.revoked.add(path);
|
|
7405
7614
|
}
|
|
7406
7615
|
renamed(from, to) {
|
|
7407
7616
|
for (const [path, count] of [...this]) {
|
|
7408
|
-
if (!
|
|
7617
|
+
if (!within5(path, from)) continue;
|
|
7409
7618
|
const moved2 = to + path.slice(from.length);
|
|
7410
7619
|
this.delete(path);
|
|
7411
7620
|
this.set(moved2, (this.get(moved2) ?? 0) + count);
|
|
7412
7621
|
if (this.revoked.delete(path)) this.revoked.add(moved2);
|
|
7413
7622
|
}
|
|
7414
7623
|
for (const [path, count] of [...this.opens]) {
|
|
7415
|
-
if (!
|
|
7624
|
+
if (!within5(path, from)) continue;
|
|
7416
7625
|
const moved2 = to + path.slice(from.length);
|
|
7417
7626
|
this.opens.delete(path);
|
|
7418
7627
|
this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
|
|
7419
7628
|
}
|
|
7420
7629
|
for (const [id2, key] of this.handles) {
|
|
7421
|
-
if (
|
|
7630
|
+
if (within5(key, from)) this.handles.set(id2, to + key.slice(from.length));
|
|
7422
7631
|
}
|
|
7423
7632
|
}
|
|
7424
7633
|
isRevoked(path) {
|
|
@@ -7525,7 +7734,7 @@ var MountTable = class {
|
|
|
7525
7734
|
locate(path) {
|
|
7526
7735
|
let best;
|
|
7527
7736
|
for (const mount of this.mounts.values()) {
|
|
7528
|
-
if (
|
|
7737
|
+
if (within5(path, mount.target) && (!best || mount.target.length > best.target.length))
|
|
7529
7738
|
best = mount;
|
|
7530
7739
|
}
|
|
7531
7740
|
if (!best) return void 0;
|
|
@@ -7668,7 +7877,7 @@ var MountTable = class {
|
|
|
7668
7877
|
async commitUnder(prefix) {
|
|
7669
7878
|
const at = normalizePath(prefix);
|
|
7670
7879
|
await Promise.all(
|
|
7671
|
-
[...this.sessions.keys()].filter((p) =>
|
|
7880
|
+
[...this.sessions.keys()].filter((p) => within5(p, at)).map((p) => this.commit(p))
|
|
7672
7881
|
);
|
|
7673
7882
|
}
|
|
7674
7883
|
async commitOverlapping(real, offset, length2) {
|
|
@@ -7703,10 +7912,10 @@ var MountTable = class {
|
|
|
7703
7912
|
}
|
|
7704
7913
|
forget(mount, path) {
|
|
7705
7914
|
mount.epoch++;
|
|
7706
|
-
for (const key of [...mount.attrs.keys()]) if (
|
|
7915
|
+
for (const key of [...mount.attrs.keys()]) if (within5(key, path)) mount.attrs.delete(key);
|
|
7707
7916
|
mount.lists.delete(path);
|
|
7708
7917
|
mount.lists.delete(parentOf7(path));
|
|
7709
|
-
for (const key of [...mount.lists.keys()]) if (
|
|
7918
|
+
for (const key of [...mount.lists.keys()]) if (within5(key, path)) mount.lists.delete(key);
|
|
7710
7919
|
}
|
|
7711
7920
|
async getattr(mount, path, rel2, settled = false) {
|
|
7712
7921
|
for (let round = 1; ; round++) {
|
|
@@ -7779,7 +7988,7 @@ var MountFs = class {
|
|
|
7779
7988
|
throw err;
|
|
7780
7989
|
}
|
|
7781
7990
|
if (attr2.kind !== "symlink") return real;
|
|
7782
|
-
if (hops >=
|
|
7991
|
+
if (hops >= MAX_LINKS7) throw errnoError("ELOOP", path);
|
|
7783
7992
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
7784
7993
|
return this.follow(this.base.resolvePath(parentOf7(real), target), hops + 1, create);
|
|
7785
7994
|
}
|
|
@@ -10813,7 +11022,7 @@ function isMissing(err) {
|
|
|
10813
11022
|
if (err?.code === "ENOENT") return true;
|
|
10814
11023
|
return err instanceof Error && err.message.startsWith("ENOENT");
|
|
10815
11024
|
}
|
|
10816
|
-
function
|
|
11025
|
+
function within6(path, root) {
|
|
10817
11026
|
return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
|
|
10818
11027
|
}
|
|
10819
11028
|
var VfsNode = class {
|
|
@@ -11134,12 +11343,12 @@ var VfsNodes = class {
|
|
|
11134
11343
|
return node;
|
|
11135
11344
|
}
|
|
11136
11345
|
holds(prefix) {
|
|
11137
|
-
for (const path of this.byPath.keys()) if (
|
|
11346
|
+
for (const path of this.byPath.keys()) if (within6(path, prefix)) return true;
|
|
11138
11347
|
return false;
|
|
11139
11348
|
}
|
|
11140
11349
|
writes(prefix) {
|
|
11141
11350
|
for (const [path, node] of this.byPath)
|
|
11142
|
-
if (node.writers > 0 &&
|
|
11351
|
+
if (node.writers > 0 && within6(path, prefix)) return true;
|
|
11143
11352
|
return false;
|
|
11144
11353
|
}
|
|
11145
11354
|
closed(node, writable2 = false) {
|
|
@@ -11183,11 +11392,11 @@ var VfsNodes = class {
|
|
|
11183
11392
|
this.kept.delete(node);
|
|
11184
11393
|
}
|
|
11185
11394
|
async flush(path) {
|
|
11186
|
-
for (const [p, node] of this.byPath) if (
|
|
11395
|
+
for (const [p, node] of this.byPath) if (within6(p, path)) await node.serial(() => node.flush());
|
|
11187
11396
|
}
|
|
11188
11397
|
async unlinking(path) {
|
|
11189
11398
|
for (const [held, node] of [...this.byPath]) {
|
|
11190
|
-
if (!
|
|
11399
|
+
if (!within6(held, path)) continue;
|
|
11191
11400
|
await node.serial(async () => {
|
|
11192
11401
|
await Promise.all([this.keepsOnDisk || node.keep(), node.limit()]);
|
|
11193
11402
|
});
|
|
@@ -11195,14 +11404,14 @@ var VfsNodes = class {
|
|
|
11195
11404
|
}
|
|
11196
11405
|
revoke(prefix) {
|
|
11197
11406
|
for (const [path, node] of [...this.byPath]) {
|
|
11198
|
-
if (!
|
|
11407
|
+
if (!within6(path, prefix)) continue;
|
|
11199
11408
|
node.revoke();
|
|
11200
11409
|
this.forget(node);
|
|
11201
11410
|
}
|
|
11202
11411
|
}
|
|
11203
11412
|
unlinked(path) {
|
|
11204
11413
|
for (const [held, node] of [...this.byPath]) {
|
|
11205
|
-
if (!
|
|
11414
|
+
if (!within6(held, path) || this.byPath.get(held) !== node) continue;
|
|
11206
11415
|
if (!this.dropName(held, node)) this.orphan(node);
|
|
11207
11416
|
}
|
|
11208
11417
|
}
|
|
@@ -11210,8 +11419,8 @@ var VfsNodes = class {
|
|
|
11210
11419
|
if (from === to) return;
|
|
11211
11420
|
const moved2 = [];
|
|
11212
11421
|
for (const [p, node] of [...this.byPath]) {
|
|
11213
|
-
if (
|
|
11214
|
-
else if (
|
|
11422
|
+
if (within6(p, from)) moved2.push([p, node]);
|
|
11423
|
+
else if (within6(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
|
|
11215
11424
|
this.orphan(node);
|
|
11216
11425
|
}
|
|
11217
11426
|
}
|
|
@@ -11757,6 +11966,7 @@ var WasmProcess = class {
|
|
|
11757
11966
|
this.fds.installAt(fd, await this.procFile(req, path));
|
|
11758
11967
|
return fd;
|
|
11759
11968
|
}
|
|
11969
|
+
if (this.writesAt(req) && this.options.readonlyAt?.(path)) throw new KernelError("EROFS");
|
|
11760
11970
|
const open = { ...req, path };
|
|
11761
11971
|
if (req.exclusive) {
|
|
11762
11972
|
await nodes.createExclusive(path, this.newOwner());
|
|
@@ -11802,6 +12012,9 @@ var WasmProcess = class {
|
|
|
11802
12012
|
};
|
|
11803
12013
|
return fs.exclusive ? fs.exclusive(first.path, look) : look();
|
|
11804
12014
|
}
|
|
12015
|
+
writesAt(req) {
|
|
12016
|
+
return (req.flags & WRITE_ACCESS) !== 0 || req.truncate === true || req.create === true || req.exclusive === true;
|
|
12017
|
+
}
|
|
11805
12018
|
async mayOpen(req) {
|
|
11806
12019
|
const { guard: guard2 } = this.options;
|
|
11807
12020
|
const inherited2 = req.orphan || req.contents !== void 0 || req.handle !== void 0;
|
|
@@ -12583,6 +12796,7 @@ function processOptions(opts, header3, hooks, created3) {
|
|
|
12583
12796
|
...inherited(opts),
|
|
12584
12797
|
...opts.owners ? { owners: true } : {},
|
|
12585
12798
|
...opts.sealed ? { sealed: opts.sealed } : {},
|
|
12799
|
+
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
12586
12800
|
...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
|
|
12587
12801
|
...opts.identity ? { identity: opts.identity } : {},
|
|
12588
12802
|
...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
|
|
@@ -15309,13 +15523,13 @@ function interpreter(head) {
|
|
|
15309
15523
|
function expandDefaults(defaults, env) {
|
|
15310
15524
|
const out = {};
|
|
15311
15525
|
for (const [key, value] of Object.entries(defaults ?? {})) {
|
|
15312
|
-
let
|
|
15526
|
+
let missing4 = false;
|
|
15313
15527
|
const expanded = value.replace(ENV_REFERENCE, (_, name2) => {
|
|
15314
15528
|
const set2 = Object.hasOwn(env, name2) ? env[name2] : void 0;
|
|
15315
|
-
if (set2 === void 0)
|
|
15529
|
+
if (set2 === void 0) missing4 = true;
|
|
15316
15530
|
return set2 ?? "";
|
|
15317
15531
|
});
|
|
15318
|
-
if (!
|
|
15532
|
+
if (!missing4) out[key] = expanded;
|
|
15319
15533
|
}
|
|
15320
15534
|
return out;
|
|
15321
15535
|
}
|
|
@@ -15362,6 +15576,8 @@ function concat5(chunks) {
|
|
|
15362
15576
|
}
|
|
15363
15577
|
var Launcher = class {
|
|
15364
15578
|
fs;
|
|
15579
|
+
processFs;
|
|
15580
|
+
prefixes;
|
|
15365
15581
|
base;
|
|
15366
15582
|
createWorker;
|
|
15367
15583
|
modulesDir;
|
|
@@ -15488,6 +15704,10 @@ var Launcher = class {
|
|
|
15488
15704
|
}
|
|
15489
15705
|
);
|
|
15490
15706
|
this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
|
|
15707
|
+
if (options.protect?.writer) throw new Error(PROTECT_WRITER);
|
|
15708
|
+
this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
|
|
15709
|
+
this.processFs = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes).view(this.fs) : this.fs;
|
|
15710
|
+
options.fs.protect?.(this.prefixes);
|
|
15491
15711
|
this.users = new UserDb(this.fs, this.owners);
|
|
15492
15712
|
this.sudoer = new Sudo({
|
|
15493
15713
|
fs: this.fs,
|
|
@@ -15838,7 +16058,8 @@ var Launcher = class {
|
|
|
15838
16058
|
env: req.env,
|
|
15839
16059
|
cwd: req.cwd,
|
|
15840
16060
|
fds: req.fds,
|
|
15841
|
-
fs: this.
|
|
16061
|
+
fs: this.processFs,
|
|
16062
|
+
...this.prefixes.length > 0 ? { readonlyAt: (path) => isProtected(this.prefixes, path) } : {},
|
|
15842
16063
|
createWorker: this.createWorker,
|
|
15843
16064
|
onError: req.report,
|
|
15844
16065
|
spawner: this.spawner(pid, req.report),
|
|
@@ -16123,6 +16344,18 @@ var Launcher = class {
|
|
|
16123
16344
|
const keyed = () => this.jobs.fromKeyboard(send);
|
|
16124
16345
|
this.settling.deliver(sig, pgid, keyed, tty.reading ? void 0 : foreground);
|
|
16125
16346
|
}
|
|
16347
|
+
async resolvePrefixes() {
|
|
16348
|
+
for (const prefix of [...this.prefixes]) {
|
|
16349
|
+
const real = await this.base.realpath?.(prefix, true).catch(() => void 0);
|
|
16350
|
+
if (real !== void 0 && !this.prefixes.includes(real)) this.prefixes.push(real);
|
|
16351
|
+
}
|
|
16352
|
+
}
|
|
16353
|
+
guardMount(target) {
|
|
16354
|
+
const at = normalizePath(target);
|
|
16355
|
+
if (isProtected(this.prefixes, at) || holdsProtected(this.prefixes, at)) {
|
|
16356
|
+
throw fsError("EROFS", at);
|
|
16357
|
+
}
|
|
16358
|
+
}
|
|
16126
16359
|
umaskOf(pid) {
|
|
16127
16360
|
return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
|
|
16128
16361
|
}
|
|
@@ -16257,6 +16490,7 @@ var Launcher = class {
|
|
|
16257
16490
|
};
|
|
16258
16491
|
}
|
|
16259
16492
|
async mount(spec) {
|
|
16493
|
+
this.guardMount(spec.target);
|
|
16260
16494
|
const fsa = spec.type === "fsa" && !spec.source.startsWith("fsa:");
|
|
16261
16495
|
const entry = await this.mounts.mount(
|
|
16262
16496
|
fsa ? { ...spec, source: `fsa:${crypto.randomUUID()}` } : spec,
|
|
@@ -16275,6 +16509,7 @@ var Launcher = class {
|
|
|
16275
16509
|
[`/${UNLINKED_DIR}`, 448]
|
|
16276
16510
|
];
|
|
16277
16511
|
for (const [path, mode] of layout) {
|
|
16512
|
+
if (isProtected(this.prefixes, normalizePath(path))) continue;
|
|
16278
16513
|
const real = await this.realLayoutPath(path);
|
|
16279
16514
|
const fresh2 = real !== void 0 && !await this.base.hasOwner?.(real);
|
|
16280
16515
|
if (fresh2) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
|
|
@@ -16337,6 +16572,7 @@ var Launcher = class {
|
|
|
16337
16572
|
}
|
|
16338
16573
|
async processMount(pid, call, signal) {
|
|
16339
16574
|
const spec = mountCall(call);
|
|
16575
|
+
this.guardMount(spec.target);
|
|
16340
16576
|
await this.permit({ op: "mount", pid, ...spec, options: { ...spec.options } });
|
|
16341
16577
|
await this.mountsOwn(
|
|
16342
16578
|
this.credOf(pid),
|
|
@@ -16353,6 +16589,7 @@ var Launcher = class {
|
|
|
16353
16589
|
}
|
|
16354
16590
|
async umountAs(cred, target) {
|
|
16355
16591
|
const at = normalizePath(target);
|
|
16592
|
+
this.guardMount(at);
|
|
16356
16593
|
await this.mountsOwn(cred, at, async () => Number(this.mounts.mounted(at)?.options.uid));
|
|
16357
16594
|
this.umount(at);
|
|
16358
16595
|
}
|
|
@@ -16366,6 +16603,7 @@ var Launcher = class {
|
|
|
16366
16603
|
async processUmount(pid, target, flags2) {
|
|
16367
16604
|
const call = umountCall(target, flags2);
|
|
16368
16605
|
let mounted;
|
|
16606
|
+
this.guardMount(call.target);
|
|
16369
16607
|
for (; ; ) {
|
|
16370
16608
|
mounted = this.mounts.mounted(call.target);
|
|
16371
16609
|
await this.permit({
|
|
@@ -16380,6 +16618,7 @@ var Launcher = class {
|
|
|
16380
16618
|
this.umount(call.target, call.detach);
|
|
16381
16619
|
}
|
|
16382
16620
|
async prepare() {
|
|
16621
|
+
await this.resolvePrefixes();
|
|
16383
16622
|
for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
|
|
16384
16623
|
if (this.owners) await this.base.chmod("/tmp", 1023);
|
|
16385
16624
|
await this.provisionPnpmHome();
|
|
@@ -16436,7 +16675,7 @@ var Launcher = class {
|
|
|
16436
16675
|
const start2 = dir ?? "/";
|
|
16437
16676
|
const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
|
|
16438
16677
|
const [file = ""] = argv;
|
|
16439
|
-
await this.base.mkdir(cwd, { recursive: true });
|
|
16678
|
+
if (!await this.base.exists(cwd)) await this.processFs.mkdir(cwd, { recursive: true });
|
|
16440
16679
|
const planned = await this.plan(file, argv, cwd, scope);
|
|
16441
16680
|
await this.mayExec(credOf(account), { file, cwd, pid: 0 }, planned);
|
|
16442
16681
|
return { cwd, file, planned };
|
|
@@ -16677,6 +16916,7 @@ function hostOptions(req, port, sealed) {
|
|
|
16677
16916
|
...req.hostname !== void 0 ? { hostname: req.hostname } : {},
|
|
16678
16917
|
...req.users !== void 0 ? { users: req.users } : {},
|
|
16679
16918
|
...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {},
|
|
16919
|
+
...req.protect ? { protect: { paths: [...req.protect.paths] } } : {},
|
|
16680
16920
|
...req.sealed ? { sealed } : {}
|
|
16681
16921
|
};
|
|
16682
16922
|
}
|