@ai-ecoverse/slicc-kernel 1.161.0 → 1.162.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 +2 -2
- package/dist/index.js.map +1 -1
- package/dist/kernel-worker.js +368 -35
- 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 +390 -44
- 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/node.d.ts +2 -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;
|
|
@@ -6541,7 +6650,9 @@ var KernelSocket = class _KernelSocket {
|
|
|
6541
6650
|
throw new KernelError("ECONNRESET", "read ECONNRESET");
|
|
6542
6651
|
return bytes.length > 0 ? bytes : null;
|
|
6543
6652
|
},
|
|
6544
|
-
write: (bytes) => raw2.tx.write(bytes)
|
|
6653
|
+
write: (bytes) => raw2.tx.write(bytes).catch((err) => {
|
|
6654
|
+
throw this.refused ? new KernelError("ECONNRESET", "read ECONNRESET") : err;
|
|
6655
|
+
}),
|
|
6545
6656
|
closeWrite: () => raw2.tx.closeWrite(),
|
|
6546
6657
|
close: () => {
|
|
6547
6658
|
raw2.rx.closeRead();
|
|
@@ -8326,7 +8437,7 @@ function satisfies(version, range2) {
|
|
|
8326
8437
|
}
|
|
8327
8438
|
|
|
8328
8439
|
// src/version.ts
|
|
8329
|
-
var KERNEL_VERSION = true ? "1.
|
|
8440
|
+
var KERNEL_VERSION = true ? "1.162.1" : "0.0.0-dev";
|
|
8330
8441
|
var OS_RELEASE = "6.0.0-slicc";
|
|
8331
8442
|
function procVersion() {
|
|
8332
8443
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -9276,6 +9387,9 @@ var LOOKUP_OPS = [
|
|
|
9276
9387
|
var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
9277
9388
|
var TOUCHED_MAX = 4096;
|
|
9278
9389
|
var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
9390
|
+
var PEER_RM_MS = 1e3;
|
|
9391
|
+
var PEER_RENAME_MS = 1e4;
|
|
9392
|
+
var PEER_GATE_MS = PEER_RENAME_MS + 5e3;
|
|
9279
9393
|
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
9280
9394
|
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
9281
9395
|
var PathGate = class {
|
|
@@ -9318,7 +9432,9 @@ var UnlinkedKeeper = class {
|
|
|
9318
9432
|
links = /* @__PURE__ */ new Map();
|
|
9319
9433
|
linked = /* @__PURE__ */ new Map();
|
|
9320
9434
|
dirOwner;
|
|
9435
|
+
peers;
|
|
9321
9436
|
constructor(fs, holders, rootOf, options = {}) {
|
|
9437
|
+
this.peers = options.peers;
|
|
9322
9438
|
this.marks = options.marks ?? new PathMarks();
|
|
9323
9439
|
const { locks, owner } = options;
|
|
9324
9440
|
this.dirOwner = options.dirOwner;
|
|
@@ -9394,22 +9510,29 @@ var UnlinkedKeeper = class {
|
|
|
9394
9510
|
lstat: (path) => unreplaced(path, async () => counted2(await fs.lstat(path))),
|
|
9395
9511
|
async rm(path, options) {
|
|
9396
9512
|
const target = fs.resolvePath("/", path);
|
|
9397
|
-
await
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
|
|
9513
|
+
const kept = await keeper.peerKeep(target, false, () => keeper.removable(target, options));
|
|
9514
|
+
if (kept.whole) return;
|
|
9515
|
+
await kept.undoing(
|
|
9516
|
+
() => replace(target, async (gated, sealed) => {
|
|
9517
|
+
const pinned = await keeper.pin(target, gated, sealed);
|
|
9518
|
+
if (pinned.moved.has(target)) pinned.commit();
|
|
9519
|
+
else await keeper.settle(pinned, () => fs.rm(path, options));
|
|
9520
|
+
})
|
|
9521
|
+
);
|
|
9402
9522
|
},
|
|
9403
9523
|
async rename(from, to) {
|
|
9404
9524
|
const target = fs.resolvePath("/", to);
|
|
9405
9525
|
const source = fs.resolvePath("/", from);
|
|
9406
9526
|
if (target === source) return fs.rename(from, to);
|
|
9407
|
-
await keeper.
|
|
9408
|
-
|
|
9409
|
-
() =>
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9527
|
+
const kept = await keeper.peerKeep(target, true, () => keeper.renamable(source, target));
|
|
9528
|
+
await kept.undoing(
|
|
9529
|
+
() => keeper.marks.around(
|
|
9530
|
+
[source],
|
|
9531
|
+
() => replace(target, async (gated, sealed) => {
|
|
9532
|
+
const pinned = await keeper.pin(target, gated, sealed);
|
|
9533
|
+
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
9534
|
+
})
|
|
9535
|
+
)
|
|
9413
9536
|
);
|
|
9414
9537
|
},
|
|
9415
9538
|
symlink: (target, path, owner) => keeper.marks.around([fs.resolvePath("/", path)], () => fs.symlink(target, path, owner)),
|
|
@@ -9554,6 +9677,114 @@ var UnlinkedKeeper = class {
|
|
|
9554
9677
|
(err) => err?.code !== "ENOENT" && err?.code !== "ENOTDIR"
|
|
9555
9678
|
);
|
|
9556
9679
|
}
|
|
9680
|
+
get id() {
|
|
9681
|
+
return this.instance;
|
|
9682
|
+
}
|
|
9683
|
+
adopt(hidden, refs) {
|
|
9684
|
+
return this.keptFile(hidden, { refs });
|
|
9685
|
+
}
|
|
9686
|
+
async peerKeep(target, over, valid = async () => true) {
|
|
9687
|
+
const none = { whole: false, undoing: (op) => op() };
|
|
9688
|
+
const peers = this.peers;
|
|
9689
|
+
if (!peers?.shared() || this.rootOf(target) !== "/") return none;
|
|
9690
|
+
const held = peers.heldWithin(target).filter((holder) => holder.instance !== void 0);
|
|
9691
|
+
if (held.length === 0 || !await valid()) return none;
|
|
9692
|
+
const tabs = /* @__PURE__ */ new Map();
|
|
9693
|
+
for (const holder of held) tabs.set(holder.tab, [...tabs.get(holder.tab) ?? [], holder]);
|
|
9694
|
+
const asked = [...tabs].map(([tab, holders]) => {
|
|
9695
|
+
const paths = holders.map((holder) => holder.path);
|
|
9696
|
+
return { tab, holders, paths };
|
|
9697
|
+
});
|
|
9698
|
+
const heard = await Promise.all(
|
|
9699
|
+
asked.map(
|
|
9700
|
+
({ tab, paths }) => peers.ask(tab, { keeping: { paths } }, over ? PEER_RENAME_MS : PEER_RM_MS)
|
|
9701
|
+
)
|
|
9702
|
+
);
|
|
9703
|
+
if (over && heard.includes(false)) {
|
|
9704
|
+
for (const { tab, paths } of asked)
|
|
9705
|
+
void peers.ask(tab, { kept: { items: [], paths } }, PEER_RM_MS);
|
|
9706
|
+
throw fsError("EBUSY", `${target}: open in another tab that did not answer`);
|
|
9707
|
+
}
|
|
9708
|
+
const { whole, moved: moved2 } = await this.setAside(peers, target, asked, heard);
|
|
9709
|
+
return { whole, undoing: (op) => this.undoing(op, moved2) };
|
|
9710
|
+
}
|
|
9711
|
+
async setAside(peers, target, asked, heard) {
|
|
9712
|
+
const mine = this.heldWithin(target);
|
|
9713
|
+
const taken = /* @__PURE__ */ new Set();
|
|
9714
|
+
let whole = false;
|
|
9715
|
+
const told = [];
|
|
9716
|
+
const moved2 = [];
|
|
9717
|
+
for (const [i, { tab, holders, paths }] of asked.entries()) {
|
|
9718
|
+
const items = [];
|
|
9719
|
+
for (const holder of holders) {
|
|
9720
|
+
const free = !mine.has(holder.path) && !taken.has(holder.path);
|
|
9721
|
+
const hidden = free ? await this.asideFor(holder.path, holder.instance) : void 0;
|
|
9722
|
+
if (hidden !== void 0) {
|
|
9723
|
+
taken.add(holder.path);
|
|
9724
|
+
moved2.push({ tab, path: holder.path, hidden, fid: holder.fid });
|
|
9725
|
+
if (holder.path === target) whole = true;
|
|
9726
|
+
}
|
|
9727
|
+
items.push({ path: holder.path, hidden, fid: holder.fid });
|
|
9728
|
+
}
|
|
9729
|
+
const answer2 = peers.ask(tab, { kept: { items, paths } }, PEER_RM_MS);
|
|
9730
|
+
if (heard[i]) told.push(answer2);
|
|
9731
|
+
}
|
|
9732
|
+
await Promise.all(told);
|
|
9733
|
+
return { whole, moved: moved2 };
|
|
9734
|
+
}
|
|
9735
|
+
async undoing(op, moved2) {
|
|
9736
|
+
try {
|
|
9737
|
+
return await op();
|
|
9738
|
+
} catch (err) {
|
|
9739
|
+
for (const { tab, path, hidden, fid } of moved2) {
|
|
9740
|
+
const back = await this.serial(async () => {
|
|
9741
|
+
if (await this.present(path)) return false;
|
|
9742
|
+
return this.fs.rename(hidden, path).then(
|
|
9743
|
+
() => true,
|
|
9744
|
+
() => false
|
|
9745
|
+
);
|
|
9746
|
+
});
|
|
9747
|
+
if (!back) continue;
|
|
9748
|
+
void this.peers?.ask(
|
|
9749
|
+
tab,
|
|
9750
|
+
{ kept: { items: [{ path, hidden: void 0, fid, undo: hidden }], paths: [] } },
|
|
9751
|
+
PEER_RM_MS
|
|
9752
|
+
);
|
|
9753
|
+
}
|
|
9754
|
+
throw err;
|
|
9755
|
+
}
|
|
9756
|
+
}
|
|
9757
|
+
async removable(target, options) {
|
|
9758
|
+
const st = await this.fs.lstat(target).catch(() => void 0);
|
|
9759
|
+
return st !== void 0 && (!st.isDirectory || options?.recursive === true);
|
|
9760
|
+
}
|
|
9761
|
+
async renamable(source, target) {
|
|
9762
|
+
const from = await this.fs.lstat(source);
|
|
9763
|
+
const to = await this.fs.lstat(target).catch(() => void 0);
|
|
9764
|
+
if (!to) return true;
|
|
9765
|
+
if (to.isDirectory && !from.isDirectory) throw fsError("EISDIR", target);
|
|
9766
|
+
if (!to.isDirectory && from.isDirectory) throw fsError("ENOTDIR", target);
|
|
9767
|
+
if (to.isDirectory && (await this.fs.readdir(target)).length > 0)
|
|
9768
|
+
throw fsError("ENOTEMPTY", target);
|
|
9769
|
+
return true;
|
|
9770
|
+
}
|
|
9771
|
+
async asideFor(path, instance) {
|
|
9772
|
+
return await this.hideFor(path, instance) ?? this.hideFor(path, instance);
|
|
9773
|
+
}
|
|
9774
|
+
hideFor(path, instance) {
|
|
9775
|
+
return this.serial(async () => {
|
|
9776
|
+
const dir = dirOf("/");
|
|
9777
|
+
const hidden = `${dir}/${instance}.p${crypto.randomUUID().replaceAll("-", "")}`;
|
|
9778
|
+
try {
|
|
9779
|
+
const owner = this.dirOwner;
|
|
9780
|
+
await this.fs.mkdir(dir, { recursive: true, ...owner ? { owner } : {} });
|
|
9781
|
+
await this.fs.rename(path, hidden);
|
|
9782
|
+
return hidden;
|
|
9783
|
+
} catch {
|
|
9784
|
+
return void 0;
|
|
9785
|
+
}
|
|
9786
|
+
});
|
|
9787
|
+
}
|
|
9557
9788
|
keptFile(hidden, entry) {
|
|
9558
9789
|
return {
|
|
9559
9790
|
hidden,
|
|
@@ -9608,7 +9839,7 @@ var UnlinkedKeeper = class {
|
|
|
9608
9839
|
}
|
|
9609
9840
|
async tidy(dir) {
|
|
9610
9841
|
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
9611
|
-
if (left.length === 0) await this.fs.rm(dir
|
|
9842
|
+
if (left.length === 0) await this.fs.rm(dir).catch(() => void 0);
|
|
9612
9843
|
}
|
|
9613
9844
|
sweep(root) {
|
|
9614
9845
|
const query = this.locks?.query?.bind(this.locks);
|
|
@@ -10853,6 +11084,11 @@ async function shownTarget(fs, target) {
|
|
|
10853
11084
|
const st = await quiet(fs.stat(target));
|
|
10854
11085
|
return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
|
|
10855
11086
|
}
|
|
11087
|
+
async function madeFor(owner, virtual) {
|
|
11088
|
+
if (!owner?.existing || await virtual() === void 0) return owner;
|
|
11089
|
+
const { existing: _, ...made2 } = owner;
|
|
11090
|
+
return made2;
|
|
11091
|
+
}
|
|
10856
11092
|
function withCommandDirs(fs, names2, userDir = async () => void 0, owners = false, targetOf2 = async () => void 0) {
|
|
10857
11093
|
async function nearest(path) {
|
|
10858
11094
|
const st = await fs.stat(path).catch(() => void 0);
|
|
@@ -10950,7 +11186,7 @@ function withCommandDirs(fs, names2, userDir = async () => void 0, owners = fals
|
|
|
10950
11186
|
}),
|
|
10951
11187
|
readFileBuffer: (path) => fs.readFileBuffer(path).catch((err) => content(path, err)),
|
|
10952
11188
|
commandTarget: async (path) => (await entryTarget(path))?.path,
|
|
10953
|
-
writeFile: (path, content2, owner) => fs.writeFile(path, content2, owner),
|
|
11189
|
+
writeFile: async (path, content2, owner) => fs.writeFile(path, content2, await madeFor(owner, () => entryTarget(path))),
|
|
10954
11190
|
link: (from, to, follow2) => fs.link(from, to, follow2),
|
|
10955
11191
|
links: (path) => mayLink(fs, path),
|
|
10956
11192
|
async exists(path) {
|
|
@@ -11115,6 +11351,10 @@ function withEtcFiles(fs, files) {
|
|
|
11115
11351
|
}
|
|
11116
11352
|
} : {},
|
|
11117
11353
|
readFile: (path) => fs.readFile(path).catch(fallback(path, (b) => new TextDecoder().decode(b))),
|
|
11354
|
+
writeFile: async (path, content, owner) => {
|
|
11355
|
+
if (owner?.existing && files.has(path)) await materialize(path);
|
|
11356
|
+
return fs.writeFile(path, content, owner);
|
|
11357
|
+
},
|
|
11118
11358
|
...fs.append ? {
|
|
11119
11359
|
append: async (path, bytes, options) => {
|
|
11120
11360
|
if (files.has(path)) await materialize(path);
|
|
@@ -13073,12 +13313,25 @@ var HeldPaths = class extends Map {
|
|
|
13073
13313
|
for (const file of this.unlinked.values()) if (file.hidden === hidden) return true;
|
|
13074
13314
|
return false;
|
|
13075
13315
|
}
|
|
13316
|
+
boundAt(path, fid) {
|
|
13317
|
+
return this.handlesAt(path).filter(
|
|
13318
|
+
(id2) => !this.keptHandles.has(id2) && this.bound.get(id2)?.fid === fid
|
|
13319
|
+
);
|
|
13320
|
+
}
|
|
13321
|
+
keepBound(ids, path, file) {
|
|
13322
|
+
for (const id2 of ids) this.keptHandles.set(id2, file);
|
|
13323
|
+
this.uses.set(file, ids.length);
|
|
13324
|
+
this.unlinked.set(path, file);
|
|
13325
|
+
}
|
|
13076
13326
|
keep(path, file) {
|
|
13077
13327
|
const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
|
|
13078
13328
|
for (const id2 of fresh3) this.keptHandles.set(id2, file);
|
|
13079
13329
|
this.uses.set(file, Math.max(fresh3.length, 1));
|
|
13080
13330
|
this.unlinked.set(path, file);
|
|
13081
13331
|
}
|
|
13332
|
+
unkeepAt(path, hidden) {
|
|
13333
|
+
for (const file of [...this.uses.keys()]) if (file.hidden === hidden) this.unkeep(path, file);
|
|
13334
|
+
}
|
|
13082
13335
|
unkeep(path, file) {
|
|
13083
13336
|
for (const [id2, kept] of this.keptHandles) if (kept === file) this.keptHandles.delete(id2);
|
|
13084
13337
|
this.uses.delete(file);
|
|
@@ -13647,12 +13900,12 @@ var MountFs = class _MountFs {
|
|
|
13647
13900
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
13648
13901
|
return this.follow(this.base.resolvePath(parentOf8(real), target), hops + 1, create);
|
|
13649
13902
|
}
|
|
13650
|
-
async open(mount, rel2, write, owner) {
|
|
13903
|
+
async open(mount, rel2, write, owner, create = write) {
|
|
13651
13904
|
return await mount.conn.call({
|
|
13652
13905
|
op: "open",
|
|
13653
13906
|
path: rel2,
|
|
13654
13907
|
write,
|
|
13655
|
-
create
|
|
13908
|
+
create,
|
|
13656
13909
|
truncate: write,
|
|
13657
13910
|
exclusive: false,
|
|
13658
13911
|
...owner
|
|
@@ -13749,10 +14002,10 @@ var MountFs = class _MountFs {
|
|
|
13749
14002
|
`${real} would be larger than this mount's maxfile (${mount.maxFile})`
|
|
13750
14003
|
);
|
|
13751
14004
|
}
|
|
13752
|
-
const made2 = await this.driverOwner(found, real, owner, false);
|
|
14005
|
+
const made2 = owner?.existing ? void 0 : await this.driverOwner(found, real, owner, false);
|
|
13753
14006
|
for (let attempt = 1; ; attempt++) {
|
|
13754
14007
|
try {
|
|
13755
|
-
return await this.writeWhole(mount, real, rel2, bytes, made2);
|
|
14008
|
+
return await this.writeWhole(mount, real, rel2, bytes, made2, owner?.existing !== true);
|
|
13756
14009
|
} catch (err) {
|
|
13757
14010
|
if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
|
|
13758
14011
|
}
|
|
@@ -13770,10 +14023,10 @@ var MountFs = class _MountFs {
|
|
|
13770
14023
|
this.table.forget(mount, real);
|
|
13771
14024
|
}
|
|
13772
14025
|
}
|
|
13773
|
-
async writeWhole(mount, real, rel2, bytes, owner) {
|
|
14026
|
+
async writeWhole(mount, real, rel2, bytes, owner, create = true) {
|
|
13774
14027
|
await this.table.commit(real);
|
|
13775
14028
|
this.table.forget(mount, real);
|
|
13776
|
-
const fh = await this.open(mount, rel2, true, owner);
|
|
14029
|
+
const fh = await this.open(mount, rel2, true, owner, create);
|
|
13777
14030
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
13778
14031
|
try {
|
|
13779
14032
|
for (let offset = 0; offset < bytes.length; offset += chunk2) {
|
|
@@ -14510,6 +14763,11 @@ function ownerFor2(entry, mode) {
|
|
|
14510
14763
|
if (!cred) return void 0;
|
|
14511
14764
|
return ownerOf(cred, mode === void 0 ? void 0 : mode & ~(entry.umask?.() ?? 18));
|
|
14512
14765
|
}
|
|
14766
|
+
function writeOwner(entry, req) {
|
|
14767
|
+
const owner = ownerFor2(entry, 438);
|
|
14768
|
+
if (req.handle === void 0 || req.mode !== 1) return owner;
|
|
14769
|
+
return { ...owner ?? {}, existing: true };
|
|
14770
|
+
}
|
|
14513
14771
|
function plainDirMode(entry, mode) {
|
|
14514
14772
|
if (entry.cred || mode === void 0) return void 0;
|
|
14515
14773
|
const wanted = mode & 4095 & ~(entry.umask?.() ?? 18);
|
|
@@ -14580,7 +14838,7 @@ async function run(entry, path, req) {
|
|
|
14580
14838
|
case "read":
|
|
14581
14839
|
return { ok: true, kind: "bytes", bytes: await readSource(entry, path) };
|
|
14582
14840
|
case "write":
|
|
14583
|
-
await fs.writeFile(path, req.body ?? new Uint8Array(0),
|
|
14841
|
+
await fs.writeFile(path, req.body ?? new Uint8Array(0), writeOwner(entry, req));
|
|
14584
14842
|
return done2;
|
|
14585
14843
|
case "exists":
|
|
14586
14844
|
return json2(await fs.exists(path));
|
|
@@ -31176,7 +31434,7 @@ var Launcher = class {
|
|
|
31176
31434
|
busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
|
|
31177
31435
|
owner: options.unlinkOwner,
|
|
31178
31436
|
marks: this.pathMarks,
|
|
31179
|
-
...this.
|
|
31437
|
+
...this.peerOptions(options.fs)
|
|
31180
31438
|
}
|
|
31181
31439
|
);
|
|
31182
31440
|
this.fs = this.processChain(fs, this.nodes, this.unlinked);
|
|
@@ -32010,7 +32268,7 @@ var Launcher = class {
|
|
|
32010
32268
|
const assign = this.mounts.locate(target.wasm) === void 0 ? this.rootFs.fileId : void 0;
|
|
32011
32269
|
return this.base.stat(target.wasm).then(
|
|
32012
32270
|
async (st) => {
|
|
32013
|
-
const fid = st.fid ?? await assign?.call(this.rootFs, target.wasm);
|
|
32271
|
+
const fid = st.fid ?? await assign?.call(this.rootFs, target.wasm)?.catch(() => void 0);
|
|
32014
32272
|
if (fid === void 0) return exeIdOf(fileIdentity(st.ino, st.mtime.getTime()));
|
|
32015
32273
|
return { exeId: `fid:${fid}:${st.mtime.getTime()}` };
|
|
32016
32274
|
},
|
|
@@ -33172,8 +33430,82 @@ var Launcher = class {
|
|
|
33172
33430
|
this.writers ??= this.writerStack();
|
|
33173
33431
|
return this.writers;
|
|
33174
33432
|
}
|
|
33433
|
+
peerGates = /* @__PURE__ */ new Map();
|
|
33434
|
+
peerOptions(fs) {
|
|
33435
|
+
const peers = fs.peerKeeps?.();
|
|
33436
|
+
queueMicrotask(() => fs.servePeerKeeps?.(this.unlinked.id, this.peerKept()));
|
|
33437
|
+
return {
|
|
33438
|
+
...peers ? { peers } : {},
|
|
33439
|
+
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
33440
|
+
};
|
|
33441
|
+
}
|
|
33442
|
+
peerKept() {
|
|
33443
|
+
const gates = this.peerGates;
|
|
33444
|
+
const gone = /* @__PURE__ */ new Set();
|
|
33445
|
+
const instance = this.unlinked.id;
|
|
33446
|
+
const ours = (hidden) => hidden.startsWith(`/${UNLINKED_DIR}/${instance}.p`) && /^[0-9a-f]+$/.test(hidden.slice(`/${UNLINKED_DIR}/${instance}.p`.length));
|
|
33447
|
+
const open2 = (tab, paths) => {
|
|
33448
|
+
for (const [key, release] of [...gates]) {
|
|
33449
|
+
const [owner, path] = key.split("\0");
|
|
33450
|
+
if (owner !== tab || paths && !paths.includes(path)) continue;
|
|
33451
|
+
gates.delete(key);
|
|
33452
|
+
release();
|
|
33453
|
+
}
|
|
33454
|
+
};
|
|
33455
|
+
return {
|
|
33456
|
+
keeping: async (tab, paths) => {
|
|
33457
|
+
for (const path of paths) {
|
|
33458
|
+
if (gone.has(tab)) return;
|
|
33459
|
+
const release = await this.unlinked.gate.acquire(path);
|
|
33460
|
+
const key = `${tab}\0${path}`;
|
|
33461
|
+
let done5 = false;
|
|
33462
|
+
const once2 = () => {
|
|
33463
|
+
if (done5) return;
|
|
33464
|
+
done5 = true;
|
|
33465
|
+
clearTimeout(timer);
|
|
33466
|
+
release();
|
|
33467
|
+
};
|
|
33468
|
+
const timer = setTimeout(() => {
|
|
33469
|
+
if (gates.get(key) === once2) gates.delete(key);
|
|
33470
|
+
once2();
|
|
33471
|
+
}, PEER_GATE_MS);
|
|
33472
|
+
if (gone.has(tab)) once2();
|
|
33473
|
+
else gates.set(key, once2);
|
|
33474
|
+
}
|
|
33475
|
+
},
|
|
33476
|
+
kept: async (tab, items, paths) => {
|
|
33477
|
+
for (const { path, hidden, fid, undo } of items) {
|
|
33478
|
+
if (undo !== void 0) this.unadopt(path, undo, fid);
|
|
33479
|
+
else this.adoptKept(path, hidden, fid, ours);
|
|
33480
|
+
}
|
|
33481
|
+
open2(tab, paths);
|
|
33482
|
+
},
|
|
33483
|
+
left: (tab) => {
|
|
33484
|
+
gone.add(tab);
|
|
33485
|
+
open2(tab);
|
|
33486
|
+
}
|
|
33487
|
+
};
|
|
33488
|
+
}
|
|
33489
|
+
unadopt(path, undo, fid) {
|
|
33490
|
+
for (const holds2 of this.held) holds2.unkeepAt(path, undo);
|
|
33491
|
+
void this.rootFs.announceHolds?.([[path, fid]], [], false);
|
|
33492
|
+
}
|
|
33493
|
+
adoptKept(path, hidden, fid, ours) {
|
|
33494
|
+
if (hidden === void 0 || !ours(hidden)) {
|
|
33495
|
+
this.held.doubt([path]);
|
|
33496
|
+
return;
|
|
33497
|
+
}
|
|
33498
|
+
const bound3 = [...this.held].map(
|
|
33499
|
+
(holds2) => [holds2, [...holds2.boundAt(path, fid), ...holds2.boundAt(hidden, fid)]]
|
|
33500
|
+
);
|
|
33501
|
+
const keeping = bound3.filter(([, ids]) => ids.length > 0);
|
|
33502
|
+
const file = this.unlinked.adopt(hidden, Math.max(keeping.length, 1));
|
|
33503
|
+
if (keeping.length === 0) file.release();
|
|
33504
|
+
for (const [holds2, ids] of keeping) holds2.keepBound(ids, path, file);
|
|
33505
|
+
void this.rootFs.announceHolds?.([], [path, hidden], false);
|
|
33506
|
+
}
|
|
33175
33507
|
rootFileId(path) {
|
|
33176
|
-
return this.mounts.locate(path) === void 0 ? this.rootFs.
|
|
33508
|
+
return this.mounts.locate(path) === void 0 ? this.rootFs.bindId?.(path) : void 0;
|
|
33177
33509
|
}
|
|
33178
33510
|
flushingPeers(fs) {
|
|
33179
33511
|
const peers = fs.peers?.();
|
|
@@ -33219,6 +33551,7 @@ var Launcher = class {
|
|
|
33219
33551
|
busy: (path) => [...this.openFiles].some((open2) => open2.holds(path)),
|
|
33220
33552
|
owner: this.unlinkOwnerOption,
|
|
33221
33553
|
marks: this.pathMarks,
|
|
33554
|
+
peers: this.rootFs.peerKeeps?.(),
|
|
33222
33555
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
33223
33556
|
}
|
|
33224
33557
|
);
|