Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.ja-JP.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ sudo reboot
| `--cloud-kernel` | *標準* | クラウド最適化カーネルを使用 |
| `--bpo-kernel` | *stable* | backportsから新しいカーネルを使用 |
| `--firmware` | *自動検出* | ハードウェア用のnon-freeファームウェアを含める |
| `--hyperv-pci-hotfix` | *無効* | インストーラー内核に一致する Debian kernel パッケージをダウンロードして initrd に組み込み、インストーラー起動初期に `pci-hyperv.ko` を読み込む(Azure NVMe / Hyper-V vPCI のディスク検出向け) |

### 詳細オプション

Expand Down Expand Up @@ -321,6 +322,12 @@ sudo ./debi.sh --ip YOUR_IP/CIDR --gateway YOUR_GATEWAY
sudo ./debi.sh --firmware
```

**インストーラーで Azure NVMe ディスクを検出できない場合(Hyper-V vPCI):**

```bash
sudo ./debi.sh --hyperv-pci-hotfix
```

**インストールのデバッグ:**

```bash
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ sudo reboot
| `--cloud-kernel` | *standard* | Use cloud-optimized kernel |
| `--bpo-kernel` | *stable* | Use newer kernel from backports |
| `--firmware` | *auto-detect* | Include non-free firmware for hardware |
| `--hyperv-pci-hotfix` | *disabled* | Download the matching Debian kernel package, embed it in the installer initrd, and load `pci-hyperv.ko` during installer startup (useful for Azure NVMe/Hyper-V vPCI disk detection) |

### Advanced Options
| Option | Default | Description |
Expand Down Expand Up @@ -294,6 +295,11 @@ sudo ./debi.sh --ip YOUR_IP/CIDR --gateway YOUR_GATEWAY
sudo ./debi.sh --firmware
```

**Azure NVMe disk not detected in installer (Hyper-V vPCI):**
```bash
sudo ./debi.sh --hyperv-pci-hotfix
```

**Installation debugging:**
```bash
# Generate preseed file only
Expand Down
8 changes: 7 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ sudo reboot
| `--cloud-kernel` | *标准内核* | 使用为云环境优化的内核 |
| `--bpo-kernel` | *稳定版内核* | 使用来自 backports 的较新内核 |
| `--firmware` | *自动检测* | 为硬件安装 non-free 固件 |
| `--hyperv-pci-hotfix` | *禁用* | 下载与安装器内核匹配的 Debian kernel 包,将其写入安装器 initrd,并在安装器启动早期加载 `pci-hyperv.ko`(适用于 Azure NVMe / Hyper-V vPCI 磁盘识别) |

### 高级选项

