Randomizing UUIDs on a cloned drive

In a separate thread, I asked about how to change UUIDs on a cloned drive, so that both drives could be used from the same machine:

With lots of help I figured it out, and eventually posted a script that did all the work. Problem: the script was buggy, and I found this out too late to fix it in the thread. So, here’s the no-longer-buggy 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"
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
	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")"
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 "Randomizing 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='.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.

echo "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"

echo "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
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."

Thanks for all who helped!