#!/bin/sh
# remount EVMS-created volumes
# see https://bugzilla.altlinux.org/28181

# destdir must be defined by this time
check_destdir() {
	[ -n "$destdir" ] || exit 0
	[ -d "$destdir" ] || exit 1
}

remount_chroot() {
	# no dups here:
	# - bring lvm down;
	# - free the appropriate volumes (could be on mdraid too);
	# - bring mdraid down;
	# - free the appropriate block devices
	(
		set -x
		check_destdir &&
		save_blkid_state &&
		populate_fstab &&
		copy_chroot_binaries &&
		umount_chroot &&
		stop_luks &&
		reset_devmapper &&
		stop_mdraid &&
		reset_devmapper &&
		part_probe &&
		start_multipath &&
		start_mdraid &&
		start_lvm &&
		start_luks &&
		mount_chroot &&
		systemd_tmpfiles_chroot &&
		set_active \
	) >& /tmp/remount.log || return $?
}

remount_destination() {
	# remount destdir after alterator-vm
	# no mount chroot filesystem (/dev, /proc, /sys)
	# no dups here:
	# - bring lvm down;
	# - free the appropriate volumes (could be on mdraid too);
	# - bring mdraid down;
	# - free the appropriate block devices
	(
		set -x
		check_destdir &&
		save_blkid_state &&
		umount_destination &&
		stop_luks &&
		reset_devmapper &&
		stop_mdraid &&
		reset_devmapper &&
		part_probe &&
		start_multipath &&
		start_mdraid &&
		start_lvm &&
		start_luks &&
		mount_destination &&
		set_active \
	) >& /tmp/remount.log || return $?
}

# avoid automatic rpm shell.req dependency
MULTIPATHD=/sbin/multipathd
MDADM=/sbin/mdadm
LVM=/sbin/lvm
CRYPTSETUP=/sbin/cryptsetup
CRYPTSETUP_KEY=/tmp/empty
PUTFILE=/usr/share/make-initrd/tools/put-file
BLKID="blkid -c /dev/null"

# alterator-vm should leave LUKS containers
# with initial empty password, see #28200
:> "$CRYPTSETUP_KEY"

# for installer-feature-desktop-other-fs, see also #29005
save_blkid_state() {
	find /dev/mapper -type l \
	| xargs -r $BLKID \
	> /tmp/blkid.dm
}

populate_fstab() {
	[ ! -f /tmp/fstab ] ||
		cat /tmp/fstab >> "$destdir/etc/fstab"
}

copy_chroot_binaries() {
	useputfile=
	if [ -x "$destdir$PUTFILE" ]; then
		useputfile='yes'
		binddir="$(mktemp -d "$destdir/tmp/copy_chroot_binaries.XXXXXXXXX")"
		workdir="${binddir#$destdir}"
		mount --bind / "$binddir"
	else
		echo "remount: file does not exist or is not available for execution: $destdir$PUTFILE" >&2
	fi

	# these are normally missing in installer environment
	# NB: /sbin writes rely on aufs in fact, would use /tmp otherwise
	#     but that might clobber cases when the binaries have to differ
	for i in "$MDADM" "$LVM" "$CRYPTSETUP"; do
		if [ ! -x "$i" -a -x "$destdir$i" ]; then
			echo "remount: copying $i" >&2
			if [ -n "$useputfile" ]; then
				chroot "$destdir" "$PUTFILE" "$workdir" "$i" ||:
			else
				cp -p "$destdir$i" "$i"
			fi
		fi
	done

	if [ -e "$destdir/etc/lvm/lvm.conf" ]; then
		echo "remount: copying /etc/lvm/lvm.conf" >&2
		if [ -n "$useputfile" ]; then
			chroot "$destdir" "$PUTFILE" "$workdir" "/etc/lvm/lvm.conf" ||:
		else
			if [ -f "$destdir/etc/lvm/lvm.conf" ]; then
				mkdir -p /etc/lvm &&
				cp -p "$destdir/etc/lvm/lvm.conf" /etc/lvm
			else
				echo "remount: /etc/lvm/lvm.conf no file -- not copying!" >&2
			fi
		fi
	fi

	if [ -n "$useputfile" ]; then
		umount "$binddir"
		rmdir "$binddir"
	fi
}

umount_chroot() {
	fuser -vv -k -m "$destdir"

	chroot "$destdir" swapoff -a

	umount "$destdir/dev/pts"
	chroot "$destdir" umount -a -v

	if [ -d "$destdir"/sys/firmware/efi/efivars ]; then
		umount -v "$destdir"/sys/firmware/efi/efivars || :
	fi

	if [ -d /run/udev ]; then
		umount -v "$destdir/run/udev"
	fi

	umount -v "$destdir/run"
	umount -v "$destdir/tmp"

	# defuse
	umount -a -v -t fuse.gvfsd-fuse

	# for the log
	cat /proc/mounts

	# recursive unmount destination directory
	umount -Rv "$destdir"

	# check that the unmount was successful
	! mountpoint "$destdir" || return 1
}

reset_devmapper() {
	# evms_deactivate does essentially the same but is normally missing
	dmsetup remove_all ||:
}

part_probe() {
	# re-read partition table for kernel
	partprobe -s ||:
	# delay 5 second for kernel
	sleep 5
}

stop_mdraid() {
	# saving state is only important *after* evms
	if [ -f /proc/mdstat -a -x "$MDADM" ]; then
		"$MDADM" --examine --scan > /tmp/mdadm.conf
		"$MDADM" -v --stop --scan
	fi
}

