# Local filesystem mounting			-*- shell-script -*-

local_top()
{
	if [ "${local_top_used}" != "yes" ]; then
		[ "${quiet?}" != "y" ] && log_begin_msg "Running /scripts/local-top"
		run_scripts /scripts/local-top
		[ "$quiet" != "y" ] && log_end_msg
	fi
	local_top_used=yes

	# Start time for measuring elapsed time in local_device_setup
	if [ -z "${local_top_time}" ]; then
		local_top_time="$(cat /proc/uptime)"
		local_top_time="${local_top_time%%[. ]*}"
		local_top_time=$((local_top_time + 1)) # round up
		export local_top_time
	fi
}

local_block()
{
	[ "${quiet?}" != "y" ] && log_begin_msg "Running /scripts/local-block"
	run_scripts /scripts/local-block "$@"
	[ "$quiet" != "y" ] && log_end_msg
}

local_premount()
{
	if [ "${local_premount_used}" != "yes" ]; then
		[ "${quiet?}" != "y" ] && log_begin_msg "Running /scripts/local-premount"
		run_scripts /scripts/local-premount
		[ "$quiet" != "y" ] && log_end_msg
	fi
	local_premount_used=yes
}

local_bottom()
{
	if [ "${local_premount_used}" = "yes" ] || [ "${local_top_used}" = "yes" ]; then
		[ "${quiet?}" != "y" ] && log_begin_msg "Running /scripts/local-bottom"
		run_scripts /scripts/local-bottom
		[ "$quiet" != "y" ] && log_end_msg
	fi
	local_premount_used=no
	local_top_used=no
	unset local_top_time
}

