Add support for btrfs snapshot in RO update. 01/260801/13 submit/tizen/20210817.140154
authorErnest Borowski <e.borowski@samsung.com>
Wed, 30 Jun 2021 12:26:38 +0000 (12:26 +0000)
committerErnest Borowski <e.borowski@samsung.com>
Wed, 11 Aug 2021 13:03:29 +0000 (13:03 +0000)
Checkpoint & restore mechanism prevents data loss during RO update
caused by e.g. power loss.

Change-Id: If9e649731da550120db3daaeb7ec21f3c56a76d4
Signed-off-by: Ernest Borowski <e.borowski@samsung.com>
scripts/40-fota.list.in
scripts/fota-init.sh

index d7f014e8e14d14cc03bf5c6ad9a5664651a6eb90..65e1112dc74a49bb2fb6e7dcabdd9864bc8744c4 100755 (executable)
@@ -38,6 +38,7 @@ WITHLIBS="
 /usr/sbin/dmsetup
 /usr/sbin/bow-restore
 /usr/sbin/fstrim
+/usr/sbin/btrfs
 "
 
 # LinkFileName:Target
index 10a2bd593bd7b1a8fc1d93f3b83eee4b1ed1b5ec..1c81da427b9f4b5828d2520f56bb89e91fc3d0ad 100755 (executable)
@@ -40,6 +40,7 @@ BOW_RESTORE="bow-restore"
 DMSETUP="dmsetup"
 FSTRIM="fstrim"
 STAT="stat"
+BTRFS="/usr/sbin/btrfs"
 
 FOTA_GUI="/usr/bin/fota_gui"
 FOTA_GUI_ENABLE=
@@ -70,17 +71,57 @@ get_partition_id() {
        PART_USER=$("$BLKID" --match-token PARTLABEL=user -o device || "$BLKID" --match-token LABEL=user -o device)
 }
 
+delete_btrfs_snapshot() {
+       PART="$1"
+       MNT_POINT="$2"
+       log "[Info] Deleting btrfs snapshot"
+       "$MOUNT" -o remount,rw "${MNT_POINT}"
+       "$BTRFS" subvolume delete "$MNT_POINT/fota/RO_update"
+       rm -rf -- "$MNT_POINT/fota"
+}
+
+restore_btrfs_snapshot() {
+       PART="$1"
+       MNT_POINT="$2"
+       if [ ! -d "$MNT_POINT/fota/RO_update" ]; then
+               log "[Info] btrfs snapshot not found"
+               return
+       fi
+       if "$BTRFS" subvolume list "$MNT_POINT/fota/RO_update"; then
+               log "[Info] Got previous snapshot."
+               # remove files from mountpoint because they will be restored from snapshot
+               # it is necessary otherwise files created during os-upgrade will be still there
+               for var in $(ls "$MNT_POINT" | grep -vE 'fota'); do
+                       rm -rf -- "$MNT_POINT/$var"
+               done
+               # remove special directories /dev /proc /sys content
+               # it is needed because "btrfs subvolume snapshot" copies some fies from this directories
+               rm -rf -- "$MNT_POINT/fota/RO_update/dev/"* "$MNT_POINT/fota/RO_update/proc/"* "$MNT_POINT/fota/RO_update/sys/"*
+               # restore files from snapshot
+               cp -a -- "$MNT_POINT/fota/RO_update/"* "$MNT_POINT"
+               delete_btrfs_snapshot "$PART" "$MNT_POINT"
+       else
+               log "[Info] Removing leftover data from previous snapshot."
+               rm -rf -- "$MNT_POINT/fota"
+       fi
+
+}
+
+
 #------------------------------------------------
 #       restore_checkpoint
 #------------------------------------------------
 restore_checkpoint() {
        # Any existing checkpoint means that the RW update was unexpectedly
        # aborded, so we must roll back the changes and update again.
-       log "[Debug] Bow restore" "${INT_LOG_FILE}"
+       log "[Debug] Checkpoint restore" "${INT_LOG_FILE}"
        PART="${1}"
-
-       if [ "$(blkid -o value -s TYPE ${PART})" = "ext4" ]; then
+       DIRECTORY=${2}
+       FS_TYPE="$(blkid -o value -s TYPE "${PART}")"
+       if [ "$FS_TYPE" = "ext4" ]; then
                "$BOW_RESTORE" "${PART}"
+       elif [ "$FS_TYPE" = "btrfs" ]; then
+               restore_btrfs_snapshot "${PART}" "${DIRECTORY}"
        fi
 }
 
@@ -120,20 +161,46 @@ mount_f2fs_partition() {
        log "[Debug] Mounted ${PARTITION} as F2FS checkpoint=disable" "${INT_LOG_FILE}"
 }
 
