#!/bin/sh

# "Setup encrypted volumes" in the main menu.
#  1. Checks required tools
#  2. Checks for valid cipher options
#  3. Commits partman changes
#  4. Checks for safe swap
#  5. Creates keyfiles
#  6. Erases to-be-encrypted partitions
#  7. Does losetup/dmsetup
#  8. Restarts partman

. /lib/partman/lib/crypto-base.sh

get_passphrase () {
	db_set partman-crypto/activate/passphrase-existing ""
	db_fset partman-crypto/activate/passphrase-existing seen false
	db_subst partman-crypto/activate/passphrase-existing DEVICE "$1"
	db_input critical partman-crypto/activate/passphrase-existing

	db_go || return 1

	db_get partman-crypto/activate/passphrase-existing || RET=''
	echo -n "$RET"
}

do_cryptsetup () {
	local dev num id size path
	local dump cipher keysize ivalgorithm keytype keyhash
	local cryptdev pass

	dev=$1
	num=$2
	id=$3
	size=$4
	path=$5

	dump="$(cryptsetup luksDump "$path")"
	cipher="$(echo "$dump" | sed -n '/^Cipher name:/s/.*[[:space:]]//p')"
	if [ "$cipher" ]; then
		crypto_load_udebs "cdebconf-$DEBIAN_FRONTEND-entropy" \
				  partman-crypto-dm
		crypto_check_required_tools dm-crypt
		crypto_load_modules dm-crypt "$cipher"
	fi
	keysize="$(echo "$dump" | sed -n '/^MK bits:/s/.*[[:space:]]//p')"
	ivalgorithm="$(echo "$dump" | sed -n '/^Cipher mode:/s/.*[[:space:]]//p')"
	keytype=passphrase
	keyhash="$(echo "$dump" | sed -n '/^Hash spec:/s/.*[[:space:]]//p')"

	cryptdev="${path##*/}_crypt"
	if ! cryptsetup status "$cryptdev" >/dev/null 2>&1; then
		while :; do
			pass="$(get_passphrase "$path")" || return 1
			if [ -z "$pass" ]; then
				return 1
			fi
			echo -n "$pass" | log-output -t partman-crypto \
				cryptsetup -d - luksOpen "$path" "$cryptdev" \
				&& break
		done

		cryptdev="/dev/mapper/$cryptdev"
		echo dm-crypt > $id/crypto_type
		echo "$keysize" > $id/keysize
		echo "$ivalgorithm" > $id/ivalgorithm
		echo "$keytype" > $id/keytype
		echo "$keyhash" > $id/keyhash
		echo cipher > $id/cipher
		echo crypto_keep > $id/method
		echo "$cryptdev" > $id/crypt_active

		db_subst partman-crypto/text/in_use DEV "${cryptdev##*/}"
		db_metaget partman-crypto/text/in_use description
		partman_lock_unit "$(mapdevfs "$path")" "$RET"
	fi
}

do_activate () {
	local found_luks dev partitions num id size type fs path name part

	found_luks=0
	for dev in $DEVICES/*; do
		[ -d "$dev" ] || continue
		cd "$dev"

		partitions=
		open_dialog PARTITIONS
		while { read_line num id size type fs path name; [ "$id" ]; }; do
			[ "$fs" != free ] || continue
			partitions="$partitions $id,$path"
		done
		close_dialog

		for part in $partitions; do
			id="${part%%,*}"
			path="${part#*,}"

			if cryptsetup isLuks "$path" 2>/dev/null; then
				found_luks=1
				do_cryptsetup "$dev" "$num" "$id" "$size" \
					"$path" || continue
			fi
		done
	done

	if [ "$found_luks" = 0 ]; then
		db_input critical partman-crypto/activate/no_luks
		db_go || true
		return
	fi

	# Encrypted devices as configured by d-i usually contain LVM PVs
	export LVM_SUPPRESS_FD_WARNINGS=1
	log-output -t partman-crypto pvscan
	log-output -t partman-crypto vgscan
	log-output -t partman-crypto vgchange -a y

	# Tell partman to detect filesystems again.
	rm -f /var/lib/partman/filesystems_detected

	stop_parted_server
	restart_partman
	exit 0
}

do_create () {
	local parts line pv output vg pathmap
	parts=""
	pathmap=""

	# Look for free partitions
	IFS="$NL"
	for line in $(crypto_list_allowed_free); do
		restore_ifs
		local dev="${line%%$TAB*}"
		line="${line#*$TAB}"
		local id="${line%%$TAB*}"
		line="${line#*$TAB}"
		local size="${line%%$TAB*}"
		local path="${line#*$TAB}"
		cd $dev
		if [ -s "$id/visual_filesystem" ]; then
			local visual="$(cat "$id/visual_filesystem")"
			output=$(printf "%-30s (%sMB; %s)" "$path" "$(convert_to_megabytes $size)" "$visual")
		else
			output=$(printf "%-30s (%sMB)" "$path" "$(convert_to_megabytes $size)")
		fi
		parts="${parts:+$parts, }$output"
		pathmap="${pathmap:+$pathmap$NL}$path$TAB$dev//$id"
		IFS="$NL"
	done
	restore_ifs
	if [ -z "$parts" ]; then
		db_input critical partman-crypto/nothing_to_setup
		db_go || true
		return
	fi

	db_subst partman-crypto/create/partitions PARTITIONS "$parts"
	db_reset partman-crypto/create/partitions
	db_input critical partman-crypto/create/partitions
	db_go || return
	db_get partman-crypto/create/partitions
	if [ -z "$RET" ]; then
		db_input critical partman-crypto/create/nosel
		db_go || true
		return
	fi
	parts=$(echo "$RET" | sed -e "s/ *([^)]*) *//g; s/ *, */\\$NL/g")

	local newparts=
	local need_commit=
	IFS="$NL"
	for part in $parts; do
		for line in $pathmap; do
			restore_ifs
			if [ "${line%%$TAB*}" = "$part" ]; then
				local devid="${line#*$TAB}"
				local path
				if path="$(crypto_prepare "${devid%//*}" "${devid#*//}")"; then
					need_commit=true
				fi
				newparts="${newparts:+$newparts }$path"
				break
			fi
			IFS="$NL"
		done
		IFS="$NL"
	done
	restore_ifs
	parts="$newparts"

	if [ "$need_commit" ]; then
		confirm_changes partman-crypto || exit 0
		commit_changes partman-crypto/commit_failed || exit $?
	fi
}

confirm_changes partman-crypto || exit 0
commit_changes partman-crypto/commit_failed || exit $?

while :; do
	db_input critical partman-crypto/mainmenu
	db_go || exit 10
	db_get partman-crypto/mainmenu
	case $RET in
	    activate)	do_activate ;; # exits if any volumes were activated
	    create)	do_create ;;
	    finish)	break ;;
	    *)
		logger -t partman-crypto "Unknown selection '$RET'"
		break ;;
	esac
done

crypto_check_setup || exit 1

crypto_setup yes || exit 1
