buninu-linux 0.17.60 → 0.18.62
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/NOTICE.md +46 -46
- package/README.md +4 -4
- package/README.zh.md +4 -4
- package/initramfs/bin/tar +32 -15
- package/initramfs/bin/unzip +74 -19
- package/initramfs/buninu/CHANGELOG.md +19 -0
- package/initramfs/buninu/apps/bunmsh/CHANGELOG.md +21 -0
- package/initramfs/buninu/apps/bunmsh/package.json +1 -1
- package/initramfs/buninu/apps/bunmsh/src/find.js +135 -56
- package/initramfs/buninu/apps/bunmsh/src/main.js +12 -1
- package/initramfs/buninu/apps/bunmsh/src/shell.js +8 -2
- package/initramfs/buninu/apps/bunmsh/test/find.test.js +103 -0
- package/initramfs/buninu/apps/bunmsh/test/interactive-typeahead.test.js +40 -0
- package/initramfs/buninu/apps/bunmsh/test/posix-bootstrap.test.js +9 -5
- package/initramfs/buninu/apps/bunmsh/test/reference.test.js +2 -5
- package/initramfs/buninu/apps/bunmsh/test/shell2.test.js +6 -5
- package/initramfs/buninu/apps/bunmsh/test/shell3.test.js +8 -7
- package/initramfs/buninu/apps/bunmsh/test/support/terminal.js +10 -0
- package/initramfs/buninu/apps/jsgotty/bunfig.toml +3 -0
- package/initramfs/buninu/apps/jsgotty/gotty.js +14 -1
- package/initramfs/buninu/apps/jsgotty/kitty-flow.js +40 -0
- package/initramfs/buninu/apps/jsgotty/package-lock.json +2 -2
- package/initramfs/buninu/apps/jsgotty/package.json +1 -1
- package/initramfs/buninu/apps/jsgotty/static/js/kitty.js +32 -4
- package/initramfs/buninu/apps/jsgotty/tests/kitty-flow-browser.test.js +43 -0
- package/initramfs/buninu/apps/jsgotty/tests/kitty-flow.test.js +55 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/fps-collector.mjs +18 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/fps-debug.js +31 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/js-eval-show-fps.js +35 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/kitty-background-debug.js +24 -0
- package/initramfs/buninu/package.json +1 -1
- package/package.json +1 -1
- package/scripts/arch.js +5 -5
- package/scripts/arch.sh +5 -5
- package/scripts/fetch.js +50 -15
- package/scripts/npm-packages.md +11 -11
- package/test/archive-bounds.test.js +128 -0
- package/test/fetch-npm.test.js +1 -1
package/NOTICE.md
CHANGED
|
@@ -156,83 +156,83 @@ the built image redistributes them.
|
|
|
156
156
|
- License: GPL-2.0-only WITH Linux-syscall-note
|
|
157
157
|
- See: `LICENSES/GPL-2.0.txt` and `LICENSES/Linux-syscall-note.txt`
|
|
158
158
|
|
|
159
|
-
Linux 6.18.
|
|
160
|
-
The default build uses package `linux-virt`, version 6.18.
|
|
161
|
-
`--linux-lts` and `--real` use package `linux-lts`, version 6.18.
|
|
159
|
+
Linux 6.18.62 as packaged by Alpine Linux 3.24 from the `linux-lts` aport.
|
|
160
|
+
The default build uses package `linux-virt`, version 6.18.62-r0.
|
|
161
|
+
`--linux-lts` and `--real` use package `linux-lts`, version 6.18.62-r0.
|
|
162
162
|
Each comes from the x86_64 or aarch64 repository to match `--arch`. All
|
|
163
163
|
listed modules are the selected package's `.ko.gz` files decompressed without
|
|
164
164
|
modification.
|
|
165
165
|
|
|
166
|
-
- Upstream source: https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.
|
|
166
|
+
- Upstream source: https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.62.tar.xz
|
|
167
167
|
- Alpine build recipe, configuration and patches (Corresponding Source),
|
|
168
|
-
pinned to the commit shipping 6.18.
|
|
169
|
-
https://gitlab.alpinelinux.org/alpine/aports/-/blob/
|
|
168
|
+
pinned to the commit shipping 6.18.62-r0 (the same for both architectures):
|
|
169
|
+
https://gitlab.alpinelinux.org/alpine/aports/-/blob/4835e466ec80d5c31c181fa130a10dc81af60c1c/main/linux-lts/APKBUILD
|
|
170
170
|
- Packages:
|
|
171
|
-
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/x86_64/linux-virt-6.18.
|
|
172
|
-
SHA-256 `
|
|
173
|
-
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/x86_64/linux-lts-6.18.
|
|
174
|
-
SHA-256 `
|
|
175
|
-
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/aarch64/linux-virt-6.18.
|
|
176
|
-
SHA-256 `
|
|
177
|
-
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/aarch64/linux-lts-6.18.
|
|
178
|
-
SHA-256 `
|
|
171
|
+
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/x86_64/linux-virt-6.18.62-r0.apk
|
|
172
|
+
SHA-256 `e121b547d9b86194a641718b52ae1c3e605085c59f31525b1649a59b3d4db0c7`
|
|
173
|
+
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/x86_64/linux-lts-6.18.62-r0.apk
|
|
174
|
+
SHA-256 `2e03d0d13959a369764d8d24d51844634d1fd6973a523f4e49166a300c782a06`
|
|
175
|
+
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/aarch64/linux-virt-6.18.62-r0.apk
|
|
176
|
+
SHA-256 `3a9a0f16adaabee0e4e05e373682249e2a99ab000faebf6aaaf682ae575c661d`
|
|
177
|
+
https://dl-cdn.alpinelinux.org/alpine/v3.24/main/aarch64/linux-lts-6.18.62-r0.apk
|
|
178
|
+
SHA-256 `29d6cbf6f432667d5d17080037b3cfd4d28d71c3b0ecb4ad7e53438abf9f24e7`
|
|
179
179
|
|
|
180
180
|
SHA-256 of files in the default x86_64 `linux-virt` image:
|
|
181
181
|
|
|
182
182
|
```text
|
|
183
|
-
kernel/x86_64/vmlinuz-virt
|
|
184
|
-
failover.ko
|
|
185
|
-
net_failover.ko
|
|
186
|
-
virtio_net.ko
|
|
183
|
+
kernel/x86_64/vmlinuz-virt fb646e37ddfe23cb9cbe1127eb641895adaaa4e6062d66eee11f9b2240d952c7
|
|
184
|
+
failover.ko 7417b9cbde8a6b751a9d5d434c3334f80025637fba4b5812fca826426de2f5f4
|
|
185
|
+
net_failover.ko 9948da8f68df63dc07567f2f8f208fb1d3c917f54766d17e8a95533bd9d5224e
|
|
186
|
+
virtio_net.ko 84f6faa9484b46f0903e1de80c68a4327340bb9f736ae465e5fde4b12e902caa
|
|
187
187
|
```
|
|
188
188
|
|
|
189
189
|
SHA-256 of files in an x86_64 `--linux-lts` image:
|
|
190
190
|
|
|
191
191
|
```text
|
|
192
|
-
kernel/x86_64/vmlinuz-lts
|
|
193
|
-
failover.ko
|
|
194
|
-
net_failover.ko
|
|
195
|
-
virtio_net.ko
|
|
196
|
-
virtio_ring.ko
|
|
197
|
-
virtio.ko
|
|
198
|
-
virtio_pci_legacy_dev.ko
|
|
199
|
-
virtio_pci_modern_dev.ko
|
|
200
|
-
virtio_pci.ko
|
|
192
|
+
kernel/x86_64/vmlinuz-lts 3504e27e6802231fed95f12e46998e73bfe1653a8ca682d448cdc559bbfc6e3e
|
|
193
|
+
failover.ko 386a443826c168bb796168d3a9916b01980e69f310c4485001168e5cbf9bf245
|
|
194
|
+
net_failover.ko 16d9fff3e30b17d0a8875154dd0ef837f2518390c49cf8683d83613c503d05cf
|
|
195
|
+
virtio_net.ko 1b384bd50f94e451c52b8475f7dd7c5eb5386e79b79217e91c8f4ef04f68a1eb
|
|
196
|
+
virtio_ring.ko e13bb8a3ca15f8dec60c12481cd258170ea973934a1747c8dcf42a04ba0f5423
|
|
197
|
+
virtio.ko 403c140a1063a0e4189339582db77315d7896d136c8b677a8cc4947bd6dfa235
|
|
198
|
+
virtio_pci_legacy_dev.ko a3554a7cacbbe417b3c4f0f3bf6a5ed556776ee30337375bf99623c30e6c9943
|
|
199
|
+
virtio_pci_modern_dev.ko 3314bb829a4404b979dc09f342b133b9af3d4c184e4fc61beaa566b49b0407dd
|
|
200
|
+
virtio_pci.ko 5163698dd42cf2a221f58982b1fbcc58d915ec78e9adfca44f09efd3493e71a1
|
|
201
201
|
```
|
|
202
202
|
|
|
203
203
|
`--real` additionally includes the following modules from that same
|
|
204
204
|
x86_64 `linux-lts` package for physical xHCI controllers and USB HID keyboards:
|
|
205
205
|
|
|
206
206
|
```text
|
|
207
|
-
usb-common.ko
|
|
208
|
-
usbcore.ko
|
|
209
|
-
xhci-hcd.ko
|
|
210
|
-
xhci-pci.ko
|
|
211
|
-
xhci-pci-renesas.ko
|
|
212
|
-
hid.ko
|
|
213
|
-
hid-generic.ko
|
|
214
|
-
usbhid.ko
|
|
207
|
+
usb-common.ko e441b7fa844c3f745e996d270df9c0c193ebffaed86da2af09c2808e5f5c6e51
|
|
208
|
+
usbcore.ko 242fa924fa31c3f09e5539f7814d71349d8393920957a1646ce693520ea709f0
|
|
209
|
+
xhci-hcd.ko 10258014a4e2b82d446731a9816caa305cd6ec01568b1ae83692e6ce72ae76eb
|
|
210
|
+
xhci-pci.ko b8ecc6e8abdd74609ffd26e04bf9511ad2281118319bbe550ce7a32a2148c735
|
|
211
|
+
xhci-pci-renesas.ko 1e24d082466dfd29dc97eb3ac0a45e78290cb3c708da6d46760f281499f6040d
|
|
212
|
+
hid.ko 550ffcb449bbc7e586f594b9bf737260c7be962885a310914f272d20ab903d8a
|
|
213
|
+
hid-generic.ko 26b65cd98dbe3df707458bb2e88cc4d7b106676740bc9775fe932dfe66884672
|
|
214
|
+
usbhid.ko c71f82a76f7cdf16d04ac01270ab959c7e01db3ef55d7e72a447e2be27820220
|
|
215
215
|
```
|
|
216
216
|
|
|
217
217
|
SHA-256 of files in the default aarch64 `linux-virt` image:
|
|
218
218
|
|
|
219
219
|
```text
|
|
220
|
-
kernel/aarch64/vmlinuz-virt
|
|
221
|
-
failover.ko
|
|
222
|
-
net_failover.ko
|
|
223
|
-
virtio_net.ko
|
|
220
|
+
kernel/aarch64/vmlinuz-virt 019381933096984ffe4d559969db30b59e223fcfd3a0f349cc7b2b7fd0b9f2bb
|
|
221
|
+
failover.ko 6eb84d8bb29606c40f645101929058d8c1a2226b81f86e1d477ce5292677b1f9
|
|
222
|
+
net_failover.ko 3c4800183b9aadb57b4d7406e17a7dc62dc1955f69eedd52194f0d575fd85e43
|
|
223
|
+
virtio_net.ko 2421ef2c32dd39a9d9d17e20849eb235e0e98d0698f9f3b239839c5367cad3b2
|
|
224
224
|
```
|
|
225
225
|
|
|
226
226
|
SHA-256 of files in an aarch64 `--linux-lts` image:
|
|
227
227
|
|
|
228
228
|
```text
|
|
229
|
-
kernel/aarch64/vmlinuz-lts
|
|
230
|
-
failover.ko
|
|
231
|
-
net_failover.ko
|
|
232
|
-
virtio_net.ko
|
|
233
|
-
virtio_pci_legacy_dev.ko
|
|
234
|
-
virtio_pci_modern_dev.ko
|
|
235
|
-
virtio_pci.ko
|
|
229
|
+
kernel/aarch64/vmlinuz-lts a411fa2f9234162c393c63c697b482c2a63702a4d06951c0e85a0ca5ad97cd28
|
|
230
|
+
failover.ko 9c7a6e582e0e2d2eb911d0181ade7cdc28b9cfbbcb4331e87ce9ce18005e93c5
|
|
231
|
+
net_failover.ko 4cfe47683cdf4ced4400069c52890595c418d81bfa3cc44f6e523278823c5326
|
|
232
|
+
virtio_net.ko 9eda63780001f09d30c917feea11b2d319a5656aaed61bce48b2bdbef5826af2
|
|
233
|
+
virtio_pci_legacy_dev.ko ae3ee1f0fec521295b3fe078a271864c484a1177a7099e85930dab9d0d0cc9d5
|
|
234
|
+
virtio_pci_modern_dev.ko f5d23038fd725d9ca5cef5f1d0c982490be9350a203fe8bc0a82c7939630a789
|
|
235
|
+
virtio_pci.ko e02dc0d67bcfd28030db4f7af6b42cb6ea3118f6527ebeab59f379c4d27ff913
|
|
236
236
|
```
|
|
237
237
|
|
|
238
238
|
### linuxx64.efi.stub and linuxaa64.efi.stub
|
package/README.md
CHANGED
|
@@ -1415,7 +1415,7 @@ initramfs/ init.js, commands, userspace: the same for every archi
|
|
|
1415
1415
|
native/<arch>/ musl and the GCC runtime (committed); bun and modules (fetched)
|
|
1416
1416
|
```
|
|
1417
1417
|
|
|
1418
|
-
The `--real` inputs are Alpine v3.24 `linux-lts-6.18.
|
|
1418
|
+
The `--real` inputs are Alpine v3.24 `linux-lts-6.18.62-r0`, musl `1.2.6-r2`,
|
|
1419
1419
|
`systemd-efistub-260.2-r0`, `libstdc++`/`libgcc` `15.2.0-r5`, and Bun 1.4.2
|
|
1420
1420
|
`linux-x64-musl-baseline`. The aarch64 guest uses the same versions from
|
|
1421
1421
|
Alpine's aarch64 repository and Bun's `linux-aarch64-musl`. There is no BusyBox
|
|
@@ -1577,7 +1577,7 @@ The physical boot chain is firmware → the UKI stub → its embedded
|
|
|
1577
1577
|
and, with the currently pinned kernel, embeds this complete command line:
|
|
1578
1578
|
|
|
1579
1579
|
```text
|
|
1580
|
-
console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.
|
|
1580
|
+
console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.62-0-lts rdinit=/bin/bun -- -e import('/init.js')
|
|
1581
1581
|
```
|
|
1582
1582
|
|
|
1583
1583
|
Serial kernel logging is retained, while the final console makes
|
|
@@ -1601,7 +1601,7 @@ the same way). Build without `--real`; this selects Alpine `linux-virt`, omits t
|
|
|
1601
1601
|
module set, and puts `ttyS0` last so serial stdio owns `/dev/console`:
|
|
1602
1602
|
|
|
1603
1603
|
```text
|
|
1604
|
-
console=tty0 console=ttyS0,115200 panic=0 PATH=/bin KERNEL_RELEASE=6.18.
|
|
1604
|
+
console=tty0 console=ttyS0,115200 panic=0 PATH=/bin KERNEL_RELEASE=6.18.62-0-virt rdinit=/bin/bun -- -e import('/init.js')
|
|
1605
1605
|
```
|
|
1606
1606
|
|
|
1607
1607
|
Build and boot it with:
|
|
@@ -1724,7 +1724,7 @@ The default `--real` image embeds this kernel command line (shown for the
|
|
|
1724
1724
|
currently pinned LTS release):
|
|
1725
1725
|
|
|
1726
1726
|
```text
|
|
1727
|
-
console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.
|
|
1727
|
+
console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.62-0-lts rdinit=/bin/bun -- -e import('/init.js')
|
|
1728
1728
|
```
|
|
1729
1729
|
|
|
1730
1730
|
The physical display (`tty0`) is the final console and therefore owns PID 1's
|
package/README.zh.md
CHANGED
|
@@ -1415,7 +1415,7 @@ initramfs/ init.js, commands, userspace: the same for every archi
|
|
|
1415
1415
|
native/<arch>/ musl and the GCC runtime (committed); bun and modules (fetched)
|
|
1416
1416
|
```
|
|
1417
1417
|
|
|
1418
|
-
`--real` 的輸入為 Alpine v3.24 `linux-lts-6.18.
|
|
1418
|
+
`--real` 的輸入為 Alpine v3.24 `linux-lts-6.18.62-r0`、musl
|
|
1419
1419
|
`1.2.6-r2`、`systemd-efistub-260.2-r0`、`libstdc++`/`libgcc`
|
|
1420
1420
|
`15.2.0-r5`,以及 Bun 1.4.2 `linux-x64-musl-baseline`。aarch64
|
|
1421
1421
|
客體使用 Alpine aarch64 儲存庫的相同版本與 Bun 的 `linux-aarch64-musl`。
|
|
@@ -1577,7 +1577,7 @@ MiB 對齊,尾端保留 34 個磁區
|
|
|
1577
1577
|
包含 xHCI 與 USB HID 模組鏈,目前鎖定核心使用以下完整命令列:
|
|
1578
1578
|
|
|
1579
1579
|
```text
|
|
1580
|
-
console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.
|
|
1580
|
+
console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.62-0-lts rdinit=/bin/bun -- -e import('/init.js')
|
|
1581
1581
|
```
|
|
1582
1582
|
|
|
1583
1583
|
保留序列核心日誌,最後指定的主控台則讓
|
|
@@ -1601,7 +1601,7 @@ pkg install qemu-system-x86-64
|
|
|
1601
1601
|
機模組集,並將 `ttyS0` 放最後,讓序列標準輸入輸出擁有 `/dev/console`:
|
|
1602
1602
|
|
|
1603
1603
|
```text
|
|
1604
|
-
console=tty0 console=ttyS0,115200 panic=0 PATH=/bin KERNEL_RELEASE=6.18.
|
|
1604
|
+
console=tty0 console=ttyS0,115200 panic=0 PATH=/bin KERNEL_RELEASE=6.18.62-0-virt rdinit=/bin/bun -- -e import('/init.js')
|
|
1605
1605
|
```
|
|
1606
1606
|
|
|
1607
1607
|
使用以下命令建置並開機:
|
|
@@ -1724,7 +1724,7 @@ Bun 腳本。`--help` 使用 `Bun.markdown.ansi`
|
|
|
1724
1724
|
鎖定的 LTS 版本):
|
|
1725
1725
|
|
|
1726
1726
|
```text
|
|
1727
|
-
console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.
|
|
1727
|
+
console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.62-0-lts rdinit=/bin/bun -- -e import('/init.js')
|
|
1728
1728
|
```
|
|
1729
1729
|
|
|
1730
1730
|
實體螢幕(`tty0`)是最後指定的主控台,因此擁有 PID 1 的
|
package/initramfs/bin/tar
CHANGED
|
@@ -142,7 +142,7 @@ const octal = (block, offset, length) => {
|
|
|
142
142
|
};
|
|
143
143
|
|
|
144
144
|
// The header checksum is computed with its own field read as spaces. A
|
|
145
|
-
// mismatch
|
|
145
|
+
// mismatch rejects the archive instead of reporting a partial listing.
|
|
146
146
|
const checksumMatches = (block) => {
|
|
147
147
|
let sum = 0;
|
|
148
148
|
for (let index = 0; index < BLOCK; index++) {
|
|
@@ -151,16 +151,19 @@ const checksumMatches = (block) => {
|
|
|
151
151
|
return sum === octal(block, 148, 8);
|
|
152
152
|
};
|
|
153
153
|
|
|
154
|
+
// Each record's length counts bytes, so the records are cut from the bytes and decoded one by one;
|
|
155
|
+
// cutting a decoded string by that length breaks on any multi-byte name.
|
|
154
156
|
const paxRecords = (data) => {
|
|
155
|
-
const records =
|
|
157
|
+
const records = Object.create(null);
|
|
156
158
|
let offset = 0;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const length = Number
|
|
162
|
-
if (!Number.
|
|
163
|
-
|
|
159
|
+
while (offset < data.length) {
|
|
160
|
+
const space = data.indexOf(0x20, offset);
|
|
161
|
+
if (space === -1) throw new Error("invalid pax record length");
|
|
162
|
+
const digits = decoder.decode(data.subarray(offset, space));
|
|
163
|
+
const length = /^[0-9]+$/.test(digits) ? Number(digits) : NaN;
|
|
164
|
+
if (!Number.isSafeInteger(length) || length <= space - offset + 1 || length > data.length - offset
|
|
165
|
+
|| data[offset + length - 1] !== 10) throw new Error("invalid pax record length");
|
|
166
|
+
const record = decoder.decode(data.subarray(space + 1, offset + length)).replace(/\n$/, "");
|
|
164
167
|
const equals = record.indexOf("=");
|
|
165
168
|
if (equals > 0) records[record.slice(0, equals)] = record.slice(equals + 1);
|
|
166
169
|
offset += length;
|
|
@@ -174,13 +177,14 @@ const entryKinds = {
|
|
|
174
177
|
};
|
|
175
178
|
|
|
176
179
|
// Every member of the archive, in order, as { name, kind, target, size, mode,
|
|
177
|
-
// mtime }.
|
|
180
|
+
// mtime }. Reject malformed input before listing or extracting any members.
|
|
178
181
|
const scanEntries = (bytes) => {
|
|
179
182
|
const entries = [];
|
|
180
183
|
let offset = 0;
|
|
181
184
|
let longName = null;
|
|
182
185
|
let longTarget = null;
|
|
183
186
|
let extended = {};
|
|
187
|
+
let global = {};
|
|
184
188
|
let ended = false;
|
|
185
189
|
entries.concatenated = false;
|
|
186
190
|
while (offset + BLOCK <= bytes.length) {
|
|
@@ -191,13 +195,24 @@ const scanEntries = (bytes) => {
|
|
|
191
195
|
ended = true;
|
|
192
196
|
longName = longTarget = null;
|
|
193
197
|
extended = {};
|
|
198
|
+
global = {};
|
|
194
199
|
continue;
|
|
195
200
|
}
|
|
196
|
-
if (!checksumMatches(block))
|
|
201
|
+
if (!checksumMatches(block)) throw new Error("invalid tar header checksum");
|
|
197
202
|
if (ended) { entries.concatenated = true; ended = false; }
|
|
198
203
|
const flag = String.fromCharCode(block[156] || 0x30);
|
|
199
|
-
|
|
204
|
+
// Extension records use their own header size, never the preceding pax size.
|
|
205
|
+
const metadata = ["L", "K", "x", "X", "g"].includes(flag);
|
|
206
|
+
const attrs = { ...global, ...extended };
|
|
207
|
+
const digits = text(block, 124, 12).trim();
|
|
208
|
+
if (!(block[124] & 0x80) && digits && !/^[0-7]+$/.test(digits))
|
|
209
|
+
throw new Error("invalid tar member size");
|
|
210
|
+
if (!metadata && attrs.size !== undefined && !/^[0-9]+$/.test(attrs.size))
|
|
211
|
+
throw new Error("invalid pax member size");
|
|
212
|
+
const size = !metadata && attrs.size !== undefined ? Number(attrs.size) : octal(block, 124, 12);
|
|
200
213
|
const dataBlocks = Math.ceil(size / BLOCK) * BLOCK;
|
|
214
|
+
if (!Number.isSafeInteger(size) || size < 0 || !Number.isSafeInteger(dataBlocks)
|
|
215
|
+
|| dataBlocks > bytes.length - offset) throw new Error("invalid or truncated tar member size");
|
|
201
216
|
const data = bytes.subarray(offset, offset + size);
|
|
202
217
|
if (flag === "L" || flag === "K") {
|
|
203
218
|
const value = decoder.decode(data).replace(/\0+$/, "");
|
|
@@ -211,13 +226,14 @@ const scanEntries = (bytes) => {
|
|
|
211
226
|
continue;
|
|
212
227
|
}
|
|
213
228
|
if (flag === "g") {
|
|
229
|
+
global = { ...global, ...paxRecords(data) };
|
|
214
230
|
offset += dataBlocks;
|
|
215
231
|
continue;
|
|
216
232
|
}
|
|
217
|
-
const prefix = text(block, 345, 155);
|
|
233
|
+
const prefix = text(block, 257, 5) === "ustar" ? text(block, 345, 155) : "";
|
|
218
234
|
const stored = text(block, 0, 100);
|
|
219
|
-
const name =
|
|
220
|
-
const target =
|
|
235
|
+
const name = attrs.path ?? longName ?? (prefix ? `${prefix}/${stored}` : stored);
|
|
236
|
+
const target = attrs.linkpath ?? longTarget ?? text(block, 157, 100);
|
|
221
237
|
entries.push({
|
|
222
238
|
name: name.replace(/^\.\//, ""),
|
|
223
239
|
archiveName: name,
|
|
@@ -233,6 +249,7 @@ const scanEntries = (bytes) => {
|
|
|
233
249
|
extended = {};
|
|
234
250
|
offset += dataBlocks;
|
|
235
251
|
}
|
|
252
|
+
if (offset !== bytes.length) throw new Error("truncated tar header");
|
|
236
253
|
return entries;
|
|
237
254
|
};
|
|
238
255
|
|
package/initramfs/bin/unzip
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// MEMBER operands may be wildcards. Unix modes and symlinks are restored.
|
|
8
8
|
// Runs under node as well as bun.
|
|
9
9
|
|
|
10
|
-
import { chmodSync, closeSync, fstatSync, lstatSync, mkdirSync, openSync, readSync, rmSync, symlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
10
|
+
import { constants, chmodSync, closeSync, fstatSync, lstatSync, mkdirSync, openSync, readSync, rmSync, symlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
11
11
|
import { basename, dirname, join, normalize, resolve } from "node:path";
|
|
12
12
|
import { inflateRawSync } from "node:zlib";
|
|
13
13
|
import { crc32 } from "../lib/crc32.js";
|
|
@@ -61,11 +61,23 @@ if (operands.length === 0) usage();
|
|
|
61
61
|
const [archive, ...patterns] = operands;
|
|
62
62
|
|
|
63
63
|
const fd = (() => {
|
|
64
|
-
try { return openSync(archive,
|
|
64
|
+
try { return openSync(archive, constants.O_RDONLY | (constants.O_NONBLOCK ?? 0)); } catch (error) { return fail(`cannot open ${archive}: ${error.message}`, 9); }
|
|
65
65
|
})();
|
|
66
|
-
const
|
|
66
|
+
const info = fstatSync(fd);
|
|
67
|
+
if (!info.isFile()) fail(`${archive}: not a regular file`, 3);
|
|
68
|
+
const size = info.size;
|
|
69
|
+
const validSize = (value) => Number.isSafeInteger(value) && value >= 0;
|
|
70
|
+
const uint64 = (buffer, offset) => {
|
|
71
|
+
const value = buffer.readBigUInt64LE(offset);
|
|
72
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER)) fail(`${archive}: ZIP64 value exceeds safe integer range`, 3);
|
|
73
|
+
return Number(value);
|
|
74
|
+
};
|
|
67
75
|
const read = (position, length) => {
|
|
68
|
-
|
|
76
|
+
if (!validSize(position) || !validSize(length) || position > size || length > size - position)
|
|
77
|
+
fail(`${archive}: invalid or truncated archive range`, 3);
|
|
78
|
+
let buffer;
|
|
79
|
+
try { buffer = Buffer.alloc(length); }
|
|
80
|
+
catch { fail(`${archive}: cannot allocate member buffer`, 3); }
|
|
69
81
|
let done = 0;
|
|
70
82
|
while (done < length) {
|
|
71
83
|
const count = readSync(fd, buffer, done, length - done, position + done);
|
|
@@ -80,28 +92,59 @@ const tailLength = Math.min(size, 65557);
|
|
|
80
92
|
const tail = read(size - tailLength, tailLength);
|
|
81
93
|
let eocd = -1;
|
|
82
94
|
for (let offset = tailLength - 22; offset >= 0; offset--) {
|
|
83
|
-
if (tail.readUInt32LE(offset) === 0x06054b50) { eocd = offset; break; }
|
|
95
|
+
if (tail.readUInt32LE(offset) === 0x06054b50 && offset + 22 + tail.readUInt16LE(offset + 20) === tail.length) { eocd = offset; break; }
|
|
84
96
|
}
|
|
85
97
|
if (eocd < 0) fail(`${archive}: not a zip archive (no end of central directory)`, 9);
|
|
98
|
+
if (tail.readUInt16LE(eocd + 4) !== 0 || tail.readUInt16LE(eocd + 6) !== 0
|
|
99
|
+
|| tail.readUInt16LE(eocd + 8) !== tail.readUInt16LE(eocd + 10))
|
|
100
|
+
fail(`${archive}: split archives are not supported`, 3);
|
|
86
101
|
let entryCount = tail.readUInt16LE(eocd + 10);
|
|
87
102
|
let directorySize = tail.readUInt32LE(eocd + 12);
|
|
88
103
|
let directoryOffset = tail.readUInt32LE(eocd + 16);
|
|
104
|
+
let directoryLimit = size - tailLength + eocd;
|
|
89
105
|
// zip64: the locator sits just before the end of central directory record.
|
|
90
106
|
if (eocd >= 20 && tail.readUInt32LE(eocd - 20) === 0x07064b50) {
|
|
91
|
-
|
|
107
|
+
if (tail.readUInt32LE(eocd - 16) !== 0 || tail.readUInt32LE(eocd - 4) !== 1)
|
|
108
|
+
fail(`${archive}: split ZIP64 archives are not supported`, 3);
|
|
109
|
+
const recordOffset = uint64(tail, eocd - 20 + 8);
|
|
110
|
+
if (recordOffset > directoryLimit - 20 - 56) fail(`${archive}: invalid ZIP64 record range`, 3);
|
|
111
|
+
const record = read(recordOffset, 56);
|
|
92
112
|
if (record.readUInt32LE(0) !== 0x06064b50) fail(`${archive}: bad zip64 end of central directory`, 3);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
113
|
+
if (record.readUInt32LE(16) !== 0 || record.readUInt32LE(20) !== 0
|
|
114
|
+
|| uint64(record, 24) !== uint64(record, 32) || uint64(record, 4) < 44)
|
|
115
|
+
fail(`${archive}: invalid or split ZIP64 directory`, 3);
|
|
116
|
+
if (uint64(record, 4) > directoryLimit - 20 - recordOffset - 12)
|
|
117
|
+
fail(`${archive}: truncated ZIP64 record`, 3);
|
|
118
|
+
directoryLimit = recordOffset;
|
|
119
|
+
entryCount = uint64(record, 32);
|
|
120
|
+
directorySize = uint64(record, 40);
|
|
121
|
+
directoryOffset = uint64(record, 48);
|
|
96
122
|
}
|
|
97
123
|
|
|
124
|
+
// Info-ZIP writes UTF-8 names without the 0x800 flag, so a name that decodes as UTF-8 is read as
|
|
125
|
+
// UTF-8 and only the rest as the old code page's stand-in, latin1.
|
|
126
|
+
const strictUtf8 = new TextDecoder("utf-8", { fatal: true });
|
|
127
|
+
const memberName = (raw, flags) => {
|
|
128
|
+
if (flags & 0x800) return raw.toString("utf8");
|
|
129
|
+
try { return strictUtf8.decode(raw); } catch { return raw.toString("latin1"); }
|
|
130
|
+
};
|
|
131
|
+
|
|
98
132
|
const entries = [];
|
|
99
|
-
|
|
100
|
-
|
|
133
|
+
if (!validSize(entryCount) || !validSize(directorySize) || !validSize(directoryOffset)
|
|
134
|
+
|| directoryOffset > directoryLimit || directorySize > directoryLimit - directoryOffset
|
|
135
|
+
|| entryCount > Math.floor(directorySize / 46)) fail(`${archive}: invalid central directory bounds`, 3);
|
|
136
|
+
let directoryPosition = 0;
|
|
137
|
+
for (let index = 0; index < entryCount; index++) {
|
|
138
|
+
if (directorySize - directoryPosition < 46) fail(`${archive}: truncated central directory`, 3);
|
|
139
|
+
const header = read(directoryOffset + directoryPosition, 46);
|
|
140
|
+
const entryLength = 46 + header.readUInt16LE(28) + header.readUInt16LE(30) + header.readUInt16LE(32);
|
|
141
|
+
if (entryLength > directorySize - directoryPosition) fail(`${archive}: truncated central entry`, 3);
|
|
142
|
+
// Read one bounded entry instead of allocating the untrusted whole-directory size.
|
|
143
|
+
const central = read(directoryOffset + directoryPosition, entryLength), offset = 0;
|
|
144
|
+
if (central.readUInt16LE(34) !== 0) fail(`${archive}: split members are not supported`, 3);
|
|
101
145
|
if (central.readUInt32LE(offset) !== 0x02014b50) fail(`${archive}: bad central directory entry ${index}`, 3);
|
|
102
146
|
const nameLength = central.readUInt16LE(offset + 28);
|
|
103
147
|
const extraLength = central.readUInt16LE(offset + 30);
|
|
104
|
-
const commentLength = central.readUInt16LE(offset + 32);
|
|
105
148
|
const flags = central.readUInt16LE(offset + 8);
|
|
106
149
|
const entry = {
|
|
107
150
|
madeBy: central.readUInt16LE(offset + 4) >> 8,
|
|
@@ -114,23 +157,33 @@ for (let offset = 0, index = 0; index < entryCount; index++) {
|
|
|
114
157
|
size: central.readUInt32LE(offset + 24),
|
|
115
158
|
external: central.readUInt32LE(offset + 38),
|
|
116
159
|
localOffset: central.readUInt32LE(offset + 42),
|
|
117
|
-
name: central.
|
|
160
|
+
name: memberName(central.subarray(offset + 46, offset + 46 + nameLength), flags),
|
|
118
161
|
};
|
|
119
162
|
// zip64 extra field: the 0xFFFFFFFF fields follow, in this order.
|
|
120
163
|
const extra = central.subarray(offset + 46 + nameLength, offset + 46 + nameLength + extraLength);
|
|
121
|
-
|
|
164
|
+
const required64 = ["size", "compressedSize", "localOffset"].filter(key => entry[key] === 0xffffffff);
|
|
165
|
+
let found64 = false;
|
|
166
|
+
for (let position = 0; position < extra.length;) {
|
|
167
|
+
if (extra.length - position < 4) fail(`${archive}: truncated extra field`, 3);
|
|
122
168
|
const id = extra.readUInt16LE(position);
|
|
123
169
|
const length = extra.readUInt16LE(position + 2);
|
|
124
|
-
if (
|
|
170
|
+
if (length > extra.length - position - 4) fail(`${archive}: truncated extra field`, 3);
|
|
171
|
+
if (id === 0x0001 && !found64) {
|
|
172
|
+
found64 = true;
|
|
125
173
|
let field = position + 4;
|
|
126
|
-
for (const key of
|
|
127
|
-
if (
|
|
174
|
+
for (const key of required64) {
|
|
175
|
+
if (field + 8 > position + 4 + length) fail(`${archive}: truncated ZIP64 extra field`, 3);
|
|
176
|
+
entry[key] = uint64(extra, field); field += 8;
|
|
128
177
|
}
|
|
129
178
|
}
|
|
130
179
|
position += 4 + length;
|
|
131
180
|
}
|
|
181
|
+
if (required64.length && !found64) fail(`${archive}: missing ZIP64 extra field`, 3);
|
|
182
|
+
if (entry.localOffset > directoryOffset || directoryOffset - entry.localOffset < 30
|
|
183
|
+
|| entry.compressedSize > directoryOffset - entry.localOffset - 30)
|
|
184
|
+
fail(`${archive}: invalid member range`, 3);
|
|
132
185
|
entries.push(entry);
|
|
133
|
-
|
|
186
|
+
directoryPosition += entryLength;
|
|
134
187
|
}
|
|
135
188
|
|
|
136
189
|
// Bun.Glob when running under Bun; elsewhere the wildcards become a RegExp:
|
|
@@ -165,10 +218,12 @@ const data = (entry) => {
|
|
|
165
218
|
const local = read(entry.localOffset, 30);
|
|
166
219
|
if (local.readUInt32LE(0) !== 0x04034b50) throw new Error("bad local header");
|
|
167
220
|
const start = entry.localOffset + 30 + local.readUInt16LE(26) + local.readUInt16LE(28);
|
|
221
|
+
if (!validSize(start) || start > directoryOffset || entry.compressedSize > directoryOffset - start)
|
|
222
|
+
throw new Error("invalid member data range");
|
|
168
223
|
const raw = read(start, entry.compressedSize);
|
|
169
224
|
let bytes;
|
|
170
225
|
if (entry.method === 0) bytes = raw;
|
|
171
|
-
else if (entry.method === 8) bytes = inflateRawSync(raw);
|
|
226
|
+
else if (entry.method === 8) bytes = inflateRawSync(raw, { maxOutputLength: Math.max(1, entry.size) });
|
|
172
227
|
else throw new Error(`compression method ${entry.method} is not supported`);
|
|
173
228
|
if (bytes.length !== entry.size) throw new Error(`size mismatch (${bytes.length} != ${entry.size})`);
|
|
174
229
|
if (crc32(bytes) !== entry.crc) throw new Error("CRC error");
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.1 - 2026-11-08
|
|
4
|
+
|
|
5
|
+
- Sync jsgotty to 1.4.2-rc0:
|
|
6
|
+
* Add browser Kitty-frame flow control: limit unacknowledged replaceable
|
|
7
|
+
image placements to two, coalesce pending updates for the same placement,
|
|
8
|
+
and pause delivery while the page is hidden. Ordinary terminal output and
|
|
9
|
+
CLI clients keep their existing ordered path.
|
|
10
|
+
* Negotiate the capability per WebSocket and acknowledge image placement
|
|
11
|
+
after decoding, preventing a suspended browser from accumulating stale
|
|
12
|
+
frames that replay when it returns to the foreground.
|
|
13
|
+
* Add automated server/browser flow tests and manual FPS diagnostic helpers.
|
|
14
|
+
- Sync bunmsh to 0.4.2:
|
|
15
|
+
* Extend `find` with grouped expressions, negation, AND/OR precedence,
|
|
16
|
+
short-circuit actions, `-prune` and `-exec` with bounded `+` batches.
|
|
17
|
+
* Stream uncaptured results during traversal while preserving command
|
|
18
|
+
substitution, pipeline capture and redirection behavior.
|
|
19
|
+
* Retain submitted interactive lines received while waiting for terminal
|
|
20
|
+
cursor-position replies, and allow slow terminals up to 400 ms to reply.
|
|
21
|
+
|
|
3
22
|
## 0.7.0 - 2026-10-26
|
|
4
23
|
|
|
5
24
|
- Sync jsgotty to 1.4.0:
|
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
All notable user-visible changes to bunmsh are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.4.2] - 2026-11-08
|
|
6
|
+
|
|
7
|
+
### Added and fixed
|
|
8
|
+
|
|
9
|
+
- Extend builtin `find` with grouped expressions, negation, AND/OR precedence,
|
|
10
|
+
short-circuit actions and `-prune`. Support `-exec` with `;` and bounded `+`
|
|
11
|
+
batches, and report invalid expressions as command errors.
|
|
12
|
+
- Stream uncaptured `find` results during traversal while preserving command
|
|
13
|
+
substitutions, pipeline captures and stdout/stderr redirection order.
|
|
14
|
+
- Increase the interactive cursor-position query timeout from 60 ms to 400 ms
|
|
15
|
+
for slow terminals when checking output without a trailing newline.
|
|
16
|
+
- Preserve commands submitted while waiting for cursor-position replies instead
|
|
17
|
+
of losing buffered input lines.
|
|
18
|
+
|
|
19
|
+
### Tests
|
|
20
|
+
|
|
21
|
+
- Isolate builtin assertions from host utilities and descriptor-redirection
|
|
22
|
+
assertions from inherited console-color settings.
|
|
23
|
+
- Expand coverage for `find` expressions, execution, streaming and capture
|
|
24
|
+
isolation, plus immediate, delayed and missing terminal cursor replies.
|
|
25
|
+
|
|
5
26
|
## [0.4.1] - 2026-10-13
|
|
6
27
|
|
|
7
28
|
- Fixed awk bypassing linker protection
|