#!/usr/local/bin/cbsd
#v11.1.12
MYARG="mode"
MYOPTARG="backupfile device file key"
MYDESC="cbsd geli helper"
ADDHELP="\
 backupfile= alternative path to backup for init for storing backup\n\
 device= path to device\n\
 file= path to image\n\
 key= path to master key file\n\
 mode= initmaster, init, attach, detach, info\n\
"
EXTHELP="wf_geli"

. ${subrdir}/nc.subr

. ${cbsdinit}

. ${system}
. ${mdtools}

readconf geli.conf

MASTER_FILE="${dbdir}/master_geli.img"
TRAP=""

change_passphrase()
{
	local _passfile _dev _pw="1" _npw
	local oldmodes=$( ${STTY_CMD} -g )

	[ -z "${1}" -o -z "${2}" ] && err 1 "${N1_COLOR}change_passphrase need for arguments${N0_COLOR}"
	_dev="${1}"
	_passfile="${2}"

	${ECHO} "${N1_COLOR}Enter passphrase for ${gelidev}: ${N0_COLOR}"

	while [ "${_pw}" != "${_npw}" ]; do
		printf "${BOLD}New Password: ${N0_COLOR}"
		${STTY_CMD} -echo
		read _pw
		printf "\n${BOLD}Retype New Password: ${N0_COLOR}"
		read _npw
		[ "${_pw}" = "${_npw}" ] || ${ECHO} "${N1_COLOR}Mismatch; try again, EOF to quit.${N0_COLOR}"
	done

	echo "${_pw}" > ${_passfile}
	${STTY_CMD} $oldmodes
	${GELI_CMD} init -J ${_passfile} ${_dev}
}

# $1 - path to dev/file
# printf md5-based key path
get_key()
{
	local _res _md5
	[ -z "${1}" ] && err 1 "${N1_COLOR}get_key need for arguments${N0_COLOR}"
	_res=$( echo $1 | ${SED_CMD} s:^${workdir}/::g )
	_md5=$( ${miscdir}/cbsd_md5 "${_res}" )
	printf "${gelidir}/${_md5}"
}


create_key()
{
	local _res _dev _keypath TRAP="true;"

	_dev="${1}"
	[ -z "${1}" ] && err 1 "${N1_COLOR}create_key need for arguments${N0_COLOR}"
	[ ! -f "${1}" -a ! -c "${1}" ] && err 1 "${N1_COLOR}No such device or file:${N2_COLOR} ${1}${N0_COLOR}"

	# if resource is file, mdconfiging them and work with it as device
	if [ -f "${1}" ]; then
		_res=$( eval find_md_by_img ${1} )
		if [ -n "${_res}" ]; then
			_md="${_res}"
		else
			_md=$( ${MDCONFIG_CMD} -a -t vnode -f ${file} )
			[ $? -ne 0 ] && err 1 "${N1_COLOR}Error for: ${N2_COLOR}${MDCONFIG_CMD} -a -t vnode -f ${file}${N0_COLOR}"
			TRAP="${TRAP} ${MDCONFIG_CMD} -d -u ${_md};"
			trap "${TRAP}" HUP INT ABRT BUS TERM EXIT
		fi
		_dev="/dev/${_md}"
		#remove workdir part in path if exist
		_keypath=$( get_key "${1}" )
	else
		_keypath=$( get_key "${_dev}" )
	fi

	if [ ! -r "${_keypath}" ]; then
		trap "${TRAP} mountmaster_file \"ro\";" HUP INT ABRT BUS TERM EXIT
		mountmaster_file "rw"
		gelidev="${_dev}"
		change_passphrase ${_dev} ${_keypath}
	fi

	trap "" HUP INT ABRT BUS TERM EXIT
}

# geli init. if $1 is empty ${device} variable will be used
init_device()
{
	local _res _arg _tst _device
	printf "${BOLD}"
	_arg="-s 4096 -K ${key} -e ${ealgo}"

	if [ -n "${1}" ]; then
		_device=${1}
	else
		_device=${device}
	fi

	[ ! -c "${_device}" ] && err 1 "${N1_COLOR}No such or unaccesible/non block special device: ${N2_COLOR}${_device}${N0_COLOR}"

	if [ -n "${backupfile}" ]; then
		_arg="${_arg} -B ${backupfile}"
		_tst=$( dirname ${backupfile} )
		[ ! -d "${_tst}" ] && ${MKDIR_CMD} -p ${_tst}
	fi

	_res=$( ${GELI_CMD} init ${_arg} ${_device} 2>&1)

	printf "${N0_COLOR}"
	[ $? -ne 0 ] && err 1 "${N1_COLOR}error: ${N0_COLOR}${_res}"
	[ -n "${_res}" ] && err 0 "${N1_COLOR}${_res}${N0_COLOR}"
}

