@ai-ecoverse/slicc-kernel 1.62.4 → 1.63.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.
@@ -429,11 +429,16 @@ var OpfsFs = class {
429
429
  }
430
430
  async ancestry(path) {
431
431
  const entries = await this.meta.get(chain(path));
432
- return entries.map((entry, i) => ({
433
- ...ownOf(entry) ?? (i === 0 ? ROOT_OWNER : inherit(entries.slice(0, i))),
434
- ...entry?.mode !== void 0 ? { mode: entry.mode } : {},
435
- ...entry?.link !== void 0 ? { link: entry.link } : {}
436
- }));
432
+ const out = [];
433
+ for (const [i, entry] of entries.entries()) {
434
+ const link2 = entry?.link !== void 0 && !await this.real(entry.path);
435
+ out.push({
436
+ ...ownOf(entry) ?? (i === 0 ? ROOT_OWNER : inherit(entries.slice(0, i))),
437
+ ...entry?.mode !== void 0 ? { mode: entry.mode } : {},
438
+ ...link2 ? { link: entry.link } : {}
439
+ });
440
+ }
441
+ return out;
437
442
  }
438
443
  async handedTo(dir) {
439
444
  return inherit(await this.meta.get(chain(dir)));
@@ -1397,15 +1402,24 @@ async function present(fs, path, directory) {
1397
1402
  const st = await fs.lstat(path).catch(() => void 0);
1398
1403
  return st !== void 0 && (!directory || st.isDirectory);
1399
1404
  }
1405
+ async function finalOf(fs, walked, want, options) {
1406
+ const perm = walked.perm;
1407
+ if ((want & X_OK) === 0 || options.directory !== void 0) {
1408
+ return [perm, options.directory !== false];
1409
+ }
1410
+ const look = options.follow === false ? fs.lstat : fs.stat;
1411
+ const st = await look.call(fs, walked.path).catch(() => void 0);
1412
+ return st ? [permOf(st), st.isDirectory] : [perm, false];
1413
+ }
1400
1414
  async function access(fs, cred, path, want, options = {}) {
1401
1415
  const walked = await walk(fs, cred, path, options.follow !== false);
1402
1416
  if (walked.denied !== void 0) {
1403
1417
  if (await present(fs, walked.denied, true)) return walked;
1404
1418
  return { path, perm: void 0, missing: true };
1405
1419
  }
1406
- const { perm } = walked;
1407
- if (!perm) return { ...walked, missing: true };
1408
- if (want === 0 || permits(cred, perm, want, options.directory !== false)) return walked;
1420
+ if (!walked.perm) return { ...walked, missing: true };
1421
+ const [perm, directory] = await finalOf(fs, walked, want, options);
1422
+ if (want === 0 || permits(cred, perm, want, directory)) return walked;
1409
1423
  if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
1410
1424
  return { ...walked, missing: true };
1411
1425
  }
@@ -1416,6 +1430,7 @@ function refused(path, errno = "EACCES") {
1416
1430
  // src/fs/dac.ts
1417
1431
  var MAX_DENIAL_KEYS = 1e4;
1418
1432
  var S_ISGID3 = 1024;
1433
+ var DIR = { directory: true };
1419
1434
  var Dac = class {
1420
1435
  mode;
1421
1436
  hook;
@@ -1452,6 +1467,17 @@ var Dac = class {
1452
1467
  );
1453
1468
  }
1454
1469
  };
1470
+ var MAX_CREATED = 1024;
1471
+ var Created = class {
1472
+ paths = /* @__PURE__ */ new Set();
1473
+ add(path) {
1474
+ if (this.paths.size >= MAX_CREATED) this.paths.clear();
1475
+ this.paths.add(normalizePath(path));
1476
+ }
1477
+ take(path) {
1478
+ return this.paths.delete(normalizePath(path));
1479
+ }
1480
+ };
1455
1481
  function deny(g, op, path, errno = "EACCES") {
1456
1482
  g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno });
1457
1483
  }
@@ -1460,20 +1486,30 @@ async function need(g, op, path, want, options = {}, shown = path) {
1460
1486
  if (walked.denied !== void 0) deny(g, op, shown);
1461
1487
  return walked;
1462
1488
  }
1489
+ function entryIn(dir, path) {
1490
+ return joinName(dir.path, path.slice(path.lastIndexOf("/") + 1));
1491
+ }
1463
1492
  async function stickyEntry(g, op, path, dir) {
1464
1493
  const perm = dir.perm;
1465
1494
  if (!perm || sticky(g.cred, perm, perm)) return;
1466
- const entry = joinName(dir.path, path.slice(path.lastIndexOf("/") + 1));
1495
+ const entry = entryIn(dir, path);
1467
1496
  if (sticky(g.cred, perm, (await ancestryOf(g.fs, entry)).at(-1))) return;
1468
1497
  if (await present(g.fs, entry, false)) deny(g, op, path, "EPERM");
1469
1498
  }
