#!/usr/local/bin/cbsd
#v13.0.0
MYARG="jroot fstab"
MYOPTARG="jname"
MYDESC="Mount jail by fstab file"
ADDHELP="\
 fstab - fstab file\n\
 jroot - root of jail\n\
"

. ${subrdir}/nc.subr
. ${cbsdinit}
. ${system}

[ ! -f "${fstab}" ] && err 1 "${fstab} does not exist"
[ ! -d "${jroot}" ] && err 1 "${jroot} does not exist"

# local fstab exist ?
have_local=0

# special sign for zfs attach
with_zfs_attach="${jailsysdir}/${jname}/with_zfs_attach"

init_localmpt_fstab()
{
	local _ret=
	[ ! -f "${fstab}.local" ] && return 0

	have_local=1

	local i=0

	# check for CRLF at the end of file
	_ret=$( ${TAIL_CMD} -n1 "${fstab}.local" )

	if [ -n "${_ret}" ]; then
		# append CRLF for proper while read loop
		printf '\n' >> "${fstab}.local"
	fi

	# always rewrite CBSDROOT first
	replacewdir file0="${fstab}.local" old="CBSDROOT"

	eval $( ${CAT_CMD} ${fstab}.local | while read _device _mountpt _fs _mode _a _b; do

		[ -z "${_mountpt}" ] && continue

		case ":${_device}" in
			:#* | :)
				continue
			;;
			*)
				case "${_fs}" in
					external)
						if [ -x "${_device}" ]; then
							echo "local_mpt${i}=\"${_mountpt}\""
							i=$(( i + 1 ))
						fi
						;;
					zfs)
						if [ ${allow_zfs} -ne 1 ]; then
							# allow_zfs not enabled, skipp
							continue
						fi
						# we need special route for jails with zfs attach, make sign for jstart script
						${TOUCH_CMD} ${with_zfs_attach}
						continue
						;;
					*)
						echo "local_mpt${i}=\"${_mountpt}\""
						i=$(( i + 1 ))
						;;
				esac
			;;
		esac
	done )
}

# return 0 if no such mount point in fstab.local exist
# return 1 if exist
# $1 - test mpt
# example:
# if ! check_for_localmpt_fstab /tmp; then
#		echo "EXIST"
# fi
check_for_localmpt_fstab()
{
	local _res=0 i local_mpt

	[ -z "${1}" ] && return 0

	for i in $( ${SEQ_CMD} 0 128 ); do
		eval local_mpt="\$local_mpt${i}"
		[ -z "${local_mpt}" ] && break
		[ "${local_mpt}" = "${1}" ] && _res=1 && break
	done

	return ${_res}
}

## MAIN
# check for CRLF at the end of file
[ ! -r ${fstab} ] && err 1 "${N1_COLOR}no such fstab file: ${N2_COLOR}${fstab}${N0_COLOR}"
_ret=$( ${TAIL_CMD} -c1 "${fstab}" )

if [ -n "${_ret}" ]; then
	# append CRLF for proper while read loop
	printf '\n' >> "${fstab}"
fi

init_localmpt_fstab

# always rewrite CBSDROOT first
replacewdir file0="${fstab}" old="CBSDROOT"

