feat: Add file-level snapshot backup mode for space efficiency

Perfect for data volumes like home partitions that are nearly full.

New mode: Files → Borg
- Creates LVM snapshot (consistent point-in-time state)
- Mounts snapshot to temporary location
- Borg backs up actual files (not empty blocks)
- Unmounts and cleans up snapshot

Benefits vs block-level backup:
- Space efficient: 305GB files vs 358GB entire LV
- Better compression: Borg works on real file data
- Superior deduplication: File-content based
- No snapshot space issues: Shorter operation time
- Easier file recovery: Browse/restore individual files

Use cases:
- Block-level: System volumes (root, boot) for exact state
- File-level: Data volumes (home) for space efficiency

GUI: Added 'Files → Borg' mode
CLI: Added 'files-to-borg' mode

Example:
sudo ./enhanced_simple_backup.sh files-to-borg /dev/internal-vg/home /repo --new-repo

This solves the 90% full home partition backup problem!
This commit is contained in:
root
2025-10-09 08:03:00 +02:00
parent f12d07be7f
commit 7708b98674
2 changed files with 292 additions and 28 deletions

View File

@@ -24,25 +24,28 @@ usage() {
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"
echo " vg-to-borg - Backup entire VG to Borg repository"
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 vg-to-borg internal-vg /path/to/borg/repo --encryption repokey --passphrase mypass"
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"
@@ -95,6 +98,7 @@ shift 3
NEW_REPO=false
ENCRYPTION="repokey"
PASSPHRASE=""
GENEROUS_SNAPSHOTS=false
while [ $# -gt 0 ]; do
case "$1" in
@@ -110,6 +114,10 @@ while [ $# -gt 0 ]; do
PASSPHRASE="$2"
shift 2
;;
--generous-snapshots)
GENEROUS_SNAPSHOTS=true
shift
;;
*)
error "Unknown option: $1"
;;
@@ -123,7 +131,7 @@ fi
# Validate mode
case "$MODE" in
"lv-to-lv"|"lv-to-raw"|"vg-to-raw"|"lv-to-borg"|"vg-to-borg")
"lv-to-lv"|"lv-to-raw"|"vg-to-raw"|"lv-to-borg"|"vg-to-borg"|"files-to-borg")
;;
*)
error "Invalid mode: $MODE"
@@ -300,10 +308,22 @@ case "$MODE" in
# 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 10% of LV size or minimum 1GB for snapshot
SNAPSHOT_SIZE_BYTES=$((LV_SIZE_BYTES / 10))
if [ $SNAPSHOT_SIZE_BYTES -lt 1073741824 ]; then
SNAPSHOT_SIZE_BYTES=1073741824 # 1GB minimum
# 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"
@@ -411,10 +431,22 @@ case "$MODE" in
# 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 10% of LV size or minimum 1GB for snapshot
SNAPSHOT_SIZE_BYTES=$((LV_SIZE_BYTES / 10))
if [ $SNAPSHOT_SIZE_BYTES -lt 1073741824 ]; then
SNAPSHOT_SIZE_BYTES=1073741824 # 1GB minimum
# 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"
@@ -445,6 +477,101 @@ case "$MODE" in
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"
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 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
log "Mounting snapshot to $TEMP_MOUNT"
mount "$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 ""