From 46bb12844f406c8f5a068b6a49e8456a0c0d98eb Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 3 Dec 2020 20:05:02 +0100 Subject: [PATCH] Rewrite datactl command (#1046) * Rewrite datactl command Prepare the target partition as part of the datactl command. Rely on partlabel for the target disk since we are always using GPT on the target disk. Use systemd and partlabel mechanism to wait and find the target data disk. Keep using the file system label to identify the source disk. Also use e2image instead of raw dd to move data. This should speed up the processes significantly. * Fix corner case when reusing same disk again --- Documentation/partition.md | 9 +- .../configs/intel_nuc_defconfig | 1 + .../configs/odroid_c2_defconfig | 1 + .../configs/odroid_c4_defconfig | 1 + .../configs/odroid_n2_defconfig | 1 + .../configs/odroid_xu4_defconfig | 1 + buildroot-external/configs/ova_defconfig | 1 + buildroot-external/configs/rpi0_w_defconfig | 1 + buildroot-external/configs/rpi2_defconfig | 1 + buildroot-external/configs/rpi3_64_defconfig | 1 + buildroot-external/configs/rpi3_defconfig | 1 + buildroot-external/configs/rpi4_64_defconfig | 1 + buildroot-external/configs/rpi4_defconfig | 1 + buildroot-external/configs/rpi_defconfig | 1 + buildroot-external/configs/tinker_defconfig | 1 + .../rootfs-overlay/usr/bin/datactl | 48 ++++++++--- .../lib/systemd/system/hassos-data.service | 9 +- .../rootfs-overlay/usr/libexec/hassos-data | 85 +++---------------- 18 files changed, 76 insertions(+), 89 deletions(-) diff --git a/Documentation/partition.md b/Documentation/partition.md index 8cc52da13..bb7d9294e 100644 --- a/Documentation/partition.md +++ b/Documentation/partition.md @@ -51,10 +51,13 @@ Log in as `root` to get to the Home Assistant CLI and then enter `login` to cont Confirm your USB SSD/HD is connected and recognized using `fdisk -l`. -Make sure the drive has no partition named `hassos-data` (or no partition at all). With the drive, use the below command (again, replacing XXX with your drive) +With the drive connected, use the following command (replacing sdx with your drive, without a partition number): ```sh -$ datactl move /dev/xxx +$ datactl move /dev/sdx ``` -Hit any key to continue, and then the move will happen after the next reboot. Once complete, the external drive will be owned and used by the system. +Enter "yes" to confirm the operation. This will prepare the disk, however, the +actual move will be running on next reboot. Once complete, the external drive +will contain the data and will need to be plugged in to successfully boot Home +Assistant OS. diff --git a/buildroot-external/configs/intel_nuc_defconfig b/buildroot-external/configs/intel_nuc_defconfig index db791a537..a70275a21 100644 --- a/buildroot-external/configs/intel_nuc_defconfig +++ b/buildroot-external/configs/intel_nuc_defconfig @@ -34,6 +34,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_LINUX_FIRMWARE=y diff --git a/buildroot-external/configs/odroid_c2_defconfig b/buildroot-external/configs/odroid_c2_defconfig index a428789f6..4bd2921c6 100644 --- a/buildroot-external/configs/odroid_c2_defconfig +++ b/buildroot-external/configs/odroid_c2_defconfig @@ -36,6 +36,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_CRDA=y diff --git a/buildroot-external/configs/odroid_c4_defconfig b/buildroot-external/configs/odroid_c4_defconfig index d9cd816a9..da9438e97 100644 --- a/buildroot-external/configs/odroid_c4_defconfig +++ b/buildroot-external/configs/odroid_c4_defconfig @@ -36,6 +36,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_CRDA=y diff --git a/buildroot-external/configs/odroid_n2_defconfig b/buildroot-external/configs/odroid_n2_defconfig index 5d1b43e51..646599344 100644 --- a/buildroot-external/configs/odroid_n2_defconfig +++ b/buildroot-external/configs/odroid_n2_defconfig @@ -36,6 +36,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_CRDA=y diff --git a/buildroot-external/configs/odroid_xu4_defconfig b/buildroot-external/configs/odroid_xu4_defconfig index 4d038c7c0..a7eb04442 100644 --- a/buildroot-external/configs/odroid_xu4_defconfig +++ b/buildroot-external/configs/odroid_xu4_defconfig @@ -36,6 +36,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_LINUX_FIRMWARE=y diff --git a/buildroot-external/configs/ova_defconfig b/buildroot-external/configs/ova_defconfig index 9f593b3b0..cd4fd0673 100644 --- a/buildroot-external/configs/ova_defconfig +++ b/buildroot-external/configs/ova_defconfig @@ -35,6 +35,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_CRDA=y diff --git a/buildroot-external/configs/rpi0_w_defconfig b/buildroot-external/configs/rpi0_w_defconfig index 908208631..1dae58233 100644 --- a/buildroot-external/configs/rpi0_w_defconfig +++ b/buildroot-external/configs/rpi0_w_defconfig @@ -37,6 +37,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_RPI_WIFI_FIRMWARE=y diff --git a/buildroot-external/configs/rpi2_defconfig b/buildroot-external/configs/rpi2_defconfig index 68d73bca6..568f71179 100644 --- a/buildroot-external/configs/rpi2_defconfig +++ b/buildroot-external/configs/rpi2_defconfig @@ -37,6 +37,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_RPI_FIRMWARE=y diff --git a/buildroot-external/configs/rpi3_64_defconfig b/buildroot-external/configs/rpi3_64_defconfig index b2d4f090f..da9378405 100644 --- a/buildroot-external/configs/rpi3_64_defconfig +++ b/buildroot-external/configs/rpi3_64_defconfig @@ -37,6 +37,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_RPI_FIRMWARE=y diff --git a/buildroot-external/configs/rpi3_defconfig b/buildroot-external/configs/rpi3_defconfig index 67bf6426e..0ad2e74c0 100644 --- a/buildroot-external/configs/rpi3_defconfig +++ b/buildroot-external/configs/rpi3_defconfig @@ -37,6 +37,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_RPI_FIRMWARE=y diff --git a/buildroot-external/configs/rpi4_64_defconfig b/buildroot-external/configs/rpi4_64_defconfig index 91bba4378..117b9b8af 100644 --- a/buildroot-external/configs/rpi4_64_defconfig +++ b/buildroot-external/configs/rpi4_64_defconfig @@ -37,6 +37,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_RPI_FIRMWARE=y diff --git a/buildroot-external/configs/rpi4_defconfig b/buildroot-external/configs/rpi4_defconfig index cac392916..add7af347 100644 --- a/buildroot-external/configs/rpi4_defconfig +++ b/buildroot-external/configs/rpi4_defconfig @@ -37,6 +37,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_RPI_FIRMWARE=y diff --git a/buildroot-external/configs/rpi_defconfig b/buildroot-external/configs/rpi_defconfig index 5362abf72..1fdb1c01d 100644 --- a/buildroot-external/configs/rpi_defconfig +++ b/buildroot-external/configs/rpi_defconfig @@ -37,6 +37,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_RPI_FIRMWARE=y diff --git a/buildroot-external/configs/tinker_defconfig b/buildroot-external/configs/tinker_defconfig index 5d9b3c7e9..a64b0c6e8 100644 --- a/buildroot-external/configs/tinker_defconfig +++ b/buildroot-external/configs/tinker_defconfig @@ -38,6 +38,7 @@ BR2_PACKAGE_BUSYBOX_INDIVIDUAL_BINARIES=y BR2_PACKAGE_PROCPS_NG=y BR2_PACKAGE_JQ=y BR2_PACKAGE_E2FSPROGS=y +BR2_PACKAGE_E2FSPROGS_E2IMAGE=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_SQUASHFS=y BR2_PACKAGE_LINUX_FIRMWARE=y diff --git a/buildroot-external/rootfs-overlay/usr/bin/datactl b/buildroot-external/rootfs-overlay/usr/bin/datactl index adc024685..ba4e0b375 100755 --- a/buildroot-external/rootfs-overlay/usr/bin/datactl +++ b/buildroot-external/rootfs-overlay/usr/bin/datactl @@ -1,31 +1,59 @@ #!/bin/sh # ============================================================================== -# HassOS data partition handling +# Home Assistant OS data partition handling # ============================================================================== set -e -OPTION_FILE=/mnt/overlay/data.opt -DATA_DEVICE_CHILD="$(findfs LABEL="hassos-data")" +# Use current mount point. This avoids "Can't be the same disk!" error +# when using a drive which has been used as a data drive previously. +DATA_DEVICE_CHILD="$(findmnt --noheadings --output=source /mnt/data)" DATA_DEVICE_ROOT="/dev/$(lsblk -no pkname "${DATA_DEVICE_CHILD}")" # Move command if [ "${1}" = "move" ] && [ -e "${2}" ]; then - DEVICE="${2}" + NEW_DEVICE_ROOT="${2}" # Check device - if ! lsblk "${DEVICE}" | grep disk > /dev/null 2>&1; then + if ! lsblk "${NEW_DEVICE_ROOT}" | grep disk > /dev/null 2>&1; then echo "[ERROR] Is not disk!" exit 1 - elif [ "${DEVICE}" = "${DATA_DEVICE_ROOT}" ]; then + elif [ "${NEW_DEVICE_ROOT}" = "${DATA_DEVICE_ROOT}" ]; then echo "[ERROR] Can't be the same disk!" exit 1 fi # Flag device - echo "WARNING: ${DEVICE} will be reset on next restart!" - echo "Press a key to move forward" - read -r + echo "WARNING: All partitions on ${NEW_DEVICE_ROOT} will be deleted!" + printf "Enter \"yes\" to confirm: " + read -r confirm + if [ "${confirm}" != "yes" ]; then + echo "Aborting." + exit 1 + fi + + sgdisk --zap-all "${NEW_DEVICE_ROOT}" + sgdisk \ + -n "0:0:0" \ + -c "0:hassos-data-external" \ + -t "0:0FC63DAF-8483-4772-8E79-3D69D8477DE4" \ + -u "0:a52a4597-fa3a-4851-aefd-2fbe9f849079" \ + "${NEW_DEVICE_ROOT}" + + touch "/mnt/overlay/move-data" + cat << EOF +Disk ${NEW_DEVICE_ROOT} has been prepared to be used as data drive and the data +move has been scheduled for the next reboot. Please reboot the device now and +make sure to leave the disk connected to the system from now on. +EOF + +else + cat << EOF +Usage: datactl move + +Moves data partition to external device provided by (without partition +number). A new partition table and a partition for the complete device will be +created by datactl. +EOF - echo "${DEVICE}" > ${OPTION_FILE} fi diff --git a/buildroot-external/rootfs-overlay/usr/lib/systemd/system/hassos-data.service b/buildroot-external/rootfs-overlay/usr/lib/systemd/system/hassos-data.service index 847ffe9c5..5d7598bbf 100644 --- a/buildroot-external/rootfs-overlay/usr/lib/systemd/system/hassos-data.service +++ b/buildroot-external/rootfs-overlay/usr/lib/systemd/system/hassos-data.service @@ -1,16 +1,17 @@ [Unit] -Description=HassOS data partition +Description=Home Assistant OS data partition migration DefaultDependencies=no RefuseManualStart=true RefuseManualStop=true -Requires=mnt-overlay.mount dev-disk-by\x2dlabel-hassos\x2ddata.device +Requires=mnt-overlay.mount dev-disk-by\x2dlabel-hassos\x2ddata.device dev-disk-by\x2dpartlabel-hassos\x2ddata\x2dexternal.service Wants=hassos-expand.service -After=mnt-overlay.mount dev-disk-by\x2dlabel-hassos\x2ddata.device systemd-fsck@dev-disk-by\x2dlabel-hassos\x2ddata.service +After=mnt-overlay.mount dev-disk-by\x2dlabel-hassos\x2ddata.device systemd-fsck@dev-disk-by\x2dlabel-hassos\x2ddata.service dev-disk-by\x2dpartlabel-hassos\x2ddata\x2dexternal.service Before=hassos-expand.service -ConditionPathExists=/mnt/overlay/data.opt +ConditionPathExists=/mnt/overlay/move-data [Service] Type=oneshot +ExecStartPre=-/usr/bin/rm -f /mnt/overlay/move-data ExecStart=/usr/libexec/hassos-data [Install] diff --git a/buildroot-external/rootfs-overlay/usr/libexec/hassos-data b/buildroot-external/rootfs-overlay/usr/libexec/hassos-data index 874992586..0e1b74023 100755 --- a/buildroot-external/rootfs-overlay/usr/libexec/hassos-data +++ b/buildroot-external/rootfs-overlay/usr/libexec/hassos-data @@ -1,83 +1,24 @@ #!/bin/sh -# shellcheck disable=SC2039 # ============================================================================== -# HassOS data partition handler +# Home Assistant OS data partition migration script # ============================================================================== set -e -OPTION_FILE=/mnt/overlay/data.opt +# Rely on systemd-udev symlinks to find current data partition by fs label +OLD_DEVICE_CHILD="$(readlink -f "/dev/disk/by-label/hassos-data")" -# New data partition exits -if ! [ -e "${OPTION_FILE}" ]; then - echo "[INFO] No data option found" - exit 0 -else - NEW_DEVICE_ROOT="$(cat ${OPTION_FILE})" - rm ${OPTION_FILE} -fi +# Rely on systemd-udev symlinks to find external data partition by partlabel +NEW_DEVICE_CHILD="$(readlink -f "/dev/disk/by-partlabel/hassos-data-external")" -# Get device information -OLD_DEVICE_CHILD="$(findfs LABEL="hassos-data")" -OLD_DEVICE_ROOT="/dev/$(lsblk -no pkname "${OLD_DEVICE_CHILD}")" -OLD_PART_NUM="${OLD_DEVICE_CHILD: -1}" - -# Wait for devices -timeout 90 \ - ash -c \ - "until [ -e \"${NEW_DEVICE_ROOT}\" ]; do sleep 5; done" \ - > /dev/null 2>&1 || true; - -# Check if block device is exists -if [ ! -b "${NEW_DEVICE_ROOT}" ]; then - echo "[ERROR] No block device ${NEW_DEVICE_ROOT}!" - exit 1 -fi -echo "[INFO] Cleanup device ${NEW_DEVICE_ROOT}!" -sgdisk -Z "${NEW_DEVICE_ROOT}" - -# Create new partition -echo "[INFO] Create new hassos-data partition" -sgdisk -o "${NEW_DEVICE_ROOT}" -sgdisk \ - -n "0:0:0" \ - -c "0:hassos-data" \ - -t "0:0FC63DAF-8483-4772-8E79-3D69D8477DE4" \ - -u "0:a52a4597-fa3a-4851-aefd-2fbe9f849079" \ - "${NEW_DEVICE_ROOT}" -sgdisk -v "${NEW_DEVICE_ROOT}" -partx -u "${NEW_DEVICE_ROOT}" - -NEW_DEVICE_CHILD="$(fdisk -l "${NEW_DEVICE_ROOT}" | grep '^/dev' | cut -d' ' -f1 | head -n 1)" - -echo "[INFO] Move hassos-data from ${OLD_DEVICE_CHILD} to ${NEW_DEVICE_CHILD}" -if ! dd if="${OLD_DEVICE_CHILD}" of="${NEW_DEVICE_CHILD}" status=none; then - echo "[ERROR] Data copy fails!" - - # Reset new data partition - sgdisk -o "${NEW_DEVICE_ROOT}" - partx -u "${NEW_DEVICE_ROOT}" +echo "[INFO] Moving data from ${OLD_DEVICE_CHILD} to ${NEW_DEVICE_CHILD}" +if ! e2image -ra -p "${OLD_DEVICE_CHILD}" "${NEW_DEVICE_CHILD}"; then + echo "[ERROR] Copying data partition to external device failed!" exit 1 fi -echo "[INFO] Remove old hassos-data partition ${OLD_PART_NUM} / ${OLD_DEVICE_ROOT}" -if sfdisk -dq "${OLD_DEVICE_ROOT}" | grep -q 'label: gpt'; then - sgdisk -d "${OLD_PART_NUM}" "${OLD_DEVICE_ROOT}" - sgdisk -v "${OLD_DEVICE_ROOT}" +# At this point we have two partition with the same fs label. Make sure +# to rename the internal partition so we won't mount it anymore. +echo "[INFO] Rename old hassos-data partition ${OLD_DEVICE_CHILD}" +e2label "${OLD_DEVICE_CHILD}" hassos-data-old -else - sfdisk --delete "${OLD_DEVICE_ROOT}" "${OLD_PART_NUM}" --force - sfdisk -V "${OLD_DEVICE_ROOT}" -fi - -echo "[INFO] fix filesystem & layout" - -# Fix filesystem -e2fsck -y "${NEW_DEVICE_CHILD}" -resize2fs -f "${NEW_DEVICE_CHILD}" - -# Fix partition layout -partx -d "${OLD_DEVICE_CHILD}" -partx -u "${OLD_DEVICE_ROOT}" -partx -u "${NEW_DEVICE_ROOT}" - -echo "[INFO] Finish hassos data movement" +echo "[INFO] Data move has been completed successfully"