@ai-ecoverse/slicc-kernel 1.41.0 → 1.41.3

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.
@@ -166,7 +166,7 @@ var init_pages = __esm({
166
166
  }
167
167
  const last = runs[runs.length - 1];
168
168
  if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
169
- return runs.map(([start, n]) => [start, Math.min(n, this.stored - start)]);
169
+ return runs.map(([start2, n]) => [start2, Math.min(n, this.stored - start2)]);
170
170
  }
171
171
  partial(at, length) {
172
172
  if (length === 0) return [];
@@ -174,16 +174,16 @@ var init_pages = __esm({
174
174
  const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length - 1) / PAGE)]);
175
175
  const runs = [];
176
176
  for (const page of edges) {
177
- const start = page * PAGE;
178
- const covered = at <= start && at + length >= start + PAGE;
179
- if (covered || this.pages.has(page) || start >= this.stored) continue;
180
- runs.push([start, Math.min(PAGE, this.stored - start)]);
177
+ const start2 = page * PAGE;
178
+ const covered = at <= start2 && at + length >= start2 + PAGE;
179
+ if (covered || this.pages.has(page) || start2 >= this.stored) continue;
180
+ runs.push([start2, Math.min(PAGE, this.stored - start2)]);
181
181
  }
182
182
  return runs;
183
183
  }
184
- fill(start, bytes) {
184
+ fill(start2, bytes) {
185
185
  for (let at = 0; at < bytes.length; at += PAGE) {
186
- const page = (start + at) / PAGE;
186
+ const page = (start2 + at) / PAGE;
187
187
  if (this.pages.has(page)) continue;
188
188
  const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
189
189
  const fresh = this.take(part.length);
@@ -249,14 +249,14 @@ var init_pages = __esm({
249
249
  while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
250
250
  j++;
251
251
  }
252
- const start = first * PAGE;
252
+ const start2 = first * PAGE;
253
253
  const end = Math.min(this.length, (first + (j - i)) * PAGE);
254
- const bytes = new Uint8Array(end - start);
254
+ const bytes = new Uint8Array(end - start2);
255
255
  for (let k = i; k < j; k++) {
256
256
  const page = this.pages.get(sorted[k]);
257
- bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start)), (k - i) * PAGE);
257
+ bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start2)), (k - i) * PAGE);
258
258
  }
259
- runs.push({ offset: start, bytes });
259
+ runs.push({ offset: start2, bytes });
260
260
  i = j;
261
261
  }
262
262
  const written2 = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
