Skip to content

Instantly share code, notes, and snippets.

@wsxq2
Last active June 28, 2024 10:02
Show Gist options
  • Save wsxq2/e6958be5f8ba63869dc943b903dd012b to your computer and use it in GitHub Desktop.
Save wsxq2/e6958be5f8ba63869dc943b903dd012b to your computer and use it in GitHub Desktop.
backup raspberry pi sd image
#!/bin/bash
set -exu
BOOTDEV=/dev/mmcblk0p1
ROOTDEV=/dev/mmcblk0p2
MOUNT_POINT=/mnt
[[ -d $MOUNT_POINT ]] || mkdir -p $MOUNT_POINT
TMPDIR=/tmp/
FILE="${1:-/tmp/rpi-`date +%Y%m%d%H%M%S`.img}"
USE_DUMP=false
color_echo()
{
local color
color="$1"
shift
echo -e '\033['"$color"'m'"$@"'\033[0m'
}
green()
{
color_echo '1;32' "$@"
}
blue()
{
color_echo '0;36' "$@"
}
yellow()
{
color_echo '1;33' "$@"
}
red()
{
color_echo '1;31' "$@"
}
exit_with_error()
{
red "${1:-error!!!}"
exit 1
}
check_root()
{
if [ `whoami` != "root" ];then
exit_with_error "This script must be run as root!"
fi
}
install_software()
{
apt update
apt install -y dosfstools parted kpartx rsync
green "software is ready"
}
create_img_file()
{
local file disksize
file="${1}"
disksize="${2}"
dd if=/dev/zero of="$file" bs=1K count=0 seek="$disksize"
}
make_part_for_img_file()
{
local file fat32_start fat32_end ext4_start
file="${1}"
fat32_start="${2}s"
fat32_end="${3}s"
ext4_start="${4}s"
parted $file --script -- mklabel msdos
parted $file --script -- mkpart primary fat32 $fat32_start $fat32_end
parted $file --script -- mkpart primary ext4 $ext4_start -1
}
create_img_file_and_part()
{
local file="${1}"
local boot_total_size=$(df -P $BOOTDEV | tail -n 1 | awk '{print $2}')
local root_used_size=$(df -P $ROOTDEV | tail -n 1 | awk '{print $3}')
local scale
$USE_DUMP && scale=1.2 || scale=1.5
local disksize=$(echo $boot_total_size $root_used_size |awk '{print int(($1+$2)*'${scale}')}')
blue "disksize is $disksize"
create_img_file $file $disksize
local tmp=$(fdisk -l |grep $BOOTDEV |grep -Eo '\b[0-9]+\b' | tr '\n' ' ')
local fat32_start=$(echo $tmp | awk '{print $1}')
local fat32_end=$(echo $tmp | awk '{print $2}')
local ext4_start=$(fdisk -l |grep $ROOTDEV |grep -Eo '\b[0-9]+\b'| head -n 1)
blue "fat32_start=$fat32_start fat32_end=$fat32_end ext4_start=$ext4_start"
make_part_for_img_file $file $fat32_start $fat32_end $ext4_start
green "create $file and parted it success"
}
backup_rootdev_rsync()
{
local file="$1"
local EXCLUDE_SWAPFILE=""
if [ -f /etc/dphys-swapfile ]; then
local SWAPFILE=`cat /etc/dphys-swapfile | grep ^CONF_SWAPFILE | cut -f 2 -d=`
if [ "$SWAPFILE" = "" ]; then
SWAPFILE=/var/swap
fi
EXCLUDE_SWAPFILE="--exclude $SWAPFILE"
fi
local boot_mnt=$(findmnt -n $BOOTDEV | awk '{print $1}')
[[ -n $boot_mnt ]] || exit_with_error "boot_mnt empty"
rsync --force -rltWDEgop --delete --stats --progress \
$EXCLUDE_SWAPFILE \
--exclude ".gvfs" \
--exclude "$boot_mnt" \
--exclude "/dev" \
--exclude "/media" \
--exclude "$MOUNT_POINT" \
--exclude "/proc" \
--exclude "/run" \
--exclude "/snap" \
--exclude "/sys" \
--exclude "/tmp" \
--exclude "lost\+found" \
--exclude "$file" \
/ $MOUNT_POINT
if [ -d /snap ]; then
mkdir $MOUNT_POINT/snap
fi
for i in boot dev media mnt proc run sys boot; do
if [ ! -d $MOUNT_POINT/$i ]; then
mkdir $MOUNT_POINT/$i
fi
done
if [ ! -d $MOUNT_POINT/tmp ]; then
mkdir $MOUNT_POINT/tmp
chmod a+w $MOUNT_POINT/tmp
fi
}
backup_rootdev_dump()
{
sudo dump -0uaf $TMPDIR/tmp12345 /
pushd $MOUNT_POINT
sudo restore -rf $TMPDIR/tmp12345
popd
#sudo dump -0uaf $MOUNT_POINT/rootdev.dump / # sudo restore -rf rootdev.dump
#sudo cp rootdev.dump $MOUNT_POINT
}
mkfs_and_mount_and_cp_files()
{
local file="$1"
local boot_label=$(dosfslabel $BOOTDEV | tail -n 1)
local root_label=$(e2label $ROOTDEV | tail -n 1)
local loopdevice=`losetup -f --show $file | tail -n 1`
kpartx -va $loopdevice
local device="/dev/mapper/$(basename $loopdevice)"
local partBoot="${device}p1"
local partRoot="${device}p2"
[[ -e "$partBoot" ]] || exit_with_error "$partBoot not exist"
[[ -e "$partRoot" ]] || exit_with_error "$partRoot not exist"
mkfs.vfat -F 32 -n "$boot_label" $partBoot
mkfs.ext4 $partRoot
e2label $partRoot $root_label
mount -t vfat $partBoot $MOUNT_POINT
# boot mount point
local boot_mnt=$(findmnt -n $BOOTDEV | awk '{print $1}')
[[ -n $boot_mnt ]] || exit_with_error "boot_mnt empty"
cp -rfp ${boot_mnt}/* $MOUNT_POINT/
local opartuuidb=`blkid -o export $BOOTDEV | grep PARTUUID`
local opartuuidr=`blkid -o export $ROOTDEV | grep PARTUUID`
local npartuuidb=`blkid -o export ${partBoot} | grep PARTUUID`
local npartuuidr=`blkid -o export ${partRoot} | grep PARTUUID`
sed -i "s/$opartuuidr/$npartuuidr/g" $MOUNT_POINT/cmdline.txt
sync
umount $MOUNT_POINT
mount -t ext4 $partRoot $MOUNT_POINT
if $USE_DUMP; then
backup_rootdev_dump
else
backup_rootdev_rsync $file
fi
sed -i "s/$opartuuidb/$npartuuidb/g" $MOUNT_POINT/etc/fstab
sed -i "s/$opartuuidr/$npartuuidr/g" $MOUNT_POINT/etc/fstab
sync
umount $MOUNT_POINT
kpartx -d $loopdevice
losetup -d $loopdevice
green "mkfs and cp files success"
}
check_root
#install_software
create_img_file_and_part $FILE
mkfs_and_mount_and_cp_files $FILE
green "All done."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment