@ai-ecoverse/slicc-kernel 1.61.0 → 1.61.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 +1 -1
- package/dist/kernel-worker.js +15 -1
- package/dist/kernel-worker.js.map +2 -2
- package/dist/node-process-worker.js +36 -7
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +15 -1
- package/dist/node.js.map +2 -2
- package/dist/process-worker.js +36 -7
- package/dist/process-worker.js.map +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -406,7 +406,7 @@ Directories are renamed with `FileSystemHandle.move()` where available, else by
|
|
|
406
406
|
- **File contents:** on a mount with `ranges`, as `tmpfs` is, and on the root file system when it is in OPFS (in a browser with sync access handles) or an in-memory root, a program reads and writes a file in 64 KiB pages: reads come from the driver 1 MiB at a time, and changed pages go back in 1 MiB runs once 8 MiB have changed, at `fsync`, and when the file is closed (from the kernel's own descriptors also 250 ms after a write). A file of any size the mount can hold works, with memory to spare for about one more copy of it. In OPFS a write opens a sync access handle for just that write. An `fsa` mount (a picked folder) has no sync access handles, and Chrome copies the whole file into a swap file for every writable stream, so it writes through one stream per open file: the first write back opens it, later runs go into it at their offsets, and it is committed, one copy of the file, when the last process writing the file closes it (or exits), before another process reads what was written, and before a rename, unlink or unmount. Until then other processes see the new size but read the old bytes outside what was written. Elsewhere (`hostfs` with an older proxy, and a root outside OPFS), file contents are read whole when a program opens a file and written back when it closes it. Before a program's next call into the kernel (any wait, read of a pipe or terminal, sleep or spawn), its changed pages also go back, so other processes and the embedder see a write as soon as the writer does anything but compute: a daemon's pidfile, a log another process follows. On a whole-file mount that holds only for files of up to 64 KiB; a larger one goes back at close, fsync and exit, as before. Beyond that, both give close-to-open consistency, as NFS has. A process that ends without closing its files keeps what it wrote: on exit, on a trap, and when a signal kills it. For a killed WASI program, the kernel lets its worker write its changed pages back before `waitpid` reports the death. The only exception is a worker that makes no call into the kernel for 2 seconds after the signal; it is stopped without that write-back.
|
|
407
407
|
- **Rules on a mount:**
|
|
408
408
|
- a rename across mounts is `EXDEV`, so `mv` copies;
|
|
409
|
-
- a hard link (`link()`, `linkat()`, Python's `os.link`, `ctx.fs.link`) works within a mount whose driver offers `links`, as `tmpfs` does: both names are one file with one inode, `st_nlink` counts them, and the data goes with the last name and the last open descriptor. Across mounts it's `EXDEV`. It's `EPERM` on the root file system (OPFS or memory), on `fsa` folders, on `hostfs` until slicc-node offers links, and for directories. `link()` links a symlink itself and `linkat(…, AT_SYMLINK_FOLLOW)` links its target, as on Linux. Descriptors the kernel holds (JS programs' files, and a WASI program's files once it forks or spawns with them) share one cache for all names of a file, so a write through one name is read at once through another, also after either name is removed or replaced while open; `link()` writes the source's pending changes back first. Within one WASI program, its own descriptors of a file's names share one buffer too, except after it starts a subprocess: the files it had open are kernel descriptors from then on, and a later open of the same file, under that name or another, is only consistent with them at open and close ([#355](https://github.com/ai-ecoverse/slicc-kernel/issues/355)); between programs, a WASI program's own descriptors see the other names' writes at open, as for one name across processes. A WASI program that removes or renames over the name it has open, while another link remains, keeps writing to the file through its descriptor, and the other names see those writes; `linkat(…, AT_SYMLINK_FOLLOW)` writes back the program's pending changes to the symlink's target first. `st_nlink` leaves out the names the kernel keeps in `.slicc-unlinked` for open descriptors. The other names of a linked file keep the attributes the kernel has cached for them for up to the mount's `attrTtl` (`tmpfs` caches none). Emscripten programs (bash, coreutils `ln`) get `EMLINK` from their own C library, which never asks the kernel, but they see links other programs made: their open descriptors of one file's names share one buffer, and one that removes the name it has open while another link remains keeps writing to the file;
|
|
409
|
+
- a hard link (`link()`, `linkat()`, Python's `os.link`, `ctx.fs.link`) works within a mount whose driver offers `links`, as `tmpfs` does: both names are one file with one inode, `st_nlink` counts them, and the data goes with the last name and the last open descriptor. Across mounts it's `EXDEV`. It's `EPERM` on the root file system (OPFS or memory), on `fsa` folders, on `hostfs` until slicc-node offers links, and for directories. `link()` links a symlink itself and `linkat(…, AT_SYMLINK_FOLLOW)` links its target, as on Linux. Descriptors the kernel holds (JS programs' files, and a WASI program's files once it forks or spawns with them) share one cache for all names of a file, so a write through one name is read at once through another, also after either name is removed or replaced while open; `link()` writes the source's pending changes back first. Within one WASI program, its own descriptors of a file's names share one buffer too, except after it starts a subprocess: the files it had open are kernel descriptors from then on, and a later open of the same file, under that name or another, is only consistent with them at open and close ([#355](https://github.com/ai-ecoverse/slicc-kernel/issues/355)); between programs, a WASI program's own descriptors see the other names' writes at open, as for one name across processes. A WASI program that removes or renames over the name it has open, while another link remains, keeps writing to the file through its descriptor, and the other names see those writes; `linkat(…, AT_SYMLINK_FOLLOW)` writes back the program's pending changes to the symlink's target first. `st_nlink` leaves out the names the kernel keeps in `.slicc-unlinked` for open descriptors, so `fstat` on a descriptor whose name was removed counts the names left: 0 once the last one is gone. The other names of a linked file keep the attributes the kernel has cached for them for up to the mount's `attrTtl` (`tmpfs` caches none). Emscripten programs (bash, coreutils `ln`) get `EMLINK` from their own C library, which never asks the kernel, but they see links other programs made: their open descriptors of one file's names share one buffer, and one that removes the name it has open while another link remains keeps writing to the file;
|
|
410
410
|
- `df` reports each mount from its driver's `statfs`;
|
|
411
411
|
- each mount has its own device number;
|
|
412
412
|
- without `chmod` support, `chmod` succeeds and changes nothing;
|
package/dist/kernel-worker.js
CHANGED
|
@@ -6266,7 +6266,7 @@ var UnlinkedKeeper = class {
|
|
|
6266
6266
|
async linkedName(hidden) {
|
|
6267
6267
|
if (!mayLink(this.fs, hidden)) return;
|
|
6268
6268
|
const st = await this.fs.lstat(hidden).catch(() => void 0);
|
|
6269
|
-
if (
|
|
6269
|
+
if (st?.nlink === void 0) return;
|
|
6270
6270
|
const id2 = `${st.dev ?? 0}:${st.ino}`;
|
|
6271
6271
|
this.linked.set(hidden, id2);
|
|
6272
6272
|
this.links.set(id2, (this.links.get(id2) ?? 0) + 1);
|
|
@@ -7945,6 +7945,12 @@ async function keptOr(entry, path, handle) {
|
|
|
7945
7945
|
const kept = entry.unlinked?.(path);
|
|
7946
7946
|
return kept && !await entry.fs.exists(path) ? kept.hidden : path;
|
|
7947
7947
|
}
|
|
7948
|
+
async function keptStat(entry, req) {
|
|
7949
|
+
const hidden = entry.keptFor?.(req.handle ?? -1)?.hidden;
|
|
7950
|
+
if (hidden === void 0)
|
|
7951
|
+
return { ok: false, errno: "ENOENT", message: "the handle keeps no file" };
|
|
7952
|
+
return json2(statJson(await entry.fs.stat(hidden)));
|
|
7953
|
+
}
|
|
7948
7954
|
async function owned(entry, own, req, path, target) {
|
|
7949
7955
|
const source = target === path ? void 0 : path;
|
|
7950
7956
|
if (UNLINKING_OPS.has(req.op) && await linkedElsewhere(entry.fs, target, source)) {
|
|
@@ -7959,6 +7965,7 @@ async function dispatchSyncFs(req) {
|
|
|
7959
7965
|
try {
|
|
7960
7966
|
const path = fs.resolvePath(cwd, req.path);
|
|
7961
7967
|
if (req.op === "kept") return json2(entry.keptFor?.(req.handle ?? -1) !== void 0);
|
|
7968
|
+
if (req.op === "stat" && req.handle !== void 0) return await keptStat(entry, req);
|
|
7962
7969
|
if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
|
|
7963
7970
|
if (req.op === "hold") {
|
|
7964
7971
|
const [on, open] = [((req.mode ?? 0) & 1) === 1, ((req.mode ?? 0) & 2) === 2];
|
|
@@ -10151,6 +10158,12 @@ var VfsNodes = class {
|
|
|
10151
10158
|
async identify(path) {
|
|
10152
10159
|
return this.byPath.has(path) || !mayLink(this.fs, path) ? void 0 : this.identity(path);
|
|
10153
10160
|
}
|
|
10161
|
+
async flushLinked(path) {
|
|
10162
|
+
if (this.byId.size === 0 || !mayLink(this.fs, path)) return;
|
|
10163
|
+
const id2 = await this.identity(path);
|
|
10164
|
+
const node = id2 === void 0 ? void 0 : this.byId.get(id2);
|
|
10165
|
+
if (node) await node.serial(() => node.flush());
|
|
10166
|
+
}
|
|
10154
10167
|
async linked(from, to) {
|
|
10155
10168
|
const node = this.byPath.get(from);
|
|
10156
10169
|
if (!node || node.orphaned) return;
|
|
@@ -10355,6 +10368,7 @@ function keepingOpen(fs, nodes) {
|
|
|
10355
10368
|
const source = fs.resolvePath("/", from);
|
|
10356
10369
|
const at = await (follow2 ? nodes.targetKey(source) : nodes.entryKey(source));
|
|
10357
10370
|
await nodes.flush(at);
|
|
10371
|
+
await nodes.flushLinked(at);
|
|
10358
10372
|
const key = await nodes.entryKey(fs.resolvePath("/", to));
|
|
10359
10373
|
await nodes.onEntry(key, () => fs.link(from, to, follow2));
|
|
10360
10374
|
await nodes.linked(at, key);
|