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

_log_msg() {
	if [ "$quiet" = "y" ]; then return; fi
	printf "$@" > /dev/kmsg || true
}

pre_mountroot() {
	[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-top"
	run_scripts /scripts/local-top
	[ "$quiet" != "y" ] && log_end_msg
}

tell_kmsg() {
	# Echos a string into /dev/kmsg, ignoring errors.
	echo "initrd: $1" >/dev/kmsg || true
}

update_yamui() {
	TEXT="$1"
	kill "$YAMUIPID"
	yamui -s 1 -t "$TEXT" -f 2 gemian &
	YAMUIPID=$!
}

update_yamui_exit() {
	TEXT="$1"
	kill "$YAMUIPID"
	yamui -s 1 -t "$TEXT" -f 3 gemian
}

halium_panic() {
	# Puts panic reason into kmsg and then starts the panic handlers
	REASON="$1"
	update_yamui_exit "PANIC: $REASON"
	tell_kmsg "PANIC for reason: $REASON"
	panic $REASON
}

identify_boot_mode() {
	# Our current list of supported boot modes:
	## BOOT_MODE = halium and android
	BOOT_MODE='halium'

	# The boot reason is exported via /proc/cmdline
	# The standard method is using androidboot.mode parameter.

	for x in $(cat /proc/cmdline); do
		case ${x} in
		androidboot.mode=*)
			android_bootmode=${x#*=}
			;;
		esac
	done

	if echo "$android_bootmode" | grep charger; then
		BOOT_MODE="android"
	fi

	## Some devices may be using 'bootreason', others 'boot_reason'
	## XXX: Find a better way to handle device specifics here

	# Krillin
	if [ -f /sys/class/BOOT/BOOT/boot/boot_mode ]; then
		boot_reason=$(cat /sys/class/BOOT/BOOT/boot/boot_mode)
		case "${boot_reason}" in
		1) BOOT_MODE="android" ;; # Meta
		4) BOOT_MODE="android" ;; # Factory
		8) BOOT_MODE="android" ;; # Power off charging
		9) BOOT_MODE="android" ;; # Low power charging
		esac
	fi

	tell_kmsg "boot mode: $BOOT_MODE"
}

resize_userdata_if_needed() {

	# See if the filesystem on the userdata partition needs resizing (usually on first boot).
	# If the difference between the partition size and the filesystem size is above a small
	# threshold, assume it needs resizing to fill the partition.

	path=$1

	# Partition size in 1k blocks
	case $path in
	/dev/mmcblk*)
		pblocks=$(grep ${path#/dev/*} /proc/partitions | awk {'print $3'})
		;;
	/dev/disk*)
		pblocks=$(grep $(basename $(readlink $path)) /proc/partitions | awk {'print $3'})
		;;
	esac
	# Filesystem size in 4k blocks
	fsblocks=$(dumpe2fs -h $path | grep "Block count" | awk {'print $3'})
	# Difference between the reported sizes in 1k blocks
	dblocks=$((pblocks - 4 * fsblocks))
	if [ $dblocks -gt 10000 ]; then
		resize2fs -f $path
		tell_kmsg "resized userdata filesystem to fill $path"
		update_yamui "resized userdata filesystem to fill $path"
	fi
}

mount_stowaways() {
	path="$1"
    
	tell_kmsg "checking filesystem integrity for $path partition"
	update_yamui "checking filesystem integrity for $path partition"
	# Mounting and umounting first, let the kernel handle the journal and
	# orphaned inodes (faster than e2fsck). Then, just run e2fsck forcing -y.
	# Also check the amount of time used by to check the filesystem.
	fsck_start=$(date +%s)
	mount -o errors=remount-ro $path /tmpmnt
	umount /tmpmnt
	e2fsck -y $path >/run/e2fsck.out 2>&1
	fsck_end=$(date +%s)
	tell_kmsg "checking filesystem for $path took (including e2fsck) $((fsck_end - fsck_start)) seconds"
	update_yamui "Checking filesystem for $path took (including e2fsck) $((fsck_end - fsck_start)) seconds"

	resize_userdata_if_needed ${path}

	tell_kmsg "mounting $path"
	update_yamui "mounting $path"

	# Mount the data partition to a temporary mount point
	# FIXME: data=journal used as a workaround for bug 1387214
	mount -o discard,data=journal $path /tmpmnt

	# Check stowaways_os= parameter
	for x in $(cat /proc/cmdline); do
		case ${x} in
		stowaways_os=*)
			stowaways_os=${x#*=}
			;;
		esac
	done

	if [ -z "$stowaways_os" ]; then
		# Use root of partition if /etc/os-release exists
		if [ -e "/tmpmnt/etc/os-release" ]; then
			mount --bind /tmpmnt ${rootmnt}
			mkdir -p "${rootmnt}/data" # in new fs
			update_yamui_exit "Gemian booting - /"
			return 0
		else
			halium_panic "/etc/os-release not found on $path"
			return 1
		fi
	else
		tell_kmsg "Found Stowaway $stowaways_os"
		update_yamui "Found Stowaway $stowaways_os"

		# OS is located at /.stowaways/{$stowaways_os}
		if [ -d "/tmpmnt/.stowaways/${stowaways_os}" ]; then
			mount --bind /tmpmnt/.stowaways/${stowaways_os} ${rootmnt}
			mkdir -p "${rootmnt}/data" # in new fs
			update_yamui_exit "Gemian booting - /.stowaways/$stowaways_os"
			return 0
		else
			halium_panic "/.stowaways/${stowaways_os} not found on $path"
			return 1
		fi
	fi
}

