backuptool: Support seamless backup and restore to extra partitions

For scripts declaring ADDOND_VERSION=3 automatically mount
vendor, product, system_ext and others (when they're dedicated partitions).

Also expose the get_output_path() function to get the path to where
a file is mounted in case it lives in a dedicated partition.

ab exapmles:
get_output_path "system/product/priv-app/MyApp.apk"  = "/postinstall/product/priv-app/MyApk.apk"
get_output_path "system/app/MySystemApp.apk"         = "/postinstall/system/app/MySystemApp.apk"

a-only examples:
get_output_path "/mnt/system/system/product/priv-app/MyApp.apk" = "/mnt/system/system/product/priv-app/MyApp.apk"

******************************************************************
Instead of cycling all scripts for each stage, run
pre-backup -> backup -> post-backup in quick succession
(and likewise for restore), to ensure backwards compatibility
with scripts that wrongly assumed their environment not to
change between steps.
This is needed because we want to undo any mounting done for V3
scripts when executing V2 scripts. If a V2 script did mounting in
pre-restore and expected things to still be mounted in restore,
we would break their (yes incorrect) assumption.

Change-Id: I73fbad6f45824fed99e4482128769435348588f5
This commit is contained in:
Alessandro Astone
2020-12-29 18:38:28 +01:00
committed by Paul Keith
parent 6245873019
commit c63fa8441b
4 changed files with 226 additions and 29 deletions

View File

@@ -34,10 +34,15 @@ restore_file() {
if [ ! -d "$DIR" ]; then if [ ! -d "$DIR" ]; then
mkdir -p "$DIR"; mkdir -p "$DIR";
fi fi
copy_file "$C/$DIR/$FILE" "$1"; copy_file "$C/$DIR/$FILE" $(get_output_path "$1");
if [ -n "$2" ]; then if [ -n "$2" ]; then
echo "Deleting obsolete file $2" echo "Deleting obsolete file $2"
rm "$2"; rm $(get_output_path "$2");
fi fi
fi fi
} }
get_output_path() {
# In recovery we mounted all partitions in the right place, so we can rely on symlinks
echo "$1"
}

View File

