@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.
- package/README.md +10 -3
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +208 -45
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +27 -5
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +289 -68
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +27 -5
- package/dist/process-worker.js.map +3 -3
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
7524
|
-
if (
|
|
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
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
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
|
|
7650
|
+
const touches2 = at < MAGIC.length && this.fresh.has(normalizePath(path));
|
|
7543
7651
|
await ranged2.pwrite(path, at, bytes, transfer);
|
|
7544
|
-
if (
|
|
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
|
|
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 (!
|
|
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
|
|
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 (
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
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) =>
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (
|
|
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 &&
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
12362
|
-
else if (
|
|
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
|
|
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 =
|
|
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 ??
|
|
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?.() ??
|
|
18437
|
+
return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK2;
|
|
18275
18438
|
}
|
|
18276
18439
|
imageOf(pid) {
|
|
18277
18440
|
const members = this.jobs.list();
|