Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 69 additions & 49 deletions usr/share/rear/finalize/Linux-i386/670_run_efibootmgr.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -8,71 +8,91 @@ is_true $USING_UEFI_BOOTLOADER || return 0
# (cf. finalize/Linux-i386/610_EFISTUB_run_efibootmgr.sh):
is_true $EFI_STUB && return

LogPrint "Creating EFI Boot Manager entries..."

local esp_mountpoint esp_mountpoint_inside boot_efi_parts boot_efi_dev

# When UEFI_BOOTLOADER is not a regular file in the restored target system
# (cf. how esp_mountpoint is set below) it means BIOS is used
# (cf. rescue/default/850_save_sysfs_uefi_vars.sh)
# which includes that also an empty UEFI_BOOTLOADER means using BIOS
# because when UEFI_BOOTLOADER is empty the test below evaluates to
# test -f /mnt/local/
# which also returns false because /mnt/local/ is a directory
# (cf. https://github.com/rear/rear/pull/2051/files#r258826856):
test -f "$TARGET_FS_ROOT/$UEFI_BOOTLOADER" || return 0
# (cf. https://github.com/rear/rear/pull/2051/files#r258826856)
# but using BIOS conflicts with USING_UEFI_BOOTLOADER is true
# i.e. we should create EFI Boot Manager entries but we cannot:
if ! test -f "$TARGET_FS_ROOT/$UEFI_BOOTLOADER" ; then
LogPrintError "Failed to create EFI Boot Manager entries (UEFI bootloader '$UEFI_BOOTLOADER' not found under target $TARGET_FS_ROOT)"
return 1
fi

# Determine where the EFI System Partition (ESP) is mounted in the currently running recovery system:
esp_mountpoint=$( df -P "$TARGET_FS_ROOT/$UEFI_BOOTLOADER" | tail -1 | awk '{print $6}' )
# Use TARGET_FS_ROOT/boot/efi as fallback ESP mountpoint:
test "$esp_mountpoint" || esp_mountpoint="$TARGET_FS_ROOT/boot/efi"
esp_mountpoint=$( filesystem_name "$TARGET_FS_ROOT/$UEFI_BOOTLOADER" )
# Use TARGET_FS_ROOT/boot/efi as fallback ESP mountpoint (filesystem_name returns "/"
# if mountpoint not found otherwise):
if [ "$esp_mountpoint" = "/" ] ; then
esp_mountpoint="$TARGET_FS_ROOT/boot/efi"
LogPrint "Mountpoint of $TARGET_FS_ROOT/$UEFI_BOOTLOADER not found, trying $esp_mountpoint"
fi

# Skip if there is no esp_mountpoint directory (e.g. the fallback ESP mountpoint may not exist).
# Double quotes are mandatory here because 'test -d' without any (possibly empty) argument results true:
test -d "$esp_mountpoint" || return 0

BootEfiDev="$( mount | grep "$esp_mountpoint" | awk '{print $1}' )"
# /dev/sda1 or /dev/mapper/vol34_part2 or /dev/mapper/mpath99p4
Dev=$( get_device_name $BootEfiDev )
# 1 (must anyway be a low nr <9)
ParNr=$( get_partition_number $Dev )
# /dev/sda or /dev/mapper/vol34_part or /dev/mapper/mpath99p or /dev/mmcblk0p
Disk=$( echo ${Dev%$ParNr} )

