@ai-ecoverse/slicc-kernel 1.88.1 → 1.89.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.
@@ -383,6 +383,14 @@ function inodeOf(path) {
383
383
  h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
384
384
  return 4294967296 * (2097151 & h2) + (h1 >>> 0) || 1;
385
385
  }
386
+ function redirected(of, to) {
387
+ return Object.assign(fsError("EXDEV", `${of} leads to the mount at ${to}`), {
388
+ redirect: { of: normalizePath(of), to: normalizePath(to) }
389
+ });
390
+ }
391
+ function redirectOf(err) {
392
+ return err?.redirect;
393
+ }
386
394
 
387
395
  // src/fs/owners.ts
388
396
  var ROOT_OWNER = Object.freeze({ uid: 0, gid: 0 });
@@ -476,39 +484,47 @@ var Integrity = class {
476
484
  this.unknown = integrityUnknown(options);
477
485
  this.report = report;
478
486
  }
479
- async real(path) {
487
+ async real(path, asked) {
480
488
  const at = normalizePath(path);
481
489
  const real = await this.root.realpath?.(at, true).catch(() => void 0) ?? at;
482
- return isProtected(this.prefixes, real) ? real : void 0;
483
- }
484
- async key(path) {
485
- const real = await this.real(path);
486
- if (real === void 0 || !this.root.version) return void 0;
490
+ if (isProtected(this.prefixes, real)) return { real, name: real };
491
+ const names = asked === void 0 ? [] : await namesOf(this.root, asked);
492
+ if (real !== at) names.push(...await namesOf(this.root, at));
493
+ const name2 = names.findLast((p) => isProtected(this.prefixes, p));
494
+ return name2 === void 0 ? void 0 : { real, name: name2 };
495
+ }
496
+ async key(path, asked) {
497
+ const found = await this.real(path, asked);
498
+ if (found === void 0) return void 0;
499
+ const { real, name: name2 } = found;
500
+ if (name2 !== real) return `${name2}\0${real}\0`;
501
+ if (!this.root.version) return void 0;
487
502
  return `${real}\0${await this.root.version(real)}`;
488
503
  }
489
- async read(path, caller, plain) {
490
- return (await this.readKeyed(path, caller, plain)).bytes;
504
+ async read(path, caller, plain, asked) {
505
+ return (await this.readKeyed(path, caller, plain, asked)).bytes;
491
506
  }
492
- async readKeyed(path, caller, plain) {
493
- const real = await this.real(path);
494
- if (real === void 0) return { bytes: await plain() };
495
- const read = this.root.readVersioned ? await this.root.readVersioned(real) : { bytes: await this.root.readFileBuffer(real), version: void 0 };
507
+ async readKeyed(path, caller, plain, asked) {
508
+ const found = await this.real(path, asked);
509
+ if (found === void 0) return { bytes: await plain() };
510
+ const { real, name: name2 } = found;
511
+ const read = name2 !== real ? { bytes: await plain(), version: void 0 } : this.root.readVersioned ? await this.root.readVersioned(real) : { bytes: await this.root.readFileBuffer(real), version: void 0 };
496
512
  const { bytes } = read;
497
513
  const keyed = read.version === void 0 ? { bytes } : { bytes, key: `${real}\0${read.version}` };
498
514
  const actual = await sha256(bytes);
499
- const denial = { pid: caller.pid, uid: caller.uid, path: real, actual };
515
+ const denial = { pid: caller.pid, uid: caller.uid, path: name2, actual };
500
516
  const tagged = caller.tag !== void 0 ? { tag: caller.tag } : {};
501
517
  let expected;
502
518
  try {
503
- expected = await this.expected(real);
519
+ expected = await this.expected(name2);
504
520
  } catch {
505
521
  this.say({ ...denial, ...tagged, op: "exec-integrity", errno: "EACCES" });
506
- throw fsError("EACCES", `integrity lookup failed: ${real}`);
522
+ throw fsError("EACCES", `integrity lookup failed: ${name2}`);
507
523
  }
508
524
  if (expected === void 0) {
509
525
  if (this.unknown === "allow") {
510
- if (!this.unverified.has(real)) {
511
- this.unverified.add(real);
526
+ if (!this.unverified.has(name2)) {
527
+ this.unverified.add(name2);
512
528
  this.say({ ...denial, ...tagged, op: "exec-unverified", errno: "ENODATA" });
513
529
  }
514
530
  return keyed;
@@ -518,7 +534,12 @@ var Integrity = class {
518
534
  }
519
535
  const known = expected !== void 0 ? { expected } : {};
520
536
  this.say({ ...denial, ...tagged, ...known, op: "exec-integrity", errno: "EACCES" });
521
- throw fsError("EACCES", `integrity check failed: ${real}`);
537
+ throw fsError("EACCES", `integrity check failed: ${name2}`);
538
+ }
539
+ forget(path) {
540
+ for (const name2 of [...this.unverified]) {
541
+ if (path === void 0 || isProtected([path], name2)) this.unverified.delete(name2);
542
+ }
522
543
  }
523
544
  async expected(path) {
524
545
  const answer = await this.lookup(path);
@@ -572,10 +593,10 @@ function missing(err) {
572
593
  const code = err?.code;
573
594
  return code === "ENOENT" || code === "ENOTDIR";
574
595
  }
575
- async function resolved(fs, path, follow2) {
596
+ async function resolved(fs, path, follow2, hops = []) {
576
597
  let done4 = [];
577
598
  let todo = partsOf(path);
578
- for (let hops = 0; todo.length > 0; ) {
599
+ for (let links = 0; todo.length > 0; ) {
579
600
  const part = todo.shift();
580
601
  if (part === "..") {
581
602
  done4.pop();
@@ -597,13 +618,20 @@ async function resolved(fs, path, follow2) {
597
618
  done4.push(part);
598
619
  continue;
599
620
  }
600
- if (++hops > MAX_LINKS) throw fsError("ELOOP", path);
621
+ if (++links > MAX_LINKS) throw fsError("ELOOP", path);
622
+ hops.push(normalizePath([at, ...todo].join("/")));
601
623
  const link2 = await fs.readlink(at);
602
624
  if (link2.startsWith("/")) done4 = [];
603
625
  todo = [...partsOf(link2), ...todo];
604
626
  }
605
627
  return `/${done4.join("/")}`;
606
628
  }
629
+ async function namesOf(fs, path) {
630
+ const lexical2 = normalizePath(path);
631
+ const hops = [];
632
+ await resolved(fs, lexical2, true, hops).catch(() => void 0);
633
+ return [lexical2, ...hops];
634
+ }
607
635
  var ProtectedFs = class {
608
636
  prefixes;
609
637
  constructor(prefixes2) {
@@ -656,8 +684,8 @@ var ProtectedFs = class {
656
684
  await check(path, true);
657
685
  return fs.utimes(path, atime, mtime);
658
686
  },
659
- async mayChange(path, follow2) {
660
- await check(path, follow2);
687
+ async mayChange(path, follow2, whole = false) {
688
+ await check(path, follow2, whole);
661
689
  },
662
690
  async stat(path) {
663
691
  return marked(path, await fs.stat(path));
@@ -984,6 +1012,7 @@ var OpfsFs = class {
984
1012
  remote;
985
1013
  ranged;
986
1014
  owners = false;
1015
+ mounts = {};
987
1016
  constructor(root, meta = new MemoryMeta(), channel, options = {}) {
988
1017
  this.root = root;
989
1018
  this.meta = sidecar(meta);
@@ -1057,6 +1086,12 @@ var OpfsFs = class {
1057
1086
  trackOwners() {
1058
1087
  this.owners = true;
1059
1088
  }
1089
+ crossings(mounted) {
1090
+ this.mounts.mounted = mounted;
1091
+ }
1092
+ crossing(of, to) {
1093
+ if (this.mounts.mounted?.(to)) throw redirected(of, to);
1094
+ }
1060
1095
  async hasOwner(path) {
1061
1096
  return ownOf((await this.meta.get([path]))[0]) !== void 0;
1062
1097
  }
@@ -1251,12 +1286,13 @@ var OpfsFs = class {
1251
1286
  if (!link2) throw err;
1252
1287
  if (link2.path === current && !follow2) return { path: current, link: link2 };
1253
1288
  const target = `${link2.link}${current.slice(link2.path.length)}`;
1254
- current = target.split("/").includes("..") ? await this.physical(parentOf3(link2.path), target, hops + 1) : resolvePath(parentOf3(link2.path), target);
1289
+ current = target.split("/").includes("..") ? await this.physical(parentOf3(link2.path), target, hops + 1, path) : resolvePath(parentOf3(link2.path), target);
1290
+ this.crossing(path, current);
1255
1291
  }
1256
1292
  }
1257
1293
  throw fsError("ELOOP", path);
1258
1294
  }
1259
- async physical(base, path, used) {
1295
+ async physical(base, path, used, of) {
1260
1296
  let done4 = [];
1261
1297
  let todo = (path.startsWith("/") ? path : `${base}/${path}`).split("/").filter((p) => p !== "" && p !== ".");
1262
1298
  for (let hops = used; todo.length > 0; ) {
@@ -1265,6 +1301,7 @@ var OpfsFs = class {
1265
1301
  else if (!todo.includes("..")) done4.push(part);
1266
1302
  else {
1267
1303
  const at = `/${[...done4, part].join("/")}`;
1304
+ this.crossing(of, [at, ...todo].join("/"));
1268
1305
  const st = await this.lstat(at);
1269
1306
  if (!st.isSymbolicLink && !st.isDirectory) throw fsError("ENOTDIR", at);
1270
1307
  if (!st.isSymbolicLink) done4.push(part);
@@ -2159,7 +2196,10 @@ var Perms = class {
2159
2196
  function parts(path) {
2160
2197
  return path.split("/").filter((part) => part !== "" && part !== ".");
2161
2198
  }
2162
- async function walk(fs, cred, path, follow2) {
2199
+ function trustedLink(perm, links) {
2200
+ return links === void 0 || perm.uid === 0 || perm.uid === links;
2201
+ }
2202
+ async function walk(fs, cred, path, follow2, links) {
2163
2203
  const perms = new Perms(fs);
2164
2204
  await perms.learn(path);
2165
2205
  const queue = parts(path);
@@ -2177,6 +2217,7 @@ async function walk(fs, cred, path, follow2) {
2177
2217
  const perm = await perms.at(next2);
2178
2218
  const last = queue.length === 0;
2179
2219
  if (perm?.link !== void 0 && (!last || follow2)) {
2220
+ if (!trustedLink(perm, links)) return { path, perm: void 0, denied: next2, link: true };
2180
2221
  if (++hops > MAX_LINKS4) return { path: next2, perm: void 0 };
2181
2222
  queue.unshift(...parts(perm.link));
2182
2223
  if (perm.link.startsWith("/")) at = "/";
@@ -2201,9 +2242,9 @@ async function finalOf(fs, walked, want, options) {
2201
2242
  return st ? [permOf(st), st.isDirectory] : [perm, false];
2202
2243
  }
2203
2244
  async function access(fs, cred, path, want, options = {}) {
2204
- const walked = await walk(fs, cred, path, options.follow !== false);
2245
+ const walked = await walk(fs, cred, path, options.follow !== false, options.links);
2205
2246
  if (walked.denied !== void 0) {
2206
- if (await present(fs, walked.denied, true)) return walked;
2247
+ if (walked.link || await present(fs, walked.denied, true)) return walked;
2207
2248
  return { path, perm: void 0, missing: true };
2208
2249
  }
2209
2250
  if (!walked.perm) return { ...walked, missing: true };
@@ -2270,6 +2311,9 @@ var Created = class {
2270
2311
  return this.paths.delete(normalizePath(path));
2271
2312
  }
2272
2313
  };
2314
+ function linked(g, options) {
2315
+ return g.links === void 0 ? options : { ...options, links: g.links };
2316
+ }
2273
2317
  function deny(g, op, path, errno = "EACCES") {
2274
2318
  const tag = g.tag === void 0 ? {} : { tag: g.tag };
2275
2319
  g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno, ...tag });
@@ -2280,8 +2324,8 @@ function ttyOpenable(g, tty) {
2280
2324
  deny(g, "open", tty.name ?? "/dev/tty");
2281
2325
  }
2282
2326
  async function need(g, op, path, want, options = {}, shown = path) {
2283
- const walked = await access(g.fs, g.cred, path, want, options);
2284
- if (walked.denied !== void 0) deny(g, op, shown);
2327
+ const walked = await access(g.fs, g.cred, path, want, linked(g, options));
2328
+ if (walked.denied !== void 0) deny(g, op, walked.link ? walked.denied : shown);
2285
2329
  return walked;
2286
2330
  }
2287
2331
  function entryIn(dir, path) {
@@ -2300,14 +2344,16 @@ async function inParent(g, op, path, old = false) {
2300
2344
  return dir2.denied === void 0 ? stickyEntry(g, op, path, dir2) : void 0;
2301
2345
  }
2302
2346
  if (!old) return void await need(g, op, parentOf4(path), W_OK | X_OK, DIR, path);
2303
- const dir = await access(g.fs, g.cred, parentOf4(path), W_OK | X_OK, DIR);
2347
+ const dir = await access(g.fs, g.cred, parentOf4(path), W_OK | X_OK, linked(g, DIR));
2304
2348
  if (dir.denied === void 0) return stickyEntry(g, op, path, dir);
2349
+ if (dir.link) return deny(g, op, dir.denied);
2305
2350
  const lookup = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
2306
2351
  if (!lookup || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
2307
2352
  }
2308
2353
  async function makeIn(g, op, path) {
2309
2354
  for (let dir = parentOf4(path); ; dir = parentOf4(dir)) {
2310
- const walked = await access(g.fs, g.cred, dir, W_OK | X_OK, DIR);
2355
+ const walked = await access(g.fs, g.cred, dir, W_OK | X_OK, linked(g, DIR));
2356
+ if (walked.link) return deny(g, op, walked.denied);
2311
2357
  if (walked.denied !== void 0) {
2312
2358
  if (!await present(g.fs, path, true)) deny(g, op, path);
2313
2359
  return;
@@ -6599,7 +6645,7 @@ var RemoteTransport = class {
6599
6645
  finish();
6600
6646
  throw e;
6601
6647
  }
6602
- const { status, statusText, headers, url, redirected, redirects } = head;
6648
+ const { status, statusText, headers, url, redirected: redirected2, redirects } = head;
6603
6649
  const read = async () => {
6604
6650
  if (finished) return void 0;
6605
6651
  const reply = await this.ask({ net: "read", nid }).catch((e) => {
@@ -6616,7 +6662,7 @@ var RemoteTransport = class {
6616
6662
  this.cancel(nid);
6617
6663
  };
6618
6664
  return {
6619
- ...redirected ? { url, redirected } : {},
6665
+ ...redirected2 ? { url, redirected: redirected2 } : {},
6620
6666
  ...redirects ? { redirects } : {},
6621
6667
  status,
6622
6668
  statusText,
@@ -7641,14 +7687,14 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
7641
7687
  const own = await userDir(dir);
7642
7688
  return own?.has(path.slice(slash + 1)) ? owned4(synthetic(false, path), dir) : void 0;
7643
7689
  }
7644
- async function linked(path) {
7690
+ async function linked2(path) {
7645
7691
  return await virtual(path) ?? virtual(await followLinks(fs, path));
7646
7692
  }
7647
7693
  async function stat(path, real) {
7648
7694
  try {
7649
7695
  return await real(path);
7650
7696
  } catch (err) {
7651
- const found = missing3(err) ? await linked(path) : void 0;
7697
+ const found = missing3(err) ? await linked2(path) : void 0;
7652
7698
  if (found) return found;
7653
7699
  throw err;
7654
7700
  }
@@ -7660,7 +7706,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
7660
7706
  try {
7661
7707
  return await fs.readFileBuffer(path);
7662
7708
  } catch (err) {
7663
- if (missing3(err) && (await linked(path))?.isFile) return new Uint8Array(0);
7709
+ if (missing3(err) && (await linked2(path))?.isFile) return new Uint8Array(0);
7664
7710
  throw err;
7665
7711
  }
7666
7712
  },
@@ -7668,7 +7714,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
7668
7714
  link: (from, to, follow2) => fs.link(from, to, follow2),
7669
7715
  links: (path) => mayLink(fs, path),
7670
7716
  async exists(path) {
7671
- return await fs.exists(path) || await linked(path) !== void 0;
7717
+ return await fs.exists(path) || await linked2(path) !== void 0;
7672
7718
  },
7673
7719
  stat: (path) => stat(path, (p) => fs.stat(p)),
7674
7720
  lstat: (path) => stat(path, (p) => fs.lstat(p)),
@@ -9093,6 +9139,7 @@ var MountTable = class {
9093
9139
  return { quota: st.blocks * st.bsize, usage: (st.blocks - st.bfree) * st.bsize };
9094
9140
  }
9095
9141
  wrap(base) {
9142
+ base.crossings?.((path) => this.locate(normalizePath(path)) !== void 0);
9096
9143
  return new MountFs(this, base);
9097
9144
  }
9098
9145
  };
@@ -9116,6 +9163,37 @@ var MountFs = class {
9116
9163
  writable(mount, path) {
9117
9164
  if (readonly(mount)) throw errnoError("EROFS", path);
9118
9165
  }
9166
+ async through(path) {
9167
+ const real = normalizePath(path);
9168
+ if (this.at(real)) return real;
9169
+ try {
9170
+ await this.base.stat(parentOf7(real));
9171
+ return real;
9172
+ } catch (err) {
9173
+ const moved2 = redirectOf(err);
9174
+ return moved2 ? normalizePath(moved2.to + real.slice(moved2.of.length)) : real;
9175
+ }
9176
+ }
9177
+ async across(from, to, redirects) {
9178
+ const moved2 = [await this.through(from), await this.through(to)];
9179
+ const same = moved2[0] === normalizePath(from) && moved2[1] === normalizePath(to);
9180
+ if (same || redirects >= MAX_LINKS7) throw errnoError("EXDEV", `${from} -> ${to}`);
9181
+ return moved2;
9182
+ }
9183
+ async based(paths, redirects, run2, again) {
9184
+ try {
9185
+ return await run2();
9186
+ } catch (err) {
9187
+ const moved2 = redirectOf(err);
9188
+ if (!moved2) throw err;
9189
+ if (redirects >= MAX_LINKS7) throw errnoError("ELOOP", paths[0]);
9190
+ const at = paths.map((path) => {
9191
+ const real = normalizePath(path);
9192
+ return within6(real, moved2.of) ? normalizePath(moved2.to + real.slice(moved2.of.length)) : real;
9193
+ });
9194
+ return again(at, redirects + 1);
9195
+ }
9196
+ }
9119
9197
  async follow(path, hops = 0, create = false) {
9120
9198
  const found = this.at(path);
9121
9199
  if (!found) return path;
@@ -9163,10 +9241,17 @@ var MountFs = class {
9163
9241
  async readFile(path) {
9164
9242
  return new TextDecoder().decode(await this.readFileBuffer(path));
9165
9243
  }
9166
- async readFileBuffer(path) {
9244
+ async readFileBuffer(path, redirects = 0) {
9167
9245
  const real = await this.follow(path);
9168
9246
  const found = this.at(real);
9169
- if (!found) return this.base.readFileBuffer(real);
9247
+ if (!found) {
9248
+ return this.based(
9249
+ [real],
9250
+ redirects,
9251
+ () => this.base.readFileBuffer(real),
9252
+ ([to], n) => this.readFileBuffer(to, n)
9253
+ );
9254
+ }
9170
9255
  for (let attempt = 1; ; attempt++) {
9171
9256
  try {
9172
9257
  return await this.readWhole(found.mount, real, found.rel);
@@ -9205,10 +9290,18 @@ var MountFs = class {
9205
9290
  }
9206
9291
  return concat4(chunks, total);
9207
9292
  }
9208
- async writeFile(path, content, owner) {
9293
+ async writeFile(path, content, owner, redirects = 0) {
9209
9294
  const real = await this.follow(path, 0, true);
9210
9295
  const found = this.at(real);
9211
- if (!found) return this.base.writeFile(real, content, owner);
9296
+ if (!found) {
9297
+ const write = () => this.base.writeFile(real, content, owner);
9298
+ return this.based(
9299
+ [real],
9300
+ redirects,
9301
+ write,
9302
+ ([to], n) => this.writeFile(to, content, owner, n)
9303
+ );
9304
+ }
9212
9305
  const { mount, rel: rel2 } = found;
9213
9306
  this.writable(mount, real);
9214
9307
  const bytes = typeof content === "string" ? new TextEncoder().encode(content) : content;
@@ -9242,10 +9335,18 @@ var MountFs = class {
9242
9335
  this.table.forget(mount, real);
9243
9336
  }
9244
9337
  }
9245
- async pread(path, offset, length2, version) {
9338
+ async pread(path, offset, length2, version, redirects = 0) {
9246
9339
  const real = await this.follow(path);
9247
9340
  const found = this.at(real);
9248
- if (!found && this.base.pread) return this.base.pread(real, offset, length2);
9341
+ const { pread } = this.base;
9342
+ if (!found && pread) {
9343
+ return this.based(
9344
+ [real],
9345
+ redirects,
9346
+ () => pread.call(this.base, real, offset, length2),
9347
+ ([to], n) => this.pread(to, offset, length2, version, n)
9348
+ );
9349
+ }
9249
9350
  if (!found) return (await this.base.readFileBuffer(real)).slice(offset, offset + length2);
9250
9351
  const { mount, rel: rel2 } = found;
9251
9352
  if ((await this.table.getattr(mount, real, rel2)).kind === "directory") {
@@ -9292,10 +9393,18 @@ var MountFs = class {
9292
9393
  );
9293
9394
  }
9294
9395
  }
9295
- async pwrite(path, offset, bytes, transfer = false) {
9396
+ async pwrite(path, offset, bytes, transfer = false, redirects = 0) {
9296
9397
  const real = await this.follow(path, 0, true);
9297
9398
  const found = this.at(real);
9298
- if (!found && this.base.pwrite) return this.base.pwrite(real, offset, bytes);
9399
+ const { pwrite } = this.base;
9400
+ if (!found && pwrite) {
9401
+ return this.based(
9402
+ [real],
9403
+ redirects,
9404
+ () => pwrite.call(this.base, real, offset, bytes),
9405
+ ([to], n) => this.pwrite(to, offset, bytes, transfer, n)
9406
+ );
9407
+ }
9299
9408
  if (!found)
9300
9409
  return this.base.writeFile(
9301
9410
  real,
@@ -9335,10 +9444,18 @@ var MountFs = class {
9335
9444
  await mount.conn.call({ op: "write", fh, offset: offset + at, bytes: part }, [part.buffer]);
9336
9445
  }
9337
9446
  }
9338
- async truncate(path, size) {
9447
+ async truncate(path, size, redirects = 0) {
9339
9448
  const real = await this.follow(path);
9340
9449
  const found = this.at(real);
9341
- if (!found && this.base.truncate) return this.base.truncate(real, size);
9450
+ const { truncate } = this.base;
9451
+ if (!found && truncate) {
9452
+ return this.based(
9453
+ [real],
9454
+ redirects,
9455
+ () => truncate.call(this.base, real, size),
9456
+ ([to], n) => this.truncate(to, size, n)
9457
+ );
9458
+ }
9342
9459
  if (!found?.mount.caps.ranges) {
9343
9460
  return this.writeFile(real, resized(await this.readFileBuffer(real), size));
9344
9461
  }
@@ -9354,8 +9471,15 @@ var MountFs = class {
9354
9471
  this.table.forget(mount, real);
9355
9472
  }
9356
9473
  }
9357
- async exists(path) {
9358
- if (!this.at(path)) return this.base.exists(path);
9474
+ async exists(path, redirects = 0) {
9475
+ if (!this.at(path)) {
9476
+ return this.based(
9477
+ [path],
9478
+ redirects,
9479
+ () => this.base.exists(path),
9480
+ ([to], n) => this.exists(to, n)
9481
+ );
9482
+ }
9359
9483
  try {
9360
9484
  await this.stat(path);
9361
9485
  return true;
@@ -9365,9 +9489,16 @@ var MountFs = class {
9365
9489
  throw err;
9366
9490
  }
9367
9491
  }
9368
- async lstat(path) {
9492
+ async lstat(path, redirects = 0) {
9369
9493
  const found = this.at(path);
9370
- if (!found) return this.base.lstat(path);
9494
+ if (!found) {
9495
+ return this.based(
9496
+ [path],
9497
+ redirects,
9498
+ () => this.base.lstat(path),
9499
+ ([to], n) => this.lstat(to, n)
9500
+ );
9501
+ }
9371
9502
  const real = normalizePath(path);
9372
9503
  if (this.table.writing(real)?.truncated) await this.table.commit(real);
9373
9504
  return this.statWriting(
@@ -9380,9 +9511,15 @@ var MountFs = class {
9380
9511
  const ends = (this.table.writing(real)?.extents ?? []).map(([, end]) => end);
9381
9512
  return statOf({ ...attr2, size: Math.max(attr2.size, ...ends) }, real, mount);
9382
9513
  }
9383
- async stat(path) {
9514
+ async stat(path, redirects = 0) {
9384
9515
  const real = await this.follow(path);
9385
- return this.at(real) ? this.lstat(real) : this.base.stat(real);
9516
+ if (this.at(real)) return this.lstat(real);
9517
+ return this.based(
9518
+ [real],
9519
+ redirects,
9520
+ () => this.base.stat(real),
9521
+ ([to], n) => this.stat(to, n)
9522
+ );
9386
9523
  }
9387
9524
  async entries(path) {
9388
9525
  const real = await this.follow(path);
@@ -9394,12 +9531,26 @@ var MountFs = class {
9394
9531
  list: await this.table.readdir(found.mount, real, found.rel)
9395
9532
  };
9396
9533
  }
9397
- async readdir(path) {
9398
- if (!this.at(path)) return this.base.readdir(path);
9534
+ async readdir(path, redirects = 0) {
9535
+ if (!this.at(path)) {
9536
+ return this.based(
9537
+ [path],
9538
+ redirects,
9539
+ () => this.base.readdir(path),
9540
+ ([to], n) => this.readdir(to, n)
9541
+ );
9542
+ }
9399
9543
  return (await this.entries(path)).list.map((e) => e.name);
9400
9544
  }
9401
- async readdirStat(path) {
9402
- if (!this.at(path)) return this.base.readdirStat(path);
9545
+ async readdirStat(path, redirects = 0) {
9546
+ if (!this.at(path)) {
9547
+ return this.based(
9548
+ [path],
9549
+ redirects,
9550
+ () => this.base.readdirStat(path),
9551
+ ([to], n) => this.readdirStat(to, n)
9552
+ );
9553
+ }
9403
9554
  const { real, mount, list } = await this.entries(path);
9404
9555
  return Promise.all(
9405
9556
  list.map(async (entry) => {
@@ -9411,9 +9562,16 @@ var MountFs = class {
9411
9562
  })
9412
9563
  );
9413
9564
  }
9414
- async mkdir(path, options = {}) {
9565
+ async mkdir(path, options = {}, redirects = 0) {
9415
9566
  const found = this.at(path);
9416
- if (!found) return this.base.mkdir(path, options);
9567
+ if (!found) {
9568
+ return this.based(
9569
+ [path],
9570
+ redirects,
9571
+ () => this.base.mkdir(path, options),
9572
+ ([to], n) => this.mkdir(to, options, n)
9573
+ );
9574
+ }
9417
9575
  const { mount, rel: rel2 } = found;
9418
9576
  const real = normalizePath(path);
9419
9577
  this.writable(mount, real);
@@ -9438,11 +9596,18 @@ var MountFs = class {
9438
9596
  await mount.conn.call({ op: "mkdir", path: sub, ...made2 });
9439
9597
  }
9440
9598
  }
9441
- async rm(path, options = {}) {
9599
+ async rm(path, options = {}, redirects = 0) {
9442
9600
  const found = this.at(path);
9443
9601
  if (!found && options.recursive && this.table.under(normalizePath(path)))
9444
9602
  return this.rmAround(normalizePath(path), options);
9445
- if (!found) return this.base.rm(path, options);
9603
+ if (!found) {
9604
+ return this.based(
9605
+ [path],
9606
+ redirects,
9607
+ () => this.base.rm(path, options),
9608
+ ([to], n) => this.rm(to, options, n)
9609
+ );
9610
+ }
9446
9611
  const { mount, rel: rel2 } = found;
9447
9612
  const real = normalizePath(path);
9448
9613
  if (rel2 === "/" && options.recursive) await this.rmEach(real, mount, rel2, options);
@@ -9483,11 +9648,22 @@ var MountFs = class {
9483
9648
  }
9484
9649
  throw first ?? errnoError("EBUSY", `${path} holds a mount point`);
9485
9650
  }
9486
- async rename(from, to) {
9651
+ async rename(from, to, redirects = 0) {
9487
9652
  const a = this.at(from);
9488
9653
  const b = this.at(to);
9489
- if (!a && !b) return this.base.rename(from, to);
9490
- if (!a || !b || a.mount !== b.mount) throw errnoError("EXDEV", `${from} -> ${to}`);
9654
+ if (!a && !b) {
9655
+ return this.based(
9656
+ [from, to],
9657
+ redirects,
9658
+ () => this.base.rename(from, to),
9659
+ ([f, t], n) => this.rename(f, t, n)
9660
+ );
9661
+ }
9662
+ if (!a || !b) {
9663
+ const [f, t] = await this.across(from, to, redirects);
9664
+ return this.rename(f, t, redirects + 1);
9665
+ }
9666
+ if (a.mount !== b.mount) throw errnoError("EXDEV", `${from} -> ${to}`);
9491
9667
  this.writable(a.mount, normalizePath(to));
9492
9668
  await this.table.commitUnder(from);
9493
9669
  await this.table.commitUnder(to);
@@ -9528,9 +9704,17 @@ var MountFs = class {
9528
9704
  this.table.forget(a.mount, normalizePath(to));
9529
9705
  }
9530
9706
  }
9531
- async symlink(target, path, owner) {
9707
+ async symlink(target, path, owner, redirects = 0) {
9532
9708
  const found = this.at(path);
9533
- if (!found) return this.base.symlink(target, path, owner);
9709
+ if (!found) {
9710
+ const make = () => this.base.symlink(target, path, owner);
9711
+ return this.based(
9712
+ [path],
9713
+ redirects,
9714
+ make,
9715
+ ([to], n) => this.symlink(target, to, owner, n)
9716
+ );
9717
+ }
9534
9718
  this.writable(found.mount, normalizePath(path));
9535
9719
  if (!found.mount.caps.symlinks)
9536
9720
  throw errnoError("EPERM", `${path}: this file system has no symlinks`);
@@ -9540,8 +9724,11 @@ var MountFs = class {
9540
9724
  await found.mount.conn.call({ op: "symlink", target, path: found.rel, ...made2 });
9541
9725
  }
9542
9726
  async realpath(path, follow2) {
9543
- if (this.at(path) || !this.base.realpath) throw errnoError("ENOSYS", path);
9544
- const real = await this.base.realpath(path, follow2);
9727
+ const { realpath: realpath2 } = this.base;
9728
+ if (this.at(path) || !realpath2) throw errnoError("ENOSYS", path);
9729
+ const real = await realpath2.call(this.base, path, follow2).catch((err) => {
9730
+ throw redirectOf(err) ? errnoError("ENOSYS", path) : err;
9731
+ });
9545
9732
  if (this.at(real)) throw errnoError("ENOSYS", path);
9546
9733
  return real;
9547
9734
  }
@@ -9563,17 +9750,30 @@ var MountFs = class {
9563
9750
  }
9564
9751
  return out;
9565
9752
  }
9566
- async readlink(path) {
9753
+ async readlink(path, redirects = 0) {
9567
9754
  const found = this.at(path);
9568
- if (!found) return this.base.readlink(path);
9755
+ if (!found) {
9756
+ return this.based(
9757
+ [path],
9758
+ redirects,
9759
+ () => this.base.readlink(path),
9760
+ ([to], n) => this.readlink(to, n)
9761
+ );
9762
+ }
9569
9763
  return await found.mount.conn.call({ op: "readlink", path: found.rel });
9570
9764
  }
9571
- async chown(path, change) {
9765
+ async chown(path, change, redirects = 0) {
9572
9766
  const real = change.follow === false || change.kind === "new" ? path : await this.follow(path);
9573
9767
  const found = this.at(real);
9574
9768
  if (found) return this.chownMounted(found, normalizePath(real), change);
9575
- if (!this.base.chown) throw errnoError("EPERM", path);
9576
- return this.base.chown(real, change);
9769
+ const { chown: chown2 } = this.base;
9770
+ if (!chown2) throw errnoError("EPERM", path);
9771
+ return this.based(
9772
+ [real],
9773
+ redirects,
9774
+ () => chown2.call(this.base, real, change),
9775
+ ([to], n) => this.chown(to, change, n)
9776
+ );
9577
9777
  }
9578
9778
  async chownMounted(found, real, change) {
9579
9779
  const { mount, rel: rel2 } = found;
@@ -9593,24 +9793,47 @@ var MountFs = class {
9593
9793
  ...Object.fromEntries(attrs.filter(([, value]) => value !== void 0))
9594
9794
  });
9595
9795
  }
9596
- async chmod(path, mode) {
9796
+ async chmod(path, mode, redirects = 0) {
9597
9797
  const real = await this.follow(path);
9598
9798
  const found = this.at(real);
9599
- if (!found) return this.base.chmod(real, mode);
9799
+ if (!found) {
9800
+ return this.based(
9801
+ [real],
9802
+ redirects,
9803
+ () => this.base.chmod(real, mode),
9804
+ ([to], n) => this.chmod(to, mode, n)
9805
+ );
9806
+ }
9600
9807
  this.writable(found.mount, real);
9601
9808
  if (!found.mount.caps.chmod) return;
9602
9809
  this.table.forget(found.mount, real);
9603
9810
  await found.mount.conn.call({ op: "setattr", path: found.rel, mode });
9604
9811
  }
9605
- async utimes(path, atime, mtime) {
9812
+ async utimes(path, atime, mtime, redirects = 0) {
9606
9813
  const real = await this.follow(path);
9607
9814
  const found = this.at(real);
9608
- if (!found) return this.base.utimes(real, atime, mtime);
9815
+ if (!found) {
9816
+ const set2 = () => this.base.utimes(real, atime, mtime);
9817
+ return this.based(
9818
+ [real],
9819
+ redirects,
9820
+ set2,
9821
+ ([to], n) => this.utimes(to, atime, mtime, n)
9822
+ );
9823
+ }
9609
9824
  await this.setMtime(found, real, mtime);
9610
9825
  }
9611
- async lutimes(path, atime, mtime) {
9826
+ async lutimes(path, atime, mtime, redirects = 0) {
9612
9827
  const found = this.at(path);
9613
- if (!found) return lutimesOf(this.base, path, atime, mtime);
9828
+ if (!found) {
9829
+ const set2 = () => lutimesOf(this.base, path, atime, mtime);
9830
+ return this.based(
9831
+ [path],
9832
+ redirects,
9833
+ set2,
9834
+ ([to], n) => this.lutimes(to, atime, mtime, n)
9835
+ );
9836
+ }
9614
9837
  const real = normalizePath(path);
9615
9838
  if (!found.mount.caps.linkTimes && (await this.lstat(real)).isSymbolicLink) {
9616
9839
  throw errnoError("EOPNOTSUPP", path);
@@ -9829,7 +10052,8 @@ async function run(entry, path, req) {
9829
10052
  function guardOf(entry) {
9830
10053
  const cred = entry.dac && entry.cred?.();
9831
10054
  if (!cred || !entry.dac) return void 0;
9832
- return { fs: entry.fs, cred, pid: entry.pid ?? 0, dac: entry.dac };
10055
+ const links = entry.links === void 0 ? {} : { links: entry.links };
10056
+ return { fs: entry.fs, cred, pid: entry.pid ?? 0, dac: entry.dac, ...links };
9833
10057
  }
9834
10058
  var FOLLOWING = /* @__PURE__ */ new Set(["exists", "stat", "chown", "statfs"]);
9835
10059
  var LOOKS = /* @__PURE__ */ new Set(["exists", "stat", "lstat"]);
@@ -9890,9 +10114,15 @@ async function holdOp(entry, path, req) {
9890
10114
  entry.hold?.(path, on, open, void 0, req.handle);
9891
10115
  return done;
9892
10116
  }
10117
+ async function followed(g, path) {
10118
+ const st = await g.fs.lstat(path).catch(() => void 0);
10119
+ if (st?.isSymbolicLink && !trustedLink(permOf(st), g.links)) deny(g, "follow", path);
10120
+ return done;
10121
+ }
9893
10122
  async function accessOp(entry, path, req) {
9894
10123
  const g = guardOf(entry);
9895
10124
  if (!g) return done;
10125
+ if (req.arg2 === "follow") return followed(g, path);
9896
10126
  const open = req.arg2 === "open";
9897
10127
  const cred = open || req.arg2 === "eaccess" ? g.cred : realIds(g.cred);
9898
10128
  const options = { follow: req.nofollow !== true, ...open ? { directory: false } : {} };
@@ -9915,15 +10145,18 @@ var CHANGING = /* @__PURE__ */ new Set([
9915
10145
  "lchown"
9916
10146
  ]);
9917
10147
  var CONTENT = /* @__PURE__ */ new Set(["write", "pwrite", "truncate"]);
10148
+ var REMOVING = /* @__PURE__ */ new Set(["unlink", "rmdir", "rm", "rename"]);
9918
10149
  async function mayChange(entry, path, req) {
9919
10150
  const { mayChange: mayChange2 } = entry.fs;
9920
10151
  if (!mayChange2) return;
9921
10152
  const { op } = req;
9922
10153
  if (CONTENT.has(op) && req.handle === void 0) return mayChange2.call(entry.fs, path, true);
9923
10154
  if (!CHANGING.has(op)) return;
9924
- await mayChange2.call(entry.fs, path, op === "utimes" || op === "chmod" || op === "chown");
10155
+ const follow2 = op === "utimes" || op === "chmod" || op === "chown";
10156
+ await mayChange2.call(entry.fs, path, follow2, REMOVING.has(op));
9925
10157
  if (op === "rename" || op === "link") {
9926
- await mayChange2.call(entry.fs, entry.fs.resolvePath(entry.cwd, req.arg2 ?? ""), false);
10158
+ const to = entry.fs.resolvePath(entry.cwd, req.arg2 ?? "");
10159
+ await mayChange2.call(entry.fs, to, false, op === "rename");
9927
10160
  }
9928
10161
  }
9929
10162
  async function guarded(entry, path, req) {
@@ -10616,7 +10849,7 @@ function mountTable(mounts = []) {
10616
10849
  }
10617
10850
 
10618
10851
  // src/version.ts
10619
- var KERNEL_VERSION = true ? "1.88.1" : "0.0.0-dev";
10852
+ var KERNEL_VERSION = true ? "1.89.0" : "0.0.0-dev";
10620
10853
  var OS_RELEASE = "6.0.0-slicc";
10621
10854
  function procVersion() {
10622
10855
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -13387,7 +13620,7 @@ var WasmProcess = class {
13387
13620
  const { guard: guard2 } = this.options;
13388
13621
  const inherited2 = req.orphan || req.contents !== void 0 || req.handle !== void 0;
13389
13622
  if (!guard2 || inherited2 || req.pin !== void 0) return;
13390
- const g = { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid };
13623
+ const g = { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid, links: guard2.links };
13391
13624
  if (req.exclusive) return inParent(g, "open", req.path);
13392
13625
  if (req.created && guard2.created?.take(req.path)) return;
13393
13626
  const access2 = req.flags & WRITE_ACCESS;
@@ -13443,7 +13676,7 @@ var WasmProcess = class {
13443
13676
  }
13444
13677
  guarded() {
13445
13678
  const { guard: guard2 } = this.options;
13446
- return guard2 && { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid };
13679
+ return guard2 && { fs: guard2.fs, dac: guard2.dac, cred: this.cred, pid: this.pid, links: guard2.links };
13447
13680
  }
13448
13681
  ttyOwner() {
13449
13682
  return { uid: this.cred.euid, gid: this.cred.egid, mode: TTY_MODE };
@@ -14169,6 +14402,7 @@ function processInit(opts, sab, umask, cred, lane) {
14169
14402
  umask,
14170
14403
  cred,
14171
14404
  ...opts.owners ? { owners: true } : {},
14405
+ ...opts.links !== void 0 ? { links: true } : {},
14172
14406
  ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {}
14173
14407
  };
14174
14408
  if (opts.program.abi === "js") {
@@ -14196,6 +14430,7 @@ function holdingToken(opts, holds, caller) {
14196
14430
  return mintSyncFsToken({
14197
14431
  ...opts.owners ? caller : {},
14198
14432
  ...opts.dac ? { dac: opts.dac, pid: opts.pid, created: caller.created } : {},
14433
+ ...opts.links !== void 0 ? { links: opts.links } : {},
14199
14434
  ...opts.fresh ? { fresh: (path) => opts.fresh?.(path, caller.umask()) } : {},
14200
14435
  fs: opts.fs,
14201
14436
  cwd: opts.cwd,
@@ -14209,6 +14444,9 @@ function holdingToken(opts, holds, caller) {
14209
14444
  renamed: (from, to) => holds.renamed(from, to)
14210
14445
  });
14211
14446
  }
14447
+ function linksOf(opts) {
14448
+ return opts.links === void 0 ? {} : { links: opts.links };
14449
+ }
14212
14450
  function clipboardName(opts) {
14213
14451
  return opts.program.abi === "js" ? {} : { clipboardName: opts.argv0 };
14214
14452
  }
@@ -14220,7 +14458,7 @@ function processOptions(opts, header3, hooks, created3) {
14220
14458
  ...opts.outputFilter ? { outputFilter: opts.outputFilter } : {},
14221
14459
  ...opts.clipboard ? { clipboard: opts.clipboard, ...clipboardName(opts) } : {},
14222
14460
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
14223
- ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
14461
+ ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3, ...linksOf(opts) } } : {},
14224
14462
  ...opts.identity ? { identity: opts.identity } : {},
14225
14463
  ...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
14226
14464
  ...opts.settling ? { settling: opts.settling } : {},
@@ -14266,6 +14504,7 @@ function threadInit(opts, program, sab, cred, thread, main) {
14266
14504
  sab,
14267
14505
  cred: copyCred(cred),
14268
14506
  owners: opts.owners === true,
14507
+ ...opts.links !== void 0 ? { links: true } : {},
14269
14508
  ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
14270
14509
  thread,
14271
14510
  signals: main
@@ -17566,6 +17805,9 @@ var SUDO_SOURCE = [
17566
17805
  ].join("\n");
17567
17806
 
17568
17807
  // src/launcher.ts
17808
+ function linksOf2(req) {
17809
+ return req.links === void 0 ? {} : { links: req.links };
17810
+ }
17569
17811
  var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
17570
17812
  var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
17571
17813
  var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
@@ -17741,6 +17983,7 @@ var Launcher = class {
17741
17983
  warned = /* @__PURE__ */ new Set();
17742
17984
  compiled = /* @__PURE__ */ new Map();
17743
17985
  texts = /* @__PURE__ */ new Map();
17986
+ revocations = 0;
17744
17987
  integrity;
17745
17988
  scannedFs;
17746
17989
  get scanned() {
@@ -17753,6 +17996,7 @@ var Launcher = class {
17753
17996
  described = /* @__PURE__ */ new Map();
17754
17997
  creds = /* @__PURE__ */ new Map();
17755
17998
  tags = /* @__PURE__ */ new Map();
17999
+ links = /* @__PURE__ */ new Map();
17756
18000
  freshModules = new FreshModules();
17757
18001
  granted = /* @__PURE__ */ new Map();
17758
18002
  users;
@@ -18055,14 +18299,16 @@ var Launcher = class {
18055
18299
  }
18056
18300
  const path = this.fs.resolvePath(cwd, file);
18057
18301
  const glue = await followLinks(this.base, path);
18058
- const linked = COMMAND2.exec(glue)?.[1];
18059
- if (linked !== void 0) {
18060
- const target = await this.resolve(`/bin/${linked}`, argv0, cwd);
18061
- const byPath = !COMMAND2.test(path) && (await this.commands()).get(linked)?.argv0Path === true;
18302
+ const asked = glue === path ? {} : { asked: path };
18303
+ const linked2 = COMMAND2.exec(glue)?.[1];
18304
+ if (linked2 !== void 0) {
18305
+ const found = await this.resolve(`/bin/${linked2}`, argv0, cwd);
18306
+ const target = found && { ...found, ...asked };
18307
+ const byPath = !COMMAND2.test(path) && (await this.commands()).get(linked2)?.argv0Path === true;
18062
18308
  return target && byPath ? { ...target, argv0: path } : target;
18063
18309
  }
18064
18310
  const own = await this.commandAt(glue, scope);
18065
- if (own) return own.script ? void 0 : targetOf(own);
18311
+ if (own) return own.script ? void 0 : { ...targetOf(own), ...asked };
18066
18312
  const head = await this.head(glue);
18067
18313
  if (!head) return void 0;
18068
18314
  if (isWasm(head)) {
@@ -18070,13 +18316,14 @@ var Launcher = class {
18070
18316
  abi: "wasi",
18071
18317
  glue,
18072
18318
  wasm: glue,
18073
- argv0: baseName(argv0 || file).replace(/\.wasm$/, "")
18319
+ argv0: baseName(argv0 || file).replace(/\.wasm$/, ""),
18320
+ ...asked
18074
18321
  };
18075
18322
  }
18076
18323
  if (interpreter(head) !== "node") return void 0;
18077
18324
  const wasm = modulePath(glue);
18078
18325
  if (!await this.base.exists(wasm)) return void 0;
18079
- return { abi: "emscripten", glue, wasm, argv0: baseName(argv0 || file) };
18326
+ return { abi: "emscripten", glue, wasm, argv0: baseName(argv0 || file), ...asked };
18080
18327
  }
18081
18328
  async head(path) {
18082
18329
  try {
@@ -18094,6 +18341,8 @@ var Launcher = class {
18094
18341
  async interpreted(file, argv, cwd, scope, depth = 0) {
18095
18342
  const command = await this.scriptCommand(file, cwd, scope);
18096
18343
  const script = command?.script ?? this.fs.resolvePath(cwd, file);
18344
+ const byPath = command && file.includes("/") && !COMMAND2.test(file);
18345
+ const named = byPath ? { [script]: this.fs.resolvePath(cwd, file) } : {};
18097
18346
  const head = await this.head(script);
18098
18347
  if (!head) return void 0;
18099
18348
  const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(scope), script);
@@ -18116,7 +18365,8 @@ var Launcher = class {
18116
18365
  target: withScriptEnv(inner.target, command),
18117
18366
  via,
18118
18367
  sources,
18119
- ...bins.length > 0 ? { bins } : {}
18368
+ ...bins.length > 0 ? { bins } : {},
18369
+ asked: { ...inner.asked, ...named }
18120
18370
  };
18121
18371
  }
18122
18372
  const target = withScriptEnv(found, command);
@@ -18125,7 +18375,8 @@ var Launcher = class {
18125
18375
  args: [...target.prefix ?? [], ...passed],
18126
18376
  via: [interp],
18127
18377
  sources: [script],
18128
- ...command?.bin ? { bins: [script] } : {}
18378
+ ...command?.bin ? { bins: [script] } : {},
18379
+ asked: named
18129
18380
  };
18130
18381
  }
18131
18382
  async plan(file, argv, cwd, scope, argv0) {
@@ -18139,7 +18390,14 @@ var Launcher = class {
18139
18390
  async mayExec(cred, req, planned) {
18140
18391
  const { dac } = this;
18141
18392
  if (!dac) return;
18142
- const g = { fs: this.fs, cred, pid: req.pid, dac, tag: req.tag };
18393
+ const g = {
18394
+ fs: this.fs,
18395
+ cred,
18396
+ pid: req.pid,
18397
+ dac,
18398
+ tag: req.tag,
18399
+ links: this.links.get(req.pid)
18400
+ };
18143
18401
  const byPath = (file) => file.includes("/") && !COMMAND2.test(file);
18144
18402
  if (byPath(req.file)) {
18145
18403
  const path = this.fs.resolvePath(req.cwd, req.file);
@@ -18296,8 +18554,8 @@ var Launcher = class {
18296
18554
  readFile: (path) => this.integrity ? this.text(path, KERNEL_CALLER) : fs.readFile(path)
18297
18555
  });
18298
18556
  }
18299
- async cacheKey(path) {
18300
- const verified = await this.integrity?.key(path);
18557
+ async cacheKey(path, asked) {
18558
+ const verified = await this.integrity?.key(path, asked);
18301
18559
  if (verified !== void 0) return verified;
18302
18560
  const st = await this.base.stat(path);
18303
18561
  return `${path}:${st.size}:${st.mtime.getTime()}`;
@@ -18306,30 +18564,31 @@ var Launcher = class {
18306
18564
  const plain = () => fs.readFileBuffer(path);
18307
18565
  return this.integrity ? this.integrity.read(path, caller, plain) : plain();
18308
18566
  }
18309
- keyed(path, caller) {
18567
+ keyed(path, caller, asked) {
18310
18568
  const plain = async () => this.base.readFileBuffer(path);
18311
- return this.integrity ? this.integrity.readKeyed(path, caller, plain) : plain().then((bytes) => ({ bytes }));
18569
+ return this.integrity ? this.integrity.readKeyed(path, caller, plain, asked) : plain().then((bytes) => ({ bytes }));
18312
18570
  }
18313
18571
  cached(cache, key, load) {
18314
18572
  const hit = cache.get(key);
18315
18573
  if (hit !== void 0) return hit;
18574
+ const revocations = this.revocations;
18316
18575
  const loading2 = load();
18317
18576
  const found = loading2.then(({ value }) => value);
18318
18577
  cache.set(key, found);
18319
- loading2.then(
18320
- (done4) => {
18321
- if (done4.key === key || done4.key === void 0 && !key.includes("\0")) return;
18322
- if (cache.get(key) === found) cache.delete(key);
18323
- if (done4.key !== void 0) cache.set(done4.key, found);
18324
- },
18325
- () => cache.delete(key)
18326
- );
18578
+ const drop = () => {
18579
+ if (cache.get(key) === found) cache.delete(key);
18580
+ };
18581
+ loading2.then((done4) => {
18582
+ if (done4.key === key || done4.key === void 0 && !key.includes("\0")) return;
18583
+ drop();
18584
+ if (done4.key !== void 0 && revocations === this.revocations) cache.set(done4.key, found);
18585
+ }, drop);
18327
18586
  return found;
18328
18587
  }
18329
- async module(path, caller) {
18330
- const key = await this.cacheKey(path);
18588
+ async module(path, caller, asked) {
18589
+ const key = await this.cacheKey(path, asked);
18331
18590
  return this.cached(this.compiled, key, async () => {
18332
- const { bytes, key: read } = await this.keyed(path, caller);
18591
+ const { bytes, key: read } = await this.keyed(path, caller, asked);
18333
18592
  const memory = importedMemory(bytes);
18334
18593
  const foreign = foreignImports(bytes);
18335
18594
  const compiled = await WebAssembly.compile(bytes);
@@ -18338,22 +18597,30 @@ var Launcher = class {
18338
18597
  }
18339
18598
  async checkedSources(planned, caller) {
18340
18599
  const { target } = planned;
18341
- const files = [target.wasm, target.glue, target.imports, ...planned.sources ?? []];
18600
+ const files = [
18601
+ target.wasm,
18602
+ target.glue,
18603
+ target.imports,
18604
+ target.asked,
18605
+ ...planned.sources ?? []
18606
+ ];
18342
18607
  const manifests = /* @__PURE__ */ new Set();
18343
18608
  for (const file of files) {
18344
18609
  const root = file === void 0 ? void 0 : PACKAGE_ROOT.exec(file)?.[1];
18345
18610
  if (root !== void 0) manifests.add(`${root}/package.json`);
18346
18611
  }
18347
- for (const source of planned.sources ?? []) await this.text(source, caller);
18612
+ for (const source of planned.sources ?? []) {
18613
+ await this.text(source, caller, planned.asked?.[source]);
18614
+ }
18348
18615
  for (const manifest of manifests) {
18349
18616
  if (await this.base.exists(manifest)) await this.text(manifest, caller);
18350
18617
  }
18351
18618
  }
18352
- async text(path, caller) {
18619
+ async text(path, caller, asked) {
18353
18620
  if (!this.integrity) return this.base.readFile(path);
18354
- const key = await this.cacheKey(path);
18621
+ const key = await this.cacheKey(path, asked);
18355
18622
  return this.cached(this.texts, key, async () => {
18356
- const { bytes, key: read } = await this.keyed(path, caller);
18623
+ const { bytes, key: read } = await this.keyed(path, caller, asked);
18357
18624
  return { value: new TextDecoder().decode(bytes), key: read };
18358
18625
  });
18359
18626
  }
@@ -18370,17 +18637,18 @@ var Launcher = class {
18370
18637
  if (target.abi === "js" && target.wasm === SUDO_PATH) {
18371
18638
  return { abi: "js", glue: SUDO_SOURCE, path: SUDO_PATH };
18372
18639
  }
18640
+ const { asked } = target;
18373
18641
  if (target.abi === "js") {
18374
- return { abi: "js", glue: await this.text(target.wasm, caller), path: target.wasm };
18642
+ return { abi: "js", glue: await this.text(target.wasm, caller, asked), path: target.wasm };
18375
18643
  }
18376
18644
  if (target.abi !== "wasi") {
18377
18645
  const [glue, { module: module2 }] = await Promise.all([
18378
- this.text(target.glue, caller),
18379
- this.module(target.wasm, caller)
18646
+ this.text(target.glue, caller, asked),
18647
+ this.module(target.wasm, caller, asked && modulePath(asked))
18380
18648
  ]);
18381
18649
  return { glue, module: module2 };
18382
18650
  }
18383
- const { module, memory, foreign } = await this.module(target.wasm, caller);
18651
+ const { module, memory, foreign } = await this.module(target.wasm, caller, asked);
18384
18652
  const names = env.SLICC_WASM_BACKTRACE === "1" ? await this.names(target.wasm, module) : void 0;
18385
18653
  const imports = target.imports ? await this.text(target.imports, caller) : void 0;
18386
18654
  return {
@@ -18421,6 +18689,10 @@ var Launcher = class {
18421
18689
  const inherited2 = req.ppid !== void 0 ? this.scrubs.get(req.ppid) : void 0;
18422
18690
  return req.scrub ? [...inherited2 ?? [], req.scrub] : inherited2;
18423
18691
  }
18692
+ marks(pid, req) {
18693
+ if (req.tag !== void 0) this.tags.set(pid, req.tag);
18694
+ if (req.links !== void 0) this.links.set(pid, req.links);
18695
+ }
18424
18696
  start(req) {
18425
18697
  this.sampling();
18426
18698
  if (req.pgid !== void 0 && this.jobs.sessionOf(req.pgid) === void 0) {
@@ -18453,6 +18725,7 @@ var Launcher = class {
18453
18725
  cred: req.cred,
18454
18726
  ...this.owners && req.program.abi === "wasi" ? { fresh: (path, umask) => this.freshModules.add(path, umask, pid) } : {},
18455
18727
  ...this.owners ? { owners: true } : {},
18728
+ ...linksOf2(req),
18456
18729
  sealed: this.sealed,
18457
18730
  ...scrubs ? { outputFilter: (file) => scrubbedAll(file, scrubs) } : {},
18458
18731
  clipboard: this.clipboard,
@@ -18500,7 +18773,7 @@ var Launcher = class {
18500
18773
  if (scrubs) this.scrubs.set(pid, scrubs);
18501
18774
  const { cred } = req;
18502
18775
  this.creds.set(pid, () => handle.cred?.() ?? copyCred(cred));
18503
- if (req.tag !== void 0) this.tags.set(pid, req.tag);
18776
+ this.marks(pid, req);
18504
18777
  if (req.ppid !== void 0 && this.controlsJobs(req.ppid)) this.settling.started(pid, req.ppid);
18505
18778
  this.jobs.add(pid, req.ppid, (sig) => handle.signal(sig), terminal);
18506
18779
  if (req.exec && req.ppid !== void 0) this.jobs.exec(req.ppid, pid, true);
@@ -18553,6 +18826,7 @@ var Launcher = class {
18553
18826
  this.childTimes.delete(pid);
18554
18827
  this.creds.delete(pid);
18555
18828
  this.tags.delete(pid);
18829
+ this.links.delete(pid);
18556
18830
  this.granted.delete(pid);
18557
18831
  }
18558
18832
  reaped(pid, parent) {
@@ -18728,6 +19002,7 @@ var Launcher = class {
18728
19002
  umask: this.umaskOf(ppid),
18729
19003
  cred,
18730
19004
  tag: this.tagOf(ppid),
19005
+ links: grant ? grant.cred.euid : this.links.get(ppid),
18731
19006
  ...group,
18732
19007
  ...exec
18733
19008
  });
@@ -18789,7 +19064,8 @@ var Launcher = class {
18789
19064
  blocked: this.blockedBy(ppid),
18790
19065
  umask: this.umaskOf(ppid),
18791
19066
  cred: this.credOf(ppid),
18792
- tag: this.tagOf(ppid)
19067
+ tag: this.tagOf(ppid),
19068
+ links: this.links.get(ppid)
18793
19069
  })
18794
19070
  );
18795
19071
  }
@@ -18969,6 +19245,23 @@ var Launcher = class {
18969
19245
  }
18970
19246
  };
18971
19247
  }
19248
+ async revokeIntegrity(path) {
19249
+ if (path !== void 0 && (typeof path !== "string" || !path.startsWith("/"))) {
19250
+ throw new TypeError("integrity.revoke takes an absolute path");
19251
+ }
19252
+ if (!this.integrity) return;
19253
+ const at = path === void 0 ? void 0 : normalizePath(path);
19254
+ const real = at && await this.base.realpath?.(at, true).catch(() => void 0);
19255
+ const roots = [at, real].filter((p) => p !== void 0);
19256
+ this.revocations++;
19257
+ const hit = (key) => key.includes("\0") && (at === void 0 || key.split("\0").slice(0, 2).some((part) => isProtected(roots, part)));
19258
+ for (const cache of [this.compiled, this.texts]) {
19259
+ for (const key of [...cache.keys()]) if (hit(key)) cache.delete(key);
19260
+ }
19261
+ if (at === void 0) this.integrity.forget();
19262
+ for (const root of roots) this.integrity.forget(root);
19263
+ this.resetCatalogs();
19264
+ }
18972
19265
  attest(path) {
18973
19266
  return attest(this.rootFs, path, {
18974
19267
  users: this.usersMode,
@@ -19466,7 +19759,8 @@ var ATTEST_OPS = /* @__PURE__ */ new Set([
19466
19759
  "attest",
19467
19760
  "watch-files",
19468
19761
  "unwatch-files",
19469
- "chmod"
19762
+ "chmod",
19763
+ "integrity-revoke"
19470
19764
  ]);
19471
19765
  function isAttestRequest(req) {
19472
19766
  return ATTEST_OPS.has(req.op);
@@ -19484,6 +19778,7 @@ function attestOps(port) {
19484
19778
  return async (l, req) => {
19485
19779
  if (req.op === "attest") return l.attest(req.path);
19486
19780
  if (req.op === "chmod") return l.fs.chmod(req.path, modeOption(req.mode)).then(() => true);
19781
+ if (req.op === "integrity-revoke") return l.revokeIntegrity(req.path).then(() => true);
19487
19782
  if (req.op === "unwatch-files") {
19488
19783
  watches.get(req.watch)?.();
19489
19784
  return watches.delete(req.watch);