@ai-ecoverse/slicc-kernel 1.20.0 → 1.21.0

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.
@@ -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 wait of waits) wait.catch(() => void 0);
751
+ for (const wait2 of waits) wait2.catch(() => void 0);
752
752
  return pipeCall(() => Promise.race(waits));
753
753
  }
754
754
  bind(addr) {
@@ -3155,6 +3155,7 @@ var CA_PATH = "/etc/ssl/certs/slicc-kernel-ca.pem";
3155
3155
  var PROXY_URL = `http://127.0.0.1:${REALM_PROXY_PORT}`;
3156
3156
  var NO_PROXY = "localhost,.localhost,127.0.0.1,127.0.0.0/8";
3157
3157
  var CA_OWNER = "slicc-kernel";
3158
+ var CA_DB = "slicc-kernel-ca";
3158
3159
  function networkEnv() {
3159
3160
  return {
3160
3161
  http_proxy: PROXY_URL,
@@ -3188,8 +3189,8 @@ function memoryCaStore() {
3188
3189
  }
3189
3190
  };
3190
3191
  }
3191
- function caStore(metadata) {
3192
- return metadata === false ? memoryCaStore() : indexedDbCaStore(`${metadata}-ca`);
3192
+ function caStore(name2) {
3193
+ return name2 === false ? memoryCaStore() : indexedDbCaStore(name2);
3193
3194
  }
3194
3195
  function base64(bytes) {
3195
3196
  let text = "";
@@ -3222,12 +3223,27 @@ function kernelCa(store) {
3222
3223
  function kernelTlsEngine(load) {
3223
3224
  return () => loadTlsEngine(load);
3224
3225
  }
3225
- async function writeCaFile(fs, ca) {
3226
- const { pem: pem2 } = await ca();
3227
- const current = await fs.exists(CA_PATH) ? await fs.readFile(CA_PATH) : void 0;
3228
- if (current === pem2) return;
3226
+ var CERTIFICATE = /-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g;
3227
+ var CA_BUNDLE_MAX = 16;
3228
+ var CA_FILE_LOCK = "slicc-kernel-ca-file";
3229
+ var CA_FILE_TRIES = 3;
3230
+ async function addToBundle(fs, own) {
3231
+ const current = await fs.exists(CA_PATH) ? await fs.readFile(CA_PATH) : "";
3232
+ const all = current.match(CERTIFICATE) ?? [];
3233
+ const others = all.filter((cert) => cert !== own);
3234
+ if (others.length < all.length) return true;
3235
+ const bundle = [...others, own].slice(-CA_BUNDLE_MAX);
3229
3236
  await fs.mkdir(CA_PATH.slice(0, CA_PATH.lastIndexOf("/")), { recursive: true });
3230
- await fs.writeFile(CA_PATH, pem2);
3237
+ await fs.writeFile(CA_PATH, `${bundle.join("\n")}
3238
+ `);
3239
+ return false;
3240
+ }
3241
+ async function writeCaFile(fs, ca, locks = globalThis.navigator?.locks) {
3242
+ const own = (await ca()).pem.trim();
3243
+ const update = async () => {
3244
+ for (let i = 0; i < CA_FILE_TRIES && !await addToBundle(fs, own); i++) ;
3245
+ };
3246
+ await (locks ? locks.request(CA_FILE_LOCK, update) : update());
3231
3247
  }
3232
3248
  function enableNetwork(net, options) {
3233
3249
  let proxy;
@@ -7185,10 +7201,164 @@ function removableMedium(onLost, open = (handle) => new OpfsFs(handle)) {
7185
7201
  };
7186
7202
  }
7187
7203
 
7204
+ // src/mount/syscall.ts
7205
+ var MS_RDONLY = 1;
7206
+ var MS_MGC_MSK = 4294901760;
7207
+ var MS_MGC_VAL = 3236757504;
7208
+ var IGNORED_FLAGS = 2 | 4 | 8 | 16 | 128 | 1024 | 2048 | 32768 | 1 << 21 | 1 << 24 | 1 << 25;
7209
+ var MNT_FORCE = 1;
7210
+ var MNT_DETACH = 2;
7211
+ var UMOUNT_NOFOLLOW = 8;
7212
+ var GENERIC = /* @__PURE__ */ new Set([
7213
+ "defaults",
7214
+ "async",
7215
+ "sync",
7216
+ "dirsync",
7217
+ "atime",
7218
+ "noatime",
7219
+ "diratime",
7220
+ "nodiratime",
7221
+ "relatime",
7222
+ "norelatime",
7223
+ "strictatime",
7224
+ "nostrictatime",
7225
+ "lazytime",
7226
+ "nolazytime",
7227
+ "suid",
7228
+ "nosuid",
7229
+ "dev",
7230
+ "nodev",
7231
+ "exec",
7232
+ "noexec",
7233
+ "auto",
7234
+ "noauto",
7235
+ "user",
7236
+ "nouser",
7237
+ "users",
7238
+ "nofail",
7239
+ "_netdev",
7240
+ "silent",
7241
+ "loud"
7242
+ ]);
7243
+ function parseMountOptions(data) {
7244
+ const options = {};
7245
+ for (const raw of data.split(",")) {
7246
+ const item = raw.trim();
7247
+ if (!item || GENERIC.has(item)) continue;
7248
+ if (item === "rw") {
7249
+ delete options.ro;
7250
+ continue;
7251
+ }
7252
+ const eq = item.indexOf("=");
7253
+ if (eq === 0) throw errnoError("EINVAL", `bad mount option ${item}`);
7254
+ if (eq < 0) options[item] = "";
7255
+ else options[item.slice(0, eq)] = item.slice(eq + 1);
7256
+ }
7257
+ if (options.maxfile !== void 0) parseSize(options.maxfile);
7258
+ return options;
7259
+ }
7260
+ function refusedTarget(target) {
7261
+ const at = normalizePath(target);
7262
+ const kernel = at === "/" || [`/proc`, `/dev`].some((k) => at === k || at.startsWith(`${k}/`));
7263
+ if (kernel) throw errnoError("EBUSY", `${at} belongs to the kernel`);
7264
+ }
7265
+ function absolute(target) {
7266
+ if (!target) throw errnoError("ENOENT", "no mount point");
7267
+ if (!target.startsWith("/")) throw errnoError("EINVAL", `not an absolute path: ${target}`);
7268
+ return normalizePath(target);
7269
+ }
7270
+ function mountCall(call) {
7271
+ let flags = call.flags >>> 0;
7272
+ if ((flags & MS_MGC_MSK) >>> 0 === MS_MGC_VAL) flags &= ~MS_MGC_MSK;
7273
+ if (flags & ~(MS_RDONLY | IGNORED_FLAGS)) {
7274
+ throw errnoError("EINVAL", `unsupported mount flags ${flags.toString(16)}`);
7275
+ }
7276
+ if (!call.type) throw errnoError("EINVAL", "no file system type");
7277
+ const target = absolute(call.target);
7278
+ refusedTarget(target);
7279
+ const options = parseMountOptions(call.data);
7280
+ if (flags & MS_RDONLY) options.ro = "";
7281
+ return { type: call.type, source: call.source || "none", target, options };
7282
+ }
7283
+ function umountCall(target, flags) {
7284
+ const known = MNT_FORCE | MNT_DETACH | UMOUNT_NOFOLLOW;
7285
+ if (flags >>> 0 & ~known) throw errnoError("EINVAL", `unsupported umount flags ${flags}`);
7286
+ const at = absolute(target);
7287
+ refusedTarget(at);
7288
+ return { target: at, detach: (flags & (MNT_FORCE | MNT_DETACH)) !== 0 };
7289
+ }
7290
+
7291
+ // src/mount/fstab.ts
7292
+ var FSTAB_PATH = "/etc/fstab";
7293
+ var FSTAB_RETRIES = [1e3, 4e3, 16e3];
7294
+ var FINAL = /* @__PURE__ */ new Set(["EINVAL", "EBUSY", "EPERM", "EACCES"]);
7295
+ function unescape(field) {
7296
+ return field.replace(
7297
+ /\\([0-7]{3})/g,
7298
+ (_, octal) => String.fromCharCode(Number.parseInt(octal, 8))
7299
+ );
7300
+ }
7301
+ function parseFstab(text) {
7302
+ const lines = [];
7303
+ text.split("\n").forEach((raw, index) => {
7304
+ const content = raw.replace(/#.*$/, "").trim();
7305
+ if (!content) return;
7306
+ const [source, target, type, options = "defaults"] = content.split(/\s+/).map(unescape);
7307
+ if (source === void 0 || target === void 0 || type === void 0) return;
7308
+ if (options.split(",").includes("noauto")) return;
7309
+ const spec = { source, target, type, options: {} };
7310
+ try {
7311
+ lines.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
7312
+ } catch (err) {
7313
+ lines.push({ line: index + 1, spec, invalid: err });
7314
+ }
7315
+ });
7316
+ return lines;
7317
+ }
7318
+ function codeOf3(err) {
7319
+ const code = err?.code;
7320
+ return typeof code === "string" ? code : "EIO";
7321
+ }
7322
+ function wait(ms, signal) {
7323
+ if (signal.aborted) return Promise.resolve();
7324
+ return new Promise((resolve) => {
7325
+ const done4 = () => {
7326
+ clearTimeout(timer);
7327
+ signal.removeEventListener("abort", done4);
7328
+ resolve();
7329
+ };
7330
+ const timer = setTimeout(done4, ms);
7331
+ signal.addEventListener("abort", done4, { once: true });
7332
+ });
7333
+ }
7334
+ async function mountFstab(lines, mount, signal, retries = FSTAB_RETRIES, discard = () => {
7335
+ }) {
7336
+ return Promise.all(
7337
+ lines.map(async ({ line, spec, invalid }) => {
7338
+ let failed = invalid;
7339
+ for (let attempt = 0; !invalid && attempt <= retries.length && !signal.aborted; attempt++) {
7340
+ try {
7341
+ const entry = await mount(spec);
7342
+ if (!signal.aborted) return { line, target: spec.target, entry };
7343
+ discard(entry);
7344
+ failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
7345
+ break;
7346
+ } catch (err) {
7347
+ failed = err;
7348
+ if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
7349
+ await wait(retries[attempt], signal);
7350
+ }
7351
+ }
7352
+ const code = codeOf3(failed);
7353
+ return { line, target: spec.target, code, error: String(failed?.message ?? code) };
7354
+ })
7355
+ );
7356
+ }
7357
+
7188
7358
  // src/mount/hostfs.ts
7189
7359
  var HOSTFS_IO = 16 * 1024 * 1024;
7190
- var codeOf3 = (err) => err?.code;
7191
- var unreachable = (err) => codeOf3(err) === "ENOTCONN";
7360
+ var codeOf4 = (err) => err?.code;
7361
+ var unreachable = (err) => codeOf4(err) === "ENOTCONN";
7192
7362
  function rel(path) {
7193
7363
  return path.replace(/^\/+/, "");
7194
7364
  }
@@ -7323,7 +7493,7 @@ function hostfsHandlers(current) {
7323
7493
  try {
7324
7494
  return { opened: await opening(), restore: void 0 };
7325
7495
  } catch (err) {
7326
- if (!flags.write || codeOf3(err) !== "EACCES") throw err;
7496
+ if (!flags.write || codeOf4(err) !== "EACCES") throw err;
7327
7497
  const mode = (await client.call("stat", { path: rel(path) })).mode ?? 0;
7328
7498
  if (mode & 128) throw err;
7329
7499
  const restore = { path: rel(path), mode };
@@ -7412,7 +7582,7 @@ function keepWatching(loop, signal) {
7412
7582
  const uncap = () => clearTimeout(capped);
7413
7583
  void ready2.promise.then(uncap);
7414
7584
  signal.addEventListener("abort", uncap, { once: true });
7415
- let wait = first;
7585
+ let wait2 = first;
7416
7586
  let up = false;
7417
7587
  let current;
7418
7588
  async function session() {
@@ -7457,7 +7627,7 @@ function keepWatching(loop, signal) {
7457
7627
  const opened2 = await session();
7458
7628
  if (signal.aborted) return;
7459
7629
  if (opened2 && Date.now() - began >= first) {
7460
- wait = first;
7630
+ wait2 = first;
7461
7631
  continue;
7462
7632
  }
7463
7633
  if (!opened2 && up) {
@@ -7465,8 +7635,8 @@ function keepWatching(loop, signal) {
7465
7635
  loop.down();
7466
7636
  }
7467
7637
  ready2.resolve();
7468
- await sleep(wait, signal);
7469
- wait = Math.min(wait * 2, most);
7638
+ await sleep(wait2, signal);
7639
+ wait2 = Math.min(wait2 * 2, most);
7470
7640
  }
7471
7641
  })();
7472
7642
  return { ready: ready2.promise, kick: () => current?.abort() };
@@ -7521,93 +7691,6 @@ async function openHostfs(spec, hook, fetch2, timing = {}) {
7521
7691
  };
7522
7692
  }
7523
7693
 
7524
- // src/mount/syscall.ts
7525
- var MS_RDONLY = 1;
7526
- var MS_MGC_MSK = 4294901760;
7527
- var MS_MGC_VAL = 3236757504;
7528
- var IGNORED_FLAGS = 2 | 4 | 8 | 16 | 128 | 1024 | 2048 | 32768 | 1 << 21 | 1 << 24 | 1 << 25;
7529
- var MNT_FORCE = 1;
7530
- var MNT_DETACH = 2;
7531
- var UMOUNT_NOFOLLOW = 8;
7532
- var GENERIC = /* @__PURE__ */ new Set([
7533
- "defaults",
7534
- "async",
7535
- "sync",
7536
- "dirsync",
7537
- "atime",
7538
- "noatime",
7539
- "diratime",
7540
- "nodiratime",
7541
- "relatime",
7542
- "norelatime",
7543
- "strictatime",
7544
- "nostrictatime",
7545
- "lazytime",
7546
- "nolazytime",
7547
- "suid",
7548
- "nosuid",
7549
- "dev",
7550
- "nodev",
7551
- "exec",
7552
- "noexec",
7553
- "auto",
7554
- "noauto",
7555
- "user",
7556
- "nouser",
7557
- "users",
7558
- "nofail",
7559
- "_netdev",
7560
- "silent",
7561
- "loud"
7562
- ]);
7563
- function parseMountOptions(data) {
7564
- const options = {};
7565
- for (const raw of data.split(",")) {
7566
- const item = raw.trim();
7567
- if (!item || GENERIC.has(item)) continue;
7568
- if (item === "rw") {
7569
- delete options.ro;
7570
- continue;
7571
- }
7572
- const eq = item.indexOf("=");
7573
- if (eq === 0) throw errnoError("EINVAL", `bad mount option ${item}`);
7574
- if (eq < 0) options[item] = "";
7575
- else options[item.slice(0, eq)] = item.slice(eq + 1);
7576
- }
7577
- if (options.maxfile !== void 0) parseSize(options.maxfile);
7578
- return options;
7579
- }
7580
- function refusedTarget(target) {
7581
- const at = normalizePath(target);
7582
- const kernel = at === "/" || [`/proc`, `/dev`].some((k) => at === k || at.startsWith(`${k}/`));
7583
- if (kernel) throw errnoError("EBUSY", `${at} belongs to the kernel`);
7584
- }
7585
- function absolute(target) {
7586
- if (!target) throw errnoError("ENOENT", "no mount point");
7587
- if (!target.startsWith("/")) throw errnoError("EINVAL", `not an absolute path: ${target}`);
7588
- return normalizePath(target);
7589
- }
7590
- function mountCall(call) {
7591
- let flags = call.flags >>> 0;
7592
- if ((flags & MS_MGC_MSK) >>> 0 === MS_MGC_VAL) flags &= ~MS_MGC_MSK;
7593
- if (flags & ~(MS_RDONLY | IGNORED_FLAGS)) {
7594
- throw errnoError("EINVAL", `unsupported mount flags ${flags.toString(16)}`);
7595
- }
7596
- if (!call.type) throw errnoError("EINVAL", "no file system type");
7597
- const target = absolute(call.target);
7598
- refusedTarget(target);
7599
- const options = parseMountOptions(call.data);
7600
- if (flags & MS_RDONLY) options.ro = "";
7601
- return { type: call.type, source: call.source || "none", target, options };
7602
- }
7603
- function umountCall(target, flags) {
7604
- const known = MNT_FORCE | MNT_DETACH | UMOUNT_NOFOLLOW;
7605
- if (flags >>> 0 & ~known) throw errnoError("EINVAL", `unsupported umount flags ${flags}`);
7606
- const at = absolute(target);
7607
- refusedTarget(at);
7608
- return { target: at, detach: (flags & (MNT_FORCE | MNT_DETACH)) !== 0 };
7609
- }
7610
-
7611
7694
  // src/mount/tmpfs.ts
7612
7695
  function tmpfs() {
7613
7696
  let inodes = 0;
@@ -8026,6 +8109,8 @@ var Launcher = class {
8026
8109
  openFiles = /* @__PURE__ */ new Set();
8027
8110
  held = /* @__PURE__ */ new Set();
8028
8111
  processMounts;
8112
+ booting = new AbortController();
8113
+ fstab = Promise.resolve([]);
8029
8114
  nextPid = 1e3;
8030
8115
  boot = Date.now();
8031
8116
  terminals = 0;
@@ -8433,6 +8518,7 @@ var Launcher = class {
8433
8518
  for (const holds of this.held) holds.revoke(at);
8434
8519
  }
8435
8520
  unmountAll() {
8521
+ this.booting.abort();
8436
8522
  const targets = this.mounts.list().map((m) => m.target);
8437
8523
  for (const target of targets.sort((a, b) => b.length - a.length)) this.umount(target, true);
8438
8524
  }
@@ -8464,6 +8550,17 @@ var Launcher = class {
8464
8550
  async prepare() {
8465
8551
  for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
8466
8552
  await writeCaFile(this.base, this.ca).catch(() => void 0);
8553
+ this.fstab = this.mountFstab();
8554
+ }
8555
+ async mountFstab() {
8556
+ const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
8557
+ return mountFstab(
8558
+ parseFstab(text),
8559
+ (spec) => this.mount(spec),
8560
+ this.booting.signal,
8561
+ FSTAB_RETRIES,
8562
+ (entry) => this.umount(entry.target, true)
8563
+ );
8467
8564
  }
8468
8565
  environment(cwd, extra) {
8469
8566
  return {
@@ -8565,6 +8662,14 @@ function act2(session, req) {
8565
8662
  function dirsChannel(name2) {
8566
8663
  return name2 === false ? void 0 : `slicc-kernel-dirs:${name2}`;
8567
8664
  }
8665
+ function caDb(req) {
8666
+ const name2 = req.ca ?? (req.metadata ? `${req.metadata}-ca` : CA_DB);
8667
+ const taken = [req.metadata ?? META_DB, req.media ?? `${req.metadata ?? META_DB}:media`];
8668
+ if (name2 !== false && taken.includes(name2)) {
8669
+ throw new Error(`the CA cannot share the IndexedDB database ${name2}`);
8670
+ }
8671
+ return name2;
8672
+ }
8568
8673
  async function stores(deps, name2, media) {
8569
8674
  const handles = media ?? (name2 === false ? false : `${name2}:media`);
8570
8675
  if (handles !== false && handles === name2) {
@@ -8647,7 +8752,7 @@ function serveKernel(port, deps) {
8647
8752
  onMountPending: (medium) => port.postMessage({ medium }),
8648
8753
  ...req.hostfs ? { hostfs: askGrant } : {},
8649
8754
  ...mountPolicy(req.processMounts),
8650
- caStore: caStore(deps.metadata ? name2 : false)
8755
+ caStore: caStore(deps.metadata ? caDb(req) : false)
8651
8756
  });
8652
8757
  await started.prepare();
8653
8758
  return started;