attach_file()
{
	local _res _md _keyfile

	# not zvol?
	if [ -f "${file}" ]; then
		_res=$( eval find_md_by_img ${file} )

		if [ -n "${_res}" ]; then
			_md="${_res}"
		else
			_md=$( ${MDCONFIG_CMD} -a -t vnode -f ${file} )
		fi
	fi

	_keyfile=$( get_key "${file}" )

	# not zvol?
	if [ -f "${file}" ]; then
		attach_device "/dev/${_md}" "${_keyfile}"
	else
		attach_device "${file}" "${_keyfile}"
	fi
}

info_file()
{
	local _res _md
	_res=$( eval find_md_by_img ${file} )

	if [ -n "${_res}" ]; then
		_md="${_res}"
	else
		_md=$( ${MDCONFIG_CMD} -a -t vnode -f ${file} )
		trap "${MDCONFIG_CMD} -d -u ${_md}" HUP INT ABRT BUS TERM EXIT
	fi

	info_device /dev/${_md}
}


init_file()
{
	local _res _md _file _dev

	if [ -n "${1}" ]; then
		_file=$1
	else
		_file=${file}
	fi

	_res=$( eval find_md_by_img ${_file} )

	mountmaster_file "rw"

	if [ -n "${_res}" ]; then
		_md="${_res}"
	else
		_md=$( ${MDCONFIG_CMD} -a -t vnode -f ${_file} )
		trap "${MDCONFIG_CMD} -d -u ${_md}" HUP INT ABRT BUS TERM EXIT
	fi

	if [ -c "${_md}.eli" ]; then
		echo "${_md}.eli"
		return 0
	fi

	_dev=$( init_device /dev/${_md} )
	[ $? -eq 0 ] && echo "${_dev}"
}


mdattach_file()
{
	local _res _md _file _dev

	if [ -n "${1}" ]; then
		_file=$1
	else
		_file=${file}
	fi
	_res=$( eval find_md_by_img ${_file} )

	if [ -n "${_res}" ]; then
		_md="${_res}"
	else
		_md=$( ${MDCONFIG_CMD} -a -t vnode -f ${_file} )
	fi

	echo ${_md}
	return 0
}


detach_file()
{
	local _res _md
	_res=$( eval find_md_by_img ${file} )

	[ -z "${_res}" ] && err 1 "${N1_COLOR}${file} not attached${N0_COLOR}"

	if [ -n "${_res}" ]; then
		_md="${_res}"
	else
		_md=$( ${MDCONFIG_CMD} -a -t vnode -f ${file} )
	fi

	detach_device /dev/${_md}
}

# $1 - path to dev
# $2 - path to key
attach_device()
{
	local _res _device _keyfile

	[ -z "${1}" -o -z "${2}" ] && err 1 "${N1_COLOR}attach_device need for arg1 arg2${N0_COLOR}"

	_device="${1}"
	_keyfile="${2}"

	if [ -c "${_device}.eli" ]; then
		err 0 "${N2_COLOR}${_device}.eli${N0_COLOR}"
	fi
	printf "${BOLD}"
	_res=$( ${GELI_CMD} attach -j ${_keyfile} ${_device} 2>&1)
	[ $? -ne 0 ] && err 1 "${N1_COLOR}error: ${N0_COLOR}${_res}"
	printf "${N0_COLOR}"
	[ -c "${device}.eli" ] && err 0 "${device}.eli"
	err 0 "${N2_COLOR}${_device}.eli${N0_COLOR}"
}

info_device()
{
	local _res _device

	if [ -n "${1}" ]; then
		_device="${1}"
	else
		_device="${device}"
	fi

	[ -c "${_device}.eli" ] && err 0 "${N2_COLOR}${_device}.eli${N0_COLOR}"
	err 1 "${N1_COLOR}No geli${N0_COLOR}"
}


