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

# destdir must be defined by this time
check_destdir() {
	[ -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 &&
		reset_devmapper &&
		stop_mdraid &&
		reset_devmapper &&
		start_multipath &&
		start_luks &&
		start_mdraid &&
		start_lvm &&
		start_luks &&
		mount_chroot &&
		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"

	if [ -x "$CRYPTSETUP" ]; then
		pushd /dev/mapper
		for i in *_luks; do
			"$CRYPTSETUP" luksClose "$i"
			dmsetup remove "$i"
		done
		popd
	fi

	chroot "$destdir" swapoff -a

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

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

	# for the log
	cat /proc/mounts

	# exclude " $destdir " itself
	grep " $destdir/" /proc/mounts |
		while read dev mnt rest; do umount -v "$mnt"; done

	# double-check just in case, there's been a weird report...
	! mountpoint "$destdir" || umount -v "$destdir" || return 1
}

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

stop_mdraid() {
	# saving state is only important *after* evms
	if [ -f /proc/mdstat -a -x "$MDADM" ]; then
		"$MDADM" --examine --scan \
		| grep '^ARRAY /dev/md[0-9/]' > /tmp/mdadm.conf
		"$MDADM" --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" --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 ||:
	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
			"$CRYPTSETUP" --key-file "$CRYPTSETUP_KEY" luksOpen "$device" "$(basename "$device")_luks"
		done
	fi
}

mount_chroot() {
	# depends on /tmp/fstab just like 10-fstab.sh
	rootfs="$(awk '{ if ($2=="/") print $1 }' < /tmp/fstab)"
	case "$rootfs" in
	UUID=*)
		rootfs="`$BLKID -U ${rootfs#UUID=}`"
		;;
	LABEL=*)
		rootfs="`$BLKID -L ${rootfs#LABEL=}`"
		;;
	/*)
		# all good as is
		;;
	*)
		echo "remount: unknown rootfs: $rootfs" >&2
		return 2
		;;
	esac

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

	mount -v "$rootfs" "$destdir" || return 3

	mount -v -o bind /dev "$destdir/dev"
	mount -v -t sysfs sysfs "$destdir/sys"
	mount -v -t proc proc "$destdir/proc"

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

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

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

# 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() {
	BOOTDEV="$(df -l --output=source "$destdir"/boot | tail -1)"
	[ -n "$BOOTDEV" ] || return 1
	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]}" ||:
	done
}
