@ai-ecoverse/slicc-kernel 1.70.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 +21 -0
- package/dist/index.js +7 -2
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +306 -73
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +296 -63
- package/dist/node.js.map +4 -4
- 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
|
}
|
|
@@ -7261,7 +7463,7 @@ function checkIds(options) {
|
|
|
7261
7463
|
if (!ID.test(value) || Number(value) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value}`);
|
|
7262
7464
|
}
|
|
7263
7465
|
}
|
|
7264
|
-
var
|
|
7466
|
+
var MAX_LINKS7 = 40;
|
|
7265
7467
|
var STALE_RETRIES = 3;
|
|
7266
7468
|
var SETTLE_ROUNDS = 4;
|
|
7267
7469
|
var DEFAULT_IO = 8 * 1024 * 1024;
|
|
@@ -7271,7 +7473,7 @@ function parseSize(text) {
|
|
|
7271
7473
|
if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
|
|
7272
7474
|
return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
|
|
7273
7475
|
}
|
|
7274
|
-
var
|
|
7476
|
+
var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
7275
7477
|
var HeldSet = class extends Set {
|
|
7276
7478
|
idle;
|
|
7277
7479
|
constructor(idle) {
|
|
@@ -7290,7 +7492,7 @@ var HeldSet = class extends Set {
|
|
|
7290
7492
|
}
|
|
7291
7493
|
};
|
|
7292
7494
|
function heldUnder(held, target) {
|
|
7293
|
-
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;
|
|
7294
7496
|
return false;
|
|
7295
7497
|
}
|
|
7296
7498
|
var HeldPaths = class extends Map {
|
|
@@ -7401,24 +7603,24 @@ var HeldPaths = class extends Map {
|
|
|
7401
7603
|
return this.owning === path;
|
|
7402
7604
|
}
|
|
7403
7605
|
revoke(prefix) {
|
|
7404
|
-
for (const path of this.keys()) if (
|
|
7606
|
+
for (const path of this.keys()) if (within5(path, prefix)) this.revoked.add(path);
|
|
7405
7607
|
}
|
|
7406
7608
|
renamed(from, to) {
|
|
7407
7609
|
for (const [path, count] of [...this]) {
|
|
7408
|
-
if (!
|
|
7610
|
+
if (!within5(path, from)) continue;
|
|
7409
7611
|
const moved2 = to + path.slice(from.length);
|
|
7410
7612
|
this.delete(path);
|
|
7411
7613
|
this.set(moved2, (this.get(moved2) ?? 0) + count);
|
|
7412
7614
|
if (this.revoked.delete(path)) this.revoked.add(moved2);
|
|
7413
7615
|
}
|
|
7414
7616
|
for (const [path, count] of [...this.opens]) {
|
|
7415
|
-
if (!
|
|
7617
|
+
if (!within5(path, from)) continue;
|
|
7416
7618
|
const moved2 = to + path.slice(from.length);
|
|
7417
7619
|
this.opens.delete(path);
|
|
7418
7620
|
this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
|
|
7419
7621
|
}
|
|
7420
7622
|
for (const [id2, key] of this.handles) {
|
|
7421
|
-
if (
|
|
7623
|
+
if (within5(key, from)) this.handles.set(id2, to + key.slice(from.length));
|
|
7422
7624
|
}
|
|
7423
7625
|
}
|
|
7424
7626
|
isRevoked(path) {
|
|
@@ -7525,7 +7727,7 @@ var MountTable = class {
|
|
|
7525
7727
|
locate(path) {
|
|
7526
7728
|
let best;
|
|
7527
7729
|
for (const mount of this.mounts.values()) {
|
|
7528
|
-
if (
|
|
7730
|
+
if (within5(path, mount.target) && (!best || mount.target.length > best.target.length))
|
|
7529
7731
|
best = mount;
|
|
7530
7732
|
}
|
|
7531
7733
|
if (!best) return void 0;
|
|
@@ -7668,7 +7870,7 @@ var MountTable = class {
|
|
|
7668
7870
|
async commitUnder(prefix) {
|
|
7669
7871
|
const at = normalizePath(prefix);
|
|
7670
7872
|
await Promise.all(
|
|
7671
|
-
[...this.sessions.keys()].filter((p) =>
|
|
7873
|
+
[...this.sessions.keys()].filter((p) => within5(p, at)).map((p) => this.commit(p))
|
|
7672
7874
|
);
|
|
7673
7875
|
}
|
|
7674
7876
|
async commitOverlapping(real, offset, length2) {
|
|
@@ -7703,10 +7905,10 @@ var MountTable = class {
|
|
|
7703
7905
|
}
|
|
7704
7906
|
forget(mount, path) {
|
|
7705
7907
|
mount.epoch++;
|
|
7706
|
-
for (const key of [...mount.attrs.keys()]) if (
|
|
7908
|
+
for (const key of [...mount.attrs.keys()]) if (within5(key, path)) mount.attrs.delete(key);
|
|
7707
7909
|
mount.lists.delete(path);
|
|
7708
7910
|
mount.lists.delete(parentOf7(path));
|
|
7709
|
-
for (const key of [...mount.lists.keys()]) if (
|
|
7911
|
+
for (const key of [...mount.lists.keys()]) if (within5(key, path)) mount.lists.delete(key);
|
|
7710
7912
|
}
|
|
7711
7913
|
async getattr(mount, path, rel2, settled = false) {
|
|
7712
7914
|
for (let round = 1; ; round++) {
|
|
@@ -7779,7 +7981,7 @@ var MountFs = class {
|
|
|
7779
7981
|
throw err;
|
|
7780
7982
|
}
|
|
7781
7983
|
if (attr2.kind !== "symlink") return real;
|
|
7782
|
-
if (hops >=
|
|
7984
|
+
if (hops >= MAX_LINKS7) throw errnoError("ELOOP", path);
|
|
7783
7985
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
7784
7986
|
return this.follow(this.base.resolvePath(parentOf7(real), target), hops + 1, create);
|
|
7785
7987
|
}
|
|
@@ -10813,7 +11015,7 @@ function isMissing(err) {
|
|
|
10813
11015
|
if (err?.code === "ENOENT") return true;
|
|
10814
11016
|
return err instanceof Error && err.message.startsWith("ENOENT");
|
|
10815
11017
|
}
|
|
10816
|
-
function
|
|
11018
|
+
function within6(path, root) {
|
|
10817
11019
|
return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
|
|
10818
11020
|
}
|
|
10819
11021
|
var VfsNode = class {
|
|
@@ -11134,12 +11336,12 @@ var VfsNodes = class {
|
|
|
11134
11336
|
return node;
|
|
11135
11337
|
}
|
|
11136
11338
|
holds(prefix) {
|
|
11137
|
-
for (const path of this.byPath.keys()) if (
|
|
11339
|
+
for (const path of this.byPath.keys()) if (within6(path, prefix)) return true;
|
|
11138
11340
|
return false;
|
|
11139
11341
|
}
|
|
11140
11342
|
writes(prefix) {
|
|
11141
11343
|
for (const [path, node] of this.byPath)
|
|
11142
|
-
if (node.writers > 0 &&
|
|
11344
|
+
if (node.writers > 0 && within6(path, prefix)) return true;
|
|
11143
11345
|
return false;
|
|
11144
11346
|
}
|
|
11145
11347
|
closed(node, writable2 = false) {
|
|
@@ -11183,11 +11385,11 @@ var VfsNodes = class {
|
|
|
11183
11385
|
this.kept.delete(node);
|
|
11184
11386
|
}
|
|
11185
11387
|
async flush(path) {
|
|
11186
|
-
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());
|
|
11187
11389
|
}
|
|
11188
11390
|
async unlinking(path) {
|
|
11189
11391
|
for (const [held, node] of [...this.byPath]) {
|
|
11190
|
-
if (!
|
|
11392
|
+
if (!within6(held, path)) continue;
|
|
11191
11393
|
await node.serial(async () => {
|
|
11192
11394
|
await Promise.all([this.keepsOnDisk || node.keep(), node.limit()]);
|
|
11193
11395
|
});
|
|
@@ -11195,14 +11397,14 @@ var VfsNodes = class {
|
|
|
11195
11397
|
}
|
|
11196
11398
|
revoke(prefix) {
|
|
11197
11399
|
for (const [path, node] of [...this.byPath]) {
|
|
11198
|
-
if (!
|
|
11400
|
+
if (!within6(path, prefix)) continue;
|
|
11199
11401
|
node.revoke();
|
|
11200
11402
|
this.forget(node);
|
|
11201
11403
|
}
|
|
11202
11404
|
}
|
|
11203
11405
|
unlinked(path) {
|
|
11204
11406
|
for (const [held, node] of [...this.byPath]) {
|
|
11205
|
-
if (!
|
|
11407
|
+
if (!within6(held, path) || this.byPath.get(held) !== node) continue;
|
|
11206
11408
|
if (!this.dropName(held, node)) this.orphan(node);
|
|
11207
11409
|
}
|
|
11208
11410
|
}
|
|
@@ -11210,8 +11412,8 @@ var VfsNodes = class {
|
|
|
11210
11412
|
if (from === to) return;
|
|
11211
11413
|
const moved2 = [];
|
|
11212
11414
|
for (const [p, node] of [...this.byPath]) {
|
|
11213
|
-
if (
|
|
11214
|
-
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)) {
|
|
11215
11417
|
this.orphan(node);
|
|
11216
11418
|
}
|
|
11217
11419
|
}
|
|
@@ -11757,6 +11959,7 @@ var WasmProcess = class {
|
|
|
11757
11959
|
this.fds.installAt(fd, await this.procFile(req, path));
|
|
11758
11960
|
return fd;
|
|
11759
11961
|
}
|
|
11962
|
+
if (this.writesAt(req) && this.options.readonlyAt?.(path)) throw new KernelError("EROFS");
|
|
11760
11963
|
const open = { ...req, path };
|
|
11761
11964
|
if (req.exclusive) {
|
|
11762
11965
|
await nodes.createExclusive(path, this.newOwner());
|
|
@@ -11802,6 +12005,9 @@ var WasmProcess = class {
|
|
|
11802
12005
|
};
|
|
11803
12006
|
return fs.exclusive ? fs.exclusive(first.path, look) : look();
|
|
11804
12007
|
}
|
|
12008
|
+
writesAt(req) {
|
|
12009
|
+
return (req.flags & WRITE_ACCESS) !== 0 || req.truncate === true || req.create === true || req.exclusive === true;
|
|
12010
|
+
}
|
|
11805
12011
|
async mayOpen(req) {
|
|
11806
12012
|
const { guard: guard2 } = this.options;
|
|
11807
12013
|
const inherited2 = req.orphan || req.contents !== void 0 || req.handle !== void 0;
|
|
@@ -12583,6 +12789,7 @@ function processOptions(opts, header3, hooks, created3) {
|
|
|
12583
12789
|
...inherited(opts),
|
|
12584
12790
|
...opts.owners ? { owners: true } : {},
|
|
12585
12791
|
...opts.sealed ? { sealed: opts.sealed } : {},
|
|
12792
|
+
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
12586
12793
|
...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
|
|
12587
12794
|
...opts.identity ? { identity: opts.identity } : {},
|
|
12588
12795
|
...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
|
|
@@ -15309,13 +15516,13 @@ function interpreter(head) {
|
|
|
15309
15516
|
function expandDefaults(defaults, env) {
|
|
15310
15517
|
const out = {};
|
|
15311
15518
|
for (const [key, value] of Object.entries(defaults ?? {})) {
|
|
15312
|
-
let
|
|
15519
|
+
let missing4 = false;
|
|
15313
15520
|
const expanded = value.replace(ENV_REFERENCE, (_, name2) => {
|
|
15314
15521
|
const set2 = Object.hasOwn(env, name2) ? env[name2] : void 0;
|
|
15315
|
-
if (set2 === void 0)
|
|
15522
|
+
if (set2 === void 0) missing4 = true;
|
|
15316
15523
|
return set2 ?? "";
|
|
15317
15524
|
});
|
|
15318
|
-
if (!
|
|
15525
|
+
if (!missing4) out[key] = expanded;
|
|
15319
15526
|
}
|
|
15320
15527
|
return out;
|
|
15321
15528
|
}
|
|
@@ -15362,6 +15569,8 @@ function concat5(chunks) {
|
|
|
15362
15569
|
}
|
|
15363
15570
|
var Launcher = class {
|
|
15364
15571
|
fs;
|
|
15572
|
+
processFs;
|
|
15573
|
+
prefixes;
|
|
15365
15574
|
base;
|
|
15366
15575
|
createWorker;
|
|
15367
15576
|
modulesDir;
|
|
@@ -15488,6 +15697,10 @@ var Launcher = class {
|
|
|
15488
15697
|
}
|
|
15489
15698
|
);
|
|
15490
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);
|
|
15491
15704
|
this.users = new UserDb(this.fs, this.owners);
|
|
15492
15705
|
this.sudoer = new Sudo({
|
|
15493
15706
|
fs: this.fs,
|
|
@@ -15838,7 +16051,8 @@ var Launcher = class {
|
|
|
15838
16051
|
env: req.env,
|
|
15839
16052
|
cwd: req.cwd,
|
|
15840
16053
|
fds: req.fds,
|
|
15841
|
-
fs: this.
|
|
16054
|
+
fs: this.processFs,
|
|
16055
|
+
...this.prefixes.length > 0 ? { readonlyAt: (path) => isProtected(this.prefixes, path) } : {},
|
|
15842
16056
|
createWorker: this.createWorker,
|
|
15843
16057
|
onError: req.report,
|
|
15844
16058
|
spawner: this.spawner(pid, req.report),
|
|
@@ -16123,6 +16337,18 @@ var Launcher = class {
|
|
|
16123
16337
|
const keyed = () => this.jobs.fromKeyboard(send);
|
|
16124
16338
|
this.settling.deliver(sig, pgid, keyed, tty.reading ? void 0 : foreground);
|
|
16125
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
|
+
}
|
|
16126
16352
|
umaskOf(pid) {
|
|
16127
16353
|
return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
|
|
16128
16354
|
}
|
|
@@ -16257,6 +16483,7 @@ var Launcher = class {
|
|
|
16257
16483
|
};
|
|
16258
16484
|
}
|
|
16259
16485
|
async mount(spec) {
|
|
16486
|
+
this.guardMount(spec.target);
|
|
16260
16487
|
const fsa = spec.type === "fsa" && !spec.source.startsWith("fsa:");
|
|
16261
16488
|
const entry = await this.mounts.mount(
|
|
16262
16489
|
fsa ? { ...spec, source: `fsa:${crypto.randomUUID()}` } : spec,
|
|
@@ -16275,6 +16502,7 @@ var Launcher = class {
|
|
|
16275
16502
|
[`/${UNLINKED_DIR}`, 448]
|
|
16276
16503
|
];
|
|
16277
16504
|
for (const [path, mode] of layout) {
|
|
16505
|
+
if (isProtected(this.prefixes, normalizePath(path))) continue;
|
|
16278
16506
|
const real = await this.realLayoutPath(path);
|
|
16279
16507
|
const fresh2 = real !== void 0 && !await this.base.hasOwner?.(real);
|
|
16280
16508
|
if (fresh2) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
|
|
@@ -16337,6 +16565,7 @@ var Launcher = class {
|
|
|
16337
16565
|
}
|
|
16338
16566
|
async processMount(pid, call, signal) {
|
|
16339
16567
|
const spec = mountCall(call);
|
|
16568
|
+
this.guardMount(spec.target);
|
|
16340
16569
|
await this.permit({ op: "mount", pid, ...spec, options: { ...spec.options } });
|
|
16341
16570
|
await this.mountsOwn(
|
|
16342
16571
|
this.credOf(pid),
|
|
@@ -16353,6 +16582,7 @@ var Launcher = class {
|
|
|
16353
16582
|
}
|
|
16354
16583
|
async umountAs(cred, target) {
|
|
16355
16584
|
const at = normalizePath(target);
|
|
16585
|
+
this.guardMount(at);
|
|
16356
16586
|
await this.mountsOwn(cred, at, async () => Number(this.mounts.mounted(at)?.options.uid));
|
|
16357
16587
|
this.umount(at);
|
|
16358
16588
|
}
|
|
@@ -16366,6 +16596,7 @@ var Launcher = class {
|
|
|
16366
16596
|
async processUmount(pid, target, flags2) {
|
|
16367
16597
|
const call = umountCall(target, flags2);
|
|
16368
16598
|
let mounted;
|
|
16599
|
+
this.guardMount(call.target);
|
|
16369
16600
|
for (; ; ) {
|
|
16370
16601
|
mounted = this.mounts.mounted(call.target);
|
|
16371
16602
|
await this.permit({
|
|
@@ -16380,6 +16611,7 @@ var Launcher = class {
|
|
|
16380
16611
|
this.umount(call.target, call.detach);
|
|
16381
16612
|
}
|
|
16382
16613
|
async prepare() {
|
|
16614
|
+
await this.resolvePrefixes();
|
|
16383
16615
|
for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
|
|
16384
16616
|
if (this.owners) await this.base.chmod("/tmp", 1023);
|
|
16385
16617
|
await this.provisionPnpmHome();
|
|
@@ -16436,7 +16668,7 @@ var Launcher = class {
|
|
|
16436
16668
|
const start2 = dir ?? "/";
|
|
16437
16669
|
const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
|
|
16438
16670
|
const [file = ""] = argv;
|
|
16439
|
-
await this.base.mkdir(cwd, { recursive: true });
|
|
16671
|
+
if (!await this.base.exists(cwd)) await this.processFs.mkdir(cwd, { recursive: true });
|
|
16440
16672
|
const planned = await this.plan(file, argv, cwd, scope);
|
|
16441
16673
|
await this.mayExec(credOf(account), { file, cwd, pid: 0 }, planned);
|
|
16442
16674
|
return { cwd, file, planned };
|
|
@@ -16677,6 +16909,7 @@ function hostOptions(req, port, sealed) {
|
|
|
16677
16909
|
...req.hostname !== void 0 ? { hostname: req.hostname } : {},
|
|
16678
16910
|
...req.users !== void 0 ? { users: req.users } : {},
|
|
16679
16911
|
...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {},
|
|
16912
|
+
...req.protect ? { protect: { paths: [...req.protect.paths] } } : {},
|
|
16680
16913
|
...req.sealed ? { sealed } : {}
|
|
16681
16914
|
};
|
|
16682
16915
|
}
|