@ai-ecoverse/slicc-kernel 1.133.4 → 1.133.5
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 +25 -1
- package/dist/kernel-worker.js +137 -4
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +82 -4
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +137 -4
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +82 -4
- package/dist/process-worker.js.map +4 -4
- package/dist/types/kernel/held-cwd.d.ts +65 -0
- package/dist/types/kernel/host.d.ts +9 -0
- package/dist/types/kernel/process.d.ts +8 -0
- package/dist/types/kernel/protocol.d.ts +7 -0
- package/dist/types/launcher.d.ts +5 -0
- package/dist/types/process/wasi/wasi-fds.d.ts +7 -0
- package/dist/types/process/wasi/wasi-host.d.ts +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -846,6 +846,24 @@ Programs mount and unmount with `mount(2)` and `umount2(2)`, so a `mount`/`umoun
|
|
|
846
846
|
- **Errors** are Linux's: `ENODEV` for an unknown type (or `hostfs` without a hook), `ENOENT` and `ENOTDIR` for the target, `EBUSY` for a target that is mounted already, and `EINVAL` for unmounting what is not a mount point.
|
|
847
847
|
- **`/proc/mounts`** escapes spaces and backslashes as Linux does, and adds `nomedium` or `failed` to the options of a mount in that state.
|
|
848
848
|
|
|
849
|
+
### The working directory
|
|
850
|
+
|
|
851
|
+
A process's working directory is the directory it changed into, not just its path ([#1164](https://github.com/ai-ecoverse/slicc-kernel/issues/1164)). The kernel records the directory's inode when a process starts and at every `chdir`. A forked child shares its parent's, and so does a spawned child unless the spawn changes directory.
|
|
852
|
+
- **When the directory is replaced:** if it's removed, or something else takes its name (`rm -r d; mkdir d`, `rm -r d; mv n d`, or `mv d x; mv n d`, from this kernel or from another tab on the same root), the process keeps the removed directory:
|
|
853
|
+
- a wasip1 program's `.` (its `.` preopen) can't be opened or listed (`ENOENT`, an empty listing for its preopen descriptor);
|
|
854
|
+
- its relative lookups and creates are `ENOENT`;
|
|
855
|
+
- `..` still reaches the parent.
|
|
856
|
+
- **Detection costs nothing in the steady state:**
|
|
857
|
+
- each held directory has a counter in memory the kernel shares with the process. It moves only when this kernel, another tab, or a `postFsNotice` reports a change at that directory or above it: the kernel's own watcher paths, and every path and tree of a peer change or notice.
|
|
858
|
+
- a process checks the counter, a memory read, before each relative lookup. Only after it moved does it ask the kernel once (`cwd-check`), and the kernel compares the inode at the path with the one it recorded.
|
|
859
|
+
- **The `rm` record:** so that `rm -r d; mkdir d` can be told apart, removing a directory leaves the same small record a rename leaves at the name it vacates (see [Metadata](#metadata)).
|
|
860
|
+
- **Limits:**
|
|
861
|
+
- a WASIX program's C library keeps the working directory as a string and turns relative paths into absolute ones before they reach the kernel. So a WASIX program still sees the new directory through relative paths, and `getcwd` still returns the path, until wasix-sysroot resolves relative paths against the working directory's descriptor ([#1164](https://github.com/ai-ecoverse/slicc-kernel/issues/1164) stays open for that).
|
|
862
|
+
- Emscripten and JS programs: [#1164](https://github.com/ai-ecoverse/slicc-kernel/issues/1164), next.
|
|
863
|
+
- a removal between the check and the lookup is a race, as with any path, and the lookup sees the new directory.
|
|
864
|
+
- on a `hostfs` folder the inode is the host's, which a host file system may reuse for the new directory (ext4 can; APFS doesn't). There the replacement isn't noticed.
|
|
865
|
+
- with more than 1023 distinct working directories held at once, the rest are not tracked.
|
|
866
|
+
|
|
849
867
|
### Exec
|
|
850
868
|
|
|
851
869
|
An exec'd program takes over the pid of the process it replaces, as on Linux: `getpid()` and `$$` in the new image, `/proc`, `ps`, `kill` and the parent's `waitpid` all agree on that pid, and the image's parent pid is the replaced process's. Its own children see that pid as their parent. A file removed or renamed over while programs hold it open keeps its bytes for them, as on Linux, whether the descriptors live in the kernel or a program opened the file in its own worker: every open, read-only ones included, is known to the kernel, and all holders share the one file (writes through one are seen through the others). The kernel does not copy it: as NFS does with its "silly rename", it moves the file into a hidden directory, `.slicc-unlinked/`, at the root of the mount it lives on (or of the root file system), and serves the holders' reads and writes from there (a worker's I/O on the old path while the path is gone). No file data is held in memory for it. The file is deleted when the last holder closes it or exits, and the directory goes as soon as it is empty. A file created at the path afterwards is the new file for everyone, also for the holder; a holder whose file another process renames (rather than removes) still uses the old path. A rename over a held file stays atomic for everyone else, as `rename(2)` is: moving the held file aside and the new one into its place are two steps, so a lookup of that name (`open`, `stat`, a read) that comes while they run waits for both, and one that started before them and missed the name in between looks again (also when it spelled the name through a symlinked parent, which the wait doesn't match by path: a miss is retried whenever any replacement finished meanwhile, up to three tries). Every open finds the old file or the new one, never no file (#903). A read through a held descriptor and the rename take the file's turn in order: lookups start to wait for the rename only once it has its turn on every held file it moves aside, so a read that came first finishes, and a rename over a file other processes keep reading never stalls them or itself (#980). A process that opens the file while the rename waits for its turn is a holder too: the rename looks for holders again once it has its turn, so that process keeps the old file as well, and a holder that closed meanwhile leaves nothing to move aside. The same holds for a directory that is removed or renamed over while programs hold files in it: the removal keeps looking for holders until nothing new appears, moving each held file aside, and lookups inside the directory wait for it. An open, a hand-over of a program's descriptor to the kernel (at a fork) or a hold that was under way while the directory went looks its path up again, so it gets the kept old file or ENOENT, never an empty file at the old name (#1013). An open of a path that is gone, with a handle the process no longer holds, is ENOENT rather than a descriptor for a missing file. The OPFS root does the same for its own two steps: a rename onto an existing name replaces the file with `move()` (or, in a browser without it, a copy into the name and a removal of the source), and a lookup that loses that race to a stale handle looks again. A `tmpfs` mount replaces the name in one step; on `fsa` and `hostfs` mounts it is the real file system's `rename`. Listings in the kernel leave `.slicc-unlinked` out, but on an `fsa` or `hostfs` mount it is visible on the real machine while a removed file is held (add `.slicc-unlinked/` to a `.gitignore` there). Each kernel names its entries after itself and holds a Web Lock for that name while it runs; when a mount is set up (and the root at start), the kernel removes the entries whose owner holds no lock, so a tab that died while holding a file leaves nothing behind for long, and entries of other live tabs on the same origin are left alone. Under the Node entry, which has no Web Locks, entries are named after the kernel's process id instead, and an entry is removed when that process is gone (`kill(pid, 0)` fails with `ESRCH`); a reused pid leaves the entry alone. Signals a program ignores stay ignored in the image it execs and in the children it spawns or forks, as POSIX has it, so `nohup cmd` and `trap '' TERM; exec cmd` keep `cmd` safe from them; caught signals go back to their default. The kernel holds what was handed down, so a new image that sets one of those signals back to the default with `signal(SIG_DFL)` still ignores it, until it installs a handler.
|
|
@@ -1087,7 +1105,7 @@ OPFS stores names, bytes, sizes and modification times, nothing else. Everything
|
|
|
1087
1105
|
|
|
1088
1106
|
- mode bits (`chmod`; a `chmod` to the mode a file or directory already has writes nothing and leaves its ctime as it was, which saves a sidecar write for every installer that sets the mode a file was made with);
|
|
1089
1107
|
- access and change times, and a modification time set explicitly with `utime` (it holds until the file is written again: any write or truncate moves it to the time of the write, also in Chrome, whose OPFS doesn't move a file's `lastModified` for writes through a sync access handle, so the kernel records the write's time itself, and it reaches the metadata store up to 100 ms after the write, so a tab closed inside that window keeps the old mtime on disk; the time follows a rename and is dropped with the file); a directory's times move whenever an entry is created, removed or renamed in it, as POSIX asks, and a time set explicitly after such a change keeps winning, also when the change was made in another kernel and its write lands later; an Emscripten program reads a directory's times again after any call that can wait on another process (a pipe or socket read, `wait`, `poll`, a sleep), so a program that waits for a child or a peer to fill a directory sees the new times (rsync's final directory times rely on it), while a walk without waits reads each directory once; `chown`/`fchown` leave the times alone (Emscripten stamps the setattr of a chown with the current time, which isn't taken as an mtime);
|
|
1090
|
-
- inode numbers that stay stable across renames. A file with no record has an inode derived from its path, and a rename keeps it on the file; the rename leaves a small record at the name it vacated with a fresh inode, so a file, symlink or directory made there later doesn't share the renamed one's ([#954](https://github.com/ai-ecoverse/slicc-kernel/issues/954)). An exclusive create doesn't count that record as a taken name, and the boot reconcile keeps it only while a live record still carries the inode it guards;
|
|
1108
|
+
- inode numbers that stay stable across renames. A file with no record has an inode derived from its path, and a rename keeps it on the file; the rename leaves a small record at the name it vacated with a fresh inode, so a file, symlink or directory made there later doesn't share the renamed one's ([#954](https://github.com/ai-ecoverse/slicc-kernel/issues/954)). Removing a directory leaves the same record, so a directory made at its name has a new inode, which is how a process's [working directory](#the-working-directory) notices the swap. The record becomes the new entry's own when something is created there; one with nothing at its name is pruned by the next boot that's alone on the root. An exclusive create doesn't count that record as a taken name, and the boot reconcile keeps it only while a live record still carries the inode it guards;
|
|
1091
1109
|
- symbolic links (`symlink`, `readlink`, `lstat`, and following them in paths), which exist only in the sidecar, with their own times: `touch -h`, `lutimes` and `utimensat` with `AT_SYMLINK_NOFOLLOW` set the link's, never the target's. A link's own mode can't be changed, as on Linux: `chmod -h`, `lchmod` and `fchmodat` with `AT_SYMLINK_NOFOLLOW` fail with `EOPNOTSUPP` (which `chmod -h` and `tar` ignore for links) and never follow it, and `open` with `O_PATH | O_NOFOLLOW` gives a descriptor for the link itself.
|
|
1092
1110
|
|
|
1093
1111
|
OPFS stays authoritative: an entry is consulted only for a path that exists in OPFS (symlinks only for paths that don't), so files written straight to OPFS, for example by the BIOS installer or a page, get the defaults: directories `0755`, files `0644`, and files under `node_modules/*/bin/` `0755`. A file deleted through the OPFS API disappears at once; its leftover entry is ignored and removed when the next kernel starts. Changes go to OPFS first and then to the sidecar, so a crash in between at worst loses the metadata of that one change. Each change is a read-modify-write inside one IndexedDB transaction, so two kernels (two tabs) on the same origin don't lose each other's updates.
|
|
@@ -1235,6 +1253,12 @@ Not part of the run yet: the rest of bios's setup (the `rest` shard, the upgrade
|
|
|
1235
1253
|
|
|
1236
1254
|
The WASIX fixtures of `test/integration/epoll.test.mjs` (#674), under `test/integration/fixtures/`, are committed with their sources, build scripts and built wasm.
|
|
1237
1255
|
|
|
1256
|
+
### `heldcwd`
|
|
1257
|
+
|
|
1258
|
+
`test/integration/fixtures/heldcwd`: a Rust wasm32-wasip1 program, built by `build.sh` with rustup's wasm32-wasip1 target, for the [working directory](#the-working-directory) rows (#1164).
|
|
1259
|
+
- `heldcwd`: lists `.`, waits for a line on stdin, then lists `.` again, tries a relative create and stat, and lists `..`.
|
|
1260
|
+
- `heldcwd loop N`: stats `old` N times, reads a line, then stats it N more times, for the count of `cwd-check` calls.
|
|
1261
|
+
|
|
1238
1262
|
### `tokiotest`
|
|
1239
1263
|
|
|
1240
1264
|
A small tokio program built for WASIX (`wasm32-wasmer-wasi`), the fixture for `test/integration/epoll.test.mjs` (#674). It runs on tokio's current-thread runtime, or on the multi-thread one (2 workers) with `--multi` first:
|
package/dist/kernel-worker.js
CHANGED
|
@@ -2307,6 +2307,9 @@ var OpfsFs = class {
|
|
|
2307
2307
|
}
|
|
2308
2308
|
}
|
|
2309
2309
|
await this.meta.remove([found.path]);
|
|
2310
|
+
if (found.handle?.kind === "directory") {
|
|
2311
|
+
await this.meta.update(found.path, (entry) => entry ?? vacatedAt(found.path));
|
|
2312
|
+
}
|
|
2310
2313
|
this.touch(Date.now(), parentOf3(found.path));
|
|
2311
2314
|
}
|
|
2312
2315
|
links() {
|
|
@@ -7092,7 +7095,7 @@ function satisfies(version, range2) {
|
|
|
7092
7095
|
}
|
|
7093
7096
|
|
|
7094
7097
|
// src/version.ts
|
|
7095
|
-
var KERNEL_VERSION = true ? "1.133.
|
|
7098
|
+
var KERNEL_VERSION = true ? "1.133.5" : "0.0.0-dev";
|
|
7096
7099
|
var OS_RELEASE = "6.0.0-slicc";
|
|
7097
7100
|
function procVersion() {
|
|
7098
7101
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -10785,6 +10788,88 @@ var FifoTable = class {
|
|
|
10785
10788
|
}
|
|
10786
10789
|
};
|
|
10787
10790
|
|
|
10791
|
+
// src/kernel/held-cwd.ts
|
|
10792
|
+
var CWD_SLOTS = 1024;
|
|
10793
|
+
var UNTRACKED = CWD_SLOTS - 1;
|
|
10794
|
+
var under4 = (path, changed) => changed === "/" || path === changed || path.startsWith(`${changed}/`);
|
|
10795
|
+
var CwdTable = class {
|
|
10796
|
+
sab = new SharedArrayBuffer(CWD_SLOTS * 4);
|
|
10797
|
+
view = new Int32Array(this.sab);
|
|
10798
|
+
held = /* @__PURE__ */ new Map();
|
|
10799
|
+
free = [];
|
|
10800
|
+
next = 0;
|
|
10801
|
+
checks = 0;
|
|
10802
|
+
inodeOf;
|
|
10803
|
+
constructor(inodeOf2) {
|
|
10804
|
+
this.inodeOf = inodeOf2;
|
|
10805
|
+
}
|
|
10806
|
+
async hold(path, from) {
|
|
10807
|
+
const shared = from === void 0 ? void 0 : this.held.get(from);
|
|
10808
|
+
if (shared && shared.path === path) return this.share(from);
|
|
10809
|
+
const ino = await this.inodeOf(path).catch(() => void 0);
|
|
10810
|
+
const slot2 = this.free.pop() ?? (this.next < UNTRACKED ? this.next++ : UNTRACKED);
|
|
10811
|
+
if (slot2 === UNTRACKED) return slot2;
|
|
10812
|
+
this.held.set(slot2, {
|
|
10813
|
+
path,
|
|
10814
|
+
ino,
|
|
10815
|
+
refs: 1,
|
|
10816
|
+
dead: false,
|
|
10817
|
+
verified: Atomics.load(this.view, slot2)
|
|
10818
|
+
});
|
|
10819
|
+
return slot2;
|
|
10820
|
+
}
|
|
10821
|
+
share(slot2) {
|
|
10822
|
+
const held = slot2 === void 0 ? void 0 : this.held.get(slot2);
|
|
10823
|
+
if (!held) return UNTRACKED;
|
|
10824
|
+
held.refs++;
|
|
10825
|
+
return slot2;
|
|
10826
|
+
}
|
|
10827
|
+
release(slot2) {
|
|
10828
|
+
const held = slot2 === void 0 ? void 0 : this.held.get(slot2);
|
|
10829
|
+
if (!held || --held.refs > 0) return;
|
|
10830
|
+
this.held.delete(slot2);
|
|
10831
|
+
Atomics.add(this.view, slot2, 1);
|
|
10832
|
+
this.free.push(slot2);
|
|
10833
|
+
}
|
|
10834
|
+
stateOf(slot2) {
|
|
10835
|
+
const held = this.held.get(slot2);
|
|
10836
|
+
return { seen: held?.verified ?? Atomics.load(this.view, slot2), dead: held?.dead === true };
|
|
10837
|
+
}
|
|
10838
|
+
touched(paths, trees = []) {
|
|
10839
|
+
if (this.held.size === 0) return;
|
|
10840
|
+
for (const [slot2, held] of this.held) {
|
|
10841
|
+
if (held.dead) continue;
|
|
10842
|
+
if (paths.some((p) => under4(held.path, p)) || trees.some((t) => under4(held.path, t))) {
|
|
10843
|
+
Atomics.add(this.view, slot2, 1);
|
|
10844
|
+
}
|
|
10845
|
+
}
|
|
10846
|
+
}
|
|
10847
|
+
async check(slot2) {
|
|
10848
|
+
this.checks++;
|
|
10849
|
+
const gen = Atomics.load(this.view, slot2);
|
|
10850
|
+
const held = this.held.get(slot2);
|
|
10851
|
+
if (!held) return { gen, alive: true };
|
|
10852
|
+
if (!held.dead) {
|
|
10853
|
+
const ino = await this.inodeOf(held.path).catch(() => null);
|
|
10854
|
+
held.dead = ino === null || held.ino !== void 0 && ino !== held.ino;
|
|
10855
|
+
held.verified = gen;
|
|
10856
|
+
}
|
|
10857
|
+
return { gen, alive: !held.dead };
|
|
10858
|
+
}
|
|
10859
|
+
};
|
|
10860
|
+
function heldInit(o) {
|
|
10861
|
+
if (!o.cwds || o.cwdSlot === void 0) return {};
|
|
10862
|
+
const state = o.cwdState ?? { seen: 0, dead: false };
|
|
10863
|
+
return { held: { sab: o.cwds, slot: o.cwdSlot, ...state } };
|
|
10864
|
+
}
|
|
10865
|
+
function heldHooks(o) {
|
|
10866
|
+
if (!o.cwdCheck || !o.cwdChdir) return {};
|
|
10867
|
+
return { cwdCheck: o.cwdCheck, cwdChdir: o.cwdChdir };
|
|
10868
|
+
}
|
|
10869
|
+
function inheritedCwd(chdir, parent) {
|
|
10870
|
+
return chdir ? void 0 : parent;
|
|
10871
|
+
}
|
|
10872
|
+
|
|
10788
10873
|
// src/fs/copy.ts
|
|
10789
10874
|
async function present2(fs, path) {
|
|
10790
10875
|
return fs.lstat(path).then(
|
|
@@ -21142,6 +21227,8 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
21142
21227
|
"fd-list",
|
|
21143
21228
|
"proc-alarm",
|
|
21144
21229
|
"proc-umask",
|
|
21230
|
+
"cwd-check",
|
|
21231
|
+
"cwd-chdir",
|
|
21145
21232
|
"proc-cred",
|
|
21146
21233
|
"proc-setcred",
|
|
21147
21234
|
"dl-log",
|
|
@@ -22467,6 +22554,14 @@ var WasmProcess = class {
|
|
|
22467
22554
|
return { ok: true, kind: "void" };
|
|
22468
22555
|
case "proc-umask":
|
|
22469
22556
|
return { ok: true, kind: "json", json: this.setUmask(req.mask) };
|
|
22557
|
+
case "cwd-check":
|
|
22558
|
+
return {
|
|
22559
|
+
ok: true,
|
|
22560
|
+
kind: "json",
|
|
22561
|
+
json: await this.options.cwdCheck?.() ?? { gen: 0, alive: true }
|
|
22562
|
+
};
|
|
22563
|
+
case "cwd-chdir":
|
|
22564
|
+
return { ok: true, kind: "json", json: await this.options.cwdChdir?.(req.path) ?? {} };
|
|
22470
22565
|
case "proc-cred":
|
|
22471
22566
|
return { ok: true, kind: "json", json: copyCred(this.cred) };
|
|
22472
22567
|
case "proc-setcred":
|
|
@@ -23016,6 +23111,7 @@ function processInit(opts, sab, umask, cred, lane) {
|
|
|
23016
23111
|
umask,
|
|
23017
23112
|
cred,
|
|
23018
23113
|
...opts.changes ? { changes: opts.changes } : {},
|
|
23114
|
+
...heldInit(opts),
|
|
23019
23115
|
...opts.owners ? { owners: true } : {},
|
|
23020
23116
|
...opts.links !== void 0 ? { links: true } : {},
|
|
23021
23117
|
...opts.ppid !== void 0 ? { ppid: opts.ppid } : {}
|
|
@@ -23089,6 +23185,7 @@ function processOptions(opts, header3, hooks, created4) {
|
|
|
23089
23185
|
...opts.identity ? { identity: opts.identity } : {},
|
|
23090
23186
|
...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
|
|
23091
23187
|
...opts.settling ? { settling: opts.settling } : {},
|
|
23188
|
+
...heldHooks(opts),
|
|
23092
23189
|
spawner: opts.spawner,
|
|
23093
23190
|
forker: opts.forker,
|
|
23094
23191
|
...opts.fork?.dl ? { dl: opts.fork.dl } : {},
|
|
@@ -23139,6 +23236,7 @@ function threadInit(opts, program, sab, cred, thread, main) {
|
|
|
23139
23236
|
owners: opts.owners === true,
|
|
23140
23237
|
...opts.links !== void 0 ? { links: true } : {},
|
|
23141
23238
|
...opts.changes ? { changes: opts.changes } : {},
|
|
23239
|
+
...heldInit(opts),
|
|
23142
23240
|
...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
|
|
23143
23241
|
thread,
|
|
23144
23242
|
signals: main
|
|
@@ -26770,6 +26868,11 @@ var Launcher = class {
|
|
|
26770
26868
|
hostname;
|
|
26771
26869
|
watchers = new FsWatchers();
|
|
26772
26870
|
changes = new FileChanges();
|
|
26871
|
+
cwds = new CwdTable(async (path) => {
|
|
26872
|
+
const { dev, ino } = await this.base.stat(path);
|
|
26873
|
+
return `${dev ?? 0}:${ino}`;
|
|
26874
|
+
});
|
|
26875
|
+
cwdSlots = /* @__PURE__ */ new Map();
|
|
26773
26876
|
pnpmHome;
|
|
26774
26877
|
pnpmChain;
|
|
26775
26878
|
pnpmMade;
|
|
@@ -26826,7 +26929,10 @@ var Launcher = class {
|
|
|
26826
26929
|
this.sealed.tickets.normalise = (path) => physicalPathAsync(this.base, path);
|
|
26827
26930
|
this.sealed.userOf = async (uid) => (await this.users.lookup(uid))?.name;
|
|
26828
26931
|
if (this.owners) options.fs.trackOwners?.();
|
|
26829
|
-
this.watchers.tap((paths) =>
|
|
26932
|
+
this.watchers.tap((paths) => {
|
|
26933
|
+
this.changes.publish(...paths);
|
|
26934
|
+
this.cwds.touched(paths);
|
|
26935
|
+
});
|
|
26830
26936
|
this.watchers.onReshape(() => {
|
|
26831
26937
|
for (const nodes of this.openFiles) nodes.reshaped();
|
|
26832
26938
|
});
|
|
@@ -26896,6 +27002,7 @@ var Launcher = class {
|
|
|
26896
27002
|
options.fs.onPeer?.(() => this.flushOpen());
|
|
26897
27003
|
options.fs.remoteChanges?.((change) => {
|
|
26898
27004
|
this.changes.flush();
|
|
27005
|
+
this.cwds.touched(change.paths, change.trees);
|
|
26899
27006
|
this.rewritten(change.paths, change.trees);
|
|
26900
27007
|
});
|
|
26901
27008
|
const holders = [this.held, this.openFiles];
|
|
@@ -27678,11 +27785,12 @@ var Launcher = class {
|
|
|
27678
27785
|
}
|
|
27679
27786
|
const done5 = this.heldUrl(program);
|
|
27680
27787
|
try {
|
|
27788
|
+
const cwdSlot = await this.cwds.hold(cwd, req.cwdFrom);
|
|
27681
27789
|
const handle = await this.admitted(
|
|
27682
27790
|
program.abi !== "js",
|
|
27683
27791
|
req.fds,
|
|
27684
|
-
(fds, quietOom) => this.start({ ...req, fds, quietOom, cwd, program, argv0: target.argv0, args, env })
|
|
27685
|
-
);
|
|
27792
|
+
(fds, quietOom) => this.start({ ...req, fds, quietOom, cwd, program, argv0: target.argv0, args, env, cwdSlot })
|
|
27793
|
+
).finally(() => this.cwds.release(cwdSlot));
|
|
27686
27794
|
void handle.exited.finally(done5);
|
|
27687
27795
|
return handle;
|
|
27688
27796
|
} catch (err) {
|
|
@@ -27769,6 +27877,8 @@ var Launcher = class {
|
|
|
27769
27877
|
throw new SpawnError("ESRCH");
|
|
27770
27878
|
}
|
|
27771
27879
|
const pid = req.pid ?? this.nextPid++;
|
|
27880
|
+
const cwdSlot = this.cwds.share(req.cwdSlot);
|
|
27881
|
+
this.cwdSlots.set(pid, cwdSlot);
|
|
27772
27882
|
const scrubs = this.treeScrubs(req);
|
|
27773
27883
|
const stack = this.stackFor(req.tag);
|
|
27774
27884
|
const terminal = req.fds.stdioTerminal();
|
|
@@ -27803,6 +27913,7 @@ var Launcher = class {
|
|
|
27803
27913
|
...this.dac ? { dac: this.dac } : {},
|
|
27804
27914
|
program: req.program,
|
|
27805
27915
|
changes: this.changes.sab,
|
|
27916
|
+
...this.heldFor(pid, cwdSlot),
|
|
27806
27917
|
argv0: req.argv0,
|
|
27807
27918
|
args: req.args,
|
|
27808
27919
|
env: req.env,
|
|
@@ -27877,6 +27988,8 @@ var Launcher = class {
|
|
|
27877
27988
|
(state) => state === "stopped" ? this.stopped.add(pid) : this.stopped.delete(pid)
|
|
27878
27989
|
);
|
|
27879
27990
|
void handle.exited.then(() => {
|
|
27991
|
+
this.cwds.release(this.cwdSlots.get(pid));
|
|
27992
|
+
this.cwdSlots.delete(pid);
|
|
27880
27993
|
this.freshModules.forget(pid);
|
|
27881
27994
|
const spent = handle.times?.();
|
|
27882
27995
|
if (spent) this.spent.set(pid, spent);
|
|
@@ -28108,6 +28221,7 @@ var Launcher = class {
|
|
|
28108
28221
|
const handle = await this.launch(planned, {
|
|
28109
28222
|
...deciding ? { decided: deciding.promise } : {},
|
|
28110
28223
|
cwd: req.cwd,
|
|
28224
|
+
cwdFrom: inheritedCwd(req.chdir, this.cwdSlots.get(ppid)),
|
|
28111
28225
|
...redeemed,
|
|
28112
28226
|
fds,
|
|
28113
28227
|
report,
|
|
@@ -28169,6 +28283,24 @@ var Launcher = class {
|
|
|
28169
28283
|
for (const resolve2 of waiting) resolve2(true);
|
|
28170
28284
|
}, 0);
|
|
28171
28285
|
}
|
|
28286
|
+
heldFor(pid, slot2) {
|
|
28287
|
+
return {
|
|
28288
|
+
cwds: this.cwds.sab,
|
|
28289
|
+
cwdSlot: slot2,
|
|
28290
|
+
cwdState: this.cwds.stateOf(slot2),
|
|
28291
|
+
cwdCheck: async () => {
|
|
28292
|
+
const current = this.cwdSlots.get(pid);
|
|
28293
|
+
return { ...await this.cwds.check(current), slot: current };
|
|
28294
|
+
},
|
|
28295
|
+
cwdChdir: (path) => this.chdirHeld(pid, path)
|
|
28296
|
+
};
|
|
28297
|
+
}
|
|
28298
|
+
async chdirHeld(pid, path) {
|
|
28299
|
+
this.cwds.release(this.cwdSlots.get(pid));
|
|
28300
|
+
const slot2 = await this.cwds.hold(path);
|
|
28301
|
+
this.cwdSlots.set(pid, slot2);
|
|
28302
|
+
return { slot: slot2, ...this.cwds.stateOf(slot2) };
|
|
28303
|
+
}
|
|
28172
28304
|
forker(ppid, parent) {
|
|
28173
28305
|
const { exec: _exec, pgid: _pgid, group: _group, pid: _pid, scrub: _scrub, ...image } = parent;
|
|
28174
28306
|
return async (state, given) => {
|
|
@@ -28176,6 +28308,7 @@ var Launcher = class {
|
|
|
28176
28308
|
...image,
|
|
28177
28309
|
quietOom,
|
|
28178
28310
|
cwd: state.cwd ?? parent.cwd,
|
|
28311
|
+
cwdSlot: this.cwdSlots.get(ppid),
|
|
28179
28312
|
fds,
|
|
28180
28313
|
ppid,
|
|
28181
28314
|
fork: state,
|