@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.
package/dist/node.js CHANGED
@@ -498,7 +498,7 @@ var KernelSocket = class _KernelSocket {
498
498
  const own = this.changes.wait(signal);
499
499
  if (this.state !== "connected" || !this.link) return own;
500
500
  const waits = [own, this.link.rx.changed(signal), this.link.tx.changed(signal)];
501
- for (const wait of waits) wait.catch(() => void 0);
501
+ for (const wait2 of waits) wait2.catch(() => void 0);
502
502
  return pipeCall(() => Promise.race(waits));
503
503
  }
504
504
  bind(addr) {
@@ -4664,12 +4664,27 @@ function kernelCa(store) {
4664
4664
  function kernelTlsEngine(load) {
4665
4665
  return () => loadTlsEngine(load);
4666
4666
  }
4667
- async function writeCaFile(fs, ca) {
4668
- const { pem: pem2 } = await ca();
4669
- const current = await fs.exists(CA_PATH) ? await fs.readFile(CA_PATH) : void 0;
4670
- if (current === pem2) return;
4667
+ var CERTIFICATE = /-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g;
4668
+ var CA_BUNDLE_MAX = 16;
4669
+ var CA_FILE_LOCK = "slicc-kernel-ca-file";
4670
+ var CA_FILE_TRIES = 3;
4671
+ async function addToBundle(fs, own) {
4672
+ const current = await fs.exists(CA_PATH) ? await fs.readFile(CA_PATH) : "";
4673
+ const all = current.match(CERTIFICATE) ?? [];
4674
+ const others = all.filter((cert) => cert !== own);
4675
+ if (others.length < all.length) return true;
4676
+ const bundle = [...others, own].slice(-CA_BUNDLE_MAX);
4671
4677
  await fs.mkdir(CA_PATH.slice(0, CA_PATH.lastIndexOf("/")), { recursive: true });
4672
- await fs.writeFile(CA_PATH, pem2);
4678
+ await fs.writeFile(CA_PATH, `${bundle.join("\n")}
4679
+ `);
4680
+ return false;
4681
+ }
4682
+ async function writeCaFile(fs, ca, locks = globalThis.navigator?.locks) {
4683
+ const own = (await ca()).pem.trim();
4684
+ const update = async () => {
4685
+ for (let i = 0; i < CA_FILE_TRIES && !await addToBundle(fs, own); i++) ;
4686
+ };
4687
+ await (locks ? locks.request(CA_FILE_LOCK, update) : update());
4673
4688
  }
4674
4689
  function enableNetwork(net, options) {
4675
4690
  let proxy;
@@ -6937,10 +6952,164 @@ function removableMedium(onLost, open = (handle) => new OpfsFs(handle)) {
6937
6952
  };
6938
6953
  }
6939
6954
 
6955
+ // src/mount/syscall.ts
6956
+ var MS_RDONLY = 1;
6957
+ var MS_MGC_MSK = 4294901760;
6958
+ var MS_MGC_VAL = 3236757504;
6959
+ var IGNORED_FLAGS = 2 | 4 | 8 | 16 | 128 | 1024 | 2048 | 32768 | 1 << 21 | 1 << 24 | 1 << 25;
6960
+ var MNT_FORCE = 1;
6961
+ var MNT_DETACH = 2;
6962
+ var UMOUNT_NOFOLLOW = 8;
6963
+ var GENERIC = /* @__PURE__ */ new Set([
6964
+ "defaults",
6965
+ "async",
6966
+ "sync",
6967
+ "dirsync",
6968
+ "atime",
6969
+ "noatime",
6970
+ "diratime",
6971
+ "nodiratime",
6972
+ "relatime",
6973
+ "norelatime",
6974
+ "strictatime",
6975
+ "nostrictatime",
6976
+ "lazytime",
6977
+ "nolazytime",
6978
+ "suid",
6979
+ "nosuid",
6980
+ "dev",
6981
+ "nodev",
6982
+ "exec",
6983
+ "noexec",
6984
+ "auto",
6985
+ "noauto",
6986
+ "user",
6987
+ "nouser",
6988
+ "users",
6989
+ "nofail",
6990
+ "_netdev",
6991
+ "silent",
6992
+ "loud"
6993
+ ]);
6994
+ function parseMountOptions(data) {
6995
+ const options = {};
6996
+ for (const raw of data.split(",")) {
6997
+ const item = raw.trim();
6998
+ if (!item || GENERIC.has(item)) continue;
6999
+ if (item === "rw") {
7000
+ delete options.ro;
7001
+ continue;
7002
+ }
7003
+ const eq = item.indexOf("=");
7004
+ if (eq === 0) throw errnoError("EINVAL", `bad mount option ${item}`);
7005
+ if (eq < 0) options[item] = "";
7006
+ else options[item.slice(0, eq)] = item.slice(eq + 1);
7007
+ }
7008
+ if (options.maxfile !== void 0) parseSize(options.maxfile);
7009
+ return options;
7010
+ }
7011
+ function refusedTarget(target) {
7012
+ const at = normalizePath(target);
7013
+ const kernel = at === "/" || [`/proc`, `/dev`].some((k) => at === k || at.startsWith(`${k}/`));
7014
+ if (kernel) throw errnoError("EBUSY", `${at} belongs to the kernel`);
7015
+ }
7016
+ function absolute(target) {
7017
+ if (!target) throw errnoError("ENOENT", "no mount point");
7018
+ if (!target.startsWith("/")) throw errnoError("EINVAL", `not an absolute path: ${target}`);
7019
+ return normalizePath(target);
7020
+ }
7021
+ function mountCall(call) {
7022
+ let flags = call.flags >>> 0;
7023
+ if ((flags & MS_MGC_MSK) >>> 0 === MS_MGC_VAL) flags &= ~MS_MGC_MSK;
7024
+ if (flags & ~(MS_RDONLY | IGNORED_FLAGS)) {
7025
+ throw errnoError("EINVAL", `unsupported mount flags ${flags.toString(16)}`);
7026
+ }
7027
+ if (!call.type) throw errnoError("EINVAL", "no file system type");
7028
+ const target = absolute(call.target);
7029
+ refusedTarget(target);
7030
+ const options = parseMountOptions(call.data);
7031
+ if (flags & MS_RDONLY) options.ro = "";
7032
+ return { type: call.type, source: call.source || "none", target, options };
7033
+ }
7034
+ function umountCall(target, flags) {
7035
+ const known = MNT_FORCE | MNT_DETACH | UMOUNT_NOFOLLOW;
7036
+ if (flags >>> 0 & ~known) throw errnoError("EINVAL", `unsupported umount flags ${flags}`);
7037
+ const at = absolute(target);
7038
+ refusedTarget(at);
7039
+ return { target: at, detach: (flags & (MNT_FORCE | MNT_DETACH)) !== 0 };
7040
+ }
7041
+
7042
+ // src/mount/fstab.ts
7043
+ var FSTAB_PATH = "/etc/fstab";
7044
+ var FSTAB_RETRIES = [1e3, 4e3, 16e3];
7045
+ var FINAL = /* @__PURE__ */ new Set(["EINVAL", "EBUSY", "EPERM", "EACCES"]);
7046
+ function unescape(field) {
7047
+ return field.replace(
7048
+ /\\([0-7]{3})/g,
7049
+ (_, octal) => String.fromCharCode(Number.parseInt(octal, 8))
7050
+ );
7051
+ }
7052
+ function parseFstab(text) {
7053
+ const lines = [];
7054
+ text.split("\n").forEach((raw, index) => {
7055
+ const content = raw.replace(/#.*$/, "").trim();
7056
+ if (!content) return;
7057
+ const [source, target, type, options = "defaults"] = content.split(/\s+/).map(unescape);
7058
+ if (source === void 0 || target === void 0 || type === void 0) return;
7059
+ if (options.split(",").includes("noauto")) return;
7060
+ const spec = { source, target, type, options: {} };
7061
+ try {
7062
+ lines.push({ line: index + 1, spec: { ...spec, options: parseMountOptions(options) } });
7063
+ } catch (err) {
7064
+ lines.push({ line: index + 1, spec, invalid: err });
7065
+ }
7066
+ });
7067
+ return lines;
7068
+ }
7069
+ function codeOf3(err) {
7070
+ const code = err?.code;
7071
+ return typeof code === "string" ? code : "EIO";
7072
+ }
7073
+ function wait(ms, signal) {
7074
+ if (signal.aborted) return Promise.resolve();
7075
+ return new Promise((resolve) => {
7076
+ const done4 = () => {
7077
+ clearTimeout(timer);
7078
+ signal.removeEventListener("abort", done4);
7079
+ resolve();
7080
+ };
7081
+ const timer = setTimeout(done4, ms);
7082
+ signal.addEventListener("abort", done4, { once: true });
7083
+ });
7084
+ }
7085
+ async function mountFstab(lines, mount, signal, retries = FSTAB_RETRIES, discard = () => {
7086
+ }) {
7087
+ return Promise.all(
7088
+ lines.map(async ({ line, spec, invalid }) => {
7089
+ let failed = invalid;
7090
+ for (let attempt = 0; !invalid && attempt <= retries.length && !signal.aborted; attempt++) {
7091
+ try {
7092
+ const entry = await mount(spec);
7093
+ if (!signal.aborted) return { line, target: spec.target, entry };
7094
+ discard(entry);
7095
+ failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
7096
+ break;
7097
+ } catch (err) {
7098
+ failed = err;
7099
+ if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
7100
+ await wait(retries[attempt], signal);
7101
+ }
7102
+ }
7103
+ const code = codeOf3(failed);
7104
+ return { line, target: spec.target, code, error: String(failed?.message ?? code) };
7105
+ })
7106
+ );
7107
+ }
7108
+
6940
7109
  // src/mount/hostfs.ts
6941
7110
  var HOSTFS_IO = 16 * 1024 * 1024;
6942
- var codeOf3 = (err) => err?.code;
6943
- var unreachable = (err) => codeOf3(err) === "ENOTCONN";
7111
+ var codeOf4 = (err) => err?.code;
7112
+ var unreachable = (err) => codeOf4(err) === "ENOTCONN";
6944
7113
  function rel(path) {
6945
7114
  return path.replace(/^\/+/, "");
6946
7115
  }
@@ -7075,7 +7244,7 @@ function hostfsHandlers(current) {
7075
7244
  try {
7076
7245
  return { opened: await opening(), restore: void 0 };
7077
7246
  } catch (err) {
7078
- if (!flags.write || codeOf3(err) !== "EACCES") throw err;
7247
+ if (!flags.write || codeOf4(err) !== "EACCES") throw err;
7079
7248
  const mode = (await client.call("stat", { path: rel(path) })).mode ?? 0;
7080
7249
  if (mode & 128) throw err;
7081
7250
  const restore = { path: rel(path), mode };
@@ -7164,7 +7333,7 @@ function keepWatching(loop, signal) {
7164
7333
  const uncap = () => clearTimeout(capped);
7165
7334
  void ready2.promise.then(uncap);
7166
7335
  signal.addEventListener("abort", uncap, { once: true });
7167
- let wait = first;
7336
+ let wait2 = first;
7168
7337
  let up = false;
7169
7338
  let current;
7170
7339
  async function session() {
@@ -7209,7 +7378,7 @@ function keepWatching(loop, signal) {
7209
7378
  const opened2 = await session();
7210
7379
  if (signal.aborted) return;
7211
7380
  if (opened2 && Date.now() - began >= first) {
7212
- wait = first;
7381
+ wait2 = first;
7213
7382
  continue;
7214
7383
  }
7215
7384
  if (!opened2 && up) {
@@ -7217,8 +7386,8 @@ function keepWatching(loop, signal) {
7217
7386
  loop.down();
7218
7387
  }
7219
7388
  ready2.resolve();
7220
- await sleep(wait, signal);
7221
- wait = Math.min(wait * 2, most);
7389
+ await sleep(wait2, signal);
7390
+ wait2 = Math.min(wait2 * 2, most);
7222
7391
  }
7223
7392
  })();
7224
7393
  return { ready: ready2.promise, kick: () => current?.abort() };
@@ -7282,93 +7451,6 @@ function memoryMedia() {
7282
7451
  };
7283
7452
  }
7284
7453
 
7285
- // src/mount/syscall.ts
7286
- var MS_RDONLY = 1;
7287
- var MS_MGC_MSK = 4294901760;
7288
- var MS_MGC_VAL = 3236757504;
7289
- var IGNORED_FLAGS = 2 | 4 | 8 | 16 | 128 | 1024 | 2048 | 32768 | 1 << 21 | 1 << 24 | 1 << 25;
7290
- var MNT_FORCE = 1;
7291
- var MNT_DETACH = 2;
7292
- var UMOUNT_NOFOLLOW = 8;
7293
- var GENERIC = /* @__PURE__ */ new Set([
7294
- "defaults",
7295
- "async",
7296
- "sync",
7297
- "dirsync",
7298
- "atime",
7299
- "noatime",
7300
- "diratime",
7301
- "nodiratime",
7302
- "relatime",
7303
- "norelatime",
7304
- "strictatime",
7305
- "nostrictatime",
7306
- "lazytime",
7307
- "nolazytime",
7308
- "suid",
7309
- "nosuid",
7310
- "dev",
7311
- "nodev",
7312
- "exec",
7313
- "noexec",
7314
- "auto",
7315
- "noauto",
7316
- "user",
7317
- "nouser",
7318
- "users",
7319
- "nofail",
7320
- "_netdev",
7321
- "silent",
7322
- "loud"
7323
- ]);
7324
- function parseMountOptions(data) {
7325
- const options = {};
7326
- for (const raw of data.split(",")) {
7327
- const item = raw.trim();
7328
- if (!item || GENERIC.has(item)) continue;
7329
- if (item === "rw") {
7330
- delete options.ro;
7331
- continue;
7332
- }
7333
- const eq = item.indexOf("=");
7334
- if (eq === 0) throw errnoError("EINVAL", `bad mount option ${item}`);
7335
- if (eq < 0) options[item] = "";
7336
- else options[item.slice(0, eq)] = item.slice(eq + 1);
7337
- }
7338
- if (options.maxfile !== void 0) parseSize(options.maxfile);
7339
- return options;
7340
- }
7341
- function refusedTarget(target) {
7342
- const at = normalizePath(target);
7343
- const kernel = at === "/" || [`/proc`, `/dev`].some((k) => at === k || at.startsWith(`${k}/`));
7344
- if (kernel) throw errnoError("EBUSY", `${at} belongs to the kernel`);
7345
- }
7346
- function absolute(target) {
7347
- if (!target) throw errnoError("ENOENT", "no mount point");
7348
- if (!target.startsWith("/")) throw errnoError("EINVAL", `not an absolute path: ${target}`);
7349
- return normalizePath(target);
7350
- }
7351
- function mountCall(call) {
7352
- let flags = call.flags >>> 0;
7353
- if ((flags & MS_MGC_MSK) >>> 0 === MS_MGC_VAL) flags &= ~MS_MGC_MSK;
7354
- if (flags & ~(MS_RDONLY | IGNORED_FLAGS)) {
7355
- throw errnoError("EINVAL", `unsupported mount flags ${flags.toString(16)}`);
7356
- }
7357
- if (!call.type) throw errnoError("EINVAL", "no file system type");
7358
- const target = absolute(call.target);
7359
- refusedTarget(target);
7360
- const options = parseMountOptions(call.data);
7361
- if (flags & MS_RDONLY) options.ro = "";
7362
- return { type: call.type, source: call.source || "none", target, options };
7363
- }
7364
- function umountCall(target, flags) {
7365
- const known = MNT_FORCE | MNT_DETACH | UMOUNT_NOFOLLOW;
7366
- if (flags >>> 0 & ~known) throw errnoError("EINVAL", `unsupported umount flags ${flags}`);
7367
- const at = absolute(target);
7368
- refusedTarget(at);
7369
- return { target: at, detach: (flags & (MNT_FORCE | MNT_DETACH)) !== 0 };
7370
- }
7371
-
7372
7454
  // src/mount/tmpfs.ts
7373
7455
  function tmpfs() {
7374
7456
  let inodes = 0;
@@ -7787,6 +7869,8 @@ var Launcher = class {
7787
7869
  openFiles = /* @__PURE__ */ new Set();
7788
7870
  held = /* @__PURE__ */ new Set();
7789
7871
  processMounts;
7872
+ booting = new AbortController();
7873
+ fstab = Promise.resolve([]);
7790
7874
  nextPid = 1e3;
7791
7875
  boot = Date.now();
7792
7876
  terminals = 0;
@@ -8194,6 +8278,7 @@ var Launcher = class {
8194
8278
  for (const holds of this.held) holds.revoke(at);
8195
8279
  }
8196
8280
  unmountAll() {
8281
+ this.booting.abort();
8197
8282
  const targets = this.mounts.list().map((m) => m.target);
8198
8283
  for (const target of targets.sort((a, b) => b.length - a.length)) this.umount(target, true);
8199
8284
  }
@@ -8225,6 +8310,17 @@ var Launcher = class {
8225
8310
  async prepare() {
8226
8311
  for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
8227
8312
  await writeCaFile(this.base, this.ca).catch(() => void 0);
8313
+ this.fstab = this.mountFstab();
8314
+ }
8315
+ async mountFstab() {
8316
+ const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
8317
+ return mountFstab(
8318
+ parseFstab(text),
8319
+ (spec) => this.mount(spec),
8320
+ this.booting.signal,
8321
+ FSTAB_RETRIES,
8322
+ (entry) => this.umount(entry.target, true)
8323
+ );
8228
8324
  }
8229
8325
  environment(cwd, extra) {
8230
8326
  return {