@ai-ecoverse/slicc-kernel 1.62.4 → 1.64.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.
@@ -427,13 +427,21 @@ var OpfsFs = class {
427
427
  trackOwners() {
428
428
  this.owners = true;
429
429
  }
430
+ async hasOwner(path) {
431
+ return ownOf((await this.meta.get([path]))[0]) !== void 0;
432
+ }
430
433
  async ancestry(path) {
431
434
  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
- }));
435
+ const out = [];
436
+ for (const [i, entry] of entries.entries()) {
437
+ const link2 = entry?.link !== void 0 && !await this.real(entry.path);
438
+ out.push({
439
+ ...ownOf(entry) ?? (i === 0 ? ROOT_OWNER : inherit(entries.slice(0, i))),
440
+ ...entry?.mode !== void 0 ? { mode: entry.mode } : {},
441
+ ...link2 ? { link: entry.link } : {}
442
+ });
443
+ }
444
+ return out;
437
445
  }
438
446
  async handedTo(dir) {
439
447
  return inherit(await this.meta.get(chain(dir)));
@@ -1397,15 +1405,24 @@ async function present(fs, path, directory) {
1397
1405
  const st = await fs.lstat(path).catch(() => void 0);
1398
1406
  return st !== void 0 && (!directory || st.isDirectory);
1399
1407
  }
1408
+ async function finalOf(fs, walked, want, options) {
1409
+ const perm = walked.perm;
1410
+ if ((want & X_OK) === 0 || options.directory !== void 0) {
1411
+ return [perm, options.directory !== false];
1412
+ }
1413
+ const look = options.follow === false ? fs.lstat : fs.stat;
1414
+ const st = await look.call(fs, walked.path).catch(() => void 0);
1415
+ return st ? [permOf(st), st.isDirectory] : [perm, false];
1416
+ }
1400
1417
  async function access(fs, cred, path, want, options = {}) {
1401
1418
  const walked = await walk(fs, cred, path, options.follow !== false);
1402
1419
  if (walked.denied !== void 0) {
1403
1420
  if (await present(fs, walked.denied, true)) return walked;
1404
1421
  return { path, perm: void 0, missing: true };
1405
1422
  }
1406
- const { perm } = walked;
1407
- if (!perm) return { ...walked, missing: true };
1408
- if (want === 0 || permits(cred, perm, want, options.directory !== false)) return walked;
1423
+ if (!walked.perm) return { ...walked, missing: true };
1424
+ const [perm, directory] = await finalOf(fs, walked, want, options);
1425
+ if (want === 0 || permits(cred, perm, want, directory)) return walked;
1409
1426
  if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
1410
1427
  return { ...walked, missing: true };
1411
1428
  }
@@ -1416,6 +1433,7 @@ function refused(path, errno = "EACCES") {
1416
1433
  // src/fs/dac.ts
1417
1434
  var MAX_DENIAL_KEYS = 1e4;
1418
1435
  var S_ISGID3 = 1024;
1436
+ var DIR = { directory: true };
1419
1437
  var Dac = class {
1420
1438
  mode;
1421
1439
  hook;
@@ -1452,6 +1470,17 @@ var Dac = class {
1452
1470
  );
1453
1471
  }
1454
1472
  };
1473
+ var MAX_CREATED = 1024;
1474
+ var Created = class {
1475
+ paths = /* @__PURE__ */ new Set();
1476
+ add(path) {
1477
+ if (this.paths.size >= MAX_CREATED) this.paths.clear();
1478
+ this.paths.add(normalizePath(path));
1479
+ }
1480
+ take(path) {
1481
+ return this.paths.delete(normalizePath(path));
1482
+ }
1483
+ };
1455
1484
  function deny(g, op, path, errno = "EACCES") {
1456
1485
  g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno });
1457
1486
  }
@@ -1460,20 +1489,30 @@ async function need(g, op, path, want, options = {}, shown = path) {
1460
1489
  if (walked.denied !== void 0) deny(g, op, shown);
1461
1490
  return walked;
1462
1491
  }
1492
+ function entryIn(dir, path) {
1493
+ return joinName(dir.path, path.slice(path.lastIndexOf("/") + 1));
1494
+ }
1463
1495
  async function stickyEntry(g, op, path, dir) {
1464
1496
  const perm = dir.perm;
1465
1497
  if (!perm || sticky(g.cred, perm, perm)) return;
1466
- const entry = joinName(dir.path, path.slice(path.lastIndexOf("/") + 1));
1498
+ const entry = entryIn(dir, path);
1467
1499
  if (sticky(g.cred, perm, (await ancestryOf(g.fs, entry)).at(-1))) return;
1468
1500
  if (await present(g.fs, entry, false)) deny(g, op, path, "EPERM");
1469
1501
  }