mount_lvm() {
	path="$1"
	lvm_vg="$2"

	tell_kmsg "Found LVM system - mlvm"
	update_yamui "Found LVM system"

	lvm pvresize "$path"

	#Seed the list with some likely volume names to check in case of no command line parameter
	lv_list="gemian debian stretch buster"

	# Check root_lv= parameter in command line, used to select between possibly multiple volume names
	for x in $(cat /proc/cmdline); do
		case ${x} in
		root_lv=*)
			lv_list=${x#*=}
			tell_kmsg "Found LVM system - mlvm: $lv_list"
			update_yamui "Found LVM system: $lv_list"
			;;
		esac
	done

	for lv_name in $lv_list; do
		if mount -t ext4 /dev/$lvm_vg/$lv_name /tmpmnt; then
			# Use root of partition if /etc/os-release exists
			if [ -e "/tmpmnt/etc/os-release" ]; then
				# Resize file system to full size
				resize2fs -f "/dev/$lvm_vg/$lv_name"
				sync

				mount --bind /tmpmnt ${rootmnt}
				mkdir -p "${rootmnt}/data" # in new fs
				update_yamui_exit "Gemian booting - /dev/$lvm_vg/$lv_name"
				return 0
			fi
			umount /tmpmnt
		fi
	done

	tell_kmsg "Installing (no gemian compatible logical volume not found)"
	update_yamui "Installing (no gemian compatible logical volume not found)"

	mkdir -p /externalmnt

	//find external media for install image
	EXTERNAL_MEDIA_DEVICES="/dev/mmcblk[1-9]*p[1-9]* /dev/sda[1-9]"

	for external_drive in $EXTERNAL_MEDIA_DEVICES; do
		[ -z "$external_drive" ] && continue
		unset TYPE
		eval "$(/sbin/blkid -c /dev/null -o export $external_drive)"

		update_yamui "Test mounting external $external_drive of type $TYPE"
		if mount -t $TYPE $external_drive /externalmnt; then
			# Check for gemian-config.txt
			[ -z "/externalmnt/gemian-config.txt" ] && continue
			. /externalmnt/gemian-config.txt
			# Expect to find
			# INSTALL_IMAGE=gemian-stretch.img.xz
			# LV_TO_REDUCE_NAME=home (optional)
			# LV_TO_REDUCE_SIZE=10G (optional - required if LV_TO_REDUCE_NAME is set)
			# NEW_LV_NAME=gemian
			# NEW_LV_SIZE=10G (optional)

			# Check if install image available
			[ -z "/externalmnt/$INSTALL_IMAGE" ] && update_yamui "Install image $INSTALL_IMAGE not found!" && continue

			# Check if space available - We should probably do some check:
			fsblocksize=$(dumpe2fs -h "/dev/$lvm_vg/$LV_TO_REDUCE_NAME" | grep "Block size" | awk {'print $3'})
			fsblocks=$(dumpe2fs -h "/dev/$lvm_vg/$LV_TO_REDUCE_NAME" | grep "Block count" | awk {'print $3'})
			fsfreeblocks=$(dumpe2fs -h "/dev/$lvm_vg/$LV_TO_REDUCE_NAME" | grep "Free blocks" | awk {'print $3'})


			if [ -n "$LV_TO_REDUCE_NAME" ]; then
				tell_kmsg "/dev/$lvm_vg/$LV_TO_REDUCE_NAME - $fsfreeblocks/$fsblocks ($fsblocksize)"
				update_yamui "/dev/$lvm_vg/$LV_TO_REDUCE_NAME - $fsfreeblocks/$fsblocks ($fsblocksize)"

				# Check and shrink fs
				e2fsck -f -y "/dev/$lvm_vg/$LV_TO_REDUCE_NAME"
				if ! lvm lvresize --resizefs --size $LV_TO_REDUCE_SIZE /dev/$lvm_vg/$LV_TO_REDUCE_NAME; then
					halium_panic "Couldn't shrink $LV_TO_REDUCE_NAME."
					return 1
				fi
			fi

			# Find new space available
			FREE_EXTENTS=$(lvm vgdisplay $lvm_vg -c | cut -d ":" -f 16)

			tell_kmsg "$lvm_vg - free extents - $FREE_EXTENTS"
			update_yamui "$lvm_vg - free extents - $FREE_EXTENTS"

			# Create new logical volume
			if [ -n "$NEW_LV_SIZE" ]; then
				if ! lvm lvcreate -L $NEW_LV_SIZE -n $NEW_LV_NAME $lvm_vg; then
					halium_panic "Couldn't create $NEW_LV_NAME with size $NEW_LV_SIZE on $lvm_vg."
					return 1
				fi
			else
				if ! lvm lvcreate -l $FREE_EXTENTS -n $NEW_LV_NAME $lvm_vg; then
					halium_panic "Couldn't create $NEW_LV_NAME on $lvm_vg."
					return 1
				fi
			fi

			tell_kmsg "Gemian Installing - $INSTALL_IMAGE to /dev/$lvm_vg/$NEW_LV_NAME"
			update_yamui "Gemian Installing - $INSTALL_IMAGE to /dev/$lvm_vg/$NEW_LV_NAME"

			# Uncompress install image to new file system
			xz -d -c "/externalmnt/$INSTALL_IMAGE" > "/dev/$lvm_vg/$NEW_LV_NAME"
			sync

			tell_kmsg "Resizing filesystem /dev/$lvm_vg/$NEW_LV_NAME"
			update_yamui "Resizing filesystem /dev/$lvm_vg/$NEW_LV_NAME"

			# Resize file system to full size
			resize2fs -f "/dev/$lvm_vg/$NEW_LV_NAME"
			sync

			if ! mount -t ext4 /dev/$lvm_vg/$NEW_LV_NAME /tmpmnt; then
				halium_panic "Couldn't mount new file system."
				return 1
			fi

			# Bind to the root mount and continue with boot
			if [ -e "/tmpmnt/etc/os-release" ]; then
				mount --bind /tmpmnt ${rootmnt}
				mkdir -p "${rootmnt}/data" # in new fs
				update_yamui_exit "Gemian booting - /dev/$lvm_vg/$NEW_LV_NAME"
				return 0
			fi
			umount /tmpmnt

		fi
	done

	halium_panic "Installable image not found on external drive"
	return 1
}

