change restore mechanism on btrfs filesystem. 83/262783/4
authorErnest Borowski <e.borowski@samsung.com>
Thu, 19 Aug 2021 20:35:55 +0000 (20:35 +0000)
committerErnest Borowski <e.borowski@samsung.com>
Tue, 24 Aug 2021 17:45:45 +0000 (17:45 +0000)
Use subvolumes instead of cp to restore files, this will speedup
restore and reduce local memory requirements.

After update system will have ROOTFS subvolume located at root volume.
/ -- root volume
/ROOTFS -- subvolume with actual data.

ROOTFS subvolume is set as default one, so it will be transparent to
system after update because mount will mount it at /.

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

index 1c81da427b9f4b5828d2520f56bb89e91fc3d0ad..c9553bca7a83a3ccd2a36afabd7386404b563267 100755 (executable)
@@ -75,36 +75,99 @@ delete_btrfs_snapshot() {
        PART="$1"
        MNT_POINT="$2"
        log "[Info] Deleting btrfs snapshot"
-       "$MOUNT" -o remount,rw "${MNT_POINT}"
+       # we have to delete /fota/RO_update subvolume form root volume
+       # so ensure that root volume is mounted
+       if ! "$UMOUNT" "$MNT_POINT"; then
+               log "[Error] Unable to umount $MNT_POINT"
+               return 1
+       fi
+       if ! "$MOUNT" -o subvolid=5,rw "$PART" "$MNT_POINT"; then
+               log "[Error] Unable to mount $PART root subvolume to: $MNT_POINT"
+               return 1
+       fi
+       # delete subvolume /fota/RO_update containing checkpoint data
        "$BTRFS" subvolume delete "$MNT_POINT/fota/RO_update"
+       # delete /fota directory, as it is no longer needed
+       # because data was already restored
        rm -rf -- "$MNT_POINT/fota"
+       if ! "$UMOUNT" "$MNT_POINT"; then
+               log "[Error] Unable to umount $MNT_POINT"
+               return 1
+       fi
+       # mount default volume
+       if ! "$MOUNT" "$PART" "$MNT_POINT"; then
+               log "[Error] Unable to mount default subvolume $PART to $MNT_POINT"
+               return 1
+       fi
 }
 
 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
+       DEST_DIR="$MNT_POINT/ROOTFS"
+
+       if [ -d "$MNT_POINT/fota/RO_update" ] && "$BTRFS" subvolume list "$MNT_POINT/fota/RO_update"; then
+               # There is snapshot form previously failed update
+               # Use snapshot as source data.
                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"
+               SOURCE_DIR="$MNT_POINT/fota/RO_update"
+               # root volume 5 has to be set as default otherwise /ROOTFS subvolume can not be deleted
+               # as it is default subvolume
+               if ! "$BTRFS" subvolume set-default "$MNT_POINT"; then
+                       log "[Error] Unable to set $DEST_DIR as default subvolume"
+                       return 1
+               fi
+               "$BTRFS" subvolume delete "$DEST_DIR"
+               rm -rf "$DEST_DIR"
+       elif [ ! -d "$MNT_POINT/ROOTFS" ]; then
+               # There is no /ROOTFS subvolume, create it and set is as default one.
+               # This will execute during first update when data on root volume 5 exist.
+               # After this update /ROOTFS will be default subvolume. Automatically mounted by mount.
+               # Use / (root volume 5) as source data.
+               log '[Info] Create initial /ROOTFS subvolume'
+               SOURCE_DIR="$MNT_POINT"
        else
+               # There is no data to restore because there was not previous update attempts.
+               # Howether data is stored in /ROOTFS instead off inside root volume 5 /
+               # Therefore there is no need to restore anything so remove any leftover data
+               # and proceed with the update.
                log "[Info] Removing leftover data from previous snapshot."
                rm -rf -- "$MNT_POINT/fota"
+               return 0
+       fi
+       if ! "$BTRFS" subvolume snapshot "$SOURCE_DIR" "$DEST_DIR"; then
+               log "[Error] Unable to create snapshot from: $SOURCE_DIR to $DEST_DIR"
+               return 1
        fi
 
+       # Remove special directories /dev /proc /sys content
+       # it is required because "btrfs subvolume snapshot" copies some files from this directories
+       rm -rf -- "$DEST_DIR/proc/"* "$DEST_DIR/sys/"*
+
+       # Make $DEST_DIR (/ROOTFS) as new root (/).
+       # From now on it will become new default filesystem mounted by mount command.
+       if ! "$BTRFS" subvolume set-default "$DEST_DIR"; then
+               log "[Error] Unable to set $DEST_DIR as default subvolume"
+               return 1
+       fi
+       if [ -d "$MNT_POINT/fota" ]; then
+               delete_btrfs_snapshot "$PART" "$MNT_POINT"
+               # Remount root volume 5 because delete_btrfs_snapshot will mount default subvolume
+               # which is /ROOTFS.
+               if ! "$UMOUNT" "$MNT_POINT"; then
+                       log "[Error] Unable to umount $MNT_POINT"
+                       return 1
+               fi
+               if ! "$MOUNT" -o subvolid=5,rw "$PART" "$MNT_POINT"; then
+                       log "[Error] Unable to mount $PART root subvolume to: $MNT_POINT"
+                       return 1
+               fi
+       fi
+       # delete all files in root volume except new rootfs /ROOTFS
+       for var in "$MNT_POINT"/* "$MNT_POINT"/.[!.]*; do
+               test "$var" = "$MNT_POINT/ROOTFS" && continue
+               rm -rf -- "$var"
+       done
 }
 
 
@@ -165,14 +228,11 @@ mount_btrfs_partition() {
        LABEL=${1}
        PARTITION=${2}
        DIRECTORY=${3}
-       if ! "${MOUNT}" -o rw "${PARTITION}" "${DIRECTORY}"; then
+       # mount btrfs root subvolume
+       if ! "${MOUNT}" -o rw,subvolid=5 "${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}"
 }
 
@@ -198,9 +258,25 @@ restore_mount_checkpoint_partition() {
                        return 1
                fi
                restore_checkpoint "${PARTITION}" "${DIRECTORY}"
-               # restore deletes snapshot directory
-               "$MKDIR" -p "$DIRECTORY/fota"
-               "$BTRFS" subvolume snapshot "$DIRECTORY" "$DIRECTORY/fota/RO_update"
+               # Create new checkpoint
+               if ! "$MKDIR" -p "$DIRECTORY/fota"; then
+                       log "[Error] Unable to create $DIRECTORY/fota dir."
+                       return 1
+               fi
+               if ! "$BTRFS" subvolume snapshot "$DIRECTORY/ROOTFS" "$DIRECTORY/fota/RO_update"; then
+                       log "[Error] Unable to create snapshot from $DIRECTORY/ROOTFS to $DIRECTORY/fota/RO_update"
+                       retun 1
+               fi
+               # mount /ROOTFS partition instead of root volume
+               if ! "$UMOUNT" "${PARTITION}"; then
+                       log "[Error] Cannot umount ${PARTITION}"
+                       return 1
+               fi
+               if ! "$MOUNT" "${PARTITION}" "${DIRECTORY}"; then
+                       log "[Error] Cannot mount ${PARTITION} into ${DIRECTORY}"
+                       return 1
+               fi
+
        else
                "${MOUNT}" "${PARTITION}" "${DIRECTORY}"
                log "[Info] Unsupported filesystem ${FSTYPE} on ${PARTITION}" "${INT_LOG_FILE}"