Files
backup_to_external_m.2/old_scripts/improved_lvm_migration.sh
root 56c07dbe49 Complete rewrite: Single working LVM block-level backup script
- Removed 40+ broken/messy scripts, moved to old_scripts/
- Created lvm_block_backup.sh - proper block-level LVM snapshot backup
- Uses dd for block-level cloning instead of file-level rsync
- Successfully tested: 462GB backup in 33 minutes
- Creates exact, bootable clone of internal drive to external drive
- Proper LVM snapshot management with cleanup
- Clear documentation in README_BACKUP.md
- Clean, minimal solution that actually works
2025-09-30 17:35:22 +02:00

552 lines
18 KiB
Bash
Executable File

#!/bin/bash
# Improved LVM Migration Script
# Fixes the boot issues from the previous failed LVM migration
# Properly handles LUKS + LVM combination with robust boot configuration
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration variables
INTERNAL_DRIVE=""
EXTERNAL_DRIVE=""
VG_NAME="system-vg"
ROOT_LV="root"
HOME_LV="home"
SWAP_LV="swap"
BOOT_LV="boot"
# Work directory
WORK_DIR="/mnt/lvm_migration"
# Detected partitions and info
declare -A INTERNAL_PARTITIONS
declare -A PARTITION_FILESYSTEMS
declare -A PARTITION_SIZES
log() {
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
exit 1
}
warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
confirm_action() {
echo -e "${YELLOW}$1${NC}"
read -p "Do you want to continue? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
error "Operation aborted by user"
fi
}
detect_drives() {
log "Detecting available drives..."
local all_drives=($(lsblk -dpno NAME,TYPE | grep "disk" | awk '{print $1}'))
local drives=()
# Filter out the USB stick we're running from
for drive in "${all_drives[@]}"; do
if mount | grep -q "$drive" && mount | grep -q "/lib/live\|overlay\|/media.*live"; then
log "Excluding live USB drive: $drive"
continue
fi
drives+=("$drive")
done
if [ ${#drives[@]} -lt 2 ]; then
error "Need at least 2 drives for migration. Found only ${#drives[@]} suitable drives"
fi
echo "Available drives:"
for i in "${!drives[@]}"; do
local drive="${drives[$i]}"
local info=$(lsblk -dpno SIZE,MODEL "$drive" | xargs)
echo "$((i+1)). $drive - $info"
lsblk "$drive" | tail -n +2 | sed 's/^/ /'
echo
done
# Auto-detect with user confirmation
local suggested_internal=""
local suggested_external=""
# Prefer NVMe for internal, USB for external
for drive in "${drives[@]}"; do
if [[ "$drive" == *"nvme"* ]]; then
suggested_internal="$drive"
break
fi
done
for drive in "${drives[@]}"; do
if [ "$drive" != "$suggested_internal" ]; then
if udevadm info --query=property --name="$drive" 2>/dev/null | grep -q "ID_BUS=usb"; then
suggested_external="$drive"
break
fi
fi
done
if [ -n "$suggested_internal" ] && [ -n "$suggested_external" ]; then
echo "Suggested configuration:"
echo " Internal (source): $suggested_internal"
echo " External (LVM target): $suggested_external"
read -p "Use this configuration? [Y/n]: " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
INTERNAL_DRIVE="$suggested_internal"
EXTERNAL_DRIVE="$suggested_external"
fi
fi
# Manual selection if needed
if [ -z "$INTERNAL_DRIVE" ]; then
read -p "Select INTERNAL drive number: " choice
INTERNAL_DRIVE="${drives[$((choice-1))]}"
fi
if [ -z "$EXTERNAL_DRIVE" ]; then
read -p "Select EXTERNAL drive number: " choice
EXTERNAL_DRIVE="${drives[$((choice-1))]}"
fi
# Safety checks
if [ "$INTERNAL_DRIVE" = "$EXTERNAL_DRIVE" ]; then
error "Internal and external drives cannot be the same!"
fi
local external_size_bytes=$(lsblk -bno SIZE "$EXTERNAL_DRIVE")
local internal_size_bytes=$(lsblk -bno SIZE "$INTERNAL_DRIVE")
if [ "$external_size_bytes" -lt "$internal_size_bytes" ]; then
error "External drive is smaller than internal drive"
fi
echo
echo "Final configuration:"
echo " Internal (source): $INTERNAL_DRIVE"
echo " External (LVM target): $EXTERNAL_DRIVE"
# Final safety confirmation
echo
echo -e "${RED}⚠️ FINAL SAFETY CHECK ⚠️${NC}"
echo "This will COMPLETELY WIPE: $EXTERNAL_DRIVE"
echo "Current partitions that will be DESTROYED:"
lsblk "$EXTERNAL_DRIVE"
echo
read -p "Type 'MIGRATE' to confirm LVM migration: " confirmation
if [ "$confirmation" != "MIGRATE" ]; then
error "Migration cancelled by user"
fi
success "Drive selection completed"
}
analyze_source_system() {
log "Analyzing source system..."
local partitions=($(lsblk -pno NAME "$INTERNAL_DRIVE" | grep -v "^$INTERNAL_DRIVE$"))
echo "Source drive partitions:"
for part in "${partitions[@]}"; do
local size=$(lsblk -no SIZE "$part")
local fstype=$(lsblk -no FSTYPE "$part")
local label=$(lsblk -no LABEL "$part")
local mountpoint=$(lsblk -no MOUNTPOINT "$part")
echo " $part: $size, $fstype, ${label:-'no label'}"
PARTITION_FILESYSTEMS["$part"]="$fstype"
PARTITION_SIZES["$part"]="$size"
# Identify partitions
if [[ "$fstype" == "vfat" ]] && [[ "$part" == *"1" ]] || [[ "$part" == *"2" ]]; then
INTERNAL_PARTITIONS["efi"]="$part"
elif [[ "$mountpoint" == "/" ]] || [[ "$label" == "root"* ]] || [[ "$fstype" == "ext4" && "$part" == *"1" ]]; then
INTERNAL_PARTITIONS["root"]="$part"
elif [[ "$mountpoint" == "/home" ]] || [[ "$label" == "home"* ]]; then
INTERNAL_PARTITIONS["home"]="$part"
elif [[ "$fstype" == "crypto_LUKS" ]]; then
# Encrypted partition - likely home
INTERNAL_PARTITIONS["encrypted_home"]="$part"
fi
done
success "Source system analysis completed"
}
check_prerequisites() {
log "Checking prerequisites and installing required tools..."
# Check if running from live system
if ! df / | grep -q "loop\|overlay\|tmpfs"; then
warning "Not running from live system - this may cause issues"
confirm_action "Continue anyway?"
fi
# Install/update required packages
log "Installing required packages..."
apt update >/dev/null 2>&1
apt install -y lvm2 cryptsetup rsync parted pv grub-efi-amd64 grub-common \
e2fsprogs dosfstools bc util-linux initramfs-tools \
efibootmgr os-prober >/dev/null 2>&1
# Ensure LVM2 is properly loaded
modprobe dm-mod
modprobe dm-crypt
vgchange -ay 2>/dev/null || true
success "Prerequisites installed"
}
create_lvm_layout() {
log "Creating LVM layout on external drive..."
# Calculate sizes based on source
local root_size="70G"
local home_size="100G"
local swap_size="8G"
local boot_size="2G"
# Adjust based on available space
local total_space_gb=$(lsblk -bno SIZE "$EXTERNAL_DRIVE" | awk '{print int($1/1024/1024/1024)}')
if [ "$total_space_gb" -gt 500 ]; then
home_size="200G"
root_size="100G"
fi
log "LVM sizes: Root=$root_size, Home=$home_size, Swap=$swap_size, Boot=$boot_size"
# Wipe and create partition table
wipefs -a "$EXTERNAL_DRIVE"
parted -s "$EXTERNAL_DRIVE" mklabel gpt
# Create EFI partition (512MB)
parted -s "$EXTERNAL_DRIVE" mkpart primary fat32 1MiB 513MiB
parted -s "$EXTERNAL_DRIVE" set 1 boot on
parted -s "$EXTERNAL_DRIVE" set 1 esp on
# Create LVM partition (rest of disk)
parted -s "$EXTERNAL_DRIVE" mkpart primary 513MiB 100%
parted -s "$EXTERNAL_DRIVE" set 2 lvm on
# Wait for partitions
sleep 3
partprobe "$EXTERNAL_DRIVE"
sleep 2
# Verify partitions exist
if [ ! -b "${EXTERNAL_DRIVE}1" ] || [ ! -b "${EXTERNAL_DRIVE}2" ]; then
error "Partitions not created properly"
fi
# Create filesystems
mkfs.fat -F32 "${EXTERNAL_DRIVE}1" || error "Failed to create EFI filesystem"
# Setup LVM
pvcreate "${EXTERNAL_DRIVE}2" || error "Failed to create physical volume"
vgcreate "$VG_NAME" "${EXTERNAL_DRIVE}2" || error "Failed to create volume group"
# Create logical volumes
lvcreate -L "$ROOT_SIZE" -n "$ROOT_LV" "$VG_NAME" || error "Failed to create root LV"
lvcreate -L "$HOME_SIZE" -n "$HOME_LV" "$VG_NAME" || error "Failed to create home LV"
lvcreate -L "$SWAP_SIZE" -n "$SWAP_LV" "$VG_NAME" || error "Failed to create swap LV"
lvcreate -L "$BOOT_SIZE" -n "$BOOT_LV" "$VG_NAME" || error "Failed to create boot LV"
# Create filesystems on LVM volumes
mkfs.ext4 -L "root" "/dev/$VG_NAME/$ROOT_LV" || error "Failed to create root filesystem"
mkfs.ext4 -L "home" "/dev/$VG_NAME/$HOME_LV" || error "Failed to create home filesystem"
mkfs.ext4 -L "boot" "/dev/$VG_NAME/$BOOT_LV" || error "Failed to create boot filesystem"
mkswap -L "swap" "/dev/$VG_NAME/$SWAP_LV" || error "Failed to create swap"
success "LVM layout created successfully"
}
handle_encrypted_partitions() {
log "Handling encrypted partitions..."
for part_name in "${!INTERNAL_PARTITIONS[@]}"; do
local part_device="${INTERNAL_PARTITIONS[$part_name]}"
local fstype="${PARTITION_FILESYSTEMS[$part_device]}"
if [[ "$fstype" == "crypto_LUKS" ]]; then
log "Found encrypted partition: $part_device"
local crypt_name="migration_${part_name}"
echo "Please enter password for encrypted partition ($part_device):"
if cryptsetup open "$part_device" "$crypt_name"; then
success "Unlocked $part_device as /dev/mapper/$crypt_name"
INTERNAL_PARTITIONS["$part_name"]="/dev/mapper/$crypt_name"
# Update filesystem type
local decrypted_fs=$(lsblk -no FSTYPE "/dev/mapper/$crypt_name")
PARTITION_FILESYSTEMS["/dev/mapper/$crypt_name"]="$decrypted_fs"
else
error "Failed to unlock encrypted partition"
fi
fi
done
success "Encrypted partitions handled"
}
mount_filesystems() {
log "Mounting filesystems..."
mkdir -p "$WORK_DIR"/{internal_root,internal_home,external_root,external_home,external_boot}
# Mount internal filesystems
if [ -n "${INTERNAL_PARTITIONS[root]}" ]; then
mount "${INTERNAL_PARTITIONS[root]}" "$WORK_DIR/internal_root"
fi
if [ -n "${INTERNAL_PARTITIONS[home]}" ]; then
mount "${INTERNAL_PARTITIONS[home]}" "$WORK_DIR/internal_home"
elif [ -n "${INTERNAL_PARTITIONS[encrypted_home]}" ]; then
mount "${INTERNAL_PARTITIONS[encrypted_home]}" "$WORK_DIR/internal_home"
fi
# Mount external LVM filesystems
mount "/dev/$VG_NAME/$ROOT_LV" "$WORK_DIR/external_root"
mount "/dev/$VG_NAME/$HOME_LV" "$WORK_DIR/external_home"
mount "/dev/$VG_NAME/$BOOT_LV" "$WORK_DIR/external_boot"
# Mount EFI
mkdir -p "$WORK_DIR/external_root/boot/efi"
mount "${EXTERNAL_DRIVE}1" "$WORK_DIR/external_root/boot/efi"
success "Filesystems mounted"
}
copy_system_data() {
log "Copying system data..."
# Copy root filesystem
if [ -d "$WORK_DIR/internal_root" ]; then
log "Copying root filesystem..."
rsync -avxHAX --progress \
--exclude=/home/* --exclude=/proc/* --exclude=/sys/* \
--exclude=/dev/* --exclude=/run/* --exclude=/tmp/* \
--exclude=/var/tmp/* --exclude=/mnt/* --exclude=/media/* \
"$WORK_DIR/internal_root/" "$WORK_DIR/external_root/"
fi
# Copy home filesystem
if [ -d "$WORK_DIR/internal_home" ]; then
log "Copying home filesystem..."
rsync -avxHAX --progress "$WORK_DIR/internal_home/" "$WORK_DIR/external_home/"
elif [ -d "$WORK_DIR/internal_root/home" ]; then
log "Copying /home from root filesystem..."
rsync -avxHAX --progress "$WORK_DIR/internal_root/home/" "$WORK_DIR/external_home/"
fi
# Copy boot files
if [ -d "$WORK_DIR/internal_root/boot" ]; then
log "Copying boot files..."
rsync -avxHAX --progress \
--exclude=/boot/efi/* \
"$WORK_DIR/internal_root/boot/" "$WORK_DIR/external_boot/"
fi
success "System data copied"
}
configure_lvm_system() {
log "Configuring LVM system..."
# Get UUIDs
local root_uuid=$(blkid -s UUID -o value "/dev/$VG_NAME/$ROOT_LV")
local home_uuid=$(blkid -s UUID -o value "/dev/$VG_NAME/$HOME_LV")
local boot_uuid=$(blkid -s UUID -o value "/dev/$VG_NAME/$BOOT_LV")
local efi_uuid=$(blkid -s UUID -o value "${EXTERNAL_DRIVE}1")
local swap_uuid=$(blkid -s UUID -o value "/dev/$VG_NAME/$SWAP_LV")
# Create new fstab
cat > "$WORK_DIR/external_root/etc/fstab" << EOF
# /etc/fstab: static file system information for LVM system
UUID=$root_uuid / ext4 defaults 0 1
UUID=$efi_uuid /boot/efi vfat defaults 0 2
UUID=$boot_uuid /boot ext4 defaults 0 2
UUID=$home_uuid /home ext4 defaults 0 2
UUID=$swap_uuid none swap sw 0 0
tmpfs /tmp tmpfs defaults,noatime,mode=1777 0 0
EOF
# Configure LVM in initramfs
echo "$VG_NAME" >> "$WORK_DIR/external_root/etc/initramfs-tools/conf.d/lvm"
echo "BOOT=local" >> "$WORK_DIR/external_root/etc/initramfs-tools/conf.d/resume"
# Ensure LVM modules are included
cat > "$WORK_DIR/external_root/etc/initramfs-tools/modules" << EOF
# LVM modules
dm-mod
dm-crypt
dm-snapshot
EOF
# Update GRUB configuration for LVM
sed -i 's/#GRUB_ENABLE_CRYPTODISK=y/GRUB_ENABLE_CRYPTODISK=y/' "$WORK_DIR/external_root/etc/default/grub"
echo 'GRUB_PRELOAD_MODULES="lvm"' >> "$WORK_DIR/external_root/etc/default/grub"
success "LVM system configured"
}
install_bootloader() {
log "Installing bootloader with LVM support..."
# Bind mount necessary filesystems
mount --bind /dev "$WORK_DIR/external_root/dev"
mount --bind /proc "$WORK_DIR/external_root/proc"
mount --bind /sys "$WORK_DIR/external_root/sys"
mount --bind /run "$WORK_DIR/external_root/run"
# Install and configure bootloader in chroot
chroot "$WORK_DIR/external_root" /bin/bash -c "
# Ensure LVM is available
vgscan
vgchange -ay
# Update initramfs with LVM support
echo 'MODULES=dep' > /etc/initramfs-tools/initramfs.conf
echo 'BOOT=local' >> /etc/initramfs-tools/initramfs.conf
update-initramfs -u -k all
# Install GRUB with LVM support
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=debian --recheck $EXTERNAL_DRIVE
# Update GRUB configuration
update-grub
# Verify LVM tools are available
which lvm && echo 'LVM tools available'
ls -la /boot/initrd.img-* | head -1
"
# Unmount bind mounts
umount "$WORK_DIR/external_root/dev" 2>/dev/null || true
umount "$WORK_DIR/external_root/proc" 2>/dev/null || true
umount "$WORK_DIR/external_root/sys" 2>/dev/null || true
umount "$WORK_DIR/external_root/run" 2>/dev/null || true
success "Bootloader installed with LVM support"
}
verify_lvm_boot() {
log "Verifying LVM boot configuration..."
# Check if initramfs contains LVM modules
local initrd_file=$(ls "$WORK_DIR/external_boot/initrd.img-"* 2>/dev/null | head -1)
if [ -n "$initrd_file" ]; then
if lsinitramfs "$initrd_file" | grep -q "dm-mod\|lvm"; then
success "Initramfs contains LVM modules"
else
warning "Initramfs may be missing LVM modules"
fi
fi
# Check GRUB configuration
if grep -q "lvm" "$WORK_DIR/external_boot/grub/grub.cfg"; then
success "GRUB configuration includes LVM support"
else
warning "GRUB configuration may not have proper LVM support"
fi
# Check fstab
if grep -q "/dev/$VG_NAME" "$WORK_DIR/external_root/etc/fstab"; then
success "fstab configured for LVM"
else
warning "fstab configuration issue"
fi
success "LVM boot verification completed"
}
cleanup() {
log "Cleaning up..."
# Unmount filesystems
umount "$WORK_DIR/external_root/boot/efi" 2>/dev/null || true
umount "$WORK_DIR/external_root" 2>/dev/null || true
umount "$WORK_DIR/external_home" 2>/dev/null || true
umount "$WORK_DIR/external_boot" 2>/dev/null || true
umount "$WORK_DIR/internal_root" 2>/dev/null || true
umount "$WORK_DIR/internal_home" 2>/dev/null || true
# Close encrypted partitions
for mapper in /dev/mapper/migration_*; do
if [ -b "$mapper" ]; then
cryptsetup close "$(basename "$mapper")" 2>/dev/null || true
fi
done
success "Cleanup completed"
}
main() {
echo -e "${GREEN}=== Improved LVM Migration Script ===${NC}"
echo "This script migrates your system to LVM with proper boot configuration"
echo "Fixes the issues from the previous failed migration"
echo
check_prerequisites
detect_drives
analyze_source_system
echo
echo "Migration Summary:"
echo " Source: $INTERNAL_DRIVE (current system)"
echo " Target: $EXTERNAL_DRIVE (new LVM system)"
echo " VG Name: $VG_NAME"
echo
confirm_action "Start LVM migration?"
create_lvm_layout
handle_encrypted_partitions
mount_filesystems
copy_system_data
configure_lvm_system
install_bootloader
verify_lvm_boot
cleanup
success "LVM migration completed successfully!"
echo
echo -e "${GREEN}=== MIGRATION COMPLETE ===${NC}"
echo "✅ System migrated to LVM with proper boot support"
echo "✅ Initramfs configured with LVM modules"
echo "✅ GRUB installed with LVM support"
echo "✅ Boot configuration verified"
echo
echo -e "${BLUE}Next steps:${NC}"
echo "1. Reboot system"
echo "2. Set external drive as first boot device in BIOS"
echo "3. Boot should work without reset loops"
echo "4. System will ask for any encryption passwords as normal"
echo
echo -e "${YELLOW}🎉 Improved LVM migration completed!${NC}"
echo "This version fixes the boot issues from the previous attempt."
}
# Trap for cleanup
trap cleanup EXIT
main "$@"