mount_lvm_or_stowaways() {
	path="$1"

	# Check for a root_lv - should only check volume groups if we want to boot one
	for x in $(cat /proc/cmdline); do
		case ${x} in
		root_lv=*)
			root_lv=${x#*=}
			tell_kmsg "Found LVM system - root_lv: $root_lv"
			;;
		esac
	done

	if [ -n "$root_lv" ]; then
		#Seed the list with likely names for volume groups ordered by preference
		vglist="planetlinux sailfish"

		# Check root_vg= parameter in command line, used to select between possibly multiple volume group names
		for x in $(cat /proc/cmdline); do
			case ${x} in
			root_vg=*)
				vglist=${x#*=}
				tell_kmsg "Found LVM system - root_vg: $vglist"
				update_yamui "Found LVM system - root_vg: $vglist"
				;;
			esac
		done

		tries_list="1 2 3 4 5 6 7"
		for tries in $tries_list; do
			tell_kmsg "LVM try: $tries"
			# Check for available volume groups
			for vgname in $vglist; do
				if lvm vgchange -a y "$vgname"; then
					tell_kmsg "LVM path: $path, vgname: $vgname"
					mount_lvm "$path" "$vgname"
					return 0
				fi
			done
			sleep 1
		done
	fi

	# Fallback to regular partitions
	mount_stowaways "$path"
}

mountroot() {
	update_yamui "Selecting Root"

	# list of possible userdata partition names
	partlist="linux userdata"

	pre_mountroot

	[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-premount"
	run_scripts /scripts/local-premount
	[ "$quiet" != "y" ] && log_end_msg

	# Put all of this script's output into /dev/kmsg
	exec &>/dev/kmsg

	# Mount root
	#
	# Create a temporary mountpoint for the bindmount
	mkdir -p /tmpmnt

	# Make sure the device has been created by udev before we try to mount
	udevadm settle

	# find the right partition
	for partname in $partlist; do
		part=$(find /dev -name $partname | tail -1)
		[ -z "$part" ] && continue
		path=$(readlink -f $part)
		[ -n "$path" ] && mount_lvm_or_stowaways "$path" && break
	done

	if [ -z "$path" ]; then
		halium_panic "Couldn't find data partition."
	fi

	[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-bottom"
	run_scripts /scripts/local-bottom
	[ "$quiet" != "y" ] && log_end_msg
	
}
