@ai-ecoverse/slicc-kernel 1.83.0 → 1.84.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.
@@ -117,6 +117,9 @@ var MemoryMeta = class {
117
117
  async remove(paths) {
118
118
  for (const path of paths) this.entries.deleteTree(path);
119
119
  }
120
+ absent(path) {
121
+ return !this.entries.has(path);
122
+ }
120
123
  async links(dir) {
121
124
  return [...this.entries.entries()].map(([, e]) => e).filter((entry) => entry.dir === dir);
122
125
  }
@@ -136,6 +139,8 @@ function settle(transaction, value) {
136
139
  transaction.onabort = () => reject(transaction.error ?? new Error("metadata transaction aborted"));
137
140
  });
138
141
  }
142
+ var REMOVE_BATCH = 256;
143
+ var REMOVE_WAIT_MS = 5;
139
144
  var IndexedDbMeta = class {
140
145
  db;
141
146
  cache = new PathMap();
@@ -171,6 +176,7 @@ var IndexedDbMeta = class {
171
176
  async get(paths) {
172
177
  const missing4 = paths.filter((path) => !this.cache.has(path));
173
178
  if (missing4.length > 0) {
179
+ this.settleRemoves();
174
180
  const generation = this.generation;
175
181
  const [transaction, store] = this.transaction("readonly");
176
182
  const found = /* @__PURE__ */ new Map();
@@ -188,6 +194,7 @@ var IndexedDbMeta = class {
188
194
  return paths.map((path) => this.cache.get(path) ?? void 0);
189
195
  }
190
196
  async update(path, change) {
197
+ this.settleRemoves();
191
198
  const [transaction, store] = this.transaction("readwrite");
192
199
  const request2 = store.get(path);
193
200
  let next2;
@@ -201,6 +208,7 @@ var IndexedDbMeta = class {
201
208
  this.cache.set(path, next2 ?? null);
202
209
  }
203
210
  async move(from, to, root) {
211
+ this.settleRemoves();
204
212
  const [transaction, store] = this.transaction("readwrite");
205
213
  store.delete(to);
206
214
  store.delete(descendants(to));
@@ -217,7 +225,28 @@ var IndexedDbMeta = class {
217
225
  await settle(transaction, () => void 0);
218
226
  this.changed({ trees: [from, to] });
219
227
  }
228
+ absent(path) {
229
+ return this.cache.has(path) && this.cache.get(path) === null;
230
+ }
231
+ removing = /* @__PURE__ */ new Map();
232
+ removeTimer;
220
233
  async remove(paths) {
234
+ this.settleRemoves();
235
+ return this.removeNow(paths);
236
+ }
237
+ async removeFile(path) {
238
+ const known = this.cache.get(path);
239
+ if (!known) return this.remove([path]);
240
+ this.generation++;
241
+ this.cache.set(path, null);
242
+ this.removing.set(path, JSON.stringify(known));
243
+ if (this.removing.size >= REMOVE_BATCH) return this.flushRemoves();
244
+ this.removeTimer ??= setTimeout(
245
+ () => void this.flushRemoves().catch(() => void 0),
246
+ REMOVE_WAIT_MS
247
+ );
248
+ }
249
+ async removeNow(paths) {
221
250
  const [transaction, store] = this.transaction("readwrite");
222
251
  for (const path of paths) {
223
252
  store.delete(path);
@@ -226,12 +255,33 @@ var IndexedDbMeta = class {
226
255
  await settle(transaction, () => void 0);
227
256
  this.changed({ trees: paths });
228
257
  }
258
+ settleRemoves() {
259
+ if (this.removing.size > 0) void this.flushRemoves().catch(() => void 0);
260
+ }
261
+ flushRemoves() {
262
+ clearTimeout(this.removeTimer);
263
+ this.removeTimer = void 0;
264
+ const batch = this.removing;
265
+ if (batch.size === 0) return Promise.resolve();
266
+ this.removing = /* @__PURE__ */ new Map();
267
+ const [transaction, store] = this.transaction("readwrite");
268
+ for (const [path, seen] of batch) {
269
+ const request2 = store.get(path);
270
+ request2.onsuccess = () => {
271
+ if (request2.result && JSON.stringify(request2.result) === seen) store.delete(path);
272
+ };
273
+ }
274
+ const paths = [...batch.keys()];
275
+ return settle(transaction, () => void this.channel.postMessage({ paths }));
276
+ }
229
277
  async links(dir) {
278
+ this.settleRemoves();
230
279
  const [transaction, store] = this.transaction("readonly");
231
280
  const request2 = store.index("dir").getAll(dir);
232
281
  return settle(transaction, () => request2.result);
233
282
  }
234
283
  async under(path) {
284
+ this.settleRemoves();
235
285
  const [transaction, store] = this.transaction("readonly");
236
286
  const self = store.get(path);
237
287
  const below = store.getAll(descendants(path));
@@ -243,6 +293,7 @@ var IndexedDbMeta = class {
243
293
  );
244
294
  }
245
295
  async all() {
296
+ this.settleRemoves();
246
297
  const [transaction, store] = this.transaction("readonly");
247
298
  const request2 = store.getAll();
248
299
  return settle(transaction, () => request2.result);
@@ -806,7 +857,14 @@ function sidecar(meta) {
806
857
  const guarded2 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
807
858
  throw translate(err, pathOf(args), "sidecar", false);
808
859
  });
809
- return Object.fromEntries(META_OPS.map((op) => [op, guarded2(op)]));
860
+ const ops = Object.fromEntries(META_OPS.map((op) => [op, guarded2(op)]));
861
+ if (meta.absent) ops.absent = meta.absent.bind(meta);
862
+ const { removeFile } = meta;
863
+ if (removeFile)
864
+ ops.removeFile = (path) => removeFile.call(meta, path).catch((err) => {
865
+ throw translate(err, path, "sidecar", false);
866
+ });
867
+ return ops;
810
868
  }
811
869
  function missing2(err) {
812
870
  const code = err?.code;
@@ -1503,6 +1561,7 @@ var OpfsFs = class {
1503
1561
  throw error;
1504
1562
  }
1505
1563
  this.refuse(found.path, true);
1564
+ if (options.file && found.handle?.kind === "directory") throw fsError("EISDIR", path);
1506
1565
  if (found.handle) {
1507
1566
  const [dir, name2] = await this.parent(found.path);
1508
1567
  try {
@@ -1513,8 +1572,19 @@ var OpfsFs = class {
1513
1572
  } catch (err) {
1514
1573
  throw translate(err, path);
1515
1574
  }
1516
- this.changed(found.path);
1575
+ const file = found.handle.kind === "file";
1576
+ if (!file || this.remote?.watched()) this.changed(found.path);
1577
+ else this.forget(found.path);
1517
1578
  this.movePending(found.path);
1579
+ if (file && this.meta.absent?.(found.path)) {
1580
+ this.touch(parentOf3(found.path));
1581
+ return;
1582
+ }
1583
+ if (file && this.meta.removeFile) {
1584
+ await this.meta.removeFile(found.path);
1585
+ this.touch(parentOf3(found.path));
1586
+ return;
1587
+ }
1518
1588
  }
1519
1589
  await this.meta.remove([found.path]);
1520
1590
  this.touch(parentOf3(found.path));
@@ -7487,9 +7557,13 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
7487
7557
  // src/fs/fresh-modules.ts
7488
7558
  var MAGIC = [0, 97, 115, 109];
7489
7559
  var MAX_FRESH = 65536;
7560
+ var DEFAULT_UMASK = 18;
7490
7561
  function isModule(bytes) {
7491
7562
  return MAGIC.every((b, i) => bytes[i] === b);
7492
7563
  }
7564
+ function within4(path, dir) {
7565
+ return path.startsWith(dir === "/" ? "/" : `${dir}/`);
7566
+ }
7493
7567
  var FreshModules = class {
7494
7568
  fresh = /* @__PURE__ */ new Map();
7495
7569
  add(path, umask, pid) {
@@ -7497,17 +7571,40 @@ var FreshModules = class {
7497
7571
  this.fresh.delete(key);
7498
7572
  const oldest = this.fresh.size >= MAX_FRESH ? this.fresh.keys().next().value : void 0;
7499
7573
  if (oldest !== void 0) this.fresh.delete(oldest);
7500
- this.fresh.set(key, { umask: umask & 511, pid, added: 0 });
7574
+ const mask = umask & 511;
7575
+ this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
7501
7576
  }
7502
7577
  forget(pid) {
7503
7578
  for (const [path, fresh2] of this.fresh) if (fresh2.pid === pid) this.fresh.delete(path);
7504
7579
  }
7580
+ drop(fresh2) {
7581
+ for (const [key, note] of [...this.fresh]) if (note === fresh2) this.fresh.delete(key);
7582
+ }
7583
+ moved(from, to) {
7584
+ const source = normalizePath(from);
7585
+ const target = normalizePath(to);
7586
+ if (source === target) return;
7587
+ for (const key of [...this.fresh.keys()]) {
7588
+ if (key === target || within4(key, target)) this.fresh.delete(key);
7589
+ }
7590
+ for (const [key, fresh2] of [...this.fresh]) {
7591
+ if (key !== source && !within4(key, source)) continue;
7592
+ this.fresh.delete(key);
7593
+ this.fresh.set(target + key.slice(source.length), fresh2);
7594
+ }
7595
+ }
7505
7596
  wrap(fs) {
7506
- const decide = async (given, bytes) => {
7597
+ const head = async (path) => {
7598
+ if (fs.pread) return fs.pread(path, 0, MAGIC.length);
7599
+ return (await fs.readFileBuffer(path)).subarray(0, MAGIC.length);
7600
+ };
7601
+ const decide = async (given, known) => {
7507
7602
  const path = normalizePath(given);
7508
7603
  const fresh2 = this.fresh.get(path);
7509
- if (!fresh2 || typeof bytes === "string" || bytes.length < MAGIC.length) return;
7604
+ if (!fresh2) return;
7605
+ const bytes = known && known.length >= MAGIC.length ? known : await head(path);
7510
7606
  const want = isModule(bytes) ? 73 & ~fresh2.umask : 0;
7607
+ fresh2.setup = false;
7511
7608
  if (want === fresh2.added) return;
7512
7609
  const mode = (await fs.stat(path)).mode & 4095;
7513
7610
  await fs.chmod(path, mode & ~fresh2.added | want);
@@ -7516,32 +7613,47 @@ var FreshModules = class {
7516
7613
  const overrides = {
7517
7614
  writeFile: async (path, content, owner) => {
7518
7615
  await fs.writeFile(path, content, owner);
7519
- await decide(path, content);
7616
+ if (this.fresh.has(normalizePath(path))) {
7617
+ await decide(
7618
+ path,
7619
+ typeof content === "string" ? new TextEncoder().encode(content) : content
7620
+ );
7621
+ }
7520
7622
  },
7521
7623
  chmod: async (path, mode) => {
7522
7624
  const fresh2 = this.fresh.get(normalizePath(path));
7523
- const creating = fresh2?.added === 0 && (mode & 4095) === (438 & ~fresh2.umask);
7524
- if (!creating) this.fresh.delete(normalizePath(path));
7625
+ if (fresh2?.setup && (mode & 4095) === (438 & ~fresh2.umask)) fresh2.setup = false;
7626
+ else if (fresh2) this.drop(fresh2);
7525
7627
  await fs.chmod(path, mode);
7526
7628
  },
7527
7629
  rm: async (path, options) => {
7528
- this.fresh.delete(normalizePath(path));
7529
7630
  await fs.rm(path, options);
7631
+ const at = normalizePath(path);
7632
+ for (const key of [...this.fresh.keys()]) {
7633
+ if (key === at || within4(key, at)) this.fresh.delete(key);
7634
+ }
7530
7635
  },
7531
7636
  rename: async (from, to) => {
7532
7637
  await fs.rename(from, to);
7533
- const fresh2 = this.fresh.get(normalizePath(from));
7534
- this.fresh.delete(normalizePath(from));
7535
- this.fresh.delete(normalizePath(to));
7638
+ this.moved(from, to);
7639
+ },
7640
+ link: async (from, to, follow2) => {
7641
+ await fs.link(from, to, follow2);
7642
+ const source = follow2 && fs.realpath ? await fs.realpath(from, true) : from;
7643
+ const fresh2 = this.fresh.get(normalizePath(source));
7536
7644
  if (fresh2) this.fresh.set(normalizePath(to), fresh2);
7537
7645
  }
7538
7646
  };
7539
7647
  const ranged2 = rangedOps(fs);
7540
7648
  if (ranged2) {
7541
7649
  overrides.pwrite = async (path, at, bytes, transfer) => {
7542
- const head = at === 0 && this.fresh.has(normalizePath(path)) ? bytes.slice(0, 4) : void 0;
7650
+ const touches2 = at < MAGIC.length && this.fresh.has(normalizePath(path));
7543
7651
  await ranged2.pwrite(path, at, bytes, transfer);
7544
- if (head) await decide(path, head);
7652
+ if (touches2) await decide(path);
7653
+ };
7654
+ overrides.truncate = async (path, size) => {
7655
+ await ranged2.truncate(path, size);
7656
+ if (size < MAGIC.length && this.fresh.has(normalizePath(path))) await decide(path);
7545
7657
  };
7546
7658
  }
7547
7659
  const bound2 = /* @__PURE__ */ new WeakMap();
@@ -7613,7 +7725,7 @@ var UNLINKED_DIR = ".slicc-unlinked";
7613
7725
  function webLocks() {
7614
7726
  return globalThis.navigator?.locks;
7615
7727
  }
7616
- var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
7728
+ var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
7617
7729
  var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
7618
7730
  var lockName = (instance) => `slicc-unlinked:${instance}`;
7619
7731
  var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
@@ -7713,7 +7825,7 @@ var UnlinkedKeeper = class {
7713
7825
  for (const holder of this.holders) {
7714
7826
  if (holder.owns(target)) continue;
7715
7827
  for (const path of holder.held()) {
7716
- if (!within4(path, target) || holder.isKept(path)) continue;
7828
+ if (!within5(path, target) || holder.isKept(path)) continue;
7717
7829
  held.set(path, [...held.get(path) ?? [], holder]);
7718
7830
  }
7719
7831
  }
@@ -8336,7 +8448,7 @@ function parseSize(text) {
8336
8448
  if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
8337
8449
  return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
8338
8450
  }
8339
- var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
8451
+ var within6 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
8340
8452
  var HeldSet = class extends Set {
8341
8453
  idle;
8342
8454
  constructor(idle) {
@@ -8355,7 +8467,7 @@ var HeldSet = class extends Set {
8355
8467
  }
8356
8468
  };
8357
8469
  function heldUnder(held, target) {
8358
- for (const paths of held) for (const path of paths.keys()) if (within5(path, target)) return true;
8470
+ for (const paths of held) for (const path of paths.keys()) if (within6(path, target)) return true;
8359
8471
  return false;
8360
8472
  }
8361
8473
  var HeldPaths = class extends Map {
@@ -8471,24 +8583,24 @@ var HeldPaths = class extends Map {
8471
8583
  return this.owning === path;
8472
8584
  }
8473
8585
  revoke(prefix) {
8474
- for (const path of this.keys()) if (within5(path, prefix)) this.revoked.add(path);
8586
+ for (const path of this.keys()) if (within6(path, prefix)) this.revoked.add(path);
8475
8587
  }
8476
8588
  renamed(from, to) {
8477
8589
  for (const [path, count2] of [...this]) {
8478
- if (!within5(path, from)) continue;
8590
+ if (!within6(path, from)) continue;
8479
8591
  const moved2 = to + path.slice(from.length);
8480
8592
  this.delete(path);
8481
8593
  this.set(moved2, (this.get(moved2) ?? 0) + count2);
8482
8594
  if (this.revoked.delete(path)) this.revoked.add(moved2);
8483
8595
  }
8484
8596
  for (const [path, count2] of [...this.opens]) {
8485
- if (!within5(path, from)) continue;
8597
+ if (!within6(path, from)) continue;
8486
8598
  const moved2 = to + path.slice(from.length);
8487
8599
  this.opens.delete(path);
8488
8600
  this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count2);
8489
8601
  }
8490
8602
  for (const [id2, key] of this.handles) {
8491
- if (within5(key, from)) this.handles.set(id2, to + key.slice(from.length));
8603
+ if (within6(key, from)) this.handles.set(id2, to + key.slice(from.length));
8492
8604
  }
8493
8605
  }
8494
8606
  isRevoked(path) {
@@ -8592,10 +8704,14 @@ var MountTable = class {
8592
8704
  ...error ? { error } : {}
8593
8705
  });
8594
8706
  }
8707
+ under(path) {
8708
+ for (const mount of this.mounts.values()) if (within6(mount.target, path)) return true;
8709
+ return false;
8710
+ }
8595
8711
  locate(path) {
8596
8712
  let best;
8597
8713
  for (const mount of this.mounts.values()) {
8598
- if (within5(path, mount.target) && (!best || mount.target.length > best.target.length))
8714
+ if (within6(path, mount.target) && (!best || mount.target.length > best.target.length))
8599
8715
  best = mount;
8600
8716
  }
8601
8717
  if (!best) return void 0;
@@ -8738,7 +8854,7 @@ var MountTable = class {
8738
8854
  async commitUnder(prefix) {
8739
8855
  const at = normalizePath(prefix);
8740
8856
  await Promise.all(
8741
- [...this.sessions.keys()].filter((p) => within5(p, at)).map((p) => this.commit(p))
8857
+ [...this.sessions.keys()].filter((p) => within6(p, at)).map((p) => this.commit(p))
8742
8858
  );
8743
8859
  }
8744
8860
  async commitOverlapping(real, offset, length2) {
@@ -8773,10 +8889,10 @@ var MountTable = class {
8773
8889
  }
8774
8890
  forget(mount, path) {
8775
8891
  mount.epoch++;
8776
- for (const key of [...mount.attrs.keys()]) if (within5(key, path)) mount.attrs.delete(key);
8892
+ for (const key of [...mount.attrs.keys()]) if (within6(key, path)) mount.attrs.delete(key);
8777
8893
  mount.lists.delete(path);
8778
8894
  mount.lists.delete(parentOf7(path));
8779
- for (const key of [...mount.lists.keys()]) if (within5(key, path)) mount.lists.delete(key);
8895
+ for (const key of [...mount.lists.keys()]) if (within6(key, path)) mount.lists.delete(key);
8780
8896
  }
8781
8897
  async getattr(mount, path, rel2, settled = false) {
8782
8898
  for (let round = 1; ; round++) {
@@ -9161,9 +9277,12 @@ var MountFs = class {
9161
9277
  }
9162
9278
  async rm(path, options = {}) {
9163
9279
  const found = this.at(path);
9280
+ if (!found && options.recursive && this.table.under(normalizePath(path)))
9281
+ return this.rmAround(normalizePath(path), options);
9164
9282
  if (!found) return this.base.rm(path, options);
9165
9283
  const { mount, rel: rel2 } = found;
9166
9284
  const real = normalizePath(path);
9285
+ if (rel2 === "/" && options.recursive) await this.rmEach(real, mount, rel2, options);
9167
9286
  if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
9168
9287
  this.writable(mount, real);
9169
9288
  await this.table.commitUnder(real);
@@ -9174,16 +9293,33 @@ var MountFs = class {
9174
9293
  if (options.force && err.code === "ENOENT") return;
9175
9294
  throw err;
9176
9295
  }
9296
+ if (options.file && attr2.kind === "directory") throw errnoError("EISDIR", real);
9177
9297
  this.table.forget(mount, real);
9178
9298
  if (attr2.kind === "directory" && options.recursive) {
9179
- for (const entry of await this.table.readdir(mount, real, rel2)) {
9180
- await this.rm(`${real}/${entry.name}`, { recursive: true, force: options.force ?? false });
9181
- }
9299
+ await this.rmEach(real, mount, rel2, { recursive: true, force: options.force ?? false });
9182
9300
  this.table.forget(mount, real);
9183
9301
  }
9184
9302
  await mount.conn.call({ op: attr2.kind === "directory" ? "rmdir" : "unlink", path: rel2 });
9185
9303
  this.table.forget(mount, real);
9186
9304
  }
9305
+ async rmEach(real, mount, rel2, options) {
9306
+ let first;
9307
+ for (const entry of await this.table.readdir(mount, real, rel2)) {
9308
+ await this.rm(joinName(real, entry.name), options).catch((err) => {
9309
+ first ??= err;
9310
+ });
9311
+ }
9312
+ if (first) throw first;
9313
+ }
9314
+ async rmAround(path, options) {
9315
+ let first;
9316
+ for (const name2 of await this.base.readdir(path)) {
9317
+ await this.rm(joinName(path, name2), options).catch((err) => {
9318
+ first ??= err;
9319
+ });
9320
+ }
9321
+ throw first ?? errnoError("EBUSY", `${path} holds a mount point`);
9322
+ }
9187
9323
  async rename(from, to) {
9188
9324
  const a = this.at(from);
9189
9325
  const b = this.at(to);
@@ -9411,6 +9547,11 @@ function statJson(s) {
9411
9547
  async function readdirStat(fs, dir) {
9412
9548
  return (await fs.readdirStat(dir)).map(([name2, stat]) => [name2, stat && statJson(stat)]);
9413
9549
  }
9550
+ async function usageAt(entry, path) {
9551
+ if (!entry.statfs) return void 0;
9552
+ const real = await realTarget(entry.fs, path).catch(() => path);
9553
+ return entry.statfs(real);
9554
+ }
9414
9555
  async function storageUsage() {
9415
9556
  const estimate = await globalThis.navigator?.storage?.estimate?.();
9416
9557
  if (typeof estimate?.quota !== "number") return null;
@@ -9482,8 +9623,7 @@ async function run(entry, path, req) {
9482
9623
  await fs.link(path, fs.resolvePath(cwd, req.arg2 ?? ""), req.mode === 1);
9483
9624
  return done;
9484
9625
  case "unlink":
9485
- if ((await fs.lstat(path)).isDirectory) throw syncError("EISDIR", path);
9486
- await fs.rm(path);
9626
+ await fs.rm(path, { file: true });
9487
9627
  return done;
9488
9628
  case "rmdir":
9489
9629
  if (!(await fs.lstat(path)).isDirectory) throw syncError("ENOTDIR", path);
@@ -9717,6 +9857,26 @@ async function dispatchSyncFs(req) {
9717
9857
  return toErrno(err);
9718
9858
  }
9719
9859
  }
9860
+ var DENIED = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
9861
+ async function rmPermitted(entry, path, req, refused2) {
9862
+ const st = await entry.fs.lstat(path).catch(() => void 0);
9863
+ if (!st?.isDirectory) return refused2;
9864
+ const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
9865
+ if (unlisted) return unlisted;
9866
+ const names = await entry.fs.readdir(path).catch(() => void 0);
9867
+ if (!names) return refused2;
9868
+ let first;
9869
+ for (const name2 of names) {
9870
+ const child = await dispatchOne(entry, {
9871
+ ...req,
9872
+ path: joinName(path, name2)
9873
+ });
9874
+ if (!child.ok) first ??= child;
9875
+ }
9876
+ if (first) return first;
9877
+ const again = await guarded(entry, path, req);
9878
+ return again ?? await run(entry, path, req);
9879
+ }
9720
9880
  async function dispatchOne(entry, req) {
9721
9881
  const { fs, cwd } = entry;
9722
9882
  try {
@@ -9726,8 +9886,11 @@ async function dispatchOne(entry, req) {
9726
9886
  if (req.op === "access") return await accessOp(entry, path, req);
9727
9887
  if (req.op === "hold") return await holdOp(entry, path, req);
9728
9888
  const refused2 = await guarded(entry, path, req);
9889
+ if (refused2 && req.op === "rm" && !refused2.ok && DENIED.has(refused2.errno)) {
9890
+ return await rmPermitted(entry, path, req, refused2);
9891
+ }
9729
9892
  if (refused2) return refused2;
9730
- if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
9893
+ if (req.op === "statfs") return json2(await usageAt(entry, path) ?? await storageUsage());
9731
9894
  if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };
9732
9895
  if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
9733
9896
  const serve = async () => run(entry, await keptOr(entry, path, req.handle), req);
@@ -11961,7 +12124,7 @@ function isMissing(err) {
11961
12124
  if (err?.code === "ENOENT") return true;
11962
12125
  return err instanceof Error && err.message.startsWith("ENOENT");
11963
12126
  }
11964
- function within6(path, root) {
12127
+ function within7(path, root) {
11965
12128
  return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
11966
12129
  }
11967
12130
  var VfsNode = class {
@@ -12282,12 +12445,12 @@ var VfsNodes = class {
12282
12445
  return node;
12283
12446
  }
12284
12447
  holds(prefix) {
12285
- for (const path of this.byPath.keys()) if (within6(path, prefix)) return true;
12448
+ for (const path of this.byPath.keys()) if (within7(path, prefix)) return true;
12286
12449
  return false;
12287
12450
  }
12288
12451
  writes(prefix) {
12289
12452
  for (const [path, node] of this.byPath)
12290
- if (node.writers > 0 && within6(path, prefix)) return true;
12453
+ if (node.writers > 0 && within7(path, prefix)) return true;
12291
12454
  return false;
12292
12455
  }
12293
12456
  closed(node, writable2 = false) {
@@ -12331,11 +12494,11 @@ var VfsNodes = class {
12331
12494
  this.kept.delete(node);
12332
12495
  }
12333
12496
  async flush(path) {
12334
- for (const [p, node] of this.byPath) if (within6(p, path)) await node.serial(() => node.flush());
12497
+ for (const [p, node] of this.byPath) if (within7(p, path)) await node.serial(() => node.flush());
12335
12498
  }
12336
12499
  async unlinking(path) {
12337
12500
  for (const [held, node] of [...this.byPath]) {
12338
- if (!within6(held, path)) continue;
12501
+ if (!within7(held, path)) continue;
12339
12502
  await node.serial(async () => {
12340
12503
  await Promise.all([this.keepsOnDisk || node.keep(), node.limit()]);
12341
12504
  });
@@ -12343,14 +12506,14 @@ var VfsNodes = class {
12343
12506
  }
12344
12507
  revoke(prefix) {
12345
12508
  for (const [path, node] of [...this.byPath]) {
12346
- if (!within6(path, prefix)) continue;
12509
+ if (!within7(path, prefix)) continue;
12347
12510
  node.revoke();
12348
12511
  this.forget(node);
12349
12512
  }
12350
12513
  }
12351
12514
  unlinked(path) {
12352
12515
  for (const [held, node] of [...this.byPath]) {
12353
- if (!within6(held, path) || this.byPath.get(held) !== node) continue;
12516
+ if (!within7(held, path) || this.byPath.get(held) !== node) continue;
12354
12517
  if (!this.dropName(held, node)) this.orphan(node);
12355
12518
  }
12356
12519
  }
@@ -12358,8 +12521,8 @@ var VfsNodes = class {
12358
12521
  if (from === to) return;
12359
12522
  const moved2 = [];
12360
12523
  for (const [p, node] of [...this.byPath]) {
12361
- if (within6(p, from)) moved2.push([p, node]);
12362
- else if (within6(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
12524
+ if (within7(p, from)) moved2.push([p, node]);
12525
+ else if (within7(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
12363
12526
  this.orphan(node);
12364
12527
  }
12365
12528
  }
@@ -12638,7 +12801,7 @@ function isWasmSyscall(req) {
12638
12801
  var MAX_READ = 1024 * 1024;
12639
12802
  var WRITE_ACCESS = 3;
12640
12803
  var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
12641
- var DEFAULT_UMASK = 18;
12804
+ var DEFAULT_UMASK2 = 18;
12642
12805
  function words(value) {
12643
12806
  if (!Array.isArray(value) || !value.every((w) => typeof w === "string")) {
12644
12807
  throw new KernelError("EINVAL");
@@ -12666,7 +12829,7 @@ var WasmProcess = class {
12666
12829
  interrupt = new AbortController();
12667
12830
  gone = new AbortController();
12668
12831
  dying = false;
12669
- umask = DEFAULT_UMASK;
12832
+ umask = DEFAULT_UMASK2;
12670
12833
  cred;
12671
12834
  execChild;
12672
12835
  killed = false;
@@ -12688,7 +12851,7 @@ var WasmProcess = class {
12688
12851
  this.fds = fds;
12689
12852
  this.options = options;
12690
12853
  this.inherited = options.ignored ?? 0;
12691
- this.umask = options.umask ?? DEFAULT_UMASK;
12854
+ this.umask = options.umask ?? DEFAULT_UMASK2;
12692
12855
  this.cred = copyCred(options.cred ?? ROOT);
12693
12856
  this.children = new ChildTable(fds, options.spawner, options.forker);
12694
12857
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
@@ -18271,7 +18434,7 @@ var Launcher = class {
18271
18434
  }
18272
18435
  }
18273
18436
  umaskOf(pid) {
18274
- return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
18437
+ return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK2;
18275
18438
  }
18276
18439
  imageOf(pid) {
18277
18440
  const members = this.jobs.list();