#!/bin/sh -efu

# Defaults
min_target_size=100G
max_target_size=545G
protected_mpoints="/ /image"

# This is for EVMS-based installers only
[ -s /etc/evms.conf ] || exit 0

# TARGETSIZE=<min>-<max> was allowed on boot
if grep -qws TARGETSIZE= /proc/cmdline; then
	for x in "$(cat /proc/cmdline)"; do
		case "$x" in
		TARGETSIZE=*-*)
			x="${x##TARGETSIZE=}"
			min_target_size="${x%-*}"
			max_target_size="${x##*-}"
			break
			;;
		esac
	done
fi


fatal() {
	echo "FATAL in $*" >&2
	exit 1
}

in_array() {
	local needle="$1"; shift

	while [ $# -gt 0 ]; do
		[ "x$needle" != "x$1" ] ||
			return 0
		shift
	done

	return 1
}

human2size() {
	local input="$1" rv=
	local slen="${#input}"
	slen="$(($slen - 1))"
	local data="${input:0:$slen}"
	local lchar="${input:$slen:1}"

	case "$lchar" in
	[0-9])	rv="$input";;
	K)	rv="$(( $data * 1024 ))";;
	M)	rv="$(( $data * 1024 * 1024 ))";;
	G)	rv="$(( $data * 1024 * 1024 * 1024 ))";;
	T)	rv="$(( $data * 1024 * 1024 * 1024 * 1024 ))";;
	esac

	[ -n "$rv" ] && [ "$rv" -gt 0 ] 2>/dev/null ||
		fatal "human2size(): Invalid argument: '$1'."
	echo -n "$rv"
}

get_whole_disk() {
	local varname="$1" partdev="$2"
	local number sysfs partn whole=""

	number="$(mountpoint -x -- "$partdev")"
	sysfs="$(readlink -fv -- "/sys/dev/block/$number")"

	if [ -r "$sysfs/partition" ]; then
		read -r partn <"$sysfs/partition" ||
			partn=
		if [ -n "$partn" ]; then
			case "$partdev" in
			*[0-9]p$partn)
				whole="${partdev%%p$partn}"
				;;
			*$partn)
				whole="${partdev%%$partn}"
				;;
			esac
		fi
		[ -n "$whole" ] && [ -b "$whole" ] &&
		[ -r "/sys/block/${whole##/dev/}/${partdev##/dev/}/dev" ] ||
			whole=
	fi

	[ -z "$whole" ] || eval "$varname=\"$whole\""
}

get_disk_size() {
	local dname="$1" nblocks disksize
	local sysfs="/sys/block/${dname##/dev/}"

	if [ ! -s "$sysfs/size" ]; then
		disksize="$(blockdev --getsize64 -- "/dev/${dname##/dev/}")"
	else
		read -r nblocks <"$sysfs/size" ||:
		disksize=$((512 * ${nblocks:-0}))
	fi

	echo -n "$disksize"
}

protect_boot_devices() {
	local number sysfs mp pdev wdev

	protected_devices=

	for mp in $protected_mpoints; do
		mountpoint -q -- "$mp" ||
			continue
		number="$(mountpoint -d -- "$mp")"
		sysfs="$(readlink -fv -- "/sys/dev/block/$number")"
		pdev="$(grep -sE ^DEVNAME= "$sysfs"/uevent |cut -f2 -d=)"
		[ -n "$pdev" ] ||
			continue
		pdev="/dev/$pdev"
		[ -b "$pdev" ] ||
			continue
		wdev="$pdev"
		get_whole_disk wdev "$pdev"
		in_array "$wdev" $protected_devices ||
			protected_devices="$protected_devices $wdev"
	done
}

search_target() {
	local dev msz mxs dsz cnt=0

	target=
	msz="$(human2size "$min_target_size")"
	mxz="$(human2size "$max_target_size")"

	for dev in $(ls /sys/block/) _; do
		[ "$dev" != _ ] ||
			continue
		[ -b "/dev/$dev" ] ||
			continue
		case "$dev" in
		loop[0-9]*|ram[0-9]*|sr[0-9]*)
			continue
			;;
		esac
		! in_array "/dev/$dev" $protected_devices ||
			continue
		dsz="$(get_disk_size "$dev")"
		[ "$dsz" -ge "$msz" ] 2>/dev/null ||
			continue
		[ "$dsz" -le "$mxz" ] 2>/dev/null ||
			continue
		cnt=$((1 + $cnt))
		target="/dev/$dev"
	done

	if [ $cnt -eq 0 ]; then
		fatal "search_target(): target device not found."
	elif [ $cnt -gt 1 ]; then
		fatal "search_target(): more one target devices found."
	fi
}

hint_evms() {
	local dev exclude=

	for dev in $(ls /sys/block/) _; do
		[ "$dev" != _ ] ||
			continue
		[ -b "/dev/$dev" ] ||
			continue
		case "$dev" in
		loop[0-9]*|ram[0-9]*|sr[0-9]*)
			continue
			;;
		esac
		[ "$target" != "/dev/$dev" ] ||
			continue
		! in_array "/dev/$dev" $protected_devices ||
			continue
		exclude="$exclude $dev*"
	done

	if [ -n "$exclude" ]; then
		sed -i "0,/^sysfs_devices /{p;d}; \
			s/^\([[:space:]]*exclude = \[\)/\1$exclude/" \
			/etc/evms.conf ||:
	fi
}


# Try to guess single target device
protect_boot_devices && search_target

# Disable to use another whole disk drives
hint_evms