-mount_checkpoint_partition() {
+mount_btrfs_partition() {
+       LABEL=${1}
+       PARTITION=${2}
+       DIRECTORY=${3}
+       if ! "${MOUNT}" -o rw "${PARTITION}" "${DIRECTORY}"; then
+               log "[Error] Cannot mount ${PARTITION} into ${DIRECTORY}"
+               return 1
+       fi
+       if ! "$MKDIR" -p "${DIRECTORY}/fota"; then
+               log "[Error] Cannot create directory for btrfs snapshot"
+               return 1
+       fi
+       log "[Debug] Mounted ${PARTITION} as btrfs" "${INT_LOG_FILE}"
+}
+
+restore_mount_checkpoint_partition() {
        LABEL=${1}
        PARTITION=${2}
        DIRECTORY=${3}
        FSTYPE=$(lsblk -o FSTYPE -n "${PARTITION}")
 
        if [ "${FSTYPE}" = "ext4" ]; then
+               restore_checkpoint "${PARTITION}" "${DIRECTORY}"
                if ! mount_bow_partition "${LABEL}" "${PARTITION}" "${DIRECTORY}" ; then
                        return 1
                fi
        elif [ "${FSTYPE}" = "f2fs" ]; then
+               restore_checkpoint "${PARTITION}" "${DIRECTORY}"
                if ! mount_f2fs_partition "${LABEL}" "${PARTITION}" "${DIRECTORY}" ; then
                        return 1
                fi
+       elif [ "${FSTYPE}" = "btrfs" ]; then
+               # btrfs has to be mounted to restore data
+               if ! mount_btrfs_partition "${LABEL}" "${PARTITION}" "${DIRECTORY}"; then
+                       return 1
+               fi
+               restore_checkpoint "${PARTITION}" "${DIRECTORY}"
+               # restore deletes snapshot directory
+               "$MKDIR" -p "$DIRECTORY/fota"
+               "$BTRFS" subvolume snapshot "$DIRECTORY" "$DIRECTORY/fota/RO_update"
        else
                "${MOUNT}" "${PARTITION}" "${DIRECTORY}"
                log "[Info] Unsupported filesystem ${FSTYPE} on ${PARTITION}" "${INT_LOG_FILE}"
@@ -159,14 +226,26 @@ commit_f2fs_partition()
        log "[Info] Changes on partition ${LABEL} commited (f2fs)" "${INT_LOG_FILE}"
 }
 
+commit_btrfs_partition()
+{
+       LABEL=${1}
+       MNT_POINT=${2}
+       PART_SYSTEM_DATA=$(blkid --match-token PARTLABEL="${LABEL}" -o device || blkid --match-token LABEL="${LABEL}" -o device)
+       delete_btrfs_snapshot "${PART_SYSTEM_DATA}" "${MNT_POINT}"
+       log "[Info] Changes on partition ${LABEL} commited (btrfs)" "${INT_LOG_FILE}"
+}
+
 commit_partition()
 {
        LABEL=${1}
+       MNT_POINT=${2}
        TYPE=$(blkid --match-token LABEL="${LABEL}" -o value -s TYPE | tail -n 1)
        if [ "${TYPE}" = "ext4" ]; then
                commit_bow_partition "${LABEL}"
        elif [ "${TYPE}" = "f2fs" ]; then
                commit_f2fs_partition "${LABEL}"
+       elif [ "$TYPE" = "btrfs" ]; then
+               commit_btrfs_partition "${LABEL}" "${MNT_POINT}"
        else
                log "[Info] Cannot commit ${LABEL}: Unsupported filesystem ${TYPE}" "${INT_LOG_FILE}"
        fi
@@ -178,19 +257,16 @@ commit_partition()
 mount_partitions() {
        get_partition_id
 
-       restore_checkpoint "${PART_ROOTFS}"
-       if ! mount_checkpoint_partition rootfs "${PART_ROOTFS}" "${FAKE_ROOT}" ; then
+       if ! restore_mount_checkpoint_partition rootfs "${PART_ROOTFS}" "${FAKE_ROOT}"; then
                return 1
        fi
 
-       restore_checkpoint "${PART_SYSTEM_DATA}"
-       if ! mount_checkpoint_partition system-data "${PART_SYSTEM_DATA}" "${FAKE_ROOT}/${SYSTEM_DATA_MNT}" ; then
+       if ! restore_mount_checkpoint_partition system-data "${PART_SYSTEM_DATA}" "${FAKE_ROOT}/${SYSTEM_DATA_MNT}"; then
                return 1
        fi
 
        if [ ! "z${PART_USER}" = "z" ]; then
-               restore_checkpoint "${PART_USER}"
-               if ! mount_checkpoint_partition user "${PART_USER}" "${FAKE_ROOT}/${USER_MNT}" ; then
+               if ! restore_mount_checkpoint_partition user "${PART_USER}" "${FAKE_ROOT}/${USER_MNT}"; then
                        return 1
                fi
        fi
@@ -237,18 +313,18 @@ umount_partitions() {
 
        if [ ! "z${PART_USER}" = "z" ]; then
                if [ "${UPGRADE_SUCCESS}" = "1" ]; then
-                       commit_partition user
+                       commit_partition user "${FAKE_ROOT}/${USER_MNT}"
                fi
                umount_partition user "${FAKE_ROOT}/${USER_MNT}"
        fi
 
        if [ "${UPGRADE_SUCCESS}" = "1" ]; then
-               commit_partition system-data
+               commit_partition system-data "${FAKE_ROOT}/${SYSTEM_DATA_MNT}"
        fi
        umount_partition system-data "${FAKE_ROOT}/${SYSTEM_DATA_MNT}"
 
        if [ "${UPGRADE_SUCCESS}" = "1" ]; then
-               commit_partition rootfs
+               commit_partition rootfs "${FAKE_ROOT}"
        fi
        umount_partition rootfs "${FAKE_ROOT}"
 }