#!/bin/sh
############################################
#
# Change fstab to use ufsid and geom labels to avoid relying on device numbers directly.
#
############################################

# : cat /etc/fstab
# # Device                Mountpoint      FStype  Options         Dump    Pass#
# /dev/ad0s1a             /               ufs     rw              1       1
# /dev/ad0s1b             none            swap    sw              0       0

string_length() {
	unset LEN
	LEN=$(echo -n "${1}" | /usr/bin/wc -m)
}

get_ufsid() {
	# $1 = device
	# : /sbin/dumpfs /dev/ad0s1a | /usr/bin/head -n 2 | /usr/bin/tail -n 1 | /usr/bin/cut -f2 -d'[' | /usr/bin/cut -f1 -d ']' | /usr/bin/sed -e 's/[[:blank:]]//g'
	# 51928c99a471c440

	unset UFSID
	local _dev="${1}"

	if [ -z "${_dev}" ]; then
		return
	fi

	ID_PARTS=$(/sbin/dumpfs /dev/${_dev} | \
		/usr/bin/head -n 2 | \
		/usr/bin/tail -n 1 | \
		/usr/bin/cut -f2 -d'[' | \
		/usr/bin/cut -f1 -d ']')
	# " 51110eb0 f288b35d " (note it has more spaces than we need/want)
	ID_PART1=$(echo ${ID_PARTS} | awk '{print $1}')
	# "51110eb0"
	ID_PART2=$(echo ${ID_PARTS} | awk '{print $2}')
	# "f288b35d"

	if [ -z "${ID_PART1}" -o -z  "${ID_PART2}" ]; then
		echo "Invalid ufsid on ${1} (${ID_PARTS}), cannot continue"
		exit -1
	fi

	# Safety check to avoid http://www.freebsd.org/cgi/query-pr.cgi?pr=156908
	string_length "${ID_PART1}"
	if [ ${LEN} -ne 8 ]; then
		ID_PART1=$(printf "%08s" "${ID_PART1}")
	fi
	string_length "${ID_PART2}"
	if [ ${LEN} -ne 8 ]; then
		ID_PART2=$(printf "%08s" "${ID_PART2}")
	fi
	UFSID="${ID_PART1}${ID_PART2}"
}

find_fs_device() {
	unset DEV
	DEV=$(/usr/bin/grep -e "[[:blank:]]*${1}[[:blank:]]" ${FSTAB} | awk '{print $1}')
	DEV=${DEV##/dev/}
}

FSTAB=${DESTDIR}/etc/fstab
unset NEED_CHANGES
cp ${FSTAB} ${FSTAB}.tmp

ALL_FILESYSTEMS=$(/usr/bin/awk '/ufs/ && !(/dev\/mirror\// || /dev\/ufsid\// || /dev\/label\// || /dev\/geom\//) {print $2}' ${DESTDIR}/etc/fstab)

for FS in ${ALL_FILESYSTEMS}
do
	find_fs_device "${FS}"
	if [ "${DEV}" != "" ]; then
		get_ufsid ${DEV}
		string_length "${UFSID}"
		if [ ${LEN} -ne 16 ]; then
			echo "Invalid UFS ID for FS ${FS} ($UFSID), skipping"
		else
			/usr/bin/sed -i '' -e "s/${DEV}/ufsid\/${UFSID}/g" ${FSTAB}.tmp
			NEED_CHANGES=1
		fi
	else
		echo "Unable to find device for ${FS}"
		exit -1
	fi
	echo "FS: ${FS} on device ${DEV} with ufsid ${UFSID}"
done

ALL_SWAPFS=$(/usr/bin/awk '/swap/ && !(/dev\/mirror\// || /dev\/ufsid\// || /dev\/label\// || /dev\/geom\//) {print $1}' ${DESTDIR}/etc/fstab)
SWAPNUM=0
for SFS in ${ALL_SWAPFS}
do
	DEV=${SFS##/dev/}
	if [ "${DEV}" != "" ]; then
		SWAPDEV=${DEV}
		echo "FS: Swap slice ${SWAPNUM} on device ${SWAPDEV}"
		if [ "${SWAPDEV}" != "" ]; then
			/usr/bin/sed -i '' -e "s/${SWAPDEV}/label\/swap${SWAPNUM}/g" ${FSTAB}.tmp
			NEED_CHANGES=1
		fi
		SWAPNUM=$((SWAPNUM+1))
	fi
done

if [ -z "${NEED_CHANGES}" ]; then
	echo Nothing to do, all filesystems and swap already use some form of device-independent labels
	exit 0
fi

echo "===================="
echo "Current fstab:"
cat ${FSTAB}
echo "===================="
echo "New fstab:"
cat ${FSTAB}.tmp

if [ "${1}" = "commit" ]; then
	COMMIT=y
else
	echo "Commit changes? (y/n):"
	read COMMIT
fi

# Commit changes
if [ "${COMMIT}" = "y" -o "${COMMIT}" = "Y" ]; then

	echo "Applying label to swap partition"
	SWAPNUM=0
	for SFS in ${ALL_SWAPFS}
	do
		find_fs_device "${SFS}"
		if [ "${DEV}" != "" ]; then
			SWAPDEV=${DEV}
			if [ -n "${SWAPDEV}" ]; then
				echo "Disabling swap ${SWAPDEV} to apply label"
				/sbin/swapoff /dev/${SWAPDEV}
				/sbin/glabel label swap${SWAPNUM} /dev/${SWAPDEV}
			fi
			SWAPNUM=$((SWAPNUM+1))
		fi
	done

	echo "Activating new fstab"
	/bin/mv -f ${FSTAB} ${FSTAB}.old
	/bin/mv -f ${FSTAB}.tmp ${FSTAB}

	echo "Re-enabling swap"
	/sbin/swapon -a 2>/dev/null >/dev/null
fi
