1643 lines
37 KiB
Bash
1643 lines
37 KiB
Bash
#!/bin/bash
|
|
|
|
version=2.0.20
|
|
|
|
# auto run grub-install if grub detected
|
|
grub_auto=1
|
|
|
|
PGM=`basename $0`
|
|
setup_command="$PGM-setup"
|
|
|
|
rsync_options="--force -rltWDEHXAgoptx"
|
|
|
|
if [ `id -u` != 0 ]
|
|
then
|
|
echo -e "$PGM needs to be run as root.\n"
|
|
exit 1
|
|
fi
|
|
|
|
confirm()
|
|
{
|
|
if ((unattended || (initialize && Unattended) ))
|
|
then
|
|
return 0
|
|
fi
|
|
printf "\n%s (yes/no): " "$1"
|
|
read resp
|
|
if [ "$resp" = "y" ] || [ "$resp" = "yes" ]
|
|
then
|
|
return 0
|
|
fi
|
|
if [ "$2" == "abort" ]
|
|
then
|
|
echo -e "Aborting!\n"
|
|
exit 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
commands="rsync parted fdisk findmnt column fsck.vfat"
|
|
packages="rsync parted util-linux mount bsdmainutils dosfstools"
|
|
need_packages=""
|
|
|
|
idx=1
|
|
for cmd in $commands
|
|
do
|
|
if ! command -v $cmd > /dev/null
|
|
then
|
|
pkg=$(echo "$packages" | cut -d " " -f $idx)
|
|
printf "%-30s %s\n" "Command not found: $cmd" "Package required: $pkg"
|
|
need_packages="$need_packages $pkg"
|
|
fi
|
|
((++idx))
|
|
done
|
|
|
|
if [ "$need_packages" != "" ]
|
|
then
|
|
confirm "Do you want to apt-get install the packages?" "abort"
|
|
apt-get install -y --no-install-recommends $need_packages
|
|
fi
|
|
|
|
clone=/mnt/clone
|
|
clone_src=/mnt/clone-src
|
|
clone_log=/var/log/$PGM.log
|
|
|
|
HOSTNAME=`hostname`
|
|
|
|
|
|
usage()
|
|
{
|
|
echo $"
|
|
usage: $PGM sdN {-v|--verbose} {-f|--force-initialize} {-f2}
|
|
{-u|--unattended} {-U|--Unattended} {-q|--quiet}
|
|
{-s|--setup host} {-e|--edit-fstab sdX } {-m|--mountdir dir }
|
|
{-L|--label-partitions label} {-l|--leave-sd-usb-boot}
|
|
{-a|--all-sync} {-F|--Force-sync} {-x} {-V|--version}
|
|
{--convert-fstab-to-partuuid}
|
|
|
|
-v - verbose rsync, list all files as they are copied.
|
|
-f - force initialize the destination disk by imaging the booted disk
|
|
partition structure. File systems are then synced or imaged.
|
|
-f2 - force initialize only the first 2 partitions to the destination.
|
|
So a multi partition USB boot can initialize clone back to
|
|
a 2 partition SD card.
|
|
-u - unattended clone if not initializing. No confirmations asked,
|
|
but abort if disk needs initializing or on error.
|
|
-U - unattended even if initializing. No confirmations asked,
|
|
but abort only on errors.
|
|
-q - quiet mode, no output unless errors or initializing. Implies -u.
|
|
-s host - add 'host' to args passed to script rpi-clone-setup and run it
|
|
after cloning but before unmounting partitions. For setting
|
|
clone disk hostname, but args can be what the script expects.
|
|
You can give multiple "-s arg" options.
|
|
-e sdX - edit destination fstab to change booted device names to new
|
|
device 'sdX'. This is Only for fstabs that use device names.
|
|
Used for setting up a USB bootable disk.
|
|
-m dir - Add dir to a custom list of mounted directories to sync. Then
|
|
the custom list will be synced instead of the default of all
|
|
mounted directories. The root directory is always synced.
|
|
Not for when initializing.
|
|
-L lbl - label for ext type partitions. If ends with '#', replace with
|
|
partition number and label all ext partitions. Otherwise,
|
|
apply label to root partition only.
|
|
-l - leave SD card to USB boot alone when cloning to SD card mmcblk0
|
|
from a USB boot. This preserves a SD card to USB boot setup
|
|
by leaving the SD card cmdline.txt using the USB root. When
|
|
cloning to USB from SD card this option sets up the SD card
|
|
cmdline.txt to boot to the USB disk.
|
|
-a - Sync all partitions if types compatible, not just mounted ones.
|
|
-F - force file system sync even if errors.
|
|
If source used > destination space error, do the sync anyway.
|
|
If a source partition mount error, skip it and do other syncs.
|
|
-x - use set -x for very verbose bash shell script debugging
|
|
-V - print rpi-clone version.
|
|
|
|
Clone a booted file system to a destination disk which is bootable.
|
|
|
|
The destination disk is a SD card (USB card reader) or USB disk 'sdN' plugged
|
|
into a USB port. The 'sdN' name should be a full disk name like sda and not
|
|
a partition name like sda1. $PGM works on a Raspberry Pi and can work on
|
|
other systems. For a destination disk that shows up as sda, run:
|
|
|
|
$ sudo rpi-clone sda
|
|
|
|
Clones can be from a booted SD card or USB disk. For a description, example
|
|
clone runs and example usage of above options, see the README.md at:
|
|
|
|
https://github.com/billw2/rpi-clone
|
|
|
|
A line logging a $PGM run is written to $clone_log.
|
|
|
|
Download:
|
|
git clone https://github.com/billw2/rpi-clone
|
|
"
|
|
exit 1
|
|
}
|
|
|
|
readable()
|
|
{
|
|
val=$1
|
|
if [ "$val" == "" ]
|
|
then
|
|
result=" ??"
|
|
else
|
|
blk_size=$2
|
|
val=$((val / 1000 * blk_size))
|
|
|
|
if ((val < 1000000))
|
|
then
|
|
result=$(echo $val \
|
|
| awk '{ byte =$1 /1000; printf "%.1f%s", byte, "MB" }')
|
|
elif ((val < 1000000000))
|
|
then
|
|
result=$(echo $val \
|
|
| awk '{ byte =$1 /1000/1000; printf "%.1f%s", byte, "GB" }')
|
|
else
|
|
result=$(echo $val \
|
|
| awk '{ byte =$1 /1000/1000/1000; printf "%.1f%s", byte, "TB" }')
|
|
fi
|
|
fi
|
|
printf -v "${3}" "%s" "$result"
|
|
}
|
|
|
|
qecho()
|
|
{
|
|
if ((!quiet))
|
|
then
|
|
echo "$@"
|
|
fi
|
|
}
|
|
|
|
qprintf()
|
|
{
|
|
if ((!quiet))
|
|
then
|
|
printf "$@"
|
|
fi
|
|
}
|
|
|
|
unmount_or_abort()
|
|
{
|
|
if [ "$1" == "" ]
|
|
then
|
|
return
|
|
fi
|
|
qprintf "\n $2\n The clone cannot proceed unless it is unmounted."
|
|
|
|
if confirm "Do you want to unmount $1?" "abort"
|
|
then
|
|
if ! umount $1
|
|
then
|
|
echo "$PGM could not unmount $1."
|
|
echo -e "Aborting!\n"
|
|
exit 0
|
|
fi
|
|
fi
|
|
}
|
|
|
|
unmount_list()
|
|
{
|
|
if [ "$1" == "" ]
|
|
then
|
|
return
|
|
fi
|
|
for dir in $1
|
|
do
|
|
qecho " unmounting $dir"
|
|
if ! umount $dir
|
|
then
|
|
qecho " Failed to unmount: $dir"
|
|
fi
|
|
done
|
|
}
|
|
|
|
mount_partition()
|
|
{
|
|
qecho " Mounting $1 on $2"
|
|
|
|
if ! mount $1 $2
|
|
then
|
|
echo " Mount failure of $1 on $2."
|
|
if [ "$3" != "" ]
|
|
then
|
|
unmount_list $3
|
|
fi
|
|
echo "Aborting!"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
rsync_file_system()
|
|
{
|
|
src_dir="$1"
|
|
dst_dir="$2"
|
|
|
|
qprintf " => rsync $1 $2 $3 ..."
|
|
|
|
if [ "$3" == "with-root-excludes" ]
|
|
then
|
|
rsync $rsync_options --delete \
|
|
$exclude_swapfile \
|
|
--exclude '.gvfs' \
|
|
--exclude '/dev/*' \
|
|
--exclude '/mnt/clone/*' \
|
|
--exclude '/proc/*' \
|
|
--exclude '/run/*' \
|
|
--exclude '/sys/*' \
|
|
--exclude '/tmp/*' \
|
|
--exclude 'lost\+found/*' \
|
|
$src_dir \
|
|
$dst_dir
|
|
else
|
|
rsync $rsync_options --delete \
|
|
--exclude '.gvfs' \
|
|
--exclude 'lost\+found/*' \
|
|
$src_dir \
|
|
$dst_dir
|
|
fi
|
|
qecho ""
|
|
}
|
|
|
|
print_partitions()
|
|
{
|
|
if ((quiet)) && ((!initialize))
|
|
then
|
|
return
|
|
fi
|
|
n_parts=$(( (n_src_parts >= n_dst_parts) ? n_src_parts : n_dst_parts ))
|
|
|
|
readable $src_disk_size "512" src_size_readable
|
|
readable $dst_disk_size "512" dst_size_readable
|
|
|
|
printf "\n%-43s%s" "Booted disk: $src_disk $src_size_readable" \
|
|
"Destination disk: $dst_disk $dst_size_readable"
|
|
echo $"
|
|
---------------------------------------------------------------------------"
|
|
out=$'Part, Size,FS,Label ,Part, Size,FS,Label\n'
|
|
for ((p = 1; p <= n_parts; p++))
|
|
do
|
|
if ((p <= n_src_parts && src_exists[p]))
|
|
then
|
|
readable ${src_size_sectors[p]} "512" tmp
|
|
printf -v sectors_readable "%7s" $tmp
|
|
pname="$p ${src_name[p]}"
|
|
out=${out}$"$pname,$sectors_readable,${src_fs_type[p]},${src_label[p]},"
|
|
else
|
|
out=${out}$" , , , ,"
|
|
fi
|
|
|
|
if ((p <= n_dst_parts && dst_exists[p]))
|
|
then
|
|
readable ${dst_size_sectors[p]} "512" tmp
|
|
printf -v sectors_readable "%7s" $tmp
|
|
out=${out}$"$p,$sectors_readable,${dst_fs_type[p]},${dst_label[p]},"
|
|
else
|
|
out=${out}$" , , , ,"
|
|
fi
|
|
out=${out}$'\n'
|
|
done
|
|
|
|
echo $"$out" | column -t -s ','
|
|
|
|
if ((alt_root_part_num > 0))
|
|
then
|
|
echo $"
|
|
** Assuming destination root partition for the clone is $dst_part_base$root_part_num
|
|
The root FS mount is not from booted $src_disk. It is ${src_root_dev#/dev/}"
|
|
|
|
fi
|
|
echo $"---------------------------------------------------------------------------"
|
|
}
|
|
|
|
print_sync_actions()
|
|
{
|
|
if ((quiet))
|
|
then
|
|
return
|
|
fi
|
|
for ((p = 1; p <= n_src_parts; p++))
|
|
do
|
|
if ((!src_exists[p]))
|
|
then
|
|
continue
|
|
fi
|
|
if ((p == root_part_num && alt_root_part_num > 0))
|
|
then
|
|
part=${src_root_dev#/dev/}
|
|
flow="$part to $dst_part_base$p"
|
|
else
|
|
flow="to $dst_part_base$p"
|
|
fi
|
|
if ((src_sync_part[p]))
|
|
then
|
|
readable ${src_used_sectors[p]} "512" used
|
|
readable ${dst_size_sectors[p]} "512" size
|
|
printf "%-22s%-14s : SYNC %s\n" \
|
|
"${src_mounted_dir[p]}" "(${used} used)" \
|
|
"$flow (${size} size)"
|
|
fi
|
|
done
|
|
}
|
|
|
|
print_image_actions()
|
|
{
|
|
for ((p = 1; p <= n_src_parts; p++))
|
|
do
|
|
if ((!src_exists[p]))
|
|
then
|
|
continue
|
|
fi
|
|
pname="$p ${src_name[p]}"
|
|
fs_type=${src_fs_type[$p]}
|
|
|
|
if ((p == root_part_num && alt_root_part_num > 0))
|
|
then
|
|
part=${src_root_dev#/dev/}
|
|
flow="$part to $dst_part_base$p"
|
|
else
|
|
flow="to $dst_part_base$p"
|
|
fi
|
|
|
|
action=""
|
|
if ((p <= n_image_parts))
|
|
then
|
|
if [ "${src_mounted_dir[p]}" == "/boot" ] && ((p == 1))
|
|
then
|
|
action="IMAGE $flow FSCK"
|
|
elif [ "$fs_type" == "swap" ]
|
|
then
|
|
action="MKSWAP"
|
|
elif ((p != ext_part_num))
|
|
then
|
|
if [ "${src_mounted_dir[p]}" != "" ] || ((p == n_src_parts))
|
|
then
|
|
if ((p < n_src_parts || last_part_space || force_sync))
|
|
then
|
|
action="MKFS SYNC $flow"
|
|
else
|
|
action="MKFS **NO SYNC**"
|
|
fi
|
|
else
|
|
action="IMAGE $flow"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if ((p == n_image_parts))
|
|
then
|
|
readable ${src_used_sectors[n_image_parts]} "512" used
|
|
printf "%-22s%-14s : RESIZE($image_space_readable) %s\n" \
|
|
"$pname" "(${used} used)" "$action"
|
|
elif ((src_used_sectors[$p] > 0 && p < n_image_parts))
|
|
then
|
|
readable ${src_used_sectors[p]} "512" used
|
|
printf "%-22s%-14s : $action\n" "$pname" "(${used} used)"
|
|
elif [ "$action" != "" ]
|
|
then
|
|
printf "%-36s : $action\n" "$pname"
|
|
fi
|
|
done
|
|
}
|
|
|
|
print_options()
|
|
{
|
|
if ((quiet))
|
|
then
|
|
return
|
|
fi
|
|
echo $"---------------------------------------------------------------------------"
|
|
|
|
if ((force_2_parts))
|
|
then
|
|
printf "%-22s : %s\n" "-f2" \
|
|
"force initialize to first two partitions only"
|
|
fi
|
|
if [ "$edit_fstab_name" != "" ]
|
|
then
|
|
printf "%-22s : %s\n" "-e clone fstab edit" \
|
|
"edit $src_part_base device entries to $edit_fstab_name"
|
|
fi
|
|
if [ "$ext_label" != "" ]
|
|
then
|
|
rep="${ext_label: -1}"
|
|
if [ "$rep" == "#" ]
|
|
then
|
|
msg="all ext partition types"
|
|
else
|
|
msg="root partition only"
|
|
fi
|
|
printf "%-22s : %s\n" "-L $ext_label" \
|
|
"volume label for $msg."
|
|
fi
|
|
if ((leave_sd_usb_boot))
|
|
then
|
|
if ((SD_slot_dst))
|
|
then
|
|
msg="leave SD card cmdline.txt bootable to USB"
|
|
elif ((SD_slot_boot))
|
|
then
|
|
msg="install boot to USB cmdline.txt on SD card"
|
|
else
|
|
msg="-l ignored. Src or dst is not a SD card slot."
|
|
fi
|
|
printf "%-22s : %s\n" "-l SD to USB boot mode" "$msg"
|
|
fi
|
|
if [ "$setup_args" != "" ]
|
|
then
|
|
printf "%-22s : %s\n" "Run setup script" "$setup_command $setup_args"
|
|
else
|
|
printf "%-22s : no\n" "Run setup script"
|
|
fi
|
|
if ((have_grub))
|
|
then
|
|
printf "%-22s : %s\n" "Run grub" \
|
|
"grub-install --root-directory=$clone /dev/$dst_disk"
|
|
fi
|
|
printf "%-22s : %s\n" "Verbose mode" "$verbose"
|
|
printf "%-23s:\n" "-----------------------"
|
|
}
|
|
|
|
ext_label()
|
|
{
|
|
pnum=$1
|
|
fs_type=$2
|
|
flag=$3
|
|
label_arg=""
|
|
|
|
if [ "$ext_label" != "" ] && [[ "$fs_type" == *"ext"* ]]
|
|
then
|
|
rep="${ext_label: -1}"
|
|
if [ "$rep" == "#" ]
|
|
then
|
|
label_arg=${ext_label:: -1}
|
|
label_arg="$flag $label_arg$pnum"
|
|
elif ((pnum == root_part_num))
|
|
then
|
|
label_arg="$flag $ext_label"
|
|
fi
|
|
fi
|
|
printf -v "${4}" "%s" "$label_arg"
|
|
}
|
|
|
|
get_src_disk()
|
|
{
|
|
partition=${1#/dev/}
|
|
disk=${partition:: -1}
|
|
num="${partition: -1}"
|
|
if [[ $disk == *"mmcblk"* ]]
|
|
then
|
|
SD_slot_boot=1
|
|
disk=${disk:0:7}
|
|
src_part_base=${disk}p
|
|
fi
|
|
printf -v "${2}" "%s" "$disk"
|
|
printf -v "${3}" "%s" "$num"
|
|
}
|
|
|
|
|
|
# ==== source (booted) disk info and default mount list
|
|
#
|
|
src_boot_dev=`findmnt /boot -o source -n`
|
|
src_root_dev=`findmnt / -o source -n`
|
|
SD_slot_boot=0
|
|
SD_slot_dst=0
|
|
src_part_base=""
|
|
|
|
boot_part_num=0
|
|
alt_root_part_num=0
|
|
|
|
|
|
if [ "$src_boot_dev" == "" ]
|
|
then
|
|
get_src_disk "$src_root_dev" "src_disk" "unused"
|
|
else
|
|
get_src_disk "$src_boot_dev" "src_disk" "boot_part_num"
|
|
fi
|
|
|
|
get_src_disk "$src_root_dev" "src_root_disk" "root_part_num"
|
|
|
|
if [ "$src_disk" == "" ]
|
|
then
|
|
echo "Cannot find booted device."
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$src_part_base" == "" ]
|
|
then
|
|
src_part_base=$src_disk
|
|
fi
|
|
|
|
if [ "$src_disk" != "$src_root_disk" ]
|
|
then
|
|
if ((SD_slot_boot))
|
|
then
|
|
# Handle SD card boots with root on different USB disk device.
|
|
# But will assume SD card has a root partition just above its root.
|
|
#
|
|
alt_root_part_num="$root_part_num"
|
|
root_part_num=$((boot_part_num + 1))
|
|
else
|
|
echo $"
|
|
Boot and root are on different disks and it's not a SD card boot.
|
|
Don't know how to partition the destination disk!"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# src_root_dev, if on device other than booted, is not in src_partition_table
|
|
# and src_fdisk_table, but is in src_df_table and src_mount_table
|
|
#
|
|
src_partition_table=$(parted -m "/dev/$src_disk" unit s print | tr -d ';')
|
|
src_fdisk_table=$(fdisk -l /dev/$src_disk | grep "^/dev/")
|
|
|
|
tmp=$(df | grep -e "^/dev/$src_disk" -e "^/dev/root" -e "$src_root_dev" \
|
|
| tr -s " ")
|
|
dev=${src_root_dev#/dev/}
|
|
src_df_table=$(echo "$tmp" | sed "s/root/$dev/")
|
|
|
|
n_src_parts=$(echo "$src_partition_table" | tail -n 1 | cut -d ":" -f 1)
|
|
src_disk_size=$(echo "$src_partition_table" \
|
|
| grep "^/dev/$src_disk" | cut -d ":" -f 2 | tr -d 's')
|
|
|
|
line=$(fdisk -l /dev/$src_disk | grep "Disk identifier:")
|
|
src_disk_ID=${line#*x}
|
|
|
|
src_mount_table=$(findmnt -o source,target -n -l \
|
|
| grep -e "^/dev/$src_disk" -e "^$src_root_dev" | tr -s " ")
|
|
n_mounts=$(echo "$src_mount_table" | wc -l)
|
|
|
|
if ((alt_root_part_num > 0 && n_src_parts < 2))
|
|
then
|
|
echo $"
|
|
Booted disk has only one partition and the root is from another device.
|
|
Don't know how to partition the destination disk!
|
|
"
|
|
exit 1
|
|
fi
|
|
|
|
|
|
line=$(echo "$src_fdisk_table" | grep "Extended")
|
|
if [ "$line" != "" ]
|
|
then
|
|
dev=$(echo "$line" | cut -d " " -f 1)
|
|
ext_part_num="${dev: -1}"
|
|
else
|
|
ext_part_num=0
|
|
fi
|
|
|
|
|
|
for ((p = 1; p <= n_src_parts; p++))
|
|
do
|
|
line=$(echo "$src_partition_table" | grep -e "^${p}:")
|
|
if [ "$line" == "" ]
|
|
then
|
|
src_exists[p]=0
|
|
continue
|
|
fi
|
|
src_exists[p]=1
|
|
|
|
if ((p == root_part_num))
|
|
then
|
|
src_partition[p]=${src_root_dev#/dev/}
|
|
src_device[p]=$src_root_dev
|
|
else
|
|
src_partition[p]="${src_part_base}${p}"
|
|
src_device[p]="/dev/${src_partition[p]}"
|
|
fi
|
|
|
|
# parted sectors are 512 bytes
|
|
src_start_sector[p]=$(echo "$line" | cut -d ":" -f 2 | tr -d 's')
|
|
src_size_sectors[p]=$(echo "$line" | cut -d ":" -f 4 | tr -d 's')
|
|
|
|
part_type=$(echo "$line" | cut -d ":" -f 5)
|
|
|
|
src_mounted_dir[p]=$(echo "$src_mount_table" \
|
|
| grep -m 1 -e "^${src_device[p]}" | cut -d " " -f 2)
|
|
if [ "${src_mounted_dir[p]}" != "" ]
|
|
then
|
|
src_sync_part[p]=1
|
|
else
|
|
src_sync_part[p]=0
|
|
fi
|
|
|
|
src_name[p]=""
|
|
if [ "$part_type" != "" ]
|
|
then
|
|
src_fs_type[p]="$part_type"
|
|
else
|
|
src_fs_type[p]="--"
|
|
fi
|
|
src_label[p]="--"
|
|
|
|
if [ "${src_mounted_dir[p]}" == "/" ]
|
|
then
|
|
src_name[p]="root"
|
|
#
|
|
# If root on device other than booted SD card, root_part_num assumed to be
|
|
# booted /boot part_num + 1 and alt_root_part_num is from root device.
|
|
#
|
|
elif ((p == root_part_num)) && ((alt_root_part_num > 0))
|
|
then
|
|
src_name[p]="root**"
|
|
elif ((p == ext_part_num))
|
|
then
|
|
src_fs_type[p]="EXT"
|
|
elif [[ "$part_type" == *"linux-swap"* ]]
|
|
then
|
|
src_fs_type[p]="swap"
|
|
elif [ "${src_mounted_dir[p]}" != "" ]
|
|
then
|
|
src_name[p]="${src_mounted_dir[p]}"
|
|
fi
|
|
|
|
if [[ "$part_type" == *"ext"* ]]
|
|
then
|
|
label=`e2label ${src_device[p]} 2> /dev/null`
|
|
if [ "$label" != "" ]
|
|
then
|
|
src_label[p]="$label"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
|
|
# command line
|
|
#
|
|
setup_args=""
|
|
edit_fstab_name=""
|
|
ext_label=""
|
|
verbose="no"
|
|
|
|
force_initialize=0
|
|
force_2_parts=0
|
|
force_sync=0
|
|
all_sync=0
|
|
usage_error=0
|
|
unattended=0
|
|
Unattended=0
|
|
quiet=0
|
|
custom_sync=0
|
|
leave_sd_usb_boot=0
|
|
convert_to_partuuid=0
|
|
|
|
while [ "$1" ]
|
|
do
|
|
case "$1" in
|
|
-v|--verbose)
|
|
verbose="yes"
|
|
rsync_options=${rsync_options}v
|
|
;;
|
|
-u|--unattended)
|
|
unattended=1
|
|
;;
|
|
-U|--Unattended-init)
|
|
unattended=1
|
|
Unattended=1
|
|
;;
|
|
-q|--quiet)
|
|
unattended=1
|
|
quiet=1
|
|
rsync_options=${rsync_options}q
|
|
;;
|
|
-s|--setup)
|
|
shift
|
|
if ! command -v $setup_command > /dev/null
|
|
then
|
|
echo "Cannot find script $setup_command for setup arg \"$1\"."
|
|
usage_error=1
|
|
fi
|
|
if [ "$setup_args" == "" ]
|
|
then
|
|
setup_args="$1"
|
|
else
|
|
setup_args="$setup_args $1"
|
|
fi
|
|
;;
|
|
-e|--edit-fstab)
|
|
shift
|
|
edit_fstab_name=$1
|
|
;;
|
|
-f|--force-initialize)
|
|
force_initialize=1
|
|
;;
|
|
-f2)
|
|
force_initialize=1
|
|
force_2_parts=1
|
|
;;
|
|
-x)
|
|
set -x
|
|
;;
|
|
-a|--all-sync)
|
|
all_sync=1
|
|
;;
|
|
-m|--mountdir)
|
|
shift
|
|
mount_ok=0
|
|
for ((p = 1; p <= n_src_parts; p++))
|
|
do
|
|
if ((!src_exists[p]))
|
|
then
|
|
continue
|
|
fi
|
|
if ((!custom_sync)) && ((p != root_part_num))
|
|
then
|
|
src_sync_part[p]=0
|
|
fi
|
|
if [ "${src_mounted_dir[p]}" == "$1" ]
|
|
then
|
|
src_sync_part[p]=1
|
|
mount_ok=1
|
|
fi
|
|
done
|
|
if ((!mount_ok))
|
|
then
|
|
echo "Asking to clone directory \"$1\", but it is not mounted."
|
|
usage_error=1
|
|
fi
|
|
custom_sync=1
|
|
;;
|
|
-L|--label_partitions)
|
|
shift
|
|
ext_label=$1
|
|
;;
|
|
-l|--leave-sd-usb-boot)
|
|
leave_sd_usb_boot=1
|
|
;;
|
|
-F|--Force-sync)
|
|
force_sync=1
|
|
;;
|
|
--convert-fstab-to-partuuid)
|
|
convert_to_partuuid=1
|
|
;;
|
|
-V|--version)
|
|
echo $PGM Version: $version
|
|
exit 0
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
;;
|
|
*)
|
|
if [ "$dst_disk" != "" ]
|
|
then
|
|
echo "Bad args"
|
|
usage
|
|
fi
|
|
dst_disk=$1
|
|
dir=`expr substr $dst_disk 1 5`
|
|
if [ "$dir" == "/dev/" ]
|
|
then
|
|
dst_disk=${dst_disk#/dev/}
|
|
fi
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if ((custom_sync)) && ((all_sync))
|
|
then
|
|
echo "-m and -a options at the same time conflict."
|
|
exit 1
|
|
fi
|
|
if ((custom_sync)) && ((force_initialize))
|
|
then
|
|
echo "-m and -f options at the same time conflict."
|
|
exit 1
|
|
fi
|
|
if [[ "$verbose" == "yes" ]] && ((quiet))
|
|
then
|
|
echo "-q and -v options at the same time conflict."
|
|
exit 1
|
|
fi
|
|
|
|
if ((usage_error))
|
|
then
|
|
echo ""
|
|
exit 1
|
|
fi
|
|
|
|
if ((convert_to_partuuid))
|
|
then
|
|
unattended=0
|
|
Unattended=0
|
|
|
|
fstab=/etc/fstab
|
|
fstab_tmp=/tmp/fstab
|
|
fstab_save=${fstab}.${PGM}-save
|
|
confirm "This will change your $fstab, are you sure?" "abort"
|
|
|
|
cp $fstab $fstab_tmp
|
|
printf "\nConverting $fstab from device names to PARTUUID\n"
|
|
count=0
|
|
for ((p = 1; p <= n_src_parts; p++))
|
|
do
|
|
if grep -q "^/dev/${src_partition[p]}" $fstab_tmp
|
|
then
|
|
partuuid=$(lsblk -n -o PARTUUID /dev/${src_partition[p]})
|
|
sed -i "s/\/dev\/${src_partition[p]}/PARTUUID=$partuuid/" $fstab_tmp
|
|
printf " Editing $fstab, changing /dev/${src_partition[p]} to $partuuid\n"
|
|
((++count))
|
|
fi
|
|
done
|
|
if ((count))
|
|
then
|
|
cp $fstab $fstab_save
|
|
cp $fstab_tmp $fstab
|
|
printf "Your original fstab is backed up to $fstab_save\n"
|
|
|
|
cmdline_txt=/boot/cmdline.txt
|
|
cmdline_save=$cmdline_txt.${PGM}-save
|
|
if [ -f $cmdline_txt ] && grep -q "$src_root_dev" $cmdline_txt
|
|
then
|
|
root_part=${src_partition[root_part_num]}
|
|
partuuid=$(lsblk -n -o PARTUUID $src_root_dev)
|
|
if [ "$partuuid" != "" ]
|
|
then
|
|
cp $cmdline_txt $cmdline_save
|
|
sed -i "s/\/dev\/$root_part/PARTUUID=$partuuid/" $cmdline_txt
|
|
printf " Editing $cmdline_txt, changing root=$src_root_dev to root=PARTUUID=$partuuid\n"
|
|
printf "Your original cmdline.txt is backed up to $cmdline_save\n"
|
|
fi
|
|
fi
|
|
else
|
|
printf "Could not find any $src_disk partition names in $fstab, nothing changed.\n"
|
|
fi
|
|
rm $fstab_tmp
|
|
echo ""
|
|
exit 0
|
|
fi
|
|
|
|
# dst_mount_flag enumerations:
|
|
live=1
|
|
temp=2
|
|
fail=3
|
|
|
|
for ((p = 1; p <= n_src_parts; p++))
|
|
do
|
|
if ((!src_exists[p]))
|
|
then
|
|
continue
|
|
fi
|
|
blocks=0
|
|
dst_mount_flag[p]=0
|
|
|
|
if [ "${src_mounted_dir[p]}" != "" ]
|
|
then # df blocks are 1024 bytes
|
|
dst_mount_flag[p]=$live
|
|
blocks=$(echo "$src_df_table" \
|
|
| grep -m 1 "^${src_device[p]}" | cut -d " " -f 3)
|
|
# in case intializing, get n_src_parts to compare to dest space
|
|
elif ((p == n_src_parts)) \
|
|
|| ((all_sync)) \
|
|
&& [ "${src_fs_type[p]}" != "EXT" ] \
|
|
&& [ "${src_fs_type[p]}" != "swap" ] \
|
|
&& [ "${src_fs_type[p]}" != "" ]
|
|
then
|
|
if mount ${src_device[p]} $clone
|
|
then
|
|
sleep 1
|
|
blocks=$(df | grep "^${src_device[p]}" \
|
|
| tr -s " " | cut -d " " -f 3)
|
|
umount $clone
|
|
dst_mount_flag[p]=$temp
|
|
if ((all_sync))
|
|
then
|
|
src_sync_part[p]=1
|
|
fi
|
|
else
|
|
dst_mount_flag[p]=$fail
|
|
fi
|
|
fi
|
|
src_used_sectors[p]=$((blocks * 2))
|
|
done
|
|
|
|
# ==== destination disk checks
|
|
#
|
|
if [ "$dst_disk" = "" ]
|
|
then
|
|
echo "No destination disk given."
|
|
usage
|
|
fi
|
|
|
|
chk_disk=`cat /proc/partitions | grep -m 1 $dst_disk`
|
|
|
|
if [ "$chk_disk" == "" ]
|
|
then
|
|
echo $"
|
|
Cannot find '$dst_disk' in the partition table. The partition table is:"
|
|
cat /proc/partitions
|
|
exit 1
|
|
fi
|
|
|
|
dst_part_base=$dst_disk
|
|
|
|
if [[ ${chk_disk: -1} =~ ^[0-9]$ ]]
|
|
then
|
|
if [[ $dst_disk == *"mmcblk"* ]]
|
|
then
|
|
SD_slot_dst=1
|
|
dst_part_base=${dst_disk}p
|
|
if [ "$edit_fstab_name" == "" ] \
|
|
&& ! grep -q "^PARTUUID=" /etc/fstab
|
|
then
|
|
edit_fstab_name=$dst_part_base
|
|
assumed_fstab_edit=1
|
|
else
|
|
assumed_fstab_edit=0
|
|
fi
|
|
else
|
|
qecho $"
|
|
Target disk $dst_disk ends with a digit so may be a partition.
|
|
$PGM requires disk names like 'sda' and not partition names like 'sda1'."
|
|
|
|
confirm "Continue anyway?" "abort"
|
|
fi
|
|
fi
|
|
|
|
if [ "$src_disk" == "$dst_disk" ]
|
|
then
|
|
echo "Destination disk $dst_disk is the booted disk. Cannot clone!"
|
|
exit 1
|
|
fi
|
|
|
|
dst_partition_table=$(parted -m "/dev/$dst_disk" unit s print | tr -d ';')
|
|
n_dst_parts=$(echo "$dst_partition_table" | tail -n 1 | cut -d ":" -f 1)
|
|
if [ "$n_dst_parts" == "/dev/$dst_disk" ]
|
|
then
|
|
n_dst_parts=0
|
|
fi
|
|
|
|
dst_disk_size=$(echo "$dst_partition_table" \
|
|
| grep "^/dev/$dst_disk" | cut -d ":" -f 2 | tr -d 's')
|
|
dst_root_dev=/dev/${dst_part_base}${root_part_num}
|
|
|
|
dst_mount_table=$(findmnt -o source,target -n -l \
|
|
| grep "^/dev/$dst_disk" | tr -s " ")
|
|
|
|
dst_fdisk_table=$(fdisk -l /dev/$dst_disk | grep "^/dev/")
|
|
line=$(echo "$dst_fdisk_table" | grep "Extended")
|
|
if [ "$line" != "" ]
|
|
then
|
|
dev=$(echo "$line" | cut -d " " -f 1)
|
|
ext_num="${dev: -1}"
|
|
else
|
|
ext_num=0
|
|
fi
|
|
|
|
for ((p = 1; p <= n_dst_parts; p++))
|
|
do
|
|
line=$(echo "$dst_partition_table" | grep -e "^${p}:")
|
|
if [ "$line" == "" ]
|
|
then
|
|
dst_exists[p]=0
|
|
continue
|
|
fi
|
|
dst_exists[p]=1
|
|
|
|
part="${dst_part_base}${p}"
|
|
dst_partition[p]="$part"
|
|
dst_device[p]="/dev/$part"
|
|
|
|
dst_start_sector[p]=$(echo "$line" | cut -d ":" -f 2 | tr -d 's')
|
|
dst_size_sectors[p]=$(echo "$line" | cut -d ":" -f 4 | tr -d 's')
|
|
|
|
part_type=$(echo "$line" | cut -d ":" -f 5)
|
|
if [ "$part_type" != "" ]
|
|
then
|
|
dst_fs_type[p]="$part_type"
|
|
else
|
|
dst_fs_type[p]="--"
|
|
fi
|
|
dst_label[p]="--"
|
|
|
|
if [[ "$part_type" == *"linux-swap"* ]]
|
|
then
|
|
dst_fs_type[p]="swap"
|
|
elif [[ "$part_type" == *"ext"* ]]
|
|
then
|
|
label=`e2label ${dst_device[p]} 2> /dev/null`
|
|
if [ "$label" != "" ]
|
|
then
|
|
dst_label[p]="$label"
|
|
fi
|
|
elif ((p == ext_num))
|
|
then
|
|
dst_fs_type[p]="EXT"
|
|
fi
|
|
done
|
|
|
|
fs_match=1
|
|
root_part_match=0
|
|
first_part_mismatch=0
|
|
|
|
initialize=$((force_initialize))
|
|
|
|
for ((p = 1; p <= n_src_parts; p++))
|
|
do
|
|
if ((!src_exists[p]))
|
|
then
|
|
continue
|
|
fi
|
|
stype=${src_fs_type[p]}
|
|
dtype=${dst_fs_type[p]}
|
|
if [ "$stype" != "$dtype" ]
|
|
then
|
|
tmp_match=0
|
|
if [[ "$stype" == *"fat"* ]] && [[ "$dtype" == *"fat"* ]]
|
|
then
|
|
tmp_match=1
|
|
elif [[ "$stype" == *"ext"* ]] && [[ "$dtype" == *"ext"* ]]
|
|
then
|
|
tmp_match=1
|
|
fi
|
|
if ((tmp_match && p == root_part_num))
|
|
then
|
|
root_part_match=1
|
|
fi
|
|
if ((!tmp_match)) && ((src_sync_part[p]))
|
|
then
|
|
first_part_mismatch=$p
|
|
fs_match=0
|
|
break
|
|
fi
|
|
fi
|
|
done
|
|
|
|
|
|
if ((!fs_match))
|
|
then
|
|
initialize=1
|
|
fs_match_string="do not match"
|
|
else
|
|
fs_match_string="OK, they match"
|
|
fi
|
|
|
|
if ((initialize)) && ((quiet))
|
|
then
|
|
echo "Quiet mode asked for but an initialize is required - can't clone!"
|
|
exit 1
|
|
fi
|
|
|
|
for ((p = n_dst_parts; p >= 1; --p))
|
|
do
|
|
if ((!dst_exists[p]))
|
|
then
|
|
continue
|
|
fi
|
|
dir=$(echo "$dst_mount_table" \
|
|
| grep -e "^${dst_device[p]}" | cut -d " " -f 2)
|
|
unmount_or_abort "$dir" \
|
|
"Destination disk partition ${dst_device[p]} is mounted on $dir."
|
|
done
|
|
|
|
mounted_dev=$(findmnt $clone -o source -n)
|
|
unmount_or_abort "$mounted_dev" \
|
|
"Directory $clone is already mounted with $mounted_dev."
|
|
|
|
mounted_dev=$(findmnt $clone_src -o source -n)
|
|
unmount_or_abort "$mounted_dev" \
|
|
"Directory $clone_src is already mounted with $mounted_dev."
|
|
|
|
mounted_dev=$(findmnt /mnt -o source -n)
|
|
unmount_or_abort "$mounted_dev" "$mounted_dev is currently mounted on /mnt."
|
|
|
|
|
|
if [ ! -d $clone ]
|
|
then
|
|
mkdir $clone
|
|
fi
|
|
if [ ! -d $clone_src ]
|
|
then
|
|
mkdir $clone_src
|
|
fi
|
|
|
|
# Do not include a dhpys swapfile in rsync. It regenerates at boot.
|
|
#
|
|
if [ -f /etc/dphys-swapfile ]
|
|
then
|
|
swapfile=`cat /etc/dphys-swapfile | grep ^CONF_SWAPFILE | cut -f 2 -d=`
|
|
if [ "$swapfile" = "" ]
|
|
then
|
|
swapfile=/var/swap
|
|
fi
|
|
exclude_swapfile="--exclude $swapfile"
|
|
fi
|
|
|
|
if ((grub_auto)) && [ -d /boot/grub ] && command -v grub-install
|
|
then
|
|
have_grub=1
|
|
else
|
|
have_grub=0
|
|
fi
|
|
|
|
print_partitions
|
|
|
|
if ((initialize))
|
|
then
|
|
if ((unattended && !Unattended))
|
|
then
|
|
echo $"
|
|
Unattended -u option not allowed when initializing.
|
|
Use -U for unattended even if initializing.
|
|
"
|
|
exit 1
|
|
fi
|
|
|
|
reason="FS types mismatch"
|
|
if ((force_initialize))
|
|
then
|
|
reason="forced by option"
|
|
fi
|
|
|
|
n_image_parts=$((force_2_parts ? 2 : n_src_parts))
|
|
|
|
start_sector=${src_start_sector[$n_image_parts]}
|
|
last_part_sectors=$((dst_disk_size - start_sector))
|
|
last_part_used=${src_used_sectors[$n_image_parts]}
|
|
last_part_space=$(( (last_part_sectors > last_part_used) ? 1 : 0 ))
|
|
|
|
if ((last_part_sectors < 7812))
|
|
then
|
|
printf "%-22s : %s\n" "** FATAL **" \
|
|
"Initialize required - $reason."
|
|
printf "%-22s : %s\n" "" \
|
|
" But destination is too small. To image partition"
|
|
readable $((start_sector + 7812)) "512" min_size
|
|
printf "%-22s : %s\n" "" \
|
|
" source sizes, minimum destination size is $min_size."
|
|
if ((n_src_parts > 2))
|
|
then
|
|
printf "%-22s : %s\n" " " \
|
|
" Options: try a two partition clone with -f2, or"
|
|
printf "%-22s : %s\n" " " \
|
|
" manually create custom partitions that can work."
|
|
fi
|
|
printf "%-23s:\n" "-----------------------"
|
|
exit 1
|
|
fi
|
|
|
|
readable $((last_part_sectors + 7812)) "512" image_space_readable
|
|
|
|
echo "== Initialize: IMAGE $src_disk partition table to $dst_disk - $reason =="
|
|
print_image_actions
|
|
print_options
|
|
|
|
printf "%-22s : %s\n" "** WARNING **" \
|
|
"All destination disk $dst_disk data will be overwritten!"
|
|
printf "%-22s : %s\n" "" \
|
|
" The partition structure will be imaged from $src_disk."
|
|
|
|
abort=0
|
|
if ((!last_part_space))
|
|
then
|
|
readable $last_part_used "512" used_readable
|
|
printf "%-22s : %s\n" "** WARNING **" \
|
|
"Destination last partition resize to $image_space_readable"
|
|
printf "%-22s : %s\n" "" \
|
|
" is too small to hold source used $used_readable."
|
|
if [ "$n_image_parts" == "$root_part_num" ] && ((!force_sync))
|
|
then
|
|
printf "%-22s : %s\n" "** FATAL **" \
|
|
"This is the root partition, so aborting!"
|
|
printf "%-22s : %s\n" "" \
|
|
" Use -F to override."
|
|
abort=1
|
|
elif ((!force_sync))
|
|
then
|
|
printf "%-22s : %s\n" "" \
|
|
" The partition SYNC is skipped, use -F to override."
|
|
else
|
|
printf "%-22s : %s\n" "" \
|
|
" ** Syncing anyway as you asked with -F. **"
|
|
fi
|
|
fi
|
|
printf "%-23s:\n" "-----------------------"
|
|
if ((abort))
|
|
then
|
|
exit 1
|
|
fi
|
|
confirm "Initialize and clone to the destination disk ${dst_disk}?" "abort"
|
|
|
|
if ((!Unattended)) && [ "$ext_label" == "" ]
|
|
then
|
|
printf "Optional destination ext type file system label (16 chars max): "
|
|
read ext_label
|
|
fi
|
|
|
|
start_time=`date '+%H:%M:%S'`
|
|
start_sec=$(date '+%s')
|
|
|
|
if [ "${src_mounted_dir[1]}" == "/boot" ]
|
|
then
|
|
image_to_part=2
|
|
msg="/boot"
|
|
else
|
|
image_to_part=1
|
|
msg="first"
|
|
fi
|
|
image_to_sector=${src_start_sector[$image_to_part]}
|
|
count=$((image_to_sector / 2 / 1024 + 4)) # in MiB blocks for dd bs=1M
|
|
|
|
printf "\nInitializing\n"
|
|
printf " Imaging past the start of $msg partition $image_to_part.\n"
|
|
sync
|
|
printf " => dd if=/dev/$src_disk of=/dev/$dst_disk bs=1M count=$count ..."
|
|
dd if=/dev/$src_disk of=/dev/$dst_disk bs=1M count=$count &> /tmp/$PGM-output
|
|
if [ "$?" != 0 ]
|
|
then
|
|
printf "\n dd failed. See /tmp/$PGM-output.\n"
|
|
printf " Try running $PGM again.\n\n"
|
|
exit 1
|
|
fi
|
|
echo ""
|
|
sync
|
|
sleep 1
|
|
sfd0=$(sfdisk -d /dev/$src_disk)
|
|
if ((force_2_parts && (n_src_parts > n_image_parts)))
|
|
then
|
|
remove_part_start=${src_partition[3]}
|
|
sfd0=$(echo "$sfd0" | sed -e "/\/dev\/$remove_part_start/,\$d")
|
|
fi
|
|
|
|
part="${src_part_base}$n_image_parts"
|
|
sfd1=$(echo "$sfd0" | sed "\/dev\/$part/s/size=[^,]*,//")
|
|
|
|
if ((ext_part_num > 0 && !force_2_parts))
|
|
then
|
|
part="${src_part_base}$ext_part_num"
|
|
sfd1=$(echo "$sfd1" | sed "\/dev\/$part/s/size=[^,]*,//")
|
|
fi
|
|
|
|
printf " Resizing last partition to end of disk ..."
|
|
for ((x = 0; x < 3; ++x))
|
|
do
|
|
sleep $((x + 1))
|
|
sfdisk --force /dev/$dst_disk &> /tmp/$PGM-output <<< "$sfd1"
|
|
if [ "$?" == 0 ]
|
|
then
|
|
break
|
|
fi
|
|
if ((x == 2))
|
|
then
|
|
printf "\n====$PGM\n==orig:\n%s\n\n==edited:\n%s\n" \
|
|
"$sfd0" "$sfd1" >> /tmp/$PGM-output
|
|
printf "\n Resize failed. See /tmp/$PGM-output.\n"
|
|
printf " Try running $PGM again.\n\n"
|
|
|
|
# Don't let dst disk keep source disk ID. Can lead to remounts.
|
|
new_id=$(od -A n -t x -N 4 /dev/urandom | tr -d " ")
|
|
qprintf "x\ni\n0x$new_id\nr\nw\nq\n" | fdisk /dev/$dst_disk > /dev/null
|
|
exit 1
|
|
fi
|
|
done
|
|
printf "\n Resize success.\n"
|
|
printf " Changing destination Disk ID ..."
|
|
sync
|
|
sleep 2
|
|
|
|
new_id=$(od -A n -t x -N 4 /dev/urandom | tr -d " ")
|
|
qprintf "x\ni\n0x$new_id\nr\nw\nq\n" | fdisk /dev/$dst_disk > /dev/null
|
|
printf "\n Delaying so partprobe can update /dev entries ..."
|
|
sync
|
|
sleep 2
|
|
partprobe "/dev/$dst_disk"
|
|
sleep 2
|
|
echo ""
|
|
|
|
for ((p = n_image_parts + 1; p <= n_src_parts; p++))
|
|
do
|
|
src_sync_part[p]=0
|
|
done
|
|
|
|
for ((p = 1; p <= n_image_parts; p++))
|
|
do
|
|
if ((!src_exists[p]))
|
|
then
|
|
continue
|
|
fi
|
|
dst_dev=/dev/${dst_part_base}${p}
|
|
fs_type=${src_fs_type[$p]}
|
|
if ((p == ext_part_num)) \
|
|
|| [ "$fs_type" == "--" ]
|
|
then
|
|
continue
|
|
fi
|
|
|
|
if [ "${src_mounted_dir[p]}" == "/boot" ] && ((p == 1))
|
|
then
|
|
# Avoid "Volume not properly unmounted" warnings.
|
|
printf " => fsck -p $dst_dev ..."
|
|
fsck -p $dst_dev &>> /tmp/$PGM-log
|
|
echo ""
|
|
ext_label $p $fs_type "" label
|
|
if [ "$label" != "" ]
|
|
then
|
|
e2label $dst_dev $label
|
|
fi
|
|
else
|
|
if [ "$fs_type" == "swap" ]
|
|
then
|
|
printf " => mkswap $dst_dev\n"
|
|
mkswap $dst_dev &>> /tmp/$PGM-log
|
|
elif ((p != ext_part_num))
|
|
then
|
|
if [ "${src_mounted_dir[p]}" != "" ] || ((p == n_image_parts))
|
|
then
|
|
if [ "$fs_type" == "fat16" ]
|
|
then
|
|
fs_type="vfat"
|
|
elif [ "$fs_type" == "fat32" ]
|
|
then
|
|
fs_type="vfat -F 32"
|
|
fi
|
|
ext_label $p $fs_type "-L" label
|
|
printf " => mkfs -t $fs_type $label $dst_dev ..."
|
|
yes | mkfs -t "$fs_type" $label "$dst_dev" &>> /tmp/$PGM-output
|
|
# mkfs -t "$fs_type" "$dst_dev" &>> /tmp/$PGM-output <<< "yes"
|
|
echo ""
|
|
if ((p == n_image_parts))
|
|
then
|
|
if ((!last_part_space))
|
|
then
|
|
src_sync_part[p]=$force_sync
|
|
else
|
|
src_sync_part[p]=1
|
|
fi
|
|
fi
|
|
else
|
|
printf " => dd if=${src_device[$p]} of=$dst_dev bs=1M ..."
|
|
dd if=${src_device[$p]} of=$dst_dev bs=1M &>> /tmp/$PGM-output
|
|
if [ "$?" != 0 ]
|
|
then
|
|
printf "\n dd failed. See /tmp/$PGM-output.\n"
|
|
else
|
|
echo ""
|
|
fi
|
|
ext_label $p $fs_type "" label
|
|
if [ "$label" != "" ]
|
|
then
|
|
echo " e2label $dst_dev $label"
|
|
e2label $dst_dev $label
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
ext_label=""
|
|
else
|
|
qecho "== SYNC $src_disk file systems to $dst_disk =="
|
|
print_sync_actions
|
|
print_options
|
|
|
|
informed=0
|
|
space_ok=1
|
|
all_sync_mount_ok=1
|
|
if ((force_sync))
|
|
then
|
|
err="WARNING"
|
|
else
|
|
err="FATAL"
|
|
fi
|
|
for ((p = 1; p <= n_src_parts; p++))
|
|
do
|
|
if ((!src_exists[p] || !dst_exists[p]))
|
|
then
|
|
continue
|
|
fi
|
|
if ((${dst_size_sectors[p]} < ${src_used_sectors[p]})) \
|
|
&& ((src_sync_part[p]))
|
|
then
|
|
qprintf "%-22s : %s\n" "** $err **" \
|
|
"Partition $p: source used > destination space."
|
|
space_ok=$force_sync
|
|
informed=1
|
|
fi
|
|
if ((all_sync && dst_mount_flag[p] == fail))
|
|
then
|
|
qprintf "%-22s : %s\n" "** $err **" \
|
|
"Partition $p: mount failed, cannot sync."
|
|
all_sync_mount_ok=$force_sync
|
|
informed=1
|
|
fi
|
|
done
|
|
if ((informed))
|
|
then
|
|
qprintf "%-23s:\n" "-----------------------"
|
|
fi
|
|
|
|
if ((!space_ok || !all_sync_mount_ok))
|
|
then
|
|
printf "\nAborting!\n"
|
|
if ((!space_ok))
|
|
then
|
|
printf " Use -F to override used > space fail or -m to select mounts to clone.\n"
|
|
fi
|
|
if ((!all_sync_mount_ok))
|
|
then
|
|
printf " Use -a -F to sync all except failed mounts.\n"
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
confirm "Ok to proceed with the clone?" "abort"
|
|
start_time=`date '+%H:%M:%S'`
|
|
start_sec=$(date '+%s')
|
|
fi
|
|
|
|
line=$(fdisk -l /dev/$dst_disk | grep "Disk identifier:")
|
|
dst_disk_ID=${line#*x}
|
|
if [ "$dst_disk_ID" == "$src_disk_ID" ]
|
|
then
|
|
qecho "Destination disk has same Disk ID as source, changing it."
|
|
new_id=$(od -A n -t x -N 4 /dev/urandom | tr -d " ")
|
|
qprintf "x\ni\n0x$new_id\nr\nw\nq\n" | fdisk /dev/$dst_disk | grep changed
|
|
sync
|
|
sleep 2
|
|
partprobe "/dev/$dst_disk"
|
|
sleep 2
|
|
|
|
line=$(fdisk -l /dev/$dst_disk | grep "Disk identifier:")
|
|
dst_disk_ID=${line#*x}
|
|
if [ "$dst_disk_ID" == "$src_disk_ID" ]
|
|
then
|
|
qecho " Failed to set a new Disk ID."
|
|
fi
|
|
fi
|
|
|
|
sync
|
|
qprintf "\nSyncing file systems (can take a long time)\n"
|
|
|
|
sync_msg_done=0
|
|
for ((p = 1; p <= n_src_parts; p++))
|
|
do
|
|
if ((!src_exists[p]))
|
|
then
|
|
continue
|
|
fi
|
|
if ((src_sync_part[p] && dst_mount_flag[p] == temp))
|
|
then
|
|
if ((!sync_msg_done))
|
|
then
|
|
qprintf "Syncing unmounted partitions:\n"
|
|
fi
|
|
sync_msg_done=1
|
|
dst_dev=/dev/${dst_part_base}${p}
|
|
fs_type=${src_fs_type[$p]}
|
|
ext_label $p $fs_type "" label
|
|
if [ "$label" != "" ]
|
|
then
|
|
qecho " e2label $dst_dev $label"
|
|
e2label $dst_dev $label
|
|
fi
|
|
|
|
mount_partition ${src_device[p]} $clone_src ""
|
|
mount_partition $dst_dev $clone "$clone_src"
|
|
unmount_list="$clone_src $clone"
|
|
rsync_file_system "${clone_src}/" "${clone}" ""
|
|
unmount_list "$unmount_list"
|
|
fi
|
|
done
|
|
|
|
qprintf "Syncing mounted partitions:\n"
|
|
|
|
fs_type=${src_fs_type[$root_part_num]}
|
|
ext_label $root_part_num $fs_type "" label
|
|
if [ "$label" != "" ]
|
|
then
|
|
qecho " e2label $dst_root_dev $label"
|
|
e2label $dst_root_dev $label
|
|
fi
|
|
|
|
mount_partition $dst_root_dev $clone ""
|
|
unmount_list="$clone"
|
|
|
|
rsync_file_system "//" "$clone" "with-root-excludes"
|
|
|
|
for ((p = 1; p <= n_src_parts; p++))
|
|
do
|
|
if ((!src_exists[p]))
|
|
then
|
|
continue
|
|
fi
|
|
if ((p != root_part_num && src_sync_part[p] && dst_mount_flag[p] == live))
|
|
then
|
|
dst_dir=$clone${src_mounted_dir[p]}
|
|
if [ ! -d $dst_dir ]
|
|
then
|
|
mkdir -p $dst_dir
|
|
fi
|
|
|
|
dst_dev=/dev/${dst_part_base}${p}
|
|
fs_type=${src_fs_type[$p]}
|
|
ext_label $p $fs_type "" label
|
|
if [ "$label" != "" ]
|
|
then
|
|
qecho " e2label $dst_dev $label"
|
|
e2label $dst_dev $label
|
|
fi
|
|
|
|
mount_partition "$dst_dev" "$dst_dir" "$unmount_list"
|
|
rsync_file_system "${src_mounted_dir[p]}/" "${dst_dir}" ""
|
|
unmount_list="$dst_dir $unmount_list"
|
|
fi
|
|
done
|
|
qecho ""
|
|
|
|
# Fix PARTUUID or device name references in cmdline.txt and fstab
|
|
#
|
|
fstab=${clone}/etc/fstab
|
|
cmdline_txt=${clone}/boot/cmdline.txt
|
|
|
|
if [ -f $cmdline_txt ]
|
|
then
|
|
if ((leave_sd_usb_boot && SD_slot_dst))
|
|
then
|
|
qecho "Leaving SD to USB boot alone."
|
|
cp $cmdline_txt ${clone}/boot/cmdline.boot
|
|
cmdline_txt=${clone}/boot/cmdline.boot
|
|
fi
|
|
if grep -q $src_disk_ID $cmdline_txt
|
|
then
|
|
qecho "Editing $cmdline_txt PARTUUID to use $dst_disk_ID"
|
|
sed -i "s/${src_disk_ID}/${dst_disk_ID}/" "$cmdline_txt"
|
|
elif [ "$edit_fstab_name" != "" ] && grep -q ${src_part_base} $cmdline_txt
|
|
then
|
|
qecho "Editing $cmdline_txt references from $src_part_base to $edit_fstab_name"
|
|
sed -i "s/${src_part_base}/$edit_fstab_name/" "$cmdline_txt"
|
|
fi
|
|
if ((leave_sd_usb_boot && SD_slot_boot))
|
|
then
|
|
qecho "Copying USB cmdline.txt to SD card to set up USB boot."
|
|
cp /boot/cmdline.txt /boot/cmdline.boot
|
|
cp $cmdline_txt /boot/cmdline.txt
|
|
fi
|
|
fi
|
|
|
|
if grep -q $src_disk_ID $fstab
|
|
then
|
|
qecho "Editing $fstab PARTUUID to use $dst_disk_ID"
|
|
sed -i "s/${src_disk_ID}/${dst_disk_ID}/g" "$fstab"
|
|
elif [ "$edit_fstab_name" != "" ] && grep -q ${src_part_base} $fstab
|
|
then
|
|
qecho "Editing $fstab references from $src_part_base to $edit_fstab_name"
|
|
sed -i "s/${src_part_base}/${edit_fstab_name}/" "$fstab"
|
|
fi
|
|
|
|
|
|
rm -f $clone/etc/udev/rules.d/70-persistent-net.rules
|
|
|
|
dst_root_vol_name=`e2label $dst_root_dev`
|
|
|
|
if [ "$dst_root_vol_name" = "" ]
|
|
then
|
|
dst_root_vol_name="no label"
|
|
fi
|
|
|
|
if ((have_grub))
|
|
then
|
|
qecho "grub-install --root-directory=$clone /dev/$dst_disk"
|
|
if ((quiet))
|
|
then
|
|
grub-install --root-directory=$clone /dev/$dst_disk &> /dev/null
|
|
else
|
|
grub-install --root-directory=$clone /dev/$dst_disk
|
|
fi
|
|
fi
|
|
|
|
if [ "$setup_args" != "" ]
|
|
then
|
|
qprintf "\nRunning setup script: $setup_command $setup_args\n"
|
|
$setup_command $setup_args
|
|
fi
|
|
|
|
date=`date '+%F %H:%M'`
|
|
echo "$date $HOSTNAME $PGM : clone to $dst_disk ($dst_root_vol_name)" \
|
|
>> $clone_log
|
|
echo "$date $HOSTNAME $PGM : clone to $dst_disk ($dst_root_vol_name)" \
|
|
>> ${clone}${clone_log}
|
|
|
|
|
|
stop_sec=$(date '+%s')
|
|
clone_sec=$((stop_sec - start_sec))
|
|
|
|
stop_time=`date '+%H:%M:%S'`
|
|
|
|
qecho "==============================="
|
|
qecho "Done with clone to /dev/$dst_disk"
|
|
qprintf " Start - %s End - %s Elapsed Time - %d:%02d\n" \
|
|
"$start_time" "$stop_time" "$((clone_sec / 60))" "$((clone_sec % 60))"
|
|
|
|
if ((!unattended))
|
|
then
|
|
echo -n $"
|
|
Cloned partitions are mounted on $clone for inspection or customizing.
|
|
|
|
Hit Enter when ready to unmount the /dev/$dst_disk partitions ..."
|
|
|
|
read resp
|
|
fi
|
|
|
|
unmount_list "$unmount_list"
|
|
qprintf "===============================\n\n"
|
|
|
|
exit 0
|