detach_device()
{
	local _res _device

	if [ -n "${1}" ]; then
		_device="${1}"
	else
		_device="${device}"
	fi
	[ ! -c "${_device}.eli" ] && err 0 "${N1_COLOR}geli: ${N1_COLOR}Device not attached: ${N2_COLOR}${_device}.eli${N0_COLOR}"
	_res=$( ${GELI_CMD} detach ${_device}.eli 2>&1)
	[ $? -ne 0 ] && err 1 "${N1_COLOR}error: ${N0_COLOR}${_res}"
}

check_gelidir()
{
	[ ! -d "${gelidir}" -o ! -r "${MASTER_FILE}" ] && err 1 "${N1_COLOR}Geli dir ${N2_COLOR}${gelidir}${N1_COLOR} is not initialized. Please use: ${N2_COLOR}cbsd geli mode=initmaster${N1_COLOR} as first step${N0_COLOR}"
	is_mounted ${gelidir} && return 0
	err 1 "${N1_COLOR}Geli dir ${N2_COLOR}${gelidir}${N1_COLOR} is not mounted. Please use: ${N2_COLOR}cbsd geli mode=initmaster${N1_COLOR} as first step${N0_COLOR}"
}

initmaster_attach_file()
{
	local _res
	[ -z "${1}" ] && return 1

	${ECHO} "${N1_COLOR}Attaching geli base image. Please use master password.${N0_COLOR}"
	_res=$( ${GELI_CMD} attach ${1} 2>&1 )
	[ ! -c "${1}.eli" ] && err 1 "${N1_COLOR}Not attached: ${_res}${N0_COLOR}"
}


initmaster_file()
{
	local _md _res

	[ -f "${MASTER_FILE}" ] && return 0
	TRAP="${RM_CMD} -f ${MASTER_FILE};"
	trap "${TRAP}" HUP INT ABRT BUS TERM EXIT
	${TRUNCATE_CMD} -s ${master_size} ${MASTER_FILE}
	${CHMOD_CMD} 0600 ${MASTER_FILE}
	_md=$( ${MDCONFIG_CMD} -a -t vnode -f ${MASTER_FILE} )
	TRAP="${MDCONFIG_CMD} -d -u ${_md}; ${TRAP}"
	trap "${TRAP}" HUP INT ABRT BUS TERM EXIT
	$ECHO "${BOLD}Initialization. Please set master password for geli base image${N0_COLOR}"
	_res=$( ${GELI_CMD} init -s 4096 /dev/${_md} 2>&1 )

	initmaster_attach_file /dev/${_md}

	TRAP="${GELI_CMD} detach /dev/${_md}.eli; ${TRAP}"
	trap "${TRAP}" HUP INT ABRT BUS TERM EXIT
	# now create filesystem on the image
	# Linux does not support postfix in bs=, e.g. bs=128k
	case "${platform}" in
		Linux)
			${DD_CMD} if=/dev/random of=/dev/${_md}.eli bs=128000 >/dev/null 2>&1 || true  ## << short write on character device exit with 1 err
			;;
		*)
			${DD_CMD} if=/dev/random of=/dev/${_md}.eli bs=128k >/dev/null 2>&1 || true  ## << short write on character device exit with 1 err
			;;
	esac
	${NEWFS_CMD} -U -m0 -n /dev/${_md}.eli >/dev/null 2>&1
	trap "" HUP INT ABRT BUS TERM EXIT
	#    # detach
	#    ${GELI_CMD} detach /dev/${_md}.eli
	#    ${MDCONFIG_CMD} -d -u ${_md}
	${ECHO} "${N1_COLOR}Init complete: ${N2_COLOR}${MASTER_FILE}${N0_COLOR}"
}

initmaster()
{
	[ ! -d "${gelidir}" -o ! -r "${MASTER_FILE}" ] && err 1 "${N1_COLOR}Geli dir ${N2_COLOR}${gelidir}${N1_COLOR} is not initialized. Please use: ${N2_COLOR}cbsd geli mode=initmaster${N1_COLOR} as first step${N0_COLOR}"
	[ "$( ${STAT_CMD} -f %Op ${gelidir} )" != "40600" ] && ${CHMOD_CMD} 0600 ${gelidir} && ${ECHO} "${N1_COLOR}Fixed permission for ${gelidir}${N0_COLOR}"
	[ "$( ${STAT_CMD} -f %Op ${MASTER_FILE} )" != "100600" ] && ${CHMOD_CMD} 0600 ${MASTER_FILE} && ${ECHO} "${N1_COLOR}Fixed permission for ${MASTER_FILE}${N0_COLOR}"
}

