From d0d986158035b6db2ee7c2b937b1463fa000d4a2 Mon Sep 17 00:00:00 2001 From: Dell Green Date: Sat, 4 Jul 2020 20:31:09 +0100 Subject: [PATCH] Partition UUID support added for gpt and dos partition tables for deterministic booting - Issue: #MEN-3725 The following new config variables have been added which closely mirror those used in meta-mender to give fine grained controll over partition device paths. If partuuid support is enabled then these partition variables must be used as format doesnt fit device/number variables scheme. If partuuid support is disabled then partition variables are optional liek in meta-mender. MENDER_ENABLE_PARTUUID MENDER_BOOT_PART MENDER_ROOTFS_PART_A MENDER_ROOTFS_PART_B MENDER_DATA_PART Example: MENDER_BOOT_PART="/dev/disk/by-partuuid/26445670-f37c-408b-be2c-3ef419866620" #gpt MENDER_BOOT_PART="/dev/disk/by-partuuid/26445670-01" #dos MENDER_BOOT_PART="/dev/sda1" #partuuid support disabled Changelog: Partition UUID support added for gpt/dos partition tables for deterministic booting Signed-off-by: Dell Green --- Dockerfile | 7 +- configs/mender_convert_config | 15 +++ mender-convert-modify | 38 ++++++- mender-convert-package | 43 +++++++- modules/config.sh | 7 ++ modules/disk.sh | 183 ++++++++++++++++++++++++++++++++++ 6 files changed, 282 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2da7fe0..d1ff318 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,8 +21,9 @@ RUN apt-get update && env DEBIAN_FRONTEND=noninteractive apt-get install -y \ file \ # to copy files between rootfs directories rsync \ -# to generate partition table +# to generate partition table and alter partitions parted \ + gdisk \ # mkfs.ext4 and family e2fsprogs \ # mkfs.xfs and family @@ -48,7 +49,9 @@ RUN apt-get update && env DEBIAN_FRONTEND=noninteractive apt-get install -y \ libgomp1 \ # zip and unzip archive zip \ - unzip + unzip \ +# manipulate binary and hex + xxd COPY --from=build /root/pxz/pxz /usr/bin/pxz diff --git a/configs/mender_convert_config b/configs/mender_convert_config index 5524dc6..3cdab31 100644 --- a/configs/mender_convert_config +++ b/configs/mender_convert_config @@ -122,6 +122,21 @@ MENDER_ROOTFS_PART_B_NUMBER="3" # Partition number of persistent data partition MENDER_DATA_PART_NUMBER="4" +# Use partition uuids rather than device and partition numbers +MENDER_ENABLE_PARTUUID="n" + +# Partition used as the boot partition. +MENDER_BOOT_PART="" + +# Partition used as the first (A) rootfs partition. +MENDER_ROOTFS_PART_A="" + +# Partition used as the first (B) rootfs partition. +MENDER_ROOTFS_PART_B="" + +# Partition used as the persistent data partition. +MENDER_DATA_PART="" + # Basename of DTB that should be loaded by the bootloader. MENDER_DTB_NAME=kernel.dtb diff --git a/mender-convert-modify b/mender-convert-modify index 89e3b4c..a8c345d 100755 --- a/mender-convert-modify +++ b/mender-convert-modify @@ -77,6 +77,12 @@ source modules/config.sh $(printf "%q " "${configs[@]}") boot_part=$(disk_boot_part) root_part=$(disk_root_part) +# Sysfs device paths +boot_part_device=$(disk_boot_part_device) +data_part_device=$(disk_data_part_device) +root_part_a_device=$(disk_root_part_a_device) +root_part_b_device=$(disk_root_part_b_device) + # Create mount points mkdir -p work/boot mkdir -p work/rootfs @@ -126,12 +132,27 @@ if [ "${MENDER_GRUB_EFI_INTEGRATION}" == "y" ]; then cat <<- EOF > work/grub-mender-grubenv-${MENDER_GRUBENV_VERSION}/mender_grubenv_defines mender_rootfsa_part=${MENDER_ROOTFS_PART_A_NUMBER} mender_rootfsb_part=${MENDER_ROOTFS_PART_B_NUMBER} -mender_kernel_root_base=${MENDER_STORAGE_DEVICE_BASE} mender_grub_storage_device=${MENDER_GRUB_STORAGE_DEVICE} kernel_imagetype=${kernel_imagetype} initrd_imagetype=${initrd_imagetype} EOF +# For partuuid support grub.cfg expects dedicated variables to be added +if [ "${MENDER_ENABLE_PARTUUID}" == "y" ]; then + rootfsa_partuuid=$(disk_get_partuuid_from_device "${root_part_a_device}") + rootfsb_partuuid=$(disk_get_partuuid_from_device "${root_part_b_device}") + log_info "Using root partition A partuuid in grubenv: $rootfsa_partuuid" + log_info "Using root partition B partuuid in grubenv: $rootfsb_partuuid" + cat <<- EOF >> work/grub-mender-grubenv-${MENDER_GRUBENV_VERSION}/mender_grubenv_defines +mender_rootfsa_uuid=${rootfsa_partuuid} +mender_rootfsb_uuid=${rootfsb_partuuid} +EOF +else + cat <<- EOF >> work/grub-mender-grubenv-${MENDER_GRUBENV_VERSION}/mender_grubenv_defines +mender_kernel_root_base=${MENDER_STORAGE_DEVICE_BASE} +EOF +fi + if [ -n "${MENDER_GRUB_KERNEL_BOOT_ARGS}" ]; then cat <<- EOF > work/grub-mender-grubenv-${MENDER_GRUBENV_VERSION}/11_bootargs_grub.cfg set bootargs="${MENDER_GRUB_KERNEL_BOOT_ARGS}" @@ -185,10 +206,13 @@ fi run_and_log_cmd "sudo mkdir -p work/rootfs/data/mender" run_and_log_cmd "sudo ln -sf /data/mender work/rootfs/var/lib/mender" +log_info "Using root device A in mender.conf: $root_part_a_device" +log_info "Using root device B in mender.conf: $root_part_b_device" + cat <<- EOF > work/mender.conf.data { - "RootfsPartA": "${MENDER_STORAGE_DEVICE_BASE}${MENDER_ROOTFS_PART_A_NUMBER}", - "RootfsPartB": "${MENDER_STORAGE_DEVICE_BASE}${MENDER_ROOTFS_PART_B_NUMBER}" + "RootfsPartA": "${root_part_a_device}", + "RootfsPartB": "${root_part_b_device}" } EOF @@ -232,13 +256,17 @@ if [ "${MENDER_DATA_PART_GROWFS}" == "y" ]; then MENDER_DATA_PART_FSTAB_OPTS="${MENDER_DATA_PART_FSTAB_OPTS},x-systemd.growfs" fi + +log_info "Using boot partition device in fstab: $boot_part_device" +log_info "Using data partition device in fstab: $data_part_device" + sudo bash -c "cat <<- EOF > work/rootfs/etc/fstab # stock fstab - you probably want to override this with a machine specific one /dev/root / auto defaults 1 1 proc /proc proc defaults 0 0 -${MENDER_STORAGE_DEVICE_BASE}${MENDER_BOOT_PART_NUMBER} ${boot_part_mountpoint} auto defaults,sync 0 0 -${MENDER_STORAGE_DEVICE_BASE}${MENDER_DATA_PART_NUMBER} /data auto ${MENDER_DATA_PART_FSTAB_OPTS} 0 0 +${boot_part_device} ${boot_part_mountpoint} auto defaults,sync 0 0 +${data_part_device} /data auto ${MENDER_DATA_PART_FSTAB_OPTS} 0 0 EOF" log_info "Performing platform specific modifications (if any)" diff --git a/mender-convert-package b/mender-convert-package index 0098497..e74e0fc 100755 --- a/mender-convert-package +++ b/mender-convert-package @@ -78,6 +78,12 @@ output_dir=work boot_part=$(disk_boot_part) root_part=$(disk_root_part) +# Sysfs device paths +boot_part_device=$(disk_boot_part_device) +data_part_device=$(disk_data_part_device) +root_part_a_device=$(disk_root_part_a_device) +root_part_b_device=$(disk_root_part_b_device) + # Final output mkdir -p deploy @@ -249,6 +255,27 @@ run_and_log_cmd "${PARTED} -s ${img_path} -- unit s mkpart primary ext2 ${rootfs run_and_log_cmd "${PARTED} -s ${img_path} -- unit s mkpart primary ext2 ${data_start} ${data_end}" run_and_log_cmd "${PARTED} -s ${img_path} print" +# Update partition uuids if required +if [ "${MENDER_ENABLE_PARTUUID}" == "y" ]; then + boot_partuuid=$(disk_get_partuuid_from_device "${boot_part_device}") + rootfsa_partuuid=$(disk_get_partuuid_from_device "${root_part_a_device}") + rootfsb_partuuid=$(disk_get_partuuid_from_device "${root_part_b_device}") + data_partuuid=$(disk_get_partuuid_from_device "${data_part_device}") + + if [ "${partition_scheme}" == "gpt" ]; then + log_info "Updating GPT partition uuids in image: '${img_path}'" + run_and_log_cmd "sgdisk -u ${MENDER_BOOT_PART_NUMBER}:${boot_partuuid} '${img_path}'" + run_and_log_cmd "sgdisk -u ${MENDER_ROOTFS_PART_A_NUMBER}:${rootfsa_partuuid} '${img_path}'" + run_and_log_cmd "sgdisk -u ${MENDER_ROOTFS_PART_B_NUMBER}:${rootfsb_partuuid} '${img_path}'" + run_and_log_cmd "sgdisk -u ${MENDER_DATA_PART_NUMBER}:${data_partuuid} '${img_path}'" + else + diskid=$(disk_get_partuuid_dos_diskid_from_device "${root_part_a_device}") + log_info "Updating MBR disk identifier for partition uuid support to: '0x${diskid}'" + run_and_log_cmd "xxd -r -p <<< '${diskid}' | LC_ALL=C rev | dd of='${img_path}' bs=1 seek=440 count=4 conv=notrunc" + fi + +fi + # Write boot-gap if [ "${MENDER_COPY_BOOT_GAP}" == "y" ]; then log_info "Writing boot gap of size: ${boot_part_sectors} (sectors)" @@ -324,16 +351,16 @@ fi distro_feature="${distro_feature} mender-convert" cat <<- EOF > deploy/${image_name}.cfg -MENDER_BOOT_PART="${MENDER_STORAGE_DEVICE_BASE}${MENDER_BOOT_PART_NUMBER}" -MENDER_ROOTFS_PART_A="${MENDER_STORAGE_DEVICE_BASE}${MENDER_ROOTFS_PART_A_NUMBER}" -MENDER_ROOTFS_PART_B="${MENDER_STORAGE_DEVICE_BASE}${MENDER_ROOTFS_PART_B_NUMBER}" +MENDER_BOOT_PART="${boot_part_device}" +MENDER_ROOTFS_PART_A="${root_part_a_device}" +MENDER_ROOTFS_PART_B="${root_part_b_device}" +MENDER_DATA_PART="${data_part_device}" MENDER_BOOT_PART_MOUNT_LOCATION="${boot_part_mountpoint}" MENDER_BOOT_PART_SIZE_MB="$(disk_sectors_to_mb ${boot_part_sectors})" MENDER_DATA_PART_SIZE_MB="${MENDER_DATA_PART_SIZE_MB}" MENDER_DEVICE_TYPE="${device_type}" MENDER_PARTITIONING_OVERHEAD_KB="$(( (${overhead_sectors} * 512) / 1024 ))" MENDER_PARTITION_ALIGNMENT="${MENDER_PARTITION_ALIGNMENT}" -MENDER_STORAGE_DEVICE_BASE="${MENDER_STORAGE_DEVICE_BASE}" MENDER_STORAGE_TOTAL_SIZE_MB="${MENDER_STORAGE_TOTAL_SIZE_MB}" MENDER_UBOOT_ENV_STORAGE_DEVICE_OFFSET="12582912" MENDER_ARTIFACT_NAME="${artifact_name}" @@ -342,6 +369,14 @@ DEPLOY_DIR_IMAGE="${PWD}/deploy" MENDER_MACHINE="${device_type}" EOF + +# Outputting device base only relevant for some configurations +if [ "${MENDER_ENABLE_PARTUUID}" != "y" ]; then + cat <<- EOF >> deploy/${image_name}.cfg +MENDER_STORAGE_DEVICE_BASE="${MENDER_STORAGE_DEVICE_BASE}" +EOF +fi + # Something that the tests expect to be defined (originally from Yocto) cat <<- EOF >> deploy/${image_name}.cfg IMAGE_FSTYPES="${image_fs_type} mender sdimg" diff --git a/modules/config.sh b/modules/config.sh index 26d8716..6084b84 100644 --- a/modules/config.sh +++ b/modules/config.sh @@ -20,3 +20,10 @@ for config in "${configs[@]}"; do log_info "Using configuration file: ${config}" source "${config}" done + + +# Fine grained partition variables override device/number variables where applicable +disk_override_partition_variable "MENDER_BOOT_PART_NUMBER" "${MENDER_BOOT_PART}" +disk_override_partition_variable "MENDER_ROOTFS_PART_A_NUMBER" "${MENDER_ROOTFS_PART_A}" +disk_override_partition_variable "MENDER_ROOTFS_PART_B_NUMBER" "${MENDER_ROOTFS_PART_B}" +disk_override_partition_variable "MENDER_DATA_PART_NUMBER" "${MENDER_DATA_PART}" diff --git a/modules/disk.sh b/modules/disk.sh index 10c8c32..f8c8310 100644 --- a/modules/disk.sh +++ b/modules/disk.sh @@ -178,3 +178,186 @@ disk_root_part() { fi echo "${root_part}" } + + +# Check if supplied argument is valid partuuid device path. +# Supports both dos and gpt paths +# +# $1 - partuuid device path +disk_is_valid_partuuid_device() { + disk_is_valid_partuuid_gpt_device "$1" || disk_is_valid_partuuid_dos_device "$1" +} + + +# Check if supplied argument is valid gpt partuuid device path. +# +# Example: /dev/disk/by-partuuid/26445670-f37c-408b-be2c-3ef419866620 +# +# $1 - gpt partuuid device path +disk_is_valid_partuuid_gpt_device() { + echo "${1}" | grep -qE '^/dev/disk/by-partuuid/([0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12})$' +} + + +# Check if supplied argument is valid dos partuuid device path. +# +# Example: /dev/disk/by-partuuid/26445670-01 +# +# $1 - dos partuuid device path +disk_is_valid_partuuid_dos_device() { + echo "${1}" | grep -qE '^/dev/disk/by-partuuid/[0-9a-f]{8}-[0-9a-f]{2}$' +} + + +# Get partuuid from supplied device path. +# Supports both dos and gpt paths +# +# $1 - partuuid device path +disk_get_partuuid_from_device() { + if ! disk_is_valid_partuuid_device "${1}"; then + log_fatal "Invalid partuuid device: '${1}'" + fi + echo "${1}" | sed "s:/dev/disk/by-partuuid/::" +} + + +# Get dos disk identifier from supplied device path. +# +# $1 - dos compatible partuuid device path +disk_get_partuuid_dos_diskid_from_device() { + if ! disk_is_valid_partuuid_dos_device "${1}"; then + log_fatal "Invalid dos partuuid device: '${1}'" + fi + partuuid=$(disk_get_partuuid_from_device "${1}") + echo "$partuuid" | cut -d- -f1 +} + + +# Get dos partuuid number from supplied device path. +# +# $1 - dos compatible partuuid device path +disk_get_partuuid_dos_part_number() { + if ! disk_is_valid_partuuid_dos_device "${1}"; then + log_fatal "Invalid dos partuuid device: '${1}'" + fi + partuuid=$(disk_get_partuuid_from_device "${1}") + echo "$partuuid" | cut -d- -f2 +} + +# Get correct device path for current configuration. +# Unrecognized or unsupported device paths will generate an error +# +# $1 - partition number to use if fine grained variable not set +# $2 - fine grained device part variable name +disk_get_part_device() { + part="${!2}" + if [ "${MENDER_ENABLE_PARTUUID}" == "y" ]; then + if ! disk_is_valid_partuuid_device "${part}"; then + log_fatal "Invalid partuuid device for ${2}: '${part}'" + fi + else + part="${MENDER_STORAGE_DEVICE_BASE}${1}" + fi + echo "${part}" +} + +disk_boot_part_device() { + disk_get_part_device "${MENDER_BOOT_PART_NUMBER}" "MENDER_BOOT_PART" +} + +disk_data_part_device() { + disk_get_part_device "${MENDER_DATA_PART_NUMBER}" "MENDER_DATA_PART" +} + +disk_root_part_a_device() { + disk_get_part_device "${MENDER_ROOTFS_PART_A_NUMBER}" "MENDER_ROOTFS_PART_A" +} + +disk_root_part_b_device() { + disk_get_part_device "${MENDER_ROOTFS_PART_B_NUMBER}" "MENDER_ROOTFS_PART_B" +} + + +# Get device partition number from device path. +# Unrecognized or unsupported device paths will generate an error +# +# $1 - device path +disk_get_device_part_number() { + dev_part="unknown" + case "$1" in + /dev/nvme*n*p* ) + dev_part=$(echo $1 | cut -dp -f2) + ;; + /dev/mmcblk*p* ) + dev_part=$(echo $1 | cut -dp -f2) + ;; + /dev/[sh]d[a-z][1-9]* ) + dev_part=${1##*d[a-z]} + ;; + ubi*_* ) + dev_part=$(echo $1 | cut -d_ -f2) + ;; + /dev/disk/by-partuuid/* ) + if disk_is_valid_partuuid_dos_device "$1";then + dev_part=$(disk_get_partuuid_dos_part_number "$1") + dev_part=$((16#${dev_part})) + else + log_fatal "partition number does not exist for GPT partuuid: '$1'" + fi + ;; + esac + part=$(printf "%d" $dev_part 2>/dev/null) + if [ $? = 1 ]; then + log_fatal "Could not determine partition number from '${1}'" + else + echo "$part" + fi +} + +# Get device base path without partition number from argument. +# Unrecognized or unsupported device paths will generate an error +# +# $1 - device path +disk_get_device_base() { + dev_base="" + case "$1" in + /dev/nvme*n*p* ) + dev_base=$(echo $1 | cut -dp -f1) + ;; + /dev/mmcblk*p* ) + dev_base=$(echo $1 | cut -dp -f1) + ;; + /dev/[sh]d[a-z][1-9]* ) + dev_base=${1%%[1-9]*} + ;; + ubi*_* ) + dev_base=$(echo $1 | cut -d_ -f1) + ;; + /dev/disk/by-partuuid/* ) + log_fatal "device base does not exist for GPT partuuid: '$1'" + ;; + esac + if [ -z "$dev_base" ]; then + log_fatal "Could not determine device base from '${1}'" + else + echo $dev_base + fi +} + + +# Conditionally redefine partition number/device if fine grained variable set. +# +# $1 - variable name +# $2 - variable value +disk_override_partition_variable() { + if [ "${MENDER_ENABLE_PARTUUID}" == "y" ]; then + if disk_is_valid_partuuid_dos_device "${2}";then + eval "${1}"=$(disk_get_device_part_number "${2}") + fi + else + if [ -n "${2}" ];then + eval "${1}"=$(disk_get_device_part_number "${2}") + MENDER_STORAGE_DEVICE_BASE=$(disk_get_device_base "${2}") + fi + fi +} \ No newline at end of file