Expand Down Expand Up @@ -323,6 +324,12 @@ sudo ./debi.sh --ip YOUR_IP/CIDR --gateway YOUR_GATEWAY
sudo ./debi.sh --firmware
```

**Azure NVMe 磁盘在安装器中无法识别(Hyper-V vPCI):**

```bash
sudo ./debi.sh --hyperv-pci-hotfix
```

**安装过程调试:**

```bash
Expand Down Expand Up @@ -354,4 +361,3 @@ sudo ./debi.sh --network-console --authorized-keys-url YOUR_KEYS_URL
*作者 [@bohanyang](https://github.com/bohanyang) • [问题反馈](https://github.com/bohanyang/debi/issues) • [GitHub 仓库](https://github.com/bohanyang/debi)*

</div>

119 changes: 119 additions & 0 deletions debi.sh
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,120 @@ download() {
fi
}

find_kernel_package() {
local package_name filename

package_name="linux-image-$1"
filename=$(
gzip -dc "$2" | awk -v pkg="$package_name" '
BEGIN { RS = ""; FS = "\n" }
$1 == "Package: " pkg {
for (i = 1; i <= NF; i++) {
if ($i ~ /^Filename: /) {
sub(/^Filename: /, "", $i)
print $i
exit
}
}
}
'
)
[ -n "$filename" ] && {
printf '%s\n' "$filename"
return 0
}

package_name="linux-image-$1-unsigned"
filename=$(
gzip -dc "$2" | awk -v pkg="$package_name" '
BEGIN { RS = ""; FS = "\n" }
$1 == "Package: " pkg {
for (i = 1; i <= NF; i++) {
if ($i ~ /^Filename: /) {
sub(/^Filename: /, "", $i)
print $i
exit
}
}
}
'
)
[ -n "$filename" ] && printf '%s\n' "$filename"
Comment on lines +93 to +130
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

find_kernel_package runs the same gzip|awk scan twice (for signed and unsigned). This can be simplified by iterating over candidate package names in a loop and reusing the same extraction logic, which will make the function easier to maintain and slightly reduce work on large Packages.gz files.

Suggested change
local package_name filename
package_name="linux-image-$1"
filename=$(
gzip -dc "$2" | awk -v pkg="$package_name" '
BEGIN { RS = ""; FS = "\n" }
$1 == "Package: " pkg {
for (i = 1; i <= NF; i++) {
if ($i ~ /^Filename: /) {
sub(/^Filename: /, "", $i)
print $i
exit
}
}
}
'
)
[ -n "$filename" ] && {
printf '%s\n' "$filename"
return 0
}
package_name="linux-image-$1-unsigned"
filename=$(
gzip -dc "$2" | awk -v pkg="$package_name" '
BEGIN { RS = ""; FS = "\n" }
$1 == "Package: " pkg {
for (i = 1; i <= NF; i++) {
if ($i ~ /^Filename: /) {
sub(/^Filename: /, "", $i)
print $i
exit
}
}
}
'
)
[ -n "$filename" ] && printf '%s\n' "$filename"
local package_base package_name filename
package_base="linux-image-$1"
for suffix in "" "-unsigned"; do
package_name="${package_base}${suffix}"
filename=$(
gzip -dc "$2" | awk -v pkg="$package_name" '
BEGIN { RS = ""; FS = "\n" }
$1 == "Package: " pkg {
for (i = 1; i <= NF; i++) {
if ($i ~ /^Filename: /) {
sub(/^Filename: /, "", $i)
print $i
exit
}
}
}
'
)
if [ -n "$filename" ]; then
printf '%s\n' "$filename"
return 0
fi
done

Copilot uses AI. Check for mistakes.
}

find_installer_kernel_version() {
cpio -it -F "$1" 2> /dev/null |
sed -n 's#^lib/modules/\([^/]*\)/.*#\1#p' |
sort -u |
head -n 1
}

inject_hyperv_pci_hotfix() {
local initrd_file installer_kernel_version packages_url package_path tmpdir

initrd_file=$1
installer_kernel_version=$(find_installer_kernel_version "$initrd_file")
[ -n "$installer_kernel_version" ] ||
err "Could not determine the installer kernel version from $initrd_file"

tmpdir=$(mktemp -d)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inject_hyperv_pci_hotfix creates a temp directory but relies on manual rm -rf calls. With set -e enabled, any failing command (e.g. a download error) will exit the script and leave $tmpdir behind. Add a trap after mktemp -d to ensure the temp directory is always cleaned up on EXIT/INT/TERM.

Suggested change
tmpdir=$(mktemp -d)
tmpdir=$(mktemp -d)
trap "rm -rf \"$tmpdir\"" EXIT INT TERM

Copilot uses AI. Check for mistakes.
packages_url="$mirror_protocol://$mirror_host$mirror_directory/dists/$suite/main/binary-$architecture/Packages.gz"
download "$packages_url" "$tmpdir/Packages.gz"

package_path=$(find_kernel_package "$installer_kernel_version" "$tmpdir/Packages.gz" || true)
[ -n "$package_path" ] || {
rm -rf "$tmpdir"
err "Could not find a Debian kernel package for installer kernel $installer_kernel_version"
}

mkdir -p \
"$tmpdir/hotfix/debi-hyperv-pci" \
"$tmpdir/hotfix/lib/debian-installer-startup.d"
download "$mirror_protocol://$mirror_host$mirror_directory/$package_path" "$tmpdir/hotfix/debi-hyperv-pci/kernel.deb"
chmod 0644 "$tmpdir/hotfix/debi-hyperv-pci/kernel.deb"

cat > "$tmpdir/hotfix/lib/debian-installer-startup.d/S25pci-hyperv-hotfix" << EOF
#!/bin/sh
set -eu

kernel_version=$installer_kernel_version
Comment on lines +164 to +168
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

installer_kernel_version comes from the downloaded initrd contents and is interpolated directly into the generated /lib/debian-installer-startup.d/... shell script without quoting/sanitization. If it contains unexpected characters, this can break the hook or (with a malicious initrd/mirror) lead to shell injection. Please validate it against an allowlist (e.g. [A-Za-z0-9.+-]) and/or write it as a safely quoted shell literal in the heredoc.

Suggested change
cat > "$tmpdir/hotfix/lib/debian-installer-startup.d/S25pci-hyperv-hotfix" << EOF
#!/bin/sh
set -eu
kernel_version=$installer_kernel_version
case ${installer_kernel_version-} in
''|*[!A-Za-z0-9.+-]*)
err "Invalid installer kernel version: ${installer_kernel_version-}"
;;
esac
cat > "$tmpdir/hotfix/lib/debian-installer-startup.d/S25pci-hyperv-hotfix" << EOF
#!/bin/sh
set -eu
kernel_version='${installer_kernel_version}'

Copilot uses AI. Check for mistakes.
module_path="/lib/modules/\$kernel_version/kernel/drivers/pci/controller/pci-hyperv.ko"
package_path=/debi-hyperv-pci/kernel.deb
tmpdir=

cleanup() {
[ -n "\$tmpdir" ] && rm -rf "\$tmpdir"
}

trap cleanup EXIT INT TERM

modprobe pci_hyperv_intf 2> /dev/null || true

if [ ! -f "\$module_path" ]; then
tmpdir=\$(mktemp -d /tmp/debi-hv-pci.XXXXXX) || exit 0
data_member=\$(ar t "\$package_path" 2> /dev/null | awk '/^data\\.tar\\./ { print; exit }')
ar p "\$package_path" "\$data_member" > "\$tmpdir/\$data_member" 2> /dev/null || exit 0
mkdir -p "\$tmpdir/extract" "\$(dirname "\$module_path")"
xzcat "\$tmpdir/\$data_member" | tar -xf - -C "\$tmpdir/extract" "./lib/modules/\$kernel_version/kernel/drivers/pci/controller/pci-hyperv.ko" 2> /dev/null || exit 0
Comment on lines +182 to +186
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the installer startup hook, data_member is matched as data.tar.* but the extraction always uses xzcat. If the .deb uses data.tar.zst or data.tar.gz, the hotfix will silently do nothing. Consider detecting the data_member suffix and using the matching decompressor (xz/gz/zstd), or constrain the match to data.tar.xz and error clearly otherwise.

Copilot uses AI. Check for mistakes.
cp -f "\$tmpdir/extract/lib/modules/\$kernel_version/kernel/drivers/pci/controller/pci-hyperv.ko" "\$module_path" || exit 0
chmod 0644 "\$module_path" || exit 0
fi

modprobe pci_hyperv 2> /dev/null || insmod "\$module_path" 2> /dev/null || true
EOF
chmod 0755 "$tmpdir/hotfix/lib/debian-installer-startup.d/S25pci-hyperv-hotfix"

(
cd "$tmpdir/hotfix"
find debi-hyperv-pci lib | cpio -o -H newc -A -F "$initrd_file" > /dev/null 2>&1
) || {
rm -rf "$tmpdir"
err 'Could not append the Hyper-V PCI hotfix to the installer initrd'
}

rm -rf "$tmpdir"
}

# Set "$mirror_proxy" with "$http/https/ftp_proxy"
# only when it is empty and one of those is not empty
set_mirror_proxy() {
Expand Down Expand Up @@ -268,6 +382,7 @@ hold=false
power_off=false
architecture=
firmware=false
hyperv_pci_hotfix=false
force_efi_extra_removable=true
grub_timeout=5
dry_run=false
Expand Down Expand Up @@ -524,6 +639,9 @@ while [ $# -gt 0 ]; do
--firmware)
firmware=true
;;
--hyperv-pci-hotfix)
hyperv_pci_hotfix=true
;;
--no-force-efi-extra-removable)
force_efi_extra_removable=false
;;
Expand Down Expand Up @@ -948,6 +1066,7 @@ save_grub_cfg='cat'
[ "$firmware" = true ] && download "$firmware_url" firmware.cpio.gz

gzip -d initrd.gz
[ "$hyperv_pci_hotfix" = true ] && inject_hyperv_pci_hotfix "$PWD/initrd"
# cpio reads a list of file names from the standard input
echo preseed.cfg | cpio -o -H newc -A -F initrd

Expand Down
Loading