# $1 - mode
mountmaster_file()
{
	local _mode _mounted _res _md
	[ -z "${1}" ] && err 1 "usage: mountmaster_file mode"
	_mode="$1"
	[ "${_mode}" != "ro" -a "${_mode}" != "rw" ] && err 1 "${N1_COLOR}Please set ${N2_COLOR}'ro' ${N1_COLOR}or ${N2_COLOR}'rw'${N0_COLOR}"

	_md=$( mdattach_file ${MASTER_FILE} )

	if is_mounted ${gelidir}; then
		# test for read/write
		_res=$( ${MKTEMP_CMD} -q ${gelidir}/XXXXX )
		if [ $? -eq 0 ]; then
			_mounted="rw"
			${RM_CMD} -f ${_res}
		else
			_mounted="ro"
		fi
		[ "${_mounted}" = "${_mode}" ] && return 0
		# we need for re-mount in new mode
		${UMOUNT_CMD} ${gelidir}
	fi

	[ ! -c "/dev/${_md}.eli" ] && initmaster_attach_file /dev/${_md}

	if [ ! -c "/dev/${_md}.eli" ]; then
		${MDCONFIG_CMD} -d -u ${_md}
		err 1 "${N1_COLOR}No eli initialized for ${N2_COLOR}${MASTER_FILE}${N0_COLOR}"
	fi

	${MOUNT_CMD} -o${_mode} /dev/${_md}.eli ${gelidir}
}


# MAIN
[ ! -d "${gelidir}" ] && ${MKDIR_CMD} -m 0600 ${gelidir}

case "${mode}" in
	init)
		check_gelidir
		# [ -z "${key}" ] && err 1 "${N1_COLOR}Please specify ${N2_COLOR}key=${N0_COLOR}"
		if [ -n "${device}" ]; then
			create_key ${device}
			attach_file ${device}
			# init_device
		elif [ -n "${file}" ]; then
			create_key ${file}
			attach_file ${file}
		# init_file
		else
			err 1 "${N1_COLOR}Please specify ${N2_COLOR}file= ${N1_COLOR}or ${N2_COLOR}device=${N0_COLOR}"
		fi
		;;
	initmaster)
		initmaster_file
		mountmaster_file "ro"
		;;
	attach)
		check_gelidir
		[ -z "${key}" ] && err 1 "${N1_COLOR}Please specify ${N2_COLOR}key=${N0_COLOR}"
		[ ! -r "${key}" ] && err 1 "${N1_COLOR}No such key or file is unreadable: ${N2_COLOR}${key}${N0_COLOR}"
		if [ -n "${device}" ]; then
			attach_device
		elif [ -n "${file}" ]; then
			attach_file
		else
			err 1 "${N1_COLOR}Please specify ${N2_COLOR}file= ${N1_COLOR}or ${N2_COLOR}device=${N0_COLOR}"
		fi
		;;
	detach)
		if [ -n "${device}" ]; then
			detach_device
		elif [ -n "${file}" ]; then
			detach_file
		else
			err 1 "${N1_COLOR}Please specify ${N2_COLOR}file= ${N1_COLOR}or ${N2_COLOR}device=${N0_COLOR}"
		fi
		;;
	info)
		if [ -n "${device}" ]; then
			info_device
		elif [ -n "${file}" ]; then
			info_file
		else
			err 1 "${N1_COLOR}Please specify ${N2_COLOR}file= ${N1_COLOR}or ${N2_COLOR}device=${N0_COLOR}"
		fi
		;;
	list)
		if [ ! -r ${MASTER_FILE} ]; then
			${ECHO} "${N1_COLOR}${CBSD_APP}: master file not initialized yed: ${N2_COLOR}${MASTER_FILE}${N0_COLOR}"
			${ECHO} "${N1_COLOR}${CBSD_APP}: please use for init masterfile: ${N2_COLOR}cbsd geli mode=initmaster${N0_COLOR}"
			exit 1
		fi
		is_mounted ${gelidir} && err 0 "${N1_COLOR}${CBSD_APP}: ${MASTER_FILE} mounted: ${N2_COLOR}${gelidir}${N0_COLOR}"
		err 1 "${N1_COLOR}${CBSD_APP}: ${MASTER_FILE} not mounted${N0_COLOR}"
		;;
	*)
		err 1 "${N1_COLOR}Unknown mode${N0_COLOR}"
		;;
esac

exit 0