@@ -339,8 +339,8 @@ var init_ranged = __esm({
339
339
  return this.pages.length;
340
340
  }
341
341
  fetch(runs) {
342
- for (const [start, n] of runs) {
343
- this.pages.fill(start, this.io.pread(this.path, start, n, this.version));
342
+ for (const [start2, n] of runs) {
343
+ this.pages.fill(start2, this.io.pread(this.path, start2, n, this.version));
344
344
  }
345
345
  }
346
346
  read(at, length) {
@@ -594,7 +594,9 @@ function toMs(v) {
594
594
  }
595
595
  function createHelpers(Fs, ops) {
596
596
  const bridgeOf = (node) => node.mount.opts.bridge;
597
+ const fileIo = (node) => pinnedIo(bridgeOf(node), node.live.handle);
597
598
  const dirty = /* @__PURE__ */ new Set();
599
+ const handled = /* @__PURE__ */ new Set();
598
600
  function markDirty(node) {
599
601
  node.live.dirty = true;
600
602
  dirty.add(node);
@@ -657,7 +659,7 @@ function createHelpers(Fs, ops) {
657
659
  function ensureLoaded(node) {
658
660
  const s = node.live;
659
661
  if (s.loaded) return;
660
- const bytes = call2(() => bridgeOf(node).readFile(liveNodePath(node)));
662
+ const bytes = call2(() => fileIo(node).readFile(liveNodePath(node)));
661
663
  s.data = bytes;
662
664
  s.len = bytes.length;
663
665
  s.loaded = true;
@@ -667,10 +669,11 @@ function createHelpers(Fs, ops) {
667
669
  if (s.ranged || !s.ranges) return s.ranged;
668
670
  const bridge = bridgeOf(node);
669
671
  const path = () => liveNodePath(node);
672
+ const file = () => fileIo(node);
670
673
  const io = {
671
- pread: (_, at, n, version) => bridge.pread(path(), at, n, version),
672
- pwrite: (_, at, bytes, transfer) => bridge.pwrite(path(), at, bytes, transfer),
673
- truncate: (_, size) => bridge.truncate(path(), size)
674
+ pread: (_, at, n, version) => file().pread(path(), at, n, version),
675
+ pwrite: (_, at, bytes, transfer) => file().pwrite(path(), at, bytes, transfer),
676
+ truncate: (_, size) => file().truncate(path(), size)
674
677
  };
675
678
  const st = call2(() => bridge.stat(path()));
676
679
  s.ranged = new RangedFile(io, path(), st.size, st.version);
@@ -691,7 +694,7 @@ function createHelpers(Fs, ops) {
691
694
  if (s.ranged) call2(() => s.ranged?.flush());
692
695
  else {
693
696
  const bytes = s.data.slice(0, s.len);
694
- call2(() => bridgeOf(node).writeFile(liveNodePath(node), bytes, true));
697
+ call2(() => fileIo(node).writeFile(liveNodePath(node), bytes, true));
695
698
  }
696
699
  s.dirty = false;
697
700
  s.stat = void 0;
@@ -738,6 +741,7 @@ function createHelpers(Fs, ops) {
738
741
  return {
739
742
  Fs,
740
743
  bridgeOf,
744
+ handled,
741
745
  call: call2,
742
746
  metadataCall,
743
747
  makeNode,
@@ -924,8 +928,12 @@ function createStreamOps(h) {
924
928
  }
925
929
  };
926
930
  const opened = (node, on) => {
931
+ const s = node.live;
927
932
  try {
928
- bridgeOf(node).hold?.(liveNodePath(node), on, true);
933
+ const handle = bridgeOf(node).hold?.(liveNodePath(node), on, true, s.handle);
934
+ s.handle = on ? handle : void 0;
935
+ if (s.handle === void 0) h.handled.delete(node);
936
+ else h.handled.add(node);
929
937
  } catch {
930
938
  }
931
939
  };
@@ -1043,6 +1051,7 @@ function createLiveVfsPlugin(Fs) {
1043
1051
  mounts,
1044
1052
  node_ops: nodeOps,
1045
1053
  stream_ops: streamOps,
1054
+ handled: h.handled,
1046
1055
  flushDirty: () => h.flushDirty(),
1047
1056
  mount(mount) {
1048
1057
  const st = h.call(() => mount.opts.bridge.stat(mount.opts.root));
@@ -1065,7 +1074,7 @@ function flushLiveVfs(Fs, plugin) {
1065
1074
  if (s.ranged) s.ranged.flush();
1066
1075
  else {
1067
1076
  const bytes = s.data.slice(0, s.len);
1068
- node.mount.opts.bridge.writeFile(liveNodePath(node), bytes, true);
1077
+ pinnedIo(node.mount.opts.bridge, s.handle).writeFile(liveNodePath(node), bytes, true);
1069
1078
  }
1070
1079
  s.dirty = false;
1071
1080
  } catch (err) {
@@ -1143,6 +1152,7 @@ function liveRoot(Fs, bridge) {
1143
1152
  plugin.mounts.add(root.mount);
1144
1153
  invalidateLiveVfs(Fs, plugin);
1145
1154
  guardRenames(Fs);
1155
+ guardKeptNodes(Fs, plugin.handled);
1146
1156
  guardStatfs(Fs);
1147
1157
  const memfs = root.memfs ?? root.node_ops;
1148
1158
  const live = plugin.node_ops;
@@ -1199,6 +1209,28 @@ function guardStatfs(Fs) {
1199
1209
  };
1200
1210
  };
1201
1211
  }
1212
+ function pinnedIo(bridge, handle) {
1213
+ return handle !== void 0 && bridge.pinned ? bridge.pinned(handle) : bridge;
1214
+ }
1215
+ function guardKeptNodes(Fs, handled) {
1216
+ const open = Fs.open?.bind(Fs);
1217
+ const forget = Fs.hashRemoveNode?.bind(Fs);
1218
+ if (!open || !forget || !Fs.lookupPath || !handled || Fs.sliccKept) return;
1219
+ Fs.sliccKept = true;
1220
+ const lookupPath = Fs.lookupPath.bind(Fs);
1221
+ Fs.open = (path, flags, mode) => {
1222
+ const name = path.slice(path.lastIndexOf("/") + 1);
1223
+ if (![...handled].some((held) => held.name === name)) return open(path, flags, mode);
1224
+ let node;
1225
+ try {
1226
+ node = lookupPath(path, { follow: true }).node;
1227
+ } catch {
1228
+ }
1229
+ const handle = node?.live?.openCount ? node.live.handle : void 0;
1230
+ if (node && handle !== void 0 && node.mount.opts.bridge.kept?.(handle)) forget(node);
1231
+ return open(path, flags, mode);
1232
+ };
1233
+ }
1202
1234
  function guardRenames(Fs) {
1203
1235
  const rename = Fs.rename?.bind(Fs);
1204
1236
  if (!rename || Fs.sliccRename) return;
@@ -1330,6 +1362,7 @@ var init_sync_fs_wire = __esm({
1330
1362
  "statfs",
1331
1363
  "hold",
1332
1364
  "create",
1365
+ "kept",
1333
1366
  "pread",
1334
1367
  "pwrite",
1335
1368
  "truncate"
@@ -1501,11 +1534,26 @@ function createSyncFsSabBridge(transport, opts = {}) {
1501
1534
  if (r.ok && r.kind === "json") return r.json;
1502
1535
  throw errnoError("EIO", path);
1503
1536
  }
1504
- return {
1505
- readFile: (path) => bytes({ op: "read", path }, path),
1537
+ const io = (id) => ({
1538
+ readFile: (path) => bytes({ op: "read", path, ...id }, path),
1506
1539
  writeFile: (path, data, open) => {
1507
- run({ op: "write", path, body: data, ...open ? { mode: 1 } : {} }, path);
1540
+ run({ op: "write", path, body: data, ...open ? { mode: 1 } : {}, ...id }, path);
1541
+ },
1542
+ pread: (path, offset, length, version) => bytes({ op: "pread", path, offset, length, ...version ? { version } : {}, ...id }, path),
1543
+ pwrite: (path, offset, body, transfer) => {
1544
+ const whole = body.byteOffset === 0 && body.byteLength === body.buffer.byteLength;
1545
+ const owned = transfer && whole && body.buffer instanceof ArrayBuffer;
1546
+ const transferred = owned ? [body.buffer] : [];
1547
+ run({ op: "pwrite", path, offset, body, ...id }, path, transferred);
1508
1548
  },
1549
+ truncate: (path, length) => {
1550
+ run({ op: "truncate", path, length, ...id }, path);
1551
+ }
1552
+ });
1553
+ return {
1554
+ ...io({}),
1555
+ pinned: (handle) => io({ handle }),
1556
+ kept: (handle) => json({ op: "kept", path: "/", handle }, "/") === true,
1509
1557
  stat: (path) => {
1510
1558
  const s = parseSyncFsStat(json({ op: "stat", path }, path));
1511
1559
  if (!s) throw errnoError("EIO", path);
@@ -1574,19 +1622,13 @@ function createSyncFsSabBridge(transport, opts = {}) {
1574
1622
  create: (path) => {
1575
1623
  run({ op: "create", path }, path);
1576
1624
  },
1577
- hold: (path, held, open) => {
1578
- run({ op: "hold", path, mode: (held ? 1 : 0) | (open ? 2 : 0) }, path);
1579
- },
1580
- statfs: (path = "/") => parseSyncFsUsage(json({ op: "statfs", path }, path)),
1581
- pread: (path, offset, length, version) => bytes({ op: "pread", path, offset, length, ...version ? { version } : {} }, path),
1582
- pwrite: (path, offset, body, transfer) => {
1583
- const whole = body.byteOffset === 0 && body.byteLength === body.buffer.byteLength;
1584
- const owned = transfer && whole && body.buffer instanceof ArrayBuffer;
1585
- run({ op: "pwrite", path, offset, body }, path, owned ? [body.buffer] : []);
1625
+ hold: (path, held, open, handle) => {
1626
+ const mode = (held ? 1 : 0) | (open ? 2 : 0);
1627
+ if (mode === 3) return json({ op: "hold", path, mode }, path);
1628
+ run({ op: "hold", path, mode, ...handle !== void 0 ? { handle } : {} }, path);
1629
+ return void 0;
1586
1630
  },
1587
- truncate: (path, length) => {
1588
- run({ op: "truncate", path, length }, path);
1589
- }
1631
+ statfs: (path = "/") => parseSyncFsUsage(json({ op: "statfs", path }, path))
1590
1632
  };
1591
1633
  }
1592
1634
  var init_sync_sab_bridge = __esm({
@@ -1794,9 +1836,9 @@ var init_kernel_streams = __esm({
1794
1836
  for (const name of ["stat", "lstat"]) {
1795
1837
  const original = fs[name];
1796
1838
  if (typeof original !== "function") continue;
1797
- fs[name] = (path, ...rest) => {
1839
+ fs[name] = (path, ...rest2) => {
1798
1840
  const getattr = this.descriptions.get(path);
1799
- return getattr ? getattr() : original.call(fs, path, ...rest);
1841
+ return getattr ? getattr() : original.call(fs, path, ...rest2);
1800
1842
  };
1801
1843
  }
1802
1844
  }
@@ -1884,13 +1926,13 @@ var init_kernel_streams = __esm({
1884
1926
  for (const name of ["chown", "lchown", "chmod", "lchmod"]) {
1885
1927
  const original = fs[name];
1886
1928
  if (typeof original !== "function") continue;
1887
- fs[name] = (path, ...rest) => isPtyPath(path) ? this.existingPty(path) : original.call(fs, path, ...rest);
1929
+ fs[name] = (path, ...rest2) => isPtyPath(path) ? this.existingPty(path) : original.call(fs, path, ...rest2);
1888
1930
  }
1889
1931
  for (const name of ["stat", "lstat"]) {
1890
1932
  const original = fs[name];
1891
1933
  if (typeof original !== "function") continue;
1892
- fs[name] = (path, ...rest) => {
1893
- if (!isPtyPath(path)) return original.call(fs, path, ...rest);
1934
+ fs[name] = (path, ...rest2) => {
1935
+ if (!isPtyPath(path)) return original.call(fs, path, ...rest2);
1894
1936
  this.existingPty(path);
1895
1937
  return ptyStat(path);
1896
1938
  };
@@ -2165,7 +2207,7 @@ function cloexecPipe2(pipe2, deps) {
2165
2207
  };
2166
2208
  }
2167
2209
  function quietTarget(dup3, deps) {
2168
- return (fd = -1, newfd = -1, flags = 0, ...rest) => {
2210
+ return (fd = -1, newfd = -1, flags = 0, ...rest2) => {
2169
2211
  const Fs = deps.fs();
2170
2212
  const target = fd === newfd || flags !== 0 ? void 0 : Fs?.getStream(newfd);
2171
2213
  if (target && Fs?.getStream(fd)) {
@@ -2174,7 +2216,7 @@ function quietTarget(dup3, deps) {
2174
2216
  } catch {
2175
2217
  }
2176
2218
  }
2177
- return dup3(fd, newfd, flags, ...rest);
2219
+ return dup3(fd, newfd, flags, ...rest2);
2178
2220
  };
2179
2221
  }
2180
2222
  function cloexecByFlag(syscall, flagArg, deps) {
@@ -2329,8 +2371,8 @@ function syncFsync(imports, fs, glue) {
2329
2371
  };
2330
2372
  }
2331
2373
  }
2332
- function offsetOf(rest) {
2333
- const at = rest.length >= 3 ? Number(rest[1]) * 2 ** 32 + (Number(rest[0]) >>> 0) : Number(BigInt.asIntN(64, BigInt(rest[0])));
2374
+ function offsetOf(rest2) {
2375
+ const at = rest2.length >= 3 ? Number(rest2[1]) * 2 ** 32 + (Number(rest2[0]) >>> 0) : Number(BigInt.asIntN(64, BigInt(rest2[0])));
2334
2376
  return at >= 0 && Number.isSafeInteger(at) ? at : void 0;
2335
2377
  }
2336
2378
  function readInto(sys, kfd, target, at) {
@@ -2344,14 +2386,14 @@ function readInto(sys, kfd, target, at) {
2344
2386
  return done;
2345
2387
  }
2346
2388
  function positioned(original, write, deps) {
2347
- return (fd, iov, iovcnt, ...rest) => {
2389
+ return (fd, iov, iovcnt, ...rest2) => {
2348
2390
  const stream = deps.fs()?.getStream(Number(fd));
2349
2391
  const memory = deps.memory();
2350
2392
  const kfd = stream?.sliccKernelFile ? stream.sliccKernelFd : void 0;
2351
2393
  if (kfd === void 0 || !memory || !(write ? deps.sys.pwrite : deps.sys.pread)) {
2352
- return original(fd, iov, iovcnt, ...rest);
2394
+ return original(fd, iov, iovcnt, ...rest2);
2353
2395
  }
2354
- const at = offsetOf(rest);
2396
+ const at = offsetOf(rest2);
2355
2397
  if (at === void 0) return wasiErrno("EINVAL");
2356
2398
  const view = new DataView(memory.buffer);
2357
2399
  let done = 0;
@@ -2367,7 +2409,7 @@ function positioned(original, write, deps) {
2367
2409
  } catch (err) {
2368
2410
  return errnoOf(err);
2369
2411
  }
2370
- view.setUint32(Number(rest[rest.length - 1]), done, true);
2412
+ view.setUint32(Number(rest2[rest2.length - 1]), done, true);
2371
2413
  return 0;
2372
2414
  };
2373
2415
  }
@@ -4357,6 +4399,8 @@ function cachingBridge(bridge, now = () => performance.now()) {
4357
4399
  utimes: (p, a, m) => mutating(() => bridge.utimes(p, a, m)),
4358
4400
  lutimes: (p, a, m) => mutating(() => bridge.lutimes(p, a, m)),
4359
4401
  ...bridge.hold ? { hold: bridge.hold.bind(bridge) } : {},
4402
+ ...bridge.kept ? { kept: bridge.kept.bind(bridge) } : {},
4403
+ ...bridge.pinned ? { pinned: (handle) => pinnedIo2(bridge, handle, mutating) } : {},
4360
4404
  ...bridge.create ? { create: (p) => mutating(() => bridge.create?.(p)) } : {},
4361
4405
  ...bridge.pread && bridge.pwrite && bridge.truncate ? {
4362
4406
  pread: bridge.pread.bind(bridge),
@@ -4366,6 +4410,17 @@ function cachingBridge(bridge, now = () => performance.now()) {
4366
4410
  invalidate
4367
4411
  };
4368
4412
  }
4413
+ function pinnedIo2(bridge, handle, mutating) {
4414
+ const io = bridge.pinned(handle);
4415
+ const { pread, pwrite, truncate } = io;
4416
+ return {
4417
+ readFile: (p) => io.readFile(p),
4418
+ writeFile: (p, bytes, open) => mutating(() => io.writeFile(p, bytes, open)),
4419
+ pread: (p, at, n, version) => pread(p, at, n, version),
4420
+ pwrite: (p, at, bytes, transfer) => mutating(() => pwrite(p, at, bytes, transfer)),
4421
+ truncate: (p, size) => mutating(() => truncate(p, size))
4422
+ };
4423
+ }
4369
4424
  var WasiError, FileBuffer, LocalFile, MISS_TTL_MS;
4370
4425
  var init_wasi_files = __esm({
4371
4426
  "src/process/wasi/wasi-files.ts"() {
@@ -4391,6 +4446,7 @@ var init_wasi_files = __esm({
4391
4446
  where;
4392
4447
  ranged;
4393
4448
  maxFile;
4449
+ handle;
4394
4450
  constructor(fs, path, empty, maxFile, size, version) {
4395
4451
  this.fs = fs;
4396
4452
  this.where = path;
@@ -4960,7 +5016,7 @@ var init_wasi_fds = __esm({
4960
5016
  this.letGo(buffer);
4961
5017
  }
4962
5018
  letGo(buffer) {
4963
- this.fs.hold?.(buffer.path, false, true);
5019
+ this.fs.hold?.(buffer.path, false, true, buffer.handle);
4964
5020
  }
4965
5021
  kind(fd, e) {
4966
5022
  if (!e.kind) {
@@ -5047,6 +5103,10 @@ var init_wasi_fds = __esm({
5047
5103
  file(path, existing, oflags, rights, fdflags) {
5048
5104
  const { readable: readable2, writable: writable2 } = openAccess(existing, oflags, rights);
5049
5105
  let buffer = this.buffers.get(path);
5106
+ if (buffer?.handle !== void 0 && this.fs.kept?.(buffer.handle)) {
5107
+ this.buffers.delete(path);
5108
+ buffer = void 0;
5109
+ }
5050
5110
  if (buffer) {
5051
5111
  if (oflags & OFLAGS.TRUNC) buffer.truncate(0);
5052
5112
  } else {
@@ -5055,9 +5115,11 @@ var init_wasi_fds = __esm({
5055
5115
  if (existing?.ranged) this.fs.invalidate?.();
5056
5116
  const st = existing?.ranged || !existing ? this.fs.stat(path) : existing;
5057
5117
  const ranged = st.ranged ? st.size : void 0;
5058
- buffer = new FileBuffer(this.fs, path, empty, st.maxFile, ranged, st.version);
5118
+ const handle = this.fs.hold?.(path, true, true);
5119
+ const io = handle !== void 0 && this.fs.pinned ? this.fs.pinned(handle) : this.fs;
5120
+ buffer = new FileBuffer(io, path, empty, st.maxFile, ranged, st.version);
5121
+ buffer.handle = handle;
5059
5122
  this.buffers.set(path, buffer);
5060
- this.fs.hold?.(path, true, true);
5061
5123
  }
5062
5124
  buffer.opens++;
5063
5125
  return new LocalFile(buffer, readable2, writable2, (fdflags & FDFLAGS.APPEND) !== 0);
@@ -5343,11 +5405,11 @@ function kernelFiletype(kind) {
5343
5405
  return { filetype: FILETYPE.UNKNOWN, seeks: false };
5344
5406
  }
5345
5407
  }
5346
- function direntRecord(next, ino, name, filetype) {
5408
+ function direntRecord(next2, ino, name, filetype) {
5347
5409
  const nameBytes = new TextEncoder().encode(name);
5348
5410
  const record = new Uint8Array(SIZE.DIRENT + nameBytes.length);
5349
5411
  const v = new DataView(record.buffer);
5350
- v.setBigUint64(0, BigInt(next), true);
5412
+ v.setBigUint64(0, BigInt(next2), true);
5351
5413
  v.setBigUint64(8, ino, true);
5352
5414
  v.setUint32(16, nameBytes.length, true);
5353
5415
  v.setUint8(20, filetype);
@@ -5861,19 +5923,19 @@ var init_wasi_host = __esm({
5861
5923
  return { filetype: FILETYPE.CHARACTER_DEVICE, size: 0n, ino: pathInode(path), mtimeNs: 0n };
5862
5924
  }
5863
5925
  this.fds.flushPath(path);
5864
- const follow = (lookup & LOOKUP_SYMLINK_FOLLOW) !== 0;
5865
- return filestatOf(path, follow ? this.o.fs.stat(path) : this.o.fs.lstat(path));
5926
+ const follow2 = (lookup & LOOKUP_SYMLINK_FOLLOW) !== 0;
5927
+ return filestatOf(path, follow2 ? this.o.fs.stat(path) : this.o.fs.lstat(path));
5866
5928
  }
5867
- setTimes(path, atim, mtim, flags, follow = true) {
5929
+ setTimes(path, atim, mtim, flags, follow2 = true) {
5868
5930
  const now = Date.now();
5869
5931
  let current;
5870
- const statOf = follow ? this.o.fs.stat.bind(this.o.fs) : this.o.fs.lstat.bind(this.o.fs);
5932
+ const statOf = follow2 ? this.o.fs.stat.bind(this.o.fs) : this.o.fs.lstat.bind(this.o.fs);
5871
5933
  const kept = (field2) => {
5872
5934
  current ??= statOf(path);
5873
5935
  return current[field2] ?? current.mtimeMs ?? now;
5874
5936
  };
5875
5937
  const pick = (set, setNow, value2, field2) => flags & setNow ? now : flags & set ? Number(value2 / NS_PER_MS) : kept(field2);
5876
- (follow ? this.o.fs.utimes : this.o.fs.lutimes).call(
5938
+ (follow2 ? this.o.fs.utimes : this.o.fs.lutimes).call(
5877
5939
  this.o.fs,
5878
5940
  path,
5879
5941
  pick(FSTFLAGS.ATIM, FSTFLAGS.ATIM_NOW, atim, "atimeMs"),
@@ -6779,6 +6841,61 @@ var init_wasix_linker = __esm({
6779
6841
  }
6780
6842
  });
6781
6843
 
6844
+ // src/fs/physical-path.ts
6845
+ function start(path) {
6846
+ return { done: [], todo: path.split("/").filter((p) => p !== ""), hops: 0 };
6847
+ }
6848
+ function next(walk) {
6849
+ while (walk.todo.length > 0) {
6850
+ const part = walk.todo.shift();
6851
+ if (part === ".") continue;
6852
+ if (part === "..") {
6853
+ walk.done.pop();
6854
+ continue;
6855
+ }
6856
+ return part;
6857
+ }
6858
+ return void 0;
6859
+ }
6860
+ function lexical(path) {
6861
+ const walk = start(path);
6862
+ for (let part = next(walk); part !== void 0; part = next(walk)) walk.done.push(part);
6863
+ return `/${walk.done.join("/")}`;
6864
+ }
6865
+ function rest(walk, part) {
6866
+ return lexical(`/${[...walk.done, part, ...walk.todo].join("/")}`);
6867
+ }
6868
+ function follow(walk, target) {
6869
+ if (++walk.hops > MAX_HOPS) return false;
6870
+ if (target.startsWith("/")) walk.done.length = 0;
6871
+ walk.todo = [...target.split("/").filter((p) => p !== ""), ...walk.todo];
6872
+ return true;
6873
+ }
6874
+ function physicalPath(fs, path) {
6875
+ const walk = start(path);
6876
+ for (let part = next(walk); part !== void 0; part = next(walk)) {
6877
+ const at = `/${[...walk.done, part].join("/")}`;
6878
+ let target;
6879
+ try {
6880
+ target = fs.lstat(at).isSymbolicLink ? fs.readlink(at) : void 0;
6881
+ } catch {
6882
+ return rest(walk, part);
6883
+ }
6884
+ if (target === void 0) walk.done.push(part);
6885
+ else if (!follow(walk, target)) {
6886
+ throw Object.assign(new Error("too many levels of symbolic links"), { code: "ELOOP" });
6887
+ }
6888
+ }
6889
+ return `/${walk.done.join("/")}`;
6890
+ }
6891
+ var MAX_HOPS;
6892
+ var init_physical_path = __esm({
6893
+ "src/fs/physical-path.ts"() {
6894
+ "use strict";
6895
+ MAX_HOPS = 40;
6896
+ }
6897
+ });
6898
+
6782
6899
  // src/process/wasi/wasix-process.ts
6783
6900
  function resolveFrom(cwd, path) {
6784
6901
  return normalize(path.startsWith("/") ? path : `${cwd}/${path}`);
@@ -6787,6 +6904,7 @@ var POSIX_TO_WASI_SIGNAL, O_WRONLY5, O_RDWR5, O_APPEND2, OFLAG_CREAT, OFLAG_TRUN
6787
6904
  var init_wasix_process = __esm({
6788
6905
  "src/process/wasi/wasix-process.ts"() {
6789
6906
  "use strict";
6907
+ init_physical_path();
6790
6908
  init_wasi_abi();
6791
6909
  init_wasi_files();
6792
6910
  init_wasi_host();
@@ -6873,10 +6991,13 @@ var init_wasix_process = __esm({
6873
6991
  const kfd = this.openFor(op, cwd);
6874
6992
  opened.push(kfd);
6875
6993
  point(op.fd, kfd);
6876
- } else if (op.cmd === "chdir") return resolveFrom(cwd, op.path);
6877
- else return fds.dir(op.fd).path;
6994
+ } else if (op.cmd === "chdir") return this.physicalFrom(cwd, op.path);
6995
+ else return physicalPath(this.host.o.fs, fds.dir(op.fd).path);
6878
6996
  return cwd;
6879
6997
  }
6998
+ physicalFrom(cwd, path) {
6999
+ return physicalPath(this.host.o.fs, path.startsWith("/") ? path : `${cwd}/${path}`);
7000
+ }
6880
7001
  openFor(op, cwd) {
6881
7002
  const path = resolveFrom(cwd, op.path);
6882
7003
  const create = (op.oflags & OFLAG_CREAT) !== 0;
@@ -7910,8 +8031,8 @@ function linkerHost(host, imports) {
7910
8031
  }
7911
8032
  },
7912
8033
  hostImports: () => {
7913
- const { env: _env, "GOT.mem": _mem, "GOT.func": _func, ...rest } = imports();
7914
- return rest;
8034
+ const { env: _env, "GOT.mem": _mem, "GOT.func": _func, ...rest2 } = imports();
8035
+ return rest2;
7915
8036
  }
7916
8037
  };
7917
8038
  }
@@ -8053,9 +8174,9 @@ async function runWasiThread(init, port) {
8053
8174
  foreign: init.program.foreign,
8054
8175
  ...program ? { program } : {}
8055
8176
  });
8056
- const start = instance.exports.wasi_thread_start;
8177
+ const start2 = instance.exports.wasi_thread_start;
8057
8178
  try {
8058
- start(thread.tid, thread.arg);
8179
+ start2(thread.tid, thread.arg);
8059
8180
  threads.exited();
8060
8181
  } catch (e) {
8061
8182
  if (e instanceof ThreadExit) {