Initial commit: Complete backup system with portable tools
- GUI and CLI backup/restore functionality - Auto-detection of internal system drive - Smart drive classification (internal vs external) - Reboot integration for clean backups/restores - Portable tools that survive cloning operations - Tool preservation system for external M.2 SSD - Complete disaster recovery workflow - Safety features and multiple confirmations - Desktop integration and launcher scripts - Comprehensive documentation
This commit is contained in:
338
backup_script.sh
Executable file
338
backup_script.sh
Executable file
@@ -0,0 +1,338 @@
|
||||
#!/bin/bash
|
||||
# System Backup Script - Command Line Version
|
||||
# For use with cron jobs or manual execution
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
SOURCE_DRIVE="" # Will be auto-detected
|
||||
TARGET_DRIVE="" # Will be detected or specified
|
||||
RESTORE_MODE=false # Restore mode flag
|
||||
LOG_FILE="/var/log/system_backup.log"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
local message="$1"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# Log to console
|
||||
echo "${timestamp} - ${message}"
|
||||
|
||||
# Log to file if writable
|
||||
if [[ -w "$LOG_FILE" ]] || [[ -w "$(dirname "$LOG_FILE")" ]]; then
|
||||
echo "${timestamp} - ${message}" >> "$LOG_FILE" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
# Error handling
|
||||
error_exit() {
|
||||
log "${RED}ERROR: $1${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Success message
|
||||
success() {
|
||||
log "${GREEN}SUCCESS: $1${NC}"
|
||||
}
|
||||
|
||||
# Warning message
|
||||
warning() {
|
||||
log "${YELLOW}WARNING: $1${NC}"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
check_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
error_exit "This script must be run as root (use sudo)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Detect root filesystem drive
|
||||
detect_root_drive() {
|
||||
log "Detecting root filesystem drive..."
|
||||
|
||||
# Find the device containing the root filesystem
|
||||
local root_device=$(df / | tail -1 | awk '{print $1}')
|
||||
|
||||
# Remove partition number to get base device
|
||||
local base_device=$(echo "$root_device" | sed 's/[0-9]*$//')
|
||||
|
||||
# Handle nvme drives (e.g., /dev/nvme0n1p1 -> /dev/nvme0n1)
|
||||
base_device=$(echo "$base_device" | sed 's/p$//')
|
||||
|
||||
echo "$base_device"
|
||||
}
|
||||
|
||||
# Detect external drives
|
||||
detect_external_drives() {
|
||||
log "Detecting external drives..."
|
||||
|
||||
# Get all block devices
|
||||
lsblk -d -n -o NAME,SIZE,TYPE,TRAN | while read -r line; do
|
||||
if [[ $line == *"disk"* ]] && [[ $line == *"usb"* ]]; then
|
||||
drive_name=$(echo "$line" | awk '{print $1}')
|
||||
drive_size=$(echo "$line" | awk '{print $2}')
|
||||
echo "/dev/$drive_name ($drive_size)"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Validate drives
|
||||
validate_drives() {
|
||||
if [[ ! -b "$SOURCE_DRIVE" ]]; then
|
||||
error_exit "Source drive $SOURCE_DRIVE does not exist or is not a block device"
|
||||
fi
|
||||
|
||||
if [[ ! -b "$TARGET_DRIVE" ]]; then
|
||||
error_exit "Target drive $TARGET_DRIVE does not exist or is not a block device"
|
||||
fi
|
||||
|
||||
if [[ "$SOURCE_DRIVE" == "$TARGET_DRIVE" ]]; then
|
||||
error_exit "Source and target drives cannot be the same"
|
||||
fi
|
||||
|
||||
# Check if target drive is mounted
|
||||
if mount | grep -q "$TARGET_DRIVE"; then
|
||||
warning "Target drive $TARGET_DRIVE is currently mounted. Unmounting..."
|
||||
umount "$TARGET_DRIVE"* 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
# Get drive size
|
||||
get_drive_size() {
|
||||
local drive=$1
|
||||
blockdev --getsize64 "$drive"
|
||||
}
|
||||
|
||||
# Clone drive
|
||||
clone_drive() {
|
||||
local source=$1
|
||||
local target=$2
|
||||
|
||||
log "Starting drive clone operation..."
|
||||
log "Source: $source"
|
||||
log "Target: $target"
|
||||
|
||||
# Get sizes
|
||||
source_size=$(get_drive_size "$source")
|
||||
target_size=$(get_drive_size "$target")
|
||||
|
||||
log "Source size: $(numfmt --to=iec-i --suffix=B $source_size)"
|
||||
log "Target size: $(numfmt --to=iec-i --suffix=B $target_size)"
|
||||
|
||||
if [[ $target_size -lt $source_size ]]; then
|
||||
error_exit "Target drive is smaller than source drive"
|
||||
fi
|
||||
|
||||
# Start cloning
|
||||
log "Starting clone operation with dd..."
|
||||
|
||||
if command -v pv >/dev/null 2>&1; then
|
||||
# Use pv for progress if available
|
||||
dd if="$source" bs=4M | pv -s "$source_size" | dd of="$target" bs=4M conv=fdatasync
|
||||
else
|
||||
# Use dd with status=progress
|
||||
dd if="$source" of="$target" bs=4M status=progress conv=fdatasync
|
||||
fi
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
success "Drive cloning completed successfully!"
|
||||
|
||||
# Restore backup tools to external drive if this was a backup operation
|
||||
if [[ "$RESTORE_MODE" != true ]]; then
|
||||
log "Preserving backup tools on external drive..."
|
||||
local restore_script="$(dirname "$0")/restore_tools_after_backup.sh"
|
||||
if [[ -f "$restore_script" ]]; then
|
||||
"$restore_script" "$target" || log "Warning: Could not preserve backup tools"
|
||||
else
|
||||
log "Warning: Tool preservation script not found"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Sync to ensure all data is written
|
||||
log "Syncing data to disk..."
|
||||
sync
|
||||
|
||||
# Verify partition table
|
||||
log "Verifying partition table on target drive..."
|
||||
fdisk -l "$target" | head -20 | tee -a "$LOG_FILE"
|
||||
|
||||
success "Backup verification completed!"
|
||||
else
|
||||
error_exit "Drive cloning failed!"
|
||||
fi
|
||||
}
|
||||
|
||||
# Create desktop entry
|
||||
create_desktop_entry() {
|
||||
local desktop_file="$HOME/Desktop/System-Backup.desktop"
|
||||
local script_path=$(realpath "$0")
|
||||
|
||||
cat > "$desktop_file" << EOF
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=System Backup
|
||||
Comment=Clone internal drive to external M.2 SSD
|
||||
Exec=gnome-terminal -- sudo "$script_path" --gui
|
||||
Icon=drive-harddisk
|
||||
Terminal=false
|
||||
Categories=System;Utility;
|
||||
EOF
|
||||
|
||||
chmod +x "$desktop_file"
|
||||
log "Desktop entry created: $desktop_file"
|
||||
}
|
||||
|
||||
# Show usage
|
||||
show_usage() {
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -s, --source DRIVE Source drive (auto-detected if not specified)"
|
||||
echo " -t, --target DRIVE Target drive (required)"
|
||||
echo " -r, --restore Restore mode (reverse source and target)"
|
||||
echo " -l, --list List available drives"
|
||||
echo " -d, --desktop Create desktop entry"
|
||||
echo " --gui Launch GUI version"
|
||||
echo " -h, --help Show this help"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 --list"
|
||||
echo " $0 --source /dev/sda --target /dev/sdb"
|
||||
echo " $0 --restore --source /dev/sdb --target /dev/sda"
|
||||
echo " $0 --desktop"
|
||||
echo " $0 --gui"
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
# Auto-detect source drive if not specified
|
||||
if [[ -z "$SOURCE_DRIVE" ]]; then
|
||||
SOURCE_DRIVE=$(detect_root_drive)
|
||||
log "Auto-detected root filesystem drive: $SOURCE_DRIVE"
|
||||
fi
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-s|--source)
|
||||
SOURCE_DRIVE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-t|--target)
|
||||
TARGET_DRIVE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-r|--restore)
|
||||
RESTORE_MODE=true
|
||||
shift
|
||||
;;
|
||||
-l|--list)
|
||||
echo "Available drives:"
|
||||
lsblk -d -o NAME,SIZE,TYPE,TRAN
|
||||
echo ""
|
||||
echo "External drives:"
|
||||
detect_external_drives
|
||||
exit 0
|
||||
;;
|
||||
-d|--desktop)
|
||||
create_desktop_entry
|
||||
exit 0
|
||||
;;
|
||||
--gui)
|
||||
python3 "$(dirname "$0")/backup_manager.py"
|
||||
exit 0
|
||||
;;
|
||||
-h|--help)
|
||||
show_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
error_exit "Unknown option: $1"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# In restore mode, swap source and target for user convenience
|
||||
if [[ "$RESTORE_MODE" == true ]]; then
|
||||
if [[ -n "$SOURCE_DRIVE" && -n "$TARGET_DRIVE" ]]; then
|
||||
log "Restore mode: swapping source and target drives"
|
||||
TEMP_DRIVE="$SOURCE_DRIVE"
|
||||
SOURCE_DRIVE="$TARGET_DRIVE"
|
||||
TARGET_DRIVE="$TEMP_DRIVE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if target drive is specified
|
||||
if [[ -z "$TARGET_DRIVE" ]]; then
|
||||
echo "Available external drives:"
|
||||
detect_external_drives
|
||||
echo ""
|
||||
read -p "Enter target drive (e.g., /dev/sdb): " TARGET_DRIVE
|
||||
|
||||
if [[ -z "$TARGET_DRIVE" ]]; then
|
||||
error_exit "Target drive not specified"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check root privileges
|
||||
check_root
|
||||
|
||||
# Validate drives
|
||||
validate_drives
|
||||
|
||||
# Confirm operation
|
||||
echo ""
|
||||
if [[ "$RESTORE_MODE" == true ]]; then
|
||||
echo "🚨 RESTORE CONFIGURATION 🚨"
|
||||
echo "This will RESTORE (overwrite target with source data):"
|
||||
else
|
||||
echo "BACKUP CONFIGURATION:"
|
||||
echo "This will BACKUP (copy source to target):"
|
||||
fi
|
||||
echo "Source: $SOURCE_DRIVE"
|
||||
echo "Target: $TARGET_DRIVE"
|
||||
echo ""
|
||||
echo "WARNING: All data on $TARGET_DRIVE will be DESTROYED!"
|
||||
|
||||
if [[ "$RESTORE_MODE" == true ]]; then
|
||||
echo ""
|
||||
echo "⚠️ CRITICAL WARNING FOR RESTORE MODE ⚠️"
|
||||
echo "You are about to OVERWRITE $TARGET_DRIVE"
|
||||
echo "Make sure this is what you intend to do!"
|
||||
echo ""
|
||||
read -p "Type 'RESTORE' to confirm or anything else to cancel: " confirm
|
||||
if [[ "$confirm" != "RESTORE" ]]; then
|
||||
log "Restore operation cancelled by user"
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
read -p "Are you sure you want to continue? (yes/no): " confirm
|
||||
if [[ "$confirm" != "yes" ]]; then
|
||||
log "Operation cancelled by user"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start operation
|
||||
if [[ "$RESTORE_MODE" == true ]]; then
|
||||
log "Starting system restore operation..."
|
||||
else
|
||||
log "Starting system backup operation..."
|
||||
fi
|
||||
clone_drive "$SOURCE_DRIVE" "$TARGET_DRIVE"
|
||||
|
||||
log "System backup completed successfully!"
|
||||
echo ""
|
||||
echo "Backup completed! You can now safely remove the external drive."
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user