@ai-ecoverse/slicc-kernel 1.127.0 → 1.128.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
@@ -4062,6 +4062,12 @@ var ProtectedFs = class {
4062
4062
  st && marked(`${dir}/${name2}`, st)
4063
4063
  ]);
4064
4064
  },
4065
+ ...fs.copyFile ? {
4066
+ async copyFile(...args) {
4067
+ await check(args[1], false);
4068
+ return fs.copyFile?.(...args);
4069
+ }
4070
+ } : {},
4065
4071
  ...fs.createExclusive ? {
4066
4072
  async createExclusive(...args) {
4067
4073
  await check(args[0], true);
@@ -4593,7 +4599,7 @@ var OpfsFs = class {
4593
4599
  }
4594
4600
  async recounting(handle, target) {
4595
4601
  if (this.counted.length === 0) return;
4596
- const where = await this.root.resolve(handle).catch(() => null);
4602
+ const where = await this.io(this.root.resolve(handle)).catch(() => null);
4597
4603
  await this.counting(where ? `/${where.join("/")}` : target);
4598
4604
  }
4599
4605
  resolvePath(base, path) {
@@ -4795,9 +4801,17 @@ var OpfsFs = class {
4795
4801
  }
4796
4802
  exact = 0;
4797
4803
  calls = 0;
4804
+ others = 0;
4798
4805
  opfsCalls() {
4799
4806
  return this.calls;
4800
4807
  }
4808
+ opfsOps() {
4809
+ return this.calls + this.others;
4810
+ }
4811
+ io(request) {
4812
+ this.others++;
4813
+ return request;
4814
+ }
4801
4815
  fresh() {
4802
4816
  this.changes++;
4803
4817
  this.absent.clear();
@@ -4821,7 +4835,13 @@ var OpfsFs = class {
4821
4835
  this.absent.set(path, performance.now());
4822
4836
  }
4823
4837
  remember(path, handle, looked = false) {
4824
- if (handle.kind !== "file") return handle;
4838
+ if (handle.kind !== "file") {
4839
+ if (looked) {
4840
+ this.dirs.set(path, handle);
4841
+ this.checked.set(handle, performance.now());
4842
+ }
4843
+ return handle;
4844
+ }
4825
4845
  if (looked) {
4826
4846
  this.found.set(handle, performance.now());
4827
4847
  this.glances.delete(handle);
@@ -5202,7 +5222,7 @@ var OpfsFs = class {
5202
5222
  async readFileBuffer(path) {
5203
5223
  const bytes = await this.reading(
5204
5224
  path,
5205
- async (handle) => new Uint8Array(await (await handle.getFile()).arrayBuffer())
5225
+ async (handle) => new Uint8Array(await (await this.io(handle.getFile())).arrayBuffer())
5206
5226
  );
5207
5227
  await this.refuseNode(path, bytes.length === 0);
5208
5228
  return bytes;
@@ -5210,7 +5230,7 @@ var OpfsFs = class {
5210
5230
  async readVersioned(path, cached = false) {
5211
5231
  return this.reading(path, async (handle) => {
5212
5232
  const stamp = await this.stamp(path, cached);
5213
- const file = await handle.getFile();
5233
+ const file = await this.io(handle.getFile());
5214
5234
  const bytes = new Uint8Array(await file.arrayBuffer());
5215
5235
  await this.refuseNode(path, bytes.length === 0);
5216
5236
  return { bytes, version: `${file.lastModified}:${file.size}:${stamp}` };
@@ -5219,23 +5239,53 @@ var OpfsFs = class {
5219
5239
  async version(path, cached = false) {
5220
5240
  return this.reading(path, async (handle) => {
5221
5241
  const stamp = await this.stamp(path, cached);
5222
- const file = await handle.getFile();
5242
+ const file = await this.io(handle.getFile());
5223
5243
  return `${file.lastModified}:${file.size}:${stamp}`;
5224
5244
  });
5225
5245
  }
5226
5246
  async pread(path, offset, length2) {
5227
5247
  return this.reading(path, async (handle) => {
5228
- const file = await handle.getFile();
5248
+ const file = await this.io(handle.getFile());
5229
5249
  await this.refuseNode(path, file.size === 0);
5230
5250
  return new Uint8Array(await file.slice(offset, offset + length2).arrayBuffer());
5231
5251
  });
5232
5252
  }
5233
5253
  async pwrite(path, offset, bytes) {
5234
- const [handle, , target] = await this.fileFor(path);
5254
+ const [kept, target] = await this.keptFor(path);
5255
+ if (kept) {
5256
+ try {
5257
+ await this.writeAt(kept, target, path, offset, bytes);
5258
+ return;
5259
+ } catch (err) {
5260
+ if (err.code !== "ENOENT") throw err;
5261
+ this.files.delete(target);
5262
+ }
5263
+ }
5264
+ const [handle] = await this.fileFor(path);
5265
+ await this.writeAt(handle, target, path, offset, bytes);
5266
+ }
5267
+ async keptFor(path) {
5268
+ const at = normalizePath(path);
5269
+ const cached = this.files.get(at);
5270
+ const fresh2 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
5271
+ const [target, existed] = fresh2 ? [at, true] : await this.writeTarget(path);
5272
+ if (!existed) return [void 0, target];
5273
+ this.refuse(target);
5274
+ const [entry] = await this.meta.get([target]);
5275
+ if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
5276
+ const kept = fresh2 ? cached : this.files.get(target);
5277
+ const usable2 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
5278
+ return [usable2 ? kept : void 0, target];
5279
+ }
5280
+ async writeAt(handle, target, path, offset, bytes) {
5235
5281
  await this.counting(target);
5236
5282
  this.wrote(handle);
5237
5283
  try {
5238
- await this.retrying(path, () => positioned(handle, (io) => io.write(offset, bytes)), true);
5284
+ await this.retrying(
5285
+ path,
5286
+ () => this.io(positioned(handle, (io) => io.write(offset, bytes))),
5287
+ true
5288
+ );
5239
5289
  } finally {
5240
5290
  this.wrote(handle);
5241
5291
  await this.recounting(handle, target);
@@ -5244,13 +5294,13 @@ var OpfsFs = class {
5244
5294
  }
5245
5295
  async truncate(path, size) {
5246
5296
  const handle = await this.existing(path, true);
5247
- await this.refuseNode(path, (await handle.getFile()).size === 0);
5297
+ await this.refuseNode(path, (await this.io(handle.getFile())).size === 0);
5248
5298
  const resolve2 = this.ranged || this.remote?.watched();
5249
5299
  const target = resolve2 || this.counted.length > 0 ? (await this.locate(path, true)).path : path;
5250
5300
  await this.counting(target);
5251
5301
  this.wrote(handle);
5252
5302
  try {
5253
- await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
5303
+ await this.retrying(path, () => this.io(positioned(handle, (io) => io.truncate(size))), true);
5254
5304
  } finally {
5255
5305
  this.wrote(handle);
5256
5306
  await this.recounting(handle, target);
@@ -5261,7 +5311,7 @@ var OpfsFs = class {
5261
5311
  const [handle, , target] = await this.fileFor(path);
5262
5312
  const writable2 = await this.retrying(
5263
5313
  path,
5264
- () => handle.createWritable({ keepExistingData: !truncate }),
5314
+ () => this.io(handle.createWritable({ keepExistingData: !truncate })),
5265
5315
  true
5266
5316
  );
5267
5317
  return {
@@ -5310,7 +5360,7 @@ var OpfsFs = class {
5310
5360
  try {
5311
5361
  const handle = await this.inDir(
5312
5362
  dir,
5313
- (parent) => parent.getFileHandle(name2, { create: true })
5363
+ (parent) => this.io(parent.getFileHandle(name2, { create: true }))
5314
5364
  );
5315
5365
  if (!existed) this.touch(Date.now(), parentOf4(target));
5316
5366
  return [handle, existed, target];
@@ -5326,7 +5376,7 @@ var OpfsFs = class {
5326
5376
  let writable2;
5327
5377
  this.wrote(file);
5328
5378
  try {
5329
- writable2 = await file.createWritable();
5379
+ writable2 = await this.io(file.createWritable());
5330
5380
  await writable2.write(content);
5331
5381
  await writable2.close();
5332
5382
  } catch (err) {
@@ -5342,6 +5392,7 @@ var OpfsFs = class {
5342
5392
  const handle = found.handle;
5343
5393
  if (handle.kind !== "directory") throw fsError("ENOTDIR", path);
5344
5394
  const children3 = /* @__PURE__ */ new Map();
5395
+ this.others++;
5345
5396
  for await (const child of handle.values()) children3.set(child.name, child);
5346
5397
  for (const link2 of await this.meta.links(found.path)) {
5347
5398
  const name2 = baseOf(link2.path);
@@ -5395,7 +5446,7 @@ var OpfsFs = class {
5395
5446
  const parent = await this.locate(parentOf4(path), true);
5396
5447
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
5397
5448
  this.refuse(resolvePath(parent.path, baseOf(path)));
5398
- await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
5449
+ await this.io(parent.handle.getDirectoryHandle(baseOf(path), { create: true }));
5399
5450
  const now = Date.now();
5400
5451
  const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
5401
5452
  await this.stampNew(made2, owner, true);
@@ -5416,7 +5467,7 @@ var OpfsFs = class {
5416
5467
  if (found !== void 0 && !found.vacated) await this.meta.remove([path]);
5417
5468
  this.forget(physical);
5418
5469
  this.refuse(physical);
5419
- await dir.getDirectoryHandle(name2, { create: true });
5470
+ await this.io(dir.getDirectoryHandle(name2, { create: true }));
5420
5471
  const now = Date.now();
5421
5472
  const made2 = `${parent.path === "/" ? "" : parent.path}/${name2}`;
5422
5473
  await this.stampNew(made2, owner, true);
@@ -5438,9 +5489,10 @@ var OpfsFs = class {
5438
5489
  if (found !== void 0 && !found.vacated) await this.meta.remove([path]);
5439
5490
  this.forget(physical);
5440
5491
  this.refuse(physical);
5441
- await dir.getFileHandle(name2, { create: true });
5492
+ const handle = await this.io(dir.getFileHandle(name2, { create: true }));
5442
5493
  const now = Date.now();
5443
5494
  const made2 = `${parent.path === "/" ? "" : parent.path}/${name2}`;
5495
+ this.remember(made2, handle, true);
5444
5496
  await this.stampNew(made2, owner, false);
5445
5497
  this.touch(now, parent.path, made2);
5446
5498
  });
@@ -5472,7 +5524,7 @@ var OpfsFs = class {
5472
5524
  try {
5473
5525
  await this.inDir(
5474
5526
  dir,
5475
- (parent) => parent.removeEntry(name2, { recursive: options.recursive === true })
5527
+ (parent) => this.io(parent.removeEntry(name2, { recursive: options.recursive === true }))
5476
5528
  );
5477
5529
  } catch (err) {
5478
5530
  throw translate(err, path);
@@ -5657,10 +5709,11 @@ var OpfsFs = class {
5657
5709
  return intents.length;
5658
5710
  }
5659
5711
  async settledRename(path) {
5660
- if (this.renamingDirs.size === 0 || typeof path !== "string") return;
5712
+ if (this.renamingDirs.size === 0 && this.copying.size === 0 || typeof path !== "string")
5713
+ return;
5661
5714
  const at = normalizePath(path);
5662
5715
  for (let real; ; ) {
5663
- const busy = this.renameAt(at) ?? (real === void 0 ? void 0 : this.renameAt(real));
5716
+ const busy = this.busyAt(at) ?? (real === void 0 ? void 0 : this.busyAt(real));
5664
5717
  if (busy) {
5665
5718
  await busy.done;
5666
5719
  continue;
@@ -5671,6 +5724,62 @@ var OpfsFs = class {
5671
5724
  if (real === at) return;
5672
5725
  }
5673
5726
  }
5727
+ busyAt(at) {
5728
+ return this.renameAt(at) ?? this.copying.get(at);
5729
+ }
5730
+ copying = /* @__PURE__ */ new Map();
5731
+ async copyFile(from, to, options) {
5732
+ const source = await this.locate(from, true);
5733
+ if (source.handle?.kind !== "file") throw fsError("EINVAL", `${from}: not a regular file`);
5734
+ const [entry] = await this.meta.get([source.path]);
5735
+ if (entry?.fifo || entry?.socket) throw fsError("EINVAL", `${from}: not a regular file`);
5736
+ const parent = await this.locate(parentOf4(to), true);
5737
+ if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", to);
5738
+ const destination = resolvePath(parent.path, baseOf(to));
5739
+ if (destination === source.path) throw fsError("EINVAL", `${from}: copied onto itself`);
5740
+ const file = await this.reading(source.path, (handle) => handle.getFile());
5741
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
5742
+ while (this.copying.has(destination)) await this.copying.get(destination)?.done;
5743
+ this.copying.set(destination, { done: promise });
5744
+ try {
5745
+ await this.copyInto(file, destination, to, options);
5746
+ } finally {
5747
+ this.copying.delete(destination);
5748
+ resolve2();
5749
+ }
5750
+ }
5751
+ async copyInto(file, destination, to, options) {
5752
+ let created4 = false;
5753
+ if (options.exclusive) {
5754
+ await this.createExclusive(destination, options.owner);
5755
+ created4 = true;
5756
+ }
5757
+ let target = destination;
5758
+ let writable2;
5759
+ let handle;
5760
+ try {
5761
+ const [made2, existed, at] = await this.fileFor(destination);
5762
+ handle = made2;
5763
+ target = at;
5764
+ if (!existed && !created4) {
5765
+ created4 = true;
5766
+ await this.stampNew(target, options.owner, false);
5767
+ }
5768
+ const [entry] = await this.meta.get([target]);
5769
+ if (entry?.fifo || entry?.socket) throw fsError("ENXIO", to);
5770
+ this.wrote(handle);
5771
+ writable2 = await handle.createWritable();
5772
+ await writable2.write(file);
5773
+ await writable2.close();
5774
+ } catch (err) {
5775
+ await writable2?.abort().catch(() => void 0);
5776
+ if (created4) await this.rm(target, { file: true }).catch(() => void 0);
5777
+ throw translate(err, to, "commit", false);
5778
+ } finally {
5779
+ if (handle) this.wrote(handle);
5780
+ }
5781
+ this.committed(target);
5782
+ }
5674
5783
  renameAt(at) {
5675
5784
  for (const m of this.renamingDirs) {
5676
5785
  if (within3(at, m.from) || within3(at, m.to) || at === parentOf4(m.from) || at === parentOf4(m.to))
@@ -5713,7 +5822,9 @@ var OpfsFs = class {
5713
5822
  }
5714
5823
  }
5715
5824
  async place(handle, dir, name2) {
5716
- const moved2 = await handle.move?.(dir, name2).then(
5825
+ const move = handle.move;
5826
+ if (move) this.others++;
5827
+ const moved2 = await move?.call(handle, dir, name2).then(
5717
5828
  () => true,
5718
5829
  () => false
5719
5830
  );
@@ -5723,7 +5834,7 @@ var OpfsFs = class {
5723
5834
  await this.copy(handle, dir, name2);
5724
5835
  } catch (err) {
5725
5836
  if (handle.kind === "directory" && this.fileMoves === before) {
5726
- await dir.removeEntry(name2, { recursive: true });
5837
+ await this.io(dir.removeEntry(name2, { recursive: true }));
5727
5838
  }
5728
5839
  throw translate(err, name2);
5729
5840
  }
@@ -5732,6 +5843,7 @@ var OpfsFs = class {
5732
5843
  fileMoves = 0;
5733
5844
  async copy(handle, dir, name2) {
5734
5845
  if (handle.kind === "file" && movable(handle)) {
5846
+ this.others++;
5735
5847
  const moved2 = await handle.move?.(dir, name2).then(
5736
5848
  () => true,
5737
5849
  () => false
@@ -5742,12 +5854,14 @@ var OpfsFs = class {
5742
5854
  }
5743
5855
  }
5744
5856
  if (handle.kind === "file") {
5745
- const writable2 = await (await dir.getFileHandle(name2, { create: true })).createWritable();
5746
- await writable2.write(await handle.getFile());
5857
+ const made2 = await this.io(dir.getFileHandle(name2, { create: true }));
5858
+ const writable2 = await this.io(made2.createWritable());
5859
+ await writable2.write(await this.io(handle.getFile()));
5747
5860
  await writable2.close();
5748
5861
  return;
5749
5862
  }
5750
- const target = await dir.getDirectoryHandle(name2, { create: true });
5863
+ const target = await this.io(dir.getDirectoryHandle(name2, { create: true }));
5864
+ this.others++;
5751
5865
  for await (const child of handle.values()) await this.copy(child, target, child.name);
5752
5866
  }
5753
5867
  async symlink(target, path, owner) {
@@ -5820,7 +5934,7 @@ var OpfsFs = class {
5820
5934
  return;
5821
5935
  }
5822
5936
  const handle = found.handle;
5823
- const file = handle.kind === "file" ? await handle.getFile() : void 0;
5937
+ const file = handle.kind === "file" ? await this.io(handle.getFile()) : void 0;
5824
5938
  this.dirTimes.delete(found.path);
5825
5939
  this.fileTimes.delete(found.path);
5826
5940
  const ctimeMs = Date.now();
@@ -5913,6 +6027,7 @@ var READS = [
5913
6027
  "readVersioned"
5914
6028
  ];
5915
6029
  var WRITES = [
6030
+ "copyFile",
5916
6031
  "writeFile",
5917
6032
  "pwrite",
5918
6033
  "truncate",
@@ -7277,7 +7392,7 @@ function satisfies(version, range2) {
7277
7392
  }
7278
7393
 
7279
7394
  // src/version.ts
7280
- var KERNEL_VERSION = true ? "1.127.0" : "0.0.0-dev";
7395
+ var KERNEL_VERSION = true ? "1.128.0" : "0.0.0-dev";
7281
7396
  var OS_RELEASE = "6.0.0-slicc";
7282
7397
  function procVersion() {
7283
7398
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -9542,6 +9657,7 @@ function withCommandDirs(fs, names2, userDir = async () => void 0, owners = fals
9542
9657
  ...fs.realpath ? { realpath: fs.realpath.bind(fs) } : {},
9543
9658
  ...fs.chown ? { chown: fs.chown.bind(fs) } : {},
9544
9659
  ...fs.createExclusive ? { createExclusive: fs.createExclusive.bind(fs) } : {},
9660
+ ...fs.copyFile ? { copyFile: fs.copyFile.bind(fs) } : {},
9545
9661
  ...fs.ancestry ? { ancestry: fs.ancestry.bind(fs) } : {},
9546
9662
  ...fs.hasOwner ? { hasOwner: fs.hasOwner.bind(fs) } : {},
9547
9663
  chmod: (path, mode) => fs.chmod(path, mode),
@@ -9943,6 +10059,12 @@ var FsWatchers = class {
9943
10059
  const overrides = {
9944
10060
  writeFile: content(fs.writeFile.bind(fs), written2),
9945
10061
  ...fs.createExclusive ? { createExclusive: content(fs.createExclusive.bind(fs), () => ["rename"]) } : {},
10062
+ ...fs.copyFile ? {
10063
+ copyFile: (from, to, options) => content(
10064
+ (_to) => fs.copyFile?.(from, to, options),
10065
+ written2
10066
+ )(to)
10067
+ } : {},
9946
10068
  mkdir: async (path, options) => {
9947
10069
  const created4 = [];
9948
10070
  if (options?.recursive) {
@@ -10600,6 +10722,33 @@ var FifoTable = class {
10600
10722
  }
10601
10723
  };
10602
10724
 
10725
+ // src/fs/copy.ts
10726
+ async function present2(fs, path) {
10727
+ return fs.lstat(path).then(
10728
+ () => true,
10729
+ (err) => {
10730
+ if (err?.code === "ENOENT") return false;
10731
+ throw err;
10732
+ }
10733
+ );
10734
+ }
10735
+ async function copyByBytes(fs, from, to, options) {
10736
+ if (!(await fs.stat(from)).isFile) throw fsError("EINVAL", `${from}: not a regular file`);
10737
+ const bytes = await fs.readFileBuffer(from);
10738
+ let created4 = false;
10739
+ if (options.exclusive) {
10740
+ if (fs.createExclusive) await fs.createExclusive(to, options.owner);
10741
+ else if (await present2(fs, to)) throw fsError("EEXIST", to);
10742
+ created4 = true;
10743
+ } else created4 = !await present2(fs, to);
10744
+ try {
10745
+ await fs.writeFile(to, bytes, options.owner);
10746
+ } catch (err) {
10747
+ if (created4) await fs.rm(to, { file: true }).catch(() => void 0);
10748
+ throw err;
10749
+ }
10750
+ }
10751
+
10603
10752
  // src/mount/protocol.ts
10604
10753
  var DRIVER_PROTOCOL = [1, 3];
10605
10754
  function driverVersionError(theirs) {
@@ -10983,12 +11132,12 @@ var MountTable = class {
10983
11132
  }
10984
11133
  list() {
10985
11134
  const mounted = [...this.mounts.values()].map(
10986
- ({ type, source, target, options, state, error, present: present3 }) => ({
11135
+ ({ type, source, target, options, state, error, present: present4 }) => ({
10987
11136
  type,
10988
11137
  source,
10989
11138
  target,
10990
11139
  options: { ...options },
10991
- state: state === "ok" && present3?.() === false ? "nomedium" : state,
11140
+ state: state === "ok" && present4?.() === false ? "nomedium" : state,
10992
11141
  ...error ? { error } : {}
10993
11142
  })
10994
11143
  );
@@ -11663,6 +11812,23 @@ var MountFs = class _MountFs {
11663
11812
  throw err;
11664
11813
  }
11665
11814
  }
11815
+ async copyFile(from, to, options, redirects = 0) {
11816
+ const source = await realTarget(this, normalizePath(from));
11817
+ const a = this.at(source);
11818
+ const b = this.at(to);
11819
+ const copy = this.base.copyFile?.bind(this.base);
11820
+ if (!a && !b && copy) {
11821
+ return this.based(
11822
+ [source, to],
11823
+ redirects,
11824
+ () => copy(source, to, options),
11825
+ ([f, t], n) => this.copyFile(f, t, options, n)
11826
+ );
11827
+ }
11828
+ if (a?.mount !== b?.mount) throw errnoError("EXDEV", `${from} -> ${to}`);
11829
+ if (a) this.writable(a.mount, normalizePath(to));
11830
+ return copyByBytes(this, source, to, options);
11831
+ }
11666
11832
  async createExclusive(path, owner, redirects = 0) {
11667
11833
  const exclusive = this.base.createExclusive?.bind(this.base);
11668
11834
  if (!this.at(path) && exclusive) {
@@ -12090,6 +12256,7 @@ var SYNC_FS_OPS = [
12090
12256
  "rm",
12091
12257
  "rename",
12092
12258
  "link",
12259
+ "copy",
12093
12260
  "unlink",
12094
12261
  "rmdir",
12095
12262
  "symlink",
@@ -12289,6 +12456,8 @@ async function run(entry, path, req) {
12289
12456
  case "symlink":
12290
12457
  await fs.symlink(req.arg2 ?? "", path, ownerFor2(entry));
12291
12458
  return done;
12459
+ case "copy":
12460
+ return copyOp(entry, path, req);
12292
12461
  case "mkfifo": {
12293
12462
  const owner = ownerFor2(entry, req.mode ?? 438) ?? { uid: 0, gid: 0 };
12294
12463
  await createExclusive(fs, path, { ...owner, fifo: true });
@@ -12395,6 +12564,16 @@ async function guard(g, path, req, cwd) {
12395
12564
  const toWalked = await walkSafe(g, to, false);
12396
12565
  return { path: fromWalked, arg2: toWalked };
12397
12566
  }
12567
+ case "copy": {
12568
+ const to = other();
12569
+ await readable(g, op, path);
12570
+ const there = !req.exclusive && await g.fs.lstat(to).then(
12571
+ () => true,
12572
+ () => false
12573
+ );
12574
+ await (there ? writable(g, op, to) : inParent(g, op, to));
12575
+ return { path: await walkSafe(g, path, true), arg2: await walkSafe(g, to, false) };
12576
+ }
12398
12577
  case "link": {
12399
12578
  const to = other();
12400
12579
  const follow2 = req.mode === 1;
@@ -12495,6 +12674,9 @@ async function mayChange(entry, path, req) {
12495
12674
  const { mayChange: mayChange2 } = entry.fs;
12496
12675
  if (!mayChange2) return;
12497
12676
  const { op } = req;
12677
+ if (op === "copy") {
12678
+ return mayChange2.call(entry.fs, entry.fs.resolvePath(entry.cwd, req.arg2 ?? ""), false);
12679
+ }
12498
12680
  if (CONTENT.has(op) && req.handle === void 0) return mayChange2.call(entry.fs, path, true);
12499
12681
  if (!CHANGING.has(op)) return;
12500
12682
  const follow2 = op === "utimes" || op === "chmod" || op === "chown";
@@ -12518,6 +12700,17 @@ async function guarded(entry, path, req) {
12518
12700
  return { refused: refused3 };
12519
12701
  }
12520
12702
  }
12703
+ async function copyOp(entry, path, req) {
12704
+ const { fs, cwd } = entry;
12705
+ const to = fs.resolvePath(cwd, req.arg2 ?? "");
12706
+ const mode = (req.mode ?? 438) & 4095 & ~(entry.umask?.() ?? 18);
12707
+ const owner = ownerFor2(entry, mode);
12708
+ const options = { exclusive: req.exclusive === true, ...owner ? { owner } : {} };
12709
+ await (fs.copyFile ? fs.copyFile(path, to, options) : copyByBytes(fs, path, to, options));
12710
+ if (!owner) await fs.chmod(to, mode);
12711
+ entry.fresh?.(to);
12712
+ return done;
12713
+ }
12521
12714
  async function createExclusive(fs, path, owner) {
12522
12715
  if (fs.createExclusive) return fs.createExclusive(path, owner);
12523
12716
  if (await fs.lstat(path).then(
@@ -12692,7 +12885,7 @@ var CREATIONS = /* @__PURE__ */ new Set([
12692
12885
  "create",
12693
12886
  "mkfifo"
12694
12887
  ]);
12695
- var NAMING = /* @__PURE__ */ new Set(["link", "rename"]);
12888
+ var NAMING = /* @__PURE__ */ new Set(["link", "rename", "copy"]);
12696
12889
  async function newDevName(entry, path, req) {
12697
12890
  const named = NAMING.has(req.op);
12698
12891
  if (!named && !CREATIONS.has(req.op)) return void 0;
@@ -13363,6 +13556,8 @@ var SYS_TREE = {
13363
13556
  slicc_fs_calls: (listing) => `${listing.fsCalls ?? 0}
13364
13557
  `,
13365
13558
  slicc_opfs_calls: (listing) => `${listing.opfsCalls ?? 0}
13559
+ `,
13560
+ slicc_opfs_ops: (listing) => `${listing.opfsOps ?? 0}
13366
13561
  `
13367
13562
  }
13368
13563
  };
@@ -19809,7 +20004,7 @@ var VfsNodes = class {
19809
20004
  if (held) throw fsError("EEXIST", path);
19810
20005
  const exclusive = this.fs.createExclusive;
19811
20006
  if (exclusive) return exclusive.call(this.fs, path, owner);
19812
- if (await present2(this.fs, path)) throw fsError("EEXIST", path);
20007
+ if (await present3(this.fs, path)) throw fsError("EEXIST", path);
19813
20008
  await this.fs.writeFile(path, new Uint8Array(0), owner);
19814
20009
  });
19815
20010
  }
@@ -20040,7 +20235,7 @@ var VfsNodes = class {
20040
20235
  }
20041
20236
  }
20042
20237
  };
20043
- async function present2(fs, path, follow2 = false) {
20238
+ async function present3(fs, path, follow2 = false) {
20044
20239
  const look = (follow2 ? void 0 : fs.lstat) ?? fs.stat;
20045
20240
  try {
20046
20241
  await (look ? look.call(fs, path) : fs.readFileBuffer(path));
@@ -20218,7 +20413,18 @@ function keepingOpen(fs, nodes, changed = () => {
20218
20413
  await nodes.onEntry(key, () => fs.link(from, to, follow2));
20219
20414
  await nodes.linked(at, key);
20220
20415
  },
20221
- mkdir: (path, options) => nodes.onEntry(fs.resolvePath("/", path), () => fs.mkdir(path, options))
20416
+ mkdir: (path, options) => nodes.onEntry(fs.resolvePath("/", path), () => fs.mkdir(path, options)),
20417
+ ...fs.copyFile ? {
20418
+ async copyFile(from, to, options) {
20419
+ const source = await nodes.targetKey(fs.resolvePath("/", from));
20420
+ await nodes.flush(source);
20421
+ await nodes.flushLinked(source);
20422
+ const at = fs.resolvePath("/", to);
20423
+ const key = await nodes.entryKey(at);
20424
+ if (await heldAt(fs, nodes, at)) return copyByBytes(this, from, to, options);
20425
+ await nodes.onEntry(key, () => fs.copyFile?.(from, to, options));
20426
+ }
20427
+ } : {}
20222
20428
  };
20223
20429
  }
20224
20430
  function vfsFile(fs, opts, nodes) {
@@ -21078,7 +21284,7 @@ var WasmProcess = class {
21078
21284
  } else if (req.create) {
21079
21285
  await nodes.onEntry(path, async () => {
21080
21286
  const marks = this.options.owners || plain2 !== void 0;
21081
- const fresh2 = marks && !await present2(fs, path, true);
21287
+ const fresh2 = marks && !await present3(fs, path, true);
21082
21288
  this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
21083
21289
  await this.fds.get(fd).file.stat?.();
21084
21290
  if (fresh2 && plain2 !== void 0) await fs.chmod?.(path, plain2);
@@ -21097,7 +21303,7 @@ var WasmProcess = class {
21097
21303
  { at: path, gen }
21098
21304
  );
21099
21305
  }
21100
- if (req.existing && !await present2(fs, path, true)) {
21306
+ if (req.existing && !await present3(fs, path, true)) {
21101
21307
  throw new KernelError("ENOENT");
21102
21308
  }
21103
21309
  } catch (err) {
@@ -21465,12 +21671,12 @@ var WasmProcess = class {
21465
21671
  async stillOpen(at, handle) {
21466
21672
  if (handle === void 0 || this.keptHandle?.(handle) !== void 0) return;
21467
21673
  if (this.holdsHandle?.(handle) !== false) return;
21468
- if (!await present2(this.options.fs, at, true)) throw new KernelError("ENOENT");
21674
+ if (!await present3(this.options.fs, at, true)) throw new KernelError("ENOENT");
21469
21675
  }
21470
21676
  async keptOr(path, handle) {
21471
21677
  if (handle !== void 0) return this.keptHandle?.(handle) ?? path;
21472
21678
  const kept = this.keptPath?.(path);
21473
- return kept !== void 0 && !await present2(this.options.fs, path, true) ? kept : path;
21679
+ return kept !== void 0 && !await present3(this.options.fs, path, true) ? kept : path;
21474
21680
  }
21475
21681
  async promote(req) {
21476
21682
  if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
@@ -26955,7 +27161,8 @@ var Launcher = class {
26955
27161
  wasm: this.memories.count(),
26956
27162
  peers: Number(this.locks.shared?.() === true),
26957
27163
  fsCalls: servedFsCalls(),
26958
- opfsCalls: this.rootFs.opfsCalls?.() ?? 0
27164
+ opfsCalls: this.rootFs.opfsCalls?.() ?? 0,
27165
+ opfsOps: this.rootFs.opfsOps?.() ?? 0
26959
27166
  }),
26960
27167
  sudo: (call, signal) => this.sudo(pid, call, signal),
26961
27168
  crontab: (call) => this.crontab(pid, call),