Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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