@ai-ecoverse/slicc-kernel 1.20.1 → 1.21.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 +16 -0
- package/dist/kernel-worker.js +185 -97
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +9 -3
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +185 -97
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +9 -3
- package/dist/process-worker.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -217,6 +217,20 @@ Directories are renamed with `FileSystemHandle.move()` where available, else by
|
|
|
217
217
|
- `options.ro` makes any mount read-only: writes fail with `EROFS`;
|
|
218
218
|
- a driver that crashes or doesn't answer within 30 s makes its mount's calls fail with `EIO`, and the mount is listed as `failed`.
|
|
219
219
|
|
|
220
|
+
### `/etc/fstab`
|
|
221
|
+
|
|
222
|
+
At boot the kernel mounts what `/etc/fstab` lists, in fstab(5) format: `source target type options` (the dump and pass fields are ignored), with `#` comments and `\040` for a space in a field. Options are those of `mount -o`, and lines with `noauto` are left out.
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
none /mnt/scratch tmpfs maxfile=1G 0 0
|
|
226
|
+
fsa:<id> /mnt/folder fsa rw 0 0
|
|
227
|
+
project /mnt/project hostfs ro 0 0
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
- **Boot never waits for it:** the lines are mounted in the background once the kernel has started, all at once, so a slow source holds up nothing.
|
|
231
|
+
- **Retries:** a line that fails is tried again after 1, 4 and 16 seconds (a driver package installed meanwhile, a mount point created later), except when it cannot succeed (`EINVAL`, `EBUSY`, `EPERM`, `EACCES`). `terminate()` stops the retries.
|
|
232
|
+
- **No user involvement:** an `fsa` line names a drive by its id: it comes up with the folder it had while its permission holds, and otherwise with no medium, asking the page for the folder through `onMountPending`, as any `fsa` mount does. With `media: false` the drive finds its folder only within the same kernel. With the default `media`, an `fsa` line reads its folder handle back from IndexedDB at boot, which crashes Chrome in an Incognito or other off-the-record profile ([#95](https://github.com/ai-ecoverse/slicc-kernel/issues/95)); a page cannot reliably tell it runs in one, so an embedder that cannot rule it out passes `media: false`, which keeps `fsa` lines in `/etc/fstab` safe. A `hostfs` line waits only for its first connection, and comes up with no medium if the proxy is not there yet.
|
|
233
|
+
|
|
220
234
|
### Removable media: `fsa`
|
|
221
235
|
|
|
222
236
|
An `fsa` mount is a drive for a local folder, from the File System Access API, and the folder is its medium.
|
|
@@ -321,6 +335,8 @@ Unit tests live in `test/unit/`, which stays out of git. They run real wasm bash
|
|
|
321
335
|
|
|
322
336
|
The Biome, TypeScript, lefthook, Renovate and CI configuration comes from [slicc-shared-web](https://github.com/ai-ecoverse/slicc-shared-web), which also provides the `slicc-lint-comments` (no comments anywhere), `slicc-no-unit-tests` (no unit tests in git) and `slicc-diff-cover` (100% coverage of changed lines) commands that `npm run lint` and the pre-commit hook use.
|
|
323
337
|
|
|
338
|
+
`tools/big-file.mjs [bytes]` is a manual check, not part of any suite. It writes a file of that size (64 MiB by default) through a tmpfs mount with the unit-test launcher, reads it back, and prints the peak RSS and its ratio to the file size. A whole file is still held in memory several times over (#86), so a 1.1 GiB file peaked at 18 GB of RSS. **Do not run it past 1 GiB on a workstation.** It refuses anything over 256 MiB unless `SLICC_BIG_FILE_I_HAVE_THE_RAM=1` is set. Measure a small file and extrapolate instead.
|
|
339
|
+
|
|
324
340
|
Releases are cut by semantic-release on every push to `main`. A failure after the version is tagged (a rejected push, a failed `npm publish`) doesn't strand that version: `tools/release-recover.mjs` runs next. For any version tagged at HEAD, it first pushes the tag if origin lacks it, then publishes the version if npm lacks it, with retries, and creates its GitHub release if that is missing too. Re-running the Release workflow completes such a version the same way.
|
|
325
341
|
|
|
326
342
|
## License
|
package/dist/kernel-worker.js
CHANGED
|
@@ -748,7 +748,7 @@ var KernelSocket = class _KernelSocket {
|
|
|
748
748
|
const own = this.changes.wait(signal);
|
|
749
749
|
if (this.state !== "connected" || !this.link) return own;
|
|
750
750
|
const waits = [own, this.link.rx.changed(signal), this.link.tx.changed(signal)];
|
|
751
|
-
for (const
|
|
751
|
+
for (const wait2 of waits) wait2.catch(() => void 0);
|
|
752
752
|
return pipeCall(() => Promise.race(waits));
|
|
753
753
|
}
|
|
754
754
|
bind(addr) {
|
|
@@ -4672,6 +4672,13 @@ var SAB_I_PUB = 7;
|
|
|
4672
4672
|
var SAB_I_SIGNALS = 8;
|
|
4673
4673
|
var SAB_I_TIMERS = 9;
|
|
4674
4674
|
var SAB_I_MEMORY = 10;
|
|
4675
|
+
var SAB_I_TOTAL_HI = 11;
|
|
4676
|
+
var SAB_I_OFFSET_HI = 12;
|
|
4677
|
+
var U32 = 2 ** 32;
|
|
4678
|
+
function storeU53(header2, lo, hi, value) {
|
|
4679
|
+
Atomics.store(header2, hi, Math.floor(value / U32));
|
|
4680
|
+
Atomics.store(header2, lo, value % U32 | 0);
|
|
4681
|
+
}
|
|
4675
4682
|
var SAB_STATE_READY = 2;
|
|
4676
4683
|
var SAB_STATUS_BYTES = 1;
|
|
4677
4684
|
var SAB_STATUS_JSON = 2;
|
|
@@ -4731,9 +4738,9 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
4731
4738
|
const chunk2 = Math.min(window.byteLength, entry.payload.byteLength - offset);
|
|
4732
4739
|
if (chunk2 > 0) window.set(entry.payload.subarray(offset, offset + chunk2));
|
|
4733
4740
|
Atomics.store(header2, SAB_I_STATUS, entry.status);
|
|
4734
|
-
|
|
4741
|
+
storeU53(header2, SAB_I_TOTAL, SAB_I_TOTAL_HI, entry.payload.byteLength);
|
|
4735
4742
|
Atomics.store(header2, SAB_I_CHUNK, chunk2);
|
|
4736
|
-
|
|
4743
|
+
storeU53(header2, SAB_I_OFFSET, SAB_I_OFFSET_HI, offset);
|
|
4737
4744
|
Atomics.store(header2, SAB_I_SEQ, id);
|
|
4738
4745
|
Atomics.store(header2, SAB_I_STATE, SAB_STATE_READY);
|
|
4739
4746
|
Atomics.add(header2, SAB_I_PUB, 1);
|
|
@@ -7201,10 +7208,164 @@ function removableMedium(onLost, open = (handle) => new OpfsFs(handle)) {
|
|
|
7201
7208
|
};
|
|
7202
7209
|
}
|
|
7203
7210
|
|
|
7211
|
+
// src/mount/syscall.ts
|
|
7212
|
+
var MS_RDONLY = 1;
|
|
7213
|
+
var MS_MGC_MSK = 4294901760;
|
|
7214
|
+
var MS_MGC_VAL = 3236757504;
|
|
7215
|
+
var IGNORED_FLAGS = 2 | 4 | 8 | 16 | 128 | 1024 | 2048 | 32768 | 1 << 21 | 1 << 24 | 1 << 25;
|
|
7216
|
+
var MNT_FORCE = 1;
|
|
7217
|
+
var MNT_DETACH = 2;
|
|
7218
|
+
var UMOUNT_NOFOLLOW = 8;
|
|
7219
|
+
var GENERIC = /* @__PURE__ */ new Set([
|
|
7220
|
+
"defaults",
|
|
7221
|
+
"async",
|
|
7222
|
+
"sync",
|
|
7223
|
+
"dirsync",
|
|
7224
|
+
"atime",
|
|
7225
|
+
"noatime",
|
|
7226
|
+
"diratime",
|
|
7227
|
+
"nodiratime",
|
|
7228
|
+
"relatime",
|
|
7229
|
+
"norelatime",
|
|
7230
|
+
"strictatime",
|
|
7231
|
+
"nostrictatime",
|
|
7232
|
+
"lazytime",
|
|
7233
|
+
"nolazytime",
|
|
7234
|
+
"suid",
|
|
7235
|
+
"nosuid",
|
|
7236
|
+
"dev",
|
|
7237
|
+
"nodev",
|
|
7238
|
+
"exec",
|
|
7239
|
+
"noexec",
|
|
7240
|
+
"auto",
|
|
7241
|
+
"noauto",
|
|
7242
|
+
"user",
|
|
7243
|
+
"nouser",
|
|
7244
|
+
"users",
|
|
7245
|
+
"nofail",
|
|
7246
|
+
"_netdev",
|
|
7247
|
+
"silent",
|
|
7248
|
+
"loud"
|
|
7249
|
+
]);
|
|
7250
|
+
function parseMountOptions(data) {
|
|
7251
|
+
const options = {};
|
|
7252
|
+
for (const raw of data.split(",")) {
|
|
7253
|
+
const item = raw.trim();
|
|
7254
|
+
if (!item || GENERIC.has(item)) continue;
|
|
7255
|
+
if (item === "rw") {
|
|
7256
|
+
delete options.ro;
|
|
7257
|
+
continue;
|
|
7258
|
+
}
|
|
7259
|
+
const eq = item.indexOf("=");
|
|
7260
|
+
if (eq === 0) throw errnoError("EINVAL", `bad mount option ${item}`);
|
|
7261
|
+
if (eq < 0) options[item] = "";
|
|
7262
|
+
else options[item.slice(0, eq)] = item.slice(eq + 1);
|
|
7263
|
+
}
|
|
7264
|
+
if (options.maxfile !== void 0) parseSize(options.maxfile);
|
|
7265
|
+
return options;
|
|
7266
|
+
}
|
|
7267
|
+
function refusedTarget(target) {
|
|
7268
|
+
const at = normalizePath(target);
|
|
7269
|
+
const kernel = at === "/" || [`/proc`, `/dev`].some((k) => at === k || at.startsWith(`${k}/`));
|
|
7270
|
+
if (kernel) throw errnoError("EBUSY", `${at} belongs to the kernel`);
|
|
7271
|
+
}
|
|
7272
|
+
function absolute(target) {
|
|
7273
|
+
if (!target) throw errnoError("ENOENT", "no mount point");
|
|
7274
|
+
if (!target.startsWith("/")) throw errnoError("EINVAL", `not an absolute path: ${target}`);
|
|
7275
|
+
return normalizePath(target);
|
|
7276
|
+
}
|
|
7277
|
+
function mountCall(call) {
|
|
7278
|
+
let flags = call.flags >>> 0;
|
|
7279
|
+
if ((flags & MS_MGC_MSK) >>> 0 === MS_MGC_VAL) flags &= ~MS_MGC_MSK;
|
|
7280
|
+
if (flags & ~(MS_RDONLY | IGNORED_FLAGS)) {
|
|
7281
|
+
throw errnoError("EINVAL", `unsupported mount flags ${flags.toString(16)}`);
|
|
7282
|
+
}
|
|
7283
|
+
if (!call.type) throw errnoError("EINVAL", "no file system type");
|
|
7284
|
+
const target = absolute(call.target);
|
|
7285
|
+
refusedTarget(target);
|
|
7286
|
+
const options = parseMountOptions(call.data);
|
|
7287
|
+
if (flags & MS_RDONLY) options.ro = "";
|
|
7288
|
+
return { type: call.type, source: call.source || "none", target, options };
|
|
7289
|
+
}
|
|
7290
|
+
function umountCall(target, flags) {
|
|
7291
|
+
const known = MNT_FORCE | MNT_DETACH | UMOUNT_NOFOLLOW;
|
|
7292
|
+
if (flags >>> 0 & ~known) throw errnoError("EINVAL", `unsupported umount flags ${flags}`);
|
|
7293
|
+
const at = absolute(target);
|
|
7294
|
+
refusedTarget(at);
|
|
7295
|
+
return { target: at, detach: (flags & (MNT_FORCE | MNT_DETACH)) !== 0 };
|
|
7296
|
+
}
|
|
7297
|
+
|
|
7298
|
+
// src/mount/fstab.ts
|
|
7299
|
+
var FSTAB_PATH = "/etc/fstab";
|
|
7300
|
+
var FSTAB_RETRIES = [1e3, 4e3, 16e3];
|
|
7301
|
+
var FINAL = /* @__PURE__ */ new Set(["EINVAL", "EBUSY", "EPERM", "EACCES"]);
|
|
7302
|
+
function unescape(field) {
|
|
7303
|
+
return field.replace(
|
|
7304
|
+
/\\([0-7]{3})/g,
|
|
7305
|
+
(_, octal) => String.fromCharCode(Number.parseInt(octal, 8))
|
|
7306
|
+
);
|
|
7307
|
+
}
|
|
7308
|
+
function parseFstab(text) {
|
|
7309
|
+
const lines = [];
|
|
7310
|
+
text.split("\n").forEach((raw, index) => {
|
|
7311
|
+
const content = raw.replace(/#.*$/, "").trim();
|
|
7312
|
+
if (!content) return;
|
|
7313
|
+
const [source, target, type, options = "defaults"] = content.split(/\s+/).map(unescape);
|
|
7314
|
+
if (source === void 0 || target === void 0 || type === void 0) return;
|
|
7315
|
+
if (options.split(",").includes("noauto")) return;
|
|
7316
|
+
const spec = { source, target, type, options: {} };
|
|
7317
|
+
try {
|
|
7318
|
+
lines.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
|
|
7319
|
+
} catch (err) {
|
|
7320
|
+
lines.push({ line: index + 1, spec, invalid: err });
|
|
7321
|
+
}
|
|
7322
|
+
});
|
|
7323
|
+
return lines;
|
|
7324
|
+
}
|
|
7325
|
+
function codeOf3(err) {
|
|
7326
|
+
const code = err?.code;
|
|
7327
|
+
return typeof code === "string" ? code : "EIO";
|
|
7328
|
+
}
|
|
7329
|
+
function wait(ms, signal) {
|
|
7330
|
+
if (signal.aborted) return Promise.resolve();
|
|
7331
|
+
return new Promise((resolve) => {
|
|
7332
|
+
const done4 = () => {
|
|
7333
|
+
clearTimeout(timer);
|
|
7334
|
+
signal.removeEventListener("abort", done4);
|
|
7335
|
+
resolve();
|
|
7336
|
+
};
|
|
7337
|
+
const timer = setTimeout(done4, ms);
|
|
7338
|
+
signal.addEventListener("abort", done4, { once: true });
|
|
7339
|
+
});
|
|
7340
|
+
}
|
|
7341
|
+
async function mountFstab(lines, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
7342
|
+
}) {
|
|
7343
|
+
return Promise.all(
|
|
7344
|
+
lines.map(async ({ line, spec, invalid }) => {
|
|
7345
|
+
let failed = invalid;
|
|
7346
|
+
for (let attempt = 0; !invalid && attempt <= retries.length && !signal.aborted; attempt++) {
|
|
7347
|
+
try {
|
|
7348
|
+
const entry = await mount(spec);
|
|
7349
|
+
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
7350
|
+
discard(entry);
|
|
7351
|
+
failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
|
|
7352
|
+
break;
|
|
7353
|
+
} catch (err) {
|
|
7354
|
+
failed = err;
|
|
7355
|
+
if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
|
|
7356
|
+
await wait(retries[attempt], signal);
|
|
7357
|
+
}
|
|
7358
|
+
}
|
|
7359
|
+
const code = codeOf3(failed);
|
|
7360
|
+
return { line, target: spec.target, code, error: String(failed?.message ?? code) };
|
|
7361
|
+
})
|
|
7362
|
+
);
|
|
7363
|
+
}
|
|
7364
|
+
|
|
7204
7365
|
// src/mount/hostfs.ts
|
|
7205
7366
|
var HOSTFS_IO = 16 * 1024 * 1024;
|
|
7206
|
-
var
|
|
7207
|
-
var unreachable = (err) =>
|
|
7367
|
+
var codeOf4 = (err) => err?.code;
|
|
7368
|
+
var unreachable = (err) => codeOf4(err) === "ENOTCONN";
|
|
7208
7369
|
function rel(path) {
|
|
7209
7370
|
return path.replace(/^\/+/, "");
|
|
7210
7371
|
}
|
|
@@ -7339,7 +7500,7 @@ function hostfsHandlers(current) {
|
|
|
7339
7500
|
try {
|
|
7340
7501
|
return { opened: await opening(), restore: void 0 };
|
|
7341
7502
|
} catch (err) {
|
|
7342
|
-
if (!flags.write ||
|
|
7503
|
+
if (!flags.write || codeOf4(err) !== "EACCES") throw err;
|
|
7343
7504
|
const mode = (await client.call("stat", { path: rel(path) })).mode ?? 0;
|
|
7344
7505
|
if (mode & 128) throw err;
|
|
7345
7506
|
const restore = { path: rel(path), mode };
|
|
@@ -7428,7 +7589,7 @@ function keepWatching(loop, signal) {
|
|
|
7428
7589
|
const uncap = () => clearTimeout(capped);
|
|
7429
7590
|
void ready2.promise.then(uncap);
|
|
7430
7591
|
signal.addEventListener("abort", uncap, { once: true });
|
|
7431
|
-
let
|
|
7592
|
+
let wait2 = first;
|
|
7432
7593
|
let up = false;
|
|
7433
7594
|
let current;
|
|
7434
7595
|
async function session() {
|
|
@@ -7473,7 +7634,7 @@ function keepWatching(loop, signal) {
|
|
|
7473
7634
|
const opened2 = await session();
|
|
7474
7635
|
if (signal.aborted) return;
|
|
7475
7636
|
if (opened2 && Date.now() - began >= first) {
|
|
7476
|
-
|
|
7637
|
+
wait2 = first;
|
|
7477
7638
|
continue;
|
|
7478
7639
|
}
|
|
7479
7640
|
if (!opened2 && up) {
|
|
@@ -7481,8 +7642,8 @@ function keepWatching(loop, signal) {
|
|
|
7481
7642
|
loop.down();
|
|
7482
7643
|
}
|
|
7483
7644
|
ready2.resolve();
|
|
7484
|
-
await sleep(
|
|
7485
|
-
|
|
7645
|
+
await sleep(wait2, signal);
|
|
7646
|
+
wait2 = Math.min(wait2 * 2, most);
|
|
7486
7647
|
}
|
|
7487
7648
|
})();
|
|
7488
7649
|
return { ready: ready2.promise, kick: () => current?.abort() };
|
|
@@ -7537,93 +7698,6 @@ async function openHostfs(spec, hook, fetch2, timing = {}) {
|
|
|
7537
7698
|
};
|
|
7538
7699
|
}
|
|
7539
7700
|
|
|
7540
|
-
// src/mount/syscall.ts
|
|
7541
|
-
var MS_RDONLY = 1;
|
|
7542
|
-
var MS_MGC_MSK = 4294901760;
|
|
7543
|
-
var MS_MGC_VAL = 3236757504;
|
|
7544
|
-
var IGNORED_FLAGS = 2 | 4 | 8 | 16 | 128 | 1024 | 2048 | 32768 | 1 << 21 | 1 << 24 | 1 << 25;
|
|
7545
|
-
var MNT_FORCE = 1;
|
|
7546
|
-
var MNT_DETACH = 2;
|
|
7547
|
-
var UMOUNT_NOFOLLOW = 8;
|
|
7548
|
-
var GENERIC = /* @__PURE__ */ new Set([
|
|
7549
|
-
"defaults",
|
|
7550
|
-
"async",
|
|
7551
|
-
"sync",
|
|
7552
|
-
"dirsync",
|
|
7553
|
-
"atime",
|
|
7554
|
-
"noatime",
|
|
7555
|
-
"diratime",
|
|
7556
|
-
"nodiratime",
|
|
7557
|
-
"relatime",
|
|
7558
|
-
"norelatime",
|
|
7559
|
-
"strictatime",
|
|
7560
|
-
"nostrictatime",
|
|
7561
|
-
"lazytime",
|
|
7562
|
-
"nolazytime",
|
|
7563
|
-
"suid",
|
|
7564
|
-
"nosuid",
|
|
7565
|
-
"dev",
|
|
7566
|
-
"nodev",
|
|
7567
|
-
"exec",
|
|
7568
|
-
"noexec",
|
|
7569
|
-
"auto",
|
|
7570
|
-
"noauto",
|
|
7571
|
-
"user",
|
|
7572
|
-
"nouser",
|
|
7573
|
-
"users",
|
|
7574
|
-
"nofail",
|
|
7575
|
-
"_netdev",
|
|
7576
|
-
"silent",
|
|
7577
|
-
"loud"
|
|
7578
|
-
]);
|
|
7579
|
-
function parseMountOptions(data) {
|
|
7580
|
-
const options = {};
|
|
7581
|
-
for (const raw of data.split(",")) {
|
|
7582
|
-
const item = raw.trim();
|
|
7583
|
-
if (!item || GENERIC.has(item)) continue;
|
|
7584
|
-
if (item === "rw") {
|
|
7585
|
-
delete options.ro;
|
|
7586
|
-
continue;
|
|
7587
|
-
}
|
|
7588
|
-
const eq = item.indexOf("=");
|
|
7589
|
-
if (eq === 0) throw errnoError("EINVAL", `bad mount option ${item}`);
|
|
7590
|
-
if (eq < 0) options[item] = "";
|
|
7591
|
-
else options[item.slice(0, eq)] = item.slice(eq + 1);
|
|
7592
|
-
}
|
|
7593
|
-
if (options.maxfile !== void 0) parseSize(options.maxfile);
|
|
7594
|
-
return options;
|
|
7595
|
-
}
|
|
7596
|
-
function refusedTarget(target) {
|
|
7597
|
-
const at = normalizePath(target);
|
|
7598
|
-
const kernel = at === "/" || [`/proc`, `/dev`].some((k) => at === k || at.startsWith(`${k}/`));
|
|
7599
|
-
if (kernel) throw errnoError("EBUSY", `${at} belongs to the kernel`);
|
|
7600
|
-
}
|
|
7601
|
-
function absolute(target) {
|
|
7602
|
-
if (!target) throw errnoError("ENOENT", "no mount point");
|
|
7603
|
-
if (!target.startsWith("/")) throw errnoError("EINVAL", `not an absolute path: ${target}`);
|
|
7604
|
-
return normalizePath(target);
|
|
7605
|
-
}
|
|
7606
|
-
function mountCall(call) {
|
|
7607
|
-
let flags = call.flags >>> 0;
|
|
7608
|
-
if ((flags & MS_MGC_MSK) >>> 0 === MS_MGC_VAL) flags &= ~MS_MGC_MSK;
|
|
7609
|
-
if (flags & ~(MS_RDONLY | IGNORED_FLAGS)) {
|
|
7610
|
-
throw errnoError("EINVAL", `unsupported mount flags ${flags.toString(16)}`);
|
|
7611
|
-
}
|
|
7612
|
-
if (!call.type) throw errnoError("EINVAL", "no file system type");
|
|
7613
|
-
const target = absolute(call.target);
|
|
7614
|
-
refusedTarget(target);
|
|
7615
|
-
const options = parseMountOptions(call.data);
|
|
7616
|
-
if (flags & MS_RDONLY) options.ro = "";
|
|
7617
|
-
return { type: call.type, source: call.source || "none", target, options };
|
|
7618
|
-
}
|
|
7619
|
-
function umountCall(target, flags) {
|
|
7620
|
-
const known = MNT_FORCE | MNT_DETACH | UMOUNT_NOFOLLOW;
|
|
7621
|
-
if (flags >>> 0 & ~known) throw errnoError("EINVAL", `unsupported umount flags ${flags}`);
|
|
7622
|
-
const at = absolute(target);
|
|
7623
|
-
refusedTarget(at);
|
|
7624
|
-
return { target: at, detach: (flags & (MNT_FORCE | MNT_DETACH)) !== 0 };
|
|
7625
|
-
}
|
|
7626
|
-
|
|
7627
7701
|
// src/mount/tmpfs.ts
|
|
7628
7702
|
function tmpfs() {
|
|
7629
7703
|
let inodes = 0;
|
|
@@ -8042,6 +8116,8 @@ var Launcher = class {
|
|
|
8042
8116
|
openFiles = /* @__PURE__ */ new Set();
|
|
8043
8117
|
held = /* @__PURE__ */ new Set();
|
|
8044
8118
|
processMounts;
|
|
8119
|
+
booting = new AbortController();
|
|
8120
|
+
fstab = Promise.resolve([]);
|
|
8045
8121
|
nextPid = 1e3;
|
|
8046
8122
|
boot = Date.now();
|
|
8047
8123
|
terminals = 0;
|
|
@@ -8449,6 +8525,7 @@ var Launcher = class {
|
|
|
8449
8525
|
for (const holds of this.held) holds.revoke(at);
|
|
8450
8526
|
}
|
|
8451
8527
|
unmountAll() {
|
|
8528
|
+
this.booting.abort();
|
|
8452
8529
|
const targets = this.mounts.list().map((m) => m.target);
|
|
8453
8530
|
for (const target of targets.sort((a, b) => b.length - a.length)) this.umount(target, true);
|
|
8454
8531
|
}
|
|
@@ -8480,6 +8557,17 @@ var Launcher = class {
|
|
|
8480
8557
|
async prepare() {
|
|
8481
8558
|
for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
|
|
8482
8559
|
await writeCaFile(this.base, this.ca).catch(() => void 0);
|
|
8560
|
+
this.fstab = this.mountFstab();
|
|
8561
|
+
}
|
|
8562
|
+
async mountFstab() {
|
|
8563
|
+
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
8564
|
+
return mountFstab(
|
|
8565
|
+
parseFstab(text),
|
|
8566
|
+
(spec) => this.mount(spec),
|
|
8567
|
+
this.booting.signal,
|
|
8568
|
+
FSTAB_RETRIES,
|
|
8569
|
+
(entry) => this.umount(entry.target, true)
|
|
8570
|
+
);
|
|
8483
8571
|
}
|
|
8484
8572
|
environment(cwd, extra) {
|
|
8485
8573
|
return {
|