#!/bin/bash

. /.initrd/initenv
. uevent-sh-functions
. initrd-sh-functions
. rdshell-sh-functions

mnt="/mnt/luks-key"
tab='	'

match_dev_in_array() {
	[ "$#" = 2 ] || return 2

	local value array_name array_size

	array_name="$1"
	value="$2"

	eval "array_size=\"\${$array_name-}\""

	[ -n "$array_size" ] && [ "$array_size" != 0 ] ||
		return 1

	local i luksdev realdev

	i=0
	while [ "$i" -lt "$array_size" ]; do
		eval "luksdev=\"\$$array_name$i\""
		i=$(($i + 1))

		realdev=
		get_dev realdev "$luksdev"

		[ "$realdev" != "$value" ] ||
			return 0
	done

	return 1
}

freekey() {
	[ -d "$mnt" ] && umount "$mnt" && rmdir "$mnt" ||:
}

findkey() {
	local path keydev luksdev prefix s v

	[ -f /etc/luks.keys ] ||
		return 2

	while IFS='' read -r s; do
		for n in path keydev luksdev; do
			v=
			if [ -n "$s" ] && [ -z "${s##*$tab*}" ]; then
				v="${s%%$tab*}"
				s="${s#*$tab}"
			else
				v="$s"
				s=
			fi
			eval "$n=\"\$v\""
		done

		if [ -z "$path" ]; then
			printf "ERROR(luks): path required.\n" >&2
			return 1
		fi

		if [ -n "$luksdev" ]; then
			get_dev "$luksdev" ||
				continue
		fi

		prefix=
		if [ -n "$keydev" ]; then
			mkdir -p -- "$mnt"
			mount -r "$keydev" "$mnt" ||
				return 1
			prefix=$mnt
		fi

		if [ ! -f "$prefix/$path" ]; then
			printf "ERROR(luks): %s: keyfile not found.\n" "$path" >&2
			return 1
		fi

		keyfile="$prefix/$path"
		printf "Found keyfile '%s' for '%s' encrypted partition.\n" "$path" "${LUKS_ROOT#/dev/}"
		return 0

	done < /etc/luks.keys

	# Keyfile not found yet.
	return 2
}

readkey() {
	local keyfile="$1"
	[ -s "$keyfile" ] ||
		return 0
	case "${LUKS_KEY_FORMAT:-plain}" in
		plain)
			{ cat "$keyfile"; printf .; } |
			sed \
				-e ':a'      \
				-e 'N'       \
				-e '$!ba'    \
				-e 's/\.$//' \
				-e 's/\n$//'
			;;
		raw)
			cat "$keyfile"
			;;
	esac
}

handler() {
	nameluks="${LUKS_ROOT##*/}-luks"

	# skip if $nameluks has already exist
	! dmsetup info "$nameluks" >/dev/null 2>&1 ||
		exit 0

	! match_dev_in_array LUKS_IGNORE "$LUKS_ROOT" ||
		exit 0

	luks_discard=
	! match_dev_in_array LUKS_DISCARD "$LUKS_ROOT" ||
		luks_discard=--allow-discards

	local rc=0 keyfile=
	if [ -n "$LUKS_KEY" ] && [ "$LUKS_KEY" != 0 ]; then
		findkey ||
			rc="$?"
		[ "$rc" != 0 ] ||
			{ readkey "$keyfile" |cryptsetup $luks_discard -d- luksOpen "$LUKS_ROOT" "$nameluks"; } ||
			rc="$?"
		freekey
	else
		# WARNING: Wait decrypt forever!
		rc=2
		while [ "$rc" = 2 ]; do
			cryptsetup $luks_discard luksOpen "$LUKS_ROOT" "$nameluks"
			rc="$?"
		done
	fi

	if [ "$rc" != 0 ]; then
		printf 'ERROR(luks): %s: unable to activate LUKS (rc=%s)\n' "$LUKS_ROOT" "$rc" >&2
		exit 1
	fi
}

while ! console_lock; do
	sleep 0.5
done

exec 0</dev/console >/dev/console 2>&1

rc=0
for e in "$eventdir"/luks.*; do
	[ -f "$e" ] || break
	r=0
	( . "$e"; handler; ) || r="$?"
	case "$r" in
		2) ;;
		1) rc=1 ;;
		0) done_event "$e" ;;
	esac
done

console_unlock
exit $rc
