From c6a4a93c857a5846051dbf45366c5ceafb2893af Mon Sep 17 00:00:00 2001 From: first Date: Fri, 24 Jan 2025 06:42:58 +0000 Subject: [PATCH] Add backup2mdisc.sh Signed-off-by: first --- backup2mdisc.sh | 243 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 backup2mdisc.sh diff --git a/backup2mdisc.sh b/backup2mdisc.sh new file mode 100644 index 0000000..5099aa1 --- /dev/null +++ b/backup2mdisc.sh @@ -0,0 +1,243 @@ +#!/usr/bin/env bash +# +# backup_to_mdisc.sh +# +# A script to: +# 1. Archive a directory. +# 2. Compress and encrypt it. +# 3. Split into chunks (default 100GB). +# 4. Generate checksums and a manifest. +# 5. Optionally create ISO images for burning to M-Disc. +# +# Usage: +# ./backup_to_mdisc.sh /path/to/source /path/to/destination [CHUNK_SIZE] [--create-iso] [--burn] +# +# Examples: +# ./backup_to_mdisc.sh /home/user/documents /media/backup 100G --create-iso +# ./backup_to_mdisc.sh /data /backup 100G --create-iso --burn +# +# Dependencies (install these if missing): +# - tar +# - xz +# - gpg +# - split +# - sha256sum (on BSD/macOS, use 'shasum -a 256') +# - genisoimage or mkisofs (for ISO creation) +# - growisofs (on Linux, for burning) +# - hdiutil (on macOS, for burning) +# +# NOTE: +# - This script assumes you have a functioning optical burner that supports +# 100GB M-Disc (BD-XL) media, and the relevant drivers and software installed. +# - For FreeBSD, adjust commands (e.g., use 'shasum -a 256' instead of 'sha256sum'). + +set -e + +##################################### +# CONFIGURATION # +##################################### + +# The default chunk size for splitting. 100GB is typical for 100GB M-Disc (BD-XL). +DEFAULT_CHUNK_SIZE="100G" + +# The name for the final TAR.XZ.GPG file (before splitting). +# This will be suffixed by .partNN once split. +ENCRYPTED_ARCHIVE_NAME="backup.tar.xz.gpg" + +# The manifest file name. +MANIFEST_NAME="backup_manifest.txt" + +##################################### +# HELPER FUNCTIONS # +##################################### + +# Cross-platform SHA-256 function. On Linux, we can rely on 'sha256sum'. +# On macOS/FreeBSD, you might need to use 'shasum -a 256'. +function compute_sha256() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" + else + shasum -a 256 "$1" + fi +} + +##################################### +# MAIN SCRIPT # +##################################### + +function usage() { + echo "Usage: $0 /path/to/source /path/to/destination [CHUNK_SIZE] [--create-iso] [--burn]" + echo + echo "Example: $0 /home/user/docs /mnt/backup 100G --create-iso --burn" + exit 1 +} + +# Parse arguments +SOURCE_DIR="$1" +DEST_DIR="$2" +CHUNK_SIZE="${3:-$DEFAULT_CHUNK_SIZE}" + +shift 3 || true + +CREATE_ISO=false +BURN_MEDIA=false + +for arg in "$@"; do + case "$arg" in + --create-iso) + CREATE_ISO=true + ;; + --burn) + BURN_MEDIA=true + ;; + *) + ;; + esac +done + +# Basic checks +if [[ -z "$SOURCE_DIR" || -z "$DEST_DIR" ]]; then + usage +fi + +if [[ ! -d "$SOURCE_DIR" ]]; then + echo "ERROR: Source directory '$SOURCE_DIR' does not exist." + exit 1 +fi + +if [[ ! -d "$DEST_DIR" ]]; then + echo "ERROR: Destination directory '$DEST_DIR' does not exist." + exit 1 +fi + +# Prompt for GPG passphrase (don't store in script for security). +echo -n "Enter GPG passphrase (will not be displayed): " +read -s GPG_PASSPHRASE +echo + +# Create a working directory inside DEST_DIR for the backup process +WORK_DIR="${DEST_DIR}/backup_$(date +%Y%m%d_%H%M%S)" +mkdir -p "$WORK_DIR" +cd "$WORK_DIR" + +echo "=== Step 1: Create a compressed TAR archive and encrypt it with GPG ===" +echo "Creating encrypted archive. This may take a while depending on your data size..." + +# We tar, compress, and encrypt in a single pipeline. +# tar -cf - : stream archive +# xz -c -9 : compress with xz at high compression +# gpg -c : symmetric encrypt, using passphrase +# +# Adjust cipher-algo or compression level (-9) as needed. + +tar -cf - "$SOURCE_DIR" \ + | xz -c -9 \ + | gpg --batch --yes --cipher-algo AES256 --passphrase "$GPG_PASSPHRASE" -c \ + > "${ENCRYPTED_ARCHIVE_NAME}" + +echo "=== Step 2: Split the encrypted archive into $CHUNK_SIZE chunks ===" +split -b "$CHUNK_SIZE" -a 3 "${ENCRYPTED_ARCHIVE_NAME}" "${ENCRYPTED_ARCHIVE_NAME}." + +# Remove the single large file to save space (optional). +rm -f "${ENCRYPTED_ARCHIVE_NAME}" + +echo "=== Step 3: Generate checksums and a manifest/catalog ===" +touch "${MANIFEST_NAME}" + +echo "Backup Manifest - $(date)" >> "${MANIFEST_NAME}" +echo "Source directory: $SOURCE_DIR" >> "${MANIFEST_NAME}" +echo "Destination directory: $DEST_DIR" >> "${MANIFEST_NAME}" +echo "Split chunk size: $CHUNK_SIZE" >> "${MANIFEST_NAME}" +echo "Encrypted archive chunk names:" >> "${MANIFEST_NAME}" +echo >> "${MANIFEST_NAME}" + +for chunk in ${ENCRYPTED_ARCHIVE_NAME}.*; do + CHUNK_SHA256=$(compute_sha256 "$chunk") + echo "$CHUNK_SHA256" >> "${MANIFEST_NAME}" +done + +echo "Manifest created at: ${WORK_DIR}/${MANIFEST_NAME}" + +# If ISO creation is requested +if [ "$CREATE_ISO" = true ]; then + echo "=== Step 4: Create an ISO for each chunk (for easier burning) ===" + + # We'll place ISOs in a subfolder + mkdir -p iso_chunks + + for chunk in ${ENCRYPTED_ARCHIVE_NAME}.*; do + ISO_BASENAME="${chunk}.iso" + + # Create a temporary directory to hold the chunk file + mkdir -p temp_dir + cp "$chunk" temp_dir/ + + # Build an ISO with a single file inside: + genisoimage -o "iso_chunks/${ISO_BASENAME}" -V "ENCRYPTED_BACKUP" temp_dir >/dev/null 2>&1 || \ + mkisofs -o "iso_chunks/${ISO_BASENAME}" -V "ENCRYPTED_BACKUP" temp_dir + + # Remove the temporary directory + rm -rf temp_dir + done + + echo "ISO files created under: ${WORK_DIR}/iso_chunks" +fi + +# If burning is requested, attempt to burn right away. +# For cross-platform compatibility, we'll provide examples. +# You may need to adapt device names (/dev/sr0, /dev/dvd, etc.). + +if [ "$BURN_MEDIA" = true ]; then + echo "=== Step 5: Burn chunks/ISOs to M-Disc ===" + + # Example using growisofs on Linux: + # growisofs -Z /dev/sr0=chunk_or_iso + # or: + # growisofs -use-the-force-luke=dao -speed=2 -Z /dev/sr0=chunk_or_iso + + # Example using hdiutil on macOS for ISO: + # hdiutil burn chunk.iso + + echo "Attempting to burn each chunk (or ISO) to M-Disc. Please ensure a blank M-Disc is loaded each time." + + if [ "$CREATE_ISO" = true ]; then + # We have ISO images + for iso_file in iso_chunks/*.iso; do + echo "Insert new disc for: $iso_file" + read -p "Press [Enter] when ready to burn..." + + # Linux (growisofs) example: + if command -v growisofs >/dev/null 2>&1; then + growisofs -Z /dev/sr0="$iso_file" + elif [[ "$OSTYPE" == "darwin"* ]]; then + # macOS example using hdiutil + hdiutil burn "$iso_file" + else + echo "No known burner command found. Please burn manually: $iso_file" + fi + done + else + # Burn the chunk files directly. + for chunk in ${ENCRYPTED_ARCHIVE_NAME}.*; do + echo "Insert new disc for: $chunk" + read -p "Press [Enter] when ready to burn..." + + if command -v growisofs >/dev/null 2>&1; then + growisofs -Z /dev/sr0="$chunk" + elif [[ "$OSTYPE" == "darwin"* ]]; then + # We can't directly burn a raw file with hdiutil. Typically you'd create an ISO first. + # So warn the user. + echo "On macOS, please create an ISO or use a separate burning tool for: $chunk" + else + echo "No known burner command found. Please burn manually: $chunk" + fi + done + fi + + echo "Burning process completed. Verify your discs for peace of mind." +fi + +echo "=== All done! ===" +echo "Your backup chunks and manifest are in: ${WORK_DIR}" +echo "Keep the manifest safe. You'll need all chunk files + passphrase to restore." +exit 0 \ No newline at end of file