# $1=device ID to mount
# $2=optionname (for root and etc)
# $3=panic if device is missing (true or false, default: true)
# Sets $DEV to the resolved device node
local_device_setup()
{
	local dev_id="$1"
	local name="$2"
	local may_panic="${3:-true}"
	local real_dev
	local time_elapsed
	local count

	# If wait-for-root understands this prefix, then use it to wait for
	# the device rather than settling the whole of udev.

	# Timeout is max(30, rootdelay) seconds (approximately)
	local slumber=30
	case $DPKG_ARCH in
		powerpc|ppc64|ppc64el)
			slumber=180
			;;
		*)
			slumber=30
			;;
	esac
	if [ "${ROOTDELAY:-0}" -gt $slumber ]; then
		slumber=$ROOTDELAY
	fi

	case "$dev_id" in
	UUID=*|LABEL=*|PARTUUID=*|/dev/*)
		FSTYPE=$( wait-for-root "$dev_id" "$slumber" )
		;;
	*)
		wait_for_udev 10
		;;
	esac

	# Load ubi with the correct MTD partition and return since fstype
	# doesn't work with a char device like ubi.
	if [ -n "$UBIMTD" ]; then
		modprobe ubi "mtd=$UBIMTD"
		DEV="${dev_id}"
		return
	fi

	# Don't wait for a device that doesn't have a corresponding
	# device in /dev and isn't resolvable by blkid (e.g. mtd0)
	if [ "${dev_id#/dev}" = "${dev_id}" ] &&
	   [ "${dev_id#*=}" = "${dev_id}" ]; then
		DEV="${dev_id}"
		echo "give out the dev id string ${dev_id}"
		return
	fi

	# If the root device hasn't shown up yet, give it a little while
	# to allow for asynchronous device discovery (e.g. USB).  We
	# also need to keep invoking the local-block scripts in case
	# there are devices stacked on top of those.
	#
	# in Ubuntu, we should never actually enter this loop as wait-for-root
	# above should have waited until the device appeared.
	if ! real_dev=$(resolve_device "${dev_id}") ||
	   ! get_fstype "${real_dev}" >/dev/null; then
		log_begin_msg "Waiting for ${name}"

		while true; do
			sleep 1
			time_elapsed="$(cat /proc/uptime)"
			time_elapsed="${time_elapsed%%[. ]*}"
			time_elapsed=$((time_elapsed - local_top_time))

			local_block "${dev_id}"

			# If mdadm's local-block script counts the
			# number of times it is run, make sure to
			# run it the expected number of times.
			while true; do
				if [ -f /run/count.mdadm.initrd ]; then
					count="$(cat /run/count.mdadm.initrd)"
				elif [ -n "${count}" ]; then
					# mdadm script deleted it; put it back
					count=$((count + 1))
					echo "${count}" >/run/count.mdadm.initrd
				else
					break
				fi
				if [ ${count} -ge ${time_elapsed} ]; then
					break;
				fi
				/scripts/local-block/mdadm "${dev_id}"
			done

			if real_dev=$(resolve_device "${dev_id}") &&
			   get_fstype "${real_dev}" >/dev/null; then
				wait_for_udev 10
				log_end_msg 0
				break
			fi
			if [ ${time_elapsed} -ge "${slumber}" ]; then
				log_end_msg 1 || true
				break
			fi
		done
	fi

	# We've given up, but we'll let the user fix matters if they can
	while ! real_dev=$(resolve_device "${dev_id}") ||
	      ! get_fstype "${real_dev}" >/dev/null; do
		if ! $may_panic; then
			echo "Gave up waiting for ${name}"
			return 1
		fi
		echo "Gave up waiting for ${name} device.  Common problems:"
		echo " - Boot args (cat /proc/cmdline)"
		echo "   - Check rootdelay= (did the system wait long enough?)"
		if [ "${name}" = root ]; then
			echo "   - Check root= (did the system wait for the right device?)"
		fi
		echo " - Missing modules (cat /proc/modules; ls /dev)"
		panic "ALERT!  ${dev_id} does not exist.  Dropping to a shell!"
	done

	DEV="${real_dev}"
}

local_mount_root()
{
	local_top
	if [ -z "${ROOT}" ]; then
		panic "No root device specified. Boot arguments must include a root= parameter."
	fi

	echo "output the ROOT value: ${ROOT}"
	local_device_setup "${ROOT}" "root file system"
	ROOT="${DEV}"
	
	# Get the root filesystem type if not set
	if [ -z "${ROOTFSTYPE}" ] || [ "${ROOTFSTYPE}" = auto ]; then
		FSTYPE=$(get_fstype "${ROOT}")
	else
		FSTYPE=${ROOTFSTYPE}
	fi

	local_premount

	if [ "${readonly?}" = "y" ] && \
	   [ -z "$LOOP" ]; then
		roflag=-r
	else
		roflag=-w
	fi

	checkfs "${ROOT}" root "${FSTYPE}"

	# Mount root
	# shellcheck disable=SC2086
	mount ${roflag} ${FSTYPE:+-t "${FSTYPE}"} ${ROOTFLAGS} "${ROOT}" "${rootmnt?}"
	mountroot_status="$?"
	if [ "$LOOP" ]; then
		if [ "$mountroot_status" != 0 ]; then
			if [ "${FSTYPE}" = ntfs ] || [ "${FSTYPE}" = vfat ]
			then
				panic "
Could not mount the partition ${ROOT}.
This could also happen if the file system is not clean because of an operating
system crash, an interrupted boot process, an improper shutdown, or unplugging
of a removable device without first unmounting or ejecting it.  To fix this,
simply reboot into Windows, let it fully start, log in, run 'chkdsk /r', then
gracefully shut down and reboot back into Windows. After this you should be
able to reboot again and resume the installation.
(filesystem = ${FSTYPE}, error code = $mountroot_status)
"
			fi
		fi

		mkdir -p /host
		mount -o move "$rootmnt" /host

		while [ ! -e "/host/${LOOP#/}" ]; do
			panic "ALERT!  /host/${LOOP#/} does not exist.  Dropping to a shell!"
		done

		# Get the loop filesystem type if not set
		FSTYPE="$LOOPFSTYPE"
		if [ -z "$FSTYPE" ] || [ "$FSTYPE" = "unknown" ]; then
			FSTYPE=$(/sbin/blkid -s TYPE -o value "/host/${LOOP#/}")
			[ -z "$FSTYPE" ] && FSTYPE="unknown"
		fi

		if [ "$readonly" = y ]; then
			roflag=-r
		else
			roflag=-w
		fi

		# FIXME This has no error checking
		modprobe loop

		# FIXME This has no error checking
		# shellcheck disable=SC2086
		mount ${roflag} -o loop -t ${FSTYPE} ${LOOPFLAGS} "/host/${LOOP#/}" "${rootmnt?}"

		if [ -d "$rootmnt/host" ]; then
			mount -o move /host "$rootmnt/host"
		fi
	fi
}

local_mount_fs()
{
	read_fstab_entry "$1"

	local_device_setup "$MNT_FSNAME" "$1 file system"
	MNT_FSNAME="${DEV}"

	local_premount

	if [ "${readonly}" = "y" ]; then
		roflag=-r
	else
		roflag=-w
	fi

	if [ "$MNT_PASS" != 0 ]; then
		checkfs "$MNT_FSNAME" "$MNT_DIR" "${MNT_TYPE}"
	fi

	# Mount filesystem
	if ! mount ${roflag} -t "${MNT_TYPE}" -o "${MNT_OPTS}" "$MNT_FSNAME" "${rootmnt}${MNT_DIR}"; then
		panic "Failed to mount ${MNT_FSNAME} as $MNT_DIR file system."
	fi
}

check_abboot()
{
	#shcheck=${rootmnt}/bin/sh
	multiconf=${rootmnt}/.snapshot.conf
	basepath=${rootmnt}/SystemBase
	
	if [ -f "$multiconf" ] && [ -d "${basepath}" ]; then	
		abuuid=$(cat "$multiconf")
		abuuid=${abuuid##*=}
		if [ -z "$abuuid" ]; then
			echo "No multiboot uuid found"
			exec /bin/sh
		fi
		bootflag=1
	else
		echo "Multiboot boot file or directory not found!"
		echo "Normal boot start!"
		bootflag=0
	fi
}

local_mount_multi()
{
	multipartid=$(grep 'UUID' "$multiconf")
	echo "output the content in multiboot.conf file: $multipartid"
	mbdevid=${multipartid##*=}
	
	if [ -n "$mbdevid" ]; then
                echo "the uuid string of ab system is ${mbdevid}"
        else
                echo "UUID string not found!"
                exec /bin/sh
        fi

        partinfo=$(blkid | grep "${mbdevid}")
        if [ -n "$partinfo" ]; then
               echo "output the part information: $partinfo"
               multiroot=${partinfo%%:*}
               echo "print out the device: ${multiroot}"
        else
               echo "A/B partition is not available"
               exec /bin/sh
        fi
	abmnt=/mnt
	mount "${multiroot}" "${abmnt}"
}

get_param_values()
{
	proptconf=${abmnt}/.prop.conf
        if [ ! -e "$proptconf" ]; then
                echo "The property file of A/B system does not exist in ${multiroot}"
        fi

	SystemA=$(grep "UUID_A" "$proptconf")
        SystemB=$(grep "UUID_B" "$proptconf")
        rollback=$(grep "rollback" "$proptconf")
	
	activeA=$(grep "active_A" "$proptconf")
        activeB=$(grep "active_B" "$proptconf")
        bootA=$(grep "bootable_A" "$proptconf")
        bootB=$(grep "bootable_B" "$proptconf")
        successfulA=$(grep "successful_A" "$proptconf")
        successfulB=$(grep "successful_B" "$proptconf")
        
	SystemA=${SystemA##*=}
        SystemB=${SystemB##*=}
	rollback=${rollback##*=}

	activeA=${activeA##*=}
        activeB=${activeB##*=}
        bootA=${bootA##*=}
        bootB=${bootB##*=}
        successfulA=${successfulA##*=}
        successfulB=${successfulB##*=}

	echo "System A: $SystemA"
	echo "System B: $SystemB"
	echo "Rollback: $rollback"
	echo "Active A: $activeA"
        echo "Active B: $activeB"
        echo "Bootable_A: $bootA"
        echo "Bootable_B: $bootB"
        echo "Successful_A: $successfulA"
        echo "Successful_B: $successfulB"
}

modify_propconf()
{
	varname=$1
	varvalue=$2
	grepvar=$(grep "$varname" "$proptconf")
	echo "the value of $varname is $grepvar and the varvalue is $varvalue"
	sed -i "s/$grepvar/${grepvar%=*}=$varvalue/g" "$proptconf"
	grep "$varname" "$proptconf"
}

reset_property()
{
        sysindex=$1
        case "$sysindex" in
        "A")
                modify_propconf "active_A" "false"
                modify_propconf "bootable_A" "false"
                modify_propconf "successful_A" "false"
		;;
        "B")
                modify_propconf "active_B" "false"
                modify_propconf "bootable_B" "false"
                modify_propconf "successful_B" "false"
        	;;
	*)
                echo "unrecognized parameter!"
		;;
        esac
}

check_propconf()
{
        availA=-1
        availB=-1
        target=C
	
	if [ "$bootA" = "false" ] && [ "$bootB" = "false" ]; then
                echo "System A and system B are both unbootable, please check!"
                bootflag=-1
                return
        else
                if [ "$bootA" = "false" ]; then
                        echo "System A is unbootable!"
                        availA=-1
                elif [ "$bootA" = "true" ]; then
                        if [ "$activeA" = "true" ]; then
                                availA=1
                        else
                                availA=0
                        fi
		else
			echo "Unrecognized bootable_A value!"
                fi

                if [ "$bootB" = "false" ]; then
                        echo "System B is unbootable!"
                        availB=-1
                elif [ "$bootB" = "true" ]; then
                        if [ "$activeB" = "true" ]; then
                                availB=1
                        else
                                availB=0
                        fi
		else
			echo "Unrecognized bootable_B value!"
                fi
        fi
	
	if [ "$availA" != "-1" ] && [ "$availB" != "-1" ]; then
                if [ "$availA" = "0" ] && [ "$availB" = "0" ]; then
                        if [ "$successfulA" = "true" ] && [ "$successfulB" = "true" ]; then
                                target='A'
                                modify_propconf "active_A" "true"
                        elif [ "$successfulA" = "true" ] && [ "$successfulB" = "false" ]; then
                                target='A'
                                modify_propconf "active_A" "true"
                        elif [ "$successfulA" = "false" ] && [ "$successfulB" = "true" ]; then
                                target='B'
                                modify_propconf "active_B" "true"
                        else
                                target='A'
                                modify_propconf "active_A" "true"
                        fi
                elif [ "$availA" = "1" ] && [ "$availB" = "0" ]; then
                        target='A'
                elif [ "$availA" = "0" ] && [ "$availB" = "1" ]; then
                        target='B'
                elif [ "$availA" = "1" ] && [ "$availB" = "1" ]; then
                        if [ "$successfulA" = "true" ] && [ "$successfulB" = "true" ]; then
                                target='A'
                                modify_propconf "active_B" "false"
                        elif [ "$successfulA" = "true" ] && [ "$successfulB" = "false" ]; then
                                target='A'
                                modify_propconf "active_B" "false"
                        elif [ "$successfulA" = "false" ] && [ "$successfulB" = "true" ]; then
                                target='B'
                                modify_propconf "active_A" "false"
                        else
                                target='A'
                                modify_propconf "active_B" "false"
                        fi
                fi
        elif [ "$availA" = "-1" ] && [ "$availB" != "-1" ]; then
                target='B'
        elif [ "$availA" != "-1" ] && [ "$availB" = "-1" ]; then
                target='A'
        fi

        boottarget="$target"
}

roll_back_check()
{
	if [ "$rollback" = "false" ]; then
		rollback="false"
                echo "No roll back"
		return
	fi

	rolldep=
	rollconf=
	depexist=
	confsuffix=".conf"
        case $boottarget in
        "A")
		rollconf="${abmnt}/$SystemA$confsuffix"                
                ;;
        "B")
                rollconf="${abmnt}/$SystemB$confsuffix"
                ;;
        *)
                rollback="false"
        	echo "no roll back! $rollback"
	        ;;
        esac 

        rolldep=$(grep "depend" "$rollconf")
        rolldep=${rolldep##*=}
        if [ -z "$rolldep" ]; then
                echo "No depend appointed in $rollconf"
		case $boottarget in
        	"A")
               		rollbackdir="${abmnt}/$SystemB"
               		;;
        	"B")
               		rollbackdir="${abmnt}/$SystemA"
               		;;
        	*)
               		rollback="false"
               		echo "Roll back failed!"
               		;;
        	esac
        else
                depexist="${abmnt}/$rolldep"	
                if [ ! -d "$depexist" ]; then
                        echo "Depend snapshot $rollconf not found!"
                        rollback="false"
                else
                        rollbackdir="${abmnt}/$rolldep"
                fi
        fi
}

check_multi_part()
{
	if [ -z "$SystemA" ] || [ -z "$SystemB" ]; then
                echo "System A or System B are not assigned here!"
		exec /bin/sh
        fi

	if [ "$rollback" = "true" ]; then
		echo "Roll back to snapshot $rollbackdir!"
		upperpath=${rollbackdir}
                upperconf=${rollbackdir}$confsuffix
	else
		case $boottarget in
        	"A")
        	        echo "mounting system A on overlay!"
			upperpath=${abmnt}/$SystemA
			upperconf=${abmnt}/$SystemA$confsuffix
			#reset_property "A"
        	        ;;
        	"B")
			echo "mounting system B on overlay!"
        	        upperpath=${abmnt}/$SystemB
			upperconf=${abmnt}/$SystemB$confsuffix
			#reset_property "B"
        	        ;;
        	*)
			echo "mount A/B system failed! Unknown choice!"
        	        exec /bin/sh
        	        ;;
        	esac
	fi

        workpath=${abmnt}/SystemWorker

        echo "System path: $upperpath"

        [ -d "${upperpath}" ] || mkdir -m 0755 "${upperpath}"
        [ -d "${workpath}" ] || mkdir -m 0755 "${workpath}"

        if [ ! -f "$upperconf" ]; then
                echo "Configuration file of $upperpath is missing!"
                touch "$upperconf"
        fi
}

chech_rm_snapshot()
{
	rmfile="${abmnt}/.rmlist"
	if [ -f "$rmfile" ]; then
		return
	fi

	while read -r line
	do
		echo "$line"
		snapitem="${abmnt}/${line##*:}"		
		rm -rf "$snapitem"
		rm "$snapitem$confsuffix"
	done < "$rmfile"

	rm "$rmfile"
}

check_snapshot()
{
	lower="${basepath}"
	dep="NULL"
	confstr="${upperpath}$confsuffix"
	
	while [ -n "$dep" ]
        do
                dep=$(grep "depend" "$confstr")
                dep=${dep##*=}
		if [ -z "$dep" ]; then
			echo "Depend not found!"
			echo "lower directory is ${lower}"
			break
		fi
		search="${abmnt}/${dep}"	
                if [ -d "${search}" ]; then
                        lower="${search}:${lower}"
                        confstr="${search}$confsuffix"
		else
			echo "lower directory is ${lower}"
			break
                fi
        done
        #echo "lower directory is ${lower}"
}

check_abboot()
{
        shcheck=${rootmnt}/bin/sh
        multiconf=${rootmnt}/.multiboot.conf
        basepath=${rootmnt}/SystemBase

        if [ -f "$shcheck" ]; then
                bootflag=0
        else
                if [ -f "$multiconf" ] && [ -d "${basepath}" ]; then
                        abuuid=$(cat "$multiconf")
                        abuuid=${abuuid##*=}
                        if [ -z "$abuuid" ]; then
                                echo "No multiboot uuid found"
                        fi
                        bootflag=1
                else
                        echo "file exists"
                        bootflag=-1
                        if [ ! -d "$basepath" ]; then
                                echo "Base system folder is not available!"
                        fi

                        if [ ! -e "$multiconf" ]; then
                                echo "Cannot find the UUID of A/B system"
                        fi
                fi
        fi
}

local_mount_ab()
{
	case $bootflag in
        "-1")
                echo "System booting failed, please check!"
                exec /bin/sh
                return
                ;;
        "0")
                echo "Booting normal system!"
                return
                ;;
        "1")	
		local_mount_multi
		get_param_values
		check_propconf
		roll_back_check
		check_multi_part
		chech_rm_snapshot	
		check_snapshot
			
		rootmnt=/abroot
        	mount -t overlay overlay -o lowerdir="${lower}",upperdir="${upperpath}",workdir="${workpath}" "${rootmnt}"
                echo "Booting A/B system!"
                ;;
	*)
		echo "Wrong system configuration!"
		;;
        esac
}

mountroot()
{
	local_mount_root
	check_abboot
	local_mount_ab
	#sleep 20
}

mount_top()
{
	# Note, also called directly in case it's overridden.
	local_top
}

mount_premount()
{
	# Note, also called directly in case it's overridden.
	local_premount
}

mount_bottom()
{
	# Note, also called directly in case it's overridden.
	local_bottom
}
