@ai-ecoverse/slicc-kernel 1.61.2 → 1.62.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
@@ -105,13 +105,13 @@ function fsError(code, path) {
105
105
  return Object.assign(new Error(`${code}: ${path}`), { code });
106
106
  }
107
107
  function normalizePath(path) {
108
- const parts = [];
108
+ const parts2 = [];
109
109
  for (const part of path.split("/")) {
110
110
  if (part === "" || part === ".") continue;
111
- if (part === "..") parts.pop();
112
- else parts.push(part);
111
+ if (part === "..") parts2.pop();
112
+ else parts2.push(part);
113
113
  }
114
- return `/${parts.join("/")}`;
114
+ return `/${parts2.join("/")}`;
115
115
  }
116
116
  function resolvePath(base, path) {
117
117
  return normalizePath(path.startsWith("/") ? path : `${base}/${path}`);
@@ -129,6 +129,355 @@ function inodeOf(path) {
129
129
  return 4294967296 * (2097151 & h2) + (h1 >>> 0) || 1;
130
130
  }
131
131
 
132
+ // src/fs/owners.ts
133
+ var ROOT_OWNER = Object.freeze({ uid: 0, gid: 0 });
134
+ var S_ISUID = 2048;
135
+ var S_ISGID = 1024;
136
+ var S_IXGRP = 8;
137
+ function ownOf(entry) {
138
+ if (entry?.uid === void 0 || entry.gid === void 0) return void 0;
139
+ return { uid: entry.uid, gid: entry.gid };
140
+ }
141
+ function handedBy(entry) {
142
+ return entry?.heir ? { uid: entry.heir[0], gid: entry.heir[1] } : ownOf(entry);
143
+ }
144
+ function inherit(entries) {
145
+ for (let i = entries.length - 1; i >= 0; i--) {
146
+ const found = handedBy(entries[i]);
147
+ if (found) return found;
148
+ }
149
+ return { ...ROOT_OWNER };
150
+ }
151
+ function chain(path) {
152
+ const parts2 = path.split("/").filter(Boolean);
153
+ return ["/", ...parts2.map((_, i) => `/${parts2.slice(0, i + 1).join("/")}`)];
154
+ }
155
+ function sameOwner(a, b) {
156
+ return a.uid === b.uid && a.gid === b.gid;
157
+ }
158
+ function ownerOf(cred, mode) {
159
+ return { uid: cred.euid, gid: cred.egid, ...mode !== void 0 ? { mode } : {} };
160
+ }
161
+ function inGroup(cred, gid) {
162
+ return cred.egid === gid || cred.groups.includes(gid);
163
+ }
164
+ function mayChown(cred, current, uid, gid) {
165
+ if (cred.euid === 0) return true;
166
+ const owner = cred.euid === current.uid;
167
+ if (uid !== void 0 && !(owner && uid === current.uid)) return false;
168
+ if (gid !== void 0 && !(owner && (gid === current.gid || inGroup(cred, gid)))) return false;
169
+ return true;
170
+ }
171
+ function clearedSetId(mode) {
172
+ const bits2 = S_ISUID | (mode & S_IXGRP ? S_ISGID : 0);
173
+ return mode & 4095 & ~bits2;
174
+ }
175
+ async function chownAs(fs, cred, path, change) {
176
+ const { uid, gid, follow: follow2 } = change;
177
+ const st = follow2 ? await fs.stat(path) : await fs.lstat(path);
178
+ const current = { uid: st.uid ?? 0, gid: st.gid ?? 0 };
179
+ if (!fs.chown || !mayChown(cred, current, uid, gid)) throw fsError("EPERM", path);
180
+ const clear = st.isFile && (uid !== void 0 || gid !== void 0) ? clearedSetId(st.mode) : void 0;
181
+ await fs.chown(path, {
182
+ ...uid !== void 0 ? { uid } : {},
183
+ ...gid !== void 0 ? { gid } : {},
184
+ follow: follow2,
185
+ ...clear !== void 0 && clear !== (st.mode & 4095) ? { mode: clear } : {}
186
+ });
187
+ }
188
+
189
+ // src/fs/walk.ts
190
+ var MAX_LINKS = 40;
191
+ function parentOf(path) {
192
+ return path.slice(0, path.lastIndexOf("/")) || "/";
193
+ }
194
+ async function mustBeDir(fs, path) {
195
+ if (!fs.lstat) return;
196
+ const st = await fs.lstat(path);
197
+ if (st.isDirectory !== true) throw fsError("ENOTDIR", path);
198
+ }
199
+ async function linkOf(fs, path) {
200
+ return fs.readlink?.(path).catch(() => void 0);
201
+ }
202
+ async function realDir(fs, dir, budget = { hops: 0 }) {
203
+ const queue = dir.split("/").filter(Boolean);
204
+ let at = "/";
205
+ while (queue.length > 0) {
206
+ const part = queue.shift();
207
+ if (part === ".") continue;
208
+ if (part === "..") {
209
+ at = parentOf(at);
210
+ continue;
211
+ }
212
+ const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
213
+ const link2 = await linkOf(fs, next2);
214
+ if (link2 === void 0) {
215
+ if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next2);
216
+ at = next2;
217
+ continue;
218
+ }
219
+ if (budget.hops >= MAX_LINKS) throw fsError("ELOOP", dir);
220
+ budget.hops++;
221
+ queue.unshift(...link2.split("/").filter(Boolean));
222
+ if (link2.startsWith("/")) at = "/";
223
+ }
224
+ return at;
225
+ }
226
+ function joinName(dir, name2) {
227
+ return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
228
+ }
229
+ async function realTarget(fs, path) {
230
+ const budget = { hops: 0 };
231
+ let dir = await realDir(fs, parentOf(path), budget);
232
+ let name2 = path.slice(path.lastIndexOf("/") + 1);
233
+ for (; ; ) {
234
+ const link2 = await linkOf(fs, joinName(dir, name2));
235
+ if (link2 === void 0) break;
236
+ if (budget.hops >= MAX_LINKS) throw fsError("ELOOP", path);
237
+ budget.hops++;
238
+ const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
239
+ dir = await realDir(fs, parentOf(target), budget);
240
+ name2 = target.slice(target.lastIndexOf("/") + 1);
241
+ }
242
+ return joinName(dir, name2);
243
+ }
244
+
245
+ // src/fs/access.ts
246
+ var R_OK = 4;
247
+ var W_OK = 2;
248
+ var X_OK = 1;
249
+ var MAX_LINKS2 = 40;
250
+ var S_ISVTX = 512;
251
+ function permOf(st, link2) {
252
+ return {
253
+ uid: st.uid ?? 0,
254
+ gid: st.gid ?? 0,
255
+ mode: st.mode & 4095,
256
+ ...link2 !== void 0 ? { link: link2 } : {}
257
+ };
258
+ }
259
+ function permits(cred, perm, want, directory = true) {
260
+ const mode = perm.mode ?? (directory ? 493 : 420);
261
+ if (cred.euid === 0) return (want & X_OK) === 0 || directory || (mode & 73) !== 0;
262
+ const shift = cred.euid === perm.uid ? 6 : inGroup(cred, perm.gid) ? 3 : 0;
263
+ return (mode >> shift & want) === want;
264
+ }
265
+ function sticky(cred, dir, entry) {
266
+ if (cred.euid === 0 || ((dir.mode ?? 0) & S_ISVTX) === 0) return true;
267
+ return cred.euid === dir.uid || cred.euid === (entry ?? dir).uid;
268
+ }
269
+ async function ancestryOf(fs, path) {
270
+ if (fs.ancestry) return fs.ancestry(path);
271
+ const out = [];
272
+ for (const at of chain(path)) {
273
+ const st = await fs.lstat(at).catch(() => void 0);
274
+ if (!st) break;
275
+ out.push(permOf(st, st.isSymbolicLink ? await fs.readlink(at) : void 0));
276
+ }
277
+ return out;
278
+ }
279
+ var Perms = class {
280
+ known = /* @__PURE__ */ new Map();
281
+ fs;
282
+ constructor(fs) {
283
+ this.fs = fs;
284
+ }
285
+ async learn(path) {
286
+ const names = chain(path);
287
+ const perms = await ancestryOf(this.fs, path);
288
+ for (let i = 0; i < names.length; i++) {
289
+ const perm = perms[i];
290
+ this.known.set(names[i], perm);
291
+ if (perm === void 0 || perm.link !== void 0) break;
292
+ }
293
+ }
294
+ async at(path) {
295
+ if (!this.known.has(path)) await this.learn(path);
296
+ return this.known.get(path);
297
+ }
298
+ };
299
+ function parts(path) {
300
+ return path.split("/").filter((part) => part !== "" && part !== ".");
301
+ }
302
+ async function walk(fs, cred, path, follow2) {
303
+ const perms = new Perms(fs);
304
+ await perms.learn(path);
305
+ const queue = parts(path);
306
+ let at = "/";
307
+ for (let hops = 0; queue.length > 0; ) {
308
+ const part = queue.shift();
309
+ const dir = await perms.at(at);
310
+ if (!dir) return { path: joinName(at, [part, ...queue].join("/")), perm: void 0 };
311
+ if (!permits(cred, dir, X_OK)) return { path, perm: void 0, denied: at };
312
+ if (part === "..") {
313
+ at = parentOf(at);
314
+ continue;
315
+ }
316
+ const next2 = joinName(at, part);
317
+ const perm = await perms.at(next2);
318
+ const last = queue.length === 0;
319
+ if (perm?.link !== void 0 && (!last || follow2)) {
320
+ if (++hops > MAX_LINKS2) return { path: next2, perm: void 0 };
321
+ queue.unshift(...parts(perm.link));
322
+ if (perm.link.startsWith("/")) at = "/";
323
+ continue;
324
+ }
325
+ if (last) return { path: next2, perm };
326
+ at = next2;
327
+ }
328
+ return { path: at, perm: await perms.at(at) };
329
+ }
330
+ async function present(fs, path, directory) {
331
+ const st = await fs.lstat(path).catch(() => void 0);
332
+ return st !== void 0 && (!directory || st.isDirectory);
333
+ }
334
+ async function access(fs, cred, path, want, options = {}) {
335
+ const walked = await walk(fs, cred, path, options.follow !== false);
336
+ if (walked.denied !== void 0) {
337
+ if (await present(fs, walked.denied, true)) return walked;
338
+ return { path, perm: void 0, missing: true };
339
+ }
340
+ const { perm } = walked;
341
+ if (!perm) return { ...walked, missing: true };
342
+ if (want === 0 || permits(cred, perm, want, options.directory !== false)) return walked;
343
+ if (await present(fs, walked.path, false)) return { ...walked, denied: walked.path };
344
+ return { ...walked, missing: true };
345
+ }
346
+ function refused(path, errno = "EACCES") {
347
+ return fsError(errno, path);
348
+ }
349
+
350
+ // src/fs/dac.ts
351
+ var MAX_DENIAL_KEYS = 1e4;
352
+ var S_ISGID2 = 1024;
353
+ var Dac = class {
354
+ mode;
355
+ hook;
356
+ log;
357
+ seen = /* @__PURE__ */ new Set();
358
+ suppressed = false;
359
+ constructor(mode, hook, log = (line) => console.warn(line)) {
360
+ this.mode = mode;
361
+ this.hook = hook;
362
+ this.log = log;
363
+ }
364
+ get enforcing() {
365
+ return this.mode === "enforce";
366
+ }
367
+ denied(denial) {
368
+ try {
369
+ this.hook?.({ ...denial });
370
+ } catch {
371
+ }
372
+ if (this.mode === "report") this.note(denial);
373
+ else throw refused(denial.path, denial.errno);
374
+ }
375
+ note(d) {
376
+ const key = `${d.uid}\0${d.op}\0${d.errno}\0${d.path}`;
377
+ if (this.seen.has(key)) return;
378
+ if (this.seen.size >= MAX_DENIAL_KEYS) {
379
+ if (!this.suppressed) this.log("slicc-kernel users report: more denials suppressed");
380
+ this.suppressed = true;
381
+ return;
382
+ }
383
+ this.seen.add(key);
384
+ this.log(
385
+ `slicc-kernel users report: ${d.errno} ${d.op} ${d.path} (uid ${d.uid}, pid ${d.pid})`
386
+ );
387
+ }
388
+ };
389
+ function deny(g, op, path, errno = "EACCES") {
390
+ g.dac.denied({ pid: g.pid, uid: g.cred.euid, op, path, errno });
391
+ }
392
+ async function need(g, op, path, want, options = {}, shown = path) {
393
+ const walked = await access(g.fs, g.cred, path, want, options);
394
+ if (walked.denied !== void 0) deny(g, op, shown);
395
+ return walked;
396
+ }
397
+ async function stickyEntry(g, op, path, dir) {
398
+ const perm = dir.perm;
399
+ if (!perm || sticky(g.cred, perm, perm)) return;
400
+ const entry = joinName(dir.path, path.slice(path.lastIndexOf("/") + 1));
401
+ if (sticky(g.cred, perm, (await ancestryOf(g.fs, entry)).at(-1))) return;
402
+ if (await present(g.fs, entry, false)) deny(g, op, path, "EPERM");
403
+ }
404
+ async function inParent(g, op, path, old = false) {
405
+ const dir = await need(g, op, parentOf(path), W_OK | X_OK, {}, path);
406
+ if (old && dir.denied === void 0) await stickyEntry(g, op, path, dir);
407
+ }
408
+ async function makeIn(g, op, path) {
409
+ for (let dir = parentOf(path); ; dir = parentOf(dir)) {
410
+ const walked = await access(g.fs, g.cred, dir, W_OK | X_OK);
411
+ if (walked.denied !== void 0) {
412
+ if (!await present(g.fs, path, true)) deny(g, op, path);
413
+ return;
414
+ }
415
+ if (!walked.missing || dir === "/") return;
416
+ }
417
+ }
418
+ async function tree(g, op, dir, perm) {
419
+ if (!permits(g.cred, perm, R_OK | W_OK | X_OK)) return deny(g, op, dir);
420
+ for (const [name2, st] of await g.fs.readdirStat(dir)) {
421
+ if (!st) continue;
422
+ const child = joinName(dir, name2);
423
+ if (!sticky(g.cred, perm, permOf(st))) deny(g, op, child, "EPERM");
424
+ if (st.isDirectory) await tree(g, op, child, permOf(st));
425
+ }
426
+ }
427
+ async function removable(g, op, path) {
428
+ await inParent(g, op, path, true);
429
+ if (g.cred.euid === 0) return;
430
+ const st = await g.fs.lstat(path).catch(() => void 0);
431
+ if (st?.isDirectory) await tree(g, op, path, permOf(st));
432
+ }
433
+ async function renamable(g, from, to) {
434
+ if (from === to) return void await need(g, "rename", from, 0, { follow: false });
435
+ await inParent(g, "rename", from, true);
436
+ await inParent(g, "rename", to, true);
437
+ if (g.cred.euid === 0 || parentOf(from) === parentOf(to)) return;
438
+ const st = await g.fs.lstat(from).catch(() => void 0);
439
+ if (st?.isDirectory && !permits(g.cred, permOf(st), W_OK)) deny(g, "rename", from);
440
+ }
441
+ async function linkable(g, from, to, follow2) {
442
+ const source = await need(g, "link", from, 0, { follow: follow2 });
443
+ const perm = source.perm;
444
+ if (perm && g.cred.euid !== 0 && perm.uid !== g.cred.euid) {
445
+ const safe = permits(g.cred, perm, R_OK | W_OK, false);
446
+ if (!safe && await present(g.fs, source.path, false)) deny(g, "link", from, "EPERM");
447
+ }
448
+ await inParent(g, "link", to);
449
+ }
450
+ async function ownerOnly(g, op, path, follow2) {
451
+ const walked = await need(g, op, path, 0, { follow: follow2 });
452
+ const perm = walked.perm;
453
+ if (!perm || g.cred.euid === 0 || perm.uid === g.cred.euid) return walked;
454
+ if (await present(g.fs, walked.path, false)) deny(g, op, path, "EPERM");
455
+ return walked;
456
+ }
457
+ async function chmodMode(g, path, mode) {
458
+ const perm = (await ownerOnly(g, "chmod", path, true)).perm;
459
+ if (!g.dac.enforcing || !perm || g.cred.euid === 0 || inGroup(g.cred, perm.gid)) return mode;
460
+ return mode & ~S_ISGID2;
461
+ }
462
+ async function retimable(g, op, path) {
463
+ await ownerOnly(g, op, path, op !== "lutimes");
464
+ }
465
+ async function listable(g, op, path) {
466
+ await need(g, op, path, R_OK | X_OK, { directory: true });
467
+ }
468
+ async function searchable(g, op, path, follow2) {
469
+ await need(g, op, path, 0, { follow: follow2 });
470
+ }
471
+ async function readable(g, op, path) {
472
+ await need(g, op, path, R_OK, { directory: false });
473
+ }
474
+ async function writable(g, op, path) {
475
+ const st = await g.fs.stat(path).catch(() => void 0);
476
+ if (st) return void await need(g, op, path, W_OK, { directory: st.isDirectory });
477
+ const target = await need(g, op, path, 0);
478
+ if (target.denied === void 0) await need(g, op, parentOf(target.path), W_OK | X_OK, {}, path);
479
+ }
480
+
132
481
  // src/kernel/pipe.ts
