@ai-ecoverse/slicc-kernel 1.58.1 → 1.59.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -794,12 +794,12 @@ var OpfsFs = class {
794
794
  const base = dir === "/" ? "" : dir;
795
795
  const metas = await this.meta.get(children2.map(([name2]) => `${base}/${name2}`));
796
796
  const handed2 = this.owners ? await this.handedTo(dir) : void 0;
797
- const owned2 = (stat, entry) => handed2 ? { ...stat, ...ownOf(entry) ?? handed2 } : stat;
797
+ const owned3 = (stat, entry) => handed2 ? { ...stat, ...ownOf(entry) ?? handed2 } : stat;
798
798
  return Promise.all(
799
799
  children2.map(async ([name2, child], i) => {
800
- if (!isHandle(child)) return [name2, owned2(this.linkStat(child), child)];
800
+ if (!isHandle(child)) return [name2, owned3(this.linkStat(child), child)];
801
801
  const stat = await this.statOf(`${base}/${name2}`, child, metas[i]).catch(() => null);
802
- return [name2, stat && owned2(stat, metas[i])];
802
+ return [name2, stat && owned3(stat, metas[i])];
803
803
  })
804
804
  );
805
805
  }
@@ -830,9 +830,9 @@ var OpfsFs = class {
830
830
  const parent = await this.locate(parentOf2(path), true);
831
831
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
832
832
  await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
833
- const made = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
834
- await this.stampNew(made, owner, true);
835
- this.touch(parent.path, made);
833
+ const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
834
+ await this.stampNew(made2, owner, true);
835
+ this.touch(parent.path, made2);
836
836
  }
