@ai-ecoverse/slicc-kernel 1.21.1 → 1.22.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 +8 -6
- package/dist/driver-worker.js +6 -4
- package/dist/driver-worker.js.map +2 -2
- package/dist/driver.js +6 -4
- package/dist/driver.js.map +2 -2
- package/dist/kernel-worker.js +533 -61
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +6 -4
- package/dist/node-driver-worker.js.map +2 -2
- package/dist/node-process-worker.js +387 -33
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +535 -62
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +387 -33
- package/dist/process-worker.js.map +4 -4
- package/package.json +1 -1
package/dist/node.js
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
import { MessageChannel as MessageChannel2, Worker } from "node:worker_threads";
|
|
3
3
|
|
|
4
4
|
// src/fs/types.ts
|
|
5
|
+
function rangedOps(fs) {
|
|
6
|
+
const { pread, pwrite, truncate } = fs;
|
|
7
|
+
if (!pread || !pwrite || !truncate) return void 0;
|
|
8
|
+
return { pread: pread.bind(fs), pwrite: pwrite.bind(fs), truncate: truncate.bind(fs) };
|
|
9
|
+
}
|
|
5
10
|
function fsError(code, path) {
|
|
6
11
|
return Object.assign(new Error(`${code}: ${path}`), { code });
|
|
7
12
|
}
|
|
@@ -2083,7 +2088,8 @@ function withCommandDirs(fs, names) {
|
|
|
2083
2088
|
symlink: (target, path) => fs.symlink(target, path),
|
|
2084
2089
|
readlink: (path) => fs.readlink(path),
|
|
2085
2090
|
chmod: (path, mode) => fs.chmod(path, mode),
|
|
2086
|
-
utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime)
|
|
2091
|
+
utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
|
|
2092
|
+
...rangedOps(fs) ?? {}
|
|
2087
2093
|
};
|
|
2088
2094
|
}
|
|
2089
2095
|
|
|
@@ -2160,6 +2166,11 @@ var FsWatchers = class {
|
|
|
2160
2166
|
chmod: after(fs.chmod.bind(fs), (path) => [path]),
|
|
2161
2167
|
utimes: after(fs.utimes.bind(fs), (path) => [path])
|
|
2162
2168
|
};
|
|
2169
|
+
const ranged2 = rangedOps(fs);
|
|
2170
|
+
if (ranged2) {
|
|
2171
|
+
overrides.pwrite = after(ranged2.pwrite, (path) => [path]);
|
|
2172
|
+
overrides.truncate = after(ranged2.truncate, (path) => [path]);
|
|
2173
|
+
}
|
|
2163
2174
|
return new Proxy(fs, {
|
|
2164
2175
|
get(target, key, receiver) {
|
|
2165
2176
|
if (Object.hasOwn(overrides, key)) return overrides[key];
|
|
@@ -2354,7 +2365,7 @@ var ChildTable = class {
|
|
|
2354
2365
|
};
|
|
2355
2366
|
|
|
2356
2367
|
// src/mount/protocol.ts
|
|
2357
|
-
var DRIVER_PROTOCOL = [1,
|
|
2368
|
+
var DRIVER_PROTOCOL = [1, 1];
|
|
2358
2369
|
function driverVersionError(theirs) {
|
|
2359
2370
|
const major = Array.isArray(theirs) ? theirs[0] : void 0;
|
|
2360
2371
|
if (major === DRIVER_PROTOCOL[0]) return void 0;
|
|
@@ -2513,7 +2524,8 @@ function statOf(attr, path, mount) {
|
|
|
2513
2524
|
ino: attr.ino ?? inodeOf(path),
|
|
2514
2525
|
dev: mount.dev,
|
|
2515
2526
|
...readonly(mount) ? { readonly: true } : {},
|
|
2516
|
-
...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {}
|
|
2527
|
+
...mount.maxFile > 0 ? { maxFile: mount.maxFile } : {},
|
|
2528
|
+
...mount.caps.ranges ? { ranged: true } : {}
|
|
2517
2529
|
};
|
|
2518
2530
|
}
|
|
2519
2531
|
function concat2(chunks2, total) {
|
|
@@ -2526,10 +2538,26 @@ function concat2(chunks2, total) {
|
|
|
2526
2538
|
}
|
|
2527
2539
|
return out;
|
|
2528
2540
|
}
|
|
2541
|
+
function resized(bytes, size) {
|
|
2542
|
+
if (size <= bytes.length) return bytes.subarray(0, size);
|
|
2543
|
+
const out = new Uint8Array(size);
|
|
2544
|
+
out.set(bytes);
|
|
2545
|
+
return out;
|
|
2546
|
+
}
|
|
2547
|
+
function handed(bytes, transfer, chunk2) {
|
|
2548
|
+
const whole = bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
2549
|
+
return transfer && whole && bytes.length <= chunk2 && bytes.buffer instanceof ArrayBuffer;
|
|
2550
|
+
}
|
|
2551
|
+
function spliced(bytes, offset, part) {
|
|
2552
|
+
const out = resized(bytes, Math.max(bytes.length, offset + part.length));
|
|
2553
|
+
out.set(part, offset);
|
|
2554
|
+
return out;
|
|
2555
|
+
}
|
|
2529
2556
|
var MountTable = class {
|
|
2530
2557
|
mounts = /* @__PURE__ */ new Map();
|
|
2531
2558
|
devices = 0;
|
|
2532
2559
|
pending = /* @__PURE__ */ new Set();
|
|
2560
|
+
notes = /* @__PURE__ */ new Map();
|
|
2533
2561
|
deps;
|
|
2534
2562
|
constructor(deps) {
|
|
2535
2563
|
this.deps = deps;
|
|
@@ -2538,7 +2566,7 @@ var MountTable = class {
|
|
|
2538
2566
|
return this.mounts.get(target);
|
|
2539
2567
|
}
|
|
2540
2568
|
list() {
|
|
2541
|
-
|
|
2569
|
+
const mounted = [...this.mounts.values()].map(
|
|
2542
2570
|
({ type, source, target, options, state, error, present }) => ({
|
|
2543
2571
|
type,
|
|
2544
2572
|
source,
|
|
@@ -2548,6 +2576,19 @@ var MountTable = class {
|
|
|
2548
2576
|
...error ? { error } : {}
|
|
2549
2577
|
})
|
|
2550
2578
|
);
|
|
2579
|
+
const noted = [...this.notes.values()].filter((note) => !this.mounts.has(note.target));
|
|
2580
|
+
return [...mounted, ...noted.map((note) => ({ ...note, options: { ...note.options } }))];
|
|
2581
|
+
}
|
|
2582
|
+
note(spec, state, error) {
|
|
2583
|
+
const target = normalizePath(spec.target);
|
|
2584
|
+
this.notes.set(target, {
|
|
2585
|
+
type: spec.type,
|
|
2586
|
+
source: spec.source,
|
|
2587
|
+
target,
|
|
2588
|
+
options: { ...spec.options },
|
|
2589
|
+
state,
|
|
2590
|
+
...error ? { error } : {}
|
|
2591
|
+
});
|
|
2551
2592
|
}
|
|
2552
2593
|
locate(path) {
|
|
2553
2594
|
let best;
|
|
@@ -2624,13 +2665,19 @@ var MountTable = class {
|
|
|
2624
2665
|
};
|
|
2625
2666
|
mounted = mount;
|
|
2626
2667
|
this.mounts.set(target, mount);
|
|
2668
|
+
this.notes.delete(target);
|
|
2627
2669
|
return this.list().find((m) => m.target === target);
|
|
2628
2670
|
}
|
|
2671
|
+
unnote(target) {
|
|
2672
|
+
this.notes.delete(normalizePath(target));
|
|
2673
|
+
}
|
|
2629
2674
|
umount(target, detach = false) {
|
|
2630
2675
|
const at = normalizePath(target);
|
|
2631
2676
|
const mount = this.mounts.get(at);
|
|
2677
|
+
if (!mount && this.notes.delete(at)) return;
|
|
2632
2678
|
if (!mount) throw errnoError("EINVAL", `${at} is not mounted`);
|
|
2633
2679
|
if (!detach && this.deps.busy(at)) throw errnoError("EBUSY", `${at} has open files`);
|
|
2680
|
+
this.notes.delete(at);
|
|
2634
2681
|
this.mounts.delete(at);
|
|
2635
2682
|
mount.conn.close();
|
|
2636
2683
|
mount.dispose();
|
|
@@ -2805,15 +2852,95 @@ var MountFs = class {
|
|
|
2805
2852
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
2806
2853
|
try {
|
|
2807
2854
|
for (let offset = 0; offset < bytes.length; offset += chunk2) {
|
|
2808
|
-
|
|
2809
|
-
|
|
2855
|
+
const part = bytes.slice(offset, offset + chunk2);
|
|
2856
|
+
await mount.conn.call({ op: "write", fh, offset, bytes: part }, [part.buffer]);
|
|
2857
|
+
}
|
|
2858
|
+
} finally {
|
|
2859
|
+
await mount.conn.call({ op: "release", fh });
|
|
2860
|
+
this.table.forget(mount, real);
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
async pread(path, offset, length2) {
|
|
2864
|
+
const real = await this.follow(path);
|
|
2865
|
+
const found = this.at(real);
|
|
2866
|
+
if (!found) return (await this.base.readFileBuffer(real)).slice(offset, offset + length2);
|
|
2867
|
+
const { mount, rel: rel2 } = found;
|
|
2868
|
+
if ((await this.table.getattr(mount, real, rel2)).kind === "directory") {
|
|
2869
|
+
throw errnoError("EISDIR", real);
|
|
2870
|
+
}
|
|
2871
|
+
const fh = await this.open(mount, rel2, false);
|
|
2872
|
+
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
2873
|
+
const chunks2 = [];
|
|
2874
|
+
let total = 0;
|
|
2875
|
+
try {
|
|
2876
|
+
while (total < length2) {
|
|
2877
|
+
const bytes = await mount.conn.call({
|
|
2878
|
+
op: "read",
|
|
2810
2879
|
fh,
|
|
2811
|
-
offset,
|
|
2812
|
-
|
|
2880
|
+
offset: offset + total,
|
|
2881
|
+
size: Math.min(chunk2, length2 - total)
|
|
2813
2882
|
});
|
|
2883
|
+
if (bytes.length === 0) break;
|
|
2884
|
+
chunks2.push(bytes);
|
|
2885
|
+
total += bytes.length;
|
|
2814
2886
|
}
|
|
2815
2887
|
} finally {
|
|
2816
2888
|
await mount.conn.call({ op: "release", fh });
|
|
2889
|
+
}
|
|
2890
|
+
return concat2(chunks2, total);
|
|
2891
|
+
}
|
|
2892
|
+
fits(mount, real, end) {
|
|
2893
|
+
if (mount.maxFile > 0 && end > mount.maxFile) {
|
|
2894
|
+
throw errnoError(
|
|
2895
|
+
"EFBIG",
|
|
2896
|
+
`${real} would be larger than this mount's maxfile (${mount.maxFile})`
|
|
2897
|
+
);
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
async pwrite(path, offset, bytes, transfer = false) {
|
|
2901
|
+
const real = await this.follow(path, 0, true);
|
|
2902
|
+
const found = this.at(real);
|
|
2903
|
+
if (!found)
|
|
2904
|
+
return this.base.writeFile(
|
|
2905
|
+
real,
|
|
2906
|
+
spliced(await this.base.readFileBuffer(real), offset, bytes)
|
|
2907
|
+
);
|
|
2908
|
+
const { mount, rel: rel2 } = found;
|
|
2909
|
+
this.writable(mount, real);
|
|
2910
|
+
this.fits(mount, real, offset + bytes.length);
|
|
2911
|
+
this.table.forget(mount, real);
|
|
2912
|
+
const fh = await mount.conn.call({
|
|
2913
|
+
op: "open",
|
|
2914
|
+
path: rel2,
|
|
2915
|
+
write: true,
|
|
2916
|
+
create: true,
|
|
2917
|
+
truncate: false,
|
|
2918
|
+
exclusive: false
|
|
2919
|
+
});
|
|
2920
|
+
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
2921
|
+
try {
|
|
2922
|
+
for (let at = 0; at < bytes.length; at += chunk2) {
|
|
2923
|
+
const part = handed(bytes, transfer, chunk2) ? bytes : bytes.slice(at, at + chunk2);
|
|
2924
|
+
await mount.conn.call({ op: "write", fh, offset: offset + at, bytes: part }, [part.buffer]);
|
|
2925
|
+
}
|
|
2926
|
+
} finally {
|
|
2927
|
+
await mount.conn.call({ op: "release", fh });
|
|
2928
|
+
this.table.forget(mount, real);
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
async truncate(path, size) {
|
|
2932
|
+
const real = await this.follow(path);
|
|
2933
|
+
const found = this.at(real);
|
|
2934
|
+
if (!found?.mount.caps.ranges) {
|
|
2935
|
+
return this.writeFile(real, resized(await this.readFileBuffer(real), size));
|
|
2936
|
+
}
|
|
2937
|
+
const { mount, rel: rel2 } = found;
|
|
2938
|
+
this.writable(mount, real);
|
|
2939
|
+
this.fits(mount, real, size);
|
|
2940
|
+
this.table.forget(mount, real);
|
|
2941
|
+
try {
|
|
2942
|
+
await mount.conn.call({ op: "setattr", path: rel2, size });
|
|
2943
|
+
} finally {
|
|
2817
2944
|
this.table.forget(mount, real);
|
|
2818
2945
|
}
|
|
2819
2946
|
}
|
|
@@ -3000,7 +3127,8 @@ function statJson(s) {
|
|
|
3000
3127
|
ino: s.ino,
|
|
3001
3128
|
...s.dev !== void 0 ? { dev: s.dev } : {},
|
|
3002
3129
|
...s.readonly ? { readonly: true } : {},
|
|
3003
|
-
...s.maxFile ? { maxFile: s.maxFile } : {}
|
|
3130
|
+
...s.maxFile ? { maxFile: s.maxFile } : {},
|
|
3131
|
+
...s.ranged ? { ranged: true } : {}
|
|
3004
3132
|
};
|
|
3005
3133
|
}
|
|
3006
3134
|
async function readdirStat(fs, dir) {
|
|
@@ -3065,10 +3193,24 @@ async function run(entry, path, req) {
|
|
|
3065
3193
|
case "utimes":
|
|
3066
3194
|
await fs.utimes(path, new Date(req.atimeMs ?? 0), new Date(req.mtimeMs ?? 0));
|
|
3067
3195
|
return done;
|
|
3196
|
+
case "pread":
|
|
3197
|
+
case "pwrite":
|
|
3198
|
+
case "truncate":
|
|
3199
|
+
return ranged(fs, path, req);
|
|
3068
3200
|
default:
|
|
3069
3201
|
return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
|
|
3070
3202
|
}
|
|
3071
3203
|
}
|
|
3204
|
+
async function ranged(fs, path, req) {
|
|
3205
|
+
const ops = rangedOps(fs);
|
|
3206
|
+
if (!ops) throw syncError("ENOSYS", path);
|
|
3207
|
+
const offset = req.offset ?? 0;
|
|
3208
|
+
if (req.op === "pread")
|
|
3209
|
+
return { ok: true, kind: "bytes", bytes: await ops.pread(path, offset, req.length ?? 0) };
|
|
3210
|
+
if (req.op === "pwrite") await ops.pwrite(path, offset, req.body ?? new Uint8Array(0), true);
|
|
3211
|
+
else await ops.truncate(path, req.length ?? 0);
|
|
3212
|
+
return done;
|
|
3213
|
+
}
|
|
3072
3214
|
async function dispatchSyncFs(req) {
|
|
3073
3215
|
const entry = resolveSyncFsToken(req.token);
|
|
3074
3216
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
@@ -5396,6 +5538,226 @@ async function accept(proc, fd, nonblock) {
|
|
|
5396
5538
|
return { fd: proc.fds.install(new OpenFile(socket), 3), peer: socket.peer };
|
|
5397
5539
|
}
|
|
5398
5540
|
|
|
5541
|
+
// src/fs/pages.ts
|
|
5542
|
+
var PAGE = 64 * 1024;
|
|
5543
|
+
var READAHEAD = 1024 * 1024;
|
|
5544
|
+
var CLEAN_LIMIT = 4 * 1024 * 1024;
|
|
5545
|
+
var WRITEBACK = 8 * 1024 * 1024;
|
|
5546
|
+
var RUN = 1024 * 1024;
|
|
5547
|
+
var Pages = class {
|
|
5548
|
+
length;
|
|
5549
|
+
stored;
|
|
5550
|
+
shrink;
|
|
5551
|
+
pages = /* @__PURE__ */ new Map();
|
|
5552
|
+
dirty = /* @__PURE__ */ new Set();
|
|
5553
|
+
free = [];
|
|
5554
|
+
pinned = false;
|
|
5555
|
+
constructor(size) {
|
|
5556
|
+
this.length = size;
|
|
5557
|
+
this.stored = size;
|
|
5558
|
+
}
|
|
5559
|
+
get dirtyBytes() {
|
|
5560
|
+
return this.dirty.size * PAGE;
|
|
5561
|
+
}
|
|
5562
|
+
get changed() {
|
|
5563
|
+
return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
|
|
5564
|
+
}
|
|
5565
|
+
missing(at, length2) {
|
|
5566
|
+
this.evict();
|
|
5567
|
+
const end = Math.min(this.stored, at + length2, this.length);
|
|
5568
|
+
const runs = [];
|
|
5569
|
+
for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
|
|
5570
|
+
if (this.pages.has(page)) continue;
|
|
5571
|
+
const last2 = runs[runs.length - 1];
|
|
5572
|
+
if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
|
|
5573
|
+
else runs.push([page * PAGE, PAGE]);
|
|
5574
|
+
}
|
|
5575
|
+
const last = runs[runs.length - 1];
|
|
5576
|
+
if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
|
|
5577
|
+
return runs.map(([start, n]) => [start, Math.min(n, this.stored - start)]);
|
|
5578
|
+
}
|
|
5579
|
+
partial(at, length2) {
|
|
5580
|
+
if (length2 === 0) return [];
|
|
5581
|
+
this.evict();
|
|
5582
|
+
const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
|
|
5583
|
+
const runs = [];
|
|
5584
|
+
for (const page of edges) {
|
|
5585
|
+
const start = page * PAGE;
|
|
5586
|
+
const covered = at <= start && at + length2 >= start + PAGE;
|
|
5587
|
+
if (covered || this.pages.has(page) || start >= this.stored) continue;
|
|
5588
|
+
runs.push([start, Math.min(PAGE, this.stored - start)]);
|
|
5589
|
+
}
|
|
5590
|
+
return runs;
|
|
5591
|
+
}
|
|
5592
|
+
fill(start, bytes) {
|
|
5593
|
+
for (let at = 0; at < bytes.length; at += PAGE) {
|
|
5594
|
+
const page = (start + at) / PAGE;
|
|
5595
|
+
if (this.pages.has(page)) continue;
|
|
5596
|
+
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
5597
|
+
const fresh = this.take(part.length);
|
|
5598
|
+
fresh.set(part);
|
|
5599
|
+
this.pages.set(page, fresh);
|
|
5600
|
+
}
|
|
5601
|
+
}
|
|
5602
|
+
read(at, length2) {
|
|
5603
|
+
const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
|
|
5604
|
+
this.readInto(at, out);
|
|
5605
|
+
return out;
|
|
5606
|
+
}
|
|
5607
|
+
readInto(at, out) {
|
|
5608
|
+
const end = Math.min(this.length, at + out.length);
|
|
5609
|
+
for (let pos = at; pos < end; ) {
|
|
5610
|
+
const page = Math.floor(pos / PAGE);
|
|
5611
|
+
const from = pos - page * PAGE;
|
|
5612
|
+
const n = Math.min(PAGE - from, end - pos);
|
|
5613
|
+
const bytes = this.pages.get(page);
|
|
5614
|
+
if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
|
|
5615
|
+
else out.fill(0, pos - at, pos - at + n);
|
|
5616
|
+
pos += n;
|
|
5617
|
+
}
|
|
5618
|
+
return Math.max(0, end - at);
|
|
5619
|
+
}
|
|
5620
|
+
write(at, bytes) {
|
|
5621
|
+
for (let done4 = 0; done4 < bytes.length; ) {
|
|
5622
|
+
const pos = at + done4;
|
|
5623
|
+
const page = Math.floor(pos / PAGE);
|
|
5624
|
+
const from = pos - page * PAGE;
|
|
5625
|
+
const n = Math.min(PAGE - from, bytes.length - done4);
|
|
5626
|
+
let target = this.pages.get(page);
|
|
5627
|
+
if (!target) {
|
|
5628
|
+
target = this.take(0);
|
|
5629
|
+
this.pages.set(page, target);
|
|
5630
|
+
}
|
|
5631
|
+
target.set(bytes.subarray(done4, done4 + n), from);
|
|
5632
|
+
this.dirty.add(page);
|
|
5633
|
+
done4 += n;
|
|
5634
|
+
}
|
|
5635
|
+
this.length = Math.max(this.length, at + bytes.length);
|
|
5636
|
+
}
|
|
5637
|
+
truncate(size) {
|
|
5638
|
+
if (size < this.stored) {
|
|
5639
|
+
this.stored = size;
|
|
5640
|
+
this.shrink = Math.min(this.shrink ?? size, size);
|
|
5641
|
+
}
|
|
5642
|
+
if (size < this.length) {
|
|
5643
|
+
const edge = Math.floor(size / PAGE);
|
|
5644
|
+
for (const page of [...this.pages.keys()]) {
|
|
5645
|
+
if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
|
|
5646
|
+
}
|
|
5647
|
+
this.pages.get(edge)?.fill(0, size - edge * PAGE);
|
|
5648
|
+
}
|
|
5649
|
+
this.length = size;
|
|
5650
|
+
}
|
|
5651
|
+
plan() {
|
|
5652
|
+
const runs = [];
|
|
5653
|
+
const sorted = [...this.dirty].sort((a, b) => a - b);
|
|
5654
|
+
for (let i = 0; i < sorted.length; ) {
|
|
5655
|
+
let j = i + 1;
|
|
5656
|
+
const first = sorted[i];
|
|
5657
|
+
while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
|
|
5658
|
+
j++;
|
|
5659
|
+
}
|
|
5660
|
+
const start = first * PAGE;
|
|
5661
|
+
const end = Math.min(this.length, (first + (j - i)) * PAGE);
|
|
5662
|
+
const bytes = new Uint8Array(end - start);
|
|
5663
|
+
for (let k = i; k < j; k++) {
|
|
5664
|
+
const page = this.pages.get(sorted[k]);
|
|
5665
|
+
bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start)), (k - i) * PAGE);
|
|
5666
|
+
}
|
|
5667
|
+
runs.push({ offset: start, bytes });
|
|
5668
|
+
i = j;
|
|
5669
|
+
}
|
|
5670
|
+
const written = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
|
|
5671
|
+
return {
|
|
5672
|
+
...this.shrink !== void 0 ? { shrink: this.shrink } : {},
|
|
5673
|
+
runs,
|
|
5674
|
+
...this.length > written ? { extend: this.length } : {}
|
|
5675
|
+
};
|
|
5676
|
+
}
|
|
5677
|
+
flushed() {
|
|
5678
|
+
this.dirty.clear();
|
|
5679
|
+
this.shrink = void 0;
|
|
5680
|
+
this.stored = this.length;
|
|
5681
|
+
this.evict();
|
|
5682
|
+
}
|
|
5683
|
+
pin() {
|
|
5684
|
+
this.pinned = true;
|
|
5685
|
+
}
|
|
5686
|
+
reset(size) {
|
|
5687
|
+
if (this.pinned) return;
|
|
5688
|
+
for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
|
|
5689
|
+
if (this.changed) return;
|
|
5690
|
+
this.length = size;
|
|
5691
|
+
this.stored = size;
|
|
5692
|
+
}
|
|
5693
|
+
evict() {
|
|
5694
|
+
if (this.pinned) return;
|
|
5695
|
+
let clean = (this.pages.size - this.dirty.size) * PAGE;
|
|
5696
|
+
for (const page of this.pages.keys()) {
|
|
5697
|
+
if (clean <= CLEAN_LIMIT) return;
|
|
5698
|
+
if (this.dirty.has(page)) continue;
|
|
5699
|
+
this.drop(page);
|
|
5700
|
+
clean -= PAGE;
|
|
5701
|
+
}
|
|
5702
|
+
}
|
|
5703
|
+
take(used) {
|
|
5704
|
+
const page = this.free.pop();
|
|
5705
|
+
if (!page) return new Uint8Array(PAGE);
|
|
5706
|
+
page.fill(0, used);
|
|
5707
|
+
return page;
|
|
5708
|
+
}
|
|
5709
|
+
drop(page) {
|
|
5710
|
+
const bytes = this.pages.get(page);
|
|
5711
|
+
this.pages.delete(page);
|
|
5712
|
+
this.dirty.delete(page);
|
|
5713
|
+
if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
|
|
5714
|
+
}
|
|
5715
|
+
};
|
|
5716
|
+
|
|
5717
|
+
// src/fs/ranged.ts
|
|
5718
|
+
var AsyncRangedFile = class {
|
|
5719
|
+
pages;
|
|
5720
|
+
io;
|
|
5721
|
+
pinned = false;
|
|
5722
|
+
path;
|
|
5723
|
+
constructor(io, path, size) {
|
|
5724
|
+
this.io = io;
|
|
5725
|
+
this.path = path;
|
|
5726
|
+
this.pages = new Pages(size);
|
|
5727
|
+
}
|
|
5728
|
+
size() {
|
|
5729
|
+
return this.pages.length;
|
|
5730
|
+
}
|
|
5731
|
+
async fetch(runs) {
|
|
5732
|
+
for (const [start, n] of runs) this.pages.fill(start, await this.io.pread(this.path, start, n));
|
|
5733
|
+
}
|
|
5734
|
+
async read(at, length2) {
|
|
5735
|
+
await this.fetch(this.pages.missing(at, length2));
|
|
5736
|
+
return this.pages.read(at, length2);
|
|
5737
|
+
}
|
|
5738
|
+
async write(at, bytes) {
|
|
5739
|
+
await this.fetch(this.pages.partial(at, bytes.length));
|
|
5740
|
+
this.pages.write(at, bytes);
|
|
5741
|
+
if (!this.pinned && this.pages.dirtyBytes >= WRITEBACK) await this.flush();
|
|
5742
|
+
}
|
|
5743
|
+
truncate(size) {
|
|
5744
|
+
this.pages.truncate(size);
|
|
5745
|
+
}
|
|
5746
|
+
async flush() {
|
|
5747
|
+
if (this.pinned || !this.pages.changed) return;
|
|
5748
|
+
const plan = this.pages.plan();
|
|
5749
|
+
if (plan.shrink !== void 0) await this.io.truncate(this.path, plan.shrink);
|
|
5750
|
+
for (const run2 of plan.runs) await this.io.pwrite(this.path, run2.offset, run2.bytes, true);
|
|
5751
|
+
if (plan.extend !== void 0) await this.io.truncate(this.path, plan.extend);
|
|
5752
|
+
this.pages.flushed();
|
|
5753
|
+
}
|
|
5754
|
+
async pin() {
|
|
5755
|
+
this.pinned = true;
|
|
5756
|
+
this.pages.pin();
|
|
5757
|
+
await this.fetch(this.pages.missing(0, this.pages.length));
|
|
5758
|
+
}
|
|
5759
|
+
};
|
|
5760
|
+
|
|
5399
5761
|
// src/kernel/vfs-file.ts
|
|
5400
5762
|
var O_ACCMODE = 3;
|
|
5401
5763
|
var O_WRONLY = 1;
|
|
@@ -5415,6 +5777,7 @@ function within3(path, root) {
|
|
|
5415
5777
|
}
|
|
5416
5778
|
var VfsNode = class {
|
|
5417
5779
|
data;
|
|
5780
|
+
ranged;
|
|
5418
5781
|
length = 0;
|
|
5419
5782
|
dirty = false;
|
|
5420
5783
|
queue = Promise.resolve();
|
|
@@ -5445,6 +5808,23 @@ var VfsNode = class {
|
|
|
5445
5808
|
clearTimeout(this.writeBack);
|
|
5446
5809
|
this.writeBack = void 0;
|
|
5447
5810
|
this.data = void 0;
|
|
5811
|
+
this.ranged = void 0;
|
|
5812
|
+
}
|
|
5813
|
+
async prepare() {
|
|
5814
|
+
if (this.revoked) throw new KernelError("EIO");
|
|
5815
|
+
if (this.ranged || this.data) return this.ranged;
|
|
5816
|
+
const io = rangedOps(this.fs);
|
|
5817
|
+
const st = io && !this.orphaned ? await this.fs.stat?.(this.path).catch(() => void 0) : void 0;
|
|
5818
|
+
if (io && st?.ranged) {
|
|
5819
|
+
this.ranged = new AsyncRangedFile(io, this.path, st.size ?? 0);
|
|
5820
|
+
return this.ranged;
|
|
5821
|
+
}
|
|
5822
|
+
await this.load();
|
|
5823
|
+
return void 0;
|
|
5824
|
+
}
|
|
5825
|
+
async keep() {
|
|
5826
|
+
const ranged2 = await this.prepare();
|
|
5827
|
+
await ranged2?.pin();
|
|
5448
5828
|
}
|
|
5449
5829
|
async load() {
|
|
5450
5830
|
if (this.revoked) throw new KernelError("EIO");
|
|
@@ -5461,22 +5841,26 @@ var VfsNode = class {
|
|
|
5461
5841
|
return this.data;
|
|
5462
5842
|
}
|
|
5463
5843
|
replace(contents2) {
|
|
5844
|
+
this.ranged = void 0;
|
|
5464
5845
|
this.data = new Uint8Array(contents2);
|
|
5465
5846
|
this.length = this.data.length;
|
|
5466
5847
|
this.missing = false;
|
|
5467
5848
|
this.markDirty();
|
|
5468
5849
|
}
|
|
5469
5850
|
async materialize() {
|
|
5470
|
-
await this.
|
|
5851
|
+
if (await this.prepare()) return;
|
|
5471
5852
|
if (!this.missing || this.orphaned) return;
|
|
5472
5853
|
this.missing = false;
|
|
5473
5854
|
await this.fs.writeFile(this.path, this.data?.slice(0, this.length) ?? new Uint8Array(0));
|
|
5855
|
+
if (!this.dirty) this.data = void 0;
|
|
5474
5856
|
}
|
|
5475
5857
|
async size() {
|
|
5476
|
-
await this.
|
|
5477
|
-
return this.length;
|
|
5858
|
+
const ranged2 = await this.prepare();
|
|
5859
|
+
return ranged2 ? ranged2.size() : this.length;
|
|
5478
5860
|
}
|
|
5479
5861
|
async pread(max, at) {
|
|
5862
|
+
const ranged2 = await this.prepare();
|
|
5863
|
+
if (ranged2) return ranged2.read(at, max);
|
|
5480
5864
|
const bytes = await this.load();
|
|
5481
5865
|
const n = Math.max(0, Math.min(max, this.length - at));
|
|
5482
5866
|
return bytes.slice(at, at + n);
|
|
@@ -5499,12 +5883,18 @@ var VfsNode = class {
|
|
|
5499
5883
|
}
|
|
5500
5884
|
renamedTo(path) {
|
|
5501
5885
|
this.path = path;
|
|
5886
|
+
if (this.ranged) this.ranged.path = path;
|
|
5502
5887
|
this.cap = void 0;
|
|
5503
5888
|
}
|
|
5504
5889
|
async pwrite(bytes, at) {
|
|
5505
5890
|
if (bytes.length === 0) return 0;
|
|
5506
5891
|
await this.fits(at + bytes.length);
|
|
5507
|
-
await this.
|
|
5892
|
+
const ranged2 = await this.prepare();
|
|
5893
|
+
if (ranged2) {
|
|
5894
|
+
await ranged2.write(at, bytes);
|
|
5895
|
+
this.markDirty();
|
|
5896
|
+
return bytes.length;
|
|
5897
|
+
}
|
|
5508
5898
|
const buf = this.ensure(at + bytes.length);
|
|
5509
5899
|
if (at > this.length) buf.fill(0, this.length, at);
|
|
5510
5900
|
buf.set(bytes, at);
|
|
@@ -5514,7 +5904,12 @@ var VfsNode = class {
|
|
|
5514
5904
|
}
|
|
5515
5905
|
async truncate(size) {
|
|
5516
5906
|
await this.fits(size);
|
|
5517
|
-
await this.
|
|
5907
|
+
const ranged2 = await this.prepare();
|
|
5908
|
+
if (ranged2) {
|
|
5909
|
+
ranged2.truncate(size);
|
|
5910
|
+
this.markDirty();
|
|
5911
|
+
return;
|
|
5912
|
+
}
|
|
5518
5913
|
const buf = this.ensure(size);
|
|
5519
5914
|
if (size > this.length) buf.fill(0, this.length, size);
|
|
5520
5915
|
this.length = size;
|
|
@@ -5539,11 +5934,12 @@ var VfsNode = class {
|
|
|
5539
5934
|
}
|
|
5540
5935
|
async flush() {
|
|
5541
5936
|
if (this.revoked && this.dirty) throw new KernelError("EIO");
|
|
5542
|
-
if (!this.dirty || !this.data || this.
|
|
5937
|
+
if (!this.dirty || this.orphaned || !(this.data || this.ranged)) return;
|
|
5543
5938
|
this.dirty = false;
|
|
5544
5939
|
const started = performance.now();
|
|
5545
5940
|
try {
|
|
5546
|
-
|
|
5941
|
+
if (this.ranged) await this.ranged.flush();
|
|
5942
|
+
else await this.fs.writeFile(this.path, this.data.slice(0, this.length));
|
|
5547
5943
|
} catch (err) {
|
|
5548
5944
|
this.dirty = true;
|
|
5549
5945
|
throw err;
|
|
@@ -5590,7 +5986,7 @@ var VfsNodes = class {
|
|
|
5590
5986
|
}
|
|
5591
5987
|
async unlinking(path) {
|
|
5592
5988
|
const node = this.byPath.get(path);
|
|
5593
|
-
if (node) await node.serial(async () => void await Promise.all([node.
|
|
5989
|
+
if (node) await node.serial(async () => void await Promise.all([node.keep(), node.limit()]));
|
|
5594
5990
|
}
|
|
5595
5991
|
revoke(prefix) {
|
|
5596
5992
|
for (const [path, node] of this.byPath) {
|
|
@@ -6739,7 +7135,8 @@ async function dispatch(h, req) {
|
|
|
6739
7135
|
case "setattr":
|
|
6740
7136
|
return h.setattr?.(req.path, {
|
|
6741
7137
|
...req.mode !== void 0 ? { mode: req.mode } : {},
|
|
6742
|
-
...req.mtime !== void 0 ? { mtime: req.mtime } : {}
|
|
7138
|
+
...req.mtime !== void 0 ? { mtime: req.mtime } : {},
|
|
7139
|
+
...req.size !== void 0 ? { size: req.size } : {}
|
|
6743
7140
|
});
|
|
6744
7141
|
case "statfs":
|
|
6745
7142
|
return h.statfs?.() ?? null;
|
|
@@ -6747,7 +7144,7 @@ async function dispatch(h, req) {
|
|
|
6747
7144
|
throw fsError2("ENOSYS", `unknown driver op ${req.op}`);
|
|
6748
7145
|
}
|
|
6749
7146
|
}
|
|
6750
|
-
function serveFilesystem(port, handlers, capabilities = {}) {
|
|
7147
|
+
function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
|
|
6751
7148
|
let open = false;
|
|
6752
7149
|
const reply = (message2, transfer = []) => port.postMessage(message2, transfer);
|
|
6753
7150
|
async function hello(message2) {
|
|
@@ -6783,7 +7180,8 @@ function serveFilesystem(port, handlers, capabilities = {}) {
|
|
|
6783
7180
|
dispatch(handlers, data).then(
|
|
6784
7181
|
(result) => {
|
|
6785
7182
|
if (result instanceof Uint8Array) {
|
|
6786
|
-
const
|
|
7183
|
+
const whole = result.byteOffset === 0 && result.byteLength === result.buffer.byteLength;
|
|
7184
|
+
const bytes = options.owned && whole ? result : result.slice();
|
|
6787
7185
|
reply({ id: data.id, result: bytes }, [bytes.buffer]);
|
|
6788
7186
|
} else reply({ id: data.id, result: result ?? null });
|
|
6789
7187
|
},
|
|
@@ -7090,28 +7488,34 @@ function wait(ms, signal) {
|
|
|
7090
7488
|
});
|
|
7091
7489
|
}
|
|
7092
7490
|
async function mountFstab(lines, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
7491
|
+
}, settled = () => {
|
|
7093
7492
|
}) {
|
|
7094
7493
|
return Promise.all(
|
|
7095
|
-
lines.map(async (
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
const entry = await mount(spec);
|
|
7100
|
-
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
7101
|
-
discard(entry);
|
|
7102
|
-
failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
|
|
7103
|
-
break;
|
|
7104
|
-
} catch (err) {
|
|
7105
|
-
failed = err;
|
|
7106
|
-
if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
|
|
7107
|
-
await wait(retries[attempt], signal);
|
|
7108
|
-
}
|
|
7109
|
-
}
|
|
7110
|
-
const code = codeOf3(failed);
|
|
7111
|
-
return { line, target: spec.target, code, error: String(failed?.message ?? code) };
|
|
7494
|
+
lines.map(async (each) => {
|
|
7495
|
+
const result = await mountLine(each, mount, signal, retries, discard);
|
|
7496
|
+
settled(each, result);
|
|
7497
|
+
return result;
|
|
7112
7498
|
})
|
|
7113
7499
|
);
|
|
7114
7500
|
}
|
|
7501
|
+
async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
|
|
7502
|
+
let failed = invalid;
|
|
7503
|
+
for (let attempt = 0; !invalid && attempt <= retries.length && !signal.aborted; attempt++) {
|
|
7504
|
+
try {
|
|
7505
|
+
const entry = await mount(spec);
|
|
7506
|
+
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
7507
|
+
discard(entry);
|
|
7508
|
+
failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
|
|
7509
|
+
break;
|
|
7510
|
+
} catch (err) {
|
|
7511
|
+
failed = err;
|
|
7512
|
+
if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
|
|
7513
|
+
await wait(retries[attempt], signal);
|
|
7514
|
+
}
|
|
7515
|
+
}
|
|
7516
|
+
const code = codeOf3(failed);
|
|
7517
|
+
return { line, target: spec.target, code, error: String(failed?.message ?? code) };
|
|
7518
|
+
}
|
|
7115
7519
|
|
|
7116
7520
|
// src/mount/hostfs.ts
|
|
7117
7521
|
var HOSTFS_IO = 16 * 1024 * 1024;
|
|
@@ -7458,6 +7862,58 @@ function memoryMedia() {
|
|
|
7458
7862
|
};
|
|
7459
7863
|
}
|
|
7460
7864
|
|
|
7865
|
+
// src/mount/chunked.ts
|
|
7866
|
+
var CHUNK2 = 1024 * 1024;
|
|
7867
|
+
var ChunkedBytes = class {
|
|
7868
|
+
chunks = [];
|
|
7869
|
+
size = 0;
|
|
7870
|
+
read(offset, length2) {
|
|
7871
|
+
const end = Math.min(this.size, offset + length2);
|
|
7872
|
+
if (end <= offset) return new Uint8Array(0);
|
|
7873
|
+
const out = new Uint8Array(end - offset);
|
|
7874
|
+
for (let at = offset; at < end; ) {
|
|
7875
|
+
const index = Math.floor(at / CHUNK2);
|
|
7876
|
+
const from = at - index * CHUNK2;
|
|
7877
|
+
const n = Math.min(CHUNK2 - from, end - at);
|
|
7878
|
+
const chunk2 = this.chunks[index];
|
|
7879
|
+
if (chunk2) out.set(chunk2.subarray(from, Math.min(from + n, chunk2.length)), at - offset);
|
|
7880
|
+
at += n;
|
|
7881
|
+
}
|
|
7882
|
+
return out;
|
|
7883
|
+
}
|
|
7884
|
+
write(offset, bytes, owned = false) {
|
|
7885
|
+
const whole = owned && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
7886
|
+
for (let done4 = 0; done4 < bytes.length; ) {
|
|
7887
|
+
const at = offset + done4;
|
|
7888
|
+
const index = Math.floor(at / CHUNK2);
|
|
7889
|
+
const from = at - index * CHUNK2;
|
|
7890
|
+
const n = Math.min(CHUNK2 - from, bytes.length - done4);
|
|
7891
|
+
if (whole && n === CHUNK2 && bytes.length === CHUNK2) this.chunks[index] = bytes;
|
|
7892
|
+
else this.room(index, from + n).set(bytes.subarray(done4, done4 + n), from);
|
|
7893
|
+
done4 += n;
|
|
7894
|
+
}
|
|
7895
|
+
this.size = Math.max(this.size, offset + bytes.length);
|
|
7896
|
+
}
|
|
7897
|
+
truncate(size) {
|
|
7898
|
+
if (size < this.size) {
|
|
7899
|
+
const keep = Math.ceil(size / CHUNK2);
|
|
7900
|
+
this.chunks.length = Math.min(this.chunks.length, keep);
|
|
7901
|
+
const tail = this.chunks[keep - 1];
|
|
7902
|
+
const from = size - (keep - 1) * CHUNK2;
|
|
7903
|
+
if (tail && from < tail.length) tail.fill(0, from);
|
|
7904
|
+
}
|
|
7905
|
+
this.size = size;
|
|
7906
|
+
}
|
|
7907
|
+
room(index, need) {
|
|
7908
|
+
const chunk2 = this.chunks[index];
|
|
7909
|
+
if (chunk2 && chunk2.length >= need) return chunk2;
|
|
7910
|
+
const grown = new Uint8Array(Math.min(CHUNK2, Math.max(need, (chunk2?.length ?? 0) * 2)));
|
|
7911
|
+
if (chunk2) grown.set(chunk2);
|
|
7912
|
+
this.chunks[index] = grown;
|
|
7913
|
+
return grown;
|
|
7914
|
+
}
|
|
7915
|
+
};
|
|
7916
|
+
|
|
7461
7917
|
// src/mount/tmpfs.ts
|
|
7462
7918
|
function tmpfs() {
|
|
7463
7919
|
let inodes = 0;
|
|
@@ -7466,7 +7922,7 @@ function tmpfs() {
|
|
|
7466
7922
|
ino: ++inodes,
|
|
7467
7923
|
mode,
|
|
7468
7924
|
mtime: Date.now(),
|
|
7469
|
-
data: new
|
|
7925
|
+
data: new ChunkedBytes(),
|
|
7470
7926
|
target: "",
|
|
7471
7927
|
children: /* @__PURE__ */ new Map()
|
|
7472
7928
|
});
|
|
@@ -7494,7 +7950,7 @@ function tmpfs() {
|
|
|
7494
7950
|
}
|
|
7495
7951
|
const attr = (node) => ({
|
|
7496
7952
|
kind: node.kind,
|
|
7497
|
-
size: node.kind === "file" ? node.data.
|
|
7953
|
+
size: node.kind === "file" ? node.data.size : node.kind === "symlink" ? node.target.length : 4096,
|
|
7498
7954
|
mtime: node.mtime,
|
|
7499
7955
|
mode: node.mode,
|
|
7500
7956
|
ino: node.ino
|
|
@@ -7529,32 +7985,25 @@ function tmpfs() {
|
|
|
7529
7985
|
},
|
|
7530
7986
|
async open(path, flags) {
|
|
7531
7987
|
const node = opened2(path, flags);
|
|
7532
|
-
|
|
7533
|
-
|
|
7988
|
+
if (flags.truncate && node.kind === "file") {
|
|
7989
|
+
node.data.truncate(0);
|
|
7990
|
+
node.mtime = Date.now();
|
|
7991
|
+
}
|
|
7992
|
+
handles.set(++nextFh, { node, write: flags.write });
|
|
7534
7993
|
return nextFh;
|
|
7535
7994
|
},
|
|
7536
7995
|
async read(fh, offset, size) {
|
|
7537
|
-
|
|
7538
|
-
return h.data.slice(offset, Math.min(h.length, offset + size));
|
|
7996
|
+
return handle(fh).node.data.read(offset, size);
|
|
7539
7997
|
},
|
|
7540
7998
|
async write(fh, offset, bytes) {
|
|
7541
7999
|
const h = handle(fh);
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
grown.set(h.data.subarray(0, h.length));
|
|
7546
|
-
h.data = grown;
|
|
7547
|
-
}
|
|
7548
|
-
h.data.set(bytes, offset);
|
|
7549
|
-
h.length = Math.max(h.length, end);
|
|
7550
|
-
h.dirty = true;
|
|
8000
|
+
if (!h.write) throw fsError2("EBADF", `handle ${fh} is read-only`);
|
|
8001
|
+
h.node.data.write(offset, bytes, true);
|
|
8002
|
+
h.node.mtime = Date.now();
|
|
7551
8003
|
},
|
|
7552
8004
|
async release(fh) {
|
|
7553
|
-
|
|
8005
|
+
handle(fh);
|
|
7554
8006
|
handles.delete(fh);
|
|
7555
|
-
if (!h.dirty) return;
|
|
7556
|
-
h.node.data = h.data.slice(0, h.length);
|
|
7557
|
-
h.node.mtime = Date.now();
|
|
7558
8007
|
},
|
|
7559
8008
|
async mkdir(path) {
|
|
7560
8009
|
const { dir, name: name2 } = parent(path);
|
|
@@ -7607,6 +8056,12 @@ function tmpfs() {
|
|
|
7607
8056
|
async setattr(path, change) {
|
|
7608
8057
|
const node = find(path);
|
|
7609
8058
|
if (change.mode !== void 0) node.mode = node.mode & ~4095 | change.mode & 4095;
|
|
8059
|
+
if (change.size !== void 0) {
|
|
8060
|
+
if (node.kind !== "file")
|
|
8061
|
+
throw fsError2(node.kind === "directory" ? "EISDIR" : "EINVAL", path);
|
|
8062
|
+
node.data.truncate(change.size);
|
|
8063
|
+
node.mtime = Date.now();
|
|
8064
|
+
}
|
|
7610
8065
|
if (change.mtime !== void 0) node.mtime = change.mtime;
|
|
7611
8066
|
},
|
|
7612
8067
|
async statfs() {
|
|
@@ -7872,6 +8327,7 @@ var Launcher = class {
|
|
|
7872
8327
|
onMountPending;
|
|
7873
8328
|
removable = /* @__PURE__ */ new Map();
|
|
7874
8329
|
hostfs;
|
|
8330
|
+
fstabRetries;
|
|
7875
8331
|
inserted = /* @__PURE__ */ new Map();
|
|
7876
8332
|
openFiles = /* @__PURE__ */ new Set();
|
|
7877
8333
|
held = /* @__PURE__ */ new Set();
|
|
@@ -7893,6 +8349,7 @@ var Launcher = class {
|
|
|
7893
8349
|
this.media = options.media ?? memoryMedia();
|
|
7894
8350
|
this.onMountPending = options.onMountPending;
|
|
7895
8351
|
this.hostfs = options;
|
|
8352
|
+
this.fstabRetries = options.fstabRetries ?? FSTAB_RETRIES;
|
|
7896
8353
|
this.processMounts = options.processMounts ?? true;
|
|
7897
8354
|
this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
|
|
7898
8355
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
@@ -8197,7 +8654,12 @@ var Launcher = class {
|
|
|
8197
8654
|
}
|
|
8198
8655
|
if (type === "tmpfs") {
|
|
8199
8656
|
const { port1, port2 } = new MessageChannel();
|
|
8200
|
-
serveFilesystem(
|
|
8657
|
+
serveFilesystem(
|
|
8658
|
+
port2,
|
|
8659
|
+
tmpfs(),
|
|
8660
|
+
{ symlinks: true, chmod: true, ranges: true, attrTtl: 0 },
|
|
8661
|
+
{ owned: true }
|
|
8662
|
+
);
|
|
8201
8663
|
return {
|
|
8202
8664
|
port: port1,
|
|
8203
8665
|
dispose: () => {
|
|
@@ -8321,12 +8783,22 @@ var Launcher = class {
|
|
|
8321
8783
|
}
|
|
8322
8784
|
async mountFstab() {
|
|
8323
8785
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
8786
|
+
const lines = parseFstab(text);
|
|
8787
|
+
for (const { spec } of lines) this.mounts.note(spec, "pending");
|
|
8324
8788
|
return mountFstab(
|
|
8325
|
-
|
|
8326
|
-
(spec) => this.mount(spec)
|
|
8789
|
+
lines,
|
|
8790
|
+
(spec) => this.mount(spec).catch((err) => {
|
|
8791
|
+
this.mounts.note(spec, "pending", err?.message ?? String(err));
|
|
8792
|
+
throw err;
|
|
8793
|
+
}),
|
|
8327
8794
|
this.booting.signal,
|
|
8328
|
-
|
|
8329
|
-
(entry) => this.umount(entry.target, true)
|
|
8795
|
+
this.fstabRetries,
|
|
8796
|
+
(entry) => this.umount(entry.target, true),
|
|
8797
|
+
({ spec }, result) => {
|
|
8798
|
+
if (!result.code || result.code === "ECANCELED" || this.booting.signal.aborted) {
|
|
8799
|
+
this.mounts.unnote(spec.target);
|
|
8800
|
+
} else this.mounts.note(spec, "failed", result.error);
|
|
8801
|
+
}
|
|
8330
8802
|
);
|
|
8331
8803
|
}
|
|
8332
8804
|
environment(cwd, extra) {
|
|
@@ -9297,7 +9769,8 @@ async function createNodeKernel(options = {}) {
|
|
|
9297
9769
|
...options.modules ? { modules: options.modules } : {},
|
|
9298
9770
|
...options.env ? { env: options.env } : {},
|
|
9299
9771
|
...transport ? { transport } : {},
|
|
9300
|
-
...options.processMounts !== void 0 ? { processMounts: options.processMounts } : {}
|
|
9772
|
+
...options.processMounts !== void 0 ? { processMounts: options.processMounts } : {},
|
|
9773
|
+
...options.fstabRetries ? { fstabRetries: options.fstabRetries } : {}
|
|
9301
9774
|
});
|
|
9302
9775
|
await launcher.prepare();
|
|
9303
9776
|
const guard = (work) => new Promise((resolve, reject) => {
|