Hello, again. I’ve fixed a couple of bugs in my script to randomize UUIDs on a cloned drive::
So, here’s the new update:
#!/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
}
Log() {
echo "######## $(date): $* ########"
}
Bail() {
if [ -n "$*" ]; then
echo "######## $(date): 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"
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"
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
Bail "Bad format for drive device '$DRIVE'"
elif [ ! -b "$DRIVE" ]; then
Bail "Can't access drive device '$DRIVE'"
elif [ ! -b "$EFI_PARTITION" ]; then
Bail "Can't access EFI partition at ''$EFI_PARTITION''"
elif [ ! "$(GetFSType "$EFI_PARTITION")" = "vfat" ]; then
Bail "Bad EFI partition at '$EFI_PARTITION' (expected vfat, found $(GetFSType "$EFI_PARTITION"))"
elif [ ! -b "$ROOT_PARTITION" ]; then
Bail "Can't access root partition at '$ROOT_PARTITION'"
elif [ ! "$(GetFSType "$ROOT_PARTITION")" = "btrfs" ]; then
Bail "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
################ Get old UUIDs ################
DRIVE_OLD_UUID="$(GetDriveUUID "$DRIVE")"
Log "Old drive '$DRIVE' UUID is '$DRIVE_OLD_UUID'"
ROOT_OLD_UUID="$(GetFSUUID "$ROOT_PARTITION")"
Log "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 ################
Log "Randomizing drive UUID"
# Commands are:
# x: Expert mode
# f: randomize disk and partition unique GUIDs
# w: Write table to disk and exit
# y: Yes, I'm sure!
echo "x
f
w
y" | sudo gdisk "$DRIVE" >/dev/null
if [ "$?" -ne 0 ]; then
Bail "gdisk failed"
fi
DRIVE_NEW_UUID="$(GetDriveUUID "$DRIVE")"
Log "New drive '$DRIVE' UUID is '$DRIVE_NEW_UUID'"
if [ "$DRIVE_NEW_UUID" = "$DRIVE_OLD_UUID" ]; then
Bail "UUID DIDN'T CHANGE"
fi
################ Update root partition file system UUID ################
Log "Randomizing root partition UUID..."
if sudo btrfstune -u -f "$ROOT_PARTITION"; then
Log "... done."
else
Bail "FAILED!"
fi
# It can take a moment before the changed UUID becomes visible.
while true; do
ROOT_NEW_UUID="$(GetFSUUID "$ROOT_PARTITION")"
if [ "$ROOT_NEW_UUID" != "$ROOT_OLD_UUID" ]; then
Log "UUID changed to '$ROOT_NEW_UUID'."
break
else
Log "UUID change not yet visible; waiting..."
fi
sleep 1
done
################ Mount all necessary partitions ################
TMPDIR="$(mktemp -d)"
Log "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 in order 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 ################
Log "Fix fstab"
sudo sed --in-place='.bak' "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.
Log "Fix /boot/efi/EFI/opensuse/grub.cfg"
sudo sed --in-place='.bak' "s/$ROOT_OLD_UUID/$ROOT_NEW_UUID/g" "$TMPDIR/boot/efi/EFI/opensuse/grub.cfg"
Log "Fix /boot/grub2/grub.cfg"
sudo sed --in-place='.bak' "s/$ROOT_OLD_UUID/$ROOT_NEW_UUID/g" "$TMPDIR/boot/grub2/grub.cfg"
# Editing /boot/grub2/grub.cfg breaks the grubenv file; rebuild
Log "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.
Log "Unmount everything"
for dir in /dev /proc /sys /run; do
sudo umount "$TMPDIR$dir"
done
sudo umount "$TMPDIR/boot/efi"
sudo umount "$TMPDIR"
Cleanup
Log "Done."
Thanks again for all the help.