#!/bin/sh

PREREQ=""

prereqs()
{
	echo "$PREREQ"
}

case $1 in
prereqs)
	prereqs
	exit 0
	;;
esac

. /usr/share/initramfs-tools/hook-functions

find_root_device() {
	[ -r /etc/fstab ] || return

	grep '^[^#]' /etc/fstab | ( \
	while read device mount type options dump pass; do
		if [ "$mount" = "/" ]; then
			echo "$device"
			return
		fi
	done )
}

find_crypt_node() {
	local rootdev node
	rootdev=$1
	node=${rootdev#/dev/mapper/}

	# make sure rootdev is some sort of logical volume
	[ "$node" != "$rootdev" ] || return 1
	# loop until we find a cryptographic volume on which
	# the rootdevice depends
	while true; do
		if (get_crypt_device $node >& /dev/null) ; then
			# return if we found a cryptographic volume
	    		echo "$node"
	    		return
		else
	    		# get the underlying volume 
			node=$(get_underlying_node $node)
	    		[ ! -z "$node" ] || return 1
		fi
	done
}

get_underlying_node() {
	local node deps major minor
	node=$1

	[ ! -z "$node" ] ||  return 1

	# find underlying device or dm volume 
	deps=$( dmsetup deps $node | \
		sed 's/[^:]*: *(\([0-9]*\), *\([0-9]\).*/\1 \2/') || return 
	[ ! -z "$deps" ] || return 1
	major=$(echo $deps | cut -d " " -f 1)
	minor=$(echo $deps | cut -d " " -f 2)	
	# Check that the underlying device is dm volume.
	node=$( dmsetup info -c --noheadings -j $major -m $minor | \
	        cut -d ":" -f 1)
	# If the underling device is not a dm volume then the
	# rootdevice does not depend on a cryptogaphic volume
	[ ! -z "$node" ] || return 1
	echo $node
}

get_crypt_device() {
	local node crypdev
	node=$1

	[ ! -z "$node" ] || return
	[ -r /etc/crypttab ] || return

	cryptdev=$( grep ^$node /etc/crypttab | \
		    head -1 | sed 's/[[:space:]]+*/ /g' | cut -d " " -f3)

	[ ! -z "$cryptdev" ] || return 1
	echo $cryptdev
}

get_device_prereq() {
	local dev node majmin lv
	dev=$1
	node=${dev#/dev/mapper/}

	# check if the device is a dm volume
	if [ "$node" != "$dev" ]; then 
		majmin=$( dmsetup info -c --noheadings $node | \
		      	  cut -d ":" -f 2,3 )

	    	if [ -x /sbin/lvdisplay ]; then
			lv=$( /sbin/lvdisplay | \
			      grep "$(majmin)$$" )
			if [ ! -z "$lv" ]; then
		    		echo lvm
		    		return
			fi
	    	fi
	fi
	echo md
}
 
get_root_opts() {
	local node rootopts
	node=$1

	[ -z "$node" ] && return
	[ -r /etc/crypttab ] || return

	rootopts=$( grep ^$node /etc/crypttab | \
		    head -1 | sed 's/[[:space:]]+*/ /g' | cut -d " " -f6)
	echo "$rootopts"
	[ ! -z "$rootopts" ] || return 1
	return 0
}

get_root_modules() {
	local rootopts
	rootopts=$1

	[ ! -z "$rootopts" ] || return

	echo "dm_mod"
	echo "dm_crypt"
	local IFS=", "
	for opt in $rootopts; do
		# Does option start with cipher=?
		value=${opt#cipher=}
		[ $value != $opt ] || continue

		# Add the cipher to the list of modules
		cipher=${value%%-*}
		echo $cipher

		# Possibly add a hash as well
		hash=${value##*:}
		[ ! -z $hash -o $hash != $value ] || continue
		echo $hash
	done
}

get_root_initramfsopts() {
	local rootnode rootopts
	rootnode=$1
	rootopts=$2

	[ ! -z "$rootnode" ] || return
	[ ! -z "$rootopts" ] || return

	echo -n "node=$rootnode"
	local IFS=", "
	for opt in $rootopts; do
		case $opt in
			cipher=*)
				echo -n ",$opt"
				;;
			hash=*)
				echo -n ",$opt"
				;;
			size=*)
				echo -n ",$opt"
				;;
			*)
				# Presumably a non-supported option
				;;
		esac
	done
	echo ""
}

# Find out which device root is on
rootdev=$(find_root_device)
[ ! -z "$rootdev" ] || exit 0
echo "$0: rootdev = $rootdev"

# Find crypto node under /dev/mapper/ 
cryptnode=$(find_crypt_node $rootdev)
[ ! -z "$cryptnode" ] || exit 0
echo "$0: cryptnode = $cryptnode"

# Find out which device the crypt node is on
cryptdev=$(get_crypt_device $cryptnode)
[ ! -z "$cryptnode" ] || exit 0
echo "$0: cryptdev = $cryptdev"

# Get crypttab root options
rootopts=$(get_root_opts $cryptnode $opts)
[ ! -z "$rootopts" ] || exit 0
echo "$0: rootopts = $rootopts"

# Calculate needed modules
modules=$(get_root_modules $rootopts | sort | uniq)
for x in $modules; do
	force_load ${x}
done

# Check the root options to write to the initramfs
initramfsopts=$(get_root_initramfsopts $cryptnode $rootopts)
echo "$0: initramfsopts = $initramfsopts"

# Check wif lvm needs to start before cryptroot
prereq=$(get_device_prereq $cryptdev)
echo "$0: prereq = $prereq"

echo "CRYPTOPTS=\"device=$cryptdev,$initramfsopts\"" > ${DESTDIR}/conf/conf.d/cryptroot
echo "PREREQ=\"$prereq\"" >> ${DESTDIR}/conf/conf.d/cryptroot

copy_exec /sbin/cryptsetup /sbin
copy_exec /sbin/dmsetup /sbin
[ -x "/etc/mkinitramfs/cryptgetpw" ] && copy_exec /etc/mkinitramfs/cryptgetpw /sbin

exit 0