1470
1502
  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);
1503
+ if (old === "replaced") {
1504
+ const dir2 = await need(g, op, parentOf3(path), W_OK | X_OK, DIR, path);
1505
+ return dir2.denied === void 0 ? stickyEntry(g, op, path, dir2) : void 0;
1506
+ }
1507
+ if (!old) return void await need(g, op, parentOf3(path), W_OK | X_OK, DIR, path);
1508
+ const dir = await access(g.fs, g.cred, parentOf3(path), W_OK | X_OK, DIR);
1509
+ if (dir.denied === void 0) return stickyEntry(g, op, path, dir);
1510
+ const lookup = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
1511
+ if (!lookup || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
1473
1512
  }
1474
1513
  async function makeIn(g, op, path) {
1475
1514
  for (let dir = parentOf3(path); ; dir = parentOf3(dir)) {
1476
- const walked = await access(g.fs, g.cred, dir, W_OK | X_OK);
1515
+ const walked = await access(g.fs, g.cred, dir, W_OK | X_OK, DIR);
1477
1516
  if (walked.denied !== void 0) {
1478
1517
  if (!await present(g.fs, path, true)) deny(g, op, path);
1479
1518
  return;
@@ -1499,7 +1538,7 @@ async function removable(g, op, path) {
1499
1538
  async function renamable(g, from, to) {
1500
1539
  if (from === to) return void await need(g, "rename", from, 0, { follow: false });
1501
1540
  await inParent(g, "rename", from, true);
1502
- await inParent(g, "rename", to, true);
1541
+ await inParent(g, "rename", to, "replaced");
1503
1542
  if (g.cred.euid === 0 || parentOf3(from) === parentOf3(to)) return;
1504
1543
  const st = await g.fs.lstat(from).catch(() => void 0);
1505
1544
  if (st?.isDirectory && !permits(g.cred, permOf(st), W_OK)) deny(g, "rename", from);
@@ -1525,8 +1564,20 @@ async function chmodMode(g, path, mode) {
1525
1564
  if (!g.dac.enforcing || !perm || g.cred.euid === 0 || inGroup(g.cred, perm.gid)) return mode;
1526
1565
  return mode & ~S_ISGID3;
1527
1566
  }
1528
- async function retimable(g, op, path) {
1529
- await ownerOnly(g, op, path, op !== "lutimes");
1567
+ async function retimable(g, op, path, now = false) {
1568
+ const follow2 = op !== "lutimes";
1569
+ if (!now) return void await ownerOnly(g, op, path, follow2);
1570
+ const walked = await need(g, op, path, 0, { follow: follow2 });
1571
+ const perm = walked.perm;
1572
+ if (!perm || g.cred.euid === 0 || perm.uid === g.cred.euid) return;
1573
+ if (permits(g.cred, perm, W_OK, false)) return;
1574
+ if (await present(g.fs, walked.path, false)) deny(g, op, path);
1575
+ }
1576
+ async function fileWritable(g, op, path) {
1577
+ await need(g, op, path, W_OK, { directory: false });
1578
+ }
1579
+ function realIds(cred) {
1580
+ return { ...cred, groups: [...cred.groups], euid: cred.ruid, egid: cred.rgid };
1530
1581
  }
1531
1582
  async function listable(g, op, path) {
1532
1583
  await need(g, op, path, R_OK | X_OK, { directory: true });
@@ -1538,10 +1589,16 @@ async function readable(g, op, path) {
1538
1589
  await need(g, op, path, R_OK, { directory: false });
1539
1590
  }
1540
1591
  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 });
1592
+ await openable(g, op, path, W_OK, true);
1593
+ }
1594
+ async function creatable(g, op, path) {
1543
1595
  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);
1596
+ if (target.denied === void 0) await need(g, op, parentOf3(target.path), W_OK | X_OK, DIR, path);
1597
+ }
1598
+ async function openable(g, op, path, want, create) {
1599
+ const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
1600
+ if (create && !st) return creatable(g, op, path);
1601
+ await need(g, op, path, want, { directory: st?.isDirectory ?? false });
1545
1602
  }
1546
1603
 
1547
1604
  // src/kernel/pipe.ts
@@ -4980,11 +5037,11 @@ function realmCa(owner, store) {
4980
5037
  opened.set(store, cas);
4981
5038
  let ca = cas.get(owner);
4982
5039
  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);
5040
+ const opening2 = RealmCa.open(owner, store);
5041
+ ca = opening2;
5042
+ cas.set(owner, opening2);
5043
+ opening2.catch(() => {
5044
+ if (cas.get(owner) === opening2) cas.delete(owner);
4988
5045
  });
4989
5046
  }
4990
5047
  return ca;
@@ -6373,6 +6430,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
6373
6430
  ...fs.realpath ? { realpath: fs.realpath.bind(fs) } : {},
6374
6431
  ...fs.chown ? { chown: fs.chown.bind(fs) } : {},
6375
6432
  ...fs.ancestry ? { ancestry: fs.ancestry.bind(fs) } : {},
6433
+ ...fs.hasOwner ? { hasOwner: fs.hasOwner.bind(fs) } : {},
6376
6434
  chmod: (path, mode) => fs.chmod(path, mode),
6377
6435
  utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
6378
6436
  lutimes: (path, atime, mtime) => lutimesOf(fs, path, atime, mtime),
@@ -6472,8 +6530,10 @@ var UnlinkedKeeper = class {
6472
6530
  count = 0;
6473
6531
  links = /* @__PURE__ */ new Map();
6474
6532
  linked = /* @__PURE__ */ new Map();
6533
+ dirOwner;
6475
6534
  constructor(fs, holders, rootOf, options = {}) {
6476
6535
  const { locks, owner } = options;
6536
+ this.dirOwner = options.dirOwner;
6477
6537
  this.fs = fs;
6478
6538
  this.holders = holders;
6479
6539
  this.rootOf = rootOf;
@@ -6584,7 +6644,8 @@ var UnlinkedKeeper = class {
6584
6644
  const dir = dirOf(this.rootOf(path));
6585
6645
  const hidden = `${dir}/${this.instance}.${++this.count}`;
6586
6646
  try {
6587
- await this.fs.mkdir(dir, { recursive: true });
6647
+ const owner = this.dirOwner;
6648
+ await this.fs.mkdir(dir, { recursive: true, ...owner ? { owner } : {} });
6588
6649
  await this.fs.rename(path, hidden);
6589
6650
  await this.linkedName(hidden);
6590
6651
  return hidden;
@@ -7963,6 +8024,10 @@ var MountFs = class {
7963
8024
  if (this.at(real)) throw errnoError("ENOSYS", path);
7964
8025
  return real;
7965
8026
  }
8027
+ async hasOwner(path) {
8028
+ if (this.at(path)) return true;
8029
+ return await this.base.hasOwner?.(path) ?? true;
8030
+ }
7966
8031
  async ancestry(path) {
7967
8032
  const names = chain(normalizePath(path));
7968
8033
  const first = names.findIndex((name2) => this.at(name2));
@@ -8052,6 +8117,9 @@ function revokeSyncFsToken(token) {
8052
8117
  }
8053
8118
 
8054
8119
  // src/realm/sync-fs-wire.ts
8120
+ var HOLD_READ = 4;
8121
+ var HOLD_WRITE = 8;
8122
+ var HOLD_CREATED = 16;
8055
8123
  var SYNC_FS_REQUEST_TIMEOUT_MS = 25e3;
8056
8124
  function syncError(code, label) {
8057
8125
  return Object.assign(new Error(`${code}: ${label}`), { code });
@@ -8175,6 +8243,7 @@ async function run(entry, path, req) {
8175
8243
  return done;
8176
8244
  case "create":
8177
8245
  await createExclusive(fs, path, ownerFor2(entry, req.mode ?? 438));
8246
+ entry.created?.add(path);
8178
8247
  return done;
8179
8248
  case "readlink":
8180
8249
  return json2(await fs.readlink(path));
@@ -8231,11 +8300,47 @@ async function guard(g, path, req, cwd) {
8231
8300
  return linkable(g, path, other(), req.mode === 1);
8232
8301
  case "utimes":
8233
8302
  case "lutimes":
8234
- return retimable(g, op, path);
8303
+ return retimable(g, op, path, req.now === true);
8304
+ case "read":
8305
+ case "pread":
8306
+ return req.handle === void 0 ? readable(g, op, path) : void 0;
8307
+ case "pwrite":
8308
+ case "truncate":
8309
+ return req.handle === void 0 ? fileWritable(g, op, path) : void 0;
8310
+ case "write":
8311
+ return req.handle === void 0 && req.mode !== 1 ? writable(g, op, path) : void 0;
8235
8312
  default:
8236
8313
  return void 0;
8237
8314
  }
8238
8315
  }
8316
+ async function opening(entry, path, mode) {
8317
+ const g = guardOf(entry);
8318
+ const want = (mode & HOLD_READ ? R_OK : 0) | (mode & HOLD_WRITE ? W_OK : 0);
8319
+ const created3 = (mode & HOLD_CREATED) !== 0 && entry.created?.take(path) === true;
8320
+ if (g && want !== 0 && !created3) {
8321
+ await need(g, "open", path, want, { directory: false });
8322
+ }
8323
+ }
8324
+ async function holdOp(entry, path, req) {
8325
+ const mode = req.mode ?? 0;
8326
+ const [on, open] = [(mode & 1) === 1, (mode & 2) === 2];
8327
+ if (on && open) {
8328
+ await opening(entry, path, mode);
8329
+ const real = await entry.fs.realpath?.(path, true).catch(() => path);
8330
+ return json2(entry.hold?.(path, on, open, real) ?? null);
8331
+ }
8332
+ entry.hold?.(path, on, open, void 0, req.handle);
8333
+ return done;
8334
+ }
8335
+ async function accessOp(entry, path, req) {
8336
+ const g = guardOf(entry);
8337
+ if (!g) return done;
8338
+ const open = req.arg2 === "open";
8339
+ const cred = open || req.arg2 === "eaccess" ? g.cred : realIds(g.cred);
8340
+ const options = { follow: req.nofollow !== true, ...open ? { directory: false } : {} };
8341
+ await need({ ...g, cred }, open ? "open" : "access", path, req.mode ?? 0, options);
8342
+ return done;
8343
+ }
8239
8344
  async function guarded(entry, path, req) {
8240
8345
  const g = guardOf(entry);
8241
8346
  if (!g) return void 0;
@@ -8299,16 +8404,6 @@ async function owned(entry, own, req, path, target) {
8299
8404
  }
8300
8405
  return own(target, () => run(entry, path, req));
8301
8406
  }
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
8407
  var IDENTITY_OPS = /* @__PURE__ */ new Set([
8313
8408
  "write",
8314
8409
  "pwrite",
@@ -8345,7 +8440,8 @@ async function dispatchOne(entry, req) {
8345
8440
  const path = fs.resolvePath(cwd, req.path);
8346
8441
  if (req.op === "kept") return json2(entry.keptFor?.(req.handle ?? -1) !== void 0);
8347
8442
  if (req.op === "stat" && req.handle !== void 0) return await keptStat(entry, req);
8348
- if (req.op === "hold") return holdOp(entry, path, req);
8443
+ if (req.op === "access") return await accessOp(entry, path, req);
8444
+ if (req.op === "hold") return await holdOp(entry, path, req);
8349
8445
  const refused2 = await guarded(entry, path, req);
8350
8446
  if (refused2) return refused2;
8351
8447
  if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
@@ -8514,188 +8610,6 @@ function attachSyncSabResponder(port, sab, token, opts) {
8514
8610
  };
8515
8611
  }
8516
8612
 
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
8613
  // src/fs/pages.ts
8700
8614
  var PAGE = 64 * 1024;
8701
8615
  var READAHEAD = 1024 * 1024;
@@ -8921,6 +8835,191 @@ var AsyncRangedFile = class {
8921
8835
  }
8922
8836
  };
8923
8837
 
8838
+ // src/realm/live-vfs-fs.ts
8839
+ var EARLY_WHOLE_FILE = 64 * 1024;
8840
+
8841
+ // src/process/process-fds.ts
8842
+ var field2 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
8843
+ function mountTable(mounts = []) {
8844
+ const root = globalThis.navigator?.storage ? "opfs" : "memory";
8845
+ const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
8846
+ for (const m of mounts) {
8847
+ const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
8848
+ if (m.state !== "ok") options.push(m.state);
8849
+ const access2 = options.includes("ro") ? [] : ["rw"];
8850
+ lines2.push(
8851
+ `${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
8852
+ );
8853
+ }
8854
+ return `${lines2.join("\n")}
8855
+ `;
8856
+ }
8857
+
8858
+ // src/process/procfs.ts
8859
+ var HZ = 100;
8860
+ var KIB = 1024;
8861
+ var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
8862
+ var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
8863
+ var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
8864
+ function commOf(info) {
8865
+ const name2 = info.argv[0] ?? "";
8866
+ return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
8867
+ }
8868
+ function ttyNumber(tty) {
8869
+ const pts = tty && /^\/dev\/pts\/(\d+)$/.exec(tty);
8870
+ if (pts) return 136 << 8 | Number(pts[1]);
8871
+ const vt = tty && /^\/dev\/tty(\d+)$/.exec(tty);
8872
+ return vt ? 4 << 8 | Number(vt[1]) : 0;
8873
+ }
8874
+ function pages(bytes) {
8875
+ return Math.ceil(bytes / 4096);
8876
+ }
8877
+ function ticks(ms) {
8878
+ return Math.max(0, Math.floor(ms * HZ / 1e3));
8879
+ }
8880
+ function pidStat(info, boot) {
8881
+ const fields = [
8882
+ info.pid,
8883
+ `(${commOf(info)})`,
8884
+ info.state,
8885
+ info.ppid,
8886
+ info.pgid,
8887
+ info.sid,
8888
+ ttyNumber(info.tty),
8889
+ info.tty ? info.pgid : -1,
8890
+ 4194304,
8891
+ 0,
8892
+ 0,
8893
+ 0,
8894
+ 0,
8895
+ 0,
8896
+ 0,
8897
+ 0,
8898
+ 0,
8899
+ 20,
8900
+ 0,
8901
+ 1,
8902
+ 0,
8903
+ ticks(info.started - boot),
8904
+ info.memory,
8905
+ pages(info.memory),
8906
+ "18446744073709551615",
8907
+ ...Array(12).fill(0),
8908
+ 17,
8909
+ 0,
8910
+ 0,
8911
+ 0,
8912
+ 0,
8913
+ 0,
8914
+ 0,
8915
+ ...Array(7).fill(0),
8916
+ 0
8917
+ ];
8918
+ return `${fields.join(" ")}
8919
+ `;
8920
+ }
8921
+ var sigMask = (mask = 0) => (mask >>> 1).toString(16).padStart(16, "0");
8922
+ function pidStatus(info) {
8923
+ const state = info.state === "Z" ? "Z (zombie)" : "S (sleeping)";
8924
+ const c = info.cred ?? ROOT;
8925
+ const ids = (r, e, s) => `${r} ${e} ${s} ${e}`;
8926
+ return [
8927
+ `Name: ${commOf(info)}`,
8928
+ `Umask: ${(info.umask ?? 18).toString(8).padStart(4, "0")}`,
8929
+ `State: ${state}`,
8930
+ `Tgid: ${info.pid}`,
8931
+ "Ngid: 0",
8932
+ `Pid: ${info.pid}`,
8933
+ `PPid: ${info.ppid}`,
8934
+ "TracerPid: 0",
8935
+ `Uid: ${ids(c.ruid, c.euid, c.suid)}`,
8936
+ `Gid: ${ids(c.rgid, c.egid, c.sgid)}`,
8937
+ "FDSize: 64",
8938
+ `VmSize: ${Math.ceil(info.memory / KIB)} kB`,
8939
+ `VmRSS: ${Math.ceil(info.memory / KIB)} kB`,
8940
+ `Groups: ${c.groups.join(" ")}`,
8941
+ `NSpid: ${info.pid}`,
8942
+ `NSpgid: ${info.pgid}`,
8943
+ `NSsid: ${info.sid}`,
8944
+ "Threads: 1",
8945
+ "SigQ: 0/0",
8946
+ "SigPnd: 0000000000000000",
8947
+ "ShdPnd: 0000000000000000",
8948
+ "SigBlk: 0000000000000000",
8949
+ `SigIgn: ${sigMask(info.sigIgn)}`,
8950
+ `SigCgt: ${sigMask(info.sigCgt)}`,
8951
+ ""
8952
+ ].join("\n");
8953
+ }
8954
+ function pidFile(name2, info, boot) {
8955
+ if (name2 === "cmdline") return info.state === "Z" ? "" : `${info.argv.join("\0")}\0`;
8956
+ if (name2 === "comm") return `${commOf(info)}
8957
+ `;
8958
+ if (name2 === "stat") return pidStat(info, boot);
8959
+ if (name2 === "statm") {
8960
+ const p = pages(info.memory);
8961
+ return `${p} ${p} 0 0 0 ${p} 0
8962
+ `;
8963
+ }
8964
+ return pidStatus(info);
8965
+ }
8966
+ function memoryInfo(source, used = 0) {
8967
+ const total = source.deviceMemory ? source.deviceMemory * KIB * KIB * KIB : FALLBACK_MEMORY;
8968
+ const free = Math.max(0, total - used - (source.usedHeap ?? 0));
8969
+ const kb = (bytes) => `${Math.floor(bytes / KIB)} kB`;
8970
+ return [
8971
+ ["MemTotal", kb(total)],
8972
+ ["MemFree", kb(free)],
8973
+ ["MemAvailable", kb(free)],
8974
+ ["Buffers", kb(0)],
8975
+ ["Cached", kb(0)],
8976
+ ["SwapCached", kb(0)],
8977
+ ["Shmem", kb(0)],
8978
+ ["SReclaimable", kb(0)],
8979
+ ["SwapTotal", kb(0)],
8980
+ ["SwapFree", kb(0)]
8981
+ ].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
8982
+ }
8983
+ function systemFile(name2, listing, now, memory) {
8984
+ const up = Math.max(0, now - listing.boot) / 1e3;
8985
+ if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
8986
+ `;
8987
+ if (name2 === "meminfo") {
8988
+ return memoryInfo(
8989
+ memory,
8990
+ listing.processes.reduce((sum, p) => sum + p.memory, 0)
8991
+ );
8992
+ }
8993
+ const live = listing.processes.filter((p) => p.state !== "Z");
8994
+ if (name2 === "loadavg") {
8995
+ const last = Math.max(0, ...listing.processes.map((p) => p.pid));
8996
+ return `0.00 0.00 0.00 1/${live.length} ${last}
8997
+ `;
8998
+ }
8999
+ const idle = ticks(now - listing.boot);
9000
+ return [
9001
+ `cpu 0 0 0 ${idle} 0 0 0 0 0 0`,
9002
+ `cpu0 0 0 0 ${idle} 0 0 0 0 0 0`,
9003
+ "intr 0",
9004
+ "ctxt 0",
9005
+ `btime ${Math.floor(listing.boot / 1e3)}`,
9006
+ `processes ${listing.processes.length}`,
9007
+ "procs_running 1",
9008
+ "procs_blocked 0",
9009
+ ""
9010
+ ].join("\n");
9011
+ }
9012
+ function memorySource(scope = globalThis) {
9013
+ const nav = scope.navigator;
9014
+ const perf = scope.performance;
9015
+ const deviceMemory = nav?.deviceMemory;
9016
+ const used = perf?.memory?.usedJSHeapSize;
9017
+ return {
9018
+ ...typeof deviceMemory === "number" && deviceMemory > 0 ? { deviceMemory } : {},
9019
+ ...typeof used === "number" ? { usedHeap: used } : {}
9020
+ };
9021
+ }
9022
+
8924
9023
  // src/process/wasi/wasi-files.ts
8925
9024
  function normalize(path) {
8926
9025
  const out = [];
@@ -11199,6 +11298,7 @@ var WasmProcess = class {
11199
11298
  throw Object.assign(new Error(alias.refused), { code: alias.refused });
11200
11299
  if (alias !== void 0) return this.openProc(req, alias);
11201
11300
  await refuseReadonly(this.options.fs, req.path, req.flags, req);
11301
+ await this.mayOpen(req);
11202
11302
  const fd = this.fds.install(heldFile(), 3);
11203
11303
  try {
11204
11304
  const nodes = this.nodes;
@@ -11244,6 +11344,17 @@ var WasmProcess = class {
11244
11344
  };
11245
11345
  return fs.exclusive ? fs.exclusive(first.path, look) : look();
11246
11346
  }
11347
+ async mayOpen(req) {
11348
+ const { guard: guard2 } = this.options;
11349
+ const inherited2 = req.orphan || req.contents !== void 0 || req.handle !== void 0;
11350
+ if (!guard2 || inherited2 || req.pin !== void 0) return;
11351
+ const g = { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid };
11352
+ if (req.exclusive) return inParent(g, "open", req.path);
11353
+ if (req.created && guard2.created?.take(req.path)) return;
11354
+ const access2 = req.flags & WRITE_ACCESS;
11355
+ const want = (access2 === 1 ? 0 : R_OK) | (access2 !== 0 || req.truncate ? W_OK : 0);
11356
+ await openable(g, "open", req.path, want, req.create === true);
11357
+ }
11247
11358
  newOwner() {
11248
11359
  if (!this.options.owners) return void 0;
11249
11360
  return { uid: this.cred.euid, gid: this.cred.egid, mode: 438 & ~this.umask };
@@ -11928,7 +12039,7 @@ function inherited(opts) {
11928
12039
  function holdingToken(opts, holds, caller) {
11929
12040
  return mintSyncFsToken({
11930
12041
  ...opts.owners ? caller : {},
11931
- ...opts.dac ? { dac: opts.dac, pid: opts.pid } : {},
12042
+ ...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
11932
12043
  fs: opts.fs,
11933
12044
  cwd: opts.cwd,
11934
12045
  ...opts.statfs ? { statfs: opts.statfs } : {},
@@ -11941,10 +12052,11 @@ function holdingToken(opts, holds, caller) {
11941
12052
  renamed: (from, to) => holds.renamed(from, to)
11942
12053
  });
11943
12054
  }
11944
- function processOptions(opts, header3, hooks) {
12055
+ function processOptions(opts, header3, hooks, created3) {
11945
12056
  return {
11946
12057
  ...inherited(opts),
11947
12058
  ...opts.owners ? { owners: true } : {},
12059
+ ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
11948
12060
  ...opts.identity ? { identity: opts.identity } : {},
11949
12061
  ...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
11950
12062
  ...opts.settling ? { settling: opts.settling } : {},
@@ -11978,20 +12090,27 @@ function spawnWasmProcess(opts) {
11978
12090
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
11979
12091
  const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
11980
12092
  const threadSignals = /* @__PURE__ */ new Map();
12093
+ const created3 = new Created();
11981
12094
  const process = new WasmProcess(
11982
12095
  opts.pid,
11983
12096
  opts.fds,
11984
- processOptions(opts, header3, {
11985
- raise: (sig) => signal(sig),
11986
- threadKill: (tid, sig) => killThread(threadSignals.get(tid), process.signalMasks().caught, sig)
11987
- })
12097
+ processOptions(
12098
+ opts,
12099
+ header3,
12100
+ {
12101
+ raise: (sig) => signal(sig),
12102
+ threadKill: (tid, sig) => killThread(threadSignals.get(tid), process.signalMasks().caught, sig)
12103
+ },
12104
+ created3
12105
+ )
11988
12106
  );
11989
12107
  publishStops(process, header3);
11990
12108
  const holds = new HeldPaths();
11991
12109
  opts.held?.add(holds);
11992
12110
  const token = holdingToken(opts, holds, {
11993
12111
  cred: () => process.cred,
11994
- umask: () => process.umask
12112
+ umask: () => process.umask,
12113
+ created: created3
11995
12114
  });
11996
12115
  process.keptPath = (path) => holds.keptAt(path)?.hidden;
11997
12116
  process.keptHandle = (handle2) => holds.keptFor(handle2)?.hidden;
@@ -13104,16 +13223,16 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
13104
13223
  });
13105
13224
  async function writable2(client, path, flags) {
13106
13225
  const { ifMatch: _, ...asked } = flags;
13107
- const opening = () => client.call("open", { path: rel(path), ...asked });
13226
+ const opening2 = () => client.call("open", { path: rel(path), ...asked });
13108
13227
  try {
13109
- return { opened: await opening(), restore: void 0 };
13228
+ return { opened: await opening2(), restore: void 0 };
13110
13229
  } catch (err) {
13111
13230
  if (!flags.write || codeOf4(err) !== "EACCES") throw err;
13112
13231
  const was = await client.call("stat", { path: rel(path) });
13113
13232
  const mode = was.mode ?? 0;
13114
13233
  if (mode & 128) throw err;
13115
13234
  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) => {
13235
+ const opened2 = await client.call("setattr", { path: rel(path), mode: mode | 128 }).then(opening2).catch(async (again) => {
13117
13236
  await settle3(client, restore).catch(() => {
13118
13237
  });
13119
13238
  throw again;
@@ -14018,6 +14137,16 @@ var NOT_FOUND = 127;
14018
14137
  var DECIDE_MS = 100;
14019
14138
  var INIT_PID = 1;
14020
14139
  var SHARED_DIRS = ["/tmp", "/home"];
14140
+ var LAYOUT = [
14141
+ ["/", 493],
14142
+ ["/root", 448],
14143
+ ["/home", 493],
14144
+ ["/etc", 493],
14145
+ [PASSWD, 420],
14146
+ [GROUP, 420],
14147
+ ["/usr", 493],
14148
+ ["/usr/local", 493]
14149
+ ];
14021
14150
  var encoder3 = new TextEncoder();
14022
14151
  var USERS_MODES = ["off", "report", "enforce"];
14023
14152
  function usersMode(mode = "off") {
@@ -14211,7 +14340,8 @@ var Launcher = class {
14211
14340
  {
14212
14341
  locks: webLocks(),
14213
14342
  busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
14214
- owner: options.unlinkOwner
14343
+ owner: options.unlinkOwner,
14344
+ ...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
14215
14345
  }
14216
14346
  );
14217
14347
  this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
@@ -14371,15 +14501,33 @@ var Launcher = class {
14371
14501
  return void 0;
14372
14502
  }
14373
14503
  const inner = await this.interpreted(interp, [interp, ...passed], cwd, scope, depth + 1);
14374
- return inner && { ...inner, target: withScriptEnv(inner.target, command) };
14504
+ const via = [interp, ...inner?.via ?? []];
14505
+ return inner && { ...inner, target: withScriptEnv(inner.target, command), via };
14375
14506
  }
14376
14507
  const target = withScriptEnv(found, command);
14377
- return { target, args: [...target.prefix ?? [], ...passed] };
14508
+ return { target, args: [...target.prefix ?? [], ...passed], via: [interp] };
14378
14509
  }
14379
14510
  async plan(file, argv, cwd, scope) {
14380
14511
  const direct = await this.resolve(file, argv[0] ?? file, cwd, scope);
14381
14512
  if (direct) return { target: direct, args: [...direct.prefix ?? [], ...argv.slice(1)] };
14382
- return this.interpreted(file, argv, cwd, scope);
14513
+ const script = await this.interpreted(file, argv, cwd, scope);
14514
+ return script && { ...script, script: true };
14515
+ }
14516
+ async mayExec(cred, req, planned) {
14517
+ const { dac } = this;
14518
+ if (!dac) return;
14519
+ const g = { fs: this.fs, cred, pid: req.pid, dac };
14520
+ const byPath = (file) => file.includes("/") && !COMMAND2.test(file);
14521
+ if (byPath(req.file)) {
14522
+ const path = this.fs.resolvePath(req.cwd, req.file);
14523
+ await need(g, "exec", path, planned?.script ? R_OK | X_OK : X_OK);
14524
+ }
14525
+ const via = planned?.via ?? [];
14526
+ for (const [i, file] of via.entries()) {
14527
+ if (!byPath(file)) continue;
14528
+ const want = i < via.length - 1 ? R_OK | X_OK : X_OK;
14529
+ await need(g, "exec", this.fs.resolvePath(req.cwd, file), want);
14530
+ }
14383
14531
  }
14384
14532
  async unrunnable(file, cwd, scope) {
14385
14533
  const path = this.fs.resolvePath(cwd, file);
@@ -14599,6 +14747,12 @@ var Launcher = class {
14599
14747
  return async (req, fds) => {
14600
14748
  const scope = await this.scopeOf(this.credOf(ppid).euid);
14601
14749
  const planned = await this.plan(req.file, req.argv, req.cwd, scope);
14750
+ try {
14751
+ await this.mayExec(this.credOf(ppid), { ...req, pid: ppid }, planned);
14752
+ } catch (err) {
14753
+ await fds.closeAll();
14754
+ throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
14755
+ }
14602
14756
  if (!planned) {
14603
14757
  await fds.closeAll();
14604
14758
  throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
@@ -14812,6 +14966,24 @@ var Launcher = class {
14812
14966
  await this.provisionPnpmHome();
14813
14967
  return entry;
14814
14968
  }
14969
+ async stampLayout() {
14970
+ const layout = [
14971
+ ...LAYOUT,
14972
+ [this.modulesDir, 493],
14973
+ [this.pnpmHome, 493],
14974
+ [`/${UNLINKED_DIR}`, 448]
14975
+ ];
14976
+ for (const [path, mode] of layout) {
14977
+ const real = await this.realLayoutPath(path);
14978
+ const fresh = real !== void 0 && !await this.base.hasOwner?.(real);
14979
+ if (fresh) await this.base.chown?.(real, { uid: 0, gid: 0, mode, follow: false });
14980
+ }
14981
+ }
14982
+ async realLayoutPath(path) {
14983
+ if (!await this.base.exists(path)) return void 0;
14984
+ if (path === "/") return path;
14985
+ return joinName(await realDir(this.base, parentOf3(path)), baseName(path));
14986
+ }
14815
14987
  async provisionPnpmHome() {
14816
14988
  await this.base.mkdir(this.pnpmHome, { recursive: true }).catch(() => void 0);
14817
14989
  }
@@ -14882,8 +15054,10 @@ var Launcher = class {
14882
15054
  }
14883
15055
  async prepare() {
14884
15056
  for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
15057
+ if (this.owners) await this.base.chmod("/tmp", 1023);
14885
15058
  await this.provisionPnpmHome();
14886
15059
  await this.users.ensure();
15060
+ if (this.owners) await this.stampLayout();
14887
15061
  await writeCaFile(this.base, this.ca).catch(() => void 0);
14888
15062
  this.fstab = this.mountFstab();
14889
15063
  }
@@ -14929,7 +15103,9 @@ var Launcher = class {
14929
15103
  const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
14930
15104
  const [file = ""] = argv;
14931
15105
  await this.base.mkdir(cwd, { recursive: true });
14932
- return { cwd, file, planned: await this.plan(file, argv, cwd, scope) };
15106
+ const planned = await this.plan(file, argv, cwd, scope);
15107
+ await this.mayExec(credOf(account), { file, cwd, pid: 0 }, planned);
15108
+ return { cwd, file, planned };
14933
15109
  }
14934
15110
  async openTerminal(argv, options) {
14935
15111
  const account = await this.account(options.user);