1470
1499
  async function inParent(g, op, path, old = false) {
1471
- const dir = await need(g, op, parentOf3(path), W_OK | X_OK, {}, path);
1472
- if (old && dir.denied === void 0) await stickyEntry(g, op, path, dir);
1500
+ if (old === "replaced") {
1501
+ const dir2 = await need(g, op, parentOf3(path), W_OK | X_OK, DIR, path);
1502
+ return dir2.denied === void 0 ? stickyEntry(g, op, path, dir2) : void 0;
1503
+ }
1504
+ if (!old) return void await need(g, op, parentOf3(path), W_OK | X_OK, DIR, path);
1505
+ const dir = await access(g.fs, g.cred, parentOf3(path), W_OK | X_OK, DIR);
1506
+ if (dir.denied === void 0) return stickyEntry(g, op, path, dir);
1507
+ const lookup = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
1508
+ if (!lookup || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
1473
1509
  }
1474
1510
  async function makeIn(g, op, path) {
1475
1511
  for (let dir = parentOf3(path); ; dir = parentOf3(dir)) {
1476
- const walked = await access(g.fs, g.cred, dir, W_OK | X_OK);
1512
+ const walked = await access(g.fs, g.cred, dir, W_OK | X_OK, DIR);
1477
1513
  if (walked.denied !== void 0) {
1478
1514
  if (!await present(g.fs, path, true)) deny(g, op, path);
1479
1515
  return;
@@ -1499,7 +1535,7 @@ async function removable(g, op, path) {
1499
1535
  async function renamable(g, from, to) {
1500
1536
  if (from === to) return void await need(g, "rename", from, 0, { follow: false });
1501
1537
  await inParent(g, "rename", from, true);
1502
- await inParent(g, "rename", to, true);
1538
+ await inParent(g, "rename", to, "replaced");
1503
1539
  if (g.cred.euid === 0 || parentOf3(from) === parentOf3(to)) return;
1504
1540
  const st = await g.fs.lstat(from).catch(() => void 0);
1505
1541
  if (st?.isDirectory && !permits(g.cred, permOf(st), W_OK)) deny(g, "rename", from);
@@ -1525,8 +1561,20 @@ async function chmodMode(g, path, mode) {
1525
1561
  if (!g.dac.enforcing || !perm || g.cred.euid === 0 || inGroup(g.cred, perm.gid)) return mode;
1526
1562
  return mode & ~S_ISGID3;
1527
1563
  }
1528
- async function retimable(g, op, path) {
1529
- await ownerOnly(g, op, path, op !== "lutimes");
1564
+ async function retimable(g, op, path, now = false) {
1565
+ const follow2 = op !== "lutimes";
1566
+ if (!now) return void await ownerOnly(g, op, path, follow2);
1567
+ const walked = await need(g, op, path, 0, { follow: follow2 });
1568
+ const perm = walked.perm;
1569
+ if (!perm || g.cred.euid === 0 || perm.uid === g.cred.euid) return;
1570
+ if (permits(g.cred, perm, W_OK, false)) return;
1571
+ if (await present(g.fs, walked.path, false)) deny(g, op, path);
1572
+ }
1573
+ async function fileWritable(g, op, path) {
1574
+ await need(g, op, path, W_OK, { directory: false });
1575
+ }
1576
+ function realIds(cred) {
1577
+ return { ...cred, groups: [...cred.groups], euid: cred.ruid, egid: cred.rgid };
1530
1578
  }
1531
1579
  async function listable(g, op, path) {
1532
1580
  await need(g, op, path, R_OK | X_OK, { directory: true });
@@ -1538,10 +1586,16 @@ async function readable(g, op, path) {
1538
1586
  await need(g, op, path, R_OK, { directory: false });
1539
1587
  }
1540
1588
  async function writable(g, op, path) {
1541
- const st = await g.fs.stat(path).catch(() => void 0);
1542
- if (st) return void await need(g, op, path, W_OK, { directory: st.isDirectory });
1589
+ await openable(g, op, path, W_OK, true);
1590
+ }
1591
+ async function creatable(g, op, path) {
1543
1592
  const target = await need(g, op, path, 0);
1544
- if (target.denied === void 0) await need(g, op, parentOf3(target.path), W_OK | X_OK, {}, path);
1593
+ if (target.denied === void 0) await need(g, op, parentOf3(target.path), W_OK | X_OK, DIR, path);
1594
+ }
1595
+ async function openable(g, op, path, want, create) {
1596
+ const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
1597
+ if (create && !st) return creatable(g, op, path);
1598
+ await need(g, op, path, want, { directory: st?.isDirectory ?? false });
1545
1599
  }
1546
1600
 
1547
1601
  // src/kernel/pipe.ts
@@ -4980,11 +5034,11 @@ function realmCa(owner, store) {
4980
5034
  opened.set(store, cas);
4981
5035
  let ca = cas.get(owner);
4982
5036
  if (!ca) {
4983
- const opening = RealmCa.open(owner, store);
4984
- ca = opening;
4985
- cas.set(owner, opening);
4986
- opening.catch(() => {
4987
- if (cas.get(owner) === opening) cas.delete(owner);
5037
+ const opening2 = RealmCa.open(owner, store);
5038
+ ca = opening2;
5039
+ cas.set(owner, opening2);
5040
+ opening2.catch(() => {
5041
+ if (cas.get(owner) === opening2) cas.delete(owner);
4988
5042
  });
4989
5043
  }
4990
5044
  return ca;
@@ -8052,6 +8106,9 @@ function revokeSyncFsToken(token) {
8052
8106
  }
8053
8107
 
8054
8108
  // src/realm/sync-fs-wire.ts
8109
+ var HOLD_READ = 4;
8110
+ var HOLD_WRITE = 8;
8111
+ var HOLD_CREATED = 16;
8055
8112
  var SYNC_FS_REQUEST_TIMEOUT_MS = 25e3;
8056
8113
  function syncError(code, label) {
8057
8114
  return Object.assign(new Error(`${code}: ${label}`), { code });
@@ -8175,6 +8232,7 @@ async function run(entry, path, req) {
8175
8232
  return done;
8176
8233
  case "create":
8177
8234
  await createExclusive(fs, path, ownerFor2(entry, req.mode ?? 438));
8235
+ entry.created?.add(path);
8178
8236
  return done;
8179
8237
  case "readlink":
8180
8238
  return json2(await fs.readlink(path));
@@ -8231,11 +8289,47 @@ async function guard(g, path, req, cwd) {
8231
8289
  return linkable(g, path, other(), req.mode === 1);
8232
8290
  case "utimes":
8233
8291
  case "lutimes":
8234
- return retimable(g, op, path);
8292
+ return retimable(g, op, path, req.now === true);
8293
+ case "read":
8294
+ case "pread":
8295
+ return req.handle === void 0 ? readable(g, op, path) : void 0;
8296
+ case "pwrite":
8297
+ case "truncate":
8298
+ return req.handle === void 0 ? fileWritable(g, op, path) : void 0;
8299
+ case "write":
8300
+ return req.handle === void 0 && req.mode !== 1 ? writable(g, op, path) : void 0;
8235
8301
  default:
8236
8302
  return void 0;
8237
8303
  }
8238
8304
  }
8305
+ async function opening(entry, path, mode) {
8306
+ const g = guardOf(entry);
8307
+ const want = (mode & HOLD_READ ? R_OK : 0) | (mode & HOLD_WRITE ? W_OK : 0);
8308
+ const created3 = (mode & HOLD_CREATED) !== 0 && entry.created?.take(path) === true;
8309
+ if (g && want !== 0 && !created3) {
8310
+ await need(g, "open", path, want, { directory: false });
8311
+ }
8312
+ }
8313
+ async function holdOp(entry, path, req) {
8314
+ const mode = req.mode ?? 0;
8315
+ const [on, open] = [(mode & 1) === 1, (mode & 2) === 2];
8316
+ if (on && open) {
8317
+ await opening(entry, path, mode);
8318
+ const real = await entry.fs.realpath?.(path, true).catch(() => path);
8319
+ return json2(entry.hold?.(path, on, open, real) ?? null);
8320
+ }
8321
+ entry.hold?.(path, on, open, void 0, req.handle);
8322
+ return done;
8323
+ }
8324
+ async function accessOp(entry, path, req) {
8325
+ const g = guardOf(entry);
8326
+ if (!g) return done;
8327
+ const open = req.arg2 === "open";
8328
+ const cred = open || req.arg2 === "eaccess" ? g.cred : realIds(g.cred);
8329
+ const options = { follow: req.nofollow !== true, ...open ? { directory: false } : {} };
8330
+ await need({ ...g, cred }, open ? "open" : "access", path, req.mode ?? 0, options);
8331
+ return done;
8332
+ }
8239
8333
  async function guarded(entry, path, req) {
8240
8334
  const g = guardOf(entry);
8241
8335
  if (!g) return void 0;
@@ -8299,16 +8393,6 @@ async function owned(entry, own, req, path, target) {
8299
8393
  }
8300
8394
  return own(target, () => run(entry, path, req));
8301
8395
  }
8302
- async function holdOp(entry, path, req) {
8303
- const mode = req.mode ?? 0;
8304
- const [on, open] = [(mode & 1) === 1, (mode & 2) === 2];
8305
- if (on && open) {
8306
- const real = await entry.fs.realpath?.(path, true).catch(() => path);
8307
- return json2(entry.hold?.(path, on, open, real) ?? null);
8308
- }
8309
- entry.hold?.(path, on, open, void 0, req.handle);
8310
- return done;
8311
- }
8312
8396
  var IDENTITY_OPS = /* @__PURE__ */ new Set([
8313
8397
  "write",
8314
8398
  "pwrite",
@@ -8345,7 +8429,8 @@ async function dispatchOne(entry, req) {
8345
8429
  const path = fs.resolvePath(cwd, req.path);
8346
8430
  if (req.op === "kept") return json2(entry.keptFor?.(req.handle ?? -1) !== void 0);
8347
8431
  if (req.op === "stat" && req.handle !== void 0) return await keptStat(entry, req);
8348
- if (req.op === "hold") return holdOp(entry, path, req);
8432
+ if (req.op === "access") return await accessOp(entry, path, req);
8433
+ if (req.op === "hold") return await holdOp(entry, path, req);
8349
8434
  const refused2 = await guarded(entry, path, req);
8350
8435
  if (refused2) return refused2;
8351
8436
  if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
@@ -8514,188 +8599,6 @@ function attachSyncSabResponder(port, sab, token, opts) {
8514
8599
  };
8515
8600
  }
8516
8601
 
8517
- // src/process/process-fds.ts
8518
- var field2 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
8519
- function mountTable(mounts = []) {
8520
- const root = globalThis.navigator?.storage ? "opfs" : "memory";
8521
- const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
8522
- for (const m of mounts) {
8523
- const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
8524
- if (m.state !== "ok") options.push(m.state);
8525
- const access2 = options.includes("ro") ? [] : ["rw"];
8526
- lines2.push(
8527
- `${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
8528
- );
8529
- }
8530
- return `${lines2.join("\n")}
8531
- `;
8532
- }
8533
-
8534
- // src/process/procfs.ts
8535
- var HZ = 100;
8536
- var KIB = 1024;
8537
- var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
8538
- var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
8539
- var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
8540
- function commOf(info) {
8541
- const name2 = info.argv[0] ?? "";
8542
- return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
8543
- }
8544
- function ttyNumber(tty) {
8545
- const pts = tty && /^\/dev\/pts\/(\d+)$/.exec(tty);
8546
- if (pts) return 136 << 8 | Number(pts[1]);
8547
- const vt = tty && /^\/dev\/tty(\d+)$/.exec(tty);
8548
- return vt ? 4 << 8 | Number(vt[1]) : 0;
8549
- }
8550
- function pages(bytes) {
8551
- return Math.ceil(bytes / 4096);
8552
- }
8553
- function ticks(ms) {
8554
- return Math.max(0, Math.floor(ms * HZ / 1e3));
8555
- }
8556
- function pidStat(info, boot) {
8557
- const fields = [
8558
- info.pid,
8559
- `(${commOf(info)})`,
8560
- info.state,
8561
- info.ppid,
8562
- info.pgid,
8563
- info.sid,
8564
- ttyNumber(info.tty),
8565
- info.tty ? info.pgid : -1,
8566
- 4194304,
8567
- 0,
8568
- 0,
8569
- 0,
8570
- 0,
8571
- 0,
8572
- 0,
8573
- 0,
8574
- 0,
8575
- 20,
8576
- 0,
8577
- 1,
8578
- 0,
8579
- ticks(info.started - boot),
8580
- info.memory,
8581
- pages(info.memory),
8582
- "18446744073709551615",
8583
- ...Array(12).fill(0),
8584
- 17,
8585
- 0,
8586
- 0,
8587
- 0,
8588
- 0,
8589
- 0,
8590
- 0,
8591
- ...Array(7).fill(0),
8592
- 0
8593
- ];
8594
- return `${fields.join(" ")}
8595
- `;
8596
- }
8597
- var sigMask = (mask = 0) => (mask >>> 1).toString(16).padStart(16, "0");
8598
- function pidStatus(info) {
8599
- const state = info.state === "Z" ? "Z (zombie)" : "S (sleeping)";
8600
- const c = info.cred ?? ROOT;
8601
- const ids = (r, e, s) => `${r} ${e} ${s} ${e}`;
8602
- return [
8603
- `Name: ${commOf(info)}`,
8604
- `Umask: ${(info.umask ?? 18).toString(8).padStart(4, "0")}`,
8605
- `State: ${state}`,
8606
- `Tgid: ${info.pid}`,
8607
- "Ngid: 0",
8608
- `Pid: ${info.pid}`,
8609
- `PPid: ${info.ppid}`,
8610
- "TracerPid: 0",
8611
- `Uid: ${ids(c.ruid, c.euid, c.suid)}`,
8612
- `Gid: ${ids(c.rgid, c.egid, c.sgid)}`,
8613
- "FDSize: 64",
8614
- `VmSize: ${Math.ceil(info.memory / KIB)} kB`,
8615
- `VmRSS: ${Math.ceil(info.memory / KIB)} kB`,
8616
- `Groups: ${c.groups.join(" ")}`,
8617
- `NSpid: ${info.pid}`,
8618
- `NSpgid: ${info.pgid}`,
8619
- `NSsid: ${info.sid}`,
8620
- "Threads: 1",
8621
- "SigQ: 0/0",
8622
- "SigPnd: 0000000000000000",
8623
- "ShdPnd: 0000000000000000",
8624
- "SigBlk: 0000000000000000",
8625
- `SigIgn: ${sigMask(info.sigIgn)}`,
8626
- `SigCgt: ${sigMask(info.sigCgt)}`,
8627
- ""
8628
- ].join("\n");
8629
- }
8630
- function pidFile(name2, info, boot) {
8631
- if (name2 === "cmdline") return info.state === "Z" ? "" : `${info.argv.join("\0")}\0`;
8632
- if (name2 === "comm") return `${commOf(info)}
8633
- `;
8634
- if (name2 === "stat") return pidStat(info, boot);
8635
- if (name2 === "statm") {
8636
- const p = pages(info.memory);
8637
- return `${p} ${p} 0 0 0 ${p} 0
8638
- `;
8639
- }
8640
- return pidStatus(info);
8641
- }
8642
- function memoryInfo(source, used = 0) {
8643
- const total = source.deviceMemory ? source.deviceMemory * KIB * KIB * KIB : FALLBACK_MEMORY;
8644
- const free = Math.max(0, total - used - (source.usedHeap ?? 0));
8645
- const kb = (bytes) => `${Math.floor(bytes / KIB)} kB`;
8646
- return [
8647
- ["MemTotal", kb(total)],
8648
- ["MemFree", kb(free)],
8649
- ["MemAvailable", kb(free)],
8650
- ["Buffers", kb(0)],
8651
- ["Cached", kb(0)],
8652
- ["SwapCached", kb(0)],
8653
- ["Shmem", kb(0)],
8654
- ["SReclaimable", kb(0)],
8655
- ["SwapTotal", kb(0)],
8656
- ["SwapFree", kb(0)]
8657
- ].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
8658
- }
8659
- function systemFile(name2, listing, now, memory) {
8660
- const up = Math.max(0, now - listing.boot) / 1e3;
8661
- if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
8662
- `;
8663
- if (name2 === "meminfo") {
8664
- return memoryInfo(
8665
- memory,
8666
- listing.processes.reduce((sum, p) => sum + p.memory, 0)
8667
- );
8668
- }
8669
- const live = listing.processes.filter((p) => p.state !== "Z");
8670
- if (name2 === "loadavg") {
8671
- const last = Math.max(0, ...listing.processes.map((p) => p.pid));
8672
- return `0.00 0.00 0.00 1/${live.length} ${last}
8673
- `;
8674
- }
8675
- const idle = ticks(now - listing.boot);
8676
- return [
8677
- `cpu 0 0 0 ${idle} 0 0 0 0 0 0`,
8678
- `cpu0 0 0 0 ${idle} 0 0 0 0 0 0`,
8679
- "intr 0",
8680
- "ctxt 0",
8681
- `btime ${Math.floor(listing.boot / 1e3)}`,
8682
- `processes ${listing.processes.length}`,
8683
- "procs_running 1",
8684
- "procs_blocked 0",
8685
- ""
8686
- ].join("\n");
8687
- }
8688
- function memorySource(scope = globalThis) {
8689
- const nav = scope.navigator;
8690
- const perf = scope.performance;
8691
- const deviceMemory = nav?.deviceMemory;
8692
- const used = perf?.memory?.usedJSHeapSize;
8693
- return {
8694
- ...typeof deviceMemory === "number" && deviceMemory > 0 ? { deviceMemory } : {},
8695
- ...typeof used === "number" ? { usedHeap: used } : {}
8696
- };
8697
- }
8698
-
8699
8602
  // src/fs/pages.ts
8700
8603
  var PAGE = 64 * 1024;
8701
8604
  var READAHEAD = 1024 * 1024;
@@ -8921,6 +8824,191 @@ var AsyncRangedFile = class {
8921
8824
  }
8922
8825
  };
8923
8826
 
8827
+ // src/realm/live-vfs-fs.ts
8828
+ var EARLY_WHOLE_FILE = 64 * 1024;
8829
+
8830
+ // src/process/process-fds.ts
8831
+ var field2 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
8832
+ function mountTable(mounts = []) {
8833
+ const root = globalThis.navigator?.storage ? "opfs" : "memory";
8834
+ const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
8835
+ for (const m of mounts) {
8836
+ const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
8837
+ if (m.state !== "ok") options.push(m.state);
8838
+ const access2 = options.includes("ro") ? [] : ["rw"];
8839
+ lines2.push(
8840
+ `${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
8841
+ );
8842
+ }
8843
+ return `${lines2.join("\n")}
8844
+ `;
8845
+ }
8846
+
8847
+ // src/process/procfs.ts
8848
+ var HZ = 100;
8849
+ var KIB = 1024;
8850
+ var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
8851
+ var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
8852
+ var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
8853
+ function commOf(info) {
8854
+ const name2 = info.argv[0] ?? "";
8855
+ return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
8856
+ }
8857
+ function ttyNumber(tty) {
8858
+ const pts = tty && /^\/dev\/pts\/(\d+)$/.exec(tty);
8859
+ if (pts) return 136 << 8 | Number(pts[1]);
8860
+ const vt = tty && /^\/dev\/tty(\d+)$/.exec(tty);
8861
+ return vt ? 4 << 8 | Number(vt[1]) : 0;
8862
+ }
8863
+ function pages(bytes) {
8864
+ return Math.ceil(bytes / 4096);
8865
+ }
8866
+ function ticks(ms) {
8867
+ return Math.max(0, Math.floor(ms * HZ / 1e3));
8868
+ }
8869
+ function pidStat(info, boot) {
8870
+ const fields = [
8871
+ info.pid,
8872
+ `(${commOf(info)})`,
8873
+ info.state,
8874
+ info.ppid,
8875
+ info.pgid,
8876
+ info.sid,
8877
+ ttyNumber(info.tty),
8878
+ info.tty ? info.pgid : -1,
8879
+ 4194304,
8880
+ 0,
8881
+ 0,
8882
+ 0,
8883
+ 0,
8884
+ 0,
8885
+ 0,
8886
+ 0,
8887
+ 0,
8888
+ 20,
8889
+ 0,
8890
+ 1,
8891
+ 0,
8892
+ ticks(info.started - boot),
8893
+ info.memory,
8894
+ pages(info.memory),
8895
+ "18446744073709551615",
8896
+ ...Array(12).fill(0),
8897
+ 17,
8898
+ 0,
8899
+ 0,
8900
+ 0,
8901
+ 0,
8902
+ 0,
8903
+ 0,
8904
+ ...Array(7).fill(0),
8905
+ 0
8906
+ ];
8907
+ return `${fields.join(" ")}
8908
+ `;
8909
+ }
8910
+ var sigMask = (mask = 0) => (mask >>> 1).toString(16).padStart(16, "0");
8911
+ function pidStatus(info) {
8912
+ const state = info.state === "Z" ? "Z (zombie)" : "S (sleeping)";
8913
+ const c = info.cred ?? ROOT;
8914
+ const ids = (r, e, s) => `${r} ${e} ${s} ${e}`;
8915
+ return [
8916
+ `Name: ${commOf(info)}`,
8917
+ `Umask: ${(info.umask ?? 18).toString(8).padStart(4, "0")}`,
8918
+ `State: ${state}`,
8919
+ `Tgid: ${info.pid}`,
8920
+ "Ngid: 0",
8921
+ `Pid: ${info.pid}`,
8922
+ `PPid: ${info.ppid}`,
8923
+ "TracerPid: 0",
8924
+ `Uid: ${ids(c.ruid, c.euid, c.suid)}`,
8925
+ `Gid: ${ids(c.rgid, c.egid, c.sgid)}`,
8926
+ "FDSize: 64",
8927
+ `VmSize: ${Math.ceil(info.memory / KIB)} kB`,
8928
+ `VmRSS: ${Math.ceil(info.memory / KIB)} kB`,
8929
+ `Groups: ${c.groups.join(" ")}`,
8930
+ `NSpid: ${info.pid}`,
8931
+ `NSpgid: ${info.pgid}`,
8932
+ `NSsid: ${info.sid}`,
8933
+ "Threads: 1",
8934
+ "SigQ: 0/0",
8935
+ "SigPnd: 0000000000000000",
8936
+ "ShdPnd: 0000000000000000",
8937
+ "SigBlk: 0000000000000000",
8938
+ `SigIgn: ${sigMask(info.sigIgn)}`,
8939
+ `SigCgt: ${sigMask(info.sigCgt)}`,
8940
+ ""
8941
+ ].join("\n");
8942
+ }
8943
+ function pidFile(name2, info, boot) {
8944
+ if (name2 === "cmdline") return info.state === "Z" ? "" : `${info.argv.join("\0")}\0`;
8945
+ if (name2 === "comm") return `${commOf(info)}
8946
+ `;
8947
+ if (name2 === "stat") return pidStat(info, boot);
8948
+ if (name2 === "statm") {
8949
+ const p = pages(info.memory);
8950
+ return `${p} ${p} 0 0 0 ${p} 0
8951
+ `;
8952
+ }
8953
+ return pidStatus(info);
8954
+ }
8955
+ function memoryInfo(source, used = 0) {
8956
+ const total = source.deviceMemory ? source.deviceMemory * KIB * KIB * KIB : FALLBACK_MEMORY;
8957
+ const free = Math.max(0, total - used - (source.usedHeap ?? 0));
8958
+ const kb = (bytes) => `${Math.floor(bytes / KIB)} kB`;
8959
+ return [
8960
+ ["MemTotal", kb(total)],
8961
+ ["MemFree", kb(free)],
8962
+ ["MemAvailable", kb(free)],
8963
+ ["Buffers", kb(0)],
8964
+ ["Cached", kb(0)],
8965
+ ["SwapCached", kb(0)],
8966
+ ["Shmem", kb(0)],
8967
+ ["SReclaimable", kb(0)],
8968
+ ["SwapTotal", kb(0)],
8969
+ ["SwapFree", kb(0)]
8970
+ ].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
8971
+ }
8972
+ function systemFile(name2, listing, now, memory) {
8973
+ const up = Math.max(0, now - listing.boot) / 1e3;
8974
+ if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
8975
+ `;
8976
+ if (name2 === "meminfo") {
8977
+ return memoryInfo(
8978
+ memory,
8979
+ listing.processes.reduce((sum, p) => sum + p.memory, 0)
8980
+ );
8981
+ }
8982
+ const live = listing.processes.filter((p) => p.state !== "Z");
8983
+ if (name2 === "loadavg") {
8984
+ const last = Math.max(0, ...listing.processes.map((p) => p.pid));
8985
+ return `0.00 0.00 0.00 1/${live.length} ${last}
8986
+ `;
8987
+ }
8988
+ const idle = ticks(now - listing.boot);
8989
+ return [
8990
+ `cpu 0 0 0 ${idle} 0 0 0 0 0 0`,
8991
+ `cpu0 0 0 0 ${idle} 0 0 0 0 0 0`,
8992
+ "intr 0",
8993
+ "ctxt 0",
8994
+ `btime ${Math.floor(listing.boot / 1e3)}`,
8995
+ `processes ${listing.processes.length}`,
8996
+ "procs_running 1",
8997
+ "procs_blocked 0",
8998
+ ""
8999
+ ].join("\n");
9000
+ }
9001
+ function memorySource(scope = globalThis) {
9002
+ const nav = scope.navigator;
9003
+ const perf = scope.performance;
9004
+ const deviceMemory = nav?.deviceMemory;
9005
+ const used = perf?.memory?.usedJSHeapSize;
9006
+ return {
9007
+ ...typeof deviceMemory === "number" && deviceMemory > 0 ? { deviceMemory } : {},
9008
+ ...typeof used === "number" ? { usedHeap: used } : {}
9009
+ };
9010
+ }
9011
+
8924
9012
  // src/process/wasi/wasi-files.ts
8925
9013
  function normalize(path) {
8926
9014
  const out = [];
@@ -11199,6 +11287,7 @@ var WasmProcess = class {
11199
11287
  throw Object.assign(new Error(alias.refused), { code: alias.refused });
11200
11288
  if (alias !== void 0) return this.openProc(req, alias);
11201
11289
  await refuseReadonly(this.options.fs, req.path, req.flags, req);
11290
+ await this.mayOpen(req);
11202
11291
  const fd = this.fds.install(heldFile(), 3);
11203
11292
  try {
11204
11293
  const nodes = this.nodes;
@@ -11244,6 +11333,17 @@ var WasmProcess = class {
11244
11333
  };
11245
11334
  return fs.exclusive ? fs.exclusive(first.path, look) : look();
11246
11335
  }
11336
+ async mayOpen(req) {
11337
+ const { guard: guard2 } = this.options;
11338
+ const inherited2 = req.orphan || req.contents !== void 0 || req.handle !== void 0;
11339
+ if (!guard2 || inherited2 || req.pin !== void 0) return;
11340
+ const g = { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid };
11341
+ if (req.exclusive) return inParent(g, "open", req.path);
11342
+ if (req.created && guard2.created?.take(req.path)) return;
11343
+ const access2 = req.flags & WRITE_ACCESS;
11344
+ const want = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
11345
+ await openable(g, "open", req.path, want, req.create === true);
11346
+ }
11247
11347
  newOwner() {
11248
11348
  if (!this.options.owners) return void 0;
11249
11349
  return { uid: this.cred.euid, gid: this.cred.egid, mode: 438 & ~this.umask };
@@ -11928,7 +12028,7 @@ function inherited(opts) {
11928
12028
  function holdingToken(opts, holds, caller) {
11929
12029
  return mintSyncFsToken({
11930
12030
  ...opts.owners ? caller : {},
11931
- ...opts.dac ? { dac: opts.dac, pid: opts.pid } : {},
12031
+ ...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
11932
12032
  fs: opts.fs,
11933
12033
  cwd: opts.cwd,
11934
12034
  ...opts.statfs ? { statfs: opts.statfs } : {},
@@ -11941,10 +12041,11 @@ function holdingToken(opts, holds, caller) {
11941
12041
  renamed: (from, to) => holds.renamed(from, to)
11942
12042
  });
11943
12043
  }
11944
- function processOptions(opts, header3, hooks) {
12044
+ function processOptions(opts, header3, hooks, created3) {
11945
12045
  return {
11946
12046
  ...inherited(opts),
11947
12047
  ...opts.owners ? { owners: true } : {},
12048
+ ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
11948
12049
  ...opts.identity ? { identity: opts.identity } : {},
11949
12050
  ...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
11950
12051
  ...opts.settling ? { settling: opts.settling } : {},
@@ -11978,20 +12079,27 @@ function spawnWasmProcess(opts) {
11978
12079
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
11979
12080
  const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
11980
12081
  const threadSignals = /* @__PURE__ */ new Map();
12082
+ const created3 = new Created();
11981
12083
  const process = new WasmProcess(
11982
12084
  opts.pid,
11983
12085
  opts.fds,
11984
- processOptions(opts, header3, {
11985
- raise: (sig) => signal(sig),
11986
- threadKill: (tid, sig) => killThread(threadSignals.get(tid), process.signalMasks().caught, sig)
11987
- })
12086
+ processOptions(
12087
+ opts,
12088
+ header3,
12089
+ {
12090
+ raise: (sig) => signal(sig),
12091
+ threadKill: (tid, sig) => killThread(threadSignals.get(tid), process.signalMasks().caught, sig)
12092
+ },
12093
+ created3
12094
+ )
11988
12095
  );
11989
12096
  publishStops(process, header3);
11990
12097
  const holds = new HeldPaths();
11991
12098
  opts.held?.add(holds);
11992
12099
  const token = holdingToken(opts, holds, {
11993
12100
  cred: () => process.cred,
11994
- umask: () => process.umask
12101
+ umask: () => process.umask,
12102
+ created: created3
11995
12103
  });
11996
12104
  process.keptPath = (path) => holds.keptAt(path)?.hidden;
11997
12105
  process.keptHandle = (handle2) => holds.keptFor(handle2)?.hidden;
@@ -13104,16 +13212,16 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
13104
13212
  });
13105
13213
  async function writable2(client, path, flags) {
13106
13214
  const { ifMatch: _, ...asked } = flags;
13107
- const opening = () => client.call("open", { path: rel(path), ...asked });
13215
+ const opening2 = () => client.call("open", { path: rel(path), ...asked });
13108
13216
  try {
13109
- return { opened: await opening(), restore: void 0 };
13217
+ return { opened: await opening2(), restore: void 0 };
13110
13218
  } catch (err) {
13111
13219
  if (!flags.write || codeOf4(err) !== "EACCES") throw err;
13112
13220
  const was = await client.call("stat", { path: rel(path) });
13113
13221
  const mode = was.mode ?? 0;
13114
13222
  if (mode & 128) throw err;
13115
13223
  const restore = { path: rel(path), mode, was };
13116
- const opened2 = await client.call("setattr", { path: rel(path), mode: mode | 128 }).then(opening).catch(async (again) => {
13224
+ const opened2 = await client.call("setattr", { path: rel(path), mode: mode | 128 }).then(opening2).catch(async (again) => {
13117
13225
  await settle3(client, restore).catch(() => {
13118
13226
  });
13119
13227
  throw again;
@@ -14371,15 +14479,33 @@ var Launcher = class {
14371
14479
  return void 0;
14372
14480
  }
14373
14481
  const inner = await this.interpreted(interp, [interp, ...passed], cwd, scope, depth + 1);
14374
- return inner && { ...inner, target: withScriptEnv(inner.target, command) };
14482
+ const via = [interp, ...inner?.via ?? []];
14483
+ return inner && { ...inner, target: withScriptEnv(inner.target, command), via };
14375
14484
  }
14376
14485
  const target = withScriptEnv(found, command);
14377
- return { target, args: [...target.prefix ?? [], ...passed] };
14486
+ return { target, args: [...target.prefix ?? [], ...passed], via: [interp] };
14378
14487
  }
14379
14488
  async plan(file, argv, cwd, scope) {
14380
14489
  const direct = await this.resolve(file, argv[0] ?? file, cwd, scope);
14381
14490
  if (direct) return { target: direct, args: [...direct.prefix ?? [], ...argv.slice(1)] };
14382
- return this.interpreted(file, argv, cwd, scope);
14491
+ const script = await this.interpreted(file, argv, cwd, scope);
14492
+ return script && { ...script, script: true };
14493
+ }
14494
+ async mayExec(cred, req, planned) {
14495
+ const { dac } = this;
14496
+ if (!dac) return;
14497
+ const g = { fs: this.fs, cred, pid: req.pid, dac };
14498
+ const byPath = (file) => file.includes("/") && !COMMAND2.test(file);
14499
+ if (byPath(req.file)) {
14500
+ const path = this.fs.resolvePath(req.cwd, req.file);
14501
+ await need(g, "exec", path, planned?.script ? R_OK | X_OK : X_OK);
14502
+ }
14503
+ const via = planned?.via ?? [];
14504
+ for (const [i, file] of via.entries()) {
14505
+ if (!byPath(file)) continue;
14506
+ const want = i < via.length - 1 ? R_OK | X_OK : X_OK;
14507
+ await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
14508
+ }
14383
14509
  }
14384
14510
  async unrunnable(file, cwd, scope) {
14385
14511
  const path = this.fs.resolvePath(cwd, file);
@@ -14599,6 +14725,12 @@ var Launcher = class {
14599
14725
  return async (req, fds) => {
14600
14726
  const scope = await this.scopeOf(this.credOf(ppid).euid);
14601
14727
  const planned = await this.plan(req.file, req.argv, req.cwd, scope);
14728
+ try {
14729
+ await this.mayExec(this.credOf(ppid), { ...req, pid: ppid }, planned);
14730
+ } catch (err) {
14731
+ await fds.closeAll();
14732
+ throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
14733
+ }
14602
14734
  if (!planned) {
14603
14735
  await fds.closeAll();
14604
14736
  throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
@@ -14882,6 +15014,7 @@ var Launcher = class {
14882
15014
  }
14883
15015
  async prepare() {
14884
15016
  for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
15017
+ if (this.owners) await this.base.chmod("/tmp", 1023);
14885
15018
  await this.provisionPnpmHome();
14886
15019
  await this.users.ensure();
14887
15020
  await writeCaFile(this.base, this.ca).catch(() => void 0);
@@ -14929,7 +15062,9 @@ var Launcher = class {
14929
15062
  const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
14930
15063
  const [file = ""] = argv;
14931
15064
  await this.base.mkdir(cwd, { recursive: true });
14932
- return { cwd, file, planned: await this.plan(file, argv, cwd, scope) };
15065
+ const planned = await this.plan(file, argv, cwd, scope);
15066
+ await this.mayExec(credOf(account), { file, cwd, pid: 0 }, planned);
15067
+ return { cwd, file, planned };
14933
15068
  }
14934
15069
  async openTerminal(argv, options) {
14935
15070
  const account = await this.account(options.user);