# Strip trailing partition remainders like '_part' or '-part' or 'p'
# if we have 'mapper' in disk device name:
if [[ ${Dev/mapper//} != $Dev ]] ; then
# we only expect mpath_partX or mpathpX or mpath-partX
case $Disk in
(*p) Disk=${Disk%p} ;;
(*-part) Disk=${Disk%-part} ;;
(*_part) Disk=${Disk%_part} ;;
(*) Log "Unsupported kpartx partition delimiter for $Dev"
esac
if ! test -d "$esp_mountpoint" ; then
LogPrintError "Failed to create EFI Boot Manager entries (no ESP mountpoint directory $esp_mountpoint)"
return 1
fi

# For eMMC devices the trailing 'p' in the Disk value
# (as in /dev/mmcblk0p that is derived from /dev/mmcblk0p1)
# needs to be stripped (to get /dev/mmcblk0), otherwise the
# efibootmgr call fails because of a wrong disk device name.
# See also https://github.com/rear/rear/issues/2103
if [[ $Disk = *'/mmcblk'+([0-9])p ]] ; then
Disk=${Disk%p}
fi
# Mount point inside the target system,
# accounting for possible trailing slashes in TARGET_FS_ROOT
esp_mountpoint_inside="${esp_mountpoint#${TARGET_FS_ROOT%%*(/)}}"

# For NVMe devices the trailing 'p' in the Disk value
# (as in /dev/nvme0n1p that is derived from /dev/nvme0n1p1)
# needs to be stripped (to get /dev/nvme0n1), otherwise the
# efibootmgr call fails because of a wrong disk device name.
# See also https://github.com/rear/rear/issues/1564
if [[ $Disk = *'/nvme'+([0-9])n+([0-9])p ]] ; then
Disk=${Disk%p}
boot_efi_parts=$( find_partition "fs:$esp_mountpoint_inside" fs )
if ! test "$boot_efi_parts" ; then
LogPrint "Unable to find ESP $esp_mountpoint_inside in layout"
LogPrint "Trying to determine device currently mounted at $esp_mountpoint as fallback"
boot_efi_dev="$( mount | grep "$esp_mountpoint" | awk '{print $1}' )"
if ! test "$boot_efi_dev" ; then
LogPrintError "Cannot create EFI Boot Manager entry (unable to find ESP $esp_mountpoint among mounted devices)"
return 1
fi
if test $(get_component_type "$boot_efi_dev") = part ; then
boot_efi_parts="$boot_efi_dev"
else
boot_efi_parts=$( find_partition "$boot_efi_dev" )
fi
if ! test "$boot_efi_parts" ; then
LogPrintError "Cannot create EFI Boot Manager entry (unable to find partition for $boot_efi_dev)"
return 1
fi
LogPrint "Using fallback EFI boot partition(s) $boot_efi_parts (unable to find ESP $esp_mountpoint_inside in layout)"
fi

local bootloader partition_block_device partition_number disk efipart

# EFI\fedora\shim.efi
BootLoader=$( echo $UEFI_BOOTLOADER | cut -d"/" -f4- | sed -e 's;/;\\;g' )
LogPrint "Creating EFI Boot Manager entry '$OS_VENDOR $OS_VERSION' for '$BootLoader' (UEFI_BOOTLOADER='$UEFI_BOOTLOADER')"
Log efibootmgr --create --gpt --disk ${Disk} --part ${ParNr} --write-signature --label \"${OS_VENDOR} ${OS_VERSION}\" --loader \"\\${BootLoader}\"
if efibootmgr --create --gpt --disk ${Disk} --part ${ParNr} --write-signature --label "${OS_VENDOR} ${OS_VERSION}" --loader "\\${BootLoader}" ; then
# ok, boot loader has been set-up - tell rear we are done using following var.
NOBOOTLOADER=''
return
fi
bootloader=$( echo $UEFI_BOOTLOADER | cut -d"/" -f4- | sed -e 's;/;\\;g' )

for efipart in $boot_efi_parts ; do
# /dev/sda1 or /dev/mapper/vol34_part2 or /dev/mapper/mpath99p4
partition_block_device=$( get_device_name $efipart )
# 1 or 2 or 4 for the examples above
partition_number=$( get_partition_number $partition_block_device )
if ! disk=$( get_device_from_partition $partition_block_device $partition_number ) ; then
LogPrintError "Cannot create EFI Boot Manager entry for ESP $partition_block_device (unable to find the underlying disk)"
# do not error out - we may be able to locate other disks if there are more of them
continue
fi
LogPrint "Creating EFI Boot Manager entry '$OS_VENDOR $OS_VERSION' for '$bootloader' (UEFI_BOOTLOADER='$UEFI_BOOTLOADER') "
Log efibootmgr --create --gpt --disk $disk --part $partition_number --write-signature --label \"${OS_VENDOR} ${OS_VERSION}\" --loader \"\\${bootloader}\"
if efibootmgr --create --gpt --disk $disk --part $partition_number --write-signature --label "${OS_VENDOR} ${OS_VERSION}" --loader "\\${bootloader}" ; then
# ok, boot loader has been set-up - continue with other disks (ESP can be on RAID)
NOBOOTLOADER=''
else
LogPrintError "efibootmgr failed to create EFI Boot Manager entry on $disk partition $partition_number (ESP $partition_block_device )"
fi
done

LogPrintError "efibootmgr failed to create EFI Boot Manager entry for '$BootLoader' (UEFI_BOOTLOADER='$UEFI_BOOTLOADER')"
is_true $NOBOOTLOADER || return 0
LogPrintError "efibootmgr failed to create EFI Boot Manager entry for '$bootloader' (UEFI_BOOTLOADER='$UEFI_BOOTLOADER')"
return 1
81 changes: 73 additions & 8 deletions usr/share/rear/lib/layout-functions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -316,12 +316,20 @@ get_child_components() {
done
}

# Return all ancestors of component $1 [ of type $2 ]
# Return all ancestors of component $1 [ of type $2 [ skipping types $3 during resolution ] ]
get_parent_components() {
declare -a ancestors devlist
declare current child parent
declare -a ancestors devlist ignoretypes
declare current child parent parenttype

devlist=( "$1" )
if [[ "$3" ]] ; then
# third argument should, if present, be a space-separated list
# of types to ignore when walking up the dependency tree.
# Convert it to array
ignoretypes=( $3 )
else
ignoretypes=()
fi
while (( ${#devlist[@]} )) ; do
current=${devlist[0]}

Expand All @@ -332,6 +340,13 @@ get_parent_components() {
if IsInArray "$parent" "${ancestors[@]}" ; then
continue
fi
### ...test if parent is of a correct type if requested...
if [[ ${#ignoretypes[@]} -gt 0 ]] ; then
parenttype=$(get_component_type "$parent")
if IsInArray "$parenttype" "${ignoretypes[@]}" ; then
continue
fi
fi
### ...and add them to the list
devlist+=( "$parent" )
ancestors+=( "$parent" )
Expand Down Expand Up @@ -359,22 +374,24 @@ get_parent_components() {
}

# find_devices <other>
# ${2+"$2"} in the following functions ensures that $2 gets passed down quoted if present
# and ignored if not present
# Find the disk device(s) component $1 resides on.
find_disk() {
get_parent_components "$1" "disk"
get_parent_components "$1" "disk" ${2+"$2"}
}

find_multipath() {
get_parent_components "$1" "multipath"
get_parent_components "$1" "multipath" ${2+"$2"}
}

find_disk_and_multipath() {
find_disk "$1"
is_true "$AUTOEXCLUDE_MULTIPATH" || find_multipath "$1"
find_disk "$1" ${2+"$2"}
is_true "$AUTOEXCLUDE_MULTIPATH" || find_multipath "$1" ${2+"$2"}
}

find_partition() {
get_parent_components "$1" "part"
get_parent_components "$1" "part" ${2+"$2"}
}

# The get_partition_number function
Expand Down Expand Up @@ -427,6 +444,54 @@ get_partition_number() {
echo $partition_number
}

# Extract the underlying device name from the full partition device name.
# Underlying device may be a disk, a multipath device or other devices that can be partitioned.
# Should we use the information in $LAYOUT_DEPS, like get_parent_component does,
# instead of string munging?
function get_device_from_partition() {
local partition_block_device
local device
local partition_number

partition_block_device=$1
test -b "$partition_block_device" || BugError "get_device_from_partition called with '$partition_block_device' that is no block device"
partition_number=${2-$(get_partition_number $partition_block_device )}
# /dev/sda or /dev/mapper/vol34_part or /dev/mapper/mpath99p or /dev/mmcblk0p
device=${partition_block_device%$partition_number}

# Strip trailing partition remainders like '_part' or '-part' or 'p'
# if we have 'mapper' in disk device name:
if [[ ${partition_block_device/mapper//} != $partition_block_device ]] ; then
# we only expect mpath_partX or mpathpX or mpath-partX
case $device in
(*p) device=${device%p} ;;
(*-part) device=${device%-part} ;;
(*_part) device=${device%_part} ;;
(*) Log "Unsupported kpartx partition delimiter for $partition_block_device"
esac
fi

# For eMMC devices the trailing 'p' in the $device value
# (as in /dev/mmcblk0p that is derived from /dev/mmcblk0p1)
# needs to be stripped (to get /dev/mmcblk0), otherwise the
# efibootmgr call fails because of a wrong disk device name.
# See also https://github.com/rear/rear/issues/2103
if [[ $device = *'/mmcblk'+([0-9])p ]] ; then
device=${device%p}
fi

# For NVMe devices the trailing 'p' in the $device value
# (as in /dev/nvme0n1p that is derived from /dev/nvme0n1p1)
# needs to be stripped (to get /dev/nvme0n1), otherwise the
# efibootmgr call fails because of a wrong disk device name.
# See also https://github.com/rear/rear/issues/1564
if [[ $device = *'/nvme'+([0-9])n+([0-9])p ]] ; then
device=${device%p}
fi

test -b "$device" && echo $device
}

# Returns partition start block or 'unknown'
# sda/sda1 or
# dm-XX
Expand Down