# first pass: test for fstab is valid
${CAT_CMD} ${fstab} | while read _device _mountpt _fs _mode _a _b; do
	case ":${_device}" in
		:#* | :)
			continue
			;;
		*)
			case "${_fs}" in
				external)
					if [ ! -x "${_device}" ]; then
						err 1 "${W1_COLOR}${CBSD_APP} error: ${N1_COLOR}external script not found or not executable, check fstabs: ${N2_COLOR}${_device}${N0_COLOR}"
						continue
					fi
					;;
				geli)
					is_mounted ${gelidir} && continue
					err 1 "Error: ${gelidir} is not initializated. Please use: 'cbsd geli mode=initmaster' first"
					;;
				zfs)
					if [ ${allow_zfs} -ne 1 ]; then
						${ECHO} "${W1_COLOR}Warning: ${N1_COLOR}mountfstab: jail has a zfs dataset:: ${N2_COLOR}${_device}${N0_COLOR}"
						${ECHO} "${W1_COLOR}Warning: ${N1_COLOR}mountfstab: but allow_zfs options is not enabled. please use: ${N2_COLOR}cbsd jset jname=${jname} allow_zfs=1${N0_COLOR}"
						continue
					fi
					# we need special route for jails with zfs attach, make sign for jstart script
					${TOUCH_CMD} ${with_zfs_attach}
					continue
					;;
				*)
					[ -z "${_device}" ] && continue
					# append workdir if it not full path
					prefix=$( substr --pos=0 --len=1 --str=${_device} )
					[ "${prefix}" != "/" ] && _device="${workdir}/${_device}"
					;;
			esac
			;;
	esac
done

${CAT_CMD} ${fstab} | while read _device _mountpt _fs _mode _a _b; do
	[ "${_fs}" = "zfs" ] && continue
	case ":${_device}" in
		:#* | :)
			continue
			;;
	esac

	# SKIP mount when mount point also exist in fstab.local. fstab.local is preferred
	if [ ${have_local} -eq 1 ]; then
		if ! check_for_localmpt_fstab ${_mountpt}; then
			continue
		fi
	fi

	mnt=0
	prefix=$( substr --pos=0 --len=1 --str=${_device} )

	if [ "${_fs}" = "geli" ]; then
		# prepare src part
		if [ "${prefix}" != "/" ]; then
			if [ -z "${jname}" ]; then
				_device="${workdir}/${_device}"
			else
				_device="${jailsysdir}/${jname}/${_device}"
			fi
		fi
		attachgeli file="${_device}" dst="${jroot}${_mountpt}" mode="${_mode}"
		continue
	fi

	if [ "${_fs}" = "external" ]; then
		${_device} -p ${jroot}${_mountpt} -o ${_mode} ${jname}
		ret=$?
		[ ${ret} -ne 0 ] && ${ECHO} "${W1_COLOR}${CBSD_APP} warning: ${N1_COLOR}unable to create mountpoint via external ${_device}: ${N2_COLOR}${jroot}${_mountpt}${N0_COLOR}"
		continue
	fi

	if [ "${_fs}" = "nullfs" ]; then
		if [ "${prefix}" != "/" ]; then
			_device="${workdir}/${_device}"
		else
			if ! [ -d ${_device} -o -c ${_device} -o -b ${_device} ]; then
				${ECHO} "${W1_COLOR}${CBSD_APP} warning: ${N1_COLOR}mountfstab: no such source directory for jail ${jname}: ${N2_COLOR}${_device}${N0_COLOR}"
				continue
			fi
		fi
	fi

	if is_mounted "${jroot}${_mountpt}"; then
		echo "${jroot}${_mountpt} already mounted" 1>&2
		mnt=1
	fi

	[ ${mnt} -eq 1 ] && continue

	# /usr/compat - work-around for migrate from /usr/compat -> /compat introduced in CBSD 11.0.16
	[ "${_mountpt}" = "/usr/compat" ] && _mountpt="/compat"

	if [ ! -d "${jroot}${_mountpt}" ]; then
		${MKDIR_CMD} -p "${jroot}${_mountpt}"
		_ret=$?
		if [ ${_ret} -ne 0 ]; then
			${ECHO} "${W1_COLOR}${CBSD_APP} warning: ${N1_COLOR}mountfstab: unable to create mountpoint for ${_fs} ${_device}: ${N2_COLOR}${jroot}${_mountpt}${N0_COLOR}"
			${ECHO} "${W1_COLOR}${CBSD_APP} warning: ${N1_COLOR}mountfstab: read-only location? Skip mount for: ${N2_COLOR}${jroot}${_mountpt}${N0_COLOR}"
			continue
		fi
	fi

	${MOUNT_CMD} -t ${_fs} -o${_mode} ${_device} ${jroot}${_mountpt}
done

exit 0
