Last active
June 28, 2024 10:02
-
-
Save wsxq2/e6958be5f8ba63869dc943b903dd012b to your computer and use it in GitHub Desktop.
backup raspberry pi sd image
This file contains 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 -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