@ai-ecoverse/slicc-kernel 1.146.2 → 1.146.4
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 +4 -0
- package/dist/kernel-worker.js +150 -52
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +96 -19
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +150 -52
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +96 -19
- package/dist/process-worker.js.map +3 -3
- package/dist/types/fs/ranged.d.ts +1 -0
- package/dist/types/fs/unlinked.d.ts +15 -4
- package/dist/types/kernel/proc-info.d.ts +1 -0
- package/dist/types/launcher.d.ts +1 -0
- package/dist/types/mount/mount-fs.d.ts +11 -1
- package/dist/types/process/wasi/wasi-fds.d.ts +4 -0
- package/dist/types/process/wasi/wasi-files.d.ts +2 -0
- package/dist/types/realm/live-vfs-fs.d.ts +1 -0
- package/dist/types/realm/sync-fs-dispatch.d.ts +1 -0
- package/dist/types/realm/sync-fs-token-registry.d.ts +6 -1
- package/dist/types/realm/sync-fs-wire.d.ts +2 -0
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -754,6 +754,10 @@ The proxy answers `200` with `Content-Type: application/vnd.slicc.raw-fetch`: a
|
|
|
754
754
|
|
|
755
755
|
**Renaming a directory on the OPFS root** ([#1122](https://github.com/ai-ecoverse/slicc-kernel/issues/1122)): Chrome's OPFS can move a file but not a directory. So the kernel renames a directory by creating the new tree and moving each file into it (copying a file only if it can't move, e.g. while it's open elsewhere), then removing the old tree. Within the kernel the rename is atomic to readers: a lookup or listing under the old or the new name, or of either parent, waits until the whole tree has moved. So a stage-then-rename updater (pnpm's, an atomic config write) is never seen half there. Another tab sharing the root sees it all at once too (see below). A root whose directories can move (the Node memory root) renames them in one step. **If the page reloads or crashes mid-rename**, some files are already in the new tree and the rest are still in the old one. So before the first move, the kernel writes the rename's intent (`{ from, to }`) to its metadata store, and clears it after the last. At the next boot, when no other tab shares the root (the same rule as for the boot's other repair of stale metadata), the kernel finishes every recorded rename: it moves what's left into the new tree, removes the old name, and clears the record. The moved directory and its files keep their owner, mode and inode, as after a rename that wasn't stopped. While another tab shares the root, a kernel finishes only renames whose kernel is gone ([#1141](https://github.com/ai-ecoverse/slicc-kernel/issues/1141)): the renaming kernel holds a Web Lock (`slicc-rename:<id>`, named in the record) for the whole rename, and the browser releases it when that tab closes or its worker ends. A kernel that boots beside a peer, and every kernel that sees a peer go, finishes each recorded rename whose lock is free, taking that lock while it does, so two survivors never replay the same rename and a live kernel's rename is never touched. A record without a lock (written by an older kernel, or where Web Locks are missing) waits for a boot that's alone, as before. While another tab finishes a rename, this kernel's readers wait as for its own, and so do the other tabs'. A kernel whose wait for another tab's rename ends with the lock free and the record still there finishes the rename itself, under that lock, and a boot that's alone takes a record's lock too, so a third tab that boots meanwhile waits for it.
|
|
756
756
|
|
|
757
|
+
**An open file stays bound to the file it opened** ([#1394](https://github.com/ai-ecoverse/slicc-kernel/issues/1394)): as on Linux, a program's open file keeps reaching the file it opened when its name is later renamed over, by a file or a symlink, by the program itself or by another process.
|
|
758
|
+
- **The kernel keeps it:** the program keeps the file's contents before its own rename over the name, as it already did before its own unlink, and a rename by another process sets the held file aside, as an unlink does. Reads and writes through the descriptor reach the old file, and `fstat` keeps its inode.
|
|
759
|
+
- **Another tab:** a tab doesn't know which files another tab holds open, so a rename over the name there can't keep the file. The descriptor then fails with `ESTALE`; it never reads or writes the file the name now names. `/proc/sys/kernel/slicc_stale_fds` counts these.
|
|
760
|
+
|
|
757
761
|
**A rename by another process** ([#1257](https://github.com/ai-ecoverse/slicc-kernel/issues/1257)): as on Linux, where a descriptor names the file and not its path, a program's open file follows a rename of that file, or of a directory above it, by another process.
|
|
758
762
|
- **The journal:** the kernel keeps the last 4096 renames it applied, each from and to a path, and counts them in the change buffer it shares with its processes. A rename in another tab on the same root reaches it through the sidecar's move notice.
|
|
759
763
|
- **Catching up:** before a WASI or Emscripten program uses an open file's path (a read, a write, a seek, an `fsync`, a close, an open), it reads that count once. If another process renamed something since, and the program holds files open, it asks the kernel once for the renames of the paths it holds and moves its open files to their new names. The kernel answers only about the paths the program sent, and, under `users: 'enforce'`, drops every rename whose old or new name the program couldn't look up (search permission on each directory above it), so no process learns names in a directory it can't search. A program's own rename moves the count without asking, when nothing else came in between.
|
package/dist/kernel-worker.js
CHANGED
|
@@ -7728,7 +7728,7 @@ function satisfies(version, range2) {
|
|
|
7728
7728
|
}
|
|
7729
7729
|
|
|
7730
7730
|
// src/version.ts
|
|
7731
|
-
var KERNEL_VERSION = true ? "1.146.
|
|
7731
|
+
var KERNEL_VERSION = true ? "1.146.4" : "0.0.0-dev";
|
|
7732
7732
|
var OS_RELEASE = "6.0.0-slicc";
|
|
7733
7733
|
function procVersion() {
|
|
7734
7734
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -8600,6 +8600,55 @@ var Crontabs = class {
|
|
|
8600
8600
|
|
|
8601
8601
|
// src/fs/unlinked.ts
|
|
8602
8602
|
var UNLINKED_DIR = ".slicc-unlinked";
|
|
8603
|
+
var PathMarks = class {
|
|
8604
|
+
gen = 0;
|
|
8605
|
+
floor = 0;
|
|
8606
|
+
touched = /* @__PURE__ */ new Map();
|
|
8607
|
+
flying = /* @__PURE__ */ new Map();
|
|
8608
|
+
generation() {
|
|
8609
|
+
return this.gen;
|
|
8610
|
+
}
|
|
8611
|
+
touch(target) {
|
|
8612
|
+
this.gen += 1;
|
|
8613
|
+
this.touched.delete(target);
|
|
8614
|
+
this.touched.set(target, this.gen);
|
|
8615
|
+
if (this.touched.size <= TOUCHED_MAX) return;
|
|
8616
|
+
const [oldest, at] = this.touched.entries().next().value;
|
|
8617
|
+
this.touched.delete(oldest);
|
|
8618
|
+
this.floor = at;
|
|
8619
|
+
}
|
|
8620
|
+
changedSince(path, gen) {
|
|
8621
|
+
if (gen < this.floor) return true;
|
|
8622
|
+
for (let p = path; this.touched.size > 0; p = parentOf5(p)) {
|
|
8623
|
+
if ((this.touched.get(p) ?? 0) > gen) return true;
|
|
8624
|
+
if (p === "/") return false;
|
|
8625
|
+
}
|
|
8626
|
+
return false;
|
|
8627
|
+
}
|
|
8628
|
+
async around(paths, op) {
|
|
8629
|
+
for (const p of paths) {
|
|
8630
|
+
this.flying.set(p, (this.flying.get(p) ?? 0) + 1);
|
|
8631
|
+
this.touch(p);
|
|
8632
|
+
}
|
|
8633
|
+
try {
|
|
8634
|
+
return await op();
|
|
8635
|
+
} finally {
|
|
8636
|
+
for (const p of paths) {
|
|
8637
|
+
const n = (this.flying.get(p) ?? 1) - 1;
|
|
8638
|
+
if (n > 0) this.flying.set(p, n);
|
|
8639
|
+
else this.flying.delete(p);
|
|
8640
|
+
this.touch(p);
|
|
8641
|
+
}
|
|
8642
|
+
}
|
|
8643
|
+
}
|
|
8644
|
+
inFlight(path) {
|
|
8645
|
+
for (let p = path; this.flying.size > 0; p = parentOf5(p)) {
|
|
8646
|
+
if (this.flying.has(p)) return true;
|
|
8647
|
+
if (p === "/") return false;
|
|
8648
|
+
}
|
|
8649
|
+
return false;
|
|
8650
|
+
}
|
|
8651
|
+
};
|
|
8603
8652
|
function webLocks() {
|
|
8604
8653
|
return globalThis.navigator?.locks;
|
|
8605
8654
|
}
|
|
@@ -8653,14 +8702,13 @@ var UnlinkedKeeper = class {
|
|
|
8653
8702
|
queue = Promise.resolve();
|
|
8654
8703
|
count = 0;
|
|
8655
8704
|
replacing = /* @__PURE__ */ new Map();
|
|
8656
|
-
|
|
8657
|
-
floor = 0;
|
|
8658
|
-
touched = /* @__PURE__ */ new Map();
|
|
8705
|
+
marks;
|
|
8659
8706
|
replaced = 0;
|
|
8660
8707
|
links = /* @__PURE__ */ new Map();
|
|
8661
8708
|
linked = /* @__PURE__ */ new Map();
|
|
8662
8709
|
dirOwner;
|
|
8663
8710
|
constructor(fs, holders, rootOf, options = {}) {
|
|
8711
|
+
this.marks = options.marks ?? new PathMarks();
|
|
8664
8712
|
const { locks, owner } = options;
|
|
8665
8713
|
this.dirOwner = options.dirOwner;
|
|
8666
8714
|
this.fs = fs;
|
|
@@ -8682,7 +8730,6 @@ var UnlinkedKeeper = class {
|
|
|
8682
8730
|
const { replacing } = this;
|
|
8683
8731
|
const replace = async (target, op) => {
|
|
8684
8732
|
const { promise, resolve: resolve2 } = Promise.withResolvers();
|
|
8685
|
-
keeper.touch(target);
|
|
8686
8733
|
let mine;
|
|
8687
8734
|
const gated = () => {
|
|
8688
8735
|
const before = replacing.get(target);
|
|
@@ -8693,10 +8740,9 @@ var UnlinkedKeeper = class {
|
|
|
8693
8740
|
if (mine) mine.sealed = true;
|
|
8694
8741
|
};
|
|
8695
8742
|
try {
|
|
8696
|
-
await op(gated, sealed);
|
|
8743
|
+
await keeper.marks.around([target], () => op(gated, sealed));
|
|
8697
8744
|
} finally {
|
|
8698
8745
|
keeper.replaced += 1;
|
|
8699
|
-
keeper.touch(target);
|
|
8700
8746
|
resolve2();
|
|
8701
8747
|
if (mine && replacing.get(target) === mine) replacing.delete(target);
|
|
8702
8748
|
}
|
|
@@ -8747,25 +8793,15 @@ var UnlinkedKeeper = class {
|
|
|
8747
8793
|
const target = fs.resolvePath("/", to);
|
|
8748
8794
|
const source = fs.resolvePath("/", from);
|
|
8749
8795
|
if (target === source) return fs.rename(from, to);
|
|
8750
|
-
keeper.
|
|
8751
|
-
|
|
8752
|
-
|
|
8796
|
+
await keeper.marks.around(
|
|
8797
|
+
[source],
|
|
8798
|
+
() => replace(target, async (gated, sealed) => {
|
|
8753
8799
|
const pinned = await keeper.pin(target, gated, sealed);
|
|
8754
8800
|
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
8755
|
-
})
|
|
8756
|
-
|
|
8757
|
-
keeper.touch(source);
|
|
8758
|
-
}
|
|
8759
|
-
},
|
|
8760
|
-
async symlink(target, path, owner) {
|
|
8761
|
-
const at = fs.resolvePath("/", path);
|
|
8762
|
-
keeper.touch(at);
|
|
8763
|
-
try {
|
|
8764
|
-
await fs.symlink(target, path, owner);
|
|
8765
|
-
} finally {
|
|
8766
|
-
keeper.touch(at);
|
|
8767
|
-
}
|
|
8801
|
+
})
|
|
8802
|
+
);
|
|
8768
8803
|
},
|
|
8804
|
+
symlink: (target, path, owner) => keeper.marks.around([fs.resolvePath("/", path)], () => fs.symlink(target, path, owner)),
|
|
8769
8805
|
readdir: async (path) => visible(await fs.readdir(path)),
|
|
8770
8806
|
readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted2(st)])
|
|
8771
8807
|
};
|
|
@@ -8777,24 +8813,13 @@ var UnlinkedKeeper = class {
|
|
|
8777
8813
|
}
|
|
8778
8814
|
}
|
|
8779
8815
|
generation() {
|
|
8780
|
-
return this.
|
|
8816
|
+
return this.marks.generation();
|
|
8781
8817
|
}
|
|
8782
8818
|
changedSince(path, gen) {
|
|
8783
|
-
|
|
8784
|
-
for (let p = path; this.touched.size > 0; p = parentOf5(p)) {
|
|
8785
|
-
if ((this.touched.get(p) ?? 0) > gen) return true;
|
|
8786
|
-
if (p === "/") return false;
|
|
8787
|
-
}
|
|
8788
|
-
return false;
|
|
8819
|
+
return this.marks.changedSince(path, gen);
|
|
8789
8820
|
}
|
|
8790
|
-
|
|
8791
|
-
this.
|
|
8792
|
-
this.touched.delete(target);
|
|
8793
|
-
this.touched.set(target, this.gen);
|
|
8794
|
-
if (this.touched.size <= TOUCHED_MAX) return;
|
|
8795
|
-
const [oldest, at] = this.touched.entries().next().value;
|
|
8796
|
-
this.touched.delete(oldest);
|
|
8797
|
-
this.floor = at;
|
|
8821
|
+
inFlight(path) {
|
|
8822
|
+
return this.marks.inFlight(path);
|
|
8798
8823
|
}
|
|
8799
8824
|
missed(path) {
|
|
8800
8825
|
for (let p = path; this.replacing.size > 0; p = parentOf5(p)) {
|
|
@@ -10606,16 +10631,16 @@ var FreshModules = class {
|
|
|
10606
10631
|
if (size < MAGIC.length && this.fresh.has(normalizePath(path))) await decide(path);
|
|
10607
10632
|
};
|
|
10608
10633
|
}
|
|
10609
|
-
const
|
|
10634
|
+
const bound3 = /* @__PURE__ */ new WeakMap();
|
|
10610
10635
|
return new Proxy(fs, {
|
|
10611
10636
|
get(target, key, receiver) {
|
|
10612
10637
|
if (Object.hasOwn(overrides, key)) return overrides[key];
|
|
10613
10638
|
const value2 = Reflect.get(target, key, receiver);
|
|
10614
10639
|
if (typeof value2 !== "function") return value2;
|
|
10615
|
-
let method =
|
|
10640
|
+
let method = bound3.get(value2);
|
|
10616
10641
|
if (method === void 0) {
|
|
10617
10642
|
method = value2.bind(target);
|
|
10618
|
-
|
|
10643
|
+
bound3.set(value2, method);
|
|
10619
10644
|
}
|
|
10620
10645
|
return method;
|
|
10621
10646
|
}
|
|
@@ -11855,6 +11880,12 @@ var HeldSet = class extends Set {
|
|
|
11855
11880
|
holds2.onIdle = this.idle;
|
|
11856
11881
|
return super.add(holds2);
|
|
11857
11882
|
}
|
|
11883
|
+
doubt(paths, over) {
|
|
11884
|
+
for (const holds2 of this) {
|
|
11885
|
+
for (const path of paths) holds2.doubt(path, true);
|
|
11886
|
+
if (over !== void 0) holds2.doubt(over);
|
|
11887
|
+
}
|
|
11888
|
+
}
|
|
11858
11889
|
delete(holds2) {
|
|
11859
11890
|
const removed = super.delete(holds2);
|
|
11860
11891
|
for (const path of /* @__PURE__ */ new Set([...holds2.keys(), ...holds2.opens.keys()])) this.idle(path);
|
|
@@ -11876,6 +11907,9 @@ var HeldPaths = class extends Map {
|
|
|
11876
11907
|
uses = /* @__PURE__ */ new Map();
|
|
11877
11908
|
lastHandle = 0;
|
|
11878
11909
|
owning;
|
|
11910
|
+
bound = /* @__PURE__ */ new Map();
|
|
11911
|
+
doubted = /* @__PURE__ */ new Map();
|
|
11912
|
+
doubts = 0;
|
|
11879
11913
|
onIdle;
|
|
11880
11914
|
keyOf(path) {
|
|
11881
11915
|
return this.aliases.get(path) ?? path;
|
|
@@ -11896,6 +11930,21 @@ var HeldPaths = class extends Map {
|
|
|
11896
11930
|
handlesAt(path) {
|
|
11897
11931
|
return [...this.handles].filter(([, key]) => key === path).map(([id2]) => id2);
|
|
11898
11932
|
}
|
|
11933
|
+
doubt(over, exact = false) {
|
|
11934
|
+
if (this.bound.size === 0) return;
|
|
11935
|
+
for (const [handle, key] of this.handles) {
|
|
11936
|
+
if (!this.bound.has(handle) || this.keptHandles.has(handle)) continue;
|
|
11937
|
+
if (exact ? key === over : within6(key, over)) this.doubted.set(handle, ++this.doubts);
|
|
11938
|
+
}
|
|
11939
|
+
}
|
|
11940
|
+
boundIno(handle) {
|
|
11941
|
+
const doubt = this.doubted.get(handle);
|
|
11942
|
+
const ino = this.bound.get(handle);
|
|
11943
|
+
return doubt === void 0 || ino === void 0 ? void 0 : { ino, doubt };
|
|
11944
|
+
}
|
|
11945
|
+
trust(handle, doubt) {
|
|
11946
|
+
if (this.doubted.get(handle) === doubt) this.doubted.delete(handle);
|
|
11947
|
+
}
|
|
11899
11948
|
keptFor(handle) {
|
|
11900
11949
|
return this.keptHandles.get(handle);
|
|
11901
11950
|
}
|
|
@@ -11932,8 +11981,16 @@ var HeldPaths = class extends Map {
|
|
|
11932
11981
|
for (const file of [...this.uses.keys()]) this.letGo(file, true);
|
|
11933
11982
|
this.keptHandles.clear();
|
|
11934
11983
|
}
|
|
11935
|
-
hold(path, on, open2 = false, real, handle) {
|
|
11936
|
-
if (open2)
|
|
11984
|
+
hold(path, on, open2 = false, real, handle, ino) {
|
|
11985
|
+
if (open2) {
|
|
11986
|
+
const made2 = this.holdOpen(path, on, real, handle);
|
|
11987
|
+
if (handle !== void 0) {
|
|
11988
|
+
this.bound.delete(handle);
|
|
11989
|
+
this.doubted.delete(handle);
|
|
11990
|
+
}
|
|
11991
|
+
if (made2 !== void 0 && ino !== void 0) this.bound.set(made2, ino);
|
|
11992
|
+
return made2;
|
|
11993
|
+
}
|
|
11937
11994
|
this.holdWrite(path, on);
|
|
11938
11995
|
return void 0;
|
|
11939
11996
|
}
|
|
@@ -13610,13 +13667,24 @@ async function holdOp(entry, path, req) {
|
|
|
13610
13667
|
const real = await entry.fs.realpath?.(path, true).catch(() => path);
|
|
13611
13668
|
await entry.source?.open(path, real);
|
|
13612
13669
|
await stillThere(entry, path, req);
|
|
13613
|
-
if (
|
|
13614
|
-
|
|
13670
|
+
if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
|
|
13671
|
+
if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
|
|
13672
|
+
return json2(entry.hold?.(path, on, open2, real, void 0, req.ino) ?? null);
|
|
13615
13673
|
}
|
|
13616
13674
|
}
|
|
13617
13675
|
entry.hold?.(path, on, open2, void 0, req.handle);
|
|
13618
13676
|
return done2;
|
|
13619
13677
|
}
|
|
13678
|
+
async function ownEntry(entry, path, real) {
|
|
13679
|
+
const own = await entry.fs.realpath?.(path, false).catch((err) => {
|
|
13680
|
+
if (err.code !== "ENOENT") throw err;
|
|
13681
|
+
return path;
|
|
13682
|
+
});
|
|
13683
|
+
if (own !== void 0 && own !== real) return false;
|
|
13684
|
+
const st = await entry.fs.lstat(path);
|
|
13685
|
+
if (st.isSymbolicLink) throw syncError("ELOOP", path);
|
|
13686
|
+
return true;
|
|
13687
|
+
}
|
|
13620
13688
|
async function followed(g, path) {
|
|
13621
13689
|
const st = await g.fs.lstat(path).catch(() => void 0);
|
|
13622
13690
|
if (st?.isSymbolicLink && !trustedLink(permOf(st), g.links)) deny(g, "follow", path);
|
|
@@ -13751,10 +13819,26 @@ async function linkedElsewhere(fs, path, from) {
|
|
|
13751
13819
|
return !(source && source.ino === st.ino && source.dev === st.dev);
|
|
13752
13820
|
}
|
|
13753
13821
|
async function keptOr(entry, path, handle) {
|
|
13754
|
-
if (handle !== void 0)
|
|
13822
|
+
if (handle !== void 0) {
|
|
13823
|
+
const hidden = entry.keptFor?.(handle)?.hidden;
|
|
13824
|
+
if (hidden !== void 0) return hidden;
|
|
13825
|
+
await bound(entry, path, handle);
|
|
13826
|
+
return path;
|
|
13827
|
+
}
|
|
13755
13828
|
const kept = entry.unlinked?.(path);
|
|
13756
13829
|
return kept && !await entry.fs.exists(path) ? kept.hidden : path;
|
|
13757
13830
|
}
|
|
13831
|
+
async function bound(entry, path, handle) {
|
|
13832
|
+
const bound3 = entry.boundIno?.(handle);
|
|
13833
|
+
if (bound3 === void 0) return;
|
|
13834
|
+
const now = await entry.fs.stat(path).catch(() => void 0);
|
|
13835
|
+
if (now?.ino === bound3.ino) {
|
|
13836
|
+
entry.trust?.(handle, bound3.doubt);
|
|
13837
|
+
return;
|
|
13838
|
+
}
|
|
13839
|
+
staleFds++;
|
|
13840
|
+
throw syncError("ESTALE", path);
|
|
13841
|
+
}
|
|
13758
13842
|
async function keptStat(entry, req) {
|
|
13759
13843
|
const hidden = entry.keptFor?.(req.handle ?? -1)?.hidden;
|
|
13760
13844
|
if (hidden === void 0)
|
|
@@ -13781,6 +13865,10 @@ var served = 0;
|
|
|
13781
13865
|
function servedFsCalls() {
|
|
13782
13866
|
return served;
|
|
13783
13867
|
}
|
|
13868
|
+
var staleFds = 0;
|
|
13869
|
+
function staleFdCount() {
|
|
13870
|
+
return staleFds;
|
|
13871
|
+
}
|
|
13784
13872
|
async function dispatchSyncFs(req) {
|
|
13785
13873
|
served++;
|
|
13786
13874
|
const entry = resolveSyncFsToken(req.token);
|
|
@@ -14556,6 +14644,8 @@ var SYS_TREE = {
|
|
|
14556
14644
|
slicc_cwd_checks: (listing) => `${listing.cwdChecks ?? 0}
|
|
14557
14645
|
`,
|
|
14558
14646
|
slicc_rename_ack_busy: (listing) => `${listing.renameAcks?.busy ?? 0}
|
|
14647
|
+
`,
|
|
14648
|
+
slicc_stale_fds: (listing) => `${listing.staleFds ?? 0}
|
|
14559
14649
|
`,
|
|
14560
14650
|
slicc_rename_acks: (listing) => {
|
|
14561
14651
|
const samples = [...listing.renameAcks?.ackMs ?? []].sort((a, b) => a - b);
|
|
@@ -19228,7 +19318,7 @@ async function writeAll3(file, bytes) {
|
|
|
19228
19318
|
const write = file.write;
|
|
19229
19319
|
for (let at = 0; at < bytes.length; ) at += await write.call(file, bytes.subarray(at));
|
|
19230
19320
|
}
|
|
19231
|
-
function
|
|
19321
|
+
function bound2(file, key) {
|
|
19232
19322
|
const method = file[key];
|
|
19233
19323
|
return typeof method === "function" ? { [key]: method.bind(file) } : {};
|
|
19234
19324
|
}
|
|
@@ -19320,9 +19410,9 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
|
|
|
19320
19410
|
later(stream.idle());
|
|
19321
19411
|
};
|
|
19322
19412
|
return new OpenFile({
|
|
19323
|
-
...
|
|
19324
|
-
...
|
|
19325
|
-
...
|
|
19413
|
+
...bound2(file, "read"),
|
|
19414
|
+
...bound2(file, "peek"),
|
|
19415
|
+
...bound2(file, "stat"),
|
|
19326
19416
|
poll: () => {
|
|
19327
19417
|
const state = pollFile(file);
|
|
19328
19418
|
return quiet2() ? state : { ...state, writable: false };
|
|
@@ -25041,7 +25131,9 @@ function holdingToken(opts, holds2, caller) {
|
|
|
25041
25131
|
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
25042
25132
|
...opts.renames ? { renames: opts.renames } : {},
|
|
25043
25133
|
...opts.renameSeq ? { renameSeq: opts.renameSeq } : {},
|
|
25044
|
-
hold: (path, on, open2, real, handle) => holds2.hold(path, on, open2, real, handle),
|
|
25134
|
+
hold: (path, on, open2, real, handle, ino) => holds2.hold(path, on, open2, real, handle, ino),
|
|
25135
|
+
boundIno: holds2.boundIno.bind(holds2),
|
|
25136
|
+
trust: holds2.trust.bind(holds2),
|
|
25045
25137
|
keptFor: (handle) => holds2.keptFor(handle),
|
|
25046
25138
|
unlinked: (path) => holds2.keptAt(path),
|
|
25047
25139
|
gate: (path, op) => opts.unlinkGate && holds2.holding(path) ? opts.unlinkGate.run(holds2.keyOf(path), op) : op(),
|
|
@@ -28971,6 +29063,7 @@ var Launcher = class {
|
|
|
28971
29063
|
fstabRetries;
|
|
28972
29064
|
inserted = /* @__PURE__ */ new Map();
|
|
28973
29065
|
openFiles = /* @__PURE__ */ new Set();
|
|
29066
|
+
pathMarks = new PathMarks();
|
|
28974
29067
|
deciding = /* @__PURE__ */ new Map();
|
|
28975
29068
|
identities = { asked: 0, timedOut: 0 };
|
|
28976
29069
|
nodes;
|
|
@@ -29091,6 +29184,7 @@ var Launcher = class {
|
|
|
29091
29184
|
this.openFiles.add(this.nodes);
|
|
29092
29185
|
options.fs.onPeer?.(() => this.flushOpen());
|
|
29093
29186
|
options.fs.remoteChanges?.((change) => {
|
|
29187
|
+
this.held.doubt(change.paths, change.moved?.to);
|
|
29094
29188
|
if (change.moved) this.moved(change.moved.from, change.moved.to);
|
|
29095
29189
|
this.changes.flush();
|
|
29096
29190
|
this.cwds.touched(change.paths, change.trees);
|
|
@@ -29105,6 +29199,7 @@ var Launcher = class {
|
|
|
29105
29199
|
locks: webLocks(),
|
|
29106
29200
|
busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
|
|
29107
29201
|
owner: options.unlinkOwner,
|
|
29202
|
+
marks: this.pathMarks,
|
|
29108
29203
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
29109
29204
|
}
|
|
29110
29205
|
);
|
|
@@ -30102,7 +30197,8 @@ var Launcher = class {
|
|
|
30102
30197
|
opfsCalls: this.rootFs.opfsCalls?.() ?? 0,
|
|
30103
30198
|
opfsOps: this.rootFs.opfsOps?.() ?? 0,
|
|
30104
30199
|
cwdChecks: this.cwds.checks,
|
|
30105
|
-
renameAcks: this.rootFs.renameWaits?.()
|
|
30200
|
+
renameAcks: this.rootFs.renameWaits?.(),
|
|
30201
|
+
staleFds: staleFdCount()
|
|
30106
30202
|
}),
|
|
30107
30203
|
sudo: (call, signal) => this.sudo(pid, call, signal),
|
|
30108
30204
|
crontab: (call) => this.crontab(pid, call),
|
|
@@ -31026,6 +31122,7 @@ var Launcher = class {
|
|
|
31026
31122
|
locks: webLocks(),
|
|
31027
31123
|
busy: (path) => [...this.openFiles].some((open2) => open2.holds(path)),
|
|
31028
31124
|
owner: this.unlinkOwnerOption,
|
|
31125
|
+
marks: this.pathMarks,
|
|
31029
31126
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
31030
31127
|
}
|
|
31031
31128
|
);
|
|
@@ -31079,6 +31176,7 @@ var Launcher = class {
|
|
|
31079
31176
|
this.renameLog.add(from, to);
|
|
31080
31177
|
if (source) return;
|
|
31081
31178
|
for (const nodes of this.openFiles) nodes.renamed(from, to);
|
|
31179
|
+
for (const holds2 of this.held) holds2.renamed(from, to);
|
|
31082
31180
|
this.watchers.moved(from, to);
|
|
31083
31181
|
}
|
|
31084
31182
|
processChain(fs, nodes, unlinked) {
|