@ai-ecoverse/slicc-kernel 1.124.12 → 1.125.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -11
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +548 -94
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +4496 -4350
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +553 -99
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +7137 -6991
- package/dist/process-worker.js.map +4 -4
- package/dist/types/fs/unlinked.d.ts +1 -0
- package/dist/types/fs/watch.d.ts +19 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/kernel/fd-table.d.ts +1 -0
- package/dist/types/kernel/host.d.ts +1 -0
- package/dist/types/kernel/net/tls-ciphers.d.ts +6 -0
- package/dist/types/kernel/net/tls-client.d.ts +13 -1
- package/dist/types/kernel/net/tls-engine.d.ts +10 -0
- package/dist/types/kernel/net/tls-uplink.d.ts +14 -1
- package/dist/types/kernel/net/uplink-socket.d.ts +1 -1
- package/dist/types/kernel/process.d.ts +4 -1
- package/dist/types/kernel/select.d.ts +1 -0
- package/dist/types/kernel/socket-syscalls.d.ts +7 -0
- package/dist/types/kernel/vfs-file.d.ts +2 -0
- package/dist/types/kernel/watch-ops.d.ts +30 -0
- package/dist/types/launcher.d.ts +1 -0
- package/dist/types/process/js/js-context.d.ts +2 -0
- package/dist/types/process/js/js-dev.d.ts +1 -0
- package/dist/types/process/js/js-net.d.ts +16 -0
- package/dist/types/process/js/js-sync.d.ts +5 -1
- package/dist/types/process/js/js-watch.d.ts +17 -0
- package/dist/types/process/process-fds.d.ts +1 -1
- package/dist/types/process/wasi/wasi-host.d.ts +1 -0
- package/package.json +4 -4
package/dist/kernel-worker.js
CHANGED
|
@@ -4028,7 +4028,12 @@ function openPipe(capacity) {
|
|
|
4028
4028
|
}
|
|
4029
4029
|
},
|
|
4030
4030
|
close: () => pipe.closeRead(),
|
|
4031
|
-
poll: () => ({
|
|
4031
|
+
poll: () => ({
|
|
4032
|
+
readable: pipe.readReady,
|
|
4033
|
+
writable: false,
|
|
4034
|
+
hangup: pipe.writersGone,
|
|
4035
|
+
nread: pipe.buffered
|
|
4036
|
+
}),
|
|
4032
4037
|
changed: (signal) => pipe.changed(signal),
|
|
4033
4038
|
identity: pipe
|
|
4034
4039
|
}),
|
|
@@ -4655,8 +4660,8 @@ function uplinkLink(conn, onReset) {
|
|
|
4655
4660
|
open = false;
|
|
4656
4661
|
conn.close();
|
|
4657
4662
|
};
|
|
4658
|
-
void inbound(conn, rx).catch(() => {
|
|
4659
|
-
onReset();
|
|
4663
|
+
void inbound(conn, rx).catch((err) => {
|
|
4664
|
+
onReset(err);
|
|
4660
4665
|
close();
|
|
4661
4666
|
tx.closeRead();
|
|
4662
4667
|
}).finally(() => rx.closeWrite());
|
|
@@ -4747,7 +4752,7 @@ var KernelSocket = class _KernelSocket {
|
|
|
4747
4752
|
state = "open";
|
|
4748
4753
|
dialing;
|
|
4749
4754
|
failed;
|
|
4750
|
-
reset
|
|
4755
|
+
reset;
|
|
4751
4756
|
local;
|
|
4752
4757
|
peer;
|
|
4753
4758
|
peerCred;
|
|
@@ -4786,19 +4791,20 @@ var KernelSocket = class _KernelSocket {
|
|
|
4786
4791
|
const { rx } = this.connection();
|
|
4787
4792
|
if (this.readShut || max <= 0) return new Uint8Array(0);
|
|
4788
4793
|
const bytes = await pipeCall(() => rx.read(max, signal));
|
|
4789
|
-
if (bytes.length === 0 && this.reset) throw
|
|
4794
|
+
if (bytes.length === 0 && this.reset) throw this.reset;
|
|
4790
4795
|
return bytes;
|
|
4791
4796
|
}
|
|
4792
4797
|
async peek(max, signal) {
|
|
4793
4798
|
const { rx } = this.connection();
|
|
4794
4799
|
if (this.readShut || max <= 0) return new Uint8Array(0);
|
|
4795
4800
|
const bytes = await pipeCall(() => rx.peek(max, signal));
|
|
4796
|
-
if (bytes.length === 0 && this.reset) throw
|
|
4801
|
+
if (bytes.length === 0 && this.reset) throw this.reset;
|
|
4797
4802
|
return bytes;
|
|
4798
4803
|
}
|
|
4799
4804
|
async write(bytes, signal) {
|
|
4800
4805
|
const { tx } = this.connection();
|
|
4801
4806
|
if (this.writeShut) throw new KernelError("EPIPE");
|
|
4807
|
+
if (this.reset?.code === "EPROTO") throw this.reset;
|
|
4802
4808
|
return pipeCall(() => tx.write(bytes, signal));
|
|
4803
4809
|
}
|
|
4804
4810
|
poll() {
|
|
@@ -4815,7 +4821,8 @@ var KernelSocket = class _KernelSocket {
|
|
|
4815
4821
|
readable: this.readShut || rx.readReady,
|
|
4816
4822
|
writable: this.writeShut || tx.writeReady,
|
|
4817
4823
|
hangup: rx.writersGone && tx.readersGone,
|
|
4818
|
-
rdhup: rx.writersGone
|
|
4824
|
+
rdhup: rx.writersGone,
|
|
4825
|
+
nread: this.readShut ? 0 : rx.buffered
|
|
4819
4826
|
};
|
|
4820
4827
|
}
|
|
4821
4828
|
changed(signal) {
|
|
@@ -4875,8 +4882,8 @@ var KernelSocket = class _KernelSocket {
|
|
|
4875
4882
|
if (this.state !== "open") throw new KernelError("EINVAL");
|
|
4876
4883
|
if (addr.family !== this.domain) throw new KernelError("EAFNOSUPPORT");
|
|
4877
4884
|
this.failed = void 0;
|
|
4878
|
-
const
|
|
4879
|
-
if (
|
|
4885
|
+
const route2 = viaUplink && addr.family === "inet" ? this.net.uplinkFor(addr.host) : void 0;
|
|
4886
|
+
if (route2) return this.dial(addr, route2);
|
|
4880
4887
|
const target = this.net.target(addr, name2);
|
|
4881
4888
|
if (!this.local) {
|
|
4882
4889
|
if (addr.family === "inet") {
|
|
@@ -4932,9 +4939,9 @@ var KernelSocket = class _KernelSocket {
|
|
|
4932
4939
|
}
|
|
4933
4940
|
}
|
|
4934
4941
|
switch;
|
|
4935
|
-
connectSecure(addr,
|
|
4942
|
+
connectSecure(addr, route2, secure2) {
|
|
4936
4943
|
if (this.state !== "open") throw new KernelError("EISCONN");
|
|
4937
|
-
return this.dial(addr,
|
|
4944
|
+
return this.dial(addr, route2, secure2);
|
|
4938
4945
|
}
|
|
4939
4946
|
upgradeSecure(make) {
|
|
4940
4947
|
if (this.state !== "connected") throw new KernelError("ENOTCONN");
|
|
@@ -4942,7 +4949,7 @@ var KernelSocket = class _KernelSocket {
|
|
|
4942
4949
|
if (this.switch.upgraded || this.writeShut || this.readShut) throw new KernelError("EINVAL");
|
|
4943
4950
|
return this.switch.upgrade(make);
|
|
4944
4951
|
}
|
|
4945
|
-
dial(addr,
|
|
4952
|
+
dial(addr, route2, secure2) {
|
|
4946
4953
|
this.state = "connecting";
|
|
4947
4954
|
const abort = new AbortController();
|
|
4948
4955
|
this.dialing = abort;
|
|
@@ -4950,13 +4957,13 @@ var KernelSocket = class _KernelSocket {
|
|
|
4950
4957
|
const timer = setTimeout(() => {
|
|
4951
4958
|
timedOut = true;
|
|
4952
4959
|
abort.abort(new KernelError("ETIMEDOUT"));
|
|
4953
|
-
},
|
|
4960
|
+
}, route2.timeoutMs ?? DIAL_TIMEOUT_MS);
|
|
4954
4961
|
const abandoned = new Promise((_, reject) => {
|
|
4955
4962
|
abort.signal.addEventListener("abort", () => reject(abort.signal.reason), { once: true });
|
|
4956
4963
|
});
|
|
4957
4964
|
const { host, port } = addr;
|
|
4958
4965
|
const dialled = new Promise(
|
|
4959
|
-
(resolve2) => resolve2(
|
|
4966
|
+
(resolve2) => resolve2(route2.uplink.dial({ network: "tcp", host, port, signal: abort.signal }))
|
|
4960
4967
|
);
|
|
4961
4968
|
dialled.then(
|
|
4962
4969
|
(conn) => abort.signal.aborted && conn.close(),
|
|
@@ -4983,8 +4990,8 @@ var KernelSocket = class _KernelSocket {
|
|
|
4983
4990
|
this.dialing = void 0;
|
|
4984
4991
|
this.switch = conn;
|
|
4985
4992
|
this.attach(
|
|
4986
|
-
uplinkLink(conn, () => {
|
|
4987
|
-
this.reset =
|
|
4993
|
+
uplinkLink(conn, (err) => {
|
|
4994
|
+
this.reset = err instanceof KernelError && err.code === "EPROTO" ? err : new KernelError("ECONNRESET");
|
|
4988
4995
|
this.changes.wake();
|
|
4989
4996
|
})
|
|
4990
4997
|
);
|
|
@@ -5105,14 +5112,14 @@ var LoopbackNet = class {
|
|
|
5105
5112
|
if (!this.origins) throw new KernelError("ENETUNREACH");
|
|
5106
5113
|
void this.origins(conn, origin).catch(() => conn.close());
|
|
5107
5114
|
}
|
|
5108
|
-
useUplink(
|
|
5109
|
-
this.route =
|
|
5115
|
+
useUplink(route2) {
|
|
5116
|
+
this.route = route2;
|
|
5110
5117
|
}
|
|
5111
5118
|
tlsClient;
|
|
5112
5119
|
uplinkFor(host) {
|
|
5113
|
-
const
|
|
5114
|
-
if (!
|
|
5115
|
-
return this.destination(host) === "uplink" ?
|
|
5120
|
+
const route2 = this.route;
|
|
5121
|
+
if (!route2) return void 0;
|
|
5122
|
+
return this.destination(host) === "uplink" ? route2 : void 0;
|
|
5116
5123
|
}
|
|
5117
5124
|
destination(host) {
|
|
5118
5125
|
return (this.route?.routes ?? LOCAL_ONLY).classify(host);
|
|
@@ -6625,7 +6632,7 @@ function satisfies(version, range2) {
|
|
|
6625
6632
|
}
|
|
6626
6633
|
|
|
6627
6634
|
// src/version.ts
|
|
6628
|
-
var KERNEL_VERSION = true ? "1.
|
|
6635
|
+
var KERNEL_VERSION = true ? "1.125.0" : "0.0.0-dev";
|
|
6629
6636
|
var OS_RELEASE = "6.0.0-slicc";
|
|
6630
6637
|
function procVersion() {
|
|
6631
6638
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -7682,16 +7689,24 @@ var UnlinkedKeeper = class {
|
|
|
7682
7689
|
gated();
|
|
7683
7690
|
const held = /* @__PURE__ */ new Map();
|
|
7684
7691
|
const hidden = /* @__PURE__ */ new Map();
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
const
|
|
7693
|
-
|
|
7692
|
+
const moved2 = [];
|
|
7693
|
+
try {
|
|
7694
|
+
for (; ; ) {
|
|
7695
|
+
await take();
|
|
7696
|
+
const now = this.heldWithin(target);
|
|
7697
|
+
const next3 = paths.filter((path) => now.has(path) && !held.has(path));
|
|
7698
|
+
if (next3.length === 0) break;
|
|
7699
|
+
for (const path of next3) {
|
|
7700
|
+
const holders = now.get(path);
|
|
7701
|
+
held.set(path, holders);
|
|
7702
|
+
for (const holder of holders) moved2.push(holder.moving?.(path) ?? (() => void 0));
|
|
7703
|
+
const at = await this.hide(path);
|
|
7704
|
+
if (at !== void 0) hidden.set(path, at);
|
|
7705
|
+
}
|
|
7694
7706
|
}
|
|
7707
|
+
} catch (err) {
|
|
7708
|
+
for (const settle3 of moved2) settle3();
|
|
7709
|
+
throw err;
|
|
7695
7710
|
}
|
|
7696
7711
|
sealed();
|
|
7697
7712
|
const done4 = () => {
|
|
@@ -7704,6 +7719,7 @@ var UnlinkedKeeper = class {
|
|
|
7704
7719
|
files.set(path, file);
|
|
7705
7720
|
for (const holder of holders) holder.keep(path, file);
|
|
7706
7721
|
}
|
|
7722
|
+
for (const settle3 of moved2) settle3();
|
|
7707
7723
|
return {
|
|
7708
7724
|
moved: new Set(hidden.keys()),
|
|
7709
7725
|
commit: () => {
|
|
@@ -9409,6 +9425,8 @@ async function physicalPathAsync(fs, path) {
|
|
|
9409
9425
|
}
|
|
9410
9426
|
|
|
9411
9427
|
// src/fs/watch.ts
|
|
9428
|
+
var WATCH_NOTES_LIMIT = 1e3;
|
|
9429
|
+
var baseOf2 = (path) => path.slice(path.lastIndexOf("/") + 1);
|
|
9412
9430
|
var WATCH_BATCH_LIMIT = 1e3;
|
|
9413
9431
|
var under3 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
|
|
9414
9432
|
var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
@@ -9418,8 +9436,39 @@ function touches(watcher, changed) {
|
|
|
9418
9436
|
);
|
|
9419
9437
|
}
|
|
9420
9438
|
var REALS_KEPT = 4096;
|
|
9439
|
+
function noted(watch, note) {
|
|
9440
|
+
if (watch.ended) return;
|
|
9441
|
+
if ("ended" in note) {
|
|
9442
|
+
watch.notes.push(note);
|
|
9443
|
+
watch.ended = true;
|
|
9444
|
+
watch.wake?.();
|
|
9445
|
+
return;
|
|
9446
|
+
}
|
|
9447
|
+
if (watch.overflowed) return;
|
|
9448
|
+
if (watch.notes.length >= WATCH_NOTES_LIMIT) {
|
|
9449
|
+
watch.overflowed = true;
|
|
9450
|
+
watch.notes.push({ overflow: true });
|
|
9451
|
+
} else watch.notes.push(note);
|
|
9452
|
+
watch.wake?.();
|
|
9453
|
+
}
|
|
9454
|
+
function route(watch, type, path) {
|
|
9455
|
+
for (const root of watch.roots) if (routed(watch, root, type, path)) return;
|
|
9456
|
+
}
|
|
9457
|
+
function routed(watch, root, type, path) {
|
|
9458
|
+
if (path === root || type === "rename" && root !== "/" && under3(root, path)) {
|
|
9459
|
+
noted(watch, { type, filename: baseOf2(root) });
|
|
9460
|
+
if (type === "rename") noted(watch, { ended: true });
|
|
9461
|
+
return true;
|
|
9462
|
+
}
|
|
9463
|
+
if (parentOf6(path) === root) noted(watch, { type, filename: baseOf2(path) });
|
|
9464
|
+
else if (watch.recursive && under3(path, root)) {
|
|
9465
|
+
noted(watch, { type, filename: path.slice(root === "/" ? 1 : root.length + 1) });
|
|
9466
|
+
} else return false;
|
|
9467
|
+
return true;
|
|
9468
|
+
}
|
|
9421
9469
|
var FsWatchers = class {
|
|
9422
9470
|
watchers = /* @__PURE__ */ new Set();
|
|
9471
|
+
typed = /* @__PURE__ */ new Set();
|
|
9423
9472
|
taps = [];
|
|
9424
9473
|
reals = /* @__PURE__ */ new Map();
|
|
9425
9474
|
reshapes = [];
|
|
@@ -9446,7 +9495,65 @@ var FsWatchers = class {
|
|
|
9446
9495
|
this.watchers.delete(watcher);
|
|
9447
9496
|
};
|
|
9448
9497
|
}
|
|
9498
|
+
typedWatch(root, recursive, alias) {
|
|
9499
|
+
const main = normalizePath(root);
|
|
9500
|
+
const other = alias === void 0 ? main : normalizePath(alias);
|
|
9501
|
+
const watch = {
|
|
9502
|
+
roots: other === main ? [main] : [main, other],
|
|
9503
|
+
recursive,
|
|
9504
|
+
notes: [],
|
|
9505
|
+
overflowed: false,
|
|
9506
|
+
ended: false,
|
|
9507
|
+
wake: void 0
|
|
9508
|
+
};
|
|
9509
|
+
this.typed.add(watch);
|
|
9510
|
+
const close = () => {
|
|
9511
|
+
this.typed.delete(watch);
|
|
9512
|
+
watch.ended = true;
|
|
9513
|
+
watch.wake?.();
|
|
9514
|
+
};
|
|
9515
|
+
return {
|
|
9516
|
+
close,
|
|
9517
|
+
next: async (signal) => {
|
|
9518
|
+
while (watch.notes.length === 0 && !watch.ended && !signal.aborted) {
|
|
9519
|
+
await new Promise((resolve2) => {
|
|
9520
|
+
const done4 = () => {
|
|
9521
|
+
watch.wake = void 0;
|
|
9522
|
+
signal.removeEventListener("abort", done4);
|
|
9523
|
+
resolve2();
|
|
9524
|
+
};
|
|
9525
|
+
watch.wake = done4;
|
|
9526
|
+
signal.addEventListener("abort", done4, { once: true });
|
|
9527
|
+
});
|
|
9528
|
+
}
|
|
9529
|
+
const notes = watch.notes.splice(0);
|
|
9530
|
+
watch.overflowed = false;
|
|
9531
|
+
if (watch.ended) this.typed.delete(watch);
|
|
9532
|
+
return notes.length === 0 && watch.ended ? [{ ended: true }] : notes;
|
|
9533
|
+
}
|
|
9534
|
+
};
|
|
9535
|
+
}
|
|
9536
|
+
covers(path) {
|
|
9537
|
+
for (const watch of this.typed) {
|
|
9538
|
+
for (const root of watch.roots) {
|
|
9539
|
+
if (path === root || parentOf6(path) === root || watch.recursive && under3(path, root))
|
|
9540
|
+
return true;
|
|
9541
|
+
}
|
|
9542
|
+
}
|
|
9543
|
+
return false;
|
|
9544
|
+
}
|
|
9545
|
+
emit(type, paths) {
|
|
9546
|
+
if (this.typed.size === 0) return;
|
|
9547
|
+
for (const path of paths) {
|
|
9548
|
+
const at = normalizePath(path);
|
|
9549
|
+
for (const watch of [...this.typed]) route(watch, type, at);
|
|
9550
|
+
}
|
|
9551
|
+
}
|
|
9449
9552
|
changed(...paths) {
|
|
9553
|
+
this.report(paths);
|
|
9554
|
+
this.emit("change", paths);
|
|
9555
|
+
}
|
|
9556
|
+
report(paths) {
|
|
9450
9557
|
for (const tap of this.taps) tap(paths);
|
|
9451
9558
|
for (const watcher of this.watchers) {
|
|
9452
9559
|
for (const path of paths) {
|
|
@@ -9466,11 +9573,15 @@ var FsWatchers = class {
|
|
|
9466
9573
|
watcher.notify(paths.length > WATCH_BATCH_LIMIT ? { overflow: true } : { paths });
|
|
9467
9574
|
}
|
|
9468
9575
|
wrap(fs) {
|
|
9469
|
-
const after = (run2, changed) => async (...args) => {
|
|
9576
|
+
const after = (run2, changed, type) => async (...args) => {
|
|
9577
|
+
let done4 = false;
|
|
9470
9578
|
try {
|
|
9471
9579
|
await run2(...args);
|
|
9580
|
+
done4 = true;
|
|
9472
9581
|
} finally {
|
|
9473
|
-
|
|
9582
|
+
const paths = changed(...args);
|
|
9583
|
+
this.report(paths);
|
|
9584
|
+
if (done4) this.emit(type, paths);
|
|
9474
9585
|
}
|
|
9475
9586
|
};
|
|
9476
9587
|
const reals = this.reals;
|
|
@@ -9489,15 +9600,21 @@ var FsWatchers = class {
|
|
|
9489
9600
|
reals.set(at, real);
|
|
9490
9601
|
return real;
|
|
9491
9602
|
};
|
|
9492
|
-
const content = (run2) => async (...args) => {
|
|
9603
|
+
const content = (run2, types) => async (...args) => {
|
|
9604
|
+
const at = normalizePath(args[0]);
|
|
9605
|
+
const fresh2 = this.covers(at) && !await fs.exists(at).catch(() => true);
|
|
9606
|
+
let done4 = false;
|
|
9493
9607
|
try {
|
|
9494
9608
|
await run2(...args);
|
|
9609
|
+
done4 = true;
|
|
9495
9610
|
} finally {
|
|
9496
|
-
const at = normalizePath(args[0]);
|
|
9497
9611
|
const real = await realOf(at);
|
|
9498
|
-
|
|
9612
|
+
const paths = real === at ? [at] : [at, real];
|
|
9613
|
+
this.report(paths);
|
|
9614
|
+
if (done4) for (const type of types(fresh2)) this.emit(type, paths);
|
|
9499
9615
|
}
|
|
9500
9616
|
};
|
|
9617
|
+
const written2 = (fresh2) => fresh2 ? ["rename", "change"] : ["change"];
|
|
9501
9618
|
const reshaping = (run2) => async (...args) => {
|
|
9502
9619
|
this.reshaped();
|
|
9503
9620
|
try {
|
|
@@ -9507,8 +9624,8 @@ var FsWatchers = class {
|
|
|
9507
9624
|
}
|
|
9508
9625
|
};
|
|
9509
9626
|
const overrides = {
|
|
9510
|
-
writeFile: content(fs.writeFile.bind(fs)),
|
|
9511
|
-
...fs.createExclusive ? { createExclusive: content(fs.createExclusive.bind(fs)) } : {},
|
|
9627
|
+
writeFile: content(fs.writeFile.bind(fs), written2),
|
|
9628
|
+
...fs.createExclusive ? { createExclusive: content(fs.createExclusive.bind(fs), () => ["rename"]) } : {},
|
|
9512
9629
|
mkdir: async (path, options) => {
|
|
9513
9630
|
const created3 = [];
|
|
9514
9631
|
if (options?.recursive) {
|
|
@@ -9518,33 +9635,39 @@ var FsWatchers = class {
|
|
|
9518
9635
|
if (created3.length > 0 || !await fs.exists(ancestor)) created3.push(ancestor);
|
|
9519
9636
|
}
|
|
9520
9637
|
}
|
|
9638
|
+
const fresh2 = !options?.recursive || !(this.covers(path) && await fs.exists(path));
|
|
9639
|
+
let done4 = false;
|
|
9521
9640
|
try {
|
|
9522
9641
|
await fs.mkdir(path, options);
|
|
9642
|
+
done4 = true;
|
|
9523
9643
|
} finally {
|
|
9524
|
-
this.
|
|
9644
|
+
this.report([...created3, path]);
|
|
9645
|
+
if (done4) this.emit("rename", fresh2 ? [...created3, path] : created3);
|
|
9525
9646
|
}
|
|
9526
9647
|
},
|
|
9527
|
-
rm: reshaping(after(fs.rm.bind(fs), (path) => [path])),
|
|
9528
|
-
rename: reshaping(after(fs.rename.bind(fs), (from, to) => [from, to])),
|
|
9648
|
+
rm: reshaping(after(fs.rm.bind(fs), (path) => [path], "rename")),
|
|
9649
|
+
rename: reshaping(after(fs.rename.bind(fs), (from, to) => [from, to], "rename")),
|
|
9529
9650
|
link: reshaping(
|
|
9530
9651
|
after(
|
|
9531
9652
|
(from, to, follow2) => fs.link(from, to, follow2),
|
|
9532
|
-
(_from, to) => [to]
|
|
9653
|
+
(_from, to) => [to],
|
|
9654
|
+
"rename"
|
|
9533
9655
|
)
|
|
9534
9656
|
),
|
|
9535
|
-
symlink: reshaping(after(fs.symlink.bind(fs), (_target, path) => [path])),
|
|
9536
|
-
chmod: after(fs.chmod.bind(fs), (path) => [path]),
|
|
9537
|
-
...fs.chown ? { chown: after(fs.chown.bind(fs), (path) => [path]) } : {},
|
|
9538
|
-
utimes: after(fs.utimes.bind(fs), (path) => [path]),
|
|
9657
|
+
symlink: reshaping(after(fs.symlink.bind(fs), (_target, path) => [path], "rename")),
|
|
9658
|
+
chmod: after(fs.chmod.bind(fs), (path) => [path], "change"),
|
|
9659
|
+
...fs.chown ? { chown: after(fs.chown.bind(fs), (path) => [path], "change") } : {},
|
|
9660
|
+
utimes: after(fs.utimes.bind(fs), (path) => [path], "change"),
|
|
9539
9661
|
lutimes: after(
|
|
9540
9662
|
(path, atime, mtime) => lutimesOf(fs, path, atime, mtime),
|
|
9541
|
-
(path) => [path]
|
|
9663
|
+
(path) => [path],
|
|
9664
|
+
"change"
|
|
9542
9665
|
)
|
|
9543
9666
|
};
|
|
9544
9667
|
const ranged2 = rangedOps(fs);
|
|
9545
9668
|
if (ranged2) {
|
|
9546
|
-
overrides.pwrite = content(ranged2.pwrite);
|
|
9547
|
-
overrides.truncate = content(ranged2.truncate);
|
|
9669
|
+
overrides.pwrite = content(ranged2.pwrite, () => ["change"]);
|
|
9670
|
+
overrides.truncate = content(ranged2.truncate, () => ["change"]);
|
|
9548
9671
|
}
|
|
9549
9672
|
return new Proxy(fs, {
|
|
9550
9673
|
get(target, key, receiver) {
|
|
@@ -10152,7 +10275,8 @@ var FifoTable = class {
|
|
|
10152
10275
|
poll: () => ({
|
|
10153
10276
|
readable: reads && pipe.readReady,
|
|
10154
10277
|
writable: writes && pipe.writeReady,
|
|
10155
|
-
hangup: reads ? pipe.writersGone : pipe.readersGone
|
|
10278
|
+
hangup: reads ? pipe.writersGone : pipe.readersGone,
|
|
10279
|
+
...reads ? { nread: pipe.buffered } : {}
|
|
10156
10280
|
}),
|
|
10157
10281
|
changed: (signal) => pipe.changed(signal)
|
|
10158
10282
|
});
|
|
@@ -10551,8 +10675,8 @@ var MountTable = class {
|
|
|
10551
10675
|
...error ? { error } : {}
|
|
10552
10676
|
})
|
|
10553
10677
|
);
|
|
10554
|
-
const
|
|
10555
|
-
return [...mounted, ...
|
|
10678
|
+
const noted2 = [...this.notes.values()].filter((note) => !this.mounts.has(note.target));
|
|
10679
|
+
return [...mounted, ...noted2.map((note) => ({ ...note, options: { ...note.options } }))];
|
|
10556
10680
|
}
|
|
10557
10681
|
listed() {
|
|
10558
10682
|
return this.list().filter((entry) => !this.system.has(entry.target));
|
|
@@ -14773,6 +14897,8 @@ function realmCa(owner, store) {
|
|
|
14773
14897
|
|
|
14774
14898
|
// src/kernel/net/tls-engine.ts
|
|
14775
14899
|
var TLS_CLIENT_INSECURE_SKIP_VERIFY = 1;
|
|
14900
|
+
var TLS_CLIENT_NO_SNI = 2;
|
|
14901
|
+
var TLS_CLIENT_VERIFY_CHAIN_ONLY = 4;
|
|
14776
14902
|
var SCRATCH = 64 * 1024;
|
|
14777
14903
|
var EOF = -1;
|
|
14778
14904
|
var TlsError = class extends Error {
|
|
@@ -14829,10 +14955,22 @@ var TlsEngine = class {
|
|
|
14829
14955
|
this.m._free(ptr);
|
|
14830
14956
|
}
|
|
14831
14957
|
}
|
|
14958
|
+
withSecret(bytes, fn) {
|
|
14959
|
+
const ptr = this.put(bytes);
|
|
14960
|
+
try {
|
|
14961
|
+
return fn(ptr);
|
|
14962
|
+
} finally {
|
|
14963
|
+
this.m.HEAPU8.fill(0, ptr, ptr + bytes.length + 1);
|
|
14964
|
+
this.m._free(ptr);
|
|
14965
|
+
}
|
|
14966
|
+
}
|
|
14832
14967
|
intoScratch(fn) {
|
|
14833
14968
|
const n = fn(this.scratch, SCRATCH);
|
|
14834
14969
|
return n > 0 ? this.taken(n) : n;
|
|
14835
14970
|
}
|
|
14971
|
+
get sessionControls() {
|
|
14972
|
+
return typeof this.m._tls_client_cert === "function";
|
|
14973
|
+
}
|
|
14836
14974
|
get client() {
|
|
14837
14975
|
return typeof this.m._tls_client_new === "function" ? this.m : void 0;
|
|
14838
14976
|
}
|
|
@@ -14985,6 +15123,27 @@ var TlsSession = class {
|
|
|
14985
15123
|
const out = this.engine.intoScratch((p, max) => this.c._tls_alpn(this.ptr, p, max));
|
|
14986
15124
|
return out instanceof Uint8Array ? new TextDecoder().decode(out) : void 0;
|
|
14987
15125
|
}
|
|
15126
|
+
ownCert(chain2, key, pass2) {
|
|
15127
|
+
const set3 = this.m._tls_client_cert;
|
|
15128
|
+
const keyLen = key[0] === 45 ? key.length + 1 : key.length;
|
|
15129
|
+
const ret = this.engine.withBytes(
|
|
15130
|
+
chain2,
|
|
15131
|
+
(c) => this.engine.withSecret(
|
|
15132
|
+
key,
|
|
15133
|
+
(k) => this.engine.withSecret(
|
|
15134
|
+
pass2,
|
|
15135
|
+
(p) => set3(this.ptr, c, chain2.length, k, keyLen, pass2.length > 0 ? p : 0, pass2.length)
|
|
15136
|
+
)
|
|
15137
|
+
)
|
|
15138
|
+
);
|
|
15139
|
+
if (ret !== 0) throw this.engine.error(ret, "client certificate");
|
|
15140
|
+
}
|
|
15141
|
+
get ciphersuite() {
|
|
15142
|
+
return this.m._tls_ciphersuite(this.ptr);
|
|
15143
|
+
}
|
|
15144
|
+
get nameMismatch() {
|
|
15145
|
+
return this.m._tls_name_mismatch(this.ptr) === 1;
|
|
15146
|
+
}
|
|
14988
15147
|
peerChain() {
|
|
14989
15148
|
const chain2 = [];
|
|
14990
15149
|
const count2 = this.c._tls_peer_count(this.ptr);
|
|
@@ -15986,7 +16145,8 @@ var KernelTty = class {
|
|
|
15986
16145
|
poll: () => ({
|
|
15987
16146
|
readable: this.readable.length > 0 || this.hungUp,
|
|
15988
16147
|
writable: true,
|
|
15989
|
-
hangup: this.hungUp
|
|
16148
|
+
hangup: this.hungUp,
|
|
16149
|
+
nread: this.readable.reduce((n, chunk2) => n + (chunk2?.length ?? 0), 0)
|
|
15990
16150
|
}),
|
|
15991
16151
|
changed: (signal) => this.changed(signal),
|
|
15992
16152
|
close: () => {
|
|
@@ -16182,7 +16342,8 @@ var PtyPair = class {
|
|
|
16182
16342
|
poll: () => ({
|
|
16183
16343
|
readable: this.queue.length > 0 || this.slaveGone,
|
|
16184
16344
|
writable: true,
|
|
16185
|
-
hangup: this.slaveGone
|
|
16345
|
+
hangup: this.slaveGone,
|
|
16346
|
+
nread: this.queue.reduce((n, chunk2) => n + chunk2.length, 0)
|
|
16186
16347
|
}),
|
|
16187
16348
|
changed: (signal) => this.changed(signal),
|
|
16188
16349
|
close: () => this.closeMaster(),
|
|
@@ -16403,11 +16564,11 @@ async function runChild(req, deps) {
|
|
|
16403
16564
|
if (pid !== 0) kill();
|
|
16404
16565
|
decided.resolve();
|
|
16405
16566
|
};
|
|
16406
|
-
const
|
|
16567
|
+
const controls2 = [];
|
|
16407
16568
|
const capture = () => {
|
|
16408
16569
|
const { promise, resolve: resolve2 } = Promise.withResolvers();
|
|
16409
16570
|
const control = { shut: false, ended: resolve2, eof: promise };
|
|
16410
|
-
|
|
16571
|
+
controls2.push(control);
|
|
16411
16572
|
return { capture: true, max, overflow: () => stop("ENOBUFS"), control };
|
|
16412
16573
|
};
|
|
16413
16574
|
const decided = Promise.withResolvers();
|
|
@@ -16441,11 +16602,11 @@ async function runChild(req, deps) {
|
|
|
16441
16602
|
let status;
|
|
16442
16603
|
try {
|
|
16443
16604
|
status = await reaped(deps, pid);
|
|
16444
|
-
const open =
|
|
16605
|
+
const open = controls2.filter((c) => c.opened);
|
|
16445
16606
|
await Promise.race([Promise.all(open.map((c) => c.eof)), decided.promise]);
|
|
16446
16607
|
} finally {
|
|
16447
16608
|
cancel?.();
|
|
16448
|
-
for (const control of
|
|
16609
|
+
for (const control of controls2) control.shut = true;
|
|
16449
16610
|
}
|
|
16450
16611
|
const out = deps.children.captured(pid, 1);
|
|
16451
16612
|
const err = deps.children.captured(pid, 2);
|
|
@@ -16464,13 +16625,17 @@ async function runChild(req, deps) {
|
|
|
16464
16625
|
// src/kernel/select.ts
|
|
16465
16626
|
function ready(fds, read, write) {
|
|
16466
16627
|
const hangup = /* @__PURE__ */ new Set();
|
|
16628
|
+
const nread = {};
|
|
16467
16629
|
const pick = (list, dir) => list.filter((fd) => {
|
|
16468
16630
|
const state = pollFile(fds.get(fd).file);
|
|
16469
16631
|
if (state.hangup) hangup.add(fd);
|
|
16470
|
-
|
|
16632
|
+
const picked = state[dir] || state.hangup;
|
|
16633
|
+
if (picked && dir === "readable" && state.nread !== void 0) nread[fd] = state.nread;
|
|
16634
|
+
return picked;
|
|
16471
16635
|
});
|
|
16472
16636
|
const out = { read: pick(read, "readable"), write: pick(write, "writable") };
|
|
16473
16637
|
if (hangup.size > 0) out.hangup = [...hangup];
|
|
16638
|
+
if (Object.keys(nread).length > 0) out.nread = nread;
|
|
16474
16639
|
return out;
|
|
16475
16640
|
}
|
|
16476
16641
|
function interruption(signal) {
|
|
@@ -17027,6 +17192,47 @@ function encodeReceived(got) {
|
|
|
17027
17192
|
return out;
|
|
17028
17193
|
}
|
|
17029
17194
|
|
|
17195
|
+
// src/kernel/net/tls-ciphers.ts
|
|
17196
|
+
var SUITES = [
|
|
17197
|
+
[4865, "TLS_AES_128_GCM_SHA256", "TLS_AES_128_GCM_SHA256", "TLSv1.3"],
|
|
17198
|
+
[4866, "TLS_AES_256_GCM_SHA384", "TLS_AES_256_GCM_SHA384", "TLSv1.3"],
|
|
17199
|
+
[4867, "TLS_CHACHA20_POLY1305_SHA256", "TLS_CHACHA20_POLY1305_SHA256", "TLSv1.3"],
|
|
17200
|
+
[4868, "TLS_AES_128_CCM_SHA256", "TLS_AES_128_CCM_SHA256", "TLSv1.3"],
|
|
17201
|
+
[4869, "TLS_AES_128_CCM_8_SHA256", "TLS_AES_128_CCM_8_SHA256", "TLSv1.3"],
|
|
17202
|
+
[49195, "ECDHE-ECDSA-AES128-GCM-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLSv1.2"],
|
|
17203
|
+
[49196, "ECDHE-ECDSA-AES256-GCM-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLSv1.2"],
|
|
17204
|
+
[49199, "ECDHE-RSA-AES128-GCM-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLSv1.2"],
|
|
17205
|
+
[49200, "ECDHE-RSA-AES256-GCM-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLSv1.2"],
|
|
17206
|
+
[52392, "ECDHE-RSA-CHACHA20-POLY1305", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "TLSv1.2"],
|
|
17207
|
+
[
|
|
17208
|
+
52393,
|
|
17209
|
+
"ECDHE-ECDSA-CHACHA20-POLY1305",
|
|
17210
|
+
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
|
17211
|
+
"TLSv1.2"
|
|
17212
|
+
],
|
|
17213
|
+
[49187, "ECDHE-ECDSA-AES128-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLSv1.2"],
|
|
17214
|
+
[49188, "ECDHE-ECDSA-AES256-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLSv1.2"],
|
|
17215
|
+
[49191, "ECDHE-RSA-AES128-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLSv1.2"],
|
|
17216
|
+
[49192, "ECDHE-RSA-AES256-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLSv1.2"],
|
|
17217
|
+
[49161, "ECDHE-ECDSA-AES128-SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLSv1.0"],
|
|
17218
|
+
[49162, "ECDHE-ECDSA-AES256-SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLSv1.0"],
|
|
17219
|
+
[49171, "ECDHE-RSA-AES128-SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLSv1.0"],
|
|
17220
|
+
[49172, "ECDHE-RSA-AES256-SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLSv1.0"],
|
|
17221
|
+
[156, "AES128-GCM-SHA256", "TLS_RSA_WITH_AES_128_GCM_SHA256", "TLSv1.2"],
|
|
17222
|
+
[157, "AES256-GCM-SHA384", "TLS_RSA_WITH_AES_256_GCM_SHA384", "TLSv1.2"],
|
|
17223
|
+
[158, "DHE-RSA-AES128-GCM-SHA256", "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "TLSv1.2"],
|
|
17224
|
+
[159, "DHE-RSA-AES256-GCM-SHA384", "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "TLSv1.2"]
|
|
17225
|
+
];
|
|
17226
|
+
function cipherOf(id2, protocol) {
|
|
17227
|
+
const known = SUITES.find(([n]) => n === id2);
|
|
17228
|
+
const hex3 = `0x${id2.toString(16).padStart(4, "0").toUpperCase()}`;
|
|
17229
|
+
return {
|
|
17230
|
+
name: known?.[1] ?? hex3,
|
|
17231
|
+
standardName: known?.[2] ?? hex3,
|
|
17232
|
+
version: known?.[3] ?? protocol
|
|
17233
|
+
};
|
|
17234
|
+
}
|
|
17235
|
+
|
|
17030
17236
|
// src/kernel/net/tls-client.ts
|
|
17031
17237
|
var WRITE_CHUNK = 16 * 1024;
|
|
17032
17238
|
var BADCERT = [
|
|
@@ -17043,6 +17249,15 @@ function verifyCode(flags2) {
|
|
|
17043
17249
|
if (flags2 === -1) return "UNABLE_TO_VERIFY_LEAF_SIGNATURE";
|
|
17044
17250
|
return BADCERT.find(([bit]) => (flags2 & bit) !== 0)?.[1] ?? "UNABLE_TO_VERIFY_LEAF_SIGNATURE";
|
|
17045
17251
|
}
|
|
17252
|
+
function isIP(name2) {
|
|
17253
|
+
return ipv4Bytes(name2) !== void 0 || name2.includes(":") && ipv6Groups(name2) !== void 0;
|
|
17254
|
+
}
|
|
17255
|
+
function clientFlags(o) {
|
|
17256
|
+
return (o.insecure ? TLS_CLIENT_INSECURE_SKIP_VERIFY : 0) | (o.sni === false ? TLS_CLIENT_NO_SNI : 0) | (o.checkHostname === false ? TLS_CLIENT_VERIFY_CHAIN_ONLY : 0);
|
|
17257
|
+
}
|
|
17258
|
+
function openSession(o) {
|
|
17259
|
+
return o.engine.clientSession(o.store, o.servername, o.alpn, clientFlags(o));
|
|
17260
|
+
}
|
|
17046
17261
|
var Ciphertext = class {
|
|
17047
17262
|
queue = [];
|
|
17048
17263
|
ended = false;
|
|
@@ -17115,17 +17330,39 @@ async function handshake(conn, o, session, inbox) {
|
|
|
17115
17330
|
}
|
|
17116
17331
|
}
|
|
17117
17332
|
}
|
|
17333
|
+
function controlsOf(o, session, protocol) {
|
|
17334
|
+
if (!o.engine.sessionControls) return {};
|
|
17335
|
+
const checked = o.checkHostname !== false && !o.insecure;
|
|
17336
|
+
return {
|
|
17337
|
+
cipher: cipherOf(session.ciphersuite, protocol),
|
|
17338
|
+
clientCertificate: o.clientCertificate === true,
|
|
17339
|
+
sni: o.sni !== false && !isIP(o.servername),
|
|
17340
|
+
hostnameChecked: checked,
|
|
17341
|
+
...o.insecure ? {} : { hostnameMatches: checked || !session.nameMismatch }
|
|
17342
|
+
};
|
|
17343
|
+
}
|
|
17118
17344
|
function infoOf(o, session) {
|
|
17119
17345
|
const flags2 = session.verifyResult;
|
|
17120
17346
|
const code = verifyCode(flags2);
|
|
17347
|
+
const protocol = session.version === 772 ? "TLSv1.3" : "TLSv1.2";
|
|
17121
17348
|
return {
|
|
17122
|
-
protocol
|
|
17349
|
+
protocol,
|
|
17123
17350
|
alpnProtocol: session.alpn ?? false,
|
|
17124
17351
|
authorized: code === void 0,
|
|
17125
17352
|
...code ? { authorizationError: code, authorizationText: o.engine.verifyInfo(flags2).trim() } : {},
|
|
17126
|
-
peerCertificates: session.peerChain()
|
|
17353
|
+
peerCertificates: session.peerChain(),
|
|
17354
|
+
...controlsOf(o, session, protocol)
|
|
17127
17355
|
};
|
|
17128
17356
|
}
|
|
17357
|
+
var FATAL_ALERT = -30592;
|
|
17358
|
+
function alerted(err) {
|
|
17359
|
+
if (err.code === FATAL_ALERT)
|
|
17360
|
+
return new KernelError(
|
|
17361
|
+
"EPROTO",
|
|
17362
|
+
`ERR_SSL_FATAL_ALERT: the server ended the session: ${err.message}`
|
|
17363
|
+
);
|
|
17364
|
+
return new KernelError("EPROTO", `ERR_SSL_PROTOCOL_ERROR: ${err.message}`);
|
|
17365
|
+
}
|
|
17129
17366
|
function plaintext(conn, session, inbox) {
|
|
17130
17367
|
let freed = false;
|
|
17131
17368
|
let writeClosed = false;
|
|
@@ -17140,7 +17377,12 @@ function plaintext(conn, session, inbox) {
|
|
|
17140
17377
|
async read() {
|
|
17141
17378
|
for (; ; ) {
|
|
17142
17379
|
if (freed) return null;
|
|
17143
|
-
|
|
17380
|
+
let got;
|
|
17381
|
+
try {
|
|
17382
|
+
got = session.read();
|
|
17383
|
+
} catch (err) {
|
|
17384
|
+
throw alerted(err);
|
|
17385
|
+
}
|
|
17144
17386
|
await send(conn, session);
|
|
17145
17387
|
if (got instanceof Uint8Array) return got;
|
|
17146
17388
|
if (got === "eof") return null;
|
|
@@ -17168,8 +17410,7 @@ function plaintext(conn, session, inbox) {
|
|
|
17168
17410
|
};
|
|
17169
17411
|
}
|
|
17170
17412
|
async function tlsClient(conn, o, taken = new Uint8Array(0)) {
|
|
17171
|
-
const
|
|
17172
|
-
const session = o.engine.clientSession(o.store, o.servername, o.alpn, flags2);
|
|
17413
|
+
const session = o.session ?? openSession(o);
|
|
17173
17414
|
const inbox = new Ciphertext(conn, session);
|
|
17174
17415
|
inbox.hand(taken);
|
|
17175
17416
|
try {
|
|
@@ -17182,10 +17423,10 @@ async function tlsClient(conn, o, taken = new Uint8Array(0)) {
|
|
|
17182
17423
|
}
|
|
17183
17424
|
|
|
17184
17425
|
// src/kernel/net/tls-uplink.ts
|
|
17185
|
-
var
|
|
17426
|
+
var isIP2 = (text) => ipv4Bytes(text) !== void 0 || text.includes(":") && ipv6Groups(text) !== void 0;
|
|
17186
17427
|
var LABEL2 = /^(?!-)[A-Za-z0-9-]{1,63}(?<!-)$/;
|
|
17187
17428
|
function serverName(name2) {
|
|
17188
|
-
if (
|
|
17429
|
+
if (isIP2(name2.replace(/^\[|\]$/g, ""))) return name2.replace(/^\[|\]$/g, "");
|
|
17189
17430
|
const bare = name2.endsWith(".") ? name2.slice(0, -1) : name2;
|
|
17190
17431
|
if (bare.length === 0 || bare.length > 253 || !bare.split(".").every((l) => LABEL2.test(l))) {
|
|
17191
17432
|
throw new KernelError(
|
|
@@ -17235,9 +17476,66 @@ function sessionOf(info, servername) {
|
|
|
17235
17476
|
authorizationText: info.authorizationText
|
|
17236
17477
|
} : {},
|
|
17237
17478
|
servername,
|
|
17238
|
-
peerCertificates: info.peerCertificates.map(encode)
|
|
17479
|
+
peerCertificates: info.peerCertificates.map(encode),
|
|
17480
|
+
...controls(info)
|
|
17239
17481
|
};
|
|
17240
17482
|
}
|
|
17483
|
+
function controls(info) {
|
|
17484
|
+
if (!info.cipher) return {};
|
|
17485
|
+
return {
|
|
17486
|
+
cipher: info.cipher,
|
|
17487
|
+
clientCertificate: info.clientCertificate === true,
|
|
17488
|
+
sni: info.sni !== false,
|
|
17489
|
+
hostnameChecked: info.hostnameChecked === true,
|
|
17490
|
+
...info.hostnameMatches === void 0 ? {} : { hostnameMatches: info.hostnameMatches }
|
|
17491
|
+
};
|
|
17492
|
+
}
|
|
17493
|
+
var KEY_ERRORS = [
|
|
17494
|
+
[-3, "ERR_OSSL_X509_KEY_VALUES_MISMATCH"],
|
|
17495
|
+
[-15232, "ERR_OSSL_BAD_DECRYPT"],
|
|
17496
|
+
[-15360, "ERR_OSSL_CRYPTO_INTERRUPTED_OR_CANCELLED"]
|
|
17497
|
+
];
|
|
17498
|
+
function keyRefused(err) {
|
|
17499
|
+
const code = err instanceof TlsError ? err.code : 0;
|
|
17500
|
+
const name2 = KEY_ERRORS.find(([n]) => n === code)?.[1] ?? "ERR_OSSL_UNSUPPORTED";
|
|
17501
|
+
const text = code === -3 ? "the key does not match the certificate" : err.message;
|
|
17502
|
+
return new KernelError("EPROTO", `${name2}: ${text}`);
|
|
17503
|
+
}
|
|
17504
|
+
function takeCert(req) {
|
|
17505
|
+
const cert = req.clientCert;
|
|
17506
|
+
if (!cert) return void 0;
|
|
17507
|
+
delete req.clientCert;
|
|
17508
|
+
if (!Array.isArray(cert.chain) || cert.chain.length === 0 || typeof cert.key !== "string")
|
|
17509
|
+
throw new KernelError("EINVAL", "ERR_INVALID_ARG_VALUE: cert and key go together");
|
|
17510
|
+
let ders;
|
|
17511
|
+
let key;
|
|
17512
|
+
try {
|
|
17513
|
+
ders = cert.chain.map(base64Der);
|
|
17514
|
+
key = base64Der(cert.key);
|
|
17515
|
+
} catch {
|
|
17516
|
+
throw new KernelError("EINVAL", "ERR_INVALID_ARG_VALUE: cert or key is not base64");
|
|
17517
|
+
}
|
|
17518
|
+
const chain2 = new Uint8Array(ders.reduce((n, d) => n + d.length, 0));
|
|
17519
|
+
let at = 0;
|
|
17520
|
+
for (const der of ders) {
|
|
17521
|
+
chain2.set(der, at);
|
|
17522
|
+
at += der.length;
|
|
17523
|
+
}
|
|
17524
|
+
const pass2 = new TextEncoder().encode(cert.passphrase ?? "");
|
|
17525
|
+
return { chain: chain2, key, pass: pass2 };
|
|
17526
|
+
}
|
|
17527
|
+
function withCert(session, cert) {
|
|
17528
|
+
if (!cert) return;
|
|
17529
|
+
try {
|
|
17530
|
+
session.ownCert(cert.chain, cert.key, cert.pass);
|
|
17531
|
+
} catch (err) {
|
|
17532
|
+
throw keyRefused(err);
|
|
17533
|
+
}
|
|
17534
|
+
}
|
|
17535
|
+
function wipe(cert) {
|
|
17536
|
+
cert?.key.fill(0);
|
|
17537
|
+
cert?.pass.fill(0);
|
|
17538
|
+
}
|
|
17241
17539
|
async function setUp(support, req) {
|
|
17242
17540
|
const engine = await support.engine();
|
|
17243
17541
|
if (!engine.client)
|
|
@@ -17255,21 +17553,66 @@ async function setUp(support, req) {
|
|
|
17255
17553
|
}
|
|
17256
17554
|
return { engine, store: own, own };
|
|
17257
17555
|
}
|
|
17258
|
-
function
|
|
17259
|
-
|
|
17260
|
-
|
|
17556
|
+
async function prepare(support, req, servername) {
|
|
17557
|
+
const cert = takeCert(req);
|
|
17558
|
+
try {
|
|
17559
|
+
return await prepareWith(support, req, servername, cert);
|
|
17560
|
+
} catch (err) {
|
|
17561
|
+
if (err instanceof KernelError) throw err;
|
|
17562
|
+
throw new KernelError("ENOTSUP", err instanceof Error ? err.message : String(err));
|
|
17563
|
+
} finally {
|
|
17564
|
+
wipe(cert);
|
|
17565
|
+
}
|
|
17566
|
+
}
|
|
17567
|
+
async function prepareWith(support, req, servername, cert) {
|
|
17568
|
+
if (req.checkHostname === false && req.insecure === true) {
|
|
17569
|
+
throw new KernelError(
|
|
17570
|
+
"EINVAL",
|
|
17571
|
+
"ERR_INVALID_ARG_VALUE: checkHostname: false needs rejectUnauthorized"
|
|
17572
|
+
);
|
|
17573
|
+
}
|
|
17574
|
+
const { engine, store, own } = await setUp(support, req);
|
|
17575
|
+
const options = {
|
|
17576
|
+
engine,
|
|
17577
|
+
store,
|
|
17578
|
+
servername,
|
|
17579
|
+
alpn: req.alpn ?? [],
|
|
17580
|
+
insecure: req.insecure === true,
|
|
17581
|
+
...req.sni === false ? { sni: false } : {},
|
|
17582
|
+
...req.checkHostname === false ? { checkHostname: false } : {},
|
|
17583
|
+
clientCertificate: cert !== void 0
|
|
17584
|
+
};
|
|
17585
|
+
let session;
|
|
17586
|
+
try {
|
|
17587
|
+
const controls2 = req.sni === false || req.checkHostname === false || cert !== void 0;
|
|
17588
|
+
if (controls2 && !engine.sessionControls) {
|
|
17589
|
+
throw new KernelError("ENOTSUP", "the installed tls engine has no client certificates");
|
|
17590
|
+
}
|
|
17591
|
+
session = openSession(options);
|
|
17592
|
+
withCert(session, cert);
|
|
17593
|
+
return { options: { ...options, session }, own };
|
|
17594
|
+
} catch (err) {
|
|
17595
|
+
session?.free();
|
|
17596
|
+
own?.release();
|
|
17597
|
+
throw err;
|
|
17598
|
+
}
|
|
17599
|
+
}
|
|
17600
|
+
function secure(prepared) {
|
|
17601
|
+
let ran = false;
|
|
17602
|
+
const make = async (raw) => {
|
|
17603
|
+
ran = true;
|
|
17261
17604
|
try {
|
|
17262
|
-
return await tlsClient(raw,
|
|
17263
|
-
engine,
|
|
17264
|
-
store,
|
|
17265
|
-
servername,
|
|
17266
|
-
alpn: req.alpn ?? [],
|
|
17267
|
-
insecure: req.insecure === true
|
|
17268
|
-
});
|
|
17605
|
+
return await tlsClient(raw, prepared.options);
|
|
17269
17606
|
} finally {
|
|
17270
|
-
own?.release();
|
|
17607
|
+
prepared.own?.release();
|
|
17271
17608
|
}
|
|
17272
17609
|
};
|
|
17610
|
+
const abandon = () => {
|
|
17611
|
+
if (ran) return;
|
|
17612
|
+
prepared.options.session.free();
|
|
17613
|
+
prepared.own?.release();
|
|
17614
|
+
};
|
|
17615
|
+
return { make, abandon };
|
|
17273
17616
|
}
|
|
17274
17617
|
function supportOf(net) {
|
|
17275
17618
|
const support = net.tlsClient;
|
|
@@ -17278,7 +17621,7 @@ function supportOf(net) {
|
|
|
17278
17621
|
}
|
|
17279
17622
|
async function uplinkAddress(proc, host) {
|
|
17280
17623
|
const bare = host.replace(/^\[|\]$/g, "");
|
|
17281
|
-
if (
|
|
17624
|
+
if (isIP2(bare)) return bare;
|
|
17282
17625
|
const [first] = await proc.resolve(host, 0, () => proc.blocking());
|
|
17283
17626
|
if (!first) throw new KernelError("ENOENT", `ENOTFOUND: ${host}`);
|
|
17284
17627
|
return first;
|
|
@@ -17287,23 +17630,31 @@ async function connectSecure(proc, socket, host, port, req) {
|
|
|
17287
17630
|
const servername = serverName(req.servername || host);
|
|
17288
17631
|
if (isLoopbackHost(servername) || isLoopbackHost(host)) throw new KernelError("ENOTSUP");
|
|
17289
17632
|
const address = await uplinkAddress(proc, host);
|
|
17290
|
-
const
|
|
17291
|
-
if (!
|
|
17633
|
+
const route2 = proc.net.uplinkFor(address);
|
|
17634
|
+
if (!route2)
|
|
17292
17635
|
throw new KernelError("ENOTSUP", "TLS in the kernel needs a destination the uplink routes");
|
|
17293
17636
|
const support = supportOf(proc.net);
|
|
17294
17637
|
let info;
|
|
17295
|
-
const make = secure(support, req, servername);
|
|
17296
|
-
|
|
17297
|
-
|
|
17298
|
-
|
|
17638
|
+
const { make, abandon } = secure(await prepare(support, req, servername));
|
|
17639
|
+
try {
|
|
17640
|
+
await socket.connectSecure({ family: "inet", host: address, port }, route2, async (conn) => {
|
|
17641
|
+
info = await conn.upgrade(make);
|
|
17642
|
+
});
|
|
17643
|
+
} finally {
|
|
17644
|
+
abandon();
|
|
17645
|
+
}
|
|
17299
17646
|
return sessionOf(info, servername);
|
|
17300
17647
|
}
|
|
17301
17648
|
async function upgradeSecure(proc, socket, req) {
|
|
17302
17649
|
const peer = socket.peer;
|
|
17303
17650
|
const host = req.servername || (peer?.family === "inet" ? peer.host : "");
|
|
17304
17651
|
const servername = serverName(host);
|
|
17305
|
-
const
|
|
17306
|
-
|
|
17652
|
+
const { make, abandon } = secure(await prepare(supportOf(proc.net), req, servername));
|
|
17653
|
+
try {
|
|
17654
|
+
return sessionOf(await socket.upgradeSecure(make), servername);
|
|
17655
|
+
} finally {
|
|
17656
|
+
abandon();
|
|
17657
|
+
}
|
|
17307
17658
|
}
|
|
17308
17659
|
|
|
17309
17660
|
// src/kernel/unix-dgram.ts
|
|
@@ -17665,17 +18016,23 @@ var NOT_HTTP_PORTS = /* @__PURE__ */ new Set([
|
|
|
17665
18016
|
8883
|
|
17666
18017
|
]);
|
|
17667
18018
|
var HOST_NAME = /^[A-Za-z0-9.-]+$|^\[?[0-9A-Fa-f:.]+\]?$/;
|
|
18019
|
+
function sessionControls(req) {
|
|
18020
|
+
return req.sni === false || req.checkHostname === false || req.clientCert !== void 0;
|
|
18021
|
+
}
|
|
17668
18022
|
function wantsKernel(req) {
|
|
17669
18023
|
if (req.path === "kernel") return true;
|
|
17670
18024
|
if (req.path === "transport") return false;
|
|
17671
|
-
return Array.isArray(req.ca) || req.alpn !== void 0 && req.alpn.length > 0 && !req.alpn.includes("http/1.1") || NOT_HTTP_PORTS.has(req.port);
|
|
18025
|
+
return Array.isArray(req.ca) || sessionControls(req) || req.alpn !== void 0 && req.alpn.length > 0 && !req.alpn.includes("http/1.1") || NOT_HTTP_PORTS.has(req.port);
|
|
17672
18026
|
}
|
|
17673
18027
|
async function secureConnect(req, proc) {
|
|
17674
18028
|
if (!Number.isInteger(req.port) || req.port < 1 || req.port > 65535)
|
|
17675
18029
|
throw new KernelError("EINVAL");
|
|
17676
18030
|
const kernelPath = wantsKernel(req) && await uplinked(proc, req.host);
|
|
17677
18031
|
if (!kernelPath) {
|
|
17678
|
-
if (req.path === "kernel" || Array.isArray(req.ca)
|
|
18032
|
+
if (req.path === "kernel" || Array.isArray(req.ca) || sessionControls(req)) {
|
|
18033
|
+
delete req.clientCert;
|
|
18034
|
+
throw new KernelError("ENOTSUP");
|
|
18035
|
+
}
|
|
17679
18036
|
return { ...tlsOverTransport(req, proc), path: "transport", substitutes: true };
|
|
17680
18037
|
}
|
|
17681
18038
|
const socket = proc.net.socket("inet");
|
|
@@ -17809,13 +18166,17 @@ var VfsNode = class {
|
|
|
17809
18166
|
const ranged2 = await this.prepare();
|
|
17810
18167
|
await ranged2?.pin();
|
|
17811
18168
|
}
|
|
18169
|
+
moving = /* @__PURE__ */ new Set();
|
|
17812
18170
|
async load() {
|
|
17813
18171
|
if (this.revoked) throw new KernelError("EIO");
|
|
17814
|
-
|
|
18172
|
+
while (!this.data) {
|
|
18173
|
+
const at = this.path;
|
|
17815
18174
|
try {
|
|
17816
|
-
this.data = await this.fs.readFileBuffer(
|
|
18175
|
+
this.data = await this.fs.readFileBuffer(at);
|
|
17817
18176
|
} catch (err) {
|
|
17818
18177
|
if (!isMissing(err)) throw err;
|
|
18178
|
+
while (this.moving.size > 0) await Promise.all(this.moving);
|
|
18179
|
+
if (this.path !== at) continue;
|
|
17819
18180
|
this.data = new Uint8Array(0);
|
|
17820
18181
|
this.missing = true;
|
|
17821
18182
|
}
|
|
@@ -18169,6 +18530,16 @@ var VfsNodes = class {
|
|
|
18169
18530
|
this.byPath.set(file.hidden, node);
|
|
18170
18531
|
this.kept.set(node, file);
|
|
18171
18532
|
}
|
|
18533
|
+
moving(path) {
|
|
18534
|
+
const node = this.byPath.get(path);
|
|
18535
|
+
if (!node) return () => void 0;
|
|
18536
|
+
const { promise, resolve: resolve2 } = Promise.withResolvers();
|
|
18537
|
+
node.moving.add(promise);
|
|
18538
|
+
return () => {
|
|
18539
|
+
node.moving.delete(promise);
|
|
18540
|
+
resolve2();
|
|
18541
|
+
};
|
|
18542
|
+
}
|
|
18172
18543
|
unkeep(path, file) {
|
|
18173
18544
|
const node = this.byPath.get(file.hidden);
|
|
18174
18545
|
if (!node) return;
|
|
@@ -18477,6 +18848,65 @@ function vfsFile(fs, opts, nodes) {
|
|
|
18477
18848
|
});
|
|
18478
18849
|
}
|
|
18479
18850
|
|
|
18851
|
+
// src/kernel/watch-ops.ts
|
|
18852
|
+
var WATCH_OPS = ["watch-open", "watch-next", "watch-close"];
|
|
18853
|
+
var WATCHING = new Set(WATCH_OPS);
|
|
18854
|
+
function isWatchSyscall(req) {
|
|
18855
|
+
return WATCHING.has(req.op);
|
|
18856
|
+
}
|
|
18857
|
+
var WATCHES_MAX = 8192;
|
|
18858
|
+
var parentOf8 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
18859
|
+
function visibleTo2(watch, root, visible) {
|
|
18860
|
+
return {
|
|
18861
|
+
close: () => watch.close(),
|
|
18862
|
+
next: async (signal) => {
|
|
18863
|
+
const notes = await watch.next(signal);
|
|
18864
|
+
const seen = /* @__PURE__ */ new Map();
|
|
18865
|
+
const shown = await Promise.all(
|
|
18866
|
+
notes.map(async (note) => {
|
|
18867
|
+
if (!("filename" in note) || !note.filename.includes("/")) return true;
|
|
18868
|
+
const dir = parentOf8(`${root === "/" ? "" : root}/${note.filename}`);
|
|
18869
|
+
if (!seen.has(dir)) seen.set(dir, visible(dir));
|
|
18870
|
+
return seen.get(dir);
|
|
18871
|
+
})
|
|
18872
|
+
);
|
|
18873
|
+
return notes.filter((_note, i) => shown[i]);
|
|
18874
|
+
}
|
|
18875
|
+
};
|
|
18876
|
+
}
|
|
18877
|
+
var ProcessWatches = class {
|
|
18878
|
+
open = /* @__PURE__ */ new Map();
|
|
18879
|
+
nextId = 1;
|
|
18880
|
+
source;
|
|
18881
|
+
constructor(source) {
|
|
18882
|
+
this.source = source;
|
|
18883
|
+
}
|
|
18884
|
+
async syscall(req, cancelled) {
|
|
18885
|
+
if (req.op === "watch-open") {
|
|
18886
|
+
if (!this.source) throw new KernelError("ENOSYS");
|
|
18887
|
+
if (this.open.size >= WATCHES_MAX) throw new KernelError("ENOSPC");
|
|
18888
|
+
const watch2 = await this.source.open(req.path, req.recursive === true);
|
|
18889
|
+
const id2 = this.nextId++;
|
|
18890
|
+
this.open.set(id2, watch2);
|
|
18891
|
+
return { ok: true, kind: "json", json: id2 };
|
|
18892
|
+
}
|
|
18893
|
+
const watch = this.open.get(req.id);
|
|
18894
|
+
if (req.op === "watch-close") {
|
|
18895
|
+
watch?.close();
|
|
18896
|
+
this.open.delete(req.id);
|
|
18897
|
+
return { ok: true, kind: "void" };
|
|
18898
|
+
}
|
|
18899
|
+
if (!watch) return { ok: true, kind: "json", json: [{ ended: true }] };
|
|
18900
|
+
const notes = await watch.next(cancelled);
|
|
18901
|
+
if (notes.some((note) => "ended" in note)) this.open.delete(req.id);
|
|
18902
|
+
return { ok: true, kind: "json", json: notes };
|
|
18903
|
+
}
|
|
18904
|
+
closeAll() {
|
|
18905
|
+
for (const watch of this.open.values()) watch.close();
|
|
18906
|
+
this.open.clear();
|
|
18907
|
+
}
|
|
18908
|
+
};
|
|
18909
|
+
|
|
18480
18910
|
// src/kernel/process.ts
|
|
18481
18911
|
function aborted(signal) {
|
|
18482
18912
|
return new Promise((_, reject) => {
|
|
@@ -18603,7 +19033,8 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
18603
19033
|
...SEM_OPS,
|
|
18604
19034
|
...HTTP_OPS,
|
|
18605
19035
|
...PTY_OPS,
|
|
18606
|
-
...HOST_OPS
|
|
19036
|
+
...HOST_OPS,
|
|
19037
|
+
...WATCH_OPS
|
|
18607
19038
|
]);
|
|
18608
19039
|
function isWasmSyscall(req) {
|
|
18609
19040
|
const op = req.op;
|
|
@@ -18655,6 +19086,7 @@ function timeLeft(req, started) {
|
|
|
18655
19086
|
}
|
|
18656
19087
|
var WasmProcess = class {
|
|
18657
19088
|
exited = false;
|
|
19089
|
+
watches;
|
|
18658
19090
|
children;
|
|
18659
19091
|
deathSignals = /* @__PURE__ */ new Map();
|
|
18660
19092
|
caught = 0;
|
|
@@ -18685,6 +19117,7 @@ var WasmProcess = class {
|
|
|
18685
19117
|
this.pid = pid;
|
|
18686
19118
|
this.fds = fds;
|
|
18687
19119
|
this.options = options;
|
|
19120
|
+
this.watches = new ProcessWatches(options.watches);
|
|
18688
19121
|
this.inherited = options.ignored ?? 0;
|
|
18689
19122
|
this.umask = options.umask ?? DEFAULT_UMASK2;
|
|
18690
19123
|
this.cred = copyCred(options.cred ?? ROOT);
|
|
@@ -18942,6 +19375,7 @@ var WasmProcess = class {
|
|
|
18942
19375
|
});
|
|
18943
19376
|
}
|
|
18944
19377
|
if (isHttpSyscall(req)) return await this.httpSyscall(req);
|
|
19378
|
+
if (isWatchSyscall(req)) return await this.watches.syscall(req, cancelled);
|
|
18945
19379
|
if (isHostSyscall(req)) {
|
|
18946
19380
|
return await hostSyscall(req, {
|
|
18947
19381
|
pid: this.pid,
|
|
@@ -20082,6 +20516,7 @@ var WasmProcess = class {
|
|
|
20082
20516
|
this.options.locks?.release(this.pid);
|
|
20083
20517
|
for (const owner of this.lockOwners) this.options.locks?.release(owner);
|
|
20084
20518
|
this.asyncOps.close();
|
|
20519
|
+
this.watches.closeAll();
|
|
20085
20520
|
for (const [target, sig] of this.deathSignals) this.sendDeath(target, sig);
|
|
20086
20521
|
this.deathSignals.clear();
|
|
20087
20522
|
for (const pid of this.children.pids()) this.options.onReap?.(pid);
|
|
@@ -20343,6 +20778,7 @@ function processOptions(opts, header3, hooks, created3) {
|
|
|
20343
20778
|
locks: opts.locks,
|
|
20344
20779
|
fifos: opts.fifos,
|
|
20345
20780
|
...opts.sems ? { sems: opts.sems } : {},
|
|
20781
|
+
watches: opts.watches,
|
|
20346
20782
|
onReap: opts.onReap,
|
|
20347
20783
|
resolver: opts.resolver,
|
|
20348
20784
|
...notifiers(header3),
|
|
@@ -21456,7 +21892,7 @@ function register(automaton, sources, ids, bytes, source) {
|
|
|
21456
21892
|
}
|
|
21457
21893
|
sources[id2].push(source);
|
|
21458
21894
|
}
|
|
21459
|
-
function
|
|
21895
|
+
function prepare2(secrets) {
|
|
21460
21896
|
const main = new Automaton();
|
|
21461
21897
|
const wrap = new Automaton();
|
|
21462
21898
|
const sources = [];
|
|
@@ -21607,7 +22043,7 @@ function merge(into, from) {
|
|
|
21607
22043
|
}
|
|
21608
22044
|
}
|
|
21609
22045
|
function compile(secrets, options = {}) {
|
|
21610
|
-
const c =
|
|
22046
|
+
const c = prepare2(secrets.map((secret) => ({ ...secret })));
|
|
21611
22047
|
const decoder4 = new TextDecoder();
|
|
21612
22048
|
const idleMs = options.idleMs ?? 250;
|
|
21613
22049
|
const scrubBytes = (input) => {
|
|
@@ -25560,6 +25996,7 @@ var Launcher = class {
|
|
|
25560
25996
|
locks: this.locks,
|
|
25561
25997
|
fifos: this.fifos,
|
|
25562
25998
|
sems: this.sems,
|
|
25999
|
+
watches: this.watchSource(pid),
|
|
25563
26000
|
resolver: this.resolver,
|
|
25564
26001
|
onReap: (child) => this.reaped(child, pid),
|
|
25565
26002
|
...req.fork ? { fork: req.fork } : {},
|
|
@@ -26103,6 +26540,23 @@ var Launcher = class {
|
|
|
26103
26540
|
mounts: this.mounts.list().map((entry) => entry.target).filter((target) => this.mounts.mounted(target) !== void 0)
|
|
26104
26541
|
});
|
|
26105
26542
|
}
|
|
26543
|
+
watchSource(pid) {
|
|
26544
|
+
return {
|
|
26545
|
+
open: async (path, recursive) => {
|
|
26546
|
+
const at = normalizePath(path);
|
|
26547
|
+
const g = this.execGuard(this.credOf(pid), pid);
|
|
26548
|
+
if (g) await readable(g, "watch", at);
|
|
26549
|
+
const st = await this.fs.stat(at);
|
|
26550
|
+
const real = await this.fs.realpath?.(at, true).catch(() => at) ?? at;
|
|
26551
|
+
const watch = this.watchers.typedWatch(real, recursive && st.isDirectory, at);
|
|
26552
|
+
if (!g || g.cred.euid === 0) return watch;
|
|
26553
|
+
return visibleTo2(watch, real, async (dir) => {
|
|
26554
|
+
const walked = await access(g.fs, g.cred, dir, R_OK | X_OK, { directory: true });
|
|
26555
|
+
return walked.denied === void 0 && walked.missing !== true;
|
|
26556
|
+
});
|
|
26557
|
+
}
|
|
26558
|
+
};
|
|
26559
|
+
}
|
|
26106
26560
|
watchFiles(paths, notify) {
|
|
26107
26561
|
this.fileWatches ??= new FileWatches(this.watchers, this.rootFs);
|
|
26108
26562
|
return this.fileWatches.watch(paths, notify);
|