837
837
  async rm(path, options = {}) {
838
838
  let found;
@@ -2644,12 +2644,22 @@ var UserDb = class {
2644
2644
  async stampHome(row, shared) {
2645
2645
  const home = this.fs.resolvePath("/", row.home);
2646
2646
  if (!this.owners || !this.fs.chown || !HOME_DIR.test(home)) return;
2647
- if (await this.fs.realpath?.(home, false).catch(() => void 0) !== home) return;
2648
- if (!(await this.fs.lstat(home)).isDirectory) return;
2647
+ if (!await this.realDir(home)) return;
2649
2648
  const gid = shared?.gid ?? row.gid;
2650
2649
  await this.fs.chown(home, { uid: row.uid, gid, follow: false, kind: "adopt" });
2651
- const mode = shared ? 1512 : row.name.startsWith("scoop-") ? 488 : 448;
2652
- await this.fs.chmod(home, mode);
2650
+ const scoop = row.name.startsWith("scoop-");
2651
+ await this.fs.chmod(home, shared ? 1512 : scoop ? 488 : 448);
2652
+ const pnpm = `${home}/.local/share/pnpm`;
2653
+ if (await this.realDir(pnpm)) await this.fs.chmod(pnpm, scoop ? 488 : 448);
2654
+ }
2655
+ async realDir(path) {
2656
+ const parts = path.split("/").filter(Boolean);
2657
+ for (let n = 1; n <= parts.length; n++) {
2658
+ const at = `/${parts.slice(0, n).join("/")}`;
2659
+ const st = await this.fs.lstat(at).catch(() => void 0);
2660
+ if (!st?.isDirectory) return false;
2661
+ }
2662
+ return true;
2653
2663
  }
2654
2664
  async read(path, fallback) {
2655
2665
  try {
@@ -2666,15 +2676,17 @@ var UserDb = class {
2666
2676
  };
2667
2677
  }
2668
2678
  async provision(row) {
2679
+ const owner = this.owners ? { uid: row.uid, gid: row.gid } : void 0;
2680
+ const made2 = owner ? { recursive: true, owner } : { recursive: true };
2669
2681
  const pnpm = `${row.home}/.local/share/pnpm`;
2670
2682
  if (!await this.fs.exists(pnpm)) {
2671
- await this.fs.mkdir(pnpm, { recursive: true });
2683
+ await this.fs.mkdir(pnpm, made2);
2672
2684
  await this.fs.chmod(pnpm, row.name.startsWith("scoop-") ? 488 : 448);
2673
2685
  }
2674
2686
  const config = `${row.home}/.config/pnpm/config.yaml`;
2675
2687
  if (!await this.fs.exists(config)) {
2676
- await this.fs.mkdir(`${row.home}/.config/pnpm`, { recursive: true });
2677
- await this.fs.writeFile(config, PNPM_CONFIG);
2688
+ await this.fs.mkdir(`${row.home}/.config/pnpm`, made2);
2689
+ await this.fs.writeFile(config, PNPM_CONFIG, owner);
2678
2690
  }
2679
2691
  }
2680
2692
  async commit(passwd, group, then) {
@@ -2851,7 +2863,7 @@ async function fsCall(fs, method, args, owner) {
2851
2863
  return { result: bytes, transfer: [bytes.buffer] };
2852
2864
  }
2853
2865
  case "writeFile":
2854
- await fs.writeFile(path(0), args[1], owner);
2866
+ await fs.writeFile(path(0), args[1], created(owner, 438));
2855
2867
  return { result: true };
2856
2868
  case "stat":
2857
2869
  return { result: await fs.stat(path(0)) };
@@ -2860,7 +2872,10 @@ async function fsCall(fs, method, args, owner) {
2860
2872
  case "readdir":
2861
2873
  return { result: await fs.readdir(path(0)) };
2862
2874
  case "mkdir":
2863
- await fs.mkdir(path(0), owner ? { recursive: true, owner } : { recursive: true });
2875
+ await fs.mkdir(
2876
+ path(0),
2877
+ owner ? { recursive: true, owner: made(owner) } : { recursive: true }
2878
+ );
2864
2879
  return { result: true };
2865
2880
  case "rm":
2866
2881
  await fs.rm(path(0), { recursive: true, force: args[1] === true });
@@ -2871,7 +2886,7 @@ async function fsCall(fs, method, args, owner) {
2871
2886
  case "realpath":
2872
2887
  return { result: await realpath(fs, String(args[0])) };
2873
2888
  case "symlink":
2874
- await fs.symlink(String(args[0]), path(1), owner);
2889
+ await fs.symlink(String(args[0]), path(1), owner && { uid: owner.uid, gid: owner.gid });
2875
2890
  return { result: true };
2876
2891
  case "readlink":
2877
2892
  return { result: await fs.readlink(path(0)) };
@@ -2893,8 +2908,16 @@ function dialFor(l, req, open) {
2893
2908
  );
2894
2909
  return { result: port2, transfer: [port2] };
2895
2910
  }
2911
+ function created(owner, mode) {
2912
+ return owner && { uid: owner.uid, gid: owner.gid, mode: mode & ~owner.umask };
2913
+ }
2914
+ function made(owner) {
2915
+ const mode = 511 & ~owner.umask;
2916
+ return { uid: owner.uid, gid: owner.gid, mode, parents: mode | 192 };
2917
+ }
2896
2918
  function ownerFor(l, self) {
2897
- return l.owners && self ? { uid: self.uid, gid: self.gid } : void 0;
2919
+ if (!l.owners || !self) return void 0;
2920
+ return { uid: self.uid, gid: self.gid, umask: self.umask ?? 18 };
2898
2921
  }
2899
2922
  async function accountOf(l, user) {
2900
2923
  const found = await l.users.lookup(user);
@@ -5782,7 +5805,17 @@ async function followLinks(fs, path) {
5782
5805
  }
5783
5806
  return current;
5784
5807
  }
5785
- function withCommandDirs(fs, names, userDir = async () => void 0) {
5808
+ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false) {
5809
+ async function nearest(path) {
5810
+ const st = await fs.stat(path).catch(() => void 0);
5811
+ if (st || path === "/") return st ?? {};
5812
+ return nearest(path.slice(0, path.lastIndexOf("/")) || "/");
5813
+ }
5814
+ async function owned3(stat2, dir) {
5815
+ if (!owners) return stat2;
5816
+ const { uid = 0, gid = 0 } = dir === void 0 ? {} : await nearest(dir);
5817
+ return { ...stat2, uid, gid };
5818
+ }
5786
5819
  async function children2(path) {
5787
5820
  if (path === "/") return ["bin", "usr"];
5788
5821
  if (path === "/usr") return ["bin"];
@@ -5791,13 +5824,18 @@ function withCommandDirs(fs, names, userDir = async () => void 0) {
5791
5824
  return own?.size ? [...own] : void 0;
5792
5825
  }
5793
5826
  async function virtual(path) {
5794
- if (path === "/usr" || path === "/bin" || path === "/usr/bin") return synthetic(true, path);
5827
+ if (path === "/usr" || path === "/bin" || path === "/usr/bin") {
5828
+ return owned3(synthetic(true, path));
5829
+ }
5795
5830
  const name2 = COMMAND.exec(path)?.[1];
5796
- if (name2 !== void 0) return (await names()).has(name2) ? synthetic(false, path) : void 0;
5797
- if ((await userDir(path))?.size) return synthetic(true, path);
5831
+ if (name2 !== void 0) {
5832
+ return (await names()).has(name2) ? owned3(synthetic(false, path)) : void 0;
5833
+ }
5834
+ if ((await userDir(path))?.size) return owned3(synthetic(true, path), path);
5798
5835
  const slash = path.lastIndexOf("/");
5799
- const own = await userDir(path.slice(0, slash));
5800
- return own?.has(path.slice(slash + 1)) ? synthetic(false, path) : void 0;
5836
+ const dir = path.slice(0, slash);
5837
+ const own = await userDir(dir);
5838
+ return own?.has(path.slice(slash + 1)) ? owned3(synthetic(false, path), dir) : void 0;
5801
5839
  }
5802
5840
  async function linked(path) {
5803
5841
  return await virtual(path) ?? virtual(await followLinks(fs, path));
@@ -6192,18 +6230,18 @@ var FsWatchers = class {
6192
6230
  const overrides = {
6193
6231
  writeFile: after(fs.writeFile.bind(fs), (path) => [path]),
6194
6232
  mkdir: async (path, options) => {
6195
- const created = [];
6233
+ const created3 = [];
6196
6234
  if (options?.recursive) {
6197
6235
  const parts = normalizePath(path).split("/").filter(Boolean);
6198
6236
  for (let at = 1; at < parts.length; at++) {
6199
6237
  const ancestor = `/${parts.slice(0, at).join("/")}`;
6200
- if (created.length > 0 || !await fs.exists(ancestor)) created.push(ancestor);
6238
+ if (created3.length > 0 || !await fs.exists(ancestor)) created3.push(ancestor);
6201
6239
  }
6202
6240
  }
6203
6241
  try {
6204
6242
  await fs.mkdir(path, options);
6205
6243
  } finally {
6206
- this.changed(...created, path);
6244
+ this.changed(...created3, path);
6207
6245
  }
6208
6246
  },
6209
6247
  rm: after(fs.rm.bind(fs), (path) => [path]),
@@ -6482,7 +6520,7 @@ async function realTarget(fs, path) {
6482
6520
  }
6483
6521
 
6484
6522
  // src/mount/protocol.ts
6485
- var DRIVER_PROTOCOL = [1, 1];
6523
+ var DRIVER_PROTOCOL = [1, 2];
6486
6524
  function driverVersionError(theirs) {
6487
6525
  const major = Array.isArray(theirs) ? theirs[0] : void 0;
6488
6526
  if (major === DRIVER_PROTOCOL[0]) return void 0;
@@ -6584,6 +6622,19 @@ var DriverConnection = class _DriverConnection {
6584
6622
  };
6585
6623
 
6586
6624
  // src/mount/mount-fs.ts
6625
+ var S_ISGID3 = 1024;
6626
+ var ID = /^\d{1,10}$/;
6627
+ var MAX_ID2 = 4294967294;
6628
+ function idOption(value) {
6629
+ return value === void 0 ? 0 : Number(value);
6630
+ }
6631
+ function checkIds(options) {
6632
+ for (const key of ["uid", "gid"]) {
6633
+ const value = options[key];
6634
+ if (value === void 0) continue;
6635
+ if (!ID.test(value) || Number(value) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value}`);
6636
+ }
6637
+ }
6587
6638
  var MAX_LINKS5 = 40;
6588
6639
  var STALE_RETRIES = 3;
6589
6640
  var SETTLE_ROUNDS = 4;
@@ -6750,31 +6801,35 @@ var HeldPaths = class extends Map {
6750
6801
  };
6751
6802
  var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
6752
6803
  var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
6753
- function inodeless(mount, attr) {
6754
- if (attr.ino !== void 0) return false;
6804
+ function inodeless(mount, attr2) {
6805
+ if (attr2.ino !== void 0) return false;
6755
6806
  if (mount.caps.links) mount.caps = { ...mount.caps, links: false };
6756
6807
  return true;
6757
6808
  }
6758
- function statOf(attr, path, mount) {
6759
- const mtime = new Date(attr.mtime);
6760
- const fallback = attr.kind === "directory" ? 16877 : attr.kind === "symlink" ? 41471 : 33188;
6809
+ function statOf(attr2, path, mount) {
6810
+ const mtime = new Date(attr2.mtime);
6811
+ const fallback = attr2.kind === "directory" ? 16877 : attr2.kind === "symlink" ? 41471 : 33188;
6761
6812
  return {
6762
- isFile: attr.kind === "file",
6763
- isDirectory: attr.kind === "directory",
6764
- isSymbolicLink: attr.kind === "symlink",
6765
- size: attr.size,
6766
- mode: attr.mode ?? fallback,
6813
+ isFile: attr2.kind === "file",
6814
+ isDirectory: attr2.kind === "directory",
6815
+ isSymbolicLink: attr2.kind === "symlink",
6816
+ size: attr2.size,
6817
+ mode: attr2.mode ?? fallback,
6767
6818
  mtime,
6768
6819
  atime: mtime,
6769
6820
  ctime: mtime,
6770
- ino: attr.ino ?? inodeOf(path),
6771
- ...!inodeless(mount, attr) && attr.nlink !== void 0 ? { nlink: attr.nlink } : {},
6821
+ ino: attr2.ino ?? inodeOf(path),
6822
+ ...!inodeless(mount, attr2) && attr2.nlink !== void 0 ? { nlink: attr2.nlink } : {},
6772
6823
  dev: mount.dev,
6773
6824
  ...readonly(mount) ? { readonly: true } : {},
6774
6825
  ...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {},
6775
6826
  ...mount.caps.ranges ? { ranged: true } : {},
6776
6827
  ...mount.caps.links ? { linkable: true } : {},
6777
- ...attr.etag !== void 0 ? { version: attr.etag } : {}
6828
+ ...attr2.etag !== void 0 ? { version: attr2.etag } : {},
6829
+ ...mount.owners ? {
6830
+ uid: (mount.caps.owners ? attr2.uid : void 0) ?? idOption(mount.options.uid),
6831
+ gid: (mount.caps.owners ? attr2.gid : void 0) ?? idOption(mount.options.gid)
6832
+ } : {}
6778
6833
  };
6779
6834
  }
6780
6835
  function concat4(chunks, total) {
@@ -6867,6 +6922,7 @@ var MountTable = class {
6867
6922
  const st = await fs.stat(target);
6868
6923
  if (!st.isDirectory) throw errnoError("ENOTDIR", target);
6869
6924
  const options = { ...spec.options };
6925
+ if (this.deps.owners) checkIds(options);
6870
6926
  const maxFile = options.maxfile !== void 0 ? parseSize(options.maxfile) : void 0;
6871
6927
  const opened2 = await this.deps.open(spec.type, { ...spec, target, options });
6872
6928
  let mounted;
@@ -6912,6 +6968,7 @@ var MountTable = class {
6912
6968
  attrs: /* @__PURE__ */ new Map(),
6913
6969
  lists: /* @__PURE__ */ new Map(),
6914
6970
  epoch: 0,
6971
+ owners: this.deps.owners === true,
6915
6972
  dispose: () => opened2.dispose(),
6916
6973
  ...opened2.present ? { present: opened2.present } : {}
6917
6974
  };
@@ -7033,15 +7090,15 @@ var MountTable = class {
7033
7090
  if (cached && cached.expires > Date.now()) return cached.value;
7034
7091
  const seen = mount.epoch;
7035
7092
  const session = this.sessions.get(path);
7036
- const attr = await mount.conn.call({ op: "getattr", path: rel2 });
7093
+ const attr2 = await mount.conn.call({ op: "getattr", path: rel2 });
7037
7094
  if (mount.epoch !== seen) {
7038
7095
  const moved2 = this.sessions.get(path) !== session || this.committing.has(path);
7039
7096
  if (settled && moved2 && round < SETTLE_ROUNDS) continue;
7040
- return attr;
7097
+ return attr2;
7041
7098
  }
7042
7099
  const ttl = mount.caps.attrTtl ?? 1e3;
7043
- if (ttl > 0) mount.attrs.set(path, { value: attr, expires: Date.now() + ttl });
7044
- return attr;
7100
+ if (ttl > 0) mount.attrs.set(path, { value: attr2, expires: Date.now() + ttl });
7101
+ return attr2;
7045
7102
  }
7046
7103
  }
7047
7104
  async readdir(mount, path, rel2) {
@@ -7088,28 +7145,43 @@ var MountFs = class {
7088
7145
  const found = this.at(path);
7089
7146
  if (!found) return path;
7090
7147
  const real = normalizePath(path);
7091
- let attr;
7148
+ let attr2;
7092
7149
  try {
7093
- attr = await this.table.getattr(found.mount, real, found.rel);
7150
+ attr2 = await this.table.getattr(found.mount, real, found.rel);
7094
7151
  } catch (err) {
7095
7152
  if (create && err.code === "ENOENT") return real;
7096
7153
  throw err;
7097
7154
  }
7098
- if (attr.kind !== "symlink") return real;
7155
+ if (attr2.kind !== "symlink") return real;
7099
7156
  if (hops >= MAX_LINKS5) throw errnoError("ELOOP", path);
7100
7157
  const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
7101
7158
  return this.follow(this.base.resolvePath(parentOf6(real), target), hops + 1, create);
7102
7159
  }
7103
- async open(mount, rel2, write) {
7160
+ async open(mount, rel2, write, owner) {
7104
7161
  return await mount.conn.call({
7105
7162
  op: "open",
7106
7163
  path: rel2,
7107
7164
  write,
7108
7165
  create: write,
7109
7166
  truncate: write,
7110
- exclusive: false
7167
+ exclusive: false,
7168
+ ...owner
7111
7169
  });
7112
7170
  }
7171
+ async driverOwner(found, real, owner, directory) {
7172
+ const { mount, rel: rel2 } = found;
7173
+ if (!owner || !mount.owners || !mount.caps.owners) return void 0;
7174
+ const dir = parentOf6(real);
7175
+ const above = statOf(await this.table.getattr(mount, dir, parentOf6(rel2)), dir, mount);
7176
+ const setgid = (above.mode & S_ISGID3) !== 0;
7177
+ let mode = owner.mode;
7178
+ if (directory && setgid) mode = (mode ?? 493) | S_ISGID3;
7179
+ return {
7180
+ uid: owner.uid,
7181
+ gid: setgid ? above.gid : owner.gid,
7182
+ ...mode !== void 0 ? { mode } : {}
7183
+ };
7184
+ }
7113
7185
  resolvePath(base, path) {
7114
7186
  return this.base.resolvePath(base, path);
7115
7187
  }
@@ -7131,9 +7203,9 @@ var MountFs = class {
7131
7203
  }
7132
7204
  async readWhole(mount, real, rel2) {
7133
7205
  await this.table.commit(real);
7134
- const attr = await this.table.getattr(mount, real, rel2);
7135
- if (attr.kind === "directory") throw errnoError("EISDIR", real);
7136
- if (mount.maxFile > 0 && attr.size > mount.maxFile) {
7206
+ const attr2 = await this.table.getattr(mount, real, rel2);
7207
+ if (attr2.kind === "directory") throw errnoError("EISDIR", real);
7208
+ if (mount.maxFile > 0 && attr2.size > mount.maxFile) {
7137
7209
  throw errnoError("EFBIG", `${real} is larger than this mount's maxfile (${mount.maxFile})`);
7138
7210
  }
7139
7211
  const fh = await this.open(mount, rel2, false);
@@ -7151,7 +7223,7 @@ var MountFs = class {
7151
7223
  if (bytes.length === 0) break;
7152
7224
  chunks.push(bytes);
7153
7225
  total += bytes.length;
7154
- if (bytes.length < chunk2 && total >= attr.size) break;
7226
+ if (bytes.length < chunk2 && total >= attr2.size) break;
7155
7227
  }
7156
7228
  } finally {
7157
7229
  await mount.conn.call({ op: "release", fh });
@@ -7171,18 +7243,19 @@ var MountFs = class {
7171
7243
  `${real} would be larger than this mount's maxfile (${mount.maxFile})`
7172
7244
  );
7173
7245
  }
7246
+ const made2 = await this.driverOwner(found, real, owner, false);
7174
7247
  for (let attempt = 1; ; attempt++) {
7175
7248
  try {
7176
- return await this.writeWhole(mount, real, rel2, bytes);
7249
+ return await this.writeWhole(mount, real, rel2, bytes, made2);
7177
7250
  } catch (err) {
7178
7251
  if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
7179
7252
  }
7180
7253
  }
7181
7254
  }
7182
- async writeWhole(mount, real, rel2, bytes) {
7255
+ async writeWhole(mount, real, rel2, bytes, owner) {
7183
7256
  await this.table.commit(real);
7184
7257
  this.table.forget(mount, real);
7185
- const fh = await this.open(mount, rel2, true);
7258
+ const fh = await this.open(mount, rel2, true, owner);
7186
7259
  const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
7187
7260
  try {
7188
7261
  for (let offset = 0; offset < bytes.length; offset += chunk2) {
@@ -7328,9 +7401,9 @@ var MountFs = class {
7328
7401
  found.mount
7329
7402
  );
7330
7403
  }
7331
- statWriting(attr, real, mount) {
7404
+ statWriting(attr2, real, mount) {
7332
7405
  const ends = (this.table.writing(real)?.extents ?? []).map(([, end]) => end);
7333
- return statOf({ ...attr, size: Math.max(attr.size, ...ends) }, real, mount);
7406
+ return statOf({ ...attr2, size: Math.max(attr2.size, ...ends) }, real, mount);
7334
7407
  }
7335
7408
  async stat(path) {
7336
7409
  const real = await this.follow(path);
@@ -7369,10 +7442,12 @@ var MountFs = class {
7369
7442
  const { mount, rel: rel2 } = found;
7370
7443
  const real = normalizePath(path);
7371
7444
  this.writable(mount, real);
7445
+ const { owner } = options;
7372
7446
  if (!options.recursive) {
7373
7447
  if (rel2 === "/") throw errnoError("EEXIST", real);
7448
+ const made2 = await this.driverOwner(found, real, owner, true);
7374
7449
  this.table.forget(mount, real);
7375
- await mount.conn.call({ op: "mkdir", path: rel2 });
7450
+ await mount.conn.call({ op: "mkdir", path: rel2, ...made2 });
7376
7451
  return;
7377
7452
  }
7378
7453
  const parts = rel2.split("/").filter(Boolean);
@@ -7382,8 +7457,10 @@ var MountFs = class {
7382
7457
  const exists = await this.table.getattr(mount, full, sub).catch(() => void 0);
7383
7458
  if (exists?.kind === "directory") continue;
7384
7459
  if (exists) throw errnoError("ENOTDIR", full);
7460
+ const own = n === parts.length ? owner : owner && { ...owner, mode: owner.parents };
7461
+ const made2 = await this.driverOwner({ mount, rel: sub }, full, own, true);
7385
7462
  this.table.forget(mount, full);
7386
- await mount.conn.call({ op: "mkdir", path: sub });
7463
+ await mount.conn.call({ op: "mkdir", path: sub, ...made2 });
7387
7464
  }
7388
7465
  }
7389
7466
  async rm(path, options = {}) {
@@ -7394,21 +7471,21 @@ var MountFs = class {
7394
7471
  if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
7395
7472
  this.writable(mount, real);
7396
7473
  await this.table.commitUnder(real);
7397
- let attr;
7474
+ let attr2;
7398
7475
  try {
7399
- attr = await this.table.getattr(mount, real, rel2);
7476
+ attr2 = await this.table.getattr(mount, real, rel2);
7400
7477
  } catch (err) {
7401
7478
  if (options.force && err.code === "ENOENT") return;
7402
7479
  throw err;
7403
7480
  }
7404
7481
  this.table.forget(mount, real);
7405
- if (attr.kind === "directory" && options.recursive) {
7482
+ if (attr2.kind === "directory" && options.recursive) {
7406
7483
  for (const entry of await this.table.readdir(mount, real, rel2)) {
7407
7484
  await this.rm(`${real}/${entry.name}`, { recursive: true, force: options.force ?? false });
7408
7485
  }
7409
7486
  this.table.forget(mount, real);
7410
7487
  }
7411
- await mount.conn.call({ op: attr.kind === "directory" ? "rmdir" : "unlink", path: rel2 });
7488
+ await mount.conn.call({ op: attr2.kind === "directory" ? "rmdir" : "unlink", path: rel2 });
7412
7489
  this.table.forget(mount, real);
7413
7490
  }
7414
7491
  async rename(from, to) {
@@ -7462,8 +7539,10 @@ var MountFs = class {
7462
7539
  this.writable(found.mount, normalizePath(path));
7463
7540
  if (!found.mount.caps.symlinks)
7464
7541
  throw errnoError("EPERM", `${path}: this file system has no symlinks`);
7542
+ const link2 = owner && { uid: owner.uid, gid: owner.gid };
7543
+ const made2 = await this.driverOwner(found, normalizePath(path), link2, false);
7465
7544
  this.table.forget(found.mount, normalizePath(path));
7466
- await found.mount.conn.call({ op: "symlink", target, path: found.rel });
7545
+ await found.mount.conn.call({ op: "symlink", target, path: found.rel, ...made2 });
7467
7546
  }
7468
7547
  async realpath(path, follow2) {
7469
7548
  if (this.at(path) || !this.base.realpath) throw errnoError("ENOSYS", path);
@@ -7478,13 +7557,29 @@ var MountFs = class {
7478
7557
  }
7479
7558
  async chown(path, change) {
7480
7559
  const real = change.follow === false || change.kind === "new" ? path : await this.follow(path);
7481
- if (this.at(real)) {
7482
- if (change.kind === "new") return;
7483
- throw errnoError("EPERM", `${path}: this file system keeps no owners`);
7484
- }
7560
+ const found = this.at(real);
7561
+ if (found) return this.chownMounted(found, normalizePath(real), change);
7485
7562
  if (!this.base.chown) throw errnoError("EPERM", path);
7486
7563
  return this.base.chown(real, change);
7487
7564
  }
7565
+ async chownMounted(found, real, change) {
7566
+ const { mount, rel: rel2 } = found;
7567
+ if (!mount.owners || !mount.caps.owners) {
7568
+ if (change.kind === "new") return;
7569
+ throw errnoError("EPERM", `${real}: this file system keeps no owners`);
7570
+ }
7571
+ this.writable(mount, real);
7572
+ const { uid, gid, mode } = change;
7573
+ const fresh = change.kind === "new" && { uid, gid, mode };
7574
+ const set2 = fresh ? await this.driverOwner(found, real, fresh, false) : change;
7575
+ this.table.forget(mount, real);
7576
+ const attrs = Object.entries({ uid: set2.uid, gid: set2.gid, mode: set2.mode });
7577
+ await mount.conn.call({
7578
+ op: "setattr",
7579
+ path: rel2,
7580
+ ...Object.fromEntries(attrs.filter(([, value]) => value !== void 0))
7581
+ });
7582
+ }
7488
7583
  async chmod(path, mode) {
7489
7584
  const found = this.at(path);
7490
7585
  if (!found) return this.base.chmod(path, mode);
@@ -7591,7 +7686,10 @@ function mkdirOptions(entry, mode = 511) {
7591
7686
  }
7592
7687
  async function chown(entry, path, req) {
7593
7688
  const cred = entry.cred?.();
7594
- if (!cred) return done;
7689
+ if (!cred) {
7690
+ await (req.op === "chown" ? entry.fs.stat(path) : entry.fs.lstat(path));
7691
+ return done;
7692
+ }
7595
7693
  await chownAs(entry.fs, cred, path, {
7596
7694
  ...req.uid !== void 0 ? { uid: req.uid } : {},
7597
7695
  ...req.gid !== void 0 ? { gid: req.gid } : {},
@@ -8681,6 +8779,11 @@ var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
8681
8779
  function message(e) {
8682
8780
  return e instanceof Error ? e.message : String(e);
8683
8781
  }
8782
+ var ERRNO = /^E[A-Z]+$|^EAI_[A-Z]+$/;
8783
+ function errnoOf(e) {
8784
+ const code = e?.code;
8785
+ return typeof code === "string" && ERRNO.test(code) ? code : "ECONNREFUSED";
8786
+ }
8684
8787
  function fail(errno, text) {
8685
8788
  return { ok: false, errno, message: text };
8686
8789
  }
@@ -8724,13 +8827,13 @@ var WsHandles = class {
8724
8827
  let socket;
8725
8828
  try {
8726
8829
  socket = await transport.websocket({
8727
- url: req.url,
8830
+ url: toHostLoopback(req.url),
8728
8831
  protocols: req.protocols,
8729
8832
  headers: req.headers,
8730
8833
  signal: entry.abort.signal
8731
8834
  });
8732
8835
  } catch (e) {
8733
- return fail("ECONNREFUSED", message(e));
8836
+ return fail(errnoOf(e), message(e));
8734
8837
  }
8735
8838
  if (!this.open.has(handle)) {
8736
8839
  socket.close(1001, "the program went away");
@@ -11415,6 +11518,7 @@ function spawnWasmProcess(opts) {
11415
11518
  cwd: opts.cwd,
11416
11519
  sab: tsab,
11417
11520
  cred: copyCred(process.cred),
11521
+ owners: opts.owners === true,
11418
11522
  ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
11419
11523
  thread
11420
11524
  };
@@ -11808,6 +11912,17 @@ var SettlingChildren = class {
11808
11912
  };
11809
11913
 
11810
11914
  // src/mount/driver.ts
11915
+ function ownerOf2(req) {
11916
+ const owner = {
11917
+ ...req.uid !== void 0 ? { uid: req.uid } : {},
11918
+ ...req.gid !== void 0 ? { gid: req.gid } : {},
11919
+ ...req.mode !== void 0 ? { mode: req.mode } : {}
11920
+ };
11921
+ return Object.keys(owner).length > 0 ? owner : void 0;
11922
+ }
11923
+ function withOwner(base, owner) {
11924
+ return owner ? { ...base, owner } : base;
11925
+ }
11811
11926
  function fsError2(code, message3 = code) {
11812
11927
  return Object.assign(new Error(message3), { code });
11813
11928
  }
@@ -11818,13 +11933,19 @@ async function dispatch(h, req) {
11818
11933
  case "readdir":
11819
11934
  return h.readdir(req.path);
11820
11935
  case "open":
11821
- return h.open(req.path, {
11822
- write: req.write,
11823
- create: req.create,
11824
- truncate: req.truncate,
11825
- exclusive: req.exclusive,
11826
- ...req.ifMatch !== void 0 ? { ifMatch: req.ifMatch } : {}
11827
- });
11936
+ return h.open(
11937
+ req.path,
11938
+ withOwner(
11939
+ {
11940
+ write: req.write,
11941
+ create: req.create,
11942
+ truncate: req.truncate,
11943
+ exclusive: req.exclusive,
11944
+ ...req.ifMatch !== void 0 ? { ifMatch: req.ifMatch } : {}
11945
+ },
11946
+ ownerOf2(req)
11947
+ )
11948
+ );
11828
11949
  case "read":
11829
11950
  return h.read(req.fh, req.offset, req.size);
11830
11951
  case "write":
@@ -11832,7 +11953,7 @@ async function dispatch(h, req) {
11832
11953
  case "release":
11833
11954
  return h.release(req.fh);
11834
11955
  case "mkdir":
11835
- return h.mkdir(req.path);
11956
+ return ownerOf2(req) ? h.mkdir(req.path, ownerOf2(req)) : h.mkdir(req.path);
11836
11957
  case "rmdir":
11837
11958
  return h.rmdir(req.path);
11838
11959
  case "unlink":
@@ -11844,7 +11965,7 @@ async function dispatch(h, req) {
11844
11965
  return h.link(req.from, req.to);
11845
11966
  case "symlink":
11846
11967
  if (!h.symlink) throw fsError2("EPERM", "this file system has no symlinks");
11847
- return h.symlink(req.target, req.path);
11968
+ return ownerOf2(req) ? h.symlink(req.target, req.path, ownerOf2(req)) : h.symlink(req.target, req.path);
11848
11969
  case "readlink":
11849
11970
  if (!h.readlink) throw fsError2("EINVAL", `${req.path} is not a symlink`);
11850
11971
  return h.readlink(req.path);
@@ -11852,7 +11973,9 @@ async function dispatch(h, req) {
11852
11973
  return h.setattr?.(req.path, {
11853
11974
  ...req.mode !== void 0 ? { mode: req.mode } : {},
11854
11975
  ...req.mtime !== void 0 ? { mtime: req.mtime } : {},
11855
- ...req.size !== void 0 ? { size: req.size } : {}
11976
+ ...req.size !== void 0 ? { size: req.size } : {},
11977
+ ...req.uid !== void 0 ? { uid: req.uid } : {},
11978
+ ...req.gid !== void 0 ? { gid: req.gid } : {}
11856
11979
  });
11857
11980
  case "statfs":
11858
11981
  return h.statfs?.() ?? null;
@@ -12003,8 +12126,8 @@ function kernelFsHandlers(fs) {
12003
12126
  getattr: async (path) => attrOf(await fs.lstat(path)),
12004
12127
  readdir: async (path) => (await fs.readdirStat(path)).flatMap(([name2, st]) => {
12005
12128
  if (!st) return [];
12006
- const attr = attrOf(st);
12007
- return [{ name: name2, kind: attr.kind, attr }];
12129
+ const attr2 = attrOf(st);
12130
+ return [{ name: name2, kind: attr2.kind, attr: attr2 }];
12008
12131
  }),
12009
12132
  async open(path, flags) {
12010
12133
  const st = await existing(path);
@@ -12434,7 +12557,7 @@ function hostfsHandlers(current, againMs = RESTORE_AGAIN_MS) {
12434
12557
  "list",
12435
12558
  { path: rel(path) }
12436
12559
  );
12437
- return entries.map(({ name: name2, attr }) => ({ name: name2, kind: attr.kind, attr }));
12560
+ return entries.map(({ name: name2, attr: attr2 }) => ({ name: name2, kind: attr2.kind, attr: attr2 }));
12438
12561
  },
12439
12562
  async open(path, flags) {
12440
12563
  const client = current();
@@ -12924,8 +13047,8 @@ var ChunkedBytes = class {
12924
13047
  }
12925
13048
  return out;
12926
13049
  }
12927
- write(offset, bytes, owned2 = false) {
12928
- const whole = owned2 && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
13050
+ write(offset, bytes, owned3 = false) {
13051
+ const whole = owned3 && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
12929
13052
  for (let done4 = 0; done4 < bytes.length; ) {
12930
13053
  const at = offset + done4;
12931
13054
  const index = Math.floor(at / CHUNK3);
@@ -12958,18 +13081,42 @@ var ChunkedBytes = class {
12958
13081
  };
12959
13082
 
12960
13083
  // src/mount/tmpfs.ts
12961
- function tmpfs() {
12962
- let inodes = 0;
12963
- const make = (kind, mode) => ({
13084
+ function idOption2(value) {
13085
+ return value !== void 0 && /^\d+$/.test(value) ? Number(value) : 0;
13086
+ }
13087
+ function created2(ino, kind, mode, from, owner) {
13088
+ return {
12964
13089
  kind,
12965
- ino: ++inodes,
12966
- mode,
13090
+ ino,
13091
+ mode: owner?.mode === void 0 ? mode : mode & ~4095 | owner.mode & 4095,
12967
13092
  mtime: Date.now(),
12968
13093
  data: new ChunkedBytes(),
12969
13094
  target: "",
12970
13095
  children: /* @__PURE__ */ new Map(),
12971
- links: 1
12972
- });
13096
+ links: 1,
13097
+ uid: owner?.uid ?? from?.uid ?? 0,
13098
+ gid: owner?.gid ?? from?.gid ?? 0
13099
+ };
13100
+ }
13101
+ function owned2(node, change) {
13102
+ if (change.uid !== void 0) node.uid = change.uid;
13103
+ if (change.gid !== void 0) node.gid = change.gid;
13104
+ }
13105
+ function attr(node) {
13106
+ return {
13107
+ kind: node.kind,
13108
+ size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
13109
+ mtime: node.mtime,
13110
+ mode: node.mode,
13111
+ ino: node.ino,
13112
+ ...node.kind === "directory" ? {} : { nlink: node.links },
13113
+ uid: node.uid,
13114
+ gid: node.gid
13115
+ };
13116
+ }
13117
+ function tmpfs() {
13118
+ let inodes = 0;
13119
+ const make = (kind, mode, from, owner) => created2(++inodes, kind, mode, from, owner);
12973
13120
  const root = make("directory", 16877);
12974
13121
  const handles = /* @__PURE__ */ new Map();
12975
13122
  let nextFh = 0;
@@ -12996,14 +13143,6 @@ function tmpfs() {
12996
13143
  if (dir.kind !== "directory") throw fsError2("ENOTDIR", path);
12997
13144
  return { dir, name: name2 };
12998
13145
  }
12999
- const attr = (node) => ({
13000
- kind: node.kind,
13001
- size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
13002
- mtime: node.mtime,
13003
- mode: node.mode,
13004
- ino: node.ino,
13005
- ...node.kind === "directory" ? {} : { nlink: node.links }
13006
- });
13007
13146
  const handle = (fh) => {
13008
13147
  const found = handles.get(fh);
13009
13148
  if (!found) throw fsError2("EBADF", `handle ${fh}`);
@@ -13016,11 +13155,16 @@ function tmpfs() {
13016
13155
  if (existing?.kind === "directory" && flags.write) throw fsError2("EISDIR", path);
13017
13156
  if (existing) return existing;
13018
13157
  if (!flags.create) throw fsError2("ENOENT", path);
13019
- const node = make("file", 33188);
13158
+ const node = make("file", 33188, dir, flags.owner);
13020
13159
  place(dir, name2, node);
13021
13160
  return node;
13022
13161
  }
13023
13162
  return {
13163
+ mount(info) {
13164
+ root.uid = idOption2(info.options.uid);
13165
+ root.gid = idOption2(info.options.gid);
13166
+ return void 0;
13167
+ },
13024
13168
  async getattr(path) {
13025
13169
  return attr(find(path));
13026
13170
  },
@@ -13053,10 +13197,10 @@ function tmpfs() {
13053
13197
  handle(fh);
13054
13198
  handles.delete(fh);
13055
13199
  },
13056
- async mkdir(path) {
13200
+ async mkdir(path, owner) {
13057
13201
  const { dir, name: name2 } = parent(path);
13058
13202
  if (dir.children.has(name2)) throw fsError2("EEXIST", path);
13059
- place(dir, name2, make("directory", 16877));
13203
+ place(dir, name2, make("directory", 16877, dir, owner));
13060
13204
  },
13061
13205
  async rmdir(path) {
13062
13206
  const { dir, name: name2 } = parent(path);
@@ -13103,10 +13247,10 @@ function tmpfs() {
13103
13247
  place(dir, name2, node);
13104
13248
  node.links++;
13105
13249
  },
13106
- async symlink(target, path) {
13250
+ async symlink(target, path, owner) {
13107
13251
  const { dir, name: name2 } = parent(path);
13108
13252
  if (dir.children.has(name2)) throw fsError2("EEXIST", path);
13109
- const node = make("symlink", 41471);
13253
+ const node = make("symlink", 41471, dir, owner && { ...owner, mode: 511 });
13110
13254
  node.target = target;
13111
13255
  place(dir, name2, node);
13112
13256
  },
@@ -13125,6 +13269,7 @@ function tmpfs() {
13125
13269
  node.mtime = Date.now();
13126
13270
  }
13127
13271
  if (change.mtime !== void 0) node.mtime = change.mtime;
13272
+ owned2(node, change);
13128
13273
  },
13129
13274
  async statfs() {
13130
13275
  return { bsize: 4096, blocks: 262144, bfree: 262144 };
@@ -13445,7 +13590,8 @@ var Launcher = class {
13445
13590
  this.mounts = new MountTable({
13446
13591
  open: (type, spec) => this.driver(type, spec),
13447
13592
  busy: (target) => [...this.openFiles].some((nodes) => nodes.holds(target)) || heldUnder(this.held, target),
13448
- changed: this.watchers.changed.bind(this.watchers)
13593
+ changed: this.watchers.changed.bind(this.watchers),
13594
+ owners: this.owners
13449
13595
  });
13450
13596
  this.base = this.watchers.wrap(this.mounts.wrap(options.fs));
13451
13597
  this.createWorker = options.createWorker;
@@ -13478,7 +13624,8 @@ var Launcher = class {
13478
13624
  const fs = withCommandDirs(
13479
13625
  this.base,
13480
13626
  async () => new Set((await this.commands()).keys()),
13481
- (dir) => this.userCommandDir(dir)
13627
+ (dir) => this.userCommandDir(dir),
13628
+ this.owners
13482
13629
  );
13483
13630
  this.nodes = new VfsNodes(fs, (path) => this.released(path));
13484
13631
  this.nodes.keepsOnDisk = true;
@@ -14008,7 +14155,15 @@ var Launcher = class {
14008
14155
  serveFilesystem(
14009
14156
  port2,
14010
14157
  tmpfs(),
14011
- { symlinks: true, links: true, chmod: true, linkTimes: true, ranges: true, attrTtl: 0 },
14158
+ {
14159
+ symlinks: true,
14160
+ links: true,
14161
+ chmod: true,
14162
+ owners: true,
14163
+ linkTimes: true,
14164
+ ranges: true,
14165
+ attrTtl: 0
14166
+ },
14012
14167
  { owned: true }
14013
14168
  );
14014
14169
  return {
@@ -14081,6 +14236,11 @@ var Launcher = class {
14081
14236
  void this.unlinked.sweep(entry.target);
14082
14237
  return entry;
14083
14238
  }
14239
+ mountedBy(spec, cred) {
14240
+ if (!this.owners || cred.euid === 0 && cred.egid === 0) return spec;
14241
+ const ids = { uid: String(cred.euid), gid: String(cred.egid) };
14242
+ return { ...spec, options: { ...ids, ...spec.options } };
14243
+ }
14084
14244
  mountRoot(path) {
14085
14245
  let root = "/";
14086
14246
  for (const { target } of this.mounts.list()) {
@@ -14124,7 +14284,7 @@ var Launcher = class {
14124
14284
  async processMount(pid, call, signal) {
14125
14285
  const spec = mountCall(call);
14126
14286
  await this.permit({ op: "mount", pid, ...spec, options: { ...spec.options } });
14127
- const entry = await this.mount(spec);
14287
+ const entry = await this.mount(this.mountedBy(spec, this.credOf(pid)));
14128
14288
  if (signal.aborted) this.umount(entry.target, true);
14129
14289
  }
14130
14290
  async processUmount(pid, target, flags) {