@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.
@@ -795,6 +795,12 @@ var ProtectedFs = class {
795
795
  st && marked(`${dir}/${name2}`, st)
796
796
  ]);
797
797
  },
798
+ ...fs.copyFile ? {
799
+ async copyFile(...args) {
800
+ await check(args[1], false);
801
+ return fs.copyFile?.(...args);
802
+ }
803
+ } : {},
798
804
  ...fs.createExclusive ? {
799
805
  async createExclusive(...args) {
800
806
  await check(args[0], true);
@@ -1330,7 +1336,7 @@ var OpfsFs = class {
1330
1336
  }
1331
1337
  async recounting(handle, target) {
1332
1338
  if (this.counted.length === 0) return;
1333
- const where = await this.root.resolve(handle).catch(() => null);
1339
+ const where = await this.io(this.root.resolve(handle)).catch(() => null);
1334
1340
  await this.counting(where ? `/${where.join("/")}` : target);
1335
1341
  }
1336
1342
  resolvePath(base, path) {
@@ -1532,9 +1538,17 @@ var OpfsFs = class {
1532
1538
  }
1533
1539
  exact = 0;
1534
1540
  calls = 0;
1541
+ others = 0;
1535
1542
  opfsCalls() {
1536
1543
  return this.calls;
1537
1544
  }
1545
+ opfsOps() {
1546
+ return this.calls + this.others;
1547
+ }
1548
+ io(request2) {
1549
+ this.others++;
1550
+ return request2;
1551
+ }
1538
1552
  fresh() {
1539
1553
  this.changes++;
1540
1554
  this.absent.clear();
@@ -1558,7 +1572,13 @@ var OpfsFs = class {
1558
1572
  this.absent.set(path, performance.now());
1559
1573
  }
1560
1574
  remember(path, handle, looked = false) {
1561
- if (handle.kind !== "file") return handle;
1575
+ if (handle.kind !== "file") {
1576
+ if (looked) {
1577
+ this.dirs.set(path, handle);
1578
+ this.checked.set(handle, performance.now());
1579
+ }
1580
+ return handle;
1581
+ }
1562
1582
  if (looked) {
1563
1583
  this.found.set(handle, performance.now());
1564
1584
  this.glances.delete(handle);
@@ -1939,7 +1959,7 @@ var OpfsFs = class {
1939
1959
  async readFileBuffer(path) {
1940
1960
  const bytes = await this.reading(
1941
1961
  path,
1942
- async (handle) => new Uint8Array(await (await handle.getFile()).arrayBuffer())
1962
+ async (handle) => new Uint8Array(await (await this.io(handle.getFile())).arrayBuffer())
1943
1963
  );
1944
1964
  await this.refuseNode(path, bytes.length === 0);
1945
1965
  return bytes;
@@ -1947,7 +1967,7 @@ var OpfsFs = class {
1947
1967
  async readVersioned(path, cached = false) {
1948
1968
  return this.reading(path, async (handle) => {
1949
1969
  const stamp = await this.stamp(path, cached);
1950
- const file = await handle.getFile();
1970
+ const file = await this.io(handle.getFile());
1951
1971
  const bytes = new Uint8Array(await file.arrayBuffer());
1952
1972
  await this.refuseNode(path, bytes.length === 0);
1953
1973
  return { bytes, version: `${file.lastModified}:${file.size}:${stamp}` };
@@ -1956,23 +1976,53 @@ var OpfsFs = class {
1956
1976
  async version(path, cached = false) {
1957
1977
  return this.reading(path, async (handle) => {
1958
1978
  const stamp = await this.stamp(path, cached);
1959
- const file = await handle.getFile();
1979
+ const file = await this.io(handle.getFile());
1960
1980
  return `${file.lastModified}:${file.size}:${stamp}`;
1961
1981
  });
1962
1982
  }
1963
1983
  async pread(path, offset, length2) {
1964
1984
  return this.reading(path, async (handle) => {
1965
- const file = await handle.getFile();
1985
+ const file = await this.io(handle.getFile());
1966
1986
  await this.refuseNode(path, file.size === 0);
1967
1987
  return new Uint8Array(await file.slice(offset, offset + length2).arrayBuffer());
1968
1988
  });
1969
1989
  }
1970
1990
  async pwrite(path, offset, bytes) {
1971
- const [handle, , target] = await this.fileFor(path);
1991
+ const [kept, target] = await this.keptFor(path);
1992
+ if (kept) {
1993
+ try {
1994
+ await this.writeAt(kept, target, path, offset, bytes);
1995
+ return;
1996
+ } catch (err) {
1997
+ if (err.code !== "ENOENT") throw err;
1998
+ this.files.delete(target);
1999
+ }
2000
+ }
2001
+ const [handle] = await this.fileFor(path);
2002
+ await this.writeAt(handle, target, path, offset, bytes);
2003
+ }
2004
+ async keptFor(path) {
2005
+ const at = normalizePath(path);
2006
+ const cached = this.files.get(at);
2007
+ const fresh2 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
2008
+ const [target, existed] = fresh2 ? [at, true] : await this.writeTarget(path);
2009
+ if (!existed) return [void 0, target];
2010
+ this.refuse(target);
2011
+ const [entry] = await this.meta.get([target]);
2012
+ if (entry?.fifo || entry?.socket) throw fsError("ENXIO", path);
2013
+ const kept = fresh2 ? cached : this.files.get(target);
2014
+ const usable2 = kept && performance.now() - (this.found.get(kept) ?? -CHECK_MS) < CHECK_MS;
2015
+ return [usable2 ? kept : void 0, target];
2016
+ }
2017
+ async writeAt(handle, target, path, offset, bytes) {
1972
2018
  await this.counting(target);
1973
2019
  this.wrote(handle);
1974
2020
  try {
1975
- await this.retrying(path, () => positioned(handle, (io) => io.write(offset, bytes)), true);
2021
+ await this.retrying(
2022
+ path,
2023
+ () => this.io(positioned(handle, (io) => io.write(offset, bytes))),
2024
+ true
2025
+ );
1976
2026
  } finally {
1977
2027
  this.wrote(handle);
1978
2028
  await this.recounting(handle, target);
@@ -1981,13 +2031,13 @@ var OpfsFs = class {
1981
2031
  }
1982
2032
  async truncate(path, size) {
1983
2033
  const handle = await this.existing(path, true);
1984
- await this.refuseNode(path, (await handle.getFile()).size === 0);
2034
+ await this.refuseNode(path, (await this.io(handle.getFile())).size === 0);
1985
2035
  const resolve2 = this.ranged || this.remote?.watched();
1986
2036
  const target = resolve2 || this.counted.length > 0 ? (await this.locate(path, true)).path : path;
1987
2037
  await this.counting(target);
1988
2038
  this.wrote(handle);
1989
2039
  try {
1990
- await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
2040
+ await this.retrying(path, () => this.io(positioned(handle, (io) => io.truncate(size))), true);
1991
2041
  } finally {
1992
2042
  this.wrote(handle);
1993
2043
  await this.recounting(handle, target);
@@ -1998,7 +2048,7 @@ var OpfsFs = class {
1998
2048
  const [handle, , target] = await this.fileFor(path);
1999
2049
  const writable2 = await this.retrying(
2000
2050
  path,
2001
- () => handle.createWritable({ keepExistingData: !truncate }),
2051
+ () => this.io(handle.createWritable({ keepExistingData: !truncate })),
2002
2052
  true
2003
2053
  );
2004
2054
  return {
@@ -2047,7 +2097,7 @@ var OpfsFs = class {
2047
2097
  try {
2048
2098
  const handle = await this.inDir(
2049
2099
  dir,
2050
- (parent) => parent.getFileHandle(name2, { create: true })
2100
+ (parent) => this.io(parent.getFileHandle(name2, { create: true }))
2051
2101
  );
2052
2102
  if (!existed) this.touch(Date.now(), parentOf3(target));
2053
2103
  return [handle, existed, target];
@@ -2063,7 +2113,7 @@ var OpfsFs = class {
2063
2113
  let writable2;
2064
2114
  this.wrote(file);
2065
2115
  try {
2066
- writable2 = await file.createWritable();
2116
+ writable2 = await this.io(file.createWritable());
2067
2117
  await writable2.write(content);
2068
2118
  await writable2.close();
2069
2119
  } catch (err) {
@@ -2079,6 +2129,7 @@ var OpfsFs = class {
2079
2129
  const handle = found.handle;
2080
2130
  if (handle.kind !== "directory") throw fsError("ENOTDIR", path);
2081
2131
  const children3 = /* @__PURE__ */ new Map();
2132
+ this.others++;
2082
2133
  for await (const child of handle.values()) children3.set(child.name, child);
2083
2134
  for (const link2 of await this.meta.links(found.path)) {
2084
2135
  const name2 = baseOf(link2.path);
@@ -2132,7 +2183,7 @@ var OpfsFs = class {
2132
2183
  const parent = await this.locate(parentOf3(path), true);
2133
2184
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
2134
2185
  this.refuse(resolvePath(parent.path, baseOf(path)));
2135
- await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
2186
+ await this.io(parent.handle.getDirectoryHandle(baseOf(path), { create: true }));
2136
2187
  const now = Date.now();
2137
2188
  const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
2138
2189
  await this.stampNew(made2, owner, true);
@@ -2153,7 +2204,7 @@ var OpfsFs = class {
2153
2204
  if (found !== void 0 && !found.vacated) await this.meta.remove([path]);
2154
2205
  this.forget(physical);
2155
2206
  this.refuse(physical);
2156
- await dir.getDirectoryHandle(name2, { create: true });
2207
+ await this.io(dir.getDirectoryHandle(name2, { create: true }));
2157
2208
  const now = Date.now();
2158
2209
  const made2 = `${parent.path === "/" ? "" : parent.path}/${name2}`;
2159
2210
  await this.stampNew(made2, owner, true);
@@ -2175,9 +2226,10 @@ var OpfsFs = class {
2175
2226
  if (found !== void 0 && !found.vacated) await this.meta.remove([path]);
2176
2227
  this.forget(physical);
2177
2228
  this.refuse(physical);
2178
- await dir.getFileHandle(name2, { create: true });
2229
+ const handle = await this.io(dir.getFileHandle(name2, { create: true }));
2179
2230
  const now = Date.now();
2180
2231
  const made2 = `${parent.path === "/" ? "" : parent.path}/${name2}`;
2232
+ this.remember(made2, handle, true);
2181
2233
  await this.stampNew(made2, owner, false);
2182
2234
  this.touch(now, parent.path, made2);
2183
2235
  });
@@ -2209,7 +2261,7 @@ var OpfsFs = class {
2209
2261
  try {
2210
2262
  await this.inDir(
2211
2263
  dir,
2212
- (parent) => parent.removeEntry(name2, { recursive: options.recursive === true })
2264
+ (parent) => this.io(parent.removeEntry(name2, { recursive: options.recursive === true }))
2213
2265
  );
2214
2266
  } catch (err) {
2215
2267
  throw translate(err, path);
@@ -2394,10 +2446,11 @@ var OpfsFs = class {
2394
2446
  return intents.length;
2395
2447
  }
2396
2448
  async settledRename(path) {
2397
- if (this.renamingDirs.size === 0 || typeof path !== "string") return;
2449
+ if (this.renamingDirs.size === 0 && this.copying.size === 0 || typeof path !== "string")
2450
+ return;
2398
2451
  const at = normalizePath(path);
2399
2452
  for (let real; ; ) {
2400
- const busy = this.renameAt(at) ?? (real === void 0 ? void 0 : this.renameAt(real));
2453
+ const busy = this.busyAt(at) ?? (real === void 0 ? void 0 : this.busyAt(real));
2401
2454
  if (busy) {
2402
2455
  await busy.done;
2403
2456
  continue;
@@ -2408,6 +2461,62 @@ var OpfsFs = class {
2408
2461
  if (real === at) return;
2409
2462
  }
2410
2463
  }
2464
+ busyAt(at) {
2465
+ return this.renameAt(at) ?? this.copying.get(at);
2466
+ }
2467
+ copying = /* @__PURE__ */ new Map();
2468
+ async copyFile(from, to, options) {
2469
+ const source = await this.locate(from, true);
2470
+ if (source.handle?.kind !== "file") throw fsError("EINVAL", `${from}: not a regular file`);
2471
+ const [entry] = await this.meta.get([source.path]);
2472
+ if (entry?.fifo || entry?.socket) throw fsError("EINVAL", `${from}: not a regular file`);
2473
+ const parent = await this.locate(parentOf3(to), true);
2474
+ if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", to);
2475
+ const destination = resolvePath(parent.path, baseOf(to));
2476
+ if (destination === source.path) throw fsError("EINVAL", `${from}: copied onto itself`);
2477
+ const file = await this.reading(source.path, (handle) => handle.getFile());
2478
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
2479
+ while (this.copying.has(destination)) await this.copying.get(destination)?.done;
2480
+ this.copying.set(destination, { done: promise });
2481
+ try {
2482
+ await this.copyInto(file, destination, to, options);
2483
+ } finally {
2484
+ this.copying.delete(destination);
2485
+ resolve2();
2486
+ }
2487
+ }
2488
+ async copyInto(file, destination, to, options) {
2489
+ let created4 = false;
2490
+ if (options.exclusive) {
2491
+ await this.createExclusive(destination, options.owner);
2492
+ created4 = true;
2493
+ }
2494
+ let target = destination;
2495
+ let writable2;
2496
+ let handle;
2497
+ try {
2498
+ const [made2, existed, at] = await this.fileFor(destination);
2499
+ handle = made2;
2500
+ target = at;
2501
+ if (!existed && !created4) {
2502
+ created4 = true;
2503
+ await this.stampNew(target, options.owner, false);
2504
+ }
2505
+ const [entry] = await this.meta.get([target]);
2506
+ if (entry?.fifo || entry?.socket) throw fsError("ENXIO", to);
2507
+ this.wrote(handle);
2508
+ writable2 = await handle.createWritable();
2509
+ await writable2.write(file);
2510
+ await writable2.close();
2511
+ } catch (err) {
2512
+ await writable2?.abort().catch(() => void 0);
2513
+ if (created4) await this.rm(target, { file: true }).catch(() => void 0);
2514
+ throw translate(err, to, "commit", false);
2515
+ } finally {
2516
+ if (handle) this.wrote(handle);
2517
+ }
2518
+ this.committed(target);
2519
+ }
2411
2520
  renameAt(at) {
2412
2521
  for (const m of this.renamingDirs) {
2413
2522
  if (within2(at, m.from) || within2(at, m.to) || at === parentOf3(m.from) || at === parentOf3(m.to))
@@ -2450,7 +2559,9 @@ var OpfsFs = class {
2450
2559
  }
2451
2560
  }
2452
2561
  async place(handle, dir, name2) {
2453
- const moved2 = await handle.move?.(dir, name2).then(
2562
+ const move = handle.move;
2563
+ if (move) this.others++;
2564
+ const moved2 = await move?.call(handle, dir, name2).then(
2454
2565
  () => true,
2455
2566
  () => false
2456
2567
  );
@@ -2460,7 +2571,7 @@ var OpfsFs = class {
2460
2571
  await this.copy(handle, dir, name2);
2461
2572
  } catch (err) {
2462
2573
  if (handle.kind === "directory" && this.fileMoves === before) {
2463
- await dir.removeEntry(name2, { recursive: true });
2574
+ await this.io(dir.removeEntry(name2, { recursive: true }));
2464
2575
  }
2465
2576
  throw translate(err, name2);
2466
2577
  }
@@ -2469,6 +2580,7 @@ var OpfsFs = class {
2469
2580
  fileMoves = 0;
2470
2581
  async copy(handle, dir, name2) {
2471
2582
  if (handle.kind === "file" && movable(handle)) {
2583
+ this.others++;
2472
2584
  const moved2 = await handle.move?.(dir, name2).then(
2473
2585
  () => true,
2474
2586
  () => false
@@ -2479,12 +2591,14 @@ var OpfsFs = class {
2479
2591
  }
2480
2592
  }
2481
2593
  if (handle.kind === "file") {
2482
- const writable2 = await (await dir.getFileHandle(name2, { create: true })).createWritable();
2483
- await writable2.write(await handle.getFile());
2594
+ const made2 = await this.io(dir.getFileHandle(name2, { create: true }));
2595
+ const writable2 = await this.io(made2.createWritable());
2596
+ await writable2.write(await this.io(handle.getFile()));
2484
2597
  await writable2.close();
2485
2598
  return;
2486
2599
  }
2487
- const target = await dir.getDirectoryHandle(name2, { create: true });
2600
+ const target = await this.io(dir.getDirectoryHandle(name2, { create: true }));
2601
+ this.others++;
2488
2602
  for await (const child of handle.values()) await this.copy(child, target, child.name);
2489
2603
  }
2490
2604
  async symlink(target, path, owner) {
@@ -2557,7 +2671,7 @@ var OpfsFs = class {
2557
2671
  return;
2558
2672
  }
2559
2673
  const handle = found.handle;
2560
- const file = handle.kind === "file" ? await handle.getFile() : void 0;
2674
+ const file = handle.kind === "file" ? await this.io(handle.getFile()) : void 0;
2561
2675
  this.dirTimes.delete(found.path);
2562
2676
  this.fileTimes.delete(found.path);
2563
2677
  const ctimeMs = Date.now();
@@ -2650,6 +2764,7 @@ var READS = [
2650
2764
  "readVersioned"
2651
2765
  ];
2652
2766
  var WRITES = [
2767
+ "copyFile",
2653
2768
  "writeFile",
2654
2769
  "pwrite",
2655
2770
  "truncate",
@@ -6823,7 +6938,7 @@ function satisfies(version, range2) {
6823
6938
  }
6824
6939
 
6825
6940
  // src/version.ts
6826
- var KERNEL_VERSION = true ? "1.127.0" : "0.0.0-dev";
6941
+ var KERNEL_VERSION = true ? "1.128.0" : "0.0.0-dev";
6827
6942
  var OS_RELEASE = "6.0.0-slicc";
6828
6943
  function procVersion() {
6829
6944
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -9447,6 +9562,7 @@ function withCommandDirs(fs, names2, userDir = async () => void 0, owners = fals
9447
9562
  ...fs.realpath ? { realpath: fs.realpath.bind(fs) } : {},
9448
9563
  ...fs.chown ? { chown: fs.chown.bind(fs) } : {},
9449
9564
  ...fs.createExclusive ? { createExclusive: fs.createExclusive.bind(fs) } : {},
9565
+ ...fs.copyFile ? { copyFile: fs.copyFile.bind(fs) } : {},
9450
9566
  ...fs.ancestry ? { ancestry: fs.ancestry.bind(fs) } : {},
9451
9567
  ...fs.hasOwner ? { hasOwner: fs.hasOwner.bind(fs) } : {},
9452
9568
  chmod: (path, mode) => fs.chmod(path, mode),
@@ -9848,6 +9964,12 @@ var FsWatchers = class {
9848
9964
  const overrides = {
9849
9965
  writeFile: content(fs.writeFile.bind(fs), written2),
9850
9966
  ...fs.createExclusive ? { createExclusive: content(fs.createExclusive.bind(fs), () => ["rename"]) } : {},
9967
+ ...fs.copyFile ? {
9968
+ copyFile: (from, to, options) => content(
9969
+ (_to) => fs.copyFile?.(from, to, options),
9970
+ written2
9971
+ )(to)
9972
+ } : {},
9851
9973
  mkdir: async (path, options) => {
9852
9974
  const created4 = [];
9853
9975
  if (options?.recursive) {
@@ -10505,6 +10627,33 @@ var FifoTable = class {
10505
10627
  }
10506
10628
  };
10507
10629
 
10630
+ // src/fs/copy.ts
10631
+ async function present2(fs, path) {
10632
+ return fs.lstat(path).then(
10633
+ () => true,
10634
+ (err) => {
10635
+ if (err?.code === "ENOENT") return false;
10636
+ throw err;
10637
+ }
10638
+ );
10639
+ }
10640
+ async function copyByBytes(fs, from, to, options) {
10641
+ if (!(await fs.stat(from)).isFile) throw fsError("EINVAL", `${from}: not a regular file`);
10642
+ const bytes = await fs.readFileBuffer(from);
10643
+ let created4 = false;
10644
+ if (options.exclusive) {
10645
+ if (fs.createExclusive) await fs.createExclusive(to, options.owner);
10646
+ else if (await present2(fs, to)) throw fsError("EEXIST", to);
10647
+ created4 = true;
10648
+ } else created4 = !await present2(fs, to);
10649
+ try {
10650
+ await fs.writeFile(to, bytes, options.owner);
10651
+ } catch (err) {
10652
+ if (created4) await fs.rm(to, { file: true }).catch(() => void 0);
10653
+ throw err;
10654
+ }
10655
+ }
10656
+
10508
10657
  // src/mount/protocol.ts
10509
10658
  var DRIVER_PROTOCOL = [1, 3];
10510
10659
  function driverVersionError(theirs) {
@@ -10888,12 +11037,12 @@ var MountTable = class {
10888
11037
  }
10889
11038
  list() {
10890
11039
  const mounted = [...this.mounts.values()].map(
10891
- ({ type, source, target, options, state, error, present: present3 }) => ({
11040
+ ({ type, source, target, options, state, error, present: present4 }) => ({
10892
11041
  type,
10893
11042
  source,
10894
11043
  target,
10895
11044
  options: { ...options },
10896
- state: state === "ok" && present3?.() === false ? "nomedium" : state,
11045
+ state: state === "ok" && present4?.() === false ? "nomedium" : state,
10897
11046
  ...error ? { error } : {}
10898
11047
  })
10899
11048
  );
@@ -11568,6 +11717,23 @@ var MountFs = class _MountFs {
11568
11717
  throw err;
11569
11718
  }
11570
11719
  }
11720
+ async copyFile(from, to, options, redirects = 0) {
11721
+ const source = await realTarget(this, normalizePath(from));
11722
+ const a = this.at(source);
11723
+ const b = this.at(to);
11724
+ const copy = this.base.copyFile?.bind(this.base);
11725
+ if (!a && !b && copy) {
11726
+ return this.based(
11727
+ [source, to],
11728
+ redirects,
11729
+ () => copy(source, to, options),
11730
+ ([f, t], n) => this.copyFile(f, t, options, n)
11731
+ );
11732
+ }
11733
+ if (a?.mount !== b?.mount) throw errnoError("EXDEV", `${from} -> ${to}`);
11734
+ if (a) this.writable(a.mount, normalizePath(to));
11735
+ return copyByBytes(this, source, to, options);
11736
+ }
11571
11737
  async createExclusive(path, owner, redirects = 0) {
11572
11738
  const exclusive = this.base.createExclusive?.bind(this.base);
11573
11739
  if (!this.at(path) && exclusive) {
@@ -11995,6 +12161,7 @@ var SYNC_FS_OPS = [
11995
12161
  "rm",
11996
12162
  "rename",
11997
12163
  "link",
12164
+ "copy",
11998
12165
  "unlink",
11999
12166
  "rmdir",
12000
12167
  "symlink",
@@ -12194,6 +12361,8 @@ async function run(entry, path, req) {
12194
12361
  case "symlink":
12195
12362
  await fs.symlink(req.arg2 ?? "", path, ownerFor2(entry));
12196
12363
  return done;
12364
+ case "copy":
12365
+ return copyOp(entry, path, req);
12197
12366
  case "mkfifo": {
12198
12367
  const owner = ownerFor2(entry, req.mode ?? 438) ?? { uid: 0, gid: 0 };
12199
12368
  await createExclusive(fs, path, { ...owner, fifo: true });
@@ -12300,6 +12469,16 @@ async function guard(g, path, req, cwd) {
12300
12469
  const toWalked = await walkSafe(g, to, false);
12301
12470
  return { path: fromWalked, arg2: toWalked };
12302
12471
  }
12472
+ case "copy": {
12473
+ const to = other();
12474
+ await readable(g, op, path);
12475
+ const there = !req.exclusive && await g.fs.lstat(to).then(
12476
+ () => true,
12477
+ () => false
12478
+ );
12479
+ await (there ? writable(g, op, to) : inParent(g, op, to));
12480
+ return { path: await walkSafe(g, path, true), arg2: await walkSafe(g, to, false) };
12481
+ }
12303
12482
  case "link": {
12304
12483
  const to = other();
12305
12484
  const follow2 = req.mode === 1;
@@ -12400,6 +12579,9 @@ async function mayChange(entry, path, req) {
12400
12579
  const { mayChange: mayChange2 } = entry.fs;
12401
12580
  if (!mayChange2) return;
12402
12581
  const { op } = req;
12582
+ if (op === "copy") {
12583
+ return mayChange2.call(entry.fs, entry.fs.resolvePath(entry.cwd, req.arg2 ?? ""), false);
12584
+ }
12403
12585
  if (CONTENT.has(op) && req.handle === void 0) return mayChange2.call(entry.fs, path, true);
12404
12586
  if (!CHANGING.has(op)) return;
12405
12587
  const follow2 = op === "utimes" || op === "chmod" || op === "chown";
@@ -12423,6 +12605,17 @@ async function guarded(entry, path, req) {
12423
12605
  return { refused: refused3 };
12424
12606
  }
12425
12607
  }
12608
+ async function copyOp(entry, path, req) {
12609
+ const { fs, cwd } = entry;
12610
+ const to = fs.resolvePath(cwd, req.arg2 ?? "");
12611
+ const mode = (req.mode ?? 438) & 4095 & ~(entry.umask?.() ?? 18);
12612
+ const owner = ownerFor2(entry, mode);
12613
+ const options = { exclusive: req.exclusive === true, ...owner ? { owner } : {} };
12614
+ await (fs.copyFile ? fs.copyFile(path, to, options) : copyByBytes(fs, path, to, options));
12615
+ if (!owner) await fs.chmod(to, mode);
12616
+ entry.fresh?.(to);
12617
+ return done;
12618
+ }
12426
12619
  async function createExclusive(fs, path, owner) {
12427
12620
  if (fs.createExclusive) return fs.createExclusive(path, owner);
12428
12621
  if (await fs.lstat(path).then(
@@ -12597,7 +12790,7 @@ var CREATIONS = /* @__PURE__ */ new Set([
12597
12790
  "create",
12598
12791
  "mkfifo"
12599
12792
  ]);
12600
- var NAMING = /* @__PURE__ */ new Set(["link", "rename"]);
12793
+ var NAMING = /* @__PURE__ */ new Set(["link", "rename", "copy"]);
12601
12794
  async function newDevName(entry, path, req) {
12602
12795
  const named = NAMING.has(req.op);
12603
12796
  if (!named && !CREATIONS.has(req.op)) return void 0;
@@ -13268,6 +13461,8 @@ var SYS_TREE = {
13268
13461
  slicc_fs_calls: (listing) => `${listing.fsCalls ?? 0}
13269
13462
  `,
13270
13463
  slicc_opfs_calls: (listing) => `${listing.opfsCalls ?? 0}
13464
+ `,
13465
+ slicc_opfs_ops: (listing) => `${listing.opfsOps ?? 0}
13271
13466
  `
13272
13467
  }
13273
13468
  };
@@ -19872,7 +20067,7 @@ var VfsNodes = class {
19872
20067
  if (held) throw fsError("EEXIST", path);
19873
20068
  const exclusive = this.fs.createExclusive;
19874
20069
  if (exclusive) return exclusive.call(this.fs, path, owner);
19875
- if (await present2(this.fs, path)) throw fsError("EEXIST", path);
20070
+ if (await present3(this.fs, path)) throw fsError("EEXIST", path);
19876
20071
  await this.fs.writeFile(path, new Uint8Array(0), owner);
19877
20072
  });
19878
20073
  }
@@ -20103,7 +20298,7 @@ var VfsNodes = class {
20103
20298
  }
20104
20299
  }
20105
20300
  };
20106
- async function present2(fs, path, follow2 = false) {
20301
+ async function present3(fs, path, follow2 = false) {
20107
20302
  const look = (follow2 ? void 0 : fs.lstat) ?? fs.stat;
20108
20303
  try {
20109
20304
  await (look ? look.call(fs, path) : fs.readFileBuffer(path));
@@ -20281,7 +20476,18 @@ function keepingOpen(fs, nodes, changed = () => {
20281
20476
  await nodes.onEntry(key, () => fs.link(from, to, follow2));
20282
20477
  await nodes.linked(at, key);
20283
20478
  },
20284
- mkdir: (path, options) => nodes.onEntry(fs.resolvePath("/", path), () => fs.mkdir(path, options))
20479
+ mkdir: (path, options) => nodes.onEntry(fs.resolvePath("/", path), () => fs.mkdir(path, options)),
20480
+ ...fs.copyFile ? {
20481
+ async copyFile(from, to, options) {
20482
+ const source = await nodes.targetKey(fs.resolvePath("/", from));
20483
+ await nodes.flush(source);
20484
+ await nodes.flushLinked(source);
20485
+ const at = fs.resolvePath("/", to);
20486
+ const key = await nodes.entryKey(at);
20487
+ if (await heldAt(fs, nodes, at)) return copyByBytes(this, from, to, options);
20488
+ await nodes.onEntry(key, () => fs.copyFile?.(from, to, options));
20489
+ }
20490
+ } : {}
20285
20491
  };
20286
20492
  }
20287
20493
  function vfsFile(fs, opts, nodes) {
@@ -21141,7 +21347,7 @@ var WasmProcess = class {
21141
21347
  } else if (req.create) {
21142
21348
  await nodes.onEntry(path, async () => {
21143
21349
  const marks = this.options.owners || plain2 !== void 0;
21144
- const fresh2 = marks && !await present2(fs, path, true);
21350
+ const fresh2 = marks && !await present3(fs, path, true);
21145
21351
  this.fds.installAt(fd, this.openVfsFile(open, await nodes.identify(path), fs));
21146
21352
  await this.fds.get(fd).file.stat?.();
21147
21353
  if (fresh2 && plain2 !== void 0) await fs.chmod?.(path, plain2);
@@ -21160,7 +21366,7 @@ var WasmProcess = class {
21160
21366
  { at: path, gen }
21161
21367
  );
21162
21368
  }
21163
- if (req.existing && !await present2(fs, path, true)) {
21369
+ if (req.existing && !await present3(fs, path, true)) {
21164
21370
  throw new KernelError("ENOENT");
21165
21371
  }
21166
21372
  } catch (err) {
@@ -21528,12 +21734,12 @@ var WasmProcess = class {
21528
21734
  async stillOpen(at, handle) {
21529
21735
  if (handle === void 0 || this.keptHandle?.(handle) !== void 0) return;
21530
21736
  if (this.holdsHandle?.(handle) !== false) return;
21531
- if (!await present2(this.options.fs, at, true)) throw new KernelError("ENOENT");
21737
+ if (!await present3(this.options.fs, at, true)) throw new KernelError("ENOENT");
21532
21738
  }
21533
21739
  async keptOr(path, handle) {
21534
21740
  if (handle !== void 0) return this.keptHandle?.(handle) ?? path;
21535
21741
  const kept = this.keptPath?.(path);
21536
- return kept !== void 0 && !await present2(this.options.fs, path, true) ? kept : path;
21742
+ return kept !== void 0 && !await present3(this.options.fs, path, true) ? kept : path;
21537
21743
  }
21538
21744
  async promote(req) {
21539
21745
  if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
@@ -26950,7 +27156,8 @@ var Launcher = class {
26950
27156
  wasm: this.memories.count(),
26951
27157
  peers: Number(this.locks.shared?.() === true),
26952
27158
  fsCalls: servedFsCalls(),
26953
- opfsCalls: this.rootFs.opfsCalls?.() ?? 0
27159
+ opfsCalls: this.rootFs.opfsCalls?.() ?? 0,
27160
+ opfsOps: this.rootFs.opfsOps?.() ?? 0
26954
27161
  }),
26955
27162
  sudo: (call, signal) => this.sudo(pid, call, signal),
26956
27163
  crontab: (call) => this.crontab(pid, call),