Skip to content

Instantly share code, notes, and snippets.

@raphendyr
Last active August 3, 2026 13:10
Show Gist options
  • Select an option

  • Save raphendyr/8c0b512b76aa3f6ef868ee76e3de928a to your computer and use it in GitHub Desktop.

Select an option

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.
#!/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!"
#!/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