#!/sbin/openrc-run
# SPDX-License-Identifier: MIT

description="Overlayfs setup for ro root"

depend() {
	# moving things later doesn't work well for some reason...
	# what's important is we don't run in parallel with another service
	# mounting things, just run after them all
	after sysfs cgroups devfs fsck_atlog udev rngd fsck root localmount
	before syslog
}


cleanup() {
	# try to move filesystems back... ugh
	if [ -e "/live/cleanup" ]; then
		while read -r submount; do
			mount --move "$mount/$submount" "$submount"
		done < /live/cleanup
	fi
	mountpoint -q "$mount" && umount "$mount"
}

start() {
	local mount=/live/overlay
	local slash_mntid parentid submount

	ebegin "Preparing overlayfs over /"

	if ! slash_mntid=$(findmnt -nr -o ID /) || [ -z "$slash_mntid" ]; then
		eerror "couldn't get / mount id"
		return 1
	fi

	# prepare a cosy tmpfs just to host our mount points
	if ! mount -t tmpfs none /live; then
		eerror "Could not mount a tmpfs to /live"
		return 1
	fi

	mkdir "/live/rootfs" "/live/overlay_work" "/live/overlay_upper" "$mount"

	if ! mount -t overlay \
			-o "lowerdir=/,workdir=/live/overlay_work,upperdir=/live/overlay_upper" \
			none "$mount"; then
		eerror "Could prepare overlay in $mount"
		cleanup
		return 1
	fi

	# no return from here if this worked...
	if ! pivot_root "$mount" "$mount/mnt"; then
		eerror "Could not bind mount $mount back to /"
		cleanup
		return 1
	fi

	rmdir "/mnt/$mount"
	# move key filesystems manually first, then check with findmnt if
	# any left this is because findmnt and mount themselves rely on /proc
	# note we cannot do anything even if this fails (trying to switch
	# back is dubious at best), so just ignore errors and let further
	# commands find nothing; system likely won't boot.
	mount --move /mnt/proc /proc || :
	mount --move /mnt/dev /dev || :

	findmnt -nr -o PARENT,TARGET | \
		while read -r parentid submount; do
			[ "$parentid" = "$slash_mntid" ] || continue
			# we don't want to remount /mnt yet if present!...
			[ "${submount#/mnt/mnt}" != "$submount" ] && continue
			if ! mount --move "$submount" "/${submount#/mnt/}"; then
				ewarn "Could not move $submount, things might break..."
			fi
		done

	if ! [ -e /live/rootfs ] || ! mount --move /mnt /live/rootfs; then
		ewarn "Could not bind mount old rootfs to /live/rootfs: staying in /mnt"
	fi

	if mountpoint -q /live/rootfs/mnt; then
		mount --move /live/rootfs/mnt /mnt \
			|| ewarn "Could not move /mnt mount"
	fi

	# this is as good as any other place to make / shared until
	# upstream provides a way of doing it:
	# https://gitlab.alpinelinux.org/alpine/aports/-/issues/13565
	mount --make-rshared /

	eend 0
}
