@ai-ecoverse/slicc-kernel 1.58.0 → 1.59.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
@@ -1774,12 +1774,22 @@ var UserDb = class {
1774
1774
  async stampHome(row, shared) {
1775
1775
  const home = this.fs.resolvePath("/", row.home);
1776
1776
  if (!this.owners || !this.fs.chown || !HOME_DIR.test(home)) return;
1777
- if (await this.fs.realpath?.(home, false).catch(() => void 0) !== home) return;
1778
- if (!(await this.fs.lstat(home)).isDirectory) return;
1777
+ if (!await this.realDir(home)) return;
1779
1778
  const gid = shared?.gid ?? row.gid;
1780
1779
  await this.fs.chown(home, { uid: row.uid, gid, follow: false, kind: "adopt" });
1781
- const mode = shared ? 1512 : row.name.startsWith("scoop-") ? 488 : 448;
1782
- await this.fs.chmod(home, mode);
1780
+ const scoop = row.name.startsWith("scoop-");
1781
+ await this.fs.chmod(home, shared ? 1512 : scoop ? 488 : 448);
1782
+ const pnpm = `${home}/.local/share/pnpm`;
1783
+ if (await this.realDir(pnpm)) await this.fs.chmod(pnpm, scoop ? 488 : 448);
1784
+ }
1785
+ async realDir(path) {
1786
+ const parts = path.split("/").filter(Boolean);
1787
+ for (let n = 1; n <= parts.length; n++) {
1788
+ const at = `/${parts.slice(0, n).join("/")}`;
1789
+ const st = await this.fs.lstat(at).catch(() => void 0);
1790
+ if (!st?.isDirectory) return false;
1791
+ }
1792
+ return true;
1783
1793
  }
1784
1794
  async read(path, fallback) {
1785
1795
  try {
@@ -1796,15 +1806,17 @@ var UserDb = class {
1796
1806
  };
1797
1807
  }
1798
1808
  async provision(row) {
1809
+ const owner = this.owners ? { uid: row.uid, gid: row.gid } : void 0;
1810
+ const made2 = owner ? { recursive: true, owner } : { recursive: true };
1799
1811
  const pnpm = `${row.home}/.local/share/pnpm`;
1800
1812
  if (!await this.fs.exists(pnpm)) {
1801
- await this.fs.mkdir(pnpm, { recursive: true });
1813
+ await this.fs.mkdir(pnpm, made2);
1802
1814
  await this.fs.chmod(pnpm, row.name.startsWith("scoop-") ? 488 : 448);
1803
1815
  }
1804
1816
  const config = `${row.home}/.config/pnpm/config.yaml`;
1805
1817
  if (!await this.fs.exists(config)) {
1806
- await this.fs.mkdir(`${row.home}/.config/pnpm`, { recursive: true });
1807
- await this.fs.writeFile(config, PNPM_CONFIG);
1818
+ await this.fs.mkdir(`${row.home}/.config/pnpm`, made2);
1819
+ await this.fs.writeFile(config, PNPM_CONFIG, owner);
1808
1820
  }
1809
1821
  }
1810
1822
  async commit(passwd, group, then) {
@@ -2008,7 +2020,7 @@ async function fsCall(fs, method, args, owner) {
2008
2020
  return { result: bytes, transfer: [bytes.buffer] };
2009
2021
  }
2010
2022
  case "writeFile":
2011
- await fs.writeFile(path(0), args[1], owner);
2023
+ await fs.writeFile(path(0), args[1], created(owner, 438));
2012
2024
  return { result: true };
2013
2025
  case "stat":
2014
2026
  return { result: await fs.stat(path(0)) };
@@ -2017,7 +2029,10 @@ async function fsCall(fs, method, args, owner) {
2017
2029
  case "readdir":
2018
2030
  return { result: await fs.readdir(path(0)) };
2019
2031
  case "mkdir":
2020
- await fs.mkdir(path(0), owner ? { recursive: true, owner } : { recursive: true });
2032
+ await fs.mkdir(
2033
+ path(0),
2034
+ owner ? { recursive: true, owner: made(owner) } : { recursive: true }
2035
+ );
2021
2036
  return { result: true };
2022
2037
  case "rm":
2023
2038
  await fs.rm(path(0), { recursive: true, force: args[1] === true });
@@ -2028,7 +2043,7 @@ async function fsCall(fs, method, args, owner) {
2028
2043
  case "realpath":
2029
2044
  return { result: await realpath(fs, String(args[0])) };
2030
2045
  case "symlink":
2031
- await fs.symlink(String(args[0]), path(1), owner);
2046
+ await fs.symlink(String(args[0]), path(1), owner && { uid: owner.uid, gid: owner.gid });
2032
2047
  return { result: true };
2033
2048
  case "readlink":
2034
2049
  return { result: await fs.readlink(path(0)) };
@@ -2050,8 +2065,16 @@ function dialFor(l, req, open) {
2050
2065
  );
2051
2066
  return { result: port2, transfer: [port2] };
2052
2067
  }
2068
+ function created(owner, mode) {
2069
+ return owner && { uid: owner.uid, gid: owner.gid, mode: mode & ~owner.umask };
2070
+ }
2071
+ function made(owner) {
2072
+ const mode = 511 & ~owner.umask;
2073
+ return { uid: owner.uid, gid: owner.gid, mode, parents: mode | 192 };
2074
+ }
2053
2075
  function ownerFor(l, self) {
2054
- return l.owners && self ? { uid: self.uid, gid: self.gid } : void 0;
2076
+ if (!l.owners || !self) return void 0;
2077
+ return { uid: self.uid, gid: self.gid, umask: self.umask ?? 18 };
2055
2078
  }
2056
2079
  async function accountOf(l, user) {
2057
2080
  const found = await l.users.lookup(user);
@@ -2925,12 +2948,12 @@ var OpfsFs = class {
2925
2948
  const base = dir === "/" ? "" : dir;
2926
2949
  const metas = await this.meta.get(children2.map(([name2]) => `${base}/${name2}`));
2927
2950
  const handed2 = this.owners ? await this.handedTo(dir) : void 0;
2928
- const owned2 = (stat, entry) => handed2 ? { ...stat, ...ownOf(entry) ?? handed2 } : stat;
2951
+ const owned3 = (stat, entry) => handed2 ? { ...stat, ...ownOf(entry) ?? handed2 } : stat;
2929
2952
  return Promise.all(
2930
2953
  children2.map(async ([name2, child], i) => {
2931
- if (!isHandle(child)) return [name2, owned2(this.linkStat(child), child)];
2954
+ if (!isHandle(child)) return [name2, owned3(this.linkStat(child), child)];
2932
2955
  const stat = await this.statOf(`${base}/${name2}`, child, metas[i]).catch(() => null);
2933
- return [name2, stat && owned2(stat, metas[i])];
2956
+ return [name2, stat && owned3(stat, metas[i])];
2934
2957
  })
2935
2958
  );
2936
2959
  }
@@ -2961,9 +2984,9 @@ var OpfsFs = class {
2961
2984
  const parent = await this.locate(parentOf2(path), true);
2962
2985
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
2963
2986
  await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
2964
- const made = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
2965
- await this.stampNew(made, owner, true);
2966
- this.touch(parent.path, made);
2987
+ const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
2988
+ await this.stampNew(made2, owner, true);
2989
+ this.touch(parent.path, made2);
2967
2990
  }
2968
2991
  async rm(path, options = {}) {
2969
2992
  let found;
@@ -4408,7 +4431,17 @@ async function followLinks(fs, path) {
4408
4431
  }
4409
4432
  return current;
4410
4433
  }
4411
- function withCommandDirs(fs, names, userDir = async () => void 0) {
4434
+ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false) {
4435
+ async function nearest(path) {
4436
+ const st = await fs.stat(path).catch(() => void 0);
4437
+ if (st || path === "/") return st ?? {};
4438
+ return nearest(path.slice(0, path.lastIndexOf("/")) || "/");
4439
+ }
4440
+ async function owned3(stat2, dir) {
4441
+ if (!owners) return stat2;
4442
+ const { uid = 0, gid = 0 } = dir === void 0 ? {} : await nearest(dir);
4443
+ return { ...stat2, uid, gid };
4444
+ }
4412
4445
  async function children2(path) {
4413
4446
  if (path === "/") return ["bin", "usr"];
4414
4447
  if (path === "/usr") return ["bin"];
@@ -4417,13 +4450,18 @@ function withCommandDirs(fs, names, userDir = async () => void 0) {
4417
4450
  return own?.size ? [...own] : void 0;
4418
4451
  }
4419
4452
  async function virtual(path) {
4420
- if (path === "/usr" || path === "/bin" || path === "/usr/bin") return synthetic(true, path);
4453
+ if (path === "/usr" || path === "/bin" || path === "/usr/bin") {
4454
+ return owned3(synthetic(true, path));
4455
+ }
4421
4456
  const name2 = COMMAND.exec(path)?.[1];
4422
- if (name2 !== void 0) return (await names()).has(name2) ? synthetic(false, path) : void 0;
4423
- if ((await userDir(path))?.size) return synthetic(true, path);
4457
+ if (name2 !== void 0) {
4458
+ return (await names()).has(name2) ? owned3(synthetic(false, path)) : void 0;
4459
+ }
4460
+ if ((await userDir(path))?.size) return owned3(synthetic(true, path), path);
4424
4461
  const slash = path.lastIndexOf("/");
4425
- const own = await userDir(path.slice(0, slash));
4426
- return own?.has(path.slice(slash + 1)) ? synthetic(false, path) : void 0;
4462
+ const dir = path.slice(0, slash);
4463
+ const own = await userDir(dir);
4464
+ return own?.has(path.slice(slash + 1)) ? owned3(synthetic(false, path), dir) : void 0;
4427
4465
  }
4428
4466
  async function linked(path) {
4429
4467
  return await virtual(path) ?? virtual(await followLinks(fs, path));
@@ -4599,18 +4637,18 @@ var FsWatchers = class {
4599
4637
  const overrides = {
4600
4638
  writeFile: after(fs.writeFile.bind(fs), (path) => [path]),
4601
4639
  mkdir: async (path, options) => {
4602
- const created = [];
4640
+ const created3 = [];
4603
4641
  if (options?.recursive) {
4604
4642
  const parts = normalizePath(path).split("/").filter(Boolean);
4605
4643
  for (let at = 1; at < parts.length; at++) {
4606
4644
  const ancestor = `/${parts.slice(0, at).join("/")}`;
4607
- if (created.length > 0 || !await fs.exists(ancestor)) created.push(ancestor);
4645
+ if (created3.length > 0 || !await fs.exists(ancestor)) created3.push(ancestor);
4608
4646
  }
4609
4647
  }
4610
4648
  try {
4611
4649
  await fs.mkdir(path, options);
4612
4650
  } finally {
4613
- this.changed(...created, path);
4651
+ this.changed(...created3, path);
4614
4652
  }
4615
4653
  },
4616
4654
  rm: after(fs.rm.bind(fs), (path) => [path]),
@@ -4889,7 +4927,7 @@ async function realTarget(fs, path) {
4889
4927
  }
4890
4928
 
4891
4929
  // src/mount/protocol.ts
4892
- var DRIVER_PROTOCOL = [1, 1];
4930
+ var DRIVER_PROTOCOL = [1, 2];
4893
4931
  function driverVersionError(theirs) {
4894
4932
  const major = Array.isArray(theirs) ? theirs[0] : void 0;
4895
4933
  if (major === DRIVER_PROTOCOL[0]) return void 0;
@@ -4991,6 +5029,19 @@ var DriverConnection = class _DriverConnection {
4991
5029
  };
4992
5030
 
4993
5031
  // src/mount/mount-fs.ts
5032
+ var S_ISGID3 = 1024;
5033
+ var ID = /^\d{1,10}$/;
5034
+ var MAX_ID2 = 4294967294;
5035
+ function idOption(value) {
5036
+ return value === void 0 ? 0 : Number(value);
5037
+ }
5038
+ function checkIds(options) {
5039
+ for (const key of ["uid", "gid"]) {
5040
+ const value = options[key];
5041
+ if (value === void 0) continue;
5042
+ if (!ID.test(value) || Number(value) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value}`);
5043
+ }
5044
+ }
4994
5045
  var MAX_LINKS5 = 40;
4995
5046
  var STALE_RETRIES = 3;
4996
5047
  var SETTLE_ROUNDS = 4;
@@ -5157,31 +5208,35 @@ var HeldPaths = class extends Map {
5157
5208
  };
5158
5209
  var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
5159
5210
  var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
5160
- function inodeless(mount, attr) {
5161
- if (attr.ino !== void 0) return false;
5211
+ function inodeless(mount, attr2) {
5212
+ if (attr2.ino !== void 0) return false;
5162
5213
  if (mount.caps.links) mount.caps = { ...mount.caps, links: false };
5163
5214
  return true;
5164
5215
  }
5165
- function statOf(attr, path, mount) {
5166
- const mtime = new Date(attr.mtime);
5167
- const fallback = attr.kind === "directory" ? 16877 : attr.kind === "symlink" ? 41471 : 33188;
5216
+ function statOf(attr2, path, mount) {
5217
+ const mtime = new Date(attr2.mtime);
5218
+ const fallback = attr2.kind === "directory" ? 16877 : attr2.kind === "symlink" ? 41471 : 33188;
5168
5219
  return {
5169
- isFile: attr.kind === "file",
5170
- isDirectory: attr.kind === "directory",
5171
- isSymbolicLink: attr.kind === "symlink",
5172
- size: attr.size,
5173
- mode: attr.mode ?? fallback,
5220
+ isFile: attr2.kind === "file",
5221
+ isDirectory: attr2.kind === "directory",
5222
+ isSymbolicLink: attr2.kind === "symlink",
5223
+ size: attr2.size,
5224
+ mode: attr2.mode ?? fallback,
5174
5225
  mtime,
5175
5226
  atime: mtime,
5176
5227
  ctime: mtime,
5177
- ino: attr.ino ?? inodeOf(path),
5178
- ...!inodeless(mount, attr) && attr.nlink !== void 0 ? { nlink: attr.nlink } : {},
5228
+ ino: attr2.ino ?? inodeOf(path),
5229
+ ...!inodeless(mount, attr2) && attr2.nlink !== void 0 ? { nlink: attr2.nlink } : {},
5179
5230
  dev: mount.dev,
5180
5231
  ...readonly(mount) ? { readonly: true } : {},
5181
5232
  ...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {},
5182
5233
  ...mount.caps.ranges ? { ranged: true } : {},
5183
5234
  ...mount.caps.links ? { linkable: true } : {},
5184
- ...attr.etag !== void 0 ? { version: attr.etag } : {}
5235
+ ...attr2.etag !== void 0 ? { version: attr2.etag } : {},
5236
+ ...mount.owners ? {
5237
+ uid: (mount.caps.owners ? attr2.uid : void 0) ?? idOption(mount.options.uid),
5238
+ gid: (mount.caps.owners ? attr2.gid : void 0) ?? idOption(mount.options.gid)
5239
+ } : {}
5185
5240
  };
5186
5241
  }
5187
5242
  function concat3(chunks2, total) {
@@ -5274,6 +5329,7 @@ var MountTable = class {
5274
5329
  const st = await fs.stat(target);
5275
5330
  if (!st.isDirectory) throw errnoError("ENOTDIR", target);
5276
5331
  const options = { ...spec.options };
5332
+ if (this.deps.owners) checkIds(options);
5277
5333
  const maxFile = options.maxfile !== void 0 ? parseSize(options.maxfile) : void 0;
5278
5334
  const opened2 = await this.deps.open(spec.type, { ...spec, target, options });
5279
5335
  let mounted;
@@ -5319,6 +5375,7 @@ var MountTable = class {
5319
5375
  attrs: /* @__PURE__ */ new Map(),
5320
5376
  lists: /* @__PURE__ */ new Map(),
5321
5377
  epoch: 0,
5378
+ owners: this.deps.owners === true,
5322
5379
  dispose: () => opened2.dispose(),
5323
5380
  ...opened2.present ? { present: opened2.present } : {}
5324
5381
  };
@@ -5440,15 +5497,15 @@ var MountTable = class {
5440
5497
  if (cached && cached.expires > Date.now()) return cached.value;
5441
5498
  const seen = mount.epoch;
5442
5499
  const session = this.sessions.get(path);
5443
- const attr = await mount.conn.call({ op: "getattr", path: rel2 });
5500
+ const attr2 = await mount.conn.call({ op: "getattr", path: rel2 });
5444
5501
  if (mount.epoch !== seen) {
5445
5502
  const moved2 = this.sessions.get(path) !== session || this.committing.has(path);
5446
5503
  if (settled && moved2 && round < SETTLE_ROUNDS) continue;
5447
- return attr;
5504
+ return attr2;
5448
5505
  }
5449
5506
  const ttl = mount.caps.attrTtl ?? 1e3;
5450
- if (ttl > 0) mount.attrs.set(path, { value: attr, expires: Date.now() + ttl });
5451
- return attr;
5507
+ if (ttl > 0) mount.attrs.set(path, { value: attr2, expires: Date.now() + ttl });
5508
+ return attr2;
5452
5509
  }
5453
5510
  }
5454
5511
  async readdir(mount, path, rel2) {
@@ -5495,28 +5552,43 @@ var MountFs = class {
5495
5552
  const found = this.at(path);
5496
5553
  if (!found) return path;
5497
5554
  const real = normalizePath(path);
5498
- let attr;
5555
+ let attr2;
5499
5556
  try {
5500
- attr = await this.table.getattr(found.mount, real, found.rel);
5557
+ attr2 = await this.table.getattr(found.mount, real, found.rel);
5501
5558
  } catch (err) {
5502
5559
  if (create && err.code === "ENOENT") return real;
5503
5560
  throw err;
5504
5561
  }
5505
- if (attr.kind !== "symlink") return real;
5562
+ if (attr2.kind !== "symlink") return real;
5506
5563
  if (hops >= MAX_LINKS5) throw errnoError("ELOOP", path);
5507
5564
  const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
5508
5565
  return this.follow(this.base.resolvePath(parentOf6(real), target), hops + 1, create);
5509
5566
  }
5510
- async open(mount, rel2, write) {
5567
+ async open(mount, rel2, write, owner) {
5511
5568
  return await mount.conn.call({
5512
5569
  op: "open",
5513
5570
  path: rel2,
5514
5571
  write,
5515
5572
  create: write,
5516
5573
  truncate: write,
5517
- exclusive: false
5574
+ exclusive: false,
5575
+ ...owner
5518
5576
  });
5519
5577
  }
5578
+ async driverOwner(found, real, owner, directory) {
5579
+ const { mount, rel: rel2 } = found;
5580
+ if (!owner || !mount.owners || !mount.caps.owners) return void 0;
5581
+ const dir = parentOf6(real);
5582
+ const above = statOf(await this.table.getattr(mount, dir, parentOf6(rel2)), dir, mount);
5583
+ const setgid = (above.mode & S_ISGID3) !== 0;
5584
+ let mode = owner.mode;
5585
+ if (directory && setgid) mode = (mode ?? 493) | S_ISGID3;
5586
+ return {
5587
+ uid: owner.uid,
5588
+ gid: setgid ? above.gid : owner.gid,
5589
+ ...mode !== void 0 ? { mode } : {}
5590
+ };
5591
+ }
5520
5592
  resolvePath(base, path) {
5521
5593
  return this.base.resolvePath(base, path);
5522
5594
  }
@@ -5538,9 +5610,9 @@ var MountFs = class {
5538
5610
  }
5539
5611
  async readWhole(mount, real, rel2) {
5540
5612
  await this.table.commit(real);
5541
- const attr = await this.table.getattr(mount, real, rel2);
5542
- if (attr.kind === "directory") throw errnoError("EISDIR", real);
5543
- if (mount.maxFile > 0 && attr.size > mount.maxFile) {
5613
+ const attr2 = await this.table.getattr(mount, real, rel2);
5614
+ if (attr2.kind === "directory") throw errnoError("EISDIR", real);
5615
+ if (mount.maxFile > 0 && attr2.size > mount.maxFile) {
5544
5616
  throw errnoError("EFBIG", `${real} is larger than this mount's maxfile (${mount.maxFile})`);
5545
5617
  }
5546
5618
  const fh = await this.open(mount, rel2, false);
@@ -5558,7 +5630,7 @@ var MountFs = class {
5558
5630
  if (bytes.length === 0) break;
5559
5631
  chunks2.push(bytes);
5560
5632
  total += bytes.length;
5561
- if (bytes.length < chunk2 && total >= attr.size) break;
5633
+ if (bytes.length < chunk2 && total >= attr2.size) break;
5562
5634
  }
5563
5635
  } finally {
5564
5636
  await mount.conn.call({ op: "release", fh });
@@ -5578,18 +5650,19 @@ var MountFs = class {
5578
5650
  `${real} would be larger than this mount's maxfile (${mount.maxFile})`
5579
5651
  );
5580
5652
  }
5653
+ const made2 = await this.driverOwner(found, real, owner, false);
5581
5654
  for (let attempt = 1; ; attempt++) {
5582
5655
  try {
5583
- return await this.writeWhole(mount, real, rel2, bytes);
5656
+ return await this.writeWhole(mount, real, rel2, bytes, made2);
5584
5657
  } catch (err) {
5585
5658
  if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
5586
5659
  }
5587
5660
  }
5588
5661
  }
5589
- async writeWhole(mount, real, rel2, bytes) {
5662
+ async writeWhole(mount, real, rel2, bytes, owner) {
5590
5663
  await this.table.commit(real);
5591
5664
  this.table.forget(mount, real);
5592
- const fh = await this.open(mount, rel2, true);
5665
+ const fh = await this.open(mount, rel2, true, owner);
5593
5666
  const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
5594
5667
  try {
5595
5668
  for (let offset = 0; offset < bytes.length; offset += chunk2) {
@@ -5735,9 +5808,9 @@ var MountFs = class {
5735
5808
  found.mount
5736
5809
  );
5737
5810
  }
5738
- statWriting(attr, real, mount) {
5811
+ statWriting(attr2, real, mount) {
5739
5812
  const ends = (this.table.writing(real)?.extents ?? []).map(([, end]) => end);
5740
- return statOf({ ...attr, size: Math.max(attr.size, ...ends) }, real, mount);
5813
+ return statOf({ ...attr2, size: Math.max(attr2.size, ...ends) }, real, mount);
5741
5814
  }
5742
5815
  async stat(path) {
5743
5816
  const real = await this.follow(path);
@@ -5776,10 +5849,12 @@ var MountFs = class {
5776
5849
  const { mount, rel: rel2 } = found;
5777
5850
  const real = normalizePath(path);
5778
5851
  this.writable(mount, real);
5852
+ const { owner } = options;
5779
5853
  if (!options.recursive) {
5780
5854
  if (rel2 === "/") throw errnoError("EEXIST", real);
5855
+ const made2 = await this.driverOwner(found, real, owner, true);
5781
5856
  this.table.forget(mount, real);
5782
- await mount.conn.call({ op: "mkdir", path: rel2 });
5857
+ await mount.conn.call({ op: "mkdir", path: rel2, ...made2 });
5783
5858
  return;
5784
5859
  }
5785
5860
  const parts = rel2.split("/").filter(Boolean);
@@ -5789,8 +5864,10 @@ var MountFs = class {
5789
5864
  const exists = await this.table.getattr(mount, full, sub).catch(() => void 0);
5790
5865
  if (exists?.kind === "directory") continue;
5791
5866
  if (exists) throw errnoError("ENOTDIR", full);
5867
+ const own = n === parts.length ? owner : owner && { ...owner, mode: owner.parents };
5868
+ const made2 = await this.driverOwner({ mount, rel: sub }, full, own, true);
5792
5869
  this.table.forget(mount, full);
5793
- await mount.conn.call({ op: "mkdir", path: sub });
5870
+ await mount.conn.call({ op: "mkdir", path: sub, ...made2 });
5794
5871
  }
5795
5872
  }
5796
5873
  async rm(path, options = {}) {
@@ -5801,21 +5878,21 @@ var MountFs = class {
5801
5878
  if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
5802
5879
  this.writable(mount, real);
5803
5880
  await this.table.commitUnder(real);
5804
- let attr;
5881
+ let attr2;
5805
5882
  try {
5806
- attr = await this.table.getattr(mount, real, rel2);
5883
+ attr2 = await this.table.getattr(mount, real, rel2);
5807
5884
  } catch (err) {
5808
5885
  if (options.force && err.code === "ENOENT") return;
5809
5886
  throw err;
5810
5887
  }
5811
5888
  this.table.forget(mount, real);
5812
- if (attr.kind === "directory" && options.recursive) {
5889
+ if (attr2.kind === "directory" && options.recursive) {
5813
5890
  for (const entry of await this.table.readdir(mount, real, rel2)) {
5814
5891
  await this.rm(`${real}/${entry.name}`, { recursive: true, force: options.force ?? false });
5815
5892
  }
5816
5893
  this.table.forget(mount, real);
5817
5894
  }
5818
- await mount.conn.call({ op: attr.kind === "directory" ? "rmdir" : "unlink", path: rel2 });
5895
+ await mount.conn.call({ op: attr2.kind === "directory" ? "rmdir" : "unlink", path: rel2 });
5819
5896
  this.table.forget(mount, real);
5820
5897
  }
5821
5898
  async rename(from, to) {
@@ -5869,8 +5946,10 @@ var MountFs = class {
5869
5946
  this.writable(found.mount, normalizePath(path));
5870
5947
  if (!found.mount.caps.symlinks)
5871
5948
  throw errnoError("EPERM", `${path}: this file system has no symlinks`);
5949
+ const link2 = owner && { uid: owner.uid, gid: owner.gid };
5950
+ const made2 = await this.driverOwner(found, normalizePath(path), link2, false);
5872
5951
  this.table.forget(found.mount, normalizePath(path));
5873
- await found.mount.conn.call({ op: "symlink", target, path: found.rel });
5952
+ await found.mount.conn.call({ op: "symlink", target, path: found.rel, ...made2 });
5874
5953
  }
5875
5954
  async realpath(path, follow2) {
5876
5955
  if (this.at(path) || !this.base.realpath) throw errnoError("ENOSYS", path);
@@ -5885,13 +5964,29 @@ var MountFs = class {
5885
5964
  }
5886
5965
  async chown(path, change) {
5887
5966
  const real = change.follow === false || change.kind === "new" ? path : await this.follow(path);
5888
- if (this.at(real)) {
5889
- if (change.kind === "new") return;
5890
- throw errnoError("EPERM", `${path}: this file system keeps no owners`);
5891
- }
5967
+ const found = this.at(real);
5968
+ if (found) return this.chownMounted(found, normalizePath(real), change);
5892
5969
  if (!this.base.chown) throw errnoError("EPERM", path);
5893
5970
  return this.base.chown(real, change);
5894
5971
  }
5972
+ async chownMounted(found, real, change) {
5973
+ const { mount, rel: rel2 } = found;
5974
+ if (!mount.owners || !mount.caps.owners) {
5975
+ if (change.kind === "new") return;
5976
+ throw errnoError("EPERM", `${real}: this file system keeps no owners`);
5977
+ }
5978
+ this.writable(mount, real);
5979
+ const { uid, gid, mode } = change;
5980
+ const fresh = change.kind === "new" && { uid, gid, mode };
5981
+ const set2 = fresh ? await this.driverOwner(found, real, fresh, false) : change;
5982
+ this.table.forget(mount, real);
5983
+ const attrs = Object.entries({ uid: set2.uid, gid: set2.gid, mode: set2.mode });
5984
+ await mount.conn.call({
5985
+ op: "setattr",
5986
+ path: rel2,
5987
+ ...Object.fromEntries(attrs.filter(([, value]) => value !== void 0))
5988
+ });
5989
+ }
5895
5990
  async chmod(path, mode) {
5896
5991
  const found = this.at(path);
5897
5992
  if (!found) return this.base.chmod(path, mode);
@@ -5998,7 +6093,10 @@ function mkdirOptions(entry, mode = 511) {
5998
6093
  }
5999
6094
  async function chown(entry, path, req) {
6000
6095
  const cred = entry.cred?.();
6001
- if (!cred) return done;
6096
+ if (!cred) {
6097
+ await (req.op === "chown" ? entry.fs.stat(path) : entry.fs.lstat(path));
6098
+ return done;
6099
+ }
6002
6100
  await chownAs(entry.fs, cred, path, {
6003
6101
  ...req.uid !== void 0 ? { uid: req.uid } : {},
6004
6102
  ...req.gid !== void 0 ? { gid: req.gid } : {},
@@ -11170,6 +11268,7 @@ function spawnWasmProcess(opts) {
11170
11268
  cwd: opts.cwd,
11171
11269
  sab: tsab,
11172
11270
  cred: copyCred(process2.cred),
11271
+ owners: opts.owners === true,
11173
11272
  ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
11174
11273
  thread
11175
11274
  };
@@ -11563,6 +11662,17 @@ var SettlingChildren = class {
11563
11662
  };
11564
11663
 
11565
11664
  // src/mount/driver.ts
11665
+ function ownerOf2(req) {
11666
+ const owner = {
11667
+ ...req.uid !== void 0 ? { uid: req.uid } : {},
11668
+ ...req.gid !== void 0 ? { gid: req.gid } : {},
11669
+ ...req.mode !== void 0 ? { mode: req.mode } : {}
11670
+ };
11671
+ return Object.keys(owner).length > 0 ? owner : void 0;
11672
+ }
11673
+ function withOwner(base, owner) {
11674
+ return owner ? { ...base, owner } : base;
11675
+ }
11566
11676
  function fsError2(code, message3 = code) {
11567
11677
  return Object.assign(new Error(message3), { code });
11568
11678
  }
@@ -11573,13 +11683,19 @@ async function dispatch(h, req) {
11573
11683
  case "readdir":
11574
11684
  return h.readdir(req.path);
11575
11685
  case "open":
11576
- return h.open(req.path, {
11577
- write: req.write,
11578
- create: req.create,
11579
- truncate: req.truncate,
11580
- exclusive: req.exclusive,
11581
- ...req.ifMatch !== void 0 ? { ifMatch: req.ifMatch } : {}
11582
- });
11686
+ return h.open(
11687
+ req.path,
11688
+ withOwner(
11689
+ {
11690
+ write: req.write,
11691
+ create: req.create,
11692
+ truncate: req.truncate,
11693
+ exclusive: req.exclusive,
11694
+ ...req.ifMatch !== void 0 ? { ifMatch: req.ifMatch } : {}
11695
+ },
11696
+ ownerOf2(req)
11697
+ )
11698
+ );
11583
11699
  case "read":
11584
11700
  return h.read(req.fh, req.offset, req.size);
11585
11701
  case "write":
@@ -11587,7 +11703,7 @@ async function dispatch(h, req) {
11587
11703
  case "release":
11588
11704
  return h.release(req.fh);
11589
11705
  case "mkdir":
11590
- return h.mkdir(req.path);
11706
+ return ownerOf2(req) ? h.mkdir(req.path, ownerOf2(req)) : h.mkdir(req.path);
11591
11707
  case "rmdir":
11592
11708
  return h.rmdir(req.path);
11593
11709
  case "unlink":
@@ -11599,7 +11715,7 @@ async function dispatch(h, req) {
11599
11715
  return h.link(req.from, req.to);
11600
11716
  case "symlink":
11601
11717
  if (!h.symlink) throw fsError2("EPERM", "this file system has no symlinks");
11602
- return h.symlink(req.target, req.path);
11718
+ return ownerOf2(req) ? h.symlink(req.target, req.path, ownerOf2(req)) : h.symlink(req.target, req.path);
11603
11719
  case "readlink":
11604
11720
  if (!h.readlink) throw fsError2("EINVAL", `${req.path} is not a symlink`);
11605
11721
  return h.readlink(req.path);
@@ -11607,7 +11723,9 @@ async function dispatch(h, req) {
11607
11723
  return h.setattr?.(req.path, {
11608
11724
  ...req.mode !== void 0 ? { mode: req.mode } : {},
11609
11725
  ...req.mtime !== void 0 ? { mtime: req.mtime } : {},
11610
- ...req.size !== void 0 ? { size: req.size } : {}
11726
+ ...req.size !== void 0 ? { size: req.size } : {},
11727
+ ...req.uid !== void 0 ? { uid: req.uid } : {},
11728
+ ...req.gid !== void 0 ? { gid: req.gid } : {}
11611
11729
  });
11612
11730
  case "statfs":
11613
11731
  return h.statfs?.() ?? null;
@@ -11758,8 +11876,8 @@ function kernelFsHandlers(fs) {
11758
11876
  getattr: async (path) => attrOf(await fs.lstat(path)),
11759
11877
  readdir: async (path) => (await fs.readdirStat(path)).flatMap(([name2, st]) => {
11760
11878
  if (!st) return [];
11761
- const attr = attrOf(st);
11762
- return [{ name: name2, kind: attr.kind, attr }];
11879
+ const attr2 = attrOf(st);
11880
+ return [{ name: name2, kind: attr2.kind, attr: attr2 }];
11763
11881
  }),
11764
11882
  async open(path, flags) {
11765
11883
  const st = await existing(path);
@@ -12189,7 +12307,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
12189
12307
  "list",
12190
12308
  { path: rel(path) }
12191
12309
  );
12192
- return entries.map(({ name: name2, attr }) => ({ name: name2, kind: attr.kind, attr }));
12310
+ return entries.map(({ name: name2, attr: attr2 }) => ({ name: name2, kind: attr2.kind, attr: attr2 }));
12193
12311
  },
12194
12312
  async open(path, flags) {
12195
12313
  const client = current();
@@ -12782,8 +12900,8 @@ var ChunkedBytes = class {
12782
12900
  }
12783
12901
  return out;
12784
12902
  }
12785
- write(offset, bytes, owned2 = false) {
12786
- const whole = owned2 && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
12903
+ write(offset, bytes, owned3 = false) {
12904
+ const whole = owned3 && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
12787
12905
  for (let done4 = 0; done4 < bytes.length; ) {
12788
12906
  const at = offset + done4;
12789
12907
  const index = Math.floor(at / CHUNK3);
@@ -12816,18 +12934,42 @@ var ChunkedBytes = class {
12816
12934
  };
12817
12935
 
12818
12936
  // src/mount/tmpfs.ts
12819
- function tmpfs() {
12820
- let inodes = 0;
12821
- const make = (kind, mode) => ({
12937
+ function idOption2(value) {
12938
+ return value !== void 0 && /^\d+$/.test(value) ? Number(value) : 0;
12939
+ }
12940
+ function created2(ino, kind, mode, from, owner) {
12941
+ return {
12822
12942
  kind,
12823
- ino: ++inodes,
12824
- mode,
12943
+ ino,
12944
+ mode: owner?.mode === void 0 ? mode : mode & ~4095 | owner.mode & 4095,
12825
12945
  mtime: Date.now(),
12826
12946
  data: new ChunkedBytes(),
12827
12947
  target: "",
12828
12948
  children: /* @__PURE__ */ new Map(),
12829
- links: 1
12830
- });
12949
+ links: 1,
12950
+ uid: owner?.uid ?? from?.uid ?? 0,
12951
+ gid: owner?.gid ?? from?.gid ?? 0
12952
+ };
12953
+ }
12954
+ function owned2(node, change) {
12955
+ if (change.uid !== void 0) node.uid = change.uid;
12956
+ if (change.gid !== void 0) node.gid = change.gid;
12957
+ }
12958
+ function attr(node) {
12959
+ return {
12960
+ kind: node.kind,
12961
+ size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
12962
+ mtime: node.mtime,
12963
+ mode: node.mode,
12964
+ ino: node.ino,
12965
+ ...node.kind === "directory" ? {} : { nlink: node.links },
12966
+ uid: node.uid,
12967
+ gid: node.gid
12968
+ };
12969
+ }
12970
+ function tmpfs() {
12971
+ let inodes = 0;
12972
+ const make = (kind, mode, from, owner) => created2(++inodes, kind, mode, from, owner);
12831
12973
  const root = make("directory", 16877);
12832
12974
  const handles = /* @__PURE__ */ new Map();
12833
12975
  let nextFh = 0;
@@ -12854,14 +12996,6 @@ function tmpfs() {
12854
12996
  if (dir.kind !== "directory") throw fsError2("ENOTDIR", path);
12855
12997
  return { dir, name: name2 };
12856
12998
  }
12857
- const attr = (node) => ({
12858
- kind: node.kind,
12859
- size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
12860
- mtime: node.mtime,
12861
- mode: node.mode,
12862
- ino: node.ino,
12863
- ...node.kind === "directory" ? {} : { nlink: node.links }
12864
- });
12865
12999
  const handle = (fh) => {
12866
13000
  const found = handles.get(fh);
12867
13001
  if (!found) throw fsError2("EBADF", `handle ${fh}`);
@@ -12874,11 +13008,16 @@ function tmpfs() {
12874
13008
  if (existing?.kind === "directory" && flags.write) throw fsError2("EISDIR", path);
12875
13009
  if (existing) return existing;
12876
13010
  if (!flags.create) throw fsError2("ENOENT", path);
12877
- const node = make("file", 33188);
13011
+ const node = make("file", 33188, dir, flags.owner);
12878
13012
  place(dir, name2, node);
12879
13013
  return node;
12880
13014
  }
12881
13015
  return {
13016
+ mount(info) {
13017
+ root.uid = idOption2(info.options.uid);
13018
+ root.gid = idOption2(info.options.gid);
13019
+ return void 0;
13020
+ },
12882
13021
  async getattr(path) {
12883
13022
  return attr(find(path));
12884
13023
  },
@@ -12911,10 +13050,10 @@ function tmpfs() {
12911
13050
  handle(fh);
12912
13051
  handles.delete(fh);
12913
13052
  },
12914
- async mkdir(path) {
13053
+ async mkdir(path, owner) {
12915
13054
  const { dir, name: name2 } = parent(path);
12916
13055
  if (dir.children.has(name2)) throw fsError2("EEXIST", path);
12917
- place(dir, name2, make("directory", 16877));
13056
+ place(dir, name2, make("directory", 16877, dir, owner));
12918
13057
  },
12919
13058
  async rmdir(path) {
12920
13059
  const { dir, name: name2 } = parent(path);
@@ -12961,10 +13100,10 @@ function tmpfs() {
12961
13100
  place(dir, name2, node);
12962
13101
  node.links++;
12963
13102
  },
12964
- async symlink(target, path) {
13103
+ async symlink(target, path, owner) {
12965
13104
  const { dir, name: name2 } = parent(path);
12966
13105
  if (dir.children.has(name2)) throw fsError2("EEXIST", path);
12967
- const node = make("symlink", 41471);
13106
+ const node = make("symlink", 41471, dir, owner && { ...owner, mode: 511 });
12968
13107
  node.target = target;
12969
13108
  place(dir, name2, node);
12970
13109
  },
@@ -12983,6 +13122,7 @@ function tmpfs() {
12983
13122
  node.mtime = Date.now();
12984
13123
  }
12985
13124
  if (change.mtime !== void 0) node.mtime = change.mtime;
13125
+ owned2(node, change);
12986
13126
  },
12987
13127
  async statfs() {
12988
13128
  return { bsize: 4096, blocks: 262144, bfree: 262144 };
@@ -13303,7 +13443,8 @@ var Launcher = class {
13303
13443
  this.mounts = new MountTable({
13304
13444
  open: (type, spec) => this.driver(type, spec),
13305
13445
  busy: (target) => [...this.openFiles].some((nodes) => nodes.holds(target)) || heldUnder(this.held, target),
13306
- changed: this.watchers.changed.bind(this.watchers)
13446
+ changed: this.watchers.changed.bind(this.watchers),
13447
+ owners: this.owners
13307
13448
  });
13308
13449
  this.base = this.watchers.wrap(this.mounts.wrap(options.fs));
13309
13450
  this.createWorker = options.createWorker;
@@ -13336,7 +13477,8 @@ var Launcher = class {
13336
13477
  const fs = withCommandDirs(
13337
13478
  this.base,
13338
13479
  async () => new Set((await this.commands()).keys()),
13339
- (dir) => this.userCommandDir(dir)
13480
+ (dir) => this.userCommandDir(dir),
13481
+ this.owners
13340
13482
  );
13341
13483
  this.nodes = new VfsNodes(fs, (path) => this.released(path));
13342
13484
  this.nodes.keepsOnDisk = true;
@@ -13866,7 +14008,15 @@ var Launcher = class {
13866
14008
  serveFilesystem(
13867
14009
  port2,
13868
14010
  tmpfs(),
13869
- { symlinks: true, links: true, chmod: true, linkTimes: true, ranges: true, attrTtl: 0 },
14011
+ {
14012
+ symlinks: true,
14013
+ links: true,
14014
+ chmod: true,
14015
+ owners: true,
14016
+ linkTimes: true,
14017
+ ranges: true,
14018
+ attrTtl: 0
14019
+ },
13870
14020
  { owned: true }
13871
14021
  );
13872
14022
  return {
@@ -13939,6 +14089,11 @@ var Launcher = class {
13939
14089
  void this.unlinked.sweep(entry.target);
13940
14090
  return entry;
13941
14091
  }
14092
+ mountedBy(spec, cred) {
14093
+ if (!this.owners || cred.euid === 0 && cred.egid === 0) return spec;
14094
+ const ids = { uid: String(cred.euid), gid: String(cred.egid) };
14095
+ return { ...spec, options: { ...ids, ...spec.options } };
14096
+ }
13942
14097
  mountRoot(path) {
13943
14098
  let root = "/";
13944
14099
  for (const { target } of this.mounts.list()) {
@@ -13982,7 +14137,7 @@ var Launcher = class {
13982
14137
  async processMount(pid, call, signal) {
13983
14138
  const spec = mountCall(call);
13984
14139
  await this.permit({ op: "mount", pid, ...spec, options: { ...spec.options } });
13985
- const entry = await this.mount(spec);
14140
+ const entry = await this.mount(this.mountedBy(spec, this.credOf(pid)));
13986
14141
  if (signal.aborted) this.umount(entry.target, true);
13987
14142
  }
13988
14143
  async processUmount(pid, target, flags) {