@ai-ecoverse/slicc-kernel 1.94.3 → 1.95.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.
package/dist/node.js CHANGED
@@ -567,8 +567,8 @@ async function inParent(g, op, path, old = false) {
567
567
  const dir = await access(g.fs, g.cred, parentOf(path), W_OK | X_OK, linked(g, DIR));
568
568
  if (dir.denied === void 0) return stickyEntry(g, op, path, dir);
569
569
  if (dir.link) return deny(g, op, dir.denied);
570
- const lookup3 = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
571
- if (!lookup3 || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
570
+ const lookup4 = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
571
+ if (!lookup4 || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
572
572
  }
573
573
  async function makeIn(g, op, path) {
574
574
  for (let dir = parentOf(path); ; dir = parentOf(dir)) {
@@ -888,6 +888,7 @@ function pollFile(file) {
888
888
  }
889
889
  var OpenFile = class {
890
890
  refs = 1;
891
+ closers;
891
892
  file;
892
893
  constructor(file) {
893
894
  this.file = file;
@@ -896,9 +897,18 @@ var OpenFile = class {
896
897
  this.refs += 1;
897
898
  return this;
898
899
  }
900
+ onClose(fn) {
901
+ this.closers ??= /* @__PURE__ */ new Set();
902
+ this.closers.add(fn);
903
+ return () => void this.closers?.delete(fn);
904
+ }
899
905
  release() {
900
906
  this.refs -= 1;
901
- if (this.refs === 0) return this.file.close();
907
+ if (this.refs !== 0) return;
908
+ const closers = this.closers;
909
+ this.closers = void 0;
910
+ for (const fn of closers ?? []) fn();
911
+ return this.file.close();
902
912
  }
903
913
  };
904
914
  function openPipe(capacity) {
@@ -1446,7 +1456,8 @@ var KernelSocket = class _KernelSocket {
1446
1456
  return {
1447
1457
  readable: this.readShut || rx.readReady,
1448
1458
  writable: this.writeShut || tx.writeReady,
1449
- hangup: rx.writersGone && tx.readersGone
1459
+ hangup: rx.writersGone && tx.readersGone,
1460
+ rdhup: rx.writersGone
1450
1461
  };
1451
1462
  }
1452
1463
  changed(signal) {
@@ -2427,6 +2438,8 @@ var UserDb = class {
2427
2438
  owners;
2428
2439
  umasks = /* @__PURE__ */ new Map();
2429
2440
  writing = Promise.resolve();
2441
+ stable;
2442
+ commits = 0;
2430
2443
  constructor(fs, owners = false) {
2431
2444
  this.fs = fs;
2432
2445
  this.owners = owners;
@@ -2462,11 +2475,18 @@ var UserDb = class {
2462
2475
  throw err;
2463
2476
  }
2464
2477
  }
2478
+ async texts() {
2479
+ for (; ; ) {
2480
+ if (this.stable) return this.stable;
2481
+ const commits = this.commits;
2482
+ const passwd = await this.read(PASSWD, DEFAULT_PASSWD);
2483
+ const group = await this.read(GROUP, DEFAULT_GROUP);
2484
+ if (this.commits === commits && !this.stable) return [passwd, group];
2485
+ }
2486
+ }
2465
2487
  async tables() {
2466
- return {
2467
- passwd: parsePasswd(await this.read(PASSWD, DEFAULT_PASSWD)),
2468
- group: parseGroup(await this.read(GROUP, DEFAULT_GROUP))
2469
- };
2488
+ const [passwd, group] = await this.texts();
2489
+ return { passwd: parsePasswd(passwd), group: parseGroup(group) };
2470
2490
  }
2471
2491
  async provision(row, umask, shared) {
2472
2492
  const dirs = 511 & ~umask | (shared ? 1024 : 0);
@@ -2485,15 +2505,25 @@ var UserDb = class {
2485
2505
  }
2486
2506
  }
2487
2507
  async commit(passwd, group, then) {
2488
- const before = [await this.read(PASSWD, DEFAULT_PASSWD), await this.read(GROUP, DEFAULT_GROUP)];
2508
+ const before = [
2509
+ await this.read(PASSWD, DEFAULT_PASSWD),
2510
+ await this.read(GROUP, DEFAULT_GROUP)
2511
+ ];
2512
+ this.stable = before;
2513
+ this.commits++;
2489
2514
  try {
2490
2515
  await this.fs.writeFile(GROUP, group);
2491
2516
  await this.fs.writeFile(PASSWD, passwd);
2517
+ this.stable = void 0;
2492
2518
  await then?.();
2493
2519
  } catch (err) {
2520
+ this.stable = before;
2494
2521
  await this.fs.writeFile(GROUP, before[1]);
2495
2522
  await this.fs.writeFile(PASSWD, before[0]);
2496
2523
  throw err;
2524
+ } finally {
2525
+ this.stable = void 0;
2526
+ this.commits++;
2497
2527
  }
2498
2528
  }
2499
2529
  serial(work) {
@@ -10624,7 +10654,7 @@ function mountTable(mounts = []) {
10624
10654
  }
10625
10655
 
10626
10656
  // src/version.ts
10627
- var KERNEL_VERSION = true ? "1.94.3" : "0.0.0-dev";
10657
+ var KERNEL_VERSION = true ? "1.95.0" : "0.0.0-dev";
10628
10658
  var OS_RELEASE = "6.0.0-slicc";
10629
10659
  function procVersion() {
10630
10660
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -11011,6 +11041,295 @@ async function procAlias(fs, path, follow2) {
11011
11041
  return step.value;
11012
11042
  }
11013
11043
 
11044
+ // src/kernel/eventfd.ts
11045
+ var EVENTFD_MAX = 0xfffffffffffffffen;
11046
+ var Notifier = class {
11047
+ waiters = [];
11048
+ wait(signal) {
11049
+ if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
11050
+ return new Promise((resolve2, reject) => {
11051
+ const onAbort = () => {
11052
+ this.waiters = this.waiters.filter((w) => w !== waiter);
11053
+ reject(new KernelError("EINTR"));
11054
+ };
11055
+ const waiter = () => {
11056
+ signal?.removeEventListener("abort", onAbort);
11057
+ resolve2();
11058
+ };
11059
+ this.waiters.push(waiter);
11060
+ signal?.addEventListener("abort", onAbort, { once: true });
11061
+ });
11062
+ }
11063
+ wake() {
11064
+ const waiters = this.waiters;
11065
+ this.waiters = [];
11066
+ for (const resolve2 of waiters) resolve2();
11067
+ }
11068
+ };
11069
+ function blocked(signal) {
11070
+ if (signal?.aborted) throw new KernelError("EAGAIN");
11071
+ }
11072
+ var EventFd = class _EventFd {
11073
+ static SIZE = 8;
11074
+ count;
11075
+ semaphore;
11076
+ changes = new Notifier();
11077
+ constructor(initial, semaphore) {
11078
+ this.count = initial;
11079
+ this.semaphore = semaphore;
11080
+ }
11081
+ async read(max, signal) {
11082
+ if (max < _EventFd.SIZE) throw new KernelError("EINVAL");
11083
+ while (this.count === 0n) {
11084
+ blocked(signal);
11085
+ await this.changes.wait(signal);
11086
+ }
11087
+ const value = this.semaphore ? 1n : this.count;
11088
+ this.count -= value;
11089
+ this.changes.wake();
11090
+ const out = new Uint8Array(_EventFd.SIZE);
11091
+ new DataView(out.buffer).setBigUint64(0, value, true);
11092
+ return out;
11093
+ }
11094
+ async write(bytes, signal) {
11095
+ if (bytes.length < _EventFd.SIZE) throw new KernelError("EINVAL");
11096
+ const value = new DataView(bytes.buffer, bytes.byteOffset, _EventFd.SIZE).getBigUint64(0, true);
11097
+ if (value > EVENTFD_MAX) throw new KernelError("EINVAL");
11098
+ while (EVENTFD_MAX - this.count < value) {
11099
+ blocked(signal);
11100
+ await this.changes.wait(signal);
11101
+ }
11102
+ this.count += value;
11103
+ if (value > 0n) this.changes.wake();
11104
+ return _EventFd.SIZE;
11105
+ }
11106
+ poll() {
11107
+ return { readable: this.count > 0n, writable: this.count < EVENTFD_MAX, hangup: false };
11108
+ }
11109
+ changed(signal) {
11110
+ return this.changes.wait(signal);
11111
+ }
11112
+ close() {
11113
+ this.changes.wake();
11114
+ }
11115
+ };
11116
+
11117
+ // src/kernel/epoll.ts
11118
+ var EPOLL = {
11119
+ IN: 1,
11120
+ OUT: 2,
11121
+ RDHUP: 4,
11122
+ PRI: 8,
11123
+ ERR: 16,
11124
+ HUP: 32,
11125
+ ET: 64,
11126
+ ONESHOT: 128
11127
+ };
11128
+ var ALWAYS = EPOLL.ERR | EPOLL.HUP;
11129
+ var ASKED = EPOLL.IN | EPOLL.OUT | EPOLL.RDHUP;
11130
+ var RETRY_MS = 50;
11131
+ var UNPOLLABLE = /* @__PURE__ */ new Set(["file", "held", "device"]);
11132
+ var EPOLL_OPS = [
11133
+ "epoll-create",
11134
+ "epoll-ctl",
11135
+ "epoll-wait",
11136
+ "eventfd-open"
11137
+ ];
11138
+ function levelOf(file) {
11139
+ const state = pollFile(file);
11140
+ let bits2 = 0;
11141
+ if (state.readable) bits2 |= EPOLL.IN;
11142
+ if (state.writable) bits2 |= EPOLL.OUT;
11143
+ if (state.rdhup) bits2 |= EPOLL.RDHUP;
11144
+ if (state.hangup) bits2 |= file.read ? EPOLL.HUP : EPOLL.ERR;
11145
+ return bits2;
11146
+ }
11147
+ function due(interest, level) {
11148
+ if (!interest.armed) return 0;
11149
+ const ready2 = level & (interest.events & ASKED | ALWAYS);
11150
+ if (ready2 === 0) return 0;
11151
+ if ((interest.events & EPOLL.ET) !== 0 && interest.reported === interest.gen) return 0;
11152
+ return ready2;
11153
+ }
11154
+ function consume(interest) {
11155
+ interest.reported = interest.gen;
11156
+ if ((interest.events & EPOLL.ONESHOT) !== 0) interest.armed = false;
11157
+ }
11158
+ function lookup(fds, fd) {
11159
+ try {
11160
+ return fds.get(fd);
11161
+ } catch {
11162
+ return void 0;
11163
+ }
11164
+ }
11165
+ function pause(ms, signal) {
11166
+ return new Promise((resolve2) => {
11167
+ const timer = setTimeout(resolve2, ms);
11168
+ signal.addEventListener(
11169
+ "abort",
11170
+ () => {
11171
+ clearTimeout(timer);
11172
+ resolve2();
11173
+ },
11174
+ { once: true }
11175
+ );
11176
+ });
11177
+ }
11178
+ var EpollFile = class _EpollFile {
11179
+ regs = /* @__PURE__ */ new Map();
11180
+ changes = new Notifier();
11181
+ cursor = 0;
11182
+ ctl(fds, how, fd, events, data) {
11183
+ const open = fds.get(fd);
11184
+ if (open.file === this) throw new KernelError("EINVAL");
11185
+ const known = this.regs.get(fd);
11186
+ const current = known?.open === open ? known : void 0;
11187
+ if (how === "add" || how === "set" && !current) {
11188
+ if (current) throw new KernelError("EEXIST");
11189
+ if (UNPOLLABLE.has(kernelFdKind(open.file))) throw new KernelError("EPERM");
11190
+ if (open.file instanceof _EpollFile && open.file.reaches(this)) throw new KernelError("ELOOP");
11191
+ if (known) this.drop(known);
11192
+ this.register(fd, open, events, data);
11193
+ } else if (!current) {
11194
+ throw new KernelError("ENOENT");
11195
+ } else if (how === "del") {
11196
+ this.drop(current);
11197
+ } else {
11198
+ current.events = events;
11199
+ current.data = data;
11200
+ current.armed = true;
11201
+ current.gen++;
11202
+ }
11203
+ this.changes.wake();
11204
+ }
11205
+ reaches(target) {
11206
+ if (this === target) return true;
11207
+ return [...this.regs.values()].some(
11208
+ (reg) => reg.open.file instanceof _EpollFile && reg.open.file.reaches(target)
11209
+ );
11210
+ }
11211
+ collect(fds, max) {
11212
+ const list = [...this.regs.values()];
11213
+ const out = [];
11214
+ const start2 = list.length === 0 ? 0 : this.cursor % list.length;
11215
+ for (let i = 0; i < list.length && out.length < max; i++) {
11216
+ const reg = list[(start2 + i) % list.length];
11217
+ if (lookup(fds, reg.fd) !== reg.open) {
11218
+ this.drop(reg);
11219
+ continue;
11220
+ }
11221
+ const bits2 = due(reg, levelOf(reg.open.file));
11222
+ if (bits2 === 0) continue;
11223
+ consume(reg);
11224
+ out.push({ events: bits2, data: reg.data });
11225
+ this.cursor = start2 + i + 1;
11226
+ }
11227
+ return out;
11228
+ }
11229
+ async wait(fds, max, timeoutMs, signal) {
11230
+ const deadline = timeoutMs < 0 ? Number.POSITIVE_INFINITY : Date.now() + timeoutMs;
11231
+ for (; ; ) {
11232
+ const events = this.collect(fds, max);
11233
+ if (events.length > 0 || Date.now() >= deadline) return events;
11234
+ const round = new AbortController();
11235
+ const woken = this.changes.wait(AbortSignal.any([round.signal, signal]));
11236
+ woken.catch(() => void 0);
11237
+ let timer;
11238
+ const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve2) => timer = setTimeout(resolve2, deadline - Date.now()))];
11239
+ try {
11240
+ await Promise.race([woken, ...expiry]);
11241
+ } finally {
11242
+ round.abort();
11243
+ clearTimeout(timer);
11244
+ }
11245
+ }
11246
+ }
11247
+ poll() {
11248
+ const ready2 = [...this.regs.values()].some((reg) => due(reg, levelOf(reg.open.file)) !== 0);
11249
+ return { readable: ready2, writable: false, hangup: false };
11250
+ }
11251
+ changed(signal) {
11252
+ return this.changes.wait(signal);
11253
+ }
11254
+ close() {
11255
+ for (const reg of [...this.regs.values()]) this.drop(reg);
11256
+ this.changes.wake();
11257
+ }
11258
+ register(fd, open, events, data) {
11259
+ const stop = new AbortController();
11260
+ const unhook = open.onClose(() => {
11261
+ this.drop(reg);
11262
+ this.changes.wake();
11263
+ });
11264
+ const reg = {
11265
+ fd,
11266
+ open,
11267
+ events,
11268
+ data,
11269
+ gen: 1,
11270
+ reported: 0,
11271
+ armed: true,
11272
+ stop,
11273
+ unhook
11274
+ };
11275
+ this.regs.set(fd, reg);
11276
+ void this.watch(reg);
11277
+ }
11278
+ drop(reg) {
11279
+ reg.stop.abort();
11280
+ reg.unhook();
11281
+ if (this.regs.get(reg.fd) === reg) this.regs.delete(reg.fd);
11282
+ }
11283
+ async watch(reg) {
11284
+ const file = reg.open.file;
11285
+ const { signal } = reg.stop;
11286
+ while (file.changed && !signal.aborted) {
11287
+ const round = new AbortController();
11288
+ const stop = () => round.abort();
11289
+ signal.addEventListener("abort", stop, { once: true });
11290
+ let failed3 = false;
11291
+ try {
11292
+ await file.changed(round.signal);
11293
+ } catch {
11294
+ failed3 = true;
11295
+ } finally {
11296
+ round.abort();
11297
+ signal.removeEventListener("abort", stop);
11298
+ }
11299
+ if (signal.aborted) return;
11300
+ reg.gen++;
11301
+ this.changes.wake();
11302
+ if (failed3) await pause(RETRY_MS, signal);
11303
+ }
11304
+ }
11305
+ };
11306
+ function epollAt(fds, fd) {
11307
+ const file = fds.get(fd).file;
11308
+ if (!(file instanceof EpollFile)) throw new KernelError("EINVAL");
11309
+ return file;
11310
+ }
11311
+ var ok = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
11312
+ async function epollSyscall(req, proc) {
11313
+ const { fds } = proc;
11314
+ switch (req.op) {
11315
+ case "epoll-create":
11316
+ return ok(fds.install(new OpenFile(new EpollFile()), 3));
11317
+ case "eventfd-open":
11318
+ return ok(fds.install(new OpenFile(new EventFd(BigInt(req.initial), req.semaphore)), 3));
11319
+ case "epoll-ctl":
11320
+ epollAt(fds, req.epfd).ctl(fds, req.how, req.fd, req.events, req.data);
11321
+ return ok();
11322
+ case "epoll-wait": {
11323
+ const ep = epollAt(fds, req.epfd);
11324
+ if (req.timeoutMs === 0 || proc.owed()) {
11325
+ const now = ep.collect(fds, req.max);
11326
+ if (req.timeoutMs === 0 || now.length > 0) return ok(now);
11327
+ }
11328
+ return ok(await ep.wait(fds, req.max, req.timeoutMs, proc.blocking()));
11329
+ }
11330
+ }
11331
+ }
11332
+
11014
11333
  // src/kernel/host-ops.ts
11015
11334
  var HOST_OPS = [
11016
11335
  "lock",
@@ -12665,14 +12984,14 @@ var Resolver = class {
12665
12984
  if (hit && hit.expires > this.now()) return hit.addresses;
12666
12985
  const signal = blocking?.();
12667
12986
  if (signal?.aborted) throw new KernelError("EINTR");
12668
- let lookup3 = this.pending.get(id2);
12669
- if (!lookup3) {
12987
+ let lookup4 = this.pending.get(id2);
12988
+ if (!lookup4) {
12670
12989
  const abort = new AbortController();
12671
- lookup3 = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
12672
- this.pending.set(id2, lookup3);
12673
- void lookup3.work.then(() => this.pending.delete(id2));
12990
+ lookup4 = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
12991
+ this.pending.set(id2, lookup4);
12992
+ void lookup4.work.then(() => this.pending.delete(id2));
12674
12993
  }
12675
- return waitFor(lookup3, signal);
12994
+ return waitFor(lookup4, signal);
12676
12995
  }
12677
12996
  async lookup(name2, family, id2, abandoned) {
12678
12997
  const uplink = this.uplink;
@@ -12713,17 +13032,17 @@ var Resolver = class {
12713
13032
  }
12714
13033
  }
12715
13034
  };
12716
- function waitFor(lookup3, signal) {
12717
- lookup3.waiters++;
13035
+ function waitFor(lookup4, signal) {
13036
+ lookup4.waiters++;
12718
13037
  return new Promise((resolve2, reject) => {
12719
13038
  const abort = () => {
12720
- if (--lookup3.waiters === 0) lookup3.abort.abort(new KernelError("EINTR"));
13039
+ if (--lookup4.waiters === 0) lookup4.abort.abort(new KernelError("EINTR"));
12721
13040
  reject(new KernelError("EINTR"));
12722
13041
  };
12723
13042
  signal?.addEventListener("abort", abort, { once: true });
12724
- void lookup3.work.then((value) => {
13043
+ void lookup4.work.then((value) => {
12725
13044
  if (signal?.aborted) return;
12726
- lookup3.waiters--;
13045
+ lookup4.waiters--;
12727
13046
  signal?.removeEventListener("abort", abort);
12728
13047
  resolve2(value);
12729
13048
  });
@@ -13370,17 +13689,17 @@ function socketAt(fds, fd) {
13370
13689
  if (!(file instanceof KernelSocket)) throw new KernelError("ENOTSOCK");
13371
13690
  return file;
13372
13691
  }
13373
- var ok = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
13692
+ var ok2 = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
13374
13693
  async function socketSyscall(req, proc) {
13375
13694
  const { fds, net } = proc;
13376
13695
  switch (req.op) {
13377
13696
  case "sock-open":
13378
- return ok(fds.install(new OpenFile(net.socket(req.domain)), 3));
13697
+ return ok2(fds.install(new OpenFile(net.socket(req.domain)), 3));
13379
13698
  case "sock-pair": {
13380
13699
  const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
13381
13700
  const first = fds.install(new OpenFile(a), 3);
13382
13701
  try {
13383
- return ok([first, fds.install(new OpenFile(b), 3)]);
13702
+ return ok2([first, fds.install(new OpenFile(b), 3)]);
13384
13703
  } catch (e) {
13385
13704
  await Promise.resolve(fds.close(first));
13386
13705
  throw e;
@@ -13388,12 +13707,12 @@ async function socketSyscall(req, proc) {
13388
13707
  }
13389
13708
  case "sock-bind":
13390
13709
  socketAt(fds, req.fd).bind(req.addr);
13391
- return ok();
13710
+ return ok2();
13392
13711
  case "sock-listen":
13393
13712
  socketAt(fds, req.fd).listen(req.backlog, proc.cred?.());
13394
- return ok();
13713
+ return ok2();
13395
13714
  case "sock-accept":
13396
- return ok(await accept(proc, req.fd, req.nonblock));
13715
+ return ok2(await accept(proc, req.fd, req.nonblock));
13397
13716
  case "sock-connect": {
13398
13717
  const socket = socketAt(fds, req.fd);
13399
13718
  const stamp = net.facadeAt(req.addr) && await proc.stamp?.();
@@ -13403,26 +13722,26 @@ async function socketSyscall(req, proc) {
13403
13722
  throw new KernelError("EINPROGRESS");
13404
13723
  }
13405
13724
  if (dialing !== void 0) await interruptible2(dialing, proc.blocking());
13406
- return ok();
13725
+ return ok2();
13407
13726
  }
13408
13727
  case "sock-shutdown":
13409
13728
  socketAt(fds, req.fd).shutdown(req.how);
13410
- return ok();
13729
+ return ok2();
13411
13730
  case "sock-name": {
13412
13731
  const socket = socketAt(fds, req.fd);
13413
13732
  const addr = req.peer ? socket.peer : socket.local;
13414
13733
  if (req.peer && !addr) throw new KernelError("ENOTCONN");
13415
- return ok(addr ?? unnamed(socket.domain));
13734
+ return ok2(addr ?? unnamed(socket.domain));
13416
13735
  }
13417
13736
  case "sock-getopt":
13418
- return ok(socketAt(fds, req.fd).getOption(req.level, req.name));
13737
+ return ok2(socketAt(fds, req.fd).getOption(req.level, req.name));
13419
13738
  case "sock-setopt":
13420
13739
  socketAt(fds, req.fd).setOption(req.level, req.name, req.value);
13421
- return ok();
13740
+ return ok2();
13422
13741
  case "sock-resolve": {
13423
13742
  const addresses = await proc.resolve(req.name, req.family, () => proc.blocking());
13424
13743
  if (addresses.length === 0) throw new KernelError("ENOENT");
13425
- return ok(addresses);
13744
+ return ok2(addresses);
13426
13745
  }
13427
13746
  }
13428
13747
  }
@@ -14123,6 +14442,9 @@ function isHttpSyscall(req) {
14123
14442
  function isSocketSyscall(req) {
14124
14443
  return req.op.startsWith("sock-");
14125
14444
  }
14445
+ function isEpollSyscall(req) {
14446
+ return EPOLL_OPS.includes(req.op);
14447
+ }
14126
14448
  function isHostSyscall(req) {
14127
14449
  return HOST_OPS.includes(req.op);
14128
14450
  }
@@ -14213,6 +14535,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
14213
14535
  "sig-mask",
14214
14536
  "sig-pause",
14215
14537
  ...SOCKET_OPS,
14538
+ ...EPOLL_OPS,
14216
14539
  ...HTTP_OPS,
14217
14540
  ...PTY_OPS,
14218
14541
  ...HOST_OPS
@@ -14327,9 +14650,9 @@ var WasmProcess = class {
14327
14650
  this.options.onPending?.(sig);
14328
14651
  if (this.options.jobs?.keyboard && KEYBOARD.has(sig) && this.waiting.size === 0)
14329
14652
  this.owed = true;
14330
- const blocked = this.interrupt;
14653
+ const blocked2 = this.interrupt;
14331
14654
  this.interrupt = new AbortController();
14332
- blocked.abort();
14655
+ blocked2.abort();
14333
14656
  return "deliver";
14334
14657
  }
14335
14658
  async spawn(req) {
@@ -14388,9 +14711,9 @@ var WasmProcess = class {
14388
14711
  this.stopped = sig;
14389
14712
  this.stops++;
14390
14713
  this.resumed = new Promise((resolve2) => this.wake = resolve2);
14391
- const blocked = this.interrupt;
14714
+ const blocked2 = this.interrupt;
14392
14715
  this.interrupt = new AbortController();
14393
- blocked.abort();
14716
+ blocked2.abort();
14394
14717
  for (const listener of this.stateListeners) listener("stopped", sig);
14395
14718
  return "stop";
14396
14719
  }
@@ -14440,6 +14763,13 @@ var WasmProcess = class {
14440
14763
  if (isTtySyscall(req)) return this.ttySyscall(req);
14441
14764
  if (isJobSyscall(req)) return this.jobSyscall(req);
14442
14765
  if (isSocketSyscall(req)) return await this.socketSyscall(req, cancelled);
14766
+ if (isEpollSyscall(req)) {
14767
+ return await epollSyscall(req, {
14768
+ fds: this.fds,
14769
+ blocking: () => this.blockingSignal(cancelled),
14770
+ owed: () => this.owed
14771
+ });
14772
+ }
14443
14773
  if (isHttpSyscall(req)) return await this.httpSyscall(req);
14444
14774
  if (isHostSyscall(req)) {
14445
14775
  return await hostSyscall(req, {
@@ -14506,6 +14836,7 @@ var WasmProcess = class {
14506
14836
  if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
14507
14837
  if (file.tty) this.checkForeground(file.tty);
14508
14838
  if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
14839
+ if (file instanceof EventFd && req.max < EventFd.SIZE) throw new KernelError("EINVAL");
14509
14840
  if (req.max <= 0) return new Uint8Array(0);
14510
14841
  if (file instanceof KernelSocket && file.unconnected) throw new KernelError("ENOTCONN");
14511
14842
  const ready2 = pollFile(file).readable;
@@ -15353,9 +15684,9 @@ var WasmProcess = class {
15353
15684
  }
15354
15685
  timerExpired(which) {
15355
15686
  this.options.onTimer?.(which);
15356
- const blocked = this.interrupt;
15687
+ const blocked2 = this.interrupt;
15357
15688
  this.interrupt = new AbortController();
15358
- blocked.abort();
15689
+ blocked2.abort();
15359
15690
  }
15360
15691
  clearAlarm() {
15361
15692
  clearTimeout(this.alarm);
@@ -15459,9 +15790,9 @@ function killThread(thread, caught, sig) {
15459
15790
  Atomics.or(thread.header, SAB_I_SIGNALS, sigbit(sig));
15460
15791
  if (held) return true;
15461
15792
  Atomics.notify(thread.header, SAB_I_SIGNALS);
15462
- const blocked = thread.interrupt;
15793
+ const blocked2 = thread.interrupt;
15463
15794
  thread.interrupt = new AbortController();
15464
- blocked.abort();
15795
+ blocked2.abort();
15465
15796
  return true;
15466
15797
  }
15467
15798
  function unblocked(header4) {
@@ -15640,8 +15971,8 @@ function threadInit(opts, program, sab, cred, thread, main) {
15640
15971
  function spawnWasmProcess(opts) {
15641
15972
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
15642
15973
  const header4 = new Int32Array(sab, 0, SAB_HEADER_I32);
15643
- const blocked = opts.fork?.wasi?.blocked ?? opts.blocked;
15644
- if (blocked && anyBlocked(blocked) && opts.program.abi === "wasi") storeBlocked(header4, blocked);
15974
+ const blocked2 = opts.fork?.wasi?.blocked ?? opts.blocked;
15975
+ if (blocked2 && anyBlocked(blocked2) && opts.program.abi === "wasi") storeBlocked(header4, blocked2);
15645
15976
  const threadSignals = /* @__PURE__ */ new Map();
15646
15977
  const created3 = new Created();
15647
15978
  const process2 = new WasmProcess(
@@ -19599,6 +19930,8 @@ var Launcher = class {
19599
19930
  const head = await this.head(glue);
19600
19931
  if (!head) return void 0;
19601
19932
  if (isWasm(head)) {
19933
+ const owner2 = await this.wasmOwner(glue, baseName(file).replace(/\.wasm$/, ""), scope);
19934
+ if (owner2) return { ...targetOf(owner2), ...asked };
19602
19935
  return {
19603
19936
  abi: "wasi",
19604
19937
  glue,
@@ -19621,6 +19954,17 @@ var Launcher = class {
19621
19954
  );
19622
19955
  return owners.find((c) => c.name === name2) ?? owners[0];
19623
19956
  }
19957
+ async wasmOwner(wasm, name2, scope) {
19958
+ const leaf = baseName(wasm);
19959
+ const real = await physicalPathAsync(this.base, wasm);
19960
+ const owners = [];
19961
+ for (const command of (await this.commands(scope)).values()) {
19962
+ if (command.abi !== "wasi" || command.script || baseName(command.wasm) !== leaf) continue;
19963
+ if (command.wasm === wasm || await physicalPathAsync(this.base, command.wasm) === real)
19964
+ owners.push(command);
19965
+ }
19966
+ return owners.find((c) => c.name === name2) ?? owners[0];
19967
+ }
19624
19968
  async head(path) {
19625
19969
  try {
19626
19970
  return (await this.base.readFileBuffer(path)).subarray(0, SHEBANG_MAX);
@@ -19708,16 +20052,29 @@ var Launcher = class {
19708
20052
  for (const bin of planned?.bins ?? []) await need(g, "exec", bin, R_OK | X_OK);
19709
20053
  }
19710
20054
  async checkedPlan(req, { ppid, cred, scope }, fds) {
19711
- const planned = await this.plan(req.file, req.argv, req.cwd, scope, req.argv0);
19712
- try {
19713
- await this.mayExec(cred, { ...req, pid: ppid }, planned);
19714
- } catch (err) {
20055
+ const refused2 = async (err) => {
19715
20056
  await fds.closeAll();
19716
20057
  throw err.code === "EACCES" ? new SpawnError("EACCES") : err;
19717
- }
20058
+ };
20059
+ const file = await this.execFile(req.file, req.cwd, this.execGuard(cred, ppid)).catch(refused2);
20060
+ const planned = await this.plan(file, req.argv, req.cwd, scope, req.argv0);
20061
+ await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused2);
19718
20062
  if (planned) return planned;
19719
20063
  await fds.closeAll();
19720
- throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
20064
+ throw new SpawnError(await this.unrunnable(file, req.cwd, scope));
20065
+ }
20066
+ execGuard(cred, pid, tag) {
20067
+ const { dac } = this;
20068
+ return dac && { fs: this.fs, cred, pid, dac, tag, links: this.links.get(pid) };
20069
+ }
20070
+ async execFile(file, cwd, g) {
20071
+ if (!file.split("/").includes("..")) return file;
20072
+ const path = file.startsWith("/") ? file : `${cwd}/${file}`;
20073
+ const slash = path.lastIndexOf("/");
20074
+ const dir = path.slice(0, slash);
20075
+ const walked = g && await need(g, "exec", dir, X_OK, { directory: true });
20076
+ const real = walked && walked.denied === void 0 ? walked.path : await physicalPathAsync(this.base, dir);
20077
+ return joinName(real, path.slice(slash + 1));
19721
20078
  }
19722
20079
  async ticketFile(req, fds) {
19723
20080
  if (req.file.startsWith("/")) return physicalPathAsync(this.base, req.file);
@@ -19816,7 +20173,8 @@ var Launcher = class {
19816
20173
  }
19817
20174
  return manifests;
19818
20175
  }
19819
- async backing(file, cwd) {
20176
+ async backing(asked, cwd) {
20177
+ const file = await this.execFile(asked, cwd);
19820
20178
  const planned = await this.plan(file, [file], cwd, "");
19821
20179
  return planned && this.backingOf(planned, file, cwd, "");
19822
20180
  }
@@ -20453,8 +20811,8 @@ var Launcher = class {
20453
20811
  }
20454
20812
  signalMasksOf(pid) {
20455
20813
  const masks = this.processes.get(pid)?.signalMasks?.();
20456
- const [blocked] = this.blockedBy(pid);
20457
- const sigBlk = blocked ? { sigBlk: blocked } : {};
20814
+ const [blocked2] = this.blockedBy(pid);
20815
+ const sigBlk = blocked2 ? { sigBlk: blocked2 } : {};
20458
20816
  return masks ? { sigIgn: masks.ignored, sigCgt: masks.caught, ...sigBlk } : sigBlk;
20459
20817
  }
20460
20818
  controlsJobs(pid) {
@@ -20902,7 +21260,7 @@ var Launcher = class {
20902
21260
  const scope = account.uid === 0 ? "" : pnpmHomeOf(account, this.pnpmHome);
20903
21261
  const start2 = dir ?? "/";
20904
21262
  const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
20905
- const [file = ""] = argv;
21263
+ const file = await this.execFile(argv[0] ?? "", cwd, this.execGuard(credOf(account), 0, tag));
20906
21264
  if (!await this.base.exists(cwd)) await this.stackFor(tag).fs.mkdir(cwd, { recursive: true });
20907
21265
  const planned = await this.plan(file, argv, cwd, scope);
20908
21266
  await this.mayExec(credOf(account), { file, cwd, pid: 0, tag }, planned);
@@ -21005,11 +21363,11 @@ var Launcher = class {
21005
21363
  // src/node/loopback.ts
21006
21364
  import { AsyncLocalStorage } from "node:async_hooks";
21007
21365
  import { subscribe } from "node:diagnostics_channel";
21008
- import { lookup } from "node:dns/promises";
21366
+ import { lookup as lookup2 } from "node:dns/promises";
21009
21367
  import { isIP } from "node:net";
21010
21368
  var DEVTOOLS_PORTS = [9222, 9223, 9224, 9225, 9226, 9227, 9228, 9229];
21011
21369
  var METADATA_V6 = [64768, 3778, 0, 0, 0, 0, 0, 596].join(":");
21012
- var resolveHost = async (host) => (await lookup(host, { all: true, verbatim: true })).map((a) => a.address);
21370
+ var resolveHost = async (host) => (await lookup2(host, { all: true, verbatim: true })).map((a) => a.address);
21013
21371
  function ipv4Place(address) {
21014
21372
  const [a, b] = address.split(".").map(Number);
21015
21373
  if (a === 127 || a === 0) return "loopback";
@@ -21404,7 +21762,7 @@ function isMemoryRoot(handle) {
21404
21762
  }
21405
21763
 
21406
21764
  // src/node/ws-diagnose.ts
21407
- import { lookup as lookup2 } from "node:dns/promises";
21765
+ import { lookup as lookup3 } from "node:dns/promises";
21408
21766
  import { isIP as isIP2, connect as tcp } from "node:net";
21409
21767
  import { connect as tls } from "node:tls";
21410
21768
  var PROBE_MS = 3e3;
@@ -21436,7 +21794,7 @@ async function diagnose(url, ms = PROBE_MS) {
21436
21794
  const secure = at.protocol === "wss:";
21437
21795
  const port = Number(at.port) || (secure ? 443 : 80);
21438
21796
  try {
21439
- await lookup2(host);
21797
+ await lookup3(host);
21440
21798
  } catch (e) {
21441
21799
  return NO_NAME.has(codeOf6(e) ?? "") ? "EAI_NONAME" : void 0;
21442
21800
  }
@@ -22512,7 +22870,7 @@ async function createNodeKernel(options = {}) {
22512
22870
  async connect({ user, tag } = {}) {
22513
22871
  if (terminated) throw new Error(TERMINATED);
22514
22872
  tagOption(tag);
22515
- if (user !== void 0 && !await launcher.users.lookup(user)) {
22873
+ if (user !== void 0 && !await guard2(() => launcher.users.lookup(user))) {
22516
22874
  throw fsError("ENOENT", `no user ${String(user)}`);
22517
22875
  }
22518
22876
  const { port1, port2 } = new MessageChannel2();