Updated script to randomize drive UUIDs

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.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.