@ai-ecoverse/slicc-kernel 1.160.3 → 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 +487 -41
- package/dist/kernel-worker.js.map +3 -3
- package/dist/meta.js.map +2 -2
- package/dist/node-process-worker.js +5 -1
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +487 -41
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +5 -1
- package/dist/process-worker.js.map +2 -2
- package/dist/types/fs/opfs.d.ts +14 -2
- package/dist/types/fs/remote.d.ts +75 -0
- package/dist/types/fs/types.d.ts +44 -0
- package/dist/types/fs/unlinked.d.ts +21 -1
- package/dist/types/kernel/host.d.ts +2 -1
- package/dist/types/kernel/proc-info.d.ts +6 -0
- package/dist/types/launcher.d.ts +5 -0
- package/dist/types/mount/mount-fs.d.ts +8 -3
- package/dist/types/realm/sync-fs-token-registry.d.ts +2 -1
- package/package.json +2 -2
package/dist/kernel-worker.js
CHANGED
|
@@ -1000,6 +1000,14 @@ var RemoteWatch = class {
|
|
|
1000
1000
|
mourned = /* @__PURE__ */ new Set();
|
|
1001
1001
|
others = /* @__PURE__ */ new Map();
|
|
1002
1002
|
union = /* @__PURE__ */ new Set();
|
|
1003
|
+
myHolds = /* @__PURE__ */ new Map();
|
|
1004
|
+
peerHolds = /* @__PURE__ */ new Map();
|
|
1005
|
+
holdsSent = 0;
|
|
1006
|
+
silent = /* @__PURE__ */ new Set();
|
|
1007
|
+
holders = /* @__PURE__ */ new Set();
|
|
1008
|
+
instances = /* @__PURE__ */ new Map();
|
|
1009
|
+
instance;
|
|
1010
|
+
keeps;
|
|
1003
1011
|
listeners = /* @__PURE__ */ new Set();
|
|
1004
1012
|
waiting = /* @__PURE__ */ new Map();
|
|
1005
1013
|
locks;
|
|
@@ -1043,7 +1051,7 @@ var RemoteWatch = class {
|
|
|
1043
1051
|
this.settled = true;
|
|
1044
1052
|
this.recount();
|
|
1045
1053
|
});
|
|
1046
|
-
this.channel.postMessage({ hello: this.tab, renames: true });
|
|
1054
|
+
this.channel.postMessage({ hello: this.tab, renames: true, keeps: true });
|
|
1047
1055
|
}
|
|
1048
1056
|
async join() {
|
|
1049
1057
|
if (!this.locks) return;
|
|
@@ -1103,8 +1111,12 @@ var RemoteWatch = class {
|
|
|
1103
1111
|
return `${this.name}:${tab}`;
|
|
1104
1112
|
}
|
|
1105
1113
|
receive(data) {
|
|
1114
|
+
const spoke = data.hello ?? data.here ?? data.tab ?? data.from;
|
|
1115
|
+
if (spoke !== void 0) this.silent.delete(spoke);
|
|
1106
1116
|
const sender = data.hello ?? data.here ?? (data.paths !== void 0 ? data.tab : void 0);
|
|
1107
1117
|
if (sender !== void 0 && data.renames) this.ackers.add(sender);
|
|
1118
|
+
const keeper = data.hello ?? data.here;
|
|
1119
|
+
if (keeper !== void 0 && data.keeps) this.holders.add(keeper);
|
|
1108
1120
|
if (data.hello !== void 0) this.greeted(data.hello);
|
|
1109
1121
|
if (data.renaming !== void 0 && data.tab !== void 0) this.heard(data);
|
|
1110
1122
|
if (data.renamed !== void 0) this.renamed?.(data.renamed);
|
|
@@ -1115,7 +1127,9 @@ var RemoteWatch = class {
|
|
|
1115
1127
|
this.met?.();
|
|
1116
1128
|
}
|
|
1117
1129
|
if (data.tab !== void 0 && data.paths !== void 0) this.announced(data);
|
|
1130
|
+
this.holdsHeard(data);
|
|
1118
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);
|
|
1119
1133
|
this.acked(data.ack, data.from);
|
|
1120
1134
|
}
|
|
1121
1135
|
if (data.changed !== void 0) this.emit({ paths: data.changed, trees: [], content: true });
|
|
@@ -1141,8 +1155,11 @@ var RemoteWatch = class {
|
|
|
1141
1155
|
void this.locks.request(name2, async () => {
|
|
1142
1156
|
this.mourned.delete(peer);
|
|
1143
1157
|
this.others.delete(peer);
|
|
1158
|
+
this.peerHolds.delete(peer);
|
|
1159
|
+
this.keeps?.left(peer);
|
|
1144
1160
|
this.peers.delete(peer);
|
|
1145
1161
|
this.ackers.delete(peer);
|
|
1162
|
+
this.holders.delete(peer);
|
|
1146
1163
|
this.flushedAt(peer);
|
|
1147
1164
|
this.rebuild();
|
|
1148
1165
|
this.recount();
|
|
@@ -1155,8 +1172,9 @@ var RemoteWatch = class {
|
|
|
1155
1172
|
greeted(peer) {
|
|
1156
1173
|
this.arrived(peer);
|
|
1157
1174
|
const answer2 = () => {
|
|
1158
|
-
this.channel.postMessage({ here: this.tab, to: peer, renames: true });
|
|
1175
|
+
this.channel.postMessage({ here: this.tab, to: peer, renames: true, keeps: true });
|
|
1159
1176
|
if (this.mine.length > 0) void this.announce(this.mine);
|
|
1177
|
+
if (this.myHolds.size > 0) this.sendHolds([...this.myHolds], []);
|
|
1160
1178
|
};
|
|
1161
1179
|
if (!this.quiet) {
|
|
1162
1180
|
answer2();
|
|
@@ -1179,6 +1197,127 @@ var RemoteWatch = class {
|
|
|
1179
1197
|
this.rebuild();
|
|
1180
1198
|
this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
|
|
1181
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
|
+
}
|
|
1228
|
+
heldBy({ tab, holds: holds2, seq: seq2 }) {
|
|
1229
|
+
const from = tab;
|
|
1230
|
+
const { add, drop } = holds2;
|
|
1231
|
+
if (holds2?.instance !== void 0) this.instances.set(from, holds2.instance);
|
|
1232
|
+
const held = this.peerHolds.get(from) ?? /* @__PURE__ */ new Map();
|
|
1233
|
+
for (const path of drop) held.delete(path);
|
|
1234
|
+
for (const [path, fid] of add) held.set(path, fid);
|
|
1235
|
+
if (held.size > 0) this.peerHolds.set(from, held);
|
|
1236
|
+
else this.peerHolds.delete(from);
|
|
1237
|
+
this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
|
|
1238
|
+
}
|
|
1239
|
+
hold(add, drop, wait2) {
|
|
1240
|
+
const fresh3 = add.filter(
|
|
1241
|
+
([path, fid]) => !this.myHolds.has(path) || this.myHolds.get(path) !== fid
|
|
1242
|
+
);
|
|
1243
|
+
const gone = drop.filter((path) => this.myHolds.has(path));
|
|
1244
|
+
for (const path of gone) this.myHolds.delete(path);
|
|
1245
|
+
for (const [path, fid] of fresh3) this.myHolds.set(path, fid);
|
|
1246
|
+
if (fresh3.length === 0 && gone.length === 0 || this.peers.size === 0)
|
|
1247
|
+
return Promise.resolve();
|
|
1248
|
+
const seq2 = this.sendHolds(fresh3, gone);
|
|
1249
|
+
if (!wait2) return Promise.resolve();
|
|
1250
|
+
const heard = new Set(
|
|
1251
|
+
[...this.peers].filter((peer) => this.holders.has(peer) && !this.silent.has(peer))
|
|
1252
|
+
);
|
|
1253
|
+
return this.acks(seq2, heard, (left) => {
|
|
1254
|
+
for (const peer of left) this.silent.add(peer);
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
rehold(from, to) {
|
|
1258
|
+
const add = [];
|
|
1259
|
+
const drop = [];
|
|
1260
|
+
for (const [path, fid] of [...this.myHolds]) {
|
|
1261
|
+
if (path !== from && !path.startsWith(`${from}/`)) continue;
|
|
1262
|
+
const moved2 = to + path.slice(from.length);
|
|
1263
|
+
this.myHolds.delete(path);
|
|
1264
|
+
this.myHolds.set(moved2, fid);
|
|
1265
|
+
drop.push(path);
|
|
1266
|
+
add.push([moved2, fid]);
|
|
1267
|
+
}
|
|
1268
|
+
if (drop.length > 0 && this.peers.size > 0) this.sendHolds(add, drop);
|
|
1269
|
+
}
|
|
1270
|
+
sendHolds(add, drop) {
|
|
1271
|
+
const seq2 = ++this.seq;
|
|
1272
|
+
this.holdsSent++;
|
|
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
|
+
});
|
|
1279
|
+
return seq2;
|
|
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
|
+
}
|
|
1305
|
+
peerHeld(path) {
|
|
1306
|
+
const out = [];
|
|
1307
|
+
for (const [tab, held] of this.peerHolds)
|
|
1308
|
+
if (held.has(path)) out.push({ tab, fid: held.get(path) });
|
|
1309
|
+
return out;
|
|
1310
|
+
}
|
|
1311
|
+
peerHeldPaths() {
|
|
1312
|
+
const out = /* @__PURE__ */ new Set();
|
|
1313
|
+
for (const held of this.peerHolds.values()) for (const path of held.keys()) out.add(path);
|
|
1314
|
+
return [...out].sort();
|
|
1315
|
+
}
|
|
1316
|
+
holdCounts() {
|
|
1317
|
+
let peers = 0;
|
|
1318
|
+
for (const held of this.peerHolds.values()) peers += held.size;
|
|
1319
|
+
return { mine: this.myHolds.size, peers, sent: this.holdsSent };
|
|
1320
|
+
}
|
|
1182
1321
|
acked(seq2, from) {
|
|
1183
1322
|
const wait2 = this.waiting.get(seq2);
|
|
1184
1323
|
if (!wait2) return;
|
|
@@ -1193,11 +1332,14 @@ var RemoteWatch = class {
|
|
|
1193
1332
|
if (!this.locks) return;
|
|
1194
1333
|
const held = new Set(((await this.locks.query()).held ?? []).map((lock) => lock.name));
|
|
1195
1334
|
let left = false;
|
|
1196
|
-
for (const tab of /* @__PURE__ */ new Set([...this.others.keys(), ...this.peers])) {
|
|
1335
|
+
for (const tab of /* @__PURE__ */ new Set([...this.others.keys(), ...this.peers, ...this.peerHolds.keys()])) {
|
|
1197
1336
|
if (!held.has(this.lockOf(tab))) {
|
|
1198
1337
|
if (this.peers.delete(tab)) left = true;
|
|
1199
1338
|
this.others.delete(tab);
|
|
1339
|
+
this.peerHolds.delete(tab);
|
|
1340
|
+
this.keeps?.left(tab);
|
|
1200
1341
|
this.ackers.delete(tab);
|
|
1342
|
+
this.holders.delete(tab);
|
|
1201
1343
|
}
|
|
1202
1344
|
}
|
|
1203
1345
|
this.rebuild();
|
|
@@ -1245,8 +1387,9 @@ var RemoteWatch = class {
|
|
|
1245
1387
|
renamedTree(lock) {
|
|
1246
1388
|
this.channel.postMessage({ renamed: lock });
|
|
1247
1389
|
}
|
|
1248
|
-
acks(seq2, from) {
|
|
1390
|
+
acks(seq2, from, missed) {
|
|
1249
1391
|
if (from.size === 0) return Promise.resolve();
|
|
1392
|
+
const left = new Set(from);
|
|
1250
1393
|
return new Promise((resolve2) => {
|
|
1251
1394
|
const done5 = () => {
|
|
1252
1395
|
clearTimeout(timer);
|
|
@@ -1254,10 +1397,11 @@ var RemoteWatch = class {
|
|
|
1254
1397
|
resolve2();
|
|
1255
1398
|
};
|
|
1256
1399
|
const timer = setTimeout(() => {
|
|
1400
|
+
missed?.(left);
|
|
1257
1401
|
done5();
|
|
1258
1402
|
void this.prune();
|
|
1259
1403
|
}, ANNOUNCE_WAIT_MS);
|
|
1260
|
-
this.waiting.set(seq2, { left
|
|
1404
|
+
this.waiting.set(seq2, { left, done: done5 });
|
|
1261
1405
|
});
|
|
1262
1406
|
}
|
|
1263
1407
|
emit(change) {
|
|
@@ -1360,6 +1504,7 @@ function translate(err, path, op = "lookup", mapped = true) {
|
|
|
1360
1504
|
return error;
|
|
1361
1505
|
}
|
|
1362
1506
|
var META_OPS = ["get", "update", "move", "remove", "links", "under", "all"];
|
|
1507
|
+
var FILE_ID_TRIES = 3;
|
|
1363
1508
|
function sidecar(meta) {
|
|
1364
1509
|
const pathOf = ([first]) => [first, "/"].flat().find((p) => typeof p === "string");
|
|
1365
1510
|
const guarded2 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
|
|
@@ -1603,18 +1748,48 @@ var OpfsFs = class {
|
|
|
1603
1748
|
this.channel?.postMessage(paths);
|
|
1604
1749
|
}
|
|
1605
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) {
|
|
1606
1761
|
const found = await this.locate(path, true);
|
|
1607
1762
|
const [entry] = await (this.meta.fresh ?? this.meta.get).call(this.meta, [found.path]);
|
|
1608
1763
|
if (entry?.fid !== void 0) return entry.fid;
|
|
1609
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;
|
|
1610
1775
|
this.idWrites++;
|
|
1611
|
-
await this.
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
const
|
|
1617
|
-
|
|
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;
|
|
1618
1793
|
}
|
|
1619
1794
|
heldUnder = () => [];
|
|
1620
1795
|
heldBy(held) {
|
|
@@ -1633,6 +1808,30 @@ var OpfsFs = class {
|
|
|
1633
1808
|
return this.remote?.listen(listener) ?? (() => {
|
|
1634
1809
|
});
|
|
1635
1810
|
}
|
|
1811
|
+
announceHolds(add, drop, wait2) {
|
|
1812
|
+
return this.remote?.hold(add, drop, wait2) ?? Promise.resolve();
|
|
1813
|
+
}
|
|
1814
|
+
holdCounts() {
|
|
1815
|
+
const remote = this.remote;
|
|
1816
|
+
return remote && { ...remote.holdCounts(), held: remote.peerHeldPaths() };
|
|
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
|
+
}
|
|
1832
|
+
rehold(from, to) {
|
|
1833
|
+
this.remote?.rehold(from, to);
|
|
1834
|
+
}
|
|
1636
1835
|
announceWatch(paths) {
|
|
1637
1836
|
return this.remote?.announce(paths) ?? Promise.resolve();
|
|
1638
1837
|
}
|
|
@@ -2577,7 +2776,7 @@ var OpfsFs = class {
|
|
|
2577
2776
|
}
|
|
2578
2777
|
}
|
|
2579
2778
|
async writeFile(path, content, owner) {
|
|
2580
|
-
const [file, existed, target] = await this.fileFor(path);
|
|
2779
|
+
const [file, existed, target] = await this.fileFor(path, owner?.existing !== true);
|
|
2581
2780
|
if (!existed) await this.stampNew(target, owner, false);
|
|
2582
2781
|
if (!existed && content.length === 0) return;
|
|
2583
2782
|
let writable2;
|
|
@@ -8236,7 +8435,7 @@ function satisfies(version, range2) {
|
|
|
8236
8435
|
}
|
|
8237
8436
|
|
|
8238
8437
|
// src/version.ts
|
|
8239
|
-
var KERNEL_VERSION = true ? "1.
|
|
8438
|
+
var KERNEL_VERSION = true ? "1.162.0" : "0.0.0-dev";
|
|
8240
8439
|
var OS_RELEASE = "6.0.0-slicc";
|
|
8241
8440
|
function procVersion() {
|
|
8242
8441
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -9186,6 +9385,9 @@ var LOOKUP_OPS = [
|
|
|
9186
9385
|
var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
9187
9386
|
var TOUCHED_MAX = 4096;
|
|
9188
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;
|
|
9189
9391
|
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
9190
9392
|
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
9191
9393
|
var PathGate = class {
|
|
@@ -9228,7 +9430,9 @@ var UnlinkedKeeper = class {
|
|
|
9228
9430
|
links = /* @__PURE__ */ new Map();
|
|
9229
9431
|
linked = /* @__PURE__ */ new Map();
|
|
9230
9432
|
dirOwner;
|
|
9433
|
+
peers;
|
|
9231
9434
|
constructor(fs, holders, rootOf, options = {}) {
|
|
9435
|
+
this.peers = options.peers;
|
|
9232
9436
|
this.marks = options.marks ?? new PathMarks();
|
|
9233
9437
|
const { locks, owner } = options;
|
|
9234
9438
|
this.dirOwner = options.dirOwner;
|
|
@@ -9304,22 +9508,29 @@ var UnlinkedKeeper = class {
|
|
|
9304
9508
|
lstat: (path) => unreplaced(path, async () => counted2(await fs.lstat(path))),
|
|
9305
9509
|
async rm(path, options) {
|
|
9306
9510
|
const target = fs.resolvePath("/", path);
|
|
9307
|
-
await
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
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
|
+
);
|
|
9312
9520
|
},
|
|
9313
9521
|
async rename(from, to) {
|
|
9314
9522
|
const target = fs.resolvePath("/", to);
|
|
9315
9523
|
const source = fs.resolvePath("/", from);
|
|
9316
9524
|
if (target === source) return fs.rename(from, to);
|
|
9317
|
-
await keeper.
|
|
9318
|
-
|
|
9319
|
-
() =>
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
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
|
+
)
|
|
9323
9534
|
);
|
|
9324
9535
|
},
|
|
9325
9536
|
symlink: (target, path, owner) => keeper.marks.around([fs.resolvePath("/", path)], () => fs.symlink(target, path, owner)),
|
|
@@ -9464,6 +9675,114 @@ var UnlinkedKeeper = class {
|
|
|
9464
9675
|
(err) => err?.code !== "ENOENT" && err?.code !== "ENOTDIR"
|
|
9465
9676
|
);
|
|
9466
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
|
+
}
|
|
9467
9786
|
keptFile(hidden, entry) {
|
|
9468
9787
|
return {
|
|
9469
9788
|
hidden,
|
|
@@ -9518,7 +9837,7 @@ var UnlinkedKeeper = class {
|
|
|
9518
9837
|
}
|
|
9519
9838
|
async tidy(dir) {
|
|
9520
9839
|
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
9521
|
-
if (left.length === 0) await this.fs.rm(dir
|
|
9840
|
+
if (left.length === 0) await this.fs.rm(dir).catch(() => void 0);
|
|
9522
9841
|
}
|
|
9523
9842
|
sweep(root) {
|
|
9524
9843
|
const query = this.locks?.query?.bind(this.locks);
|
|
@@ -10763,6 +11082,11 @@ async function shownTarget(fs, target) {
|
|
|
10763
11082
|
const st = await quiet(fs.stat(target));
|
|
10764
11083
|
return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
|
|
10765
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
|
+
}
|
|
10766
11090
|
function withCommandDirs(fs, names2, userDir = async () => void 0, owners = false, targetOf2 = async () => void 0) {
|
|
10767
11091
|
async function nearest(path) {
|
|
10768
11092
|
const st = await fs.stat(path).catch(() => void 0);
|
|
@@ -10860,7 +11184,7 @@ function withCommandDirs(fs, names2, userDir = async () => void 0, owners = fals
|
|
|
10860
11184
|
}),
|
|
10861
11185
|
readFileBuffer: (path) => fs.readFileBuffer(path).catch((err) => content(path, err)),
|
|
10862
11186
|
commandTarget: async (path) => (await entryTarget(path))?.path,
|
|
10863
|
-
writeFile: (path, content2, owner) => fs.writeFile(path, content2, owner),
|
|
11187
|
+
writeFile: async (path, content2, owner) => fs.writeFile(path, content2, await madeFor(owner, () => entryTarget(path))),
|
|
10864
11188
|
link: (from, to, follow2) => fs.link(from, to, follow2),
|
|
10865
11189
|
links: (path) => mayLink(fs, path),
|
|
10866
11190
|
async exists(path) {
|
|
@@ -11025,6 +11349,10 @@ function withEtcFiles(fs, files) {
|
|
|
11025
11349
|
}
|
|
11026
11350
|
} : {},
|
|
11027
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
|
+
},
|
|
11028
11356
|
...fs.append ? {
|
|
11029
11357
|
append: async (path, bytes, options) => {
|
|
11030
11358
|
if (files.has(path)) await materialize(path);
|
|
@@ -12880,12 +13208,15 @@ function linkedTo(mount, path) {
|
|
|
12880
13208
|
var within6 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
12881
13209
|
var HeldSet = class extends Set {
|
|
12882
13210
|
idle;
|
|
12883
|
-
|
|
13211
|
+
moved;
|
|
13212
|
+
constructor(idle2, moved2) {
|
|
12884
13213
|
super();
|
|
12885
13214
|
this.idle = idle2;
|
|
13215
|
+
this.moved = moved2;
|
|
12886
13216
|
}
|
|
12887
13217
|
add(holds2) {
|
|
12888
13218
|
holds2.onIdle = this.idle;
|
|
13219
|
+
holds2.onMoved = this.moved;
|
|
12889
13220
|
return super.add(holds2);
|
|
12890
13221
|
}
|
|
12891
13222
|
doubt(paths, over) {
|
|
@@ -12922,6 +13253,7 @@ var HeldPaths = class extends Map {
|
|
|
12922
13253
|
doubted = /* @__PURE__ */ new Map();
|
|
12923
13254
|
doubts = 0;
|
|
12924
13255
|
onIdle;
|
|
13256
|
+
onMoved;
|
|
12925
13257
|
keyOf(path) {
|
|
12926
13258
|
return this.aliases.get(path) ?? path;
|
|
12927
13259
|
}
|
|
@@ -12979,12 +13311,25 @@ var HeldPaths = class extends Map {
|
|
|
12979
13311
|
for (const file of this.unlinked.values()) if (file.hidden === hidden) return true;
|
|
12980
13312
|
return false;
|
|
12981
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
|
+
}
|
|
12982
13324
|
keep(path, file) {
|
|
12983
13325
|
const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
|
|
12984
13326
|
for (const id2 of fresh3) this.keptHandles.set(id2, file);
|
|
12985
13327
|
this.uses.set(file, Math.max(fresh3.length, 1));
|
|
12986
13328
|
this.unlinked.set(path, file);
|
|
12987
13329
|
}
|
|
13330
|
+
unkeepAt(path, hidden) {
|
|
13331
|
+
for (const file of [...this.uses.keys()]) if (file.hidden === hidden) this.unkeep(path, file);
|
|
13332
|
+
}
|
|
12988
13333
|
unkeep(path, file) {
|
|
12989
13334
|
for (const [id2, kept] of this.keptHandles) if (kept === file) this.keptHandles.delete(id2);
|
|
12990
13335
|
this.uses.delete(file);
|
|
@@ -13082,6 +13427,7 @@ var HeldPaths = class extends Map {
|
|
|
13082
13427
|
for (const [id2, key] of this.handles) {
|
|
13083
13428
|
if (within6(key, from)) this.handles.set(id2, to + key.slice(from.length));
|
|
13084
13429
|
}
|
|
13430
|
+
this.onMoved?.(from, to);
|
|
13085
13431
|
}
|
|
13086
13432
|
isRevoked(path) {
|
|
13087
13433
|
return this.revoked.has(path);
|
|
@@ -13552,12 +13898,12 @@ var MountFs = class _MountFs {
|
|
|
13552
13898
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
13553
13899
|
return this.follow(this.base.resolvePath(parentOf8(real), target), hops + 1, create);
|
|
13554
13900
|
}
|
|
13555
|
-
async open(mount, rel2, write, owner) {
|
|
13901
|
+
async open(mount, rel2, write, owner, create = write) {
|
|
13556
13902
|
return await mount.conn.call({
|
|
13557
13903
|
op: "open",
|
|
13558
13904
|
path: rel2,
|
|
13559
13905
|
write,
|
|
13560
|
-
create
|
|
13906
|
+
create,
|
|
13561
13907
|
truncate: write,
|
|
13562
13908
|
exclusive: false,
|
|
13563
13909
|
...owner
|
|
@@ -13654,10 +14000,10 @@ var MountFs = class _MountFs {
|
|
|
13654
14000
|
`${real} would be larger than this mount's maxfile (${mount.maxFile})`
|
|
13655
14001
|
);
|
|
13656
14002
|
}
|
|
13657
|
-
const made2 = await this.driverOwner(found, real, owner, false);
|
|
14003
|
+
const made2 = owner?.existing ? void 0 : await this.driverOwner(found, real, owner, false);
|
|
13658
14004
|
for (let attempt = 1; ; attempt++) {
|
|
13659
14005
|
try {
|
|
13660
|
-
return await this.writeWhole(mount, real, rel2, bytes, made2);
|
|
14006
|
+
return await this.writeWhole(mount, real, rel2, bytes, made2, owner?.existing !== true);
|
|
13661
14007
|
} catch (err) {
|
|
13662
14008
|
if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
|
|
13663
14009
|
}
|
|
@@ -13675,10 +14021,10 @@ var MountFs = class _MountFs {
|
|
|
13675
14021
|
this.table.forget(mount, real);
|
|
13676
14022
|
}
|
|
13677
14023
|
}
|
|
13678
|
-
async writeWhole(mount, real, rel2, bytes, owner) {
|
|
14024
|
+
async writeWhole(mount, real, rel2, bytes, owner, create = true) {
|
|
13679
14025
|
await this.table.commit(real);
|
|
13680
14026
|
this.table.forget(mount, real);
|
|
13681
|
-
const fh = await this.open(mount, rel2, true, owner);
|
|
14027
|
+
const fh = await this.open(mount, rel2, true, owner, create);
|
|
13682
14028
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
13683
14029
|
try {
|
|
13684
14030
|
for (let offset = 0; offset < bytes.length; offset += chunk2) {
|
|
@@ -14415,6 +14761,11 @@ function ownerFor2(entry, mode) {
|
|
|
14415
14761
|
if (!cred) return void 0;
|
|
14416
14762
|
return ownerOf(cred, mode === void 0 ? void 0 : mode & ~(entry.umask?.() ?? 18));
|
|
14417
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
|
+
}
|
|
14418
14769
|
function plainDirMode(entry, mode) {
|
|
14419
14770
|
if (entry.cred || mode === void 0) return void 0;
|
|
14420
14771
|
const wanted = mode & 4095 & ~(entry.umask?.() ?? 18);
|
|
@@ -14485,7 +14836,7 @@ async function run(entry, path, req) {
|
|
|
14485
14836
|
case "read":
|
|
14486
14837
|
return { ok: true, kind: "bytes", bytes: await readSource(entry, path) };
|
|
14487
14838
|
case "write":
|
|
14488
|
-
await fs.writeFile(path, req.body ?? new Uint8Array(0),
|
|
14839
|
+
await fs.writeFile(path, req.body ?? new Uint8Array(0), writeOwner(entry, req));
|
|
14489
14840
|
return done2;
|
|
14490
14841
|
case "exists":
|
|
14491
14842
|
return json2(await fs.exists(path));
|
|
@@ -14709,12 +15060,18 @@ async function holdOp(entry, path, req) {
|
|
|
14709
15060
|
if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
|
|
14710
15061
|
const fid = req.ino === void 0 ? void 0 : await idOf(entry, real ?? path);
|
|
14711
15062
|
if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
|
|
14712
|
-
return json2(entry
|
|
15063
|
+
return json2(await heldOpen(entry, path, real, req, fid));
|
|
14713
15064
|
}
|
|
14714
15065
|
}
|
|
14715
15066
|
entry.hold?.(path, on, open2, void 0, req.handle);
|
|
14716
15067
|
return done2;
|
|
14717
15068
|
}
|
|
15069
|
+
async function heldOpen(entry, path, real, req, fid) {
|
|
15070
|
+
const handle = entry.hold?.(path, true, true, real, void 0, req.ino, fid);
|
|
15071
|
+
if (handle === void 0) return null;
|
|
15072
|
+
await entry.announceHold?.(real ?? path, fid, ((req.mode ?? 0) & HOLD_CREATED) !== 0);
|
|
15073
|
+
return handle;
|
|
15074
|
+
}
|
|
14718
15075
|
async function idOf(entry, path) {
|
|
14719
15076
|
return await entry.fileId?.(path)?.catch(() => void 0) ?? void 0;
|
|
14720
15077
|
}
|
|
@@ -15732,6 +16089,10 @@ var SYS_TREE = {
|
|
|
15732
16089
|
`,
|
|
15733
16090
|
slicc_file_ids: (listing) => `${listing.fileIds ?? 0}
|
|
15734
16091
|
`,
|
|
16092
|
+
slicc_peer_holds: ({ holds: holds2 }) => `${holds2?.mine ?? 0} ${holds2?.peers ?? 0} ${holds2?.sent ?? 0}
|
|
16093
|
+
`,
|
|
16094
|
+
slicc_peer_held: ({ holds: holds2 }) => (holds2?.held ?? []).map((path) => `${path}
|
|
16095
|
+
`).join(""),
|
|
15735
16096
|
slicc_rename_acks: (listing) => {
|
|
15736
16097
|
const samples = [...listing.renameAcks?.ackMs ?? []].sort((a, b) => a - b);
|
|
15737
16098
|
const median = samples[Math.floor(samples.length / 2)] ?? 0;
|
|
@@ -26974,6 +27335,7 @@ function holdingToken(opts, holds2, caller) {
|
|
|
26974
27335
|
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
26975
27336
|
...opts.renames ? { renames: opts.renames } : {},
|
|
26976
27337
|
...opts.fileId ? { fileId: opts.fileId } : {},
|
|
27338
|
+
...opts.announceHold ? { announceHold: opts.announceHold } : {},
|
|
26977
27339
|
...opts.renameSeq ? { renameSeq: opts.renameSeq } : {},
|
|
26978
27340
|
hold: (path, on, open2, real, handle, ino, fid) => holds2.hold(path, on, open2, real, handle, ino, fid),
|
|
26979
27341
|
boundIno: holds2.boundIno.bind(holds2),
|
|
@@ -30927,7 +31289,12 @@ var Launcher = class {
|
|
|
30927
31289
|
identities = { asked: 0, timedOut: 0 };
|
|
30928
31290
|
nodes;
|
|
30929
31291
|
unlinked;
|
|
30930
|
-
held = new HeldSet(
|
|
31292
|
+
held = new HeldSet(
|
|
31293
|
+
(path) => this.released(path),
|
|
31294
|
+
(from, to) => {
|
|
31295
|
+
if (this.mounts.locate(to) === void 0) this.rootFs.rehold?.(from, to);
|
|
31296
|
+
}
|
|
31297
|
+
);
|
|
30931
31298
|
processMounts;
|
|
30932
31299
|
booting = new AbortController();
|
|
30933
31300
|
fstab = Promise.resolve([]);
|
|
@@ -31065,7 +31432,7 @@ var Launcher = class {
|
|
|
31065
31432
|
busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
|
|
31066
31433
|
owner: options.unlinkOwner,
|
|
31067
31434
|
marks: this.pathMarks,
|
|
31068
|
-
...this.
|
|
31435
|
+
...this.peerOptions(options.fs)
|
|
31069
31436
|
}
|
|
31070
31437
|
);
|
|
31071
31438
|
this.fs = this.processChain(fs, this.nodes, this.unlinked);
|
|
@@ -31899,7 +32266,7 @@ var Launcher = class {
|
|
|
31899
32266
|
const assign = this.mounts.locate(target.wasm) === void 0 ? this.rootFs.fileId : void 0;
|
|
31900
32267
|
return this.base.stat(target.wasm).then(
|
|
31901
32268
|
async (st) => {
|
|
31902
|
-
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);
|
|
31903
32270
|
if (fid === void 0) return exeIdOf(fileIdentity(st.ino, st.mtime.getTime()));
|
|
31904
32271
|
return { exeId: `fid:${fid}:${st.mtime.getTime()}` };
|
|
31905
32272
|
},
|
|
@@ -32145,7 +32512,8 @@ var Launcher = class {
|
|
|
32145
32512
|
cwdChecks: this.cwds.checks,
|
|
32146
32513
|
renameAcks: this.rootFs.renameWaits?.(),
|
|
32147
32514
|
staleFds: staleFdCount(),
|
|
32148
|
-
fileIds: this.rootFs.fileIdWrites?.() ?? 0
|
|
32515
|
+
fileIds: this.rootFs.fileIdWrites?.() ?? 0,
|
|
32516
|
+
holds: this.rootFs.holdCounts?.()
|
|
32149
32517
|
}),
|
|
32150
32518
|
sudo: (call, signal) => this.sudo(pid, call, signal),
|
|
32151
32519
|
crontab: (call) => this.crontab(pid, call),
|
|
@@ -32157,6 +32525,7 @@ var Launcher = class {
|
|
|
32157
32525
|
statfs: (path) => this.mounts.statfs(path),
|
|
32158
32526
|
renames: (since, held) => this.renameLog.since(since, held),
|
|
32159
32527
|
fileId: (path) => this.locks.shared?.() === true ? this.rootFileId(path) : void 0,
|
|
32528
|
+
announceHold: (path, fid, created4) => this.locks.shared?.() === true && this.mounts.locate(path) === void 0 ? this.rootFs.announceHolds?.([[path, fid]], [], !created4) : void 0,
|
|
32160
32529
|
renameSeq: (from, to) => this.renameLog.seqOf(from, to),
|
|
32161
32530
|
mounts: () => this.mounts.list(),
|
|
32162
32531
|
mount: (call, signal) => this.processMount(pid, call, signal),
|
|
@@ -32598,6 +32967,8 @@ var Launcher = class {
|
|
|
32598
32967
|
const at = normalizePath(path);
|
|
32599
32968
|
this.unlinked.closed(at);
|
|
32600
32969
|
this.writers?.unlinked.closed(at);
|
|
32970
|
+
if (![...this.held].some((holds2) => holds2.holding(at)))
|
|
32971
|
+
void this.rootFs.announceHolds?.([], [at], false);
|
|
32601
32972
|
const open2 = [...this.openFiles].some((nodes) => nodes.writes(at)) || heldUnder(this.held, at);
|
|
32602
32973
|
if (!open2) void this.mounts.commit(at).catch(() => void 0);
|
|
32603
32974
|
}
|
|
@@ -33057,8 +33428,82 @@ var Launcher = class {
|
|
|
33057
33428
|
this.writers ??= this.writerStack();
|
|
33058
33429
|
return this.writers;
|
|
33059
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
|
+
}
|
|
33060
33505
|
rootFileId(path) {
|
|
33061
|
-
return this.mounts.locate(path) === void 0 ? this.rootFs.
|
|
33506
|
+
return this.mounts.locate(path) === void 0 ? this.rootFs.bindId?.(path) : void 0;
|
|
33062
33507
|
}
|
|
33063
33508
|
flushingPeers(fs) {
|
|
33064
33509
|
const peers = fs.peers?.();
|
|
@@ -33104,6 +33549,7 @@ var Launcher = class {
|
|
|
33104
33549
|
busy: (path) => [...this.openFiles].some((open2) => open2.holds(path)),
|
|
33105
33550
|
owner: this.unlinkOwnerOption,
|
|
33106
33551
|
marks: this.pathMarks,
|
|
33552
|
+
peers: this.rootFs.peerKeeps?.(),
|
|
33107
33553
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
33108
33554
|
}
|
|
33109
33555
|
);
|