It is not supposed to be managed by grub2-mkconfig
.
The default /boot/efi/EFI/opensuse/grub.cfg
redirects to /boot/grub2/grub.cfg
, but it finds it by UUID, so it must be rewritten to fix. The easiest way is to just rebuild it, but you’re right: I instead should patch the UUIDs.
OK: it all works. Procedure:
- Randomize drive UUID using gdisk
- Fetch old root partition file system UUID
- Randomize root partition file system UUID
- Fetch new root partition file system UUID
- Patch root partition file system UUID in
/etc/fstab
- Patch root partition file system UUID in
/boot/efi/EFI/opensuse/grub.cfg
- Patch root partition file system UUID in
/boot/grub2/grub.cfg
- Fix grub environment block
Here’s the finished shell script:
#!/usr/bin/env bash
#
# For use on a cloned drive. Randomizes the drive and file system UUIDs, while leaving the
# drive bootable.
#
# Assumptions:
# - Recent version of openSUSE
# - Root partition is btrfs
# - sudo is timestamped so one password entry gives you multiple accesses
# - (probably others)
set -o errexit
set -o pipefail
TMPDIR=''
CLEANUP=1
Cleanup() {
if [ -n "$CLEANUP" ]; then
if [ -n "$TMPDIR" ]; then
rm -rf "$TMPDIR"
fi
else
if [ -n "$TMPDIR" ]; then
echo "There is debris which you should clean up here: '$TMPDIR'"
fi
fi
}
Bail() {
if [ -n "$*" ]; then
echo "ERROR: $*"
fi
Cleanup
exit 1
}
Usage() {
echo "Takes three arguments:"
echo " <drive device> (e.g. '/dev/sda')"
echo " <EFI partition number> (e.g. '1')"
echo " <root partition number> (e.g. '2')"
Bail
}
GetFSType() {
sudo lsblk -o FSTYPE "$1" 2>/dev/null | tail -n1
}
GetFSUUID() {
sudo lsblk -o UUID "$1" 2>/dev/null | tail -n1
}
GetDriveUUID() {
sudo fdisk -l "$1" | grep 'Disk identifier: ' | cut -c18-
}
DRIVE="$1"
EFI_PARTITION="$1$2"
ROOT_PARTITION="$1$3"
if ! sudo --validate --prompt "[sudo] Need root access, so enter root password: " ; then
Bail "Must have root access"
fi
if [ $# -ne 3 ]; then
Usage
elif [ ! "${DRIVE:0:5}" = "/dev/" ]; then
echo "Bad format for drive device '$DRIVE'"
Usage
elif [ ! -b "$DRIVE" ]; then
echo "Can't access drive device '$DRIVE'"
Usage
elif [ ! -b "$EFI_PARTITION" ]; then
echo "Can't access EFI partition at ''$EFI_PARTITION''"
Usage
elif [ ! "$(GetFSType "$EFI_PARTITION")" = "vfat" ]; then
echo "Bad EFI partition at '$EFI_PARTITION' (expected vfat, found $(GetFSType "$EFI_PARTITION"))"
elif [ ! -b "$ROOT_PARTITION" ]; then
echo "Can't access root partition at '$ROOT_PARTITION'"
Usage
elif [ ! "$(GetFSType "$ROOT_PARTITION")" = "btrfs" ]; then
echo "Bad root partition at '$ROOT_PARTITION' (must be btrfs, found $(GetFSType "$ROOT_PARTITION"))"
elif [ -n "$(findmnt "$ROOT_PARTITION")" ]; then
Bail "Root partition '$ROOT_PARTITION' already mounted; please unmount"
elif [ -n "$(findmnt "$EFI_PARTITION")" ]; then
Bail "EFI partition '$EFI_PARTITION' already mounted; please unmount"
fi
echo
################ Get old UUIDs ################
echo "Old drive '$DRIVE' UUID is '$(GetDriveUUID "$DRIVE")'"
ROOT_OLD_UUID="$(GetFSUUID "$ROOT_PARTITION")"
ROOT_OLD_UUID="cd4b8972-f6bc-4749-9742-a6f30e0e166c"
echo "Old root partition '$ROOT_PARTITION' file system UUID is '$ROOT_OLD_UUID'"
if [ ! "${#ROOT_OLD_UUID}" -eq 36 ]; then
Bail "unexpected UUID length (${#ROOT_OLD_UUID}) of partition '$ROOT_PARTITION'"
fi
################ Update drive UUID ################
echo "Randomizing drive UUID"
echo "x
f
w
y" | sudo gdisk "$DRIVE" >/dev/null
if [ "$?" -ne 0 ]; then
echo "Blew it"
Bail
fi
echo "New drive '$DRIVE' UUID is '$(GetDriveUUID "$DRIVE")'"
echo
################ Update root partition file system UUID ################
echo "Changing root partition UUID..."
if sudo btrfstune -u -f "$ROOT_PARTITION"; then
echo "... done."
else
echo "... FAILED!!!!! BAILING!!!!!"
Bail
fi
ROOT_NEW_UUID="$(GetFSUUID "$ROOT_PARTITION")"
echo "Root partition '$ROOT_PARTITION' file system UUID is now '$ROOT_NEW_UUID'"
################ Mount all necessary partitions ################
TMPDIR="$(mktemp -d)"
echo "Mounting partitions on $TMPDIR"
if ! sudo mount "$ROOT_PARTITION" "$TMPDIR"; then
Bail "mount $ROOT_PARTITION $TMPDIR failed"
fi
if ! sudo mount "$EFI_PARTITION" "$TMPDIR/boot/efi"; then
Bail "mount $EFI_PARTITION $TMPDIR/boot/efi failed"
fi
# Only needed to chroot grub2-editenv dummy
for dir in /dev /proc /sys /run; do
if ! sudo mount --bind "$dir" "$TMPDIR$dir"; then
Bail "mount $dir $TMPDIR$dir failed"
fi
done
################ Fix fstab and grub.cfg files ################
echo "Fix fstab"
sudo sed --in-place "s/$ROOT_OLD_UUID/$ROOT_NEW_UUID/g" "$TMPDIR/etc/fstab"
# The grub.cfg files are very clearly marked "DO NOT EDIT". But, the following is the
# best way to fix them, given that we aren't changing anything else except for the
# root partition UUID.
echo "Fix /boot/efi/EFI/opensuse/grub.cfg"
sudo sed --in-place "s/$ROOT_OLD_UUID/$ROOT_NEW_UUID/g" "$TMPDIR/boot/efi/EFI/opensuse/grub.cfg"
echo "Fix /boot/grub2/grub.cfg"
sudo sed --in-place "s/$ROOT_OLD_UUID/$ROOT_NEW_UUID/g" "$TMPDIR/boot/grub2/grub.cfg"
# Editing /boot/grub2/grub.cfg breaks the grubenv file; rebuild
echo "Rebuild grub environment block"
if ! sudo chroot "$TMPDIR" grub2-editenv - unset dummy; then
Bail "'sudo chroot $TMPDIR grub2-editenv - unset dummy' failed!"
fi
################ Unmount everything ################
# Can't really handle errors here, so don't try.
for dir in /dev /proc /sys /run; do
sudo umount "$TMPDIR$dir"
done
sudo umount "$TMPDIR/boot/efi"
sudo umount "$TMPDIR"
Cleanup
echo "Done."
And, thanks for all the help.
Dan
Too late to edit the script, but I have one tweak. Change
DRIVE="$1"
EFI_PARTITION="$1$2"
ROOT_PARTITION="$1$3"
to
DRIVE="$1"
for PARTITION_SEPARATOR in '' 'p'; do
if [ -b "$1$PARTITION_SEPARATOR$2" ]; then
break
fi
done
EFI_PARTITION="$1$PARTITION_SEPARATOR$2"
ROOT_PARTITION="$1$PARTITION_SEPARATOR$3"
This handles the cases where partition device files are just the partition numbers appended to the drive file (e.g. sda2
), or have “p” between the drive and partition (e.g. nvme0n1p1
).