@ai-ecoverse/slicc-kernel 1.56.2 → 1.57.1

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.
@@ -533,10 +533,26 @@ function pathInode(path) {
533
533
  }
534
534
  return h;
535
535
  }
536
- function cachingBridge(bridge, now = () => performance.now()) {
537
- const stats = /* @__PURE__ */ new Map();
538
- const lstats = /* @__PURE__ */ new Map();
536
+ function inoKey(st) {
537
+ return "ino" in st && typeof st.ino === "number" ? st.ino % 4294967296 | 0 : 0;
538
+ }
539
+ function pathHash(path) {
540
+ let h = 2166136261;
541
+ for (let i = 0; i < path.length; i++) h = Math.imul(h ^ path.charCodeAt(i), 16777619);
542
+ return h === 0 ? 1 : h;
543
+ }
544
+ function index(map, key, path) {
545
+ const paths = map.get(key) ?? /* @__PURE__ */ new Set();
546
+ paths.add(path);
547
+ map.set(key, paths);
548
+ }
549
+ function cachingBridge(bridge, now = () => performance.now(), shared) {
550
+ const blind = shared !== void 0 && shared.length * 4 < SHARED_IDS_BYTES;
551
+ const cache = new StatCache(blind ? void 0 : shared);
552
+ const { stats, lstats } = cache;
539
553
  const cached = (map, path, get) => {
554
+ if (blind) return get();
555
+ cache.fresh();
540
556
  let hit = map.get(path);
541
557
  if (hit === void 0 || "until" in hit && hit.until <= now()) {
542
558
  try {
@@ -544,22 +560,29 @@ function cachingBridge(bridge, now = () => performance.now()) {
544
560
  } catch (e) {
545
561
  hit = { error: e, until: now() + MISS_TTL_MS };
546
562
  }
547
- map.set(path, hit);
563
+ cache.put(map, path, hit);
548
564
  }
549
565
  if ("until" in hit) throw hit.error;
550
566
  return hit;
551
567
  };
552
- const invalidate = () => {
553
- stats.clear();
554
- lstats.clear();
568
+ const invalidate = () => cache.mutated([]);
569
+ const touched = (path, ino) => cache.changed(path, ino);
570
+ const mutating = (op, ...paths) => {
571
+ try {
572
+ return op();
573
+ } finally {
574
+ cache.mutated(paths);
575
+ }
555
576
  };
556
- const mutating = (op) => {
577
+ const rewriting = (p, op, ...also) => {
578
+ const ino = cache.inoOf(p, () => bridge.stat(p));
557
579
  try {
558
580
  return op();
559
581
  } finally {
560
- invalidate();
582
+ cache.mutated([p, ...also], ino);
561
583
  }
562
584
  };
585
+ const entry = (p) => [p, p.slice(0, p.lastIndexOf("/")) || "/"];
563
586
  return {
564
587
  readFile: (p) => bridge.readFile(p),
565
588
  readdir: (p) => bridge.readdir(p),
@@ -567,9 +590,9 @@ function cachingBridge(bridge, now = () => performance.now()) {
567
590
  const list = bridge.readdirStat(p);
568
591
  const base = p === "/" ? "" : p;
569
592
  for (const [name2, st] of list) {
570
- if (!st) continue;
571
- lstats.set(`${base}/${name2}`, st);
572
- if (!st.isSymbolicLink) stats.set(`${base}/${name2}`, st);
593
+ if (!st || blind) continue;
594
+ cache.put(lstats, `${base}/${name2}`, st);
595
+ if (!st.isSymbolicLink) cache.put(stats, `${base}/${name2}`, st);
573
596
  }
574
597
  return list;
575
598
  },
@@ -584,40 +607,41 @@ function cachingBridge(bridge, now = () => performance.now()) {
584
607
  return false;
585
608
  }
586
609
  },
587
- writeFile: (p, bytes, open) => mutating(() => bridge.writeFile(p, bytes, open)),
588
- mkdir: (p) => mutating(() => bridge.mkdir(p)),
610
+ writeFile: (p, bytes, open) => rewriting(p, () => bridge.writeFile(p, bytes, open), ...entry(p).slice(1)),
611
+ mkdir: (p) => mutating(() => bridge.mkdir(p), ...entry(p)),
589
612
  rm: (p) => mutating(() => bridge.rm(p)),
590
613
  rename: (from, to) => mutating(() => bridge.rename(from, to)),
591
614
  unlink: (p) => mutating(() => bridge.unlink(p)),
592
615
  rmdir: (p) => mutating(() => bridge.rmdir(p)),
593
- symlink: (target, link) => mutating(() => bridge.symlink(target, link)),
594
- chmod: (p, mode) => mutating(() => bridge.chmod(p, mode)),
595
- utimes: (p, a, m) => mutating(() => bridge.utimes(p, a, m)),
596
- lutimes: (p, a, m) => mutating(() => bridge.lutimes(p, a, m)),
616
+ symlink: (target, link) => mutating(() => bridge.symlink(target, link), ...entry(link)),
617
+ chmod: (p, mode) => rewriting(p, () => bridge.chmod(p, mode)),
618
+ utimes: (p, a, m) => rewriting(p, () => bridge.utimes(p, a, m)),
619
+ lutimes: (p, a, m) => mutating(() => bridge.lutimes(p, a, m), p),
597
620
  ...bridge.hold ? { hold: bridge.hold.bind(bridge) } : {},
598
621
  ...bridge.kept ? { kept: bridge.kept.bind(bridge) } : {},
599
- ...bridge.pinned ? { pinned: (handle) => pinnedIo(bridge, handle, mutating) } : {},
600
- ...bridge.create ? { create: (p) => mutating(() => bridge.create?.(p)) } : {},
622
+ ...bridge.pinned ? { pinned: (handle) => pinnedIo(bridge, handle, rewriting) } : {},
623
+ ...bridge.create ? { create: (p) => mutating(() => bridge.create?.(p), ...entry(p)) } : {},
601
624
  ...bridge.pread && bridge.pwrite && bridge.truncate ? {
602
625
  pread: bridge.pread.bind(bridge),
603
- pwrite: (p, at, bytes, transfer) => mutating(() => bridge.pwrite?.(p, at, bytes, transfer)),
604
- truncate: (p, size) => mutating(() => bridge.truncate?.(p, size))
626
+ pwrite: (p, at, bytes, transfer) => rewriting(p, () => bridge.pwrite?.(p, at, bytes, transfer)),
627
+ truncate: (p, size) => rewriting(p, () => bridge.truncate?.(p, size))
605
628
  } : {},
606
- invalidate
629
+ invalidate,
630
+ touched
607
631
  };
608
632
  }
609
- function pinnedIo(bridge, handle, mutating) {
633
+ function pinnedIo(bridge, handle, rewriting) {
610
634
  const io = bridge.pinned(handle);
611
635
  const { pread, pwrite, truncate } = io;
612
636
  return {
613
637
  readFile: (p) => io.readFile(p),
614
- writeFile: (p, bytes, open) => mutating(() => io.writeFile(p, bytes, open)),
638
+ writeFile: (p, bytes, open) => rewriting(p, () => io.writeFile(p, bytes, open)),
615
639
  pread: (p, at, n, version) => pread(p, at, n, version),
616
- pwrite: (p, at, bytes, transfer) => mutating(() => pwrite(p, at, bytes, transfer)),
617
- truncate: (p, size) => mutating(() => truncate(p, size))
640
+ pwrite: (p, at, bytes, transfer) => rewriting(p, () => pwrite(p, at, bytes, transfer)),
641
+ truncate: (p, size) => rewriting(p, () => truncate(p, size))
618
642
  };
619
643
  }
620
- var WasiError, FileBuffer, LocalFile, MAX_LINKS, MISS_TTL_MS;
644
+ var WasiError, FileBuffer, LocalFile, MAX_LINKS, MISS_TTL_MS, FS_GEN, RING, RING_SIZE, SLOT, SHARED_IDS_BYTES, StatCache;
621
645
  var init_wasi_files = __esm({
622
646
  "src/process/wasi/wasi-files.ts"() {
623
647
  "use strict";
@@ -807,6 +831,110 @@ var init_wasi_files = __esm({
807
831
  };
808
832
  MAX_LINKS = 40;
809
833
  MISS_TTL_MS = 20;
834
+ FS_GEN = 4;
835
+ RING = 8;
836
+ RING_SIZE = 64;
837
+ SLOT = 3;
838
+ SHARED_IDS_BYTES = (RING + RING_SIZE * SLOT) * 4;
839
+ StatCache = class {
840
+ stats = /* @__PURE__ */ new Map();
841
+ lstats = /* @__PURE__ */ new Map();
842
+ byHash = /* @__PURE__ */ new Map();
843
+ byIno = /* @__PURE__ */ new Map();
844
+ shared;
845
+ seen;
846
+ constructor(shared) {
847
+ this.shared = shared;
848
+ this.seen = shared ? Atomics.load(shared, FS_GEN) : 0;
849
+ }
850
+ put(map, path, entry) {
851
+ map.set(path, entry);
852
+ if (!this.shared) return;
853
+ index(this.byHash, pathHash(path), path);
854
+ const ino = inoKey(entry);
855
+ if (ino !== 0) index(this.byIno, ino, path);
856
+ }
857
+ fresh() {
858
+ if (!this.shared) return;
859
+ const now = Atomics.load(this.shared, FS_GEN);
860
+ if (now === this.seen) return;
861
+ this.replay(now);
862
+ this.seen = now;
863
+ }
864
+ changed(path, ino = 0) {
865
+ const key = ino % 4294967296 | 0;
866
+ if (this.shared) {
867
+ const g = this.publish(this.shared, path === void 0 ? 0 : pathHash(path), key);
868
+ this.replay(g - 1);
869
+ this.seen = g;
870
+ if (path === void 0) this.clear();
871
+ else this.drop(pathHash(path), key);
872
+ }
873
+ if (path === void 0) return;
874
+ this.stats.delete(path);
875
+ this.lstats.delete(path);
876
+ }
877
+ mutated(paths, ino = 0) {
878
+ this.clear();
879
+ if (!this.shared) return;
880
+ const key = ino % 4294967296 | 0;
881
+ for (const [i, hash] of (paths.length > 0 ? paths.map(pathHash) : [0]).entries()) {
882
+ this.seen = this.publish(this.shared, hash, i === 0 ? key : 0);
883
+ }
884
+ }
885
+ inoOf(path, look) {
886
+ if (!this.shared) return 0;
887
+ this.fresh();
888
+ const hit = this.stats.get(path);
889
+ if (hit && "ino" in hit && typeof hit.ino === "number") return hit.ino;
890
+ try {
891
+ return look().ino ?? 0;
892
+ } catch {
893
+ return 0;
894
+ }
895
+ }
896
+ publish(shared, hash, ino) {
897
+ const g = Atomics.add(shared, FS_GEN, 1) + 1;
898
+ const at = RING + g % RING_SIZE * SLOT;
899
+ Atomics.store(shared, at, 0);
900
+ Atomics.store(shared, at + 1, hash);
901
+ Atomics.store(shared, at + 2, ino);
902
+ Atomics.store(shared, at, g);
903
+ return g;
904
+ }
905
+ replay(upTo) {
906
+ const shared = this.shared;
907
+ const missed = [];
908
+ for (let g = this.seen + 1; g <= upTo && upTo - this.seen <= RING_SIZE; g++) {
909
+ const at = RING + g % RING_SIZE * SLOT;
910
+ const tag = Atomics.load(shared, at);
911
+ const hash = Atomics.load(shared, at + 1);
912
+ const ino = Atomics.load(shared, at + 2);
913
+ if (tag !== g || hash === 0 || Atomics.load(shared, at) !== g) break;
914
+ missed.push([hash, ino]);
915
+ }
916
+ if (missed.length < upTo - this.seen) this.clear();
917
+ else for (const [hash, ino] of missed) this.drop(hash, ino);
918
+ }
919
+ drop(hash, ino) {
920
+ const paths = [
921
+ ...this.byHash.get(hash) ?? [],
922
+ ...ino === 0 ? [] : this.byIno.get(ino) ?? []
923
+ ];
924
+ for (const path of paths) {
925
+ this.stats.delete(path);
926
+ this.lstats.delete(path);
927
+ }
928
+ this.byHash.delete(hash);
929
+ if (ino !== 0) this.byIno.delete(ino);
930
+ }
931
+ clear() {
932
+ this.stats.clear();
933
+ this.lstats.clear();
934
+ this.byHash.clear();
935
+ this.byIno.clear();
936
+ }
937
+ };
810
938
  }
811
939
  });
812
940
 
@@ -836,6 +964,7 @@ var init_wasi_threads = __esm({
836
964
  "src/process/wasi/wasi-threads.ts"() {
837
965
  "use strict";
838
966
  init_protocol();
967
+ init_wasi_files();
839
968
  LAST_TID = 0;
840
969
  RUNNING = 1;
841
970
  MAIN_TID = 1;
@@ -858,7 +987,7 @@ var init_wasi_threads = __esm({
858
987
  this.memory = memory;
859
988
  this.cap = cap;
860
989
  this.tid = tid;
861
- this.ids = new Int32Array(ids ?? new SharedArrayBuffer(16));
990
+ this.ids = new Int32Array(ids ?? new SharedArrayBuffer(SHARED_IDS_BYTES));
862
991
  if (!ids) Atomics.store(this.ids, LAST_TID, MAIN_TID);
863
992
  }
864
993
  spawn(arg) {
@@ -1327,7 +1456,14 @@ var init_wasi_fds = __esm({
1327
1456
  handed.add(e.file.buffer);
1328
1457
  promoted.set(e.file, fd);
1329
1458
  }
1330
- this.table.set(fd, { type: "kernel", kind: "file", nonblock: false, append: e.file.append });
1459
+ this.table.set(fd, {
1460
+ type: "kernel",
1461
+ kind: "file",
1462
+ nonblock: false,
1463
+ append: e.file.append,
1464
+ path: e.file.path,
1465
+ ino: this.inoOf(e.file.path)
1466
+ });
1331
1467
  }
1332
1468
  for (const file of promoted.keys()) {
1333
1469
  if (--file.buffer.opens <= 0) {
@@ -1441,9 +1577,21 @@ var init_wasi_fds = __esm({
1441
1577
  buffer.orphan();
1442
1578
  this.buffers.delete(path);
1443
1579
  }
1580
+ inoOf(path) {
1581
+ try {
1582
+ return this.fs.stat(path).ino;
1583
+ } catch {
1584
+ return void 0;
1585
+ }
1586
+ }
1444
1587
  renamed(from, to) {
1445
1588
  if (from === to) return;
1446
1589
  if (this.shared) this.kernel.call({ op: "fd-path-renamed", from, to });
1590
+ for (const e of this.table.values()) {
1591
+ if (e.type === "kernel" && e.path !== void 0 && within(e.path, from)) {
1592
+ e.path = to + e.path.slice(from.length);
1593
+ }
1594
+ }
1447
1595
  const moved = [];
1448
1596
  for (const [p, buffer] of this.buffers) {
1449
1597
  if (within(p, from)) moved.push([p, buffer]);
@@ -1522,7 +1670,10 @@ var init_wasi_fds = __esm({
1522
1670
  const made = !existing && this.createNew(path, oflags);
1523
1671
  const truncate = made || (oflags & OFLAGS.TRUNC) !== 0;
1524
1672
  const fd = this.kernel.sys.openVfs(path, flags, 0, truncate ? { truncate } : {});
1525
- this.table.set(fd, { type: "kernel", kind: "file", nonblock: false, append });
1673
+ const was = existing && this.inoOf(path);
1674
+ if (truncate) this.fs.touched?.(path, was);
1675
+ const ino = existing ? was : this.inoOf(path);
1676
+ this.table.set(fd, { type: "kernel", kind: "file", nonblock: false, append, path, ino });
1526
1677
  if (append) this.publishFlags(fd, { nonblock: false, append });
1527
1678
  this.bump();
1528
1679
  return fd;
@@ -2316,11 +2467,29 @@ var init_wasi_host = __esm({
2316
2467
  if (!kernel.sys.pread) throw new WasiError("ESPIPE");
2317
2468
  return kernel.sys.pread(fd, Math.min(max, MAX_READ), at);
2318
2469
  },
2319
- pwrite: (body, at) => kernel.call({ op: "fd-pwrite", fd, offset: at, body }),
2470
+ pwrite: (body, at) => this.changed(fd, e, kernel.call({ op: "fd-pwrite", fd, offset: at, body })),
2320
2471
  size: () => this.kernelStat(fd).size,
2321
- truncate: (size) => void kernel.call({ op: "fd-resize", fd, size })
2472
+ truncate: (size) => this.changed(fd, e, void kernel.call({ op: "fd-resize", fd, size }))
2322
2473
  };
2323
2474
  }
2475
+ unnamed = /* @__PURE__ */ new WeakSet();
2476
+ changed(fd, e, result) {
2477
+ if (e.path === void 0 && !this.unnamed.has(e)) this.learnPath(fd, e);
2478
+ this.o.fs.touched?.(e.path, e.ino);
2479
+ return result;
2480
+ }
2481
+ learnPath(fd, e) {
2482
+ this.unnamed.add(e);
2483
+ let path;
2484
+ try {
2485
+ const st = this.kernelStat(fd);
2486
+ path = st.orphan ? void 0 : st.path;
2487
+ if (path !== void 0) e.ino = this.o.fs.stat(path).ino;
2488
+ } catch {
2489
+ return;
2490
+ }
2491
+ e.path = path;
2492
+ }
2324
2493
  kernelStat(fd) {
2325
2494
  return this.o.kernel.call({ op: "fd-vfs-stat", fd });
2326
2495
  }
@@ -2329,7 +2498,8 @@ var init_wasi_host = __esm({
2329
2498
  const e = this.fds.get(fd);
2330
2499
  if (e.type === "kernel") {
2331
2500
  try {
2332
- return this.o.kernel.sys.write(fd, data, { nonblock: e.nonblock });
2501
+ const n = this.o.kernel.sys.write(fd, data, { nonblock: e.nonblock });
2502
+ return this.fds.kind(fd, e) === "file" ? this.changed(fd, e, n) : n;
2333
2503
  } catch (err) {
2334
2504
  const broken = err.code === "EPIPE";
2335
2505
  if (broken && this.fds.kind(fd, e) !== "socket") throw new WasiExit(SIGPIPE_EXIT);
@@ -3025,8 +3195,8 @@ var init_sync_sab_wire = __esm({
3025
3195
  });
3026
3196
 
3027
3197
  // src/process/js/async-sab.ts
3028
- async function changed(header, index2, seen, waitAsync = defaultWaitAsync) {
3029
- const waited = waitAsync(header, index2, seen);
3198
+ async function changed(header, index3, seen, waitAsync = defaultWaitAsync) {
3199
+ const waited = waitAsync(header, index3, seen);
3030
3200
  if (waited.async) await waited.value;
3031
3201
  }
3032
3202
  function createAsyncSabTransport(sab, port3, waitAsync = defaultWaitAsync) {
@@ -3184,7 +3354,9 @@ function parseSyncFsStat(json) {
3184
3354
  ...s.readonly === true ? { readonly: true } : {},
3185
3355
  ...typeof s.maxFile === "number" ? { maxFile: s.maxFile } : {},
3186
3356
  ...s.ranged === true ? { ranged: true } : {},
3187
- ...typeof s.version === "string" ? { version: s.version } : {}
3357
+ ...typeof s.version === "string" ? { version: s.version } : {},
3358
+ ...typeof s.uid === "number" ? { uid: s.uid } : {},
3359
+ ...typeof s.gid === "number" ? { gid: s.gid } : {}
3188
3360
  };
3189
3361
  }
3190
3362
  var SYNC_FS_OPS, SYNC_FS_REQUEST_TIMEOUT_MS;
@@ -3215,7 +3387,9 @@ var init_sync_fs_wire = __esm({
3215
3387
  "kept",
3216
3388
  "pread",
3217
3389
  "pwrite",
3218
- "truncate"
3390
+ "truncate",
3391
+ "chown",
3392
+ "lchown"
3219
3393
  ];
3220
3394
  SYNC_FS_REQUEST_TIMEOUT_MS = 25e3;
3221
3395
  }
@@ -3916,7 +4090,7 @@ function openRequest(abs, o) {
3916
4090
  ...o.truncate ? { truncate: true } : {}
3917
4091
  };
3918
4092
  }
3919
- function index(n, what) {
4093
+ function index2(n, what) {
3920
4094
  if (!Number.isSafeInteger(n) || n < 0) throw new JsCallError("EINVAL", what);
3921
4095
  }
3922
4096
  var CHUNK2, MAX_IO, O_WRONLY3, O_RDWR3, O_APPEND3;
@@ -4207,8 +4381,8 @@ function createSyncFsSabBridge(transport, opts = {}) {
4207
4381
  if (typeof v !== "boolean") throw errnoError("EIO", path);
4208
4382
  return v;
4209
4383
  },
4210
- mkdir: (path) => {
4211
- run({ op: "mkdir", path }, path);
4384
+ mkdir: (path, mode) => {
4385
+ run({ op: "mkdir", path, ...mode !== void 0 ? { mode } : {} }, path);
4212
4386
  },
4213
4387
  rm: (path) => {
4214
4388
  run({ op: "rm", path }, path);
@@ -4239,9 +4413,14 @@ function createSyncFsSabBridge(transport, opts = {}) {
4239
4413
  lutimes: (path, atimeMs, mtimeMs) => {
4240
4414
  run({ op: "lutimes", path, atimeMs, mtimeMs }, path);
4241
4415
  },
4242
- create: (path) => {
4243
- run({ op: "create", path }, path);
4416
+ create: (path, mode) => {
4417
+ run({ op: "create", path, ...mode !== void 0 ? { mode } : {} }, path);
4418
+ },
4419
+ chown: (path, uid, gid) => {
4420
+ const ids = { ...uid !== void 0 ? { uid } : {}, ...gid !== void 0 ? { gid } : {} };
4421
+ run({ op: "lchown", path, ...ids }, path);
4244
4422
  },
4423
+ ...opts.owners ? { owners: true } : {},
4245
4424
  hold: (path, held, open, handle) => {
4246
4425
  const mode = (held ? 1 : 0) | (open ? 2 : 0);
4247
4426
  if (mode === 3) return json({ op: "hold", path, mode }, path);
@@ -4275,7 +4454,7 @@ function syncOps(kernel, shared) {
4275
4454
  return got;
4276
4455
  };
4277
4456
  const read2 = (fd, max = CHUNK2) => {
4278
- index(max, "read");
4457
+ index2(max, "read");
4279
4458
  const emulated = readDevice(info(fd), max, shared.random);
4280
4459
  if (emulated) return emulated;
4281
4460
  const r = kernel.blocking({ op: "fd-read", fd, max: Math.min(max, MAX_IO) });
@@ -4353,8 +4532,8 @@ function syncFile(kernel, fd, path, append, forget) {
4353
4532
  return kernel.json({ op: "fd-vfs-stat", fd }).size;
4354
4533
  },
4355
4534
  read(position, length) {
4356
- index(position, "read");
4357
- index(length, "read");
4535
+ index2(position, "read");
4536
+ index2(length, "read");
4358
4537
  const parts = [];
4359
4538
  let got = 0;
4360
4539
  do {
@@ -4369,7 +4548,7 @@ function syncFile(kernel, fd, path, append, forget) {
4369
4548
  return joined(parts, got);
4370
4549
  },
4371
4550
  write(position, data) {
4372
- index(position, "write");
4551
+ index2(position, "write");
4373
4552
  let at = 0;
4374
4553
  do {
4375
4554
  live();
@@ -4383,7 +4562,7 @@ function syncFile(kernel, fd, path, append, forget) {
4383
4562
  },
4384
4563
  truncate(size) {
4385
4564
  live();
4386
- index(size, "truncate");
4565
+ index2(size, "truncate");
4387
4566
  kernel.call({ op: "fd-resize", fd, size });
4388
4567
  },
4389
4568
  sync() {
@@ -4779,7 +4958,7 @@ function createContext(o) {
4779
4958
  return got;
4780
4959
  };
4781
4960
  const read2 = async (fd, max = CHUNK2, signal) => {
4782
- index(max, "read");
4961
+ index2(max, "read");
4783
4962
  const emulated = readDevice(await info(fd), max, random);
4784
4963
  if (emulated) return emulated;
4785
4964
  return readPipe(kernel, fd, Math.min(max, MAX_IO), signal);
@@ -4926,8 +5105,8 @@ function fileHandle(kernel, fd, path, append, forget) {
4926
5105
  return s.size;
4927
5106
  },
4928
5107
  async read(position, length) {
4929
- index(position, "read");
4930
- index(length, "read");
5108
+ index2(position, "read");
5109
+ index2(length, "read");
4931
5110
  const parts = [];
4932
5111
  let got = 0;
4933
5112
  do {
@@ -4942,7 +5121,7 @@ function fileHandle(kernel, fd, path, append, forget) {
4942
5121
  return joined(parts, got);
4943
5122
  },
4944
5123
  async write(position, data) {
4945
- index(position, "write");
5124
+ index2(position, "write");
4946
5125
  let at = 0;
4947
5126
  do {
4948
5127
  live();
@@ -4956,7 +5135,7 @@ function fileHandle(kernel, fd, path, append, forget) {
4956
5135
  },
4957
5136
  async truncate(size) {
4958
5137
  live();
4959
- index(size, "truncate");
5138
+ index2(size, "truncate");
4960
5139
  await kernel.call({ op: "fd-resize", fd, size });
4961
5140
  },
4962
5141
  async sync() {
@@ -5447,6 +5626,7 @@ function createNodeOps(h) {
5447
5626
  return {
5448
5627
  getattr(node) {
5449
5628
  const st = statOf(node);
5629
+ const owned = bridgeOf(node).owners === true;
5450
5630
  const s = node.live;
5451
5631
  const size = Fs.isDir(node.mode) ? 4096 : s.ranged?.size() ?? (s.loaded ? s.len : st.size);
5452
5632
  const mtime = new Date(st.mtimeMs ?? 0);
@@ -5455,8 +5635,8 @@ function createNodeOps(h) {
5455
5635
  ino: st.ino ?? inodeOf(liveNodePath(node)),
5456
5636
  mode: node.mode,
5457
5637
  nlink: 1,
5458
- uid: 0,
5459
- gid: 0,
5638
+ uid: owned ? st.uid ?? 0 : 0,
5639
+ gid: owned ? st.gid ?? 0 : 0,
5460
5640
  rdev: 0,
5461
5641
  size,
5462
5642
  atime: new Date(st.atimeMs ?? mtime.getTime()),
@@ -5499,14 +5679,17 @@ function createNodeOps(h) {
5499
5679
  mknod(parent, name2, mode) {
5500
5680
  parent.live.listed = void 0;
5501
5681
  const path = childPath(parent, name2);
5502
- if (Fs.isDir(mode)) {
5503
- call2(() => bridgeOf(parent).mkdir(path));
5504
- const perm = mode & PERM_MASK & ~UMASK;
5505
- if (perm !== DEFAULT_DIR_PERM) metadataCall(() => bridgeOf(parent).chmod(path, perm));
5682
+ const bridge = bridgeOf(parent);
5683
+ const perm = bridge.owners ? mode & PERM_MASK : void 0;
5684
+ if (Fs.isDir(mode) && perm !== void 0) {
5685
+ call2(() => bridge.mkdir(path, perm));
5686
+ } else if (Fs.isDir(mode)) {
5687
+ call2(() => bridge.mkdir(path));
5688
+ const masked = mode & PERM_MASK & ~UMASK;
5689
+ if (masked !== DEFAULT_DIR_PERM) metadataCall(() => bridge.chmod(path, masked));
5506
5690
  } else if (Fs.isFile(mode)) {
5507
- const bridge = bridgeOf(parent);
5508
5691
  call2(
5509
- () => bridge.create ? bridge.create(path) : bridge.writeFile(path, new Uint8Array(0))
5692
+ () => bridge.create ? bridge.create(path, perm) : bridge.writeFile(path, new Uint8Array(0))
5510
5693
  );
5511
5694
  } else {
5512
5695
  throw new Fs.ErrnoError(wasiErrno("EPERM"));
@@ -5909,6 +6092,32 @@ function guardRenames(Fs) {
5909
6092
  rename(from, to);
5910
6093
  };
5911
6094
  }
6095
+ function idOf(value2) {
6096
+ return value2 === -1 || value2 === 4294967295 ? void 0 : value2 >>> 0;
6097
+ }
6098
+ function useLiveChown(Fs) {
6099
+ const chown = Fs.chown?.bind(Fs);
6100
+ if (!chown) return;
6101
+ Fs.chown = (path, uid, gid, dontFollow) => {
6102
+ const node = typeof path === "string" ? Fs.lookupPath(path, { follow: !dontFollow }).node : path;
6103
+ if (!node.live || node.live.orphan) return chown(path, uid, gid, dontFollow);
6104
+ try {
6105
+ node.mount.opts.bridge.chown?.(liveNodePath(node), idOf(uid), idOf(gid));
6106
+ } catch (err) {
6107
+ throw toErrno(Fs, err);
6108
+ }
6109
+ node.live.stat = void 0;
6110
+ if (node.parent?.live) node.parent.live.listed = void 0;
6111
+ };
6112
+ const { getStreamChecked } = Fs;
6113
+ if (!Fs.fchown || !getStreamChecked) return;
6114
+ Fs.fchown = (fd, uid, gid) => {
6115
+ const stream = getStreamChecked.call(Fs, fd);
6116
+ const live = stream.node.live !== void 0;
6117
+ const target = live || stream.path === void 0 ? stream.node : stream.path;
6118
+ Fs.chown(target, uid, gid);
6119
+ };
6120
+ }
5912
6121
  var S_IFDIR, S_IFREG, S_IFLNK, PERM_MASK, DEFAULT_DIR_PERM, UMASK, SEEK_CUR, SEEK_END, EARLY_WHOLE_FILE;
5913
6122
  var init_live_vfs_fs = __esm({
5914
6123
  "src/realm/live-vfs-fs.ts"() {
@@ -5953,6 +6162,7 @@ function mountVfsIntoEmscripten(Fs, deps) {
5953
6162
  const dirs = topLevelDirs(bridge, warn);
5954
6163
  const { plugin, mounted } = mountLiveVfsDirs(Fs, bridge, dirs ?? [], warn);
5955
6164
  if (dirs) liveRoot(Fs, bridge);
6165
+ if (bridge.owners && Fs.lookupPath) useLiveChown(Fs);
5956
6166
  try {
5957
6167
  Fs.chdir(deps.cwd);
5958
6168
  } catch (err) {
@@ -8358,6 +8568,9 @@ function reportGrowth(imports, publish) {
8358
8568
  return grown;
8359
8569
  };
8360
8570
  }
8571
+ function callerOwner(Fs, init) {
8572
+ return init.owners ? {} : { owner: ownByCaller(Fs, init.cred, credAbi(init.program.glue)) };
8573
+ }
8361
8574
  async function runWasmProcess(init, port3, deps = {}) {
8362
8575
  const signals = new SignalGate(
8363
8576
  createSyncSabTransport(init.sab, port3, {
@@ -8468,7 +8681,7 @@ async function runWasmProcess(init, port3, deps = {}) {
8468
8681
  const running = module;
8469
8682
  if (!hasStreams(running)) return runStdioOnly(running, init);
8470
8683
  const vfs = mountVfsIntoEmscripten(running.FS, {
8471
- bridge: createSyncFsSabBridge(transport),
8684
+ bridge: createSyncFsSabBridge(transport, init.owners ? { owners: true } : {}),
8472
8685
  cwd: init.cwd,
8473
8686
  warn: deps.warn ?? say(2)
8474
8687
  });
@@ -8510,7 +8723,7 @@ async function runWasmProcess(init, port3, deps = {}) {
8510
8723
  describeFork: () => describeForFork(running.FS, sys, streams, livePath),
8511
8724
  inherit: (actions) => describeInherited(running.FS, sys, streams, livePath, actions),
8512
8725
  stdioPromoter: () => vfsPromoter(running.FS, sys, streams, livePath),
8513
- owner: ownByCaller(running.FS, init.cred, credAbi(init.program.glue))
8726
+ ...callerOwner(running.FS, init)
8514
8727
  });
8515
8728
  running.sliccKernel.http = createHttpKernel(transport);
8516
8729
  running.sliccKernel.hostname = hostnameOf(init.env);
@@ -9694,8 +9907,8 @@ function readExports(r, s) {
9694
9907
  for (let i = r.u32(); i > 0; i--) {
9695
9908
  const name2 = r.name();
9696
9909
  const kind = r.byte();
9697
- const index2 = r.u32();
9698
- if (kind === 0) s.info.exports[name2] = index2;
9910
+ const index3 = r.u32();
9911
+ if (kind === 0) s.info.exports[name2] = index3;
9699
9912
  }
9700
9913
  }
9701
9914
  function readElems(r, s) {
@@ -9998,15 +10211,15 @@ var init_wasix_linker = __esm({
9998
10211
  return out;
9999
10212
  }
10000
10213
  allocateClosure() {
10001
- const index2 = this.indices.allocate(this.table.length);
10002
- growTable(this.table, index2 + 1);
10003
- this.live.add(index2);
10004
- return index2;
10214
+ const index3 = this.indices.allocate(this.table.length);
10215
+ growTable(this.table, index3 + 1);
10216
+ this.live.add(index3);
10217
+ return index3;
10005
10218
  }
10006
- freeClosure(index2) {
10007
- this.indices?.free(index2);
10008
- this.live.delete(index2);
10009
- this.table.set(index2, null);
10219
+ freeClosure(index3) {
10220
+ this.indices?.free(index3);
10221
+ this.live.delete(index3);
10222
+ this.table.set(index3, null);
10010
10223
  }
10011
10224
  live = /* @__PURE__ */ new Set();
10012
10225
  prepareClosure(record) {
@@ -10239,12 +10452,12 @@ var init_wasix_linker = __esm({
10239
10452
  this.indexTable();
10240
10453
  const known = this.slots.get(fn);
10241
10454
  if (known !== void 0) return known;
10242
- const index2 = this.reserve(1);
10243
- growTable(this.table, index2 + 1);
10244
- this.table.set(index2, fn);
10245
- this.slots.set(fn, index2);
10246
- this.publisher?.({ kind: "slot", index: index2, handle, name: name2 });
10247
- return index2;
10455
+ const index3 = this.reserve(1);
10456
+ growTable(this.table, index3 + 1);
10457
+ this.table.set(index3, fn);
10458
+ this.slots.set(fn, index3);
10459
+ this.publisher?.({ kind: "slot", index: index3, handle, name: name2 });
10460
+ return index3;
10248
10461
  }
10249
10462
  allocate(size, alignment) {
10250
10463
  if (size === 0) return 0;
@@ -10529,11 +10742,11 @@ var init_wasix_sockets = __esm({
10529
10742
  });
10530
10743
 
10531
10744
  // src/process/wasi/wasix-host.ts
10532
- function futexWait(view, index2, expected, ms, interrupted) {
10745
+ function futexWait(view, index3, expected, ms, interrupted) {
10533
10746
  const deadline = performance.now() + ms;
10534
10747
  for (; ; ) {
10535
10748
  const slice = interrupted ? Math.min(FUTEX_SLICE_MS, ms) : ms;
10536
- if (Atomics.wait(view, index2, expected, slice) !== "timed-out") return true;
10749
+ if (Atomics.wait(view, index3, expected, slice) !== "timed-out") return true;
10537
10750
  if (interrupted?.()) return true;
10538
10751
  ms = deadline - performance.now();
10539
10752
  if (ms <= 0) return false;
@@ -10662,21 +10875,21 @@ var init_wasix_host = __esm({
10662
10875
  if (!this.linker) throw new WasiError("ENOTSUP");
10663
10876
  this.mem.view().setUint32(out, this.linker.allocateClosure(), true);
10664
10877
  },
10665
- closure_prepare: (backing, index2, argTypes, argCount, resultTypes, resultCount, userData) => {
10878
+ closure_prepare: (backing, index3, argTypes, argCount, resultTypes, resultCount, userData) => {
10666
10879
  if (!this.linker) throw new WasiError("ENOTSUP");
10667
10880
  const params = valueTypes(this.mem.bytes(argTypes, argCount));
10668
10881
  const results = valueTypes(this.mem.bytes(resultTypes, resultCount));
10669
10882
  if (!params || !results) throw new WasiError("EINVAL");
10670
10883
  try {
10671
- this.linker.prepareClosure({ index: index2, backing, userData, params, results });
10884
+ this.linker.prepareClosure({ index: index3, backing, userData, params, results });
10672
10885
  } catch (e) {
10673
10886
  if (e instanceof DlError) throw new WasiError("EINVAL");
10674
10887
  throw e;
10675
10888
  }
10676
10889
  },
10677
- closure_free: (index2) => {
10890
+ closure_free: (index3) => {
10678
10891
  if (!this.linker) throw new WasiError("ENOTSUP");
10679
- this.linker.freeClosure(index2);
10892
+ this.linker.freeClosure(index3);
10680
10893
  },
10681
10894
  call_dynamic: (...a) => void callDynamic(
10682
10895
  { mem: this.mem, table: () => this.table, types: this.types },
@@ -10981,10 +11194,10 @@ function parseFunctionNames(payload) {
10981
11194
  if (end > payload.length) throw new RangeError("truncated name section");
10982
11195
  if (id === 1) {
10983
11196
  for (let count = leb(); count > 0; count--) {
10984
- const index2 = leb();
11197
+ const index3 = leb();
10985
11198
  const length = leb();
10986
11199
  if (at + length > end) throw new RangeError("truncated name section");
10987
- names.set(index2, decoder3.decode(payload.subarray(at, at + length)));
11200
+ names.set(index3, decoder3.decode(payload.subarray(at, at + length)));
10988
11201
  at += length;
10989
11202
  }
10990
11203
  }
@@ -11008,7 +11221,7 @@ function nameFrame(line, module, name2) {
11008
11221
  }
11009
11222
  function sidecarNames(read2, path) {
11010
11223
  let names;
11011
- return (index2) => {
11224
+ return (index3) => {
11012
11225
  if (!names) {
11013
11226
  try {
11014
11227
  names = parseFunctionNames(read2(path));
@@ -11016,7 +11229,7 @@ function sidecarNames(read2, path) {
11016
11229
  names = /* @__PURE__ */ new Map();
11017
11230
  }
11018
11231
  }
11019
- return names.get(index2);
11232
+ return names.get(index3);
11020
11233
  };
11021
11234
  }
11022
11235
  var UNNAMED_FRAME, WASM_MODULE;
@@ -11377,7 +11590,7 @@ async function runWasiProcess(init, port3) {
11377
11590
  parent: () => identify(transport, init).ppid,
11378
11591
  kernel: { sys: traced(stats, "kernel", sys), call: call2 },
11379
11592
  fs: procfsBridge(
11380
- cachingBridge(traced(stats, "fs", createSyncFsSabBridge(transport))),
11593
+ cachingBridge(traced(stats, "fs", createSyncFsSabBridge(transport)), void 0, threads?.ids),
11381
11594
  procTree(call2, id.pid)
11382
11595
  ),
11383
11596
  ...descriptors(init, fork, threads)
@@ -11470,7 +11683,10 @@ async function runWasiThread(init, port3) {
11470
11683
  ppid: id.ppid,
11471
11684
  parent: () => identify(transport, init).ppid,
11472
11685
  kernel: { sys, call: call2 },
11473
- fs: procfsBridge(cachingBridge(createSyncFsSabBridge(transport)), procTree(call2, id.pid)),
11686
+ fs: procfsBridge(
11687
+ cachingBridge(createSyncFsSabBridge(transport), void 0, threads.ids),
11688
+ procTree(call2, id.pid)
11689
+ ),
11474
11690
  shared: threads.ids
11475
11691
  });
11476
11692
  const program = await programImports(init.program, host, transport, thread.tid);
@@ -11582,13 +11798,13 @@ var init_wasi_runtime = __esm({
11582
11798
  },
11583
11799
  unlock: () => this.call({ op: "dl-unlock", owner: this.owner }),
11584
11800
  allocate: (floor) => {
11585
- const index2 = this.call({ op: "dl-closure", floor });
11801
+ const index3 = this.call({ op: "dl-closure", floor });
11586
11802
  this.catchUp();
11587
11803
  this.bump();
11588
- return index2;
11804
+ return index3;
11589
11805
  },
11590
- free: (index2) => {
11591
- this.call({ op: "dl-closure-free", index: index2 });
11806
+ free: (index3) => {
11807
+ this.call({ op: "dl-closure-free", index: index3 });
11592
11808
  this.bump();
11593
11809
  }
11594
11810
  };