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
This commit is contained in:
667
old_scripts/direct_clone_backup.sh
Executable file
667
old_scripts/direct_clone_backup.sh
Executable file
@@ -0,0 +1,667 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Direct 1-to-1 Clone Script
|
||||
# Creates an exact copy of internal drive to external drive without LVM conversion
|
||||
# Preserves all partitions, LUKS encryption, and boot structures exactly as they are
|
||||
# MUST BE RUN FROM A LIVE USB SYSTEM
|
||||
|
||||
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=""
|
||||
WORK_DIR="/mnt/clone_work"
|
||||
|
||||
# Partition mapping
|
||||
declare -A PARTITION_MAP
|
||||
declare -A PARTITION_FILESYSTEMS
|
||||
declare -A PARTITION_LABELS
|
||||
declare -A PARTITION_UUIDS
|
||||
|
||||
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..."
|
||||
|
||||
# Find all block devices that are disks (not partitions), excluding the live USB
|
||||
local all_drives=($(lsblk -dpno NAME,TYPE,SIZE,MODEL | grep "disk" | awk '{print $1}'))
|
||||
local drives=()
|
||||
|
||||
# Filter out the USB stick we're running from (if running from live system)
|
||||
for drive in "${all_drives[@]}"; do
|
||||
# Check if this drive contains the live system
|
||||
if mount | grep -q "$drive" && mount | grep -q "/lib/live\|/media.*live\|overlay"; then
|
||||
log "Excluding live USB drive: $drive"
|
||||
continue
|
||||
fi
|
||||
drives+=("$drive")
|
||||
done
|
||||
|
||||
if [ ${#drives[@]} -lt 2 ]; then
|
||||
error "Need at least 2 drives for cloning. Found only ${#drives[@]} suitable drives"
|
||||
echo "Available drives:"
|
||||
for drive in "${all_drives[@]}"; do
|
||||
local info=$(lsblk -dpno NAME,SIZE,MODEL "$drive" | awk '{print $2 " " $3}')
|
||||
echo " $drive - $info"
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Available drives for cloning:"
|
||||
for i in "${!drives[@]}"; do
|
||||
local drive="${drives[$i]}"
|
||||
local info=$(lsblk -dpno NAME,SIZE,MODEL "$drive" | awk '{print $2 " " $3}' | xargs)
|
||||
local is_usb=""
|
||||
|
||||
# Check if it's a USB drive
|
||||
if udevadm info --query=property --name="$drive" 2>/dev/null | grep -q "ID_BUS=usb"; then
|
||||
is_usb=" (USB)"
|
||||
fi
|
||||
|
||||
echo "$((i+1)). $drive - $info$is_usb"
|
||||
|
||||
# Show partition layout
|
||||
echo " Partitions:"
|
||||
lsblk "$drive" | tail -n +2 | sed 's/^/ /'
|
||||
echo
|
||||
done
|
||||
|
||||
# Auto-detection with user confirmation
|
||||
local suggested_internal=""
|
||||
local suggested_external=""
|
||||
|
||||
# Try to suggest internal drive (prefer NVMe, then non-USB drives)
|
||||
for drive in "${drives[@]}"; do
|
||||
if [[ "$drive" == *"nvme"* ]]; then
|
||||
suggested_internal="$drive"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$suggested_internal" ]; then
|
||||
# If no NVMe, prefer non-USB drives
|
||||
for drive in "${drives[@]}"; do
|
||||
if ! udevadm info --query=property --name="$drive" 2>/dev/null | grep -q "ID_BUS=usb"; then
|
||||
suggested_internal="$drive"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Try to suggest external drive (prefer USB, larger capacity)
|
||||
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 [ -z "$suggested_external" ]; then
|
||||
# If no USB found, use the other drive
|
||||
for drive in "${drives[@]}"; do
|
||||
if [ "$drive" != "$suggested_internal" ]; then
|
||||
suggested_external="$drive"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Show suggestions and get user confirmation
|
||||
if [ -n "$suggested_internal" ] && [ -n "$suggested_external" ]; then
|
||||
echo "Suggested configuration:"
|
||||
local internal_info=$(lsblk -dpno SIZE,MODEL "$suggested_internal" | xargs)
|
||||
local external_info=$(lsblk -dpno SIZE,MODEL "$suggested_external" | xargs)
|
||||
echo " Internal (source): $suggested_internal - $internal_info"
|
||||
echo " External (target): $suggested_external - $external_info"
|
||||
echo
|
||||
read -p "Use this configuration? [Y/n]: " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Nn]$ ]]; then
|
||||
# Manual selection
|
||||
INTERNAL_DRIVE=""
|
||||
EXTERNAL_DRIVE=""
|
||||
else
|
||||
INTERNAL_DRIVE="$suggested_internal"
|
||||
EXTERNAL_DRIVE="$suggested_external"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Manual selection if auto-detection failed or user declined
|
||||
if [ -z "$INTERNAL_DRIVE" ]; then
|
||||
echo "Select INTERNAL drive (source - your current system):"
|
||||
for i in "${!drives[@]}"; do
|
||||
local drive="${drives[$i]}"
|
||||
local info=$(lsblk -dpno SIZE,MODEL "$drive" | xargs)
|
||||
echo "$((i+1)). $drive - $info"
|
||||
done
|
||||
read -p "Enter number [1-${#drives[@]}]: " choice
|
||||
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "${#drives[@]}" ]; then
|
||||
INTERNAL_DRIVE="${drives[$((choice-1))]}"
|
||||
else
|
||||
error "Invalid selection"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$EXTERNAL_DRIVE" ]; then
|
||||
echo
|
||||
echo "Select EXTERNAL drive (target - will be completely overwritten!):"
|
||||
for i in "${!drives[@]}"; do
|
||||
local drive="${drives[$i]}"
|
||||
if [ "$drive" != "$INTERNAL_DRIVE" ]; then
|
||||
local info=$(lsblk -dpno SIZE,MODEL "$drive" | xargs)
|
||||
echo "$((i+1)). $drive - $info"
|
||||
fi
|
||||
done
|
||||
read -p "Enter number [1-${#drives[@]}]: " choice
|
||||
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "${#drives[@]}" ]; then
|
||||
local selected_drive="${drives[$((choice-1))]}"
|
||||
if [ "$selected_drive" != "$INTERNAL_DRIVE" ]; then
|
||||
EXTERNAL_DRIVE="$selected_drive"
|
||||
else
|
||||
error "Cannot use the same drive as both source and target!"
|
||||
fi
|
||||
else
|
||||
error "Invalid selection"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Final validation
|
||||
if [ "$INTERNAL_DRIVE" = "$EXTERNAL_DRIVE" ]; then
|
||||
error "Internal and external drives cannot be the same!"
|
||||
fi
|
||||
|
||||
# Check drive sizes
|
||||
local internal_size_bytes=$(lsblk -bno SIZE "$INTERNAL_DRIVE")
|
||||
local external_size_bytes=$(lsblk -bno SIZE "$EXTERNAL_DRIVE")
|
||||
|
||||
if [ "$external_size_bytes" -lt "$internal_size_bytes" ]; then
|
||||
error "External drive ($EXTERNAL_DRIVE) is smaller than internal drive ($INTERNAL_DRIVE). Cannot clone."
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Final drive selection:"
|
||||
echo " Internal (source): $INTERNAL_DRIVE ($(lsblk -dpno SIZE,MODEL "$INTERNAL_DRIVE" | xargs))"
|
||||
echo " External (target): $EXTERNAL_DRIVE ($(lsblk -dpno SIZE,MODEL "$EXTERNAL_DRIVE" | xargs))"
|
||||
|
||||
success "Drive detection completed"
|
||||
|
||||
# Final safety check
|
||||
echo
|
||||
echo -e "${RED}⚠️ FINAL SAFETY CHECK ⚠️${NC}"
|
||||
echo "You are about to COMPLETELY CLONE this drive:"
|
||||
echo " Source: $INTERNAL_DRIVE"
|
||||
echo " Target: $EXTERNAL_DRIVE (will be completely overwritten!)"
|
||||
echo
|
||||
echo "Current partitions on target drive that will be DESTROYED:"
|
||||
lsblk "$EXTERNAL_DRIVE" || true
|
||||
echo
|
||||
echo -e "${RED}This will DESTROY ALL DATA on $EXTERNAL_DRIVE!${NC}"
|
||||
echo "The entire drive will be overwritten with an exact copy of $INTERNAL_DRIVE"
|
||||
echo
|
||||
read -p "Type 'CLONE' to confirm you want to overwrite $EXTERNAL_DRIVE: " confirmation
|
||||
if [ "$confirmation" != "CLONE" ]; then
|
||||
error "Clone operation cancelled by user for safety"
|
||||
fi
|
||||
}
|
||||
|
||||
analyze_source_drive() {
|
||||
log "Analyzing source drive structure..."
|
||||
|
||||
# Get partition information
|
||||
local partitions=($(lsblk -pno NAME "$INTERNAL_DRIVE" | grep -v "^$INTERNAL_DRIVE$"))
|
||||
|
||||
echo "Source drive structure:"
|
||||
lsblk "$INTERNAL_DRIVE"
|
||||
echo
|
||||
|
||||
for part in "${partitions[@]}"; do
|
||||
local size=$(lsblk -no SIZE "$part")
|
||||
local fstype=$(lsblk -no FSTYPE "$part")
|
||||
local label=$(lsblk -no LABEL "$part")
|
||||
local uuid=$(lsblk -no UUID "$part")
|
||||
local mountpoint=$(lsblk -no MOUNTPOINT "$part")
|
||||
|
||||
echo " $part:"
|
||||
echo " Size: $size"
|
||||
echo " Filesystem: ${fstype:-'unknown'}"
|
||||
echo " Label: ${label:-'none'}"
|
||||
echo " UUID: ${uuid:-'none'}"
|
||||
echo " Mounted at: ${mountpoint:-'not mounted'}"
|
||||
|
||||
# Store information for later use
|
||||
PARTITION_FILESYSTEMS["$part"]="$fstype"
|
||||
PARTITION_LABELS["$part"]="$label"
|
||||
PARTITION_UUIDS["$part"]="$uuid"
|
||||
|
||||
# Check if it's encrypted
|
||||
if [[ "$fstype" == "crypto_LUKS" ]]; then
|
||||
log "Found LUKS encrypted partition: $part"
|
||||
fi
|
||||
|
||||
echo
|
||||
done
|
||||
|
||||
success "Source drive analysis completed"
|
||||
}
|
||||
|
||||
check_prerequisites() {
|
||||
log "Checking prerequisites..."
|
||||
|
||||
# Check if running from live system
|
||||
local root_device=$(df / | tail -1 | awk '{print $1}')
|
||||
if [[ "$root_device" == *"loop"* ]] || [[ "$root_device" == *"overlay"* ]] || [[ "$root_device" == *"tmpfs"* ]]; then
|
||||
success "Running from live system - good!"
|
||||
else
|
||||
warning "This might not be a live system. For safety, this should be run from a live USB!"
|
||||
confirm_action "Continue anyway? (Not recommended)"
|
||||
fi
|
||||
|
||||
# Check for required tools
|
||||
local missing_tools=()
|
||||
for tool in dd pv rsync cryptsetup grub-install lsblk blkid partprobe; do
|
||||
if ! command -v $tool >/dev/null 2>&1; then
|
||||
missing_tools+=("$tool")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#missing_tools[@]} -gt 0 ]; then
|
||||
warning "Missing required tools: ${missing_tools[*]}"
|
||||
log "Installing missing tools..."
|
||||
apt update && apt install -y util-linux pv rsync cryptsetup grub-common grub-efi-amd64 || {
|
||||
error "Failed to install required tools. Please install manually: ${missing_tools[*]}"
|
||||
}
|
||||
fi
|
||||
|
||||
success "Prerequisites check passed"
|
||||
}
|
||||
|
||||
perform_direct_clone() {
|
||||
log "Starting direct drive clone..."
|
||||
log "This will create an exact bit-for-bit copy of $INTERNAL_DRIVE to $EXTERNAL_DRIVE"
|
||||
|
||||
# Get drive size for progress tracking
|
||||
local total_size_bytes=$(lsblk -bno SIZE "$INTERNAL_DRIVE")
|
||||
local total_size_human=$(lsblk -no SIZE "$INTERNAL_DRIVE")
|
||||
|
||||
log "Cloning $total_size_human from $INTERNAL_DRIVE to $EXTERNAL_DRIVE"
|
||||
log "This will take a while depending on drive size and speed..."
|
||||
|
||||
# Use dd with progress monitoring via pv
|
||||
if dd if="$INTERNAL_DRIVE" bs=64M status=none | pv -s "$total_size_bytes" -w 80 | dd of="$EXTERNAL_DRIVE" bs=64M status=none; then
|
||||
success "Drive clone completed successfully"
|
||||
else
|
||||
error "Drive clone failed!"
|
||||
fi
|
||||
|
||||
# Force kernel to re-read partition table
|
||||
log "Updating partition table on cloned drive..."
|
||||
partprobe "$EXTERNAL_DRIVE" || warning "Failed to update partition table"
|
||||
sync
|
||||
sleep 3
|
||||
|
||||
success "Direct clone operation completed"
|
||||
}
|
||||
|
||||
fix_uuids_and_boot() {
|
||||
log "Fixing UUIDs and boot configuration on cloned drive..."
|
||||
|
||||
mkdir -p "$WORK_DIR"
|
||||
|
||||
# Get new partition list from cloned drive
|
||||
local new_partitions=($(lsblk -pno NAME "$EXTERNAL_DRIVE" | grep -v "^$EXTERNAL_DRIVE$"))
|
||||
|
||||
# Create new UUIDs for all partitions to avoid conflicts
|
||||
for part in "${new_partitions[@]}"; do
|
||||
local fstype=$(lsblk -no FSTYPE "$part")
|
||||
local old_uuid=$(lsblk -no UUID "$part")
|
||||
|
||||
log "Processing partition $part (filesystem: ${fstype:-'unknown'})"
|
||||
|
||||
# Skip encrypted partitions - they'll keep their UUID
|
||||
if [[ "$fstype" == "crypto_LUKS" ]]; then
|
||||
log "Skipping LUKS partition UUID change - encryption handles this"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Skip swap partitions for now - we'll handle them separately
|
||||
if [[ "$fstype" == "swap" ]]; then
|
||||
log "Regenerating swap UUID for $part"
|
||||
swapoff "$part" 2>/dev/null || true
|
||||
mkswap -U random "$part" || warning "Failed to regenerate swap UUID"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Generate new UUID for filesystem partitions
|
||||
if [[ -n "$fstype" && "$fstype" != "crypto_LUKS" ]]; then
|
||||
case "$fstype" in
|
||||
"ext2"|"ext3"|"ext4")
|
||||
log "Generating new UUID for ext filesystem on $part"
|
||||
tune2fs -U random "$part" || warning "Failed to change UUID for $part"
|
||||
;;
|
||||
"vfat")
|
||||
log "Generating new UUID for FAT filesystem on $part"
|
||||
# For FAT32, we'll use mlabel (part of mtools) if available, or skip
|
||||
if command -v mlabel >/dev/null 2>&1; then
|
||||
# Generate a random 8-character hex string for FAT32
|
||||
local new_fat_uuid=$(openssl rand -hex 4 | tr '[:lower:]' '[:upper:]')
|
||||
echo "mtools_skip_check=1" > ~/.mtoolsrc
|
||||
mlabel -i "$part" -N "${new_fat_uuid:0:8}" || warning "Failed to change FAT UUID"
|
||||
rm -f ~/.mtoolsrc
|
||||
else
|
||||
warning "Cannot change FAT UUID - mtools not available"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
log "Skipping UUID change for unknown filesystem type: $fstype"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
|
||||
# Now we need to update /etc/fstab on the cloned system
|
||||
log "Mounting cloned system to update configuration..."
|
||||
|
||||
# Find the root partition (usually the largest ext4 partition or check for typical structure)
|
||||
local root_partition=""
|
||||
local boot_partition=""
|
||||
local efi_partition=""
|
||||
|
||||
for part in "${new_partitions[@]}"; do
|
||||
local fstype=$(lsblk -no FSTYPE "$part")
|
||||
local size_bytes=$(lsblk -bno SIZE "$part")
|
||||
|
||||
# Detect EFI partition (usually small FAT32)
|
||||
if [[ "$fstype" == "vfat" && "$size_bytes" -lt 1073741824 ]]; then # Less than 1GB
|
||||
efi_partition="$part"
|
||||
log "Detected EFI partition: $part"
|
||||
fi
|
||||
|
||||
# Detect boot partition (usually ext4, smaller than root)
|
||||
if [[ "$fstype" == "ext4" && "$size_bytes" -lt 5368709120 && "$size_bytes" -gt 104857600 ]]; then # Between 100MB and 5GB
|
||||
boot_partition="$part"
|
||||
log "Detected boot partition: $part"
|
||||
fi
|
||||
|
||||
# Detect root partition (usually largest ext4 or check for encrypted)
|
||||
if [[ "$fstype" == "ext4" && "$size_bytes" -gt 5368709120 ]]; then # Larger than 5GB
|
||||
root_partition="$part"
|
||||
log "Detected root partition: $part"
|
||||
fi
|
||||
|
||||
# Handle LUKS encrypted partitions
|
||||
if [[ "$fstype" == "crypto_LUKS" ]]; then
|
||||
log "Found encrypted partition that might be root: $part"
|
||||
# We'll try to unlock it if needed
|
||||
local crypt_name="cloned_root_$(basename "$part")"
|
||||
echo "This appears to be an encrypted partition. Please enter the password to mount and update configuration:"
|
||||
if cryptsetup open "$part" "$crypt_name"; then
|
||||
# Check if the decrypted partition is the root
|
||||
local decrypted_fs=$(lsblk -no FSTYPE "/dev/mapper/$crypt_name")
|
||||
if [[ "$decrypted_fs" == "ext4" ]]; then
|
||||
root_partition="/dev/mapper/$crypt_name"
|
||||
log "Using decrypted partition as root: $root_partition"
|
||||
fi
|
||||
else
|
||||
warning "Could not unlock encrypted partition. Configuration update may be incomplete."
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$root_partition" ]; then
|
||||
warning "Could not automatically detect root partition. Manual configuration may be needed."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Mount the cloned root filesystem
|
||||
log "Mounting cloned root filesystem: $root_partition"
|
||||
mount "$root_partition" "$WORK_DIR" || {
|
||||
error "Failed to mount cloned root filesystem"
|
||||
}
|
||||
|
||||
# Mount boot partition if exists
|
||||
if [ -n "$boot_partition" ]; then
|
||||
log "Mounting boot partition: $boot_partition"
|
||||
mkdir -p "$WORK_DIR/boot"
|
||||
mount "$boot_partition" "$WORK_DIR/boot" || warning "Failed to mount boot partition"
|
||||
fi
|
||||
|
||||
# Mount EFI partition if exists
|
||||
if [ -n "$efi_partition" ]; then
|
||||
log "Mounting EFI partition: $efi_partition"
|
||||
mkdir -p "$WORK_DIR/boot/efi"
|
||||
mount "$efi_partition" "$WORK_DIR/boot/efi" || warning "Failed to mount EFI partition"
|
||||
fi
|
||||
|
||||
# Update /etc/fstab with new UUIDs
|
||||
if [ -f "$WORK_DIR/etc/fstab" ]; then
|
||||
log "Updating /etc/fstab with new UUIDs..."
|
||||
cp "$WORK_DIR/etc/fstab" "$WORK_DIR/etc/fstab.backup"
|
||||
|
||||
# Generate new fstab with current UUIDs
|
||||
echo "# /etc/fstab: static file system information." > "$WORK_DIR/etc/fstab.new"
|
||||
echo "# Updated after cloning $(date)" >> "$WORK_DIR/etc/fstab.new"
|
||||
echo "#" >> "$WORK_DIR/etc/fstab.new"
|
||||
echo "# <file system> <mount point> <type> <options> <dump> <pass>" >> "$WORK_DIR/etc/fstab.new"
|
||||
|
||||
# Add entries for each partition with current UUIDs
|
||||
for part in "${new_partitions[@]}"; do
|
||||
local current_uuid=$(lsblk -no UUID "$part")
|
||||
local fstype=$(lsblk -no FSTYPE "$part")
|
||||
|
||||
if [ -n "$current_uuid" ]; then
|
||||
case "$part" in
|
||||
*"1")
|
||||
if [[ "$fstype" == "vfat" ]]; then
|
||||
echo "UUID=$current_uuid /boot/efi vfat defaults 0 2" >> "$WORK_DIR/etc/fstab.new"
|
||||
fi
|
||||
;;
|
||||
*"2")
|
||||
if [[ "$fstype" == "ext4" ]]; then
|
||||
# Could be boot or root - determine by size
|
||||
local size_bytes=$(lsblk -bno SIZE "$part")
|
||||
if [ "$size_bytes" -lt 5368709120 ]; then # Less than 5GB = boot
|
||||
echo "UUID=$current_uuid /boot ext4 defaults 0 2" >> "$WORK_DIR/etc/fstab.new"
|
||||
else # Root partition
|
||||
echo "UUID=$current_uuid / ext4 defaults 0 1" >> "$WORK_DIR/etc/fstab.new"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
*"3")
|
||||
if [[ "$fstype" == "ext4" ]]; then
|
||||
echo "UUID=$current_uuid / ext4 defaults 0 1" >> "$WORK_DIR/etc/fstab.new"
|
||||
elif [[ "$fstype" == "swap" ]]; then
|
||||
echo "UUID=$current_uuid none swap sw 0 0" >> "$WORK_DIR/etc/fstab.new"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
if [[ "$fstype" == "swap" ]]; then
|
||||
echo "UUID=$current_uuid none swap sw 0 0" >> "$WORK_DIR/etc/fstab.new"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
|
||||
# Add tmpfs entry
|
||||
echo "tmpfs /tmp tmpfs defaults,noatime,mode=1777 0 0" >> "$WORK_DIR/etc/fstab.new"
|
||||
|
||||
# Replace old fstab with new one
|
||||
mv "$WORK_DIR/etc/fstab.new" "$WORK_DIR/etc/fstab"
|
||||
|
||||
success "Updated /etc/fstab with new UUIDs"
|
||||
else
|
||||
warning "/etc/fstab not found in cloned system"
|
||||
fi
|
||||
|
||||
success "UUID and boot configuration updated"
|
||||
}
|
||||
|
||||
install_bootloader() {
|
||||
log "Installing/repairing bootloader on cloned drive..."
|
||||
|
||||
# Bind mount necessary filesystems for chroot
|
||||
mount --bind /dev "$WORK_DIR/dev"
|
||||
mount --bind /proc "$WORK_DIR/proc"
|
||||
mount --bind /sys "$WORK_DIR/sys"
|
||||
mount --bind /run "$WORK_DIR/run"
|
||||
|
||||
# Update GRUB configuration and reinstall bootloader
|
||||
chroot "$WORK_DIR" /bin/bash -c "
|
||||
# Update initramfs to ensure all modules are included
|
||||
update-initramfs -u -k all
|
||||
|
||||
# Reinstall GRUB bootloader
|
||||
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=debian --recheck $EXTERNAL_DRIVE
|
||||
|
||||
# Update GRUB configuration
|
||||
update-grub
|
||||
" || warning "Some bootloader operations failed but continuing..."
|
||||
|
||||
# Unmount bind mounts
|
||||
umount "$WORK_DIR/dev" 2>/dev/null || true
|
||||
umount "$WORK_DIR/proc" 2>/dev/null || true
|
||||
umount "$WORK_DIR/sys" 2>/dev/null || true
|
||||
umount "$WORK_DIR/run" 2>/dev/null || true
|
||||
|
||||
success "Bootloader installation completed"
|
||||
}
|
||||
|
||||
cleanup_clone() {
|
||||
log "Cleaning up..."
|
||||
|
||||
# Unmount all filesystems
|
||||
umount "$WORK_DIR/boot/efi" 2>/dev/null || true
|
||||
umount "$WORK_DIR/boot" 2>/dev/null || true
|
||||
umount "$WORK_DIR" 2>/dev/null || true
|
||||
|
||||
# Close any encrypted volumes we opened
|
||||
for mapper in /dev/mapper/cloned_*; do
|
||||
if [ -b "$mapper" ]; then
|
||||
local crypt_name=$(basename "$mapper")
|
||||
cryptsetup close "$crypt_name" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove work directory
|
||||
rmdir "$WORK_DIR" 2>/dev/null || true
|
||||
|
||||
success "Cleanup completed"
|
||||
}
|
||||
|
||||
verify_clone() {
|
||||
log "Performing basic verification of cloned drive..."
|
||||
|
||||
# Check if partition table was copied correctly
|
||||
log "Verifying partition table..."
|
||||
local internal_partcount=$(lsblk -no NAME "$INTERNAL_DRIVE" | grep -c "^[├└]─")
|
||||
local external_partcount=$(lsblk -no NAME "$EXTERNAL_DRIVE" | grep -c "^[├└]─")
|
||||
|
||||
if [ "$internal_partcount" -eq "$external_partcount" ]; then
|
||||
success "Partition count matches: $internal_partcount partitions"
|
||||
else
|
||||
warning "Partition count mismatch: internal=$internal_partcount, external=$external_partcount"
|
||||
fi
|
||||
|
||||
# Show final layout
|
||||
echo
|
||||
echo "Original drive layout:"
|
||||
lsblk "$INTERNAL_DRIVE"
|
||||
echo
|
||||
echo "Cloned drive layout:"
|
||||
lsblk "$EXTERNAL_DRIVE"
|
||||
|
||||
success "Basic verification completed"
|
||||
}
|
||||
|
||||
main() {
|
||||
echo -e "${GREEN}=== Direct 1-to-1 Clone Script ===${NC}"
|
||||
echo "This script creates an exact copy of your internal drive to external drive"
|
||||
echo "WITHOUT any LVM conversion - preserves original structure exactly"
|
||||
echo "Run this from a live USB system for best results"
|
||||
echo
|
||||
|
||||
check_prerequisites
|
||||
detect_drives
|
||||
analyze_source_drive
|
||||
|
||||
echo
|
||||
echo "Clone Summary:"
|
||||
echo " Source: $INTERNAL_DRIVE"
|
||||
echo " Target: $EXTERNAL_DRIVE (will be completely overwritten)"
|
||||
echo " Operation: Bit-perfect clone preserving all partitions and structures"
|
||||
echo
|
||||
|
||||
confirm_action "WARNING: This will COMPLETELY OVERWRITE $EXTERNAL_DRIVE!"
|
||||
|
||||
perform_direct_clone
|
||||
fix_uuids_and_boot
|
||||
install_bootloader
|
||||
verify_clone
|
||||
cleanup_clone
|
||||
|
||||
success "Direct 1-to-1 clone completed successfully!"
|
||||
echo
|
||||
echo -e "${GREEN}=== CLONE COMPLETE ===${NC}"
|
||||
echo "✅ Exact copy created on external drive"
|
||||
echo "✅ UUIDs updated to prevent conflicts"
|
||||
echo "✅ Bootloader installed and configured"
|
||||
echo "✅ Original internal drive unchanged"
|
||||
echo
|
||||
echo -e "${BLUE}Next steps:${NC}"
|
||||
echo "1. Reboot your system"
|
||||
echo "2. Enter BIOS/UEFI settings and configure:"
|
||||
echo " • Set external drive as first boot device"
|
||||
echo " • Ensure UEFI mode is enabled (if system uses UEFI)"
|
||||
echo " • Disable Secure Boot if having issues"
|
||||
echo "3. Boot from external drive"
|
||||
echo "4. Should ask for LUKS password (if encrypted) and boot normally"
|
||||
echo
|
||||
echo -e "${GREEN}Your cloned system features:${NC}"
|
||||
echo "• Identical to original - same encryption, same structure"
|
||||
echo "• Independent UUIDs to avoid conflicts"
|
||||
echo "• Original internal drive preserved as backup"
|
||||
echo "• No LVM complexity - simple and reliable"
|
||||
echo
|
||||
echo -e "${YELLOW}🎉 Simple 1-to-1 clone completed successfully!${NC}"
|
||||
echo "The external drive should boot exactly like your original system!"
|
||||
}
|
||||
|
||||
# Trap to ensure cleanup on exit
|
||||
trap cleanup_clone EXIT
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user