@@ -8,9 +8,20 @@ export SYSDEV="$(readlink -nf "$2")"
export SYSFS="$3" export SYSFS="$3"
export V=18.1 export V=18.1
export ADDOND_VERSION=3
# Partitions to mount for backup/restore in V3
export all_V3_partitions="vendor product system_ext"
# Scripts in /system/addon.d expect to find backuptool.functions in /tmp # Scripts in /system/addon.d expect to find backuptool.functions in /tmp
cp -f /tmp/install/bin/backuptool.functions /tmp cp -f /tmp/install/bin/backuptool.functions /tmp
get_script_version() {
version=$(grep "^# ADDOND_VERSION=" $1 | cut -d= -f2)
[ -z "$version" ] && version=1
echo $version
}
# Preserve /system/addon.d in /tmp/addon.d # Preserve /system/addon.d in /tmp/addon.d
preserve_addon_d() { preserve_addon_d() {
if [ -d $S/addon.d/ ]; then if [ -d $S/addon.d/ ]; then
@@ -43,15 +54,31 @@ fi
return 0 return 0
} }
# Execute /system/addon.d/*.sh scripts with $1 parameter # Execute /system/addon.d/*.sh scripts with each $@ parameter
run_stage() { run_stages() {
if [ -d /tmp/addon.d/ ]; then if [ -d /tmp/addon.d/ ]; then
for script in $(find /tmp/addon.d/ -name '*.sh' |sort -n); do for script in $(find /tmp/addon.d/ -name '*.sh' |sort -n); do
$script $1 v=$(get_script_version $script)
if [ $v -ge 3 ]; then
mount_extra $all_V3_partitions
else
umount_extra $all_V3_partitions
fi
for stage in $@; do
if [ $v -ge 3 ]; then
$script $stage
else
ADDOND_VERSION=2 $script $stage
fi
done
done done
fi fi
} }
#####################
### Mount helpers ###
#####################
determine_system_mount() { determine_system_mount() {
if grep -q -e"^$SYSDEV" /proc/mounts; then if grep -q -e"^$SYSDEV" /proc/mounts; then
umount $(grep -e"^$SYSDEV" /proc/mounts | cut -d" " -f2) umount $(grep -e"^$SYSDEV" /proc/mounts | cut -d" " -f2)
@@ -76,26 +103,77 @@ unmount_system() {
umount $SYSMOUNT umount $SYSMOUNT
} }
get_block_for_mount_point() {
grep -v "^#" /etc/recovery.fstab | grep " $1 " | tail -n1 | tr -s ' ' | cut -d' ' -f1
}
find_block() {
local name="$1"
local fstab_entry=$(get_block_for_mount_point "/$name")
# P-SAR hacks
[ -z "$fstab_entry" ] && [ "$name" = "system" ] && fstab_entry=$(get_block_for_mount_point "/")
[ -z "$fstab_entry" ] && [ "$name" = "system" ] && fstab_entry=$(get_block_for_mount_point "/system_root")
local dev
if [ "$DYNAMIC_PARTITIONS" = "true" ]; then
if [ -n "$fstab_entry" ]; then
dev="${BLK_PATH}/${fstab_entry}"
else
dev="${BLK_PATH}/${name}"
fi
else
if [ -n "$fstab_entry" ]; then
dev="$fstab_entry"
else
dev="${BLK_PATH}/${name}"
fi
fi
if [ -b "$dev" ]; then
echo "$dev"
fi
}
determine_system_mount determine_system_mount
DYNAMIC_PARTITIONS=$(getprop ro.boot.dynamic_partitions)
BLK_PATH=$(dirname "$SYSDEV")
mount_extra() {
for partition in $@; do
mnt_point="/$partition"
mountpoint "$mnt_point" >/dev/null 2>&1 && break
blk_dev=$(find_block "$partition")
if [ -e "$blk_dev" ]; then
[ "$DYNAMIC_PARTITIONS" = "true" ] && blockdev --setrw "$blk_dev"
mkdir -p "$mnt_point"
mount -o rw "$blk_dev" "$mnt_point"
fi
done
}
umount_extra() {
for partition in $@; do
umount -l "/$partition" 2>/dev/null
done
}
case "$1" in case "$1" in
backup) backup)
mount_system mount_system
if check_prereq; then if check_prereq; then
mkdir -p $C mkdir -p $C
preserve_addon_d preserve_addon_d
run_stage pre-backup run_stages pre-backup backup post-backup
run_stage backup
run_stage post-backup
fi fi
unmount_system unmount_system
;; ;;
restore) restore)
mount_system mount_system
if check_prereq; then if check_prereq; then
run_stage pre-restore run_stages pre-restore restore post-restore
run_stage restore umount_extra $all_V3_partitions
run_stage post-restore
restore_addon_d restore_addon_d
rm -rf $C rm -rf $C
sync sync

View File

