Last active
August 3, 2026 13:10
-
-
Save raphendyr/8c0b512b76aa3f6ef868ee76e3de928a to your computer and use it in GitHub Desktop.
Scripts to make space efficient clones of partitions. E.g. backup windows or linux disk to external drive.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| set -eu | |
| h() { | |
| echo | |
| echo "######################################################################" | |
| echo "### $1" | |
| echo "######################################################################" | |
| echo | |
| } | |
| x() { | |
| echo " > $1" | |
| } | |
| yesno() { | |
| echo "$1 [y/N]?" | |
| read -r answer | |
| case "$answer" in | |
| y|Y|yes|YES) return 0;; | |
| *) return 1 ;; | |
| esac | |
| } | |
| missing=false | |
| for cmd in efibootmgr ntfsclone qemu-img sfdisk dumpe2fs cryptsetup; do | |
| if ! command -v $cmd >/dev/null; then | |
| x "Missing $cmd" | |
| missing=true | |
| fi | |
| done | |
| if $missing; then | |
| if command -v apt-get >/dev/null; then | |
| apt-get update | |
| apt-get install efibootmgr fdisk ntfs-3g qemu-utils e2fsprogs cryptsetup | |
| elif command -v pacman >/dev/null; then | |
| pacman -Sy qemu-img | |
| else | |
| exit 1 | |
| fi | |
| fi | |
| if [[ -e efivars.tar.gz || -e efibootmgr.txt ]]; then | |
| echo "This folder contains existing backup. Change to empty folder!" | |
| yesno "Overwrite" || exit 1 | |
| fi | |
| devs="$(echo /dev/nvme*n1)" | |
| echo "Will backup:" | |
| for d in $devs; do echo " $d"; done | |
| yesno "Continue" || exit 1 | |
| h "Backup partition tables" | |
| for d in $devs; do | |
| x "sfdisk -d $d" | |
| sfdisk -d "$d" | tee "${d##*/}.sfdisk" | |
| done | |
| h "Backup EFI boot vars" | |
| x "efibootmgr" | |
| efibootmgr | tee 'efibootmgr.txt' | |
| if ! [ -d /sys/firmware/efi/efivars/ ]; then modprobe efivarfs; fi | |
| if [ -d /sys/firmware/efi/efivars/ ]; then | |
| x "tar -cf efivars.tar.gz /sys/firmware/efi/efivars/Boot..." | |
| tar -zcvf efivars.tar.gz --absolute-names /sys/firmware/efi/efivars/Boot{0,Order}*-* | |
| else | |
| echo "ERROR: efivarfs not present at /sys/firmware/efi/efivars" | |
| fi | |
| sha256sum -b efi* | tee efi.sha256sum | |
| h "Backup info" | |
| for d in $devs; do | |
| for p in ${d}p*; do | |
| blkid $p | tee ${p##*/}.blkid | |
| case "$(blkid -s TYPE -o value $p)" in | |
| ntfs) | |
| x "ntfsresize info $p" | |
| ntfsresize --info $p | tee ${p##*/}.ntfsinfo | |
| ;; | |
| ext*) | |
| x "dumpe2fs $p" | |
| dumpe2fs -h "$p" | tee ${p##*/}.dumpe2fs | |
| ;; | |
| crypto_LUKS) | |
| x "cryptsetup luksDump $p" | |
| cryptsetup luksDump "$p" | tee ${p##*/}.luksDump | |
| ;; | |
| esac | |
| done | |
| done | |
| h "Backup data" | |
| x "For io speed: S_COLORS=always watch -c -n 1 iostat -hz" | |
| for d in $devs; do | |
| for p in ${d}p*; do | |
| case "$(blkid -s PARTLABEL -o value "$p")" in | |
| swap*) | |
| echo "Skipping $d, as it's swap" | |
| continue | |
| ;; | |
| esac | |
| case "$(blkid -s TYPE -o value "$p")" in | |
| ntfs) | |
| x "ntfsclone '$p' to '${p##*/}.ntfsclone'" | |
| ntfsclone --save-image --overwrite ${p##*/}.ntfsclone $p | |
| ;; | |
| #vfat) | |
| # x "dd '$p' to '${p##*/}.raw'" | |
| # dd if=$p of=${p##*/}.raw bs=4M status=progress | |
| # ;; | |
| *) | |
| x "qemu-img convert $p ${p##*/}.qcow2" | |
| qemu-img convert -p -O qcow2 -c -o compression_type=zstd "$p" ${p##*/}.qcow2 | |
| ;; | |
| esac | |
| done | |
| done | |
| sync | |
| h "Calculating sha sums" | |
| for d in $devs; do | |
| sha256sum -b ${d##*/}p* | tee ${d##*/}.sha256sum | |
| done | |
| sync | |
| h "Done!" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| set -eu | |
| h() { | |
| echo | |
| echo "######################################################################" | |
| echo "### $1" | |
| echo "######################################################################" | |
| echo | |
| } | |
| x() { | |
| echo " > $1" | |
| } | |
| yesno() { | |
| echo "$1 [y/N]?" | |
| read -r answer | |
| case "$answer" in | |
| y|Y|yes|YES) return 0;; | |
| *) return 1 ;; | |
| esac | |
| } | |
| if ! command -v qemu-img >/dev/null; then | |
| if command -v apt-get >/dev/null; then | |
| yesno "Install qemu-utils" || exit 1 | |
| apt-get update -y | |
| apt-get install -y qemu-utils | |
| elif command -v pacman >/dev/null; then | |
| yesno "Install qemu-img" || exit 1 | |
| pacman -Sy --noconfirm qemu-img | |
| else | |
| echo "Missing command qemu-img" | |
| exit 1 | |
| fi | |
| fi | |
| devs="$(echo /dev/nvme*n1)" | |
| echo "Will attempt to restore:" | |
| for d in $devs; do echo " $d"; done | |
| yesno "Continue" || exit 1 | |
| h "Verify integrity" | |
| if ! yesno "Skip sha256 check"; then | |
| sha256sum -c efi.sha256sum | |
| for d in $devs; do | |
| sha256sum -c ${d##*/}.sha256sum | |
| done | |
| fi | |
| h "Restore data" | |
| for dev in $devs; do | |
| for blkid in ${dev##*/}p*.blkid; do | |
| name=${blkid%.blkid} | |
| part="/dev/$name" | |
| target_type="$(blkid -s TYPE -o value $part)" | |
| if [ -f "$name.ntfsinfo" ]; then | |
| cat $blkid $name.ntfsinfo | |
| if [ "$target_type" = ntfs ]; then | |
| x "ntfs restore '$name' to '$part'" | |
| ntfsclone --restore-image --overwrite $part $name.ntfsclone | |
| else | |
| echo "ERROR: target is not an ntfs volume: $target_type" | |
| fi | |
| else | |
| cat $blkid | |
| x "qemu-img convert $name.qcow2 $part" | |
| qemu-img convert -p -n -f qcow2 -O raw "$name.qcow2" "$part" | |
| fi | |
| done | |
| done | |
| h "Restore EFI boot vars" | |
| x "tar -xf efivars.tar.gz" | |
| tar -zxvf efivars.tar.gz --absolute-names | |
| sync |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment