@ai-ecoverse/slicc-kernel 1.146.1 → 1.146.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/kernel-worker.js +78 -39
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +402 -369
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +78 -39
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +48 -15
- package/dist/process-worker.js.map +3 -3
- package/dist/types/fs/unlinked.d.ts +15 -4
- package/dist/types/launcher.d.ts +1 -0
- package/dist/types/process/js/async-sab.d.ts +1 -1
- package/dist/types/process/wasi/wasi-fds.d.ts +3 -0
- package/dist/types/realm/sync-sab-bridge.d.ts +4 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1128,7 +1128,7 @@ A character device (`0666`), there in every `users` mode, through which programs
|
|
|
1128
1128
|
- **No hash known:** when `lookup` has none (`undefined`), `unknown: 'allow'` (the default) runs the file and reports `{ op: 'exec-unverified', errno: 'ENODATA', … }` once per path, while `unknown: 'deny'` refuses it as a mismatch.
|
|
1129
1129
|
- **The cache:** a result is kept per real path and the file's version: what OPFS itself reports (its `lastModified` and `size`), plus the count of writes kernels made to the file, which the metadata store keeps (Chrome doesn't move `lastModified` for a write through a sync access handle). A kernel with `integrity` set counts a write to a protected file before its bytes change, and again once the write is over (also when it failed), at wherever the file is by then; the count follows a rename, and no `utimes` turns it back. The version also holds the count of moves into protection: a rename or a symlink at or above a protected path counts one before and after it, so a move into a protected path re-verifies every protected program once. So a change made around the kernel, same size included, is checked again at the next start. A kernel without `integrity` counts nothing (so writes into protected paths cost no metadata update), and its writes don't move the version another kernel on the origin reads: give every kernel on one origin the same `integrity`. The cache is the kernel's own, in memory, so nothing verified outlives it. The compiled module is cached by the same key, so a program that hasn't changed costs nothing more to start.
|
|
1130
1130
|
- **Revoking:** a result is kept until the file changes, so when `lookup`'s answers change (an updated hash list, a package revoked or reinstalled), call `await kernel.integrity.revoke(path?)` (on the page and in Node). It drops what the kernel verified for that path, or for everything under it when it's a directory (its real path and the name a link reached it by both count), or for every path without one, and rebuilds the command catalog from the manifests as checked again. A load that was under way when `revoke` was called keeps nothing. It takes effect at the next start: running processes keep what they loaded. `path` must be absolute; without `integrity` it does nothing.
|
|
1131
|
-
- **Where `lookup` gets its hashes:** from a source independent of the installed files, such as the per-file hashes the embedder derives from the
|
|
1131
|
+
- **Where `lookup` gets its hashes:** from a source independent of the installed files, such as the per-file hashes the embedder derives from the published package tarball when it builds its catalog. Hashes read back from the installed files prove nothing.
|
|
1132
1132
|
- **Links out of the paths:** a file reached through a protected path but whose real path lies outside every path (a symlink at or below a protected path, or one of its directories, pointing elsewhere, a command of the catalog included) is still checked, under the last protected name on the way: the kernel asks `lookup(name)` for the bytes of the real file, `unknown` applies when it has no hash, and `onDenial` reports the name as `path`. Writes outside the paths aren't counted, so such a result isn't cached: the file is hashed again at every start.
|
|
1133
1133
|
- **Sources a program reads itself** ([#454](https://github.com/ai-ecoverse/slicc-kernel/issues/454)): a runtime that loads code on its own is checked too, when a process reads the file through the kernel. That covers jsh's and slicc-node's modules and the `package.json` files whose `main`/`exports` they follow, python's `.py`, `.pyc`, `.pth` and zip imports, `dlopen`'s side modules, and a script's interpreter reading the script after its exec check (a file swapped in between is refused then).
|
|
1134
1134
|
- **Which files:** those under a path whose name ends in one of `integrity.sources`, plus every regular file with an execute bit (a `#!` script without a suffix, a sourced script). The default is `['.js', '.mjs', '.cjs', '.json', '.py', '.pyc', '.pth', '.zip', '.so', '.wasm', '.sh']`. An embedder can replace the list, and `sources: []` turns these checks off and leaves only the ones above. Data files aren't checked, since a package can hold files no `lookup` knows (lock files, caches), which `unknown: 'deny'` would make unreadable.
|
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.3" : "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)) {
|
|
@@ -13610,13 +13635,24 @@ async function holdOp(entry, path, req) {
|
|
|
13610
13635
|
const real = await entry.fs.realpath?.(path, true).catch(() => path);
|
|
13611
13636
|
await entry.source?.open(path, real);
|
|
13612
13637
|
await stillThere(entry, path, req);
|
|
13613
|
-
if (
|
|
13638
|
+
if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
|
|
13639
|
+
if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
|
|
13614
13640
|
return json2(entry.hold?.(path, on, open2, real) ?? null);
|
|
13615
13641
|
}
|
|
13616
13642
|
}
|
|
13617
13643
|
entry.hold?.(path, on, open2, void 0, req.handle);
|
|
13618
13644
|
return done2;
|
|
13619
13645
|
}
|
|
13646
|
+
async function ownEntry(entry, path, real) {
|
|
13647
|
+
const own = await entry.fs.realpath?.(path, false).catch((err) => {
|
|
13648
|
+
if (err.code !== "ENOENT") throw err;
|
|
13649
|
+
return path;
|
|
13650
|
+
});
|
|
13651
|
+
if (own !== void 0 && own !== real) return false;
|
|
13652
|
+
const st = await entry.fs.lstat(path);
|
|
13653
|
+
if (st.isSymbolicLink) throw syncError("ELOOP", path);
|
|
13654
|
+
return true;
|
|
13655
|
+
}
|
|
13620
13656
|
async function followed(g, path) {
|
|
13621
13657
|
const st = await g.fs.lstat(path).catch(() => void 0);
|
|
13622
13658
|
if (st?.isSymbolicLink && !trustedLink(permOf(st), g.links)) deny(g, "follow", path);
|
|
@@ -28971,6 +29007,7 @@ var Launcher = class {
|
|
|
28971
29007
|
fstabRetries;
|
|
28972
29008
|
inserted = /* @__PURE__ */ new Map();
|
|
28973
29009
|
openFiles = /* @__PURE__ */ new Set();
|
|
29010
|
+
pathMarks = new PathMarks();
|
|
28974
29011
|
deciding = /* @__PURE__ */ new Map();
|
|
28975
29012
|
identities = { asked: 0, timedOut: 0 };
|
|
28976
29013
|
nodes;
|
|
@@ -29105,6 +29142,7 @@ var Launcher = class {
|
|
|
29105
29142
|
locks: webLocks(),
|
|
29106
29143
|
busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
|
|
29107
29144
|
owner: options.unlinkOwner,
|
|
29145
|
+
marks: this.pathMarks,
|
|
29108
29146
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
29109
29147
|
}
|
|
29110
29148
|
);
|
|
@@ -31026,6 +31064,7 @@ var Launcher = class {
|
|
|
31026
31064
|
locks: webLocks(),
|
|
31027
31065
|
busy: (path) => [...this.openFiles].some((open2) => open2.holds(path)),
|
|
31028
31066
|
owner: this.unlinkOwnerOption,
|
|
31067
|
+
marks: this.pathMarks,
|
|
31029
31068
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
31030
31069
|
}
|
|
31031
31070
|
);
|