@@ -39,10 +39,35 @@ backup_file() {
restore_file() { restore_file() {
if [ -e "$C/$1" -o -L "$C/$1" ]; then if [ -e "$C/$1" -o -L "$C/$1" ]; then
move_file "$C/$1" "/postinstall/$1"; move_file "$C/$1" $(get_output_path "$1");
if [ -n "$2" ]; then if [ -n "$2" ]; then
echo "Deleting obsolete file $2" echo "Deleting obsolete file $2"
rm "$2"; rm $(get_output_path "$2");
fi fi
fi fi
} }
get_output_path() {
if [ $ADDOND_VERSION -lt 3 ]; then
echo "/postinstall/$1"
return
fi
file=$(echo "$1" | sed "s|^$S/||")
if __is_on_mounted_partition "$file"; then
echo "/postinstall/$file"
else
echo "/postinstall/$1"
fi
}
__is_on_mounted_partition() {
for p in $all_V3_partitions; do
mnt_point="/postinstall/$p"
if echo "$1" | grep -q "^$p/" && [ ! -L "$mnt_point" ] && mountpoint >/dev/null 2>&1 "$mnt_point"; then
return 0
fi
done
return 1
}

View File

@@ -7,26 +7,31 @@ export S=/system
export C=/postinstall/tmp/backupdir export C=/postinstall/tmp/backupdir
export V=18.1 export V=18.1
export ADDOND_VERSION=2 export ADDOND_VERSION=3
# Partitions to mount for backup/restore in V3
export all_V3_partitions="vendor product system_ext odm oem"
# Scripts in /system/addon.d expect to find backuptool.functions in /tmp # Scripts in /system/addon.d expect to find backuptool.functions in /tmp
mkdir -p /postinstall/tmp/ mkdir -p /postinstall/tmp/
mountpoint /postinstall/tmp >/dev/null 2>&1 || mount -t tmpfs tmpfs /postinstall/tmp mountpoint /postinstall/tmp >/dev/null 2>&1 || mount -t tmpfs tmpfs /postinstall/tmp
cp -f /postinstall/system/bin/backuptool_ab.functions /postinstall/tmp/backuptool.functions cp -f /postinstall/system/bin/backuptool_ab.functions /postinstall/tmp/backuptool.functions
get_script_version() {
version=$(grep "^# ADDOND_VERSION=" $1 | cut -d= -f2)
[ -z "$version" ] && version=1
echo $version
}
# Preserve /system/addon.d in /tmp/addon.d # Preserve /system/addon.d in /tmp/addon.d
preserve_addon_d() { preserve_addon_d() {
if [ -d /system/addon.d/ ]; then if [ -d /system/addon.d/ ]; then
mkdir -p /postinstall/tmp/addon.d/ mkdir -p /postinstall/tmp/addon.d/
cp -a /system/addon.d/* /postinstall/tmp/addon.d/ cp -a /system/addon.d/* /postinstall/tmp/addon.d/
# Discard any scripts that aren't at least our version level # Discard any version 1 script, as it is not compatible with a/b
for f in /postinstall/tmp/addon.d/*sh; do for f in /postinstall/tmp/addon.d/*sh; do
SCRIPT_VERSION=$(grep "^# ADDOND_VERSION=" $f | cut -d= -f2) if [ $(get_script_version $f) = 1 ]; then
if [ -z "$SCRIPT_VERSION" ]; then
SCRIPT_VERSION=1
fi
if [ $SCRIPT_VERSION -lt $ADDOND_VERSION ]; then
rm $f rm $f
fi fi
done done
@@ -58,8 +63,8 @@ fi
return 0 return 0
} }
# Execute /system/addon.d/*.sh scripts with $1 parameter # Execute /system/addon.d/*.sh scripts with each $@ parameter
run_stage() { run_stages() {
if [ -d /postinstall/tmp/addon.d/ ]; then if [ -d /postinstall/tmp/addon.d/ ]; then
for script in $(find /postinstall/tmp/addon.d/ -name '*.sh' |sort -n); do for script in $(find /postinstall/tmp/addon.d/ -name '*.sh' |sort -n); do
# we have no /sbin/sh in android, only recovery # we have no /sbin/sh in android, only recovery
@@ -67,26 +72,110 @@ if [ -d /postinstall/tmp/addon.d/ ]; then
sed -i '0,/#!\/sbin\/sh/{s|#!/sbin/sh|#!/system/bin/sh|}' $script sed -i '0,/#!\/sbin\/sh/{s|#!/sbin/sh|#!/system/bin/sh|}' $script
# we can't count on /tmp existing on an A/B device, so utilize /postinstall/tmp as tmpfs # we can't count on /tmp existing on an A/B device, so utilize /postinstall/tmp as tmpfs
sed -i 's|. /tmp/backuptool.functions|. /postinstall/tmp/backuptool.functions|g' $script sed -i 's|. /tmp/backuptool.functions|. /postinstall/tmp/backuptool.functions|g' $script
$script $1
v=$(get_script_version $script)
if [ $v -ge 3 ]; then
mount_extra $all_V3_partitions
else
umount_extra $all_V3_partitions
fi
for stage in $@; do
if [ $v -ge 3 ]; then
$script $stage
else
ADDOND_VERSION=2 $script $stage
fi
done
done done
fi fi
} }
#####################
### Mount helpers ###
#####################
get_block_for_mount_point() {
grep -v "^#" /vendor/etc/fstab.$(getprop ro.boot.hardware) | grep " $1 " | tail -n1 | tr -s ' ' | cut -d' ' -f1
}
find_block() {
local name="$1"
local fstab_entry=$(get_block_for_mount_point "/$name")
# P-SAR hacks
[ -z "$fstab_entry" ] && [ "$name" = "system" ] && fstab_entry=$(get_block_for_mount_point "/")
[ -z "$fstab_entry" ] && [ "$name" = "system" ] && fstab_entry=$(get_block_for_mount_point "/system_root")
local dev
if [ "$DYNAMIC_PARTITIONS" = "true" ]; then
if [ -n "$fstab_entry" ]; then
dev="${BLK_PATH}/${fstab_entry}${SLOT_SUFFIX}"
else
dev="${BLK_PATH}/${name}${SLOT_SUFFIX}"
fi
else
if [ -n "$fstab_entry" ]; then
dev="${fstab_entry}${SLOT_SUFFIX}"
else
dev="${BLK_PATH}/${name}${SLOT_SUFFIX}"
fi
fi
if [ -b "$dev" ]; then
echo "$dev"
fi
}
DYNAMIC_PARTITIONS=$(getprop ro.boot.dynamic_partitions)
if [ "$DYNAMIC_PARTITIONS" = "true" ]; then
BLK_PATH="/dev/block/mapper"
else
BLK_PATH=/dev/block/bootdevice/by-name
fi
CURRENTSLOT=$(getprop ro.boot.slot_suffix)
if [ ! -z "$CURRENTSLOT" ]; then
if [ "$CURRENTSLOT" = "_a" ]; then
# Opposite slot
SLOT_SUFFIX="_b"
else
SLOT_SUFFIX="_a"
fi
fi
mount_extra() {
for partition in $@; do
mnt_point="/postinstall/$partition"
mountpoint "$mnt_point" >/dev/null 2>&1 && break
blk_dev=$(find_block "$partition")
if [ -n "$blk_dev" ]; then
[ "$DYNAMIC_PARTITIONS" = "true" ] && blockdev --setrw "$blk_dev"
mount -o rw "$blk_dev" "$mnt_point"
fi
done
}
umount_extra() {
for partition in $@; do
# Careful with unmounting. If the update has a partition less than the current system,
# /postinstall/$partition is a symlink to /system/$partition, which on the active slot
# is a symlink to /$partition which is a mountpoint we would end up unmounting!
[ ! -L "/postinstall/$partition" ] && umount -l "/postinstall/$partition" 2>/dev/null
done
}
case "$1" in case "$1" in
backup) backup)
if check_prereq; then if check_prereq; then
mkdir -p $C mkdir -p $C
preserve_addon_d preserve_addon_d
run_stage pre-backup run_stages pre-backup backup post-backup
run_stage backup
run_stage post-backup
fi fi
;; ;;
restore) restore)
if check_prereq; then if check_prereq; then
run_stage pre-restore run_stages pre-restore restore post-restore
run_stage restore umount_extra $all_V3_partitions
run_stage post-restore
restore_addon_d restore_addon_d
rm -rf $C rm -rf $C
umount /postinstall/tmp umount /postinstall/tmp