@ai-ecoverse/slicc-kernel 1.99.0 → 1.99.2
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 +32 -1
- package/dist/kernel-worker.js +46 -16
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +27 -5
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +46 -16
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +27 -5
- package/dist/process-worker.js.map +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -346,7 +346,7 @@ The module's default export (or its `main` export) is called with a context and
|
|
|
346
346
|
- `fdStatus(fd)` (`{ type, tty, seekable }`, `type` being `file`, `pipe`, `tty`, `socket`, `device` or `directory`) and `isatty(fd)`, to tell `< file` from a pipe;
|
|
347
347
|
- `open(path, { read, write, append, create, exclusive, truncate })`, which returns a file with `read(position, length)`, `write(position, bytes)`, `size()`, `truncate(size)`, `sync()` and `close()`. Reads and writes go through the kernel's paged files at those positions, so a large file is never read whole;
|
|
348
348
|
- `fs`: `stat`, `lstat`, `exists`, `readdir`, `mkdir` (with parents, or, with `{ recursive: false }`, as Node's `fs.mkdir` without `recursive`: `EEXIST` for an existing entry and `ENOENT` for a missing parent, atomically among the processes of one kernel, so it can serve as a lock there, but not between two kernels on one origin, such as two tabs ([#519](https://github.com/ai-ecoverse/slicc-kernel/issues/519)); `sync.fs.mkdir` takes the same option), `rm` (recursive), `unlink`, `rename`, `symlink`, `readlink`, `statfs`, `chmod(path, mode)`, and `readFile`/`writeFile` for small files. `chmod` takes the permission bits (`0` to `0o7777`, `EINVAL` otherwise) and follows a symlink; as `chmod(2)`, only the file's owner or root may (`EPERM`), so a program can make a script it wrote runnable by path. `sync.fs.chmod` is the same, synchronously. `statfs(path)` answers `{ type, bsize, blocks, bfree, bavail, files, ffree, quota, usage }`: the fields of Node's `StatFs`, plus `quota` and `usage` in bytes (what jsh 0.17 reads). The values come from the same source as an Emscripten program's `statfs`/`df`: the driver of the mount the path is on, or, on the root file system, the origin's storage estimate (`navigator.storage.estimate()`). `bsize` is 4096, `bfree` and `bavail` are what's left of the quota, and `type`, `files` and `ffree` are 0, since the kernel doesn't know them. Where there's no estimate (the Node entry's root), every count is 0. A missing path is `ENOENT`. `sync.fs` has it too. `rm` removes a whole tree in one kernel call (on the OPFS root, one recursive removal and one metadata delete: 50,000 files take about 3 s in Chrome, where `rm -r` takes about 25 s) with the result of a file-by-file removal: symlinks inside the tree are removed, never followed; a tree holding a protected path is refused with `EROFS` and nothing is removed; a mount inside the tree is emptied and its mount point answers `EBUSY`, while the rest is removed; under `users`, what the user may remove is removed and the call then fails with the first `EACCES`/`EPERM`; files a process holds open stay readable to it;
|
|
349
|
-
- `/dev`, in `open`, `fs` and their `sync` forms, as a WASI or Emscripten program sees it, with the same modes and owners as under `users`: `null`, `zero`, `full`, `random`, `urandom` and `tty` (character devices, `0666`, root's), `stdin`, `stdout` and `stderr` (links to `/proc/self/fd/0`, `1`, `2`), `fd` (a link to `/proc/self/fd`, so `/dev/fd/N` is descriptor N) and the kernel's terminals `/dev/ttyN` and `/dev/pts/N` (with users on, owned by the session's user or the master's opener, `0620`). `/dev/shm` and `/dev/ptmx`
|
|
349
|
+
- `/dev`, in `open`, `fs` and their `sync` forms, as a WASI or Emscripten program sees it, with the same modes and owners as under `users`: `null`, `zero`, `full`, `random`, `urandom` and `tty` (character devices, `0666`, root's), `stdin`, `stdout` and `stderr` (links to `/proc/self/fd/0`, `1`, `2`), `fd` (a link to `/proc/self/fd`, so `/dev/fd/N` is descriptor N) and the kernel's terminals `/dev/ttyN` and `/dev/pts/N` (with users on, owned by the session's user or the master's opener, `0620`). `/dev/shm` is the kernel's shared tmpfs (see [Mounts](#mounts)), read and written like any other mount; `/dev/ptmx` isn't there. `stat` through `/dev/stdin`, `/dev/stdout` or `/dev/fd/N` describes the program's own descriptor (the file it is redirected to, a pipe, a terminal), `readlink` of `/dev/fd/N` names it (`pipe:[N]` and `socket:[N]` for a pipe or a socket), and a closed descriptor is `ENOENT`. Opening a device gives a kernel descriptor on it; opening `/dev/std*` or `/dev/fd/N` opens the file again with the flags asked for when the descriptor is on a file (as Linux reopens `/proc/self/fd/N`, so `append` appends and `truncate` truncates), and otherwise duplicates the descriptor; `/dev/tty` is the program's controlling terminal (`ENXIO` without one); and `/dev/ttyN` and `/dev/pts/N` are opened by name through the kernel, so a terminal that isn't the user's is `EACCES` under `enforce`. A file on anything but a regular file reads and writes as a stream (the position is ignored) and only in the directions it was opened for (`EBADF` otherwise), its `size()` is 0 and `truncate` is `EINVAL`. `readFile` reads a device or a descriptor to its end, up to 64 MiB, and fails with `EFBIG` past that (where Node's `readFile` throws `ERR_FS_FILE_TOO_LARGE`), so `readFile('/dev/zero')` fails instead of hanging, while a bounded `read` of it works. Other paths pay only a prefix check: they make the same kernel calls as before. Creating, removing or renaming names in `/dev` goes to the root file system as before;
|
|
350
350
|
- `/proc`, in `fs`, `open` and their `sync` forms, the same as a WASI program's (see [`/proc`](#proc)): `stat`, `lstat`, `exists`, `readdir`, `readlink` and `readFile` answer it, and `/proc/self` is the program's own pid. It's read-only, so every change in it is `EROFS`: `writeFile`, `mkdir`, `rm`, `unlink`, `chmod`, a `rename`, `link` or `symlink` into or out of it, and an `open` for writing, creating or truncating. A file opened in it is a snapshot read at the open, with no descriptor (`fd` is `-1`): its `read` and `size` answer from the snapshot until it's closed, and `write` and `truncate` are `EBADF`. Other paths pay only a prefix check;
|
|
351
351
|
- `signals`: `on(signal, handler)` catches a signal (by number or as `'SIGINT'`), `ignore(signal)` ignores it and `reset(signal)` restores its default action. A call blocked in a read or write is restarted after a handler runs; signals without a handler keep their default actions, so an uncaught `SIGINT` ends the program with 130 and `SIGKILL` always does;
|
|
352
352
|
- `sync`: `read`, `write`, `close`, `open` (whose files' methods are synchronous too) and `fs`, made synchronously, for code that cannot `await`, such as a compiled program's file system or a `readFileSync`. A synchronous call holds the worker until the kernel answers, so nothing else of the program runs meanwhile, timers and callbacks included; a signal that arrives during a blocked read or write runs its handler, and the call is then restarted. Descriptors are shared with the rest of the context, so a file opened with `sync.open` can be closed with `close`, and the other way around; `sync.run` and `sync.exit` are described next;
|
|
@@ -554,6 +554,8 @@ Directories are renamed with `FileSystemHandle.move()` where available, else by
|
|
|
554
554
|
|
|
555
555
|
`kernel.mount({ type, source, target, options })` mounts a file system on an existing directory. Every process sees it, Emscripten and WASI alike, and so does every attached client. `kernel.umount(target)` unmounts it, and fails with `EBUSY` while a process has a file open under it. `kernel.mounts()` lists the table, as does `/proc/mounts`. The Node entry and attached clients have the same three calls (client protocol 1.2).
|
|
556
556
|
|
|
557
|
+
`/dev/shm` is a tmpfs the kernel mounts at boot (source `shm`), mode `1777` and owned by root, so with `users` the sticky bit keeps one user from removing or renaming another's file there (`EPERM`). It's in `/proc/mounts`, but not in `kernel.mounts()`, which lists the mounts a caller made. Every process sees the same files, Emscripten (whose glue otherwise keeps a private `/dev/shm` in memory), WASI and JS programs alike, and they last until the kernel terminates, which is what musl's `shm_open` and `sem_open` need.
|
|
558
|
+
|
|
557
559
|
- **Built in:** `tmpfs`, which lives in memory until unmounted, in 1 MiB chunks; `fsa`, a folder the user picks (see [Removable media](#removable-media-fsa)); and `hostfs`, a folder the local proxy exports (see [Host folders](#host-folders-hostfs)).
|
|
558
560
|
- **Package drivers:** a package declares a type in `"slicc": { "filesystems": { "<type>": { "module": "<file>" } } }`, and the kernel starts that module in a worker of its own for each mount. The module is a single self-contained file: it can't import bare specifiers.
|
|
559
561
|
- Its default export receives `{ fetch }`, the kernel's network transport. It returns `{ handlers, capabilities }`.
|
|
@@ -883,6 +885,35 @@ The Biome, TypeScript, lefthook, Renovate and CI configuration comes from [slicc
|
|
|
883
885
|
|
|
884
886
|
Releases are cut by semantic-release on every push to `main`, in two jobs, and only on `main`. The first job runs on the bench pool, so it starts within a minute of the push, before `main` has moved on. It computes the version, tags the commit and creates the GitHub release, with `npmPublish: false`. A run that starts after `main` has moved skips; the run for the newer commit releases it. The second job runs on a GitHub-hosted runner, because npm's trusted publishing and provenance accept no self-hosted runner. It publishes the tagged commit with `tools/release-recover.mjs`. However long it waits for a runner, it publishes exactly the commit the run started on, so the provenance, the tag and the package's `gitHead` agree. For any version tagged at HEAD, `tools/release-recover.mjs` pushes the tag if origin lacks it, publishes the version if npm lacks it (with retries), and creates its GitHub release if that is missing too. So a failure after the version is tagged (a rejected push, a failed `npm publish`) doesn't strand it, and re-running the Release workflow completes it the same way.
|
|
885
887
|
|
|
888
|
+
### Certification (`cert-review`)
|
|
889
|
+
|
|
890
|
+
`.github/workflows/cert-review.yml` certifies every release from CI. It runs when the Release workflow succeeds on `main`. The version is the tag on the released commit. The delta is read from the newest earlier release whose `cert-review` status is `success` or `pending`, skipping failed and unreviewed ones (or `base.json`'s release, or the tag before), so a release whose review was cancelled or failed is covered by the next one, and the line says `covers <first>–<last>`. It also runs on `workflow_dispatch` with a `version`, optionally with `prev` and `base`, or with `planted` (a branch) and `pr` for proofs. A dispatch off `main`, or a planted one, is a dry run. Each release (its commit) has its own concurrency group, so no pending review is cancelled by a newer one. The tools are in `tools/cert-review/`:
|
|
891
|
+
|
|
892
|
+
- **plan:** works out the version, the previous tag, the commit and the base (`base.mjs`). The base is the newest earlier release whose `cert-review` commit status is `success`, or, until there is one, the release `base.json` names (the cert lane's last line). CI never reads the npm `certified` dist-tag.
|
|
893
|
+
- **delta** (`delta.mjs`): lists the pull requests merged between the two tags (their merge commits, plus `pr` for a planted branch). Each gets its conventional-commit kind (`fix`, `feat`, `perf` and `revert` are behaviour changes) and the integration test files and titles it adds or changes. The titles are read from the pull request's patch: the added `test(…)` lines and the hunk headers git puts above changed lines. Escapes are decoded as JavaScript does, and a title git cut short, or a template literal, matches as a prefix.
|
|
894
|
+
- **suite** (`suite.mjs`, `reporter.mjs`) runs on the bench pool, without secrets. It takes the tag's tree with its own dependencies and Chromium, and replaces `dist/` with the **published npm tarball** (for a planted branch, its own `npm pack`).
|
|
895
|
+
- The release job runs the release's whole `test/integration/` suite, then the delta's test files again on the **previous release's dist** (the NEGATIVE).
|
|
896
|
+
- The base job runs the base release's own suite on its own published dist.
|
|
897
|
+
- Each run uses the tree's own `npm test` command, with a reporter that records every test and subtest with its status and error.
|
|
898
|
+
- **kernel-compat** (`compat.mjs`) supplies the base-set and catalog evidence: homescoop's kernel-compat run for the release.
|
|
899
|
+
- Release runs share one group and intermediate ones are cancelled. When there is no finished run for the release commit, it dispatches `kernel-compat.yml` for that version (whose run is named `Kernel compat v<version>`) and waits up to 150 minutes.
|
|
900
|
+
- It reads the run's report artifact, and **re-runs every regressing package once** (a dispatch with that `set`).
|
|
901
|
+
- A regression that doesn't reproduce is a `compat-flake`; one that does is a `compat-regression`. A crash that reproduces and doesn't happen on the reference kernel is a `compat-crash`, which record FAILs in code.
|
|
902
|
+
- **review:** `findings.mjs` decides in code, per pull request:
|
|
903
|
+
- `negative` (a fix's test fails on the previous dist and passes on the release) or `passes` (a feature's test passes);
|
|
904
|
+
- otherwise a finding: `no-test`, `no-negative`, `fails-on-release`, `not-run`, `prev-not-run` or `no-titles`.
|
|
905
|
+
|
|
906
|
+
It also diffs the two suites test by test (`regressed`, `newFailing`, `fixed`, `stillFailing`, `removed`), flags a suite run that didn't finish (`suite-incomplete`), and adds the kernel-compat findings. Claude, on Bedrock with the key only in that step, then reads those artifacts and `instructions.txt` (which has the roll-forward FAIL list verbatim) and writes `verdict.json` and `comment.md`. Its tools are reading files, `gh pr view`, and `gh issue list`/`view`. `gh issue create` is allowed only for a release on `main`; otherwise, and for an issue in another repository, it lists the issue under `elsewhere` for the cert lane to file.
|
|
907
|
+
- **record** (`record.mjs`), in code, with read-only permissions:
|
|
908
|
+
- It re-downloads the tarball and checks `dist.integrity` (sha512) and the sha256, the SLSA v1 provenance (the tarball is its subject; `release.yml`, `refs/heads/main`, a GitHub-hosted builder; commit = `gitHead` = the tag), `npm audit signatures`, and that the commit is on `main`. A problem there, a `compat-crash`, or a planted build is a FAIL whatever the review says.
|
|
909
|
+
- It adds the **known issues as of the release**: every issue labelled `known-issue` (open or closed) and every one in `known.json`, unless it was fixed before the release. That holds when `known.json`'s `fixedIn` names an earlier or equal version, or the merged pull request that closed it is an ancestor of the tag, or it was closed (with no closing pull request) before the release commit. An issue closed as not planned or as a duplicate is dropped.
|
|
910
|
+
- The line is `certified @ai-ecoverse/slicc-kernel@<v> sha256=<…> (ci: <run>; known: …)`. Any test gaps from the findings (`no-test`, `no-negative`, `prev-not-run`, `no-titles`, `not-run`) are appended in code as `test gaps: #<pr> <kind>`, so a fix without a NEGATIVE is never certified silently.
|
|
911
|
+
- **publish** runs only for a release on `main`, never for a dry run. It puts the line into a `cert-review` commit status on the tag's commit and into the GitHub release's notes, between `<!-- cert-review -->` markers.
|
|
912
|
+
|
|
913
|
+
Moving the `certified` dist-tag stays manual. Automating it needs an npm token that may change dist-tags, in a step that re-hashes the tarball and tags only on a `certified` line.
|
|
914
|
+
|
|
915
|
+
Not part of the run yet: bios's real setup (the base-set sweep in bios, the upgrade boot, the sudo rows under `enforce`). It would plug in as one more job before review that runs slicc-bios's `npm run test:cert` with `CERT_COMMANDS` and the release's tarball, and leaves its results next to the others.
|
|
916
|
+
|
|
886
917
|
## Test fixtures
|
|
887
918
|
|
|
888
919
|
The WASIX fixtures of `test/integration/epoll.test.mjs` (#674), under `test/integration/fixtures/`, are committed with their sources, build scripts and built wasm.
|
package/dist/kernel-worker.js
CHANGED
|
@@ -5063,7 +5063,7 @@ function serveClient(port, host) {
|
|
|
5063
5063
|
case "umount":
|
|
5064
5064
|
return { result: await l.umountAs(clientCred(self), req.target).then(() => true) };
|
|
5065
5065
|
case "mounts":
|
|
5066
|
-
return { result: l.mounts.
|
|
5066
|
+
return { result: l.mounts.listed() };
|
|
5067
5067
|
case "dial":
|
|
5068
5068
|
return dialFor(l, req, watches);
|
|
5069
5069
|
case "serve-cdp":
|
|
@@ -8449,6 +8449,18 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
|
|
|
8449
8449
|
};
|
|
8450
8450
|
}
|
|
8451
8451
|
|
|
8452
|
+
// src/fs/dev-removal.ts
|
|
8453
|
+
var SHM = "/dev/shm";
|
|
8454
|
+
function inDevTree(path) {
|
|
8455
|
+
if (path.startsWith(`${SHM}/`)) return false;
|
|
8456
|
+
return path === "/dev" || path.startsWith("/dev/");
|
|
8457
|
+
}
|
|
8458
|
+
function devRemoval(path, cred) {
|
|
8459
|
+
if (!inDevTree(path)) return void 0;
|
|
8460
|
+
if (cred && cred.euid !== 0) return "EACCES";
|
|
8461
|
+
return path === "/dev" ? "EBUSY" : "EPERM";
|
|
8462
|
+
}
|
|
8463
|
+
|
|
8452
8464
|
// src/fs/fresh-modules.ts
|
|
8453
8465
|
var MAGIC = [0, 97, 115, 109];
|
|
8454
8466
|
var MAX_FRESH = 65536;
|
|
@@ -9293,12 +9305,18 @@ var DriverConnection = class _DriverConnection {
|
|
|
9293
9305
|
timeoutMs;
|
|
9294
9306
|
nextId = 0;
|
|
9295
9307
|
onFail;
|
|
9296
|
-
|
|
9308
|
+
idle;
|
|
9309
|
+
constructor(port, capabilities, waiting, timeoutMs, options) {
|
|
9297
9310
|
this.port = port;
|
|
9298
9311
|
this.capabilities = capabilities;
|
|
9299
9312
|
this.waiting = waiting;
|
|
9300
9313
|
this.timeoutMs = timeoutMs;
|
|
9301
|
-
this.onFail = onFail;
|
|
9314
|
+
this.onFail = options.onFail;
|
|
9315
|
+
this.idle = options.idle === true;
|
|
9316
|
+
if (this.idle) port.unref?.();
|
|
9317
|
+
}
|
|
9318
|
+
settled() {
|
|
9319
|
+
if (this.idle && this.waiting.size === 0) this.port.unref?.();
|
|
9302
9320
|
}
|
|
9303
9321
|
static async open(port, mount, options = {}) {
|
|
9304
9322
|
const timeoutMs = options.timeoutMs ?? DRIVER_TIMEOUT_MS;
|
|
@@ -9314,6 +9332,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
9314
9332
|
clearTimeout(call.timer);
|
|
9315
9333
|
if (data.errno) call.reject(errnoError(data.errno, data.message ?? data.errno));
|
|
9316
9334
|
else call.resolve(data.result);
|
|
9335
|
+
if (options.idle && waiting.size === 0) port.unref?.();
|
|
9317
9336
|
});
|
|
9318
9337
|
port.start?.();
|
|
9319
9338
|
port.postMessage({ hello: { protocol: DRIVER_PROTOCOL, mount } });
|
|
@@ -9332,7 +9351,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
9332
9351
|
const refused2 = driverVersionError(hello.protocol);
|
|
9333
9352
|
if (refused2) throw errnoError("EPROTO", refused2);
|
|
9334
9353
|
if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
|
|
9335
|
-
return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options
|
|
9354
|
+
return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options);
|
|
9336
9355
|
}
|
|
9337
9356
|
call(call, transfer = []) {
|
|
9338
9357
|
if (this.failure) return Promise.reject(this.failure);
|
|
@@ -9341,6 +9360,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
9341
9360
|
const timer = unref(
|
|
9342
9361
|
setTimeout(() => {
|
|
9343
9362
|
this.waiting.delete(id2);
|
|
9363
|
+
this.settled();
|
|
9344
9364
|
const error = errnoError(
|
|
9345
9365
|
"EIO",
|
|
9346
9366
|
`the driver did not answer ${call.op} within ${this.timeoutMs} ms`
|
|
@@ -9350,6 +9370,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
9350
9370
|
this.onFail?.(error);
|
|
9351
9371
|
}, this.timeoutMs)
|
|
9352
9372
|
);
|
|
9373
|
+
if (this.idle && this.waiting.size === 0) this.port.ref?.();
|
|
9353
9374
|
this.waiting.set(id2, { resolve: resolve2, reject, timer });
|
|
9354
9375
|
this.port.postMessage({ ...call, id: id2 }, transfer);
|
|
9355
9376
|
});
|
|
@@ -9362,6 +9383,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
9362
9383
|
call.reject(error);
|
|
9363
9384
|
}
|
|
9364
9385
|
this.waiting.clear();
|
|
9386
|
+
this.settled();
|
|
9365
9387
|
}
|
|
9366
9388
|
close() {
|
|
9367
9389
|
this.fail(errnoError("EIO", "the file system is unmounted"));
|
|
@@ -9626,6 +9648,7 @@ var MountTable = class {
|
|
|
9626
9648
|
devices = 0;
|
|
9627
9649
|
pending = /* @__PURE__ */ new Set();
|
|
9628
9650
|
notes = /* @__PURE__ */ new Map();
|
|
9651
|
+
system = /* @__PURE__ */ new Set();
|
|
9629
9652
|
deps;
|
|
9630
9653
|
constructor(deps) {
|
|
9631
9654
|
this.deps = deps;
|
|
@@ -9647,6 +9670,13 @@ var MountTable = class {
|
|
|
9647
9670
|
const noted = [...this.notes.values()].filter((note) => !this.mounts.has(note.target));
|
|
9648
9671
|
return [...mounted, ...noted.map((note) => ({ ...note, options: { ...note.options } }))];
|
|
9649
9672
|
}
|
|
9673
|
+
listed() {
|
|
9674
|
+
return this.list().filter((entry) => !this.system.has(entry.target));
|
|
9675
|
+
}
|
|
9676
|
+
async mountSystem(spec, fs) {
|
|
9677
|
+
await this.mount(spec, fs);
|
|
9678
|
+
this.system.add(normalizePath(spec.target));
|
|
9679
|
+
}
|
|
9650
9680
|
note(spec, state, error) {
|
|
9651
9681
|
const target = normalizePath(spec.target);
|
|
9652
9682
|
this.notes.set(target, {
|
|
@@ -9708,6 +9738,7 @@ var MountTable = class {
|
|
|
9708
9738
|
{ source: spec.source, options },
|
|
9709
9739
|
{
|
|
9710
9740
|
...this.deps.timeoutMs ? { timeoutMs: this.deps.timeoutMs } : {},
|
|
9741
|
+
...opened2.idle ? { idle: true } : {},
|
|
9711
9742
|
onInvalidate: (paths) => this.invalidated(target, paths),
|
|
9712
9743
|
onFail: (error) => {
|
|
9713
9744
|
if (mounted) this.crashed(mounted, error);
|
|
@@ -9753,6 +9784,7 @@ var MountTable = class {
|
|
|
9753
9784
|
if (!mount && this.notes.delete(at)) return;
|
|
9754
9785
|
if (!mount) throw errnoError("EINVAL", `${at} is not mounted`);
|
|
9755
9786
|
if (!detach && this.deps.busy(at)) throw errnoError("EBUSY", `${at} has open files`);
|
|
9787
|
+
this.system.delete(at);
|
|
9756
9788
|
this.notes.delete(at);
|
|
9757
9789
|
this.mounts.delete(at);
|
|
9758
9790
|
this.deps.reshaped?.();
|
|
@@ -10661,16 +10693,6 @@ var MountFs = class _MountFs {
|
|
|
10661
10693
|
}
|
|
10662
10694
|
};
|
|
10663
10695
|
|
|
10664
|
-
// src/fs/dev-removal.ts
|
|
10665
|
-
function inDevTree(path) {
|
|
10666
|
-
return path === "/dev" || path.startsWith("/dev/");
|
|
10667
|
-
}
|
|
10668
|
-
function devRemoval(path, cred) {
|
|
10669
|
-
if (!inDevTree(path)) return void 0;
|
|
10670
|
-
if (cred && cred.euid !== 0) return "EACCES";
|
|
10671
|
-
return path === "/dev" ? "EBUSY" : "EPERM";
|
|
10672
|
-
}
|
|
10673
|
-
|
|
10674
10696
|
// src/realm/sync-fs-token-registry.ts
|
|
10675
10697
|
var registry = /* @__PURE__ */ new Map();
|
|
10676
10698
|
function mintSyncFsToken(entry) {
|
|
@@ -12085,7 +12107,7 @@ var PATH_OPS = new Set(SYNC_FS_OPS);
|
|
|
12085
12107
|
var EARLY_WHOLE_FILE = 64 * 1024;
|
|
12086
12108
|
|
|
12087
12109
|
// src/version.ts
|
|
12088
|
-
var KERNEL_VERSION = true ? "1.99.
|
|
12110
|
+
var KERNEL_VERSION = true ? "1.99.2" : "0.0.0-dev";
|
|
12089
12111
|
var OS_RELEASE = "6.0.0-slicc";
|
|
12090
12112
|
function procVersion() {
|
|
12091
12113
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -22021,8 +22043,10 @@ var Launcher = class {
|
|
|
22021
22043
|
},
|
|
22022
22044
|
{ owned: true }
|
|
22023
22045
|
);
|
|
22046
|
+
if (spec.target === SHM) port2.unref?.();
|
|
22024
22047
|
return {
|
|
22025
22048
|
port: port1,
|
|
22049
|
+
...spec.target === SHM ? { idle: true } : {},
|
|
22026
22050
|
dispose: () => {
|
|
22027
22051
|
port1.close();
|
|
22028
22052
|
port2.close();
|
|
@@ -22123,6 +22147,11 @@ var Launcher = class {
|
|
|
22123
22147
|
this.pnpmStale();
|
|
22124
22148
|
return entry;
|
|
22125
22149
|
}
|
|
22150
|
+
async mountShm() {
|
|
22151
|
+
await this.base.mkdir(SHM, { recursive: true });
|
|
22152
|
+
await this.mounts.mountSystem({ type: "tmpfs", source: "shm", target: SHM }, this.fs);
|
|
22153
|
+
await this.fs.chmod(SHM, 1023);
|
|
22154
|
+
}
|
|
22126
22155
|
async ownTmp() {
|
|
22127
22156
|
const real = await this.realLayoutPath("/tmp");
|
|
22128
22157
|
const st = real === void 0 ? void 0 : await this.base.lstat(real).catch(() => void 0);
|
|
@@ -22279,6 +22308,7 @@ var Launcher = class {
|
|
|
22279
22308
|
await this.resolvePrefixes();
|
|
22280
22309
|
for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
|
|
22281
22310
|
if (this.owners) await this.ownTmp();
|
|
22311
|
+
await this.mountShm();
|
|
22282
22312
|
await this.pnpmReady();
|
|
22283
22313
|
await this.users.ensure();
|
|
22284
22314
|
if (this.owners) await this.stampLayout();
|
|
@@ -22827,7 +22857,7 @@ function serveKernel(port, deps) {
|
|
|
22827
22857
|
if (req.op === "mount") return l.mount(req.spec);
|
|
22828
22858
|
if (req.op === "umount") return l.umount(req.target);
|
|
22829
22859
|
if (req.op === "insert") return l.insert(req.target, req.handle, req.source);
|
|
22830
|
-
return l.mounts.
|
|
22860
|
+
return l.mounts.listed();
|
|
22831
22861
|
}
|
|
22832
22862
|
const attestOp = attestOps(port);
|
|
22833
22863
|
async function handle(req) {
|