added bios support

This commit is contained in:
robojerk 2025-08-28 12:11:37 -07:00
parent ceb06d340a
commit 1de1b781e5

View file

@ -21,6 +21,7 @@ EFI_MOUNT_POINT="/mnt/boot/efi"
GRUB_CFG_PATH="/mnt/boot/grub/grub.cfg"
BACKUP_DIR="/tmp/grub-backup-$(date +%Y%m%d-%H%M%S)"
LOG_FILE=$(mktemp "/tmp/grub-repair-$(date +%Y%m%d-%H%M%S).XXXXXX.log" 2>/dev/null || echo "/tmp/grub-repair-$(date +%Y%m%d-%H%M%S).log")
EFI_PARTITION_PATH="" # Store detected EFI partition for reuse
# Function to print colored output
print_status() {
@ -36,6 +37,11 @@ Usage: $SCRIPT_NAME [OPTIONS] COMMAND
GRUB and EFI Repair Script for Live ISO Recovery
Features:
- Automatically detects UEFI vs BIOS boot mode
- Installs appropriate GRUB target (x86_64-efi or i386-pc)
- Supports modern partition layouts (Fedora-style: /boot/efi, /boot, /)
OPTIONS:
-h, --help Show this help message
-v, --verbose Enable verbose output
@ -76,6 +82,51 @@ EXAMPLES:
EOF
}
# Function to validate partition number
validate_partition() {
local partition="$1"
# Check if partition is numeric
if [[ ! "$partition" =~ ^[0-9]+$ ]]; then
print_status "$RED" "Error: Partition number must be a positive integer: $partition"
exit 1
fi
# Check if partition number is reasonable (1-128)
if [[ "$partition" -lt 1 || "$partition" -gt 128 ]]; then
print_status "$RED" "Error: Partition number must be between 1 and 128: $partition"
exit 1
fi
echo "$partition"
}
# Function to sanitize user input paths
sanitize_path() {
local path="$1"
local description="$2"
# Check if path is absolute
if [[ "$path" != /* ]]; then
print_status "$RED" "Error: $description must be an absolute path: $path"
exit 1
fi
# Check for dangerous path components
if [[ "$path" == *".."* ]]; then
print_status "$RED" "Error: $description contains dangerous path components: $path"
exit 1
fi
# Check for null bytes or other dangerous characters
if [[ "$path" == *$'\0'* ]]; then
print_status "$RED" "Error: $description contains null bytes: $path"
exit 1
fi
echo "$path"
}
# Function to check if running as root
check_root() {
if [[ $EUID -ne 0 ]]; then
@ -101,10 +152,27 @@ check_live_iso() {
fi
}
# Function to detect boot mode
detect_boot_mode() {
# Check if we're running in UEFI mode
if [[ -d "/sys/firmware/efi" ]]; then
echo "uefi"
return 0
else
echo "bios"
return 0
fi
}
# Function to detect available disks and partitions
detect_systems() {
print_status "$BLUE" "Detecting available disks and partitions..."
# Show boot mode
local boot_mode=$(detect_boot_mode)
echo "Boot mode: $boot_mode"
echo ""
echo "Available disks:"
lsblk -d -o NAME,SIZE,TYPE,MOUNTPOINT
@ -214,7 +282,7 @@ detect_root_partition() {
local full_path="/dev/$partition"
if [[ -b "$full_path" ]]; then
local fstype=$(echo "$blkid_output" | grep "$full_path:" | grep -o 'TYPE="[^"]*"' | cut -d'"' -f2)
if [[ "$fstype" == "ext4" || "$fstype" == "xfs" || "$fstype" == "btrfs" ]]; then
if [[ "$fstype" == "ext4" || "$fstype" == "xfs" || "$fstype" == "btrfs" || "$fstype" == "zfs" || "$fstype" == "f2fs" ]]; then
root_partitions="$root_partitions $full_path"
fi
fi
@ -268,7 +336,7 @@ detect_root_partition() {
local test_partition="${disk}${part_num}"
if [[ -b "$test_partition" ]]; then
local fstype=$(echo "$blkid_output" | grep "$test_partition:" | grep -o 'TYPE="[^"]*"' | cut -d'"' -f2)
if [[ "$fstype" == "ext4" || "$fstype" == "xfs" || "$fstype" == "btrfs" ]]; then
if [[ "$fstype" == "ext4" || "$fstype" == "xfs" || "$fstype" == "btrfs" || "$fstype" == "zfs" || "$fstype" == "f2fs" ]]; then
echo "$test_partition"
return 0
fi
@ -380,7 +448,7 @@ mount_system() {
# (e.g., Fedora-style: EFI=1, Boot=2, Root=3), so we need to be smart
# about detecting which partition is actually the root filesystem
local fstype=$(blkid -s TYPE -o value "$partition_device" 2>/dev/null)
if [[ "$fstype" != "ext4" && "$fstype" != "xfs" && "$fstype" != "btrfs" ]]; then
if [[ "$fstype" != "ext4" && "$fstype" != "xfs" && "$fstype" != "btrfs" && "$fstype" != "zfs" && "$fstype" != "f2fs" ]]; then
print_status "$YELLOW" "Warning: Partition $partition_device has filesystem type '$fstype', which may not be a root partition"
print_status "$BLUE" "Attempting to detect the actual root partition..."
@ -440,6 +508,18 @@ mount_system() {
exit 1
fi
# Mount /dev/shm for shared memory access
if ! mount --bind /dev/shm "$MOUNT_POINT/dev/shm"; then
print_status "$YELLOW" "Warning: Failed to mount /dev/shm, continuing without it"
# Note: /dev/shm is optional but recommended for full chroot functionality
fi
# Mount /run for runtime data
if ! mount --bind /run "$MOUNT_POINT/run"; then
print_status "$YELLOW" "Warning: Failed to mount /run, continuing without it"
# Note: /run is optional but recommended for full chroot functionality
fi
# Mount EFI partition if it exists
local efi_partition=$(detect_efi_partition "$device")
if [[ -n "$efi_partition" && -b "$efi_partition" ]]; then
@ -456,6 +536,8 @@ mount_system() {
umount "$MOUNT_POINT" 2>/dev/null || true
exit 1
fi
# Store EFI partition path for potential reuse by other functions
EFI_PARTITION_PATH="$efi_partition"
else
print_status "$YELLOW" "Warning: EFI partition not found, some operations may fail"
fi
@ -527,6 +609,14 @@ unmount_system() {
print_status "$GREEN" "Boot partition unmounted"
fi
if mountpoint -q "$MOUNT_POINT/run"; then
umount "$MOUNT_POINT/run"
fi
if mountpoint -q "$MOUNT_POINT/dev/shm"; then
umount "$MOUNT_POINT/dev/shm"
fi
if mountpoint -q "$MOUNT_POINT/dev/pts"; then
umount "$MOUNT_POINT/dev/pts"
fi
@ -581,16 +671,99 @@ install_grub() {
exit 1
fi
# Detect boot mode
local boot_mode=$(detect_boot_mode)
print_status "$BLUE" "Detected boot mode: $boot_mode"
# Detect distribution for appropriate bootloader ID
local bootloader_id="grub"
local bootloader_label="GRUB"
if [[ -d "$MOUNT_POINT/etc" ]]; then
local detected_distro=$(detect_distribution "$MOUNT_POINT")
case "$detected_distro" in
*ubuntu*)
bootloader_id="ubuntu"
bootloader_label="Ubuntu"
;;
*fedora*|*redhat*|*centos*)
bootloader_id="fedora"
bootloader_label="Fedora/RHEL"
;;
*arch*)
bootloader_id="arch"
bootloader_label="Arch Linux"
;;
*debian*)
bootloader_id="debian"
bootloader_label="Debian"
;;
*)
# Use generic GRUB ID for other distributions
bootloader_id="grub"
bootloader_label="GRUB"
;;
esac
print_status "$BLUE" "Detected distribution: $detected_distro, using bootloader ID: $bootloader_id"
fi
# Chroot into the system
print_status "$BLUE" "Chrooting into system to install GRUB..."
# Install GRUB to the mounted system
if chroot "$MOUNT_POINT" grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=grub --recheck; then
print_status "$GREEN" "GRUB installed successfully"
else
print_status "$RED" "Error: GRUB installation failed"
# Install GRUB based on boot mode
if [[ "$boot_mode" == "uefi" ]]; then
# Check if EFI partition is mounted
if [[ ! -d "$EFI_MOUNT_POINT" ]] || ! mountpoint -q "$EFI_MOUNT_POINT"; then
print_status "$RED" "Error: EFI partition not mounted. Required for UEFI GRUB installation."
exit 1
fi
print_status "$BLUE" "Installing GRUB for UEFI mode with bootloader ID: $bootloader_id"
if chroot "$MOUNT_POINT" grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id="$bootloader_id" --recheck 2>&1; then
print_status "$GREEN" "GRUB installed successfully for UEFI mode"
else
print_status "$RED" "Error: UEFI GRUB installation failed"
print_status "$YELLOW" "Run 'chroot $MOUNT_POINT grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=$bootloader_id --recheck' manually for detailed error output"
exit 1
fi
else
# BIOS mode - install to MBR
print_status "$BLUE" "Installing GRUB for BIOS mode..."
# Get the disk device (remove partition number)
local disk_device=""
if command -v lsblk >/dev/null 2>&1; then
# Find the root partition and get its parent disk
local root_partition=$(findmnt -n -o SOURCE "$MOUNT_POINT" 2>/dev/null | head -1)
if [[ -n "$root_partition" ]]; then
disk_device=$(lsblk -rno PKNAME "$root_partition" 2>/dev/null | head -1)
if [[ -n "$disk_device" ]]; then
disk_device="/dev/$disk_device"
fi
fi
fi
# Fallback: extract disk from partition path string
if [[ -z "$disk_device" ]]; then
local root_partition=$(findmnt -n -o SOURCE "$MOUNT_POINT" 2>/dev/null | head -1)
if [[ -n "$root_partition" ]]; then
disk_device="${root_partition%[0-9]*}"
fi
fi
if [[ -z "$disk_device" ]]; then
print_status "$RED" "Error: Could not determine disk device for BIOS GRUB installation"
exit 1
fi
print_status "$BLUE" "Installing GRUB to disk: $disk_device"
if chroot "$MOUNT_POINT" grub-install --target=i386-pc --recheck "$disk_device" 2>&1; then
print_status "$GREEN" "GRUB installed successfully for BIOS mode"
else
print_status "$RED" "Error: BIOS GRUB installation failed"
print_status "$YELLOW" "Run 'chroot $MOUNT_POINT grub-install --target=i386-pc --recheck $disk_device' manually for detailed error output"
exit 1
fi
fi
}
# Function to update GRUB configuration
@ -605,10 +778,11 @@ update_grub() {
fi
# Update GRUB configuration
if chroot "$MOUNT_POINT" grub-mkconfig -o /boot/grub/grub.cfg; then
if chroot "$MOUNT_POINT" grub-mkconfig -o /boot/grub/grub.cfg 2>&1; then
print_status "$GREEN" "GRUB configuration updated successfully"
else
print_status "$RED" "Error: GRUB configuration update failed"
print_status "$YELLOW" "Run 'chroot $MOUNT_POINT grub-mkconfig -o /boot/grub/grub.cfg' manually for detailed error output"
exit 1
fi
}
@ -616,6 +790,7 @@ update_grub() {
# Function to check EFI partition
check_efi() {
local device="$1"
local temp_mount="" # Initialize temp_mount variable
print_status "$BLUE" "Checking EFI partition..."
@ -624,15 +799,37 @@ check_efi() {
exit 1
fi
# Detect EFI partition
# Check boot mode first
local boot_mode=$(detect_boot_mode)
if [[ "$boot_mode" == "bios" ]]; then
print_status "$YELLOW" "System is running in BIOS mode - EFI operations not applicable"
print_status "$BLUE" "Skipping EFI partition check for BIOS system"
return 0
fi
# Check if EFI partition is already mounted (from mount_system)
if [[ -d "$EFI_MOUNT_POINT" ]] && mountpoint -q "$EFI_MOUNT_POINT"; then
print_status "$BLUE" "Using already mounted EFI partition at: $EFI_MOUNT_POINT"
# Check EFI contents
print_status "$BLUE" "EFI partition contents:"
ls -la "$EFI_MOUNT_POINT"
# Check for existing boot entries
if [[ -d "$EFI_MOUNT_POINT/EFI" ]]; then
print_status "$BLUE" "EFI directory contents:"
find "$EFI_MOUNT_POINT/EFI" -type f -name "*.efi" 2>/dev/null || echo "No EFI files found"
fi
else
# EFI partition not mounted, detect and mount temporarily
local efi_partition=$(detect_efi_partition "$device")
if [[ -z "$efi_partition" ]]; then
print_status "$RED" "Error: No EFI partition found on device $device"
exit 1
fi
# Mount EFI partition temporarily
local temp_mount="/tmp/efi-temp"
print_status "$BLUE" "Mounting EFI partition temporarily for inspection..."
temp_mount="/tmp/efi-temp"
mkdir -p "$temp_mount"
mount "$efi_partition" "$temp_mount"
@ -645,6 +842,7 @@ check_efi() {
print_status "$BLUE" "EFI directory contents:"
find "$temp_mount/EFI" -type f -name "*.efi" 2>/dev/null || echo "No EFI files found"
fi
fi
# Attempt to repair boot entries if efibootmgr is available
if command -v efibootmgr >/dev/null 2>&1; then
@ -657,10 +855,18 @@ check_efi() {
# Try to create a new boot entry for GRUB
local grub_efi_path=""
local grub_efi_name=""
local efi_base_path=""
# Determine the base path for EFI operations
if [[ -d "$EFI_MOUNT_POINT" ]] && mountpoint -q "$EFI_MOUNT_POINT"; then
efi_base_path="$EFI_MOUNT_POINT"
else
efi_base_path="$temp_mount"
fi
# Search for GRUB EFI binaries in common locations
for grub_path in "EFI/grub/grubx64.efi" "EFI/grub/grub.efi" "EFI/ubuntu/grubx64.efi" "EFI/fedora/grubx64.efi" "EFI/arch/grubx64.efi"; do
if [[ -f "$temp_mount/$grub_path" ]]; then
if [[ -f "$efi_base_path/$grub_path" ]]; then
grub_efi_path="$grub_path"
grub_efi_name=$(basename "$grub_path")
break
@ -745,9 +951,11 @@ check_efi() {
print_status "$YELLOW" "efibootmgr not available, skipping boot entry repair"
fi
# Unmount temporary mount
# Unmount temporary mount if we created one
if [[ -n "$temp_mount" ]] && mountpoint -q "$temp_mount"; then
umount "$temp_mount"
rmdir "$temp_mount"
fi
print_status "$GREEN" "EFI partition check and repair completed"
}
@ -800,11 +1008,49 @@ cleanup() {
# Remove temporary mount points
rmdir "$MOUNT_POINT" 2>/dev/null || true
rmdir "$EFI_MOUNT_POINT" 2>/dev/null || true
rmdir "$EFI_MOINT_POINT" 2>/dev/null || true
# Remove temporary mount directories created during detection
find /tmp -maxdepth 1 -name "root-check-*" -type d 2>/dev/null | xargs -r rm -rf
find /tmp -maxdepth 1 -name "boot-check-*" -type d 2>/dev/null | xargs -r rm -rf
find /tmp -maxdepth 1 -name "efi-temp" -type d 2>/dev/null | xargs -r rm -rf
# Remove backup directory if it exists
if [[ -d "$BACKUP_DIR" ]]; then
rm -rf "$BACKUP_DIR"
print_status "$BLUE" "Removed backup directory: $BACKUP_DIR"
fi
print_status "$GREEN" "Cleanup completed"
}
# Function to perform comprehensive cleanup (clean command)
comprehensive_clean() {
print_status "$BLUE" "Performing comprehensive cleanup..."
# First, run the standard cleanup
cleanup
# Additional cleanup operations for the clean command
# Remove old log files (older than 7 days)
print_status "$BLUE" "Removing old log files..."
find /tmp -maxdepth 1 -name "grub-repair-*.log" -mtime +7 2>/dev/null | xargs -r rm -f
# Remove old backup directories (older than 7 days)
print_status "$BLUE" "Removing old backup directories..."
find /tmp -maxdepth 1 -name "grub-backup-*" -mtime +7 -type d 2>/dev/null | xargs -r rm -rf
# Clean up any remaining temporary files
print_status "$BLUE" "Removing remaining temporary files..."
find /tmp -maxdepth 1 -name "grub-repair-*" -type f 2>/dev/null | xargs -r rm -f
# Reset global variables to clean state
EFI_PARTITION_PATH=""
print_status "$GREEN" "Comprehensive cleanup completed"
}
# Function to complete boot repair
fix_boot() {
local device="$1"
@ -812,6 +1058,39 @@ fix_boot() {
print_status "$BLUE" "Starting complete boot repair..."
# Show boot mode
local boot_mode=$(detect_boot_mode)
print_status "$BLUE" "Detected boot mode: $boot_mode"
# Show detected partitions for confirmation
print_status "$BLUE" "Detected partitions:"
local efi_partition=$(detect_efi_partition "$device")
if [[ -n "$efi_partition" ]]; then
print_status "$BLUE" " EFI partition: $efi_partition"
fi
local root_partition=$(detect_root_partition "$device")
if [[ -n "$root_partition" ]]; then
print_status "$BLUE" " Root partition: $root_partition"
fi
# Final confirmation before proceeding
print_status "$YELLOW" "About to perform the following operations:"
print_status "$BLUE" " 1. Mount system partitions"
print_status "$BLUE" " 2. Install GRUB for $boot_mode mode"
print_status "$BLUE" " 3. Update GRUB configuration"
if [[ "$boot_mode" == "uefi" ]]; then
print_status "$BLUE" " 4. Check and repair EFI boot entries"
fi
print_status "$BLUE" " 5. Unmount all partitions"
print_status "$YELLOW" "Continue with boot repair? (y/N)"
read -r response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
print_status "$BLUE" "Boot repair cancelled by user"
exit 0
fi
# Create backup if requested
if [[ "$CREATE_BACKUP" == "true" ]]; then
create_backup
@ -826,8 +1105,12 @@ fix_boot() {
# Update GRUB configuration
update_grub
# Check and repair EFI
# Check and repair EFI (only for UEFI systems)
if [[ "$boot_mode" == "uefi" ]]; then
check_efi "$device"
else
print_status "$BLUE" "Skipping EFI operations for BIOS system"
fi
print_status "$GREEN" "Boot repair completed successfully!"
print_status "$BLUE" "You can now reboot your system."
@ -835,7 +1118,7 @@ fix_boot() {
# Main script logic
main() {
# Parse command line arguments
# Initialize variables
local device=""
local partition="1"
local command=""
@ -843,6 +1126,8 @@ main() {
local FORCE="false"
local VERBOSE="false"
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
@ -854,15 +1139,15 @@ main() {
shift
;;
-d|--device)
device="$2"
device=$(sanitize_path "$2" "Device path")
shift 2
;;
-p|--partition)
partition="$2"
partition=$(validate_partition "$2")
shift 2
;;
-m|--mount)
MOUNT_POINT="$2"
MOUNT_POINT=$(sanitize_path "$2" "Mount point")
EFI_MOUNT_POINT="$MOUNT_POINT/boot/efi"
shift 2
;;
@ -875,7 +1160,7 @@ main() {
shift
;;
-l|--log)
LOG_FILE="$2"
LOG_FILE=$(sanitize_path "$2" "Log file path")
shift 2
;;
-*)
@ -909,7 +1194,7 @@ main() {
set -x
fi
# Execute command
# Validate and execute command
case "$command" in
detect)
detect_systems
@ -939,7 +1224,7 @@ main() {
show_status
;;
clean)
cleanup
comprehensive_clean
;;
*)
print_status "$RED" "Error: Unknown command: $command"