@ai-ecoverse/slicc-kernel 1.161.0 → 1.162.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/dist/index.js.map +1 -1
- package/dist/kernel-worker.js +365 -34
- package/dist/kernel-worker.js.map +3 -3
- package/dist/meta.js.map +2 -2
- package/dist/node-process-worker.js +1 -1
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +365 -34
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +1 -1
- package/dist/process-worker.js.map +2 -2
- package/dist/types/fs/opfs.d.ts +6 -2
- package/dist/types/fs/remote.d.ts +56 -0
- package/dist/types/fs/types.d.ts +36 -0
- package/dist/types/fs/unlinked.d.ts +21 -1
- package/dist/types/kernel/host.d.ts +1 -1
- package/dist/types/launcher.d.ts +5 -0
- package/dist/types/mount/mount-fs.d.ts +5 -2
- package/dist/types/realm/sync-fs-token-registry.d.ts +1 -1
- package/package.json +2 -2
package/dist/kernel-worker.js
CHANGED
|
@@ -1005,6 +1005,9 @@ var RemoteWatch = class {
|
|
|
1005
1005
|
holdsSent = 0;
|
|
1006
1006
|
silent = /* @__PURE__ */ new Set();
|
|
1007
1007
|
holders = /* @__PURE__ */ new Set();
|
|
1008
|
+
instances = /* @__PURE__ */ new Map();
|
|
1009
|
+
instance;
|
|
1010
|
+
keeps;
|
|
1008
1011
|
listeners = /* @__PURE__ */ new Set();
|
|
1009
1012
|
waiting = /* @__PURE__ */ new Map();
|
|
1010
1013
|
locks;
|
|
@@ -1124,8 +1127,9 @@ var RemoteWatch = class {
|
|
|
1124
1127
|
this.met?.();
|
|
1125
1128
|
}
|
|
1126
1129
|
if (data.tab !== void 0 && data.paths !== void 0) this.announced(data);
|
|
1127
|
-
|
|
1130
|
+
this.holdsHeard(data);
|
|
1128
1131
|
if (data.ack !== void 0 && data.to === this.tab && data.from !== void 0) {
|
|
1132
|
+
if (data.instance !== void 0) this.instances.set(data.from, data.instance);
|
|
1129
1133
|
this.acked(data.ack, data.from);
|
|
1130
1134
|
}
|
|
1131
1135
|
if (data.changed !== void 0) this.emit({ paths: data.changed, trees: [], content: true });
|
|
@@ -1152,6 +1156,7 @@ var RemoteWatch = class {
|
|
|
1152
1156
|
this.mourned.delete(peer);
|
|
1153
1157
|
this.others.delete(peer);
|
|
1154
1158
|
this.peerHolds.delete(peer);
|
|
1159
|
+
this.keeps?.left(peer);
|
|
1155
1160
|
this.peers.delete(peer);
|
|
1156
1161
|
this.ackers.delete(peer);
|
|
1157
1162
|
this.holders.delete(peer);
|
|
@@ -1192,9 +1197,38 @@ var RemoteWatch = class {
|
|
|
1192
1197
|
this.rebuild();
|
|
1193
1198
|
this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
|
|
1194
1199
|
}
|
|
1200
|
+
holdsHeard(data) {
|
|
1201
|
+
if (data.tab === void 0) return;
|
|
1202
|
+
if (data.holds !== void 0) this.heldBy(data);
|
|
1203
|
+
if (data.to === this.tab && (data.keeping || data.kept)) void this.keepAsked(data);
|
|
1204
|
+
}
|
|
1205
|
+
keepChains = /* @__PURE__ */ new Map();
|
|
1206
|
+
keepAsked(data) {
|
|
1207
|
+
const from = data.tab;
|
|
1208
|
+
const next3 = (this.keepChains.get(from) ?? Promise.resolve()).then(() => this.keepOne(data));
|
|
1209
|
+
this.keepChains.set(from, next3);
|
|
1210
|
+
return next3;
|
|
1211
|
+
}
|
|
1212
|
+
async keepOne({ tab, seq: seq2, keeping, kept }) {
|
|
1213
|
+
const from = tab;
|
|
1214
|
+
const keeps = this.keeps;
|
|
1215
|
+
try {
|
|
1216
|
+
if (keeping) await keeps?.keeping(from, keeping.paths);
|
|
1217
|
+
if (kept)
|
|
1218
|
+
await keeps?.kept(from, kept.items, kept.paths ?? kept.items.map((item) => item.path));
|
|
1219
|
+
} finally {
|
|
1220
|
+
this.channel.postMessage({
|
|
1221
|
+
ack: seq2,
|
|
1222
|
+
from: this.tab,
|
|
1223
|
+
to: from,
|
|
1224
|
+
...this.instance === void 0 ? {} : { instance: this.instance }
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1195
1228
|
heldBy({ tab, holds: holds2, seq: seq2 }) {
|
|
1196
1229
|
const from = tab;
|
|
1197
1230
|
const { add, drop } = holds2;
|
|
1231
|
+
if (holds2?.instance !== void 0) this.instances.set(from, holds2.instance);
|
|
1198
1232
|
const held = this.peerHolds.get(from) ?? /* @__PURE__ */ new Map();
|
|
1199
1233
|
for (const path of drop) held.delete(path);
|
|
1200
1234
|
for (const [path, fid] of add) held.set(path, fid);
|
|
@@ -1236,9 +1270,38 @@ var RemoteWatch = class {
|
|
|
1236
1270
|
sendHolds(add, drop) {
|
|
1237
1271
|
const seq2 = ++this.seq;
|
|
1238
1272
|
this.holdsSent++;
|
|
1239
|
-
|
|
1273
|
+
const instance = this.instance === void 0 ? {} : { instance: this.instance };
|
|
1274
|
+
this.channel.postMessage({
|
|
1275
|
+
tab: this.tab,
|
|
1276
|
+
holds: { add: [...add], drop: [...drop], ...instance },
|
|
1277
|
+
seq: seq2
|
|
1278
|
+
});
|
|
1240
1279
|
return seq2;
|
|
1241
1280
|
}
|
|
1281
|
+
heldWithin(target) {
|
|
1282
|
+
const out = [];
|
|
1283
|
+
for (const [tab, held] of this.peerHolds) {
|
|
1284
|
+
for (const [path, fid] of held) {
|
|
1285
|
+
if (path !== target && !path.startsWith(`${target}/`)) continue;
|
|
1286
|
+
out.push({ tab, path, fid, instance: this.instances.get(tab) });
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
return out;
|
|
1290
|
+
}
|
|
1291
|
+
ask(to, body, cap) {
|
|
1292
|
+
const seq2 = ++this.seq;
|
|
1293
|
+
this.channel.postMessage({ tab: this.tab, to, seq: seq2, ...body });
|
|
1294
|
+
return new Promise((resolve2) => {
|
|
1295
|
+
const done5 = () => {
|
|
1296
|
+
clearTimeout(timer);
|
|
1297
|
+
this.waiting.delete(seq2);
|
|
1298
|
+
resolve2(left.size === 0);
|
|
1299
|
+
};
|
|
1300
|
+
const left = /* @__PURE__ */ new Set([to]);
|
|
1301
|
+
const timer = setTimeout(done5, cap);
|
|
1302
|
+
this.waiting.set(seq2, { left, done: done5 });
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1242
1305
|
peerHeld(path) {
|
|
1243
1306
|
const out = [];
|
|
1244
1307
|
for (const [tab, held] of this.peerHolds)
|
|
@@ -1274,6 +1337,7 @@ var RemoteWatch = class {
|
|
|
1274
1337
|
if (this.peers.delete(tab)) left = true;
|
|
1275
1338
|
this.others.delete(tab);
|
|
1276
1339
|
this.peerHolds.delete(tab);
|
|
1340
|
+
this.keeps?.left(tab);
|
|
1277
1341
|
this.ackers.delete(tab);
|
|
1278
1342
|
this.holders.delete(tab);
|
|
1279
1343
|
}
|
|
@@ -1440,6 +1504,7 @@ function translate(err, path, op = "lookup", mapped = true) {
|
|
|
1440
1504
|
return error;
|
|
1441
1505
|
}
|
|
1442
1506
|
var META_OPS = ["get", "update", "move", "remove", "links", "under", "all"];
|
|
1507
|
+
var FILE_ID_TRIES = 3;
|
|
1443
1508
|
function sidecar(meta) {
|
|
1444
1509
|
const pathOf = ([first]) => [first, "/"].flat().find((p) => typeof p === "string");
|
|
1445
1510
|
const guarded2 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
|
|
@@ -1683,18 +1748,48 @@ var OpfsFs = class {
|
|
|
1683
1748
|
this.channel?.postMessage(paths);
|
|
1684
1749
|
}
|
|
1685
1750
|
async fileId(path) {
|
|
1751
|
+
for (let tries = 0; tries < FILE_ID_TRIES; tries++) {
|
|
1752
|
+
const fid = await this.assignId(path);
|
|
1753
|
+
if (fid !== void 0) return fid;
|
|
1754
|
+
}
|
|
1755
|
+
throw fsError("EAGAIN", `${path}: moved while its file id was assigned`);
|
|
1756
|
+
}
|
|
1757
|
+
bindId(path) {
|
|
1758
|
+
return this.assignId(path);
|
|
1759
|
+
}
|
|
1760
|
+
async assignId(path) {
|
|
1686
1761
|
const found = await this.locate(path, true);
|
|
1687
1762
|
const [entry] = await (this.meta.fresh ?? this.meta.get).call(this.meta, [found.path]);
|
|
1688
1763
|
if (entry?.fid !== void 0) return entry.fid;
|
|
1689
1764
|
const fid = crypto.randomUUID();
|
|
1765
|
+
let moved2 = false;
|
|
1766
|
+
await this.meta.update(found.path, (now) => {
|
|
1767
|
+
const vacatedSince = now?.vacated && (!entry?.vacated || now.ino !== entry.ino);
|
|
1768
|
+
if (vacatedSince || entry !== void 0 && now === void 0) {
|
|
1769
|
+
moved2 = true;
|
|
1770
|
+
return now;
|
|
1771
|
+
}
|
|
1772
|
+
return { ...occupied(now), path: found.path, fid: now?.fid ?? fid };
|
|
1773
|
+
});
|
|
1774
|
+
if (moved2) return void 0;
|
|
1690
1775
|
this.idWrites++;
|
|
1691
|
-
await this.
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
const
|
|
1697
|
-
|
|
1776
|
+
const there = await this.locate(path, true).then(
|
|
1777
|
+
(at) => at.path === found.path,
|
|
1778
|
+
() => false
|
|
1779
|
+
);
|
|
1780
|
+
if (there) return (await this.meta.get([found.path]))[0]?.fid ?? fid;
|
|
1781
|
+
const gone = await this.locate(found.path, true).then(
|
|
1782
|
+
() => false,
|
|
1783
|
+
(err) => missing2(err)
|
|
1784
|
+
);
|
|
1785
|
+
if (gone) {
|
|
1786
|
+
await this.meta.update(found.path, (now) => {
|
|
1787
|
+
if (now?.fid !== fid || now.vacated) return now;
|
|
1788
|
+
const { fid: _, ...rest2 } = now;
|
|
1789
|
+
return rest2;
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
return void 0;
|
|
1698
1793
|
}
|
|
1699
1794
|
heldUnder = () => [];
|
|
1700
1795
|
heldBy(held) {
|
|
@@ -1720,6 +1815,20 @@ var OpfsFs = class {
|
|
|
1720
1815
|
const remote = this.remote;
|
|
1721
1816
|
return remote && { ...remote.holdCounts(), held: remote.peerHeldPaths() };
|
|
1722
1817
|
}
|
|
1818
|
+
peerKeeps() {
|
|
1819
|
+
const remote = this.remote;
|
|
1820
|
+
if (!remote) return void 0;
|
|
1821
|
+
return {
|
|
1822
|
+
shared: () => this.shared(),
|
|
1823
|
+
heldWithin: (path) => remote.heldWithin(path),
|
|
1824
|
+
ask: (to, body, cap) => remote.ask(to, body, cap)
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
servePeerKeeps(instance, handlers) {
|
|
1828
|
+
if (!this.remote) return;
|
|
1829
|
+
this.remote.instance = instance;
|
|
1830
|
+
this.remote.keeps = handlers;
|
|
1831
|
+
}
|
|
1723
1832
|
rehold(from, to) {
|
|
1724
1833
|
this.remote?.rehold(from, to);
|
|
1725
1834
|
}
|
|
@@ -2667,7 +2776,7 @@ var OpfsFs = class {
|
|
|
2667
2776
|
}
|
|
2668
2777
|
}
|
|
2669
2778
|
async writeFile(path, content, owner) {
|
|
2670
|
-
const [file, existed, target] = await this.fileFor(path);
|
|
2779
|
+
const [file, existed, target] = await this.fileFor(path, owner?.existing !== true);
|
|
2671
2780
|
if (!existed) await this.stampNew(target, owner, false);
|
|
2672
2781
|
if (!existed && content.length === 0) return;
|
|
2673
2782
|
let writable2;
|
|
@@ -8326,7 +8435,7 @@ function satisfies(version, range2) {
|
|
|
8326
8435
|
}
|
|
8327
8436
|
|
|
8328
8437
|
// src/version.ts
|
|
8329
|
-
var KERNEL_VERSION = true ? "1.
|
|
8438
|
+
var KERNEL_VERSION = true ? "1.162.0" : "0.0.0-dev";
|
|
8330
8439
|
var OS_RELEASE = "6.0.0-slicc";
|
|
8331
8440
|
function procVersion() {
|
|
8332
8441
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -9276,6 +9385,9 @@ var LOOKUP_OPS = [
|
|
|
9276
9385
|
var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
9277
9386
|
var TOUCHED_MAX = 4096;
|
|
9278
9387
|
var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
9388
|
+
var PEER_RM_MS = 1e3;
|
|
9389
|
+
var PEER_RENAME_MS = 1e4;
|
|
9390
|
+
var PEER_GATE_MS = PEER_RENAME_MS + 5e3;
|
|
9279
9391
|
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
9280
9392
|
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
9281
9393
|
var PathGate = class {
|
|
@@ -9318,7 +9430,9 @@ var UnlinkedKeeper = class {
|
|
|
9318
9430
|
links = /* @__PURE__ */ new Map();
|
|
9319
9431
|
linked = /* @__PURE__ */ new Map();
|
|
9320
9432
|
dirOwner;
|
|
9433
|
+
peers;
|
|
9321
9434
|
constructor(fs, holders, rootOf, options = {}) {
|
|
9435
|
+
this.peers = options.peers;
|
|
9322
9436
|
this.marks = options.marks ?? new PathMarks();
|
|
9323
9437
|
const { locks, owner } = options;
|
|
9324
9438
|
this.dirOwner = options.dirOwner;
|
|
@@ -9394,22 +9508,29 @@ var UnlinkedKeeper = class {
|
|
|
9394
9508
|
lstat: (path) => unreplaced(path, async () => counted2(await fs.lstat(path))),
|
|
9395
9509
|
async rm(path, options) {
|
|
9396
9510
|
const target = fs.resolvePath("/", path);
|
|
9397
|
-
await
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
|
|
9511
|
+
const kept = await keeper.peerKeep(target, false, () => keeper.removable(target, options));
|
|
9512
|
+
if (kept.whole) return;
|
|
9513
|
+
await kept.undoing(
|
|
9514
|
+
() => replace(target, async (gated, sealed) => {
|
|
9515
|
+
const pinned = await keeper.pin(target, gated, sealed);
|
|
9516
|
+
if (pinned.moved.has(target)) pinned.commit();
|
|
9517
|
+
else await keeper.settle(pinned, () => fs.rm(path, options));
|
|
9518
|
+
})
|
|
9519
|
+
);
|
|
9402
9520
|
},
|
|
9403
9521
|
async rename(from, to) {
|
|
9404
9522
|
const target = fs.resolvePath("/", to);
|
|
9405
9523
|
const source = fs.resolvePath("/", from);
|
|
9406
9524
|
if (target === source) return fs.rename(from, to);
|
|
9407
|
-
await keeper.
|
|
9408
|
-
|
|
9409
|
-
() =>
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9525
|
+
const kept = await keeper.peerKeep(target, true, () => keeper.renamable(source, target));
|
|
9526
|
+
await kept.undoing(
|
|
9527
|
+
() => keeper.marks.around(
|
|
9528
|
+
[source],
|
|
9529
|
+
() => replace(target, async (gated, sealed) => {
|
|
9530
|
+
const pinned = await keeper.pin(target, gated, sealed);
|
|
9531
|
+
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
9532
|
+
})
|
|
9533
|
+
)
|
|
9413
9534
|
);
|
|
9414
9535
|
},
|
|
9415
9536
|
symlink: (target, path, owner) => keeper.marks.around([fs.resolvePath("/", path)], () => fs.symlink(target, path, owner)),
|
|
@@ -9554,6 +9675,114 @@ var UnlinkedKeeper = class {
|
|
|
9554
9675
|
(err) => err?.code !== "ENOENT" && err?.code !== "ENOTDIR"
|
|
9555
9676
|
);
|
|
9556
9677
|
}
|
|
9678
|
+
get id() {
|
|
9679
|
+
return this.instance;
|
|
9680
|
+
}
|
|
9681
|
+
adopt(hidden, refs) {
|
|
9682
|
+
return this.keptFile(hidden, { refs });
|
|
9683
|
+
}
|
|
9684
|
+
async peerKeep(target, over, valid = async () => true) {
|
|
9685
|
+
const none = { whole: false, undoing: (op) => op() };
|
|
9686
|
+
const peers = this.peers;
|
|
9687
|
+
if (!peers?.shared() || this.rootOf(target) !== "/") return none;
|
|
9688
|
+
const held = peers.heldWithin(target).filter((holder) => holder.instance !== void 0);
|
|
9689
|
+
if (held.length === 0 || !await valid()) return none;
|
|
9690
|
+
const tabs = /* @__PURE__ */ new Map();
|
|
9691
|
+
for (const holder of held) tabs.set(holder.tab, [...tabs.get(holder.tab) ?? [], holder]);
|
|
9692
|
+
const asked = [...tabs].map(([tab, holders]) => {
|
|
9693
|
+
const paths = holders.map((holder) => holder.path);
|
|
9694
|
+
return { tab, holders, paths };
|
|
9695
|
+
});
|
|
9696
|
+
const heard = await Promise.all(
|
|
9697
|
+
asked.map(
|
|
9698
|
+
({ tab, paths }) => peers.ask(tab, { keeping: { paths } }, over ? PEER_RENAME_MS : PEER_RM_MS)
|
|
9699
|
+
)
|
|
9700
|
+
);
|
|
9701
|
+
if (over && heard.includes(false)) {
|
|
9702
|
+
for (const { tab, paths } of asked)
|
|
9703
|
+
void peers.ask(tab, { kept: { items: [], paths } }, PEER_RM_MS);
|
|
9704
|
+
throw fsError("EBUSY", `${target}: open in another tab that did not answer`);
|
|
9705
|
+
}
|
|
9706
|
+
const { whole, moved: moved2 } = await this.setAside(peers, target, asked, heard);
|
|
9707
|
+
return { whole, undoing: (op) => this.undoing(op, moved2) };
|
|
9708
|
+
}
|
|
9709
|
+
async setAside(peers, target, asked, heard) {
|
|
9710
|
+
const mine = this.heldWithin(target);
|
|
9711
|
+
const taken = /* @__PURE__ */ new Set();
|
|
9712
|
+
let whole = false;
|
|
9713
|
+
const told = [];
|
|
9714
|
+
const moved2 = [];
|
|
9715
|
+
for (const [i, { tab, holders, paths }] of asked.entries()) {
|
|
9716
|
+
const items = [];
|
|
9717
|
+
for (const holder of holders) {
|
|
9718
|
+
const free = !mine.has(holder.path) && !taken.has(holder.path);
|
|
9719
|
+
const hidden = free ? await this.asideFor(holder.path, holder.instance) : void 0;
|
|
9720
|
+
if (hidden !== void 0) {
|
|
9721
|
+
taken.add(holder.path);
|
|
9722
|
+
moved2.push({ tab, path: holder.path, hidden, fid: holder.fid });
|
|
9723
|
+
if (holder.path === target) whole = true;
|
|
9724
|
+
}
|
|
9725
|
+
items.push({ path: holder.path, hidden, fid: holder.fid });
|
|
9726
|
+
}
|
|
9727
|
+
const answer2 = peers.ask(tab, { kept: { items, paths } }, PEER_RM_MS);
|
|
9728
|
+
if (heard[i]) told.push(answer2);
|
|
9729
|
+
}
|
|
9730
|
+
await Promise.all(told);
|
|
9731
|
+
return { whole, moved: moved2 };
|
|
9732
|
+
}
|
|
9733
|
+
async undoing(op, moved2) {
|
|
9734
|
+
try {
|
|
9735
|
+
return await op();
|
|
9736
|
+
} catch (err) {
|
|
9737
|
+
for (const { tab, path, hidden, fid } of moved2) {
|
|
9738
|
+
const back = await this.serial(async () => {
|
|
9739
|
+
if (await this.present(path)) return false;
|
|
9740
|
+
return this.fs.rename(hidden, path).then(
|
|
9741
|
+
() => true,
|
|
9742
|
+
() => false
|
|
9743
|
+
);
|
|
9744
|
+
});
|
|
9745
|
+
if (!back) continue;
|
|
9746
|
+
void this.peers?.ask(
|
|
9747
|
+
tab,
|
|
9748
|
+
{ kept: { items: [{ path, hidden: void 0, fid, undo: hidden }], paths: [] } },
|
|
9749
|
+
PEER_RM_MS
|
|
9750
|
+
);
|
|
9751
|
+
}
|
|
9752
|
+
throw err;
|
|
9753
|
+
}
|
|
9754
|
+
}
|
|
9755
|
+
async removable(target, options) {
|
|
9756
|
+
const st = await this.fs.lstat(target).catch(() => void 0);
|
|
9757
|
+
return st !== void 0 && (!st.isDirectory || options?.recursive === true);
|
|
9758
|
+
}
|
|
9759
|
+
async renamable(source, target) {
|
|
9760
|
+
const from = await this.fs.lstat(source);
|
|
9761
|
+
const to = await this.fs.lstat(target).catch(() => void 0);
|
|
9762
|
+
if (!to) return true;
|
|
9763
|
+
if (to.isDirectory && !from.isDirectory) throw fsError("EISDIR", target);
|
|
9764
|
+
if (!to.isDirectory && from.isDirectory) throw fsError("ENOTDIR", target);
|
|
9765
|
+
if (to.isDirectory && (await this.fs.readdir(target)).length > 0)
|
|
9766
|
+
throw fsError("ENOTEMPTY", target);
|
|
9767
|
+
return true;
|
|
9768
|
+
}
|
|
9769
|
+
async asideFor(path, instance) {
|
|
9770
|
+
return await this.hideFor(path, instance) ?? this.hideFor(path, instance);
|
|
9771
|
+
}
|
|
9772
|
+
hideFor(path, instance) {
|
|
9773
|
+
return this.serial(async () => {
|
|
9774
|
+
const dir = dirOf("/");
|
|
9775
|
+
const hidden = `${dir}/${instance}.p${crypto.randomUUID().replaceAll("-", "")}`;
|
|
9776
|
+
try {
|
|
9777
|
+
const owner = this.dirOwner;
|
|
9778
|
+
await this.fs.mkdir(dir, { recursive: true, ...owner ? { owner } : {} });
|
|
9779
|
+
await this.fs.rename(path, hidden);
|
|
9780
|
+
return hidden;
|
|
9781
|
+
} catch {
|
|
9782
|
+
return void 0;
|
|
9783
|
+
}
|
|
9784
|
+
});
|
|
9785
|
+
}
|
|
9557
9786
|
keptFile(hidden, entry) {
|
|
9558
9787
|
return {
|
|
9559
9788
|
hidden,
|
|
@@ -9608,7 +9837,7 @@ var UnlinkedKeeper = class {
|
|
|
9608
9837
|
}
|
|
9609
9838
|
async tidy(dir) {
|
|
9610
9839
|
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
9611
|
-
if (left.length === 0) await this.fs.rm(dir
|
|
9840
|
+
if (left.length === 0) await this.fs.rm(dir).catch(() => void 0);
|
|
9612
9841
|
}
|
|
9613
9842
|
sweep(root) {
|
|
9614
9843
|
const query = this.locks?.query?.bind(this.locks);
|
|
@@ -10853,6 +11082,11 @@ async function shownTarget(fs, target) {
|
|
|
10853
11082
|
const st = await quiet(fs.stat(target));
|
|
10854
11083
|
return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
|
|
10855
11084
|
}
|
|
11085
|
+
async function madeFor(owner, virtual) {
|
|
11086
|
+
if (!owner?.existing || await virtual() === void 0) return owner;
|
|
11087
|
+
const { existing: _, ...made2 } = owner;
|
|
11088
|
+
return made2;
|
|
11089
|
+
}
|
|
10856
11090
|
function withCommandDirs(fs, names2, userDir = async () => void 0, owners = false, targetOf2 = async () => void 0) {
|
|
10857
11091
|
async function nearest(path) {
|
|
10858
11092
|
const st = await fs.stat(path).catch(() => void 0);
|
|
@@ -10950,7 +11184,7 @@ function withCommandDirs(fs, names2, userDir = async () => void 0, owners = fals
|
|
|
10950
11184
|
}),
|
|
10951
11185
|
readFileBuffer: (path) => fs.readFileBuffer(path).catch((err) => content(path, err)),
|
|
10952
11186
|
commandTarget: async (path) => (await entryTarget(path))?.path,
|
|
10953
|
-
writeFile: (path, content2, owner) => fs.writeFile(path, content2, owner),
|
|
11187
|
+
writeFile: async (path, content2, owner) => fs.writeFile(path, content2, await madeFor(owner, () => entryTarget(path))),
|
|
10954
11188
|
link: (from, to, follow2) => fs.link(from, to, follow2),
|
|
10955
11189
|
links: (path) => mayLink(fs, path),
|
|
10956
11190
|
async exists(path) {
|
|
@@ -11115,6 +11349,10 @@ function withEtcFiles(fs, files) {
|
|
|
11115
11349
|
}
|
|
11116
11350
|
} : {},
|
|
11117
11351
|
readFile: (path) => fs.readFile(path).catch(fallback(path, (b) => new TextDecoder().decode(b))),
|
|
11352
|
+
writeFile: async (path, content, owner) => {
|
|
11353
|
+
if (owner?.existing && files.has(path)) await materialize(path);
|
|
11354
|
+
return fs.writeFile(path, content, owner);
|
|
11355
|
+
},
|
|
11118
11356
|
...fs.append ? {
|
|
11119
11357
|
append: async (path, bytes, options) => {
|
|
11120
11358
|
if (files.has(path)) await materialize(path);
|
|
@@ -13073,12 +13311,25 @@ var HeldPaths = class extends Map {
|
|
|
13073
13311
|
for (const file of this.unlinked.values()) if (file.hidden === hidden) return true;
|
|
13074
13312
|
return false;
|
|
13075
13313
|
}
|
|
13314
|
+
boundAt(path, fid) {
|
|
13315
|
+
return this.handlesAt(path).filter(
|
|
13316
|
+
(id2) => !this.keptHandles.has(id2) && this.bound.get(id2)?.fid === fid
|
|
13317
|
+
);
|
|
13318
|
+
}
|
|
13319
|
+
keepBound(ids, path, file) {
|
|
13320
|
+
for (const id2 of ids) this.keptHandles.set(id2, file);
|
|
13321
|
+
this.uses.set(file, ids.length);
|
|
13322
|
+
this.unlinked.set(path, file);
|
|
13323
|
+
}
|
|
13076
13324
|
keep(path, file) {
|
|
13077
13325
|
const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
|
|
13078
13326
|
for (const id2 of fresh3) this.keptHandles.set(id2, file);
|
|
13079
13327
|
this.uses.set(file, Math.max(fresh3.length, 1));
|
|
13080
13328
|
this.unlinked.set(path, file);
|
|
13081
13329
|
}
|
|
13330
|
+
unkeepAt(path, hidden) {
|
|
13331
|
+
for (const file of [...this.uses.keys()]) if (file.hidden === hidden) this.unkeep(path, file);
|
|
13332
|
+
}
|
|
13082
13333
|
unkeep(path, file) {
|
|
13083
13334
|
for (const [id2, kept] of this.keptHandles) if (kept === file) this.keptHandles.delete(id2);
|
|
13084
13335
|
this.uses.delete(file);
|
|
@@ -13647,12 +13898,12 @@ var MountFs = class _MountFs {
|
|
|
13647
13898
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
13648
13899
|
return this.follow(this.base.resolvePath(parentOf8(real), target), hops + 1, create);
|
|
13649
13900
|
}
|
|
13650
|
-
async open(mount, rel2, write, owner) {
|
|
13901
|
+
async open(mount, rel2, write, owner, create = write) {
|
|
13651
13902
|
return await mount.conn.call({
|
|
13652
13903
|
op: "open",
|
|
13653
13904
|
path: rel2,
|
|
13654
13905
|
write,
|
|
13655
|
-
create
|
|
13906
|
+
create,
|
|
13656
13907
|
truncate: write,
|
|
13657
13908
|
exclusive: false,
|
|
13658
13909
|
...owner
|
|
@@ -13749,10 +14000,10 @@ var MountFs = class _MountFs {
|
|
|
13749
14000
|
`${real} would be larger than this mount's maxfile (${mount.maxFile})`
|
|
13750
14001
|
);
|
|
13751
14002
|
}
|
|
13752
|
-
const made2 = await this.driverOwner(found, real, owner, false);
|
|
14003
|
+
const made2 = owner?.existing ? void 0 : await this.driverOwner(found, real, owner, false);
|
|
13753
14004
|
for (let attempt = 1; ; attempt++) {
|
|
13754
14005
|
try {
|
|
13755
|
-
return await this.writeWhole(mount, real, rel2, bytes, made2);
|
|
14006
|
+
return await this.writeWhole(mount, real, rel2, bytes, made2, owner?.existing !== true);
|
|
13756
14007
|
} catch (err) {
|
|
13757
14008
|
if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
|
|
13758
14009
|
}
|
|
@@ -13770,10 +14021,10 @@ var MountFs = class _MountFs {
|
|
|
13770
14021
|
this.table.forget(mount, real);
|
|
13771
14022
|
}
|
|
13772
14023
|
}
|
|
13773
|
-
async writeWhole(mount, real, rel2, bytes, owner) {
|
|
14024
|
+
async writeWhole(mount, real, rel2, bytes, owner, create = true) {
|
|
13774
14025
|
await this.table.commit(real);
|
|
13775
14026
|
this.table.forget(mount, real);
|
|
13776
|
-
const fh = await this.open(mount, rel2, true, owner);
|
|
14027
|
+
const fh = await this.open(mount, rel2, true, owner, create);
|
|
13777
14028
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
13778
14029
|
try {
|
|
13779
14030
|
for (let offset = 0; offset < bytes.length; offset += chunk2) {
|
|
@@ -14510,6 +14761,11 @@ function ownerFor2(entry, mode) {
|
|
|
14510
14761
|
if (!cred) return void 0;
|
|
14511
14762
|
return ownerOf(cred, mode === void 0 ? void 0 : mode & ~(entry.umask?.() ?? 18));
|
|
14512
14763
|
}
|
|
14764
|
+
function writeOwner(entry, req) {
|
|
14765
|
+
const owner = ownerFor2(entry, 438);
|
|
14766
|
+
if (req.handle === void 0 || req.mode !== 1) return owner;
|
|
14767
|
+
return { ...owner ?? {}, existing: true };
|
|
14768
|
+
}
|
|
14513
14769
|
function plainDirMode(entry, mode) {
|
|
14514
14770
|
if (entry.cred || mode === void 0) return void 0;
|
|
14515
14771
|
const wanted = mode & 4095 & ~(entry.umask?.() ?? 18);
|
|
@@ -14580,7 +14836,7 @@ async function run(entry, path, req) {
|
|
|
14580
14836
|
case "read":
|
|
14581
14837
|
return { ok: true, kind: "bytes", bytes: await readSource(entry, path) };
|
|
14582
14838
|
case "write":
|
|
14583
|
-
await fs.writeFile(path, req.body ?? new Uint8Array(0),
|
|
14839
|
+
await fs.writeFile(path, req.body ?? new Uint8Array(0), writeOwner(entry, req));
|
|
14584
14840
|
return done2;
|
|
14585
14841
|
case "exists":
|
|
14586
14842
|
return json2(await fs.exists(path));
|
|
@@ -31176,7 +31432,7 @@ var Launcher = class {
|
|
|
31176
31432
|
busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
|
|
31177
31433
|
owner: options.unlinkOwner,
|
|
31178
31434
|
marks: this.pathMarks,
|
|
31179
|
-
...this.
|
|
31435
|
+
...this.peerOptions(options.fs)
|
|
31180
31436
|
}
|
|
31181
31437
|
);
|
|
31182
31438
|
this.fs = this.processChain(fs, this.nodes, this.unlinked);
|
|
@@ -32010,7 +32266,7 @@ var Launcher = class {
|
|
|
32010
32266
|
const assign = this.mounts.locate(target.wasm) === void 0 ? this.rootFs.fileId : void 0;
|
|
32011
32267
|
return this.base.stat(target.wasm).then(
|
|
32012
32268
|
async (st) => {
|
|
32013
|
-
const fid = st.fid ?? await assign?.call(this.rootFs, target.wasm);
|
|
32269
|
+
const fid = st.fid ?? await assign?.call(this.rootFs, target.wasm)?.catch(() => void 0);
|
|
32014
32270
|
if (fid === void 0) return exeIdOf(fileIdentity(st.ino, st.mtime.getTime()));
|
|
32015
32271
|
return { exeId: `fid:${fid}:${st.mtime.getTime()}` };
|
|
32016
32272
|
},
|
|
@@ -33172,8 +33428,82 @@ var Launcher = class {
|
|
|
33172
33428
|
this.writers ??= this.writerStack();
|
|
33173
33429
|
return this.writers;
|
|
33174
33430
|
}
|
|
33431
|
+
peerGates = /* @__PURE__ */ new Map();
|
|
33432
|
+
peerOptions(fs) {
|
|
33433
|
+
const peers = fs.peerKeeps?.();
|
|
33434
|
+
queueMicrotask(() => fs.servePeerKeeps?.(this.unlinked.id, this.peerKept()));
|
|
33435
|
+
return {
|
|
33436
|
+
...peers ? { peers } : {},
|
|
33437
|
+
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
33438
|
+
};
|
|
33439
|
+
}
|
|
33440
|
+
peerKept() {
|
|
33441
|
+
const gates = this.peerGates;
|
|
33442
|
+
const gone = /* @__PURE__ */ new Set();
|
|
33443
|
+
const instance = this.unlinked.id;
|
|
33444
|
+
const ours = (hidden) => hidden.startsWith(`/${UNLINKED_DIR}/${instance}.p`) && /^[0-9a-f]+$/.test(hidden.slice(`/${UNLINKED_DIR}/${instance}.p`.length));
|
|
33445
|
+
const open2 = (tab, paths) => {
|
|
33446
|
+
for (const [key, release] of [...gates]) {
|
|
33447
|
+
const [owner, path] = key.split("\0");
|
|
33448
|
+
if (owner !== tab || paths && !paths.includes(path)) continue;
|
|
33449
|
+
gates.delete(key);
|
|
33450
|
+
release();
|
|
33451
|
+
}
|
|
33452
|
+
};
|
|
33453
|
+
return {
|
|
33454
|
+
keeping: async (tab, paths) => {
|
|
33455
|
+
for (const path of paths) {
|
|
33456
|
+
if (gone.has(tab)) return;
|
|
33457
|
+
const release = await this.unlinked.gate.acquire(path);
|
|
33458
|
+
const key = `${tab}\0${path}`;
|
|
33459
|
+
let done5 = false;
|
|
33460
|
+
const once2 = () => {
|
|
33461
|
+
if (done5) return;
|
|
33462
|
+
done5 = true;
|
|
33463
|
+
clearTimeout(timer);
|
|
33464
|
+
release();
|
|
33465
|
+
};
|
|
33466
|
+
const timer = setTimeout(() => {
|
|
33467
|
+
if (gates.get(key) === once2) gates.delete(key);
|
|
33468
|
+
once2();
|
|
33469
|
+
}, PEER_GATE_MS);
|
|
33470
|
+
if (gone.has(tab)) once2();
|
|
33471
|
+
else gates.set(key, once2);
|
|
33472
|
+
}
|
|
33473
|
+
},
|
|
33474
|
+
kept: async (tab, items, paths) => {
|
|
33475
|
+
for (const { path, hidden, fid, undo } of items) {
|
|
33476
|
+
if (undo !== void 0) this.unadopt(path, undo, fid);
|
|
33477
|
+
else this.adoptKept(path, hidden, fid, ours);
|
|
33478
|
+
}
|
|
33479
|
+
open2(tab, paths);
|
|
33480
|
+
},
|
|
33481
|
+
left: (tab) => {
|
|
33482
|
+
gone.add(tab);
|
|
33483
|
+
open2(tab);
|
|
33484
|
+
}
|
|
33485
|
+
};
|
|
33486
|
+
}
|
|
33487
|
+
unadopt(path, undo, fid) {
|
|
33488
|
+
for (const holds2 of this.held) holds2.unkeepAt(path, undo);
|
|
33489
|
+
void this.rootFs.announceHolds?.([[path, fid]], [], false);
|
|
33490
|
+
}
|
|
33491
|
+
adoptKept(path, hidden, fid, ours) {
|
|
33492
|
+
if (hidden === void 0 || !ours(hidden)) {
|
|
33493
|
+
this.held.doubt([path]);
|
|
33494
|
+
return;
|
|
33495
|
+
}
|
|
33496
|
+
const bound3 = [...this.held].map(
|
|
33497
|
+
(holds2) => [holds2, [...holds2.boundAt(path, fid), ...holds2.boundAt(hidden, fid)]]
|
|
33498
|
+
);
|
|
33499
|
+
const keeping = bound3.filter(([, ids]) => ids.length > 0);
|
|
33500
|
+
const file = this.unlinked.adopt(hidden, Math.max(keeping.length, 1));
|
|
33501
|
+
if (keeping.length === 0) file.release();
|
|
33502
|
+
for (const [holds2, ids] of keeping) holds2.keepBound(ids, path, file);
|
|
33503
|
+
void this.rootFs.announceHolds?.([], [path, hidden], false);
|
|
33504
|
+
}
|
|
33175
33505
|
rootFileId(path) {
|
|
33176
|
-
return this.mounts.locate(path) === void 0 ? this.rootFs.
|
|
33506
|
+
return this.mounts.locate(path) === void 0 ? this.rootFs.bindId?.(path) : void 0;
|
|
33177
33507
|
}
|
|
33178
33508
|
flushingPeers(fs) {
|
|
33179
33509
|
const peers = fs.peers?.();
|
|
@@ -33219,6 +33549,7 @@ var Launcher = class {
|
|
|
33219
33549
|
busy: (path) => [...this.openFiles].some((open2) => open2.holds(path)),
|
|
33220
33550
|
owner: this.unlinkOwnerOption,
|
|
33221
33551
|
marks: this.pathMarks,
|
|
33552
|
+
peers: this.rootFs.peerKeeps?.(),
|
|
33222
33553
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
33223
33554
|
}
|
|
33224
33555
|
);
|