From d296082c822499b64720558cd56bd1cca67bac6e Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Mon, 8 Dec 2025 13:09:21 -0500 Subject: [PATCH] Replace fdisk -l with sysfs-based disk enumeration for better busybox compatibility fdisk -l is unreliable in busybox environments due to 2TB max size based on max sector assumption. Changes: - Add list_block_devices() helper function to initrd/etc/functions and initrd/etc/gui_functions that uses sysfs to enumerate all block devices (SATA, NVMe, VirtIO, IDE) - Update show_system_info() in initrd/etc/gui_functions to use sysfs for disk size reporting instead of parsing fdisk output - Update show_system_info() in initrd/bin/oem-system-info-xx30 to use sysfs for disk size reporting - Replace device_has_partitions() in initrd/etc/functions to check for partition entries in sysfs instead of parsing fdisk output - Replace is_gpt_bios_grub() in initrd/etc/functions to use sysfs partition attributes (PARTTYPENAME) instead of fdisk parsing. Improves reliability for GPT disk detection while maintaining backward compatibility. - Update detect_boot_device() in initrd/etc/functions to use list_block_devices() - Update boot device selection in initrd/bin/config-gui.sh to use list_block_devices() - Update root device selection in initrd/bin/config-gui.sh to use list_block_devices() - Update root device detection in initrd/bin/root-hashes-gui.sh to use list_block_devices() Benefits: - Fixes disk detection failures with virtio block devices (qcow2 disks) - Works reliably in busybox environments - More robust than fdisk output parsing - Supports all block device types (sd*, nvme*, vd*, hd*) - Improves debuggability with explicit logging Note: Interactive partitioning in initrd/etc/luks-functions still uses fdisk for actually writing partition tables, which is its legitimate use case. Signed-off-by: Thierry Laurion --- initrd/bin/config-gui.sh | 6 +- initrd/bin/oem-system-info-xx30 | 6 +- initrd/bin/root-hashes-gui.sh | 2 +- initrd/etc/functions | 104 +++++++++++++++++++++++++------- initrd/etc/gui_functions | 6 +- 5 files changed, 96 insertions(+), 28 deletions(-) diff --git a/initrd/bin/config-gui.sh b/initrd/bin/config-gui.sh index b741bf71d..c5763803a 100755 --- a/initrd/bin/config-gui.sh +++ b/initrd/bin/config-gui.sh @@ -119,8 +119,9 @@ while true; do exit 0 ;; "b") + #Change /boot device CURRENT_OPTION="$(load_config_value CONFIG_BOOT_DEV)" - if ! fdisk -l 2>/dev/null | grep "Disk /dev/" | cut -f2 -d " " | cut -f1 -d ":" >/tmp/disklist.txt; then + if ! list_block_devices >/tmp/disklist.txt; then whiptail_error --title 'ERROR: No bootable devices found' \ --msgbox " $ERROR\n\n" 0 80 exit 1 @@ -214,8 +215,9 @@ while true; do fi ;; "R") + #Change the root device for hashing CURRENT_OPTION="$(load_config_value CONFIG_ROOT_DEV)" - fdisk -l 2>/dev/null | grep "Disk /dev/" | cut -f2 -d " " | cut -f1 -d ":" >/tmp/disklist.txt + list_block_devices >/tmp/disklist.txt # filter out extraneous options >/tmp/root_device_list.txt for i in $(cat /tmp/disklist.txt); do diff --git a/initrd/bin/oem-system-info-xx30 b/initrd/bin/oem-system-info-xx30 index 41f57d278..d93990cfa 100755 --- a/initrd/bin/oem-system-info-xx30 +++ b/initrd/bin/oem-system-info-xx30 @@ -50,5 +50,9 @@ kernel=$(uname -s -r) FB_OPTIONS="" if whiptail --version | grep "fbwhiptail"; then FB_OPTIONS="--text-size 12"; fi + +# Build disk information using shared sysfs helper +disk_info="$(disk_info_sysfs)" + whiptail_type $BG_COLOR_MAIN_MENU $FB_OPTIONS --title 'System Info' \ - --msgbox "${BOARD_NAME}\nFW_VER: ${FW_VER}\nKernel: ${kernel}\nCPU: ${cpustr} RAM: ${memtotal} GB $battery_status\n$(fdisk -l | grep -e '/dev/sd.:' -e '/dev/nvme.*:' | sed 's/B,.*/B/')\n\n$(cat /tmp/devices_usb_pci)" 0 80 + --msgbox "${BOARD_NAME}\nFW_VER: ${FW_VER}\nKernel: ${kernel}\nCPU: ${cpustr} RAM: ${memtotal} GB $battery_status\n${disk_info}\n$(cat /tmp/devices_usb_pci)" 0 80 diff --git a/initrd/bin/root-hashes-gui.sh b/initrd/bin/root-hashes-gui.sh index 67881f554..9b5ac15ce 100755 --- a/initrd/bin/root-hashes-gui.sh +++ b/initrd/bin/root-hashes-gui.sh @@ -367,7 +367,7 @@ detect_root_device() fi # generate list of possible boot devices - fdisk -l 2>/dev/null | grep "Disk /dev/" | cut -f2 -d " " | cut -f1 -d ":" > /tmp/disklist + list_block_devices > /tmp/disklist # filter out extraneous options > /tmp_root_device_list diff --git a/initrd/etc/functions b/initrd/etc/functions index fb5e35d95..efaf002dd 100644 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -659,24 +659,77 @@ enable_usb_storage() { device_has_partitions() { local DEVICE="$1" - # fdisk normally says "doesn't contain a valid partition table" for - # devices that lack a partition table - except for FAT32. - # - # FAT32 devices have a volume boot record that looks enough like an MBR - # to satisfy fdisk. In that case, fdisk prints a partition table header - # but no partitions. - # - # This check covers that: [ $(fdisk -l "$b" | wc -l) -eq 5 ] - # In both cases the output is 5 lines: 3 about device info, 1 empty line - # and the 5th will be the table header or the invalid message. - local DISK_DATA=$(fdisk -l "$DEVICE" 2>/dev/null) - if echo "$DISK_DATA" | grep -q "doesn't contain a valid partition table" || - [ "$(echo "$DISK_DATA" | wc -l)" -eq 5 ]; then - # No partition table + # Check if device has any partitions by checking sysfs + # A device with partitions will have entries like /sys/block/sda/sda1, /sys/block/vda/vda1, etc + local device_name=$(basename "$DEVICE") + + if [ ! -e "/sys/block/$device_name" ]; then + # Device doesn't exist return 1 fi - # There is a partition table - return 0 + + # Count partitions - handle nvme partition naming (nvme0n1p1) and traditional (sda1, vda1, hda1) + if [ -n "$(ls -1 /sys/block/$device_name/ 2>/dev/null | grep -E "^${device_name}(p)?[0-9]")" ]; then + # There is a partition table + return 0 + fi + # No partition table + return 1 +} + +# List all block devices using sysfs +# Outputs one device path per line (e.g., /dev/sda, /dev/vda, /dev/nvme0n1) +list_block_devices() { + TRACE_FUNC + for dev in /sys/block/sd* /sys/block/nvme* /sys/block/vd* /sys/block/hd*; do + if [ -e "$dev" ]; then + echo "/dev/$(basename "$dev")" + fi + done | sort +} + +# Build displayable disk information using sysfs (vs current busybox's 2tb limit per https://bugs.busybox.net/show_bug.cgi?id=16276) +# Output format matches prior fdisk parsing: "Disk /dev/: GB" per line +disk_info_sysfs() { + TRACE_FUNC + local disk_info="" + DEBUG "disk_info_sysfs: Starting disk enumeration" + for dev in /sys/block/sd* /sys/block/nvme* /sys/block/vd* /sys/block/hd*; do + if [ -e "$dev" ]; then + local devname=$(basename "$dev") + DEBUG "disk_info_sysfs: Found device $dev (devname=$devname)" + local size_bytes=$(cat "$dev/size" 2>/dev/null) + DEBUG "disk_info_sysfs: /dev/${devname} reports size_bytes=$size_bytes sectors from $dev/size" + if [ -n "$size_bytes" ] && [ "$size_bytes" -gt 0 ]; then + # Determine sector size for this device from sysfs; prefer hw_sector_size + local sector_size=512 + if [ -r "$dev/queue/hw_sector_size" ]; then + sector_size=$(cat "$dev/queue/hw_sector_size" 2>/dev/null || echo 512) + DEBUG "disk_info_sysfs: /dev/${devname} using hw_sector_size=$sector_size from $dev/queue/hw_sector_size" + elif [ -r "$dev/queue/logical_block_size" ]; then + sector_size=$(cat "$dev/queue/logical_block_size" 2>/dev/null || echo 512) + DEBUG "disk_info_sysfs: /dev/${devname} using logical_block_size=$sector_size from $dev/queue/logical_block_size" + else + DEBUG "disk_info_sysfs: /dev/${devname} using default sector_size=$sector_size (no queue info available)" + fi + # Convert from sectors * sector_size to bytes, then to decimal GB. + # + # Explanation of the constants: + # - We divide by 1,000,000,000 (1000000000) to report decimal GB (10^9 bytes), + # which matches the historical output used elsewhere in Heads. + # - Shell arithmetic is integer-only, so to round to the nearest GB we add + # half the divisor (500,000,000) before dividing. This implements + # rounding instead of truncation. + # If you prefer binary GiB (1024^3), replace the divisor and half-divisor + # with 1073741824 and 536870912 respectively, or use `awk`/`printf` + # for fractional output. + local size_gb=$(((size_bytes * sector_size + 500000000) / 1000000000)) + disk_info="${disk_info}Disk /dev/${devname}: ${size_gb} GB\n" + DEBUG "disk_info_sysfs: /dev/${devname} calculated as ${size_gb} GB (${size_bytes} sectors * ${sector_size} bytes/sector = $((size_bytes * sector_size)) bytes)" + fi + fi + done + echo -n "$disk_info" } list_usb_storage() { @@ -1087,12 +1140,19 @@ is_gpt_bios_grub() { NUMBER="${BASH_REMATCH[1]}" - # Now we know the device and partition number, get the type. This is - # specific to GPT disks, MBR disks are shown differently by fdisk. - TRACE "$PART_DEV is partition $NUMBER of $DEVICE" - if [ "$(fdisk -l "/dev/$DEVICE" 2>/dev/null | awk '$1 == '"$NUMBER"' {print $5}')" == grub ]; then - return 0 + # Check partition type using sysfs if available, otherwise check for grub type + # For GPT disks, check /sys/class/block///uevent for PARTTYPENAME + local part_sys="/sys/class/block/$DEVICE/$(basename "$PART_DEV")" + if [ -e "$part_sys/uevent" ]; then + if grep -q "PARTTYPENAME=BIOS boot" "$part_sys/uevent"; then + TRACE "$PART_DEV is a GPT BIOS grub partition" + return 0 + fi fi + + # Fallback: try to detect using other sysfs attributes if available + # For MBR disks, we would need to check partition type differently + DEBUG "$PART_DEV - unable to confirm it's a bios-grub partition via sysfs" return 1 } @@ -1163,7 +1223,7 @@ detect_boot_device() { fi # generate list of possible boot devices - fdisk -l 2>/dev/null | grep "Disk /dev/" | cut -f2 -d " " | cut -f1 -d ":" >/tmp/disklist + list_block_devices >/tmp/disklist # Check each possible boot device for i in $(cat /tmp/disklist); do diff --git a/initrd/etc/gui_functions b/initrd/etc/gui_functions index b321352fe..6739eaf03 100755 --- a/initrd/etc/gui_functions +++ b/initrd/etc/gui_functions @@ -172,6 +172,9 @@ show_system_info() { cpustr=$(cat /proc/cpuinfo | grep 'model name' | uniq | sed -r 's/\(R\)//;s/\(TM\)//;s/CPU //;s/model name.*: //') kernel=$(uname -s -r) + # Get disk information using the shared sysfs helper from functions + local disk_info="$(disk_info_sysfs)" + local msgbox="${BOARD_NAME} FW_VER: ${FW_VER} @@ -181,8 +184,7 @@ show_system_info() { Microcode: $(cat /proc/cpuinfo | grep microcode | uniq | cut -d':' -f2 | tr -d ' ') RAM: ${memtotal} GB $battery_status - $(fdisk -l 2>/dev/null | grep -e '/dev/sd.:' -e '/dev/nvme.*:' | sed 's/B,.*/B/') - " + ${disk_info}" local msgbox_rm_tabs=$(echo "$msgbox" | tr -d "\t")