start_mdraid() {
	if [ -s /tmp/mdadm.conf -a -x "$MDADM" ]; then
		# an arbitrary value of the year: packages installed already
		sysctl -w dev.raid.speed_limit_max=1000000
		# chroot's mdadm.conf populated by 45-mdadm.sh
		"$MDADM" -v --assemble --run --scan --config=/tmp/mdadm.conf ||:
	fi
}

start_multipath() {
	if [ -x "$MULTIPATHD" ]; then
		"$MULTIPATHD" reconfigure ||:
	fi
}

start_lvm() {
	if [ -x "$LVM" ]; then
		"$LVM" pvscan ||:
		"$LVM" vgscan ||:
		"$LVM" vgchange -ay --noudevsync ||:
		"$LVM" lvscan ||:
		"$LVM" lvchange -ay --noudevsync ||:
	fi
}

stop_luks() {
	if [ -x "$CRYPTSETUP" ]; then
		pushd /dev/mapper
		for i in *_luks; do
			"$CRYPTSETUP" close "$i"
		done
		popd
	fi
}

start_luks() {
	if [ -x "$CRYPTSETUP" ]; then
		for device in $($BLKID -o device); do
			[ "$($BLKID -o value -s TYPE "$device")" = "crypto_LUKS" ] || continue
			"$CRYPTSETUP" isLuks "$device" || continue
			echo "" | "$CRYPTSETUP" luksOpen --test-passphrase "$device" || continue
			"$CRYPTSETUP" --key-file "$CRYPTSETUP_KEY" luksOpen "$device" "$(basename "$device")_luks"
		done
	fi
}

mount_destfs() {
	# depends on /tmp/fstab just like 10-fstab.sh
	local mpoint="$1"
	mountpoint -q "$destdir""$mpoint" && return 0
	local destfs="$(awk -v mpoint="$mpoint" '{ if ($2==mpoint) print $1 }' < /tmp/fstab)"
	case "$destfs" in
	UUID=*)
		destfs="`$BLKID -U ${destfs#UUID=}`"
		;;
	LABEL=*)
		destfs="`$BLKID -L ${destfs#LABEL=}`"
		;;
	/*)
		# all good as is
		;;
	*)
		echo "remount: unknown filesystem for $mpoint: $destfs" >&2
		return 2
		;;
	esac

	if [ -z "$destfs" ]; then
		echo "remount: unable to re-identify device for $mpoint"
		echo "by ${UUID:+UUID=$UUID}${LABEL:+LABEL=$LABEL}"
		time $BLKID
		return 3
	fi >&2

	mountopts="$(grep "[[:space:]]$mpoint[[:space:]]" /tmp/fstab | awk '{ print $4 }')"

	mount -v "$destfs" "$destdir$mpoint" -o "$mountopts" || return 3
}

mount_chroot() {
	mount_destfs / || return $?

	mount -v -o bind /dev "$destdir/dev"
	mount -v -o bind /dev/pts "$destdir/dev/pts"
	mount -v -t sysfs sysfs "$destdir/sys"
	mount -v -t proc proc "$destdir/proc"
	
	mount runfs -t tmpfs -o mode=755 "$destdir/run"
	mount tmpfs -t tmpfs -o mode=755 "$destdir/tmp"

	if [ -d /run/udev ]; then
		mkdir -p "$destdir/run/udev"
		mount -v -o bind /run/udev "$destdir/run/udev"
	fi

	if [ -d /sys/firmware/efi/efivars ]; then
		mount -v -o bind /sys/firmware/efi/efivars \
		"$destdir/sys/firmware/efi/efivars" || :
	fi

	chroot "$destdir" mount -a -v ||:
	chroot "$destdir" swapon -a ||:

	# NB: use "sleep 3600" here to debug :)
}

# unmount destdir after alterator-vm
umount_destination() {
	swapoff -a
	fuser -vv -k -m "$destdir"

	# recursive unmount destination directory
	umount -Rv "$destdir"

	# check that the unmount was successful
	! mountpoint "$destdir" || return 1
}

# mount destdir without make chroot, after umount_destination
mount_destination() {
	[ -s /tmp/fstab ] || return 4
	for mpoint in $(awk '{ print $2 }' < /tmp/fstab | grep / | sort); do
		mount_destfs $mpoint || return $?
	done
}

systemd_tmpfiles_chroot() {
	if [ -s $destdir/sbin/systemd-tmpfiles ]; then
		systemd_tmpfiles=/sbin/systemd-tmpfiles
	elif [ -s $destdir/sbin/systemd-tmpfiles.standalone ]; then
		systemd_tmpfiles=/sbin/systemd-tmpfiles.standalone
	else
		return 5
	fi
	chroot "$destdir" $systemd_tmpfiles --create
}

# some firmwares, notably intel/dell ones, refuse to boot
# off drives without active partitions; provide some
# NB: lvm/luks don't count as plain /boot is expected then
# NB: we don't want EVMS names so do this *late*
set_active() {
	cat /proc/mounts | grep -q "$destination"/boot/efi && return 0
	mountpoint -q $destdir || return 6
	local BOOTDEV=
	[ -d "$destdir"/boot ] || BOOTDEV="$(df -l --output=source "$destdir" | tail -1)"
	[ -n "$BOOTDEV" ] || BOOTDEV="$(df -l --output=source "$destdir"/boot | tail -1)"
	[ -n "$BOOTDEV" ] || return 7
	BOOTDEV="${BOOTDEV#/dev/}"
	case "$BOOTDEV" in
		md*) BOOTDEV="$(ls /sys/block/"$BOOTDEV"/slaves/)";;
	esac
	[ -n "$BOOTDEV" ] || return 0
	for d in $BOOTDEV; do
		sfdisk -q -A "/dev/${d%%[0-9]*}" "${d##*[^0-9]}" 2>/dev/null ||:
	done
}