133
482
  var PIPE_CAPACITY = 64 * 1024;
134
483
  var PipeError = class extends Error {
@@ -372,7 +721,7 @@ function written(device, bytes) {
372
721
  if (device === "full" && bytes.length > 0) throw new KernelError("ENOSPC");
373
722
  return bytes.length;
374
723
  }
375
- function deviceFile(device, access) {
724
+ function deviceFile(device, access2) {
376
725
  const read = async (max) => {
377
726
  if (device === "null") return new Uint8Array(0);
378
727
  const out = new Uint8Array(max);
@@ -384,10 +733,10 @@ function deviceFile(device, access) {
384
733
  return out;
385
734
  };
386
735
  return new OpenFile({
387
- ...access !== "write" ? { read } : {},
388
- ...access !== "read" ? { write: async (bytes) => written(device, bytes) } : {},
736
+ ...access2 !== "write" ? { read } : {},
737
+ ...access2 !== "read" ? { write: async (bytes) => written(device, bytes) } : {},
389
738
  seek: async () => 0,
390
- heldMeta: { device, ...access ? { access } : {} },
739
+ heldMeta: { device, ...access2 ? { access: access2 } : {} },
391
740
  close: () => {
392
741
  }
393
742
  });
@@ -1824,9 +2173,9 @@ var UserDb = class {
1824
2173
  if (await this.realDir(pnpm)) await this.fs.chmod(pnpm, scoop ? 488 : 448);
1825
2174
  }
1826
2175
  async realDir(path) {
1827
- const parts = path.split("/").filter(Boolean);
1828
- for (let n = 1; n <= parts.length; n++) {
1829
- const at = `/${parts.slice(0, n).join("/")}`;
2176
+ const parts2 = path.split("/").filter(Boolean);
2177
+ for (let n = 1; n <= parts2.length; n++) {
2178
+ const at = `/${parts2.slice(0, n).join("/")}`;
1830
2179
  const st = await this.fs.lstat(at).catch(() => void 0);
1831
2180
  if (!st?.isDirectory) return false;
1832
2181
  }
@@ -2054,7 +2403,7 @@ function versionError(theirs) {
2054
2403
  }
2055
2404
 
2056
2405
  // src/client/serve-client.ts
2057
- var MAX_LINKS = 40;
2406
+ var MAX_LINKS3 = 40;
2058
2407
  function act(session, req, signal) {
2059
2408
  if (req.action === "write") session.write(req.bytes);
2060
2409
  else if (req.action === "resize") session.resize(req.cols, req.rows);
@@ -2062,26 +2411,50 @@ function act(session, req, signal) {
2062
2411
  else session.close();
2063
2412
  }
2064
2413
  async function realpath(fs, path) {
2065
- let parts = fs.resolvePath("/", path).split("/").filter(Boolean);
2414
+ let parts2 = fs.resolvePath("/", path).split("/").filter(Boolean);
2066
2415
  let resolved = "";
2067
- for (let hops = 0; parts.length > 0; ) {
2068
- const [name2, ...rest2] = parts;
2416
+ for (let hops = 0; parts2.length > 0; ) {
2417
+ const [name2, ...rest2] = parts2;
2069
2418
  const next2 = `${resolved}/${name2}`;
2070
2419
  if (!(await fs.lstat(next2)).isSymbolicLink) {
2071
2420
  resolved = next2;
2072
- parts = rest2;
2421
+ parts2 = rest2;
2073
2422
  continue;
2074
2423
  }
2075
- if (++hops > MAX_LINKS) throw fsError("ELOOP", path);
2424
+ if (++hops > MAX_LINKS3) throw fsError("ELOOP", path);
2076
2425
  const target = await fs.readlink(next2);
2077
2426
  const base = target.startsWith("/") ? "/" : resolved || "/";
2078
- parts = [...fs.resolvePath(base, target).split("/").filter(Boolean), ...rest2];
2427
+ parts2 = [...fs.resolvePath(base, target).split("/").filter(Boolean), ...rest2];
2079
2428
  resolved = "";
2080
2429
  }
2081
2430
  return resolved || "/";
2082
2431
  }
2083
- async function fsCall(fs, method, args, owner) {
2432
+ var CLIENT_CHECKS = {
2433
+ readFile: (g, at) => readable(g, "read", at(0)),
2434
+ writeFile: (g, at) => writable(g, "write", at(0)),
2435
+ stat: (g, at) => searchable(g, "stat", at(0), true),
2436
+ lstat: (g, at) => searchable(g, "lstat", at(0), false),
2437
+ readdir: (g, at) => listable(g, "readdir", at(0)),
2438
+ mkdir: (g, at) => makeIn(g, "mkdir", at(0)),
2439
+ rm: (g, at) => removable(g, "rm", at(0)),
2440
+ rename: (g, at) => renamable(g, at(0), at(1)),
2441
+ realpath: (g, at) => searchable(g, "realpath", at(0), true),
2442
+ symlink: (g, at) => inParent(g, "symlink", at(1)),
2443
+ readlink: (g, at) => searchable(g, "readlink", at(0), false),
2444
+ exists: (g, at) => searchable(g, "exists", at(0), true)
2445
+ };
2446
+ async function fsCall(fs, method, args, owner, g) {
2084
2447
  const path = (i) => fs.resolvePath("/", String(args[i]));
2448
+ if (g) {
2449
+ const denied2 = await Promise.resolve(CLIENT_CHECKS[method](g, path)).then(
2450
+ () => false,
2451
+ (err) => {
2452
+ if (method === "exists") return true;
2453
+ throw err;
2454
+ }
2455
+ );
2456
+ if (denied2) return { result: false };
2457
+ }
2085
2458
  switch (method) {
2086
2459
  case "readFile": {
2087
2460
  const bytes = (await fs.readFileBuffer(path(0))).slice();
@@ -2144,6 +2517,10 @@ function ownerFor(l, self) {
2144
2517
  if (!l.owners || !self) return void 0;
2145
2518
  return { uid: self.uid, gid: self.gid, umask: self.umask ?? 18 };
2146
2519
  }
2520
+ function guardFor(l, self) {
2521
+ if (!l.dac || !self) return void 0;
2522
+ return { fs: l.fs, cred: credOf(self), pid: 0, dac: l.dac };
2523
+ }
2147
2524
  async function accountOf(l, user) {
2148
2525
  const found = await l.users.lookup(user);
2149
2526
  if (!found) throw fsError("ENOENT", `no user ${String(user)}`);
@@ -2212,17 +2589,28 @@ function cdpRegistry(send, gone) {
2212
2589
  }
2213
2590
  };
2214
2591
  }
2215
- function watchOp(l, req, watches, send) {
2592
+ function visibleTo(g, notify) {
2593
+ if (!g?.dac.enforcing) return notify;
2594
+ let queue = Promise.resolve();
2595
+ const seen = async (path) => (await access(g.fs, g.cred, parentOf(path), R_OK | X_OK, { directory: true })).denied === void 0;
2596
+ return (change) => {
2597
+ queue = queue.then(async () => {
2598
+ if (!("paths" in change)) return notify(change);
2599
+ const paths = [];
2600
+ for (const path of change.paths) if (await seen(path)) paths.push(path);
2601
+ if (paths.length > 0) notify({ paths });
2602
+ });
2603
+ };
2604
+ }
2605
+ async function watchOp(l, req, watches, send, g) {
2216
2606
  if (req.op === "unwatch") {
2217
2607
  watches.get(req.watch)?.();
2218
2608
  watches.delete(req.watch);
2219
2609
  return { result: true };
2220
2610
  }
2221
- const unwatch = l.watchers.watch(
2222
- req.paths,
2223
- { recursive: req.recursive },
2224
- (change) => send({ watch: req.id, change })
2225
- );
2611
+ if (g) for (const path of req.paths) await readable(g, "watch", l.fs.resolvePath("/", path));
2612
+ const notify = visibleTo(g, (change) => send({ watch: req.id, change }));
2613
+ const unwatch = l.watchers.watch(req.paths, { recursive: req.recursive }, notify);
2226
2614
  watches.set(req.id, unwatch);
2227
2615
  return { result: req.id };
2228
2616
  }
@@ -2256,9 +2644,9 @@ function serveClient(port, host) {
2256
2644
  ended.resolve();
2257
2645
  }
2258
2646
  async function hello(message3) {
2259
- const refused = versionError(message3.protocol);
2260
- if (refused) {
2261
- send({ hello: { protocol: PROTOCOL, error: refused } });
2647
+ const refused2 = versionError(message3.protocol);
2648
+ if (refused2) {
2649
+ send({ hello: { protocol: PROTOCOL, error: refused2 } });
2262
2650
  detach();
2263
2651
  return;
2264
2652
  }
@@ -2338,10 +2726,10 @@ function serveClient(port, host) {
2338
2726
  if (!FS_METHODS.includes(req.method)) {
2339
2727
  throw new Error(`unknown file system call ${req.method}`);
2340
2728
  }
2341
- return fsCall(l.fs, req.method, req.args, ownerFor(l, self));
2729
+ return fsCall(l.fs, req.method, req.args, ownerFor(l, self), guardFor(l, self));
2342
2730
  case "watch":
2343
2731
  case "unwatch":
2344
- return watchOp(l, req, watches, send);
2732
+ return watchOp(l, req, watches, send, guardFor(l, self));
2345
2733
  case "mount":
2346
2734
  return { result: await l.mount(req.spec) };
2347
2735
  case "umount":
@@ -2399,11 +2787,11 @@ function serveClient(port, host) {
2399
2787
  }
2400
2788
 
2401
2789
  // src/fs/meta.ts
2402
- function parentOf(path) {
2790
+ function parentOf2(path) {
2403
2791
  return path.slice(0, path.lastIndexOf("/")) || "/";
2404
2792
  }
2405
2793
  function moved(entry, path) {
2406
- return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir: parentOf(path) };
2794
+ return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir: parentOf2(path) };
2407
2795
  }
2408
2796
  function under(path, root) {
2409
2797
  return path === root || path.startsWith(`${root}/`);
@@ -2446,71 +2834,14 @@ var MemoryMeta = class {
2446
2834
  }
2447
2835
  };
2448
2836
 
2449
- // src/fs/owners.ts
2450
- var ROOT_OWNER = Object.freeze({ uid: 0, gid: 0 });
2451
- var S_ISUID = 2048;
2452
- var S_ISGID = 1024;
2453
- var S_IXGRP = 8;
2454
- function ownOf(entry) {
2455
- if (entry?.uid === void 0 || entry.gid === void 0) return void 0;
2456
- return { uid: entry.uid, gid: entry.gid };
2457
- }
2458
- function handedBy(entry) {
2459
- return entry?.heir ? { uid: entry.heir[0], gid: entry.heir[1] } : ownOf(entry);
2460
- }
2461
- function inherit(entries) {
2462
- for (let i = entries.length - 1; i >= 0; i--) {
2463
- const found = handedBy(entries[i]);
2464
- if (found) return found;
2465
- }
2466
- return { ...ROOT_OWNER };
2467
- }
2468
- function chain(path) {
2469
- const parts = path.split("/").filter(Boolean);
2470
- return ["/", ...parts.map((_, i) => `/${parts.slice(0, i + 1).join("/")}`)];
2471
- }
2472
- function sameOwner(a, b) {
2473
- return a.uid === b.uid && a.gid === b.gid;
2474
- }
2475
- function ownerOf(cred, mode) {
2476
- return { uid: cred.euid, gid: cred.egid, ...mode !== void 0 ? { mode } : {} };
2477
- }
2478
- function inGroup(cred, gid) {
2479
- return cred.egid === gid || cred.groups.includes(gid);
2480
- }
2481
- function mayChown(cred, current, uid, gid) {
2482
- if (cred.euid === 0) return true;
2483
- const owner = cred.euid === current.uid;
2484
- if (uid !== void 0 && !(owner && uid === current.uid)) return false;
2485
- if (gid !== void 0 && !(owner && (gid === current.gid || inGroup(cred, gid)))) return false;
2486
- return true;
2487
- }
2488
- function clearedSetId(mode) {
2489
- const bits2 = S_ISUID | (mode & S_IXGRP ? S_ISGID : 0);
2490
- return mode & 4095 & ~bits2;
2491
- }
2492
- async function chownAs(fs, cred, path, change) {
2493
- const { uid, gid, follow: follow2 } = change;
2494
- const st = follow2 ? await fs.stat(path) : await fs.lstat(path);
2495
- const current = { uid: st.uid ?? 0, gid: st.gid ?? 0 };
2496
- if (!fs.chown || !mayChown(cred, current, uid, gid)) throw fsError("EPERM", path);
2497
- const clear = st.isFile && (uid !== void 0 || gid !== void 0) ? clearedSetId(st.mode) : void 0;
2498
- await fs.chown(path, {
2499
- ...uid !== void 0 ? { uid } : {},
2500
- ...gid !== void 0 ? { gid } : {},
2501
- follow: follow2,
2502
- ...clear !== void 0 && clear !== (st.mode & 4095) ? { mode: clear } : {}
2503
- });
2504
- }
2505
-
2506
2837
  // src/fs/opfs.ts
2507
2838
  var S_IFDIR = 16384;
2508
2839
  var S_IFREG = 32768;
2509
2840
  var S_IFLNK = 40960;
2510
2841
  var PERM_MASK = 4095;
2511
- var S_ISGID2 = 1024;
2842
+ var S_ISGID3 = 1024;
2512
2843
  var READ_ATTEMPTS = 3;
2513
- var MAX_LINKS2 = 40;
2844
+ var MAX_LINKS4 = 40;
2514
2845
  var CHECK_MS = 1e3;
2515
2846
  var EXECUTABLE = /\/node_modules\/(?:@[^/]+\/)?[^/]+\/bin\//;
2516
2847
  var ERRNO_BY_DOM_ERROR = {
@@ -2542,10 +2873,10 @@ function translate(err, path, op = "lookup", mapped = true) {
2542
2873
  var META_OPS = ["get", "update", "move", "remove", "links", "under", "all"];
2543
2874
  function sidecar(meta) {
2544
2875
  const pathOf = ([first]) => [first, "/"].flat().find((p) => typeof p === "string");
2545
- const guarded = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
2876
+ const guarded2 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
2546
2877
  throw translate(err, pathOf(args), "sidecar", false);
2547
2878
  });
2548
- return Object.fromEntries(META_OPS.map((op) => [op, guarded(op)]));
2879
+ return Object.fromEntries(META_OPS.map((op) => [op, guarded2(op)]));
2549
2880
  }
2550
2881
  function missing(err) {
2551
2882
  const code = err?.code;
@@ -2554,15 +2885,15 @@ function missing(err) {
2554
2885
  function split(path) {
2555
2886
  return path.split("/").filter(Boolean);
2556
2887
  }
2557
- function parentOf2(path) {
2888
+ function parentOf3(path) {
2558
2889
  return path.slice(0, path.lastIndexOf("/")) || "/";
2559
2890
  }
2560
2891
  function baseOf(path) {
2561
2892
  return path.slice(path.lastIndexOf("/") + 1);
2562
2893
  }
2563
2894
  function prefixes(path) {
2564
- const parts = split(path);
2565
- return parts.map((_, i) => `/${parts.slice(0, i + 1).join("/")}`);
2895
+ const parts2 = split(path);
2896
+ return parts2.map((_, i) => `/${parts2.slice(0, i + 1).join("/")}`);
2566
2897
  }
2567
2898
  function within(path, root) {
2568
2899
  return path === root || path.startsWith(`${root}/`);
@@ -2583,39 +2914,39 @@ async function openSync(handle) {
2583
2914
  }
2584
2915
  }
2585
2916
  }
2586
- function writeAll(access, at, bytes) {
2917
+ function writeAll(access2, at, bytes) {
2587
2918
  for (let done4 = 0; done4 < bytes.length; ) {
2588
- const n = access.write(bytes.subarray(done4), { at: at + done4 });
2919
+ const n = access2.write(bytes.subarray(done4), { at: at + done4 });
2589
2920
  if (!(n > 0))
2590
2921
  throw Object.assign(new Error("the file system took no more bytes"), { code: "ENOSPC" });
2591
2922
  done4 += n;
2592
2923
  }
2593
2924
  }
2594
2925
  async function positioned(handle, op) {
2595
- const access = await openSync(handle);
2596
- if (access) {
2926
+ const access2 = await openSync(handle);
2927
+ if (access2) {
2597
2928
  try {
2598
2929
  op({
2599
- write: (at, bytes) => writeAll(access, at, bytes),
2600
- truncate: (n) => access.truncate(n)
2930
+ write: (at, bytes) => writeAll(access2, at, bytes),
2931
+ truncate: (n) => access2.truncate(n)
2601
2932
  });
2602
- access.flush();
2933
+ access2.flush();
2603
2934
  } finally {
2604
- access.close();
2935
+ access2.close();
2605
2936
  }
2606
2937
  return;
2607
2938
  }
2608
- const writable = await handle.createWritable({ keepExistingData: true });
2939
+ const writable2 = await handle.createWritable({ keepExistingData: true });
2609
2940
  try {
2610
2941
  await op({
2611
- write: (at, bytes) => writable.write({ type: "write", position: at, data: bytes }),
2612
- truncate: (n) => writable.truncate(n)
2942
+ write: (at, bytes) => writable2.write({ type: "write", position: at, data: bytes }),
2943
+ truncate: (n) => writable2.truncate(n)
2613
2944
  });
2614
2945
  } catch (err) {
2615
- await writable.abort();
2946
+ await writable2.abort();
2616
2947
  throw err;
2617
2948
  }
2618
- await writable.close();
2949
+ await writable2.close();
2619
2950
  }
2620
2951
  function isHandle(child) {
2621
2952
  return "kind" in child;
@@ -2650,13 +2981,21 @@ var OpfsFs = class {
2650
2981
  trackOwners() {
2651
2982
  this.owners = true;
2652
2983
  }
2984
+ async ancestry(path) {
2985
+ const entries = await this.meta.get(chain(path));
2986
+ return entries.map((entry, i) => ({
2987
+ ...ownOf(entry) ?? (i === 0 ? ROOT_OWNER : inherit(entries.slice(0, i))),
2988
+ ...entry?.mode !== void 0 ? { mode: entry.mode } : {},
2989
+ ...entry?.link !== void 0 ? { link: entry.link } : {}
2990
+ }));
2991
+ }
2653
2992
  async handedTo(dir) {
2654
2993
  return inherit(await this.meta.get(chain(dir)));
2655
2994
  }
2656
2995
  async ownerAt(path, entry) {
2657
2996
  const own = ownOf(entry);
2658
2997
  if (own) return own;
2659
- return path === "/" ? { ...ROOT_OWNER } : this.handedTo(parentOf2(path));
2998
+ return path === "/" ? { ...ROOT_OWNER } : this.handedTo(parentOf3(path));
2660
2999
  }
2661
3000
  async owned(stat, path, entry) {
2662
3001
  return this.owners ? { ...stat, ...await this.ownerAt(path, entry) } : stat;
@@ -2666,11 +3005,11 @@ var OpfsFs = class {
2666
3005
  const entries = await this.meta.get(chain(path));
2667
3006
  const self = entries.pop();
2668
3007
  const parent = entries.at(-1);
2669
- const setgid = ((parent?.mode ?? 0) & S_ISGID2) !== 0;
3008
+ const setgid = ((parent?.mode ?? 0) & S_ISGID3) !== 0;
2670
3009
  const parentGid = (ownOf(parent) ?? inherit(entries.slice(0, -1))).gid;
2671
3010
  const want = { uid: owner.uid, gid: setgid ? parentGid : owner.gid };
2672
3011
  let mode = owner.mode === void 0 ? void 0 : owner.mode & PERM_MASK;
2673
- if (directory && setgid) mode = (mode ?? 493) | S_ISGID2;
3012
+ if (directory && setgid) mode = (mode ?? 493) | S_ISGID3;
2674
3013
  const fields = {};
2675
3014
  if (mode !== void 0 && mode !== (this.mode(path, directory, void 0) & PERM_MASK)) {
2676
3015
  fields.mode = mode;
@@ -2686,7 +3025,7 @@ var OpfsFs = class {
2686
3025
  async chown(path, change) {
2687
3026
  if (!this.owners) return;
2688
3027
  if (change.kind === "new") {
2689
- const at = resolvePath((await this.locate(parentOf2(path), true)).path, baseOf(path));
3028
+ const at = resolvePath((await this.locate(parentOf3(path), true)).path, baseOf(path));
2690
3029
  const { uid = 0, gid = 0, mode } = change;
2691
3030
  return this.stampNew(at, { uid, gid, ...mode !== void 0 ? { mode } : {} }, false);
2692
3031
  }
@@ -2714,30 +3053,30 @@ var OpfsFs = class {
2714
3053
  const [stored] = source.link ? [source.link] : await this.meta.get([source.path]);
2715
3054
  return ownOf(stored) ? void 0 : this.ownerAt(source.path, stored);
2716
3055
  }
2717
- async walk(parts, from, start2) {
3056
+ async walk(parts2, from, start2) {
2718
3057
  let dir = start2;
2719
- for (let at = from; at < parts.length; at++) {
2720
- dir = await dir.getDirectoryHandle(parts[at]);
2721
- this.dirs.set(`/${parts.slice(0, at + 1).join("/")}`, dir);
3058
+ for (let at = from; at < parts2.length; at++) {
3059
+ dir = await dir.getDirectoryHandle(parts2[at]);
3060
+ this.dirs.set(`/${parts2.slice(0, at + 1).join("/")}`, dir);
2722
3061
  this.checked.set(dir, performance.now());
2723
3062
  }
2724
3063
  return dir;
2725
3064
  }
2726
3065
  async dir(path) {
2727
- const parts = split(path);
2728
- let from = parts.length;
3066
+ const parts2 = split(path);
3067
+ let from = parts2.length;
2729
3068
  let cached;
2730
3069
  for (; from > 0 && !cached; from--)
2731
- cached = this.dirs.get(`/${parts.slice(0, from).join("/")}`);
3070
+ cached = this.dirs.get(`/${parts2.slice(0, from).join("/")}`);
2732
3071
  if (cached) from++;
2733
- const at = `/${parts.slice(0, from).join("/")}`;
3072
+ const at = `/${parts2.slice(0, from).join("/")}`;
2734
3073
  if (cached && !await this.current(at, cached)) {
2735
3074
  this.forget(at);
2736
3075
  cached = void 0;
2737
3076
  from = 0;
2738
3077
  }
2739
3078
  try {
2740
- return await this.walk(parts, from, cached ?? this.root);
3079
+ return await this.walk(parts2, from, cached ?? this.root);
2741
3080
  } catch (err) {
2742
3081
  throw translate(err, path);
2743
3082
  }
@@ -2767,10 +3106,10 @@ var OpfsFs = class {
2767
3106
  return kind === "file" ? parent.getFileHandle(name2) : parent.getDirectoryHandle(name2);
2768
3107
  }
2769
3108
  async parent(path) {
2770
- const parts = split(path);
2771
- const name2 = parts.pop();
3109
+ const parts2 = split(path);
3110
+ const name2 = parts2.pop();
2772
3111
  if (name2 === void 0) throw fsError("EBUSY", path);
2773
- return [`/${parts.join("/")}`, name2];
3112
+ return [`/${parts2.join("/")}`, name2];
2774
3113
  }
2775
3114
  async handle(path) {
2776
3115
  if (split(path).length === 0) return this.root;
@@ -2797,13 +3136,13 @@ var OpfsFs = class {
2797
3136
  async linkAlong(path) {
2798
3137
  for (const entry of await this.meta.get(prefixes(path))) {
2799
3138
  if (entry?.link === void 0) continue;
2800
- if (!await this.real(entry.path) && await this.real(parentOf2(entry.path))) return entry;
3139
+ if (!await this.real(entry.path) && await this.real(parentOf3(entry.path))) return entry;
2801
3140
  }
2802
3141
  return void 0;
2803
3142
  }
2804
3143
  async locate(path, follow2) {
2805
3144
  let current = path;
2806
- for (let hops = 0; hops <= MAX_LINKS2; hops++) {
3145
+ for (let hops = 0; hops <= MAX_LINKS4; hops++) {
2807
3146
  try {
2808
3147
  return { path: current, handle: await this.handle(current) };
2809
3148
  } catch (err) {
@@ -2812,7 +3151,7 @@ var OpfsFs = class {
2812
3151
  if (!link2) throw err;
2813
3152
  if (link2.path === current && !follow2) return { path: current, link: link2 };
2814
3153
  const target = `${link2.link}${current.slice(link2.path.length)}`;
2815
- current = target.split("/").includes("..") ? await this.physical(parentOf2(link2.path), target, hops + 1) : resolvePath(parentOf2(link2.path), target);
3154
+ current = target.split("/").includes("..") ? await this.physical(parentOf3(link2.path), target, hops + 1) : resolvePath(parentOf3(link2.path), target);
2816
3155
  }
2817
3156
  }
2818
3157
  throw fsError("ELOOP", path);
@@ -2829,7 +3168,7 @@ var OpfsFs = class {
2829
3168
  const st = await this.lstat(at);
2830
3169
  if (!st.isSymbolicLink && !st.isDirectory) throw fsError("ENOTDIR", at);
2831
3170
  if (!st.isSymbolicLink) done4.push(part);
2832
- else if (++hops > MAX_LINKS2) throw fsError("ELOOP", path);
3171
+ else if (++hops > MAX_LINKS4) throw fsError("ELOOP", path);
2833
3172
  else {
2834
3173
  const link2 = await this.readlink(at);
2835
3174
  if (link2.startsWith("/")) done4 = [];
@@ -2972,19 +3311,19 @@ var OpfsFs = class {
2972
3311
  }
2973
3312
  async writeSession(path, truncate) {
2974
3313
  const handle = (await this.fileFor(path))[0];
2975
- const writable = await this.retrying(
3314
+ const writable2 = await this.retrying(
2976
3315
  path,
2977
3316
  () => handle.createWritable({ keepExistingData: !truncate }),
2978
3317
  true
2979
3318
  );
2980
3319
  return {
2981
- write: (at, bytes) => writable.write({ type: "write", position: at, data: bytes }).catch((err) => {
3320
+ write: (at, bytes) => writable2.write({ type: "write", position: at, data: bytes }).catch((err) => {
2982
3321
  throw translate(err, path, "write", false);
2983
3322
  }),
2984
- truncate: (size) => writable.truncate(size).catch((err) => {
3323
+ truncate: (size) => writable2.truncate(size).catch((err) => {
2985
3324
  throw translate(err, path, "write", false);
2986
3325
  }),
2987
- close: () => writable.close().catch((err) => {
3326
+ close: () => writable2.close().catch((err) => {
2988
3327
  throw translate(err, path, "commit", false);
2989
3328
  })
2990
3329
  };
@@ -2999,8 +3338,8 @@ var OpfsFs = class {
2999
3338
  if (!missing(err)) throw err;
3000
3339
  }
3001
3340
  const [entry] = await this.meta.get([path]);
3002
- const wanted = entry?.link === void 0 ? path : resolvePath(parentOf2(path), entry.link);
3003
- return [resolvePath((await this.locate(parentOf2(wanted), true)).path, baseOf(wanted)), false];
3341
+ const wanted = entry?.link === void 0 ? path : resolvePath(parentOf3(path), entry.link);
3342
+ return [resolvePath((await this.locate(parentOf3(wanted), true)).path, baseOf(wanted)), false];
3004
3343
  }
3005
3344
  async fileFor(path) {
3006
3345
  const [target, existed] = await this.writeTarget(path);
@@ -3010,7 +3349,7 @@ var OpfsFs = class {
3010
3349
  dir,
3011
3350
  (parent) => parent.getFileHandle(name2, { create: true })
3012
3351
  );
3013
- if (!existed) this.touch(parentOf2(target));
3352
+ if (!existed) this.touch(parentOf3(target));
3014
3353
  return [handle, existed, target];
3015
3354
  } catch (err) {
3016
3355
  const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
@@ -3022,9 +3361,9 @@ var OpfsFs = class {
3022
3361
  if (!existed) await this.stampNew(target, owner, false);
3023
3362
  if (!existed && content.length === 0) return;
3024
3363
  try {
3025
- const writable = await file.createWritable();
3026
- await writable.write(content);
3027
- await writable.close();
3364
+ const writable2 = await file.createWritable();
3365
+ await writable2.write(content);
3366
+ await writable2.close();
3028
3367
  } catch (err) {
3029
3368
  throw translate(err, path, "commit", false);
3030
3369
  }
@@ -3080,9 +3419,9 @@ var OpfsFs = class {
3080
3419
  gid: owner.gid,
3081
3420
  ...mode ? { mode, parents: mode } : {}
3082
3421
  };
3083
- await this.mkdir(parentOf2(path), { recursive: true, ...above ? { owner: above } : {} });
3422
+ await this.mkdir(parentOf3(path), { recursive: true, ...above ? { owner: above } : {} });
3084
3423
  }
3085
- const parent = await this.locate(parentOf2(path), true);
3424
+ const parent = await this.locate(parentOf3(path), true);
3086
3425
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
3087
3426
  await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
3088
3427
  const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
@@ -3111,7 +3450,7 @@ var OpfsFs = class {
3111
3450
  this.changed(found.path);
3112
3451
  }
3113
3452
  await this.meta.remove([found.path]);
3114
- this.touch(parentOf2(found.path));
3453
+ this.touch(parentOf3(found.path));
3115
3454
  }
3116
3455
  links() {
3117
3456
  return false;
@@ -3123,7 +3462,7 @@ var OpfsFs = class {
3123
3462
  if (from === to) return;
3124
3463
  const source = await this.locate(from, false);
3125
3464
  this.forget(source.path);
3126
- const parent = await this.locate(parentOf2(to), true);
3465
+ const parent = await this.locate(parentOf3(to), true);
3127
3466
  const destination = resolvePath(parent.path, baseOf(to));
3128
3467
  const sourceStat = await this.describe(source);
3129
3468
  if (sourceStat.isDirectory && within(destination, source.path)) throw fsError("EINVAL", to);
@@ -3151,7 +3490,7 @@ var OpfsFs = class {
3151
3490
  if (source.link) {
3152
3491
  if (target?.handle) await this.rm(destination);
3153
3492
  await this.meta.move(source.path, destination, carry);
3154
- this.touch(parentOf2(source.path), parent.path);
3493
+ this.touch(parentOf3(source.path), parent.path);
3155
3494
  return;
3156
3495
  }
3157
3496
  const dir = parent.handle;
@@ -3173,7 +3512,7 @@ var OpfsFs = class {
3173
3512
  }
3174
3513
  if (copied) await this.restamp(destination, timed2);
3175
3514
  this.changed(source.path, destination);
3176
- this.touch(parentOf2(source.path), parent.path);
3515
+ this.touch(parentOf3(source.path), parent.path);
3177
3516
  }
3178
3517
  async modified(path) {
3179
3518
  const handle = await this.handle(path);
@@ -3210,16 +3549,16 @@ var OpfsFs = class {
3210
3549
  }
3211
3550
  async copy(handle, dir, name2) {
3212
3551
  if (handle.kind === "file") {
3213
- const writable = await (await dir.getFileHandle(name2, { create: true })).createWritable();
3214
- await writable.write(await handle.getFile());
3215
- await writable.close();
3552
+ const writable2 = await (await dir.getFileHandle(name2, { create: true })).createWritable();
3553
+ await writable2.write(await handle.getFile());
3554
+ await writable2.close();
3216
3555
  return;
3217
3556
  }
3218
3557
  const target = await dir.getDirectoryHandle(name2, { create: true });
3219
3558
  for await (const child of handle.values()) await this.copy(child, target, child.name);
3220
3559
  }
3221
3560
  async symlink(target, path, owner) {
3222
- const parent = await this.locate(parentOf2(path), true);
3561
+ const parent = await this.locate(parentOf3(path), true);
3223
3562
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
3224
3563
  const at = resolvePath(parent.path, baseOf(path));
3225
3564
  if (await this.locate(at, false).then(
@@ -3295,7 +3634,7 @@ var OpfsFs = class {
3295
3634
  let removed = 0;
3296
3635
  for (const entry of await this.meta.all()) {
3297
3636
  const real = await this.real(entry.path);
3298
- const stale = entry.link === void 0 ? !real : real || !await this.real(parentOf2(entry.path));
3637
+ const stale = entry.link === void 0 ? !real : real || !await this.real(parentOf3(entry.path));
3299
3638
  if (!stale) continue;
3300
3639
  await this.meta.update(entry.path, () => void 0);
3301
3640
  removed++;
@@ -3366,7 +3705,7 @@ function webLocks() {
3366
3705
  return globalThis.navigator?.locks;
3367
3706
  }
3368
3707
  var within2 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
3369
- var parentOf3 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
3708
+ var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
3370
3709
  var lockName = (instance) => `slicc-unlinked:${instance}`;
3371
3710
  var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
3372
3711
  var PathGate = class {
@@ -3497,7 +3836,7 @@ var UnlinkedKeeper = class {
3497
3836
  await this.serial(async () => {
3498
3837
  await Promise.allSettled([this.fs.rename(file.hidden, path)]);
3499
3838
  this.unlinkedName(file.hidden);
3500
- await this.tidy(parentOf3(file.hidden));
3839
+ await this.tidy(parentOf4(file.hidden));
3501
3840
  });
3502
3841
  }
3503
3842
  done4();
@@ -3537,7 +3876,7 @@ var UnlinkedKeeper = class {
3537
3876
  }
3538
3877
  await this.fs.rm(hidden).catch(() => void 0);
3539
3878
  this.unlinkedName(hidden);
3540
- await this.tidy(parentOf3(hidden));
3879
+ await this.tidy(parentOf4(hidden));
3541
3880
  }
3542
3881
  async linkedName(hidden) {
3543
3882
  if (!mayLink(this.fs, hidden)) return;
@@ -4547,7 +4886,7 @@ function binfmtOf(binfmts, path) {
4547
4886
  var S_IFDIR2 = 16384;
4548
4887
  var S_IFREG2 = 32768;
4549
4888
  var COMMAND = /^\/(?:usr\/)?bin\/([^/]+)$/;
4550
- var MAX_LINKS3 = 40;
4889
+ var MAX_LINKS5 = 40;
4551
4890
  function missing2(err) {
4552
4891
  return err?.code === "ENOENT";
4553
4892
  }
@@ -4567,7 +4906,7 @@ function synthetic(directory, path) {
4567
4906
  }
4568
4907
  async function followLinks(fs, path) {
4569
4908
  let current = path;
4570
- for (let hops = 0; hops < MAX_LINKS3; hops++) {
4909
+ for (let hops = 0; hops < MAX_LINKS5; hops++) {
4571
4910
  const st = await fs.lstat(current).catch(() => void 0);
4572
4911
  if (!st?.isSymbolicLink) return current;
4573
4912
  const parent = current.slice(0, current.lastIndexOf("/")) || "/";
@@ -4674,6 +5013,7 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
4674
5013
  readlink: (path) => fs.readlink(path),
4675
5014
  ...fs.realpath ? { realpath: fs.realpath.bind(fs) } : {},
4676
5015
  ...fs.chown ? { chown: fs.chown.bind(fs) } : {},
5016
+ ...fs.ancestry ? { ancestry: fs.ancestry.bind(fs) } : {},
4677
5017
  chmod: (path, mode) => fs.chmod(path, mode),
4678
5018
  utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
4679
5019
  lutimes: (path, atime, mtime) => lutimesOf(fs, path, atime, mtime),
@@ -4686,12 +5026,12 @@ var MAX_HOPS = 40;
4686
5026
  function start(path) {
4687
5027
  return { done: [], todo: path.split("/").filter((p) => p !== ""), hops: 0 };
4688
5028
  }
4689
- function next(walk) {
4690
- while (walk.todo.length > 0) {
4691
- const part = walk.todo.shift();
5029
+ function next(walk2) {
5030
+ while (walk2.todo.length > 0) {
5031
+ const part = walk2.todo.shift();
4692
5032
  if (part === ".") continue;
4693
5033
  if (part === "..") {
4694
- walk.done.pop();
5034
+ walk2.done.pop();
4695
5035
  continue;
4696
5036
  }
4697
5037
  return part;
@@ -4699,42 +5039,42 @@ function next(walk) {
4699
5039
  return void 0;
4700
5040
  }
4701
5041
  function lexical(path) {
4702
- const walk = start(path);
4703
- for (let part = next(walk); part !== void 0; part = next(walk)) walk.done.push(part);
4704
- return `/${walk.done.join("/")}`;
5042
+ const walk2 = start(path);
5043
+ for (let part = next(walk2); part !== void 0; part = next(walk2)) walk2.done.push(part);
5044
+ return `/${walk2.done.join("/")}`;
4705
5045
  }
4706
- function rest(walk, part) {
4707
- return lexical(`/${[...walk.done, part, ...walk.todo].join("/")}`);
5046
+ function rest(walk2, part) {
5047
+ return lexical(`/${[...walk2.done, part, ...walk2.todo].join("/")}`);
4708
5048
  }
4709
- function follow(walk, target) {
4710
- if (++walk.hops > MAX_HOPS) return false;
4711
- if (target.startsWith("/")) walk.done.length = 0;
4712
- walk.todo = [...target.split("/").filter((p) => p !== ""), ...walk.todo];
5049
+ function follow(walk2, target) {
5050
+ if (++walk2.hops > MAX_HOPS) return false;
5051
+ if (target.startsWith("/")) walk2.done.length = 0;
5052
+ walk2.todo = [...target.split("/").filter((p) => p !== ""), ...walk2.todo];
4713
5053
  return true;
4714
5054
  }
4715
5055
  async function physicalPathAsync(fs, path) {
4716
- const walk = start(path);
4717
- for (let part = next(walk); part !== void 0; part = next(walk)) {
4718
- const at = `/${[...walk.done, part].join("/")}`;
5056
+ const walk2 = start(path);
5057
+ for (let part = next(walk2); part !== void 0; part = next(walk2)) {
5058
+ const at = `/${[...walk2.done, part].join("/")}`;
4719
5059
  let target;
4720
5060
  try {
4721
5061
  target = (await fs.lstat(at)).isSymbolicLink ? await fs.readlink(at) : void 0;
4722
5062
  } catch {
4723
- return rest(walk, part);
5063
+ return rest(walk2, part);
4724
5064
  }
4725
- if (target === void 0) walk.done.push(part);
4726
- else if (!follow(walk, target)) return lexical(path);
5065
+ if (target === void 0) walk2.done.push(part);
5066
+ else if (!follow(walk2, target)) return lexical(path);
4727
5067
  }
4728
- return `/${walk.done.join("/")}`;
5068
+ return `/${walk2.done.join("/")}`;
4729
5069
  }
4730
5070
 
4731
5071
  // src/fs/watch.ts
4732
5072
  var WATCH_BATCH_LIMIT = 1e3;
4733
5073
  var under2 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
4734
- var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
5074
+ var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
4735
5075
  function touches(watcher, changed) {
4736
5076
  return watcher.paths.some(
4737
- (watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf4(changed) === watched)
5077
+ (watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf5(changed) === watched)
4738
5078
  );
4739
5079
  }
4740
5080
  var FsWatchers = class {
@@ -4783,9 +5123,9 @@ var FsWatchers = class {
4783
5123
  mkdir: async (path, options) => {
4784
5124
  const created3 = [];
4785
5125
  if (options?.recursive) {
4786
- const parts = normalizePath(path).split("/").filter(Boolean);
4787
- for (let at = 1; at < parts.length; at++) {
4788
- const ancestor = `/${parts.slice(0, at).join("/")}`;
5126
+ const parts2 = normalizePath(path).split("/").filter(Boolean);
5127
+ for (let at = 1; at < parts2.length; at++) {
5128
+ const ancestor = `/${parts2.slice(0, at).join("/")}`;
4789
5129
  if (created3.length > 0 || !await fs.exists(ancestor)) created3.push(ancestor);
4790
5130
  }
4791
5131
  }
@@ -5014,62 +5354,6 @@ var ChildTable = class {
5014
5354
  }
5015
5355
  };
5016
5356
 
5017
- // src/fs/walk.ts
5018
- var MAX_LINKS4 = 40;
5019
- function parentOf5(path) {
5020
- return path.slice(0, path.lastIndexOf("/")) || "/";
5021
- }
5022
- async function mustBeDir(fs, path) {
5023
- if (!fs.lstat) return;
5024
- const st = await fs.lstat(path);
5025
- if (st.isDirectory !== true) throw fsError("ENOTDIR", path);
5026
- }
5027
- async function linkOf(fs, path) {
5028
- return fs.readlink?.(path).catch(() => void 0);
5029
- }
5030
- async function realDir(fs, dir, budget = { hops: 0 }) {
5031
- const queue = dir.split("/").filter(Boolean);
5032
- let at = "/";
5033
- while (queue.length > 0) {
5034
- const part = queue.shift();
5035
- if (part === ".") continue;
5036
- if (part === "..") {
5037
- at = parentOf5(at);
5038
- continue;
5039
- }
5040
- const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
5041
- const link2 = await linkOf(fs, next2);
5042
- if (link2 === void 0) {
5043
- if (queue.find((p) => p !== ".") === "..") await mustBeDir(fs, next2);
5044
- at = next2;
5045
- continue;
5046
- }
5047
- if (budget.hops >= MAX_LINKS4) throw fsError("ELOOP", dir);
5048
- budget.hops++;
5049
- queue.unshift(...link2.split("/").filter(Boolean));
5050
- if (link2.startsWith("/")) at = "/";
5051
- }
5052
- return at;
5053
- }
5054
- function joinName(dir, name2) {
5055
- return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
5056
- }
5057
- async function realTarget(fs, path) {
5058
- const budget = { hops: 0 };
5059
- let dir = await realDir(fs, parentOf5(path), budget);
5060
- let name2 = path.slice(path.lastIndexOf("/") + 1);
5061
- for (; ; ) {
5062
- const link2 = await linkOf(fs, joinName(dir, name2));
5063
- if (link2 === void 0) break;
5064
- if (budget.hops >= MAX_LINKS4) throw fsError("ELOOP", path);
5065
- budget.hops++;
5066
- const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
5067
- dir = await realDir(fs, parentOf5(target), budget);
5068
- name2 = target.slice(target.lastIndexOf("/") + 1);
5069
- }
5070
- return joinName(dir, name2);
5071
- }
5072
-
5073
5357
  // src/mount/protocol.ts
5074
5358
  var DRIVER_PROTOCOL = [1, 2];
5075
5359
  function driverVersionError(theirs) {
@@ -5132,8 +5416,8 @@ var DriverConnection = class _DriverConnection {
5132
5416
  } finally {
5133
5417
  clearTimeout(timer);
5134
5418
  }
5135
- const refused = driverVersionError(hello.protocol);
5136
- if (refused) throw errnoError("EPROTO", refused);
5419
+ const refused2 = driverVersionError(hello.protocol);
5420
+ if (refused2) throw errnoError("EPROTO", refused2);
5137
5421
  if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
5138
5422
  return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options.onFail);
5139
5423
  }
@@ -5173,7 +5457,7 @@ var DriverConnection = class _DriverConnection {
5173
5457
  };
5174
5458
 
5175
5459
  // src/mount/mount-fs.ts
5176
- var S_ISGID3 = 1024;
5460
+ var S_ISGID4 = 1024;
5177
5461
  var ID = /^\d{1,10}$/;
5178
5462
  var MAX_ID2 = 4294967294;
5179
5463
  function idOption(value) {
@@ -5186,7 +5470,7 @@ function checkIds(options) {
5186
5470
  if (!ID.test(value) || Number(value) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value}`);
5187
5471
  }
5188
5472
  }
5189
- var MAX_LINKS5 = 40;
5473
+ var MAX_LINKS6 = 40;
5190
5474
  var STALE_RETRIES = 3;
5191
5475
  var SETTLE_ROUNDS = 4;
5192
5476
  var DEFAULT_IO = 8 * 1024 * 1024;
@@ -5424,12 +5708,12 @@ var MountTable = class {
5424
5708
  }
5425
5709
  list() {
5426
5710
  const mounted = [...this.mounts.values()].map(
5427
- ({ type, source, target, options, state, error, present: present2 }) => ({
5711
+ ({ type, source, target, options, state, error, present: present3 }) => ({
5428
5712
  type,
5429
5713
  source,
5430
5714
  target,
5431
5715
  options: { ...options },
5432
- state: state === "ok" && present2?.() === false ? "nomedium" : state,
5716
+ state: state === "ok" && present3?.() === false ? "nomedium" : state,
5433
5717
  ...error ? { error } : {}
5434
5718
  })
5435
5719
  );
@@ -5704,7 +5988,7 @@ var MountFs = class {
5704
5988
  throw err;
5705
5989
  }
5706
5990
  if (attr2.kind !== "symlink") return real;
5707
- if (hops >= MAX_LINKS5) throw errnoError("ELOOP", path);
5991
+ if (hops >= MAX_LINKS6) throw errnoError("ELOOP", path);
5708
5992
  const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
5709
5993
  return this.follow(this.base.resolvePath(parentOf6(real), target), hops + 1, create);
5710
5994
  }
@@ -5724,9 +6008,9 @@ var MountFs = class {
5724
6008
  if (!owner || !mount.owners || !mount.caps.owners) return void 0;
5725
6009
  const dir = parentOf6(real);
5726
6010
  const above = statOf(await this.table.getattr(mount, dir, parentOf6(rel2)), dir, mount);
5727
- const setgid = (above.mode & S_ISGID3) !== 0;
6011
+ const setgid = (above.mode & S_ISGID4) !== 0;
5728
6012
  let mode = owner.mode;
5729
- if (directory && setgid) mode = (mode ?? 493) | S_ISGID3;
6013
+ if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
5730
6014
  return {
5731
6015
  uid: owner.uid,
5732
6016
  gid: setgid ? above.gid : owner.gid,
@@ -6001,14 +6285,14 @@ var MountFs = class {
6001
6285
  await mount.conn.call({ op: "mkdir", path: rel2, ...made2 });
6002
6286
  return;
6003
6287
  }
6004
- const parts = rel2.split("/").filter(Boolean);
6005
- for (let n = 1; n <= parts.length; n++) {
6006
- const sub = `/${parts.slice(0, n).join("/")}`;
6288
+ const parts2 = rel2.split("/").filter(Boolean);
6289
+ for (let n = 1; n <= parts2.length; n++) {
6290
+ const sub = `/${parts2.slice(0, n).join("/")}`;
6007
6291
  const full = normalizePath(`${mount.target}${sub}`);
6008
6292
  const exists = await this.table.getattr(mount, full, sub).catch(() => void 0);
6009
6293
  if (exists?.kind === "directory") continue;
6010
6294
  if (exists) throw errnoError("ENOTDIR", full);
6011
- const own = n === parts.length ? owner : owner && { ...owner, mode: owner.parents };
6295
+ const own = n === parts2.length ? owner : owner && { ...owner, mode: owner.parents };
6012
6296
  const made2 = await this.driverOwner({ mount, rel: sub }, full, own, true);
6013
6297
  this.table.forget(mount, full);
6014
6298
  await mount.conn.call({ op: "mkdir", path: sub, ...made2 });
@@ -6101,6 +6385,20 @@ var MountFs = class {
6101
6385
  if (this.at(real)) throw errnoError("ENOSYS", path);
6102
6386
  return real;
6103
6387
  }
6388
+ async ancestry(path) {
6389
+ const names = chain(normalizePath(path));
6390
+ const first = names.findIndex((name2) => this.at(name2));
6391
+ if (first < 0) return ancestryOf(this.base, path);
6392
+ const out = first === 0 ? [] : await ancestryOf(this.base, names[first - 1]);
6393
+ if (out.length < first || out.some((perm) => perm?.link !== void 0)) return out;
6394
+ for (const name2 of names.slice(first)) {
6395
+ const st = await this.lstat(name2).catch(() => void 0);
6396
+ if (!st) break;
6397
+ out.push(permOf(st, st.isSymbolicLink ? await this.readlink(name2) : void 0));
6398
+ if (st.isSymbolicLink) break;
6399
+ }
6400
+ return out;
6401
+ }
6104
6402
  async readlink(path) {
6105
6403
  const found = this.at(path);
6106
6404
  if (!found) return this.base.readlink(path);
@@ -6311,9 +6609,11 @@ async function run(entry, path, req) {
6311
6609
  return done;
6312
6610
  case "readlink":
6313
6611
  return json2(await fs.readlink(path));
6314
- case "chmod":
6315
- await fs.chmod(path, req.mode ?? 0);
6612
+ case "chmod": {
6613
+ const g = guardOf(entry);
6614
+ await fs.chmod(path, g ? await chmodMode(g, path, req.mode ?? 0) : req.mode ?? 0);
6316
6615
  return done;
6616
+ }
6317
6617
  case "utimes":
6318
6618
  await fs.utimes(path, new Date(req.atimeMs ?? 0), new Date(req.mtimeMs ?? 0));
6319
6619
  return done;
@@ -6331,6 +6631,53 @@ async function run(entry, path, req) {
6331
6631
  return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
6332
6632
  }
6333
6633
  }
6634
+ function guardOf(entry) {
6635
+ const cred = entry.dac && entry.cred?.();
6636
+ if (!cred || !entry.dac) return void 0;
6637
+ return { fs: entry.fs, cred, pid: entry.pid ?? 0, dac: entry.dac };
6638
+ }
6639
+ var FOLLOWING = /* @__PURE__ */ new Set(["exists", "stat", "chown", "statfs"]);
6640
+ var NOT_FOLLOWING = /* @__PURE__ */ new Set(["lstat", "readlink", "lchown"]);
6641
+ async function guard(g, path, req, cwd) {
6642
+ const { op } = req;
6643
+ if (FOLLOWING.has(op) || NOT_FOLLOWING.has(op)) return searchable(g, op, path, FOLLOWING.has(op));
6644
+ const other = () => g.fs.resolvePath(cwd, req.arg2 ?? "");
6645
+ switch (op) {
6646
+ case "readdir":
6647
+ case "readdir-stat":
6648
+ return listable(g, op, path);
6649
+ case "mkdir":
6650
+ return makeIn(g, op, path);
6651
+ case "create":
6652
+ case "symlink":
6653
+ return inParent(g, op, path);
6654
+ case "unlink":
6655
+ case "rmdir":
6656
+ return inParent(g, op, path, true);
6657
+ case "rm":
6658
+ return removable(g, op, path);
6659
+ case "rename":
6660
+ return renamable(g, path, other());
6661
+ case "link":
6662
+ return linkable(g, path, other(), req.mode === 1);
6663
+ case "utimes":
6664
+ case "lutimes":
6665
+ return retimable(g, op, path);
6666
+ default:
6667
+ return void 0;
6668
+ }
6669
+ }
6670
+ async function guarded(entry, path, req) {
6671
+ const g = guardOf(entry);
6672
+ if (!g) return void 0;
6673
+ try {
6674
+ await guard(g, path, req, entry.cwd);
6675
+ return void 0;
6676
+ } catch (err) {
6677
+ const refusal = err.code === "EACCES";
6678
+ return req.op === "exists" && refusal ? json2(false) : toErrno(err);
6679
+ }
6680
+ }
6334
6681
  async function createExclusive(fs, path, owner) {
6335
6682
  if (fs.createExclusive) return fs.createExclusive(path, owner);
6336
6683
  if (await fs.lstat(path).then(
@@ -6383,6 +6730,16 @@ async function owned(entry, own, req, path, target) {
6383
6730
  }
6384
6731
  return own(target, () => run(entry, path, req));
6385
6732
  }
6733
+ async function holdOp(entry, path, req) {
6734
+ const mode = req.mode ?? 0;
6735
+ const [on, open] = [(mode & 1) === 1, (mode & 2) === 2];
6736
+ if (on && open) {
6737
+ const real = await entry.fs.realpath?.(path, true).catch(() => path);
6738
+ return json2(entry.hold?.(path, on, open, real) ?? null);
6739
+ }
6740
+ entry.hold?.(path, on, open, void 0, req.handle);
6741
+ return done;
6742
+ }
6386
6743
  async function dispatchSyncFs(req) {
6387
6744
  const entry = resolveSyncFsToken(req.token);
6388
6745
  if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
@@ -6391,16 +6748,10 @@ async function dispatchSyncFs(req) {
6391
6748
  const path = fs.resolvePath(cwd, req.path);
6392
6749
  if (req.op === "kept") return json2(entry.keptFor?.(req.handle ?? -1) !== void 0);
6393
6750
  if (req.op === "stat" && req.handle !== void 0) return await keptStat(entry, req);
6751
+ if (req.op === "hold") return holdOp(entry, path, req);
6752
+ const refused2 = await guarded(entry, path, req);
6753
+ if (refused2) return refused2;
6394
6754
  if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
6395
- if (req.op === "hold") {
6396
- const [on, open] = [((req.mode ?? 0) & 1) === 1, ((req.mode ?? 0) & 2) === 2];
6397
- if (on && open) {
6398
- const real = await fs.realpath?.(path, true).catch(() => path);
6399
- return json2(entry.hold?.(path, on, open, real) ?? null);
6400
- }
6401
- entry.hold?.(path, on, open, void 0, req.handle);
6402
- return done;
6403
- }
6404
6755
  if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };
6405
6756
  if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
6406
6757
  const serve = async () => run(entry, await keptOr(entry, path, req.handle), req);
@@ -6601,9 +6952,9 @@ function mountTable(mounts = []) {
6601
6952
  for (const m of mounts) {
6602
6953
  const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
6603
6954
  if (m.state !== "ok") options.push(m.state);
6604
- const access = options.includes("ro") ? [] : ["rw"];
6955
+ const access2 = options.includes("ro") ? [] : ["rw"];
6605
6956
  lines2.push(
6606
- `${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access, ...options].join(",")} 0 0`
6957
+ `${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
6607
6958
  );
6608
6959
  }
6609
6960
  return `${lines2.join("\n")}
@@ -7039,8 +7390,8 @@ var ProcTree = class {
7039
7390
  alive(pid) {
7040
7391
  return this.listing().processes.some((p) => p.pid === pid);
7041
7392
  }
7042
- node(parts) {
7043
- const [top, leaf, ...rest2] = parts;
7393
+ node(parts2) {
7394
+ const [top, leaf, ...rest2] = parts2;
7044
7395
  if (top === void 0) {
7045
7396
  return {
7046
7397
  kind: "dir",
@@ -7078,9 +7429,9 @@ var ProcTree = class {
7078
7429
  };
7079
7430
  }
7080
7431
  parts(path, follow2) {
7081
- const parts = path.split("/").filter((p) => p !== "").slice(1);
7082
- if (parts[0] === "self" && (follow2 || parts.length > 1)) parts[0] = String(this.pid);
7083
- return parts;
7432
+ const parts2 = path.split("/").filter((p) => p !== "").slice(1);
7433
+ if (parts2[0] === "self" && (follow2 || parts2.length > 1)) parts2[0] = String(this.pid);
7434
+ return parts2;
7084
7435
  }
7085
7436
  find(path, follow2) {
7086
7437
  const node = this.node(this.parts(path, follow2));
@@ -7166,8 +7517,8 @@ function refusalOf(found) {
7166
7517
  return found === false ? { refused: "ENOTDIR" } : found;
7167
7518
  }
7168
7519
  function throughLink(target, done4, todo) {
7169
- const parts = target.split("/").filter((part) => part !== "");
7170
- return [target.startsWith("/") ? [] : done4, [...parts, ...todo]];
7520
+ const parts2 = target.split("/").filter((part) => part !== "");
7521
+ return [target.startsWith("/") ? [] : done4, [...parts2, ...todo]];
7171
7522
  }
7172
7523
  async function procAlias(fs, path, follow2) {
7173
7524
  const { readlink, lstat } = fs;
@@ -7177,14 +7528,14 @@ async function procAlias(fs, path, follow2) {
7177
7528
  () => false
7178
7529
  );
7179
7530
  if (!physical) return void 0;
7180
- const walk = procWalk(path, follow2);
7181
- let step = walk.next();
7531
+ const walk2 = procWalk(path, follow2);
7532
+ let step = walk2.next();
7182
7533
  while (!step.done) {
7183
7534
  const { at, check } = step.value;
7184
7535
  const link2 = await readlink.call(fs, at).catch(() => void 0);
7185
7536
  const isDir = (st) => st.isDirectory === true;
7186
7537
  const target = link2 ?? (check ? await lstat.call(fs, at).then(isDir, failed) : true);
7187
- step = walk.next(target);
7538
+ step = walk2.next(target);
7188
7539
  }
7189
7540
  return step.value;
7190
7541
  }
@@ -7778,8 +8129,8 @@ var RealmProxy = class {
7778
8129
  }
7779
8130
  async connect(conn, incoming, req, idle) {
7780
8131
  const target = tunnelTarget(req);
7781
- const refused = target.port === 443 ? await this.tunnelReady(target) : void 0;
7782
- if (refused) throw refused;
8132
+ const refused2 = target.port === 443 ? await this.tunnelReady(target) : void 0;
8133
+ if (refused2) throw refused2;
7783
8134
  await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
7784
8135
  try {
7785
8136
  const first = await incoming.peek(this.stop.signal);
@@ -7922,10 +8273,10 @@ var RealmProxy = class {
7922
8273
  };
7923
8274
 
7924
8275
  // src/kernel/net/x509.ts
7925
- function concat4(parts) {
7926
- const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
8276
+ function concat4(parts2) {
8277
+ const out = new Uint8Array(parts2.reduce((n, p) => n + p.length, 0));
7927
8278
  let at = 0;
7928
- for (const p of parts) {
8279
+ for (const p of parts2) {
7929
8280
  out.set(p, at);
7930
8281
  at += p.length;
7931
8282
  }
@@ -7941,8 +8292,8 @@ function tlv(tag, ...contents2) {
7941
8292
  const body = concat4(contents2);
7942
8293
  return concat4([Uint8Array.of(tag), length(body.length), body]);
7943
8294
  }
7944
- var seq = (...parts) => tlv(48, ...parts);
7945
- var set = (...parts) => tlv(49, ...parts);
8295
+ var seq = (...parts2) => tlv(48, ...parts2);
8296
+ var set = (...parts2) => tlv(49, ...parts2);
7946
8297
  var octets = (bytes) => tlv(4, bytes);
7947
8298
  var bits = (bytes, unused = 0) => tlv(3, Uint8Array.of(unused), bytes);
7948
8299
  var TRUE = tlv(1, Uint8Array.of(255));
@@ -8030,8 +8381,8 @@ function randomSerial() {
8030
8381
  function ipv4(host) {
8031
8382
  const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
8032
8383
  if (!m) return void 0;
8033
- const parts = m.slice(1).map(Number);
8034
- return parts.every((p) => p <= 255) ? Uint8Array.from(parts) : void 0;
8384
+ const parts2 = m.slice(1).map(Number);
8385
+ return parts2.every((p) => p <= 255) ? Uint8Array.from(parts2) : void 0;
8035
8386
  }
8036
8387
  function ipv6(host) {
8037
8388
  if (!host.includes(":") || !/^[0-9A-Fa-f:.]+$/.test(host)) return void 0;
@@ -8860,9 +9211,9 @@ var HttpHandles = class {
8860
9211
  // src/kernel/net/routes.ts
8861
9212
  var HOST_ADDRESS = HOST_LOOPBACK_ADDRESS.join(".");
8862
9213
  function ipv4Bytes(text) {
8863
- const parts = text.split(".");
8864
- if (parts.length !== 4 || !parts.every((p) => /^\d{1,3}$/.test(p))) return void 0;
8865
- const bytes = parts.map(Number);
9214
+ const parts2 = text.split(".");
9215
+ if (parts2.length !== 4 || !parts2.every((p) => /^\d{1,3}$/.test(p))) return void 0;
9216
+ const bytes = parts2.map(Number);
8866
9217
  return bytes.every((b) => b <= 255) ? bytes : void 0;
8867
9218
  }
8868
9219
  function ipv6Groups(text) {
@@ -8889,9 +9240,9 @@ function addressBits(host) {
8889
9240
  return v6 ? { v6: true, bits: toBits(v6, 16) } : void 0;
8890
9241
  }
8891
9242
  function parsePrefix(text) {
8892
- const parts = text.trim().split("/");
8893
- if (parts.length > 2) return void 0;
8894
- const [address = "", size] = parts;
9243
+ const parts2 = text.trim().split("/");
9244
+ if (parts2.length > 2) return void 0;
9245
+ const [address = "", size] = parts2;
8895
9246
  const parsed2 = addressBits(address);
8896
9247
  if (!parsed2) return void 0;
8897
9248
  const max = parsed2.v6 ? 128 : 32;
@@ -9852,10 +10203,10 @@ var VfsNode = class {
9852
10203
  this.serial(() => this.flush()).catch(() => void 0);
9853
10204
  }, delay);
9854
10205
  }
9855
- ensure(need) {
10206
+ ensure(need2) {
9856
10207
  const cur = this.data ?? new Uint8Array(0);
9857
- if (cur.length >= need) return cur;
9858
- const grown = new Uint8Array(Math.max(need, cur.length * 2, 256));
10208
+ if (cur.length >= need2) return cur;
10209
+ const grown = new Uint8Array(Math.max(need2, cur.length * 2, 256));
9859
10210
  grown.set(cur.subarray(0, this.length));
9860
10211
  this.data = grown;
9861
10212
  return grown;
@@ -9898,14 +10249,14 @@ var VfsNodes = class {
9898
10249
  return run2;
9899
10250
  }
9900
10251
  async entryKey(path) {
9901
- const parent = parentOf5(path);
10252
+ const parent = parentOf(path);
9902
10253
  const dir = await this.real(parent, true, () => realDir(this.fs, parent));
9903
10254
  return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
9904
10255
  }
9905
10256
  targetKey(path) {
9906
10257
  return this.real(path, true, () => realTarget(this.fs, path));
9907
10258
  }
9908
- async real(path, follow2, walk) {
10259
+ async real(path, follow2, walk2) {
9909
10260
  if (this.fs.realpath) {
9910
10261
  try {
9911
10262
  return await this.fs.realpath(path, follow2);
@@ -9913,13 +10264,13 @@ var VfsNodes = class {
9913
10264
  if (errCode(err) === "ELOOP") throw err;
9914
10265
  }
9915
10266
  }
9916
- return walk();
10267
+ return walk2();
9917
10268
  }
9918
10269
  createExclusive(path, owner) {
9919
10270
  return this.onEntry(path, async () => {
9920
10271
  const node = this.byPath.get(path);
9921
10272
  const held = node ? await node.serial(async () => node.holds()) : false;
9922
- if (held || await present(this.fs, path)) throw fsError("EEXIST", path);
10273
+ if (held || await present2(this.fs, path)) throw fsError("EEXIST", path);
9923
10274
  await this.fs.writeFile(path, new Uint8Array(0), owner);
9924
10275
  });
9925
10276
  }
@@ -9979,13 +10330,13 @@ var VfsNodes = class {
9979
10330
  if (id2 !== void 0) this.know(node, id2);
9980
10331
  return node;
9981
10332
  }
9982
- open(path, pin, writable = false, id2) {
10333
+ open(path, pin, writable2 = false, id2) {
9983
10334
  const node = this.named(path, id2) ?? this.create(path, id2, new VfsNode(this.fs, path, void 0, false, false, pin));
9984
10335
  node.opens++;
9985
- if (writable) node.writers++;
10336
+ if (writable2) node.writers++;
9986
10337
  return node;
9987
10338
  }
9988
- adopt(path, contents2, dirty, writable = false, id2) {
10339
+ adopt(path, contents2, dirty, writable2 = false, id2) {
9989
10340
  let node = this.named(path, id2);
9990
10341
  if (!node) {
9991
10342
  node = this.create(path, id2, new VfsNode(this.fs, path, contents2, false, dirty));
@@ -9993,7 +10344,7 @@ var VfsNodes = class {
9993
10344
  node.replace(contents2);
9994
10345
  }
9995
10346
  node.opens++;
9996
- if (writable) node.writers++;
10347
+ if (writable2) node.writers++;
9997
10348
  return node;
9998
10349
  }
9999
10350
  holds(prefix) {
@@ -10005,11 +10356,11 @@ var VfsNodes = class {
10005
10356
  if (node.writers > 0 && within5(path, prefix)) return true;
10006
10357
  return false;
10007
10358
  }
10008
- closed(node, writable = false) {
10359
+ closed(node, writable2 = false) {
10009
10360
  node.opens--;
10010
- if (writable) node.writers--;
10361
+ if (writable2) node.writers--;
10011
10362
  if (node.opens > 0) {
10012
- if (writable && node.writers === 0) this.onClosed?.(node.path);
10363
+ if (writable2 && node.writers === 0) this.onClosed?.(node.path);
10013
10364
  return;
10014
10365
  }
10015
10366
  this.forget(node);
@@ -10086,7 +10437,7 @@ var VfsNodes = class {
10086
10437
  }
10087
10438
  }
10088
10439
  };
10089
- async function present(fs, path, follow2 = false) {
10440
+ async function present2(fs, path, follow2 = false) {
10090
10441
  const look = (follow2 ? void 0 : fs.lstat) ?? fs.stat;
10091
10442
  try {
10092
10443
  await (look ? look.call(fs, path) : fs.readFileBuffer(path));
@@ -10100,7 +10451,7 @@ function errCode(err) {
10100
10451
  return err?.code;
10101
10452
  }
10102
10453
  async function targetDir(fs, path) {
10103
- return parentOf5(await realTarget(fs, path).catch(() => path));
10454
+ return parentOf(await realTarget(fs, path).catch(() => path));
10104
10455
  }
10105
10456
  async function refuseReadonly(fs, path, flags, opts = {}) {
10106
10457
  const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
@@ -10150,10 +10501,10 @@ function keepingOpen(fs, nodes) {
10150
10501
  };
10151
10502
  }
10152
10503
  function vfsFile(fs, opts, nodes) {
10153
- const access = opts.flags & O_ACCMODE;
10154
- const readable = access !== O_WRONLY;
10155
- const writable = access === O_WRONLY || access === O_RDWR;
10156
- const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true, writable, opts.id) : nodes.open(opts.path, opts.pin, writable, opts.id);
10504
+ const access2 = opts.flags & O_ACCMODE;
10505
+ const readable2 = access2 !== O_WRONLY;
10506
+ const writable2 = access2 === O_WRONLY || access2 === O_RDWR;
10507
+ const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true, writable2, opts.id) : nodes.open(opts.path, opts.pin, writable2, opts.id);
10157
10508
  let openError;
10158
10509
  const atOpen = (op) => {
10159
10510
  void node.serial(op).catch((err) => {
@@ -10174,12 +10525,12 @@ function vfsFile(fs, opts, nodes) {
10174
10525
  return op();
10175
10526
  });
10176
10527
  return new OpenFile({
10177
- read: readable ? (max) => serial(async () => {
10528
+ read: readable2 ? (max) => serial(async () => {
10178
10529
  const out = await node.pread(max, offset);
10179
10530
  offset += out.length;
10180
10531
  return out;
10181
10532
  }) : void 0,
10182
- write: writable ? (bytes) => serial(async () => {
10533
+ write: writable2 ? (bytes) => serial(async () => {
10183
10534
  if (opts.flags & O_APPEND) offset = await node.size();
10184
10535
  offset += await node.pwrite(bytes, offset);
10185
10536
  return bytes.length;
@@ -10194,15 +10545,15 @@ function vfsFile(fs, opts, nodes) {
10194
10545
  return offset;
10195
10546
  }),
10196
10547
  pread: (max, at) => serial(() => {
10197
- if (!readable) throw new KernelError("EBADF");
10548
+ if (!readable2) throw new KernelError("EBADF");
10198
10549
  return node.pread(max, at);
10199
10550
  }),
10200
10551
  pwrite: (bytes, at) => serial(() => {
10201
- if (!writable) throw new KernelError("EBADF");
10552
+ if (!writable2) throw new KernelError("EBADF");
10202
10553
  return node.pwrite(bytes, at);
10203
10554
  }),
10204
10555
  resize: (size) => serial(() => {
10205
- if (!writable) throw new KernelError("EBADF");
10556
+ if (!writable2) throw new KernelError("EBADF");
10206
10557
  return node.truncate(size);
10207
10558
  }),
10208
10559
  stat: () => serial(async () => ({
@@ -10216,7 +10567,7 @@ function vfsFile(fs, opts, nodes) {
10216
10567
  takeOpenError();
10217
10568
  await node.flush();
10218
10569
  } finally {
10219
- nodes?.closed(node, writable);
10570
+ nodes?.closed(node, writable2);
10220
10571
  }
10221
10572
  })
10222
10573
  });
@@ -10587,7 +10938,7 @@ var WasmProcess = class {
10587
10938
  this.fds.installAt(fd, this.openVfsFile(open));
10588
10939
  } else if (req.create) {
10589
10940
  await nodes.onEntry(path, async () => {
10590
- const fresh = this.options.owners && !await present(this.options.fs, path, true);
10941
+ const fresh = this.options.owners && !await present2(this.options.fs, path, true);
10591
10942
  this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
10592
10943
  await this.fds.get(fd).file.stat?.();
10593
10944
  if (fresh) await this.options.fs.chown?.(path, { ...this.newOwner(), kind: "new" });
@@ -10595,7 +10946,7 @@ var WasmProcess = class {
10595
10946
  } else {
10596
10947
  this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path)));
10597
10948
  }
10598
- if (req.existing && !await present(this.options.fs, path, true)) {
10949
+ if (req.existing && !await present2(this.options.fs, path, true)) {
10599
10950
  throw new KernelError("ENOENT");
10600
10951
  }
10601
10952
  } catch (err) {
@@ -10615,14 +10966,14 @@ var WasmProcess = class {
10615
10966
  if (req.flags & WRITE_ACCESS || req.create || req.truncate || req.exclusive) {
10616
10967
  throw new KernelError("EROFS");
10617
10968
  }
10618
- const tree = new ProcTree(
10969
+ const tree2 = new ProcTree(
10619
10970
  {
10620
10971
  procList: () => this.options.processes?.() ?? { boot: 0, processes: [] },
10621
10972
  mountList: () => this.options.mounts?.() ?? []
10622
10973
  },
10623
10974
  (await this.options.identity?.())?.pid ?? this.pid
10624
10975
  );
10625
- return memoryFile(tree.read(path), path, req.position);
10976
+ return memoryFile(tree2.read(path), path, req.position);
10626
10977
  }
10627
10978
  openVfsFile(req, id2) {
10628
10979
  return vfsFile(
@@ -10774,7 +11125,7 @@ var WasmProcess = class {
10774
11125
  const held = handle === void 0 ? void 0 : this.keptHandle?.(handle);
10775
11126
  if (held !== void 0) return held;
10776
11127
  const kept = this.keptPath?.(path);
10777
- return kept !== void 0 && !await present(this.options.fs, path, true) ? kept : path;
11128
+ return kept !== void 0 && !await present2(this.options.fs, path, true) ? kept : path;
10778
11129
  }
10779
11130
  async promote(req) {
10780
11131
  if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
@@ -11284,6 +11635,7 @@ function inherited(opts) {
11284
11635
  function holdingToken(opts, holds, caller) {
11285
11636
  return mintSyncFsToken({
11286
11637
  ...opts.owners ? caller : {},
11638
+ ...opts.dac ? { dac: opts.dac, pid: opts.pid } : {},
11287
11639
  fs: opts.fs,
11288
11640
  cwd: opts.cwd,
11289
11641
  ...opts.statfs ? { statfs: opts.statfs } : {},
@@ -11913,9 +12265,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
11913
12265
  let open = false;
11914
12266
  const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
11915
12267
  async function hello(message3) {
11916
- const refused = driverVersionError(message3.protocol);
11917
- if (refused) {
11918
- reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused, errno: "EPROTO" } });
12268
+ const refused2 = driverVersionError(message3.protocol);
12269
+ if (refused2) {
12270
+ reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused2, errno: "EPROTO" } });
11919
12271
  return;
11920
12272
  }
11921
12273
  try {
@@ -12457,7 +12809,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
12457
12809
  }, againMs);
12458
12810
  timer.unref?.();
12459
12811
  });
12460
- async function writable(client, path, flags) {
12812
+ async function writable2(client, path, flags) {
12461
12813
  const { ifMatch: _, ...asked } = flags;
12462
12814
  const opening = () => client.call("open", { path: rel(path), ...asked });
12463
12815
  try {
@@ -12488,7 +12840,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
12488
12840
  async open(path, flags) {
12489
12841
  const client = current();
12490
12842
  const token = client.token;
12491
- const { opened: opened2, restore } = await writable(client, path, flags);
12843
+ const { opened: opened2, restore } = await writable2(client, path, flags);
12492
12844
  if (flags.ifMatch !== void 0 && opened2.attr?.etag !== flags.ifMatch) {
12493
12845
  await client.call("release", { fh: opened2.fh }, true);
12494
12846
  throw fsError2("ESTALE", `${path} changed on the host while it was being read`);
@@ -12661,7 +13013,7 @@ var PORT_DEAD_MS = 1e4;
12661
13013
  var PORT_LINGER_MS = 1e3;
12662
13014
  function timed(transport, ms) {
12663
13015
  const inflight = /* @__PURE__ */ new Set();
12664
- const guard = (name2, run2, late, expire) => new Promise((resolve, reject) => {
13016
+ const guard2 = (name2, run2, late, expire) => new Promise((resolve, reject) => {
12665
13017
  let expired = false;
12666
13018
  const running = run2();
12667
13019
  inflight.add(running);
@@ -12710,15 +13062,15 @@ function timed(transport, ms) {
12710
13062
  },
12711
13063
  retire: (why) => transport.retire?.(why),
12712
13064
  ...transport.cleanup ? { cleanup } : {},
12713
- call: (op, body, handle) => guard(
13065
+ call: (op, body, handle) => guard2(
12714
13066
  op,
12715
13067
  () => transport.call(op, body, handle),
12716
13068
  op === "open" ? release : void 0,
12717
13069
  op === "release" ? (running) => void cleanup("release", body ?? {}, running).catch(() => {
12718
13070
  }) : void 0
12719
13071
  ),
12720
- read: (fh, offset, size, ifMatch) => guard("read", () => transport.read(fh, offset, size, ifMatch)),
12721
- write: (fh, offset, bytes) => guard("write", () => transport.write(fh, offset, bytes)),
13072
+ read: (fh, offset, size, ifMatch) => guard2("read", () => transport.read(fh, offset, size, ifMatch)),
13073
+ write: (fh, offset, bytes) => guard2("write", () => transport.write(fh, offset, bytes)),
12722
13074
  watch: (signal, onEvent, onOpen) => transport.watch(signal, onEvent, onOpen)
12723
13075
  };
12724
13076
  }
@@ -13099,10 +13451,10 @@ var ChunkedBytes = class {
13099
13451
  }
13100
13452
  this.size = size;
13101
13453
  }
13102
- room(index, need) {
13454
+ room(index, need2) {
13103
13455
  const chunk2 = this.chunks[index];
13104
- if (chunk2 && chunk2.length >= need) return chunk2;
13105
- const grown = new Uint8Array(Math.min(CHUNK3, Math.max(need, (chunk2?.length ?? 0) * 2)));
13456
+ if (chunk2 && chunk2.length >= need2) return chunk2;
13457
+ const grown = new Uint8Array(Math.min(CHUNK3, Math.max(need2, (chunk2?.length ?? 0) * 2)));
13106
13458
  if (chunk2) grown.set(chunk2);
13107
13459
  this.chunks[index] = grown;
13108
13460
  return grown;
@@ -13149,14 +13501,14 @@ function tmpfs() {
13149
13501
  const root = make("directory", 16877);
13150
13502
  const handles = /* @__PURE__ */ new Map();
13151
13503
  let nextFh = 0;
13152
- const parts = (path) => path.split("/").filter(Boolean);
13504
+ const parts2 = (path) => path.split("/").filter(Boolean);
13153
13505
  const place = (dir, name2, node) => {
13154
13506
  dir.children.set(name2, node);
13155
13507
  dir.mtime = Date.now();
13156
13508
  };
13157
13509
  function find(path) {
13158
13510
  let node = root;
13159
- for (const name2 of parts(path)) {
13511
+ for (const name2 of parts2(path)) {
13160
13512
  if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
13161
13513
  const next2 = node.children.get(name2);
13162
13514
  if (!next2) throw fsError2("ENOENT", path);
@@ -13165,7 +13517,7 @@ function tmpfs() {
13165
13517
  return node;
13166
13518
  }
13167
13519
  function parent(path) {
13168
- const names = parts(path);
13520
+ const names = parts2(path);
13169
13521
  const name2 = names.pop();
13170
13522
  if (name2 === void 0) throw fsError2("EBUSY", "the root of a mount");
13171
13523
  const dir = find(`/${names.join("/")}`);
@@ -13250,8 +13602,8 @@ function tmpfs() {
13250
13602
  dir.mtime = Date.now();
13251
13603
  },
13252
13604
  async rename(from, to) {
13253
- const a = `/${parts(from).join("/")}`;
13254
- const b = `/${parts(to).join("/")}`;
13605
+ const a = `/${parts2(from).join("/")}`;
13606
+ const b = `/${parts2(to).join("/")}`;
13255
13607
  if (b !== a && b.startsWith(`${a}/`)) throw fsError2("EINVAL", `${to} is inside ${from}`);
13256
13608
  const source = parent(from);
13257
13609
  const node = source.dir.children.get(source.name);
@@ -13610,11 +13962,13 @@ var Launcher = class {
13610
13962
  booting = new AbortController();
13611
13963
  fstab = Promise.resolve([]);
13612
13964
  usersMode;
13965
+ dac;
13613
13966
  nextPid = 1e3;
13614
13967
  boot = Date.now();
13615
13968
  terminals = 0;
13616
13969
  constructor(options) {
13617
13970
  this.usersMode = usersMode(options.users);
13971
+ this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
13618
13972
  if (this.owners) options.fs.trackOwners?.();
13619
13973
  this.mounts = new MountTable({
13620
13974
  open: (type, spec) => this.driver(type, spec),
@@ -13945,6 +14299,7 @@ var Launcher = class {
13945
14299
  ...req.umask !== void 0 ? { umask: req.umask } : {},
13946
14300
  cred: req.cred,
13947
14301
  ...this.owners ? { owners: true } : {},
14302
+ ...this.dac ? { dac: this.dac } : {},
13948
14303
  program: req.program,
13949
14304
  argv0: req.argv0,
13950
14305
  args: req.args,
@@ -14908,7 +15263,7 @@ function dialStream(port, onClose) {
14908
15263
  let closed = false;
14909
15264
  let reader;
14910
15265
  const acks = [];
14911
- const readable = new ReadableStream({
15266
+ const readable2 = new ReadableStream({
14912
15267
  start(controller) {
14913
15268
  reader = controller;
14914
15269
  },
@@ -14949,7 +15304,7 @@ function dialStream(port, onClose) {
14949
15304
  else finish(errorWithCode(data.error));
14950
15305
  });
14951
15306
  port.start?.();
14952
- const writable = new WritableStream({
15307
+ const writable2 = new WritableStream({
14953
15308
  write(chunk2) {
14954
15309
  if (closed) throw errorWithCode("EPIPE: the connection is closed");
14955
15310
  const copy = chunk2.slice();
@@ -14968,7 +15323,7 @@ function dialStream(port, onClose) {
14968
15323
  shut();
14969
15324
  finish(error);
14970
15325
  };
14971
- return { readable, writable, close, fail: fail3 };
15326
+ return { readable: readable2, writable: writable2, close, fail: fail3 };
14972
15327
  }
14973
15328
 
14974
15329
  // src/client/loopback-fetch.ts
@@ -15452,9 +15807,9 @@ async function greet(channel, lock, timeoutMs) {
15452
15807
  } finally {
15453
15808
  clearTimeout(timer);
15454
15809
  }
15455
- const refused = hello.error ?? versionError(hello.protocol);
15456
- if (refused) {
15457
- const error = new Error(refused);
15810
+ const refused2 = hello.error ?? versionError(hello.protocol);
15811
+ if (refused2) {
15812
+ const error = new Error(refused2);
15458
15813
  channel.end(error);
15459
15814
  throw error;
15460
15815
  }
@@ -15604,10 +15959,11 @@ async function createNodeKernel(options = {}) {
15604
15959
  ...hostfsFetchOf(options),
15605
15960
  ...options.cdp ? { cdp: options.cdp } : {},
15606
15961
  ...options.hostname !== void 0 ? { hostname: options.hostname } : {},
15607
- ...options.users !== void 0 ? { users: options.users } : {}
15962
+ ...options.users !== void 0 ? { users: options.users } : {},
15963
+ ...options.onDenial ? { onDenial: options.onDenial } : {}
15608
15964
  });
15609
15965
  await launcher.prepare();
15610
- const guard = (work) => new Promise((resolve, reject) => {
15966
+ const guard2 = (work) => new Promise((resolve, reject) => {
15611
15967
  if (terminated) return reject(new Error(TERMINATED));
15612
15968
  pending.add(reject);
15613
15969
  work().then(resolve, reject).finally(() => pending.delete(reject));
@@ -15619,7 +15975,7 @@ async function createNodeKernel(options = {}) {
15619
15975
  const { stdin, onStdout, onStderr, ...rest2 } = opts;
15620
15976
  const onOut = textOf(onStdout);
15621
15977
  const onErr = textOf(onStderr);
15622
- const result = await guard(
15978
+ const result = await guard2(
15623
15979
  () => launcher.run(argv, {
15624
15980
  ...rest2,
15625
15981
  ...stdin !== void 0 ? { stdin: typeof stdin === "string" ? new TextEncoder().encode(stdin) : stdin } : {},
@@ -15637,7 +15993,7 @@ async function createNodeKernel(options = {}) {
15637
15993
  const { onData, ...rest2 } = opts;
15638
15994
  let listener = onData ?? null;
15639
15995
  const backlog = [];
15640
- const session = await guard(
15996
+ const session = await guard2(
15641
15997
  () => launcher.openTerminal(argv, {
15642
15998
  ...rest2,
15643
15999
  onData: (bytes) => listener ? listener(bytes) : backlog.push(bytes)
@@ -15645,7 +16001,7 @@ async function createNodeKernel(options = {}) {
15645
16001
  );
15646
16002
  return {
15647
16003
  pid: session.pid,
15648
- exited: guard(() => session.exited),
16004
+ exited: guard2(() => session.exited),
15649
16005
  get onData() {
15650
16006
  return listener;
15651
16007
  },
@@ -15665,20 +16021,20 @@ async function createNodeKernel(options = {}) {
15665
16021
  await launcher.fs.writeFile(path, data);
15666
16022
  },
15667
16023
  readFile: async (path) => launcher.fs.readFileBuffer(path),
15668
- mount: (spec) => guard(() => launcher.mount(spec)),
15669
- umount: (target) => guard(async () => launcher.umount(target)),
16024
+ mount: (spec) => guard2(() => launcher.mount(spec)),
16025
+ umount: (target) => guard2(async () => launcher.umount(target)),
15670
16026
  mounts: async () => launcher.mounts.list(),
15671
- setRoutes: (routes) => guard(async () => launcher.setRoutes(routes)),
16027
+ setRoutes: (routes) => guard2(async () => launcher.setRoutes(routes)),
15672
16028
  users: {
15673
16029
  mode: launcher.usersMode,
15674
- add: (user) => guard(() => launcher.users.add(user)),
15675
- remove: (name2, options2) => guard(() => launcher.users.remove(name2, options2)),
15676
- list: () => guard(() => launcher.users.list())
16030
+ add: (user) => guard2(() => launcher.users.add(user)),
16031
+ remove: (name2, options2) => guard2(() => launcher.users.remove(name2, options2)),
16032
+ list: () => guard2(() => launcher.users.list())
15677
16033
  },
15678
16034
  groups: {
15679
- list: () => guard(() => launcher.users.groups()),
15680
- addMember: (group, user) => guard(() => launcher.users.addMember(group, user)),
15681
- removeMember: (group, user) => guard(() => launcher.users.removeMember(group, user))
16035
+ list: () => guard2(() => launcher.users.groups()),
16036
+ addMember: (group, user) => guard2(() => launcher.users.addMember(group, user)),
16037
+ removeMember: (group, user) => guard2(() => launcher.users.removeMember(group, user))
15682
16038
  },
15683
16039
  async connect({ user } = {}) {
15684
16040
  if (terminated) throw new Error(TERMINATED);