@ai-ecoverse/slicc-kernel 1.71.1 → 1.73.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 -6
- package/dist/index.js +42 -3
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +470 -51
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +37 -6
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +432 -48
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +37 -6
- package/dist/process-worker.js.map +2 -2
- package/package.json +4 -3
package/dist/node.js
CHANGED
|
@@ -371,6 +371,7 @@ var Dac = class {
|
|
|
371
371
|
log;
|
|
372
372
|
seen = /* @__PURE__ */ new Set();
|
|
373
373
|
suppressed = false;
|
|
374
|
+
tagOf = () => void 0;
|
|
374
375
|
constructor(mode, hook, log = (line) => console.warn(line)) {
|
|
375
376
|
this.mode = mode;
|
|
376
377
|
this.hook = hook;
|
|
@@ -379,7 +380,9 @@ var Dac = class {
|
|
|
379
380
|
get enforcing() {
|
|
380
381
|
return this.mode === "enforce";
|
|
381
382
|
}
|
|
382
|
-
denied(
|
|
383
|
+
denied(given) {
|
|
384
|
+
const tag = given.tag ?? (given.pid ? this.tagOf(given.pid) : void 0);
|
|
385
|
+
const denial = tag === void 0 ? given : { ...given, tag };
|
|
383
386
|
try {
|
|
384
387
|
this.hook?.({ ...denial });
|
|
385
388
|
} catch {
|
|
@@ -397,7 +400,7 @@ var Dac = class {
|
|
|
397
400
|
}
|
|
398
401
|
this.seen.add(key);
|
|
399
402
|
this.log(
|
|
400
|
-
`slicc-kernel users report: ${d.errno} ${d.op} ${d.path} (uid ${d.uid}, pid ${d.pid})`
|
|
403
|
+
`slicc-kernel users report: ${d.errno} ${d.op} ${d.path} (uid ${d.uid}, pid ${d.pid}${d.tag === void 0 ? "" : `, tag ${d.tag}`})`
|
|
401
404
|
);
|
|
402
405
|
}
|
|
403
406
|
};
|
|
@@ -413,7 +416,8 @@ var Created = class {
|
|
|
413
416
|
}
|
|
414
417
|
};
|
|
415
418
|
function deny(g, op, path, errno = "EACCES") {
|
|
416
|
-
g.
|
|
419
|
+
const tag = g.tag === void 0 ? {} : { tag: g.tag };
|
|
420
|
+
g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno, ...tag });
|
|
417
421
|
}
|
|
418
422
|
var TTY_MODE = 400;
|
|
419
423
|
function ttyOpenable(g, tty) {
|
|
@@ -2521,6 +2525,12 @@ var CLIENT_CHECKS = {
|
|
|
2521
2525
|
readlink: (g, at) => searchable(g, "readlink", at(0), false),
|
|
2522
2526
|
exists: (g, at) => searchable(g, "exists", at(0), true)
|
|
2523
2527
|
};
|
|
2528
|
+
function clientFs(l, tag, self, req) {
|
|
2529
|
+
if (!FS_METHODS.includes(req.method)) {
|
|
2530
|
+
throw new Error(`unknown file system call ${req.method}`);
|
|
2531
|
+
}
|
|
2532
|
+
return fsCall(l.fsFor(tag), req.method, req.args, ownerFor(l, self), guardFor(l, self, tag));
|
|
2533
|
+
}
|
|
2524
2534
|
async function fsCall(fs, method, args, owner, g) {
|
|
2525
2535
|
const path = (i) => fs.resolvePath("/", String(args[i]));
|
|
2526
2536
|
if (g) {
|
|
@@ -2595,9 +2605,9 @@ function ownerFor(l, self) {
|
|
|
2595
2605
|
if (!l.owners || !self) return void 0;
|
|
2596
2606
|
return { uid: self.uid, gid: self.gid, umask: self.umask ?? 18 };
|
|
2597
2607
|
}
|
|
2598
|
-
function guardFor(l, self) {
|
|
2608
|
+
function guardFor(l, self, tag) {
|
|
2599
2609
|
if (!l.dac || !self) return void 0;
|
|
2600
|
-
return { fs: l.fs, cred: credOf(self), pid: 0, dac: l.dac };
|
|
2610
|
+
return { fs: l.fs, cred: credOf(self), pid: 0, dac: l.dac, tag };
|
|
2601
2611
|
}
|
|
2602
2612
|
async function accountOf(l, user) {
|
|
2603
2613
|
const found = await l.users.lookup(user);
|
|
@@ -2701,6 +2711,7 @@ async function watchOp(l, req, watches, send, g) {
|
|
|
2701
2711
|
function serveClient(port, host) {
|
|
2702
2712
|
let state = "new";
|
|
2703
2713
|
let self;
|
|
2714
|
+
const guard2 = (l) => guardFor(l, self, host.tag);
|
|
2704
2715
|
const terminals = /* @__PURE__ */ new Map();
|
|
2705
2716
|
const groups = /* @__PURE__ */ new Set();
|
|
2706
2717
|
const sessions = /* @__PURE__ */ new Set();
|
|
@@ -2807,13 +2818,10 @@ function serveClient(port, host) {
|
|
|
2807
2818
|
case "ps":
|
|
2808
2819
|
return { result: processes(l, self) };
|
|
2809
2820
|
case "fs":
|
|
2810
|
-
|
|
2811
|
-
throw new Error(`unknown file system call ${req.method}`);
|
|
2812
|
-
}
|
|
2813
|
-
return fsCall(l.processFs, req.method, req.args, ownerFor(l, self), guardFor(l, self));
|
|
2821
|
+
return clientFs(l, host.tag, self, req);
|
|
2814
2822
|
case "watch":
|
|
2815
2823
|
case "unwatch":
|
|
2816
|
-
return watchOp(l, req, watches, send,
|
|
2824
|
+
return watchOp(l, req, watches, send, guard2(l));
|
|
2817
2825
|
case "mount":
|
|
2818
2826
|
return { result: await l.mountAs(clientCred(self), req.spec) };
|
|
2819
2827
|
case "umount":
|
|
@@ -2998,7 +3006,13 @@ var MemoryMeta = class {
|
|
|
2998
3006
|
};
|
|
2999
3007
|
|
|
3000
3008
|
// src/fs/protect.ts
|
|
3001
|
-
|
|
3009
|
+
function writerTags(protection) {
|
|
3010
|
+
const writers = protection?.writers ?? [];
|
|
3011
|
+
if (!Array.isArray(writers) || writers.some((tag) => typeof tag !== "string" || tag === "")) {
|
|
3012
|
+
throw new TypeError("protect.writers must be a list of process tags");
|
|
3013
|
+
}
|
|
3014
|
+
return new Set(writers);
|
|
3015
|
+
}
|
|
3002
3016
|
function protectedPrefixes(paths) {
|
|
3003
3017
|
return [...new Set(paths.map((p) => normalizePath(p)))];
|
|
3004
3018
|
}
|
|
@@ -3174,6 +3188,121 @@ var ProtectedFs = class {
|
|
|
3174
3188
|
}
|
|
3175
3189
|
};
|
|
3176
3190
|
|
|
3191
|
+
// src/fs/remote.ts
|
|
3192
|
+
var ANNOUNCE_WAIT_MS = 500;
|
|
3193
|
+
var PRUNE_EVERY_MS = 3e4;
|
|
3194
|
+
function lockManager() {
|
|
3195
|
+
return globalThis.navigator?.locks;
|
|
3196
|
+
}
|
|
3197
|
+
var RemoteWatch = class {
|
|
3198
|
+
channel;
|
|
3199
|
+
name;
|
|
3200
|
+
tab = crypto.randomUUID();
|
|
3201
|
+
peers = /* @__PURE__ */ new Set();
|
|
3202
|
+
others = /* @__PURE__ */ new Map();
|
|
3203
|
+
union = /* @__PURE__ */ new Set();
|
|
3204
|
+
listeners = /* @__PURE__ */ new Set();
|
|
3205
|
+
waiting = /* @__PURE__ */ new Map();
|
|
3206
|
+
locks;
|
|
3207
|
+
mine = [];
|
|
3208
|
+
seq = 0;
|
|
3209
|
+
pruned = 0;
|
|
3210
|
+
constructor(name2, options = {}) {
|
|
3211
|
+
this.name = name2;
|
|
3212
|
+
this.locks = options.locks === void 0 ? lockManager() : options.locks ?? void 0;
|
|
3213
|
+
this.channel = new BroadcastChannel(name2);
|
|
3214
|
+
this.channel.onmessage = ({ data }) => this.receive(data);
|
|
3215
|
+
this.channel.unref?.();
|
|
3216
|
+
void this.locks?.request(this.lockOf(this.tab), () => new Promise(() => {
|
|
3217
|
+
}));
|
|
3218
|
+
this.channel.postMessage({ hello: this.tab });
|
|
3219
|
+
}
|
|
3220
|
+
lockOf(tab) {
|
|
3221
|
+
return `${this.name}:${tab}`;
|
|
3222
|
+
}
|
|
3223
|
+
receive(data) {
|
|
3224
|
+
if (data.hello !== void 0) this.greeted(data.hello);
|
|
3225
|
+
if (data.here !== void 0 && data.to === this.tab) this.peers.add(data.here);
|
|
3226
|
+
if (data.tab !== void 0 && data.paths !== void 0) this.announced(data);
|
|
3227
|
+
if (data.ack !== void 0 && data.to === this.tab && data.from !== void 0) {
|
|
3228
|
+
this.acked(data.ack, data.from);
|
|
3229
|
+
}
|
|
3230
|
+
if (data.changed !== void 0) this.emit({ paths: data.changed, trees: [] });
|
|
3231
|
+
}
|
|
3232
|
+
greeted(peer) {
|
|
3233
|
+
this.peers.add(peer);
|
|
3234
|
+
this.channel.postMessage({ here: this.tab, to: peer });
|
|
3235
|
+
if (this.mine.length > 0) void this.announce(this.mine);
|
|
3236
|
+
}
|
|
3237
|
+
announced({ tab, paths, seq: seq2 }) {
|
|
3238
|
+
const from = tab;
|
|
3239
|
+
this.peers.add(from);
|
|
3240
|
+
if (paths.length > 0) this.others.set(from, paths);
|
|
3241
|
+
else this.others.delete(from);
|
|
3242
|
+
this.rebuild();
|
|
3243
|
+
this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
|
|
3244
|
+
}
|
|
3245
|
+
acked(seq2, from) {
|
|
3246
|
+
const wait2 = this.waiting.get(seq2);
|
|
3247
|
+
if (!wait2) return;
|
|
3248
|
+
wait2.left.delete(from);
|
|
3249
|
+
if (wait2.left.size === 0) wait2.done();
|
|
3250
|
+
}
|
|
3251
|
+
rebuild() {
|
|
3252
|
+
this.union.clear();
|
|
3253
|
+
for (const paths of this.others.values()) for (const path of paths) this.union.add(path);
|
|
3254
|
+
}
|
|
3255
|
+
async prune() {
|
|
3256
|
+
if (!this.locks) return;
|
|
3257
|
+
const held = new Set(((await this.locks.query()).held ?? []).map((lock) => lock.name));
|
|
3258
|
+
for (const tab of /* @__PURE__ */ new Set([...this.others.keys(), ...this.peers])) {
|
|
3259
|
+
if (!held.has(this.lockOf(tab))) {
|
|
3260
|
+
this.others.delete(tab);
|
|
3261
|
+
this.peers.delete(tab);
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
this.rebuild();
|
|
3265
|
+
}
|
|
3266
|
+
watched() {
|
|
3267
|
+
return this.union.size > 0;
|
|
3268
|
+
}
|
|
3269
|
+
wrote(path) {
|
|
3270
|
+
if (!this.union.has(path)) return;
|
|
3271
|
+
this.channel.postMessage({ changed: [path] });
|
|
3272
|
+
const now = Date.now();
|
|
3273
|
+
if (now - this.pruned < PRUNE_EVERY_MS) return;
|
|
3274
|
+
this.pruned = now;
|
|
3275
|
+
void this.prune();
|
|
3276
|
+
}
|
|
3277
|
+
announce(paths) {
|
|
3278
|
+
this.mine = [...paths];
|
|
3279
|
+
const seq2 = ++this.seq;
|
|
3280
|
+
this.channel.postMessage({ tab: this.tab, paths: this.mine, seq: seq2 });
|
|
3281
|
+
if (this.peers.size === 0) return Promise.resolve();
|
|
3282
|
+
return new Promise((resolve) => {
|
|
3283
|
+
const done4 = () => {
|
|
3284
|
+
clearTimeout(timer);
|
|
3285
|
+
this.waiting.delete(seq2);
|
|
3286
|
+
resolve();
|
|
3287
|
+
};
|
|
3288
|
+
const timer = setTimeout(() => {
|
|
3289
|
+
done4();
|
|
3290
|
+
void this.prune();
|
|
3291
|
+
}, ANNOUNCE_WAIT_MS);
|
|
3292
|
+
this.waiting.set(seq2, { left: new Set(this.peers), done: done4 });
|
|
3293
|
+
});
|
|
3294
|
+
}
|
|
3295
|
+
emit(change) {
|
|
3296
|
+
for (const listener of this.listeners) listener(change);
|
|
3297
|
+
}
|
|
3298
|
+
listen(listener) {
|
|
3299
|
+
this.listeners.add(listener);
|
|
3300
|
+
return () => {
|
|
3301
|
+
this.listeners.delete(listener);
|
|
3302
|
+
};
|
|
3303
|
+
}
|
|
3304
|
+
};
|
|
3305
|
+
|
|
3177
3306
|
// src/fs/opfs.ts
|
|
3178
3307
|
var S_IFDIR = 16384;
|
|
3179
3308
|
var S_IFREG = 32768;
|
|
@@ -3297,6 +3426,7 @@ var OpfsFs = class {
|
|
|
3297
3426
|
dirs = new PathMap();
|
|
3298
3427
|
checked = /* @__PURE__ */ new WeakMap();
|
|
3299
3428
|
channel;
|
|
3429
|
+
remote;
|
|
3300
3430
|
ranged;
|
|
3301
3431
|
owners = false;
|
|
3302
3432
|
constructor(root, meta = new MemoryMeta(), channel, options = {}) {
|
|
@@ -3304,9 +3434,15 @@ var OpfsFs = class {
|
|
|
3304
3434
|
this.meta = sidecar(meta);
|
|
3305
3435
|
this.ranged = options.ranged === true;
|
|
3306
3436
|
this.channel = channel === void 0 ? void 0 : new BroadcastChannel(channel);
|
|
3437
|
+
this.remote = channel === void 0 ? void 0 : new RemoteWatch(`${channel}:watch`);
|
|
3307
3438
|
if (this.channel) {
|
|
3439
|
+
const remote = this.remote;
|
|
3440
|
+
meta.listen?.(
|
|
3441
|
+
(c) => remote?.emit({ paths: c.paths ?? [], trees: c.trees ?? [] })
|
|
3442
|
+
);
|
|
3308
3443
|
this.channel.onmessage = ({ data }) => {
|
|
3309
3444
|
for (const path of data) this.forget(path);
|
|
3445
|
+
remote?.emit({ paths: data, trees: [] });
|
|
3310
3446
|
};
|
|
3311
3447
|
this.channel.unref?.();
|
|
3312
3448
|
}
|
|
@@ -3315,6 +3451,16 @@ var OpfsFs = class {
|
|
|
3315
3451
|
for (const path of paths) this.forget(path);
|
|
3316
3452
|
this.channel?.postMessage(paths);
|
|
3317
3453
|
}
|
|
3454
|
+
remoteChanges(listener) {
|
|
3455
|
+
return this.remote?.listen(listener) ?? (() => {
|
|
3456
|
+
});
|
|
3457
|
+
}
|
|
3458
|
+
announceWatch(paths) {
|
|
3459
|
+
return this.remote?.announce(paths) ?? Promise.resolve();
|
|
3460
|
+
}
|
|
3461
|
+
committed(target) {
|
|
3462
|
+
if (this.remote?.watched()) this.remote.wrote(target);
|
|
3463
|
+
}
|
|
3318
3464
|
resolvePath(base, path) {
|
|
3319
3465
|
return resolvePath(base, path);
|
|
3320
3466
|
}
|
|
@@ -3374,6 +3520,9 @@ var OpfsFs = class {
|
|
|
3374
3520
|
protect(prefixes2) {
|
|
3375
3521
|
this.guarded = prefixes2;
|
|
3376
3522
|
}
|
|
3523
|
+
unguarded() {
|
|
3524
|
+
return Object.create(this, { guarded: { value: [] } });
|
|
3525
|
+
}
|
|
3377
3526
|
refuse(path, holding = false) {
|
|
3378
3527
|
if (isProtected(this.guarded, path) || holding && holdsProtected(this.guarded, path)) {
|
|
3379
3528
|
throw fsError("EROFS", path);
|
|
@@ -3543,15 +3692,15 @@ var OpfsFs = class {
|
|
|
3543
3692
|
}
|
|
3544
3693
|
dirTimes = /* @__PURE__ */ new Map();
|
|
3545
3694
|
dirDirty = /* @__PURE__ */ new Set();
|
|
3546
|
-
|
|
3695
|
+
flushing = {};
|
|
3547
3696
|
touch(...dirs) {
|
|
3548
3697
|
const now = Date.now();
|
|
3549
3698
|
for (const dir of dirs) {
|
|
3550
3699
|
this.dirTimes.set(dir, now);
|
|
3551
3700
|
this.dirDirty.add(dir);
|
|
3552
3701
|
}
|
|
3553
|
-
this.
|
|
3554
|
-
this.
|
|
3702
|
+
this.flushing.dir ??= Promise.resolve().then(() => this.flushDirTimes()).catch(() => void 0).finally(() => {
|
|
3703
|
+
this.flushing.dir = void 0;
|
|
3555
3704
|
});
|
|
3556
3705
|
}
|
|
3557
3706
|
async flushDirTimes() {
|
|
@@ -3666,15 +3815,18 @@ var OpfsFs = class {
|
|
|
3666
3815
|
);
|
|
3667
3816
|
}
|
|
3668
3817
|
async pwrite(path, offset, bytes) {
|
|
3669
|
-
const handle =
|
|
3818
|
+
const [handle, , target] = await this.fileFor(path);
|
|
3670
3819
|
await this.retrying(path, () => positioned(handle, (io) => io.write(offset, bytes)), true);
|
|
3820
|
+
this.committed(target);
|
|
3671
3821
|
}
|
|
3672
3822
|
async truncate(path, size) {
|
|
3673
3823
|
const handle = await this.existing(path, true);
|
|
3824
|
+
const target = this.remote?.watched() ? (await this.locate(path, true)).path : path;
|
|
3674
3825
|
await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
|
|
3826
|
+
this.committed(target);
|
|
3675
3827
|
}
|
|
3676
3828
|
async writeSession(path, truncate) {
|
|
3677
|
-
const handle =
|
|
3829
|
+
const [handle, , target] = await this.fileFor(path);
|
|
3678
3830
|
const writable2 = await this.retrying(
|
|
3679
3831
|
path,
|
|
3680
3832
|
() => handle.createWritable({ keepExistingData: !truncate }),
|
|
@@ -3687,9 +3839,12 @@ var OpfsFs = class {
|
|
|
3687
3839
|
truncate: (size) => writable2.truncate(size).catch((err) => {
|
|
3688
3840
|
throw translate(err, path, "write", false);
|
|
3689
3841
|
}),
|
|
3690
|
-
close:
|
|
3691
|
-
|
|
3692
|
-
|
|
3842
|
+
close: async () => {
|
|
3843
|
+
await writable2.close().catch((err) => {
|
|
3844
|
+
throw translate(err, path, "commit", false);
|
|
3845
|
+
});
|
|
3846
|
+
this.committed(target);
|
|
3847
|
+
}
|
|
3693
3848
|
};
|
|
3694
3849
|
}
|
|
3695
3850
|
async readFile(path) {
|
|
@@ -3738,6 +3893,7 @@ var OpfsFs = class {
|
|
|
3738
3893
|
await writable2?.abort().catch(() => void 0);
|
|
3739
3894
|
throw translate(err, path, "commit", false);
|
|
3740
3895
|
}
|
|
3896
|
+
this.committed(target);
|
|
3741
3897
|
}
|
|
3742
3898
|
async listing(path) {
|
|
3743
3899
|
const found = await this.locate(path, true);
|
|
@@ -3759,12 +3915,12 @@ var OpfsFs = class {
|
|
|
3759
3915
|
const base = dir === "/" ? "" : dir;
|
|
3760
3916
|
const metas = await this.meta.get(children2.map(([name2]) => `${base}/${name2}`));
|
|
3761
3917
|
const handed2 = this.owners ? await this.handedTo(dir) : void 0;
|
|
3762
|
-
const
|
|
3918
|
+
const owned4 = (stat, entry) => handed2 ? { ...stat, ...ownOf(entry) ?? handed2 } : stat;
|
|
3763
3919
|
return Promise.all(
|
|
3764
3920
|
children2.map(async ([name2, child], i) => {
|
|
3765
|
-
if (!isHandle(child)) return [name2,
|
|
3921
|
+
if (!isHandle(child)) return [name2, owned4(this.linkStat(child), child)];
|
|
3766
3922
|
const stat = await this.statOf(`${base}/${name2}`, child, metas[i]).catch(() => null);
|
|
3767
|
-
return [name2, stat &&
|
|
3923
|
+
return [name2, stat && owned4(stat, metas[i])];
|
|
3768
3924
|
})
|
|
3769
3925
|
);
|
|
3770
3926
|
}
|
|
@@ -5273,6 +5429,136 @@ function binfmtOf(binfmts, path) {
|
|
|
5273
5429
|
return best === void 0 ? void 0 : binfmts.get(best);
|
|
5274
5430
|
}
|
|
5275
5431
|
|
|
5432
|
+
// src/fs/attest.ts
|
|
5433
|
+
var ATTEST_MAX = 1024 * 1024;
|
|
5434
|
+
var UNOWNED = 1e3;
|
|
5435
|
+
function refuse(code, path, why) {
|
|
5436
|
+
throw Object.assign(new Error(`attest ${path}: ${why}`), { code });
|
|
5437
|
+
}
|
|
5438
|
+
function chainOf(path) {
|
|
5439
|
+
const parts2 = path.split("/").filter(Boolean);
|
|
5440
|
+
return ["/", ...parts2.map((_, i) => `/${parts2.slice(0, i + 1).join("/")}`)];
|
|
5441
|
+
}
|
|
5442
|
+
function under2(path, target) {
|
|
5443
|
+
return path === target || path.startsWith(`${target}/`);
|
|
5444
|
+
}
|
|
5445
|
+
function owned(path, st) {
|
|
5446
|
+
return { path, uid: st.uid ?? UNOWNED, gid: st.gid ?? UNOWNED, mode: st.mode };
|
|
5447
|
+
}
|
|
5448
|
+
function unchanged(a, b, bytes) {
|
|
5449
|
+
return a.ino === b.ino && a.mtime.getTime() === b.mtime.getTime() && a.size === b.size && bytes.length === b.size;
|
|
5450
|
+
}
|
|
5451
|
+
async function sha256(bytes) {
|
|
5452
|
+
const digest = new Uint8Array(
|
|
5453
|
+
await crypto.subtle.digest("SHA-256", bytes)
|
|
5454
|
+
);
|
|
5455
|
+
return Array.from(digest, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
5456
|
+
}
|
|
5457
|
+
function attested(path, st, bytes, digest, o, parents) {
|
|
5458
|
+
const { uid, gid, mode } = owned(path, st);
|
|
5459
|
+
return {
|
|
5460
|
+
path,
|
|
5461
|
+
bytes,
|
|
5462
|
+
uid,
|
|
5463
|
+
gid,
|
|
5464
|
+
mode,
|
|
5465
|
+
ino: st.ino,
|
|
5466
|
+
mtimeMs: st.mtime.getTime(),
|
|
5467
|
+
sha256: digest,
|
|
5468
|
+
users: o.users,
|
|
5469
|
+
onRootFs: true,
|
|
5470
|
+
parents
|
|
5471
|
+
};
|
|
5472
|
+
}
|
|
5473
|
+
async function trustedParents(fs, path, o) {
|
|
5474
|
+
const parents = [];
|
|
5475
|
+
for (const dir of chainOf(path).slice(0, -1)) {
|
|
5476
|
+
const st = await fs.lstat(dir);
|
|
5477
|
+
if (st.isSymbolicLink) refuse("EACCES", path, `${dir} is a symlink`);
|
|
5478
|
+
if (!st.isDirectory) refuse("ENOTDIR", path, `${dir} is not a directory`);
|
|
5479
|
+
const perm = owned(dir, st);
|
|
5480
|
+
if (o.users !== "off" && (perm.uid !== 0 || (perm.mode & 18) !== 0)) {
|
|
5481
|
+
refuse("EACCES", path, `${dir} is not root-owned or is writable by group or others`);
|
|
5482
|
+
}
|
|
5483
|
+
parents.push(perm);
|
|
5484
|
+
}
|
|
5485
|
+
return parents;
|
|
5486
|
+
}
|
|
5487
|
+
async function attest(fs, asked, o) {
|
|
5488
|
+
if (typeof asked !== "string" || !asked.startsWith("/")) {
|
|
5489
|
+
refuse("EINVAL", String(asked), "not an absolute path");
|
|
5490
|
+
}
|
|
5491
|
+
const path = normalizePath(asked);
|
|
5492
|
+
const mount = o.mounts.find((target) => {
|
|
5493
|
+
const at = normalizePath(target);
|
|
5494
|
+
return at === "/" || under2(path, at);
|
|
5495
|
+
});
|
|
5496
|
+
if (mount !== void 0) refuse("EACCES", path, `under the mount ${mount}`);
|
|
5497
|
+
const parents = await trustedParents(fs, path, o);
|
|
5498
|
+
for (let tries = 0; ; tries++) {
|
|
5499
|
+
const before = await fs.lstat(path);
|
|
5500
|
+
if (before.isSymbolicLink) refuse("EACCES", path, "is a symlink");
|
|
5501
|
+
if (!before.isFile)
|
|
5502
|
+
refuse(before.isDirectory ? "EISDIR" : "EINVAL", path, "is not a regular file");
|
|
5503
|
+
if (before.size > ATTEST_MAX) refuse("EFBIG", path, `is over ${ATTEST_MAX} bytes`);
|
|
5504
|
+
const bytes = (await fs.readFileBuffer(path)).slice();
|
|
5505
|
+
const after = await fs.lstat(path);
|
|
5506
|
+
if (unchanged(before, after, bytes)) {
|
|
5507
|
+
return attested(path, after, bytes, await sha256(bytes), o, parents);
|
|
5508
|
+
}
|
|
5509
|
+
if (tries >= 1) refuse("EAGAIN", path, "changed while it was read");
|
|
5510
|
+
}
|
|
5511
|
+
}
|
|
5512
|
+
var WATCH_DEBOUNCE_MS = 50;
|
|
5513
|
+
var FileWatches = class {
|
|
5514
|
+
live = /* @__PURE__ */ new Set();
|
|
5515
|
+
local;
|
|
5516
|
+
remote;
|
|
5517
|
+
constructor(local, remote) {
|
|
5518
|
+
this.local = local;
|
|
5519
|
+
this.remote = remote;
|
|
5520
|
+
}
|
|
5521
|
+
announce() {
|
|
5522
|
+
return this.remote.announceWatch?.([...new Set([...this.live].flat())]) ?? Promise.resolve();
|
|
5523
|
+
}
|
|
5524
|
+
async watch(paths, notify) {
|
|
5525
|
+
const files = paths.map(normalizePath);
|
|
5526
|
+
const timers = /* @__PURE__ */ new Map();
|
|
5527
|
+
let active = true;
|
|
5528
|
+
const fire = (changed, trees) => {
|
|
5529
|
+
for (const file of files) {
|
|
5530
|
+
const hit = [...changed, ...trees].some((p) => under2(file, normalizePath(p)));
|
|
5531
|
+
if (!hit) continue;
|
|
5532
|
+
clearTimeout(timers.get(file));
|
|
5533
|
+
timers.set(
|
|
5534
|
+
file,
|
|
5535
|
+
setTimeout(() => {
|
|
5536
|
+
timers.delete(file);
|
|
5537
|
+
if (active) notify(file);
|
|
5538
|
+
}, WATCH_DEBOUNCE_MS)
|
|
5539
|
+
);
|
|
5540
|
+
}
|
|
5541
|
+
};
|
|
5542
|
+
const unwatch = this.local.watch(
|
|
5543
|
+
files,
|
|
5544
|
+
{ recursive: false },
|
|
5545
|
+
(change) => fire("overflow" in change ? files : change.paths, [])
|
|
5546
|
+
);
|
|
5547
|
+
const unlisten = this.remote.remoteChanges?.((change) => fire(change.paths, change.trees));
|
|
5548
|
+
this.live.add(files);
|
|
5549
|
+
await this.announce();
|
|
5550
|
+
return () => {
|
|
5551
|
+
if (!active) return;
|
|
5552
|
+
active = false;
|
|
5553
|
+
for (const timer of timers.values()) clearTimeout(timer);
|
|
5554
|
+
unwatch();
|
|
5555
|
+
unlisten?.();
|
|
5556
|
+
this.live.delete(files);
|
|
5557
|
+
void this.announce();
|
|
5558
|
+
};
|
|
5559
|
+
}
|
|
5560
|
+
};
|
|
5561
|
+
|
|
5276
5562
|
// src/fs/commands.ts
|
|
5277
5563
|
var S_IFDIR2 = 16384;
|
|
5278
5564
|
var S_IFREG2 = 32768;
|
|
@@ -5311,7 +5597,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
5311
5597
|
if (st || path === "/") return st ?? {};
|
|
5312
5598
|
return nearest(path.slice(0, path.lastIndexOf("/")) || "/");
|
|
5313
5599
|
}
|
|
5314
|
-
async function
|
|
5600
|
+
async function owned4(stat2, dir) {
|
|
5315
5601
|
if (!owners) return stat2;
|
|
5316
5602
|
const { uid = 0, gid = 0 } = dir === void 0 ? {} : await nearest(dir);
|
|
5317
5603
|
return { ...stat2, uid, gid };
|
|
@@ -5325,17 +5611,17 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
5325
5611
|
}
|
|
5326
5612
|
async function virtual(path) {
|
|
5327
5613
|
if (path === "/usr" || path === "/bin" || path === "/usr/bin") {
|
|
5328
|
-
return
|
|
5614
|
+
return owned4(synthetic(true, path));
|
|
5329
5615
|
}
|
|
5330
5616
|
const name2 = COMMAND.exec(path)?.[1];
|
|
5331
5617
|
if (name2 !== void 0) {
|
|
5332
|
-
return (await names()).has(name2) ?
|
|
5618
|
+
return (await names()).has(name2) ? owned4(synthetic(false, path)) : void 0;
|
|
5333
5619
|
}
|
|
5334
|
-
if ((await userDir(path))?.size) return
|
|
5620
|
+
if ((await userDir(path))?.size) return owned4(synthetic(true, path), path);
|
|
5335
5621
|
const slash = path.lastIndexOf("/");
|
|
5336
5622
|
const dir = path.slice(0, slash);
|
|
5337
5623
|
const own = await userDir(dir);
|
|
5338
|
-
return own?.has(path.slice(slash + 1)) ?
|
|
5624
|
+
return own?.has(path.slice(slash + 1)) ? owned4(synthetic(false, path), dir) : void 0;
|
|
5339
5625
|
}
|
|
5340
5626
|
async function linked(path) {
|
|
5341
5627
|
return await virtual(path) ?? virtual(await followLinks(fs, path));
|
|
@@ -5462,11 +5748,11 @@ async function physicalPathAsync(fs, path) {
|
|
|
5462
5748
|
|
|
5463
5749
|
// src/fs/watch.ts
|
|
5464
5750
|
var WATCH_BATCH_LIMIT = 1e3;
|
|
5465
|
-
var
|
|
5751
|
+
var under3 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
|
|
5466
5752
|
var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
5467
5753
|
function touches(watcher, changed) {
|
|
5468
5754
|
return watcher.paths.some(
|
|
5469
|
-
(watched) => changed === watched ||
|
|
5755
|
+
(watched) => changed === watched || under3(watched, changed) || (watcher.recursive ? under3(changed, watched) : parentOf6(changed) === watched)
|
|
5470
5756
|
);
|
|
5471
5757
|
}
|
|
5472
5758
|
var FsWatchers = class {
|
|
@@ -7185,7 +7471,7 @@ async function keptStat(entry, req) {
|
|
|
7185
7471
|
return { ok: false, errno: "ENOENT", message: "the handle keeps no file" };
|
|
7186
7472
|
return json2(statJson(await entry.fs.stat(hidden)));
|
|
7187
7473
|
}
|
|
7188
|
-
async function
|
|
7474
|
+
async function owned2(entry, own, req, path, target) {
|
|
7189
7475
|
const source = target === path ? void 0 : path;
|
|
7190
7476
|
if (UNLINKING_OPS.has(req.op) && await linkedElsewhere(entry.fs, target, source)) {
|
|
7191
7477
|
return run(entry, path, req);
|
|
@@ -7240,7 +7526,7 @@ async function dispatchOne(entry, req) {
|
|
|
7240
7526
|
}
|
|
7241
7527
|
if (!entry.own || !OWN_OPS.has(req.op)) return await run(entry, path, req);
|
|
7242
7528
|
const target = TWO_PATHS.has(req.op) ? fs.resolvePath(cwd, req.arg2 ?? "") : path;
|
|
7243
|
-
return await
|
|
7529
|
+
return await owned2(entry, entry.own, req, path, target);
|
|
7244
7530
|
} catch (err) {
|
|
7245
7531
|
return toErrno(err);
|
|
7246
7532
|
}
|
|
@@ -11443,6 +11729,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
11443
11729
|
"proc-sudo",
|
|
11444
11730
|
"proc-run",
|
|
11445
11731
|
"proc-exit",
|
|
11732
|
+
"proc-kill-self",
|
|
11446
11733
|
"proc-fork",
|
|
11447
11734
|
"proc-kill",
|
|
11448
11735
|
"thread-kill",
|
|
@@ -11570,6 +11857,20 @@ var WasmProcess = class {
|
|
|
11570
11857
|
else this.adoptExec(pid);
|
|
11571
11858
|
return pid;
|
|
11572
11859
|
}
|
|
11860
|
+
async killSelf(sig, target, probe2) {
|
|
11861
|
+
if (!this.options.exitSignal) throw new KernelError("ENOSYS");
|
|
11862
|
+
if (!isSignal(sig) || defaultAction(sig) !== "terminate") throw new KernelError("EINVAL");
|
|
11863
|
+
const { caught, ignored } = this.signalMasks();
|
|
11864
|
+
const to = target === 0 ? -this.pgid() : target ?? this.pid;
|
|
11865
|
+
if (sig !== SIG.KILL && (caught | ignored) & sigbit(sig)) {
|
|
11866
|
+
if (!await this.options.kill?.(to, sig)) throw new KernelError("ESRCH");
|
|
11867
|
+
return { ok: true, kind: "void" };
|
|
11868
|
+
}
|
|
11869
|
+
if (probe2) return { ok: true, kind: "json", json: "terminate" };
|
|
11870
|
+
if (to !== this.pid) await this.options.kill?.(to, sig);
|
|
11871
|
+
this.options.exitSignal(sig);
|
|
11872
|
+
return DYING;
|
|
11873
|
+
}
|
|
11573
11874
|
run(req) {
|
|
11574
11875
|
return runChild(req, {
|
|
11575
11876
|
children: this.children,
|
|
@@ -12167,6 +12468,8 @@ var WasmProcess = class {
|
|
|
12167
12468
|
if (!this.options.exitNow) throw new KernelError("ENOSYS");
|
|
12168
12469
|
this.options.exitNow(req.status & 255);
|
|
12169
12470
|
return DYING;
|
|
12471
|
+
case "proc-kill-self":
|
|
12472
|
+
return this.killSelf(req.sig, req.target, req.probe);
|
|
12170
12473
|
case "proc-identity":
|
|
12171
12474
|
return { ok: true, kind: "json", json: await this.options.identity?.() ?? null };
|
|
12172
12475
|
case "proc-exec": {
|
|
@@ -12601,6 +12904,7 @@ function processOptions(opts, header4, hooks, created3) {
|
|
|
12601
12904
|
raise: hooks.raise,
|
|
12602
12905
|
threadKill: hooks.threadKill,
|
|
12603
12906
|
...hooks.exitNow ? { exitNow: hooks.exitNow } : {},
|
|
12907
|
+
...hooks.exitSignal ? { exitSignal: hooks.exitSignal } : {},
|
|
12604
12908
|
...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header4, SAB_I_SIGNALS) } : {}
|
|
12605
12909
|
};
|
|
12606
12910
|
}
|
|
@@ -12634,7 +12938,10 @@ function spawnWasmProcess(opts) {
|
|
|
12634
12938
|
{
|
|
12635
12939
|
raise: (sig) => signal(sig),
|
|
12636
12940
|
threadKill: (tid, sig) => killThread(threadSignals.get(tid), process2.signalMasks().caught, sig),
|
|
12637
|
-
...opts.program.abi === "js" ? {
|
|
12941
|
+
...opts.program.abi === "js" ? {
|
|
12942
|
+
exitNow: (code) => exitNow(code),
|
|
12943
|
+
exitSignal: (sig) => queueMicrotask(() => finish(128 + sig, sig))
|
|
12944
|
+
} : {}
|
|
12638
12945
|
},
|
|
12639
12946
|
created3
|
|
12640
12947
|
)
|
|
@@ -14542,8 +14849,8 @@ var ChunkedBytes = class {
|
|
|
14542
14849
|
}
|
|
14543
14850
|
return out;
|
|
14544
14851
|
}
|
|
14545
|
-
write(offset, bytes,
|
|
14546
|
-
const whole =
|
|
14852
|
+
write(offset, bytes, owned4 = false) {
|
|
14853
|
+
const whole = owned4 && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
14547
14854
|
for (let done4 = 0; done4 < bytes.length; ) {
|
|
14548
14855
|
const at = offset + done4;
|
|
14549
14856
|
const index = Math.floor(at / CHUNK3);
|
|
@@ -14593,7 +14900,7 @@ function created2(ino, kind, mode, from, owner) {
|
|
|
14593
14900
|
gid: owner?.gid ?? from?.gid ?? 0
|
|
14594
14901
|
};
|
|
14595
14902
|
}
|
|
14596
|
-
function
|
|
14903
|
+
function owned3(node, change) {
|
|
14597
14904
|
if (change.uid !== void 0) node.uid = change.uid;
|
|
14598
14905
|
if (change.gid !== void 0) node.gid = change.gid;
|
|
14599
14906
|
}
|
|
@@ -14764,7 +15071,7 @@ function tmpfs() {
|
|
|
14764
15071
|
node.mtime = Date.now();
|
|
14765
15072
|
}
|
|
14766
15073
|
if (change.mtime !== void 0) node.mtime = change.mtime;
|
|
14767
|
-
|
|
15074
|
+
owned3(node, change);
|
|
14768
15075
|
},
|
|
14769
15076
|
async statfs() {
|
|
14770
15077
|
return { bsize: 4096, blocks: 262144, bfree: 262144 };
|
|
@@ -15456,6 +15763,10 @@ var Launcher = class {
|
|
|
15456
15763
|
fs;
|
|
15457
15764
|
processFs;
|
|
15458
15765
|
prefixes;
|
|
15766
|
+
writerTags;
|
|
15767
|
+
unguardedRoot;
|
|
15768
|
+
writers;
|
|
15769
|
+
unlinkOwnerOption;
|
|
15459
15770
|
base;
|
|
15460
15771
|
createWorker;
|
|
15461
15772
|
modulesDir;
|
|
@@ -15513,12 +15824,16 @@ var Launcher = class {
|
|
|
15513
15824
|
usersMode;
|
|
15514
15825
|
dac;
|
|
15515
15826
|
sealed;
|
|
15827
|
+
rootFs;
|
|
15828
|
+
fileWatches;
|
|
15516
15829
|
nextPid = 1e3;
|
|
15517
15830
|
boot = Date.now();
|
|
15518
15831
|
terminals = 0;
|
|
15519
15832
|
constructor(options) {
|
|
15520
15833
|
this.usersMode = usersMode(options.users);
|
|
15834
|
+
this.rootFs = options.fs;
|
|
15521
15835
|
this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
|
|
15836
|
+
if (this.dac) this.dac.tagOf = (pid) => this.tagOf(pid);
|
|
15522
15837
|
this.sealed = new SealedBroker(
|
|
15523
15838
|
options.sealed,
|
|
15524
15839
|
this.usersMode,
|
|
@@ -15569,7 +15884,7 @@ var Launcher = class {
|
|
|
15569
15884
|
this.nodes = new VfsNodes(fs, (path) => this.released(path));
|
|
15570
15885
|
this.nodes.keepsOnDisk = true;
|
|
15571
15886
|
this.openFiles.add(this.nodes);
|
|
15572
|
-
const holders = [this.held,
|
|
15887
|
+
const holders = [this.held, this.openFiles];
|
|
15573
15888
|
this.unlinked = new UnlinkedKeeper(
|
|
15574
15889
|
fs,
|
|
15575
15890
|
{ [Symbol.iterator]: () => holders.flatMap((set2) => [...set2]).values() },
|
|
@@ -15582,7 +15897,12 @@ var Launcher = class {
|
|
|
15582
15897
|
}
|
|
15583
15898
|
);
|
|
15584
15899
|
this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
|
|
15585
|
-
|
|
15900
|
+
this.writerTags = writerTags(options.protect);
|
|
15901
|
+
this.unlinkOwnerOption = options.unlinkOwner;
|
|
15902
|
+
if (this.writerTags.size > 0 && options.fs.protect && !options.fs.unguarded) {
|
|
15903
|
+
throw new TypeError("protect.writers needs a root file system with an unguarded view");
|
|
15904
|
+
}
|
|
15905
|
+
this.unguardedRoot = options.fs.unguarded?.() ?? options.fs;
|
|
15586
15906
|
this.prefixes = protectedPrefixes(options.protect?.paths ?? []);
|
|
15587
15907
|
this.processFs = this.prefixes.length > 0 ? new ProtectedFs(this.prefixes).view(this.fs) : this.fs;
|
|
15588
15908
|
options.fs.protect?.(this.prefixes);
|
|
@@ -15768,7 +16088,7 @@ var Launcher = class {
|
|
|
15768
16088
|
async mayExec(cred, req, planned) {
|
|
15769
16089
|
const { dac } = this;
|
|
15770
16090
|
if (!dac) return;
|
|
15771
|
-
const g = { fs: this.fs, cred, pid: req.pid, dac };
|
|
16091
|
+
const g = { fs: this.fs, cred, pid: req.pid, dac, tag: req.tag };
|
|
15772
16092
|
const byPath = (file) => file.includes("/") && !COMMAND2.test(file);
|
|
15773
16093
|
if (byPath(req.file)) {
|
|
15774
16094
|
const path = this.fs.resolvePath(req.cwd, req.file);
|
|
@@ -15906,6 +16226,7 @@ var Launcher = class {
|
|
|
15906
16226
|
throw fsError("ESRCH", `no process group ${req.pgid}`);
|
|
15907
16227
|
}
|
|
15908
16228
|
const pid = this.nextPid++;
|
|
16229
|
+
const stack = this.stackFor(req.tag);
|
|
15909
16230
|
const terminal = req.fds.stdioTerminal();
|
|
15910
16231
|
const decided = req.decided ?? Promise.resolve(true);
|
|
15911
16232
|
const count = (byParent) => {
|
|
@@ -15936,8 +16257,8 @@ var Launcher = class {
|
|
|
15936
16257
|
env: req.env,
|
|
15937
16258
|
cwd: req.cwd,
|
|
15938
16259
|
fds: req.fds,
|
|
15939
|
-
fs:
|
|
15940
|
-
...this.prefixes.length > 0 ? { readonlyAt: (path) => isProtected(this.prefixes, path) } : {},
|
|
16260
|
+
fs: stack.fs,
|
|
16261
|
+
...this.prefixes.length > 0 && !stack.writer ? { readonlyAt: (path) => isProtected(this.prefixes, path) } : {},
|
|
15941
16262
|
createWorker: this.createWorker,
|
|
15942
16263
|
onError: req.report,
|
|
15943
16264
|
spawner: this.spawner(pid, req.report),
|
|
@@ -15952,9 +16273,9 @@ var Launcher = class {
|
|
|
15952
16273
|
}),
|
|
15953
16274
|
sudo: (call, signal) => this.sudo(pid, call, signal),
|
|
15954
16275
|
openFiles: this.openFiles,
|
|
15955
|
-
nodes:
|
|
16276
|
+
nodes: stack.nodes,
|
|
15956
16277
|
held: this.held,
|
|
15957
|
-
unlinkGate:
|
|
16278
|
+
unlinkGate: stack.unlinked.gate,
|
|
15958
16279
|
statfs: (path) => this.mounts.statfs(path),
|
|
15959
16280
|
mounts: () => this.mounts.list(),
|
|
15960
16281
|
mount: (call, signal) => this.processMount(pid, call, signal),
|
|
@@ -16255,6 +16576,7 @@ var Launcher = class {
|
|
|
16255
16576
|
released(path) {
|
|
16256
16577
|
const at = normalizePath(path);
|
|
16257
16578
|
this.unlinked.closed(at);
|
|
16579
|
+
this.writers?.unlinked.closed(at);
|
|
16258
16580
|
const open = [...this.openFiles].some((nodes) => nodes.writes(at)) || heldUnder(this.held, at);
|
|
16259
16581
|
if (!open) void this.mounts.commit(at).catch(() => void 0);
|
|
16260
16582
|
}
|
|
@@ -16271,6 +16593,13 @@ var Launcher = class {
|
|
|
16271
16593
|
kill(pid, sig, sender, from) {
|
|
16272
16594
|
const session = from === void 0 ? void 0 : this.jobs.sidOf(from);
|
|
16273
16595
|
const may = (target) => !sender || maySignal(sender, this.credOf(target)) || sig === SIG.CONT && session !== void 0 && this.jobs.sidOf(target) === session || from !== void 0 && this.imageOf(from) === this.imageOf(target) || from !== void 0 && this.granted.get(this.jobs.shown(target)) === from;
|
|
16596
|
+
if (pid === -1) {
|
|
16597
|
+
const sender2 = from === void 0 ? void 0 : this.imageOf(from);
|
|
16598
|
+
return this.killAll(
|
|
16599
|
+
sig,
|
|
16600
|
+
(member) => this.imageOf(member) !== sender2 && may(this.imageOf(member))
|
|
16601
|
+
);
|
|
16602
|
+
}
|
|
16274
16603
|
if (pid < 0) return this.jobs.killGroup(-pid, sig, (member) => may(this.imageOf(member)));
|
|
16275
16604
|
const handle = this.processes.get(pid);
|
|
16276
16605
|
if (!handle) return false;
|
|
@@ -16278,6 +16607,15 @@ var Launcher = class {
|
|
|
16278
16607
|
if (sig !== 0) handle.signal(sig);
|
|
16279
16608
|
return true;
|
|
16280
16609
|
}
|
|
16610
|
+
killAll(sig, may) {
|
|
16611
|
+
let any = false;
|
|
16612
|
+
for (const [pid, handle] of [...this.processes]) {
|
|
16613
|
+
if (pid === 1 || !may(pid)) continue;
|
|
16614
|
+
any = true;
|
|
16615
|
+
if (sig !== 0) handle.signal(sig);
|
|
16616
|
+
}
|
|
16617
|
+
return any;
|
|
16618
|
+
}
|
|
16281
16619
|
async driver(type, spec) {
|
|
16282
16620
|
if (type === "fsa") return this.fsaDriver(spec);
|
|
16283
16621
|
if (type === "hostfs") {
|
|
@@ -16367,6 +16705,16 @@ var Launcher = class {
|
|
|
16367
16705
|
}
|
|
16368
16706
|
};
|
|
16369
16707
|
}
|
|
16708
|
+
attest(path) {
|
|
16709
|
+
return attest(this.rootFs, path, {
|
|
16710
|
+
users: this.usersMode,
|
|
16711
|
+
mounts: this.mounts.list().map((entry) => entry.target).filter((target) => this.mounts.mounted(target) !== void 0)
|
|
16712
|
+
});
|
|
16713
|
+
}
|
|
16714
|
+
watchFiles(paths, notify) {
|
|
16715
|
+
this.fileWatches ??= new FileWatches(this.watchers, this.rootFs);
|
|
16716
|
+
return this.fileWatches.watch(paths, notify);
|
|
16717
|
+
}
|
|
16370
16718
|
async mount(spec) {
|
|
16371
16719
|
this.guardMount(spec.target);
|
|
16372
16720
|
const fsa = spec.type === "fsa" && !spec.source.startsWith("fsa:");
|
|
@@ -16547,21 +16895,57 @@ var Launcher = class {
|
|
|
16547
16895
|
...account.umask !== void 0 ? { umask: account.umask } : {}
|
|
16548
16896
|
};
|
|
16549
16897
|
}
|
|
16550
|
-
|
|
16898
|
+
fsFor(tag) {
|
|
16899
|
+
return this.stackFor(tag).fs;
|
|
16900
|
+
}
|
|
16901
|
+
isWriter(tag) {
|
|
16902
|
+
return tag !== void 0 && this.prefixes.length > 0 && this.writerTags.has(tag);
|
|
16903
|
+
}
|
|
16904
|
+
stackFor(tag) {
|
|
16905
|
+
if (!this.isWriter(tag)) {
|
|
16906
|
+
return { fs: this.processFs, nodes: this.nodes, unlinked: this.unlinked, writer: false };
|
|
16907
|
+
}
|
|
16908
|
+
this.writers ??= this.writerStack();
|
|
16909
|
+
return this.writers;
|
|
16910
|
+
}
|
|
16911
|
+
writerStack() {
|
|
16912
|
+
const fs = withCommandDirs(
|
|
16913
|
+
this.watchers.wrap(this.mounts.wrap(this.unguardedRoot)),
|
|
16914
|
+
async () => new Set((await this.commands()).keys()),
|
|
16915
|
+
(dir) => this.userCommandDir(dir),
|
|
16916
|
+
this.owners
|
|
16917
|
+
);
|
|
16918
|
+
const nodes = new VfsNodes(fs, (path) => this.released(path));
|
|
16919
|
+
nodes.keepsOnDisk = true;
|
|
16920
|
+
this.openFiles.add(nodes);
|
|
16921
|
+
const unlinked = new UnlinkedKeeper(
|
|
16922
|
+
fs,
|
|
16923
|
+
{ [Symbol.iterator]: () => [this.held, this.openFiles].flatMap((set2) => [...set2]).values() },
|
|
16924
|
+
(path) => this.mountRoot(path),
|
|
16925
|
+
{
|
|
16926
|
+
locks: webLocks(),
|
|
16927
|
+
busy: (path) => [...this.openFiles].some((open) => open.holds(path)),
|
|
16928
|
+
owner: this.unlinkOwnerOption,
|
|
16929
|
+
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
16930
|
+
}
|
|
16931
|
+
);
|
|
16932
|
+
return { fs: unlinked.wrap(keepingOpen(fs, nodes)), nodes, unlinked, writer: true };
|
|
16933
|
+
}
|
|
16934
|
+
async starting(argv, dir, account, tag) {
|
|
16551
16935
|
this.resetCatalogs();
|
|
16552
16936
|
const scope = account.uid === 0 ? "" : pnpmHomeOf(account, this.pnpmHome);
|
|
16553
16937
|
const start2 = dir ?? "/";
|
|
16554
16938
|
const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
|
|
16555
16939
|
const [file = ""] = argv;
|
|
16556
|
-
if (!await this.base.exists(cwd)) await this.
|
|
16940
|
+
if (!await this.base.exists(cwd)) await this.stackFor(tag).fs.mkdir(cwd, { recursive: true });
|
|
16557
16941
|
const planned = await this.plan(file, argv, cwd, scope);
|
|
16558
|
-
await this.mayExec(credOf(account), { file, cwd, pid: 0 }, planned);
|
|
16942
|
+
await this.mayExec(credOf(account), { file, cwd, pid: 0, tag }, planned);
|
|
16559
16943
|
return { cwd, file, planned };
|
|
16560
16944
|
}
|
|
16561
16945
|
async openTerminal(argv, options) {
|
|
16562
16946
|
tagOption(options.tag);
|
|
16563
16947
|
const { account, tty: ttyGroup } = await this.terminalAccount(options.user);
|
|
16564
|
-
const { cwd, file, planned } = await this.starting(argv, options.cwd, account);
|
|
16948
|
+
const { cwd, file, planned } = await this.starting(argv, options.cwd, account, options.tag);
|
|
16565
16949
|
if (!planned) throw new Error(`${file}: command not found`);
|
|
16566
16950
|
let leader = 0;
|
|
16567
16951
|
const tty = new KernelTty(
|
|
@@ -16612,7 +16996,7 @@ var Launcher = class {
|
|
|
16612
16996
|
async run(argv, options = {}) {
|
|
16613
16997
|
tagOption(options.tag);
|
|
16614
16998
|
const account = await this.account(options.user);
|
|
16615
|
-
const { cwd, file, planned } = await this.starting(argv, options.cwd, account);
|
|
16999
|
+
const { cwd, file, planned } = await this.starting(argv, options.cwd, account, options.tag);
|
|
16616
17000
|
const env = this.environment(cwd, options.env, account);
|
|
16617
17001
|
const out = [];
|
|
16618
17002
|
const err = [];
|