@ai-ecoverse/slicc-kernel 1.37.0 → 1.37.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 +1 -1
- package/dist/kernel-worker.js +323 -47
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +29 -12
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +326 -50
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +29 -12
- package/dist/process-worker.js.map +2 -2
- package/package.json +1 -1
package/dist/node.js
CHANGED
|
@@ -3394,13 +3394,193 @@ function withCommandDirs(fs, names) {
|
|
|
3394
3394
|
};
|
|
3395
3395
|
}
|
|
3396
3396
|
|
|
3397
|
+
// src/fs/unlinked.ts
|
|
3398
|
+
var UNLINKED_DIR = ".slicc-unlinked";
|
|
3399
|
+
function webLocks() {
|
|
3400
|
+
return globalThis.navigator?.locks;
|
|
3401
|
+
}
|
|
3402
|
+
var within2 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
3403
|
+
var parentOf3 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
3404
|
+
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
3405
|
+
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
3406
|
+
var PathGate = class {
|
|
3407
|
+
tails = /* @__PURE__ */ new Map();
|
|
3408
|
+
async acquire(path) {
|
|
3409
|
+
const prev = this.tails.get(path) ?? Promise.resolve();
|
|
3410
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
3411
|
+
const tail = prev.then(() => promise);
|
|
3412
|
+
this.tails.set(path, tail);
|
|
3413
|
+
await prev;
|
|
3414
|
+
return () => {
|
|
3415
|
+
resolve();
|
|
3416
|
+
if (this.tails.get(path) === tail) this.tails.delete(path);
|
|
3417
|
+
};
|
|
3418
|
+
}
|
|
3419
|
+
async run(path, op) {
|
|
3420
|
+
const release = await this.acquire(path);
|
|
3421
|
+
try {
|
|
3422
|
+
return await op();
|
|
3423
|
+
} finally {
|
|
3424
|
+
release();
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
};
|
|
3428
|
+
var UnlinkedKeeper = class {
|
|
3429
|
+
fs;
|
|
3430
|
+
holders;
|
|
3431
|
+
rootOf;
|
|
3432
|
+
locks;
|
|
3433
|
+
busy;
|
|
3434
|
+
waiting = /* @__PURE__ */ new Set();
|
|
3435
|
+
instance = crypto.randomUUID();
|
|
3436
|
+
gate = new PathGate();
|
|
3437
|
+
queue = Promise.resolve();
|
|
3438
|
+
count = 0;
|
|
3439
|
+
constructor(fs, holders, rootOf, locks, busy = () => false) {
|
|
3440
|
+
this.fs = fs;
|
|
3441
|
+
this.holders = holders;
|
|
3442
|
+
this.rootOf = rootOf;
|
|
3443
|
+
this.locks = locks;
|
|
3444
|
+
this.busy = busy;
|
|
3445
|
+
void locks?.request(lockName(this.instance), () => new Promise(() => void 0));
|
|
3446
|
+
}
|
|
3447
|
+
serial(op) {
|
|
3448
|
+
const next = this.queue.then(op);
|
|
3449
|
+
this.queue = next.catch(() => void 0);
|
|
3450
|
+
return next;
|
|
3451
|
+
}
|
|
3452
|
+
wrap() {
|
|
3453
|
+
const fs = this.fs;
|
|
3454
|
+
const keeper = this;
|
|
3455
|
+
const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
|
|
3456
|
+
return {
|
|
3457
|
+
...fs,
|
|
3458
|
+
async rm(path, options) {
|
|
3459
|
+
const target = fs.resolvePath("/", path);
|
|
3460
|
+
const pinned = await keeper.pin(target);
|
|
3461
|
+
if (pinned.moved.has(target)) pinned.commit();
|
|
3462
|
+
else await keeper.settle(pinned, () => fs.rm(path, options));
|
|
3463
|
+
},
|
|
3464
|
+
async rename(from, to) {
|
|
3465
|
+
const pinned = await keeper.pin(fs.resolvePath("/", to));
|
|
3466
|
+
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
3467
|
+
},
|
|
3468
|
+
readdir: async (path) => visible(await fs.readdir(path)),
|
|
3469
|
+
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR)
|
|
3470
|
+
};
|
|
3471
|
+
}
|
|
3472
|
+
async settle(pinned, op) {
|
|
3473
|
+
try {
|
|
3474
|
+
await op();
|
|
3475
|
+
} catch (err) {
|
|
3476
|
+
await pinned.rollback();
|
|
3477
|
+
throw err;
|
|
3478
|
+
}
|
|
3479
|
+
pinned.commit();
|
|
3480
|
+
}
|
|
3481
|
+
async pin(target) {
|
|
3482
|
+
const held = /* @__PURE__ */ new Map();
|
|
3483
|
+
for (const holder of this.holders) {
|
|
3484
|
+
if (holder.owns(target)) continue;
|
|
3485
|
+
for (const path of holder.opens.keys()) {
|
|
3486
|
+
if (!within2(path, target) || holder.unlinked.has(path)) continue;
|
|
3487
|
+
held.set(path, [...held.get(path) ?? [], holder]);
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
const paths = [...held.keys()].sort();
|
|
3491
|
+
const releases = [];
|
|
3492
|
+
for (const path of paths) releases.push(await this.gate.acquire(path));
|
|
3493
|
+
const hidden = /* @__PURE__ */ new Map();
|
|
3494
|
+
for (const path of paths) {
|
|
3495
|
+
const at = await this.hide(path);
|
|
3496
|
+
if (at !== void 0) hidden.set(path, at);
|
|
3497
|
+
}
|
|
3498
|
+
const done4 = () => {
|
|
3499
|
+
for (const release of releases) release();
|
|
3500
|
+
};
|
|
3501
|
+
return {
|
|
3502
|
+
moved: new Set(hidden.keys()),
|
|
3503
|
+
commit: () => {
|
|
3504
|
+
for (const [path, at] of hidden) {
|
|
3505
|
+
const holders = held.get(path);
|
|
3506
|
+
const file = this.keptFile(at, { refs: holders.length });
|
|
3507
|
+
for (const holder of holders) holder.unlinked.set(path, file);
|
|
3508
|
+
}
|
|
3509
|
+
done4();
|
|
3510
|
+
},
|
|
3511
|
+
rollback: async () => {
|
|
3512
|
+
for (const [path, at] of hidden) {
|
|
3513
|
+
await this.serial(async () => {
|
|
3514
|
+
await Promise.allSettled([this.fs.rename(at, path)]);
|
|
3515
|
+
await this.tidy(parentOf3(at));
|
|
3516
|
+
});
|
|
3517
|
+
}
|
|
3518
|
+
done4();
|
|
3519
|
+
}
|
|
3520
|
+
};
|
|
3521
|
+
}
|
|
3522
|
+
keptFile(hidden, entry) {
|
|
3523
|
+
return {
|
|
3524
|
+
hidden,
|
|
3525
|
+
release: () => {
|
|
3526
|
+
if (--entry.refs === 0) void this.serial(() => this.drop(hidden));
|
|
3527
|
+
}
|
|
3528
|
+
};
|
|
3529
|
+
}
|
|
3530
|
+
hide(path) {
|
|
3531
|
+
return this.serial(async () => {
|
|
3532
|
+
const dir = dirOf(this.rootOf(path));
|
|
3533
|
+
const hidden = `${dir}/${this.instance}.${++this.count}`;
|
|
3534
|
+
try {
|
|
3535
|
+
await this.fs.mkdir(dir, { recursive: true });
|
|
3536
|
+
await this.fs.rename(path, hidden);
|
|
3537
|
+
return hidden;
|
|
3538
|
+
} catch {
|
|
3539
|
+
await this.tidy(dir);
|
|
3540
|
+
return void 0;
|
|
3541
|
+
}
|
|
3542
|
+
});
|
|
3543
|
+
}
|
|
3544
|
+
closed(path) {
|
|
3545
|
+
if (this.waiting.delete(path)) void this.serial(() => this.drop(path));
|
|
3546
|
+
}
|
|
3547
|
+
async drop(hidden) {
|
|
3548
|
+
if (this.busy(hidden)) {
|
|
3549
|
+
this.waiting.add(hidden);
|
|
3550
|
+
return;
|
|
3551
|
+
}
|
|
3552
|
+
await this.fs.rm(hidden).catch(() => void 0);
|
|
3553
|
+
await this.tidy(parentOf3(hidden));
|
|
3554
|
+
}
|
|
3555
|
+
async tidy(dir) {
|
|
3556
|
+
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
3557
|
+
if (left.length === 0) await this.fs.rm(dir, { recursive: true }).catch(() => void 0);
|
|
3558
|
+
}
|
|
3559
|
+
sweep(root) {
|
|
3560
|
+
const query = this.locks?.query?.bind(this.locks);
|
|
3561
|
+
if (!query) return Promise.resolve();
|
|
3562
|
+
return this.serial(async () => {
|
|
3563
|
+
const dir = dirOf(root);
|
|
3564
|
+
const names = await this.fs.readdir(dir).catch(() => void 0);
|
|
3565
|
+
if (!names) return;
|
|
3566
|
+
const held = new Set(((await query()).held ?? []).map((lock) => lock.name));
|
|
3567
|
+
for (const name2 of names) {
|
|
3568
|
+
const owner = name2.slice(0, name2.lastIndexOf("."));
|
|
3569
|
+
if (owner === this.instance || held.has(lockName(owner))) continue;
|
|
3570
|
+
await this.fs.rm(`${dir}/${name2}`, { recursive: true }).catch(() => void 0);
|
|
3571
|
+
}
|
|
3572
|
+
await this.tidy(dir);
|
|
3573
|
+
});
|
|
3574
|
+
}
|
|
3575
|
+
};
|
|
3576
|
+
|
|
3397
3577
|
// src/fs/watch.ts
|
|
3398
3578
|
var WATCH_BATCH_LIMIT = 1e3;
|
|
3399
3579
|
var under2 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
|
|
3400
|
-
var
|
|
3580
|
+
var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
3401
3581
|
function touches(watcher, changed) {
|
|
3402
3582
|
return watcher.paths.some(
|
|
3403
|
-
(watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) :
|
|
3583
|
+
(watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf4(changed) === watched)
|
|
3404
3584
|
);
|
|
3405
3585
|
}
|
|
3406
3586
|
var FsWatchers = class {
|
|
@@ -3782,7 +3962,7 @@ function parseSize(text) {
|
|
|
3782
3962
|
if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
|
|
3783
3963
|
return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
|
|
3784
3964
|
}
|
|
3785
|
-
var
|
|
3965
|
+
var within3 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
3786
3966
|
var HeldSet = class extends Set {
|
|
3787
3967
|
idle;
|
|
3788
3968
|
constructor(idle) {
|
|
@@ -3795,18 +3975,47 @@ var HeldSet = class extends Set {
|
|
|
3795
3975
|
}
|
|
3796
3976
|
delete(holds) {
|
|
3797
3977
|
const removed = super.delete(holds);
|
|
3798
|
-
for (const path of holds.keys()) this.idle(path);
|
|
3978
|
+
for (const path of /* @__PURE__ */ new Set([...holds.keys(), ...holds.opens.keys()])) this.idle(path);
|
|
3979
|
+
for (const kept of holds.unlinked.values()) kept.release();
|
|
3980
|
+
holds.unlinked.clear();
|
|
3799
3981
|
return removed;
|
|
3800
3982
|
}
|
|
3801
3983
|
};
|
|
3802
3984
|
function heldUnder(held, target) {
|
|
3803
|
-
for (const paths of held) for (const path of paths.keys()) if (
|
|
3985
|
+
for (const paths of held) for (const path of paths.keys()) if (within3(path, target)) return true;
|
|
3804
3986
|
return false;
|
|
3805
3987
|
}
|
|
3806
3988
|
var HeldPaths = class extends Map {
|
|
3807
3989
|
revoked = /* @__PURE__ */ new Set();
|
|
3990
|
+
opens = /* @__PURE__ */ new Map();
|
|
3991
|
+
unlinked = /* @__PURE__ */ new Map();
|
|
3992
|
+
aliases = /* @__PURE__ */ new Map();
|
|
3993
|
+
owning;
|
|
3808
3994
|
onIdle;
|
|
3809
|
-
|
|
3995
|
+
keyOf(path) {
|
|
3996
|
+
return this.aliases.get(path) ?? path;
|
|
3997
|
+
}
|
|
3998
|
+
keptAt(path) {
|
|
3999
|
+
return this.unlinked.get(this.keyOf(path));
|
|
4000
|
+
}
|
|
4001
|
+
holding(path) {
|
|
4002
|
+
return this.opens.has(this.keyOf(path));
|
|
4003
|
+
}
|
|
4004
|
+
hold(path, on, open = false, real) {
|
|
4005
|
+
if (open) {
|
|
4006
|
+
const key = real ?? this.keyOf(path);
|
|
4007
|
+
if (key !== path) this.aliases.set(path, key);
|
|
4008
|
+
const count2 = (this.opens.get(key) ?? 0) + (on ? 1 : -1);
|
|
4009
|
+
if (count2 > 0) this.opens.set(key, count2);
|
|
4010
|
+
else {
|
|
4011
|
+
this.opens.delete(key);
|
|
4012
|
+
this.unlinked.get(key)?.release();
|
|
4013
|
+
this.unlinked.delete(key);
|
|
4014
|
+
for (const [alias, target] of this.aliases) if (target === key) this.aliases.delete(alias);
|
|
4015
|
+
this.onIdle?.(key);
|
|
4016
|
+
}
|
|
4017
|
+
return;
|
|
4018
|
+
}
|
|
3810
4019
|
const count = (this.get(path) ?? 0) + (on ? 1 : -1);
|
|
3811
4020
|
if (count > 0) this.set(path, count);
|
|
3812
4021
|
else {
|
|
@@ -3815,23 +4024,40 @@ var HeldPaths = class extends Map {
|
|
|
3815
4024
|
this.onIdle?.(path);
|
|
3816
4025
|
}
|
|
3817
4026
|
}
|
|
4027
|
+
async own(path, op) {
|
|
4028
|
+
this.owning = path;
|
|
4029
|
+
try {
|
|
4030
|
+
return await op();
|
|
4031
|
+
} finally {
|
|
4032
|
+
this.owning = void 0;
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
owns(path) {
|
|
4036
|
+
return this.owning === path;
|
|
4037
|
+
}
|
|
3818
4038
|
revoke(prefix) {
|
|
3819
|
-
for (const path of this.keys()) if (
|
|
4039
|
+
for (const path of this.keys()) if (within3(path, prefix)) this.revoked.add(path);
|
|
3820
4040
|
}
|
|
3821
4041
|
renamed(from, to) {
|
|
3822
4042
|
for (const [path, count] of [...this]) {
|
|
3823
|
-
if (!
|
|
4043
|
+
if (!within3(path, from)) continue;
|
|
3824
4044
|
const moved2 = to + path.slice(from.length);
|
|
3825
4045
|
this.delete(path);
|
|
3826
4046
|
this.set(moved2, (this.get(moved2) ?? 0) + count);
|
|
3827
4047
|
if (this.revoked.delete(path)) this.revoked.add(moved2);
|
|
3828
4048
|
}
|
|
4049
|
+
for (const [path, count] of [...this.opens]) {
|
|
4050
|
+
if (!within3(path, from)) continue;
|
|
4051
|
+
const moved2 = to + path.slice(from.length);
|
|
4052
|
+
this.opens.delete(path);
|
|
4053
|
+
this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count);
|
|
4054
|
+
}
|
|
3829
4055
|
}
|
|
3830
4056
|
isRevoked(path) {
|
|
3831
4057
|
return this.revoked.has(path);
|
|
3832
4058
|
}
|
|
3833
4059
|
};
|
|
3834
|
-
var
|
|
4060
|
+
var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
3835
4061
|
var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
|
|
3836
4062
|
function statOf(attr, path, mount) {
|
|
3837
4063
|
const mtime = new Date(attr.mtime);
|
|
@@ -3920,7 +4146,7 @@ var MountTable = class {
|
|
|
3920
4146
|
locate(path) {
|
|
3921
4147
|
let best;
|
|
3922
4148
|
for (const mount of this.mounts.values()) {
|
|
3923
|
-
if (
|
|
4149
|
+
if (within3(path, mount.target) && (!best || mount.target.length > best.target.length))
|
|
3924
4150
|
best = mount;
|
|
3925
4151
|
}
|
|
3926
4152
|
if (!best) return void 0;
|
|
@@ -4059,7 +4285,7 @@ var MountTable = class {
|
|
|
4059
4285
|
async commitUnder(prefix) {
|
|
4060
4286
|
const at = normalizePath(prefix);
|
|
4061
4287
|
await Promise.all(
|
|
4062
|
-
[...this.sessions.keys()].filter((p) =>
|
|
4288
|
+
[...this.sessions.keys()].filter((p) => within3(p, at)).map((p) => this.commit(p))
|
|
4063
4289
|
);
|
|
4064
4290
|
}
|
|
4065
4291
|
async commitOverlapping(real, offset, length2) {
|
|
@@ -4093,10 +4319,10 @@ var MountTable = class {
|
|
|
4093
4319
|
this.deps.changed?.(...full);
|
|
4094
4320
|
}
|
|
4095
4321
|
forget(mount, path) {
|
|
4096
|
-
for (const key of [...mount.attrs.keys()]) if (
|
|
4322
|
+
for (const key of [...mount.attrs.keys()]) if (within3(key, path)) mount.attrs.delete(key);
|
|
4097
4323
|
mount.lists.delete(path);
|
|
4098
|
-
mount.lists.delete(
|
|
4099
|
-
for (const key of [...mount.lists.keys()]) if (
|
|
4324
|
+
mount.lists.delete(parentOf5(path));
|
|
4325
|
+
for (const key of [...mount.lists.keys()]) if (within3(key, path)) mount.lists.delete(key);
|
|
4100
4326
|
}
|
|
4101
4327
|
async getattr(mount, path, rel2) {
|
|
4102
4328
|
if (mount.conn.failure) throw mount.conn.failure;
|
|
@@ -4161,7 +4387,7 @@ var MountFs = class {
|
|
|
4161
4387
|
if (attr.kind !== "symlink") return real;
|
|
4162
4388
|
if (hops >= MAX_LINKS4) throw errnoError("ELOOP", path);
|
|
4163
4389
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
4164
|
-
return this.follow(this.base.resolvePath(
|
|
4390
|
+
return this.follow(this.base.resolvePath(parentOf5(real), target), hops + 1, create);
|
|
4165
4391
|
}
|
|
4166
4392
|
async open(mount, rel2, write) {
|
|
4167
4393
|
return await mount.conn.call({
|
|
@@ -4675,6 +4901,8 @@ async function ranged(fs, path, req) {
|
|
|
4675
4901
|
else await ops.truncate(path, req.length ?? 0);
|
|
4676
4902
|
return done;
|
|
4677
4903
|
}
|
|
4904
|
+
var UNLINKED_OPS = /* @__PURE__ */ new Set(["read", "pread", "pwrite", "truncate"]);
|
|
4905
|
+
var OWN_OPS = /* @__PURE__ */ new Set(["rm", "unlink", "rmdir", "rename"]);
|
|
4678
4906
|
async function dispatchSyncFs(req) {
|
|
4679
4907
|
const entry = resolveSyncFsToken(req.token);
|
|
4680
4908
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
@@ -4683,11 +4911,22 @@ async function dispatchSyncFs(req) {
|
|
|
4683
4911
|
const path = fs.resolvePath(cwd, req.path);
|
|
4684
4912
|
if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
|
|
4685
4913
|
if (req.op === "hold") {
|
|
4686
|
-
|
|
4914
|
+
const [on, open] = [((req.mode ?? 0) & 1) === 1, ((req.mode ?? 0) & 2) === 2];
|
|
4915
|
+
const real = on && open ? await fs.realpath?.(path, true).catch(() => path) : void 0;
|
|
4916
|
+
entry.hold?.(path, on, open, real);
|
|
4687
4917
|
return done;
|
|
4688
4918
|
}
|
|
4689
4919
|
if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };
|
|
4690
|
-
|
|
4920
|
+
if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
|
|
4921
|
+
const serve = async () => {
|
|
4922
|
+
const kept = entry.unlinked?.(path);
|
|
4923
|
+
return run(entry, kept && !await fs.exists(path) ? kept.hidden : path, req);
|
|
4924
|
+
};
|
|
4925
|
+
return await (entry.gate ? entry.gate(path, serve) : serve());
|
|
4926
|
+
}
|
|
4927
|
+
if (!entry.own || !OWN_OPS.has(req.op)) return await run(entry, path, req);
|
|
4928
|
+
const target = req.op === "rename" ? fs.resolvePath(cwd, req.arg2 ?? "") : path;
|
|
4929
|
+
return await entry.own(target, () => run(entry, path, req));
|
|
4691
4930
|
} catch (err) {
|
|
4692
4931
|
return toErrno(err);
|
|
4693
4932
|
}
|
|
@@ -6266,7 +6505,7 @@ function parsePrefix(text) {
|
|
|
6266
6505
|
if (length2 < 0 || length2 > max) return void 0;
|
|
6267
6506
|
return { ...parsed, length: length2 };
|
|
6268
6507
|
}
|
|
6269
|
-
function
|
|
6508
|
+
function within4(prefix, addr) {
|
|
6270
6509
|
if (prefix.v6 !== addr.v6) return false;
|
|
6271
6510
|
const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
|
|
6272
6511
|
return addr.bits >> shift === prefix.bits >> shift;
|
|
@@ -6306,9 +6545,9 @@ var Routes = class {
|
|
|
6306
6545
|
if (!addr) return "unreachable";
|
|
6307
6546
|
if (addr.v6 && addr.bits === 1n) return "loopback";
|
|
6308
6547
|
if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
|
|
6309
|
-
if (RESERVED.some((p) =>
|
|
6548
|
+
if (RESERVED.some((p) => within4(p, addr))) return "reserved";
|
|
6310
6549
|
if (addr.v6 && !this.ipv6) return "unreachable";
|
|
6311
|
-
if (this.exit || this.table.some((p) =>
|
|
6550
|
+
if (this.exit || this.table.some((p) => within4(p, addr))) return "uplink";
|
|
6312
6551
|
return "unreachable";
|
|
6313
6552
|
}
|
|
6314
6553
|
};
|
|
@@ -7277,7 +7516,7 @@ function isMissing(err) {
|
|
|
7277
7516
|
if (err?.code === "ENOENT") return true;
|
|
7278
7517
|
return err instanceof Error && err.message.startsWith("ENOENT");
|
|
7279
7518
|
}
|
|
7280
|
-
function
|
|
7519
|
+
function within5(path, root) {
|
|
7281
7520
|
return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
|
|
7282
7521
|
}
|
|
7283
7522
|
var VfsNode = class {
|
|
@@ -7482,7 +7721,7 @@ var VfsNodes = class {
|
|
|
7482
7721
|
return run2;
|
|
7483
7722
|
}
|
|
7484
7723
|
async entryKey(path) {
|
|
7485
|
-
const parent =
|
|
7724
|
+
const parent = parentOf6(path);
|
|
7486
7725
|
const dir = await this.real(parent, true, () => realDir(this.fs, parent));
|
|
7487
7726
|
return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
|
|
7488
7727
|
}
|
|
@@ -7530,12 +7769,12 @@ var VfsNodes = class {
|
|
|
7530
7769
|
return node;
|
|
7531
7770
|
}
|
|
7532
7771
|
holds(prefix) {
|
|
7533
|
-
for (const path of this.byPath.keys()) if (
|
|
7772
|
+
for (const path of this.byPath.keys()) if (within5(path, prefix)) return true;
|
|
7534
7773
|
return false;
|
|
7535
7774
|
}
|
|
7536
7775
|
writes(prefix) {
|
|
7537
7776
|
for (const [path, node] of this.byPath)
|
|
7538
|
-
if (node.writers > 0 &&
|
|
7777
|
+
if (node.writers > 0 && within5(path, prefix)) return true;
|
|
7539
7778
|
return false;
|
|
7540
7779
|
}
|
|
7541
7780
|
closed(node, writable = false) {
|
|
@@ -7549,24 +7788,24 @@ var VfsNodes = class {
|
|
|
7549
7788
|
this.onClosed?.(node.path);
|
|
7550
7789
|
}
|
|
7551
7790
|
async flush(path) {
|
|
7552
|
-
for (const [p, node] of this.byPath) if (
|
|
7791
|
+
for (const [p, node] of this.byPath) if (within5(p, path)) await node.serial(() => node.flush());
|
|
7553
7792
|
}
|
|
7554
7793
|
async unlinking(path) {
|
|
7555
7794
|
for (const [held, node] of [...this.byPath]) {
|
|
7556
|
-
if (!
|
|
7795
|
+
if (!within5(held, path)) continue;
|
|
7557
7796
|
await node.serial(async () => void await Promise.all([node.keep(), node.limit()]));
|
|
7558
7797
|
}
|
|
7559
7798
|
}
|
|
7560
7799
|
revoke(prefix) {
|
|
7561
7800
|
for (const [path, node] of this.byPath) {
|
|
7562
|
-
if (!
|
|
7801
|
+
if (!within5(path, prefix)) continue;
|
|
7563
7802
|
node.revoke();
|
|
7564
7803
|
this.byPath.delete(path);
|
|
7565
7804
|
}
|
|
7566
7805
|
}
|
|
7567
7806
|
unlinked(path) {
|
|
7568
7807
|
for (const [held, node] of [...this.byPath]) {
|
|
7569
|
-
if (!
|
|
7808
|
+
if (!within5(held, path)) continue;
|
|
7570
7809
|
node.orphaned = true;
|
|
7571
7810
|
this.byPath.delete(held);
|
|
7572
7811
|
}
|
|
@@ -7575,8 +7814,8 @@ var VfsNodes = class {
|
|
|
7575
7814
|
if (from === to) return;
|
|
7576
7815
|
const moved2 = [];
|
|
7577
7816
|
for (const [p, node] of this.byPath) {
|
|
7578
|
-
if (
|
|
7579
|
-
else if (
|
|
7817
|
+
if (within5(p, from)) moved2.push(node);
|
|
7818
|
+
else if (within5(p, to)) {
|
|
7580
7819
|
node.orphaned = true;
|
|
7581
7820
|
this.byPath.delete(p);
|
|
7582
7821
|
}
|
|
@@ -7599,7 +7838,7 @@ async function present(fs, path, follow = false) {
|
|
|
7599
7838
|
}
|
|
7600
7839
|
}
|
|
7601
7840
|
var MAX_LINKS5 = 40;
|
|
7602
|
-
function
|
|
7841
|
+
function parentOf6(path) {
|
|
7603
7842
|
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
7604
7843
|
}
|
|
7605
7844
|
async function linkOf(fs, path) {
|
|
@@ -7612,7 +7851,7 @@ async function realDir(fs, dir, budget = { hops: 0 }) {
|
|
|
7612
7851
|
const part = queue.shift();
|
|
7613
7852
|
if (part === ".") continue;
|
|
7614
7853
|
if (part === "..") {
|
|
7615
|
-
at =
|
|
7854
|
+
at = parentOf6(at);
|
|
7616
7855
|
continue;
|
|
7617
7856
|
}
|
|
7618
7857
|
const next = at === "/" ? `/${part}` : `${at}/${part}`;
|
|
@@ -7636,7 +7875,7 @@ function joinName(dir, name2) {
|
|
|
7636
7875
|
}
|
|
7637
7876
|
async function realTarget(fs, path) {
|
|
7638
7877
|
const budget = { hops: 0 };
|
|
7639
|
-
let dir = await realDir(fs,
|
|
7878
|
+
let dir = await realDir(fs, parentOf6(path), budget);
|
|
7640
7879
|
let name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
7641
7880
|
for (; ; ) {
|
|
7642
7881
|
const link2 = await linkOf(fs, joinName(dir, name2));
|
|
@@ -7644,13 +7883,13 @@ async function realTarget(fs, path) {
|
|
|
7644
7883
|
if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", path);
|
|
7645
7884
|
budget.hops++;
|
|
7646
7885
|
const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
|
|
7647
|
-
dir = await realDir(fs,
|
|
7886
|
+
dir = await realDir(fs, parentOf6(target), budget);
|
|
7648
7887
|
name2 = target.slice(target.lastIndexOf("/") + 1);
|
|
7649
7888
|
}
|
|
7650
7889
|
return joinName(dir, name2);
|
|
7651
7890
|
}
|
|
7652
7891
|
async function targetDir(fs, path) {
|
|
7653
|
-
return
|
|
7892
|
+
return parentOf6(await realTarget(fs, path).catch(() => path));
|
|
7654
7893
|
}
|
|
7655
7894
|
async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
7656
7895
|
const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
|
|
@@ -8039,7 +8278,7 @@ var WasmProcess = class {
|
|
|
8039
8278
|
const fd = this.fds.install(heldFile(), 3);
|
|
8040
8279
|
try {
|
|
8041
8280
|
const nodes = this.nodes;
|
|
8042
|
-
const path = req.orphan ? req.path : req.exclusive ? await nodes.entryKey(req.path) : await nodes.targetKey(req.path);
|
|
8281
|
+
const path = req.orphan ? req.path : req.exclusive ? await nodes.entryKey(req.path) : req.create ? await nodes.targetKey(req.path) : await this.keptOr(await nodes.targetKey(req.path));
|
|
8043
8282
|
const open = { ...req, path };
|
|
8044
8283
|
if (req.exclusive) {
|
|
8045
8284
|
await nodes.createExclusive(path);
|
|
@@ -8195,6 +8434,11 @@ var WasmProcess = class {
|
|
|
8195
8434
|
return { ok: true, kind: "json", json: await file.stat() };
|
|
8196
8435
|
}
|
|
8197
8436
|
}
|
|
8437
|
+
keptPath;
|
|
8438
|
+
async keptOr(path) {
|
|
8439
|
+
const kept = this.keptPath?.(path);
|
|
8440
|
+
return kept !== void 0 && !await present(this.options.fs, path, true) ? kept : path;
|
|
8441
|
+
}
|
|
8198
8442
|
async promote(req) {
|
|
8199
8443
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
8200
8444
|
if (req.share !== void 0) {
|
|
@@ -8202,7 +8446,7 @@ var WasmProcess = class {
|
|
|
8202
8446
|
return;
|
|
8203
8447
|
}
|
|
8204
8448
|
if (req.path === void 0) throw new KernelError("EINVAL");
|
|
8205
|
-
const path = req.orphan ? req.path : await this.nodes.targetKey(req.path);
|
|
8449
|
+
const path = req.orphan ? req.path : await this.keptOr(await this.nodes.targetKey(req.path));
|
|
8206
8450
|
if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
|
|
8207
8451
|
const file = vfsFile(
|
|
8208
8452
|
this.options.fs,
|
|
@@ -8561,6 +8805,19 @@ function inherited(opts) {
|
|
|
8561
8805
|
...opts.umask !== void 0 ? { umask: opts.umask } : {}
|
|
8562
8806
|
};
|
|
8563
8807
|
}
|
|
8808
|
+
function holdingToken(opts, holds) {
|
|
8809
|
+
return mintSyncFsToken({
|
|
8810
|
+
fs: opts.fs,
|
|
8811
|
+
cwd: opts.cwd,
|
|
8812
|
+
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
8813
|
+
hold: (path, on, open, real) => holds.hold(path, on, open, real),
|
|
8814
|
+
unlinked: (path) => holds.keptAt(path),
|
|
8815
|
+
gate: (path, op) => opts.unlinkGate && holds.holding(path) ? opts.unlinkGate.run(holds.keyOf(path), op) : op(),
|
|
8816
|
+
own: (path, op) => holds.own(path, op),
|
|
8817
|
+
revoked: (path) => holds.isRevoked(path),
|
|
8818
|
+
renamed: (from, to) => holds.renamed(from, to)
|
|
8819
|
+
});
|
|
8820
|
+
}
|
|
8564
8821
|
function spawnWasmProcess(opts) {
|
|
8565
8822
|
const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
|
|
8566
8823
|
const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
|
|
@@ -8605,14 +8862,8 @@ function spawnWasmProcess(opts) {
|
|
|
8605
8862
|
});
|
|
8606
8863
|
const holds = new HeldPaths();
|
|
8607
8864
|
opts.held?.add(holds);
|
|
8608
|
-
const token =
|
|
8609
|
-
|
|
8610
|
-
cwd: opts.cwd,
|
|
8611
|
-
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
8612
|
-
hold: (path, on) => holds.hold(path, on),
|
|
8613
|
-
revoked: (path) => holds.isRevoked(path),
|
|
8614
|
-
renamed: (from, to) => holds.renamed(from, to)
|
|
8615
|
-
});
|
|
8865
|
+
const token = holdingToken(opts, holds);
|
|
8866
|
+
process.keptPath = (path) => holds.keptAt(path)?.hidden;
|
|
8616
8867
|
const worker = opts.createWorker();
|
|
8617
8868
|
const dying = new Dying();
|
|
8618
8869
|
const dispatch2 = (req) => dying.serving(() => isWasmSyscall(req) ? process.syscall(req) : dispatchSyncFs(req));
|
|
@@ -10261,6 +10512,7 @@ var Launcher = class {
|
|
|
10261
10512
|
deciding = /* @__PURE__ */ new Map();
|
|
10262
10513
|
identities = { asked: 0, timedOut: 0 };
|
|
10263
10514
|
nodes;
|
|
10515
|
+
unlinked;
|
|
10264
10516
|
held = new HeldSet((path) => this.released(path));
|
|
10265
10517
|
processMounts;
|
|
10266
10518
|
booting = new AbortController();
|
|
@@ -10305,7 +10557,15 @@ var Launcher = class {
|
|
|
10305
10557
|
const fs = withCommandDirs(this.base, async () => new Set((await this.commands()).keys()));
|
|
10306
10558
|
this.nodes = new VfsNodes(fs, (path) => this.released(path));
|
|
10307
10559
|
this.openFiles.add(this.nodes);
|
|
10308
|
-
this.
|
|
10560
|
+
this.unlinked = new UnlinkedKeeper(
|
|
10561
|
+
fs,
|
|
10562
|
+
this.held,
|
|
10563
|
+
(path) => this.mountRoot(path),
|
|
10564
|
+
webLocks(),
|
|
10565
|
+
(path) => [...this.openFiles].some((nodes) => nodes.holds(path))
|
|
10566
|
+
);
|
|
10567
|
+
this.fs = keepingOpen(this.unlinked.wrap(), this.nodes);
|
|
10568
|
+
void this.unlinked.sweep("/");
|
|
10309
10569
|
this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
|
|
10310
10570
|
this.catalog = void 0;
|
|
10311
10571
|
this.binfmts = void 0;
|
|
@@ -10515,6 +10775,7 @@ var Launcher = class {
|
|
|
10515
10775
|
openFiles: this.openFiles,
|
|
10516
10776
|
nodes: this.nodes,
|
|
10517
10777
|
held: this.held,
|
|
10778
|
+
unlinkGate: this.unlinked.gate,
|
|
10518
10779
|
statfs: (path) => this.mounts.statfs(path),
|
|
10519
10780
|
mounts: () => this.mounts.list(),
|
|
10520
10781
|
mount: (call, signal) => this.processMount(pid, call, signal),
|
|
@@ -10672,6 +10933,7 @@ var Launcher = class {
|
|
|
10672
10933
|
}
|
|
10673
10934
|
released(path) {
|
|
10674
10935
|
const at = normalizePath(path);
|
|
10936
|
+
this.unlinked.closed(at);
|
|
10675
10937
|
const open = [...this.openFiles].some((nodes) => nodes.writes(at)) || heldUnder(this.held, at);
|
|
10676
10938
|
if (!open) void this.mounts.commit(at).catch(() => void 0);
|
|
10677
10939
|
}
|
|
@@ -10767,10 +11029,23 @@ var Launcher = class {
|
|
|
10767
11029
|
}
|
|
10768
11030
|
};
|
|
10769
11031
|
}
|
|
10770
|
-
mount(spec) {
|
|
10771
|
-
|
|
10772
|
-
|
|
10773
|
-
|
|
11032
|
+
async mount(spec) {
|
|
11033
|
+
const fsa = spec.type === "fsa" && !spec.source.startsWith("fsa:");
|
|
11034
|
+
const entry = await this.mounts.mount(
|
|
11035
|
+
fsa ? { ...spec, source: `fsa:${crypto.randomUUID()}` } : spec,
|
|
11036
|
+
this.fs
|
|
11037
|
+
);
|
|
11038
|
+
void this.unlinked.sweep(entry.target);
|
|
11039
|
+
return entry;
|
|
11040
|
+
}
|
|
11041
|
+
mountRoot(path) {
|
|
11042
|
+
let root = "/";
|
|
11043
|
+
for (const { target } of this.mounts.list()) {
|
|
11044
|
+
if (target.length > root.length && (path === target || path.startsWith(`${target}/`))) {
|
|
11045
|
+
root = target;
|
|
11046
|
+
}
|
|
11047
|
+
}
|
|
11048
|
+
return root;
|
|
10774
11049
|
}
|
|
10775
11050
|
async insert(target, handle, source) {
|
|
10776
11051
|
const at = normalizePath(target);
|
|
@@ -10784,6 +11059,7 @@ var Launcher = class {
|
|
|
10784
11059
|
await this.media.put(slot.id, handle);
|
|
10785
11060
|
slot.medium.insert(handle);
|
|
10786
11061
|
slot.served.invalidate(true);
|
|
11062
|
+
void this.unlinked.sweep(at);
|
|
10787
11063
|
}
|
|
10788
11064
|
umount(target, detach = false) {
|
|
10789
11065
|
const at = normalizePath(target);
|