@ai-ecoverse/slicc-kernel 1.54.2 → 1.54.3
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 +1 -1
- package/dist/kernel-worker.js +759 -358
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +176 -46
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +861 -460
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +176 -46
- package/dist/process-worker.js.map +3 -3
- package/package.json +3 -3
package/dist/kernel-worker.js
CHANGED
|
@@ -1188,6 +1188,32 @@ function bytesSource(data) {
|
|
|
1188
1188
|
}
|
|
1189
1189
|
});
|
|
1190
1190
|
}
|
|
1191
|
+
function memoryFile(data, path, position = 0) {
|
|
1192
|
+
let offset = position;
|
|
1193
|
+
return new OpenFile({
|
|
1194
|
+
read: async (max) => {
|
|
1195
|
+
const out = data.subarray(offset, offset + max);
|
|
1196
|
+
offset += out.length;
|
|
1197
|
+
return out;
|
|
1198
|
+
},
|
|
1199
|
+
pread: async (max, at) => data.subarray(at, at + max),
|
|
1200
|
+
seek: async (by, whence) => {
|
|
1201
|
+
const at = (whence === 1 ? offset : whence === 2 ? data.length : 0) + by;
|
|
1202
|
+
if (at < 0) throw new KernelError("EINVAL");
|
|
1203
|
+
offset = at;
|
|
1204
|
+
return at;
|
|
1205
|
+
},
|
|
1206
|
+
pwrite: async () => {
|
|
1207
|
+
throw new KernelError("EBADF");
|
|
1208
|
+
},
|
|
1209
|
+
resize: async () => {
|
|
1210
|
+
throw new KernelError("EBADF");
|
|
1211
|
+
},
|
|
1212
|
+
stat: async () => ({ path, size: data.length }),
|
|
1213
|
+
close: () => {
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1191
1217
|
function sinkFile(onData) {
|
|
1192
1218
|
return new OpenFile({
|
|
1193
1219
|
write: async (bytes) => {
|
|
@@ -7459,149 +7485,725 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
7459
7485
|
};
|
|
7460
7486
|
}
|
|
7461
7487
|
|
|
7462
|
-
// src/
|
|
7463
|
-
var
|
|
7464
|
-
|
|
7465
|
-
"
|
|
7466
|
-
"
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
var json3 = (value) => ({ ok: true, kind: "json", json: value });
|
|
7475
|
-
var done2 = { ok: true, kind: "void" };
|
|
7476
|
-
var LockTable = class {
|
|
7477
|
-
held = /* @__PURE__ */ new Map();
|
|
7478
|
-
lock(pid, path, exclusive, fd) {
|
|
7479
|
-
const holders = this.held.get(path) ?? /* @__PURE__ */ new Map();
|
|
7480
|
-
for (const [other, h] of holders) if (other !== pid && (exclusive || h.exclusive)) return false;
|
|
7481
|
-
const mine = holders.get(pid) ?? { exclusive, fds: /* @__PURE__ */ new Set() };
|
|
7482
|
-
mine.exclusive = exclusive;
|
|
7483
|
-
mine.fds.add(fd);
|
|
7484
|
-
holders.set(pid, mine);
|
|
7485
|
-
this.held.set(path, holders);
|
|
7486
|
-
return true;
|
|
7488
|
+
// src/process/process-fds.ts
|
|
7489
|
+
var field2 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
|
|
7490
|
+
function mountTable(mounts = []) {
|
|
7491
|
+
const root = globalThis.navigator?.storage ? "opfs" : "memory";
|
|
7492
|
+
const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
|
|
7493
|
+
for (const m of mounts) {
|
|
7494
|
+
const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
|
|
7495
|
+
if (m.state !== "ok") options.push(m.state);
|
|
7496
|
+
const access = options.includes("ro") ? [] : ["rw"];
|
|
7497
|
+
lines2.push(
|
|
7498
|
+
`${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access, ...options].join(",")} 0 0`
|
|
7499
|
+
);
|
|
7487
7500
|
}
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7501
|
+
return `${lines2.join("\n")}
|
|
7502
|
+
`;
|
|
7503
|
+
}
|
|
7504
|
+
|
|
7505
|
+
// src/process/procfs.ts
|
|
7506
|
+
var HZ = 100;
|
|
7507
|
+
var KIB = 1024;
|
|
7508
|
+
var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
|
|
7509
|
+
var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
|
|
7510
|
+
var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
|
|
7511
|
+
function commOf(info) {
|
|
7512
|
+
const name2 = info.argv[0] ?? "";
|
|
7513
|
+
return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
|
|
7514
|
+
}
|
|
7515
|
+
function ttyNumber(tty) {
|
|
7516
|
+
const pts = tty && /^\/dev\/pts\/(\d+)$/.exec(tty);
|
|
7517
|
+
if (pts) return 136 << 8 | Number(pts[1]);
|
|
7518
|
+
const vt = tty && /^\/dev\/tty(\d+)$/.exec(tty);
|
|
7519
|
+
return vt ? 4 << 8 | Number(vt[1]) : 0;
|
|
7520
|
+
}
|
|
7521
|
+
function pages(bytes) {
|
|
7522
|
+
return Math.ceil(bytes / 4096);
|
|
7523
|
+
}
|
|
7524
|
+
function ticks(ms) {
|
|
7525
|
+
return Math.max(0, Math.floor(ms * HZ / 1e3));
|
|
7526
|
+
}
|
|
7527
|
+
function pidStat(info, boot) {
|
|
7528
|
+
const fields = [
|
|
7529
|
+
info.pid,
|
|
7530
|
+
`(${commOf(info)})`,
|
|
7531
|
+
info.state,
|
|
7532
|
+
info.ppid,
|
|
7533
|
+
info.pgid,
|
|
7534
|
+
info.sid,
|
|
7535
|
+
ttyNumber(info.tty),
|
|
7536
|
+
info.tty ? info.pgid : -1,
|
|
7537
|
+
4194304,
|
|
7538
|
+
0,
|
|
7539
|
+
0,
|
|
7540
|
+
0,
|
|
7541
|
+
0,
|
|
7542
|
+
0,
|
|
7543
|
+
0,
|
|
7544
|
+
0,
|
|
7545
|
+
0,
|
|
7546
|
+
20,
|
|
7547
|
+
0,
|
|
7548
|
+
1,
|
|
7549
|
+
0,
|
|
7550
|
+
ticks(info.started - boot),
|
|
7551
|
+
info.memory,
|
|
7552
|
+
pages(info.memory),
|
|
7553
|
+
"18446744073709551615",
|
|
7554
|
+
...Array(12).fill(0),
|
|
7555
|
+
17,
|
|
7556
|
+
0,
|
|
7557
|
+
0,
|
|
7558
|
+
0,
|
|
7559
|
+
0,
|
|
7560
|
+
0,
|
|
7561
|
+
0,
|
|
7562
|
+
...Array(7).fill(0),
|
|
7563
|
+
0
|
|
7564
|
+
];
|
|
7565
|
+
return `${fields.join(" ")}
|
|
7566
|
+
`;
|
|
7567
|
+
}
|
|
7568
|
+
var sigMask = (mask = 0) => (mask >>> 1).toString(16).padStart(16, "0");
|
|
7569
|
+
function pidStatus(info) {
|
|
7570
|
+
const state = info.state === "Z" ? "Z (zombie)" : "S (sleeping)";
|
|
7571
|
+
const c = info.cred ?? ROOT;
|
|
7572
|
+
const ids = (r, e, s) => `${r} ${e} ${s} ${e}`;
|
|
7573
|
+
return [
|
|
7574
|
+
`Name: ${commOf(info)}`,
|
|
7575
|
+
`Umask: ${(info.umask ?? 18).toString(8).padStart(4, "0")}`,
|
|
7576
|
+
`State: ${state}`,
|
|
7577
|
+
`Tgid: ${info.pid}`,
|
|
7578
|
+
"Ngid: 0",
|
|
7579
|
+
`Pid: ${info.pid}`,
|
|
7580
|
+
`PPid: ${info.ppid}`,
|
|
7581
|
+
"TracerPid: 0",
|
|
7582
|
+
`Uid: ${ids(c.ruid, c.euid, c.suid)}`,
|
|
7583
|
+
`Gid: ${ids(c.rgid, c.egid, c.sgid)}`,
|
|
7584
|
+
"FDSize: 64",
|
|
7585
|
+
`VmSize: ${Math.ceil(info.memory / KIB)} kB`,
|
|
7586
|
+
`VmRSS: ${Math.ceil(info.memory / KIB)} kB`,
|
|
7587
|
+
`Groups: ${c.groups.join(" ")}`,
|
|
7588
|
+
`NSpid: ${info.pid}`,
|
|
7589
|
+
`NSpgid: ${info.pgid}`,
|
|
7590
|
+
`NSsid: ${info.sid}`,
|
|
7591
|
+
"Threads: 1",
|
|
7592
|
+
"SigQ: 0/0",
|
|
7593
|
+
"SigPnd: 0000000000000000",
|
|
7594
|
+
"ShdPnd: 0000000000000000",
|
|
7595
|
+
"SigBlk: 0000000000000000",
|
|
7596
|
+
`SigIgn: ${sigMask(info.sigIgn)}`,
|
|
7597
|
+
`SigCgt: ${sigMask(info.sigCgt)}`,
|
|
7598
|
+
""
|
|
7599
|
+
].join("\n");
|
|
7600
|
+
}
|
|
7601
|
+
function pidFile(name2, info, boot) {
|
|
7602
|
+
if (name2 === "cmdline") return info.state === "Z" ? "" : `${info.argv.join("\0")}\0`;
|
|
7603
|
+
if (name2 === "comm") return `${commOf(info)}
|
|
7604
|
+
`;
|
|
7605
|
+
if (name2 === "stat") return pidStat(info, boot);
|
|
7606
|
+
if (name2 === "statm") {
|
|
7607
|
+
const p = pages(info.memory);
|
|
7608
|
+
return `${p} ${p} 0 0 0 ${p} 0
|
|
7609
|
+
`;
|
|
7492
7610
|
}
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7611
|
+
return pidStatus(info);
|
|
7612
|
+
}
|
|
7613
|
+
function memoryInfo(source, used = 0) {
|
|
7614
|
+
const total = source.deviceMemory ? source.deviceMemory * KIB * KIB * KIB : FALLBACK_MEMORY;
|
|
7615
|
+
const free = Math.max(0, total - used - (source.usedHeap ?? 0));
|
|
7616
|
+
const kb = (bytes) => `${Math.floor(bytes / KIB)} kB`;
|
|
7617
|
+
return [
|
|
7618
|
+
["MemTotal", kb(total)],
|
|
7619
|
+
["MemFree", kb(free)],
|
|
7620
|
+
["MemAvailable", kb(free)],
|
|
7621
|
+
["Buffers", kb(0)],
|
|
7622
|
+
["Cached", kb(0)],
|
|
7623
|
+
["SwapCached", kb(0)],
|
|
7624
|
+
["Shmem", kb(0)],
|
|
7625
|
+
["SReclaimable", kb(0)],
|
|
7626
|
+
["SwapTotal", kb(0)],
|
|
7627
|
+
["SwapFree", kb(0)]
|
|
7628
|
+
].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
|
|
7629
|
+
}
|
|
7630
|
+
function systemFile(name2, listing, now, memory) {
|
|
7631
|
+
const up = Math.max(0, now - listing.boot) / 1e3;
|
|
7632
|
+
if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
|
|
7633
|
+
`;
|
|
7634
|
+
if (name2 === "meminfo") {
|
|
7635
|
+
return memoryInfo(
|
|
7636
|
+
memory,
|
|
7637
|
+
listing.processes.reduce((sum, p) => sum + p.memory, 0)
|
|
7638
|
+
);
|
|
7498
7639
|
}
|
|
7499
|
-
|
|
7500
|
-
|
|
7640
|
+
const live = listing.processes.filter((p) => p.state !== "Z");
|
|
7641
|
+
if (name2 === "loadavg") {
|
|
7642
|
+
const last = Math.max(0, ...listing.processes.map((p) => p.pid));
|
|
7643
|
+
return `0.00 0.00 0.00 1/${live.length} ${last}
|
|
7644
|
+
`;
|
|
7501
7645
|
}
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7646
|
+
const idle = ticks(now - listing.boot);
|
|
7647
|
+
return [
|
|
7648
|
+
`cpu 0 0 0 ${idle} 0 0 0 0 0 0`,
|
|
7649
|
+
`cpu0 0 0 0 ${idle} 0 0 0 0 0 0`,
|
|
7650
|
+
"intr 0",
|
|
7651
|
+
"ctxt 0",
|
|
7652
|
+
`btime ${Math.floor(listing.boot / 1e3)}`,
|
|
7653
|
+
`processes ${listing.processes.length}`,
|
|
7654
|
+
"procs_running 1",
|
|
7655
|
+
"procs_blocked 0",
|
|
7656
|
+
""
|
|
7657
|
+
].join("\n");
|
|
7658
|
+
}
|
|
7659
|
+
function memorySource(scope = globalThis) {
|
|
7660
|
+
const nav = scope.navigator;
|
|
7661
|
+
const perf = scope.performance;
|
|
7662
|
+
const deviceMemory = nav?.deviceMemory;
|
|
7663
|
+
const used = perf?.memory?.usedJSHeapSize;
|
|
7664
|
+
return {
|
|
7665
|
+
...typeof deviceMemory === "number" && deviceMemory > 0 ? { deviceMemory } : {},
|
|
7666
|
+
...typeof used === "number" ? { usedHeap: used } : {}
|
|
7667
|
+
};
|
|
7668
|
+
}
|
|
7669
|
+
|
|
7670
|
+
// src/fs/pages.ts
|
|
7671
|
+
var PAGE = 64 * 1024;
|
|
7672
|
+
var READAHEAD = 1024 * 1024;
|
|
7673
|
+
var CLEAN_LIMIT = 4 * 1024 * 1024;
|
|
7674
|
+
var WRITEBACK = 8 * 1024 * 1024;
|
|
7675
|
+
var RUN = 1024 * 1024;
|
|
7676
|
+
var Pages = class {
|
|
7677
|
+
length;
|
|
7678
|
+
stored;
|
|
7679
|
+
shrink;
|
|
7680
|
+
pages = /* @__PURE__ */ new Map();
|
|
7681
|
+
dirty = /* @__PURE__ */ new Set();
|
|
7682
|
+
free = [];
|
|
7683
|
+
pinned = false;
|
|
7684
|
+
constructor(size) {
|
|
7685
|
+
this.length = size;
|
|
7686
|
+
this.stored = size;
|
|
7515
7687
|
}
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
const running = new AbortController();
|
|
7519
|
-
this.running.set(id2, running);
|
|
7520
|
-
this.run(req, running.signal).then(
|
|
7521
|
-
(result) => this.complete(id2, result),
|
|
7522
|
-
(err) => this.complete(id2, { ok: false, errno: "EIO", message: String(err) })
|
|
7523
|
-
);
|
|
7524
|
-
return id2;
|
|
7688
|
+
get dirtyBytes() {
|
|
7689
|
+
return this.dirty.size * PAGE;
|
|
7525
7690
|
}
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
this.complete(id2, json3(value));
|
|
7529
|
-
return id2;
|
|
7691
|
+
get changed() {
|
|
7692
|
+
return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
|
|
7530
7693
|
}
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
this.
|
|
7534
|
-
|
|
7694
|
+
missing(at, length2) {
|
|
7695
|
+
this.evict();
|
|
7696
|
+
const end = Math.min(this.stored, at + length2, this.length);
|
|
7697
|
+
const runs = [];
|
|
7698
|
+
for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
|
|
7699
|
+
if (this.pages.has(page)) continue;
|
|
7700
|
+
const last2 = runs[runs.length - 1];
|
|
7701
|
+
if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
|
|
7702
|
+
else runs.push([page * PAGE, PAGE]);
|
|
7703
|
+
}
|
|
7704
|
+
const last = runs[runs.length - 1];
|
|
7705
|
+
if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
|
|
7706
|
+
return runs.map(([start2, n]) => [start2, Math.min(n, this.stored - start2)]);
|
|
7535
7707
|
}
|
|
7536
|
-
|
|
7537
|
-
if (
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7708
|
+
partial(at, length2) {
|
|
7709
|
+
if (length2 === 0) return [];
|
|
7710
|
+
this.evict();
|
|
7711
|
+
const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
|
|
7712
|
+
const runs = [];
|
|
7713
|
+
for (const page of edges) {
|
|
7714
|
+
const start2 = page * PAGE;
|
|
7715
|
+
const covered = at <= start2 && at + length2 >= start2 + PAGE;
|
|
7716
|
+
if (covered || this.pages.has(page) || start2 >= this.stored) continue;
|
|
7717
|
+
runs.push([start2, Math.min(PAGE, this.stored - start2)]);
|
|
7718
|
+
}
|
|
7719
|
+
return runs;
|
|
7542
7720
|
}
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7721
|
+
fill(start2, bytes) {
|
|
7722
|
+
for (let at = 0; at < bytes.length; at += PAGE) {
|
|
7723
|
+
const page = (start2 + at) / PAGE;
|
|
7724
|
+
if (this.pages.has(page)) continue;
|
|
7725
|
+
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
7726
|
+
const fresh = this.take(part.length);
|
|
7727
|
+
fresh.set(part);
|
|
7728
|
+
this.pages.set(page, fresh);
|
|
7729
|
+
}
|
|
7546
7730
|
}
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
this.
|
|
7550
|
-
|
|
7551
|
-
if (at >= 0) this.ready.splice(at, 1);
|
|
7731
|
+
read(at, length2) {
|
|
7732
|
+
const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
|
|
7733
|
+
this.readInto(at, out);
|
|
7734
|
+
return out;
|
|
7552
7735
|
}
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
const
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
const
|
|
7560
|
-
if (
|
|
7736
|
+
readInto(at, out) {
|
|
7737
|
+
const end = Math.min(this.length, at + out.length);
|
|
7738
|
+
for (let pos = at; pos < end; ) {
|
|
7739
|
+
const page = Math.floor(pos / PAGE);
|
|
7740
|
+
const from = pos - page * PAGE;
|
|
7741
|
+
const n = Math.min(PAGE - from, end - pos);
|
|
7742
|
+
const bytes = this.pages.get(page);
|
|
7743
|
+
if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
|
|
7744
|
+
else out.fill(0, pos - at, pos - at + n);
|
|
7745
|
+
pos += n;
|
|
7561
7746
|
}
|
|
7747
|
+
return Math.max(0, end - at);
|
|
7562
7748
|
}
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
let
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
this.
|
|
7573
|
-
signal.removeEventListener("abort", abort);
|
|
7574
|
-
};
|
|
7575
|
-
const wake = () => {
|
|
7576
|
-
cleanup();
|
|
7577
|
-
resolve(true);
|
|
7578
|
-
};
|
|
7579
|
-
const abort = () => {
|
|
7580
|
-
cleanup();
|
|
7581
|
-
reject(new KernelError("EINTR"));
|
|
7582
|
-
};
|
|
7583
|
-
this.waiters.add(wake);
|
|
7584
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
7585
|
-
if (timeoutMs !== void 0) {
|
|
7586
|
-
timer = setTimeout(() => {
|
|
7587
|
-
cleanup();
|
|
7588
|
-
resolve(false);
|
|
7589
|
-
}, timeoutMs);
|
|
7749
|
+
write(at, bytes) {
|
|
7750
|
+
for (let done4 = 0; done4 < bytes.length; ) {
|
|
7751
|
+
const pos = at + done4;
|
|
7752
|
+
const page = Math.floor(pos / PAGE);
|
|
7753
|
+
const from = pos - page * PAGE;
|
|
7754
|
+
const n = Math.min(PAGE - from, bytes.length - done4);
|
|
7755
|
+
let target = this.pages.get(page);
|
|
7756
|
+
if (!target) {
|
|
7757
|
+
target = this.take(0);
|
|
7758
|
+
this.pages.set(page, target);
|
|
7590
7759
|
}
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
this.results.set(id2, result);
|
|
7597
|
-
this.ready.push(id2);
|
|
7598
|
-
this.wake();
|
|
7599
|
-
this.onReady?.();
|
|
7760
|
+
target.set(bytes.subarray(done4, done4 + n), from);
|
|
7761
|
+
this.dirty.add(page);
|
|
7762
|
+
done4 += n;
|
|
7763
|
+
}
|
|
7764
|
+
this.length = Math.max(this.length, at + bytes.length);
|
|
7600
7765
|
}
|
|
7601
|
-
|
|
7602
|
-
|
|
7766
|
+
truncate(size) {
|
|
7767
|
+
if (size < this.stored) {
|
|
7768
|
+
this.stored = size;
|
|
7769
|
+
this.shrink = Math.min(this.shrink ?? size, size);
|
|
7770
|
+
}
|
|
7771
|
+
if (size < this.length) {
|
|
7772
|
+
const edge = Math.floor(size / PAGE);
|
|
7773
|
+
for (const page of [...this.pages.keys()]) {
|
|
7774
|
+
if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
|
|
7775
|
+
}
|
|
7776
|
+
this.pages.get(edge)?.fill(0, size - edge * PAGE);
|
|
7777
|
+
}
|
|
7778
|
+
this.length = size;
|
|
7603
7779
|
}
|
|
7604
|
-
|
|
7780
|
+
plan() {
|
|
7781
|
+
const runs = [];
|
|
7782
|
+
const sorted = [...this.dirty].sort((a, b) => a - b);
|
|
7783
|
+
for (let i = 0; i < sorted.length; ) {
|
|
7784
|
+
let j = i + 1;
|
|
7785
|
+
const first = sorted[i];
|
|
7786
|
+
while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
|
|
7787
|
+
j++;
|
|
7788
|
+
}
|
|
7789
|
+
const start2 = first * PAGE;
|
|
7790
|
+
const end = Math.min(this.length, (first + (j - i)) * PAGE);
|
|
7791
|
+
const bytes = new Uint8Array(end - start2);
|
|
7792
|
+
for (let k = i; k < j; k++) {
|
|
7793
|
+
const page = this.pages.get(sorted[k]);
|
|
7794
|
+
bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start2)), (k - i) * PAGE);
|
|
7795
|
+
}
|
|
7796
|
+
runs.push({ offset: start2, bytes });
|
|
7797
|
+
i = j;
|
|
7798
|
+
}
|
|
7799
|
+
const written2 = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
|
|
7800
|
+
return {
|
|
7801
|
+
...this.shrink !== void 0 ? { shrink: this.shrink } : {},
|
|
7802
|
+
runs,
|
|
7803
|
+
...this.length > written2 ? { extend: this.length } : {}
|
|
7804
|
+
};
|
|
7805
|
+
}
|
|
7806
|
+
flushed() {
|
|
7807
|
+
this.dirty.clear();
|
|
7808
|
+
this.shrink = void 0;
|
|
7809
|
+
this.stored = this.length;
|
|
7810
|
+
this.evict();
|
|
7811
|
+
}
|
|
7812
|
+
pin() {
|
|
7813
|
+
this.pinned = true;
|
|
7814
|
+
}
|
|
7815
|
+
reset(size) {
|
|
7816
|
+
if (this.pinned) return;
|
|
7817
|
+
for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
|
|
7818
|
+
if (this.changed) return;
|
|
7819
|
+
this.length = size;
|
|
7820
|
+
this.stored = size;
|
|
7821
|
+
}
|
|
7822
|
+
evict() {
|
|
7823
|
+
if (this.pinned) return;
|
|
7824
|
+
let clean = (this.pages.size - this.dirty.size) * PAGE;
|
|
7825
|
+
for (const page of this.pages.keys()) {
|
|
7826
|
+
if (clean <= CLEAN_LIMIT) return;
|
|
7827
|
+
if (this.dirty.has(page)) continue;
|
|
7828
|
+
this.drop(page);
|
|
7829
|
+
clean -= PAGE;
|
|
7830
|
+
}
|
|
7831
|
+
}
|
|
7832
|
+
take(used) {
|
|
7833
|
+
const page = this.free.pop();
|
|
7834
|
+
if (!page) return new Uint8Array(PAGE);
|
|
7835
|
+
page.fill(0, used);
|
|
7836
|
+
return page;
|
|
7837
|
+
}
|
|
7838
|
+
drop(page) {
|
|
7839
|
+
const bytes = this.pages.get(page);
|
|
7840
|
+
this.pages.delete(page);
|
|
7841
|
+
this.dirty.delete(page);
|
|
7842
|
+
if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
|
|
7843
|
+
}
|
|
7844
|
+
};
|
|
7845
|
+
|
|
7846
|
+
// src/fs/ranged.ts
|
|
7847
|
+
var AsyncRangedFile = class {
|
|
7848
|
+
pages;
|
|
7849
|
+
io;
|
|
7850
|
+
pinned = false;
|
|
7851
|
+
version;
|
|
7852
|
+
path;
|
|
7853
|
+
constructor(io, path, size, version) {
|
|
7854
|
+
this.io = io;
|
|
7855
|
+
this.path = path;
|
|
7856
|
+
this.version = version;
|
|
7857
|
+
this.pages = new Pages(size);
|
|
7858
|
+
}
|
|
7859
|
+
size() {
|
|
7860
|
+
return this.pages.length;
|
|
7861
|
+
}
|
|
7862
|
+
async fetch(runs) {
|
|
7863
|
+
for (const [start2, n] of runs) {
|
|
7864
|
+
this.pages.fill(start2, await this.io.pread(this.path, start2, n, this.version));
|
|
7865
|
+
}
|
|
7866
|
+
}
|
|
7867
|
+
async read(at, length2) {
|
|
7868
|
+
await this.fetch(this.pages.missing(at, length2));
|
|
7869
|
+
return this.pages.read(at, length2);
|
|
7870
|
+
}
|
|
7871
|
+
async write(at, bytes) {
|
|
7872
|
+
await this.fetch(this.pages.partial(at, bytes.length));
|
|
7873
|
+
this.pages.write(at, bytes);
|
|
7874
|
+
if (!this.pinned && this.pages.dirtyBytes >= WRITEBACK) await this.flush();
|
|
7875
|
+
}
|
|
7876
|
+
truncate(size) {
|
|
7877
|
+
this.pages.truncate(size);
|
|
7878
|
+
}
|
|
7879
|
+
async flush() {
|
|
7880
|
+
if (this.pinned || !this.pages.changed) return;
|
|
7881
|
+
const plan = this.pages.plan();
|
|
7882
|
+
if (plan.shrink !== void 0) await this.io.truncate(this.path, plan.shrink);
|
|
7883
|
+
for (const run2 of plan.runs) await this.io.pwrite(this.path, run2.offset, run2.bytes, true);
|
|
7884
|
+
if (plan.extend !== void 0) await this.io.truncate(this.path, plan.extend);
|
|
7885
|
+
this.version = void 0;
|
|
7886
|
+
this.pages.flushed();
|
|
7887
|
+
}
|
|
7888
|
+
async pin() {
|
|
7889
|
+
this.pinned = true;
|
|
7890
|
+
this.pages.pin();
|
|
7891
|
+
await this.fetch(this.pages.missing(0, this.pages.length));
|
|
7892
|
+
}
|
|
7893
|
+
};
|
|
7894
|
+
|
|
7895
|
+
// src/process/wasi/wasi-files.ts
|
|
7896
|
+
function normalize(path) {
|
|
7897
|
+
const out = [];
|
|
7898
|
+
for (const seg of path.split("/")) {
|
|
7899
|
+
if (seg === "" || seg === ".") continue;
|
|
7900
|
+
if (seg === "..") out.pop();
|
|
7901
|
+
else out.push(seg);
|
|
7902
|
+
}
|
|
7903
|
+
return `/${out.join("/")}`;
|
|
7904
|
+
}
|
|
7905
|
+
|
|
7906
|
+
// src/process/wasi/wasi-procfs.ts
|
|
7907
|
+
var DIR_MODE = 16749;
|
|
7908
|
+
var FILE_MODE = 33060;
|
|
7909
|
+
var LINK_MODE = 41471;
|
|
7910
|
+
var PID_NAMES = PID_FILES;
|
|
7911
|
+
var SYSTEM_NAMES = SYSTEM_FILES;
|
|
7912
|
+
function inProc(path) {
|
|
7913
|
+
return path === "/proc" || path.startsWith("/proc/");
|
|
7914
|
+
}
|
|
7915
|
+
var ProcTree = class {
|
|
7916
|
+
source;
|
|
7917
|
+
pid;
|
|
7918
|
+
memory;
|
|
7919
|
+
constructor(source, pid, memory = memorySource()) {
|
|
7920
|
+
this.source = source;
|
|
7921
|
+
this.pid = pid;
|
|
7922
|
+
this.memory = memory;
|
|
7923
|
+
}
|
|
7924
|
+
listing() {
|
|
7925
|
+
return this.source.procList();
|
|
7926
|
+
}
|
|
7927
|
+
alive(pid) {
|
|
7928
|
+
return this.listing().processes.some((p) => p.pid === pid);
|
|
7929
|
+
}
|
|
7930
|
+
node(parts) {
|
|
7931
|
+
const [top, leaf, ...rest2] = parts;
|
|
7932
|
+
if (top === void 0) {
|
|
7933
|
+
return {
|
|
7934
|
+
kind: "dir",
|
|
7935
|
+
names: () => [
|
|
7936
|
+
...SYSTEM_NAMES,
|
|
7937
|
+
"mounts",
|
|
7938
|
+
"self",
|
|
7939
|
+
...this.listing().processes.map((p) => String(p.pid))
|
|
7940
|
+
]
|
|
7941
|
+
};
|
|
7942
|
+
}
|
|
7943
|
+
if (top === "self") {
|
|
7944
|
+
return leaf === void 0 ? { kind: "link", target: String(this.pid) } : void 0;
|
|
7945
|
+
}
|
|
7946
|
+
if (leaf === void 0 && top === "mounts") {
|
|
7947
|
+
return { kind: "file", content: () => mountTable(this.source.mountList()) };
|
|
7948
|
+
}
|
|
7949
|
+
if (leaf === void 0 && SYSTEM_NAMES.includes(top)) {
|
|
7950
|
+
return {
|
|
7951
|
+
kind: "file",
|
|
7952
|
+
content: () => systemFile(top, this.listing(), Date.now(), this.memory)
|
|
7953
|
+
};
|
|
7954
|
+
}
|
|
7955
|
+
if (!/^[1-9]\d*$/.test(top) || rest2.length > 0 || !this.alive(Number(top))) return void 0;
|
|
7956
|
+
if (leaf === void 0) return { kind: "dir", names: () => [...PID_NAMES] };
|
|
7957
|
+
if (!PID_NAMES.includes(leaf)) return void 0;
|
|
7958
|
+
return {
|
|
7959
|
+
kind: "file",
|
|
7960
|
+
content: () => {
|
|
7961
|
+
const listing = this.listing();
|
|
7962
|
+
const info = listing.processes.find((p) => p.pid === Number(top));
|
|
7963
|
+
if (!info) throw syncError("ENOENT", `/proc/${top}/${leaf}`);
|
|
7964
|
+
return pidFile(leaf, info, listing.boot);
|
|
7965
|
+
}
|
|
7966
|
+
};
|
|
7967
|
+
}
|
|
7968
|
+
parts(path, follow2) {
|
|
7969
|
+
const parts = path.split("/").filter((p) => p !== "").slice(1);
|
|
7970
|
+
if (parts[0] === "self" && (follow2 || parts.length > 1)) parts[0] = String(this.pid);
|
|
7971
|
+
return parts;
|
|
7972
|
+
}
|
|
7973
|
+
find(path, follow2) {
|
|
7974
|
+
const node = this.node(this.parts(path, follow2));
|
|
7975
|
+
if (!node) throw syncError("ENOENT", path);
|
|
7976
|
+
return node;
|
|
7977
|
+
}
|
|
7978
|
+
stat(path, follow2 = true) {
|
|
7979
|
+
const node = this.find(path, follow2);
|
|
7980
|
+
const base = {
|
|
7981
|
+
isFile: false,
|
|
7982
|
+
isDirectory: false,
|
|
7983
|
+
size: 0,
|
|
7984
|
+
mtimeMs: Date.now(),
|
|
7985
|
+
readonly: true
|
|
7986
|
+
};
|
|
7987
|
+
if (node.kind === "dir") return { ...base, isDirectory: true, mode: DIR_MODE };
|
|
7988
|
+
if (node.kind === "link") {
|
|
7989
|
+
return { ...base, isSymbolicLink: true, mode: LINK_MODE, size: node.target.length };
|
|
7990
|
+
}
|
|
7991
|
+
return { ...base, isFile: true, mode: FILE_MODE, size: this.bytes(node).length };
|
|
7992
|
+
}
|
|
7993
|
+
bytes(node) {
|
|
7994
|
+
return new TextEncoder().encode(node.kind === "file" ? node.content() : "");
|
|
7995
|
+
}
|
|
7996
|
+
read(path) {
|
|
7997
|
+
const node = this.find(path, true);
|
|
7998
|
+
if (node.kind !== "file") throw syncError("EISDIR", path);
|
|
7999
|
+
return this.bytes(node);
|
|
8000
|
+
}
|
|
8001
|
+
readdir(path) {
|
|
8002
|
+
const node = this.find(path, true);
|
|
8003
|
+
if (node.kind !== "dir") throw syncError("ENOTDIR", path);
|
|
8004
|
+
return node.names();
|
|
8005
|
+
}
|
|
8006
|
+
readlink(path) {
|
|
8007
|
+
const node = this.find(path, false);
|
|
8008
|
+
if (node.kind !== "link") throw syncError("EINVAL", path);
|
|
8009
|
+
return node.target;
|
|
8010
|
+
}
|
|
8011
|
+
exists(path) {
|
|
8012
|
+
try {
|
|
8013
|
+
this.find(path, true);
|
|
8014
|
+
return true;
|
|
8015
|
+
} catch {
|
|
8016
|
+
return false;
|
|
8017
|
+
}
|
|
8018
|
+
}
|
|
8019
|
+
};
|
|
8020
|
+
var MAX_HOPS2 = 40;
|
|
8021
|
+
function* procWalk(path, follow2) {
|
|
8022
|
+
let done4 = [];
|
|
8023
|
+
let todo = path.split("/").filter((part) => part !== "");
|
|
8024
|
+
let hops = 0;
|
|
8025
|
+
while (todo.length > 0) {
|
|
8026
|
+
const part = todo.shift();
|
|
8027
|
+
if (part === ".") continue;
|
|
8028
|
+
if (part === "..") {
|
|
8029
|
+
done4.pop();
|
|
8030
|
+
continue;
|
|
8031
|
+
}
|
|
8032
|
+
const at = `/${[...done4, part].join("/")}`;
|
|
8033
|
+
const whole = normalize(`/${[...done4, part, ...todo].join("/")}`);
|
|
8034
|
+
if (inProc(at) && inProc(whole)) return whole;
|
|
8035
|
+
if (!follow2 && todo.length === 0) return void 0;
|
|
8036
|
+
const target = yield at;
|
|
8037
|
+
if (target === null) return void 0;
|
|
8038
|
+
if (target === void 0) done4.push(part);
|
|
8039
|
+
else {
|
|
8040
|
+
if (++hops > MAX_HOPS2) return void 0;
|
|
8041
|
+
if (target.startsWith("/")) done4 = [];
|
|
8042
|
+
todo = [...target.split("/").filter((part2) => part2 !== ""), ...todo];
|
|
8043
|
+
}
|
|
8044
|
+
}
|
|
8045
|
+
return void 0;
|
|
8046
|
+
}
|
|
8047
|
+
async function procAlias(fs, path, follow2) {
|
|
8048
|
+
const { readlink, lstat } = fs;
|
|
8049
|
+
if (!readlink || !lstat) return void 0;
|
|
8050
|
+
const physical = await lstat.call(fs, "/proc").then(
|
|
8051
|
+
() => true,
|
|
8052
|
+
() => false
|
|
8053
|
+
);
|
|
8054
|
+
if (!physical) return void 0;
|
|
8055
|
+
const walk = procWalk(path, follow2);
|
|
8056
|
+
let step = walk.next();
|
|
8057
|
+
while (!step.done) {
|
|
8058
|
+
const target = await readlink.call(fs, step.value).catch(() => void 0);
|
|
8059
|
+
step = walk.next(target);
|
|
8060
|
+
}
|
|
8061
|
+
return step.value;
|
|
8062
|
+
}
|
|
8063
|
+
|
|
8064
|
+
// src/kernel/host-ops.ts
|
|
8065
|
+
var HOST_OPS = [
|
|
8066
|
+
"lock",
|
|
8067
|
+
"unlock",
|
|
8068
|
+
"async-submit",
|
|
8069
|
+
"async-wait",
|
|
8070
|
+
"async-take",
|
|
8071
|
+
"async-resolve",
|
|
8072
|
+
"async-hold",
|
|
8073
|
+
"async-cancel",
|
|
8074
|
+
"async-close"
|
|
8075
|
+
];
|
|
8076
|
+
var json3 = (value) => ({ ok: true, kind: "json", json: value });
|
|
8077
|
+
var done2 = { ok: true, kind: "void" };
|
|
8078
|
+
var LockTable = class {
|
|
8079
|
+
held = /* @__PURE__ */ new Map();
|
|
8080
|
+
lock(pid, path, exclusive, fd) {
|
|
8081
|
+
const holders = this.held.get(path) ?? /* @__PURE__ */ new Map();
|
|
8082
|
+
for (const [other, h] of holders) if (other !== pid && (exclusive || h.exclusive)) return false;
|
|
8083
|
+
const mine = holders.get(pid) ?? { exclusive, fds: /* @__PURE__ */ new Set() };
|
|
8084
|
+
mine.exclusive = exclusive;
|
|
8085
|
+
mine.fds.add(fd);
|
|
8086
|
+
holders.set(pid, mine);
|
|
8087
|
+
this.held.set(path, holders);
|
|
8088
|
+
return true;
|
|
8089
|
+
}
|
|
8090
|
+
unlock(pid, path) {
|
|
8091
|
+
const holders = this.held.get(path);
|
|
8092
|
+
holders?.delete(pid);
|
|
8093
|
+
if (holders?.size === 0) this.held.delete(path);
|
|
8094
|
+
}
|
|
8095
|
+
closed(pid, fd) {
|
|
8096
|
+
for (const [path, holders] of [...this.held]) {
|
|
8097
|
+
const mine = holders.get(pid);
|
|
8098
|
+
if (mine?.fds.delete(fd) && mine.fds.size === 0) this.unlock(pid, path);
|
|
8099
|
+
}
|
|
8100
|
+
}
|
|
8101
|
+
release(pid) {
|
|
8102
|
+
for (const path of [...this.held.keys()]) this.unlock(pid, path);
|
|
8103
|
+
}
|
|
8104
|
+
};
|
|
8105
|
+
var AsyncOps = class {
|
|
8106
|
+
next = 1;
|
|
8107
|
+
results = /* @__PURE__ */ new Map();
|
|
8108
|
+
ready = [];
|
|
8109
|
+
running = /* @__PURE__ */ new Map();
|
|
8110
|
+
waiters = /* @__PURE__ */ new Set();
|
|
8111
|
+
closed = false;
|
|
8112
|
+
run;
|
|
8113
|
+
onReady;
|
|
8114
|
+
constructor(run2, onReady) {
|
|
8115
|
+
this.run = run2;
|
|
8116
|
+
this.onReady = onReady;
|
|
8117
|
+
}
|
|
8118
|
+
submit(req) {
|
|
8119
|
+
const id2 = this.hold();
|
|
8120
|
+
const running = new AbortController();
|
|
8121
|
+
this.running.set(id2, running);
|
|
8122
|
+
this.run(req, running.signal).then(
|
|
8123
|
+
(result) => this.complete(id2, result),
|
|
8124
|
+
(err) => this.complete(id2, { ok: false, errno: "EIO", message: String(err) })
|
|
8125
|
+
);
|
|
8126
|
+
return id2;
|
|
8127
|
+
}
|
|
8128
|
+
resolve(value) {
|
|
8129
|
+
const id2 = this.hold();
|
|
8130
|
+
this.complete(id2, json3(value));
|
|
8131
|
+
return id2;
|
|
8132
|
+
}
|
|
8133
|
+
hold() {
|
|
8134
|
+
const id2 = this.next++;
|
|
8135
|
+
this.results.set(id2, void 0);
|
|
8136
|
+
return id2;
|
|
8137
|
+
}
|
|
8138
|
+
take(id2) {
|
|
8139
|
+
if (!this.results.has(id2)) throw new KernelError("EINVAL");
|
|
8140
|
+
const result = this.results.get(id2);
|
|
8141
|
+
if (!result) throw new KernelError("EAGAIN");
|
|
8142
|
+
this.forget(id2);
|
|
8143
|
+
return result;
|
|
8144
|
+
}
|
|
8145
|
+
cancel(id2) {
|
|
8146
|
+
this.running.get(id2)?.abort();
|
|
8147
|
+
this.forget(id2);
|
|
8148
|
+
}
|
|
8149
|
+
forget(id2) {
|
|
8150
|
+
this.running.delete(id2);
|
|
8151
|
+
this.results.delete(id2);
|
|
8152
|
+
const at = this.ready.indexOf(id2);
|
|
8153
|
+
if (at >= 0) this.ready.splice(at, 1);
|
|
8154
|
+
}
|
|
8155
|
+
async wait(timeoutMs, signal) {
|
|
8156
|
+
for (; ; ) {
|
|
8157
|
+
if (this.closed) return -1;
|
|
8158
|
+
const id2 = this.ready.shift();
|
|
8159
|
+
if (id2 !== void 0) return id2;
|
|
8160
|
+
if (timeoutMs !== void 0 && timeoutMs <= 0) return 0;
|
|
8161
|
+
const woke = await this.sleep(timeoutMs, signal);
|
|
8162
|
+
if (!woke) return 0;
|
|
8163
|
+
}
|
|
8164
|
+
}
|
|
8165
|
+
close() {
|
|
8166
|
+
this.closed = true;
|
|
8167
|
+
this.wake();
|
|
8168
|
+
}
|
|
8169
|
+
sleep(timeoutMs, signal) {
|
|
8170
|
+
return new Promise((resolve, reject) => {
|
|
8171
|
+
let timer;
|
|
8172
|
+
const cleanup = () => {
|
|
8173
|
+
clearTimeout(timer);
|
|
8174
|
+
this.waiters.delete(wake);
|
|
8175
|
+
signal.removeEventListener("abort", abort);
|
|
8176
|
+
};
|
|
8177
|
+
const wake = () => {
|
|
8178
|
+
cleanup();
|
|
8179
|
+
resolve(true);
|
|
8180
|
+
};
|
|
8181
|
+
const abort = () => {
|
|
8182
|
+
cleanup();
|
|
8183
|
+
reject(new KernelError("EINTR"));
|
|
8184
|
+
};
|
|
8185
|
+
this.waiters.add(wake);
|
|
8186
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
8187
|
+
if (timeoutMs !== void 0) {
|
|
8188
|
+
timer = setTimeout(() => {
|
|
8189
|
+
cleanup();
|
|
8190
|
+
resolve(false);
|
|
8191
|
+
}, timeoutMs);
|
|
8192
|
+
}
|
|
8193
|
+
});
|
|
8194
|
+
}
|
|
8195
|
+
complete(id2, result) {
|
|
8196
|
+
this.running.delete(id2);
|
|
8197
|
+
if (!this.results.has(id2)) return;
|
|
8198
|
+
this.results.set(id2, result);
|
|
8199
|
+
this.ready.push(id2);
|
|
8200
|
+
this.wake();
|
|
8201
|
+
this.onReady?.();
|
|
8202
|
+
}
|
|
8203
|
+
wake() {
|
|
8204
|
+
for (const wake of [...this.waiters]) wake();
|
|
8205
|
+
}
|
|
8206
|
+
};
|
|
7605
8207
|
async function hostSyscall(req, ctx) {
|
|
7606
8208
|
switch (req.op) {
|
|
7607
8209
|
case "lock":
|
|
@@ -8617,231 +9219,6 @@ async function accept(proc, fd, nonblock) {
|
|
|
8617
9219
|
return { fd: proc.fds.install(new OpenFile(socket), 3), peer: socket.peer };
|
|
8618
9220
|
}
|
|
8619
9221
|
|
|
8620
|
-
// src/fs/pages.ts
|
|
8621
|
-
var PAGE = 64 * 1024;
|
|
8622
|
-
var READAHEAD = 1024 * 1024;
|
|
8623
|
-
var CLEAN_LIMIT = 4 * 1024 * 1024;
|
|
8624
|
-
var WRITEBACK = 8 * 1024 * 1024;
|
|
8625
|
-
var RUN = 1024 * 1024;
|
|
8626
|
-
var Pages = class {
|
|
8627
|
-
length;
|
|
8628
|
-
stored;
|
|
8629
|
-
shrink;
|
|
8630
|
-
pages = /* @__PURE__ */ new Map();
|
|
8631
|
-
dirty = /* @__PURE__ */ new Set();
|
|
8632
|
-
free = [];
|
|
8633
|
-
pinned = false;
|
|
8634
|
-
constructor(size) {
|
|
8635
|
-
this.length = size;
|
|
8636
|
-
this.stored = size;
|
|
8637
|
-
}
|
|
8638
|
-
get dirtyBytes() {
|
|
8639
|
-
return this.dirty.size * PAGE;
|
|
8640
|
-
}
|
|
8641
|
-
get changed() {
|
|
8642
|
-
return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
|
|
8643
|
-
}
|
|
8644
|
-
missing(at, length2) {
|
|
8645
|
-
this.evict();
|
|
8646
|
-
const end = Math.min(this.stored, at + length2, this.length);
|
|
8647
|
-
const runs = [];
|
|
8648
|
-
for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
|
|
8649
|
-
if (this.pages.has(page)) continue;
|
|
8650
|
-
const last2 = runs[runs.length - 1];
|
|
8651
|
-
if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
|
|
8652
|
-
else runs.push([page * PAGE, PAGE]);
|
|
8653
|
-
}
|
|
8654
|
-
const last = runs[runs.length - 1];
|
|
8655
|
-
if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
|
|
8656
|
-
return runs.map(([start2, n]) => [start2, Math.min(n, this.stored - start2)]);
|
|
8657
|
-
}
|
|
8658
|
-
partial(at, length2) {
|
|
8659
|
-
if (length2 === 0) return [];
|
|
8660
|
-
this.evict();
|
|
8661
|
-
const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
|
|
8662
|
-
const runs = [];
|
|
8663
|
-
for (const page of edges) {
|
|
8664
|
-
const start2 = page * PAGE;
|
|
8665
|
-
const covered = at <= start2 && at + length2 >= start2 + PAGE;
|
|
8666
|
-
if (covered || this.pages.has(page) || start2 >= this.stored) continue;
|
|
8667
|
-
runs.push([start2, Math.min(PAGE, this.stored - start2)]);
|
|
8668
|
-
}
|
|
8669
|
-
return runs;
|
|
8670
|
-
}
|
|
8671
|
-
fill(start2, bytes) {
|
|
8672
|
-
for (let at = 0; at < bytes.length; at += PAGE) {
|
|
8673
|
-
const page = (start2 + at) / PAGE;
|
|
8674
|
-
if (this.pages.has(page)) continue;
|
|
8675
|
-
const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
|
|
8676
|
-
const fresh = this.take(part.length);
|
|
8677
|
-
fresh.set(part);
|
|
8678
|
-
this.pages.set(page, fresh);
|
|
8679
|
-
}
|
|
8680
|
-
}
|
|
8681
|
-
read(at, length2) {
|
|
8682
|
-
const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
|
|
8683
|
-
this.readInto(at, out);
|
|
8684
|
-
return out;
|
|
8685
|
-
}
|
|
8686
|
-
readInto(at, out) {
|
|
8687
|
-
const end = Math.min(this.length, at + out.length);
|
|
8688
|
-
for (let pos = at; pos < end; ) {
|
|
8689
|
-
const page = Math.floor(pos / PAGE);
|
|
8690
|
-
const from = pos - page * PAGE;
|
|
8691
|
-
const n = Math.min(PAGE - from, end - pos);
|
|
8692
|
-
const bytes = this.pages.get(page);
|
|
8693
|
-
if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
|
|
8694
|
-
else out.fill(0, pos - at, pos - at + n);
|
|
8695
|
-
pos += n;
|
|
8696
|
-
}
|
|
8697
|
-
return Math.max(0, end - at);
|
|
8698
|
-
}
|
|
8699
|
-
write(at, bytes) {
|
|
8700
|
-
for (let done4 = 0; done4 < bytes.length; ) {
|
|
8701
|
-
const pos = at + done4;
|
|
8702
|
-
const page = Math.floor(pos / PAGE);
|
|
8703
|
-
const from = pos - page * PAGE;
|
|
8704
|
-
const n = Math.min(PAGE - from, bytes.length - done4);
|
|
8705
|
-
let target = this.pages.get(page);
|
|
8706
|
-
if (!target) {
|
|
8707
|
-
target = this.take(0);
|
|
8708
|
-
this.pages.set(page, target);
|
|
8709
|
-
}
|
|
8710
|
-
target.set(bytes.subarray(done4, done4 + n), from);
|
|
8711
|
-
this.dirty.add(page);
|
|
8712
|
-
done4 += n;
|
|
8713
|
-
}
|
|
8714
|
-
this.length = Math.max(this.length, at + bytes.length);
|
|
8715
|
-
}
|
|
8716
|
-
truncate(size) {
|
|
8717
|
-
if (size < this.stored) {
|
|
8718
|
-
this.stored = size;
|
|
8719
|
-
this.shrink = Math.min(this.shrink ?? size, size);
|
|
8720
|
-
}
|
|
8721
|
-
if (size < this.length) {
|
|
8722
|
-
const edge = Math.floor(size / PAGE);
|
|
8723
|
-
for (const page of [...this.pages.keys()]) {
|
|
8724
|
-
if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
|
|
8725
|
-
}
|
|
8726
|
-
this.pages.get(edge)?.fill(0, size - edge * PAGE);
|
|
8727
|
-
}
|
|
8728
|
-
this.length = size;
|
|
8729
|
-
}
|
|
8730
|
-
plan() {
|
|
8731
|
-
const runs = [];
|
|
8732
|
-
const sorted = [...this.dirty].sort((a, b) => a - b);
|
|
8733
|
-
for (let i = 0; i < sorted.length; ) {
|
|
8734
|
-
let j = i + 1;
|
|
8735
|
-
const first = sorted[i];
|
|
8736
|
-
while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
|
|
8737
|
-
j++;
|
|
8738
|
-
}
|
|
8739
|
-
const start2 = first * PAGE;
|
|
8740
|
-
const end = Math.min(this.length, (first + (j - i)) * PAGE);
|
|
8741
|
-
const bytes = new Uint8Array(end - start2);
|
|
8742
|
-
for (let k = i; k < j; k++) {
|
|
8743
|
-
const page = this.pages.get(sorted[k]);
|
|
8744
|
-
bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start2)), (k - i) * PAGE);
|
|
8745
|
-
}
|
|
8746
|
-
runs.push({ offset: start2, bytes });
|
|
8747
|
-
i = j;
|
|
8748
|
-
}
|
|
8749
|
-
const written2 = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
|
|
8750
|
-
return {
|
|
8751
|
-
...this.shrink !== void 0 ? { shrink: this.shrink } : {},
|
|
8752
|
-
runs,
|
|
8753
|
-
...this.length > written2 ? { extend: this.length } : {}
|
|
8754
|
-
};
|
|
8755
|
-
}
|
|
8756
|
-
flushed() {
|
|
8757
|
-
this.dirty.clear();
|
|
8758
|
-
this.shrink = void 0;
|
|
8759
|
-
this.stored = this.length;
|
|
8760
|
-
this.evict();
|
|
8761
|
-
}
|
|
8762
|
-
pin() {
|
|
8763
|
-
this.pinned = true;
|
|
8764
|
-
}
|
|
8765
|
-
reset(size) {
|
|
8766
|
-
if (this.pinned) return;
|
|
8767
|
-
for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
|
|
8768
|
-
if (this.changed) return;
|
|
8769
|
-
this.length = size;
|
|
8770
|
-
this.stored = size;
|
|
8771
|
-
}
|
|
8772
|
-
evict() {
|
|
8773
|
-
if (this.pinned) return;
|
|
8774
|
-
let clean = (this.pages.size - this.dirty.size) * PAGE;
|
|
8775
|
-
for (const page of this.pages.keys()) {
|
|
8776
|
-
if (clean <= CLEAN_LIMIT) return;
|
|
8777
|
-
if (this.dirty.has(page)) continue;
|
|
8778
|
-
this.drop(page);
|
|
8779
|
-
clean -= PAGE;
|
|
8780
|
-
}
|
|
8781
|
-
}
|
|
8782
|
-
take(used) {
|
|
8783
|
-
const page = this.free.pop();
|
|
8784
|
-
if (!page) return new Uint8Array(PAGE);
|
|
8785
|
-
page.fill(0, used);
|
|
8786
|
-
return page;
|
|
8787
|
-
}
|
|
8788
|
-
drop(page) {
|
|
8789
|
-
const bytes = this.pages.get(page);
|
|
8790
|
-
this.pages.delete(page);
|
|
8791
|
-
this.dirty.delete(page);
|
|
8792
|
-
if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
|
|
8793
|
-
}
|
|
8794
|
-
};
|
|
8795
|
-
|
|
8796
|
-
// src/fs/ranged.ts
|
|
8797
|
-
var AsyncRangedFile = class {
|
|
8798
|
-
pages;
|
|
8799
|
-
io;
|
|
8800
|
-
pinned = false;
|
|
8801
|
-
version;
|
|
8802
|
-
path;
|
|
8803
|
-
constructor(io, path, size, version) {
|
|
8804
|
-
this.io = io;
|
|
8805
|
-
this.path = path;
|
|
8806
|
-
this.version = version;
|
|
8807
|
-
this.pages = new Pages(size);
|
|
8808
|
-
}
|
|
8809
|
-
size() {
|
|
8810
|
-
return this.pages.length;
|
|
8811
|
-
}
|
|
8812
|
-
async fetch(runs) {
|
|
8813
|
-
for (const [start2, n] of runs) {
|
|
8814
|
-
this.pages.fill(start2, await this.io.pread(this.path, start2, n, this.version));
|
|
8815
|
-
}
|
|
8816
|
-
}
|
|
8817
|
-
async read(at, length2) {
|
|
8818
|
-
await this.fetch(this.pages.missing(at, length2));
|
|
8819
|
-
return this.pages.read(at, length2);
|
|
8820
|
-
}
|
|
8821
|
-
async write(at, bytes) {
|
|
8822
|
-
await this.fetch(this.pages.partial(at, bytes.length));
|
|
8823
|
-
this.pages.write(at, bytes);
|
|
8824
|
-
if (!this.pinned && this.pages.dirtyBytes >= WRITEBACK) await this.flush();
|
|
8825
|
-
}
|
|
8826
|
-
truncate(size) {
|
|
8827
|
-
this.pages.truncate(size);
|
|
8828
|
-
}
|
|
8829
|
-
async flush() {
|
|
8830
|
-
if (this.pinned || !this.pages.changed) return;
|
|
8831
|
-
const plan = this.pages.plan();
|
|
8832
|
-
if (plan.shrink !== void 0) await this.io.truncate(this.path, plan.shrink);
|
|
8833
|
-
for (const run2 of plan.runs) await this.io.pwrite(this.path, run2.offset, run2.bytes, true);
|
|
8834
|
-
if (plan.extend !== void 0) await this.io.truncate(this.path, plan.extend);
|
|
8835
|
-
this.version = void 0;
|
|
8836
|
-
this.pages.flushed();
|
|
8837
|
-
}
|
|
8838
|
-
async pin() {
|
|
8839
|
-
this.pinned = true;
|
|
8840
|
-
this.pages.pin();
|
|
8841
|
-
await this.fetch(this.pages.missing(0, this.pages.length));
|
|
8842
|
-
}
|
|
8843
|
-
};
|
|
8844
|
-
|
|
8845
9222
|
// src/kernel/vfs-file.ts
|
|
8846
9223
|
var O_ACCMODE = 3;
|
|
8847
9224
|
var O_WRONLY = 1;
|
|
@@ -9481,6 +9858,7 @@ function isWasmSyscall(req) {
|
|
|
9481
9858
|
return typeof op === "string" && SYSCALL_OPS.has(op);
|
|
9482
9859
|
}
|
|
9483
9860
|
var MAX_READ = 1024 * 1024;
|
|
9861
|
+
var WRITE_ACCESS = 3;
|
|
9484
9862
|
var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
|
|
9485
9863
|
var DEFAULT_UMASK = 18;
|
|
9486
9864
|
var DYING = { ok: false, errno: "EINTR", message: "the process is being killed" };
|
|
@@ -9691,11 +10069,18 @@ var WasmProcess = class {
|
|
|
9691
10069
|
);
|
|
9692
10070
|
}
|
|
9693
10071
|
async openVfs(req) {
|
|
10072
|
+
if (inProc(req.path)) return this.openProc(req, req.path);
|
|
10073
|
+
const alias = req.orphan ? void 0 : await procAlias(this.options.fs, req.path, !req.exclusive);
|
|
10074
|
+
if (alias !== void 0) return this.openProc(req, alias);
|
|
9694
10075
|
await refuseReadonly(this.options.fs, req.path, req.flags, req);
|
|
9695
10076
|
const fd = this.fds.install(heldFile(), 3);
|
|
9696
10077
|
try {
|
|
9697
10078
|
const nodes = this.nodes;
|
|
9698
10079
|
const path = req.orphan ? req.path : req.exclusive ? await nodes.entryKey(req.path) : req.create ? await nodes.targetKey(req.path) : await this.keptOr(await nodes.targetKey(req.path), req.handle);
|
|
10080
|
+
if (inProc(path)) {
|
|
10081
|
+
this.fds.installAt(fd, await this.procFile(req, path));
|
|
10082
|
+
return fd;
|
|
10083
|
+
}
|
|
9699
10084
|
const open = { ...req, path };
|
|
9700
10085
|
if (req.exclusive) {
|
|
9701
10086
|
await nodes.createExclusive(path);
|
|
@@ -9717,6 +10102,22 @@ var WasmProcess = class {
|
|
|
9717
10102
|
}
|
|
9718
10103
|
return fd;
|
|
9719
10104
|
}
|
|
10105
|
+
async openProc(req, path) {
|
|
10106
|
+
return this.fds.install(await this.procFile(req, path), 3);
|
|
10107
|
+
}
|
|
10108
|
+
async procFile(req, path) {
|
|
10109
|
+
if (req.flags & WRITE_ACCESS || req.create || req.truncate || req.exclusive) {
|
|
10110
|
+
throw new KernelError("EROFS");
|
|
10111
|
+
}
|
|
10112
|
+
const tree = new ProcTree(
|
|
10113
|
+
{
|
|
10114
|
+
procList: () => this.options.processes?.() ?? { boot: 0, processes: [] },
|
|
10115
|
+
mountList: () => this.options.mounts?.() ?? []
|
|
10116
|
+
},
|
|
10117
|
+
(await this.options.identity?.())?.pid ?? this.pid
|
|
10118
|
+
);
|
|
10119
|
+
return memoryFile(tree.read(path), path, req.position);
|
|
10120
|
+
}
|
|
9720
10121
|
openVfsFile(req) {
|
|
9721
10122
|
return vfsFile(
|
|
9722
10123
|
this.options.fs,
|
|
@@ -11219,8 +11620,8 @@ function umountCall(target, flags) {
|
|
|
11219
11620
|
var FSTAB_PATH = "/etc/fstab";
|
|
11220
11621
|
var FSTAB_RETRIES = [1e3, 4e3, 16e3];
|
|
11221
11622
|
var FINAL = /* @__PURE__ */ new Set(["EINVAL", "EBUSY", "EPERM", "EACCES"]);
|
|
11222
|
-
function unescape(
|
|
11223
|
-
return
|
|
11623
|
+
function unescape(field3) {
|
|
11624
|
+
return field3.replace(
|
|
11224
11625
|
/\\([0-7]{3})/g,
|
|
11225
11626
|
(_, octal) => String.fromCharCode(Number.parseInt(octal, 8))
|
|
11226
11627
|
);
|
|
@@ -12040,7 +12441,7 @@ function importedMemory(bytes) {
|
|
|
12040
12441
|
const count = u32();
|
|
12041
12442
|
for (let i = 0; i < count; i++) {
|
|
12042
12443
|
const module = name2();
|
|
12043
|
-
const
|
|
12444
|
+
const field3 = name2();
|
|
12044
12445
|
const kind = bytes[at++];
|
|
12045
12446
|
if (kind === 0) u32();
|
|
12046
12447
|
else if (kind === 1) {
|
|
@@ -12048,7 +12449,7 @@ function importedMemory(bytes) {
|
|
|
12048
12449
|
limits();
|
|
12049
12450
|
} else if (kind === 2) {
|
|
12050
12451
|
const l = limits();
|
|
12051
|
-
return { module, name:
|
|
12452
|
+
return { module, name: field3, ...l };
|
|
12052
12453
|
} else if (kind === 3) at += 2;
|
|
12053
12454
|
else if (kind === 4) {
|
|
12054
12455
|
at++;
|
|
@@ -12139,12 +12540,12 @@ function readImports(r, types) {
|
|
|
12139
12540
|
const foreign = {};
|
|
12140
12541
|
for (let i = r.u32(); i > 0; i--) {
|
|
12141
12542
|
const module = r.name();
|
|
12142
|
-
const
|
|
12543
|
+
const field3 = r.name();
|
|
12143
12544
|
const kind = r.byte();
|
|
12144
12545
|
if (kind !== 0) skipImport(r, kind);
|
|
12145
12546
|
else {
|
|
12146
12547
|
const type = types[r.u32()] ?? "other";
|
|
12147
|
-
if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[
|
|
12548
|
+
if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[field3] = type;
|
|
12148
12549
|
}
|
|
12149
12550
|
}
|
|
12150
12551
|
return foreign;
|