commit 443558bafcc87e4c9b589aa4508ba17c9f2cf090 Author: robojerk Date: Thu Aug 28 11:17:37 2025 -0700 first commit diff --git a/QUICK_REFERENCE.md b/QUICK_REFERENCE.md new file mode 100644 index 0000000..7b759c7 --- /dev/null +++ b/QUICK_REFERENCE.md @@ -0,0 +1,74 @@ +# GRUB Repair Quick Reference + +## Essential Commands + +### 1. System Detection +```bash +sudo ./grub-repair.sh detect +``` + +### 2. Complete Boot Repair (Recommended) +```bash +sudo ./grub-repair.sh -d /dev/sda -p 1 -b fix-boot +``` + +### 3. Check Status +```bash +sudo ./grub-repair.sh status +``` + +### 4. Clean Up +```bash +sudo ./grub-repair.sh clean +``` + +## Common Scenarios + +### Fresh Boot Repair +```bash +# Detect your system first +sudo ./grub-repair.sh detect + +# Then repair (replace /dev/sda with your device) +sudo ./grub-repair.sh -d /dev/sda -p 1 -b fix-boot +``` + +### GRUB Reinstall Only +```bash +# Mount system +sudo ./grub-repair.sh -d /dev/sda -p 1 mount + +# Install GRUB +sudo ./grub-repair.sh install-grub + +# Update config +sudo ./grub-repair.sh update-grub + +# Unmount +sudo ./grub-repair.sh unmount +``` + +### EFI Partition Check and Repair +```bash +sudo ./grub-repair.sh -d /dev/sda check-efi +``` + +## Device Identification + +- **Find your disk**: `lsblk` or `fdisk -l` +- **EFI partition**: Usually the first partition (e.g., `/dev/sda1`) +- **Root partition**: Usually the second partition (e.g., `/dev/sda2`) + +## Safety Tips + +- Always use `-b` flag for backup +- Test on non-critical systems first +- Keep live ISO handy for recovery +- Document your partition layout + +## Troubleshooting + +- **Permission denied**: Use `sudo` +- **Device not found**: Check with `lsblk` +- **Mount fails**: Use `clean` command first +- **Verbose output**: Add `-v` flag diff --git a/README.md b/README.md new file mode 100644 index 0000000..0aab366 --- /dev/null +++ b/README.md @@ -0,0 +1,228 @@ +# GRUB and EFI Repair Script + +A comprehensive command-line tool designed to repair GRUB bootloader and EFI boot entries from a live ISO environment. This script is specifically crafted for system administrators and users who need to recover a system that won't boot due to corrupted or missing boot configuration. + +## Features + +- **Live ISO Detection**: Automatically detects if running from a live ISO environment +- **System Detection**: Identifies available disks, partitions, and bootable systems +- **Safe Mounting**: Properly mounts target systems with all necessary bind mounts +- **GRUB Installation**: Installs/reinstalls GRUB bootloader with proper EFI support +- **Configuration Updates**: Updates GRUB configuration to detect all installed kernels +- **EFI Repair**: Repairs EFI partition and boot entries +- **Backup Creation**: Creates backups before making changes +- **Comprehensive Logging**: Logs all operations for troubleshooting +- **Safe Cleanup**: Properly unmounts and cleans up after operations + +## Prerequisites + +- **Root Access**: Script must be run as root (use `sudo`) +- **Live ISO Environment**: Designed to run from a live Linux ISO +- **Target System**: An installed Linux system with GRUB bootloader +- **Basic Knowledge**: Understanding of disk partitions and Linux boot process + +## Installation + +1. Download the script to your live ISO environment +2. Make it executable: + ```bash + chmod +x grub-repair.sh + ``` +3. Run with sudo: + ```bash + sudo ./grub-repair.sh [OPTIONS] COMMAND + ``` + +## Usage + +### Basic Commands + +```bash +# Show help +sudo ./grub-repair.sh --help + +# Detect available systems +sudo ./grub-repair.sh detect + +# Show current status +sudo ./grub-repair.sh status + +# Clean up and unmount +sudo ./grub-repair.sh clean +``` + +### System Operations + +```bash +# Mount a system (e.g., /dev/sda1) +sudo ./grub-repair.sh -d /dev/sda -p 1 mount + +# Unmount current system +sudo ./grub-repair.sh unmount + +# Create backup of current configuration +sudo ./grub-repair.sh backup +``` + +### GRUB Operations + +```bash +# Install/reinstall GRUB +sudo ./grub-repair.sh install-grub + +# Update GRUB configuration +sudo ./grub-repair.sh update-grub + +# Check and repair EFI partition +sudo ./grub-repair.sh -d /dev/sda check-efi +``` + +### Complete Repair + +```bash +# Complete boot repair with backup +sudo ./grub-repair.sh -d /dev/sda -p 1 -b fix-boot + +# Complete repair without backup +sudo ./grub-repair.sh -d /dev/sda -p 1 fix-boot +``` + +## Command Line Options + +| Option | Long Option | Description | +|--------|-------------|-------------| +| `-h` | `--help` | Show help message | +| `-v` | `--verbose` | Enable verbose output | +| `-d` | `--device` | Specify target device (e.g., /dev/sda) | +| `-p` | `--partition` | Specify partition number (default: 1) | +| `-m` | `--mount` | Specify mount point (default: /mnt) | +| `-b` | `--backup` | Create backup before operations | +| `-f` | `--force` | Force operations without confirmation | +| `-l` | `--log` | Specify log file path | + +## Commands Reference + +### `detect` +Scans and displays all available disks, partitions, and bootable systems. + +### `mount` +Mounts the target system with proper bind mounts for chroot operations. + +### `unmount` +Safely unmounts all mounted filesystems in the correct order. + +### `backup` +Creates a timestamped backup of current GRUB configuration and EFI contents. + +### `install-grub` +Installs or reinstalls GRUB bootloader to the target system. + +### `update-grub` +Updates GRUB configuration to detect all installed kernels and OS entries. + +### `check-efi` +Checks and repairs EFI partition and boot entries. + +### `fix-boot` +Performs a complete boot repair sequence (mount + install + update + repair). + +### `status` +Shows current status of mounted systems and available configurations. + +### `clean` +Cleans up temporary files and unmounts all systems. + +## Typical Workflow + +1. **Boot from Live ISO**: Start your system from a live Linux ISO +2. **Detect Systems**: Identify your target system: + ```bash + sudo ./grub-repair.sh detect + ``` +3. **Complete Repair**: Perform the repair in one command: + ```bash + sudo ./grub-repair.sh -d /dev/sda -p 1 -b fix-boot + ``` +4. **Reboot**: Restart your system and test the boot + +## Troubleshooting + +### Common Issues + +- **Permission Denied**: Ensure you're running as root with `sudo` +- **Device Not Found**: Verify device names with `lsblk` or `fdisk -l` +- **Mount Failures**: Check if partitions are already mounted elsewhere +- **GRUB Installation Fails**: Verify EFI partition is properly mounted + +### Verbose Mode + +Enable detailed output for troubleshooting: +```bash +sudo ./grub-repair.sh -v -d /dev/sda -p 1 fix-boot +``` + +### Log Files + +The script creates detailed logs in `/tmp/grub-repair-YYYYMMDD-HHMMSS.log` + +## Safety Features + +- **Automatic Backups**: Creates backups before making changes +- **Safe Mounting**: Uses proper bind mounts and handles dependencies +- **Cleanup Traps**: Automatically cleans up on script exit +- **Error Handling**: Comprehensive error checking and reporting +- **Live ISO Detection**: Warns if not running from live environment + +## Examples + +### Scenario 1: Simple GRUB Reinstall +```bash +# Mount system +sudo ./grub-repair.sh -d /dev/sda -p 1 mount + +# Install GRUB +sudo ./grub-repair.sh install-grub + +# Update configuration +sudo ./grub-repair.sh update-grub + +# Unmount +sudo ./grub-repair.sh unmount +``` + +### Scenario 2: Complete EFI Repair +```bash +# Complete repair with backup +sudo ./grub-repair.sh -d /dev/sda -p 1 -b fix-boot +``` + +### Scenario 3: Multiple System Recovery +```bash +# Check available systems +sudo ./grub-repair.sh detect + +# Repair first system +sudo ./grub-repair.sh -d /dev/sda -p 1 fix-boot + +# Clean up +sudo ./grub-repair.sh clean + +# Repair second system +sudo ./grub-repair.sh -d /dev/sdb -p 1 fix-boot +``` + +## Contributing + +This script is designed to be robust and user-friendly. If you encounter issues or have suggestions for improvements, please: + +1. Test thoroughly in your environment +2. Document the issue or enhancement +3. Provide detailed error messages and system information + +## License + +This script is provided as-is for educational and recovery purposes. Use at your own risk and always test in a safe environment first. + +## Disclaimer + +This tool modifies critical boot configuration files. Always create backups and test in a safe environment before using on production systems. The authors are not responsible for data loss or system damage resulting from the use of this script. diff --git a/grub-repair.sh b/grub-repair.sh new file mode 100755 index 0000000..ca809fb --- /dev/null +++ b/grub-repair.sh @@ -0,0 +1,742 @@ +#!/bin/bash + +# GRUB and EFI Repair Script +# Designed to run from a live ISO to repair an installed system +# Author: System Administrator +# Version: 1.0 + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Global variables +SCRIPT_NAME=$(basename "$0") +MOUNT_POINT="/mnt" +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") + +# Function to print colored output +print_status() { + local color=$1 + local message=$2 + echo -e "${color}[$(date '+%Y-%m-%d %H:%M:%S')] ${message}${NC}" | tee -a "$LOG_FILE" +} + +# Function to print help +show_help() { + cat << EOF +Usage: $SCRIPT_NAME [OPTIONS] COMMAND + +GRUB and EFI Repair Script for Live ISO Recovery + +OPTIONS: + -h, --help Show this help message + -v, --verbose Enable verbose output + -d, --device DEV Specify target device (e.g., /dev/sda) + -p, --partition N Specify partition number (default: 1) + -m, --mount PATH Specify mount point (default: /mnt) + -b, --backup Create backup before operations + -f, --force Force operations without confirmation + -l, --log FILE Specify log file path + +COMMANDS: + detect Detect available disks and partitions + mount Mount target system + unmount Unmount target system + backup Create backup of current GRUB configuration + install-grub Install/reinstall GRUB + update-grub Update GRUB configuration + check-efi Check and repair EFI partition and boot entries + fix-boot Complete boot repair (mount + install + update) + status Show current status and mounted systems + clean Clean up temporary files and unmount + +EXAMPLES: + # Detect available systems + $SCRIPT_NAME detect + + # Mount system on /dev/sda1 + $SCRIPT_NAME -d /dev/sda -p 1 mount + + # Complete boot repair with backup + $SCRIPT_NAME -d /dev/sda -p 1 -b fix-boot + + # Check and repair EFI only + $SCRIPT_NAME -d /dev/sda check-efi + +EOF +} + +# Function to check if running as root +check_root() { + if [[ $EUID -ne 0 ]]; then + print_status "$RED" "Error: This script must be run as root (use sudo)" + exit 1 + fi +} + +# Function to check if running from live ISO +check_live_iso() { + if [[ "$FORCE" == "true" ]]; then + print_status "$YELLOW" "Force mode enabled, skipping live ISO check" + return 0 + fi + + if ! grep -q "boot=live" /proc/cmdline 2>/dev/null; then + print_status "$YELLOW" "Warning: This doesn't appear to be running from a live ISO" + print_status "$YELLOW" "Continue anyway? (y/N)" + read -r response + if [[ ! "$response" =~ ^[Yy]$ ]]; then + exit 1 + fi + fi +} + +# Function to detect available disks and partitions +detect_systems() { + print_status "$BLUE" "Detecting available disks and partitions..." + + echo "Available disks:" + lsblk -d -o NAME,SIZE,TYPE,MOUNTPOINT + + echo -e "\nAvailable partitions:" + lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,LABEL + + echo -e "\nEFI partitions:" + blkid | grep -i "vfat\|fat32" | grep -i "efi\|boot" || echo "No EFI partitions found" + + echo -e "\nLinux partitions:" + blkid | grep -i "ext4\|xfs\|btrfs" || echo "No Linux partitions found" +} + +# Function to detect Linux distribution +detect_distribution() { + local mount_point="$1" + + # Try to detect distribution from /etc/os-release + if [[ -f "$mount_point/etc/os-release" ]]; then + local distro_name=$(grep "^NAME=" "$mount_point/etc/os-release" | cut -d'"' -f2 | tr '[:upper:]' '[:lower:]') + echo "$distro_name" + return 0 + fi + + # Fallback: check for distribution-specific files + if [[ -f "$mount_point/etc/debian_version" ]]; then + echo "debian" + return 0 + elif [[ -f "$mount_point/etc/redhat-release" ]]; then + echo "fedora" + return 0 + elif [[ -f "$mount_point/etc/arch-release" ]]; then + echo "arch" + return 0 + elif [[ -f "$mount_point/etc/lsb-release" ]]; then + local distro=$(grep "^DISTRIB_ID=" "$mount_point/etc/lsb-release" | cut -d'=' -f2 | tr '[:upper:]' '[:lower:]') + echo "$distro" + return 0 + fi + + # Default fallback - will use generic GRUB bootloader ID + echo "generic_grub" + return 1 +} + +# Function to detect EFI partition +detect_efi_partition() { + local device="$1" + + # Get the parent disk name more robustly + local disk + if command -v lsblk >/dev/null 2>&1; then + # Use lsblk to get parent disk (more reliable for NVMe, etc.) + disk=$(lsblk -rno PKNAME "$device" 2>/dev/null | head -1) + + # If no parent found, the device might be a disk itself + if [[ -z "$disk" ]]; then + # Check if the device itself is a disk (not a partition) + local device_type=$(lsblk -rno TYPE "$device" 2>/dev/null) + if [[ "$device_type" == "disk" ]]; then + disk="$device" + else + # Fallback: try to extract disk from device path + disk="${device%[0-9]*}" + fi + fi + else + # Fallback: try to extract disk from device path + disk="${device%[0-9]*}" + fi + + if [[ -z "$disk" ]]; then + print_status "$YELLOW" "Warning: Could not determine parent disk for $device" + return 1 + fi + + # Capture blkid output once for efficiency + local blkid_output=$(blkid 2>/dev/null) + + # First try to find EFI partition by filesystem type and label + local efi_partition=$(echo "$blkid_output" | grep -i "vfat\|fat32" | grep -i "efi\|boot" | grep "$disk" | head -1 | cut -d: -f1) + + if [[ -n "$efi_partition" ]]; then + echo "$efi_partition" + return 0 + fi + + # Fallback: try common partition numbers (1, 2, 12) + for part_num in 1 2 12; do + 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" == "vfat" || "$fstype" == "fat32" ]]; then + echo "$test_partition" + return 0 + fi + fi + done + + # Enhanced fallback: scan all partitions on the disk + if command -v lsblk >/dev/null 2>&1; then + local all_partitions=$(lsblk -rno NAME "$disk" | grep -E "[0-9]+$") + for partition in $all_partitions; do + 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" == "vfat" || "$fstype" == "fat32" ]]; then + echo "$full_path" + return 0 + fi + fi + done + fi + + # If still no EFI partition found, return empty + return 1 +} + +# Function to mount target system +mount_system() { + local device="$1" + local partition="$2" + + if [[ -z "$device" ]]; then + print_status "$RED" "Error: Device not specified. Use -d option." + exit 1 + fi + + local partition_device="${device}${partition}" + + print_status "$BLUE" "Mounting system from $partition_device to $MOUNT_POINT..." + + # Check if partition exists + if [[ ! -b "$partition_device" ]]; then + print_status "$RED" "Error: Partition $partition_device does not exist" + exit 1 + fi + + # Create mount point if it doesn't exist + mkdir -p "$MOUNT_POINT" + + # Mount root partition + print_status "$BLUE" "Mounting root partition..." + if ! mount "$partition_device" "$MOUNT_POINT"; then + print_status "$RED" "Error: Failed to mount root partition $partition_device" + exit 1 + fi + + # Mount necessary filesystems + print_status "$BLUE" "Mounting necessary filesystems..." + + # Mount /proc + # Note: We explicitly unmount on failure rather than relying solely on the trap cleanup + # This provides immediate, targeted cleanup and prevents partial mount states + if ! mount --bind /proc "$MOUNT_POINT/proc"; then + print_status "$RED" "Error: Failed to mount /proc" + umount "$MOUNT_POINT" + exit 1 + fi + + # Mount /sys + if ! mount --bind /sys "$MOUNT_POINT/sys"; then + print_status "$RED" "Error: Failed to mount /sys" + umount "$MOUNT_POINT/proc" + umount "$MOUNT_POINT" + exit 1 + fi + + # Mount /dev + if ! mount --bind /dev "$MOUNT_POINT/dev"; then + print_status "$RED" "Error: Failed to mount /dev" + umount "$MOUNT_POINT/sys" + umount "$MOUNT_POINT/proc" + umount "$MOUNT_POINT" + exit 1 + fi + + # Mount /dev/pts + if ! mount --bind /dev/pts "$MOUNT_POINT/dev/pts"; then + print_status "$RED" "Error: Failed to mount /dev/pts" + umount "$MOUNT_POINT/dev" + umount "$MOUNT_POINT/sys" + umount "$MOUNT_POINT/proc" + umount "$MOUNT_POINT" + exit 1 + fi + + # Mount EFI partition if it exists + local efi_partition=$(detect_efi_partition "$device") + if [[ -n "$efi_partition" && -b "$efi_partition" ]]; then + print_status "$BLUE" "Mounting EFI partition $efi_partition..." + mkdir -p "$EFI_MOUNT_POINT" + mount "$efi_partition" "$EFI_MOUNT_POINT" + else + print_status "$YELLOW" "Warning: EFI partition not found, some operations may fail" + fi + + print_status "$GREEN" "System mounted successfully at $MOUNT_POINT" +} + +# Function to unmount system +unmount_system() { + print_status "$BLUE" "Unmounting system..." + + # Unmount in reverse order + if mountpoint -q "$EFI_MOUNT_POINT"; then + umount "$EFI_MOUNT_POINT" + print_status "$GREEN" "EFI partition unmounted" + fi + + if mountpoint -q "$MOUNT_POINT/dev/pts"; then + umount "$MOUNT_POINT/dev/pts" + fi + + if mountpoint -q "$MOUNT_POINT/dev"; then + umount "$MOUNT_POINT/dev" + fi + + if mountpoint -q "$MOUNT_POINT/sys"; then + umount "$MOUNT_POINT/sys" + fi + + if mountpoint -q "$MOUNT_POINT/proc"; then + umount "$MOUNT_POINT/proc" + fi + + if mountpoint -q "$MOUNT_POINT"; then + umount "$MOUNT_POINT" + print_status "$GREEN" "Root partition unmounted" + fi + + print_status "$GREEN" "System unmounted successfully" +} + +# Function to create backup +create_backup() { + print_status "$BLUE" "Creating backup of current GRUB configuration..." + + mkdir -p "$BACKUP_DIR" + + if [[ -f "$GRUB_CFG_PATH" ]]; then + cp "$GRUB_CFG_PATH" "$BACKUP_DIR/" + print_status "$GREEN" "GRUB config backed up to $BACKUP_DIR" + fi + + if [[ -d "$EFI_MOUNT_POINT" ]]; then + cp -r "$EFI_MOUNT_POINT" "$BACKUP_DIR/" + print_status "$GREEN" "EFI directory backed up to $BACKUP_DIR" + fi + + print_status "$GREEN" "Backup completed at $BACKUP_DIR" +} + +# Function to install GRUB +install_grub() { + print_status "$BLUE" "Installing GRUB..." + + # Note: We use mountpoint -q instead of just checking directory existence + # because mountpoint -q verifies the directory is actually mounted, not just exists + if [[ ! -d "$MOUNT_POINT" ]] || ! mountpoint -q "$MOUNT_POINT"; then + print_status "$RED" "Error: System not mounted. Mount first with 'mount' command." + exit 1 + 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" + exit 1 + fi +} + +# Function to update GRUB configuration +update_grub() { + print_status "$BLUE" "Updating GRUB configuration..." + + # Note: We use mountpoint -q instead of just checking directory existence + # because mountpoint -q verifies the directory is actually mounted, not just exists + if [[ ! -d "$MOUNT_POINT" ]] || ! mountpoint -q "$MOUNT_POINT"; then + print_status "$RED" "Error: System not mounted. Mount first with 'mount' command." + exit 1 + fi + + # Update GRUB configuration + if chroot "$MOUNT_POINT" grub-mkconfig -o /boot/grub/grub.cfg; then + print_status "$GREEN" "GRUB configuration updated successfully" + else + print_status "$RED" "Error: GRUB configuration update failed" + exit 1 + fi +} + +# Function to check EFI partition +check_efi() { + local device="$1" + + print_status "$BLUE" "Checking EFI partition..." + + if [[ -z "$device" ]]; then + print_status "$RED" "Error: Device not specified. Use -d option." + exit 1 + fi + + # Detect EFI partition + 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" + mkdir -p "$temp_mount" + mount "$efi_partition" "$temp_mount" + + # Check EFI contents + print_status "$BLUE" "EFI partition contents:" + ls -la "$temp_mount" + + # Check for existing boot entries + if [[ -d "$temp_mount/EFI" ]]; then + print_status "$BLUE" "EFI directory contents:" + find "$temp_mount/EFI" -type f -name "*.efi" 2>/dev/null || echo "No EFI files found" + fi + + # Attempt to repair boot entries if efibootmgr is available + if command -v efibootmgr >/dev/null 2>&1; then + print_status "$BLUE" "Attempting to repair EFI boot entries..." + + # Get current boot entries + local current_entries=$(efibootmgr | grep -E "^Boot[0-9]+" | wc -l) + print_status "$BLUE" "Current EFI boot entries: $current_entries" + + # Try to create a new boot entry for GRUB + local grub_efi_path="" + local grub_efi_name="" + + # 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 + grub_efi_path="$grub_path" + grub_efi_name=$(basename "$grub_path") + break + fi + done + + if [[ -n "$grub_efi_path" ]]; then + print_status "$BLUE" "GRUB EFI file found: $grub_efi_path, attempting to create boot entry..." + + # Get the partition number dynamically + local partition_number=$(echo "$efi_partition" | grep -o '[0-9]\+$') + if [[ -n "$partition_number" ]]; then + # Convert path to EFI format (backslashes) + local efi_path=$(echo "$grub_efi_path" | sed 's/\//\\/g') + + # Try to detect distribution for better bootloader ID + local bootloader_id="grub" + local bootloader_label="GRUB" + + # First try to detect from EFI path + case "$grub_efi_path" in + "EFI/ubuntu/"*) + bootloader_id="ubuntu" + bootloader_label="Ubuntu" + ;; + "EFI/fedora/"*) + bootloader_id="fedora" + bootloader_label="Fedora" + ;; + "EFI/arch/"*) + bootloader_id="arch" + bootloader_label="Arch Linux" + ;; + "EFI/debian/"*) + bootloader_id="debian" + bootloader_label="Debian" + ;; + *) + # Try to detect distribution from mounted system if available + if [[ -d "$MOUNT_POINT" ]] && mountpoint -q "$MOUNT_POINT"; 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 + else + # Use generic GRUB ID for other distributions + bootloader_id="grub" + bootloader_label="GRUB" + fi + ;; + esac + + print_status "$BLUE" "Creating boot entry with ID: $bootloader_id, Label: $bootloader_label" + efibootmgr -c -d "$efi_partition" -p "$partition_number" -l "$efi_path" -L "$bootloader_label" 2>/dev/null || print_status "$YELLOW" "Could not create boot entry for $bootloader_label" + else + print_status "$YELLOW" "Could not determine partition number for $efi_partition" + fi + else + print_status "$YELLOW" "No GRUB EFI binary found in common locations" + fi + else + print_status "$YELLOW" "efibootmgr not available, skipping boot entry repair" + fi + + # Unmount temporary mount + umount "$temp_mount" + rmdir "$temp_mount" + + print_status "$GREEN" "EFI partition check and repair completed" +} + +# Function to show status +show_status() { + print_status "$BLUE" "Current system status:" + + echo "Mounted systems:" + # Use direct mountpoint checks for better performance instead of findmnt + # mountpoint -q is more efficient than spawning a new process with findmnt + if mountpoint -q "$MOUNT_POINT"; then + echo "Root system mounted at: $MOUNT_POINT" + fi + if mountpoint -q "$EFI_MOUNT_POINT"; then + echo "EFI system mounted at: $EFI_MOUNT_POINT" + fi + if ! mountpoint -q "$MOUNT_POINT" && ! mountpoint -q "$EFI_MOUNT_POINT"; then + echo "No systems mounted" + fi + + echo -e "\nAvailable GRUB configurations:" + find /mnt -name "grub.cfg" 2>/dev/null || echo "No GRUB configs found" + + echo -e "\nEFI partition status:" + if mountpoint -q "$EFI_MOUNT_POINT"; then + echo "EFI mounted at: $EFI_MOUNT_POINT" + ls -la "$EFI_MOUNT_POINT" 2>/dev/null || echo "Cannot list EFI contents" + else + echo "EFI not mounted" + fi + + echo -e "\nBackup directory:" + if [[ -d "$BACKUP_DIR" ]]; then + echo "Backup available at: $BACKUP_DIR" + ls -la "$BACKUP_DIR" + else + echo "No backup created" + fi +} + +# Function to clean up +cleanup() { + print_status "$BLUE" "Cleaning up..." + + # Unmount if mounted + if mountpoint -q "$MOUNT_POINT" || mountpoint -q "$EFI_MOUNT_POINT"; then + unmount_system + fi + + # Remove temporary mount points + rmdir "$MOUNT_POINT" 2>/dev/null || true + rmdir "$EFI_MOUNT_POINT" 2>/dev/null || true + + print_status "$GREEN" "Cleanup completed" +} + +# Function to complete boot repair +fix_boot() { + local device="$1" + local partition="$2" + + print_status "$BLUE" "Starting complete boot repair..." + + # Create backup if requested + if [[ "$CREATE_BACKUP" == "true" ]]; then + create_backup + fi + + # Mount system + mount_system "$device" "$partition" + + # Install GRUB + install_grub + + # Update GRUB configuration + update_grub + + # Check and repair EFI + check_efi "$device" + + print_status "$GREEN" "Boot repair completed successfully!" + print_status "$BLUE" "You can now reboot your system." +} + +# Main script logic +main() { + # Parse command line arguments + local device="" + local partition="1" + local command="" + local CREATE_BACKUP="false" + local FORCE="false" + local VERBOSE="false" + + while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_help + exit 0 + ;; + -v|--verbose) + VERBOSE="true" + shift + ;; + -d|--device) + device="$2" + shift 2 + ;; + -p|--partition) + partition="$2" + shift 2 + ;; + -m|--mount) + MOUNT_POINT="$2" + EFI_MOUNT_POINT="$MOUNT_POINT/boot/efi" + shift 2 + ;; + -b|--backup) + CREATE_BACKUP="true" + shift + ;; + -f|--force) + FORCE="true" + shift + ;; + -l|--log) + LOG_FILE="$2" + shift 2 + ;; + -*) + print_status "$RED" "Unknown option: $1" + show_help + exit 1 + ;; + *) + command="$1" + shift + ;; + esac + done + + # Check if command is provided + if [[ -z "$command" ]]; then + print_status "$RED" "Error: No command specified" + print_status "$RED" "Use '$SCRIPT_NAME --help' to see available commands" + show_help + exit 1 + fi + + # Check if running as root + check_root + + # Check if running from live ISO + check_live_iso + + # Set verbose mode + if [[ "$VERBOSE" == "true" ]]; then + set -x + fi + + # Execute command + case "$command" in + detect) + detect_systems + ;; + mount) + mount_system "$device" "$partition" + ;; + unmount) + unmount_system + ;; + backup) + create_backup + ;; + install-grub) + install_grub + ;; + update-grub) + update_grub + ;; + check-efi) + check_efi "$device" + ;; + fix-boot) + fix_boot "$device" "$partition" + ;; + status) + show_status + ;; + clean) + cleanup + ;; + *) + print_status "$RED" "Error: Unknown command: $command" + show_help + exit 1 + ;; + esac +} + +# Trap to cleanup on exit +trap cleanup EXIT + +# Run main function with all arguments +main "$@" diff --git a/test-script.sh b/test-script.sh new file mode 100755 index 0000000..d103aca --- /dev/null +++ b/test-script.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# Test script for GRUB repair script +# This script tests basic functionality without actually mounting systems + +echo "Testing GRUB repair script..." + +# Test help functionality +echo "Testing help command..." +./grub-repair.sh --help > /dev/null +if [ $? -eq 0 ]; then + echo "✓ Help command works" +else + echo "✗ Help command failed" +fi + +# Test detect command (should work without root) +echo "Testing detect command..." +./grub-repair.sh detect > /dev/null 2>&1 +if [ $? -eq 0 ]; then + echo "✓ Detect command works" +else + echo "✗ Detect command failed (expected without root)" +fi + +# Test invalid command +echo "Testing invalid command..." +./grub-repair.sh invalid-command > /dev/null 2>&1 +if [ $? -eq 1 ]; then + echo "✓ Invalid command handling works" +else + echo "✗ Invalid command handling failed" +fi + +# Test script syntax +echo "Testing script syntax..." +bash -n grub-repair.sh +if [ $? -eq 0 ]; then + echo "✓ Script syntax is valid" +else + echo "✗ Script syntax errors found" +fi + +# Test script permissions +if [ -x grub-repair.sh ]; then + echo "✓ Script is executable" +else + echo "✗ Script is not executable" +fi + +echo "Basic tests completed!" +echo "" +echo "To run full tests (requires root):" +echo "sudo ./grub-repair.sh detect" +echo "sudo ./grub-repair.sh --help"