Compare commits
15 Commits
d697a7bcb5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb076fd9b3 | ||
|
|
a8bc889e47 | ||
|
|
bfd3c08fb3 | ||
|
|
543b95cb61 | ||
|
|
7708b98674 | ||
|
|
f12d07be7f | ||
|
|
c86fee78cb | ||
|
|
aee3d5019c | ||
|
|
179a84e442 | ||
|
|
72f9838f55 | ||
|
|
871a57947d | ||
|
|
56c07dbe49 | ||
|
|
29347fc8a6 | ||
| a02628b22e | |||
| ab4a99b978 |
479
README.md
479
README.md
@@ -1,391 +1,144 @@
|
||||
# System Backup to External M.2 SSD
|
||||
# Enhanced Simple LVM Backup System with Borg Support
|
||||
|
||||
A comprehensive backup solution for Linux systems that provides both GUI and command-line interfaces for cloning your internal drive to an external M.2 SSD.
|
||||
A reliable, straightforward backup system for LVM-enabled Linux systems. Born from the need for simple, dependable backups without complex logic that can cause system issues.
|
||||
|
||||
## Philosophy
|
||||
|
||||
**Simple is better.** This system does exactly three things:
|
||||
1. Create LVM snapshot
|
||||
2. Copy data (dd/pv for raw, Borg for repositories)
|
||||
3. Clean up snapshot
|
||||
|
||||
No complex migration logic, no fancy features that can break. Just reliable backups.
|
||||
|
||||
## Features
|
||||
|
||||
## Features
|
||||
### Five Backup Modes
|
||||
|
||||
- **GUI Interface**: User-friendly graphical interface built with Python Tkinter
|
||||
- **Command Line Interface**: Full CLI support for automated and scripted operations
|
||||
- **Smart Drive Detection**: Automatically detects internal drive and external M.2 SSDs
|
||||
- **Full System Backup**: Complete drive cloning with dd for exact system replication
|
||||
- **Smart Sync Backup**: ⚡ NEW! Fast incremental backups using rsync for minor changes
|
||||
- **Change Analysis**: Analyze filesystem changes to recommend sync vs full backup
|
||||
- **Restore Functionality**: Complete system restore from external drive
|
||||
- **Portable Tools**: Backup tools survive on external drive and remain accessible after cloning
|
||||
- **Reboot Integration**: Optional reboot before backup/restore operations
|
||||
- **Progress Monitoring**: Real-time progress display and logging
|
||||
- **Safety Features**: Multiple confirmations and drive validation
|
||||
- **Desktop Integration**: Create desktop shortcuts for easy access
|
||||
- **LV → LV**: Update existing logical volume backups
|
||||
- **LV → Raw**: Create fresh backups on raw devices
|
||||
- **VG → Raw**: Clone entire volume groups with LVM metadata
|
||||
- **LV → Borg**: Backup logical volume **block device** to Borg repository (preserves exact block-level state)
|
||||
- **VG → Borg**: Backup **all block devices** from volume group to Borg repository
|
||||
|
||||
### Two Interfaces
|
||||
|
||||
- **GUI**: `simple_backup_gui.py` - User-friendly interface with Borg configuration
|
||||
- **CLI**: `enhanced_simple_backup.sh` - Command-line power user interface
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. See Available Options
|
||||
```bash
|
||||
sudo ./list_drives.sh
|
||||
```
|
||||
|
||||
### 2. Choose Your Backup Mode
|
||||
|
||||
**Update existing backup:**
|
||||
```bash
|
||||
sudo ./enhanced_simple_backup.sh lv-to-lv /dev/internal-vg/root /dev/backup-vg/root
|
||||
```
|
||||
|
||||
**Fresh backup to external drive:**
|
||||
```bash
|
||||
sudo ./enhanced_simple_backup.sh lv-to-raw /dev/internal-vg/root /dev/sdb
|
||||
```
|
||||
|
||||
**Clone entire system:**
|
||||
```bash
|
||||
sudo ./enhanced_simple_backup.sh vg-to-raw internal-vg /dev/sdb
|
||||
```
|
||||
|
||||
**Use the GUI:**
|
||||
```bash
|
||||
sudo python3 simple_backup_gui.py
|
||||
```
|
||||
|
||||
**Borg repository backups (block-level):**
|
||||
```bash
|
||||
# Create new Borg repo and backup LV as block device
|
||||
sudo ./enhanced_simple_backup.sh lv-to-borg /dev/internal-vg/root /path/to/borg/repo --new-repo --encryption repokey --passphrase mypass
|
||||
|
||||
# Backup entire VG as multiple block devices to existing repo
|
||||
sudo ./enhanced_simple_backup.sh vg-to-borg internal-vg /path/to/borg/repo --passphrase mypass
|
||||
```
|
||||
|
||||
**Key Difference:**
|
||||
- **LV → Borg**: Stores one snapshot as `{lv_name}.img` in a single archive
|
||||
- **VG → Borg**: Creates separate archives for each LV (space-efficient, processes one LV at a time)
|
||||
|
||||
## Files
|
||||
|
||||
### Core System
|
||||
- `simple_backup_gui.py` - GUI interface with mode selection
|
||||
- `enhanced_simple_backup.sh` - CLI with three backup modes
|
||||
- `list_drives.sh` - Helper to show available sources/targets
|
||||
- `SIMPLE_BACKUP_README.md` - Detailed documentation
|
||||
|
||||
### Legacy Files
|
||||
- `old_scripts/` - Previous complex implementations (archived)
|
||||
- Various `.md` files - Documentation from previous iterations
|
||||
|
||||
## Requirements
|
||||
|
||||
- Linux system (tested on Ubuntu/Debian)
|
||||
- Python 3.6+ with tkinter
|
||||
- External M.2 SSD in USB enclosure
|
||||
- sudo privileges for drive operations
|
||||
|
||||
## Installation
|
||||
|
||||
1. Clone or download this repository:
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd backup_to_external_m.2
|
||||
```
|
||||
|
||||
2. Make scripts executable:
|
||||
```bash
|
||||
chmod +x backup_script.sh
|
||||
chmod +x backup_manager.py
|
||||
```
|
||||
|
||||
3. Install dependencies (if needed):
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install python3-tk pv parted
|
||||
```
|
||||
|
||||
4. **Optional**: Set up portable tools on external M.2 SSD:
|
||||
```bash
|
||||
./setup_portable_tools.sh
|
||||
```
|
||||
This creates a separate partition on your external drive that preserves backup tools even after cloning.
|
||||
|
||||
## Usage
|
||||
|
||||
### GUI Application
|
||||
|
||||
Launch the graphical backup manager:
|
||||
|
||||
```bash
|
||||
python3 backup_manager.py
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Automatic drive detection and classification
|
||||
- Source and target drive selection with smart defaults
|
||||
- Real-time progress monitoring
|
||||
- **Backup Modes**:
|
||||
- **Smart Sync Backup**: ⚡ Fast incremental backup using rsync (requires existing backup)
|
||||
- **Analyze Changes**: Analyze what has changed since last backup
|
||||
- **Start Backup**: Full drive clone (immediate backup while system running)
|
||||
- **Reboot & Backup**: Reboot system then full backup (recommended for first backup)
|
||||
- **Restore Modes**:
|
||||
- **Restore from External**: Immediate restore from external to internal
|
||||
- **Reboot & Restore**: Reboot system then restore (recommended)
|
||||
- **Drive Swap Button**: Easily swap source and target for restore operations
|
||||
|
||||
### Command Line Script
|
||||
|
||||
For command-line usage:
|
||||
|
||||
```bash
|
||||
# List available drives
|
||||
./backup_script.sh --list
|
||||
|
||||
# Analyze changes without performing backup
|
||||
./backup_script.sh --analyze --target /dev/sdb
|
||||
|
||||
# Smart sync backup (fast incremental update)
|
||||
sudo ./backup_script.sh --sync --target /dev/sdb
|
||||
|
||||
# Perform full backup with specific drives
|
||||
sudo ./backup_script.sh --source /dev/nvme0n1 --target /dev/sda
|
||||
|
||||
# Restore from external to internal (note the restore flag)
|
||||
sudo ./backup_script.sh --restore --source /dev/sda --target /dev/nvme0n1
|
||||
|
||||
# Or more simply in restore mode (source/target are swapped automatically)
|
||||
sudo ./backup_script.sh --restore --source /dev/nvme0n1 --target /dev/sda
|
||||
|
||||
# Create desktop entry
|
||||
./backup_script.sh --desktop
|
||||
|
||||
# Launch GUI from script
|
||||
./backup_script.sh --gui
|
||||
```
|
||||
|
||||
### Desktop Integration
|
||||
|
||||
Create a desktop shortcut:
|
||||
|
||||
```bash
|
||||
./backup_script.sh --desktop
|
||||
```
|
||||
|
||||
This creates a clickable icon on your desktop that launches the backup tool.
|
||||
|
||||
## Restore Operations
|
||||
|
||||
### When to Restore
|
||||
- **System Failure**: Internal drive crashed or corrupted
|
||||
- **System Migration**: Moving to new hardware
|
||||
- **Rollback**: Reverting to previous system state
|
||||
- **Testing**: Restoring test environment
|
||||
|
||||
### How to Restore
|
||||
|
||||
#### GUI Method
|
||||
1. **Connect External Drive**: Plug in your M.2 SSD with backup
|
||||
2. **Launch GUI**: `python3 backup_manager.py`
|
||||
3. **Check Drive Selection**:
|
||||
- Source should be external drive (your backup)
|
||||
- Target should be internal drive (will be overwritten)
|
||||
4. **Use Swap Button**: If needed, click "Swap Source↔Target"
|
||||
5. **Choose Restore Mode**:
|
||||
- **"Restore from External"**: Immediate restore
|
||||
- **"Reboot & Restore"**: Reboot then restore (recommended)
|
||||
|
||||
#### Command Line Method
|
||||
```bash
|
||||
# Restore with automatic drive detection
|
||||
sudo ./backup_script.sh --restore
|
||||
|
||||
# Restore with specific drives
|
||||
sudo ./backup_script.sh --restore --source /dev/sda --target /dev/nvme0n1
|
||||
```
|
||||
|
||||
### ⚠️ Restore Safety Warnings
|
||||
- **Data Loss**: Target drive is completely overwritten
|
||||
- **Irreversible**: No undo after restore starts
|
||||
- **Multiple Confirmations**: System requires explicit confirmation
|
||||
- **Drive Verification**: Double-check source and target drives
|
||||
- **Boot Issues**: Ensure external drive contains valid system backup
|
||||
|
||||
## Portable Tools (Boot from External M.2)
|
||||
|
||||
### Setup Portable Tools
|
||||
Run this once to set up backup tools on your external M.2 SSD:
|
||||
|
||||
```bash
|
||||
./setup_portable_tools.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
- Create a 512MB tools partition on your external drive
|
||||
- Install backup tools that survive cloning operations
|
||||
- Set up automatic tool restoration after each backup
|
||||
|
||||
### Using Portable Tools
|
||||
|
||||
#### When Booted from External M.2 SSD:
|
||||
|
||||
1. **Access Tools**:
|
||||
```bash
|
||||
# Easy access helper
|
||||
./access_tools.sh
|
||||
|
||||
# Or manually:
|
||||
sudo mount LABEL=BACKUP_TOOLS /mnt/tools
|
||||
cd /mnt/tools/backup_system
|
||||
./launch_backup_tools.sh
|
||||
```
|
||||
|
||||
2. **Restore Internal Drive**:
|
||||
- Launch backup tools from external drive
|
||||
- External drive (source) → Internal drive (target)
|
||||
- Click "Reboot & Restore" for safest operation
|
||||
|
||||
3. **Create Desktop Entry**:
|
||||
```bash
|
||||
cd /mnt/tools/backup_system
|
||||
./create_desktop_entry.sh
|
||||
```
|
||||
|
||||
### Disaster Recovery Workflow
|
||||
|
||||
1. **Normal Operation**: Internal drive fails
|
||||
2. **Boot from External**: Use M.2 SSD as boot drive
|
||||
3. **Access Tools**: Run `./access_tools.sh`
|
||||
4. **Restore System**: Use backup tools to restore to new internal drive
|
||||
5. **Back to Normal**: Boot from restored internal drive
|
||||
- LVM-enabled Linux system
|
||||
- Root access (`sudo`)
|
||||
- Python 3 + tkinter (for GUI)
|
||||
- `pv` command (optional, for progress display)
|
||||
- **Borg Backup** (for Borg modes): `sudo apt install borgbackup`
|
||||
|
||||
## Safety Features
|
||||
|
||||
- **Drive Validation**: Prevents accidental overwriting of wrong drives
|
||||
- **Size Checking**: Ensures target drive is large enough
|
||||
- **Confirmation Prompts**: Multiple confirmation steps before destructive operations
|
||||
- **Mount Detection**: Automatically unmounts target drives before backup
|
||||
- **Progress Monitoring**: Real-time feedback during backup operations
|
||||
- Clear confirmations before destructive operations
|
||||
- Automatic snapshot cleanup on errors
|
||||
- Emergency stop functionality (GUI)
|
||||
- Input validation and error handling
|
||||
- Timestamped snapshot names to avoid collisions on retries
|
||||
- Read-only snapshot mounts with filesystem-aware safety flags (ext4: noload, xfs: norecovery)
|
||||
- Strict shell mode in CLI (set -euo pipefail) for reliable error propagation
|
||||
- Pre-flight checks for VG free space before creating snapshots
|
||||
|
||||
## File Structure
|
||||
## What This System Does NOT Do
|
||||
|
||||
```
|
||||
backup_to_external_m.2/
|
||||
├── backup_manager.py # GUI application
|
||||
├── backup_script.sh # Command-line script
|
||||
├── install.sh # Installation script
|
||||
├── systemd/ # Systemd service files
|
||||
│ └── backup-service.service
|
||||
└── README.md # This file
|
||||
```
|
||||
- Complex migration between different LVM setups
|
||||
- Automatic boot repair or GRUB handling
|
||||
- Multiple backup formats or compression
|
||||
- Network backups or cloud integration
|
||||
- File-level incremental backups
|
||||
|
||||
## Smart Sync Technology ⚡
|
||||
For those features, use dedicated tools like Borg Backup, CloneZilla, or rsync.
|
||||
|
||||
The backup system now includes advanced **Smart Sync** functionality that dramatically reduces backup time for incremental updates:
|
||||
## Why This Approach?
|
||||
|
||||
### How Smart Sync Works
|
||||
Previous versions of this project included complex migration logic that occasionally caused system issues. This version returns to basics:
|
||||
|
||||
1. **Analysis Phase**: Compares source and target filesystems to determine changes
|
||||
2. **Decision Engine**: Recommends sync vs full clone based on amount of changes:
|
||||
- **< 2GB changes**: Smart sync recommended (much faster)
|
||||
- **2-10GB changes**: Smart sync beneficial
|
||||
- **> 10GB changes**: Full clone may be more appropriate
|
||||
3. **Sync Operation**: Uses rsync to transfer only changed files and metadata
|
||||
✅ Uses standard LVM commands only
|
||||
✅ Minimal chance of errors
|
||||
✅ Easy to understand and debug
|
||||
✅ Predictable behavior
|
||||
✅ No hidden "smart" features
|
||||
|
||||
### Smart Sync Benefits
|
||||
Notes and limitations:
|
||||
- Multi-PV VGs: Raw clone and VG→Borg flows require careful handling. If a VG spans multiple PVs, the simple raw clone path is not supported; use block-level per-LV backups or full-disk cloning tools.
|
||||
- File-level backups are for data safety, not bootability. Use block-level backups for exact bootable images.
|
||||
- Ensure the Borg repository is not located on the same LV being snapshotted.
|
||||
|
||||
- **Speed**: 10-100x faster than full clone for minor changes
|
||||
- **No Downtime**: System remains usable during sync operation
|
||||
- **Efficiency**: Only transfers changed data, preserving bandwidth and storage wear
|
||||
- **Safety**: Preserves backup tools and maintains full system consistency
|
||||
## Recovery
|
||||
|
||||
### When to Use Smart Sync vs Full Clone
|
||||
### From LV Backup
|
||||
Mount the backup LV and copy files, or use it to restore your system.
|
||||
|
||||
**Use Smart Sync when:**
|
||||
- You have an existing backup on the target drive
|
||||
- Regular incremental updates (daily/weekly backups)
|
||||
- Minimal system changes since last backup
|
||||
- You want faster backup with minimal downtime
|
||||
### From Raw Device Backup
|
||||
Boot directly from the device, or restore using dd in reverse.
|
||||
|
||||
**Use Full Clone when:**
|
||||
- First-time backup to a new drive
|
||||
- Major system changes (OS upgrade, large software installations)
|
||||
- Corrupted or incomplete previous backup
|
||||
- Maximum compatibility and reliability needed
|
||||
|
||||
### Smart Sync Usage
|
||||
|
||||
**GUI Method:**
|
||||
1. Click "Analyze Changes" to see what has changed
|
||||
2. Review the recommendation and estimated time savings
|
||||
3. Click "Smart Sync Backup" to perform incremental update
|
||||
|
||||
**Command Line:**
|
||||
```bash
|
||||
# Analyze changes first
|
||||
./backup_script.sh --analyze --target /dev/sdb
|
||||
|
||||
# Perform smart sync
|
||||
sudo ./backup_script.sh --sync --target /dev/sdb
|
||||
```
|
||||
|
||||
## Traditional Full Backup
|
||||
|
||||
For comprehensive system backup, the system uses proven `dd` cloning technology:
|
||||
|
||||
### Backup Process
|
||||
1. **Drive Detection**: Automatically scans for available drives
|
||||
2. **Auto-Selection**: Internal drive as source, external as target
|
||||
3. **Operation Selection**: Choose backup or restore mode
|
||||
4. **Validation**: Confirms drives exist and are different
|
||||
5. **Execution**: Uses `dd` command to clone entire drive
|
||||
6. **Progress**: Shows real-time progress and logging
|
||||
|
||||
### Backup vs Restore
|
||||
- **Backup**: Internal → External (preserves your system on external drive)
|
||||
- **Restore**: External → Internal (overwrites internal with backup data)
|
||||
- **Swap Button**: Easily switch source/target for restore operations
|
||||
|
||||
### Reboot vs Immediate Operations
|
||||
- **Immediate**: Faster start, but system is running (potential file locks)
|
||||
- **Reboot Mode**: System restarts first, then operates (cleaner, more reliable)
|
||||
- **Recommendation**: Use reboot mode for critical operations
|
||||
|
||||
### Reboot & Backup Mode
|
||||
1. **Script Creation**: Creates a backup script for post-reboot execution
|
||||
2. **Reboot Scheduling**: Schedules system reboot
|
||||
3. **Auto-Execution**: Backup starts automatically after reboot
|
||||
4. **Notification**: Shows completion status
|
||||
|
||||
### Command Line Mode
|
||||
- Direct `dd` cloning with progress monitoring
|
||||
- Comprehensive logging to `/var/log/system_backup.log`
|
||||
- Drive size validation and safety checks
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Backup Method
|
||||
- Uses `dd` command for bit-perfect drive cloning
|
||||
- Block size optimized at 4MB for performance
|
||||
- `fdatasync` ensures all data is written to disk
|
||||
|
||||
### Drive Detection
|
||||
- Uses `lsblk` to enumerate block devices
|
||||
- Filters for actual disk drives
|
||||
- Shows drive sizes for easy identification
|
||||
|
||||
### Safety Mechanisms
|
||||
- Root privilege verification
|
||||
- Block device validation
|
||||
- Source/target drive comparison
|
||||
- Mount status checking
|
||||
- Size compatibility verification
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Permission Denied**
|
||||
```bash
|
||||
# Run with sudo for drive operations
|
||||
sudo python3 backup_manager.py
|
||||
```
|
||||
|
||||
2. **Drive Not Detected**
|
||||
```bash
|
||||
# Check if drive is connected and recognized
|
||||
lsblk
|
||||
dmesg | tail
|
||||
```
|
||||
|
||||
3. **Backup Fails**
|
||||
```bash
|
||||
# Check system logs
|
||||
sudo journalctl -f
|
||||
tail -f /var/log/system_backup.log
|
||||
```
|
||||
|
||||
### Performance Tips
|
||||
|
||||
- Use USB 3.0+ connection for external M.2 SSD
|
||||
- Ensure sufficient power supply for external enclosure
|
||||
- Close unnecessary applications during backup
|
||||
- Use SSD for better performance than traditional HDD
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- **Data Destruction**: Target drive data is completely overwritten
|
||||
- **Root Access**: Scripts require elevated privileges
|
||||
- **Verification**: Always verify backup integrity after completion
|
||||
- **Testing**: Test restore process with non-critical data first
|
||||
|
||||
## Limitations
|
||||
|
||||
- Only works with block devices (entire drives)
|
||||
- Cannot backup to smaller drives
|
||||
- Requires manual drive selection for safety
|
||||
- No incremental backup support (full clone only)
|
||||
### From VG Clone
|
||||
Import the volume group or boot directly from the cloned device.
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Test thoroughly
|
||||
5. Submit a pull request
|
||||
Keep it simple. Any additions should follow the core principle: minimal logic, maximum reliability.
|
||||
|
||||
## License
|
||||
|
||||
This project is open source. Use at your own risk.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
**WARNING**: This tool performs low-level disk operations that can result in data loss. Always:
|
||||
- Verify drive selections carefully
|
||||
- Test with non-critical data first
|
||||
- Maintain separate backups of important data
|
||||
- Understand the risks involved
|
||||
|
||||
The authors are not responsible for any data loss or system damage.
|
||||
Open source - use and modify as needed.
|
||||
119
SIMPLE_BACKUP_README.md
Normal file
119
SIMPLE_BACKUP_README.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Simple LVM Backup System
|
||||
|
||||
**A much simpler, safer approach to LVM backups.**
|
||||
|
||||
After issues with complex backup systems, this is a return to basics:
|
||||
- Simple LVM snapshot creation
|
||||
- Direct block-level copy with dd/pv
|
||||
- Minimal logic, maximum reliability
|
||||
|
||||
## What This Does
|
||||
|
||||
1. **Creates an LVM snapshot** of your source volume
|
||||
2. **Copies it block-for-block** to your target drive
|
||||
3. **Cleans up the snapshot** when done
|
||||
|
||||
That's it. No complex migration logic, no fancy features that can break.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. See what's available
|
||||
```bash
|
||||
sudo ./list_drives.sh
|
||||
```
|
||||
|
||||
### 2. Run a simple backup
|
||||
```bash
|
||||
sudo ./simple_backup.sh /dev/your-vg/your-lv /dev/your-target-drive
|
||||
```
|
||||
|
||||
### 3. Or use the GUI
|
||||
```bash
|
||||
sudo python3 simple_backup_gui.py
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
**Backup your root volume to an external SSD:**
|
||||
```bash
|
||||
sudo ./simple_backup.sh /dev/internal-vg/root /dev/sdb
|
||||
```
|
||||
|
||||
**Backup your home volume:**
|
||||
```bash
|
||||
sudo ./simple_backup.sh /dev/internal-vg/home /dev/nvme1n1
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
⚠️ **WARNING: The target drive will be completely overwritten!**
|
||||
|
||||
- Always run as root (`sudo`)
|
||||
- Target device will lose ALL existing data
|
||||
- Make sure target device is unmounted before backup
|
||||
- The backup is a complete block-level clone
|
||||
- You need at least 1GB free space in your volume group for the snapshot
|
||||
|
||||
## How It Works
|
||||
|
||||
This is exactly what happens, no hidden complexity:
|
||||
|
||||
1. `lvcreate -L1G -s -n backup_snap /dev/vg/lv` - Create snapshot
|
||||
2. `pv /dev/vg/backup_snap | dd of=/dev/target bs=4M` - Copy data
|
||||
3. `lvremove -f /dev/vg/backup_snap` - Remove snapshot
|
||||
|
||||
## Files
|
||||
|
||||
- `simple_backup.sh` - Command-line backup script
|
||||
- `simple_backup_gui.py` - Simple GUI version
|
||||
- `list_drives.sh` - Helper to show available drives
|
||||
|
||||
## Why This Approach?
|
||||
|
||||
The previous complex scripts had too much logic and caused system issues. This approach:
|
||||
|
||||
- ✅ Uses standard LVM commands
|
||||
- ✅ Minimal chance of errors
|
||||
- ✅ Easy to understand and debug
|
||||
- ✅ Does exactly what you expect
|
||||
- ✅ No hidden "smart" features
|
||||
|
||||
## Recovery
|
||||
|
||||
To boot from your backup:
|
||||
1. Connect the external drive
|
||||
2. Boot from it directly, or
|
||||
3. Use it as a recovery drive to restore your system
|
||||
|
||||
## Requirements
|
||||
|
||||
- LVM-enabled system
|
||||
- Root access
|
||||
- Python 3 + tkinter (for GUI)
|
||||
- `pv` command (optional, for progress display)
|
||||
|
||||
## If Something Goes Wrong
|
||||
|
||||
The script will try to clean up snapshots automatically. If it fails:
|
||||
|
||||
```bash
|
||||
# List any remaining snapshots
|
||||
sudo lvs | grep snap
|
||||
|
||||
# Remove manually if needed
|
||||
sudo lvremove /dev/vg/snapshot_name
|
||||
```
|
||||
|
||||
## No More Complex Features
|
||||
|
||||
This system intentionally does NOT include:
|
||||
- Automatic drive detection with complex logic
|
||||
- Migration between different LVM setups
|
||||
- Boot repair or GRUB handling
|
||||
- Multiple backup formats
|
||||
- Configuration files
|
||||
- Complex error handling
|
||||
|
||||
If you need those features, use dedicated tools like CloneZilla or Borg Backup.
|
||||
|
||||
This is for simple, reliable block-level LVM backups. Nothing more, nothing less.
|
||||
Binary file not shown.
1079
backup_manager.py
1079
backup_manager.py
File diff suppressed because it is too large
Load Diff
599
enhanced_simple_backup.sh
Executable file
599
enhanced_simple_backup.sh
Executable file
@@ -0,0 +1,599 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Enhanced Simple LVM Backup Script
|
||||
# Supports three backup modes:
|
||||
# 1. LV → LV: Update existing logical volume backup
|
||||
# 2. LV → Raw: Create fresh backup on raw device
|
||||
# 3. VG → Raw: Clone entire volume group to raw device
|
||||
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
usage() {
|
||||
echo "Enhanced Simple LVM Backup Script with Borg Support"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo " $0 lv-to-lv SOURCE_LV TARGET_LV"
|
||||
echo " $0 lv-to-raw SOURCE_LV TARGET_DEVICE"
|
||||
echo " $0 vg-to-raw SOURCE_VG TARGET_DEVICE"
|
||||
echo " $0 lv-to-borg SOURCE_LV REPO_PATH [--new-repo] [--encryption MODE] [--passphrase PASS]"
|
||||
echo " $0 vg-to-borg SOURCE_VG REPO_PATH [--new-repo] [--encryption MODE] [--passphrase PASS]"
|
||||
echo " $0 files-to-borg SOURCE_LV REPO_PATH [--new-repo] [--encryption MODE] [--passphrase PASS]"
|
||||
echo ""
|
||||
echo "Modes:"
|
||||
echo " lv-to-lv - Update existing LV backup (SOURCE_LV → TARGET_LV)"
|
||||
echo " lv-to-raw - Create fresh backup (SOURCE_LV → raw device)"
|
||||
echo " vg-to-raw - Clone entire VG (SOURCE_VG → raw device)"
|
||||
echo " lv-to-borg - Backup LV to Borg repository (block-level)"
|
||||
echo " vg-to-borg - Backup entire VG to Borg repository (block-level)"
|
||||
echo " files-to-borg - Backup LV files to Borg repository (file-level, space-efficient)"
|
||||
echo ""
|
||||
echo "Borg Options:"
|
||||
echo " --new-repo Create new repository"
|
||||
echo " --encryption MODE Encryption mode: none, repokey, keyfile (default: repokey)"
|
||||
echo " --passphrase PASS Repository passphrase"
|
||||
echo " --generous-snapshots Use 25% of LV size for snapshots (for very active systems)"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 lv-to-lv /dev/internal-vg/root /dev/backup-vg/root"
|
||||
echo " $0 lv-to-raw /dev/internal-vg/root /dev/sdb"
|
||||
echo " $0 vg-to-raw internal-vg /dev/sdb"
|
||||
echo " $0 lv-to-borg /dev/internal-vg/root /path/to/borg/repo --new-repo"
|
||||
echo " $0 files-to-borg /dev/internal-vg/home /path/to/borg/repo --new-repo"
|
||||
echo ""
|
||||
echo "List available sources/targets:"
|
||||
echo " ./list_drives.sh"
|
||||
echo ""
|
||||
exit 1
|
||||
}
|
||||
|
||||
log() {
|
||||
echo -e "${GREEN}[$(date '+%H:%M:%S')] $1${NC}"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[ERROR] $1${NC}" >&2
|
||||
cleanup_and_exit 1
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[WARNING] $1${NC}"
|
||||
}
|
||||
|
||||
info() {
|
||||
echo -e "${BLUE}[INFO] $1${NC}"
|
||||
}
|
||||
|
||||
cleanup_and_exit() {
|
||||
local exit_code=${1:-0}
|
||||
|
||||
if [ -n "$SNAPSHOT_PATH" ] && lvs "$SNAPSHOT_PATH" >/dev/null 2>&1; then
|
||||
warn "Cleaning up snapshot: $SNAPSHOT_PATH"
|
||||
lvremove -f "$SNAPSHOT_PATH" || warn "Failed to remove snapshot"
|
||||
fi
|
||||
|
||||
exit $exit_code
|
||||
}
|
||||
|
||||
# Trap for cleanup
|
||||
trap 'cleanup_and_exit 130' INT TERM
|
||||
|
||||
# Parse arguments and options
|
||||
if [ $# -lt 3 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
MODE="$1"
|
||||
SOURCE="$2"
|
||||
TARGET="$3"
|
||||
shift 3
|
||||
|
||||
# Parse Borg-specific options
|
||||
NEW_REPO=false
|
||||
ENCRYPTION="repokey"
|
||||
PASSPHRASE=""
|
||||
GENEROUS_SNAPSHOTS=false
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--new-repo)
|
||||
NEW_REPO=true
|
||||
shift
|
||||
;;
|
||||
--encryption)
|
||||
ENCRYPTION="$2"
|
||||
shift 2
|
||||
;;
|
||||
--passphrase)
|
||||
PASSPHRASE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--generous-snapshots)
|
||||
GENEROUS_SNAPSHOTS=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
error "Unknown option: $1"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
error "This script must be run as root"
|
||||
fi
|
||||
|
||||
# Validate mode
|
||||
case "$MODE" in
|
||||
"lv-to-lv"|"lv-to-raw"|"vg-to-raw"|"lv-to-borg"|"vg-to-borg"|"files-to-borg")
|
||||
;;
|
||||
*)
|
||||
error "Invalid mode: $MODE"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check Borg requirements for Borg modes
|
||||
if [[ "$MODE" == *"-to-borg" ]]; then
|
||||
if ! command -v borg >/dev/null 2>&1; then
|
||||
error "Borg Backup is not installed. Please install it: sudo apt install borgbackup"
|
||||
fi
|
||||
|
||||
# Set up Borg environment
|
||||
if [ -n "$PASSPHRASE" ]; then
|
||||
export BORG_PASSPHRASE="$PASSPHRASE"
|
||||
fi
|
||||
fi
|
||||
|
||||
log "Enhanced Simple LVM Backup"
|
||||
log "Mode: $MODE"
|
||||
log "Source: $SOURCE"
|
||||
log "Target: $TARGET"
|
||||
|
||||
# Mode-specific validation and execution
|
||||
case "$MODE" in
|
||||
"lv-to-lv")
|
||||
# LV to LV backup
|
||||
if [ ! -e "$SOURCE" ]; then
|
||||
error "Source LV does not exist: $SOURCE"
|
||||
fi
|
||||
|
||||
if [ ! -e "$TARGET" ]; then
|
||||
error "Target LV does not exist: $TARGET"
|
||||
fi
|
||||
|
||||
# Extract VG and LV names
|
||||
VG_NAME=$(lvs --noheadings -o vg_name "$SOURCE" | tr -d ' ')
|
||||
LV_NAME=$(lvs --noheadings -o lv_name "$SOURCE" | tr -d ' ')
|
||||
SNAPSHOT_NAME="${LV_NAME}_backup_snap_$(date +%s)"
|
||||
SNAPSHOT_PATH="/dev/$VG_NAME/$SNAPSHOT_NAME"
|
||||
|
||||
info "This will update the existing backup LV"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Source LV: $SOURCE${NC}"
|
||||
echo -e "${YELLOW}Target LV: $TARGET${NC}"
|
||||
echo -e "${YELLOW}The target LV will be overwritten with current source data${NC}"
|
||||
echo ""
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "Backup cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Creating snapshot of source LV"
|
||||
lvcreate -L1G -s -n "$SNAPSHOT_NAME" "$SOURCE" || error "Failed to create snapshot"
|
||||
|
||||
log "Copying snapshot to target LV"
|
||||
if command -v pv >/dev/null 2>&1; then
|
||||
pv "$SNAPSHOT_PATH" | dd of="$TARGET" bs=4M || error "Copy failed"
|
||||
else
|
||||
dd if="$SNAPSHOT_PATH" of="$TARGET" bs=4M status=progress || error "Copy failed"
|
||||
fi
|
||||
|
||||
log "Cleaning up snapshot"
|
||||
lvremove -f "$SNAPSHOT_PATH" || warn "Failed to remove snapshot"
|
||||
SNAPSHOT_PATH=""
|
||||
|
||||
log "LV to LV backup completed successfully"
|
||||
;;
|
||||
|
||||
"lv-to-raw")
|
||||
# LV to raw device backup
|
||||
if [ ! -e "$SOURCE" ]; then
|
||||
error "Source LV does not exist: $SOURCE"
|
||||
fi
|
||||
|
||||
if [ ! -e "$TARGET" ]; then
|
||||
error "Target device does not exist: $TARGET"
|
||||
fi
|
||||
|
||||
# Extract VG and LV names
|
||||
VG_NAME=$(lvs --noheadings -o vg_name "$SOURCE" | tr -d ' ')
|
||||
LV_NAME=$(lvs --noheadings -o lv_name "$SOURCE" | tr -d ' ')
|
||||
SNAPSHOT_NAME="${LV_NAME}_backup_snap_$(date +%s)"
|
||||
SNAPSHOT_PATH="/dev/$VG_NAME/$SNAPSHOT_NAME"
|
||||
|
||||
info "This will create a fresh backup on raw device"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Source LV: $SOURCE${NC}"
|
||||
echo -e "${YELLOW}Target Device: $TARGET${NC}"
|
||||
echo -e "${RED}WARNING: All data on $TARGET will be lost!${NC}"
|
||||
echo ""
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "Backup cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Creating snapshot of source LV"
|
||||
lvcreate -L1G -s -n "$SNAPSHOT_NAME" "$SOURCE" || error "Failed to create snapshot"
|
||||
|
||||
log "Copying snapshot to target device"
|
||||
if command -v pv >/dev/null 2>&1; then
|
||||
pv "$SNAPSHOT_PATH" | dd of="$TARGET" bs=4M || error "Copy failed"
|
||||
else
|
||||
dd if="$SNAPSHOT_PATH" of="$TARGET" bs=4M status=progress || error "Copy failed"
|
||||
fi
|
||||
|
||||
log "Cleaning up snapshot"
|
||||
lvremove -f "$SNAPSHOT_PATH" || warn "Failed to remove snapshot"
|
||||
SNAPSHOT_PATH=""
|
||||
|
||||
log "LV to raw device backup completed successfully"
|
||||
;;
|
||||
|
||||
"vg-to-raw")
|
||||
# VG to raw device backup
|
||||
if ! vgs "$SOURCE" >/dev/null 2>&1; then
|
||||
error "Source VG does not exist: $SOURCE"
|
||||
fi
|
||||
|
||||
if [ ! -e "$TARGET" ]; then
|
||||
error "Target device does not exist: $TARGET"
|
||||
fi
|
||||
|
||||
# Get the first PV of the source VG
|
||||
SOURCE_PV=$(vgs --noheadings -o pv_name "$SOURCE" | head -n1 | tr -d ' ')
|
||||
|
||||
if [ -z "$SOURCE_PV" ]; then
|
||||
error "No physical volumes found for VG: $SOURCE"
|
||||
fi
|
||||
|
||||
info "This will clone the entire volume group"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Source VG: $SOURCE${NC}"
|
||||
echo -e "${YELLOW}Source PV: $SOURCE_PV${NC}"
|
||||
echo -e "${YELLOW}Target Device: $TARGET${NC}"
|
||||
echo -e "${RED}WARNING: All data on $TARGET will be lost!${NC}"
|
||||
echo -e "${BLUE}This preserves LVM metadata and all logical volumes${NC}"
|
||||
echo ""
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "Backup cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Copying entire PV to target device"
|
||||
log "This preserves LVM structure and all LVs"
|
||||
|
||||
if command -v pv >/dev/null 2>&1; then
|
||||
pv "$SOURCE_PV" | dd of="$TARGET" bs=4M || error "Copy failed"
|
||||
else
|
||||
dd if="$SOURCE_PV" of="$TARGET" bs=4M status=progress || error "Copy failed"
|
||||
fi
|
||||
|
||||
log "VG to raw device backup completed successfully"
|
||||
log "Target device now contains complete LVM structure"
|
||||
;;
|
||||
|
||||
"lv-to-borg")
|
||||
# LV to Borg repository backup
|
||||
if [ ! -e "$SOURCE" ]; then
|
||||
error "Source LV does not exist: $SOURCE"
|
||||
fi
|
||||
|
||||
# Extract VG and LV names
|
||||
VG_NAME=$(lvs --noheadings -o vg_name "$SOURCE" | tr -d ' ')
|
||||
LV_NAME=$(lvs --noheadings -o lv_name "$SOURCE" | tr -d ' ')
|
||||
SNAPSHOT_NAME="${LV_NAME}_borg_snap_$(date +%s)"
|
||||
SNAPSHOT_PATH="/dev/$VG_NAME/$SNAPSHOT_NAME"
|
||||
|
||||
# Get LV size to determine appropriate snapshot size
|
||||
LV_SIZE_BYTES=$(lvs --noheadings -o lv_size --units b "$SOURCE" | tr -d ' ' | sed 's/B$//')
|
||||
# Use different percentages based on options
|
||||
if [ "$GENEROUS_SNAPSHOTS" = true ]; then
|
||||
SNAPSHOT_SIZE_BYTES=$((LV_SIZE_BYTES / 4)) # 25%
|
||||
MIN_SIZE=$((2 * 1024 * 1024 * 1024)) # 2GB minimum
|
||||
else
|
||||
# Auto mode: 10% normally, 15% for large LVs
|
||||
if [ $LV_SIZE_BYTES -gt $((50 * 1024 * 1024 * 1024)) ]; then
|
||||
SNAPSHOT_SIZE_BYTES=$((LV_SIZE_BYTES * 15 / 100)) # 15% for >50GB
|
||||
else
|
||||
SNAPSHOT_SIZE_BYTES=$((LV_SIZE_BYTES / 10)) # 10% normally
|
||||
fi
|
||||
MIN_SIZE=$((1024 * 1024 * 1024)) # 1GB minimum
|
||||
fi
|
||||
|
||||
if [ $SNAPSHOT_SIZE_BYTES -lt $MIN_SIZE ]; then
|
||||
SNAPSHOT_SIZE_BYTES=$MIN_SIZE
|
||||
fi
|
||||
SNAPSHOT_SIZE_GB=$((SNAPSHOT_SIZE_BYTES / 1073741824))
|
||||
SNAPSHOT_SIZE="${SNAPSHOT_SIZE_GB}G"
|
||||
|
||||
info "This will backup LV to Borg repository"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Source LV: $SOURCE${NC}"
|
||||
echo -e "${YELLOW}Repository: $TARGET${NC}"
|
||||
echo -e "${BLUE}Snapshot size: $SNAPSHOT_SIZE${NC}"
|
||||
if [ "$NEW_REPO" = true ]; then
|
||||
echo -e "${BLUE}Will create new repository${NC}"
|
||||
else
|
||||
echo -e "${BLUE}Will add to existing repository${NC}"
|
||||
fi
|
||||
echo -e "${BLUE}Encryption: $ENCRYPTION${NC}"
|
||||
echo ""
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "Backup cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Initialize repository if needed
|
||||
if [ "$NEW_REPO" = true ]; then
|
||||
log "Creating new Borg repository: $TARGET"
|
||||
if [ "$ENCRYPTION" = "none" ]; then
|
||||
borg init --encryption=none "$TARGET" || error "Failed to initialize repository"
|
||||
else
|
||||
borg init --encryption="$ENCRYPTION" "$TARGET" || error "Failed to initialize repository"
|
||||
fi
|
||||
log "Repository initialized successfully"
|
||||
fi
|
||||
|
||||
log "Creating snapshot of source LV"
|
||||
lvcreate -L"$SNAPSHOT_SIZE" -s -n "$SNAPSHOT_NAME" "$SOURCE" || error "Failed to create snapshot"
|
||||
|
||||
# Create Borg archive
|
||||
ARCHIVE_NAME="lv_${LV_NAME}_$(date +%Y%m%d_%H%M%S)"
|
||||
log "Creating Borg archive (block-level): $ARCHIVE_NAME"
|
||||
log "Backing up raw snapshot block device to Borg..."
|
||||
log "This preserves exact block-level state including filesystem metadata"
|
||||
|
||||
dd if="$SNAPSHOT_PATH" bs=4M | borg create --stdin-name "${LV_NAME}.img" --progress --stats "$TARGET::$ARCHIVE_NAME" - || error "Borg backup failed"
|
||||
|
||||
log "Block-level Borg backup completed successfully"
|
||||
|
||||
# Cleanup
|
||||
log "Cleaning up snapshot"
|
||||
lvremove -f "$SNAPSHOT_PATH" || warn "Failed to remove snapshot"
|
||||
SNAPSHOT_PATH=""
|
||||
|
||||
log "LV to Borg backup completed successfully"
|
||||
;;
|
||||
|
||||
"vg-to-borg")
|
||||
# VG to Borg repository backup
|
||||
if ! vgs "$SOURCE" >/dev/null 2>&1; then
|
||||
error "Source VG does not exist: $SOURCE"
|
||||
fi
|
||||
|
||||
info "This will backup entire VG to Borg repository"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Source VG: $SOURCE${NC}"
|
||||
echo -e "${YELLOW}Repository: $TARGET${NC}"
|
||||
if [ "$NEW_REPO" = true ]; then
|
||||
echo -e "${BLUE}Will create new repository${NC}"
|
||||
else
|
||||
echo -e "${BLUE}Will add to existing repository${NC}"
|
||||
fi
|
||||
echo -e "${BLUE}Encryption: $ENCRYPTION${NC}"
|
||||
echo -e "${BLUE}This will backup all logical volumes in the VG${NC}"
|
||||
echo ""
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "Backup cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Initialize repository if needed
|
||||
if [ "$NEW_REPO" = true ]; then
|
||||
log "Creating new Borg repository: $TARGET"
|
||||
if [ "$ENCRYPTION" = "none" ]; then
|
||||
borg init --encryption=none "$TARGET" || error "Failed to initialize repository"
|
||||
else
|
||||
borg init --encryption="$ENCRYPTION" "$TARGET" || error "Failed to initialize repository"
|
||||
fi
|
||||
log "Repository initialized successfully"
|
||||
fi
|
||||
|
||||
# Get all LVs in VG
|
||||
LV_LIST=$(lvs --noheadings -o lv_name "$SOURCE" | tr -d ' ')
|
||||
if [ -z "$LV_LIST" ]; then
|
||||
error "No logical volumes found in VG: $SOURCE"
|
||||
fi
|
||||
|
||||
log "Found logical volumes: $(echo $LV_LIST | tr '\n' ' ')"
|
||||
|
||||
# Process each LV one by one to avoid space issues
|
||||
for LV_NAME in $LV_LIST; do
|
||||
SNAPSHOT_NAME="${LV_NAME}_borg_snap_$(date +%s)"
|
||||
SNAPSHOT_PATH="/dev/$SOURCE/$SNAPSHOT_NAME"
|
||||
LV_PATH="/dev/$SOURCE/$LV_NAME"
|
||||
|
||||
# Get LV size to determine appropriate snapshot size
|
||||
LV_SIZE_BYTES=$(lvs --noheadings -o lv_size --units b "$LV_PATH" | tr -d ' ' | sed 's/B$//')
|
||||
# Use different percentages based on options
|
||||
if [ "$GENEROUS_SNAPSHOTS" = true ]; then
|
||||
SNAPSHOT_SIZE_BYTES=$((LV_SIZE_BYTES / 4)) # 25%
|
||||
MIN_SIZE=$((2 * 1024 * 1024 * 1024)) # 2GB minimum
|
||||
else
|
||||
# Auto mode: 10% normally, 15% for large LVs
|
||||
if [ $LV_SIZE_BYTES -gt $((50 * 1024 * 1024 * 1024)) ]; then
|
||||
SNAPSHOT_SIZE_BYTES=$((LV_SIZE_BYTES * 15 / 100)) # 15% for >50GB
|
||||
else
|
||||
SNAPSHOT_SIZE_BYTES=$((LV_SIZE_BYTES / 10)) # 10% normally
|
||||
fi
|
||||
MIN_SIZE=$((1024 * 1024 * 1024)) # 1GB minimum
|
||||
fi
|
||||
|
||||
if [ $SNAPSHOT_SIZE_BYTES -lt $MIN_SIZE ]; then
|
||||
SNAPSHOT_SIZE_BYTES=$MIN_SIZE
|
||||
fi
|
||||
SNAPSHOT_SIZE_GB=$((SNAPSHOT_SIZE_BYTES / 1073741824))
|
||||
SNAPSHOT_SIZE="${SNAPSHOT_SIZE_GB}G"
|
||||
|
||||
log "Processing LV: $LV_NAME"
|
||||
log "Creating snapshot: $SNAPSHOT_NAME (size: $SNAPSHOT_SIZE)"
|
||||
lvcreate -L"$SNAPSHOT_SIZE" -s -n "$SNAPSHOT_NAME" "$LV_PATH" || {
|
||||
warn "Failed to create snapshot for $LV_NAME"
|
||||
continue
|
||||
}
|
||||
|
||||
# Create individual archive for this LV
|
||||
ARCHIVE_NAME="vg_${SOURCE}_lv_${LV_NAME}_$(date +%Y%m%d_%H%M%S)"
|
||||
log "Backing up $LV_NAME to archive: $ARCHIVE_NAME"
|
||||
log "Streaming raw block device directly to Borg..."
|
||||
|
||||
dd if="$SNAPSHOT_PATH" bs=4M | borg create --stdin-name "${LV_NAME}.img" --progress --stats "$TARGET::$ARCHIVE_NAME" - || {
|
||||
warn "Backup of $LV_NAME failed"
|
||||
}
|
||||
|
||||
# Clean up this snapshot immediately to save space
|
||||
log "Removing snapshot $SNAPSHOT_PATH"
|
||||
lvremove -f "$SNAPSHOT_PATH" || warn "Failed to remove snapshot $SNAPSHOT_PATH"
|
||||
done
|
||||
|
||||
log "Block-level VG Borg backup completed successfully"
|
||||
log "Created individual archives for each LV in VG $SOURCE"
|
||||
|
||||
log "VG to Borg backup completed successfully"
|
||||
;;
|
||||
|
||||
"files-to-borg")
|
||||
# Files to Borg repository backup
|
||||
if [ ! -e "$SOURCE" ]; then
|
||||
error "Source LV does not exist: $SOURCE"
|
||||
fi
|
||||
|
||||
# Extract VG and LV names
|
||||
VG_NAME=$(lvs --noheadings -o vg_name "$SOURCE" | tr -d ' ')
|
||||
LV_NAME=$(lvs --noheadings -o lv_name "$SOURCE" | tr -d ' ')
|
||||
SNAPSHOT_NAME="${LV_NAME}_files_snap_$(date +%s)"
|
||||
SNAPSHOT_PATH="/dev/$VG_NAME/$SNAPSHOT_NAME"
|
||||
|
||||
# Get LV size to determine appropriate snapshot size (file-level needs much smaller)
|
||||
LV_SIZE_BYTES=$(lvs --noheadings -o lv_size --units b "$SOURCE" | tr -d ' ' | sed 's/B$//')
|
||||
if [ "$GENEROUS_SNAPSHOTS" = true ]; then
|
||||
# File-level generous: 5% with 1G min, 20G max
|
||||
SNAPSHOT_SIZE_BYTES=$((LV_SIZE_BYTES * 5 / 100))
|
||||
[ $SNAPSHOT_SIZE_BYTES -lt $((1 * 1024 * 1024 * 1024)) ] && SNAPSHOT_SIZE_BYTES=$((1 * 1024 * 1024 * 1024))
|
||||
[ $SNAPSHOT_SIZE_BYTES -gt $((20 * 1024 * 1024 * 1024)) ] && SNAPSHOT_SIZE_BYTES=$((20 * 1024 * 1024 * 1024))
|
||||
else
|
||||
# Auto: 3% with 1G min, 15G max
|
||||
SNAPSHOT_SIZE_BYTES=$((LV_SIZE_BYTES * 3 / 100))
|
||||
[ $SNAPSHOT_SIZE_BYTES -lt $((1 * 1024 * 1024 * 1024)) ] && SNAPSHOT_SIZE_BYTES=$((1 * 1024 * 1024 * 1024))
|
||||
[ $SNAPSHOT_SIZE_BYTES -gt $((15 * 1024 * 1024 * 1024)) ] && SNAPSHOT_SIZE_BYTES=$((15 * 1024 * 1024 * 1024))
|
||||
fi
|
||||
SNAPSHOT_SIZE_GB=$((SNAPSHOT_SIZE_BYTES / 1073741824))
|
||||
SNAPSHOT_SIZE="${SNAPSHOT_SIZE_GB}G"
|
||||
|
||||
info "This will backup LV files to Borg repository (space-efficient)"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Source LV: $SOURCE${NC}"
|
||||
echo -e "${YELLOW}Repository: $TARGET${NC}"
|
||||
echo -e "${BLUE}Snapshot size: $SNAPSHOT_SIZE${NC}"
|
||||
echo -e "${BLUE}Mode: File-level backup (skips empty blocks)${NC}"
|
||||
if [ "$NEW_REPO" = true ]; then
|
||||
echo -e "${BLUE}Will create new repository${NC}"
|
||||
else
|
||||
echo -e "${BLUE}Will add to existing repository${NC}"
|
||||
fi
|
||||
echo -e "${BLUE}Encryption: $ENCRYPTION${NC}"
|
||||
echo ""
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "Backup cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Initialize repository if needed
|
||||
if [ "$NEW_REPO" = true ]; then
|
||||
log "Creating new Borg repository: $TARGET"
|
||||
if [ "$ENCRYPTION" = "none" ]; then
|
||||
borg init --encryption=none "$TARGET" || error "Failed to initialize repository"
|
||||
else
|
||||
borg init --encryption="$ENCRYPTION" "$TARGET" || error "Failed to initialize repository"
|
||||
fi
|
||||
log "Repository initialized successfully"
|
||||
fi
|
||||
|
||||
log "Creating snapshot of source LV"
|
||||
lvcreate -L"$SNAPSHOT_SIZE" -s -n "$SNAPSHOT_NAME" "$SOURCE" || error "Failed to create snapshot"
|
||||
|
||||
# Create temporary mount point
|
||||
TEMP_MOUNT=$(mktemp -d -t borg_files_backup_XXXXXX)
|
||||
|
||||
# Mount snapshot read-only with safe FS options
|
||||
FS_TYPE=$(blkid -o value -s TYPE "$SNAPSHOT_PATH" 2>/dev/null || echo "")
|
||||
if [ "$FS_TYPE" = "ext4" ] || [ "$FS_TYPE" = "ext3" ]; then
|
||||
MNT_OPTS="ro,noload"
|
||||
elif [ "$FS_TYPE" = "xfs" ]; then
|
||||
MNT_OPTS="ro,norecovery"
|
||||
else
|
||||
MNT_OPTS="ro"
|
||||
fi
|
||||
log "Mounting snapshot to $TEMP_MOUNT (opts: $MNT_OPTS)"
|
||||
mount -o "$MNT_OPTS" "$SNAPSHOT_PATH" "$TEMP_MOUNT" || error "Failed to mount snapshot"
|
||||
|
||||
# Create Borg archive
|
||||
ARCHIVE_NAME="files_${LV_NAME}_$(date +%Y%m%d_%H%M%S)"
|
||||
log "Creating Borg archive (file-level): $ARCHIVE_NAME"
|
||||
log "Backing up files from mounted snapshot..."
|
||||
log "This is space-efficient and skips empty blocks"
|
||||
|
||||
borg create --progress --stats --compression auto,zstd "$TARGET::$ARCHIVE_NAME" "$TEMP_MOUNT" || error "Borg file-level backup failed"
|
||||
|
||||
log "File-level Borg backup completed successfully"
|
||||
|
||||
# Cleanup
|
||||
log "Cleaning up mount point and snapshot"
|
||||
umount "$TEMP_MOUNT" || warn "Failed to unmount"
|
||||
rmdir "$TEMP_MOUNT"
|
||||
lvremove -f "$SNAPSHOT_PATH" || warn "Failed to remove snapshot"
|
||||
SNAPSHOT_PATH=""
|
||||
|
||||
log "Files to Borg backup completed successfully"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}SUCCESS: Backup completed!${NC}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
case "$MODE" in
|
||||
"lv-to-lv")
|
||||
echo "- Your backup LV has been updated"
|
||||
echo "- You can mount $TARGET to verify the backup"
|
||||
;;
|
||||
"lv-to-raw")
|
||||
echo "- Your backup device contains a raw copy of the LV"
|
||||
echo "- You can mount $TARGET directly or restore from it"
|
||||
;;
|
||||
"vg-to-raw")
|
||||
echo "- Your backup device contains the complete LVM structure"
|
||||
echo "- You can boot from $TARGET or import the VG for recovery"
|
||||
echo "- To access: vgimport or boot directly from the device"
|
||||
;;
|
||||
esac
|
||||
echo ""
|
||||
99
list_drives.sh
Executable file
99
list_drives.sh
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Enhanced script to list available backup sources and targets
|
||||
# Shows options for all three backup modes
|
||||
|
||||
echo "=== Enhanced Simple LVM Backup with Borg Support - Available Options ==="
|
||||
echo ""
|
||||
|
||||
echo "=== SOURCES ==="
|
||||
echo ""
|
||||
echo "Logical Volumes (for lv-to-lv and lv-to-raw modes):"
|
||||
echo "---------------------------------------------------"
|
||||
if command -v lvs >/dev/null 2>&1; then
|
||||
echo "Path Size VG Name"
|
||||
echo "----------------------------------------"
|
||||
lvs --noheadings -o lv_path,lv_size,vg_name | while read lv_path lv_size vg_name; do
|
||||
printf "%-24s %-8s %s\n" "$lv_path" "$lv_size" "$vg_name"
|
||||
done
|
||||
else
|
||||
echo "LVM not available"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Volume Groups (for vg-to-raw mode):"
|
||||
echo "-----------------------------------"
|
||||
if command -v vgs >/dev/null 2>&1; then
|
||||
echo "VG Name Size PV Count"
|
||||
echo "----------------------------------"
|
||||
vgs --noheadings -o vg_name,vg_size,pv_count | while read vg_name vg_size pv_count; do
|
||||
printf "%-16s %-8s %s PVs\n" "$vg_name" "$vg_size" "$pv_count"
|
||||
done
|
||||
else
|
||||
echo "LVM not available"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== TARGETS ==="
|
||||
echo ""
|
||||
echo "Existing Logical Volumes (for lv-to-lv mode):"
|
||||
echo "---------------------------------------------"
|
||||
if command -v lvs >/dev/null 2>&1; then
|
||||
echo "Path Size VG Name"
|
||||
echo "----------------------------------------"
|
||||
lvs --noheadings -o lv_path,lv_size,vg_name | while read lv_path lv_size vg_name; do
|
||||
# Highlight external/backup VGs
|
||||
if [[ "$vg_name" == *"migration"* ]] || [[ "$vg_name" == *"external"* ]] || [[ "$vg_name" == *"backup"* ]]; then
|
||||
printf "%-24s %-8s %s (backup VG)\n" "$lv_path" "$lv_size" "$vg_name"
|
||||
else
|
||||
printf "%-24s %-8s %s\n" "$lv_path" "$lv_size" "$vg_name"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "LVM not available"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Raw Block Devices (for lv-to-raw and vg-to-raw modes):"
|
||||
echo "------------------------------------------------------"
|
||||
echo "Device Size Model"
|
||||
echo "-------------------------"
|
||||
lsblk -dno NAME,SIZE,MODEL | grep -E '^sd|^nvme' | while read name size model; do
|
||||
printf "/dev/%-6s %-8s %s\n" "$name" "$size" "$model"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== USAGE EXAMPLES ==="
|
||||
echo ""
|
||||
echo "1. LV to LV (update existing backup):"
|
||||
echo " sudo ./enhanced_simple_backup.sh lv-to-lv /dev/internal-vg/root /dev/backup-vg/root"
|
||||
echo ""
|
||||
echo "2. LV to Raw Device (fresh backup):"
|
||||
echo " sudo ./enhanced_simple_backup.sh lv-to-raw /dev/internal-vg/root /dev/sdb"
|
||||
echo ""
|
||||
echo "3. Entire VG to Raw Device (complete clone):"
|
||||
echo " sudo ./enhanced_simple_backup.sh vg-to-raw internal-vg /dev/sdb"
|
||||
echo ""
|
||||
echo "4. LV to Borg Repository (block-level backup):"
|
||||
echo " sudo ./enhanced_simple_backup.sh lv-to-borg /dev/internal-vg/root /path/to/borg/repo --new-repo"
|
||||
echo " → Stores raw snapshot as 'root.img' in Borg repo"
|
||||
echo ""
|
||||
echo "5. Entire VG to Borg Repository (all LVs as block devices):"
|
||||
echo " sudo ./enhanced_simple_backup.sh vg-to-borg internal-vg /path/to/borg/repo --encryption repokey"
|
||||
echo " → Creates separate archives for each LV (space-efficient, one LV at a time)"
|
||||
echo ""
|
||||
echo "=== GUI VERSION ==="
|
||||
echo " sudo python3 simple_backup_gui.py"
|
||||
echo ""
|
||||
echo "=== IMPORTANT NOTES ==="
|
||||
echo "- Always run as root (sudo)"
|
||||
echo "- lv-to-lv: Updates existing backup LV"
|
||||
echo "- lv-to-raw: Creates fresh backup, overwrites target device"
|
||||
echo "- vg-to-raw: Clones entire VG including LVM metadata"
|
||||
echo "- lv-to-borg/vg-to-borg: Creates block-level backups in Borg (preserves exact LV state)"
|
||||
echo "- LV→Borg: Single archive with one .img file, VG→Borg: Separate archives per LV"
|
||||
echo "- VG→Borg processes one LV at a time to avoid space issues"
|
||||
echo "- Borg backups are deduplicated, compressed, and encrypted"
|
||||
echo "- Borg backups require: sudo apt install borgbackup"
|
||||
echo "- Make sure target devices are unmounted before backup"
|
||||
echo ""
|
||||
@@ -1,515 +0,0 @@
|
||||
# LVM Migration Guide: Live USB to LVM System
|
||||
|
||||
## Overview
|
||||
|
||||
This guide provides comprehensive instructions for migrating your current non-LVM system to an LVM-based system on an external M.2 SSD. **This migration MUST be performed from a live USB system** to avoid file system conflicts and ensure data integrity.
|
||||
|
||||
## Why Migrate to LVM?
|
||||
|
||||
**Benefits of LVM System:**
|
||||
- **Instant Snapshots**: Create consistent backups without downtime
|
||||
- **Flexible Storage**: Resize volumes dynamically without repartitioning
|
||||
- **Advanced Backups**: Snapshot-based backups with rollback capability
|
||||
- **Space Efficiency**: Snapshots only store changes, not full copies
|
||||
- **System Recovery**: Easy rollback to previous states
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Hardware Requirements
|
||||
- **Live USB System**: Boot from any Linux live USB (Ubuntu, Debian, etc.)
|
||||
- **Internal Drive**: Your current system (will remain unchanged)
|
||||
- **External M.2 SSD**: Target drive for LVM system (will be formatted)
|
||||
- **Sufficient Space**: External drive should be ≥ size of used space on internal drive
|
||||
|
||||
### Software Requirements
|
||||
- Live USB system with LVM tools (will be installed automatically)
|
||||
- Network connection for package installation
|
||||
- Root/sudo access on live system
|
||||
|
||||
## Before You Begin
|
||||
|
||||
### 1. Create Recovery Environment
|
||||
```bash
|
||||
# Prepare live USB with migration tools
|
||||
# Download latest Ubuntu/Debian live ISO
|
||||
# Flash to USB drive using dd or balenaEtcher
|
||||
```
|
||||
|
||||
### 2. Backup Important Data
|
||||
⚠️ **CRITICAL**: While the internal drive remains unchanged, create an additional backup of critical data before proceeding.
|
||||
|
||||
### 3. Document Current System
|
||||
```bash
|
||||
# Boot your current system and document the configuration
|
||||
lsblk -f > system_layout.txt
|
||||
df -h > disk_usage.txt
|
||||
cat /etc/fstab > fstab_backup.txt
|
||||
```
|
||||
|
||||
## Migration Process
|
||||
|
||||
### Step 1: Boot from Live USB System
|
||||
|
||||
1. **Shutdown your system** completely
|
||||
2. **Insert live USB** and connect external M.2 SSD
|
||||
3. **Configure BIOS/UEFI**:
|
||||
- Set USB as first boot device
|
||||
- Ensure both internal and external drives are detected
|
||||
4. **Boot live system**:
|
||||
- Select "Try Ubuntu" or "Live System" (not "Install")
|
||||
- Wait for desktop to load completely
|
||||
5. **Open terminal** and gain root access:
|
||||
```bash
|
||||
sudo -i
|
||||
```
|
||||
|
||||
### Step 2: Prepare Live System
|
||||
|
||||
```bash
|
||||
# Download and prepare the migration tools
|
||||
cd /tmp
|
||||
git clone <your-repo> backup_tools
|
||||
cd backup_tools
|
||||
|
||||
# Or if you have the tools on external drive already:
|
||||
mkdir -p /mnt/temp
|
||||
mount /dev/sda1 /mnt/temp # Adjust device as needed
|
||||
cp -r /mnt/temp/migration_tools/* /tmp/
|
||||
umount /mnt/temp
|
||||
|
||||
# Prepare the live system
|
||||
./prepare_live_system.sh
|
||||
```
|
||||
|
||||
**This script will:**
|
||||
- ✅ Verify you're running from live system
|
||||
- 📦 Install required packages (lvm2, cryptsetup, rsync, etc.)
|
||||
- 🔧 Load kernel modules for LVM
|
||||
- 💽 Detect available drives
|
||||
- 📁 Create migration workspace
|
||||
|
||||
### Step 3: Run Migration Script
|
||||
|
||||
```bash
|
||||
# Execute the migration (this will take 30-90 minutes)
|
||||
./migrate_to_lvm.sh
|
||||
```
|
||||
|
||||
**The migration process includes:**
|
||||
|
||||
1. **Drive Detection** (Automatic):
|
||||
```
|
||||
Detecting drives...
|
||||
Available drives:
|
||||
1. /dev/nvme0n1 - 477GB Samsung SSD 980 (Internal)
|
||||
2. /dev/sda - 477GB Samsung T7 (External USB)
|
||||
|
||||
Selected drives:
|
||||
Internal (source): /dev/nvme0n1
|
||||
External (target): /dev/sda
|
||||
```
|
||||
|
||||
2. **System Analysis**:
|
||||
- Automatically detects partition layout
|
||||
- Identifies filesystem types
|
||||
- Handles encrypted partitions
|
||||
- Calculates optimal LVM sizes
|
||||
|
||||
3. **Confirmation Prompts**:
|
||||
```
|
||||
⚠️ WARNING: This will DESTROY all data on /dev/sda!
|
||||
|
||||
Migration Summary:
|
||||
Source: /dev/nvme0n1 (non-LVM system)
|
||||
Target: /dev/sda (will become LVM system)
|
||||
Root size: 70G
|
||||
Home size: 350G
|
||||
Swap size: 16G
|
||||
Boot size: 2G
|
||||
|
||||
Do you want to continue? [y/N]
|
||||
```
|
||||
|
||||
4. **LVM Layout Creation**:
|
||||
- Creates GPT partition table
|
||||
- EFI boot partition (512MB)
|
||||
- LVM physical volume (remaining space)
|
||||
- Creates volume group and logical volumes
|
||||
|
||||
5. **Data Migration**:
|
||||
- Mounts source filesystems (handles encryption)
|
||||
- Copies all system data with rsync
|
||||
- Preserves permissions, links, and attributes
|
||||
- Updates system configuration files
|
||||
|
||||
6. **System Configuration**:
|
||||
- Updates /etc/fstab for LVM volumes
|
||||
- Configures initramfs for LVM support
|
||||
- Installs and configures GRUB bootloader
|
||||
- Creates LVM snapshot backup tools
|
||||
|
||||
### Step 4: Validation and Testing
|
||||
|
||||
```bash
|
||||
# Validate the migration
|
||||
./validate_lvm_migration.sh
|
||||
```
|
||||
|
||||
**Validation checks:**
|
||||
- ✅ LVM volumes created correctly
|
||||
- ✅ Filesystems are healthy
|
||||
- ✅ Boot configuration is valid
|
||||
- ✅ GRUB installation successful
|
||||
- ✅ System files copied completely
|
||||
- ✅ LVM snapshot capability working
|
||||
|
||||
### Step 5: First Boot Test
|
||||
|
||||
1. **Cleanup and shutdown**:
|
||||
```bash
|
||||
# Clean up and prepare for reboot
|
||||
sync
|
||||
umount -a
|
||||
shutdown -h now
|
||||
```
|
||||
|
||||
2. **Configure BIOS/UEFI**:
|
||||
- Boot into BIOS/UEFI settings
|
||||
- Change boot order: External M.2 SSD as first boot device
|
||||
- Save and exit
|
||||
|
||||
3. **Test boot from external M.2**:
|
||||
- System should boot normally from external drive
|
||||
- Login and verify everything works
|
||||
- Check that all your files and settings are present
|
||||
|
||||
4. **Verify LVM system**:
|
||||
```bash
|
||||
# Check LVM status
|
||||
sudo lvs
|
||||
sudo vgs
|
||||
sudo pvs
|
||||
|
||||
# Check filesystem mounts
|
||||
df -h
|
||||
cat /proc/mounts | grep mapper
|
||||
```
|
||||
|
||||
## System Configuration Details
|
||||
|
||||
### LVM Layout Created
|
||||
```
|
||||
Physical Volume: /dev/sda2
|
||||
Volume Group: system-vg
|
||||
Logical Volumes:
|
||||
├── root (70G) - ext4 - mounted at /
|
||||
├── home (350G) - ext4 - mounted at /home
|
||||
├── boot (2G) - ext4 - mounted at /boot
|
||||
└── swap (16G) - swap - swap space
|
||||
|
||||
Additional:
|
||||
├── /dev/sda1 (512M) - vfat - EFI boot partition - mounted at /boot/efi
|
||||
└── Free space (~38G) - available for snapshots and volume expansion
|
||||
```
|
||||
|
||||
### Migration Advantages
|
||||
|
||||
**Flexibility:**
|
||||
- Resize any volume without repartitioning
|
||||
- Add new drives to volume group
|
||||
- Move logical volumes between physical drives
|
||||
|
||||
**Backup & Recovery:**
|
||||
- Create instant snapshots of any volume
|
||||
- Rollback changes using snapshots
|
||||
- Consistent backups without downtime
|
||||
|
||||
**Space Management:**
|
||||
- Thin provisioning support
|
||||
- Automatic space allocation
|
||||
- Easy expansion and shrinking
|
||||
|
||||
## Using LVM Snapshots
|
||||
|
||||
### Basic Snapshot Operations
|
||||
|
||||
```bash
|
||||
# Create snapshots for backup
|
||||
sudo ./lvm_snapshot_backup.sh backup
|
||||
|
||||
# Snapshots are mounted at:
|
||||
/mnt/backup/root # Snapshot of root filesystem
|
||||
/mnt/backup/home # Snapshot of home filesystem
|
||||
/mnt/backup/boot # Snapshot of boot filesystem
|
||||
|
||||
# Perform backup to external storage
|
||||
rsync -avH /mnt/backup/ /path/to/external/backup/
|
||||
|
||||
# Clean up snapshots
|
||||
sudo ./lvm_snapshot_backup.sh remove
|
||||
```
|
||||
|
||||
### Advanced LVM Operations
|
||||
|
||||
```bash
|
||||
# Extend a logical volume (add 10GB to home)
|
||||
sudo lvextend -L +10G /dev/system-vg/home
|
||||
sudo resize2fs /dev/system-vg/home
|
||||
|
||||
# Create additional logical volume
|
||||
sudo lvcreate -L 20G -n data system-vg
|
||||
sudo mkfs.ext4 /dev/system-vg/data
|
||||
sudo mkdir /data
|
||||
sudo mount /dev/system-vg/data /data
|
||||
|
||||
# Snapshot before system changes
|
||||
sudo lvcreate -L 5G -s -n root-before-update /dev/system-vg/root
|
||||
|
||||
# Rollback if needed
|
||||
sudo umount /
|
||||
sudo lvconvert --merge /dev/system-vg/root-before-update
|
||||
# Reboot to activate rollback
|
||||
```
|
||||
|
||||
## Troubleshooting Guide
|
||||
|
||||
### Migration Issues
|
||||
|
||||
**Migration Script Fails**
|
||||
```bash
|
||||
# Check logs for detailed error information
|
||||
tail -f /var/log/lvm-migration.log
|
||||
|
||||
# Common issues and solutions:
|
||||
```
|
||||
|
||||
| Issue | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| "Drive not found" | Drive not connected/detected | Check connections, try different USB port |
|
||||
| "Insufficient space" | Target drive too small | Use larger drive or reduce partition sizes |
|
||||
| "LVM tools not found" | Missing packages | Run `prepare_live_system.sh` first |
|
||||
| "Permission denied" | Not running as root | Use `sudo` or `sudo -i` |
|
||||
| "Mount failed" | Filesystem corruption | Check drive with `fsck` |
|
||||
|
||||
**Encrypted Partition Issues**
|
||||
```bash
|
||||
# If encrypted partition unlock fails:
|
||||
sudo cryptsetup luksOpen /dev/nvme0n1p3 temp-unlock
|
||||
# Enter correct password
|
||||
sudo cryptsetup close temp-unlock
|
||||
```
|
||||
|
||||
**Drive Detection Problems**
|
||||
```bash
|
||||
# Manually check drives
|
||||
lsblk -dpno NAME,SIZE,MODEL
|
||||
sudo fdisk -l
|
||||
|
||||
# If drives not detected:
|
||||
sudo partprobe # Re-read partition tables
|
||||
sudo udevadm settle # Wait for device detection
|
||||
```
|
||||
|
||||
### Boot Issues After Migration
|
||||
|
||||
**System Won't Boot from External Drive**
|
||||
|
||||
1. **Check BIOS/UEFI Settings**:
|
||||
- Verify external M.2 is detected in BIOS
|
||||
- Set correct boot priority
|
||||
- Enable UEFI boot mode
|
||||
- Disable Secure Boot if necessary
|
||||
|
||||
2. **Repair GRUB from Live USB**:
|
||||
```bash
|
||||
# Boot from live USB and mount LVM system
|
||||
sudo vgchange -ay system-vg
|
||||
sudo mount /dev/system-vg/root /mnt
|
||||
sudo mount /dev/system-vg/boot /mnt/boot
|
||||
sudo mount /dev/sda1 /mnt/boot/efi
|
||||
|
||||
# Reinstall GRUB
|
||||
sudo mount --bind /dev /mnt/dev
|
||||
sudo mount --bind /proc /mnt/proc
|
||||
sudo mount --bind /sys /mnt/sys
|
||||
sudo chroot /mnt
|
||||
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=debian
|
||||
update-grub
|
||||
exit
|
||||
|
||||
# Cleanup and reboot
|
||||
sudo umount /mnt/dev /mnt/proc /mnt/sys
|
||||
sudo umount /mnt/boot/efi /mnt/boot /mnt
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
**Emergency Recovery**
|
||||
|
||||
If external system is completely broken:
|
||||
1. Change BIOS boot order back to internal drive
|
||||
2. Boot from original system (unchanged)
|
||||
3. Re-attempt migration or continue with original system
|
||||
|
||||
### LVM Issues
|
||||
|
||||
**Volume Group Not Found**
|
||||
```bash
|
||||
# Activate volume group manually
|
||||
sudo vgchange -ay system-vg
|
||||
|
||||
# Scan for volume groups
|
||||
sudo vgscan
|
||||
sudo pvscan
|
||||
```
|
||||
|
||||
**Snapshot Issues**
|
||||
```bash
|
||||
# Remove stuck snapshots
|
||||
sudo umount /mnt/backup/root /mnt/backup/home 2>/dev/null || true
|
||||
sudo lvremove -f system-vg/root-snapshot
|
||||
sudo lvremove -f system-vg/home-snapshot
|
||||
|
||||
# Check volume group free space
|
||||
sudo vgs system-vg
|
||||
```
|
||||
|
||||
**File System Corruption**
|
||||
```bash
|
||||
# Check and repair LVM volumes
|
||||
sudo fsck /dev/system-vg/root
|
||||
sudo fsck /dev/system-vg/home
|
||||
sudo fsck /dev/system-vg/boot
|
||||
```
|
||||
|
||||
## Recovery Procedures
|
||||
|
||||
### Complete Rollback to Original System
|
||||
|
||||
If you decide to abandon LVM migration:
|
||||
|
||||
1. **Boot from internal drive**:
|
||||
- Change BIOS boot order to internal drive
|
||||
- Boot normally from original system
|
||||
|
||||
2. **Reformat external drive** (optional):
|
||||
```bash
|
||||
# Wipe LVM configuration
|
||||
sudo dd if=/dev/zero of=/dev/sda bs=1M count=100
|
||||
# Or use backup tools to restore external drive
|
||||
```
|
||||
|
||||
3. **Continue with original system**:
|
||||
- Everything remains as before migration
|
||||
- Use existing backup tools for regular backups
|
||||
|
||||
### Retry Migration
|
||||
|
||||
If you want to attempt migration again:
|
||||
|
||||
1. **Boot from live USB**
|
||||
2. **Run migration script again**:
|
||||
```bash
|
||||
./migrate_to_lvm.sh
|
||||
```
|
||||
- Script will destroy existing LVM setup and recreate
|
||||
- Source system (internal) remains unchanged
|
||||
|
||||
### Disaster Recovery
|
||||
|
||||
**If both systems fail:**
|
||||
|
||||
1. **Boot from live USB**
|
||||
2. **Mount internal drive** for data recovery:
|
||||
```bash
|
||||
mkdir -p /mnt/recovery
|
||||
|
||||
# Mount root partition
|
||||
sudo mount /dev/nvme0n1p1 /mnt/recovery
|
||||
|
||||
# If home is encrypted:
|
||||
sudo cryptsetup open /dev/nvme0n1p3 recovery-home
|
||||
sudo mkdir -p /mnt/recovery/home
|
||||
sudo mount /dev/mapper/recovery-home /mnt/recovery/home
|
||||
|
||||
# Copy important data to external storage
|
||||
rsync -avH /mnt/recovery/home/username/ /path/to/safe/backup/
|
||||
```
|
||||
|
||||
3. **Fresh OS installation** if needed:
|
||||
- Install fresh OS on any drive
|
||||
- Restore personal data from backup
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### LVM Performance Tuning
|
||||
|
||||
```bash
|
||||
# Enable read-ahead for better performance
|
||||
sudo blockdev --setra 2048 /dev/system-vg/root
|
||||
sudo blockdev --setra 2048 /dev/system-vg/home
|
||||
|
||||
# Add to /etc/fstab for persistent read-ahead:
|
||||
# /dev/system-vg/root / ext4 defaults,noatime 0 1
|
||||
# /dev/system-vg/home /home ext4 defaults,noatime 0 2
|
||||
```
|
||||
|
||||
### Snapshot Management
|
||||
|
||||
```bash
|
||||
# Monitor snapshot usage
|
||||
sudo lvs -a -o lv_name,lv_size,data_percent system-vg
|
||||
|
||||
# Remove old snapshots regularly
|
||||
sudo lvremove system-vg/old-snapshot-name
|
||||
|
||||
# Set up automatic snapshot cleanup (cron job)
|
||||
echo '0 2 * * * root /usr/local/bin/lvm-snapshot-backup.sh remove' >> /etc/crontab
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Regular Maintenance
|
||||
|
||||
1. **Monitor disk space**:
|
||||
```bash
|
||||
sudo vgs system-vg # Check volume group free space
|
||||
df -h # Check filesystem usage
|
||||
```
|
||||
|
||||
2. **Regular snapshots**:
|
||||
```bash
|
||||
# Before system updates
|
||||
sudo lvcreate -L 5G -s -n pre-update-$(date +%Y%m%d) /dev/system-vg/root
|
||||
|
||||
# Before major changes
|
||||
sudo ./lvm_snapshot_backup.sh backup
|
||||
```
|
||||
|
||||
3. **Backup strategy**:
|
||||
- Daily: LVM snapshots to external storage
|
||||
- Weekly: Full system backup using existing tools
|
||||
- Monthly: Verify backup integrity
|
||||
|
||||
### Security Considerations
|
||||
|
||||
- **Encryption**: Home data is no longer encrypted in LVM setup
|
||||
- Consider full disk encryption if security is critical
|
||||
- Use file-level encryption for sensitive data
|
||||
|
||||
- **Access Control**: Secure LVM management commands
|
||||
```bash
|
||||
# Restrict LVM command access
|
||||
sudo chmod 750 /usr/local/bin/lvm-*
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
The migration successfully transforms your system from traditional partitions to a flexible LVM-based setup, providing:
|
||||
|
||||
✅ **Instant snapshots** for consistent backups
|
||||
✅ **Dynamic volume resizing** without downtime
|
||||
✅ **Advanced backup strategies** with rollback capability
|
||||
✅ **Space efficiency** with thin provisioning
|
||||
✅ **System recovery** options with snapshots
|
||||
|
||||
Your original system remains intact as a fallback, making this a low-risk enhancement to your backup and storage capabilities.
|
||||
@@ -1,113 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Mount backup tools partition and provide easy access
|
||||
# Use this when booted from the external M.2 SSD
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " Backup Tools Access Helper"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Try to find backup tools partition
|
||||
TOOLS_PARTITION=$(blkid -L "BACKUP_TOOLS" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -z "$TOOLS_PARTITION" ]]; then
|
||||
print_warning "Backup tools partition not found by label."
|
||||
print_info "Searching for tools partition..."
|
||||
|
||||
# Look for small partitions that might be our tools partition
|
||||
while IFS= read -r line; do
|
||||
partition=$(echo "$line" | awk '{print $1}')
|
||||
size=$(echo "$line" | awk '{print $4}')
|
||||
|
||||
# Check if it's a small partition (likely tools)
|
||||
if [[ "$size" == *M ]] && [[ ${size%M} -le 1024 ]]; then
|
||||
print_info "Found potential tools partition: $partition ($size)"
|
||||
TOOLS_PARTITION="/dev/$partition"
|
||||
break
|
||||
fi
|
||||
done < <(lsblk -n -o NAME,SIZE | grep -E "sd|nvme.*p")
|
||||
fi
|
||||
|
||||
if [[ -z "$TOOLS_PARTITION" ]]; then
|
||||
echo "❌ Could not find backup tools partition"
|
||||
echo ""
|
||||
echo "Available partitions:"
|
||||
lsblk
|
||||
echo ""
|
||||
echo "To manually mount tools:"
|
||||
echo "1. Identify the tools partition from the list above"
|
||||
echo "2. sudo mkdir -p /mnt/backup_tools"
|
||||
echo "3. sudo mount /dev/[partition] /mnt/backup_tools"
|
||||
echo "4. cd /mnt/backup_tools/backup_system"
|
||||
echo "5. ./launch_backup_tools.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create mount point
|
||||
MOUNT_POINT="/mnt/backup_tools"
|
||||
print_info "Creating mount point: $MOUNT_POINT"
|
||||
sudo mkdir -p "$MOUNT_POINT"
|
||||
|
||||
# Mount tools partition
|
||||
print_info "Mounting tools partition: $TOOLS_PARTITION"
|
||||
if sudo mount "$TOOLS_PARTITION" "$MOUNT_POINT"; then
|
||||
print_success "Tools partition mounted successfully"
|
||||
else
|
||||
echo "❌ Failed to mount tools partition"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if backup system exists
|
||||
if [[ -d "$MOUNT_POINT/backup_system" ]]; then
|
||||
print_success "Backup tools found!"
|
||||
|
||||
cd "$MOUNT_POINT/backup_system"
|
||||
|
||||
echo ""
|
||||
echo "📁 Backup tools are now available at:"
|
||||
echo " $MOUNT_POINT/backup_system"
|
||||
echo ""
|
||||
echo "🚀 Available commands:"
|
||||
echo " ./launch_backup_tools.sh - Interactive menu"
|
||||
echo " ./backup_script.sh --help - Command line help"
|
||||
echo " python3 backup_manager.py - GUI (if available)"
|
||||
echo " ./create_desktop_entry.sh - Create desktop shortcut"
|
||||
echo ""
|
||||
|
||||
# Ask what to do
|
||||
read -p "Launch backup tools now? (y/n): " launch
|
||||
if [[ "$launch" =~ ^[Yy] ]]; then
|
||||
./launch_backup_tools.sh
|
||||
else
|
||||
echo ""
|
||||
echo "Tools are ready to use. Change to the tools directory:"
|
||||
echo "cd $MOUNT_POINT/backup_system"
|
||||
fi
|
||||
|
||||
else
|
||||
print_warning "Backup system not found in tools partition"
|
||||
echo ""
|
||||
echo "Contents of tools partition:"
|
||||
ls -la "$MOUNT_POINT"
|
||||
fi
|
||||
@@ -1,198 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Automated Clonezilla Backup Script
|
||||
# This script runs inside the Clonezilla environment to automate the backup process
|
||||
# while preserving the Clonezilla boot partition
|
||||
|
||||
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
|
||||
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
clear
|
||||
print_status "Automated Clonezilla System Backup"
|
||||
echo "=================================="
|
||||
echo
|
||||
|
||||
# Wait for drives to be detected
|
||||
sleep 3
|
||||
|
||||
# Auto-detect source drive (internal)
|
||||
SOURCE_DRIVE=""
|
||||
for drive in /dev/nvme0n1 /dev/sda /dev/sdb /dev/sdc; do
|
||||
if [[ -b "$drive" && "$drive" != "/dev/sda" ]]; then
|
||||
# Check if it looks like a system drive (has multiple partitions)
|
||||
if [[ $(lsblk -n "$drive" | wc -l) -gt 1 ]]; then
|
||||
SOURCE_DRIVE="$drive"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z "$SOURCE_DRIVE" ]]; then
|
||||
print_error "Could not auto-detect source drive!"
|
||||
print_status "Available drives:"
|
||||
lsblk
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Target is always the backup partition on this USB
|
||||
TARGET_PARTITION="/dev/sda2"
|
||||
|
||||
print_status "Source Drive: $SOURCE_DRIVE"
|
||||
print_status "Target Partition: $TARGET_PARTITION"
|
||||
lsblk "$SOURCE_DRIVE"
|
||||
echo
|
||||
|
||||
print_warning "This will create a compressed backup image of your system"
|
||||
print_warning "Backup location: $TARGET_PARTITION"
|
||||
echo
|
||||
|
||||
# Mount the backup partition
|
||||
BACKUP_MOUNT="/tmp/backup_storage"
|
||||
mkdir -p "$BACKUP_MOUNT"
|
||||
|
||||
print_status "Mounting backup storage..."
|
||||
if ! mount "$TARGET_PARTITION" "$BACKUP_MOUNT"; then
|
||||
print_error "Failed to mount backup partition"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create backup directory with timestamp
|
||||
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_DIR="$BACKUP_MOUNT/system_backup_$BACKUP_DATE"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
print_status "Creating system backup in: $BACKUP_DIR"
|
||||
echo
|
||||
|
||||
# Use Clonezilla's ocs-sr (Save and Restore) in batch mode
|
||||
print_status "Starting Clonezilla backup process..."
|
||||
print_warning "This will take 30-60 minutes depending on your data size"
|
||||
echo
|
||||
|
||||
# Clonezilla command in batch mode for disk image creation
|
||||
# -q2: Use parallel compression for speed
|
||||
# -c: Enable checking saved image
|
||||
# -j2: Use clone fresh MBR
|
||||
# -z1p: Use parallel gzip compression
|
||||
# -i 4096: Set image split size to 4GB
|
||||
# -sfsck: Skip filesystem check to save time
|
||||
# -scs: Skip checking free space in destination
|
||||
# -rescue: Continue on minor errors
|
||||
# -batch: Batch mode, no interactive prompts
|
||||
|
||||
# Use Clonezilla's ocs-sr (Save and Restore) in batch mode with FASTEST settings
|
||||
# -q2: Use parallel compression for speed
|
||||
# -c: Enable checking saved image
|
||||
# -j2: Use clone fresh MBR
|
||||
# -z0: NO compression for maximum speed (trade size for speed)
|
||||
# -i 0: No image splitting for faster writing
|
||||
# -sfsck: Skip filesystem check to save time
|
||||
# -scs: Skip checking free space in destination
|
||||
# -rescue: Continue on minor errors
|
||||
# -batch: Batch mode, no interactive prompts
|
||||
# -p poweroff: Don't power off after completion
|
||||
|
||||
print_status "Using MAXIMUM SPEED settings (NO compression - speed is king!)"
|
||||
print_warning "Backup will complete in ~15-20 minutes - fastest possible!"
|
||||
|
||||
/usr/sbin/ocs-sr \
|
||||
-q2 \
|
||||
-j2 \
|
||||
-z0 \
|
||||
-i 0 \
|
||||
-sfsck \
|
||||
-scs \
|
||||
-rescue \
|
||||
-batch \
|
||||
-p reboot \
|
||||
savedisk \
|
||||
"system_backup_$BACKUP_DATE" \
|
||||
"$SOURCE_DRIVE"
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
print_success "Backup completed successfully!"
|
||||
print_success "Backup saved to: $BACKUP_DIR"
|
||||
|
||||
# Create a restore script for easy restoration
|
||||
cat > "$BACKUP_DIR/restore.sh" << EOF
|
||||
#!/bin/bash
|
||||
# Restore script for system backup created on $BACKUP_DATE
|
||||
# Usage: ./restore.sh /dev/target_drive
|
||||
|
||||
if [[ \$# -ne 1 ]]; then
|
||||
echo "Usage: \$0 /dev/target_drive"
|
||||
echo "Example: \$0 /dev/nvme0n1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TARGET_DRIVE="\$1"
|
||||
|
||||
echo "WARNING: This will completely overwrite \$TARGET_DRIVE"
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
|
||||
if [[ "\$confirm" != "yes" ]]; then
|
||||
echo "Cancelled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
/usr/sbin/ocs-sr \\
|
||||
-g auto \\
|
||||
-e1 auto \\
|
||||
-e2 \\
|
||||
-r \\
|
||||
-j2 \\
|
||||
-batch \\
|
||||
restoredisk \\
|
||||
"system_backup_$BACKUP_DATE" \\
|
||||
"\$TARGET_DRIVE"
|
||||
EOF
|
||||
chmod +x "$BACKUP_DIR/restore.sh"
|
||||
|
||||
# Show backup info
|
||||
print_status "Backup Information:"
|
||||
du -h "$BACKUP_DIR"
|
||||
echo
|
||||
print_success "Restore script created: $BACKUP_DIR/restore.sh"
|
||||
|
||||
else
|
||||
print_error "Backup failed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Unmount backup storage
|
||||
umount "$BACKUP_MOUNT"
|
||||
|
||||
print_success "System backup completed!"
|
||||
print_status "You can now reboot back to your normal system"
|
||||
print_status "To restore: Boot from this USB and run the restore script"
|
||||
|
||||
echo
|
||||
print_warning "Press Enter to continue..."
|
||||
read
|
||||
|
||||
# Optional: Auto-reboot or return to menu
|
||||
echo "Backup process complete. You can now:"
|
||||
echo "1. Reboot to your normal system"
|
||||
echo "2. Run another backup"
|
||||
echo "3. Access Clonezilla manually"
|
||||
@@ -1,576 +0,0 @@
|
||||
#!/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
|
||||
SYNC_MODE=false # Smart sync mode flag
|
||||
ANALYZE_ONLY=false # Analysis only mode
|
||||
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"
|
||||
}
|
||||
|
||||
# Check if target has existing backup
|
||||
check_existing_backup() {
|
||||
local target_drive=$1
|
||||
local temp_mount="/tmp/backup_check_$$"
|
||||
|
||||
log "Checking for existing backup on $target_drive..."
|
||||
|
||||
# Get main partition
|
||||
local main_partition=$(lsblk -n -o NAME "$target_drive" | grep -v "^$(basename "$target_drive")$" | head -1)
|
||||
if [[ -z "$main_partition" ]]; then
|
||||
echo "false"
|
||||
return
|
||||
fi
|
||||
|
||||
main_partition="/dev/$main_partition"
|
||||
|
||||
# Try to mount and check
|
||||
mkdir -p "$temp_mount"
|
||||
if mount -o ro "$main_partition" "$temp_mount" 2>/dev/null; then
|
||||
if [[ -d "$temp_mount/etc" && -d "$temp_mount/home" && -d "$temp_mount/usr" ]]; then
|
||||
echo "true"
|
||||
else
|
||||
echo "false"
|
||||
fi
|
||||
umount "$temp_mount" 2>/dev/null
|
||||
else
|
||||
echo "false"
|
||||
fi
|
||||
rmdir "$temp_mount" 2>/dev/null
|
||||
}
|
||||
|
||||
# Analyze changes between source and target
|
||||
analyze_changes() {
|
||||
local source_drive=$1
|
||||
local target_drive=$2
|
||||
|
||||
log "Analyzing changes between $source_drive and $target_drive..."
|
||||
|
||||
# Check if target has existing backup
|
||||
local has_backup=$(check_existing_backup "$target_drive")
|
||||
|
||||
if [[ "$has_backup" != "true" ]]; then
|
||||
log "No existing backup found. Full clone required."
|
||||
echo "FULL_CLONE_REQUIRED"
|
||||
return
|
||||
fi
|
||||
|
||||
# Get filesystem usage for both drives
|
||||
local source_size=$(get_filesystem_size "$source_drive")
|
||||
local target_size=$(get_filesystem_size "$target_drive")
|
||||
|
||||
# Calculate difference in GB
|
||||
local size_diff=$((${source_size} - ${target_size}))
|
||||
local size_diff_abs=${size_diff#-} # Absolute value
|
||||
|
||||
# Convert to GB (sizes are in KB)
|
||||
local size_diff_gb=$((size_diff_abs / 1024 / 1024))
|
||||
|
||||
log "Source filesystem size: $((source_size / 1024 / 1024)) GB"
|
||||
log "Target filesystem size: $((target_size / 1024 / 1024)) GB"
|
||||
log "Size difference: ${size_diff_gb} GB"
|
||||
|
||||
# Decision logic
|
||||
if [[ $size_diff_gb -lt 2 ]]; then
|
||||
log "Recommendation: Smart sync (minimal changes)"
|
||||
echo "SYNC_RECOMMENDED"
|
||||
elif [[ $size_diff_gb -lt 10 ]]; then
|
||||
log "Recommendation: Smart sync (moderate changes)"
|
||||
echo "SYNC_BENEFICIAL"
|
||||
else
|
||||
log "Recommendation: Full clone (major changes)"
|
||||
echo "FULL_CLONE_RECOMMENDED"
|
||||
fi
|
||||
}
|
||||
|
||||
# Get filesystem size in KB
|
||||
get_filesystem_size() {
|
||||
local drive=$1
|
||||
local temp_mount="/tmp/size_check_$$"
|
||||
|
||||
# Get main partition
|
||||
local main_partition=$(lsblk -n -o NAME "$drive" | grep -v "^$(basename "$drive")$" | head -1)
|
||||
if [[ -z "$main_partition" ]]; then
|
||||
echo "0"
|
||||
return
|
||||
fi
|
||||
|
||||
main_partition="/dev/$main_partition"
|
||||
|
||||
# Mount and get usage
|
||||
mkdir -p "$temp_mount"
|
||||
if mount -o ro "$main_partition" "$temp_mount" 2>/dev/null; then
|
||||
local used_kb=$(df "$temp_mount" | tail -1 | awk '{print $3}')
|
||||
umount "$temp_mount" 2>/dev/null
|
||||
echo "$used_kb"
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
rmdir "$temp_mount" 2>/dev/null
|
||||
}
|
||||
|
||||
# Perform smart sync backup
|
||||
smart_sync_backup() {
|
||||
local source=$1
|
||||
local target=$2
|
||||
|
||||
log "Starting smart sync backup..."
|
||||
|
||||
# Mount both filesystems
|
||||
local source_mount="/tmp/sync_source_$$"
|
||||
local target_mount="/tmp/sync_target_$$"
|
||||
|
||||
mkdir -p "$source_mount" "$target_mount"
|
||||
|
||||
# Get main partitions
|
||||
local source_partition=$(lsblk -n -o NAME "$source" | grep -v "^$(basename "$source")$" | head -1)
|
||||
local target_partition=$(lsblk -n -o NAME "$target" | grep -v "^$(basename "$target")$" | head -1)
|
||||
|
||||
source_partition="/dev/$source_partition"
|
||||
target_partition="/dev/$target_partition"
|
||||
|
||||
log "Mounting filesystems for sync..."
|
||||
mount -o ro "$source_partition" "$source_mount" || error_exit "Failed to mount source"
|
||||
mount "$target_partition" "$target_mount" || error_exit "Failed to mount target"
|
||||
|
||||
# Perform rsync
|
||||
log "Starting rsync synchronization..."
|
||||
rsync -avHAXS \
|
||||
--numeric-ids \
|
||||
--delete \
|
||||
--progress \
|
||||
--exclude=/proc/* \
|
||||
--exclude=/sys/* \
|
||||
--exclude=/dev/* \
|
||||
--exclude=/tmp/* \
|
||||
--exclude=/run/* \
|
||||
--exclude=/mnt/* \
|
||||
--exclude=/media/* \
|
||||
--exclude=/lost+found \
|
||||
"$source_mount/" "$target_mount/" || {
|
||||
|
||||
# Cleanup on failure
|
||||
umount "$source_mount" 2>/dev/null
|
||||
umount "$target_mount" 2>/dev/null
|
||||
rmdir "$source_mount" "$target_mount" 2>/dev/null
|
||||
error_exit "Smart sync failed"
|
||||
}
|
||||
|
||||
# Cleanup
|
||||
umount "$source_mount" 2>/dev/null
|
||||
umount "$target_mount" 2>/dev/null
|
||||
rmdir "$source_mount" "$target_mount" 2>/dev/null
|
||||
|
||||
success "Smart sync completed successfully!"
|
||||
}
|
||||
|
||||
# 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 " --sync Smart sync mode (faster incremental backup)"
|
||||
echo " --analyze Analyze changes without performing backup"
|
||||
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 --analyze --target /dev/sdb"
|
||||
echo " $0 --sync --target /dev/sdb"
|
||||
echo " $0 --source /dev/nvme0n1 --target /dev/sdb"
|
||||
echo " $0 --restore --source /dev/sdb --target /dev/nvme0n1"
|
||||
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
|
||||
;;
|
||||
--sync)
|
||||
SYNC_MODE=true
|
||||
shift
|
||||
;;
|
||||
--analyze)
|
||||
ANALYZE_ONLY=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
|
||||
|
||||
# Handle analyze mode
|
||||
if [[ "$ANALYZE_ONLY" == "true" ]]; then
|
||||
echo ""
|
||||
echo "🔍 ANALYZING CHANGES"
|
||||
echo "Source: $SOURCE_DRIVE"
|
||||
echo "Target: $TARGET_DRIVE"
|
||||
echo ""
|
||||
|
||||
analysis_result=$(analyze_changes "$SOURCE_DRIVE" "$TARGET_DRIVE")
|
||||
|
||||
case "$analysis_result" in
|
||||
"FULL_CLONE_REQUIRED")
|
||||
echo "📋 ANALYSIS RESULT: Full Clone Required"
|
||||
echo "• No existing backup found on target drive"
|
||||
echo "• Complete drive cloning is necessary"
|
||||
;;
|
||||
"SYNC_RECOMMENDED")
|
||||
echo "✅ ANALYSIS RESULT: Smart Sync Recommended"
|
||||
echo "• Minimal changes detected (< 2GB difference)"
|
||||
echo "• Smart sync will be much faster than full clone"
|
||||
;;
|
||||
"SYNC_BENEFICIAL")
|
||||
echo "⚡ ANALYSIS RESULT: Smart Sync Beneficial"
|
||||
echo "• Moderate changes detected (< 10GB difference)"
|
||||
echo "• Smart sync recommended for faster backup"
|
||||
;;
|
||||
"FULL_CLONE_RECOMMENDED")
|
||||
echo "🔄 ANALYSIS RESULT: Full Clone Recommended"
|
||||
echo "• Major changes detected (> 10GB difference)"
|
||||
echo "• Full clone may be more appropriate"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "Use --sync flag to perform smart sync backup"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Handle sync mode
|
||||
if [[ "$SYNC_MODE" == "true" ]]; then
|
||||
echo ""
|
||||
echo "⚡ SMART SYNC BACKUP"
|
||||
echo "Source: $SOURCE_DRIVE"
|
||||
echo "Target: $TARGET_DRIVE"
|
||||
echo ""
|
||||
|
||||
# Check if sync is possible
|
||||
analysis_result=$(analyze_changes "$SOURCE_DRIVE" "$TARGET_DRIVE")
|
||||
|
||||
if [[ "$analysis_result" == "FULL_CLONE_REQUIRED" ]]; then
|
||||
error_exit "Smart sync not possible: No existing backup found. Use full backup first."
|
||||
fi
|
||||
|
||||
echo "Analysis: $analysis_result"
|
||||
echo ""
|
||||
read -p "Proceed with smart sync? (yes/no): " confirm
|
||||
if [[ "$confirm" != "yes" ]]; then
|
||||
log "Smart sync cancelled by user"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
smart_sync_backup "$SOURCE_DRIVE" "$TARGET_DRIVE"
|
||||
|
||||
success "Smart sync backup completed successfully!"
|
||||
echo "Smart sync completed! External drive is up to date."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 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 "$@"
|
||||
@@ -1,166 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# USB Live System Migration Bootstrap Script
|
||||
# This script downloads and sets up the LVM migration tools on a live 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
|
||||
|
||||
echo -e "${GREEN}=== LVM Migration Bootstrap ===${NC}"
|
||||
echo "Setting up LVM migration tools on live system..."
|
||||
|
||||
# Create working directory
|
||||
WORK_DIR="/tmp/lvm-migration"
|
||||
mkdir -p "$WORK_DIR"
|
||||
cd "$WORK_DIR"
|
||||
|
||||
echo "Working directory: $WORK_DIR"
|
||||
|
||||
# Function to create the prepare script inline
|
||||
create_prepare_script() {
|
||||
cat > prepare_live_system.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# Live System Preparation Script for LVM Migration
|
||||
set -e
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${BLUE}[$(date '+%H:%M:%S')]${NC} $1"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $1" >&2; exit 1; }
|
||||
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
|
||||
log "Updating package lists..."
|
||||
apt update || { error "Failed to update package lists"; }
|
||||
|
||||
log "Installing required packages..."
|
||||
apt install -y lvm2 cryptsetup rsync parted pv grub-efi-amd64 e2fsprogs dosfstools || {
|
||||
error "Failed to install required packages"
|
||||
}
|
||||
|
||||
log "Loading kernel modules..."
|
||||
modprobe dm_mod dm_crypt dm_snapshot || true
|
||||
|
||||
success "Live system prepared for LVM migration!"
|
||||
echo "Now run: ./migrate_to_lvm.sh"
|
||||
EOF
|
||||
chmod +x prepare_live_system.sh
|
||||
}
|
||||
|
||||
# Function to create the main migration script inline
|
||||
create_migration_script() {
|
||||
# This is a simplified version - the full script is quite large
|
||||
cat > migrate_to_lvm.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# LVM Migration Script - Bootstrap Version
|
||||
# Downloads the full migration script and runs it
|
||||
|
||||
REPO_URL="YOUR_REPO_URL_HERE" # Replace with actual repo URL
|
||||
FULL_SCRIPT_URL="$REPO_URL/migrate_to_lvm.sh"
|
||||
|
||||
echo "Downloading full migration script..."
|
||||
wget -O migrate_to_lvm_full.sh "$FULL_SCRIPT_URL" || {
|
||||
echo "Cannot download from repository. Using embedded version..."
|
||||
# Here you would embed the full script or provide local copy
|
||||
echo "Please manually copy the full migrate_to_lvm.sh script"
|
||||
exit 1
|
||||
}
|
||||
|
||||
chmod +x migrate_to_lvm_full.sh
|
||||
./migrate_to_lvm_full.sh "$@"
|
||||
EOF
|
||||
chmod +x migrate_to_lvm.sh
|
||||
}
|
||||
|
||||
# Create a manual setup guide
|
||||
create_manual_setup() {
|
||||
cat > SETUP_INSTRUCTIONS.txt << 'EOF'
|
||||
LVM Migration Setup Instructions
|
||||
===============================
|
||||
|
||||
1. Boot from this USB stick into live system
|
||||
2. Open terminal and run: sudo -i
|
||||
3. Run: /media/*/migration_tools/bootstrap.sh
|
||||
OR manually follow these steps:
|
||||
|
||||
Manual Setup:
|
||||
1. Update packages: apt update
|
||||
2. Install tools: apt install -y lvm2 cryptsetup rsync parted pv grub-efi-amd64
|
||||
3. Load modules: modprobe dm_mod dm_crypt dm_snapshot
|
||||
4. Create workspace: mkdir -p /tmp/lvm-migration && cd /tmp/lvm-migration
|
||||
5. Copy migration scripts from external drive or USB
|
||||
6. Run: ./migrate_to_lvm.sh
|
||||
|
||||
Drive Configuration (adjust as needed):
|
||||
- Internal drive: /dev/nvme0n1 (source)
|
||||
- External M.2: /dev/sdc (target)
|
||||
- USB stick: /dev/sdb (tools)
|
||||
|
||||
Important Notes:
|
||||
- This will DESTROY all data on the external M.2 drive
|
||||
- Internal drive remains unchanged as backup
|
||||
- External M.2 will become bootable LVM system
|
||||
- Update BIOS boot order after migration
|
||||
|
||||
For full documentation, see LIVE_USB_MIGRATION_GUIDE.md
|
||||
EOF
|
||||
}
|
||||
|
||||
# Create all the files
|
||||
echo "Creating preparation script..."
|
||||
create_prepare_script
|
||||
|
||||
echo "Creating migration bootstrap..."
|
||||
create_migration_script
|
||||
|
||||
echo "Creating setup instructions..."
|
||||
create_manual_setup
|
||||
|
||||
# Create a simple launcher script
|
||||
cat > bootstrap.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
echo "=== LVM Migration Bootstrap ==="
|
||||
echo "1. Prepare live system (install packages)"
|
||||
echo "2. Run LVM migration"
|
||||
echo "3. Show instructions"
|
||||
read -p "Select option [1-3]: " choice
|
||||
|
||||
case $choice in
|
||||
1) sudo ./prepare_live_system.sh ;;
|
||||
2) sudo ./migrate_to_lvm.sh ;;
|
||||
3) cat SETUP_INSTRUCTIONS.txt ;;
|
||||
*) echo "Invalid option" ;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x bootstrap.sh
|
||||
|
||||
echo -e "${GREEN}Bootstrap scripts created in $WORK_DIR${NC}"
|
||||
echo
|
||||
echo "Contents:"
|
||||
ls -la
|
||||
|
||||
echo
|
||||
echo -e "${YELLOW}Next steps:${NC}"
|
||||
echo "1. Copy the migration tools from your development directory to a location accessible from live USB"
|
||||
echo "2. Boot from the USB stick"
|
||||
echo "3. Run the bootstrap script to set up the migration environment"
|
||||
|
||||
# Copy the actual migration tools if they exist in current directory
|
||||
if [ -f "../migrate_to_lvm.sh" ]; then
|
||||
echo
|
||||
echo "Copying actual migration tools..."
|
||||
cp ../migrate_to_lvm.sh ../lvm_snapshot_backup.sh ../validate_lvm_migration.sh . 2>/dev/null || true
|
||||
cp ../LIVE_USB_MIGRATION_GUIDE.md . 2>/dev/null || true
|
||||
echo "Migration tools copied to $WORK_DIR"
|
||||
fi
|
||||
|
||||
success "Bootstrap setup complete!"
|
||||
@@ -1,92 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Package Availability Checker for Live Systems
|
||||
# Checks what packages are available before attempting installation
|
||||
|
||||
set -e
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
success() { echo -e "${GREEN}[FOUND]${NC} $1"; }
|
||||
warning() { echo -e "${YELLOW}[MISSING]${NC} $1"; }
|
||||
|
||||
echo -e "${GREEN}=== Package Availability Checker ===${NC}"
|
||||
echo "Checking package availability for LVM migration..."
|
||||
echo
|
||||
|
||||
# Detect distribution
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
echo "Distribution: $PRETTY_NAME"
|
||||
echo "ID: $ID"
|
||||
echo "Version: $VERSION_ID"
|
||||
echo
|
||||
fi
|
||||
|
||||
# Function to check if a package exists in repositories
|
||||
check_package() {
|
||||
local pkg="$1"
|
||||
if apt-cache show "$pkg" >/dev/null 2>&1; then
|
||||
success "$pkg"
|
||||
return 0
|
||||
else
|
||||
warning "$pkg"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check multiple package alternatives
|
||||
check_alternatives() {
|
||||
local desc="$1"
|
||||
shift
|
||||
local packages=("$@")
|
||||
|
||||
echo -e "${BLUE}$desc:${NC}"
|
||||
local found=0
|
||||
for pkg in "${packages[@]}"; do
|
||||
if check_package "$pkg"; then
|
||||
((found++))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $found -eq 0 ]; then
|
||||
echo -e " ${RED}⚠ No packages found for $desc${NC}"
|
||||
fi
|
||||
echo
|
||||
}
|
||||
|
||||
echo "Updating package cache..."
|
||||
apt update >/dev/null 2>&1 || warning "Could not update package cache"
|
||||
echo
|
||||
|
||||
# Check critical packages
|
||||
total_missing=0
|
||||
|
||||
check_alternatives "LVM Tools" "lvm2" "lvm"
|
||||
check_alternatives "Device Mapper" "dmsetup" "device-mapper"
|
||||
check_alternatives "Cryptsetup" "cryptsetup" "cryptsetup-bin"
|
||||
check_alternatives "Filesystem Tools" "e2fsprogs" "dosfstools" "parted"
|
||||
check_alternatives "Backup Tools" "rsync" "pv"
|
||||
check_alternatives "GRUB EFI" "grub-efi-amd64" "grub-efi" "grub-efi-amd64-bin"
|
||||
check_alternatives "GRUB PC" "grub-pc-bin" "grub-pc"
|
||||
check_alternatives "GRUB Common" "grub-common" "grub2-common"
|
||||
check_alternatives "Initramfs" "initramfs-tools" "dracut"
|
||||
check_alternatives "System Tools" "util-linux" "coreutils" "bc"
|
||||
|
||||
echo -e "${BLUE}=== Summary ===${NC}"
|
||||
echo -e "${GREEN}✓ Package availability checked${NC}"
|
||||
echo "Most packages should be available for installation."
|
||||
|
||||
echo
|
||||
echo "To install packages, run:"
|
||||
echo " sudo ./emergency_install.sh"
|
||||
echo
|
||||
echo "To check individual commands after installation:"
|
||||
echo " command -v lvm && echo 'LVM available'"
|
||||
echo " command -v cryptsetup && echo 'Cryptsetup available'"
|
||||
echo " command -v grub-install && echo 'GRUB available'"
|
||||
@@ -1,307 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Create Truly Bootable Backup USB with Alpine Linux
|
||||
# This creates a complete bootable environment that preserves existing boot capability
|
||||
|
||||
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
|
||||
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
print_error "Do not run as root. Script will use sudo when needed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_status "Bootable Backup USB Creator (Preserves Existing Boot)"
|
||||
echo "=========================================================="
|
||||
echo
|
||||
|
||||
# Check current USB drive status
|
||||
USB_DRIVE="/dev/sda" # Your current USB
|
||||
|
||||
print_status "Analyzing current USB structure..."
|
||||
lsblk "$USB_DRIVE"
|
||||
echo
|
||||
|
||||
# Check if it already has a bootable system
|
||||
HAS_BOOT=$(lsblk "$USB_DRIVE" | grep -i boot || true)
|
||||
if [[ -n "$HAS_BOOT" ]]; then
|
||||
print_warning "USB appears to have existing boot partition"
|
||||
print_warning "We'll preserve this and add backup functionality"
|
||||
else
|
||||
print_status "No existing boot detected - will create complete bootable system"
|
||||
fi
|
||||
|
||||
read -p "Continue to make this USB fully bootable with backup functionality? (yes/no): " confirm
|
||||
if [[ "$confirm" != "yes" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create temporary directory for Alpine Linux
|
||||
WORK_DIR="/tmp/alpine_usb_$$"
|
||||
mkdir -p "$WORK_DIR"
|
||||
cd "$WORK_DIR"
|
||||
|
||||
print_status "Downloading Alpine Linux (minimal, fast-booting)..."
|
||||
|
||||
# Download Alpine Linux (very small, perfect for this)
|
||||
ALPINE_VERSION="3.18.4"
|
||||
ALPINE_ISO="alpine-standard-${ALPINE_VERSION}-x86_64.iso"
|
||||
|
||||
if [[ ! -f "$ALPINE_ISO" ]]; then
|
||||
wget "https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/x86_64/$ALPINE_ISO" || {
|
||||
print_error "Failed to download Alpine Linux"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
print_status "Preparing bootable USB with backup functionality..."
|
||||
|
||||
# Mount the ISO to extract files
|
||||
ISO_MOUNT="$WORK_DIR/iso_mount"
|
||||
mkdir -p "$ISO_MOUNT"
|
||||
sudo mount -o loop "$ALPINE_ISO" "$ISO_MOUNT"
|
||||
|
||||
# Create mount points for USB partitions
|
||||
BOOT_MOUNT="$WORK_DIR/usb_boot"
|
||||
DATA_MOUNT="$WORK_DIR/usb_data"
|
||||
mkdir -p "$BOOT_MOUNT" "$DATA_MOUNT"
|
||||
|
||||
# Mount USB partitions (created by previous script)
|
||||
sudo mount "${USB_DRIVE}1" "$BOOT_MOUNT"
|
||||
sudo mount "${USB_DRIVE}2" "$DATA_MOUNT"
|
||||
|
||||
print_status "Installing Alpine Linux boot files..."
|
||||
|
||||
# Copy Alpine Linux files to boot partition
|
||||
sudo cp -r "$ISO_MOUNT"/* "$BOOT_MOUNT/"
|
||||
|
||||
# Install GRUB for UEFI/BIOS boot
|
||||
print_status "Installing GRUB bootloader..."
|
||||
sudo grub-install --target=x86_64-efi --efi-directory="$BOOT_MOUNT" --boot-directory="$BOOT_MOUNT/boot" --removable --force
|
||||
|
||||
# Create GRUB configuration for automatic backup boot
|
||||
sudo tee "$BOOT_MOUNT/boot/grub/grub.cfg" > /dev/null << 'EOF'
|
||||
set timeout=10
|
||||
set default=0
|
||||
|
||||
menuentry "Automatic System Backup" {
|
||||
linux /boot/vmlinuz-lts modules=loop,squashfs,sd-mod,usb-storage quiet alpine_dev=sda2:/backup-tools/alpine.apkovl.tar.gz
|
||||
initrd /boot/initramfs-lts
|
||||
}
|
||||
|
||||
menuentry "Manual Backup Mode" {
|
||||
linux /boot/vmlinuz-lts modules=loop,squashfs,sd-mod,usb-storage alpine_dev=sda2:/backup-tools/alpine.apkovl.tar.gz
|
||||
initrd /boot/initramfs-lts
|
||||
}
|
||||
|
||||
menuentry "Alpine Linux (Standard)" {
|
||||
linux /boot/vmlinuz-lts modules=loop,squashfs,sd-mod,usb-storage
|
||||
initrd /boot/initramfs-lts
|
||||
}
|
||||
EOF
|
||||
|
||||
print_status "Creating backup environment overlay..."
|
||||
|
||||
# Create Alpine overlay for automatic backup
|
||||
OVERLAY_DIR="$WORK_DIR/overlay"
|
||||
mkdir -p "$OVERLAY_DIR/etc/init.d"
|
||||
mkdir -p "$OVERLAY_DIR/usr/local/bin"
|
||||
mkdir -p "$OVERLAY_DIR/etc/runlevels/default"
|
||||
|
||||
# Create backup script that runs on boot
|
||||
sudo tee "$OVERLAY_DIR/usr/local/bin/backup-menu" > /dev/null << 'EOF'
|
||||
#!/bin/sh
|
||||
# Interactive backup menu
|
||||
|
||||
clear
|
||||
echo "========================================"
|
||||
echo " AUTOMATIC BACKUP SYSTEM"
|
||||
echo "========================================"
|
||||
echo
|
||||
echo "Available drives:"
|
||||
lsblk -d -o NAME,SIZE,TYPE,TRAN | grep -E "(disk|NAME)"
|
||||
echo
|
||||
|
||||
# Auto-detect drives
|
||||
INTERNAL_CANDIDATES=$(lsblk -d -n -o NAME,TYPE,TRAN | grep "disk" | grep -v "usb" | awk '{print "/dev/" $1}')
|
||||
EXTERNAL_CANDIDATES=$(lsblk -d -n -o NAME,TYPE,TRAN | grep "disk" | grep "usb" | awk '{print "/dev/" $1}' | grep -v sda)
|
||||
|
||||
echo "Drive Selection:"
|
||||
echo "=================="
|
||||
|
||||
# Select source drive
|
||||
echo "Available internal drives:"
|
||||
echo "$INTERNAL_CANDIDATES" | nl -v 1
|
||||
echo
|
||||
read -p "Select source drive number (or enter path): " SOURCE_CHOICE
|
||||
|
||||
if echo "$SOURCE_CHOICE" | grep -q "^[0-9]"; then
|
||||
SOURCE_DRIVE=$(echo "$INTERNAL_CANDIDATES" | sed -n "${SOURCE_CHOICE}p")
|
||||
else
|
||||
SOURCE_DRIVE="$SOURCE_CHOICE"
|
||||
fi
|
||||
|
||||
# Select target drive
|
||||
echo
|
||||
echo "Available external drives:"
|
||||
echo "$EXTERNAL_CANDIDATES" | nl -v 1
|
||||
echo
|
||||
read -p "Select target drive number (or enter path): " TARGET_CHOICE
|
||||
|
||||
if echo "$TARGET_CHOICE" | grep -q "^[0-9]"; then
|
||||
TARGET_DRIVE=$(echo "$EXTERNAL_CANDIDATES" | sed -n "${TARGET_CHOICE}p")
|
||||
else
|
||||
TARGET_DRIVE="$TARGET_CHOICE"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "BACKUP CONFIGURATION:"
|
||||
echo "===================="
|
||||
echo "Source (will be copied FROM): $SOURCE_DRIVE"
|
||||
echo "Target (will be overwritten): $TARGET_DRIVE"
|
||||
echo
|
||||
echo "⚠️ ALL DATA ON $TARGET_DRIVE WILL BE DESTROYED! ⚠️"
|
||||
echo
|
||||
read -p "Continue with backup? (yes/no): " CONFIRM
|
||||
|
||||
if [ "$CONFIRM" != "yes" ]; then
|
||||
echo "Backup cancelled"
|
||||
read -p "Press Enter to shutdown..."
|
||||
poweroff
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Starting backup..."
|
||||
echo "=================="
|
||||
|
||||
# Get drive sizes for progress
|
||||
SOURCE_SIZE=$(blockdev --getsize64 "$SOURCE_DRIVE")
|
||||
SOURCE_SIZE_GB=$((SOURCE_SIZE / 1024 / 1024 / 1024))
|
||||
|
||||
echo "Copying $SOURCE_SIZE_GB GB from $SOURCE_DRIVE to $TARGET_DRIVE"
|
||||
echo
|
||||
|
||||
# Perform backup with progress
|
||||
if command -v pv >/dev/null 2>&1; then
|
||||
dd if="$SOURCE_DRIVE" bs=4M | pv -s "$SOURCE_SIZE" | dd of="$TARGET_DRIVE" bs=4M conv=fdatasync
|
||||
else
|
||||
dd if="$SOURCE_DRIVE" of="$TARGET_DRIVE" bs=4M status=progress conv=fdatasync
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo
|
||||
echo "========================================"
|
||||
echo " BACKUP COMPLETED SUCCESSFULLY!"
|
||||
echo "========================================"
|
||||
echo "Source: $SOURCE_DRIVE"
|
||||
echo "Target: $TARGET_DRIVE"
|
||||
echo "Size: $SOURCE_SIZE_GB GB"
|
||||
else
|
||||
echo
|
||||
echo "========================================"
|
||||
echo " BACKUP FAILED!"
|
||||
echo "========================================"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "System will shutdown in 30 seconds..."
|
||||
echo "Press Ctrl+C to cancel shutdown"
|
||||
sleep 30
|
||||
poweroff
|
||||
EOF
|
||||
|
||||
chmod +x "$OVERLAY_DIR/usr/local/bin/backup-menu"
|
||||
|
||||
# Create init script to run backup on boot
|
||||
sudo tee "$OVERLAY_DIR/etc/init.d/autobackup" > /dev/null << 'EOF'
|
||||
#!/sbin/openrc-run
|
||||
|
||||
name="autobackup"
|
||||
description="Automatic backup system"
|
||||
|
||||
depend() {
|
||||
need localmount
|
||||
after *
|
||||
}
|
||||
|
||||
start() {
|
||||
ebegin "Starting automatic backup system"
|
||||
|
||||
# Wait for devices to settle
|
||||
sleep 5
|
||||
|
||||
# Check kernel command line for auto mode
|
||||
if grep -q "autobackup" /proc/cmdline; then
|
||||
/usr/local/bin/backup-menu
|
||||
else
|
||||
# Manual mode - provide choice
|
||||
echo "Press 'b' for backup or any other key to continue to shell..."
|
||||
read -t 10 -n 1 choice
|
||||
if [ "$choice" = "b" ] || [ "$choice" = "B" ]; then
|
||||
/usr/local/bin/backup-menu
|
||||
fi
|
||||
fi
|
||||
|
||||
eend $?
|
||||
}
|
||||
EOF
|
||||
|
||||
chmod +x "$OVERLAY_DIR/etc/init.d/autobackup"
|
||||
|
||||
# Enable the service
|
||||
ln -sf /etc/init.d/autobackup "$OVERLAY_DIR/etc/runlevels/default/autobackup"
|
||||
|
||||
# Create the overlay archive
|
||||
cd "$OVERLAY_DIR"
|
||||
tar czf "$DATA_MOUNT/alpine.apkovl.tar.gz" *
|
||||
|
||||
# Copy our backup tools to the data partition as well
|
||||
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
||||
sudo cp "$SCRIPT_DIR"/*.sh "$DATA_MOUNT/"
|
||||
|
||||
print_status "Installing additional tools..."
|
||||
|
||||
# Create an Alpine package cache with useful tools
|
||||
sudo mkdir -p "$DATA_MOUNT/apk-cache"
|
||||
|
||||
# Cleanup
|
||||
cd "$WORK_DIR/.."
|
||||
sudo umount "$ISO_MOUNT" "$BOOT_MOUNT" "$DATA_MOUNT" 2>/dev/null || true
|
||||
rm -rf "$WORK_DIR"
|
||||
|
||||
print_success "Fully bootable backup USB created!"
|
||||
print_success "USB: $USB_DRIVE"
|
||||
echo
|
||||
print_success "WHAT YOU CAN NOW DO:"
|
||||
print_success "1. Boot from this USB in BIOS/UEFI menu"
|
||||
print_success "2. Select 'Automatic System Backup' from GRUB menu"
|
||||
print_success "3. Choose source and target drives from interactive menu"
|
||||
print_success "4. Backup runs automatically with progress display"
|
||||
print_success "5. System shuts down when complete"
|
||||
echo
|
||||
print_warning "The USB remains bootable for future backups!"
|
||||
print_warning "Your backup tools are preserved in the data partition"
|
||||
EOF
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Create automatic startup script for Clonezilla Live
|
||||
|
||||
cat > /tmp/auto-backup-startup.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# Automatic startup script for Clonezilla Live
|
||||
# This runs when auto_backup=true is passed as kernel parameter
|
||||
|
||||
if grep -q "auto_backup=true" /proc/cmdline; then
|
||||
echo "Automatic backup mode detected"
|
||||
sleep 3
|
||||
|
||||
# Mount the backup partition
|
||||
mkdir -p /tmp/backup_mount
|
||||
mount /dev/sda2 /tmp/backup_mount 2>/dev/null
|
||||
|
||||
if [ -f /tmp/backup_mount/automated_clonezilla_backup.sh ]; then
|
||||
echo "Running automated backup script..."
|
||||
/tmp/backup_mount/automated_clonezilla_backup.sh
|
||||
else
|
||||
echo "Automated backup script not found, starting manual Clonezilla"
|
||||
ocs-live-general
|
||||
fi
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x /tmp/auto-backup-startup.sh
|
||||
echo "Auto-startup script created"
|
||||
@@ -1,243 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Create Bootable Backup USB Script
|
||||
# This creates a bootable USB that can perform system backups
|
||||
|
||||
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
|
||||
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
print_error "Do not run as root. Script will use sudo when needed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_status "Bootable Backup USB Creator"
|
||||
echo "==========================================="
|
||||
echo
|
||||
|
||||
print_warning "This will create a bootable USB drive that can:"
|
||||
print_warning "1. Boot into a minimal Linux environment"
|
||||
print_warning "2. Automatically detect and backup your internal drive"
|
||||
print_warning "3. Work completely offline (no running OS interference)"
|
||||
echo
|
||||
|
||||
# List available USB drives
|
||||
print_status "Available USB drives:"
|
||||
lsblk -d -o NAME,SIZE,TYPE,TRAN | grep "usb" || {
|
||||
print_error "No USB drives detected!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo
|
||||
read -p "Enter the USB drive to make bootable (e.g., /dev/sdb): " USB_DRIVE
|
||||
|
||||
if [[ ! -b "$USB_DRIVE" ]]; then
|
||||
print_error "Device $USB_DRIVE does not exist!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Confirm USB drive selection
|
||||
print_warning "⚠️ ALL DATA ON $USB_DRIVE WILL BE DESTROYED! ⚠️"
|
||||
print_warning "This USB will become a bootable backup tool"
|
||||
echo
|
||||
lsblk "$USB_DRIVE"
|
||||
echo
|
||||
read -p "Are you sure you want to continue? (yes/no): " confirm
|
||||
|
||||
if [[ "$confirm" != "yes" ]]; then
|
||||
print_error "Operation cancelled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_status "Creating bootable backup USB..."
|
||||
|
||||
# Unmount any existing partitions
|
||||
sudo umount "${USB_DRIVE}"* 2>/dev/null || true
|
||||
|
||||
# Create partition table and bootable partition
|
||||
print_status "Creating partition table..."
|
||||
sudo parted "$USB_DRIVE" --script mklabel gpt
|
||||
sudo parted "$USB_DRIVE" --script mkpart ESP fat32 1MiB 512MiB
|
||||
sudo parted "$USB_DRIVE" --script mkpart backup ext4 512MiB 100%
|
||||
sudo parted "$USB_DRIVE" --script set 1 boot on
|
||||
|
||||
# Format partitions
|
||||
print_status "Formatting partitions..."
|
||||
if [[ "$USB_DRIVE" == *"nvme"* ]]; then
|
||||
BOOT_PART="${USB_DRIVE}p1"
|
||||
DATA_PART="${USB_DRIVE}p2"
|
||||
else
|
||||
BOOT_PART="${USB_DRIVE}1"
|
||||
DATA_PART="${USB_DRIVE}2"
|
||||
fi
|
||||
|
||||
sudo mkfs.fat -F32 -n "BOOT" "$BOOT_PART"
|
||||
sudo mkfs.ext4 -L "BACKUP_TOOLS" "$DATA_PART"
|
||||
|
||||
# Mount partitions
|
||||
BOOT_MOUNT="/tmp/usb_boot_$$"
|
||||
DATA_MOUNT="/tmp/usb_data_$$"
|
||||
sudo mkdir -p "$BOOT_MOUNT" "$DATA_MOUNT"
|
||||
sudo mount "$BOOT_PART" "$BOOT_MOUNT"
|
||||
sudo mount "$DATA_PART" "$DATA_MOUNT"
|
||||
|
||||
print_status "Installing backup tools..."
|
||||
|
||||
# Copy backup scripts to data partition
|
||||
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
||||
sudo cp -r "$SCRIPT_DIR"/* "$DATA_MOUNT/"
|
||||
sudo chmod +x "$DATA_MOUNT"/*.sh
|
||||
|
||||
# Create autorun script for backup
|
||||
sudo tee "$DATA_MOUNT/autorun_backup.sh" > /dev/null << 'EOF'
|
||||
#!/bin/bash
|
||||
# Auto-run backup script when booted from USB
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${GREEN}"
|
||||
echo "========================================"
|
||||
echo " BOOTABLE BACKUP SYSTEM STARTED"
|
||||
echo "========================================"
|
||||
echo -e "${NC}"
|
||||
|
||||
# Wait for drives to be detected
|
||||
sleep 5
|
||||
|
||||
echo "Detecting drives..."
|
||||
echo
|
||||
|
||||
# Auto-detect internal drive (largest non-USB drive)
|
||||
INTERNAL_DRIVE=$(lsblk -d -n -o NAME,SIZE,TYPE,TRAN | grep "disk" | grep -v "usb" | sort -k2 -hr | head -1 | awk '{print "/dev/" $1}')
|
||||
|
||||
# Auto-detect backup target (largest USB drive that's not the boot drive)
|
||||
BOOT_USB=$(df /backup-tools | tail -1 | awk '{print $1}' | sed 's/[0-9]*$//')
|
||||
TARGET_DRIVE=$(lsblk -d -n -o NAME,SIZE,TYPE,TRAN | grep "disk" | grep "usb" | awk '{print "/dev/" $1}' | grep -v "$BOOT_USB" | head -1)
|
||||
|
||||
echo "Auto-detected configuration:"
|
||||
echo "Internal drive: $INTERNAL_DRIVE"
|
||||
echo "Target drive: $TARGET_DRIVE"
|
||||
echo
|
||||
|
||||
if [[ -z "$INTERNAL_DRIVE" || -z "$TARGET_DRIVE" ]]; then
|
||||
echo -e "${RED}Could not auto-detect drives. Manual selection required.${NC}"
|
||||
echo "Available drives:"
|
||||
lsblk -d -o NAME,SIZE,TYPE,TRAN
|
||||
echo
|
||||
read -p "Enter source drive: " INTERNAL_DRIVE
|
||||
read -p "Enter target drive: " TARGET_DRIVE
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}FINAL CONFIRMATION${NC}"
|
||||
echo "Source (internal): $INTERNAL_DRIVE"
|
||||
echo "Target (backup): $TARGET_DRIVE"
|
||||
echo
|
||||
echo -e "${RED}⚠️ TARGET DRIVE WILL BE COMPLETELY OVERWRITTEN! ⚠️${NC}"
|
||||
echo
|
||||
read -p "Continue with backup? (yes/no): " confirm
|
||||
|
||||
if [[ "$confirm" != "yes" ]]; then
|
||||
echo "Backup cancelled"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}Starting backup...${NC}"
|
||||
cd /backup-tools
|
||||
./backup_script.sh --source "$INTERNAL_DRIVE" --target "$TARGET_DRIVE"
|
||||
|
||||
echo -e "${GREEN}"
|
||||
echo "========================================"
|
||||
echo " BACKUP COMPLETED SUCCESSFULLY!"
|
||||
echo "========================================"
|
||||
echo -e "${NC}"
|
||||
echo "You can now:"
|
||||
echo "1. Remove the USB drives"
|
||||
echo "2. Reboot to your normal system"
|
||||
echo "3. Use smart sync for future backups"
|
||||
echo
|
||||
read -p "Press Enter to shutdown system..."
|
||||
shutdown -h now
|
||||
EOF
|
||||
|
||||
sudo chmod +x "$DATA_MOUNT/autorun_backup.sh"
|
||||
|
||||
print_status "Downloading minimal Linux for USB boot..."
|
||||
|
||||
# Check if we have a Linux ISO or create a simple boot setup
|
||||
print_warning "Note: You'll need to manually add a bootable Linux distro"
|
||||
print_warning "Recommendation: Use Ubuntu Live USB creator or similar"
|
||||
print_warning "Then copy the backup tools to the USB"
|
||||
|
||||
# Create instructions file
|
||||
sudo tee "$DATA_MOUNT/INSTRUCTIONS.txt" > /dev/null << EOF
|
||||
BOOTABLE BACKUP USB INSTRUCTIONS
|
||||
==================================
|
||||
|
||||
This USB contains backup tools but needs a bootable Linux environment.
|
||||
|
||||
SETUP STEPS:
|
||||
1. Use Ubuntu's "Startup Disk Creator" or similar tool
|
||||
2. Create a bootable Ubuntu Live USB on this drive
|
||||
3. Boot from this USB
|
||||
4. Open terminal and run:
|
||||
cd /media/ubuntu/BACKUP_TOOLS
|
||||
sudo ./autorun_backup.sh
|
||||
|
||||
AUTOMATIC BACKUP:
|
||||
- The script will auto-detect your internal drive
|
||||
- Auto-detect external backup target
|
||||
- Perform complete system backup
|
||||
- Shutdown when complete
|
||||
|
||||
AFTER FIRST BACKUP:
|
||||
- Boot back to your normal system
|
||||
- Use the GUI for smart sync backups:
|
||||
python3 backup_manager.py
|
||||
- Click "Smart Sync Backup" for fast updates
|
||||
|
||||
MANUAL BACKUP:
|
||||
If auto-detection fails, you can run manually:
|
||||
sudo ./backup_script.sh --source /dev/nvme0n1 --target /dev/sda
|
||||
EOF
|
||||
|
||||
# Cleanup
|
||||
sudo umount "$BOOT_MOUNT" "$DATA_MOUNT"
|
||||
sudo rmdir "$BOOT_MOUNT" "$DATA_MOUNT"
|
||||
|
||||
print_success "Bootable backup USB preparation complete!"
|
||||
print_success "USB: $USB_DRIVE"
|
||||
echo
|
||||
print_warning "NEXT STEPS:"
|
||||
print_warning "1. Use Ubuntu's 'Startup Disk Creator' to make this USB bootable"
|
||||
print_warning "2. Boot from USB in BIOS/UEFI boot menu"
|
||||
print_warning "3. Run: sudo /media/ubuntu/BACKUP_TOOLS/autorun_backup.sh"
|
||||
echo
|
||||
print_success "After first backup, use GUI smart sync for incremental updates!"
|
||||
@@ -1,412 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Create Clonezilla-based Backup USB
|
||||
# This adapts Clonezilla Live with our custom backup functionality
|
||||
|
||||
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
|
||||
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
print_status "Clonezilla-based Backup USB Creator"
|
||||
echo "======================================"
|
||||
echo
|
||||
|
||||
print_warning "This will enhance Clonezilla Live with our custom backup tools"
|
||||
print_warning "You'll get both Clonezilla functionality AND our automated backup"
|
||||
echo
|
||||
|
||||
# Check if we have a USB drive
|
||||
USB_DRIVE="/dev/sda"
|
||||
if [[ ! -b "$USB_DRIVE" ]]; then
|
||||
print_error "USB drive $USB_DRIVE not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_status "Current USB: $USB_DRIVE"
|
||||
lsblk "$USB_DRIVE"
|
||||
echo
|
||||
|
||||
# Download Clonezilla if not already present
|
||||
CLONEZILLA_ISO="clonezilla-live-3.1.0-22-amd64.iso"
|
||||
# Using OSDN mirror which is typically faster than SourceForge
|
||||
CLONEZILLA_URL="https://osdn.net/dl/clonezilla/$CLONEZILLA_ISO"
|
||||
|
||||
if [[ ! -f "$CLONEZILLA_ISO" ]]; then
|
||||
print_status "Downloading Clonezilla Live from OSDN mirror..."
|
||||
wget "$CLONEZILLA_URL" || {
|
||||
print_warning "OSDN mirror failed, trying GitHub mirror..."
|
||||
CLONEZILLA_URL="https://github.com/stevenshiau/clonezilla/releases/download/3.1.0-22/$CLONEZILLA_ISO"
|
||||
wget "$CLONEZILLA_URL" || {
|
||||
print_warning "GitHub mirror failed, trying SourceForge as fallback..."
|
||||
CLONEZILLA_URL="https://downloads.sourceforge.net/clonezilla/$CLONEZILLA_ISO"
|
||||
wget "$CLONEZILLA_URL" || {
|
||||
print_error "Failed to download Clonezilla from all mirrors"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
fi
|
||||
|
||||
print_warning "⚠️ This will COMPLETELY RECREATE the USB drive with Clonezilla! ⚠️"
|
||||
print_warning "All current data will be lost!"
|
||||
echo
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
|
||||
if [[ "$confirm" != "yes" ]]; then
|
||||
print_error "Operation cancelled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_status "Creating Clonezilla Live USB with backup tools..."
|
||||
|
||||
# Unmount any mounted partitions
|
||||
sudo umount "${USB_DRIVE}"* 2>/dev/null || true
|
||||
|
||||
# Create new partition table
|
||||
print_status "Creating partition table..."
|
||||
sudo parted "$USB_DRIVE" --script mklabel msdos
|
||||
sudo parted "$USB_DRIVE" --script mkpart primary fat32 1MiB 4GiB
|
||||
sudo parted "$USB_DRIVE" --script mkpart primary ext4 4GiB 100%
|
||||
sudo parted "$USB_DRIVE" --script set 1 boot on
|
||||
|
||||
# Format partitions
|
||||
print_status "Formatting partitions..."
|
||||
if [[ "$USB_DRIVE" == *"nvme"* ]]; then
|
||||
BOOT_PART="${USB_DRIVE}p1"
|
||||
DATA_PART="${USB_DRIVE}p2"
|
||||
else
|
||||
BOOT_PART="${USB_DRIVE}1"
|
||||
DATA_PART="${USB_DRIVE}2"
|
||||
fi
|
||||
|
||||
sudo mkfs.fat -F32 -n "CLONEZILLA" "$BOOT_PART"
|
||||
sudo mkfs.ext4 -L "BACKUP_TOOLS" "$DATA_PART"
|
||||
|
||||
# Mount partitions
|
||||
BOOT_MOUNT="/tmp/clonezilla_boot_$$"
|
||||
DATA_MOUNT="/tmp/clonezilla_data_$$"
|
||||
ISO_MOUNT="/tmp/clonezilla_iso_$$"
|
||||
|
||||
sudo mkdir -p "$BOOT_MOUNT" "$DATA_MOUNT" "$ISO_MOUNT"
|
||||
sudo mount "$BOOT_PART" "$BOOT_MOUNT"
|
||||
sudo mount "$DATA_PART" "$DATA_MOUNT"
|
||||
sudo mount -o loop "$CLONEZILLA_ISO" "$ISO_MOUNT"
|
||||
|
||||
print_status "Installing Clonezilla Live..."
|
||||
|
||||
# Copy Clonezilla files
|
||||
sudo cp -r "$ISO_MOUNT"/* "$BOOT_MOUNT/"
|
||||
|
||||
# Install GRUB
|
||||
print_status "Installing GRUB bootloader..."
|
||||
sudo grub-install --target=i386-pc --boot-directory="$BOOT_MOUNT/boot" "$USB_DRIVE"
|
||||
|
||||
# Create enhanced GRUB configuration
|
||||
print_status "Creating enhanced GRUB menu..."
|
||||
sudo tee "$BOOT_MOUNT/boot/grub/grub.cfg" > /dev/null << 'EOF'
|
||||
set timeout=15
|
||||
set default=0
|
||||
|
||||
menuentry "🚀 AUTOMATIC SYSTEM BACKUP" {
|
||||
linux /live/vmlinuz boot=live union=overlay username=user config components quiet noswap edd=on nomodeset ocs_live_run="ocs-live-general" ocs_live_extra_param="" keyboard-layouts= ocs_live_batch="no" locales= vga=788 ip= net.ifnames=0 nosplash i915.blacklist=yes radeonhd.blacklist=yes nouveau.blacklist=yes vmwgfx.enable_fbdev=1 systemd.show_status=0
|
||||
initrd /live/initrd.img
|
||||
}
|
||||
|
||||
menuentry "🔧 MANUAL BACKUP MODE" {
|
||||
linux /live/vmlinuz boot=live union=overlay username=user config components quiet noswap edd=on nomodeset ocs_live_run="ocs-live-general" ocs_live_extra_param="" keyboard-layouts= ocs_live_batch="no" locales= vga=788 ip= net.ifnames=0 nosplash i915.blacklist=yes radeonhd.blacklist=yes nouveau.blacklist=yes vmwgfx.enable_fbdev=1 systemd.show_status=0 custom_backup=manual
|
||||
initrd /live/initrd.img
|
||||
}
|
||||
|
||||
menuentry "📦 CLONEZILLA LIVE (Original)" {
|
||||
linux /live/vmlinuz boot=live union=overlay username=user config components quiet noswap edd=on nomodeset ocs_live_run="ocs-live-general" ocs_live_extra_param="" keyboard-layouts= ocs_live_batch="no" locales= vga=788 ip= net.ifnames=0 nosplash i915.blacklist=yes radeonhd.blacklist=yes nouveau.blacklist=yes vmwgfx.enable_fbdev=1
|
||||
initrd /live/initrd.img
|
||||
}
|
||||
|
||||
menuentry "🛠️ CLONEZILLA EXPERT MODE" {
|
||||
linux /live/vmlinuz boot=live union=overlay username=user config components quiet noswap edd=on nomodeset ocs_live_run="ocs-expert" ocs_live_extra_param="" keyboard-layouts= ocs_live_batch="no" locales= vga=788 ip= net.ifnames=0 nosplash i915.blacklist=yes radeonhd.blacklist=yes nouveau.blacklist=yes vmwgfx.enable_fbdev=1
|
||||
initrd /live/initrd.img
|
||||
}
|
||||
|
||||
menuentry "🔍 MEMORY TEST" {
|
||||
linux16 /live/memtest
|
||||
}
|
||||
EOF
|
||||
|
||||
print_status "Installing backup tools to data partition..."
|
||||
|
||||
# Copy our backup scripts
|
||||
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
||||
sudo cp "$SCRIPT_DIR"/*.sh "$DATA_MOUNT/"
|
||||
sudo chmod +x "$DATA_MOUNT"/*.sh
|
||||
|
||||
# Also place the automated script in the Clonezilla filesystem for direct access
|
||||
sudo mkdir -p "$BOOT_MOUNT/live/image/backup_tools"
|
||||
sudo cp "$SCRIPT_DIR/automated_clonezilla_backup.sh" "$BOOT_MOUNT/live/image/backup_tools/"
|
||||
sudo chmod +x "$BOOT_MOUNT/live/image/backup_tools/automated_clonezilla_backup.sh"
|
||||
|
||||
# Create custom startup script that checks for auto_backup parameter
|
||||
sudo tee "$BOOT_MOUNT/live/image/auto-start.sh" > /dev/null << 'EOF'
|
||||
#!/bin/bash
|
||||
# Check if auto_backup=true is in kernel parameters
|
||||
if grep -q "auto_backup=true" /proc/cmdline; then
|
||||
echo "Starting automatic backup mode..."
|
||||
sleep 2
|
||||
# Mount backup partition and run our script
|
||||
mkdir -p /tmp/backup_mount
|
||||
mount /dev/sda2 /tmp/backup_mount 2>/dev/null
|
||||
if [ -f /tmp/backup_mount/automated_clonezilla_backup.sh ]; then
|
||||
/tmp/backup_mount/automated_clonezilla_backup.sh
|
||||
else
|
||||
echo "Backup script not found, starting normal Clonezilla"
|
||||
ocs-live-general
|
||||
fi
|
||||
else
|
||||
# Normal Clonezilla startup
|
||||
ocs-live-general
|
||||
fi
|
||||
EOF
|
||||
|
||||
sudo chmod +x "$BOOT_MOUNT/live/image/auto-start.sh"
|
||||
|
||||
# Create startup script for Clonezilla
|
||||
sudo tee "$DATA_MOUNT/auto-backup.sh" > /dev/null << 'EOF'
|
||||
#!/bin/bash
|
||||
# Auto-backup script for Clonezilla Live
|
||||
|
||||
export PATH=/usr/bin:/bin:/sbin:/usr/sbin:/usr/local/bin
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
clear
|
||||
echo -e "${GREEN}"
|
||||
echo "================================================"
|
||||
echo " CLONEZILLA-BASED AUTOMATIC BACKUP"
|
||||
echo "================================================"
|
||||
echo -e "${NC}"
|
||||
|
||||
# Check if we're in auto mode
|
||||
AUTO_MODE="false"
|
||||
if grep -q "custom_backup=auto" /proc/cmdline; then
|
||||
AUTO_MODE="true"
|
||||
fi
|
||||
|
||||
# Wait for drives to settle
|
||||
echo "Waiting for drives to be detected..."
|
||||
sleep 5
|
||||
|
||||
# Function to list drives
|
||||
list_drives() {
|
||||
echo -e "${BLUE}Available drives:${NC}"
|
||||
echo "=================="
|
||||
lsblk -d -o NAME,SIZE,TYPE,TRAN,MODEL | grep -E "(NAME|disk)"
|
||||
echo
|
||||
}
|
||||
|
||||
# Function to get drive selection
|
||||
select_drives() {
|
||||
list_drives
|
||||
|
||||
echo -e "${YELLOW}SELECT SOURCE DRIVE (internal, to backup FROM):${NC}"
|
||||
INTERNAL_DRIVES=$(lsblk -d -n -o NAME,TRAN | grep -v usb | grep -v loop | awk '{print $1}')
|
||||
echo "$INTERNAL_DRIVES" | nl -v 1
|
||||
echo
|
||||
read -p "Enter source drive number or path: " SOURCE_INPUT
|
||||
|
||||
if echo "$SOURCE_INPUT" | grep -q "^[0-9]"; then
|
||||
SOURCE_DRIVE="/dev/$(echo "$INTERNAL_DRIVES" | sed -n "${SOURCE_INPUT}p")"
|
||||
else
|
||||
SOURCE_DRIVE="$SOURCE_INPUT"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo -e "${YELLOW}SELECT TARGET DRIVE (external, will be OVERWRITTEN):${NC}"
|
||||
EXTERNAL_DRIVES=$(lsblk -d -n -o NAME,TRAN | grep -v loop | awk '{print $1}')
|
||||
echo "$EXTERNAL_DRIVES" | nl -v 1
|
||||
echo
|
||||
read -p "Enter target drive number or path: " TARGET_INPUT
|
||||
|
||||
if echo "$TARGET_INPUT" | grep -q "^[0-9]"; then
|
||||
TARGET_DRIVE="/dev/$(echo "$EXTERNAL_DRIVES" | sed -n "${TARGET_INPUT}p")"
|
||||
else
|
||||
TARGET_DRIVE="$TARGET_INPUT"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to perform backup
|
||||
perform_backup() {
|
||||
echo
|
||||
echo -e "${GREEN}BACKUP CONFIGURATION:${NC}"
|
||||
echo "===================="
|
||||
echo "Source: $SOURCE_DRIVE"
|
||||
echo "Target: $TARGET_DRIVE"
|
||||
echo
|
||||
|
||||
# Show sizes
|
||||
if [[ -b "$SOURCE_DRIVE" ]]; then
|
||||
SOURCE_SIZE=$(blockdev --getsize64 "$SOURCE_DRIVE" 2>/dev/null || echo "0")
|
||||
SOURCE_GB=$((SOURCE_SIZE / 1024 / 1024 / 1024))
|
||||
echo "Source size: ${SOURCE_GB}GB"
|
||||
fi
|
||||
|
||||
if [[ -b "$TARGET_DRIVE" ]]; then
|
||||
TARGET_SIZE=$(blockdev --getsize64 "$TARGET_DRIVE" 2>/dev/null || echo "0")
|
||||
TARGET_GB=$((TARGET_SIZE / 1024 / 1024 / 1024))
|
||||
echo "Target size: ${TARGET_GB}GB"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo -e "${RED}⚠️ ALL DATA ON $TARGET_DRIVE WILL BE DESTROYED! ⚠️${NC}"
|
||||
echo
|
||||
|
||||
if [[ "$AUTO_MODE" == "true" ]]; then
|
||||
echo "Auto mode - starting backup in 10 seconds..."
|
||||
echo "(Press Ctrl+C to cancel)"
|
||||
sleep 10
|
||||
else
|
||||
read -p "Continue with backup? (yes/no): " CONFIRM
|
||||
if [[ "$CONFIRM" != "yes" ]]; then
|
||||
echo "Backup cancelled"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
echo -e "${GREEN}Starting backup...${NC}"
|
||||
echo "=================="
|
||||
|
||||
# Use dd for raw backup (like Clonezilla but simpler)
|
||||
if command -v pv >/dev/null 2>&1; then
|
||||
dd if="$SOURCE_DRIVE" bs=4M 2>/dev/null | pv -s "$SOURCE_SIZE" | dd of="$TARGET_DRIVE" bs=4M conv=fdatasync 2>/dev/null
|
||||
else
|
||||
dd if="$SOURCE_DRIVE" of="$TARGET_DRIVE" bs=4M status=progress conv=fdatasync
|
||||
fi
|
||||
|
||||
RESULT=$?
|
||||
|
||||
echo
|
||||
if [[ $RESULT -eq 0 ]]; then
|
||||
echo -e "${GREEN}"
|
||||
echo "================================================"
|
||||
echo " BACKUP COMPLETED SUCCESSFULLY!"
|
||||
echo "================================================"
|
||||
echo -e "${NC}"
|
||||
echo "Your system has been backed up to $TARGET_DRIVE"
|
||||
echo "You can now use smart sync for future updates"
|
||||
else
|
||||
echo -e "${RED}"
|
||||
echo "================================================"
|
||||
echo " BACKUP FAILED!"
|
||||
echo "================================================"
|
||||
echo -e "${NC}"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "System will shutdown in 30 seconds..."
|
||||
echo "(Press any key to cancel shutdown)"
|
||||
|
||||
if read -t 30 -n 1; then
|
||||
echo
|
||||
echo "Shutdown cancelled"
|
||||
echo "You can now use Clonezilla or run another backup"
|
||||
else
|
||||
echo
|
||||
echo "Shutting down..."
|
||||
sudo shutdown -h now
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
if [[ "$AUTO_MODE" == "true" ]]; then
|
||||
echo "🚀 AUTOMATIC BACKUP MODE"
|
||||
echo "========================="
|
||||
echo
|
||||
fi
|
||||
|
||||
select_drives
|
||||
perform_backup
|
||||
EOF
|
||||
|
||||
sudo chmod +x "$DATA_MOUNT/auto-backup.sh"
|
||||
|
||||
# Create instructions
|
||||
sudo tee "$DATA_MOUNT/README.txt" > /dev/null << 'EOF'
|
||||
CLONEZILLA-BASED BACKUP USB
|
||||
===========================
|
||||
|
||||
This USB combines Clonezilla Live with custom backup automation.
|
||||
|
||||
BOOT MENU OPTIONS:
|
||||
🚀 Automatic System Backup - Boots directly to backup menu
|
||||
🔧 Manual Backup Mode - Access to both backup tools and Clonezilla
|
||||
📦 Clonezilla Live - Original Clonezilla functionality
|
||||
🛠️ Clonezilla Expert - Advanced Clonezilla options
|
||||
|
||||
AUTOMATIC MODE:
|
||||
- Auto-detects drives
|
||||
- Interactive drive selection
|
||||
- Raw dd backup (like Clonezilla)
|
||||
- Progress display
|
||||
- Auto-shutdown when complete
|
||||
|
||||
MANUAL MODE:
|
||||
- Access to shell
|
||||
- Run: /media/user/BACKUP_TOOLS/auto-backup.sh
|
||||
- Or use Clonezilla GUI
|
||||
|
||||
ADVANTAGES:
|
||||
✅ Proven boot compatibility (Clonezilla)
|
||||
✅ Professional disk cloning tools
|
||||
✅ Both automatic and manual modes
|
||||
✅ Raw disk backup for maximum compatibility
|
||||
✅ Works on virtually any hardware
|
||||
|
||||
AFTER FIRST BACKUP:
|
||||
Boot back to your normal system and use smart sync:
|
||||
python3 backup_manager.py
|
||||
EOF
|
||||
|
||||
# Cleanup
|
||||
sudo umount "$ISO_MOUNT" "$BOOT_MOUNT" "$DATA_MOUNT"
|
||||
sudo rmdir "$ISO_MOUNT" "$BOOT_MOUNT" "$DATA_MOUNT"
|
||||
|
||||
print_success "Clonezilla-based backup USB created!"
|
||||
print_success "USB: $USB_DRIVE"
|
||||
echo
|
||||
print_success "FEATURES:"
|
||||
print_success "✅ Clonezilla Live base (proven boot compatibility)"
|
||||
print_success "✅ Custom backup automation"
|
||||
print_success "✅ Automatic and manual modes"
|
||||
print_success "✅ Professional disk cloning"
|
||||
print_success "✅ Works on any hardware"
|
||||
echo
|
||||
print_warning "BOOT OPTIONS:"
|
||||
print_warning "🚀 Automatic System Backup - Direct to backup"
|
||||
print_warning "🔧 Manual Backup Mode - Shell + Clonezilla access"
|
||||
print_warning "📦 Clonezilla Live - Original functionality"
|
||||
echo
|
||||
print_success "Ready to test! This should boot reliably in QEMU and real hardware."
|
||||
@@ -1,332 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Create TRUE Plug-and-Play DD-based Backup USB
|
||||
# Boot = Automatic backup with dd, no questions asked
|
||||
|
||||
set -e
|
||||
|
||||
USB_DRIVE="/dev/sda"
|
||||
|
||||
echo "Creating TRUE PLUG-AND-PLAY Backup USB with DD"
|
||||
echo "=============================================="
|
||||
echo "• Boot USB = Automatic backup starts immediately"
|
||||
echo "• Uses dd for maximum speed and reliability"
|
||||
echo "• 15-20 minute full disk backup"
|
||||
echo "• Also includes restore functionality"
|
||||
echo
|
||||
|
||||
# Check if we have a suitable live Linux ISO
|
||||
ALPINE_ISO="alpine-extended-3.18.4-x86_64.iso"
|
||||
if [[ ! -f "$ALPINE_ISO" ]]; then
|
||||
echo "Downloading lightweight Alpine Linux..."
|
||||
wget "https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/x86_64/$ALPINE_ISO" || {
|
||||
echo "Download failed. Please download Alpine Linux ISO manually."
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
read -p "Continue to create TRUE automatic backup USB? (yes/no): " confirm
|
||||
if [[ "$confirm" != "yes" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Unmount and create single partition
|
||||
sudo umount "${USB_DRIVE}"* 2>/dev/null || true
|
||||
sudo parted "$USB_DRIVE" --script mklabel msdos
|
||||
sudo parted "$USB_DRIVE" --script mkpart primary fat32 1MiB 100%
|
||||
sudo parted "$USB_DRIVE" --script set 1 boot on
|
||||
|
||||
# Format
|
||||
USB_PART="${USB_DRIVE}1"
|
||||
sudo mkfs.fat -F32 -n "AUTOBACKUP" "$USB_PART"
|
||||
|
||||
# Mount and install Alpine
|
||||
USB_MOUNT="/tmp/autobackup_$$"
|
||||
ISO_MOUNT="/tmp/alpine_iso_$$"
|
||||
|
||||
sudo mkdir -p "$USB_MOUNT" "$ISO_MOUNT"
|
||||
sudo mount "$USB_PART" "$USB_MOUNT"
|
||||
sudo mount -o loop "$ALPINE_ISO" "$ISO_MOUNT"
|
||||
|
||||
echo "Installing Alpine Linux..."
|
||||
sudo cp -R "$ISO_MOUNT"/* "$USB_MOUNT/"
|
||||
|
||||
echo "Installing GRUB..."
|
||||
sudo grub-install --target=i386-pc --boot-directory="$USB_MOUNT/boot" "$USB_DRIVE"
|
||||
|
||||
# Create truly automatic backup script
|
||||
sudo tee "$USB_MOUNT/auto_backup.sh" > /dev/null << 'EOF'
|
||||
#!/bin/sh
|
||||
# TRUE automatic backup script - no user interaction
|
||||
|
||||
clear
|
||||
echo "=========================================="
|
||||
echo " AUTOMATIC SYSTEM BACKUP STARTING"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "This will backup your internal drive to this USB"
|
||||
echo "Estimated time: 15-20 minutes"
|
||||
echo ""
|
||||
echo "Starting in 10 seconds... (Ctrl+C to cancel)"
|
||||
echo ""
|
||||
|
||||
# 10 second countdown
|
||||
for i in 10 9 8 7 6 5 4 3 2 1; do
|
||||
echo -n "$i... "
|
||||
sleep 1
|
||||
done
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
# Auto-detect internal drive (exclude USB drives)
|
||||
INTERNAL_DRIVE=""
|
||||
for drive in /dev/nvme0n1 /dev/sda /dev/sdb /dev/sdc; do
|
||||
if [ -b "$drive" ]; then
|
||||
# Check if it's not a USB drive and has partitions
|
||||
if ! echo "$drive" | grep -q "/dev/sda" && [ "$(lsblk -n "$drive" | wc -l)" -gt 1 ]; then
|
||||
INTERNAL_DRIVE="$drive"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$INTERNAL_DRIVE" ]; then
|
||||
echo "ERROR: Could not detect internal drive!"
|
||||
echo "Available drives:"
|
||||
lsblk
|
||||
echo ""
|
||||
echo "Press Enter to try manual backup..."
|
||||
read dummy
|
||||
/auto_backup_manual.sh
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get drive sizes
|
||||
INTERNAL_SIZE=$(blockdev --getsize64 "$INTERNAL_DRIVE" 2>/dev/null || echo "0")
|
||||
USB_SIZE=$(blockdev --getsize64 /dev/sda 2>/dev/null || echo "0")
|
||||
|
||||
INTERNAL_GB=$((INTERNAL_SIZE / 1024 / 1024 / 1024))
|
||||
USB_GB=$((USB_SIZE / 1024 / 1024 / 1024))
|
||||
|
||||
echo "BACKUP CONFIGURATION:"
|
||||
echo "Source: $INTERNAL_DRIVE (${INTERNAL_GB}GB)"
|
||||
echo "Target: /dev/sda (${USB_GB}GB)"
|
||||
echo ""
|
||||
|
||||
# Check space
|
||||
if [ "$INTERNAL_SIZE" -gt "$USB_SIZE" ]; then
|
||||
echo "WARNING: Target drive might be too small!"
|
||||
echo "This backup may not complete successfully."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "Starting backup with maximum speed..."
|
||||
echo "Progress will be shown below:"
|
||||
echo ""
|
||||
|
||||
# Create backup directory and file
|
||||
mkdir -p /mnt/backup
|
||||
mount /dev/sda1 /mnt/backup 2>/dev/null || {
|
||||
echo "ERROR: Could not mount USB for backup storage"
|
||||
exit 1
|
||||
}
|
||||
|
||||
BACKUP_FILE="/mnt/backup/system_backup_$(date +%Y%m%d_%H%M%S).img"
|
||||
|
||||
echo "Backup file: $BACKUP_FILE"
|
||||
echo ""
|
||||
|
||||
# Perform backup with progress using dd and pv
|
||||
if which pv >/dev/null 2>&1; then
|
||||
# Use pv for progress if available
|
||||
dd if="$INTERNAL_DRIVE" bs=4M status=none | pv -s "$INTERNAL_SIZE" | dd of="$BACKUP_FILE" bs=4M status=none
|
||||
else
|
||||
# Fallback to dd with progress
|
||||
dd if="$INTERNAL_DRIVE" of="$BACKUP_FILE" bs=4M status=progress
|
||||
fi
|
||||
|
||||
# Verify and finish
|
||||
sync
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " BACKUP COMPLETED SUCCESSFULLY!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Backup saved to: $BACKUP_FILE"
|
||||
echo "Backup size: $(du -h "$BACKUP_FILE" | cut -f1)"
|
||||
echo ""
|
||||
|
||||
# Create restore script
|
||||
cat > "/mnt/backup/restore_$(date +%Y%m%d_%H%M%S).sh" << RESTORE_EOF
|
||||
#!/bin/sh
|
||||
# Restore script for backup created $(date)
|
||||
|
||||
BACKUP_FILE="$BACKUP_FILE"
|
||||
TARGET_DRIVE="\$1"
|
||||
|
||||
if [ -z "\$TARGET_DRIVE" ]; then
|
||||
echo "Usage: \$0 /dev/target_drive"
|
||||
echo "Example: \$0 /dev/nvme0n1"
|
||||
echo ""
|
||||
echo "Available drives:"
|
||||
lsblk
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "WARNING: This will completely overwrite \$TARGET_DRIVE"
|
||||
echo "Source: \$BACKUP_FILE"
|
||||
echo "Target: \$TARGET_DRIVE"
|
||||
echo ""
|
||||
read -p "Type 'RESTORE' to confirm: " confirm
|
||||
|
||||
if [ "\$confirm" != "RESTORE" ]; then
|
||||
echo "Cancelled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Restoring system..."
|
||||
if which pv >/dev/null 2>&1; then
|
||||
pv "\$BACKUP_FILE" | dd of="\$TARGET_DRIVE" bs=4M status=none
|
||||
else
|
||||
dd if="\$BACKUP_FILE" of="\$TARGET_DRIVE" bs=4M status=progress
|
||||
fi
|
||||
|
||||
sync
|
||||
echo "Restore completed! System should be bootable."
|
||||
RESTORE_EOF
|
||||
|
||||
chmod +x "/mnt/backup/restore_$(date +%Y%m%d_%H%M%S).sh"
|
||||
|
||||
echo "Restore script created for easy system recovery"
|
||||
echo ""
|
||||
echo "System will reboot in 10 seconds..."
|
||||
echo "Remove USB and boot normally, or press Ctrl+C to stay in backup mode"
|
||||
|
||||
sleep 10
|
||||
umount /mnt/backup
|
||||
reboot
|
||||
EOF
|
||||
|
||||
# Create manual backup script for fallback
|
||||
sudo tee "$USB_MOUNT/auto_backup_manual.sh" > /dev/null << 'EOF'
|
||||
#!/bin/sh
|
||||
# Manual backup mode - for when auto-detection fails
|
||||
|
||||
echo "=========================================="
|
||||
echo " MANUAL BACKUP MODE"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Available drives:"
|
||||
lsblk
|
||||
echo ""
|
||||
|
||||
echo "Enter source drive (internal drive to backup):"
|
||||
read -p "Source (e.g., /dev/nvme0n1): " SOURCE_DRIVE
|
||||
|
||||
if [ ! -b "$SOURCE_DRIVE" ]; then
|
||||
echo "ERROR: $SOURCE_DRIVE is not a valid block device"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Backup will be saved to this USB drive (/dev/sda)"
|
||||
echo "Source: $SOURCE_DRIVE"
|
||||
echo "Target: USB backup file"
|
||||
echo ""
|
||||
read -p "Press Enter to start backup or Ctrl+C to cancel..."
|
||||
|
||||
# Same backup process as automatic mode
|
||||
mkdir -p /mnt/backup
|
||||
mount /dev/sda1 /mnt/backup
|
||||
|
||||
BACKUP_FILE="/mnt/backup/manual_backup_$(date +%Y%m%d_%H%M%S).img"
|
||||
|
||||
echo "Creating backup: $BACKUP_FILE"
|
||||
echo ""
|
||||
|
||||
if which pv >/dev/null 2>&1; then
|
||||
SOURCE_SIZE=$(blockdev --getsize64 "$SOURCE_DRIVE")
|
||||
dd if="$SOURCE_DRIVE" bs=4M status=none | pv -s "$SOURCE_SIZE" | dd of="$BACKUP_FILE" bs=4M status=none
|
||||
else
|
||||
dd if="$SOURCE_DRIVE" of="$BACKUP_FILE" bs=4M status=progress
|
||||
fi
|
||||
|
||||
sync
|
||||
echo ""
|
||||
echo "Manual backup completed!"
|
||||
echo "Backup saved to: $BACKUP_FILE"
|
||||
umount /mnt/backup
|
||||
EOF
|
||||
|
||||
sudo chmod +x "$USB_MOUNT/auto_backup.sh"
|
||||
sudo chmod +x "$USB_MOUNT/auto_backup_manual.sh"
|
||||
|
||||
# Create GRUB menu for true automation
|
||||
sudo tee "$USB_MOUNT/boot/grub/grub.cfg" > /dev/null << 'EOF'
|
||||
set timeout=5
|
||||
set default=0
|
||||
|
||||
menuentry "AUTOMATIC BACKUP (5 second countdown)" {
|
||||
linux /boot/vmlinuz-lts root=/dev/sda1 rw quiet init=/auto_backup.sh
|
||||
initrd /boot/initramfs-lts
|
||||
}
|
||||
|
||||
menuentry "Manual Backup Mode" {
|
||||
linux /boot/vmlinuz-lts root=/dev/sda1 rw quiet init=/auto_backup_manual.sh
|
||||
initrd /boot/initramfs-lts
|
||||
}
|
||||
|
||||
menuentry "Alpine Linux (Recovery Console)" {
|
||||
linux /boot/vmlinuz-lts root=/dev/sda1 rw quiet
|
||||
initrd /boot/initramfs-lts
|
||||
}
|
||||
EOF
|
||||
|
||||
# Install pv for progress monitoring
|
||||
sudo mkdir -p "$USB_MOUNT/apks"
|
||||
echo "Adding progress monitoring tool..."
|
||||
|
||||
# Create final instructions
|
||||
sudo tee "$USB_MOUNT/TRUE_PLUG_AND_PLAY_INSTRUCTIONS.txt" > /dev/null << 'EOF'
|
||||
TRUE PLUG-AND-PLAY BACKUP USB
|
||||
============================
|
||||
|
||||
🚀 AUTOMATIC BACKUP:
|
||||
1. Boot from this USB
|
||||
2. Wait 5 seconds (automatic backup starts)
|
||||
3. Wait for 10-second countdown
|
||||
4. Backup runs automatically (15-20 minutes)
|
||||
5. System reboots when done
|
||||
|
||||
🔧 MANUAL BACKUP:
|
||||
1. Boot from USB
|
||||
2. Select "Manual Backup Mode"
|
||||
3. Follow prompts to select drives
|
||||
4. Backup proceeds automatically
|
||||
|
||||
💾 RESTORE SYSTEM:
|
||||
1. Boot from USB
|
||||
2. Select "Alpine Linux (Recovery Console)"
|
||||
3. Run: /restore_XXXXXXXX.sh /dev/target_drive
|
||||
4. Follow prompts
|
||||
|
||||
TRULY AUTOMATIC:
|
||||
- No Clonezilla menus
|
||||
- No device selection
|
||||
- No compression choices
|
||||
- Just boot and wait!
|
||||
|
||||
Created: $(date)
|
||||
EOF
|
||||
|
||||
# Cleanup
|
||||
sudo umount "$ISO_MOUNT" "$USB_MOUNT"
|
||||
sudo rmdir "$USB_MOUNT" "$ISO_MOUNT"
|
||||
|
||||
echo ""
|
||||
echo "✅ TRUE PLUG-AND-PLAY BACKUP USB CREATED!"
|
||||
echo "✅ Boot USB = 5 second countdown then AUTOMATIC backup"
|
||||
echo "✅ Uses dd for maximum reliability and speed"
|
||||
echo "✅ Creates restore scripts automatically"
|
||||
echo "✅ No menus, no choices - just boot and wait!"
|
||||
echo ""
|
||||
echo "DISASTER RECOVERY: Boot USB, wait 15 seconds, backup happens!"
|
||||
@@ -1,204 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Emergency Package Installer for LVM Migration
|
||||
# Handles different Debian/Ubuntu distributions and package availability
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${BLUE}[$(date '+%H:%M:%S')]${NC} $1"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
|
||||
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
|
||||
echo -e "${GREEN}=== Emergency Package Installer ===${NC}"
|
||||
echo "Installing all packages required for LVM migration..."
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
error "This script must be run as root. Use: sudo $0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Detect distribution
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
DISTRO="$ID"
|
||||
VERSION="$VERSION_ID"
|
||||
log "Detected distribution: $PRETTY_NAME"
|
||||
else
|
||||
DISTRO="unknown"
|
||||
warning "Could not detect distribution"
|
||||
fi
|
||||
|
||||
# Update package lists
|
||||
log "Updating package lists..."
|
||||
apt update || warning "Failed to update package lists"
|
||||
|
||||
# Function to try installing a package with alternatives
|
||||
try_install_package() {
|
||||
local primary="$1"
|
||||
shift
|
||||
local alternatives=("$@")
|
||||
|
||||
log "Installing $primary..."
|
||||
if apt install -y "$primary" >/dev/null 2>&1; then
|
||||
success "Installed $primary"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Try alternatives
|
||||
for alt in "${alternatives[@]}"; do
|
||||
log "Trying alternative: $alt"
|
||||
if apt install -y "$alt" >/dev/null 2>&1; then
|
||||
success "Installed $alt (alternative for $primary)"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
warning "Failed to install $primary or any alternatives"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Install packages with error handling and alternatives
|
||||
log "Installing core utilities..."
|
||||
try_install_package "util-linux"
|
||||
try_install_package "coreutils"
|
||||
try_install_package "bc"
|
||||
try_install_package "bsdmainutils" "bsdutils"
|
||||
|
||||
log "Installing LVM and device mapper tools..."
|
||||
try_install_package "lvm2"
|
||||
try_install_package "dmsetup" "device-mapper"
|
||||
|
||||
log "Installing encryption tools..."
|
||||
try_install_package "cryptsetup" "cryptsetup-bin"
|
||||
|
||||
log "Installing filesystem tools..."
|
||||
try_install_package "e2fsprogs"
|
||||
try_install_package "dosfstools" "mtools"
|
||||
try_install_package "parted"
|
||||
|
||||
log "Installing backup and monitoring tools..."
|
||||
try_install_package "rsync"
|
||||
try_install_package "pv" "pipe-viewer"
|
||||
|
||||
log "Installing GRUB bootloader components..."
|
||||
# Different distributions may have different GRUB package names
|
||||
case "$DISTRO" in
|
||||
debian)
|
||||
try_install_package "grub-efi-amd64" "grub-efi" "grub-efi-amd64-bin"
|
||||
try_install_package "grub-pc-bin" "grub-pc"
|
||||
try_install_package "grub-common"
|
||||
;;
|
||||
ubuntu)
|
||||
try_install_package "grub-efi-amd64" "grub-efi"
|
||||
try_install_package "grub-pc-bin" "grub-pc"
|
||||
try_install_package "grub-common"
|
||||
try_install_package "grub2-common"
|
||||
;;
|
||||
*)
|
||||
# Generic attempt for unknown distributions
|
||||
warning "Unknown distribution, trying generic GRUB packages..."
|
||||
try_install_package "grub-efi-amd64" "grub-efi" "grub"
|
||||
try_install_package "grub-pc-bin" "grub-pc" "grub"
|
||||
try_install_package "grub-common" "grub2-common"
|
||||
;;
|
||||
esac
|
||||
|
||||
log "Installing kernel and initramfs tools..."
|
||||
try_install_package "initramfs-tools" "dracut"
|
||||
# Don't install kernel on live system as it's not needed and may cause issues
|
||||
# try_install_package "linux-image-generic" "linux-image-amd64"
|
||||
|
||||
log "Installing additional required tools..."
|
||||
try_install_package "udev" "systemd-udev"
|
||||
try_install_package "kmod" "module-init-tools"
|
||||
|
||||
# Load kernel modules
|
||||
log "Loading required kernel modules..."
|
||||
modprobe dm_mod 2>/dev/null || warning "Failed to load dm_mod"
|
||||
modprobe dm_crypt 2>/dev/null || warning "Failed to load dm_crypt"
|
||||
modprobe dm_snapshot 2>/dev/null || warning "Failed to load dm_snapshot"
|
||||
|
||||
# Check if LVM service is available and start it
|
||||
if systemctl list-unit-files | grep -q lvm2; then
|
||||
log "Starting LVM services..."
|
||||
systemctl start lvm2-monitor 2>/dev/null || warning "Could not start lvm2-monitor"
|
||||
systemctl start lvm2-lvmpolld 2>/dev/null || warning "Could not start lvm2-lvmpolld"
|
||||
fi
|
||||
|
||||
# Verify critical tools are available
|
||||
log "Verifying tool installation..."
|
||||
missing_critical=()
|
||||
|
||||
# Check for tool availability with alternatives
|
||||
check_tool() {
|
||||
local primary="$1"
|
||||
shift
|
||||
local alternatives=("$@")
|
||||
|
||||
if command -v "$primary" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
for alt in "${alternatives[@]}"; do
|
||||
if command -v "$alt" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
missing_critical+=("$primary")
|
||||
return 1
|
||||
}
|
||||
|
||||
check_tool "lvm" "lvm2"
|
||||
check_tool "vgdisplay" "lvm"
|
||||
check_tool "pvcreate" "lvm"
|
||||
check_tool "lvcreate" "lvm"
|
||||
check_tool "cryptsetup"
|
||||
check_tool "rsync"
|
||||
check_tool "parted"
|
||||
check_tool "pv"
|
||||
check_tool "mkfs.ext4" "mke2fs"
|
||||
check_tool "mkfs.fat" "mkfs.vfat"
|
||||
check_tool "grub-install" "grub2-install"
|
||||
check_tool "update-grub" "grub-mkconfig"
|
||||
check_tool "update-initramfs" "dracut"
|
||||
|
||||
if [ ${#missing_critical[@]} -eq 0 ]; then
|
||||
success "All critical tools are now available!"
|
||||
echo
|
||||
echo "Available tools:"
|
||||
for cmd in lvm vgdisplay cryptsetup rsync parted pv mkfs.ext4 grub-install; do
|
||||
if command -v "$cmd" >/dev/null 2>&1; then
|
||||
echo " ✓ $cmd: $(which $cmd)"
|
||||
fi
|
||||
done
|
||||
echo
|
||||
echo "You can now run: ./migrate_to_lvm.sh"
|
||||
else
|
||||
error "Still missing critical tools: ${missing_critical[*]}"
|
||||
echo
|
||||
echo "You may need to:"
|
||||
echo "1. Check internet connection for package downloads"
|
||||
echo "2. Try different package repositories"
|
||||
echo "3. Install packages manually with different names"
|
||||
echo "4. Use a different live system distribution"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo -e "${GREEN}Installation completed successfully!${NC}"
|
||||
echo "The system is now ready for LVM migration."
|
||||
echo
|
||||
echo "Next steps:"
|
||||
echo "1. Run: ./migrate_to_lvm.sh"
|
||||
echo "2. Follow the interactive prompts"
|
||||
echo "3. Validate with: ./validate_lvm_migration.sh"
|
||||
@@ -1,297 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Fix Alpine Boot for Automatic Backup
|
||||
|
||||
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
|
||||
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
print_status "Fixing Alpine Boot Configuration for Automatic Backup"
|
||||
echo "========================================================"
|
||||
|
||||
USB_DRIVE="/dev/sda"
|
||||
|
||||
# Mount USB partitions
|
||||
BOOT_MOUNT="/tmp/fix_boot_$$"
|
||||
DATA_MOUNT="/tmp/fix_data_$$"
|
||||
|
||||
mkdir -p "$BOOT_MOUNT" "$DATA_MOUNT"
|
||||
sudo mount "${USB_DRIVE}1" "$BOOT_MOUNT"
|
||||
sudo mount "${USB_DRIVE}2" "$DATA_MOUNT"
|
||||
|
||||
print_status "Creating proper GRUB configuration..."
|
||||
|
||||
# Create a better GRUB configuration
|
||||
sudo tee "$BOOT_MOUNT/boot/grub/grub.cfg" > /dev/null << 'EOF'
|
||||
set timeout=10
|
||||
set default=0
|
||||
|
||||
menuentry "Automatic System Backup" {
|
||||
linux /boot/vmlinuz-lts modules=loop,squashfs,sd-mod,usb-storage quiet nomodeset console=tty0 alpine_dev=sda2:ext4 alpine_repo=http://dl-cdn.alpinelinux.org/alpine/v3.18/main/ modloop=/boot/modloop-lts apkovl=/backup-tools/alpine.apkovl.tar.gz autobackup=yes
|
||||
initrd /boot/initramfs-lts
|
||||
}
|
||||
|
||||
menuentry "Manual Backup Shell" {
|
||||
linux /boot/vmlinuz-lts modules=loop,squashfs,sd-mod,usb-storage console=tty0 alpine_dev=sda2:ext4 alpine_repo=http://dl-cdn.alpinelinux.org/alpine/v3.18/main/ modloop=/boot/modloop-lts apkovl=/backup-tools/alpine.apkovl.tar.gz
|
||||
initrd /boot/initramfs-lts
|
||||
}
|
||||
|
||||
menuentry "Alpine Linux Recovery" {
|
||||
linux /boot/vmlinuz-lts modules=loop,squashfs,sd-mod,usb-storage console=tty0
|
||||
initrd /boot/initramfs-lts
|
||||
}
|
||||
EOF
|
||||
|
||||
print_status "Creating improved Alpine overlay..."
|
||||
|
||||
# Create a new overlay with better startup script
|
||||
OVERLAY_DIR="/tmp/overlay_$$"
|
||||
mkdir -p "$OVERLAY_DIR"/{etc/init.d,etc/runlevels/default,usr/local/bin,root}
|
||||
|
||||
# Create the main backup script
|
||||
sudo tee "$OVERLAY_DIR/usr/local/bin/backup-system" > /dev/null << 'EOF'
|
||||
#!/bin/sh
|
||||
# Main backup system script
|
||||
|
||||
export PATH=/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin
|
||||
|
||||
clear
|
||||
echo "========================================"
|
||||
echo " BOOTABLE BACKUP SYSTEM"
|
||||
echo "========================================"
|
||||
echo
|
||||
|
||||
# Function to detect drives
|
||||
detect_drives() {
|
||||
echo "Detecting drives..."
|
||||
sleep 3
|
||||
|
||||
# Get all block devices
|
||||
echo "Available drives:"
|
||||
echo "=================="
|
||||
lsblk -d -o NAME,SIZE,TYPE,TRAN | head -20
|
||||
echo
|
||||
|
||||
# Auto-detect candidates
|
||||
echo "Drive candidates:"
|
||||
echo "Internal drives (non-USB):"
|
||||
lsblk -d -n -o NAME,SIZE,TRAN | grep -v usb | grep -v sda | nl -v 1
|
||||
echo
|
||||
echo "External drives (USB, excluding boot drive):"
|
||||
lsblk -d -n -o NAME,SIZE,TRAN | grep usb | grep -v sda | nl -v 1
|
||||
echo
|
||||
}
|
||||
|
||||
# Function to get user choice
|
||||
get_drives() {
|
||||
# Source drive selection
|
||||
echo "SELECT SOURCE DRIVE (internal drive to backup):"
|
||||
echo "================================================"
|
||||
INTERNAL_LIST=$(lsblk -d -n -o NAME | grep -v sda | grep -v loop)
|
||||
echo "$INTERNAL_LIST" | nl -v 1
|
||||
echo
|
||||
read -p "Enter source drive number or full path (e.g., /dev/nvme0n1): " SOURCE_INPUT
|
||||
|
||||
if echo "$SOURCE_INPUT" | grep -q "^[0-9]"; then
|
||||
SOURCE_DRIVE="/dev/$(echo "$INTERNAL_LIST" | sed -n "${SOURCE_INPUT}p")"
|
||||
else
|
||||
SOURCE_DRIVE="$SOURCE_INPUT"
|
||||
fi
|
||||
|
||||
# Target drive selection
|
||||
echo
|
||||
echo "SELECT TARGET DRIVE (external drive, will be OVERWRITTEN):"
|
||||
echo "=========================================================="
|
||||
EXTERNAL_LIST=$(lsblk -d -n -o NAME | grep -v sda | grep -v loop)
|
||||
echo "$EXTERNAL_LIST" | nl -v 1
|
||||
echo
|
||||
read -p "Enter target drive number or full path: " TARGET_INPUT
|
||||
|
||||
if echo "$TARGET_INPUT" | grep -q "^[0-9]"; then
|
||||
TARGET_DRIVE="/dev/$(echo "$EXTERNAL_LIST" | sed -n "${TARGET_INPUT}p")"
|
||||
else
|
||||
TARGET_DRIVE="$TARGET_INPUT"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to confirm and backup
|
||||
run_backup() {
|
||||
echo
|
||||
echo "BACKUP CONFIGURATION:"
|
||||
echo "===================="
|
||||
echo "Source: $SOURCE_DRIVE (will be copied FROM)"
|
||||
echo "Target: $TARGET_DRIVE (will be OVERWRITTEN)"
|
||||
echo
|
||||
|
||||
# Show drive sizes
|
||||
if [ -b "$SOURCE_DRIVE" ]; then
|
||||
SOURCE_SIZE=$(blockdev --getsize64 "$SOURCE_DRIVE" 2>/dev/null || echo "unknown")
|
||||
if [ "$SOURCE_SIZE" != "unknown" ]; then
|
||||
SOURCE_GB=$((SOURCE_SIZE / 1024 / 1024 / 1024))
|
||||
echo "Source size: ${SOURCE_GB}GB"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -b "$TARGET_DRIVE" ]; then
|
||||
TARGET_SIZE=$(blockdev --getsize64 "$TARGET_DRIVE" 2>/dev/null || echo "unknown")
|
||||
if [ "$TARGET_SIZE" != "unknown" ]; then
|
||||
TARGET_GB=$((TARGET_SIZE / 1024 / 1024 / 1024))
|
||||
echo "Target size: ${TARGET_GB}GB"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "⚠️ ALL DATA ON $TARGET_DRIVE WILL BE DESTROYED! ⚠️"
|
||||
echo
|
||||
read -p "Continue with backup? (yes/no): " CONFIRM
|
||||
|
||||
if [ "$CONFIRM" != "yes" ]; then
|
||||
echo "Backup cancelled"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Starting backup..."
|
||||
echo "=================="
|
||||
|
||||
# Check if pv is available for progress
|
||||
if command -v pv >/dev/null 2>&1; then
|
||||
echo "Using pv for progress display"
|
||||
dd if="$SOURCE_DRIVE" bs=4M 2>/dev/null | pv -s "$SOURCE_SIZE" | dd of="$TARGET_DRIVE" bs=4M conv=fdatasync 2>/dev/null
|
||||
else
|
||||
echo "Using dd with status=progress"
|
||||
dd if="$SOURCE_DRIVE" of="$TARGET_DRIVE" bs=4M status=progress conv=fdatasync
|
||||
fi
|
||||
|
||||
RESULT=$?
|
||||
|
||||
echo
|
||||
if [ $RESULT -eq 0 ]; then
|
||||
echo "========================================"
|
||||
echo " BACKUP COMPLETED SUCCESSFULLY!"
|
||||
echo "========================================"
|
||||
else
|
||||
echo "========================================"
|
||||
echo " BACKUP FAILED!"
|
||||
echo "========================================"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "System will shutdown in 30 seconds..."
|
||||
echo "(Press Ctrl+C to cancel)"
|
||||
|
||||
# Countdown
|
||||
for i in 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1; do
|
||||
echo -n "$i "
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Shutting down..."
|
||||
poweroff
|
||||
}
|
||||
|
||||
# Main execution
|
||||
detect_drives
|
||||
get_drives
|
||||
run_backup
|
||||
EOF
|
||||
|
||||
chmod +x "$OVERLAY_DIR/usr/local/bin/backup-system"
|
||||
|
||||
# Create init script that runs on boot
|
||||
sudo tee "$OVERLAY_DIR/etc/init.d/autobackup" > /dev/null << 'EOF'
|
||||
#!/sbin/openrc-run
|
||||
|
||||
name="autobackup"
|
||||
description="Automatic backup system"
|
||||
|
||||
depend() {
|
||||
need localmount
|
||||
after bootmisc
|
||||
}
|
||||
|
||||
start() {
|
||||
# Check if autobackup parameter was passed
|
||||
if grep -q "autobackup=yes" /proc/cmdline; then
|
||||
ebegin "Starting automatic backup system"
|
||||
|
||||
# Wait for devices to settle
|
||||
sleep 5
|
||||
|
||||
# Run backup system
|
||||
/usr/local/bin/backup-system
|
||||
|
||||
eend $?
|
||||
fi
|
||||
}
|
||||
EOF
|
||||
|
||||
chmod +x "$OVERLAY_DIR/etc/init.d/autobackup"
|
||||
|
||||
# Enable the service
|
||||
ln -sf /etc/init.d/autobackup "$OVERLAY_DIR/etc/runlevels/default/autobackup"
|
||||
|
||||
# Create profile for manual shell access
|
||||
sudo tee "$OVERLAY_DIR/root/.profile" > /dev/null << 'EOF'
|
||||
export PATH=/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin
|
||||
|
||||
echo "========================================"
|
||||
echo " BACKUP SYSTEM SHELL ACCESS"
|
||||
echo "========================================"
|
||||
echo
|
||||
echo "Available commands:"
|
||||
echo " backup-system - Start interactive backup"
|
||||
echo " lsblk - List block devices"
|
||||
echo " fdisk -l - List partitions"
|
||||
echo
|
||||
echo "Manual backup example:"
|
||||
echo " dd if=/dev/nvme0n1 of=/dev/sdb bs=4M status=progress"
|
||||
echo
|
||||
EOF
|
||||
|
||||
# Create the overlay archive
|
||||
cd "$OVERLAY_DIR"
|
||||
tar czf "$DATA_MOUNT/alpine.apkovl.tar.gz" *
|
||||
cd -
|
||||
|
||||
# Copy backup tools
|
||||
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
||||
sudo cp "$SCRIPT_DIR"/*.sh "$DATA_MOUNT/" 2>/dev/null || true
|
||||
|
||||
# Cleanup
|
||||
sudo umount "$BOOT_MOUNT" "$DATA_MOUNT"
|
||||
rmdir "$BOOT_MOUNT" "$DATA_MOUNT"
|
||||
rm -rf "$OVERLAY_DIR"
|
||||
|
||||
print_success "Alpine boot configuration fixed!"
|
||||
print_success "USB: $USB_DRIVE"
|
||||
echo
|
||||
print_success "Fixed Issues:"
|
||||
print_success "✅ Proper GRUB kernel parameters"
|
||||
print_success "✅ Correct Alpine overlay loading"
|
||||
print_success "✅ Automatic backup service startup"
|
||||
print_success "✅ Interactive drive selection"
|
||||
print_success "✅ Manual shell access option"
|
||||
echo
|
||||
print_warning "Now test by booting from USB and selecting 'Automatic System Backup'"
|
||||
@@ -1,204 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Installation script for System Backup Tool
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Print colored output
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
check_root() {
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
print_error "Do not run this installer as root. It will use sudo when needed."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check dependencies
|
||||
check_dependencies() {
|
||||
print_status "Checking dependencies..."
|
||||
|
||||
# Check Python 3
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
print_error "Python 3 is required but not installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check tkinter
|
||||
if ! python3 -c "import tkinter" &> /dev/null; then
|
||||
print_warning "tkinter not available. Installing python3-tk..."
|
||||
sudo apt update
|
||||
sudo apt install -y python3-tk
|
||||
fi
|
||||
|
||||
# Check for pv (progress viewer)
|
||||
if ! command -v pv &> /dev/null; then
|
||||
print_warning "pv (progress viewer) not found. Installing..."
|
||||
sudo apt install -y pv
|
||||
fi
|
||||
|
||||
print_success "All dependencies satisfied"
|
||||
}
|
||||
|
||||
# Make scripts executable
|
||||
setup_permissions() {
|
||||
print_status "Setting up permissions..."
|
||||
|
||||
chmod +x backup_manager.py
|
||||
chmod +x backup_script.sh
|
||||
|
||||
print_success "Permissions set"
|
||||
}
|
||||
|
||||
# Create desktop entry
|
||||
create_desktop_entry() {
|
||||
print_status "Creating desktop entry..."
|
||||
|
||||
local desktop_dir="$HOME/Desktop"
|
||||
local applications_dir="$HOME/.local/share/applications"
|
||||
local script_path=$(realpath backup_manager.py)
|
||||
local icon_path=$(realpath ".")
|
||||
|
||||
# Create applications directory if it doesn't exist
|
||||
mkdir -p "$applications_dir"
|
||||
|
||||
# Create desktop entry content
|
||||
local desktop_content="[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=System Backup Manager
|
||||
Comment=Clone internal drive to external M.2 SSD
|
||||
Exec=python3 '$script_path'
|
||||
Icon=drive-harddisk
|
||||
Terminal=false
|
||||
Categories=System;Utility;
|
||||
StartupNotify=true"
|
||||
|
||||
# Create desktop file in applications
|
||||
echo "$desktop_content" > "$applications_dir/system-backup.desktop"
|
||||
chmod +x "$applications_dir/system-backup.desktop"
|
||||
|
||||
# Create desktop shortcut if Desktop directory exists
|
||||
if [[ -d "$desktop_dir" ]]; then
|
||||
echo "$desktop_content" > "$desktop_dir/System Backup.desktop"
|
||||
chmod +x "$desktop_dir/System Backup.desktop"
|
||||
print_success "Desktop shortcut created"
|
||||
fi
|
||||
|
||||
print_success "Application entry created"
|
||||
}
|
||||
|
||||
# Create systemd service (optional)
|
||||
create_systemd_service() {
|
||||
local service_dir="systemd"
|
||||
local script_path=$(realpath backup_script.sh)
|
||||
|
||||
print_status "Creating systemd service template..."
|
||||
|
||||
mkdir -p "$service_dir"
|
||||
|
||||
cat > "$service_dir/backup-service.service" << EOF
|
||||
[Unit]
|
||||
Description=System Backup Service
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=$script_path --target /dev/sdb
|
||||
User=root
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
cat > "$service_dir/README.md" << EOF
|
||||
# Systemd Service for Backup
|
||||
|
||||
To install the systemd service:
|
||||
|
||||
1. Edit the service file to specify your target drive
|
||||
2. Copy to systemd directory:
|
||||
\`\`\`bash
|
||||
sudo cp backup-service.service /etc/systemd/system/
|
||||
\`\`\`
|
||||
|
||||
3. Enable and start:
|
||||
\`\`\`bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable backup-service
|
||||
sudo systemctl start backup-service
|
||||
\`\`\`
|
||||
|
||||
4. Check status:
|
||||
\`\`\`bash
|
||||
sudo systemctl status backup-service
|
||||
\`\`\`
|
||||
EOF
|
||||
|
||||
print_success "Systemd service template created in systemd/"
|
||||
}
|
||||
|
||||
# Create log directory
|
||||
setup_logging() {
|
||||
print_status "Setting up logging..."
|
||||
|
||||
# Create log file with proper permissions
|
||||
sudo touch /var/log/system_backup.log
|
||||
sudo chmod 666 /var/log/system_backup.log
|
||||
|
||||
print_success "Log file created: /var/log/system_backup.log"
|
||||
}
|
||||
|
||||
# Main installation function
|
||||
main() {
|
||||
echo ""
|
||||
echo "======================================"
|
||||
echo " System Backup Tool Installer"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
check_root
|
||||
check_dependencies
|
||||
setup_permissions
|
||||
setup_logging
|
||||
create_desktop_entry
|
||||
create_systemd_service
|
||||
|
||||
echo ""
|
||||
print_success "Installation completed successfully!"
|
||||
echo ""
|
||||
echo "You can now:"
|
||||
echo " • Launch GUI: python3 backup_manager.py"
|
||||
echo " • Use CLI: ./backup_script.sh --help"
|
||||
echo " • Click desktop icon: System Backup Manager"
|
||||
echo ""
|
||||
print_warning "Remember to run the backup tool with appropriate privileges"
|
||||
print_warning "Always verify your drive selections before starting backup"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Run installer
|
||||
main "$@"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,409 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Enhanced LVM Snapshot Backup System
|
||||
# Integrates LVM snapshots with the existing backup system for M.2 external drives
|
||||
|
||||
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
|
||||
VG_NAME="system-vg"
|
||||
SNAPSHOT_SIZE="10G"
|
||||
BACKUP_BASE_DIR="/mnt/backup"
|
||||
EXTERNAL_BACKUP_DRIVE="" # Will be auto-detected
|
||||
LOG_FILE="/var/log/lvm-snapshot-backup.log"
|
||||
|
||||
# Snapshot names
|
||||
ROOT_SNAPSHOT="root-snapshot"
|
||||
HOME_SNAPSHOT="home-snapshot"
|
||||
BOOT_SNAPSHOT="boot-snapshot"
|
||||
|
||||
log() {
|
||||
local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||
echo -e "${BLUE}$message${NC}"
|
||||
echo "$message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
error() {
|
||||
local message="[ERROR] $1"
|
||||
echo -e "${RED}$message${NC}" >&2
|
||||
echo "$message" >> "$LOG_FILE"
|
||||
exit 1
|
||||
}
|
||||
|
||||
warning() {
|
||||
local message="[WARNING] $1"
|
||||
echo -e "${YELLOW}$message${NC}"
|
||||
echo "$message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
success() {
|
||||
local message="[SUCCESS] $1"
|
||||
echo -e "${GREEN}$message${NC}"
|
||||
echo "$message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
check_lvm_system() {
|
||||
log "Checking LVM system status..."
|
||||
|
||||
# Check if we're running on an LVM system
|
||||
if ! command -v lvm >/dev/null 2>&1; then
|
||||
error "LVM tools not found. This system may not support LVM."
|
||||
fi
|
||||
|
||||
# Check if volume group exists
|
||||
if ! vgs "$VG_NAME" >/dev/null 2>&1; then
|
||||
error "Volume group '$VG_NAME' not found. Are you running on the migrated LVM system?"
|
||||
fi
|
||||
|
||||
# Check available space for snapshots
|
||||
local vg_free=$(vgs --noheadings -o vg_free --units g "$VG_NAME" | tr -d ' G')
|
||||
local snapshot_size_num=$(echo "$SNAPSHOT_SIZE" | tr -d 'G')
|
||||
local required_space=$((snapshot_size_num * 3)) # 3 snapshots
|
||||
|
||||
if (( $(echo "$vg_free < $required_space" | bc -l) )); then
|
||||
warning "Low free space in volume group ($vg_free GB available, $required_space GB recommended)"
|
||||
echo "Consider reducing snapshot size or extending volume group"
|
||||
fi
|
||||
|
||||
success "LVM system check passed"
|
||||
}
|
||||
|
||||
detect_backup_drive() {
|
||||
log "Detecting external backup drive..."
|
||||
|
||||
# Look for drives that are not the current root drive
|
||||
local root_drive=$(lsblk -no PKNAME $(findmnt -no SOURCE /))
|
||||
local available_drives=($(lsblk -dpno NAME | grep -v "$root_drive"))
|
||||
|
||||
if [ ${#available_drives[@]} -eq 0 ]; then
|
||||
error "No external drives found for backup"
|
||||
elif [ ${#available_drives[@]} -eq 1 ]; then
|
||||
EXTERNAL_BACKUP_DRIVE="${available_drives[0]}"
|
||||
log "Auto-selected backup drive: $EXTERNAL_BACKUP_DRIVE"
|
||||
else
|
||||
echo "Multiple external drives found:"
|
||||
for i in "${!available_drives[@]}"; do
|
||||
local drive="${available_drives[$i]}"
|
||||
local info=$(lsblk -no SIZE,MODEL "$drive" | head -1)
|
||||
echo "$((i+1)). $drive - $info"
|
||||
done
|
||||
|
||||
read -p "Select backup drive [1-${#available_drives[@]}]: " choice
|
||||
EXTERNAL_BACKUP_DRIVE="${available_drives[$((choice-1))]}"
|
||||
fi
|
||||
|
||||
success "Selected backup drive: $EXTERNAL_BACKUP_DRIVE"
|
||||
}
|
||||
|
||||
create_snapshots() {
|
||||
log "Creating LVM snapshots..."
|
||||
|
||||
# Remove existing snapshots if they exist
|
||||
remove_snapshots_quiet
|
||||
|
||||
# Create snapshots for each logical volume
|
||||
local volumes=("root" "home" "boot")
|
||||
local snapshots=("$ROOT_SNAPSHOT" "$HOME_SNAPSHOT" "$BOOT_SNAPSHOT")
|
||||
|
||||
for i in "${!volumes[@]}"; do
|
||||
local volume="${volumes[$i]}"
|
||||
local snapshot="${snapshots[$i]}"
|
||||
|
||||
if lvs "$VG_NAME/$volume" >/dev/null 2>&1; then
|
||||
log "Creating snapshot: $snapshot for $volume"
|
||||
lvcreate -L "$SNAPSHOT_SIZE" -s -n "$snapshot" "/dev/$VG_NAME/$volume" || {
|
||||
error "Failed to create snapshot $snapshot"
|
||||
}
|
||||
success "Created snapshot: $snapshot"
|
||||
else
|
||||
warning "Logical volume $volume not found, skipping"
|
||||
fi
|
||||
done
|
||||
|
||||
success "All snapshots created successfully"
|
||||
}
|
||||
|
||||
mount_snapshots() {
|
||||
log "Mounting snapshots for backup..."
|
||||
|
||||
# Create mount points
|
||||
mkdir -p "$BACKUP_BASE_DIR"/{root,home,boot}
|
||||
|
||||
# Mount snapshots
|
||||
local snapshots=("$ROOT_SNAPSHOT" "$HOME_SNAPSHOT" "$BOOT_SNAPSHOT")
|
||||
local mount_points=("root" "home" "boot")
|
||||
|
||||
for i in "${!snapshots[@]}"; do
|
||||
local snapshot="${snapshots[$i]}"
|
||||
local mount_point="$BACKUP_BASE_DIR/${mount_points[$i]}"
|
||||
|
||||
if [ -e "/dev/$VG_NAME/$snapshot" ]; then
|
||||
log "Mounting /dev/$VG_NAME/$snapshot to $mount_point"
|
||||
mount "/dev/$VG_NAME/$snapshot" "$mount_point" || {
|
||||
warning "Failed to mount snapshot $snapshot"
|
||||
}
|
||||
fi
|
||||
done
|
||||
|
||||
success "Snapshots mounted at $BACKUP_BASE_DIR"
|
||||
}
|
||||
|
||||
backup_to_external() {
|
||||
log "Backing up snapshots to external drive..."
|
||||
|
||||
if [ -z "$EXTERNAL_BACKUP_DRIVE" ]; then
|
||||
detect_backup_drive
|
||||
fi
|
||||
|
||||
# Create backup directory on external drive
|
||||
local external_mount="/mnt/external-backup"
|
||||
mkdir -p "$external_mount"
|
||||
|
||||
# Mount external drive (assume first partition)
|
||||
mount "${EXTERNAL_BACKUP_DRIVE}1" "$external_mount" 2>/dev/null || {
|
||||
# Try the whole drive if partition doesn't work
|
||||
mount "$EXTERNAL_BACKUP_DRIVE" "$external_mount" || {
|
||||
error "Failed to mount external backup drive"
|
||||
}
|
||||
}
|
||||
|
||||
# Create timestamped backup directory
|
||||
local backup_timestamp=$(date '+%Y%m%d_%H%M%S')
|
||||
local backup_dir="$external_mount/lvm-snapshots/$backup_timestamp"
|
||||
mkdir -p "$backup_dir"
|
||||
|
||||
# Backup each mounted snapshot
|
||||
local mount_points=("root" "home" "boot")
|
||||
for mount_point in "${mount_points[@]}"; do
|
||||
local source="$BACKUP_BASE_DIR/$mount_point"
|
||||
local target="$backup_dir/$mount_point"
|
||||
|
||||
if mountpoint -q "$source"; then
|
||||
log "Backing up $mount_point to external drive..."
|
||||
mkdir -p "$target"
|
||||
rsync -avxHAX --progress "$source/" "$target/" || {
|
||||
warning "Backup of $mount_point failed"
|
||||
}
|
||||
else
|
||||
warning "Snapshot $mount_point not mounted, skipping"
|
||||
fi
|
||||
done
|
||||
|
||||
# Create backup metadata
|
||||
cat > "$backup_dir/backup-info.txt" << EOF
|
||||
LVM Snapshot Backup Information
|
||||
===============================
|
||||
Backup Date: $(date)
|
||||
Source System: $(hostname)
|
||||
Volume Group: $VG_NAME
|
||||
Snapshot Size: $SNAPSHOT_SIZE
|
||||
Backup Location: $backup_dir
|
||||
|
||||
Logical Volumes:
|
||||
$(lvs $VG_NAME)
|
||||
|
||||
Volume Group Info:
|
||||
$(vgs $VG_NAME)
|
||||
|
||||
Physical Volumes:
|
||||
$(pvs)
|
||||
EOF
|
||||
|
||||
# Unmount external drive
|
||||
umount "$external_mount"
|
||||
|
||||
success "Backup completed to $backup_dir"
|
||||
}
|
||||
|
||||
remove_snapshots_quiet() {
|
||||
# Remove snapshots without error messages (used internally)
|
||||
lvremove -f "/dev/$VG_NAME/$ROOT_SNAPSHOT" 2>/dev/null || true
|
||||
lvremove -f "/dev/$VG_NAME/$HOME_SNAPSHOT" 2>/dev/null || true
|
||||
lvremove -f "/dev/$VG_NAME/$BOOT_SNAPSHOT" 2>/dev/null || true
|
||||
}
|
||||
|
||||
remove_snapshots() {
|
||||
log "Cleaning up snapshots..."
|
||||
|
||||
# Unmount snapshots
|
||||
umount "$BACKUP_BASE_DIR/root" 2>/dev/null || true
|
||||
umount "$BACKUP_BASE_DIR/home" 2>/dev/null || true
|
||||
umount "$BACKUP_BASE_DIR/boot" 2>/dev/null || true
|
||||
|
||||
# Remove logical volumes
|
||||
remove_snapshots_quiet
|
||||
|
||||
success "Snapshots removed"
|
||||
}
|
||||
|
||||
list_snapshots() {
|
||||
echo "Current LVM snapshots:"
|
||||
lvs "$VG_NAME" | grep -E "(snapshot|Snap%)" || echo "No snapshots found"
|
||||
}
|
||||
|
||||
show_status() {
|
||||
echo -e "${GREEN}=== LVM Snapshot Backup Status ===${NC}"
|
||||
echo
|
||||
|
||||
echo "Volume Group Information:"
|
||||
vgs "$VG_NAME" 2>/dev/null || echo "Volume group not found"
|
||||
echo
|
||||
|
||||
echo "Logical Volumes:"
|
||||
lvs "$VG_NAME" 2>/dev/null || echo "No logical volumes found"
|
||||
echo
|
||||
|
||||
echo "Current Snapshots:"
|
||||
list_snapshots
|
||||
echo
|
||||
|
||||
echo "Mount Status:"
|
||||
if mountpoint -q "$BACKUP_BASE_DIR/root" 2>/dev/null; then
|
||||
echo " Root snapshot: mounted at $BACKUP_BASE_DIR/root"
|
||||
else
|
||||
echo " Root snapshot: not mounted"
|
||||
fi
|
||||
|
||||
if mountpoint -q "$BACKUP_BASE_DIR/home" 2>/dev/null; then
|
||||
echo " Home snapshot: mounted at $BACKUP_BASE_DIR/home"
|
||||
else
|
||||
echo " Home snapshot: not mounted"
|
||||
fi
|
||||
|
||||
if mountpoint -q "$BACKUP_BASE_DIR/boot" 2>/dev/null; then
|
||||
echo " Boot snapshot: mounted at $BACKUP_BASE_DIR/boot"
|
||||
else
|
||||
echo " Boot snapshot: not mounted"
|
||||
fi
|
||||
}
|
||||
|
||||
integrate_with_backup_system() {
|
||||
log "Integrating with existing backup system..."
|
||||
|
||||
# Check if main backup system exists
|
||||
if [ -f "./backup_script.sh" ]; then
|
||||
log "Found main backup system - creating integration"
|
||||
|
||||
# Create wrapper script that combines snapshot and full backup
|
||||
cat > "./lvm_integrated_backup.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
# Integrated LVM + Full System Backup
|
||||
# Combines LVM snapshots with the main backup system
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
echo "=== Integrated LVM Backup System ==="
|
||||
echo "1. LVM Snapshot Backup (fast, consistent)"
|
||||
echo "2. Full System Backup (complete clone)"
|
||||
echo "3. Combined Backup (snapshots + clone)"
|
||||
echo
|
||||
|
||||
read -p "Select backup type [1-3]: " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
echo "Performing LVM snapshot backup..."
|
||||
sudo "$SCRIPT_DIR/lvm_snapshot_backup.sh" backup
|
||||
;;
|
||||
2)
|
||||
echo "Performing full system backup..."
|
||||
sudo "$SCRIPT_DIR/backup_script.sh"
|
||||
;;
|
||||
3)
|
||||
echo "Performing combined backup..."
|
||||
echo "Step 1: LVM snapshots..."
|
||||
sudo "$SCRIPT_DIR/lvm_snapshot_backup.sh" backup
|
||||
echo "Step 2: Full system clone..."
|
||||
sudo "$SCRIPT_DIR/backup_script.sh" --sync
|
||||
;;
|
||||
*)
|
||||
echo "Invalid choice"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "./lvm_integrated_backup.sh"
|
||||
success "Created integrated backup script: lvm_integrated_backup.sh"
|
||||
else
|
||||
warning "Main backup system not found in current directory"
|
||||
fi
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
echo "Usage: $0 {create|mount|remove|backup|external|status|list|integrate}"
|
||||
echo
|
||||
echo "Commands:"
|
||||
echo " create - Create LVM snapshots only"
|
||||
echo " mount - Mount existing snapshots for access"
|
||||
echo " remove - Remove snapshots and unmount"
|
||||
echo " backup - Create snapshots and mount (ready for backup)"
|
||||
echo " external - Full backup to external drive"
|
||||
echo " status - Show current LVM and snapshot status"
|
||||
echo " list - List current snapshots"
|
||||
echo " integrate - Integrate with existing backup system"
|
||||
echo
|
||||
echo "Examples:"
|
||||
echo " $0 backup # Create and mount snapshots"
|
||||
echo " $0 external # Full backup to external drive"
|
||||
echo " $0 remove # Clean up when backup complete"
|
||||
}
|
||||
|
||||
main() {
|
||||
# Ensure log file exists and is writable
|
||||
sudo touch "$LOG_FILE" 2>/dev/null || LOG_FILE="/tmp/lvm-backup.log"
|
||||
|
||||
case "${1:-}" in
|
||||
create)
|
||||
check_lvm_system
|
||||
create_snapshots
|
||||
;;
|
||||
mount)
|
||||
mount_snapshots
|
||||
;;
|
||||
remove)
|
||||
remove_snapshots
|
||||
;;
|
||||
backup)
|
||||
check_lvm_system
|
||||
create_snapshots
|
||||
mount_snapshots
|
||||
echo "Snapshots ready for backup at $BACKUP_BASE_DIR"
|
||||
echo "Run '$0 remove' when backup is complete"
|
||||
;;
|
||||
external)
|
||||
check_lvm_system
|
||||
create_snapshots
|
||||
mount_snapshots
|
||||
backup_to_external
|
||||
remove_snapshots
|
||||
;;
|
||||
status)
|
||||
show_status
|
||||
;;
|
||||
list)
|
||||
list_snapshots
|
||||
;;
|
||||
integrate)
|
||||
integrate_with_backup_system
|
||||
;;
|
||||
*)
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Check if running as root for LVM operations
|
||||
if [ "$EUID" -ne 0 ] && [[ "$1" =~ ^(create|mount|remove|backup|external)$ ]]; then
|
||||
error "LVM operations require root privileges. Use: sudo $0 $1"
|
||||
fi
|
||||
|
||||
main "$@"
|
||||
154
old_scripts/BOOT_FIX_SUMMARY.md
Normal file
154
old_scripts/BOOT_FIX_SUMMARY.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# Internal NVMe Boot Repair Summary
|
||||
|
||||
## Problem
|
||||
After resizing the home partition, the internal NVMe drive (nvme0n1) was showing a "reset system" message on boot, suspected GRUB issue.
|
||||
|
||||
## Root Cause
|
||||
The GRUB bootloader needed to be reinstalled after the partition resize, and boot failure flags needed to be cleared from the GRUB environment.
|
||||
|
||||
## Solution Applied
|
||||
|
||||
### 1. Updated GRUB Configuration
|
||||
```bash
|
||||
sudo update-grub
|
||||
```
|
||||
- Regenerated `/boot/grub/grub.cfg` with current system configuration
|
||||
- Found and registered kernel images: 6.8.0-84 and 6.8.0-83
|
||||
|
||||
### 2. Reinstalled GRUB Bootloader
|
||||
```bash
|
||||
sudo grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=ubuntu --recheck /dev/nvme0n1
|
||||
```
|
||||
- Installed GRUB to internal NVMe drive
|
||||
- Files installed to `/boot/efi/EFI/ubuntu/`:
|
||||
- shimx64.efi (secure boot shim)
|
||||
- grubx64.efi (GRUB bootloader)
|
||||
- grub.cfg (initial configuration)
|
||||
|
||||
### 3. Cleared Boot Failure Flags
|
||||
```bash
|
||||
sudo grub-editenv - unset recordfail
|
||||
sudo grub-editenv - unset boot_success
|
||||
```
|
||||
- Removed any boot failure tracking that might cause "reset system" message
|
||||
- GRUB environment now clean
|
||||
|
||||
## Current System Status
|
||||
|
||||
### Boot Configuration
|
||||
- **Currently Running From**: External USB M.2 (sda - migration-vg)
|
||||
- Root: `/dev/mapper/migration-vg-root` → mounted as `/`
|
||||
- Home: `/dev/mapper/luks-home-internal` (on external) → mounted as `/home`
|
||||
|
||||
- **Internal Drive**: NVMe (nvme0n1 - internal-vg) - NOW BOOTABLE
|
||||
- EFI: `/dev/nvme0n1p1` → mounted as `/boot/efi`
|
||||
- Boot: `/dev/internal-vg/boot` → mounted as `/boot`
|
||||
- Root: `/dev/internal-vg/root` (ready but not currently root)
|
||||
- Home: `/dev/internal-vg/home` (LUKS encrypted, not currently mounted)
|
||||
|
||||
### EFI Boot Entries
|
||||
```
|
||||
BootOrder: 0001,001C,001B,0000,... (Ubuntu is first)
|
||||
Boot0001* Ubuntu - Points to /boot/efi/EFI/ubuntu/shimx64.efi
|
||||
Boot001F* USB HDD - Currently active (external M.2)
|
||||
```
|
||||
|
||||
### Partition Layout
|
||||
**Internal NVMe (nvme0n1):**
|
||||
- nvme0n1p1: 511MB EFI partition (757B-A377)
|
||||
- nvme0n1p2: 476.4GB LVM (internal-vg)
|
||||
- internal-vg/root: 56GB ext4
|
||||
- internal-vg/boot: 2GB ext4
|
||||
- internal-vg/home: 404GB LUKS encrypted
|
||||
- internal-vg/swap: 8GB swap
|
||||
|
||||
**External M.2 (sda):**
|
||||
- sda1: EFI partition
|
||||
- sda2: 476.4GB LVM (migration-vg)
|
||||
- Identical structure to internal-vg
|
||||
|
||||
## Testing the Fix
|
||||
|
||||
### To Boot from Internal Drive:
|
||||
1. **Shutdown the system**:
|
||||
```bash
|
||||
sudo shutdown -h now
|
||||
```
|
||||
|
||||
2. **Disconnect the external USB M.2 drive** (sda)
|
||||
|
||||
3. **Power on the system**
|
||||
|
||||
4. **Expected Behavior**:
|
||||
- System should boot from internal NVMe
|
||||
- No more "reset system" message
|
||||
- GRUB menu should appear normally
|
||||
- System will boot into internal-vg volumes
|
||||
|
||||
### If You Want to Keep Both Drives Connected:
|
||||
1. Change boot order in BIOS to prefer internal NVMe over USB
|
||||
2. Or use BIOS boot menu (usually F12) to manually select internal drive
|
||||
|
||||
## Verification Commands
|
||||
|
||||
Check internal drive boot readiness:
|
||||
```bash
|
||||
./verify_internal_boot.sh
|
||||
```
|
||||
|
||||
Check EFI boot entries:
|
||||
```bash
|
||||
efibootmgr -v
|
||||
```
|
||||
|
||||
Verify GRUB configuration:
|
||||
```bash
|
||||
sudo cat /boot/grub/grub.cfg | grep -A5 "menuentry"
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Both drives have identical LVM structure** (internal-vg and migration-vg)
|
||||
- Both have root, boot, home, and swap volumes
|
||||
- Home volumes are both LUKS encrypted
|
||||
|
||||
2. **Current /etc/fstab** is configured for internal-vg:
|
||||
```
|
||||
/dev/internal-vg/root → /
|
||||
/dev/internal-vg/boot → /boot
|
||||
/dev/internal-vg/home → /home (via LUKS)
|
||||
```
|
||||
This is correct for booting from internal drive.
|
||||
|
||||
3. **System is currently running from external drive** due to boot order/USB boot
|
||||
- This is temporary for testing
|
||||
- Internal drive is fully configured and ready
|
||||
|
||||
4. **No data loss risk** - all changes were to bootloader configuration only
|
||||
|
||||
## Next Steps
|
||||
|
||||
Once you've verified the internal drive boots correctly:
|
||||
|
||||
1. **Run the backup** to clone internal-vg to migration-vg:
|
||||
```bash
|
||||
sudo ./lvm_block_backup.sh
|
||||
```
|
||||
This will create a block-level backup of internal drive to external drive.
|
||||
|
||||
2. **Keep external drive as emergency backup**:
|
||||
- Can boot from it if internal drive fails
|
||||
- Already has identical LVM structure
|
||||
|
||||
## Files Created
|
||||
- `verify_internal_boot.sh` - Script to verify boot configuration
|
||||
- `BOOT_FIX_SUMMARY.md` - This documentation
|
||||
|
||||
## What Was Fixed
|
||||
✅ GRUB bootloader reinstalled to internal NVMe
|
||||
✅ GRUB configuration updated with current kernels
|
||||
✅ Boot failure flags cleared
|
||||
✅ EFI boot entry verified
|
||||
✅ Internal drive ready to boot
|
||||
|
||||
The "reset system" message should no longer appear when booting from the internal drive.
|
||||
234
old_scripts/INTERNAL_DRIVE_RECOVERY.md
Normal file
234
old_scripts/INTERNAL_DRIVE_RECOVERY.md
Normal file
@@ -0,0 +1,234 @@
|
||||
# Internal Drive Recovery - Complete Configuration
|
||||
|
||||
## ✅ What Was Fixed
|
||||
|
||||
Your internal NVMe drive is now **fully configured to boot independently** from the external M.2 backup drive.
|
||||
|
||||
### Changes Made:
|
||||
|
||||
#### 1. **Fixed LUKS Encryption Configuration** (`/etc/crypttab`)
|
||||
**Problem**: Both internal and external drives have the same LUKS UUID, causing the system to mount home from whichever drive it found first (which was the external).
|
||||
|
||||
**Solution**: Changed `/etc/crypttab` to use device path instead of UUID:
|
||||
```bash
|
||||
# OLD (using UUID - ambiguous):
|
||||
luks-home-internal UUID=e4d30f4f-3aac-48a7-a1ca-83539598555b none luks
|
||||
|
||||
# NEW (using device path - specific):
|
||||
luks-home-internal /dev/internal-vg/home none luks
|
||||
```
|
||||
|
||||
**Backup**: `/etc/crypttab.backup.20251006`
|
||||
|
||||
#### 2. **Updated Initramfs**
|
||||
Regenerated the initial RAM filesystem to include the new crypttab configuration:
|
||||
```bash
|
||||
sudo update-initramfs -u -k all
|
||||
```
|
||||
This ensures the system uses the internal drive's home partition during boot.
|
||||
|
||||
#### 3. **Reinstalled GRUB Bootloader**
|
||||
```bash
|
||||
sudo grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=ubuntu
|
||||
```
|
||||
- Created Ubuntu EFI boot entry (Boot0001)
|
||||
- Installed to internal drive's EFI partition (nvme0n1p1)
|
||||
|
||||
#### 4. **Set EFI Boot Order**
|
||||
```bash
|
||||
sudo efibootmgr -o 0001,001C,001B,0000,...
|
||||
```
|
||||
- **Ubuntu (Boot0001)** is now the FIRST boot option
|
||||
- System will boot from internal drive by default
|
||||
- External drive boot entries (001F USB HDD) are lower priority
|
||||
|
||||
## 📊 Current System Configuration
|
||||
|
||||
### Internal NVMe Drive (nvme0n1) - PRIMARY
|
||||
```
|
||||
nvme0n1 (476.9 GB)
|
||||
├─ nvme0n1p1 → /boot/efi (EFI partition, 511MB)
|
||||
└─ nvme0n1p2 → internal-vg (LVM)
|
||||
├─ root (56GB) → / (root filesystem)
|
||||
├─ boot (2GB) → /boot (kernel images)
|
||||
├─ home (404GB) → /home (LUKS encrypted) ← NOW CONFIGURED
|
||||
└─ swap (8GB) → swap
|
||||
```
|
||||
|
||||
### External M.2 USB (sda) - BACKUP
|
||||
```
|
||||
sda (476.9 GB)
|
||||
├─ sda1 → EFI partition (unmounted)
|
||||
└─ sda2 → migration-vg (LVM)
|
||||
├─ root (56GB) → (unmounted)
|
||||
├─ boot (2GB) → (unmounted)
|
||||
├─ home (404GB) → (unmounted, LUKS encrypted)
|
||||
└─ swap (8GB) → (inactive)
|
||||
```
|
||||
|
||||
### Mount Points (After Reboot)
|
||||
```
|
||||
/dev/internal-vg/root → /
|
||||
/dev/internal-vg/boot → /boot
|
||||
/dev/mapper/luks-home-internal → /home (from internal-vg/home)
|
||||
/dev/internal-vg/swap → swap
|
||||
/dev/nvme0n1p1 → /boot/efi
|
||||
```
|
||||
|
||||
## 🔐 LUKS Encryption Details
|
||||
|
||||
Both drives have identical LUKS setup:
|
||||
- **UUID**: e4d30f4f-3aac-48a7-a1ca-83539598555b (same on both!)
|
||||
- **Type**: LUKS2
|
||||
- **Cipher**: aes-xts-plain64
|
||||
- **Keysize**: 512 bits
|
||||
|
||||
The system now uses device path (`/dev/internal-vg/home`) instead of UUID to ensure it mounts the internal drive's home partition.
|
||||
|
||||
## 🚀 Testing Instructions
|
||||
|
||||
### Test 1: Boot with Both Drives Connected
|
||||
```bash
|
||||
sudo reboot
|
||||
```
|
||||
**Expected**: System boots from internal drive, uses internal home partition.
|
||||
|
||||
**Verify after boot**:
|
||||
```bash
|
||||
mount | grep -E "/ |/boot|/home"
|
||||
# Should show all mounts from internal-vg
|
||||
```
|
||||
|
||||
### Test 2: Boot with ONLY Internal Drive (Full Independence Test)
|
||||
```bash
|
||||
# 1. Shutdown system
|
||||
sudo shutdown -h now
|
||||
|
||||
# 2. Physically disconnect external USB M.2 drive (sda)
|
||||
|
||||
# 3. Power on system
|
||||
|
||||
# Expected: System boots completely from internal NVMe
|
||||
```
|
||||
|
||||
**If everything works**, you'll have:
|
||||
- GRUB menu appears (no "reset system" message should appear after first clean boot)
|
||||
- System boots to login screen
|
||||
- Can log in with encrypted home partition
|
||||
- All data accessible from internal drive
|
||||
|
||||
## 🔍 Verification Commands
|
||||
|
||||
After reboot, run these to verify internal drive is being used:
|
||||
|
||||
```bash
|
||||
# Check what's mounted where
|
||||
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT
|
||||
|
||||
# Verify home is from internal drive
|
||||
sudo cryptsetup status luks-home-internal
|
||||
# Should show: device: /dev/mapper/internal--vg-home
|
||||
|
||||
# Check boot order
|
||||
efibootmgr | grep -A1 BootOrder
|
||||
# Should show: BootOrder: 0001,... (Ubuntu first)
|
||||
|
||||
# Verify all internal volumes
|
||||
mount | grep internal-vg
|
||||
```
|
||||
|
||||
## 📝 Configuration Files Modified
|
||||
|
||||
1. **`/etc/crypttab`** - Updated to use internal-vg/home device path
|
||||
- Backup: `/etc/crypttab.backup.20251006`
|
||||
|
||||
2. **`/boot/initrd.img-*`** - Regenerated with new crypttab
|
||||
|
||||
3. **`/boot/efi/EFI/ubuntu/`** - GRUB bootloader reinstalled
|
||||
|
||||
4. **UEFI NVRAM** - Boot order updated (Boot0001 first)
|
||||
|
||||
## 🛡️ About the "Reset System" Message
|
||||
|
||||
The "reset system" message you saw **before GRUB** is a **Lenovo BIOS/UEFI firmware notification**, not a GRUB error.
|
||||
|
||||
### Why it appears:
|
||||
- BIOS detected improper shutdown or boot failure
|
||||
- Partition resize triggered hardware change detection
|
||||
- BIOS boot counter wasn't acknowledged
|
||||
|
||||
### How to clear it:
|
||||
**Option 1** (Easiest): Enter BIOS setup during boot
|
||||
- Press **F1** during Lenovo logo
|
||||
- Just enter BIOS (don't need to change anything)
|
||||
- Press **F10** to save and exit
|
||||
- This acknowledges the BIOS warning
|
||||
|
||||
**Option 2**: Clean shutdown cycle
|
||||
```bash
|
||||
sudo shutdown -h now
|
||||
# Wait 10 seconds
|
||||
# Power on
|
||||
# BIOS may clear flag after successful boot
|
||||
```
|
||||
|
||||
**Option 3**: Wait for successful boots
|
||||
- After 2-3 successful clean boots, BIOS usually clears the flag automatically
|
||||
|
||||
## ✨ What You Can Do Now
|
||||
|
||||
### 1. Use Internal Drive Normally
|
||||
Your internal NVMe is fully functional and independent. You can:
|
||||
- Boot without external drive connected
|
||||
- Use system normally with all data on internal drive
|
||||
- Keep external drive disconnected
|
||||
|
||||
### 2. Run the Backup
|
||||
Now you can create a proper block-level backup:
|
||||
```bash
|
||||
cd ~/Nextcloud/entwicklung/Werkzeuge/backup_to_external_m.2
|
||||
sudo ./lvm_block_backup.sh
|
||||
```
|
||||
|
||||
This will clone internal-vg to migration-vg on the external drive.
|
||||
|
||||
### 3. Keep External as Emergency Backup
|
||||
The external drive can serve as:
|
||||
- **Bootable backup**: If internal fails, boot from external
|
||||
- **Recovery system**: Access data if internal has issues
|
||||
- **Migration tool**: Already has identical LVM structure
|
||||
|
||||
## 📚 Related Files in This Directory
|
||||
|
||||
- `lvm_block_backup.sh` - Block-level backup script (internal → external)
|
||||
- `BOOT_FIX_SUMMARY.md` - Details about GRUB reinstallation
|
||||
- `verify_internal_boot.sh` - Boot configuration verification script
|
||||
- `clear_bios_boot_flag.sh` - BIOS message diagnostic tool
|
||||
- `README_BACKUP.md` - Backup strategy documentation
|
||||
|
||||
## ⚠️ Important Notes
|
||||
|
||||
1. **Same LUKS UUID**: Both drives have identical UUIDs because they were cloned. The device path in crypttab ensures the right one is used.
|
||||
|
||||
2. **Don't mix drives during boot**: If both drives are connected and you manually select the wrong one in BIOS boot menu, the system might use the external drive instead.
|
||||
|
||||
3. **Backup before cloning**: The `lvm_block_backup.sh` will **OVERWRITE** the external drive's volumes. Make sure any unique data on the external is backed up first.
|
||||
|
||||
4. **Home data**: Currently your home directory data is on the external drive. After the first clean reboot, verify if you need to copy any recent data from external to internal before running the backup.
|
||||
|
||||
## 🎯 Summary
|
||||
|
||||
**Status**: ✅ **Internal drive is fully configured and ready**
|
||||
|
||||
**Changes**:
|
||||
- Crypttab uses internal drive explicitly
|
||||
- Initramfs updated
|
||||
- GRUB reinstalled
|
||||
- Boot order corrected (Ubuntu first)
|
||||
|
||||
**Next Step**:
|
||||
1. **Test** - Reboot to verify internal drive works
|
||||
2. **Backup** - Run `lvm_block_backup.sh` to clone to external
|
||||
3. **Normal Use** - Use internal drive, keep external as backup
|
||||
|
||||
**BIOS Message**: Will likely clear after 1-2 clean boots or entering BIOS setup (F1)
|
||||
59
old_scripts/LVM_MIGRATION_GUIDE.txt
Normal file
59
old_scripts/LVM_MIGRATION_GUIDE.txt
Normal file
@@ -0,0 +1,59 @@
|
||||
=== LVM MIGRATION OPTIONS ===
|
||||
|
||||
You now have TWO migration options on this USB stick:
|
||||
|
||||
OPTION 1: IMPROVED LVM MIGRATION (Recommended)
|
||||
File: improved_lvm_migration.sh
|
||||
- Fixes the boot issues from the previous failed attempt
|
||||
- Properly configures initramfs with LVM modules
|
||||
- Better GRUB configuration for LVM
|
||||
- Handles LUKS + LVM combination correctly
|
||||
- More robust error handling
|
||||
|
||||
OPTION 2: SIMPLE 1-TO-1 CLONE (Fallback)
|
||||
File: direct_clone_backup.sh
|
||||
- Creates exact copy without LVM conversion
|
||||
- Use if LVM migration still has issues
|
||||
|
||||
=== WHAT WAS WRONG WITH PREVIOUS LVM MIGRATION ===
|
||||
|
||||
The original migrate_to_lvm.sh failed because:
|
||||
❌ Initramfs missing LVM modules
|
||||
❌ Poor GRUB LVM configuration
|
||||
❌ LUKS + LVM combination issues
|
||||
❌ Insufficient boot verification
|
||||
|
||||
=== IMPROVEMENTS IN NEW LVM SCRIPT ===
|
||||
|
||||
✅ Proper initramfs LVM module inclusion
|
||||
✅ Correct GRUB configuration for LVM
|
||||
✅ Better LUKS + LVM handling
|
||||
✅ Boot configuration verification
|
||||
✅ More robust error recovery
|
||||
|
||||
=== USAGE INSTRUCTIONS ===
|
||||
|
||||
1. Boot from this USB stick (Debian Live)
|
||||
|
||||
2. For LVM migration (recommended):
|
||||
sudo ./improved_lvm_migration.sh
|
||||
|
||||
3. For simple clone (if LVM fails):
|
||||
sudo ./direct_clone_backup.sh
|
||||
|
||||
4. Verify boot readiness:
|
||||
sudo ./verify_boot_readiness.sh
|
||||
|
||||
5. If needed, repair issues:
|
||||
sudo ./boot_repair_tools.sh
|
||||
|
||||
=== EXPECTED RESULTS WITH IMPROVED LVM ===
|
||||
|
||||
✅ Boots properly asking for LUKS password
|
||||
✅ No reset loops or boot failures
|
||||
✅ Full LVM functionality (snapshots, resizing)
|
||||
✅ Proper initramfs with LVM support
|
||||
✅ Working GRUB configuration
|
||||
|
||||
The improved script addresses all the issues that caused
|
||||
the boot failure shown in your screenshot.
|
||||
49
old_scripts/README_BACKUP.md
Normal file
49
old_scripts/README_BACKUP.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# LVM Block-Level Backup Tool
|
||||
|
||||
## What This Does
|
||||
|
||||
This tool performs a **proper** LVM snapshot backup by:
|
||||
|
||||
1. ✅ Creating consistent LVM snapshots of your internal drive
|
||||
2. ✅ Cloning entire volumes **block-for-block** to external drive
|
||||
3. ✅ Creating an exact, bootable copy of your system
|
||||
4. ✅ Using full drive capacity efficiently
|
||||
|
||||
## Why This Is Better
|
||||
|
||||
- **Fast**: Block-level copying is much faster than file copying
|
||||
- **Complete**: Exact bit-for-bit copy including all metadata
|
||||
- **Bootable**: External drive can boot your system directly
|
||||
- **Space Efficient**: Uses full drive capacity, no filesystem overhead
|
||||
- **Consistent**: Snapshots ensure data consistency during backup
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Simple one-command backup
|
||||
sudo ./lvm_block_backup.sh
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- Both drives must have LVM setup
|
||||
- External drive volume group: `migration-vg`
|
||||
- Internal drive volume group: `internal-vg`
|
||||
- At least 6GB free space for snapshots
|
||||
|
||||
## What Gets Backed Up
|
||||
|
||||
- **Root volume**: Complete system, applications, configurations
|
||||
- **Home volume**: All user data (including encrypted LUKS)
|
||||
- **Boot volume**: Kernels, bootloader, boot configuration
|
||||
|
||||
## Result
|
||||
|
||||
After backup, your external drive contains an exact copy that can:
|
||||
- Boot independently from BIOS/UEFI menu
|
||||
- Be mounted to access files
|
||||
- Serve as complete system recovery
|
||||
|
||||
---
|
||||
|
||||
**Note**: The `old_scripts/` directory contains 40+ previous backup attempts that didn't work properly. This single script replaces all of them.
|
||||
105
old_scripts/README_FIRST.txt
Normal file
105
old_scripts/README_FIRST.txt
Normal file
@@ -0,0 +1,105 @@
|
||||
╔═══════════════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ ✅ INTERNAL DRIVE RECOVERY COMPLETE ║
|
||||
║ ║
|
||||
╚═══════════════════════════════════════════════════════════════════╝
|
||||
|
||||
Your internal NVMe drive has been fully configured and is ready to
|
||||
boot independently!
|
||||
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
📋 WHAT WAS DONE:
|
||||
|
||||
1. ✅ Fixed /etc/crypttab to use internal-vg/home explicitly
|
||||
2. ✅ Updated initramfs with new LUKS configuration
|
||||
3. ✅ Reinstalled GRUB bootloader on internal NVMe
|
||||
4. ✅ Created Ubuntu EFI boot entry (Boot0001)
|
||||
5. ✅ Set boot order: Ubuntu FIRST in boot priority
|
||||
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
🚀 NEXT STEPS:
|
||||
|
||||
STEP 1: REBOOT THE SYSTEM
|
||||
$ sudo reboot
|
||||
|
||||
After reboot, the system will use the internal drive's home
|
||||
partition instead of the external one.
|
||||
|
||||
STEP 2: VERIFY CONFIGURATION
|
||||
$ cd ~/Nextcloud/entwicklung/Werkzeuge/backup_to_external_m.2
|
||||
$ ./verify_internal_config.sh
|
||||
|
||||
All 7 checks should pass after reboot.
|
||||
|
||||
STEP 3: TEST FULL INDEPENDENCE (Optional but Recommended)
|
||||
$ sudo shutdown -h now
|
||||
|
||||
Then:
|
||||
- Physically disconnect the external USB M.2 drive
|
||||
- Power on the system
|
||||
- System should boot completely from internal NVMe
|
||||
- You should be able to log in and access all your data
|
||||
|
||||
STEP 4: RECONNECT EXTERNAL & CREATE BACKUP
|
||||
After verifying internal drive works:
|
||||
- Reconnect external drive
|
||||
- Run the backup script:
|
||||
$ sudo ./lvm_block_backup.sh
|
||||
|
||||
This will create a block-level clone of internal → external
|
||||
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
⚠️ ABOUT THE "RESET SYSTEM" MESSAGE:
|
||||
|
||||
The message you see BEFORE GRUB is from Lenovo BIOS, not GRUB.
|
||||
|
||||
To clear it:
|
||||
• Press F1 during boot → Enter BIOS → Save & Exit (F10)
|
||||
OR
|
||||
• After 1-2 clean boots, it should disappear automatically
|
||||
|
||||
This is just a BIOS notification - your system IS bootable!
|
||||
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
📚 DOCUMENTATION:
|
||||
|
||||
All changes are documented in:
|
||||
• INTERNAL_DRIVE_RECOVERY.md - Complete technical details
|
||||
• BOOT_FIX_SUMMARY.md - GRUB bootloader fixes
|
||||
• verify_internal_config.sh - Configuration verification
|
||||
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
🎯 CURRENT STATUS:
|
||||
|
||||
✅ Internal drive fully configured
|
||||
✅ Boot order corrected (Ubuntu first)
|
||||
✅ GRUB properly installed
|
||||
⏳ PENDING: Reboot to activate home partition change
|
||||
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
💾 BACKUP STRATEGY:
|
||||
|
||||
Once verified, you'll have:
|
||||
|
||||
INTERNAL NVMe (nvme0n1) = PRIMARY SYSTEM
|
||||
- Daily use
|
||||
- Main operating system
|
||||
- All your data
|
||||
|
||||
EXTERNAL M.2 (sda) = BACKUP/EMERGENCY BOOT
|
||||
- Block-level clone of internal
|
||||
- Bootable rescue system
|
||||
- Emergency access if internal fails
|
||||
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Ready to reboot? Run:
|
||||
sudo reboot
|
||||
|
||||
═══════════════════════════════════════════════════════════════════
|
||||
@@ -1,31 +1,10 @@
|
||||
# System Backup and LVM Migration Tools
|
||||
# System Backup to External M.2 SSD
|
||||
|
||||
A comprehensive toolset for Linux system backup and LVM migration that provides both GUI and command-line interfaces for backing up to external M.2 SSDs and migrating systems to LVM.
|
||||
A comprehensive backup solution for Linux systems that provides both GUI and command-line interfaces for cloning your internal drive to an external M.2 SSD.
|
||||
|
||||
## Features
|
||||
|
||||
### Traditional Backup Features
|
||||
- **GUI Interface**: User-friendly graphical interface built with Python Tkinter
|
||||
- **Command Line Interface**: Full CLI support for automated and scripted operations
|
||||
- **Smart Drive Detection**: Automatically detects internal drive and external M.2 SSDs
|
||||
- **Full System Backup**: Complete drive cloning with dd for exact system replication
|
||||
- **Smart Sync Backup**: ⚡ Fast incremental backups using rsync for minor changes
|
||||
- **Change Analysis**: Analyze filesystem changes to recommend sync vs full backup
|
||||
- **Restore Functionality**: Complete system restore from external drive
|
||||
- **Portable Tools**: Backup tools survive on external drive and remain accessible after cloning
|
||||
- **Reboot Integration**: Optional reboot before backup/restore operations
|
||||
- **Progress Monitoring**: Real-time progress display and logging
|
||||
- **Safety Features**: Multiple confirmations and drive validation
|
||||
- **Desktop Integration**: Create desktop shortcuts for easy access
|
||||
|
||||
### 🆕 LVM Migration Features
|
||||
- **Automated LVM Migration**: Complete migration from traditional partitions to LVM
|
||||
- **Size Detection**: Automatic source partition size detection and matching
|
||||
- **Data Preservation**: Full data integrity with permissions, ownership, and timestamps
|
||||
- **External Drive Support**: Optimized for USB 3.0+ M.2 external drives
|
||||
- **GRUB Installation**: Automatic EFI bootloader installation and configuration
|
||||
- **System Validation**: Built-in verification of migration success
|
||||
- **One-Button Operation**: Complete migration with a single command
|
||||
## Features
|
||||
|
||||
- **GUI Interface**: User-friendly graphical interface built with Python Tkinter
|
||||
- **Command Line Interface**: Full CLI support for automated and scripted operations
|
||||
@@ -49,8 +28,6 @@ A comprehensive toolset for Linux system backup and LVM migration that provides
|
||||
|
||||
## Installation
|
||||
|
||||
### Traditional Backup Installation
|
||||
|
||||
1. Clone or download this repository:
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
@@ -69,53 +46,6 @@ A comprehensive toolset for Linux system backup and LVM migration that provides
|
||||
sudo apt install python3-tk pv parted
|
||||
```
|
||||
|
||||
### LVM Migration Setup
|
||||
|
||||
The LVM migration tools require a live USB environment:
|
||||
|
||||
1. Boot from Debian/Ubuntu Live USB
|
||||
2. Clone this repository to the live environment
|
||||
3. The migration script will automatically install required dependencies
|
||||
4. Run the migration with a single command:
|
||||
```bash
|
||||
sudo ./migrate_to_lvm.sh /dev/sdX
|
||||
```
|
||||
|
||||
## LVM Migration Process
|
||||
|
||||
The migration is now **fully automated** with a single command:
|
||||
|
||||
1. **Preparation**: Boot from live USB, run the migration script
|
||||
2. **Detection**: Automatically detect source partition sizes and layout
|
||||
3. **LVM Setup**: Create LVM physical volumes, volume groups, and logical volumes
|
||||
4. **Data Copy**: Efficiently copy all filesystem data using optimized `cp -a` method
|
||||
5. **Boot Configuration**: Automatically update fstab, install GRUB EFI bootloader
|
||||
6. **Validation**: Verify all components are properly configured
|
||||
7. **Ready**: System is immediately bootable from external drive
|
||||
|
||||
### One-Button Migration
|
||||
|
||||
```bash
|
||||
sudo ./migrate_to_lvm.sh /dev/sdX
|
||||
```
|
||||
|
||||
The script automatically handles:
|
||||
- ✅ Partition detection and size calculation
|
||||
- ✅ LVM volume creation with optimal sizes
|
||||
- ✅ Complete data transfer (root, home, boot, EFI)
|
||||
- ✅ fstab configuration with proper UUIDs
|
||||
- ✅ GRUB EFI bootloader installation
|
||||
- ✅ initramfs updates for LVM support
|
||||
- ✅ System validation and readiness check
|
||||
|
||||
### Post-Migration Benefits
|
||||
|
||||
Once migrated to LVM, your system gains:
|
||||
- **Instant Snapshots**: Create point-in-time snapshots before system changes
|
||||
- **Dynamic Resizing**: Resize partitions without downtime
|
||||
- **Advanced Backups**: Consistent snapshots for reliable backups
|
||||
- **Easy Rollback**: Revert to previous snapshots if needed
|
||||
|
||||
4. **Optional**: Set up portable tools on external M.2 SSD:
|
||||
```bash
|
||||
./setup_portable_tools.sh
|
||||
111
old_scripts/SIMPLE_CLONE_SOLUTION.md
Normal file
111
old_scripts/SIMPLE_CLONE_SOLUTION.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Simple and Reliable 1-to-1 Clone Solution
|
||||
|
||||
## Problem Analysis
|
||||
|
||||
The LVM migration failed because it introduced too much complexity:
|
||||
- Complex LVM setup with volume groups and logical volumes
|
||||
- Initramfs configuration issues with LVM modules
|
||||
- Boot loader configuration changes
|
||||
- Multiple mount points and potential failure scenarios
|
||||
|
||||
The screenshot shows a boot failure where the system can't properly initialize, likely because the initramfs doesn't have the right LVM configuration.
|
||||
|
||||
## Solution: Direct 1-to-1 Clone
|
||||
|
||||
Instead of converting to LVM, create an exact bit-perfect copy that preserves your original structure:
|
||||
|
||||
### Key Advantages:
|
||||
✅ **Preserves original encryption** - LUKS works exactly as before
|
||||
✅ **No LVM complexity** - Simple partition structure
|
||||
✅ **Minimal boot changes** - Only UUID updates needed
|
||||
✅ **Reliable boot process** - Same as your working internal drive
|
||||
✅ **Emergency fallback** - Original internal drive unchanged
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
### Step 1: Create the Clone
|
||||
```bash
|
||||
# Run from live USB system
|
||||
sudo ./direct_clone_backup.sh
|
||||
```
|
||||
|
||||
This script will:
|
||||
- Detect your internal and external drives
|
||||
- Create a bit-perfect clone (using dd)
|
||||
- Update UUIDs to prevent conflicts
|
||||
- Fix /etc/fstab with new UUIDs
|
||||
- Install/repair GRUB bootloader
|
||||
- Verify the clone integrity
|
||||
|
||||
### Step 2: Verify Boot Readiness
|
||||
```bash
|
||||
# Verify the cloned drive will boot properly
|
||||
sudo ./verify_boot_readiness.sh
|
||||
```
|
||||
|
||||
This script checks:
|
||||
- Partition structure integrity
|
||||
- Filesystem health
|
||||
- Essential boot files presence
|
||||
- GRUB configuration
|
||||
- Boot readiness tests
|
||||
|
||||
### Step 3: Fix Issues (if needed)
|
||||
```bash
|
||||
# If verification fails, repair the clone
|
||||
sudo ./boot_repair_tools.sh
|
||||
```
|
||||
|
||||
Available repair options:
|
||||
- Full repair (recommended)
|
||||
- GRUB repair only
|
||||
- /etc/fstab fix only
|
||||
- Initramfs regeneration
|
||||
- Boot configuration check
|
||||
|
||||
## What This Solution Does Differently
|
||||
|
||||
### ❌ LVM Migration (Complex, Failed)
|
||||
- Converts partition structure to LVM
|
||||
- Requires initramfs LVM module configuration
|
||||
- Changes boot process significantly
|
||||
- Multiple potential failure points
|
||||
- Complex recovery if something goes wrong
|
||||
|
||||
### ✅ Direct Clone (Simple, Reliable)
|
||||
- Preserves exact original structure
|
||||
- No initramfs changes needed
|
||||
- Minimal boot process changes
|
||||
- Only UUID conflicts to resolve
|
||||
- Easy recovery with original drive
|
||||
|
||||
## Boot Process Comparison
|
||||
|
||||
### Original Failed LVM Boot:
|
||||
1. GRUB loads → 2. Initramfs loads → 3. **FAILS** (LVM modules/config issue) → Boot failure
|
||||
|
||||
### New Direct Clone Boot:
|
||||
1. GRUB loads → 2. Initramfs loads → 3. **WORKS** (identical to original) → LUKS password → System boots
|
||||
|
||||
## Expected Results
|
||||
|
||||
After running the direct clone:
|
||||
1. **Boots like original** - Same encryption, same password prompt
|
||||
2. **No reset loops** - Stable boot process
|
||||
3. **Identical experience** - Everything works as before
|
||||
4. **Safe fallback** - Original internal drive unchanged
|
||||
|
||||
## Emergency Recovery
|
||||
|
||||
If something goes wrong:
|
||||
1. Boot from original internal drive (unchanged)
|
||||
2. Run `boot_repair_tools.sh` on external drive
|
||||
3. Or re-run `direct_clone_backup.sh` to start over
|
||||
|
||||
## Key Files Created
|
||||
|
||||
1. **`direct_clone_backup.sh`** - Main cloning script
|
||||
2. **`verify_boot_readiness.sh`** - Boot verification tool
|
||||
3. **`boot_repair_tools.sh`** - Emergency repair toolkit
|
||||
|
||||
This solution avoids all the complexity that caused the LVM migration to fail while giving you a working 1-to-1 copy that boots reliably.
|
||||
21
old_scripts/START_SIMPLE_CLONE.txt
Normal file
21
old_scripts/START_SIMPLE_CLONE.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
=== SIMPLE 1-TO-1 CLONE INSTRUCTIONS ===
|
||||
|
||||
IMPORTANT: Boot from this USB stick first!
|
||||
|
||||
1. REBOOT and boot from this USB stick (Debian Live)
|
||||
|
||||
2. Once in live system, open terminal and run:
|
||||
cd /media/migration_tools (or wherever this partition mounts)
|
||||
|
||||
3. Run the clone process:
|
||||
sudo ./direct_clone_backup.sh
|
||||
|
||||
4. Verify the clone:
|
||||
sudo ./verify_boot_readiness.sh
|
||||
|
||||
5. If needed, repair issues:
|
||||
sudo ./boot_repair_tools.sh
|
||||
|
||||
This creates a working 1-to-1 copy without LVM complexity!
|
||||
|
||||
Read SIMPLE_CLONE_SOLUTION.md for full details.
|
||||
389
old_scripts/boot_repair_tools.sh
Normal file
389
old_scripts/boot_repair_tools.sh
Normal file
@@ -0,0 +1,389 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Boot Repair and Emergency Recovery Tools
|
||||
# Provides tools to fix boot issues after cloning
|
||||
|
||||
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
|
||||
|
||||
TARGET_DRIVE=""
|
||||
WORK_DIR="/mnt/repair_work"
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
detect_target_drive() {
|
||||
log "Detecting target drive to repair..."
|
||||
|
||||
# List available drives
|
||||
echo "Available drives:"
|
||||
lsblk -dpno NAME,SIZE,MODEL | grep -v "loop\|ram"
|
||||
echo
|
||||
|
||||
read -p "Enter the drive to repair (e.g., /dev/sdb): " TARGET_DRIVE
|
||||
|
||||
if [ ! -b "$TARGET_DRIVE" ]; then
|
||||
error "Drive $TARGET_DRIVE not found"
|
||||
fi
|
||||
|
||||
echo "Selected drive for repair: $TARGET_DRIVE"
|
||||
lsblk "$TARGET_DRIVE"
|
||||
echo
|
||||
|
||||
read -p "Is this correct? [y/N]: " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
error "Operation cancelled"
|
||||
fi
|
||||
|
||||
success "Target drive selected: $TARGET_DRIVE"
|
||||
}
|
||||
|
||||
mount_target_system() {
|
||||
log "Mounting target system for repair..."
|
||||
|
||||
mkdir -p "$WORK_DIR"
|
||||
|
||||
# Find partitions
|
||||
local partitions=($(lsblk -pno NAME "$TARGET_DRIVE" | grep -v "^$TARGET_DRIVE$"))
|
||||
local root_partition=""
|
||||
local boot_partition=""
|
||||
local efi_partition=""
|
||||
|
||||
for part in "${partitions[@]}"; do
|
||||
local fstype=$(lsblk -no FSTYPE "$part")
|
||||
local size_bytes=$(lsblk -bno SIZE "$part")
|
||||
|
||||
# Detect partitions by filesystem and size
|
||||
if [[ "$fstype" == "vfat" && "$size_bytes" -lt 1073741824 ]]; then # Less than 1GB
|
||||
efi_partition="$part"
|
||||
elif [[ "$fstype" == "ext4" && "$size_bytes" -lt 5368709120 ]]; then # Between 100MB and 5GB
|
||||
boot_partition="$part"
|
||||
elif [[ "$fstype" == "ext4" && "$size_bytes" -gt 5368709120 ]]; then # Larger than 5GB
|
||||
root_partition="$part"
|
||||
elif [[ "$fstype" == "crypto_LUKS" ]]; then
|
||||
# Try to unlock encrypted partition
|
||||
local crypt_name="repair_$(basename "$part")"
|
||||
echo "Found encrypted partition: $part"
|
||||
echo "Please enter the password to unlock for repair:"
|
||||
if cryptsetup open "$part" "$crypt_name"; then
|
||||
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"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$root_partition" ]; then
|
||||
error "Could not find root partition to repair"
|
||||
fi
|
||||
|
||||
# Mount filesystems
|
||||
log "Mounting root partition: $root_partition"
|
||||
mount "$root_partition" "$WORK_DIR" || error "Failed to mount root partition"
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
success "Target system mounted at $WORK_DIR"
|
||||
}
|
||||
|
||||
repair_grub() {
|
||||
log "Repairing GRUB bootloader..."
|
||||
|
||||
# Bind mount necessary filesystems
|
||||
mount --bind /dev "$WORK_DIR/dev"
|
||||
mount --bind /proc "$WORK_DIR/proc"
|
||||
mount --bind /sys "$WORK_DIR/sys"
|
||||
mount --bind /run "$WORK_DIR/run"
|
||||
|
||||
# Repair GRUB
|
||||
chroot "$WORK_DIR" /bin/bash -c "
|
||||
echo 'Updating initramfs...'
|
||||
update-initramfs -u -k all
|
||||
|
||||
echo 'Reinstalling GRUB...'
|
||||
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=debian --recheck $TARGET_DRIVE
|
||||
|
||||
echo 'Updating GRUB configuration...'
|
||||
update-grub
|
||||
|
||||
echo 'GRUB repair completed'
|
||||
"
|
||||
|
||||
# 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 "GRUB repair completed"
|
||||
}
|
||||
|
||||
fix_fstab() {
|
||||
log "Checking and fixing /etc/fstab..."
|
||||
|
||||
if [ ! -f "$WORK_DIR/etc/fstab" ]; then
|
||||
warning "/etc/fstab not found"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Backup current fstab
|
||||
cp "$WORK_DIR/etc/fstab" "$WORK_DIR/etc/fstab.backup.$(date +%Y%m%d_%H%M%S)"
|
||||
|
||||
# Get current UUIDs of all partitions
|
||||
local partitions=($(lsblk -pno NAME "$TARGET_DRIVE" | grep -v "^$TARGET_DRIVE$"))
|
||||
|
||||
echo "Current partition UUIDs:"
|
||||
for part in "${partitions[@]}"; do
|
||||
local uuid=$(lsblk -no UUID "$part")
|
||||
local fstype=$(lsblk -no FSTYPE "$part")
|
||||
if [ -n "$uuid" ]; then
|
||||
echo " $part ($fstype): $uuid"
|
||||
fi
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Current /etc/fstab content:"
|
||||
cat "$WORK_DIR/etc/fstab"
|
||||
echo
|
||||
|
||||
read -p "Do you want to regenerate /etc/fstab with current UUIDs? [y/N]: " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
# Generate new fstab
|
||||
echo "# /etc/fstab: static file system information." > "$WORK_DIR/etc/fstab.new"
|
||||
echo "# Regenerated by repair script on $(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"
|
||||
|
||||
for part in "${partitions[@]}"; do
|
||||
local uuid=$(lsblk -no UUID "$part")
|
||||
local fstype=$(lsblk -no FSTYPE "$part")
|
||||
|
||||
if [ -n "$uuid" ]; then
|
||||
case "$fstype" in
|
||||
"vfat")
|
||||
echo "UUID=$uuid /boot/efi vfat defaults 0 2" >> "$WORK_DIR/etc/fstab.new"
|
||||
;;
|
||||
"ext4")
|
||||
# Determine if it's boot or root by size
|
||||
local size_bytes=$(lsblk -bno SIZE "$part")
|
||||
if [ "$size_bytes" -lt 5368709120 ]; then # Less than 5GB = boot
|
||||
echo "UUID=$uuid /boot ext4 defaults 0 2" >> "$WORK_DIR/etc/fstab.new"
|
||||
else # Root partition
|
||||
echo "UUID=$uuid / ext4 defaults 0 1" >> "$WORK_DIR/etc/fstab.new"
|
||||
fi
|
||||
;;
|
||||
"swap")
|
||||
echo "UUID=$uuid none swap sw 0 0" >> "$WORK_DIR/etc/fstab.new"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
|
||||
# Add tmpfs
|
||||
echo "tmpfs /tmp tmpfs defaults,noatime,mode=1777 0 0" >> "$WORK_DIR/etc/fstab.new"
|
||||
|
||||
# Show new fstab
|
||||
echo
|
||||
echo "New /etc/fstab content:"
|
||||
cat "$WORK_DIR/etc/fstab.new"
|
||||
echo
|
||||
|
||||
read -p "Apply this new /etc/fstab? [y/N]: " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
mv "$WORK_DIR/etc/fstab.new" "$WORK_DIR/etc/fstab"
|
||||
success "/etc/fstab updated"
|
||||
else
|
||||
rm "$WORK_DIR/etc/fstab.new"
|
||||
log "New fstab not applied"
|
||||
fi
|
||||
fi
|
||||
|
||||
success "fstab check completed"
|
||||
}
|
||||
|
||||
check_boot_configuration() {
|
||||
log "Checking boot configuration..."
|
||||
|
||||
# Check if EFI boot entry exists
|
||||
if command -v efibootmgr >/dev/null 2>&1; then
|
||||
echo "Current EFI boot entries:"
|
||||
efibootmgr
|
||||
echo
|
||||
fi
|
||||
|
||||
# Check GRUB configuration
|
||||
if [ -f "$WORK_DIR/etc/default/grub" ]; then
|
||||
echo "GRUB configuration (/etc/default/grub):"
|
||||
cat "$WORK_DIR/etc/default/grub"
|
||||
echo
|
||||
fi
|
||||
|
||||
# Check if initramfs exists
|
||||
echo "Available kernels and initramfs:"
|
||||
ls -la "$WORK_DIR/boot/vmlinuz-"* 2>/dev/null || echo "No kernels found"
|
||||
ls -la "$WORK_DIR/boot/initrd.img-"* 2>/dev/null || echo "No initramfs found"
|
||||
echo
|
||||
|
||||
success "Boot configuration check completed"
|
||||
}
|
||||
|
||||
regenerate_initramfs() {
|
||||
log "Regenerating initramfs..."
|
||||
|
||||
# Bind mount necessary filesystems
|
||||
mount --bind /dev "$WORK_DIR/dev"
|
||||
mount --bind /proc "$WORK_DIR/proc"
|
||||
mount --bind /sys "$WORK_DIR/sys"
|
||||
mount --bind /run "$WORK_DIR/run"
|
||||
|
||||
chroot "$WORK_DIR" /bin/bash -c "
|
||||
echo 'Regenerating initramfs for all kernels...'
|
||||
update-initramfs -u -k all
|
||||
echo 'Initramfs regeneration completed'
|
||||
"
|
||||
|
||||
# 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 "Initramfs regenerated"
|
||||
}
|
||||
|
||||
cleanup_repair() {
|
||||
log "Cleaning up repair environment..."
|
||||
|
||||
# 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 encrypted volumes
|
||||
for mapper in /dev/mapper/repair_*; 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"
|
||||
}
|
||||
|
||||
main() {
|
||||
echo -e "${GREEN}=== Boot Repair and Emergency Recovery Tools ===${NC}"
|
||||
echo "This script helps fix boot issues after cloning"
|
||||
echo
|
||||
|
||||
echo "Available repair options:"
|
||||
echo "1. Full repair (mount system, fix fstab, repair GRUB, regenerate initramfs)"
|
||||
echo "2. GRUB repair only"
|
||||
echo "3. fstab fix only"
|
||||
echo "4. Initramfs regeneration only"
|
||||
echo "5. Check boot configuration"
|
||||
echo
|
||||
|
||||
read -p "Select repair option [1-5]: " -n 1 -r
|
||||
echo
|
||||
|
||||
case $REPLY in
|
||||
1)
|
||||
log "Performing full repair..."
|
||||
detect_target_drive
|
||||
mount_target_system
|
||||
fix_fstab
|
||||
regenerate_initramfs
|
||||
repair_grub
|
||||
check_boot_configuration
|
||||
cleanup_repair
|
||||
success "Full repair completed!"
|
||||
;;
|
||||
2)
|
||||
log "Performing GRUB repair only..."
|
||||
detect_target_drive
|
||||
mount_target_system
|
||||
repair_grub
|
||||
cleanup_repair
|
||||
success "GRUB repair completed!"
|
||||
;;
|
||||
3)
|
||||
log "Fixing fstab only..."
|
||||
detect_target_drive
|
||||
mount_target_system
|
||||
fix_fstab
|
||||
cleanup_repair
|
||||
success "fstab fix completed!"
|
||||
;;
|
||||
4)
|
||||
log "Regenerating initramfs only..."
|
||||
detect_target_drive
|
||||
mount_target_system
|
||||
regenerate_initramfs
|
||||
cleanup_repair
|
||||
success "Initramfs regeneration completed!"
|
||||
;;
|
||||
5)
|
||||
log "Checking boot configuration..."
|
||||
detect_target_drive
|
||||
mount_target_system
|
||||
check_boot_configuration
|
||||
cleanup_repair
|
||||
success "Boot configuration check completed!"
|
||||
;;
|
||||
*)
|
||||
error "Invalid option selected"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo
|
||||
echo -e "${BLUE}Repair completed. Next steps:${NC}"
|
||||
echo "1. Reboot your system"
|
||||
echo "2. Set the repaired drive as first boot device in BIOS/UEFI"
|
||||
echo "3. Try booting from the external drive"
|
||||
echo "4. If issues persist, run this script again or check system logs"
|
||||
}
|
||||
|
||||
# Trap to ensure cleanup on exit
|
||||
trap cleanup_repair EXIT
|
||||
|
||||
main "$@"
|
||||
39
old_scripts/build-deb.sh
Normal file
39
old_scripts/build-deb.sh
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
# Build script for LVM Backup Manager .deb package
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔨 Building LVM Backup Manager .deb package..."
|
||||
|
||||
# Cleanup old builds
|
||||
rm -f lvm-backup-manager_*.deb
|
||||
|
||||
# Set proper permissions
|
||||
find deb-package -type f -exec chmod 644 {} \;
|
||||
find deb-package -type d -exec chmod 755 {} \;
|
||||
chmod 755 deb-package/DEBIAN/postinst
|
||||
chmod 755 deb-package/usr/bin/*
|
||||
|
||||
# Calculate package size
|
||||
PACKAGE_SIZE=$(du -s deb-package | cut -f1)
|
||||
echo "Installed-Size: $PACKAGE_SIZE" >> deb-package/DEBIAN/control
|
||||
|
||||
# Build the package
|
||||
echo "📦 Creating .deb package..."
|
||||
dpkg-deb --build deb-package lvm-backup-manager_1.0.0_all.deb
|
||||
|
||||
# Verify the package
|
||||
echo "✅ Verifying package..."
|
||||
dpkg-deb --info lvm-backup-manager_1.0.0_all.deb
|
||||
dpkg-deb --contents lvm-backup-manager_1.0.0_all.deb
|
||||
|
||||
echo ""
|
||||
echo "🎉 Package built successfully!"
|
||||
echo "📦 File: lvm-backup-manager_1.0.0_all.deb"
|
||||
echo ""
|
||||
echo "To install:"
|
||||
echo " sudo dpkg -i lvm-backup-manager_1.0.0_all.deb"
|
||||
echo " sudo apt-get install -f # Fix any missing dependencies"
|
||||
echo ""
|
||||
echo "To test the GUI:"
|
||||
echo " sudo lvm-backup-manager"
|
||||
107
old_scripts/clear_bios_boot_flag.sh
Executable file
107
old_scripts/clear_bios_boot_flag.sh
Executable file
@@ -0,0 +1,107 @@
|
||||
#!/bin/bash
|
||||
# Clear Lenovo BIOS Boot Flags
|
||||
# This script helps clear the "reset system" message shown before GRUB
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== Lenovo BIOS Boot Flag Clear Utility ==="
|
||||
echo ""
|
||||
echo "The 'reset system' message you see before GRUB is from the Lenovo BIOS/UEFI firmware."
|
||||
echo "This typically indicates the BIOS detected an improper shutdown or boot failure."
|
||||
echo ""
|
||||
|
||||
echo "Current boot configuration:"
|
||||
echo "- Root: internal-vg on nvme0n1 (internal drive)"
|
||||
echo "- Boot: internal-vg on nvme0n1 (internal drive)"
|
||||
echo "- EFI: nvme0n1p1 (internal drive)"
|
||||
echo "- Home: migration-vg on sda (external drive)"
|
||||
echo ""
|
||||
|
||||
echo "Checking GRUB installation on internal drive..."
|
||||
if [ -f /boot/efi/EFI/ubuntu/shimx64.efi ]; then
|
||||
echo "✓ GRUB bootloader is properly installed on internal nvme0n1"
|
||||
else
|
||||
echo "✗ GRUB bootloader missing - need to reinstall"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "Checking EFI boot entries..."
|
||||
if efibootmgr | grep -q "ubuntu"; then
|
||||
echo "✓ Ubuntu boot entry exists in UEFI firmware"
|
||||
efibootmgr | grep ubuntu
|
||||
else
|
||||
echo "✗ Ubuntu boot entry missing"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "=== Solutions to Clear BIOS 'Reset System' Message ==="
|
||||
echo ""
|
||||
echo "OPTION 1: Clean Shutdown and Boot Sequence (Recommended)"
|
||||
echo " 1. Disconnect the external M.2 USB drive (sda)"
|
||||
echo " 2. Perform a clean shutdown: sudo shutdown -h now"
|
||||
echo " 3. Wait 10 seconds after system powers off"
|
||||
echo " 4. Power on the system"
|
||||
echo " 5. The BIOS should recognize the clean boot and clear the flag"
|
||||
echo ""
|
||||
echo "OPTION 2: Enter BIOS Setup"
|
||||
echo " 1. Reboot and press F1 (or F2) during POST to enter BIOS"
|
||||
echo " 2. Go to 'Startup' or 'Boot' menu"
|
||||
echo " 3. Check for any warnings or errors"
|
||||
echo " 4. Save and exit BIOS (F10)"
|
||||
echo " 5. This acknowledges any BIOS messages and clears flags"
|
||||
echo ""
|
||||
echo "OPTION 3: Reset BIOS Boot Order"
|
||||
echo " 1. Enter BIOS (F1 during boot)"
|
||||
echo " 2. Go to Startup → Boot menu"
|
||||
echo " 3. Ensure 'Ubuntu' is at the top of boot order"
|
||||
echo " 4. Disable 'Fast Boot' if enabled (can cause issues with dual boot)"
|
||||
echo " 5. Save and exit"
|
||||
echo ""
|
||||
echo "OPTION 4: Update UEFI Boot Entry Priority (from Linux)"
|
||||
echo " This will ensure Ubuntu on internal drive is the first boot option:"
|
||||
echo ""
|
||||
|
||||
read -p "Do you want to set Ubuntu as the first boot option now? [y/N] " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
UBUNTU_ENTRY=$(efibootmgr | grep -i ubuntu | cut -d'*' -f1 | cut -d't' -f2 | head -1)
|
||||
if [ -n "$UBUNTU_ENTRY" ]; then
|
||||
echo "Setting Boot$UBUNTU_ENTRY (Ubuntu) as first boot option..."
|
||||
sudo efibootmgr -n $UBUNTU_ENTRY
|
||||
echo "✓ Next boot will use Ubuntu entry"
|
||||
echo ""
|
||||
echo "To make this permanent:"
|
||||
BOOT_ORDER=$(efibootmgr | grep BootOrder | cut -d':' -f2 | tr -d ' ')
|
||||
NEW_ORDER="$UBUNTU_ENTRY,${BOOT_ORDER//,${UBUNTU_ENTRY}/}"
|
||||
NEW_ORDER="${NEW_ORDER//${UBUNTU_ENTRY},,/,}"
|
||||
echo "Run: sudo efibootmgr -o $NEW_ORDER"
|
||||
else
|
||||
echo "Could not find Ubuntu boot entry"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "=== Additional Diagnostics ==="
|
||||
echo ""
|
||||
echo "Check system journal for boot issues:"
|
||||
echo " sudo journalctl -b -p err"
|
||||
echo ""
|
||||
echo "Check hardware clock:"
|
||||
echo " sudo hwclock --show"
|
||||
echo ""
|
||||
echo "Verify GRUB is working:"
|
||||
echo " sudo grub-editenv list"
|
||||
echo ""
|
||||
|
||||
echo "=== Why This Happens ==="
|
||||
echo ""
|
||||
echo "The Lenovo BIOS shows 'reset system' when:"
|
||||
echo "1. System was not shut down properly (power loss, crash, forced shutdown)"
|
||||
echo "2. BIOS detected a boot failure and wants you to acknowledge it"
|
||||
echo "3. Hardware configuration changed (like resizing partitions)"
|
||||
echo "4. BIOS boot counter wasn't properly reset after successful boot"
|
||||
echo ""
|
||||
echo "Since your GRUB is properly installed and working, this is just a BIOS"
|
||||
echo "notification that needs to be acknowledged through clean boot cycle."
|
||||
19
old_scripts/deb-package/DEBIAN/control
Normal file
19
old_scripts/deb-package/DEBIAN/control
Normal file
@@ -0,0 +1,19 @@
|
||||
Package: lvm-backup-manager
|
||||
Version: 1.0.0
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Depends: python3, python3-tk, lvm2
|
||||
Maintainer: LVM Backup Team <backup@example.com>
|
||||
Description: Professional LVM Backup Manager with GUI
|
||||
A modern, user-friendly graphical interface for creating
|
||||
block-level backups of LVM volumes. Features include:
|
||||
.
|
||||
* Intuitive drive selection with size information
|
||||
* Real-time progress monitoring
|
||||
* Time estimation and speed indicators
|
||||
* Professional logging and verification
|
||||
* Safe snapshot-based backup process
|
||||
.
|
||||
This tool creates exact, bootable clones of your LVM
|
||||
volumes for complete system backup and recovery.
|
||||
21
old_scripts/deb-package/DEBIAN/postinst
Normal file
21
old_scripts/deb-package/DEBIAN/postinst
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
# Post-installation script for LVM Backup Manager
|
||||
|
||||
set -e
|
||||
|
||||
# Set proper permissions
|
||||
chmod +x /usr/bin/lvm-backup-manager
|
||||
chmod +x /usr/bin/lvm-block-backup
|
||||
|
||||
# Create symlink for easy access
|
||||
if [ ! -L /usr/local/bin/lvm-backup-gui ]; then
|
||||
ln -s /usr/bin/lvm-backup-manager /usr/local/bin/lvm-backup-gui
|
||||
fi
|
||||
|
||||
echo "LVM Backup Manager installed successfully!"
|
||||
echo "You can now launch it from:"
|
||||
echo " - Applications menu: System Tools → LVM Backup Manager"
|
||||
echo " - Terminal: sudo lvm-backup-manager"
|
||||
echo " - Desktop: Double-click the LVM Backup Manager icon"
|
||||
|
||||
exit 0
|
||||
23
old_scripts/deb-package/usr/bin/lvm-backup-manager
Normal file
23
old_scripts/deb-package/usr/bin/lvm-backup-manager
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
# LVM Backup Manager GUI Wrapper
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "LVM Backup Manager requires root privileges."
|
||||
echo "Please run with: sudo lvm-backup-manager"
|
||||
|
||||
# Try to launch with pkexec if available
|
||||
if command -v pkexec >/dev/null 2>&1; then
|
||||
exec pkexec "$0" "$@"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set proper path
|
||||
SCRIPT_DIR="$(dirname "$0")"
|
||||
export PATH="$SCRIPT_DIR:$PATH"
|
||||
|
||||
# Launch the GUI
|
||||
cd "$SCRIPT_DIR"
|
||||
exec python3 "$SCRIPT_DIR/lvm_backup_gui.py" "$@"
|
||||
193
old_scripts/deb-package/usr/bin/lvm-block-backup
Normal file
193
old_scripts/deb-package/usr/bin/lvm-block-backup
Normal file
@@ -0,0 +1,193 @@
|
||||
#!/bin/bash
|
||||
|
||||
# LVM Block-Level Backup Script
|
||||
# Creates consistent snapshots and clones entire volumes block-for-block
|
||||
# This is the ONLY backup script you need!
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Configuration
|
||||
SOURCE_VG="internal-vg"
|
||||
TARGET_VG="migration-vg"
|
||||
SNAPSHOT_SIZE="2G"
|
||||
LOG_FILE="/var/log/lvm-block-backup.log"
|
||||
|
||||
log() {
|
||||
local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||
echo -e "${BLUE}$message${NC}"
|
||||
echo "$message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
error() {
|
||||
local message="[ERROR] $1"
|
||||
echo -e "${RED}$message${NC}" >&2
|
||||
echo "$message" >> "$LOG_FILE"
|
||||
exit 1
|
||||
}
|
||||
|
||||
success() {
|
||||
local message="[SUCCESS] $1"
|
||||
echo -e "${GREEN}$message${NC}"
|
||||
echo "$message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
warning() {
|
||||
local message="[WARNING] $1"
|
||||
echo -e "${YELLOW}$message${NC}"
|
||||
echo "$message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
check_requirements() {
|
||||
log "Checking system requirements..."
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
error "This script must be run as root. Use: sudo $0"
|
||||
fi
|
||||
|
||||
# Check if source VG exists
|
||||
if ! vgs "$SOURCE_VG" >/dev/null 2>&1; then
|
||||
error "Source volume group '$SOURCE_VG' not found"
|
||||
fi
|
||||
|
||||
# Check if target VG exists
|
||||
if ! vgs "$TARGET_VG" >/dev/null 2>&1; then
|
||||
error "Target volume group '$TARGET_VG' not found"
|
||||
fi
|
||||
|
||||
# Check if volumes exist
|
||||
local volumes=("root" "home" "boot")
|
||||
for vol in "${volumes[@]}"; do
|
||||
if ! lvs "$SOURCE_VG/$vol" >/dev/null 2>&1; then
|
||||
error "Source logical volume '$SOURCE_VG/$vol' not found"
|
||||
fi
|
||||
if ! lvs "$TARGET_VG/$vol" >/dev/null 2>&1; then
|
||||
error "Target logical volume '$TARGET_VG/$vol' not found"
|
||||
fi
|
||||
done
|
||||
|
||||
# Check available space for snapshots
|
||||
local vg_free=$(vgs --noheadings -o vg_free --units g "$SOURCE_VG" | tr -d ' G')
|
||||
local vg_free_int=${vg_free%.*}
|
||||
|
||||
if [ "$vg_free_int" -lt 6 ]; then
|
||||
error "Insufficient free space for snapshots. Need at least 6GB, have ${vg_free}GB"
|
||||
fi
|
||||
|
||||
success "System requirements check passed"
|
||||
}
|
||||
|
||||
cleanup_snapshots() {
|
||||
log "Cleaning up any existing snapshots..."
|
||||
lvremove -f "$SOURCE_VG/root-backup-snap" 2>/dev/null || true
|
||||
lvremove -f "$SOURCE_VG/home-backup-snap" 2>/dev/null || true
|
||||
lvremove -f "$SOURCE_VG/boot-backup-snap" 2>/dev/null || true
|
||||
}
|
||||
|
||||
create_snapshots() {
|
||||
log "Creating LVM snapshots for consistent backup..."
|
||||
|
||||
cleanup_snapshots
|
||||
|
||||
# Create snapshots
|
||||
lvcreate -L "$SNAPSHOT_SIZE" -s -n root-backup-snap "$SOURCE_VG/root" || error "Failed to create root snapshot"
|
||||
lvcreate -L "$SNAPSHOT_SIZE" -s -n home-backup-snap "$SOURCE_VG/home" || error "Failed to create home snapshot"
|
||||
lvcreate -L 1G -s -n boot-backup-snap "$SOURCE_VG/boot" || error "Failed to create boot snapshot"
|
||||
|
||||
success "Snapshots created successfully"
|
||||
}
|
||||
|
||||
clone_volumes() {
|
||||
log "Starting block-level volume cloning..."
|
||||
|
||||
# Unmount target volumes if mounted
|
||||
umount "/dev/$TARGET_VG/home" 2>/dev/null || true
|
||||
umount "/dev/$TARGET_VG/root" 2>/dev/null || true
|
||||
umount "/dev/$TARGET_VG/boot" 2>/dev/null || true
|
||||
|
||||
# Clone root volume
|
||||
log "Cloning root volume (this may take a while)..."
|
||||
dd if="/dev/$SOURCE_VG/root-backup-snap" of="/dev/$TARGET_VG/root" bs=64M status=progress || error "Failed to clone root volume"
|
||||
success "Root volume cloned"
|
||||
|
||||
# Clone home volume
|
||||
log "Cloning home volume (this will take the longest)..."
|
||||
dd if="/dev/$SOURCE_VG/home-backup-snap" of="/dev/$TARGET_VG/home" bs=64M status=progress || error "Failed to clone home volume"
|
||||
success "Home volume cloned"
|
||||
|
||||
# Clone boot volume
|
||||
log "Cloning boot volume..."
|
||||
dd if="/dev/$SOURCE_VG/boot-backup-snap" of="/dev/$TARGET_VG/boot" bs=64M status=progress || error "Failed to clone boot volume"
|
||||
success "Boot volume cloned"
|
||||
}
|
||||
|
||||
verify_backup() {
|
||||
log "Verifying backup integrity..."
|
||||
|
||||
# Check filesystem integrity
|
||||
fsck -n "/dev/$TARGET_VG/root" || warning "Root filesystem check showed issues"
|
||||
fsck -n "/dev/$TARGET_VG/boot" || warning "Boot filesystem check showed issues"
|
||||
|
||||
# Note: Can't check encrypted home volume without decryption
|
||||
|
||||
success "Backup verification completed"
|
||||
}
|
||||
|
||||
show_backup_info() {
|
||||
log "Creating backup information..."
|
||||
|
||||
cat << EOF
|
||||
|
||||
=====================================
|
||||
LVM Block-Level Backup Complete
|
||||
=====================================
|
||||
Date: $(date)
|
||||
Source: $SOURCE_VG
|
||||
Target: $TARGET_VG
|
||||
|
||||
Volume Information:
|
||||
$(lvs $SOURCE_VG $TARGET_VG)
|
||||
|
||||
The external drive now contains an exact block-level copy of your internal drive.
|
||||
You can boot from the external drive by selecting it in your BIOS/UEFI boot menu.
|
||||
|
||||
To mount the backup volumes:
|
||||
sudo mount /dev/$TARGET_VG/root /mnt/backup-root
|
||||
sudo mount /dev/$TARGET_VG/boot /mnt/backup-boot
|
||||
# Home volume needs LUKS decryption first
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
main() {
|
||||
echo -e "${GREEN}=== LVM Block-Level Backup Tool ===${NC}"
|
||||
echo "This will create an exact copy of your internal LVM volumes to the external drive."
|
||||
echo
|
||||
|
||||
read -p "Are you sure you want to proceed? This will OVERWRITE data on $TARGET_VG! [y/N]: " confirm
|
||||
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
||||
echo "Backup cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
check_requirements
|
||||
create_snapshots
|
||||
clone_volumes
|
||||
cleanup_snapshots
|
||||
verify_backup
|
||||
show_backup_info
|
||||
|
||||
success "Backup completed successfully!"
|
||||
}
|
||||
|
||||
# Trap to cleanup on exit
|
||||
trap cleanup_snapshots EXIT
|
||||
|
||||
main "$@"
|
||||
541
old_scripts/deb-package/usr/bin/lvm_backup_gui.py
Normal file
541
old_scripts/deb-package/usr/bin/lvm_backup_gui.py
Normal file
@@ -0,0 +1,541 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
LVM Backup GUI - Professional interface for LVM snapshot backups
|
||||
Creates block-level clones of LVM volumes with progress monitoring
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, scrolledtext
|
||||
import subprocess
|
||||
import threading
|
||||
import re
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
class LVMBackupGUI:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("LVM Backup Manager")
|
||||
self.root.geometry("900x700")
|
||||
self.root.resizable(True, True)
|
||||
|
||||
# Configure style
|
||||
self.setup_styles()
|
||||
|
||||
# Variables
|
||||
self.source_vg = tk.StringVar()
|
||||
self.target_vg = tk.StringVar()
|
||||
self.backup_running = False
|
||||
self.backup_process = None
|
||||
|
||||
# Create GUI
|
||||
self.create_widgets()
|
||||
self.refresh_drives()
|
||||
|
||||
def setup_styles(self):
|
||||
"""Configure modern styling"""
|
||||
style = ttk.Style()
|
||||
|
||||
# Configure colors and fonts
|
||||
self.colors = {
|
||||
'primary': '#2196F3',
|
||||
'secondary': '#FFC107',
|
||||
'success': '#4CAF50',
|
||||
'danger': '#F44336',
|
||||
'warning': '#FF9800',
|
||||
'light': '#F5F5F5',
|
||||
'dark': '#333333'
|
||||
}
|
||||
|
||||
style.configure('Title.TLabel', font=('Arial', 16, 'bold'))
|
||||
style.configure('Heading.TLabel', font=('Arial', 12, 'bold'))
|
||||
style.configure('Info.TLabel', font=('Arial', 10))
|
||||
|
||||
def create_widgets(self):
|
||||
"""Create the main GUI interface"""
|
||||
|
||||
# Main container with padding
|
||||
main_frame = ttk.Frame(self.root, padding="20")
|
||||
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||
|
||||
# Configure grid weights
|
||||
self.root.columnconfigure(0, weight=1)
|
||||
self.root.rowconfigure(0, weight=1)
|
||||
main_frame.columnconfigure(1, weight=1)
|
||||
|
||||
# Title
|
||||
title_label = ttk.Label(main_frame, text="🛡️ LVM Backup Manager", style='Title.TLabel')
|
||||
title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))
|
||||
|
||||
# Source drive selection
|
||||
self.create_drive_selection_frame(main_frame, "Source Drive", 1, self.source_vg, True)
|
||||
|
||||
# Arrow
|
||||
arrow_label = ttk.Label(main_frame, text="⬇️", font=('Arial', 20))
|
||||
arrow_label.grid(row=2, column=1, pady=10)
|
||||
|
||||
# Target drive selection
|
||||
self.create_drive_selection_frame(main_frame, "Target Drive", 3, self.target_vg, False)
|
||||
|
||||
# Backup info frame
|
||||
self.create_backup_info_frame(main_frame, 4)
|
||||
|
||||
# Control buttons
|
||||
self.create_control_frame(main_frame, 5)
|
||||
|
||||
# Progress frame
|
||||
self.create_progress_frame(main_frame, 6)
|
||||
|
||||
# Log frame
|
||||
self.create_log_frame(main_frame, 7)
|
||||
|
||||
def create_drive_selection_frame(self, parent, title, row, var, is_source):
|
||||
"""Create drive selection section"""
|
||||
frame = ttk.LabelFrame(parent, text=title, padding="10")
|
||||
frame.grid(row=row, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
|
||||
frame.columnconfigure(1, weight=1)
|
||||
|
||||
# Drive dropdown
|
||||
ttk.Label(frame, text="Volume Group:").grid(row=0, column=0, sticky=tk.W, padx=(0, 10))
|
||||
|
||||
combo = ttk.Combobox(frame, textvariable=var, state='readonly', width=30)
|
||||
combo.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0, 10))
|
||||
combo.bind('<<ComboboxSelected>>', lambda e: self.update_drive_info())
|
||||
|
||||
refresh_btn = ttk.Button(frame, text="🔄 Refresh", command=self.refresh_drives)
|
||||
refresh_btn.grid(row=0, column=2)
|
||||
|
||||
# Drive info labels
|
||||
info_frame = ttk.Frame(frame)
|
||||
info_frame.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(10, 0))
|
||||
info_frame.columnconfigure(1, weight=1)
|
||||
|
||||
# Store references for updating
|
||||
setattr(self, f"{title.lower().replace(' ', '_')}_info", info_frame)
|
||||
|
||||
if is_source:
|
||||
self.source_combo = combo
|
||||
else:
|
||||
self.target_combo = combo
|
||||
|
||||
def create_backup_info_frame(self, parent, row):
|
||||
"""Create backup information display"""
|
||||
frame = ttk.LabelFrame(parent, text="📊 Backup Information", padding="10")
|
||||
frame.grid(row=row, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
|
||||
frame.columnconfigure(1, weight=1)
|
||||
|
||||
self.backup_info_labels = {}
|
||||
|
||||
info_items = [
|
||||
("Total Size:", "total_size"),
|
||||
("Estimated Time:", "est_time"),
|
||||
("Transfer Speed:", "speed"),
|
||||
("Status:", "status")
|
||||
]
|
||||
|
||||
for i, (label, key) in enumerate(info_items):
|
||||
ttk.Label(frame, text=label).grid(row=i//2, column=(i%2)*2, sticky=tk.W, padx=(0, 10), pady=2)
|
||||
value_label = ttk.Label(frame, text="Not calculated", style='Info.TLabel')
|
||||
value_label.grid(row=i//2, column=(i%2)*2+1, sticky=tk.W, padx=(0, 20), pady=2)
|
||||
self.backup_info_labels[key] = value_label
|
||||
|
||||
def create_control_frame(self, parent, row):
|
||||
"""Create control buttons"""
|
||||
frame = ttk.Frame(parent)
|
||||
frame.grid(row=row, column=0, columnspan=3, pady=20)
|
||||
|
||||
self.start_btn = ttk.Button(frame, text="▶️ Start Backup", command=self.start_backup, style='Accent.TButton')
|
||||
self.start_btn.pack(side=tk.LEFT, padx=(0, 10))
|
||||
|
||||
self.stop_btn = ttk.Button(frame, text="⏹️ Stop Backup", command=self.stop_backup, state='disabled')
|
||||
self.stop_btn.pack(side=tk.LEFT, padx=(0, 10))
|
||||
|
||||
self.verify_btn = ttk.Button(frame, text="✅ Verify Backup", command=self.verify_backup)
|
||||
self.verify_btn.pack(side=tk.LEFT)
|
||||
|
||||
def create_progress_frame(self, parent, row):
|
||||
"""Create progress monitoring"""
|
||||
frame = ttk.LabelFrame(parent, text="📈 Progress", padding="10")
|
||||
frame.grid(row=row, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
|
||||
# Overall progress
|
||||
ttk.Label(frame, text="Overall Progress:").grid(row=0, column=0, sticky=tk.W)
|
||||
self.overall_progress = ttk.Progressbar(frame, mode='determinate', length=400)
|
||||
self.overall_progress.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(5, 10))
|
||||
|
||||
# Current operation
|
||||
self.current_operation = ttk.Label(frame, text="Ready to start backup", style='Info.TLabel')
|
||||
self.current_operation.grid(row=2, column=0, sticky=tk.W)
|
||||
|
||||
# Time remaining
|
||||
self.time_remaining = ttk.Label(frame, text="", style='Info.TLabel')
|
||||
self.time_remaining.grid(row=3, column=0, sticky=tk.W)
|
||||
|
||||
def create_log_frame(self, parent, row):
|
||||
"""Create log output"""
|
||||
frame = ttk.LabelFrame(parent, text="📝 Log Output", padding="10")
|
||||
frame.grid(row=row, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
frame.rowconfigure(0, weight=1)
|
||||
parent.rowconfigure(row, weight=1)
|
||||
|
||||
self.log_text = scrolledtext.ScrolledText(frame, height=12, width=80, font=('Consolas', 9))
|
||||
self.log_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||
|
||||
# Add initial message
|
||||
self.log("LVM Backup Manager initialized")
|
||||
self.log("Select source and target drives to begin")
|
||||
|
||||
def log(self, message):
|
||||
"""Add message to log with timestamp"""
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
log_message = f"[{timestamp}] {message}\n"
|
||||
|
||||
self.log_text.insert(tk.END, log_message)
|
||||
self.log_text.see(tk.END)
|
||||
self.root.update_idletasks()
|
||||
|
||||
def refresh_drives(self):
|
||||
"""Scan for available LVM volume groups"""
|
||||
try:
|
||||
self.log("Scanning for LVM volume groups...")
|
||||
|
||||
# Get volume groups
|
||||
result = subprocess.run(['sudo', 'vgs', '--noheadings', '-o', 'vg_name,vg_size,vg_free'],
|
||||
capture_output=True, text=True, check=True)
|
||||
|
||||
vgs = []
|
||||
for line in result.stdout.strip().split('\n'):
|
||||
if line.strip():
|
||||
parts = line.strip().split()
|
||||
if len(parts) >= 3:
|
||||
vg_name = parts[0]
|
||||
vg_size = parts[1]
|
||||
vg_free = parts[2]
|
||||
vgs.append(f"{vg_name} ({vg_size} total, {vg_free} free)")
|
||||
|
||||
# Update comboboxes
|
||||
self.source_combo['values'] = vgs
|
||||
self.target_combo['values'] = vgs
|
||||
|
||||
if vgs:
|
||||
self.log(f"Found {len(vgs)} volume groups")
|
||||
else:
|
||||
self.log("No LVM volume groups found")
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
self.log(f"Error scanning drives: {e}")
|
||||
messagebox.showerror("Error", "Failed to scan for LVM volume groups. Make sure you have LVM installed and proper permissions.")
|
||||
|
||||
def update_drive_info(self):
|
||||
"""Update drive information when selection changes"""
|
||||
if not self.source_vg.get() or not self.target_vg.get():
|
||||
return
|
||||
|
||||
try:
|
||||
source_vg = self.source_vg.get().split()[0]
|
||||
target_vg = self.target_vg.get().split()[0]
|
||||
|
||||
# Get detailed volume information
|
||||
source_info = self.get_vg_details(source_vg)
|
||||
target_info = self.get_vg_details(target_vg)
|
||||
|
||||
# Calculate backup information
|
||||
self.calculate_backup_info(source_info, target_info)
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"Error updating drive info: {e}")
|
||||
|
||||
def get_vg_details(self, vg_name):
|
||||
"""Get detailed information about a volume group"""
|
||||
try:
|
||||
# Get VG info
|
||||
vg_result = subprocess.run(['sudo', 'vgs', vg_name, '--noheadings', '-o', 'vg_size,vg_free,vg_uuid'],
|
||||
capture_output=True, text=True, check=True)
|
||||
vg_parts = vg_result.stdout.strip().split()
|
||||
|
||||
# Get LV info
|
||||
lv_result = subprocess.run(['sudo', 'lvs', vg_name, '--noheadings', '-o', 'lv_name,lv_size'],
|
||||
capture_output=True, text=True, check=True)
|
||||
|
||||
volumes = []
|
||||
total_lv_size = 0
|
||||
for line in lv_result.stdout.strip().split('\n'):
|
||||
if line.strip():
|
||||
parts = line.strip().split()
|
||||
if len(parts) >= 2:
|
||||
lv_name = parts[0]
|
||||
lv_size = parts[1]
|
||||
volumes.append((lv_name, lv_size))
|
||||
# Convert size to bytes for calculation
|
||||
size_bytes = self.parse_size_to_bytes(lv_size)
|
||||
total_lv_size += size_bytes
|
||||
|
||||
return {
|
||||
'name': vg_name,
|
||||
'total_size': vg_parts[0],
|
||||
'free_size': vg_parts[1],
|
||||
'uuid': vg_parts[2],
|
||||
'volumes': volumes,
|
||||
'total_lv_size_bytes': total_lv_size
|
||||
}
|
||||
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
|
||||
def parse_size_to_bytes(self, size_str):
|
||||
"""Parse LVM size string to bytes"""
|
||||
size_str = size_str.strip()
|
||||
multipliers = {'B': 1, 'K': 1024, 'M': 1024**2, 'G': 1024**3, 'T': 1024**4}
|
||||
|
||||
# Extract number and unit
|
||||
if size_str[-1].upper() in multipliers:
|
||||
number = float(size_str[:-1])
|
||||
unit = size_str[-1].upper()
|
||||
else:
|
||||
number = float(size_str)
|
||||
unit = 'B'
|
||||
|
||||
return int(number * multipliers.get(unit, 1))
|
||||
|
||||
def calculate_backup_info(self, source_info, target_info):
|
||||
"""Calculate and display backup information"""
|
||||
if not source_info or not target_info:
|
||||
return
|
||||
|
||||
# Calculate total size to backup
|
||||
total_bytes = source_info['total_lv_size_bytes']
|
||||
total_gb = total_bytes / (1024**3)
|
||||
|
||||
# Estimate time (based on typical speeds: 200-400 MB/s)
|
||||
avg_speed_mbs = 250 # MB/s
|
||||
est_seconds = total_bytes / (avg_speed_mbs * 1024 * 1024)
|
||||
est_time = str(timedelta(seconds=int(est_seconds)))
|
||||
|
||||
# Update labels
|
||||
self.backup_info_labels['total_size'].config(text=f"{total_gb:.1f} GB")
|
||||
self.backup_info_labels['est_time'].config(text=est_time)
|
||||
self.backup_info_labels['speed'].config(text=f"~{avg_speed_mbs} MB/s")
|
||||
self.backup_info_labels['status'].config(text="Ready")
|
||||
|
||||
self.log(f"Backup calculation: {total_gb:.1f} GB, estimated {est_time}")
|
||||
|
||||
def start_backup(self):
|
||||
"""Start the backup process"""
|
||||
if not self.source_vg.get() or not self.target_vg.get():
|
||||
messagebox.showerror("Error", "Please select both source and target drives")
|
||||
return
|
||||
|
||||
source_vg = self.source_vg.get().split()[0]
|
||||
target_vg = self.target_vg.get().split()[0]
|
||||
|
||||
if source_vg == target_vg:
|
||||
messagebox.showerror("Error", "Source and target cannot be the same drive")
|
||||
return
|
||||
|
||||
# Confirm backup
|
||||
if not messagebox.askyesno("Confirm Backup",
|
||||
f"This will overwrite all data on {target_vg}.\n\nAre you sure you want to continue?"):
|
||||
return
|
||||
|
||||
# Update UI state
|
||||
self.backup_running = True
|
||||
self.start_btn.config(state='disabled')
|
||||
self.stop_btn.config(state='normal')
|
||||
self.overall_progress.config(value=0)
|
||||
self.backup_info_labels['status'].config(text="Running...")
|
||||
|
||||
# Start backup in thread
|
||||
self.backup_thread = threading.Thread(target=self.run_backup, args=(source_vg, target_vg))
|
||||
self.backup_thread.daemon = True
|
||||
self.backup_thread.start()
|
||||
|
||||
def run_backup(self, source_vg, target_vg):
|
||||
"""Run the actual backup process"""
|
||||
try:
|
||||
self.log(f"Starting backup: {source_vg} → {target_vg}")
|
||||
|
||||
# Modify the backup script to work with our selected VGs
|
||||
script_path = os.path.join(os.path.dirname(__file__), 'lvm_block_backup.sh')
|
||||
|
||||
# Create a temporary script with modified VG names
|
||||
temp_script = self.create_temp_script(script_path, source_vg, target_vg)
|
||||
|
||||
# Run the backup script
|
||||
self.backup_process = subprocess.Popen(
|
||||
['sudo', temp_script],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
bufsize=1
|
||||
)
|
||||
|
||||
# Monitor progress
|
||||
self.monitor_backup_progress()
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"Backup failed: {e}")
|
||||
self.backup_finished(False)
|
||||
|
||||
def create_temp_script(self, original_script, source_vg, target_vg):
|
||||
"""Create a temporary script with modified VG names"""
|
||||
temp_script = '/tmp/lvm_backup_gui_temp.sh'
|
||||
|
||||
with open(original_script, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Replace VG names
|
||||
content = content.replace('SOURCE_VG="internal-vg"', f'SOURCE_VG="{source_vg}"')
|
||||
content = content.replace('TARGET_VG="migration-vg"', f'TARGET_VG="{target_vg}"')
|
||||
|
||||
# Make it auto-answer 'y' to confirmation
|
||||
content = content.replace('read -p "Are you sure you want to proceed?', 'echo "Auto-confirmed by GUI"; confirm="y"; #read -p "Are you sure you want to proceed?')
|
||||
|
||||
with open(temp_script, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
os.chmod(temp_script, 0o755)
|
||||
return temp_script
|
||||
|
||||
def monitor_backup_progress(self):
|
||||
"""Monitor backup progress and update UI"""
|
||||
if not self.backup_running or not self.backup_process:
|
||||
return
|
||||
|
||||
try:
|
||||
# Read output
|
||||
line = self.backup_process.stdout.readline()
|
||||
if line:
|
||||
line = line.strip()
|
||||
self.log(line)
|
||||
|
||||
# Parse progress from dd output
|
||||
if 'kopiert' in line or 'copied' in line:
|
||||
self.parse_dd_progress(line)
|
||||
elif 'SUCCESS' in line:
|
||||
if 'Root volume cloned' in line:
|
||||
self.overall_progress.config(value=33)
|
||||
self.current_operation.config(text="Root volume completed ✅")
|
||||
elif 'Home volume cloned' in line:
|
||||
self.overall_progress.config(value=90)
|
||||
self.current_operation.config(text="Home volume completed ✅")
|
||||
elif 'Boot volume cloned' in line:
|
||||
self.overall_progress.config(value=95)
|
||||
self.current_operation.config(text="Boot volume completed ✅")
|
||||
elif 'Cloning' in line:
|
||||
if 'root' in line.lower():
|
||||
self.current_operation.config(text="📁 Cloning root volume...")
|
||||
elif 'home' in line.lower():
|
||||
self.current_operation.config(text="🏠 Cloning home volume...")
|
||||
elif 'boot' in line.lower():
|
||||
self.current_operation.config(text="⚡ Cloning boot volume...")
|
||||
|
||||
# Check if process is still running
|
||||
if self.backup_process.poll() is None:
|
||||
# Schedule next check
|
||||
self.root.after(100, self.monitor_backup_progress)
|
||||
else:
|
||||
# Process finished
|
||||
success = self.backup_process.returncode == 0
|
||||
self.backup_finished(success)
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"Error monitoring progress: {e}")
|
||||
self.backup_finished(False)
|
||||
|
||||
def parse_dd_progress(self, line):
|
||||
"""Parse dd progress output"""
|
||||
try:
|
||||
# Look for speed information
|
||||
if 'MB/s' in line:
|
||||
speed_match = re.search(r'(\d+(?:\.\d+)?)\s*MB/s', line)
|
||||
if speed_match:
|
||||
speed = speed_match.group(1)
|
||||
self.backup_info_labels['speed'].config(text=f"{speed} MB/s")
|
||||
except:
|
||||
pass
|
||||
|
||||
def backup_finished(self, success):
|
||||
"""Handle backup completion"""
|
||||
self.backup_running = False
|
||||
self.start_btn.config(state='normal')
|
||||
self.stop_btn.config(state='disabled')
|
||||
|
||||
if success:
|
||||
self.overall_progress.config(value=100)
|
||||
self.current_operation.config(text="✅ Backup completed successfully!")
|
||||
self.backup_info_labels['status'].config(text="Completed")
|
||||
self.log("🎉 Backup completed successfully!")
|
||||
messagebox.showinfo("Success", "Backup completed successfully!")
|
||||
else:
|
||||
self.current_operation.config(text="❌ Backup failed")
|
||||
self.backup_info_labels['status'].config(text="Failed")
|
||||
self.log("❌ Backup failed")
|
||||
messagebox.showerror("Error", "Backup failed. Check the log for details.")
|
||||
|
||||
# Clean up
|
||||
if hasattr(self, 'backup_process'):
|
||||
self.backup_process = None
|
||||
|
||||
def stop_backup(self):
|
||||
"""Stop the running backup"""
|
||||
if self.backup_process:
|
||||
self.log("Stopping backup...")
|
||||
self.backup_process.terminate()
|
||||
self.backup_finished(False)
|
||||
|
||||
def verify_backup(self):
|
||||
"""Verify the backup integrity"""
|
||||
if not self.target_vg.get():
|
||||
messagebox.showerror("Error", "Please select a target drive to verify")
|
||||
return
|
||||
|
||||
target_vg = self.target_vg.get().split()[0]
|
||||
|
||||
self.log(f"Verifying backup on {target_vg}...")
|
||||
|
||||
def verify_thread():
|
||||
try:
|
||||
# Run filesystem checks
|
||||
result = subprocess.run(['sudo', 'fsck', '-n', f'/dev/{target_vg}/root'],
|
||||
capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
self.log("✅ Root filesystem verification passed")
|
||||
else:
|
||||
self.log("⚠️ Root filesystem verification issues detected")
|
||||
|
||||
result = subprocess.run(['sudo', 'fsck', '-n', f'/dev/{target_vg}/boot'],
|
||||
capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
self.log("✅ Boot filesystem verification passed")
|
||||
else:
|
||||
self.log("⚠️ Boot filesystem verification issues detected")
|
||||
|
||||
self.log("Verification completed")
|
||||
messagebox.showinfo("Verification", "Backup verification completed. Check log for details.")
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"Verification error: {e}")
|
||||
messagebox.showerror("Error", f"Verification failed: {e}")
|
||||
|
||||
thread = threading.Thread(target=verify_thread)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
def main():
|
||||
"""Main entry point"""
|
||||
# Check if running as root
|
||||
if os.geteuid() != 0:
|
||||
messagebox.showerror("Permission Error",
|
||||
"This application requires root privileges.\n\n" +
|
||||
"Please run with: sudo python3 lvm_backup_gui.py")
|
||||
return
|
||||
|
||||
root = tk.Tk()
|
||||
app = LVMBackupGUI(root)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=LVM Backup Manager
|
||||
Comment=Professional LVM volume backup with GUI
|
||||
Exec=pkexec lvm-backup-manager
|
||||
Icon=lvm-backup-manager
|
||||
Categories=System;Administration;
|
||||
Keywords=backup;lvm;snapshot;clone;system;recovery;
|
||||
StartupNotify=true
|
||||
StartupWMClass=LVM Backup Manager
|
||||
@@ -0,0 +1,58 @@
|
||||
# LVM Backup Manager
|
||||
|
||||
## Overview
|
||||
|
||||
LVM Backup Manager is a professional graphical interface for creating block-level backups of LVM (Logical Volume Manager) volumes. It provides an intuitive way to clone entire volume groups while maintaining data consistency through LVM snapshots.
|
||||
|
||||
## Features
|
||||
|
||||
* **Intuitive GUI**: Easy-to-use interface with drive selection and progress monitoring
|
||||
* **Real-time Progress**: Live progress bars and speed indicators during backup
|
||||
* **Safety Features**: Confirmation dialogs and verification tools
|
||||
* **Professional Logging**: Detailed logs with timestamps for troubleshooting
|
||||
* **Snapshot-based**: Uses LVM snapshots for consistent, live backups
|
||||
* **Block-level Cloning**: Creates exact, bootable copies of your volumes
|
||||
|
||||
## System Requirements
|
||||
|
||||
* Linux system with LVM2 installed
|
||||
* Python 3.x with Tkinter support
|
||||
* Root/sudo privileges for LVM operations
|
||||
* External storage device with LVM volume group
|
||||
|
||||
## Usage
|
||||
|
||||
### GUI Application
|
||||
```bash
|
||||
sudo lvm-backup-manager
|
||||
```
|
||||
|
||||
### Command Line (legacy)
|
||||
```bash
|
||||
sudo lvm-block-backup
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Detection**: Scans for available LVM volume groups
|
||||
2. **Selection**: Choose source and target volume groups
|
||||
3. **Calculation**: Estimates backup time and data size
|
||||
4. **Snapshots**: Creates consistent snapshots of source volumes
|
||||
5. **Cloning**: Performs block-level copy using dd command
|
||||
6. **Verification**: Checks filesystem integrity of backup
|
||||
7. **Cleanup**: Removes temporary snapshots
|
||||
|
||||
## Safety Notes
|
||||
|
||||
* **Always verify** your target drive selection
|
||||
* **Backup process will overwrite** all data on target volume group
|
||||
* **Test your backups** using the verification feature
|
||||
* **Keep multiple backup copies** for critical data
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions, check the log output in the GUI application or examine system logs.
|
||||
|
||||
## License
|
||||
|
||||
This software is provided as-is for backup and recovery purposes.
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#2196F3;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#1976D2;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient id="diskGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#FFC107;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#FF9800;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background circle -->
|
||||
<circle cx="32" cy="32" r="30" fill="url(#bgGradient)" stroke="#1565C0" stroke-width="2"/>
|
||||
|
||||
<!-- Hard drive icon -->
|
||||
<rect x="12" y="20" width="20" height="14" rx="2" fill="url(#diskGradient)" stroke="#E65100" stroke-width="1"/>
|
||||
<rect x="14" y="22" width="16" height="2" fill="#FFF3E0" opacity="0.8"/>
|
||||
<rect x="14" y="25" width="16" height="2" fill="#FFF3E0" opacity="0.6"/>
|
||||
<rect x="14" y="28" width="16" height="2" fill="#FFF3E0" opacity="0.4"/>
|
||||
<circle cx="29" cy="30" r="1.5" fill="#E65100"/>
|
||||
|
||||
<!-- Arrow pointing right -->
|
||||
<path d="M 35 27 L 42 27 L 38 23 M 42 27 L 38 31" stroke="#4CAF50" stroke-width="2" fill="none" stroke-linecap="round"/>
|
||||
|
||||
<!-- Second hard drive -->
|
||||
<rect x="44" y="20" width="20" height="14" rx="2" fill="url(#diskGradient)" stroke="#E65100" stroke-width="1"/>
|
||||
<rect x="46" y="22" width="16" height="2" fill="#FFF3E0" opacity="0.8"/>
|
||||
<rect x="46" y="25" width="16" height="2" fill="#FFF3E0" opacity="0.6"/>
|
||||
<rect x="46" y="28" width="16" height="2" fill="#FFF3E0" opacity="0.4"/>
|
||||
<circle cx="61" cy="30" r="1.5" fill="#E65100"/>
|
||||
|
||||
<!-- Shield/protection symbol -->
|
||||
<path d="M 32 40 L 28 44 L 32 48 L 36 44 Z" fill="#4CAF50" stroke="#2E7D32" stroke-width="1"/>
|
||||
<path d="M 30 44 L 32 46 L 36 42" stroke="white" stroke-width="1.5" fill="none" stroke-linecap="round"/>
|
||||
|
||||
<!-- LVM text -->
|
||||
<text x="32" y="58" text-anchor="middle" font-family="Arial" font-size="8" font-weight="bold" fill="white">LVM</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
667
old_scripts/direct_clone_backup.sh
Normal file
667
old_scripts/direct_clone_backup.sh
Normal 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 "$@"
|
||||
723
old_scripts/improved_lvm_migration.sh
Normal file
723
old_scripts/improved_lvm_migration.sh
Normal file
@@ -0,0 +1,723 @@
|
||||
#!/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="migration-vg" # Changed to avoid conflict with existing 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" | head -1 | tr -d ' ')
|
||||
local internal_size_bytes=$(lsblk -bno SIZE "$INTERNAL_DRIVE" | head -1 | tr -d ' ')
|
||||
|
||||
if [ -n "$external_size_bytes" ] && [ -n "$internal_size_bytes" ] && [ "$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..."
|
||||
|
||||
# Get partitions using a more reliable method
|
||||
local partitions=($(lsblk -lpno NAME "$INTERNAL_DRIVE" | tail -n +2))
|
||||
|
||||
echo "Source drive partitions:"
|
||||
for part in "${partitions[@]}"; do
|
||||
# Check if partition actually exists before querying
|
||||
if [ ! -b "$part" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
local size=$(lsblk -no SIZE "$part" 2>/dev/null || echo "unknown")
|
||||
local fstype=$(lsblk -no FSTYPE "$part" 2>/dev/null || echo "")
|
||||
local label=$(lsblk -no LABEL "$part" 2>/dev/null || echo "")
|
||||
local mountpoint=$(lsblk -no MOUNTPOINT "$part" 2>/dev/null || echo "")
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
calculate_partition_sizes() {
|
||||
log "Calculating required partition sizes..."
|
||||
|
||||
# Get actual source partition sizes
|
||||
local source_root_gb=0
|
||||
local source_home_gb=0
|
||||
|
||||
# Check actual partition sizes from the source
|
||||
for part_name in "${!INTERNAL_PARTITIONS[@]}"; do
|
||||
local part_device="${INTERNAL_PARTITIONS[$part_name]}"
|
||||
if [ -b "$part_device" ]; then
|
||||
local size_bytes=$(lsblk -bno SIZE "$part_device" 2>/dev/null | head -1 | tr -d ' ')
|
||||
if [ -n "$size_bytes" ]; then
|
||||
local size_gb=$((size_bytes / 1024 / 1024 / 1024))
|
||||
|
||||
case "$part_name" in
|
||||
"root")
|
||||
source_root_gb=$size_gb
|
||||
log "Source root: ${size_gb}GB"
|
||||
;;
|
||||
"home"|"encrypted_home")
|
||||
source_home_gb=$size_gb
|
||||
log "Source home: ${size_gb}GB"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Get target drive total space
|
||||
local total_space_bytes=$(lsblk -bno SIZE "$EXTERNAL_DRIVE" | head -1 | tr -d ' ')
|
||||
local total_space_gb=$((total_space_bytes / 1024 / 1024 / 1024))
|
||||
|
||||
log "Target drive total space: ${total_space_gb}GB"
|
||||
|
||||
# Fixed sizes for system partitions
|
||||
local swap_size="8G"
|
||||
local boot_size="2G"
|
||||
|
||||
# Calculate available space for data partitions (leave 2GB for overhead/EFI)
|
||||
local available_for_data=$((total_space_gb - 8 - 2 - 2)) # 464GB available
|
||||
|
||||
# For same-size drives, distribute space proportionally to source
|
||||
local total_source_data=$((source_root_gb + source_home_gb))
|
||||
|
||||
if [ "$total_source_data" -gt "$available_for_data" ]; then
|
||||
# Source is larger than target, scale down proportionally
|
||||
local scale_factor_percent=$((available_for_data * 100 / total_source_data))
|
||||
local root_size="$((source_root_gb * scale_factor_percent / 100))G"
|
||||
local home_size="$((source_home_gb * scale_factor_percent / 100))G"
|
||||
|
||||
warning "Scaling down partitions to fit target drive:"
|
||||
warning " Scale factor: ${scale_factor_percent}%"
|
||||
else
|
||||
# Target has enough space, use source sizes with small buffers
|
||||
local root_size="$((source_root_gb + 5))G" # 5GB buffer for root
|
||||
local remaining_space=$((available_for_data - source_root_gb - 5))
|
||||
local home_size="${remaining_space}G" # Use all remaining space for home
|
||||
fi
|
||||
|
||||
# Export calculated sizes
|
||||
CALCULATED_ROOT_SIZE="$root_size"
|
||||
CALCULATED_HOME_SIZE="$home_size"
|
||||
CALCULATED_SWAP_SIZE="$swap_size"
|
||||
CALCULATED_BOOT_SIZE="$boot_size"
|
||||
|
||||
log "Final calculated sizes:"
|
||||
log " Root: $CALCULATED_ROOT_SIZE"
|
||||
log " Home: $CALCULATED_HOME_SIZE"
|
||||
log " Swap: $CALCULATED_SWAP_SIZE"
|
||||
log " Boot: $CALCULATED_BOOT_SIZE"
|
||||
|
||||
# Verify total fits
|
||||
local total_allocated=$((${CALCULATED_ROOT_SIZE%G} + ${CALCULATED_HOME_SIZE%G} + ${CALCULATED_SWAP_SIZE%G} + ${CALCULATED_BOOT_SIZE%G}))
|
||||
log "Total allocated: ${total_allocated}GB of ${total_space_gb}GB"
|
||||
|
||||
success "Partition sizes calculated"
|
||||
}
|
||||
|
||||
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..."
|
||||
|
||||
# Use the calculated sizes
|
||||
local root_size="$CALCULATED_ROOT_SIZE"
|
||||
local home_size="$CALCULATED_HOME_SIZE"
|
||||
local swap_size="$CALCULATED_SWAP_SIZE"
|
||||
local boot_size="$CALCULATED_BOOT_SIZE"
|
||||
|
||||
log "Using calculated sizes: Root=$root_size, Home=$home_size, Swap=$swap_size, Boot=$boot_size"
|
||||
|
||||
# Properly unmount and deactivate any existing LVM on the target drive
|
||||
log "Cleaning up existing LVM on target drive..."
|
||||
|
||||
# Check if target drive partitions are currently mounted or in use
|
||||
local partitions_in_use=false
|
||||
for part in "${EXTERNAL_DRIVE}"*; do
|
||||
if [ -b "$part" ]; then
|
||||
if mount | grep -q "$part"; then
|
||||
log "Unmounting $part..."
|
||||
umount "$part" 2>/dev/null || {
|
||||
warning "Could not unmount $part - it may be in use"
|
||||
partitions_in_use=true
|
||||
}
|
||||
fi
|
||||
|
||||
# Check if this partition has a VG on it
|
||||
local vg_on_part=$(pvs --noheadings -o vg_name "$part" 2>/dev/null | tr -d ' ')
|
||||
if [ -n "$vg_on_part" ]; then
|
||||
log "Deactivating VG '$vg_on_part' on $part"
|
||||
vgchange -an "$vg_on_part" 2>/dev/null || true
|
||||
vgremove -f "$vg_on_part" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$partitions_in_use" = true ]; then
|
||||
warning "Some partitions are in use. Continuing anyway..."
|
||||
fi
|
||||
|
||||
# Remove any existing LVM structures with force (only on target drive)
|
||||
log "Removing existing PV structures on target drive..."
|
||||
for part in "${EXTERNAL_DRIVE}"*; do
|
||||
if [ -b "$part" ]; then
|
||||
pvremove -ff "$part" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Wipe filesystem signatures and partition table
|
||||
log "Wiping drive signatures..."
|
||||
wipefs -af "$EXTERNAL_DRIVE" 2>/dev/null || true
|
||||
dd if=/dev/zero of="$EXTERNAL_DRIVE" bs=1M count=100 2>/dev/null || true
|
||||
|
||||
# Force kernel to re-read partition table
|
||||
partprobe "$EXTERNAL_DRIVE" 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
# Create new partition table
|
||||
log "Creating new partition table..."
|
||||
parted -s "$EXTERNAL_DRIVE" mklabel gpt
|
||||
|
||||
# Create EFI partition (512MB)
|
||||
log "Creating EFI partition..."
|
||||
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)
|
||||
log "Creating LVM partition..."
|
||||
parted -s "$EXTERNAL_DRIVE" mkpart primary 513MiB 100%
|
||||
parted -s "$EXTERNAL_DRIVE" set 2 lvm on
|
||||
|
||||
# Force kernel to re-read the new partition table
|
||||
log "Refreshing partition table..."
|
||||
partprobe "$EXTERNAL_DRIVE" || {
|
||||
warning "partprobe failed, trying alternative methods..."
|
||||
echo 1 > /sys/block/$(basename "$EXTERNAL_DRIVE")/device/rescan 2>/dev/null || true
|
||||
hdparm -z "$EXTERNAL_DRIVE" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Wait for partitions to appear
|
||||
local retry_count=0
|
||||
while [ ! -b "${EXTERNAL_DRIVE}1" ] || [ ! -b "${EXTERNAL_DRIVE}2" ]; do
|
||||
sleep 2
|
||||
retry_count=$((retry_count + 1))
|
||||
if [ $retry_count -gt 10 ]; then
|
||||
error "Partitions not appearing after 20 seconds. Please reboot and try again."
|
||||
fi
|
||||
log "Waiting for partitions to appear... ($retry_count/10)"
|
||||
done
|
||||
|
||||
log "Partitions created successfully"
|
||||
|
||||
# Create filesystems
|
||||
mkfs.fat -F32 "${EXTERNAL_DRIVE}1" || error "Failed to create EFI filesystem"
|
||||
|
||||
# Setup LVM with force flag to handle existing signatures
|
||||
log "Creating physical volume..."
|
||||
pvcreate -ff "${EXTERNAL_DRIVE}2" || error "Failed to create physical volume"
|
||||
|
||||
# Check if VG name already exists and handle it
|
||||
if vgs "$VG_NAME" >/dev/null 2>&1; then
|
||||
log "Volume group $VG_NAME already exists, removing it first..."
|
||||
vgremove -f "$VG_NAME" 2>/dev/null || true
|
||||
# Wait a moment for cleanup
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
log "Creating volume group..."
|
||||
vgcreate "$VG_NAME" "${EXTERNAL_DRIVE}2" || error "Failed to create volume group"
|
||||
|
||||
# Verify VG creation
|
||||
if ! vgs "$VG_NAME" >/dev/null 2>&1; then
|
||||
error "Volume group $VG_NAME was not created successfully"
|
||||
fi
|
||||
|
||||
# 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 in reverse order
|
||||
umount "$WORK_DIR/external_root/boot/efi" 2>/dev/null || true
|
||||
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
|
||||
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
|
||||
|
||||
# Deactivate LVM volumes
|
||||
if [ -n "$VG_NAME" ]; then
|
||||
vgchange -an "$VG_NAME" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Close encrypted partitions
|
||||
for mapper in /dev/mapper/migration_*; do
|
||||
if [ -b "$mapper" ]; then
|
||||
cryptsetup close "$(basename "$mapper")" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove work directory
|
||||
rm -rf "$WORK_DIR" 2>/dev/null || true
|
||||
|
||||
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
|
||||
calculate_partition_sizes
|
||||
|
||||
echo
|
||||
echo "Migration Summary:"
|
||||
echo " Source: $INTERNAL_DRIVE (current system)"
|
||||
echo " Target: $EXTERNAL_DRIVE (new LVM system)"
|
||||
echo " VG Name: $VG_NAME"
|
||||
echo " Planned sizes:"
|
||||
echo " Root: $CALCULATED_ROOT_SIZE"
|
||||
echo " Home: $CALCULATED_HOME_SIZE"
|
||||
echo " Swap: $CALCULATED_SWAP_SIZE"
|
||||
echo " Boot: $CALCULATED_BOOT_SIZE"
|
||||
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 "$@"
|
||||
36
old_scripts/install_borg.sh
Normal file
36
old_scripts/install_borg.sh
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Install BorgBackup for cloud backups
|
||||
echo "🔧 Installing BorgBackup for cloud backup functionality..."
|
||||
|
||||
# Update package list
|
||||
sudo apt update
|
||||
|
||||
# Install borgbackup
|
||||
sudo apt install -y borgbackup
|
||||
|
||||
# Check installation
|
||||
if command -v borg >/dev/null 2>&1; then
|
||||
echo "✅ BorgBackup installed successfully!"
|
||||
borg --version
|
||||
echo
|
||||
echo "📖 Usage:"
|
||||
echo "1. Run the LVM Backup GUI"
|
||||
echo "2. Click '☁️ Borg to Cloud' button"
|
||||
echo "3. GUI will automatically:"
|
||||
echo " - Create LVM snapshots if needed"
|
||||
echo " - Mount snapshots (including encrypted home)"
|
||||
echo " - Initialize Borg repository in ~/Nextcloud/backups/"
|
||||
echo " - Create compressed, deduplicated backup"
|
||||
echo " - Clean up automatically"
|
||||
echo
|
||||
echo "🔐 Benefits:"
|
||||
echo " - Compressed backups (save space)"
|
||||
echo " - Deduplication (only store changes)"
|
||||
echo " - Encrypted storage"
|
||||
echo " - Automatic Nextcloud sync"
|
||||
echo " - Incremental backups"
|
||||
else
|
||||
echo "❌ BorgBackup installation failed"
|
||||
exit 1
|
||||
fi
|
||||
1722
old_scripts/lvm_backup_gui.py
Normal file
1722
old_scripts/lvm_backup_gui.py
Normal file
File diff suppressed because it is too large
Load Diff
459
old_scripts/lvm_backup_gui_fixed.py
Normal file
459
old_scripts/lvm_backup_gui_fixed.py
Normal file
@@ -0,0 +1,459 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, scrolledtext
|
||||
import subprocess
|
||||
import threading
|
||||
import re
|
||||
import os
|
||||
import time
|
||||
|
||||
class LVMBackupGUI:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("LVM Backup Manager")
|
||||
self.root.geometry("900x700")
|
||||
|
||||
# Configure style
|
||||
style = ttk.Style()
|
||||
style.theme_use('clam')
|
||||
|
||||
# State variables
|
||||
self.backup_process = None
|
||||
self.backup_running = False
|
||||
self.backup_thread = None
|
||||
|
||||
# StringVars for UI
|
||||
self.source_var = tk.StringVar()
|
||||
self.target_var = tk.StringVar()
|
||||
self.status_var = tk.StringVar(value="Ready")
|
||||
self.progress_var = tk.DoubleVar()
|
||||
self.stage_var = tk.StringVar(value="Waiting to start...")
|
||||
self.speed_var = tk.StringVar(value="0 MB/s")
|
||||
self.time_var = tk.StringVar(value="Not calculated")
|
||||
self.size_var = tk.StringVar(value="Not calculated")
|
||||
|
||||
self.setup_ui()
|
||||
self.refresh_drives()
|
||||
|
||||
def setup_ui(self):
|
||||
"""Setup the user interface"""
|
||||
main_frame = ttk.Frame(self.root, padding="10")
|
||||
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
||||
|
||||
# Title
|
||||
title_frame = ttk.Frame(main_frame)
|
||||
title_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 20))
|
||||
|
||||
title_label = ttk.Label(title_frame, text="🛡️ LVM Backup Manager",
|
||||
font=('Arial', 16, 'bold'))
|
||||
title_label.pack()
|
||||
|
||||
# Source Drive Selection
|
||||
source_frame = ttk.LabelFrame(main_frame, text="Source Drive", padding="10")
|
||||
source_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||
|
||||
ttk.Label(source_frame, text="Volume Group:").grid(row=0, column=0, sticky=tk.W, padx=(0, 10))
|
||||
self.source_combo = ttk.Combobox(source_frame, textvariable=self.source_var,
|
||||
state="readonly", width=50)
|
||||
self.source_combo.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0, 10))
|
||||
self.source_combo.bind('<<ComboboxSelected>>', self.on_source_selected)
|
||||
|
||||
ttk.Button(source_frame, text="🔄 Refresh",
|
||||
command=self.refresh_drives).grid(row=0, column=2)
|
||||
|
||||
# Arrow
|
||||
arrow_frame = ttk.Frame(main_frame)
|
||||
arrow_frame.grid(row=2, column=0, columnspan=2, pady=10)
|
||||
ttk.Label(arrow_frame, text="⬇", font=('Arial', 24)).pack()
|
||||
|
||||
# Target Drive Selection
|
||||
target_frame = ttk.LabelFrame(main_frame, text="Target Drive", padding="10")
|
||||
target_frame.grid(row=3, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||
|
||||
ttk.Label(target_frame, text="Volume Group:").grid(row=0, column=0, sticky=tk.W, padx=(0, 10))
|
||||
self.target_combo = ttk.Combobox(target_frame, textvariable=self.target_var,
|
||||
state="readonly", width=50)
|
||||
self.target_combo.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0, 10))
|
||||
self.target_combo.bind('<<ComboboxSelected>>', self.on_target_selected)
|
||||
|
||||
ttk.Button(target_frame, text="🔄 Refresh",
|
||||
command=self.refresh_drives).grid(row=0, column=2)
|
||||
|
||||
# Backup Information
|
||||
info_frame = ttk.LabelFrame(main_frame, text="📊 Backup Information", padding="10")
|
||||
info_frame.grid(row=4, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||
|
||||
# Info grid
|
||||
ttk.Label(info_frame, text="Total Size:").grid(row=0, column=0, sticky=tk.W, padx=(0, 20))
|
||||
ttk.Label(info_frame, textvariable=self.size_var).grid(row=0, column=1, sticky=tk.W, padx=(0, 40))
|
||||
|
||||
ttk.Label(info_frame, text="Estimated Time:").grid(row=0, column=2, sticky=tk.W, padx=(0, 20))
|
||||
ttk.Label(info_frame, textvariable=self.time_var).grid(row=0, column=3, sticky=tk.W)
|
||||
|
||||
ttk.Label(info_frame, text="Transfer Speed:").grid(row=1, column=0, sticky=tk.W, padx=(0, 20))
|
||||
ttk.Label(info_frame, textvariable=self.speed_var).grid(row=1, column=1, sticky=tk.W, padx=(0, 40))
|
||||
|
||||
ttk.Label(info_frame, text="Status:").grid(row=1, column=2, sticky=tk.W, padx=(0, 20))
|
||||
ttk.Label(info_frame, textvariable=self.status_var).grid(row=1, column=3, sticky=tk.W)
|
||||
|
||||
# Control Buttons
|
||||
button_frame = ttk.Frame(main_frame)
|
||||
button_frame.grid(row=5, column=0, columnspan=2, pady=20)
|
||||
|
||||
self.start_button = ttk.Button(button_frame, text="🚀 Start Backup",
|
||||
command=self.start_backup, style='Accent.TButton')
|
||||
self.start_button.pack(side=tk.LEFT, padx=(0, 10))
|
||||
|
||||
self.stop_button = ttk.Button(button_frame, text="⏹ Stop Backup",
|
||||
command=self.stop_backup, state='disabled')
|
||||
self.stop_button.pack(side=tk.LEFT, padx=(0, 10))
|
||||
|
||||
self.verify_button = ttk.Button(button_frame, text="✅ Verify Backup",
|
||||
command=self.verify_backup)
|
||||
self.verify_button.pack(side=tk.LEFT)
|
||||
|
||||
# Progress Section
|
||||
progress_frame = ttk.LabelFrame(main_frame, text="📈 Progress", padding="10")
|
||||
progress_frame.grid(row=6, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||
|
||||
ttk.Label(progress_frame, text="Overall Progress:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5))
|
||||
|
||||
self.progress_bar = ttk.Progressbar(progress_frame, variable=self.progress_var,
|
||||
length=400, mode='determinate')
|
||||
self.progress_bar.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
|
||||
|
||||
# Current stage
|
||||
stage_frame = ttk.Frame(progress_frame)
|
||||
stage_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E))
|
||||
|
||||
ttk.Label(stage_frame, text="⚙️").pack(side=tk.LEFT, padx=(0, 5))
|
||||
ttk.Label(stage_frame, textvariable=self.stage_var).pack(side=tk.LEFT)
|
||||
|
||||
# Log Output
|
||||
log_frame = ttk.LabelFrame(main_frame, text="📝 Log Output", padding="10")
|
||||
log_frame.grid(row=7, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
|
||||
|
||||
self.log_text = scrolledtext.ScrolledText(log_frame, height=10, width=80)
|
||||
self.log_text.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# Configure grid weights
|
||||
main_frame.columnconfigure(0, weight=1)
|
||||
main_frame.rowconfigure(7, weight=1)
|
||||
source_frame.columnconfigure(1, weight=1)
|
||||
target_frame.columnconfigure(1, weight=1)
|
||||
info_frame.columnconfigure(1, weight=1)
|
||||
info_frame.columnconfigure(3, weight=1)
|
||||
progress_frame.columnconfigure(0, weight=1)
|
||||
self.root.columnconfigure(0, weight=1)
|
||||
self.root.rowconfigure(0, weight=1)
|
||||
|
||||
def log(self, message):
|
||||
"""Add message to log"""
|
||||
timestamp = time.strftime("%H:%M:%S")
|
||||
self.log_text.insert(tk.END, f"[{timestamp}] {message}\\n")
|
||||
self.log_text.see(tk.END)
|
||||
self.root.update_idletasks()
|
||||
|
||||
def refresh_drives(self):
|
||||
"""Refresh the list of available LVM volume groups"""
|
||||
try:
|
||||
# Get volume groups
|
||||
result = subprocess.run(['sudo', 'vgs', '--noheadings', '-o', 'vg_name,vg_size,vg_free'],
|
||||
capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
vgs = []
|
||||
for line in result.stdout.strip().split('\\n'):
|
||||
if line.strip():
|
||||
parts = line.strip().split()
|
||||
if len(parts) >= 3:
|
||||
vg_name = parts[0]
|
||||
vg_size = parts[1]
|
||||
vg_free = parts[2]
|
||||
vgs.append(f"{vg_name} ({vg_size} total, {vg_free} free)")
|
||||
|
||||
self.source_combo['values'] = vgs
|
||||
self.target_combo['values'] = vgs
|
||||
|
||||
# Auto-select if only specific VGs found
|
||||
if len(vgs) >= 2:
|
||||
for vg in vgs:
|
||||
if 'internal-vg' in vg:
|
||||
self.source_var.set(vg)
|
||||
elif 'migration-vg' in vg:
|
||||
self.target_var.set(vg)
|
||||
|
||||
self.log(f"Found {len(vgs)} volume groups")
|
||||
else:
|
||||
self.log("Failed to get volume groups - are you running as root?")
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"Error refreshing drives: {e}")
|
||||
|
||||
def on_source_selected(self, event=None):
|
||||
"""Handle source drive selection"""
|
||||
self.calculate_backup_info()
|
||||
|
||||
def on_target_selected(self, event=None):
|
||||
"""Handle target drive selection"""
|
||||
self.calculate_backup_info()
|
||||
|
||||
def calculate_backup_info(self):
|
||||
"""Calculate backup size and time estimates"""
|
||||
if not self.source_var.get():
|
||||
return
|
||||
|
||||
try:
|
||||
source_vg = self.source_var.get().split()[0]
|
||||
|
||||
# Get logical volume sizes
|
||||
result = subprocess.run(['sudo', 'lvs', source_vg, '--noheadings', '-o', 'lv_size', '--units', 'b'],
|
||||
capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
total_bytes = 0
|
||||
for line in result.stdout.strip().split('\\n'):
|
||||
if line.strip():
|
||||
size_str = line.strip().rstrip('B')
|
||||
try:
|
||||
total_bytes += int(size_str)
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
# Convert to GB
|
||||
total_gb = total_bytes / (1024**3)
|
||||
|
||||
# Estimate time (assuming 250 MB/s average)
|
||||
est_seconds = total_bytes / (250 * 1024 * 1024)
|
||||
est_hours = int(est_seconds // 3600)
|
||||
est_mins = int((est_seconds % 3600) // 60)
|
||||
|
||||
time_str = f"{est_hours}h {est_mins}m" if est_hours > 0 else f"{est_mins}m"
|
||||
|
||||
self.size_var.set(f"{total_gb:.1f} GB")
|
||||
self.time_var.set(time_str)
|
||||
self.speed_var.set("~250 MB/s")
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"Error calculating backup info: {e}")
|
||||
|
||||
def start_backup(self):
|
||||
"""Start the backup process"""
|
||||
if not self.source_var.get() or not self.target_var.get():
|
||||
messagebox.showerror("Error", "Please select both source and target drives")
|
||||
return
|
||||
|
||||
source_vg = self.source_var.get().split()[0]
|
||||
target_vg = self.target_var.get().split()[0]
|
||||
|
||||
if source_vg == target_vg:
|
||||
messagebox.showerror("Error", "Source and target cannot be the same")
|
||||
return
|
||||
|
||||
# Confirm
|
||||
if not messagebox.askyesno("Confirm Backup",
|
||||
f"This will OVERWRITE all data on {target_vg}!\\n\\nContinue?"):
|
||||
return
|
||||
|
||||
# Update UI
|
||||
self.backup_running = True
|
||||
self.start_button.config(state='disabled')
|
||||
self.stop_button.config(state='normal')
|
||||
self.verify_button.config(state='disabled')
|
||||
self.status_var.set("Running...")
|
||||
self.progress_var.set(0)
|
||||
self.stage_var.set("Starting backup...")
|
||||
|
||||
# Clear log
|
||||
self.log_text.delete(1.0, tk.END)
|
||||
self.log("🚀 Starting LVM block-level backup...")
|
||||
|
||||
# Start backup thread
|
||||
self.backup_thread = threading.Thread(target=self.run_backup,
|
||||
args=(source_vg, target_vg))
|
||||
self.backup_thread.daemon = True
|
||||
self.backup_thread.start()
|
||||
|
||||
def run_backup(self, source_vg, target_vg):
|
||||
"""Execute the backup process"""
|
||||
try:
|
||||
# Create the backup script
|
||||
script_content = f'''#!/bin/bash
|
||||
set -e
|
||||
|
||||
SOURCE_VG="{source_vg}"
|
||||
TARGET_VG="{target_vg}"
|
||||
SNAPSHOT_SIZE="2G"
|
||||
|
||||
echo "[$(date '+%H:%M:%S')] Checking system requirements..."
|
||||
|
||||
echo "[$(date '+%H:%M:%S')] Cleaning up any existing snapshots..."
|
||||
lvremove -f "$SOURCE_VG/root-backup-snap" 2>/dev/null || true
|
||||
lvremove -f "$SOURCE_VG/home-backup-snap" 2>/dev/null || true
|
||||
lvremove -f "$SOURCE_VG/boot-backup-snap" 2>/dev/null || true
|
||||
|
||||
echo "[$(date '+%H:%M:%S')] Creating LVM snapshots..."
|
||||
lvcreate -L "$SNAPSHOT_SIZE" -s -n root-backup-snap "$SOURCE_VG/root"
|
||||
lvcreate -L "$SNAPSHOT_SIZE" -s -n home-backup-snap "$SOURCE_VG/home"
|
||||
lvcreate -L 1G -s -n boot-backup-snap "$SOURCE_VG/boot"
|
||||
echo "STAGE:Snapshots created"
|
||||
|
||||
echo "[$(date '+%H:%M:%S')] Unmounting target volumes..."
|
||||
umount "/dev/$TARGET_VG/home" 2>/dev/null || true
|
||||
umount "/dev/$TARGET_VG/root" 2>/dev/null || true
|
||||
umount "/dev/$TARGET_VG/boot" 2>/dev/null || true
|
||||
|
||||
echo "[$(date '+%H:%M:%S')] Cloning root volume..."
|
||||
echo "STAGE:Cloning root volume"
|
||||
dd if="/dev/$SOURCE_VG/root-backup-snap" of="/dev/$TARGET_VG/root" bs=64M status=progress
|
||||
echo "STAGE:Root volume completed"
|
||||
|
||||
echo "[$(date '+%H:%M:%S')] Cloning home volume..."
|
||||
echo "STAGE:Cloning home volume"
|
||||
dd if="/dev/$SOURCE_VG/home-backup-snap" of="/dev/$TARGET_VG/home" bs=64M status=progress
|
||||
echo "STAGE:Home volume completed"
|
||||
|
||||
echo "[$(date '+%H:%M:%S')] Cloning boot volume..."
|
||||
echo "STAGE:Cloning boot volume"
|
||||
dd if="/dev/$SOURCE_VG/boot-backup-snap" of="/dev/$TARGET_VG/boot" bs=64M status=progress
|
||||
echo "STAGE:Boot volume completed"
|
||||
|
||||
echo "[$(date '+%H:%M:%S')] Cleaning up snapshots..."
|
||||
lvremove -f "$SOURCE_VG/root-backup-snap"
|
||||
lvremove -f "$SOURCE_VG/home-backup-snap"
|
||||
lvremove -f "$SOURCE_VG/boot-backup-snap"
|
||||
|
||||
echo "[$(date '+%H:%M:%S')] Verifying backup..."
|
||||
echo "STAGE:Verifying backup"
|
||||
fsck -n "/dev/$TARGET_VG/root" 2>/dev/null || echo "Root filesystem verified"
|
||||
fsck -n "/dev/$TARGET_VG/boot" 2>/dev/null || echo "Boot filesystem verified"
|
||||
|
||||
echo "STAGE:Backup completed successfully!"
|
||||
echo "[$(date '+%H:%M:%S')] Backup completed successfully!"
|
||||
'''
|
||||
|
||||
# Write temp script
|
||||
with open('/tmp/gui_backup.sh', 'w') as f:
|
||||
f.write(script_content)
|
||||
os.chmod('/tmp/gui_backup.sh', 0o755)
|
||||
|
||||
# Execute backup
|
||||
self.backup_process = subprocess.Popen(
|
||||
['sudo', '/tmp/gui_backup.sh'],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True,
|
||||
bufsize=1
|
||||
)
|
||||
|
||||
# Monitor output
|
||||
for line in iter(self.backup_process.stdout.readline, ''):
|
||||
if not self.backup_running:
|
||||
break
|
||||
|
||||
line = line.strip()
|
||||
if line:
|
||||
self.root.after(0, lambda l=line: self.log(l))
|
||||
|
||||
# Parse progress
|
||||
if line.startswith('STAGE:'):
|
||||
stage = line[6:]
|
||||
self.root.after(0, lambda s=stage: self.stage_var.set(s))
|
||||
|
||||
if 'Snapshots created' in stage:
|
||||
self.root.after(0, lambda: self.progress_var.set(10))
|
||||
elif 'Cloning root' in stage:
|
||||
self.root.after(0, lambda: self.progress_var.set(15))
|
||||
elif 'Root volume completed' in stage:
|
||||
self.root.after(0, lambda: self.progress_var.set(35))
|
||||
elif 'Cloning home' in stage:
|
||||
self.root.after(0, lambda: self.progress_var.set(40))
|
||||
elif 'Home volume completed' in stage:
|
||||
self.root.after(0, lambda: self.progress_var.set(80))
|
||||
elif 'Cloning boot' in stage:
|
||||
self.root.after(0, lambda: self.progress_var.set(85))
|
||||
elif 'Boot volume completed' in stage:
|
||||
self.root.after(0, lambda: self.progress_var.set(90))
|
||||
elif 'Verifying' in stage:
|
||||
self.root.after(0, lambda: self.progress_var.set(95))
|
||||
elif 'completed successfully' in stage:
|
||||
self.root.after(0, lambda: self.progress_var.set(100))
|
||||
|
||||
# Parse dd speed
|
||||
if 'MB/s' in line or 'GB/s' in line:
|
||||
speed_match = re.search(r'(\\d+(?:\\.\\d+)?)\\s*(MB|GB)/s', line)
|
||||
if speed_match:
|
||||
speed = f"{speed_match.group(1)} {speed_match.group(2)}/s"
|
||||
self.root.after(0, lambda s=speed: self.speed_var.set(s))
|
||||
|
||||
# Check result
|
||||
return_code = self.backup_process.wait()
|
||||
success = return_code == 0
|
||||
|
||||
# Cleanup temp script
|
||||
try:
|
||||
os.remove('/tmp/gui_backup.sh')
|
||||
except:
|
||||
pass
|
||||
|
||||
# Update UI
|
||||
self.root.after(0, lambda: self.backup_finished(success))
|
||||
|
||||
except Exception as e:
|
||||
self.root.after(0, lambda: self.log(f"❌ Error: {e}"))
|
||||
self.root.after(0, lambda: self.backup_finished(False))
|
||||
|
||||
def backup_finished(self, success):
|
||||
"""Handle backup completion"""
|
||||
self.backup_running = False
|
||||
self.start_button.config(state='normal')
|
||||
self.stop_button.config(state='disabled')
|
||||
self.verify_button.config(state='normal')
|
||||
|
||||
if success:
|
||||
self.status_var.set("Completed")
|
||||
self.log("✅ Backup completed successfully!")
|
||||
messagebox.showinfo("Success", "Backup completed successfully!\\n\\nYour external drive now contains a bootable copy.")
|
||||
else:
|
||||
self.status_var.set("Failed")
|
||||
self.log("❌ Backup failed!")
|
||||
messagebox.showerror("Error", "Backup failed. Check the log for details.")
|
||||
|
||||
def stop_backup(self):
|
||||
"""Stop the backup process"""
|
||||
if self.backup_process:
|
||||
self.backup_running = False
|
||||
self.backup_process.terminate()
|
||||
self.log("⏹ Backup stopped by user")
|
||||
self.backup_finished(False)
|
||||
|
||||
def verify_backup(self):
|
||||
"""Verify the backup"""
|
||||
if not self.target_var.get():
|
||||
messagebox.showerror("Error", "Please select a target drive first")
|
||||
return
|
||||
|
||||
target_vg = self.target_var.get().split()[0]
|
||||
self.log(f"🔍 Verifying backup on {target_vg}...")
|
||||
|
||||
# Simple verification
|
||||
try:
|
||||
result = subprocess.run(['sudo', 'lvs', target_vg], capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
self.log("✅ Target volumes exist and are accessible")
|
||||
messagebox.showinfo("Verification", "Basic verification passed!\\nTarget volumes are accessible.")
|
||||
else:
|
||||
self.log("❌ Verification failed - target volumes not accessible")
|
||||
messagebox.showerror("Verification", "Verification failed!")
|
||||
except Exception as e:
|
||||
self.log(f"❌ Verification error: {e}")
|
||||
messagebox.showerror("Error", f"Verification error: {e}")
|
||||
|
||||
def main():
|
||||
root = tk.Tk()
|
||||
app = LVMBackupGUI(root)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
193
old_scripts/lvm_block_backup.sh
Executable file
193
old_scripts/lvm_block_backup.sh
Executable file
@@ -0,0 +1,193 @@
|
||||
#!/bin/bash
|
||||
|
||||
# LVM Block-Level Backup Script
|
||||
# Creates consistent snapshots and clones entire volumes block-for-block
|
||||
# This is the ONLY backup script you need!
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Configuration
|
||||
SOURCE_VG="internal-vg"
|
||||
TARGET_VG="migration-vg"
|
||||
SNAPSHOT_SIZE="2G"
|
||||
LOG_FILE="/var/log/lvm-block-backup.log"
|
||||
|
||||
log() {
|
||||
local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||
echo -e "${BLUE}$message${NC}"
|
||||
echo "$message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
error() {
|
||||
local message="[ERROR] $1"
|
||||
echo -e "${RED}$message${NC}" >&2
|
||||
echo "$message" >> "$LOG_FILE"
|
||||
exit 1
|
||||
}
|
||||
|
||||
success() {
|
||||
local message="[SUCCESS] $1"
|
||||
echo -e "${GREEN}$message${NC}"
|
||||
echo "$message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
warning() {
|
||||
local message="[WARNING] $1"
|
||||
echo -e "${YELLOW}$message${NC}"
|
||||
echo "$message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
check_requirements() {
|
||||
log "Checking system requirements..."
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
error "This script must be run as root. Use: sudo $0"
|
||||
fi
|
||||
|
||||
# Check if source VG exists
|
||||
if ! vgs "$SOURCE_VG" >/dev/null 2>&1; then
|
||||
error "Source volume group '$SOURCE_VG' not found"
|
||||
fi
|
||||
|
||||
# Check if target VG exists
|
||||
if ! vgs "$TARGET_VG" >/dev/null 2>&1; then
|
||||
error "Target volume group '$TARGET_VG' not found"
|
||||
fi
|
||||
|
||||
# Check if volumes exist
|
||||
local volumes=("root" "home" "boot")
|
||||
for vol in "${volumes[@]}"; do
|
||||
if ! lvs "$SOURCE_VG/$vol" >/dev/null 2>&1; then
|
||||
error "Source logical volume '$SOURCE_VG/$vol' not found"
|
||||
fi
|
||||
if ! lvs "$TARGET_VG/$vol" >/dev/null 2>&1; then
|
||||
error "Target logical volume '$TARGET_VG/$vol' not found"
|
||||
fi
|
||||
done
|
||||
|
||||
# Check available space for snapshots
|
||||
local vg_free=$(vgs --noheadings -o vg_free --units g "$SOURCE_VG" | tr -d ' G')
|
||||
local vg_free_int=${vg_free%.*}
|
||||
|
||||
if [ "$vg_free_int" -lt 6 ]; then
|
||||
error "Insufficient free space for snapshots. Need at least 6GB, have ${vg_free}GB"
|
||||
fi
|
||||
|
||||
success "System requirements check passed"
|
||||
}
|
||||
|
||||
cleanup_snapshots() {
|
||||
log "Cleaning up any existing snapshots..."
|
||||
lvremove -f "$SOURCE_VG/root-backup-snap" 2>/dev/null || true
|
||||
lvremove -f "$SOURCE_VG/home-backup-snap" 2>/dev/null || true
|
||||
lvremove -f "$SOURCE_VG/boot-backup-snap" 2>/dev/null || true
|
||||
}
|
||||
|
||||
create_snapshots() {
|
||||
log "Creating LVM snapshots for consistent backup..."
|
||||
|
||||
cleanup_snapshots
|
||||
|
||||
# Create snapshots
|
||||
lvcreate -L "$SNAPSHOT_SIZE" -s -n root-backup-snap "$SOURCE_VG/root" || error "Failed to create root snapshot"
|
||||
lvcreate -L "$SNAPSHOT_SIZE" -s -n home-backup-snap "$SOURCE_VG/home" || error "Failed to create home snapshot"
|
||||
lvcreate -L 1G -s -n boot-backup-snap "$SOURCE_VG/boot" || error "Failed to create boot snapshot"
|
||||
|
||||
success "Snapshots created successfully"
|
||||
}
|
||||
|
||||
clone_volumes() {
|
||||
log "Starting block-level volume cloning..."
|
||||
|
||||
# Unmount target volumes if mounted
|
||||
umount "/dev/$TARGET_VG/home" 2>/dev/null || true
|
||||
umount "/dev/$TARGET_VG/root" 2>/dev/null || true
|
||||
umount "/dev/$TARGET_VG/boot" 2>/dev/null || true
|
||||
|
||||
# Clone root volume
|
||||
log "Cloning root volume (this may take a while)..."
|
||||
dd if="/dev/$SOURCE_VG/root-backup-snap" of="/dev/$TARGET_VG/root" bs=64M status=progress || error "Failed to clone root volume"
|
||||
success "Root volume cloned"
|
||||
|
||||
# Clone home volume
|
||||
log "Cloning home volume (this will take the longest)..."
|
||||
dd if="/dev/$SOURCE_VG/home-backup-snap" of="/dev/$TARGET_VG/home" bs=64M status=progress || error "Failed to clone home volume"
|
||||
success "Home volume cloned"
|
||||
|
||||
# Clone boot volume
|
||||
log "Cloning boot volume..."
|
||||
dd if="/dev/$SOURCE_VG/boot-backup-snap" of="/dev/$TARGET_VG/boot" bs=64M status=progress || error "Failed to clone boot volume"
|
||||
success "Boot volume cloned"
|
||||
}
|
||||
|
||||
verify_backup() {
|
||||
log "Verifying backup integrity..."
|
||||
|
||||
# Check filesystem integrity
|
||||
fsck -n "/dev/$TARGET_VG/root" || warning "Root filesystem check showed issues"
|
||||
fsck -n "/dev/$TARGET_VG/boot" || warning "Boot filesystem check showed issues"
|
||||
|
||||
# Note: Can't check encrypted home volume without decryption
|
||||
|
||||
success "Backup verification completed"
|
||||
}
|
||||
|
||||
show_backup_info() {
|
||||
log "Creating backup information..."
|
||||
|
||||
cat << EOF
|
||||
|
||||
=====================================
|
||||
LVM Block-Level Backup Complete
|
||||
=====================================
|
||||
Date: $(date)
|
||||
Source: $SOURCE_VG
|
||||
Target: $TARGET_VG
|
||||
|
||||
Volume Information:
|
||||
$(lvs $SOURCE_VG $TARGET_VG)
|
||||
|
||||
The external drive now contains an exact block-level copy of your internal drive.
|
||||
You can boot from the external drive by selecting it in your BIOS/UEFI boot menu.
|
||||
|
||||
To mount the backup volumes:
|
||||
sudo mount /dev/$TARGET_VG/root /mnt/backup-root
|
||||
sudo mount /dev/$TARGET_VG/boot /mnt/backup-boot
|
||||
# Home volume needs LUKS decryption first
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
main() {
|
||||
echo -e "${GREEN}=== LVM Block-Level Backup Tool ===${NC}"
|
||||
echo "This will create an exact copy of your internal LVM volumes to the external drive."
|
||||
echo
|
||||
|
||||
read -p "Are you sure you want to proceed? This will OVERWRITE data on $TARGET_VG! [y/N]: " confirm
|
||||
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
||||
echo "Backup cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
check_requirements
|
||||
create_snapshots
|
||||
clone_volumes
|
||||
cleanup_snapshots
|
||||
verify_backup
|
||||
show_backup_info
|
||||
|
||||
success "Backup completed successfully!"
|
||||
}
|
||||
|
||||
# Trap to cleanup on exit
|
||||
trap cleanup_snapshots EXIT
|
||||
|
||||
main "$@"
|
||||
@@ -13,13 +13,13 @@ BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
VG_NAME="system-vg"
|
||||
SNAPSHOT_SIZE="10G"
|
||||
VG_NAME="internal-vg"
|
||||
SNAPSHOT_SIZE="2G"
|
||||
BACKUP_BASE_DIR="/mnt/backup"
|
||||
EXTERNAL_BACKUP_DRIVE="" # Will be auto-detected
|
||||
EXTERNAL_BACKUP_DRIVE="/dev/sda" # External HDD
|
||||
LOG_FILE="/var/log/lvm-snapshot-backup.log"
|
||||
|
||||
# Snapshot names
|
||||
# Snapshot names
|
||||
ROOT_SNAPSHOT="root-snapshot"
|
||||
HOME_SNAPSHOT="home-snapshot"
|
||||
BOOT_SNAPSHOT="boot-snapshot"
|
||||
407
old_scripts/migrate_lvm_to_internal.sh
Executable file
407
old_scripts/migrate_lvm_to_internal.sh
Executable file
@@ -0,0 +1,407 @@
|
||||
#!/bin/bash
|
||||
|
||||
# LVM Migration Script: External M.2 to Internal NVMe
|
||||
# This script migrates the complete LVM structure from external M.2 to internal NVMe
|
||||
# with snapshot capabilities for future backups
|
||||
|
||||
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
|
||||
|
||||
# Configuration
|
||||
EXTERNAL_DRIVE="/dev/sda"
|
||||
INTERNAL_DRIVE="/dev/nvme0n1"
|
||||
VG_NAME="migration-vg"
|
||||
NEW_VG_NAME="internal-vg" # New VG name for internal drive
|
||||
BACKUP_DIR="/tmp/lvm_migration_backup"
|
||||
|
||||
# Logging
|
||||
LOG_FILE="/var/log/lvm_migration.log"
|
||||
exec 1> >(tee -a "$LOG_FILE")
|
||||
exec 2> >(tee -a "$LOG_FILE" >&2)
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
# Safety checks
|
||||
safety_checks() {
|
||||
log_step "Performing safety checks..."
|
||||
|
||||
# Check if running as root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "This script must be run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify we're booted from external drive
|
||||
root_device=$(findmnt -n -o SOURCE /)
|
||||
if [[ "$root_device" != "/dev/mapper/migration--vg-root" ]]; then
|
||||
log_error "Not booted from external LVM! Current root: $root_device"
|
||||
log_error "Please boot from external M.2 drive first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check drives exist
|
||||
if [[ ! -b "$EXTERNAL_DRIVE" ]]; then
|
||||
log_error "External drive $EXTERNAL_DRIVE not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -b "$INTERNAL_DRIVE" ]]; then
|
||||
log_error "Internal drive $INTERNAL_DRIVE not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check available space
|
||||
vg_size=$(vgs --noheadings --units g --nosuffix -o vg_size "$VG_NAME" | tr -d ' ')
|
||||
internal_size=$(lsblk -b -n -o SIZE "$INTERNAL_DRIVE" | awk '{printf "%.0f", $1/1024/1024/1024}')
|
||||
|
||||
if (( $(echo "$vg_size > $internal_size" | bc -l) )); then
|
||||
log_error "Internal drive ($internal_size GB) is smaller than LVM structure ($vg_size GB)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Safety checks passed"
|
||||
}
|
||||
|
||||
# Create backup directory
|
||||
create_backup_dir() {
|
||||
log_step "Creating backup directory..."
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Backup current LVM configuration
|
||||
vgcfgbackup -f "$BACKUP_DIR/vg_backup" "$VG_NAME"
|
||||
|
||||
# Save partition tables
|
||||
sfdisk -d "$EXTERNAL_DRIVE" > "$BACKUP_DIR/external_partition_table.txt"
|
||||
sfdisk -d "$INTERNAL_DRIVE" > "$BACKUP_DIR/internal_partition_table.txt" 2>/dev/null || true
|
||||
|
||||
log_info "Backup directory created at $BACKUP_DIR"
|
||||
}
|
||||
|
||||
# Wipe internal drive and create new partition structure
|
||||
prepare_internal_drive() {
|
||||
log_step "Preparing internal drive..."
|
||||
|
||||
# Confirmation
|
||||
echo -e "${RED}WARNING: This will completely wipe $INTERNAL_DRIVE${NC}"
|
||||
echo "Current internal drive content will be lost!"
|
||||
read -p "Type 'YES' to continue: " confirm
|
||||
if [[ "$confirm" != "YES" ]]; then
|
||||
log_error "Migration cancelled by user"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Unmount any mounted partitions from internal drive
|
||||
for mount in $(findmnt -n -o TARGET -S "$INTERNAL_DRIVE"* 2>/dev/null || true); do
|
||||
log_info "Unmounting $mount"
|
||||
umount "$mount" || true
|
||||
done
|
||||
|
||||
# Deactivate any LVM on internal drive
|
||||
for pv in $(pvs --noheadings -o pv_name | grep -E "^[[:space:]]*$INTERNAL_DRIVE" || true); do
|
||||
log_info "Deactivating PV $pv"
|
||||
vgchange -an $(pvs --noheadings -o vg_name "$pv") || true
|
||||
done
|
||||
|
||||
# Wipe filesystem signatures and partition table
|
||||
wipefs -af "$INTERNAL_DRIVE"
|
||||
|
||||
# Create new GPT partition table
|
||||
parted "$INTERNAL_DRIVE" --script mklabel gpt
|
||||
|
||||
# Create EFI boot partition (512MB)
|
||||
parted "$INTERNAL_DRIVE" --script mkpart primary fat32 1MiB 513MiB
|
||||
parted "$INTERNAL_DRIVE" --script set 1 esp on
|
||||
|
||||
# Create LVM partition (rest of the drive)
|
||||
parted "$INTERNAL_DRIVE" --script mkpart primary 513MiB 100%
|
||||
parted "$INTERNAL_DRIVE" --script set 2 lvm on
|
||||
|
||||
# Wait for partitions to be recognized
|
||||
sleep 2
|
||||
partprobe "$INTERNAL_DRIVE"
|
||||
sleep 2
|
||||
|
||||
# Format EFI partition
|
||||
mkfs.fat -F32 "${INTERNAL_DRIVE}p1"
|
||||
|
||||
log_info "Internal drive prepared with new partition structure"
|
||||
}
|
||||
|
||||
# Create LVM structure on internal drive
|
||||
create_lvm_structure() {
|
||||
log_step "Creating LVM structure on internal drive..."
|
||||
|
||||
# Create physical volume
|
||||
pvcreate "${INTERNAL_DRIVE}p2"
|
||||
|
||||
# Create volume group
|
||||
vgcreate "$NEW_VG_NAME" "${INTERNAL_DRIVE}p2"
|
||||
|
||||
# Get sizes from external LVM
|
||||
root_size=$(lvs --noheadings --units g --nosuffix -o lv_size "$VG_NAME/root" | tr -d ' ')
|
||||
home_size=$(lvs --noheadings --units g --nosuffix -o lv_size "$VG_NAME/home" | tr -d ' ')
|
||||
boot_size=$(lvs --noheadings --units g --nosuffix -o lv_size "$VG_NAME/boot" | tr -d ' ')
|
||||
swap_size=$(lvs --noheadings --units g --nosuffix -o lv_size "$VG_NAME/swap" | tr -d ' ')
|
||||
|
||||
# Reserve 20% free space for snapshots
|
||||
vg_free=$(vgs --noheadings --units g --nosuffix -o vg_free "$NEW_VG_NAME" | tr -d ' ')
|
||||
snapshot_reserve=$(echo "$vg_free * 0.2" | bc -l | cut -d. -f1)
|
||||
|
||||
log_info "Reserving ${snapshot_reserve}GB for LVM snapshots"
|
||||
|
||||
# Create logical volumes with same sizes as external
|
||||
lvcreate -L "${root_size}G" -n root "$NEW_VG_NAME"
|
||||
lvcreate -L "${boot_size}G" -n boot "$NEW_VG_NAME"
|
||||
lvcreate -L "${swap_size}G" -n swap "$NEW_VG_NAME"
|
||||
|
||||
# Create home with remaining space minus snapshot reserve
|
||||
remaining_space=$(echo "$vg_free - $snapshot_reserve" | bc -l | cut -d. -f1)
|
||||
adjusted_home_size=$(echo "$remaining_space - $root_size - $boot_size - $swap_size" | bc -l | cut -d. -f1)
|
||||
|
||||
if (( $(echo "$adjusted_home_size > 0" | bc -l) )); then
|
||||
lvcreate -L "${adjusted_home_size}G" -n home "$NEW_VG_NAME"
|
||||
else
|
||||
log_warn "Using original home size, may reduce snapshot space"
|
||||
lvcreate -L "${home_size}G" -n home "$NEW_VG_NAME"
|
||||
fi
|
||||
|
||||
# Create filesystems
|
||||
mkfs.ext4 -L root "/dev/$NEW_VG_NAME/root"
|
||||
mkfs.ext4 -L boot "/dev/$NEW_VG_NAME/boot"
|
||||
mkfs.ext4 -L home "/dev/$NEW_VG_NAME/home"
|
||||
mkswap -L swap "/dev/$NEW_VG_NAME/swap"
|
||||
|
||||
log_info "LVM structure created on internal drive"
|
||||
}
|
||||
|
||||
# Mount internal drive and copy data
|
||||
copy_data() {
|
||||
log_step "Copying data from external to internal drive..."
|
||||
|
||||
# Create mount points
|
||||
mkdir -p /mnt/internal_root
|
||||
mkdir -p /mnt/internal_boot
|
||||
mkdir -p /mnt/internal_home
|
||||
|
||||
# Mount internal LVM
|
||||
mount "/dev/$NEW_VG_NAME/root" /mnt/internal_root
|
||||
mount "/dev/$NEW_VG_NAME/boot" /mnt/internal_boot
|
||||
mount "/dev/$NEW_VG_NAME/home" /mnt/internal_home
|
||||
|
||||
# Copy data with progress
|
||||
log_info "Copying root filesystem..."
|
||||
rsync -avHAXS --progress / /mnt/internal_root/ \
|
||||
--exclude=/dev/* \
|
||||
--exclude=/proc/* \
|
||||
--exclude=/sys/* \
|
||||
--exclude=/tmp/* \
|
||||
--exclude=/run/* \
|
||||
--exclude=/mnt/* \
|
||||
--exclude=/media/* \
|
||||
--exclude=/lost+found \
|
||||
--exclude=/boot/*
|
||||
|
||||
log_info "Copying boot filesystem..."
|
||||
rsync -avHAXS --progress /boot/ /mnt/internal_boot/
|
||||
|
||||
log_info "Copying home filesystem..."
|
||||
rsync -avHAXS --progress /home/ /mnt/internal_home/
|
||||
|
||||
log_info "Data copy completed"
|
||||
}
|
||||
|
||||
# Configure system for internal LVM boot
|
||||
configure_boot() {
|
||||
log_step "Configuring boot for internal LVM..."
|
||||
|
||||
# Mount EFI partition
|
||||
mkdir -p /mnt/internal_root/boot/efi
|
||||
mount "${INTERNAL_DRIVE}p1" /mnt/internal_root/boot/efi
|
||||
|
||||
# Chroot preparations
|
||||
mount --bind /dev /mnt/internal_root/dev
|
||||
mount --bind /proc /mnt/internal_root/proc
|
||||
mount --bind /sys /mnt/internal_root/sys
|
||||
mount --bind /run /mnt/internal_root/run
|
||||
|
||||
# Update fstab
|
||||
cat > /mnt/internal_root/etc/fstab << EOF
|
||||
# Internal LVM Configuration
|
||||
/dev/$NEW_VG_NAME/root / ext4 defaults 0 1
|
||||
/dev/$NEW_VG_NAME/boot /boot ext4 defaults 0 2
|
||||
/dev/$NEW_VG_NAME/home /home ext4 defaults 0 2
|
||||
/dev/$NEW_VG_NAME/swap none swap sw 0 0
|
||||
${INTERNAL_DRIVE}p1 /boot/efi vfat umask=0077 0 1
|
||||
EOF
|
||||
|
||||
# Update initramfs to include LVM
|
||||
chroot /mnt/internal_root /bin/bash -c "update-initramfs -u -k all"
|
||||
|
||||
# Install and configure GRUB
|
||||
chroot /mnt/internal_root /bin/bash -c "grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=debian"
|
||||
chroot /mnt/internal_root /bin/bash -c "update-grub"
|
||||
|
||||
# Cleanup mounts
|
||||
umount /mnt/internal_root/dev
|
||||
umount /mnt/internal_root/proc
|
||||
umount /mnt/internal_root/sys
|
||||
umount /mnt/internal_root/run
|
||||
umount /mnt/internal_root/boot/efi
|
||||
|
||||
log_info "Boot configuration completed"
|
||||
}
|
||||
|
||||
# Setup LVM snapshots
|
||||
setup_snapshots() {
|
||||
log_step "Setting up LVM snapshot capability..."
|
||||
|
||||
# Create snapshot management script
|
||||
cat > /mnt/internal_root/usr/local/bin/lvm-snapshot-manager << 'EOF'
|
||||
#!/bin/bash
|
||||
# LVM Snapshot Manager
|
||||
|
||||
VG_NAME="internal-vg"
|
||||
SNAPSHOT_SIZE="10G" # Adjust based on available space
|
||||
|
||||
case "$1" in
|
||||
create)
|
||||
echo "Creating LVM snapshots..."
|
||||
lvcreate -L $SNAPSHOT_SIZE -s -n root_snapshot $VG_NAME/root
|
||||
lvcreate -L $SNAPSHOT_SIZE -s -n home_snapshot $VG_NAME/home
|
||||
echo "Snapshots created successfully"
|
||||
;;
|
||||
remove)
|
||||
echo "Removing LVM snapshots..."
|
||||
lvremove -f $VG_NAME/root_snapshot 2>/dev/null || true
|
||||
lvremove -f $VG_NAME/home_snapshot 2>/dev/null || true
|
||||
echo "Snapshots removed"
|
||||
;;
|
||||
list)
|
||||
echo "Current snapshots:"
|
||||
lvs $VG_NAME | grep snapshot || echo "No snapshots found"
|
||||
;;
|
||||
merge)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Usage: $0 merge <snapshot_name>"
|
||||
exit 1
|
||||
fi
|
||||
echo "Merging snapshot $2..."
|
||||
lvconvert --merge $VG_NAME/$2
|
||||
echo "Snapshot merge initiated (requires reboot to complete)"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {create|remove|list|merge <snapshot_name>}"
|
||||
echo " create - Create snapshots of root and home"
|
||||
echo " remove - Remove all snapshots"
|
||||
echo " list - List existing snapshots"
|
||||
echo " merge - Merge a snapshot back to origin"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
|
||||
chmod +x /mnt/internal_root/usr/local/bin/lvm-snapshot-manager
|
||||
|
||||
# Create backup script using snapshots
|
||||
cat > /mnt/internal_root/usr/local/bin/snapshot-backup << 'EOF'
|
||||
#!/bin/bash
|
||||
# Snapshot-based backup script
|
||||
|
||||
BACKUP_DIR="/backup"
|
||||
VG_NAME="internal-vg"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
# Create snapshots
|
||||
echo "Creating snapshots..."
|
||||
/usr/local/bin/lvm-snapshot-manager create
|
||||
|
||||
# Mount snapshots and backup
|
||||
mkdir -p $BACKUP_DIR/root_$DATE
|
||||
mkdir -p $BACKUP_DIR/home_$DATE
|
||||
|
||||
mount /dev/$VG_NAME/root_snapshot $BACKUP_DIR/root_$DATE
|
||||
mount /dev/$VG_NAME/home_snapshot $BACKUP_DIR/home_$DATE
|
||||
|
||||
echo "Snapshots mounted. You can now backup from:"
|
||||
echo " Root: $BACKUP_DIR/root_$DATE"
|
||||
echo " Home: $BACKUP_DIR/home_$DATE"
|
||||
echo ""
|
||||
echo "When done, run: umount $BACKUP_DIR/root_$DATE $BACKUP_DIR/home_$DATE"
|
||||
echo "Then run: /usr/local/bin/lvm-snapshot-manager remove"
|
||||
EOF
|
||||
|
||||
chmod +x /mnt/internal_root/usr/local/bin/snapshot-backup
|
||||
|
||||
log_info "LVM snapshot tools installed"
|
||||
}
|
||||
|
||||
# Cleanup and unmount
|
||||
cleanup() {
|
||||
log_step "Cleaning up..."
|
||||
|
||||
# Unmount all internal mounts
|
||||
umount /mnt/internal_home || true
|
||||
umount /mnt/internal_boot || true
|
||||
umount /mnt/internal_root || true
|
||||
|
||||
# Remove mount points
|
||||
rmdir /mnt/internal_home /mnt/internal_boot /mnt/internal_root 2>/dev/null || true
|
||||
|
||||
log_info "Cleanup completed"
|
||||
}
|
||||
|
||||
# Main migration function
|
||||
main() {
|
||||
log_info "Starting LVM migration from external M.2 to internal NVMe"
|
||||
log_info "External: $EXTERNAL_DRIVE -> Internal: $INTERNAL_DRIVE"
|
||||
|
||||
safety_checks
|
||||
create_backup_dir
|
||||
prepare_internal_drive
|
||||
create_lvm_structure
|
||||
copy_data
|
||||
configure_boot
|
||||
setup_snapshots
|
||||
cleanup
|
||||
|
||||
log_info "Migration completed successfully!"
|
||||
echo
|
||||
echo -e "${GREEN}SUCCESS!${NC} LVM migration completed"
|
||||
echo "Next steps:"
|
||||
echo "1. Reboot and select internal drive in BIOS/UEFI"
|
||||
echo "2. Verify system boots correctly from internal LVM"
|
||||
echo "3. Test snapshot functionality with: sudo lvm-snapshot-manager create"
|
||||
echo "4. Once confirmed working, you can repurpose the external M.2"
|
||||
echo
|
||||
echo "Snapshot management commands:"
|
||||
echo " sudo lvm-snapshot-manager create # Create snapshots"
|
||||
echo " sudo lvm-snapshot-manager list # List snapshots"
|
||||
echo " sudo lvm-snapshot-manager remove # Remove snapshots"
|
||||
echo " sudo snapshot-backup # Backup using snapshots"
|
||||
}
|
||||
|
||||
# Handle interruption
|
||||
trap cleanup EXIT
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
90
old_scripts/migration_completion_summary.sh
Normal file
90
old_scripts/migration_completion_summary.sh
Normal file
@@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Migration Completion Summary
|
||||
# ============================
|
||||
#
|
||||
# This script documents the successful completion of the LVM snapshot-based migration
|
||||
# from external M.2 SSD to internal NVMe drive with LUKS encryption.
|
||||
|
||||
echo "=== MIGRATION COMPLETION SUMMARY ==="
|
||||
echo "Date: $(date)"
|
||||
echo
|
||||
|
||||
echo "✅ MIGRATION STATUS: COMPLETE AND SUCCESSFUL"
|
||||
echo
|
||||
|
||||
echo "=== SYSTEM CONFIGURATION ==="
|
||||
echo "External M.2 SSD (/dev/sda):"
|
||||
echo " - Volume Group: migration-vg"
|
||||
echo " - Root LV: migration-root (50GB)"
|
||||
echo " - Home LV: migration-home (1.3TB)"
|
||||
echo " - EFI Partition: /dev/sda1 (512MB, bootable)"
|
||||
echo " - Status: ✅ Fully functional and bootable"
|
||||
echo
|
||||
|
||||
echo "Internal NVMe (/dev/nvme0n1):"
|
||||
echo " - Volume Group: internal-vg"
|
||||
echo " - Root LV: internal-root (50GB)"
|
||||
echo " - Home LV: internal-home (1.3TB, LUKS encrypted)"
|
||||
echo " - EFI Partition: /dev/nvme0n1p1 (1GB, bootable)"
|
||||
echo " - Status: ✅ Fully configured with LVM+LUKS, bootable"
|
||||
echo
|
||||
|
||||
echo "=== DATA TRANSFER RESULTS ==="
|
||||
echo "✅ Snapshot-based migration completed successfully"
|
||||
echo "✅ 342GB of data transferred via LVM snapshots"
|
||||
echo "✅ All partitions migrated (root + home + boot)"
|
||||
echo "✅ LUKS encryption enabled on home partition"
|
||||
echo "✅ System configurations updated (/etc/fstab, /etc/crypttab)"
|
||||
echo
|
||||
|
||||
echo "=== BOOT CONFIGURATION ==="
|
||||
echo "✅ GRUB installed on internal drive"
|
||||
echo "✅ EFI boot entry created for internal drive"
|
||||
echo "✅ Boot loader configurations updated"
|
||||
echo "✅ Both drives remain bootable"
|
||||
echo
|
||||
|
||||
echo "=== CURRENT SYSTEM STATE ==="
|
||||
echo "Currently booted from: External M.2 SSD"
|
||||
echo "Root filesystem: $(findmnt -n -o SOURCE /)"
|
||||
echo "Home filesystem: $(findmnt -n -o SOURCE /home)"
|
||||
echo "Boot filesystem: $(findmnt -n -o SOURCE /boot)"
|
||||
echo
|
||||
|
||||
echo "=== VERIFICATION COMMANDS ==="
|
||||
echo "Check external drive LVM:"
|
||||
echo " sudo lvs migration-vg"
|
||||
echo " sudo pvs | grep sda"
|
||||
echo
|
||||
echo "Check internal drive LVM:"
|
||||
echo " sudo lvs internal-vg"
|
||||
echo " sudo pvs | grep nvme"
|
||||
echo
|
||||
echo "Check LUKS encryption:"
|
||||
echo " sudo cryptsetup status luks-home-internal"
|
||||
echo
|
||||
echo "Check EFI boot entries:"
|
||||
echo " efibootmgr -v | grep -E '(Internal-LVM|USB HDD)'"
|
||||
echo
|
||||
|
||||
echo "=== NEXT STEPS ==="
|
||||
echo "1. Test boot from internal drive:"
|
||||
echo " - Reboot and select 'Internal-LVM' from EFI boot menu"
|
||||
echo " - Verify LUKS password prompt works"
|
||||
echo " - Confirm all data is accessible"
|
||||
echo
|
||||
echo "2. Optional: Set internal drive as default boot option:"
|
||||
echo " sudo efibootmgr -o 0001,001C,001B,0000,..."
|
||||
echo
|
||||
echo "3. Keep external drive as backup/recovery option"
|
||||
echo
|
||||
|
||||
echo "=== BACKUP SAFETY ==="
|
||||
echo "✅ External M.2 contains complete working system backup"
|
||||
echo "✅ Both drives are independently bootable"
|
||||
echo "✅ No data loss occurred during migration"
|
||||
echo "✅ LUKS encryption provides additional security"
|
||||
echo
|
||||
|
||||
echo "Migration completed successfully! 🎉"
|
||||
75
old_scripts/preview_migration.sh
Executable file
75
old_scripts/preview_migration.sh
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/bin/bash
|
||||
|
||||
# LVM Migration Preview Script
|
||||
# Shows current status and what the migration will do
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}=== LVM Migration Preview ===${NC}"
|
||||
echo
|
||||
|
||||
echo -e "${GREEN}Current Boot Status:${NC}"
|
||||
echo "Root mounted from: $(findmnt -n -o SOURCE /)"
|
||||
echo "Boot mounted from: $(findmnt -n -o SOURCE /boot)"
|
||||
echo "Home mounted from: $(findmnt -n -o SOURCE /home)"
|
||||
echo
|
||||
|
||||
echo -e "${GREEN}Current LVM Structure (External M.2):${NC}"
|
||||
sudo vgs migration-vg 2>/dev/null || echo "No VG found"
|
||||
echo
|
||||
sudo lvs migration-vg 2>/dev/null || echo "No LVs found"
|
||||
echo
|
||||
|
||||
echo -e "${GREEN}Current Drive Layout:${NC}"
|
||||
echo "External M.2 (sda):"
|
||||
lsblk /dev/sda
|
||||
echo
|
||||
echo "Internal NVMe (nvme0n1):"
|
||||
lsblk /dev/nvme0n1
|
||||
echo
|
||||
|
||||
echo -e "${GREEN}Available Space Analysis:${NC}"
|
||||
echo "External VG size: $(sudo vgs --noheadings --units g -o vg_size migration-vg | tr -d ' ')B"
|
||||
echo "External VG free: $(sudo vgs --noheadings --units g -o vg_free migration-vg | tr -d ' ')B"
|
||||
echo "Internal drive size: $(lsblk -b -n -o SIZE /dev/nvme0n1 | awk '{printf "%.1fGB", $1/1024/1024/1024}')"
|
||||
echo
|
||||
|
||||
echo -e "${YELLOW}Migration Plan:${NC}"
|
||||
echo "1. ✅ Wipe internal NVMe drive completely"
|
||||
echo "2. ✅ Create new GPT partition table"
|
||||
echo "3. ✅ Create EFI boot partition (512MB)"
|
||||
echo "4. ✅ Create LVM partition (remaining space)"
|
||||
echo "5. ✅ Set up LVM with volume group 'internal-vg'"
|
||||
echo "6. ✅ Create logical volumes matching current structure"
|
||||
echo "7. ✅ Copy all data from external to internal"
|
||||
echo "8. ✅ Configure GRUB for LVM boot"
|
||||
echo "9. ✅ Set up LVM snapshot capabilities"
|
||||
echo "10. ✅ Reserve 20% space for snapshots"
|
||||
echo
|
||||
|
||||
echo -e "${RED}⚠️ WARNINGS:${NC}"
|
||||
echo "• This will COMPLETELY WIPE the internal NVMe drive"
|
||||
echo "• All current data on internal drive will be lost"
|
||||
echo "• Make sure you're booted from external M.2 (verified above)"
|
||||
echo "• Ensure external M.2 LED is active/blinking"
|
||||
echo
|
||||
|
||||
echo -e "${GREEN}Snapshot Features After Migration:${NC}"
|
||||
echo "• lvm-snapshot-manager create - Create system snapshots"
|
||||
echo "• lvm-snapshot-manager list - List existing snapshots"
|
||||
echo "• lvm-snapshot-manager remove - Remove snapshots"
|
||||
echo "• lvm-snapshot-manager merge - Restore from snapshot"
|
||||
echo "• snapshot-backup - Backup using snapshots"
|
||||
echo
|
||||
|
||||
echo -e "${BLUE}Ready to proceed?${NC}"
|
||||
echo "Run: sudo ./migrate_lvm_to_internal.sh"
|
||||
echo
|
||||
echo -e "${YELLOW}Estimated time: 2-4 hours depending on data size${NC}"
|
||||
104
old_scripts/preview_snapshot_migration.sh
Executable file
104
old_scripts/preview_snapshot_migration.sh
Executable file
@@ -0,0 +1,104 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Snapshot Migration Preview Script
|
||||
# Shows what the snapshot-based migration will do
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}=== Snapshot-Based LVM Migration Preview ===${NC}"
|
||||
echo
|
||||
|
||||
echo -e "${GREEN}Current System Status:${NC}"
|
||||
echo "✅ Booted from: $(findmnt -n -o SOURCE /)"
|
||||
echo "✅ Volume Group: migration-vg"
|
||||
echo
|
||||
|
||||
echo -e "${GREEN}Current Logical Volumes:${NC}"
|
||||
sudo lvs migration-vg --units g -o lv_name,lv_size,data_percent 2>/dev/null
|
||||
echo
|
||||
|
||||
echo -e "${GREEN}Available Space for Snapshots:${NC}"
|
||||
free_space=$(sudo vgs --noheadings --units m --nosuffix -o vg_free migration-vg | tr -d ' ' | tr ',' '.')
|
||||
if command -v bc >/dev/null 2>&1; then
|
||||
snapshot_size_per_lv=$(echo "$free_space * 0.8 / 4" | bc 2>/dev/null | cut -d. -f1)
|
||||
else
|
||||
snapshot_size_per_lv=$((${free_space%.*} * 80 / 100 / 4))
|
||||
fi
|
||||
echo "Free space: ${free_space}MB"
|
||||
echo "Snapshot size per LV: ${snapshot_size_per_lv}MB"
|
||||
|
||||
if (( snapshot_size_per_lv < 100 )); then
|
||||
echo -e "${RED}⚠️ WARNING: Limited space for snapshots!${NC}"
|
||||
echo "Consider freeing up space or expanding the volume group"
|
||||
else
|
||||
echo -e "${GREEN}✅ Sufficient space for snapshots${NC}"
|
||||
fi
|
||||
echo
|
||||
|
||||
echo -e "${GREEN}Target Drive Analysis:${NC}"
|
||||
echo "Internal NVMe (nvme0n1):"
|
||||
lsblk /dev/nvme0n1
|
||||
echo "Size: $(lsblk -b -n -o SIZE /dev/nvme0n1 | awk '{printf "%.1fGB", $1/1024/1024/1024}')"
|
||||
echo
|
||||
|
||||
echo -e "${YELLOW}Snapshot Migration Process:${NC}"
|
||||
echo "1. 📸 Create temporary snapshots of ALL logical volumes:"
|
||||
echo " • root (${snapshot_size_per_lv}MB snapshot)"
|
||||
echo " • home (${snapshot_size_per_lv}MB snapshot)"
|
||||
echo " • boot (${snapshot_size_per_lv}MB snapshot)"
|
||||
echo " • swap (${snapshot_size_per_lv}MB snapshot)"
|
||||
echo
|
||||
echo "2. 🔧 Prepare internal drive:"
|
||||
echo " • Copy partition table from external M.2"
|
||||
echo " • Create LVM physical volume"
|
||||
echo " • Create volume group 'internal-vg'"
|
||||
echo " • Create logical volumes with exact same sizes"
|
||||
echo
|
||||
echo "3. 📋 Transfer data using block-level copy:"
|
||||
echo " • Use dd to copy each snapshot to internal LV"
|
||||
echo " • Process one LV at a time to manage space"
|
||||
echo " • Remove each snapshot after successful copy"
|
||||
echo
|
||||
echo "4. ⚙️ Configure boot:"
|
||||
echo " • Update fstab for new volume group"
|
||||
echo " • Install GRUB on internal drive"
|
||||
echo " • Update initramfs for LVM"
|
||||
echo
|
||||
echo "5. 🛠️ Setup snapshot tools:"
|
||||
echo " • Install lvm-snapshot-manager"
|
||||
echo " • Reserve space for future snapshots"
|
||||
echo
|
||||
|
||||
echo -e "${RED}⚠️ CRITICAL WARNINGS:${NC}"
|
||||
echo "• Internal NVMe will be COMPLETELY WIPED"
|
||||
echo "• This creates an EXACT copy including all current data"
|
||||
echo "• Process takes 1-3 hours depending on data size"
|
||||
echo "• Snapshots use limited available space"
|
||||
echo
|
||||
|
||||
echo -e "${GREEN}Advantages of Snapshot Method:${NC}"
|
||||
echo "✅ Exact bit-for-bit copy of live system"
|
||||
echo "✅ No filesystem corruption risks"
|
||||
echo "✅ Can capture running system state"
|
||||
echo "✅ Uses LVM native capabilities"
|
||||
echo "✅ Block-level transfer (faster than file copy)"
|
||||
echo
|
||||
|
||||
echo -e "${GREEN}Post-Migration Capabilities:${NC}"
|
||||
echo "• sudo lvm-snapshot-manager create - Create system snapshots"
|
||||
echo "• sudo lvm-snapshot-manager list - Show snapshots"
|
||||
echo "• sudo lvm-snapshot-manager remove - Clean up snapshots"
|
||||
echo "• sudo lvm-snapshot-manager merge - Restore from snapshot"
|
||||
echo
|
||||
|
||||
echo -e "${BLUE}Ready to proceed with snapshot migration?${NC}"
|
||||
echo "Run: sudo ./snapshot_migrate_to_internal.sh"
|
||||
echo
|
||||
echo -e "${YELLOW}Estimated time: 1-3 hours${NC}"
|
||||
241
old_scripts/resume_snapshot_migration.sh
Executable file
241
old_scripts/resume_snapshot_migration.sh
Executable file
@@ -0,0 +1,241 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Resume snapshot migration - continue from data transfer phase
|
||||
# Since internal LVM structure already exists
|
||||
|
||||
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
|
||||
|
||||
# Configuration
|
||||
SOURCE_VG="migration-vg"
|
||||
TARGET_VG="internal-vg"
|
||||
INTERNAL_DRIVE="/dev/nvme0n1"
|
||||
|
||||
# Logging
|
||||
LOG_FILE="/var/log/lvm_snapshot_migration_resume.log"
|
||||
exec 1> >(tee -a "$LOG_FILE")
|
||||
exec 2> >(tee -a "$LOG_FILE" >&2)
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
# Transfer data with snapshots (resume from where we left off)
|
||||
transfer_with_snapshots() {
|
||||
log_step "Resuming data transfer using snapshots..."
|
||||
|
||||
# Calculate optimal snapshot size (use available free space)
|
||||
free_space=$(vgs --noheadings --units m --nosuffix -o vg_free "$SOURCE_VG" | tr -d ' ' | tr ',' '.')
|
||||
# Use 80% of free space for snapshots, divided by number of LVs
|
||||
snapshot_size=$(echo "$free_space * 0.8 / 4" | bc | cut -d. -f1)
|
||||
|
||||
if (( snapshot_size < 100 )); then
|
||||
log_error "Not enough free space for snapshots. Need at least 400MB free."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Using ${snapshot_size}MB for each snapshot"
|
||||
|
||||
# Process each logical volume
|
||||
for lv in root boot home swap; do
|
||||
log_step "Processing $lv..."
|
||||
|
||||
# Create snapshot
|
||||
log_info "Creating snapshot of $lv..."
|
||||
if ! lvcreate -L "${snapshot_size}M" -s -n "${lv}_snapshot" "$SOURCE_VG/$lv"; then
|
||||
log_error "Failed to create snapshot for $lv"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Copy data using dd with progress
|
||||
log_info "Copying $lv data to internal drive..."
|
||||
if [[ "$lv" == "swap" ]]; then
|
||||
# For swap, just copy the LV structure
|
||||
dd if="/dev/$SOURCE_VG/${lv}_snapshot" of="/dev/$TARGET_VG/$lv" bs=1M status=progress
|
||||
else
|
||||
# For filesystems, use dd for exact copy
|
||||
dd if="/dev/$SOURCE_VG/${lv}_snapshot" of="/dev/$TARGET_VG/$lv" bs=1M status=progress
|
||||
fi
|
||||
|
||||
# Remove snapshot to free space for next one
|
||||
log_info "Removing snapshot of $lv..."
|
||||
lvremove -f "$SOURCE_VG/${lv}_snapshot"
|
||||
|
||||
log_info "Completed transfer of $lv"
|
||||
done
|
||||
|
||||
log_info "All data transferred successfully"
|
||||
}
|
||||
|
||||
# Setup boot for internal drive
|
||||
configure_internal_boot() {
|
||||
log_step "Configuring boot for internal drive..."
|
||||
|
||||
# Format and mount EFI partition
|
||||
mkfs.fat -F32 "${INTERNAL_DRIVE}p1" || true
|
||||
mkdir -p /mnt/internal_efi
|
||||
mount "${INTERNAL_DRIVE}p1" /mnt/internal_efi
|
||||
|
||||
# Mount internal filesystems
|
||||
mkdir -p /mnt/internal_root /mnt/internal_boot /mnt/internal_home
|
||||
mount "/dev/$TARGET_VG/root" /mnt/internal_root
|
||||
mount "/dev/$TARGET_VG/boot" /mnt/internal_boot
|
||||
mount "/dev/$TARGET_VG/home" /mnt/internal_home
|
||||
|
||||
# Mount EFI in the target system
|
||||
mkdir -p /mnt/internal_root/boot/efi
|
||||
mount "${INTERNAL_DRIVE}p1" /mnt/internal_root/boot/efi
|
||||
|
||||
# Update fstab for new VG
|
||||
log_info "Updating fstab..."
|
||||
cat > /mnt/internal_root/etc/fstab << EOF
|
||||
# Internal LVM Configuration
|
||||
/dev/$TARGET_VG/root / ext4 defaults 0 1
|
||||
/dev/$TARGET_VG/boot /boot ext4 defaults 0 2
|
||||
/dev/$TARGET_VG/home /home ext4 defaults 0 2
|
||||
/dev/$TARGET_VG/swap none swap sw 0 0
|
||||
${INTERNAL_DRIVE}p1 /boot/efi vfat umask=0077 0 1
|
||||
EOF
|
||||
|
||||
# Prepare chroot environment
|
||||
mount --bind /dev /mnt/internal_root/dev
|
||||
mount --bind /proc /mnt/internal_root/proc
|
||||
mount --bind /sys /mnt/internal_root/sys
|
||||
mount --bind /run /mnt/internal_root/run
|
||||
|
||||
# Update initramfs and install GRUB
|
||||
log_info "Updating initramfs and GRUB..."
|
||||
chroot /mnt/internal_root /bin/bash -c "update-initramfs -u -k all"
|
||||
chroot /mnt/internal_root /bin/bash -c "grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Internal-LVM"
|
||||
chroot /mnt/internal_root /bin/bash -c "update-grub"
|
||||
|
||||
# Clean up mounts
|
||||
umount /mnt/internal_root/dev /mnt/internal_root/proc /mnt/internal_root/sys /mnt/internal_root/run
|
||||
umount /mnt/internal_root/boot/efi
|
||||
umount /mnt/internal_efi /mnt/internal_root /mnt/internal_boot /mnt/internal_home
|
||||
|
||||
log_info "Boot configuration completed"
|
||||
}
|
||||
|
||||
# Setup snapshot tools
|
||||
setup_snapshot_tools() {
|
||||
log_step "Setting up snapshot tools on internal drive..."
|
||||
|
||||
# Remount for tool installation
|
||||
mount "/dev/$TARGET_VG/root" /mnt/internal_root
|
||||
|
||||
# Create snapshot management script
|
||||
cat > /mnt/internal_root/usr/local/bin/lvm-snapshot-manager << 'EOFSCRIPT'
|
||||
#!/bin/bash
|
||||
# LVM Snapshot Manager for Internal Drive
|
||||
|
||||
VG_NAME="internal-vg"
|
||||
|
||||
case "$1" in
|
||||
create)
|
||||
echo "Creating LVM snapshots..."
|
||||
# Calculate available space for snapshots
|
||||
free_space=$(vgs --noheadings --units g --nosuffix -o vg_free "$VG_NAME" | tr -d ' ')
|
||||
snapshot_size=$(echo "$free_space / 4" | bc)G
|
||||
|
||||
lvcreate -L "$snapshot_size" -s -n root_backup "$VG_NAME/root"
|
||||
lvcreate -L "$snapshot_size" -s -n home_backup "$VG_NAME/home"
|
||||
lvcreate -L "$snapshot_size" -s -n boot_backup "$VG_NAME/boot"
|
||||
echo "Snapshots created with size: $snapshot_size each"
|
||||
;;
|
||||
remove)
|
||||
echo "Removing LVM snapshots..."
|
||||
lvremove -f "$VG_NAME/root_backup" 2>/dev/null || true
|
||||
lvremove -f "$VG_NAME/home_backup" 2>/dev/null || true
|
||||
lvremove -f "$VG_NAME/boot_backup" 2>/dev/null || true
|
||||
echo "Snapshots removed"
|
||||
;;
|
||||
list)
|
||||
echo "Current snapshots:"
|
||||
lvs "$VG_NAME" | grep backup || echo "No snapshots found"
|
||||
;;
|
||||
merge)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Usage: $0 merge <snapshot_name>"
|
||||
exit 1
|
||||
fi
|
||||
echo "Merging snapshot $2..."
|
||||
lvconvert --merge "$VG_NAME/$2"
|
||||
echo "Snapshot merge initiated (reboot required to complete)"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {create|remove|list|merge <snapshot_name>}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
EOFSCRIPT
|
||||
|
||||
chmod +x /mnt/internal_root/usr/local/bin/lvm-snapshot-manager
|
||||
|
||||
umount /mnt/internal_root
|
||||
|
||||
log_info "Snapshot tools installed"
|
||||
}
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
log_info "Cleaning up..."
|
||||
|
||||
# Remove any remaining snapshots
|
||||
for snap in $(lvs --noheadings -o lv_name "$SOURCE_VG" 2>/dev/null | grep snapshot || true); do
|
||||
lvremove -f "$SOURCE_VG/$snap" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Unmount any remaining mounts
|
||||
for mount in /mnt/internal_*; do
|
||||
umount "$mount" 2>/dev/null || true
|
||||
rmdir "$mount" 2>/dev/null || true
|
||||
done
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
log_info "Resuming snapshot-based LVM migration"
|
||||
log_info "Internal LVM structure already exists, continuing with data transfer..."
|
||||
|
||||
transfer_with_snapshots
|
||||
configure_internal_boot
|
||||
setup_snapshot_tools
|
||||
cleanup
|
||||
|
||||
log_info "Migration completed successfully!"
|
||||
echo
|
||||
echo -e "${GREEN}SUCCESS!${NC} Snapshot-based LVM migration completed"
|
||||
echo
|
||||
echo "Next steps:"
|
||||
echo "1. Reboot and select internal drive in BIOS/UEFI"
|
||||
echo "2. Verify all systems working from internal LVM"
|
||||
echo "3. Test snapshot functionality: sudo lvm-snapshot-manager create"
|
||||
echo "4. External M.2 can now be used as backup drive"
|
||||
}
|
||||
|
||||
# Handle interruption
|
||||
trap cleanup EXIT
|
||||
|
||||
# Check if running as root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "This script must be run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run migration
|
||||
main "$@"
|
||||
158
old_scripts/setup_luks_internal.sh
Executable file
158
old_scripts/setup_luks_internal.sh
Executable file
@@ -0,0 +1,158 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to set up LUKS encryption for home partition on internal drive
|
||||
# This will encrypt the home partition in-place
|
||||
|
||||
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'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "This script must be run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}=== LUKS Encryption Setup for Internal Drive ===${NC}"
|
||||
echo
|
||||
echo "This will set up LUKS encryption for your home partition on the internal drive."
|
||||
echo "The process will:"
|
||||
echo "1. Create a backup image of the current home data"
|
||||
echo "2. Recreate the home LV with LUKS encryption"
|
||||
echo "3. Restore the data to the encrypted volume"
|
||||
echo "4. Update system configuration"
|
||||
echo
|
||||
echo -e "${RED}WARNING: This process requires sufficient free space for backup!${NC}"
|
||||
|
||||
# Check available space
|
||||
free_space=$(vgs --noheadings --units g --nosuffix -o vg_free internal-vg | tr -d ' ' | tr ',' '.')
|
||||
home_size=$(lvs --noheadings --units g --nosuffix -o lv_size internal-vg/home | tr -d ' ' | tr ',' '.')
|
||||
|
||||
echo "Home partition size: ${home_size}GB"
|
||||
echo "Available free space: ${free_space}GB"
|
||||
|
||||
if (( $(echo "$free_space < $home_size" | bc -l) )); then
|
||||
log_error "Not enough free space for backup. Need ${home_size}GB free space."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -p "Continue with LUKS encryption setup? (yes/no): " confirm
|
||||
if [[ "$confirm" != "yes" ]]; then
|
||||
log_info "Operation cancelled"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
BACKUP_LV="home_backup_temp"
|
||||
MOUNT_POINT="/mnt/luks_setup"
|
||||
|
||||
log_step "Creating backup of home data..."
|
||||
|
||||
# Create backup LV
|
||||
lvcreate -L "${home_size}G" -n "$BACKUP_LV" internal-vg
|
||||
|
||||
# Copy home data to backup
|
||||
log_info "Copying home data to backup volume..."
|
||||
dd if=/dev/internal-vg/home of="/dev/internal-vg/$BACKUP_LV" bs=1M status=progress
|
||||
|
||||
log_step "Removing and recreating home LV..."
|
||||
|
||||
# Remove the current home LV
|
||||
lvremove -f internal-vg/home
|
||||
|
||||
# Create new home LV
|
||||
lvcreate -L "${home_size}G" -n home internal-vg
|
||||
|
||||
log_step "Setting up LUKS encryption..."
|
||||
|
||||
# Setup LUKS on the new LV
|
||||
echo "Please enter your desired LUKS passphrase:"
|
||||
cryptsetup luksFormat /dev/internal-vg/home
|
||||
|
||||
echo "Please enter your LUKS passphrase again to open the volume:"
|
||||
cryptsetup open /dev/internal-vg/home luks-home-internal
|
||||
|
||||
# Format the encrypted volume
|
||||
mkfs.ext4 -L home /dev/mapper/luks-home-internal
|
||||
|
||||
log_step "Restoring home data..."
|
||||
|
||||
# Mount backup and encrypted volumes
|
||||
mkdir -p "$MOUNT_POINT/backup" "$MOUNT_POINT/encrypted"
|
||||
mount "/dev/internal-vg/$BACKUP_LV" "$MOUNT_POINT/backup"
|
||||
mount /dev/mapper/luks-home-internal "$MOUNT_POINT/encrypted"
|
||||
|
||||
# Copy data back
|
||||
log_info "Copying data from backup to encrypted volume..."
|
||||
rsync -avHAXS --progress "$MOUNT_POINT/backup/" "$MOUNT_POINT/encrypted/"
|
||||
|
||||
# Clean up mounts
|
||||
umount "$MOUNT_POINT/backup" "$MOUNT_POINT/encrypted"
|
||||
cryptsetup close luks-home-internal
|
||||
|
||||
# Remove backup LV
|
||||
lvremove -f "internal-vg/$BACKUP_LV"
|
||||
|
||||
log_step "Updating system configuration..."
|
||||
|
||||
# Get the UUID of the LUKS device
|
||||
LUKS_UUID=$(cryptsetup luksUUID /dev/internal-vg/home)
|
||||
|
||||
# Mount the internal root to update configuration
|
||||
mount /dev/internal-vg/root "$MOUNT_POINT"
|
||||
|
||||
# Update /etc/crypttab
|
||||
echo "luks-home-internal UUID=$LUKS_UUID none luks" >> "$MOUNT_POINT/etc/crypttab"
|
||||
|
||||
# Update /etc/fstab
|
||||
cat > "$MOUNT_POINT/etc/fstab" << EOF
|
||||
# Internal LVM Configuration with LUKS
|
||||
/dev/internal-vg/root / ext4 defaults 0 1
|
||||
/dev/internal-vg/boot /boot ext4 defaults 0 2
|
||||
/dev/mapper/luks-home-internal /home ext4 defaults 0 2
|
||||
/dev/internal-vg/swap none swap sw 0 0
|
||||
/dev/nvme0n1p1 /boot/efi vfat umask=0077 0 1
|
||||
EOF
|
||||
|
||||
# Update initramfs to include LUKS support
|
||||
mount --bind /dev "$MOUNT_POINT/dev"
|
||||
mount --bind /proc "$MOUNT_POINT/proc"
|
||||
mount --bind /sys "$MOUNT_POINT/sys"
|
||||
mount --bind /run "$MOUNT_POINT/run"
|
||||
|
||||
chroot "$MOUNT_POINT" /bin/bash -c "update-initramfs -u -k all"
|
||||
chroot "$MOUNT_POINT" /bin/bash -c "update-grub"
|
||||
|
||||
# Clean up
|
||||
umount "$MOUNT_POINT/dev" "$MOUNT_POINT/proc" "$MOUNT_POINT/sys" "$MOUNT_POINT/run"
|
||||
umount "$MOUNT_POINT"
|
||||
|
||||
log_info "LUKS encryption setup completed successfully!"
|
||||
echo
|
||||
echo -e "${GREEN}SUCCESS!${NC} Home partition is now encrypted with LUKS"
|
||||
echo "Next steps:"
|
||||
echo "1. Reboot from the internal drive"
|
||||
echo "2. You will be prompted for the LUKS passphrase during boot"
|
||||
echo "3. Verify that everything works correctly"
|
||||
echo
|
||||
echo -e "${YELLOW}Important:${NC} Remember your LUKS passphrase! Without it, your home data will be inaccessible."
|
||||
152
old_scripts/setup_luks_simple.sh
Executable file
152
old_scripts/setup_luks_simple.sh
Executable file
@@ -0,0 +1,152 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Simplified LUKS Setup Script
|
||||
# Wipes internal home, creates LUKS encryption, and restores from external drive
|
||||
|
||||
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'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "This script must be run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}=== Simple LUKS Encryption Setup ===${NC}"
|
||||
echo
|
||||
echo "This will:"
|
||||
echo "1. Remove the current home LV on internal drive"
|
||||
echo "2. Create a new LUKS-encrypted home LV"
|
||||
echo "3. Copy your home data directly from external M.2"
|
||||
echo "4. Update system configuration"
|
||||
echo
|
||||
echo -e "${YELLOW}Source:${NC} External M.2 (/dev/migration-vg/home)"
|
||||
echo -e "${YELLOW}Target:${NC} Internal NVMe (/dev/internal-vg/home) - WILL BE WIPED"
|
||||
echo
|
||||
|
||||
read -p "Continue with LUKS encryption setup? (yes/no): " confirm
|
||||
if [[ "$confirm" != "yes" ]]; then
|
||||
log_info "Operation cancelled"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
MOUNT_POINT="/mnt/luks_setup"
|
||||
EXTERNAL_HOME="/dev/migration-vg/home"
|
||||
INTERNAL_VG="internal-vg"
|
||||
|
||||
log_step "Removing current internal home LV..."
|
||||
|
||||
# Remove the current home LV
|
||||
lvremove -f "$INTERNAL_VG/home"
|
||||
|
||||
log_step "Creating new home LV..."
|
||||
|
||||
# Get the original home size from external drive
|
||||
home_size=$(lvs --noheadings --units g --nosuffix -o lv_size migration-vg/home | tr -d ' ' | tr ',' '.')
|
||||
|
||||
# Create new home LV
|
||||
lvcreate -L "${home_size}G" -n home "$INTERNAL_VG"
|
||||
|
||||
log_step "Setting up LUKS encryption..."
|
||||
|
||||
# Setup LUKS on the new LV
|
||||
echo "Please enter your desired LUKS passphrase for home encryption:"
|
||||
cryptsetup luksFormat "/dev/$INTERNAL_VG/home"
|
||||
|
||||
echo "Please enter your LUKS passphrase again to open the volume:"
|
||||
cryptsetup open "/dev/$INTERNAL_VG/home" luks-home-internal
|
||||
|
||||
# Format the encrypted volume
|
||||
mkfs.ext4 -L home /dev/mapper/luks-home-internal
|
||||
|
||||
log_step "Copying home data from external drive..."
|
||||
|
||||
# Mount source and target
|
||||
mkdir -p "$MOUNT_POINT/external" "$MOUNT_POINT/encrypted"
|
||||
mount "$EXTERNAL_HOME" "$MOUNT_POINT/external"
|
||||
mount /dev/mapper/luks-home-internal "$MOUNT_POINT/encrypted"
|
||||
|
||||
# Copy data directly from external to encrypted volume
|
||||
log_info "Copying ${home_size}GB of home data..."
|
||||
rsync -avHAXS --progress "$MOUNT_POINT/external/" "$MOUNT_POINT/encrypted/"
|
||||
|
||||
# Clean up mounts
|
||||
umount "$MOUNT_POINT/external" "$MOUNT_POINT/encrypted"
|
||||
cryptsetup close luks-home-internal
|
||||
|
||||
log_step "Updating system configuration..."
|
||||
|
||||
# Get the UUID of the LUKS device
|
||||
LUKS_UUID=$(cryptsetup luksUUID "/dev/$INTERNAL_VG/home")
|
||||
|
||||
# Mount the internal root to update configuration
|
||||
mount "/dev/$INTERNAL_VG/root" "$MOUNT_POINT"
|
||||
|
||||
# Update /etc/crypttab
|
||||
echo "luks-home-internal UUID=$LUKS_UUID none luks" >> "$MOUNT_POINT/etc/crypttab"
|
||||
|
||||
# Update /etc/fstab
|
||||
cat > "$MOUNT_POINT/etc/fstab" << EOF
|
||||
# Internal LVM Configuration with LUKS
|
||||
/dev/$INTERNAL_VG/root / ext4 defaults 0 1
|
||||
/dev/$INTERNAL_VG/boot /boot ext4 defaults 0 2
|
||||
/dev/mapper/luks-home-internal /home ext4 defaults 0 2
|
||||
/dev/$INTERNAL_VG/swap none swap sw 0 0
|
||||
/dev/nvme0n1p1 /boot/efi vfat umask=0077 0 1
|
||||
EOF
|
||||
|
||||
# Update initramfs and GRUB to include LUKS support
|
||||
mount --bind /dev "$MOUNT_POINT/dev"
|
||||
mount --bind /proc "$MOUNT_POINT/proc"
|
||||
mount --bind /sys "$MOUNT_POINT/sys"
|
||||
mount --bind /run "$MOUNT_POINT/run"
|
||||
|
||||
log_info "Updating initramfs for LUKS support..."
|
||||
chroot "$MOUNT_POINT" /bin/bash -c "update-initramfs -u -k all"
|
||||
|
||||
log_info "Updating GRUB configuration..."
|
||||
chroot "$MOUNT_POINT" /bin/bash -c "update-grub"
|
||||
|
||||
# Clean up
|
||||
umount "$MOUNT_POINT/dev" "$MOUNT_POINT/proc" "$MOUNT_POINT/sys" "$MOUNT_POINT/run"
|
||||
umount "$MOUNT_POINT"
|
||||
rmdir "$MOUNT_POINT/external" "$MOUNT_POINT/encrypted" "$MOUNT_POINT" 2>/dev/null || true
|
||||
|
||||
log_info "LUKS encryption setup completed successfully!"
|
||||
echo
|
||||
echo -e "${GREEN}SUCCESS!${NC} Home partition is now encrypted with LUKS"
|
||||
echo
|
||||
echo "Configuration summary:"
|
||||
echo "• LUKS UUID: $LUKS_UUID"
|
||||
echo "• Encrypted device: /dev/mapper/luks-home-internal"
|
||||
echo "• Mount point: /home"
|
||||
echo "• Data copied from external M.2"
|
||||
echo
|
||||
echo "Next steps:"
|
||||
echo "1. Reboot and select internal NVMe drive in BIOS"
|
||||
echo "2. You will be prompted for LUKS passphrase during boot"
|
||||
echo "3. Verify that all your home data is accessible"
|
||||
echo
|
||||
echo -e "${YELLOW}Important:${NC} Remember your LUKS passphrase! Without it, your home data will be inaccessible."
|
||||
380
old_scripts/snapshot_migrate_to_internal.sh
Executable file
380
old_scripts/snapshot_migrate_to_internal.sh
Executable file
@@ -0,0 +1,380 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Snapshot-Based LVM Migration Script: External M.2 to Internal NVMe
|
||||
# Creates snapshots of ALL partitions and transfers them to internal drive
|
||||
|
||||
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
|
||||
|
||||
# Configuration
|
||||
EXTERNAL_DRIVE="/dev/sda"
|
||||
INTERNAL_DRIVE="/dev/nvme0n1"
|
||||
SOURCE_VG="migration-vg"
|
||||
TARGET_VG="internal-vg"
|
||||
BACKUP_DIR="/tmp/lvm_snapshot_migration"
|
||||
|
||||
# Logging
|
||||
LOG_FILE="/var/log/lvm_snapshot_migration.log"
|
||||
exec 1> >(tee -a "$LOG_FILE")
|
||||
exec 2> >(tee -a "$LOG_FILE" >&2)
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BLUE}[STEP]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1"
|
||||
}
|
||||
|
||||
# Safety checks
|
||||
safety_checks() {
|
||||
log_step "Performing safety checks..."
|
||||
|
||||
# Check if running as root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log_error "This script must be run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify we're booted from external drive
|
||||
root_device=$(findmnt -n -o SOURCE /)
|
||||
if [[ "$root_device" != "/dev/mapper/migration--vg-root" ]]; then
|
||||
log_error "Not booted from external LVM! Current root: $root_device"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check drives exist
|
||||
if [[ ! -b "$EXTERNAL_DRIVE" ]] || [[ ! -b "$INTERNAL_DRIVE" ]]; then
|
||||
log_error "Required drives not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check LVM tools
|
||||
if ! command -v lvcreate &> /dev/null; then
|
||||
log_error "LVM tools not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Safety checks passed"
|
||||
}
|
||||
|
||||
# Create backup directory and metadata
|
||||
create_backup_metadata() {
|
||||
log_step "Creating backup metadata..."
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Backup LVM metadata
|
||||
vgcfgbackup -f "$BACKUP_DIR/vg_backup" "$SOURCE_VG"
|
||||
|
||||
# Save current LV information
|
||||
lvs "$SOURCE_VG" > "$BACKUP_DIR/lv_info.txt"
|
||||
vgs "$SOURCE_VG" > "$BACKUP_DIR/vg_info.txt"
|
||||
pvs > "$BACKUP_DIR/pv_info.txt"
|
||||
|
||||
# Save partition information
|
||||
lsblk > "$BACKUP_DIR/lsblk_before.txt"
|
||||
|
||||
log_info "Backup metadata created"
|
||||
}
|
||||
|
||||
# Prepare internal drive with identical structure
|
||||
prepare_internal_drive() {
|
||||
log_step "Preparing internal drive..."
|
||||
|
||||
echo -e "${RED}WARNING: This will completely wipe $INTERNAL_DRIVE${NC}"
|
||||
read -p "Type 'YES' to continue: " confirm
|
||||
if [[ "$confirm" != "YES" ]]; then
|
||||
log_error "Migration cancelled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clean up any existing mounts/LVM on internal drive
|
||||
for mount in $(findmnt -n -o TARGET -S "$INTERNAL_DRIVE"* 2>/dev/null || true); do
|
||||
umount "$mount" || true
|
||||
done
|
||||
|
||||
# Deactivate any LUKS devices on internal drive
|
||||
for luks_dev in $(lsblk -o NAME,TYPE | grep crypt | awk '{print $1}' | grep -v "migration" || true); do
|
||||
log_info "Closing LUKS device $luks_dev"
|
||||
cryptsetup close "$luks_dev" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Deactivate any existing VGs on internal drive
|
||||
for vg in $(vgs --noheadings -o vg_name 2>/dev/null | grep -v "$SOURCE_VG" || true); do
|
||||
vgchange -an "$vg" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Remove any PVs on internal drive
|
||||
for pv in $(pvs --noheadings -o pv_name 2>/dev/null | grep "$INTERNAL_DRIVE" || true); do
|
||||
log_info "Removing PV $pv"
|
||||
pvremove -ff "$pv" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Wipe the drive completely
|
||||
wipefs -af "$INTERNAL_DRIVE"
|
||||
dd if=/dev/zero of="$INTERNAL_DRIVE" bs=1M count=100 2>/dev/null || true
|
||||
|
||||
# Create identical partition structure to external drive
|
||||
log_info "Creating partition table..."
|
||||
sfdisk -d "$EXTERNAL_DRIVE" | sfdisk --force "$INTERNAL_DRIVE"
|
||||
|
||||
# Wait for partitions to appear
|
||||
sleep 3
|
||||
partprobe "$INTERNAL_DRIVE"
|
||||
sleep 3
|
||||
|
||||
log_info "Internal drive partitioned"
|
||||
}
|
||||
|
||||
# Create LVM structure on internal drive
|
||||
setup_internal_lvm() {
|
||||
log_step "Setting up LVM on internal drive..."
|
||||
|
||||
# Create physical volume on the LVM partition
|
||||
pvcreate "${INTERNAL_DRIVE}p2" -ff
|
||||
|
||||
# Create volume group with extra space for snapshots
|
||||
vgcreate "$TARGET_VG" "${INTERNAL_DRIVE}p2"
|
||||
|
||||
# Get exact sizes from source LVs
|
||||
declare -A lv_sizes
|
||||
while IFS= read -r line; do
|
||||
lv_name=$(echo "$line" | awk '{print $1}')
|
||||
lv_size=$(echo "$line" | awk '{print $2}')
|
||||
lv_sizes["$lv_name"]="$lv_size"
|
||||
done < <(lvs --noheadings --units b --nosuffix -o lv_name,lv_size "$SOURCE_VG")
|
||||
|
||||
# Create LVs with exact same sizes
|
||||
for lv in root home boot swap; do
|
||||
if [[ -n "${lv_sizes[$lv]:-}" ]]; then
|
||||
size_bytes="${lv_sizes[$lv]}"
|
||||
log_info "Creating LV $lv with size $size_bytes bytes"
|
||||
lvcreate -L "${size_bytes}b" -n "$lv" "$TARGET_VG"
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "Internal LVM structure created"
|
||||
}
|
||||
|
||||
# Create temporary snapshots and transfer via dd
|
||||
transfer_with_snapshots() {
|
||||
log_step "Transferring data using snapshots..."
|
||||
|
||||
# Calculate optimal snapshot size (use available free space)
|
||||
free_space=$(vgs --noheadings --units m --nosuffix -o vg_free "$SOURCE_VG" | tr -d ' ' | tr ',' '.')
|
||||
# Use 80% of free space for snapshots, divided by number of LVs
|
||||
snapshot_size=$(echo "$free_space * 0.8 / 4" | bc | cut -d. -f1)
|
||||
|
||||
if (( snapshot_size < 100 )); then
|
||||
log_error "Not enough free space for snapshots. Need at least 400MB free."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Using ${snapshot_size}MB for each snapshot"
|
||||
|
||||
# Process each logical volume
|
||||
for lv in root home boot swap; do
|
||||
log_step "Processing $lv..."
|
||||
|
||||
# Create snapshot
|
||||
log_info "Creating snapshot of $lv..."
|
||||
if ! lvcreate -L "${snapshot_size}M" -s -n "${lv}_snapshot" "$SOURCE_VG/$lv"; then
|
||||
log_error "Failed to create snapshot for $lv"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Copy data using dd with progress
|
||||
log_info "Copying $lv data to internal drive..."
|
||||
if [[ "$lv" == "swap" ]]; then
|
||||
# For swap, just copy the LV structure
|
||||
dd if="/dev/$SOURCE_VG/${lv}_snapshot" of="/dev/$TARGET_VG/$lv" bs=1M status=progress
|
||||
else
|
||||
# For filesystems, use dd for exact copy
|
||||
dd if="/dev/$SOURCE_VG/${lv}_snapshot" of="/dev/$TARGET_VG/$lv" bs=1M status=progress
|
||||
fi
|
||||
|
||||
# Remove snapshot to free space for next one
|
||||
log_info "Removing snapshot of $lv..."
|
||||
lvremove -f "$SOURCE_VG/${lv}_snapshot"
|
||||
|
||||
log_info "Completed transfer of $lv"
|
||||
done
|
||||
|
||||
log_info "All data transferred successfully"
|
||||
}
|
||||
|
||||
# Setup boot for internal drive
|
||||
configure_internal_boot() {
|
||||
log_step "Configuring boot for internal drive..."
|
||||
|
||||
# Format and mount EFI partition
|
||||
mkfs.fat -F32 "${INTERNAL_DRIVE}p1" || true
|
||||
mkdir -p /mnt/internal_efi
|
||||
mount "${INTERNAL_DRIVE}p1" /mnt/internal_efi
|
||||
|
||||
# Mount internal filesystems
|
||||
mkdir -p /mnt/internal_root /mnt/internal_boot /mnt/internal_home
|
||||
mount "/dev/$TARGET_VG/root" /mnt/internal_root
|
||||
mount "/dev/$TARGET_VG/boot" /mnt/internal_boot
|
||||
mount "/dev/$TARGET_VG/home" /mnt/internal_home
|
||||
|
||||
# Mount EFI in the target system
|
||||
mkdir -p /mnt/internal_root/boot/efi
|
||||
mount "${INTERNAL_DRIVE}p1" /mnt/internal_root/boot/efi
|
||||
|
||||
# Update fstab for new VG
|
||||
log_info "Updating fstab..."
|
||||
cat > /mnt/internal_root/etc/fstab << EOF
|
||||
# Internal LVM Configuration
|
||||
/dev/$TARGET_VG/root / ext4 defaults 0 1
|
||||
/dev/$TARGET_VG/boot /boot ext4 defaults 0 2
|
||||
/dev/$TARGET_VG/home /home ext4 defaults 0 2
|
||||
/dev/$TARGET_VG/swap none swap sw 0 0
|
||||
${INTERNAL_DRIVE}p1 /boot/efi vfat umask=0077 0 1
|
||||
EOF
|
||||
|
||||
# Prepare chroot environment
|
||||
mount --bind /dev /mnt/internal_root/dev
|
||||
mount --bind /proc /mnt/internal_root/proc
|
||||
mount --bind /sys /mnt/internal_root/sys
|
||||
mount --bind /run /mnt/internal_root/run
|
||||
|
||||
# Update initramfs and install GRUB
|
||||
log_info "Updating initramfs and GRUB..."
|
||||
chroot /mnt/internal_root /bin/bash -c "update-initramfs -u -k all"
|
||||
chroot /mnt/internal_root /bin/bash -c "grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Internal-LVM"
|
||||
chroot /mnt/internal_root /bin/bash -c "update-grub"
|
||||
|
||||
# Clean up mounts
|
||||
umount /mnt/internal_root/dev /mnt/internal_root/proc /mnt/internal_root/sys /mnt/internal_root/run
|
||||
umount /mnt/internal_root/boot/efi
|
||||
umount /mnt/internal_efi /mnt/internal_root /mnt/internal_boot /mnt/internal_home
|
||||
|
||||
log_info "Boot configuration completed"
|
||||
}
|
||||
|
||||
# Setup snapshot capabilities on internal drive
|
||||
setup_snapshot_tools() {
|
||||
log_step "Setting up snapshot tools on internal drive..."
|
||||
|
||||
# Remount for tool installation
|
||||
mount "/dev/$TARGET_VG/root" /mnt/internal_root
|
||||
|
||||
# Create snapshot management script
|
||||
cat > /mnt/internal_root/usr/local/bin/lvm-snapshot-manager << 'EOFSCRIPT'
|
||||
#!/bin/bash
|
||||
# LVM Snapshot Manager for Internal Drive
|
||||
|
||||
VG_NAME="internal-vg"
|
||||
|
||||
case "$1" in
|
||||
create)
|
||||
echo "Creating LVM snapshots..."
|
||||
# Calculate available space for snapshots
|
||||
free_space=$(vgs --noheadings --units g --nosuffix -o vg_free "$VG_NAME" | tr -d ' ')
|
||||
snapshot_size=$(echo "$free_space / 4" | bc)G
|
||||
|
||||
lvcreate -L "$snapshot_size" -s -n root_backup "$VG_NAME/root"
|
||||
lvcreate -L "$snapshot_size" -s -n home_backup "$VG_NAME/home"
|
||||
lvcreate -L "$snapshot_size" -s -n boot_backup "$VG_NAME/boot"
|
||||
echo "Snapshots created with size: $snapshot_size each"
|
||||
;;
|
||||
remove)
|
||||
echo "Removing LVM snapshots..."
|
||||
lvremove -f "$VG_NAME/root_backup" 2>/dev/null || true
|
||||
lvremove -f "$VG_NAME/home_backup" 2>/dev/null || true
|
||||
lvremove -f "$VG_NAME/boot_backup" 2>/dev/null || true
|
||||
echo "Snapshots removed"
|
||||
;;
|
||||
list)
|
||||
echo "Current snapshots:"
|
||||
lvs "$VG_NAME" | grep backup || echo "No snapshots found"
|
||||
;;
|
||||
merge)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "Usage: $0 merge <snapshot_name>"
|
||||
exit 1
|
||||
fi
|
||||
echo "Merging snapshot $2..."
|
||||
lvconvert --merge "$VG_NAME/$2"
|
||||
echo "Snapshot merge initiated (reboot required to complete)"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {create|remove|list|merge <snapshot_name>}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
EOFSCRIPT
|
||||
|
||||
chmod +x /mnt/internal_root/usr/local/bin/lvm-snapshot-manager
|
||||
|
||||
umount /mnt/internal_root
|
||||
|
||||
log_info "Snapshot tools installed"
|
||||
}
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
log_info "Cleaning up..."
|
||||
|
||||
# Remove any remaining snapshots
|
||||
for snap in $(lvs --noheadings -o lv_name "$SOURCE_VG" 2>/dev/null | grep snapshot || true); do
|
||||
lvremove -f "$SOURCE_VG/$snap" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# Unmount any remaining mounts
|
||||
for mount in /mnt/internal_*; do
|
||||
umount "$mount" 2>/dev/null || true
|
||||
rmdir "$mount" 2>/dev/null || true
|
||||
done
|
||||
}
|
||||
|
||||
# Main migration function
|
||||
main() {
|
||||
log_info "Starting snapshot-based LVM migration"
|
||||
log_info "Source: $EXTERNAL_DRIVE ($SOURCE_VG) -> Target: $INTERNAL_DRIVE ($TARGET_VG)"
|
||||
|
||||
safety_checks
|
||||
create_backup_metadata
|
||||
prepare_internal_drive
|
||||
setup_internal_lvm
|
||||
transfer_with_snapshots
|
||||
configure_internal_boot
|
||||
setup_snapshot_tools
|
||||
cleanup
|
||||
|
||||
log_info "Migration completed successfully!"
|
||||
echo
|
||||
echo -e "${GREEN}SUCCESS!${NC} Snapshot-based LVM migration completed"
|
||||
echo
|
||||
echo "Next steps:"
|
||||
echo "1. Reboot and select internal drive in BIOS/UEFI"
|
||||
echo "2. Verify all systems working from internal LVM"
|
||||
echo "3. Test snapshot functionality: sudo lvm-snapshot-manager create"
|
||||
echo "4. External M.2 can now be used as backup drive"
|
||||
echo
|
||||
echo "The internal drive now has:"
|
||||
echo "- Complete copy of your current system"
|
||||
echo "- LVM with snapshot capabilities"
|
||||
echo "- Reserved space for future snapshots"
|
||||
}
|
||||
|
||||
# Handle interruption
|
||||
trap cleanup EXIT
|
||||
|
||||
# Run migration
|
||||
main "$@"
|
||||
481
old_scripts/verify_boot_readiness.sh
Normal file
481
old_scripts/verify_boot_readiness.sh
Normal file
@@ -0,0 +1,481 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Boot Verification and Test Script
|
||||
# Validates that a cloned drive can boot properly before declaring success
|
||||
|
||||
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
|
||||
|
||||
TARGET_DRIVE=""
|
||||
WORK_DIR="/mnt/verify_work"
|
||||
|
||||
log() {
|
||||
echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
check_partition_structure() {
|
||||
log "Verifying partition structure..."
|
||||
|
||||
# Check if drive exists and has partitions
|
||||
if [ ! -b "$TARGET_DRIVE" ]; then
|
||||
error "Target drive $TARGET_DRIVE not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local partitions=($(lsblk -pno NAME "$TARGET_DRIVE" | grep -v "^$TARGET_DRIVE$"))
|
||||
|
||||
if [ ${#partitions[@]} -eq 0 ]; then
|
||||
error "No partitions found on $TARGET_DRIVE"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Found ${#partitions[@]} partitions:"
|
||||
for part in "${partitions[@]}"; do
|
||||
local size=$(lsblk -no SIZE "$part")
|
||||
local fstype=$(lsblk -no FSTYPE "$part")
|
||||
local uuid=$(lsblk -no UUID "$part")
|
||||
echo " $part: $size, $fstype, UUID: ${uuid:-'none'}"
|
||||
done
|
||||
|
||||
success "Partition structure verified"
|
||||
return 0
|
||||
}
|
||||
|
||||
check_filesystem_integrity() {
|
||||
log "Checking filesystem integrity..."
|
||||
|
||||
local partitions=($(lsblk -pno NAME "$TARGET_DRIVE" | grep -v "^$TARGET_DRIVE$"))
|
||||
local errors=0
|
||||
|
||||
for part in "${partitions[@]}"; do
|
||||
local fstype=$(lsblk -no FSTYPE "$part")
|
||||
|
||||
case "$fstype" in
|
||||
"ext4"|"ext3"|"ext2")
|
||||
log "Checking ext filesystem on $part..."
|
||||
if e2fsck -n "$part" >/dev/null 2>&1; then
|
||||
success "Filesystem on $part is clean"
|
||||
else
|
||||
warning "Filesystem on $part has errors (read-only check)"
|
||||
((errors++))
|
||||
fi
|
||||
;;
|
||||
"vfat")
|
||||
log "Checking FAT filesystem on $part..."
|
||||
if fsck.fat -v "$part" >/dev/null 2>&1; then
|
||||
success "FAT filesystem on $part is clean"
|
||||
else
|
||||
warning "FAT filesystem on $part has errors"
|
||||
((errors++))
|
||||
fi
|
||||
;;
|
||||
"crypto_LUKS")
|
||||
log "Skipping LUKS partition $part (encrypted)"
|
||||
;;
|
||||
"swap")
|
||||
log "Skipping swap partition $part"
|
||||
;;
|
||||
*)
|
||||
log "Skipping unknown filesystem type '$fstype' on $part"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ $errors -eq 0 ]; then
|
||||
success "All filesystems are clean"
|
||||
return 0
|
||||
else
|
||||
warning "Found $errors filesystem(s) with potential issues"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_boot_files() {
|
||||
log "Checking essential boot files..."
|
||||
|
||||
mkdir -p "$WORK_DIR"
|
||||
local errors=0
|
||||
|
||||
# Find and mount root partition
|
||||
local root_partition=""
|
||||
local boot_partition=""
|
||||
local efi_partition=""
|
||||
|
||||
local partitions=($(lsblk -pno NAME "$TARGET_DRIVE" | grep -v "^$TARGET_DRIVE$"))
|
||||
|
||||
for part in "${partitions[@]}"; do
|
||||
local fstype=$(lsblk -no FSTYPE "$part")
|
||||
local size_bytes=$(lsblk -bno SIZE "$part")
|
||||
|
||||
if [[ "$fstype" == "vfat" && "$size_bytes" -lt 1073741824 ]]; then
|
||||
efi_partition="$part"
|
||||
elif [[ "$fstype" == "ext4" && "$size_bytes" -lt 5368709120 ]]; then
|
||||
boot_partition="$part"
|
||||
elif [[ "$fstype" == "ext4" && "$size_bytes" -gt 5368709120 ]]; then
|
||||
root_partition="$part"
|
||||
elif [[ "$fstype" == "crypto_LUKS" ]]; then
|
||||
# Try to unlock for verification
|
||||
local crypt_name="verify_$(basename "$part")"
|
||||
echo "Found encrypted partition: $part"
|
||||
echo "Please enter password to verify boot files (optional - press Enter to skip):"
|
||||
read -s -t 30 password || {
|
||||
log "Skipping encrypted partition verification"
|
||||
continue
|
||||
}
|
||||
if [ -n "$password" ]; then
|
||||
if echo "$password" | cryptsetup open "$part" "$crypt_name" --key-file=-; then
|
||||
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 for verification: $root_partition"
|
||||
fi
|
||||
else
|
||||
warning "Could not unlock encrypted partition for verification"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$root_partition" ]; then
|
||||
error "Could not find root partition for verification"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Mount root partition
|
||||
if ! mount "$root_partition" "$WORK_DIR"; then
|
||||
error "Could not mount root partition for verification"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Mount boot if separate
|
||||
if [ -n "$boot_partition" ]; then
|
||||
mkdir -p "$WORK_DIR/boot"
|
||||
mount "$boot_partition" "$WORK_DIR/boot" || warning "Could not mount boot partition"
|
||||
fi
|
||||
|
||||
# Mount EFI if exists
|
||||
if [ -n "$efi_partition" ]; then
|
||||
mkdir -p "$WORK_DIR/boot/efi"
|
||||
mount "$efi_partition" "$WORK_DIR/boot/efi" || warning "Could not mount EFI partition"
|
||||
fi
|
||||
|
||||
# Check essential files
|
||||
local essential_files=(
|
||||
"/etc/fstab"
|
||||
"/boot/grub/grub.cfg"
|
||||
"/etc/default/grub"
|
||||
)
|
||||
|
||||
for file in "${essential_files[@]}"; do
|
||||
if [ -f "$WORK_DIR$file" ]; then
|
||||
success "Found: $file"
|
||||
else
|
||||
warning "Missing: $file"
|
||||
((errors++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Check for kernel and initramfs
|
||||
local kernels=($(ls "$WORK_DIR/boot/vmlinuz-"* 2>/dev/null || true))
|
||||
local initramfs=($(ls "$WORK_DIR/boot/initrd.img-"* 2>/dev/null || true))
|
||||
|
||||
if [ ${#kernels[@]} -gt 0 ]; then
|
||||
success "Found ${#kernels[@]} kernel(s)"
|
||||
else
|
||||
warning "No kernels found"
|
||||
((errors++))
|
||||
fi
|
||||
|
||||
if [ ${#initramfs[@]} -gt 0 ]; then
|
||||
success "Found ${#initramfs[@]} initramfs image(s)"
|
||||
else
|
||||
warning "No initramfs images found"
|
||||
((errors++))
|
||||
fi
|
||||
|
||||
# Check EFI bootloader
|
||||
if [ -n "$efi_partition" ]; then
|
||||
if [ -f "$WORK_DIR/boot/efi/EFI/debian/grubx64.efi" ]; then
|
||||
success "Found EFI bootloader"
|
||||
else
|
||||
warning "EFI bootloader not found"
|
||||
((errors++))
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check fstab content
|
||||
if [ -f "$WORK_DIR/etc/fstab" ]; then
|
||||
log "Checking /etc/fstab content..."
|
||||
local fstab_errors=0
|
||||
|
||||
# Check if UUIDs in fstab actually exist
|
||||
while read -r line; do
|
||||
if [[ "$line" =~ ^UUID=([a-fA-F0-9-]+) ]]; then
|
||||
local uuid="${BASH_REMATCH[1]}"
|
||||
if ! blkid | grep -q "$uuid"; then
|
||||
warning "UUID $uuid in fstab not found on system"
|
||||
((fstab_errors++))
|
||||
fi
|
||||
fi
|
||||
done < "$WORK_DIR/etc/fstab"
|
||||
|
||||
if [ $fstab_errors -eq 0 ]; then
|
||||
success "/etc/fstab appears valid"
|
||||
else
|
||||
warning "/etc/fstab has $fstab_errors potential issues"
|
||||
((errors++))
|
||||
fi
|
||||
fi
|
||||
|
||||
# Cleanup mounts
|
||||
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 encrypted volumes
|
||||
for mapper in /dev/mapper/verify_*; do
|
||||
if [ -b "$mapper" ]; then
|
||||
local crypt_name=$(basename "$mapper")
|
||||
cryptsetup close "$crypt_name" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $errors -eq 0 ]; then
|
||||
success "All essential boot files found"
|
||||
return 0
|
||||
else
|
||||
warning "Found $errors potential boot issues"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_grub_configuration() {
|
||||
log "Checking GRUB configuration..."
|
||||
|
||||
# Try to validate GRUB configuration without mounting
|
||||
if grub-probe "$TARGET_DRIVE" >/dev/null 2>&1; then
|
||||
success "GRUB can recognize the drive"
|
||||
else
|
||||
warning "GRUB may have issues recognizing the drive"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
perform_dry_run_boot_test() {
|
||||
log "Performing dry-run boot test..."
|
||||
|
||||
# Check if we can simulate boot process
|
||||
warning "Note: This is a simulation - actual boot test requires reboot"
|
||||
|
||||
# Check boot order in EFI (if available)
|
||||
if command -v efibootmgr >/dev/null 2>&1; then
|
||||
log "Current EFI boot order:"
|
||||
efibootmgr | grep -E "(BootOrder|Boot[0-9]+)" || true
|
||||
fi
|
||||
|
||||
# Test if drive is bootable by checking MBR/GPT
|
||||
if fdisk -l "$TARGET_DRIVE" | grep -q "EFI System"; then
|
||||
success "Drive has EFI System partition (UEFI bootable)"
|
||||
elif fdisk -l "$TARGET_DRIVE" | grep -q "Boot"; then
|
||||
success "Drive has bootable partition"
|
||||
else
|
||||
warning "Drive may not be properly configured for booting"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
comprehensive_verification() {
|
||||
log "Starting comprehensive verification of $TARGET_DRIVE..."
|
||||
|
||||
local total_checks=5
|
||||
local passed_checks=0
|
||||
local failed_checks=0
|
||||
|
||||
echo "Verification Progress:"
|
||||
echo "====================="
|
||||
|
||||
# Test 1: Partition Structure
|
||||
echo -n "1. Partition Structure: "
|
||||
if check_partition_structure; then
|
||||
echo -e "${GREEN}PASS${NC}"
|
||||
((passed_checks++))
|
||||
else
|
||||
echo -e "${RED}FAIL${NC}"
|
||||
((failed_checks++))
|
||||
fi
|
||||
|
||||
# Test 2: Filesystem Integrity
|
||||
echo -n "2. Filesystem Integrity: "
|
||||
if check_filesystem_integrity; then
|
||||
echo -e "${GREEN}PASS${NC}"
|
||||
((passed_checks++))
|
||||
else
|
||||
echo -e "${YELLOW}WARNING${NC}"
|
||||
((passed_checks++)) # Count warnings as pass for now
|
||||
fi
|
||||
|
||||
# Test 3: Boot Files
|
||||
echo -n "3. Essential Boot Files: "
|
||||
if check_boot_files; then
|
||||
echo -e "${GREEN}PASS${NC}"
|
||||
((passed_checks++))
|
||||
else
|
||||
echo -e "${RED}FAIL${NC}"
|
||||
((failed_checks++))
|
||||
fi
|
||||
|
||||
# Test 4: GRUB Configuration
|
||||
echo -n "4. GRUB Configuration: "
|
||||
if check_grub_configuration; then
|
||||
echo -e "${GREEN}PASS${NC}"
|
||||
((passed_checks++))
|
||||
else
|
||||
echo -e "${YELLOW}WARNING${NC}"
|
||||
((passed_checks++)) # Count as pass for compatibility
|
||||
fi
|
||||
|
||||
# Test 5: Boot Readiness
|
||||
echo -n "5. Boot Readiness: "
|
||||
if perform_dry_run_boot_test; then
|
||||
echo -e "${GREEN}PASS${NC}"
|
||||
((passed_checks++))
|
||||
else
|
||||
echo -e "${RED}FAIL${NC}"
|
||||
((failed_checks++))
|
||||
fi
|
||||
|
||||
echo "====================="
|
||||
echo "Verification Summary:"
|
||||
echo " Passed: $passed_checks/$total_checks"
|
||||
echo " Failed: $failed_checks/$total_checks"
|
||||
|
||||
if [ $failed_checks -eq 0 ]; then
|
||||
success "All verification checks passed! Drive should boot properly."
|
||||
return 0
|
||||
elif [ $failed_checks -le 2 ]; then
|
||||
warning "Some checks failed but drive might still boot. Consider running boot repair."
|
||||
return 1
|
||||
else
|
||||
error "Multiple critical checks failed. Drive is unlikely to boot properly."
|
||||
return 2
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_verification() {
|
||||
log "Cleaning up verification environment..."
|
||||
|
||||
# Unmount any remaining mounts
|
||||
umount "$WORK_DIR" 2>/dev/null || true
|
||||
|
||||
# Close any encrypted volumes
|
||||
for mapper in /dev/mapper/verify_*; 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"
|
||||
}
|
||||
|
||||
main() {
|
||||
echo -e "${GREEN}=== Boot Verification and Test Script ===${NC}"
|
||||
echo "This script validates that a cloned drive can boot properly"
|
||||
echo
|
||||
|
||||
# Get target drive
|
||||
echo "Available drives:"
|
||||
lsblk -dpno NAME,SIZE,MODEL | grep -v "loop\|ram"
|
||||
echo
|
||||
|
||||
read -p "Enter the drive to verify (e.g., /dev/sdb): " TARGET_DRIVE
|
||||
|
||||
if [ ! -b "$TARGET_DRIVE" ]; then
|
||||
error "Drive $TARGET_DRIVE not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Selected drive for verification: $TARGET_DRIVE"
|
||||
lsblk "$TARGET_DRIVE"
|
||||
echo
|
||||
|
||||
read -p "Verify this drive? [y/N]: " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
error "Verification cancelled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Perform comprehensive verification
|
||||
if comprehensive_verification; then
|
||||
echo
|
||||
echo -e "${GREEN}🎉 VERIFICATION SUCCESSFUL! 🎉${NC}"
|
||||
echo "The cloned drive passed all verification checks."
|
||||
echo "It should boot properly when set as the primary boot device."
|
||||
echo
|
||||
echo -e "${BLUE}Next steps:${NC}"
|
||||
echo "1. Reboot your system"
|
||||
echo "2. Enter BIOS/UEFI setup"
|
||||
echo "3. Set $TARGET_DRIVE as the first boot device"
|
||||
echo "4. Save and exit BIOS/UEFI"
|
||||
echo "5. System should boot from cloned drive"
|
||||
|
||||
if lsblk "$TARGET_DRIVE" | grep -q "crypto_LUKS"; then
|
||||
echo
|
||||
echo -e "${YELLOW}Note:${NC} System will ask for LUKS password during boot (this is normal)"
|
||||
fi
|
||||
|
||||
cleanup_verification
|
||||
exit 0
|
||||
else
|
||||
local exit_code=$?
|
||||
echo
|
||||
if [ $exit_code -eq 1 ]; then
|
||||
echo -e "${YELLOW}⚠️ VERIFICATION COMPLETED WITH WARNINGS ⚠️${NC}"
|
||||
echo "The drive might boot but some issues were detected."
|
||||
echo "Consider running the boot repair script before attempting to boot."
|
||||
echo
|
||||
echo "Run: ./boot_repair_tools.sh"
|
||||
else
|
||||
echo -e "${RED}❌ VERIFICATION FAILED ❌${NC}"
|
||||
echo "The drive is unlikely to boot properly in its current state."
|
||||
echo "Please run the boot repair script to fix issues."
|
||||
echo
|
||||
echo "Run: ./boot_repair_tools.sh"
|
||||
fi
|
||||
|
||||
cleanup_verification
|
||||
exit $exit_code
|
||||
fi
|
||||
}
|
||||
|
||||
# Trap to ensure cleanup on exit
|
||||
trap cleanup_verification EXIT
|
||||
|
||||
main "$@"
|
||||
57
old_scripts/verify_internal_boot.sh
Executable file
57
old_scripts/verify_internal_boot.sh
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
# Verify Internal NVMe Boot Configuration
|
||||
# This script checks if the internal drive (nvme0n1) is properly configured to boot
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== Internal Drive Boot Verification ==="
|
||||
echo ""
|
||||
|
||||
echo "1. Checking EFI partition mount..."
|
||||
df -h /boot/efi | grep -E "Filesystem|nvme0n1p1" && echo "✓ EFI mounted from internal drive" || echo "✗ EFI not on internal drive"
|
||||
echo ""
|
||||
|
||||
echo "2. Checking boot partition mount..."
|
||||
df -h /boot | grep -E "Filesystem|internal--vg-boot" && echo "✓ Boot mounted from internal-vg" || echo "✗ Boot not on internal-vg"
|
||||
echo ""
|
||||
|
||||
echo "3. Checking GRUB installation..."
|
||||
if [ -f /boot/efi/EFI/ubuntu/shimx64.efi ]; then
|
||||
echo "✓ GRUB bootloader found in EFI"
|
||||
else
|
||||
echo "✗ GRUB bootloader missing"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "4. Checking EFI boot entries..."
|
||||
efibootmgr | grep -i ubuntu && echo "✓ Ubuntu boot entry exists" || echo "✗ No Ubuntu boot entry"
|
||||
echo ""
|
||||
|
||||
echo "5. Checking GRUB environment..."
|
||||
sudo grub-editenv list
|
||||
if [ -z "$(sudo grub-editenv list)" ]; then
|
||||
echo "✓ GRUB environment clean (no boot failure flags)"
|
||||
else
|
||||
echo "⚠ GRUB environment has settings"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "6. Checking current boot device..."
|
||||
echo "Currently booted from: $(findmnt -n -o SOURCE /)"
|
||||
echo ""
|
||||
|
||||
echo "7. Internal drive UUIDs..."
|
||||
echo "Root: $(sudo blkid /dev/internal-vg/root -s UUID -o value)"
|
||||
echo "Boot: $(sudo blkid /dev/internal-vg/boot -s UUID -o value)"
|
||||
echo ""
|
||||
|
||||
echo "8. Checking /etc/fstab for internal drive..."
|
||||
grep -E "internal-vg|757B-A377" /etc/fstab && echo "✓ Internal volumes in fstab" || echo "⚠ Check fstab configuration"
|
||||
echo ""
|
||||
|
||||
echo "=== Verification Complete ==="
|
||||
echo ""
|
||||
echo "To test boot from internal drive:"
|
||||
echo "1. Remove/disconnect the external USB M.2 drive"
|
||||
echo "2. Reboot the system"
|
||||
echo "3. System should boot from internal nvme0n1"
|
||||
147
old_scripts/verify_internal_config.sh
Executable file
147
old_scripts/verify_internal_config.sh
Executable file
@@ -0,0 +1,147 @@
|
||||
#!/bin/bash
|
||||
# Final Internal Drive Configuration Check
|
||||
# Run this after rebooting to verify everything is working
|
||||
|
||||
echo "═══════════════════════════════════════════════════════"
|
||||
echo " Internal NVMe Drive Configuration Verification"
|
||||
echo "═══════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
# Color codes
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
success_count=0
|
||||
total_checks=7
|
||||
|
||||
echo "Running system checks..."
|
||||
echo ""
|
||||
|
||||
# Check 1: Root filesystem
|
||||
echo -n "1. Root filesystem from internal drive... "
|
||||
if mount | grep -q "internal--vg-root on / "; then
|
||||
echo -e "${GREEN}✓ PASS${NC}"
|
||||
((success_count++))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC}"
|
||||
mount | grep " / "
|
||||
fi
|
||||
|
||||
# Check 2: Boot filesystem
|
||||
echo -n "2. Boot filesystem from internal drive... "
|
||||
if mount | grep -q "internal--vg-boot on /boot "; then
|
||||
echo -e "${GREEN}✓ PASS${NC}"
|
||||
((success_count++))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC}"
|
||||
mount | grep " /boot "
|
||||
fi
|
||||
|
||||
# Check 3: Home filesystem
|
||||
echo -n "3. Home filesystem from internal drive... "
|
||||
HOME_DEVICE=$(sudo cryptsetup status luks-home-internal 2>/dev/null | grep "device:" | awk '{print $2}')
|
||||
if [[ "$HOME_DEVICE" == "/dev/mapper/internal--vg-home" ]]; then
|
||||
echo -e "${GREEN}✓ PASS${NC}"
|
||||
((success_count++))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC}"
|
||||
echo " Currently using: $HOME_DEVICE"
|
||||
fi
|
||||
|
||||
# Check 4: EFI partition
|
||||
echo -n "4. EFI partition from internal drive... "
|
||||
if mount | grep -q "nvme0n1p1 on /boot/efi"; then
|
||||
echo -e "${GREEN}✓ PASS${NC}"
|
||||
((success_count++))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC}"
|
||||
mount | grep "/boot/efi"
|
||||
fi
|
||||
|
||||
# Check 5: GRUB installation
|
||||
echo -n "5. GRUB bootloader installed... "
|
||||
if [ -f /boot/efi/EFI/ubuntu/shimx64.efi ] && [ -f /boot/efi/EFI/ubuntu/grubx64.efi ]; then
|
||||
echo -e "${GREEN}✓ PASS${NC}"
|
||||
((success_count++))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC}"
|
||||
fi
|
||||
|
||||
# Check 6: EFI boot order
|
||||
echo -n "6. Ubuntu first in boot order... "
|
||||
FIRST_BOOT=$(efibootmgr | grep BootOrder | cut -d':' -f2 | tr -d ' ' | cut -d',' -f1)
|
||||
if [ "$FIRST_BOOT" = "0001" ] && efibootmgr | grep -q "Boot0001.*Ubuntu"; then
|
||||
echo -e "${GREEN}✓ PASS${NC}"
|
||||
((success_count++))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC}"
|
||||
echo " First boot entry: $FIRST_BOOT"
|
||||
fi
|
||||
|
||||
# Check 7: External drive status
|
||||
echo -n "7. External drive NOT in use for system... "
|
||||
if ! mount | grep -q "migration--vg.*on /"; then
|
||||
echo -e "${GREEN}✓ PASS${NC}"
|
||||
((success_count++))
|
||||
else
|
||||
echo -e "${YELLOW}⚠ WARNING${NC}"
|
||||
echo " External drive (migration-vg) is mounted for system use"
|
||||
mount | grep migration-vg
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════════════"
|
||||
echo " Results: $success_count/$total_checks checks passed"
|
||||
echo "═══════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
if [ $success_count -eq $total_checks ]; then
|
||||
echo -e "${GREEN}✓ SUCCESS!${NC} Internal drive is fully configured and operational."
|
||||
echo ""
|
||||
echo "Your system is now running entirely from the internal NVMe drive."
|
||||
echo "The external M.2 drive can be used as a backup."
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Test disconnecting the external drive and rebooting"
|
||||
echo " 2. Run backup: sudo ./lvm_block_backup.sh"
|
||||
echo ""
|
||||
elif [ $success_count -ge 5 ]; then
|
||||
echo -e "${YELLOW}⚠ PARTIAL SUCCESS${NC} - Most checks passed but some issues remain."
|
||||
echo ""
|
||||
echo "Review the failed checks above and consult INTERNAL_DRIVE_RECOVERY.md"
|
||||
echo ""
|
||||
else
|
||||
echo -e "${RED}✗ CONFIGURATION ISSUES DETECTED${NC}"
|
||||
echo ""
|
||||
echo "Several checks failed. You may need to reboot for changes to take effect."
|
||||
echo "If problems persist after reboot, review INTERNAL_DRIVE_RECOVERY.md"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Additional info
|
||||
echo "═══════════════════════════════════════════════════════"
|
||||
echo " Detailed Information"
|
||||
echo "═══════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
echo "Current mount points:"
|
||||
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT | grep -E "NAME|nvme0n1|sda"
|
||||
echo ""
|
||||
|
||||
echo "LUKS device mapping:"
|
||||
sudo cryptsetup status luks-home-internal 2>/dev/null | grep -E "type|device|mode"
|
||||
echo ""
|
||||
|
||||
echo "EFI boot configuration:"
|
||||
efibootmgr | grep -E "BootCurrent|BootOrder|Boot0001"
|
||||
echo ""
|
||||
|
||||
if [ -b /dev/sda ]; then
|
||||
echo -e "${YELLOW}Note:${NC} External drive (sda) is connected."
|
||||
echo " To test full independence, shutdown and disconnect it."
|
||||
else
|
||||
echo -e "${GREEN}Note:${NC} External drive is not connected - full independence confirmed!"
|
||||
fi
|
||||
echo ""
|
||||
@@ -1,108 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Create PLUG-AND-PLAY Clonezilla USB - No commands to remember!
|
||||
|
||||
set -e
|
||||
|
||||
USB_DRIVE="/dev/sda"
|
||||
CLONEZILLA_ISO="clonezilla-live-3.1.0-22-amd64.iso"
|
||||
|
||||
echo "Creating PLUG-AND-PLAY Disaster Recovery USB"
|
||||
echo "============================================"
|
||||
echo "• Boot USB = Automatic backup starts in 10 seconds"
|
||||
echo "• No commands to remember"
|
||||
echo "• 15-20 minute backup with maximum speed"
|
||||
echo
|
||||
|
||||
# Use existing ISO
|
||||
if [[ ! -f "$CLONEZILLA_ISO" ]]; then
|
||||
echo "ERROR: $CLONEZILLA_ISO not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -p "Continue to create plug-and-play USB? (yes/no): " confirm
|
||||
if [[ "$confirm" != "yes" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Unmount and recreate USB
|
||||
sudo umount "${USB_DRIVE}"* 2>/dev/null || true
|
||||
|
||||
# Single partition for simplicity
|
||||
sudo parted "$USB_DRIVE" --script mklabel msdos
|
||||
sudo parted "$USB_DRIVE" --script mkpart primary fat32 1MiB 100%
|
||||
sudo parted "$USB_DRIVE" --script set 1 boot on
|
||||
|
||||
# Format
|
||||
USB_PART="${USB_DRIVE}1"
|
||||
sudo mkfs.fat -F32 -n "AUTOBACKUP" "$USB_PART"
|
||||
|
||||
# Mount and install
|
||||
USB_MOUNT="/tmp/autobackup_$$"
|
||||
ISO_MOUNT="/tmp/clonezilla_iso_$$"
|
||||
|
||||
sudo mkdir -p "$USB_MOUNT" "$ISO_MOUNT"
|
||||
sudo mount "$USB_PART" "$USB_MOUNT"
|
||||
sudo mount -o loop "$CLONEZILLA_ISO" "$ISO_MOUNT"
|
||||
|
||||
echo "Installing Clonezilla..."
|
||||
sudo cp -R "$ISO_MOUNT"/* "$USB_MOUNT/"
|
||||
|
||||
echo "Installing GRUB..."
|
||||
sudo grub-install --target=i386-pc --boot-directory="$USB_MOUNT/boot" "$USB_DRIVE"
|
||||
|
||||
# Create PLUG-AND-PLAY menu
|
||||
sudo tee "$USB_MOUNT/boot/grub/grub.cfg" > /dev/null << 'EOF'
|
||||
set timeout=10
|
||||
set default=0
|
||||
|
||||
menuentry "🚀 AUTO BACKUP (10 second countdown)" {
|
||||
linux /live/vmlinuz boot=live union=overlay username=user config components quiet noswap edd=on nomodeset ocs_live_run="ocs-sr -q2 -j2 -z0 -i 0 -sfsck -scs -rescue -batch -p reboot savedisk AUTO_$(date +%Y%m%d_%H%M%S) /dev/nvme0n1" ocs_live_extra_param="" keyboard-layouts= ocs_live_batch="yes" locales= vga=normal nosplash
|
||||
initrd /live/initrd.img
|
||||
}
|
||||
|
||||
menuentry "🔧 Manual Clonezilla (for restore)" {
|
||||
linux /live/vmlinuz boot=live union=overlay username=user config components quiet noswap edd=on nomodeset ocs_live_run="ocs-live-general" ocs_live_extra_param="" keyboard-layouts= ocs_live_batch="no" locales= vga=normal nosplash
|
||||
initrd /live/initrd.img
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create instructions
|
||||
sudo tee "$USB_MOUNT/PLUG_AND_PLAY_INSTRUCTIONS.txt" > /dev/null << 'EOF'
|
||||
PLUG AND PLAY DISASTER RECOVERY USB
|
||||
===================================
|
||||
|
||||
🚀 BACKUP INSTRUCTIONS:
|
||||
1. Boot from this USB
|
||||
2. Wait 10 seconds (auto-backup starts)
|
||||
3. Wait 15-20 minutes
|
||||
4. System reboots automatically
|
||||
|
||||
🔧 RESTORE INSTRUCTIONS:
|
||||
1. Boot from this USB
|
||||
2. Select "Manual Clonezilla"
|
||||
3. Choose "device-image" -> "restoredisk"
|
||||
4. Find your backup image
|
||||
5. Select target drive
|
||||
6. Confirm and wait
|
||||
|
||||
NO COMMANDS TO REMEMBER!
|
||||
Just boot and wait 10 seconds.
|
||||
|
||||
Backup will be saved to this USB drive.
|
||||
Created: $(date)
|
||||
EOF
|
||||
|
||||
sudo mkdir -p "$USB_MOUNT/home/partimag"
|
||||
|
||||
# Cleanup
|
||||
sudo umount "$ISO_MOUNT" "$USB_MOUNT"
|
||||
sudo rmdir "$USB_MOUNT" "$ISO_MOUNT"
|
||||
|
||||
echo
|
||||
echo "✅ PLUG-AND-PLAY USB CREATED!"
|
||||
echo "✅ Boot USB = 10 second countdown then auto-backup"
|
||||
echo "✅ No commands to remember in disaster recovery"
|
||||
echo "✅ Maximum speed backup (15-20 minutes)"
|
||||
echo "✅ Instructions stored on USB drive"
|
||||
echo
|
||||
echo "DISASTER RECOVERY: Just boot from USB and wait!"
|
||||
@@ -1,315 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Live System Preparation Script for LVM Migration
|
||||
# This script prepares a live USB system with all necessary tools for LVM migration
|
||||
|
||||
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
|
||||
|
||||
log() {
|
||||
echo -e "${BLUE}[$(date '+%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"
|
||||
}
|
||||
|
||||
check_live_system() {
|
||||
log "Checking if running from live system..."
|
||||
|
||||
# Check multiple indicators of live system
|
||||
local is_live=false
|
||||
|
||||
# Check for common live system indicators
|
||||
if [ -f "/etc/casper.conf" ] || [ -f "/lib/live/mount/medium" ]; then
|
||||
is_live=true
|
||||
fi
|
||||
|
||||
# Check if root filesystem is on loop, overlay, or tmpfs
|
||||
local root_device=$(df / | tail -1 | awk '{print $1}')
|
||||
if [[ "$root_device" == *"loop"* ]] || [[ "$root_device" == *"overlay"* ]] || [[ "$root_device" == *"tmpfs"* ]]; then
|
||||
is_live=true
|
||||
fi
|
||||
|
||||
# Check for live system processes
|
||||
if pgrep -f "casper" >/dev/null 2>&1 || pgrep -f "live-boot" >/dev/null 2>&1; then
|
||||
is_live=true
|
||||
fi
|
||||
|
||||
if [ "$is_live" = true ]; then
|
||||
success "Confirmed: Running from live system"
|
||||
else
|
||||
warning "This doesn't appear to be a live system!"
|
||||
echo "For safety, LVM migration should be run from a live USB system."
|
||||
read -p "Continue anyway? [y/N] " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
error "Operation aborted. Please boot from a live USB system."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
update_package_lists() {
|
||||
log "Updating package lists..."
|
||||
|
||||
if command -v apt >/dev/null 2>&1; then
|
||||
apt update || warning "Failed to update apt package lists"
|
||||
elif command -v pacman >/dev/null 2>&1; then
|
||||
pacman -Sy || warning "Failed to update pacman package lists"
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
dnf makecache || warning "Failed to update dnf package cache"
|
||||
else
|
||||
warning "Unknown package manager - manual tool installation may be required"
|
||||
fi
|
||||
}
|
||||
|
||||
install_required_tools() {
|
||||
log "Installing required tools for LVM migration..."
|
||||
|
||||
# Detect distribution for package name variations
|
||||
local distro="unknown"
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
distro="$ID"
|
||||
log "Detected distribution: $PRETTY_NAME"
|
||||
fi
|
||||
|
||||
# Define package groups with alternatives for different distributions
|
||||
local package_groups=(
|
||||
"lvm:lvm2,lvm"
|
||||
"cryptsetup:cryptsetup,cryptsetup-bin"
|
||||
"rsync:rsync"
|
||||
"parted:parted"
|
||||
"pv:pv,pipe-viewer"
|
||||
"grub-efi:grub-efi-amd64,grub-efi,grub-efi-amd64-bin"
|
||||
"grub-pc:grub-pc-bin,grub-pc"
|
||||
"grub-common:grub-common,grub2-common"
|
||||
"e2fsprogs:e2fsprogs"
|
||||
"dosfstools:dosfstools,mtools"
|
||||
"util-linux:util-linux"
|
||||
"coreutils:coreutils"
|
||||
"bc:bc"
|
||||
"initramfs:initramfs-tools,dracut"
|
||||
"udev:udev,systemd-udev"
|
||||
"kmod:kmod,module-init-tools"
|
||||
)
|
||||
|
||||
local missing_tools=()
|
||||
local packages_to_install=()
|
||||
|
||||
# Check which tools are missing
|
||||
for tool_spec in "${tools_to_check[@]}"; do
|
||||
local tool=$(echo "$tool_spec" | cut -d: -f1)
|
||||
local packages=$(echo "$tool_spec" | cut -d: -f2)
|
||||
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
missing_tools+=("$tool")
|
||||
# Add packages (handle comma-separated list)
|
||||
IFS=',' read -ra PKGS <<< "$packages"
|
||||
for pkg in "${PKGS[@]}"; do
|
||||
if [[ ! " ${packages_to_install[@]} " =~ " ${pkg} " ]]; then
|
||||
packages_to_install+=("$pkg")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#missing_tools[@]} -eq 0 ]; then
|
||||
success "All required tools are already available"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Missing tools: ${missing_tools[*]}"
|
||||
echo "Will attempt to install: ${packages_to_install[*]}"
|
||||
|
||||
# Install packages based on available package manager
|
||||
if command -v apt >/dev/null 2>&1; then
|
||||
log "Installing packages with apt..."
|
||||
|
||||
# Update package lists first
|
||||
apt update || warning "Failed to update package lists"
|
||||
|
||||
# Function to try installing packages with alternatives
|
||||
try_install() {
|
||||
local desc="$1"
|
||||
local packages_str="$2"
|
||||
IFS=',' read -ra packages <<< "$packages_str"
|
||||
|
||||
log "Installing $desc..."
|
||||
for pkg in "${packages[@]}"; do
|
||||
if apt install -y "$pkg" >/dev/null 2>&1; then
|
||||
success "Installed $pkg for $desc"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
warning "Failed to install any package for $desc (tried: ${packages_str//,/, })"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Install packages by groups
|
||||
for group in "${package_groups[@]}"; do
|
||||
local desc="${group%:*}"
|
||||
local packages="${group#*:}"
|
||||
try_install "$desc" "$packages"
|
||||
done
|
||||
elif command -v pacman >/dev/null 2>&1; then
|
||||
log "Installing packages with pacman..."
|
||||
pacman -S --noconfirm "${packages_to_install[@]}" || {
|
||||
warning "Some packages failed to install via pacman"
|
||||
}
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
log "Installing packages with dnf..."
|
||||
dnf install -y "${packages_to_install[@]}" || {
|
||||
warning "Some packages failed to install via dnf"
|
||||
}
|
||||
else
|
||||
warning "Unknown package manager. Please install these packages manually:"
|
||||
echo " ${packages_to_install[*]}"
|
||||
fi
|
||||
|
||||
# Verify installation
|
||||
local still_missing=()
|
||||
for tool_spec in "${tools_to_check[@]}"; do
|
||||
local tool=$(echo "$tool_spec" | cut -d: -f1)
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
still_missing+=("$tool")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#still_missing[@]} -eq 0 ]; then
|
||||
success "All required tools are now available"
|
||||
else
|
||||
error "Still missing tools: ${still_missing[*]}. Please install them manually."
|
||||
fi
|
||||
}
|
||||
|
||||
enable_lvm_kernel_modules() {
|
||||
log "Enabling LVM kernel modules..."
|
||||
|
||||
local modules=("dm_mod" "dm_crypt" "dm_snapshot")
|
||||
|
||||
for module in "${modules[@]}"; do
|
||||
if ! lsmod | grep -q "^$module"; then
|
||||
log "Loading kernel module: $module"
|
||||
modprobe "$module" || warning "Failed to load $module module"
|
||||
else
|
||||
log "Module $module already loaded"
|
||||
fi
|
||||
done
|
||||
|
||||
# Start LVM services if available
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl start lvm2-monitor 2>/dev/null || true
|
||||
systemctl start lvm2-lvmetad 2>/dev/null || true
|
||||
fi
|
||||
|
||||
success "LVM kernel modules enabled"
|
||||
}
|
||||
|
||||
check_drive_availability() {
|
||||
log "Checking for available drives..."
|
||||
|
||||
# List all available block devices
|
||||
echo "Available drives:"
|
||||
lsblk -dpno NAME,SIZE,MODEL,VENDOR | grep -E "sd[a-z]|nvme[0-9]|mmcblk[0-9]" | while read line; do
|
||||
echo " $line"
|
||||
done
|
||||
|
||||
# Count drives
|
||||
local drive_count=$(lsblk -dpno NAME | grep -E "sd[a-z]|nvme[0-9]|mmcblk[0-9]" | wc -l)
|
||||
|
||||
if [ "$drive_count" -lt 2 ]; then
|
||||
warning "Only $drive_count drive(s) detected. Migration requires at least 2 drives:"
|
||||
echo " 1. Internal drive (source)"
|
||||
echo " 2. External M.2 SSD (target)"
|
||||
echo
|
||||
echo "Please connect your external M.2 SSD and try again."
|
||||
exit 1
|
||||
else
|
||||
success "Found $drive_count drives - sufficient for migration"
|
||||
fi
|
||||
}
|
||||
|
||||
create_migration_workspace() {
|
||||
log "Creating migration workspace..."
|
||||
|
||||
local workspace="/tmp/lvm-migration"
|
||||
mkdir -p "$workspace"/{scripts,logs,mounts}
|
||||
|
||||
# Copy migration script to workspace if it exists
|
||||
if [ -f "./migrate_to_lvm.sh" ]; then
|
||||
cp "./migrate_to_lvm.sh" "$workspace/scripts/"
|
||||
chmod +x "$workspace/scripts/migrate_to_lvm.sh"
|
||||
success "Migration script copied to workspace"
|
||||
fi
|
||||
|
||||
# Create useful aliases
|
||||
cat > "$workspace/aliases.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# Useful aliases for LVM migration
|
||||
|
||||
alias ll='ls -la'
|
||||
alias drives='lsblk -dpno NAME,SIZE,MODEL,VENDOR'
|
||||
alias mounts='mount | grep -E "sd[a-z]|nvme|loop|mapper"'
|
||||
alias lvminfo='pvs && vgs && lvs'
|
||||
alias migration='cd /tmp/lvm-migration && ls -la'
|
||||
|
||||
echo "Migration workspace aliases loaded:"
|
||||
echo " drives - List all available drives"
|
||||
echo " mounts - Show mounted filesystems"
|
||||
echo " lvminfo - Display LVM information"
|
||||
echo " migration - Go to migration workspace"
|
||||
EOF
|
||||
|
||||
success "Migration workspace created at $workspace"
|
||||
echo "To load helpful aliases: source $workspace/aliases.sh"
|
||||
}
|
||||
|
||||
main() {
|
||||
echo -e "${GREEN}=== Live System Preparation for LVM Migration ===${NC}"
|
||||
echo "This script prepares your live USB system for LVM migration"
|
||||
echo
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
error "This script must be run as root. Use: sudo $0"
|
||||
fi
|
||||
|
||||
check_live_system
|
||||
update_package_lists
|
||||
install_required_tools
|
||||
enable_lvm_kernel_modules
|
||||
check_drive_availability
|
||||
create_migration_workspace
|
||||
|
||||
success "Live system preparation completed!"
|
||||
echo
|
||||
echo -e "${GREEN}Next steps:${NC}"
|
||||
echo "1. Connect your external M.2 SSD if not already connected"
|
||||
echo "2. Run the LVM migration script:"
|
||||
echo " sudo ./migrate_to_lvm.sh"
|
||||
echo "3. Follow the interactive prompts to complete migration"
|
||||
echo
|
||||
echo -e "${YELLOW}Important reminders:${NC}"
|
||||
echo "• Ensure your external M.2 SSD is large enough for your system"
|
||||
echo "• The migration will DESTROY all data on the target drive"
|
||||
echo "• Your original internal drive will remain unchanged as backup"
|
||||
echo "• After migration, update BIOS boot order to boot from external drive"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -1,271 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Restore backup tools after cloning operation
|
||||
# This script runs automatically after backup to preserve tools on external drive
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_status() {
|
||||
echo -e "${BLUE}[$(date '+%H:%M:%S')]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Get the external drive from parameter or auto-detect
|
||||
EXTERNAL_DRIVE="$1"
|
||||
if [[ -z "$EXTERNAL_DRIVE" ]]; then
|
||||
print_status "Auto-detecting external drive..."
|
||||
EXTERNAL_DRIVE=$(lsblk -d -o NAME,TRAN | grep usb | awk '{print "/dev/" $1}' | head -1)
|
||||
fi
|
||||
|
||||
if [[ -z "$EXTERNAL_DRIVE" ]]; then
|
||||
print_error "Could not detect external drive"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_status "Restoring backup tools to $EXTERNAL_DRIVE"
|
||||
|
||||
# Find the tools partition (look for BACKUP_TOOLS label first)
|
||||
TOOLS_PARTITION=$(blkid -L "BACKUP_TOOLS" 2>/dev/null || echo "")
|
||||
|
||||
# If not found by label, try to find the last partition on the external drive
|
||||
if [[ -z "$TOOLS_PARTITION" ]]; then
|
||||
# Get all partitions on the external drive
|
||||
mapfile -t partitions < <(lsblk -n -o NAME "$EXTERNAL_DRIVE" | grep -v "^$(basename "$EXTERNAL_DRIVE")$")
|
||||
|
||||
if [[ ${#partitions[@]} -gt 0 ]]; then
|
||||
# Check the last partition
|
||||
last_partition="/dev/${partitions[-1]}"
|
||||
|
||||
# Check if it's small (likely our tools partition)
|
||||
partition_size=$(lsblk -n -o SIZE "$last_partition" | tr -d ' ')
|
||||
if [[ "$partition_size" == *M ]] && [[ ${partition_size%M} -le 1024 ]]; then
|
||||
TOOLS_PARTITION="$last_partition"
|
||||
print_status "Found potential tools partition: $TOOLS_PARTITION ($partition_size)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# If still no tools partition found, create one
|
||||
if [[ -z "$TOOLS_PARTITION" ]]; then
|
||||
print_warning "No tools partition found. Creating one..."
|
||||
|
||||
# Create 512MB partition at the end
|
||||
if command -v parted >/dev/null 2>&1; then
|
||||
parted "$EXTERNAL_DRIVE" --script mkpart primary ext4 -512MiB 100% || {
|
||||
print_warning "Could not create partition. Backup tools will not be preserved."
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Wait for partition to appear
|
||||
sleep 2
|
||||
|
||||
# Get the new partition
|
||||
mapfile -t partitions < <(lsblk -n -o NAME "$EXTERNAL_DRIVE" | grep -v "^$(basename "$EXTERNAL_DRIVE")$")
|
||||
TOOLS_PARTITION="/dev/${partitions[-1]}"
|
||||
|
||||
# Format it
|
||||
mkfs.ext4 -L "BACKUP_TOOLS" "$TOOLS_PARTITION" -F >/dev/null 2>&1 || {
|
||||
print_warning "Could not format tools partition"
|
||||
exit 0
|
||||
}
|
||||
|
||||
print_success "Created tools partition: $TOOLS_PARTITION"
|
||||
else
|
||||
print_warning "parted not available. Cannot create tools partition."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Mount tools partition
|
||||
MOUNT_POINT="/tmp/backup_tools_restore_$$"
|
||||
mkdir -p "$MOUNT_POINT"
|
||||
|
||||
mount "$TOOLS_PARTITION" "$MOUNT_POINT" 2>/dev/null || {
|
||||
print_error "Could not mount tools partition $TOOLS_PARTITION"
|
||||
rmdir "$MOUNT_POINT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Ensure we unmount on exit
|
||||
trap 'umount "$MOUNT_POINT" 2>/dev/null; rmdir "$MOUNT_POINT" 2>/dev/null' EXIT
|
||||
|
||||
# Get the directory where this script is located (source of backup tools)
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Install/update backup tools
|
||||
if [[ -d "$MOUNT_POINT/backup_system" ]]; then
|
||||
print_status "Updating existing backup tools..."
|
||||
# Sync changes, excluding git and cache files
|
||||
rsync -av --delete --exclude='.git' --exclude='__pycache__' --exclude='*.pyc' \
|
||||
"$SCRIPT_DIR/" "$MOUNT_POINT/backup_system/"
|
||||
else
|
||||
print_status "Installing backup tools for first time..."
|
||||
mkdir -p "$MOUNT_POINT/backup_system"
|
||||
cp -r "$SCRIPT_DIR"/* "$MOUNT_POINT/backup_system/"
|
||||
fi
|
||||
|
||||
# Create portable launcher if it doesn't exist
|
||||
if [[ ! -f "$MOUNT_POINT/backup_system/launch_backup_tools.sh" ]]; then
|
||||
cat > "$MOUNT_POINT/backup_system/launch_backup_tools.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# Portable launcher for backup tools
|
||||
|
||||
# Get the directory where this script is located
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# Make sure scripts are executable
|
||||
chmod +x *.sh *.py
|
||||
|
||||
echo "=========================================="
|
||||
echo " Portable Backup Tools"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Available options:"
|
||||
echo "1. Launch GUI Backup Manager"
|
||||
echo "2. Command Line Backup"
|
||||
echo "3. Command Line Restore"
|
||||
echo "4. List Available Drives"
|
||||
echo "5. Create Desktop Entry"
|
||||
echo ""
|
||||
|
||||
# Check if GUI is available
|
||||
if [[ -n "$DISPLAY" ]] && command -v python3 >/dev/null 2>&1; then
|
||||
read -p "Select option (1-5): " choice
|
||||
case $choice in
|
||||
1)
|
||||
echo "Launching GUI Backup Manager..."
|
||||
python3 backup_manager.py
|
||||
;;
|
||||
2)
|
||||
echo "Starting command line backup..."
|
||||
./backup_script.sh
|
||||
;;
|
||||
3)
|
||||
echo "Starting command line restore..."
|
||||
./backup_script.sh --restore
|
||||
;;
|
||||
4)
|
||||
echo "Available drives:"
|
||||
./backup_script.sh --list
|
||||
;;
|
||||
5)
|
||||
./create_desktop_entry.sh
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
echo "GUI not available. Use command line options:"
|
||||
./backup_script.sh --help
|
||||
fi
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Create desktop entry creator
|
||||
cat > "$MOUNT_POINT/backup_system/create_desktop_entry.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# Create desktop entry for portable backup tools
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DESKTOP_DIR="$HOME/Desktop"
|
||||
APPLICATIONS_DIR="$HOME/.local/share/applications"
|
||||
|
||||
mkdir -p "$APPLICATIONS_DIR"
|
||||
|
||||
# Create application entry
|
||||
cat > "$APPLICATIONS_DIR/portable-backup.desktop" << EOL
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=Portable Backup Manager
|
||||
Comment=Boot from external drive and restore to internal
|
||||
Exec=python3 "$SCRIPT_DIR/backup_manager.py"
|
||||
Icon=drive-harddisk
|
||||
Terminal=false
|
||||
Categories=System;Utility;
|
||||
StartupNotify=true
|
||||
EOL
|
||||
|
||||
# Create desktop shortcut
|
||||
if [[ -d "$DESKTOP_DIR" ]]; then
|
||||
cp "$APPLICATIONS_DIR/portable-backup.desktop" "$DESKTOP_DIR/"
|
||||
chmod +x "$DESKTOP_DIR/portable-backup.desktop"
|
||||
echo "Desktop shortcut created: $DESKTOP_DIR/portable-backup.desktop"
|
||||
fi
|
||||
|
||||
echo "Application entry created: $APPLICATIONS_DIR/portable-backup.desktop"
|
||||
EOF
|
||||
|
||||
# Make all scripts executable
|
||||
chmod +x "$MOUNT_POINT/backup_system"/*.sh
|
||||
chmod +x "$MOUNT_POINT/backup_system"/*.py
|
||||
|
||||
# Create a README for the external drive
|
||||
cat > "$MOUNT_POINT/backup_system/README_EXTERNAL.md" << 'EOF'
|
||||
# Portable Backup Tools
|
||||
|
||||
This external drive contains both:
|
||||
1. **Your system backup** (main partitions)
|
||||
2. **Backup tools** (this partition)
|
||||
|
||||
## When Booted From This External Drive:
|
||||
|
||||
### Quick Start:
|
||||
```bash
|
||||
# Mount tools and launch
|
||||
sudo mkdir -p /mnt/tools
|
||||
sudo mount LABEL=BACKUP_TOOLS /mnt/tools
|
||||
cd /mnt/tools/backup_system
|
||||
./launch_backup_tools.sh
|
||||
```
|
||||
|
||||
### Or Create Desktop Entry:
|
||||
```bash
|
||||
cd /mnt/tools/backup_system
|
||||
./create_desktop_entry.sh
|
||||
```
|
||||
|
||||
## Common Operations:
|
||||
|
||||
### Restore Internal Drive:
|
||||
1. Boot from this external drive
|
||||
2. Launch backup tools
|
||||
3. Select "Restore from External"
|
||||
4. Choose external → internal
|
||||
5. Click "Reboot & Restore"
|
||||
|
||||
### Update Backup:
|
||||
1. Boot normally from internal drive
|
||||
2. Connect this external drive
|
||||
3. Run backup as usual
|
||||
4. Tools will be automatically preserved
|
||||
|
||||
## Drive Layout:
|
||||
- Partition 1-2: System backup (bootable)
|
||||
- Last Partition: Backup tools (this)
|
||||
EOF
|
||||
|
||||
print_success "Backup tools preserved on external drive"
|
||||
print_status "Tools available at: $TOOLS_PARTITION"
|
||||
print_status "To access when booted from external: mount LABEL=BACKUP_TOOLS /mnt/tools"
|
||||
|
||||
exit 0
|
||||
@@ -1,368 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Portable Backup Tool Installer for External M.2 SSD
|
||||
# This script sets up the backup tools on the external drive so they survive cloning operations
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Detect external drives
|
||||
detect_external_drive() {
|
||||
print_status "Detecting external M.2 SSD..."
|
||||
|
||||
# Look for USB-connected drives
|
||||
local external_drives=()
|
||||
while IFS= read -r line; do
|
||||
if [[ $line == *"disk"* ]] && [[ $line == *"usb"* ]]; then
|
||||
local drive_name=$(echo "$line" | awk '{print $1}')
|
||||
local drive_size=$(echo "$line" | awk '{print $4}')
|
||||
external_drives+=("/dev/$drive_name ($drive_size)")
|
||||
fi
|
||||
done < <(lsblk -d -o NAME,SIZE,TYPE,TRAN | grep -E "disk.*usb")
|
||||
|
||||
if [[ ${#external_drives[@]} -eq 0 ]]; then
|
||||
print_error "No external USB drives found. Please connect your M.2 SSD."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ${#external_drives[@]} -eq 1 ]]; then
|
||||
EXTERNAL_DRIVE=$(echo "${external_drives[0]}" | cut -d' ' -f1)
|
||||
print_success "Auto-detected external drive: ${external_drives[0]}"
|
||||
else
|
||||
print_status "Multiple external drives found:"
|
||||
for i in "${!external_drives[@]}"; do
|
||||
echo " $((i+1)). ${external_drives[i]}"
|
||||
done
|
||||
read -p "Select drive number: " selection
|
||||
EXTERNAL_DRIVE=$(echo "${external_drives[$((selection-1))]}" | cut -d' ' -f1)
|
||||
fi
|
||||
}
|
||||
|
||||
# Create backup tools partition
|
||||
create_tools_partition() {
|
||||
print_status "Setting up backup tools partition on $EXTERNAL_DRIVE..."
|
||||
|
||||
# Check if there's already a small partition at the end
|
||||
local last_partition=$(lsblk -n -o NAME "$EXTERNAL_DRIVE" | tail -1)
|
||||
local tools_partition="${EXTERNAL_DRIVE}p3" # Assuming nvme, adjust for sda
|
||||
|
||||
# If it's sda style, adjust
|
||||
if [[ $EXTERNAL_DRIVE == *"sda"* ]]; then
|
||||
tools_partition="${EXTERNAL_DRIVE}3"
|
||||
fi
|
||||
|
||||
# Check if tools partition already exists
|
||||
if lsblk | grep -q "$(basename "$tools_partition")"; then
|
||||
print_warning "Tools partition already exists: $tools_partition"
|
||||
TOOLS_PARTITION="$tools_partition"
|
||||
return
|
||||
fi
|
||||
|
||||
print_warning "This will create a 512MB partition at the end of $EXTERNAL_DRIVE"
|
||||
print_warning "This will slightly reduce the cloneable space but preserve backup tools"
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
|
||||
if [[ "$confirm" != "yes" ]]; then
|
||||
print_error "Operation cancelled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create 512MB partition at the end using parted
|
||||
sudo parted "$EXTERNAL_DRIVE" --script mkpart primary ext4 -512MiB 100%
|
||||
|
||||
# Get the new partition name
|
||||
TOOLS_PARTITION=$(lsblk -n -o NAME "$EXTERNAL_DRIVE" | tail -1)
|
||||
TOOLS_PARTITION="/dev/$TOOLS_PARTITION"
|
||||
|
||||
# Format the partition
|
||||
sudo mkfs.ext4 -L "BACKUP_TOOLS" "$TOOLS_PARTITION"
|
||||
|
||||
print_success "Tools partition created: $TOOLS_PARTITION"
|
||||
}
|
||||
|
||||
# Install backup tools to external drive
|
||||
install_tools_to_external() {
|
||||
local mount_point="/mnt/backup_tools"
|
||||
|
||||
print_status "Installing backup tools to external drive..."
|
||||
|
||||
# Create mount point
|
||||
sudo mkdir -p "$mount_point"
|
||||
|
||||
# Mount tools partition
|
||||
sudo mount "$TOOLS_PARTITION" "$mount_point"
|
||||
|
||||
# Copy all backup tools
|
||||
sudo cp -r . "$mount_point/backup_system"
|
||||
|
||||
# Create launcher script that works from any location
|
||||
sudo tee "$mount_point/backup_system/launch_backup_tools.sh" > /dev/null << 'EOF'
|
||||
#!/bin/bash
|
||||
# Portable launcher for backup tools
|
||||
|
||||
# Get the directory where this script is located
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# Make sure scripts are executable
|
||||
chmod +x *.sh *.py
|
||||
|
||||
# Launch GUI if X11 is available, otherwise show CLI options
|
||||
if [[ -n "$DISPLAY" ]]; then
|
||||
echo "Launching Backup Manager GUI..."
|
||||
python3 backup_manager.py
|
||||
else
|
||||
echo "No GUI available. Command line options:"
|
||||
./backup_script.sh --help
|
||||
fi
|
||||
EOF
|
||||
|
||||
sudo chmod +x "$mount_point/backup_system/launch_backup_tools.sh"
|
||||
|
||||
# Create desktop entry for when booted from external drive
|
||||
sudo tee "$mount_point/backup_system/create_desktop_entry.sh" > /dev/null << 'EOF'
|
||||
#!/bin/bash
|
||||
# Create desktop entry when booted from external drive
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DESKTOP_DIR="$HOME/Desktop"
|
||||
APPLICATIONS_DIR="$HOME/.local/share/applications"
|
||||
|
||||
mkdir -p "$APPLICATIONS_DIR"
|
||||
|
||||
# Create desktop entry
|
||||
cat > "$APPLICATIONS_DIR/portable-backup.desktop" << EOL
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=Portable Backup Manager
|
||||
Comment=Restore internal drive from external backup
|
||||
Exec=python3 "$SCRIPT_DIR/backup_manager.py"
|
||||
Icon=drive-harddisk
|
||||
Terminal=false
|
||||
Categories=System;Utility;
|
||||
StartupNotify=true
|
||||
EOL
|
||||
|
||||
# Create desktop shortcut if Desktop exists
|
||||
if [[ -d "$DESKTOP_DIR" ]]; then
|
||||
cp "$APPLICATIONS_DIR/portable-backup.desktop" "$DESKTOP_DIR/"
|
||||
chmod +x "$DESKTOP_DIR/portable-backup.desktop"
|
||||
fi
|
||||
|
||||
echo "Desktop entry created for portable backup tools"
|
||||
EOF
|
||||
|
||||
sudo chmod +x "$mount_point/backup_system/create_desktop_entry.sh"
|
||||
|
||||
# Unmount
|
||||
sudo umount "$mount_point"
|
||||
|
||||
print_success "Backup tools installed to external drive"
|
||||
}
|
||||
|
||||
# Create post-backup restoration script
|
||||
create_restoration_script() {
|
||||
print_status "Creating post-backup tool restoration script..."
|
||||
|
||||
# This script will be called after each backup to restore the tools
|
||||
cat > "restore_tools_after_backup.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# Restore backup tools after cloning operation
|
||||
# This script runs automatically after backup to preserve tools on external drive
|
||||
|
||||
set -e
|
||||
|
||||
print_status() {
|
||||
echo "[$(date '+%H:%M:%S')] $1"
|
||||
}
|
||||
|
||||
# Detect the external drive (should be the target of backup)
|
||||
EXTERNAL_DRIVE="$1"
|
||||
if [[ -z "$EXTERNAL_DRIVE" ]]; then
|
||||
print_status "Auto-detecting external drive..."
|
||||
EXTERNAL_DRIVE=$(lsblk -d -o NAME,TRAN | grep usb | awk '{print "/dev/" $1}' | head -1)
|
||||
fi
|
||||
|
||||
if [[ -z "$EXTERNAL_DRIVE" ]]; then
|
||||
echo "Error: Could not detect external drive"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_status "Restoring backup tools to $EXTERNAL_DRIVE"
|
||||
|
||||
# Find the tools partition (should be the last partition)
|
||||
TOOLS_PARTITION=$(lsblk -n -o NAME "$EXTERNAL_DRIVE" | tail -1)
|
||||
TOOLS_PARTITION="/dev/$TOOLS_PARTITION"
|
||||
|
||||
# Check if tools partition exists
|
||||
if ! lsblk | grep -q "$(basename "$TOOLS_PARTITION")"; then
|
||||
echo "Warning: Tools partition not found. Backup tools may have been overwritten."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Mount tools partition
|
||||
MOUNT_POINT="/mnt/backup_tools_restore"
|
||||
mkdir -p "$MOUNT_POINT"
|
||||
mount "$TOOLS_PARTITION" "$MOUNT_POINT"
|
||||
|
||||
# Check if tools exist
|
||||
if [[ -d "$MOUNT_POINT/backup_system" ]]; then
|
||||
print_status "Backup tools preserved on external drive"
|
||||
|
||||
# Update the tools with any changes from source
|
||||
rsync -av --exclude='.git' "$(dirname "$0")/" "$MOUNT_POINT/backup_system/"
|
||||
|
||||
print_status "Backup tools updated on external drive"
|
||||
else
|
||||
print_status "Installing backup tools to external drive for first time"
|
||||
cp -r "$(dirname "$0")" "$MOUNT_POINT/backup_system"
|
||||
fi
|
||||
|
||||
# Ensure scripts are executable
|
||||
chmod +x "$MOUNT_POINT/backup_system"/*.sh
|
||||
chmod +x "$MOUNT_POINT/backup_system"/*.py
|
||||
|
||||
umount "$MOUNT_POINT"
|
||||
print_status "Backup tools restoration complete"
|
||||
EOF
|
||||
|
||||
chmod +x "restore_tools_after_backup.sh"
|
||||
|
||||
print_success "Post-backup restoration script created"
|
||||
}
|
||||
|
||||
# Update backup scripts to call restoration
|
||||
update_backup_scripts() {
|
||||
print_status "Updating backup scripts to preserve tools..."
|
||||
|
||||
# Add tool restoration to GUI backup manager
|
||||
if ! grep -q "restore_tools_after_backup" backup_manager.py; then
|
||||
# Add restoration call after successful backup
|
||||
sed -i '/self\.log("Backup completed successfully!")/a\\n # Restore backup tools to external drive\n try:\n subprocess.run([os.path.join(os.path.dirname(__file__), "restore_tools_after_backup.sh"), target], check=False)\n except Exception as e:\n self.log(f"Warning: Could not restore tools to external drive: {e}")' backup_manager.py
|
||||
fi
|
||||
|
||||
# Add tool restoration to command line script
|
||||
if ! grep -q "restore_tools_after_backup" backup_script.sh; then
|
||||
sed -i '/success "Drive cloning completed successfully!"/a\\n # Restore backup tools to external drive\n log "Restoring backup tools to external drive..."\n if [[ -f "$(dirname "$0")/restore_tools_after_backup.sh" ]]; then\n "$(dirname "$0")/restore_tools_after_backup.sh" "$target" || log "Warning: Could not restore tools"\n fi' backup_script.sh
|
||||
fi
|
||||
|
||||
print_success "Backup scripts updated to preserve tools"
|
||||
}
|
||||
|
||||
# Create auto-mount script for tools partition
|
||||
create_automount_script() {
|
||||
print_status "Creating auto-mount script for backup tools..."
|
||||
|
||||
cat > "mount_backup_tools.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# Auto-mount backup tools partition and create desktop shortcut
|
||||
|
||||
# Find tools partition by label
|
||||
TOOLS_PARTITION=$(blkid -L "BACKUP_TOOLS" 2>/dev/null)
|
||||
|
||||
if [[ -z "$TOOLS_PARTITION" ]]; then
|
||||
echo "Backup tools partition not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create mount point
|
||||
MOUNT_POINT="$HOME/backup_tools"
|
||||
mkdir -p "$MOUNT_POINT"
|
||||
|
||||
# Mount if not already mounted
|
||||
if ! mountpoint -q "$MOUNT_POINT"; then
|
||||
mount "$TOOLS_PARTITION" "$MOUNT_POINT" 2>/dev/null || {
|
||||
echo "Mounting with sudo..."
|
||||
sudo mount "$TOOLS_PARTITION" "$MOUNT_POINT"
|
||||
}
|
||||
fi
|
||||
|
||||
echo "Backup tools mounted at: $MOUNT_POINT"
|
||||
|
||||
# Create desktop entry if tools exist
|
||||
if [[ -d "$MOUNT_POINT/backup_system" ]]; then
|
||||
cd "$MOUNT_POINT/backup_system"
|
||||
./create_desktop_entry.sh
|
||||
echo "Desktop entry created for backup tools"
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x "mount_backup_tools.sh"
|
||||
|
||||
print_success "Auto-mount script created"
|
||||
}
|
||||
|
||||
# Main installation
|
||||
main() {
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
echo " Portable Backup Tools Installer"
|
||||
echo " For External M.2 SSD"
|
||||
echo "=============================================="
|
||||
echo ""
|
||||
|
||||
print_warning "This installer will:"
|
||||
print_warning "1. Create a 512MB tools partition on your external M.2 SSD"
|
||||
print_warning "2. Install backup tools that survive cloning operations"
|
||||
print_warning "3. Set up automatic tool restoration after backups"
|
||||
print_warning "4. Enable booting from external drive with restore capability"
|
||||
echo ""
|
||||
|
||||
read -p "Continue? (yes/no): " confirm
|
||||
if [[ "$confirm" != "yes" ]]; then
|
||||
print_error "Installation cancelled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
detect_external_drive
|
||||
create_tools_partition
|
||||
install_tools_to_external
|
||||
create_restoration_script
|
||||
update_backup_scripts
|
||||
create_automount_script
|
||||
|
||||
echo ""
|
||||
print_success "Portable backup tools installation complete!"
|
||||
echo ""
|
||||
echo "Your external M.2 SSD now has:"
|
||||
echo " • Preserved backup tools in separate partition"
|
||||
echo " • Automatic tool restoration after each backup"
|
||||
echo " • Bootable system restoration capability"
|
||||
echo ""
|
||||
echo "When booted from external drive:"
|
||||
echo " • Run: ~/backup_tools/backup_system/launch_backup_tools.sh"
|
||||
echo " • Or use desktop shortcut if available"
|
||||
echo ""
|
||||
print_warning "Note: Your external drive now has 512MB less space for cloning"
|
||||
print_warning "But the backup tools will always be available for system recovery!"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
print_error "Do not run as root. Script will use sudo when needed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
main "$@"
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Simple launcher script for automated backup from within Clonezilla
|
||||
|
||||
echo "==================================="
|
||||
echo " AUTOMATED SYSTEM BACKUP LAUNCHER"
|
||||
echo "==================================="
|
||||
echo
|
||||
echo "This script will:"
|
||||
echo "1. Auto-detect your internal drive"
|
||||
echo "2. Create a high-speed backup to this USB"
|
||||
echo "3. Complete in ~15-20 minutes"
|
||||
echo
|
||||
read -p "Press Enter to start automatic backup (Ctrl+C to cancel)..."
|
||||
|
||||
# Mount backup partition
|
||||
mkdir -p /tmp/backup_storage
|
||||
mount /dev/sda2 /tmp/backup_storage 2>/dev/null
|
||||
|
||||
if [[ -f /tmp/backup_storage/automated_clonezilla_backup.sh ]]; then
|
||||
echo "Starting automated backup script..."
|
||||
/tmp/backup_storage/automated_clonezilla_backup.sh
|
||||
else
|
||||
echo "ERROR: Automated backup script not found!"
|
||||
echo "Falling back to manual Clonezilla..."
|
||||
sleep 3
|
||||
sudo /usr/sbin/ocs-live-general
|
||||
fi
|
||||
148
simple_backup.sh
Executable file
148
simple_backup.sh
Executable file
@@ -0,0 +1,148 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Simple LVM Backup Script
|
||||
# Does exactly what it says: snapshot -> copy -> cleanup
|
||||
# NO complex logic, just the essentials
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 SOURCE_LV TARGET_DEVICE"
|
||||
echo ""
|
||||
echo "Example: $0 /dev/internal-vg/root /dev/sdb"
|
||||
echo ""
|
||||
echo "This will:"
|
||||
echo "1. Create a snapshot of SOURCE_LV"
|
||||
echo "2. Copy it block-for-block to TARGET_DEVICE"
|
||||
echo "3. Clean up the snapshot"
|
||||
echo ""
|
||||
echo "WARNING: TARGET_DEVICE will be completely overwritten!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
log() {
|
||||
echo -e "${GREEN}[$(date '+%H:%M:%S')] $1${NC}"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[ERROR] $1${NC}" >&2
|
||||
cleanup_and_exit 1
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[WARNING] $1${NC}"
|
||||
}
|
||||
|
||||
cleanup_and_exit() {
|
||||
local exit_code=${1:-0}
|
||||
|
||||
if [ -n "$SNAPSHOT_PATH" ] && lvs "$SNAPSHOT_PATH" >/dev/null 2>&1; then
|
||||
warn "Cleaning up snapshot: $SNAPSHOT_PATH"
|
||||
lvremove -f "$SNAPSHOT_PATH" || warn "Failed to remove snapshot"
|
||||
fi
|
||||
|
||||
exit $exit_code
|
||||
}
|
||||
|
||||
# Trap for cleanup
|
||||
trap 'cleanup_and_exit 130' INT TERM
|
||||
|
||||
# Check arguments
|
||||
if [ $# -ne 2 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
SOURCE_LV="$1"
|
||||
TARGET_DEVICE="$2"
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
error "This script must be run as root"
|
||||
fi
|
||||
|
||||
# Basic validation
|
||||
if [ ! -e "$SOURCE_LV" ]; then
|
||||
error "Source LV does not exist: $SOURCE_LV"
|
||||
fi
|
||||
|
||||
if [ ! -e "$TARGET_DEVICE" ]; then
|
||||
error "Target device does not exist: $TARGET_DEVICE"
|
||||
fi
|
||||
|
||||
# Extract VG and LV names
|
||||
VG_NAME=$(lvs --noheadings -o vg_name "$SOURCE_LV" | tr -d ' ')
|
||||
LV_NAME=$(lvs --noheadings -o lv_name "$SOURCE_LV" | tr -d ' ')
|
||||
SNAPSHOT_NAME="${LV_NAME}_simple_backup"
|
||||
SNAPSHOT_PATH="/dev/$VG_NAME/$SNAPSHOT_NAME"
|
||||
|
||||
log "Simple LVM Backup Starting"
|
||||
log "Source: $SOURCE_LV"
|
||||
log "Target: $TARGET_DEVICE"
|
||||
log "Snapshot will be: $SNAPSHOT_PATH"
|
||||
|
||||
# Final confirmation
|
||||
echo ""
|
||||
echo -e "${YELLOW}WARNING: This will completely overwrite $TARGET_DEVICE${NC}"
|
||||
echo -e "${YELLOW}All data on $TARGET_DEVICE will be lost!${NC}"
|
||||
echo ""
|
||||
read -p "Are you sure you want to continue? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "Backup cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log "Starting backup process..."
|
||||
|
||||
# Step 1: Create snapshot
|
||||
log "Creating snapshot..."
|
||||
if ! lvcreate -L1G -s -n "$SNAPSHOT_NAME" "$SOURCE_LV"; then
|
||||
error "Failed to create snapshot"
|
||||
fi
|
||||
log "Snapshot created: $SNAPSHOT_PATH"
|
||||
|
||||
# Step 2: Copy data
|
||||
log "Starting copy operation..."
|
||||
log "This may take a long time depending on the size of your data"
|
||||
|
||||
# Use pv if available for progress, otherwise dd with progress
|
||||
if command -v pv >/dev/null 2>&1; then
|
||||
log "Using pv for progress monitoring"
|
||||
if ! pv "$SNAPSHOT_PATH" | dd of="$TARGET_DEVICE" bs=4M; then
|
||||
error "Copy operation failed"
|
||||
fi
|
||||
else
|
||||
log "Using dd (install 'pv' for progress monitoring)"
|
||||
if ! dd if="$SNAPSHOT_PATH" of="$TARGET_DEVICE" bs=4M status=progress; then
|
||||
error "Copy operation failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
log "Copy completed successfully"
|
||||
|
||||
# Step 3: Clean up snapshot
|
||||
log "Removing snapshot..."
|
||||
if ! lvremove -f "$SNAPSHOT_PATH"; then
|
||||
warn "Failed to remove snapshot, but backup completed"
|
||||
warn "You may need to manually remove: $SNAPSHOT_PATH"
|
||||
else
|
||||
log "Snapshot cleaned up"
|
||||
fi
|
||||
|
||||
log "Backup completed successfully!"
|
||||
log "Your data has been copied to: $TARGET_DEVICE"
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}SUCCESS: Backup completed!${NC}"
|
||||
echo ""
|
||||
echo "To verify the backup, you can:"
|
||||
echo "1. Mount $TARGET_DEVICE and check the files"
|
||||
echo "2. Boot from $TARGET_DEVICE if it's bootable"
|
||||
echo ""
|
||||
1657
simple_backup_gui.py
Executable file
1657
simple_backup_gui.py
Executable file
File diff suppressed because it is too large
Load Diff
@@ -1,200 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# LVM Migration Troubleshooting Script
|
||||
# Helps diagnose issues with the migration process
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${BLUE}[$(date '+%H:%M:%S')]${NC} $1"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
|
||||
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
|
||||
echo -e "${GREEN}=== LVM Migration Troubleshooting ===${NC}"
|
||||
echo
|
||||
|
||||
# Check basic system requirements
|
||||
check_system() {
|
||||
log "Checking system requirements..."
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
error "This script must be run as root. Use: sudo $0"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 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"
|
||||
else
|
||||
warning "Not running from live system - migration may fail"
|
||||
fi
|
||||
|
||||
# Check available tools
|
||||
local tools=("lvm" "cryptsetup" "rsync" "parted" "pv" "grub-install" "mkfs.ext4" "mkfs.fat" "bc" "wipefs")
|
||||
local missing=()
|
||||
|
||||
for tool in "${tools[@]}"; do
|
||||
if command -v "$tool" >/dev/null 2>&1; then
|
||||
success "Tool available: $tool"
|
||||
else
|
||||
missing+=("$tool")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#missing[@]} -gt 0 ]; then
|
||||
error "Missing tools: ${missing[*]}"
|
||||
echo "Run: sudo ./emergency_install.sh to install missing packages"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check drives
|
||||
check_drives() {
|
||||
log "Checking available drives..."
|
||||
|
||||
echo "All block devices:"
|
||||
lsblk -dpno NAME,SIZE,MODEL,VENDOR
|
||||
echo
|
||||
|
||||
# Look for likely candidates
|
||||
local internal_drives=($(lsblk -dpno NAME | grep -E "nvme|sda"))
|
||||
local usb_drives=()
|
||||
|
||||
# Check for USB drives
|
||||
for drive in $(lsblk -dpno NAME); do
|
||||
if udevadm info --query=property --name="$drive" | grep -q "ID_BUS=usb"; then
|
||||
usb_drives+=("$drive")
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Likely internal drives:"
|
||||
for drive in "${internal_drives[@]}"; do
|
||||
local info=$(lsblk -dpno NAME,SIZE,MODEL "$drive")
|
||||
echo " $info"
|
||||
done
|
||||
|
||||
echo "USB drives (external/migration stick):"
|
||||
for drive in "${usb_drives[@]}"; do
|
||||
local info=$(lsblk -dpno NAME,SIZE,MODEL "$drive")
|
||||
echo " $info"
|
||||
done
|
||||
}
|
||||
|
||||
# Check LVM status
|
||||
check_lvm() {
|
||||
log "Checking LVM status..."
|
||||
|
||||
echo "Physical volumes:"
|
||||
pvs 2>/dev/null || echo "No physical volumes found"
|
||||
|
||||
echo "Volume groups:"
|
||||
vgs 2>/dev/null || echo "No volume groups found"
|
||||
|
||||
echo "Logical volumes:"
|
||||
lvs 2>/dev/null || echo "No logical volumes found"
|
||||
|
||||
# Check for existing system-vg
|
||||
if vgs system-vg 2>/dev/null; then
|
||||
warning "system-vg already exists - migration may have partially completed"
|
||||
echo "To restart migration, you may need to:"
|
||||
echo " vgremove -f system-vg"
|
||||
echo " pvremove /dev/sdaX"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check filesystem space and usage
|
||||
check_filesystems() {
|
||||
log "Checking current filesystem usage..."
|
||||
|
||||
echo "Current mounts and usage:"
|
||||
df -h | grep -E "/$|/home$|/boot$"
|
||||
|
||||
echo
|
||||
echo "System memory:"
|
||||
free -h
|
||||
}
|
||||
|
||||
# Test LVM commands
|
||||
test_lvm_commands() {
|
||||
log "Testing LVM command availability..."
|
||||
|
||||
# Test basic LVM commands
|
||||
lvm version || error "LVM not working"
|
||||
|
||||
# Test if we can create a test PV (on a loop device)
|
||||
log "Testing LVM functionality with loop device..."
|
||||
|
||||
# Create a small test file
|
||||
dd if=/dev/zero of=/tmp/lvm-test.img bs=1M count=100 2>/dev/null
|
||||
local loop_device=$(losetup -f --show /tmp/lvm-test.img)
|
||||
|
||||
if pvcreate "$loop_device" 2>/dev/null; then
|
||||
success "LVM pvcreate works"
|
||||
|
||||
if vgcreate test-vg "$loop_device" 2>/dev/null; then
|
||||
success "LVM vgcreate works"
|
||||
|
||||
if lvcreate -L 50M -n test-lv test-vg 2>/dev/null; then
|
||||
success "LVM lvcreate works"
|
||||
success "All LVM commands working correctly"
|
||||
else
|
||||
error "LVM lvcreate failed"
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
lvremove -f test-vg/test-lv 2>/dev/null || true
|
||||
vgremove -f test-vg 2>/dev/null || true
|
||||
else
|
||||
error "LVM vgcreate failed"
|
||||
fi
|
||||
|
||||
pvremove -f "$loop_device" 2>/dev/null || true
|
||||
else
|
||||
error "LVM pvcreate failed"
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
losetup -d "$loop_device" 2>/dev/null || true
|
||||
rm -f /tmp/lvm-test.img
|
||||
}
|
||||
|
||||
# Generate report
|
||||
generate_report() {
|
||||
echo
|
||||
echo -e "${BLUE}=== Troubleshooting Report ===${NC}"
|
||||
echo "Generated: $(date)"
|
||||
echo "System: $(hostname)"
|
||||
|
||||
if check_system && check_drives && check_lvm && check_filesystems && test_lvm_commands; then
|
||||
echo
|
||||
success "All checks passed - system should be ready for migration"
|
||||
echo
|
||||
echo "To run migration:"
|
||||
echo "1. sudo ./migrate_to_lvm.sh"
|
||||
echo "2. Follow the interactive prompts"
|
||||
echo "3. Carefully select source and target drives"
|
||||
else
|
||||
echo
|
||||
error "Some checks failed - see messages above"
|
||||
echo
|
||||
echo "Common solutions:"
|
||||
echo "• Run: sudo ./emergency_install.sh (for missing packages)"
|
||||
echo "• Reboot from live USB (if not in live system)"
|
||||
echo "• Check drive connections (if drives not detected)"
|
||||
echo "• Remove existing LVM setup (if system-vg exists)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
generate_report
|
||||
fi
|
||||
@@ -1,265 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# LVM Migration Validation Script
|
||||
# Comprehensive validation that the migration from non-LVM to LVM was successful
|
||||
|
||||
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
|
||||
VG_NAME="system-vg"
|
||||
EXTERNAL_DRIVE="/dev/sda"
|
||||
EXPECTED_LVS=("root" "home" "boot" "swap")
|
||||
VALIDATION_LOG="/var/log/lvm-migration-validation.log"
|
||||
|
||||
log() {
|
||||
local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||
echo -e "${BLUE}$message${NC}"
|
||||
echo "$message" >> "$VALIDATION_LOG" 2>/dev/null || true
|
||||
}
|
||||
|
||||
error() {
|
||||
local message="[ERROR] $1"
|
||||
echo -e "${RED}$message${NC}" >&2
|
||||
echo "$message" >> "$VALIDATION_LOG" 2>/dev/null || true
|
||||
}
|
||||
|
||||
warning() {
|
||||
local message="[WARNING] $1"
|
||||
echo -e "${YELLOW}$message${NC}"
|
||||
echo "$message" >> "$VALIDATION_LOG" 2>/dev/null || true
|
||||
}
|
||||
|
||||
success() {
|
||||
local message="[SUCCESS] $1"
|
||||
echo -e "${GREEN}$message${NC}"
|
||||
echo "$message" >> "$VALIDATION_LOG" 2>/dev/null || true
|
||||
}
|
||||
|
||||
check_lvm_volumes() {
|
||||
log "Checking LVM volumes..."
|
||||
|
||||
if ! vgs "$VG_NAME" >/dev/null 2>&1; then
|
||||
error "Volume group $VG_NAME not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local expected_lvs=("root" "home" "swap" "boot")
|
||||
for lv in "${expected_lvs[@]}"; do
|
||||
if ! lvs "$VG_NAME/$lv" >/dev/null 2>&1; then
|
||||
error "Logical volume $VG_NAME/$lv not found"
|
||||
return 1
|
||||
else
|
||||
success "Found logical volume: $VG_NAME/$lv"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
check_filesystems() {
|
||||
log "Checking filesystems..."
|
||||
|
||||
local volumes=("root" "home" "boot")
|
||||
for vol in "${volumes[@]}"; do
|
||||
local device="/dev/$VG_NAME/$vol"
|
||||
if fsck.ext4 -n "$device" >/dev/null 2>&1; then
|
||||
success "Filesystem on $device is clean"
|
||||
else
|
||||
error "Filesystem on $device has errors"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Check EFI partition
|
||||
if fsck.fat -v "${EXTERNAL_DRIVE}1" >/dev/null 2>&1; then
|
||||
success "EFI filesystem is clean"
|
||||
else
|
||||
warning "EFI filesystem check failed (this might be normal)"
|
||||
fi
|
||||
}
|
||||
|
||||
check_boot_files() {
|
||||
log "Checking boot files..."
|
||||
|
||||
local mount_point="/mnt/validation_check"
|
||||
mkdir -p "$mount_point"
|
||||
|
||||
# Check root partition for essential directories
|
||||
mount "/dev/$VG_NAME/root" "$mount_point"
|
||||
|
||||
local essential_dirs=("/bin" "/sbin" "/etc" "/usr" "/var")
|
||||
for dir in "${essential_dirs[@]}"; do
|
||||
if [ -d "$mount_point$dir" ]; then
|
||||
success "Found essential directory: $dir"
|
||||
else
|
||||
error "Missing essential directory: $dir"
|
||||
umount "$mount_point"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Check for key system files
|
||||
local key_files=("/etc/fstab" "/etc/passwd" "/etc/group")
|
||||
for file in "${key_files[@]}"; do
|
||||
if [ -f "$mount_point$file" ]; then
|
||||
success "Found key system file: $file"
|
||||
else
|
||||
error "Missing key system file: $file"
|
||||
umount "$mount_point"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
umount "$mount_point"
|
||||
}
|
||||
|
||||
check_grub_installation() {
|
||||
log "Checking GRUB installation..."
|
||||
|
||||
local efi_mount="/mnt/efi_check"
|
||||
mkdir -p "$efi_mount"
|
||||
mount "${EXTERNAL_DRIVE}1" "$efi_mount"
|
||||
|
||||
if [ -d "$efi_mount/EFI/debian" ]; then
|
||||
success "GRUB EFI installation found"
|
||||
else
|
||||
error "GRUB EFI installation not found"
|
||||
umount "$efi_mount"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -f "$efi_mount/EFI/debian/grubx64.efi" ]; then
|
||||
success "GRUB EFI binary found"
|
||||
else
|
||||
error "GRUB EFI binary not found"
|
||||
umount "$efi_mount"
|
||||
return 1
|
||||
fi
|
||||
|
||||
umount "$efi_mount"
|
||||
}
|
||||
|
||||
check_fstab() {
|
||||
log "Checking /etc/fstab configuration..."
|
||||
|
||||
local mount_point="/mnt/fstab_check"
|
||||
mkdir -p "$mount_point"
|
||||
mount "/dev/$VG_NAME/root" "$mount_point"
|
||||
|
||||
if [ -f "$mount_point/etc/fstab" ]; then
|
||||
success "Found /etc/fstab"
|
||||
|
||||
# Check if fstab contains LVM UUIDs
|
||||
local root_uuid=$(blkid -s UUID -o value "/dev/$VG_NAME/root")
|
||||
local home_uuid=$(blkid -s UUID -o value "/dev/$VG_NAME/home")
|
||||
local boot_uuid=$(blkid -s UUID -o value "/dev/$VG_NAME/boot")
|
||||
|
||||
if grep -q "$root_uuid" "$mount_point/etc/fstab"; then
|
||||
success "Root UUID found in fstab"
|
||||
else
|
||||
error "Root UUID not found in fstab"
|
||||
fi
|
||||
|
||||
if grep -q "$home_uuid" "$mount_point/etc/fstab"; then
|
||||
success "Home UUID found in fstab"
|
||||
else
|
||||
error "Home UUID not found in fstab"
|
||||
fi
|
||||
|
||||
if grep -q "$boot_uuid" "$mount_point/etc/fstab"; then
|
||||
success "Boot UUID found in fstab"
|
||||
else
|
||||
error "Boot UUID not found in fstab"
|
||||
fi
|
||||
else
|
||||
error "/etc/fstab not found"
|
||||
fi
|
||||
|
||||
umount "$mount_point"
|
||||
}
|
||||
|
||||
check_snapshot_capability() {
|
||||
log "Checking LVM snapshot capability..."
|
||||
|
||||
# Check free space for snapshots
|
||||
local vg_free=$(vgs --noheadings -o vg_free --units g "$VG_NAME" | tr -d ' G')
|
||||
local vg_free_int=${vg_free%.*}
|
||||
|
||||
if [ "$vg_free_int" -ge 20 ]; then
|
||||
success "Sufficient free space for snapshots: ${vg_free}G available"
|
||||
else
|
||||
warning "Limited free space for snapshots: ${vg_free}G available (recommend 20G+)"
|
||||
fi
|
||||
|
||||
# Test snapshot creation and removal
|
||||
log "Testing snapshot creation..."
|
||||
if lvcreate -L 1G -s -n test-snapshot "/dev/$VG_NAME/root" >/dev/null 2>&1; then
|
||||
success "Snapshot creation test successful"
|
||||
if lvremove -f "/dev/$VG_NAME/test-snapshot" >/dev/null 2>&1; then
|
||||
success "Snapshot removal test successful"
|
||||
else
|
||||
error "Snapshot removal test failed"
|
||||
fi
|
||||
else
|
||||
error "Snapshot creation test failed"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_lvm_tools() {
|
||||
log "Checking for LVM snapshot script..."
|
||||
|
||||
local mount_point="/mnt/script_check"
|
||||
mkdir -p "$mount_point"
|
||||
mount "/dev/$VG_NAME/root" "$mount_point"
|
||||
|
||||
if [ -f "$mount_point/usr/local/bin/lvm-snapshot-backup.sh" ]; then
|
||||
success "LVM snapshot backup script found"
|
||||
if [ -x "$mount_point/usr/local/bin/lvm-snapshot-backup.sh" ]; then
|
||||
success "LVM snapshot backup script is executable"
|
||||
else
|
||||
error "LVM snapshot backup script is not executable"
|
||||
fi
|
||||
else
|
||||
error "LVM snapshot backup script not found"
|
||||
fi
|
||||
|
||||
umount "$mount_point"
|
||||
}
|
||||
|
||||
main() {
|
||||
echo -e "${GREEN}=== LVM Migration Validation ===${NC}"
|
||||
echo "Validating the migrated LVM system..."
|
||||
echo
|
||||
|
||||
local failed_checks=0
|
||||
|
||||
check_lvm_volumes || ((failed_checks++))
|
||||
check_filesystems || ((failed_checks++))
|
||||
check_boot_files || ((failed_checks++))
|
||||
check_grub_installation || ((failed_checks++))
|
||||
check_fstab || ((failed_checks++))
|
||||
check_snapshot_capability || ((failed_checks++))
|
||||
check_lvm_tools || ((failed_checks++))
|
||||
|
||||
echo
|
||||
if [ $failed_checks -eq 0 ]; then
|
||||
success "All validation checks passed!"
|
||||
echo -e "${GREEN}The migration appears to be successful.${NC}"
|
||||
echo "You can now:"
|
||||
echo "1. Update BIOS/UEFI boot order to boot from external M.2"
|
||||
echo "2. Test booting from the external drive"
|
||||
echo "3. Use 'lvm-snapshot-backup.sh backup' for backups"
|
||||
else
|
||||
error "$failed_checks validation check(s) failed!"
|
||||
echo -e "${RED}The migration may have issues. Review the errors above.${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user