#!/bin/sh

###############################################################################
#	F.U.L.L.S.T.O.R.Y initramfs live media init functions
#
#	Copyright:	© 2007-2010 Kel Modderman <kel@otaku42.de>
#			© 2006-2014 Stefan Lippers-Hollmann <s.l-h@gmx.de>
#			© 2007-2014 Niall Walsh <niallwalsh@celtux.org>
#	License:	GPLv2
#
#	F.U.L.L.S.T.O.R.Y Project Homepage:
#	https://github.com/fullstory
#
#	This script snippet needs to get executed under busybox sh, due to
#	using awk; klibc sh doesn't provide an awk implementation

###############################################################################
# 	use information exported via proc and sysfs to make block device list
# 	bubble-sort cdrom devices to top of block device stack
###############################################################################
fll_finger_blockdevs()
{
	unset FINGERED

	#
	# cdrom device detection. we could glob /sys/block/ and use udevadm
	# info output to identify devices with ID_CDOM=1 in environment, but
	# that method has proven to be unreliable ...
	#
	if [ "${FLL_FROMHD}" != "yes" ] && [ -r /proc/sys/dev/cdrom/info ]; then
		FINGERED=$(awk -F: '
			$1 == "drive name" && NF > 1 {
				split($2, node, " ")
				for (n in node) {
					if (!system("test -b /dev/" node[n]))
						cdrom[i++] = "/dev/" node[n]
				}
			}
			END {
				for (c in cdrom)
					print cdrom[c]
			}
		' /proc/sys/dev/cdrom/info)

		echo "${FINGERED}"
	fi

	#
	# disk device detection
	#
	for path in /sys/block/*; do
		[ -e "${path}" ] || continue

		disk=${path#/sys/block/}

		if [ "${FINGERED}" ] && echo "${FINGERED}" | \
			grep -q -w "${disk}"; then
		   	continue
		fi

		case "${disk}" in
			ps3d*)
				# udev seems to not know much about these
				# special PS3 specific block devices, so
				# avoid disk type checking
				;;
			nbd*)
				# udev doesn't reflect state of nbd device
				[ -n "${FLL_NBD}" ] || continue
				nbd-client -c ${disk} > /dev/null || continue
				;;
			*)
				udevadm info -q env -p "${path}" | \
					grep -q 'ID_TYPE=disk' || continue
				;;
		esac

		# use shell wildcard to expand partitions from sysfs
		for sub_path in ${path}/${disk}[1-9]* ${path}/${disk}p[1-9]*; do
			# trying p between disk and partition number for nbd
			[ -e "${sub_path}" ] || continue

			part="${sub_path#${path}/}"

			if [ -b "/dev/${part}" ]; then
				FINGERED="${FINGERED} /dev/${part}"
			fi
		done

		# also attempt to probe the base device, in some cases this is
		# the only partition
		if [ -b "/dev/${disk}" ]; then
			FINGERED="${FINGERED} /dev/${disk}"
		fi
	done

	echo ${FINGERED}
}

###############################################################################
# 	identify filesystem type of block device
###############################################################################
fll_finger_fstype()
{
	FSTYPE="$(/sbin/blkid -s TYPE -o value ${1})"

	if [ -n "${FSTYPE}" ] ; then
		export FSTYPE
		return 0
	fi

	unset FSTYPE
	return 1
}

###############################################################################
# 	identify label of block device, fallback to gpt partlabel
###############################################################################
fll_finger_label()
{
	LABEL="$(/sbin/blkid -s LABEL -o value ${1})"

	if [ -n "${LABEL}" ] ; then
		export LABEL
		return 0
	fi
	LABEL="$(/sbin/blkid -s PARTLABEL -o value ${1})"
	if [ -n "${LABEL}" ] ; then
		export LABEL
		return 0
	fi
	unset LABEL
	return 1
}

###############################################################################
#	modprobe loop and wait for device node creation
###############################################################################
fll_setup_dev_loop()
{
	# load loop device support
	modprobe "${MODPROBE_OPTIONS}" loop >/dev/null 2>&1

	# loop around for max of 5 seconds and wait for /dev/loop* device nodes
	LOOP_WAIT="10"

	while [ "${LOOP_WAIT}" -gt 0 ]; do
		c=0
		# TODO
		# it did check /dev/loop* and return 0 if anything existed
		for l in 0 1 2 3; do
			if [ -e "/dev/loop${l}" ]; then
				c=$((${c}+1))
			fi
		done

		if [ "${c}" -eq "4" ]; then
			return 0
		fi

		sleep 1
		LOOP_WAIT=$(( ${LOOP_WAIT} - 1 ))
	done

	panic "Unable to setup loop mounted device, no loop device nodes exist"
}

###############################################################################
#	generic mount function usage: fll_mount fs src mnt options
###############################################################################
fll_mount()
{
	FS="${1}"
	SRC="${2}"
	DST="${3}"
	shift 3

	unset FLL_MOUNT_OPTS

	case "${FS}" in
		iso9660|squashfs|udf)
			FLL_MOUNT_OPTS="ro"

			if [ -f "${SRC}" ] && [ ! -b "${SRC}" ]; then
				fll_setup_dev_loop

				LOOP_SRC_DEV="$(losetup -f)"
				if ! losetup "${LOOP_SRC_DEV}" "${SRC}"; then
					panic "Failed to setup loop device for ${SRC} on ${LOOP_SRC_DEV}"
				fi

				SRC=${LOOP_SRC_DEV}
			fi
			;;
		ntfs)
			if [ "${#}" -gt 0 ]; then
				FLL_MOUNT_OPTS="${@},dmask=0022,fmask=0133"
			else
				FLL_MOUNT_OPTS="ro,dmask=0022,fmask=0133"
			fi
			;;
		vfat)
			# we need this for FLL_IMAGE on DOS-filesystems
			if [ "${#}" -gt 0 ]; then
				FLL_MOUNT_OPTS="${@},shortname=winnt,umask=0"
			else
				FLL_MOUNT_OPTS="shortname=winnt,umask=0"
			fi
			;;
		suspend|swap|luks|lvm*)
			# filesystem blacklist
			return 1
			;;
		*)
			if [ "${#}" -gt 0 ]; then
				FLL_MOUNT_OPTS="noatime,${@}"
			fi
			;;
	esac

	if [ "${FLL_MOUNT_OPTS}" ]; then
		mount -t ${FS} -o ${FLL_MOUNT_OPTS} ${SRC} ${DST} >/dev/null 2>&1
		return "${?}"
	else
		mount -t ${FS} ${SRC} ${DST} >/dev/null 2>&1
		return "${?}"
	fi
}

###############################################################################
#	umount and remove a stack of mount points
###############################################################################
fll_umount()
{
	[ "${#}" -ge 1 ] || return 0

	for m in ${@}; do
		grep -q "${m}" /proc/mounts || continue

		umount "${m}" 2>/dev/null

		# if a plain umount fails, try the lazy option
		# if the lazy option fails then panic with error
		if [ "${?}" -ne 0 ]; then
			umount -l "${m}" || panic "failed to umount ${m}"
		fi

		rmdir "${m}"
	done
}

###############################################################################
#      calculate total, used and unused memory: MEMTOTAL MEMUSED MEMFREE
###############################################################################
fll_meminfo()
{
	awk '
		/^MemTotal:/ { total=$2 }
		/^(MemFree|Buffers|Cached):/ { free+=$2 }
		END {
			printf("MEMTOTAL=%d\n", int(total) / 1024)
			printf("MEMFREE=%d\n",  int(free) / 1024)
			printf("MEMUSED=%d\n",  (int(total) - int(free)) / 1024)
		}
	' /proc/meminfo
}

###############################################################################
#	utility function to display update when cp'ing live media into ram
###############################################################################
fll_copy_with_perc()
{
	if [ ! -f "${1}" ] || [ ! -d "${2}" ]; then
		return 1
	fi

	# background the copy, grab the process id
	cp "${1}" "${2}/${1##*/}" &

	# calculate copying progress
	awk -v pid="${!}" -v origfile="${1}" -v destfile="${2}/${1##*/}" '
		function size_of(file,    cmd, size)
		{
			cmd = "ls -l " file
			if ((cmd | getline) > 0) {
				size = int($5 / 1024)
				close(cmd)
				if (size > 0)
					return size
			}

			return 0
		}

		BEGIN {
			do {
				if (system("test -f " destfile) == 0) {
					break
				}
				else if (system("sleep 1") == 0) {
					wait++
				}
				else {
					exit(1)
				}
			} while (wait < 15)

			size_dest = perc = 0
			size_orig = size_of(origfile)
			if (size_orig <= 0)
				exit(1)

			do {
				perc = int(100 * size_of(destfile) / size_orig)
				if (perc < 100) {
					printf("\r[")
					for (i = 0; i <= perc; i += 2)
						printf("=")
					printf(">")
					for (i = perc; i < 100; i += 2)
						printf(" ")
					printf("]  ")
					printf("%02d%s", perc, "%")
				}
			} while (system("test -d /proc/" pid) == 0 &&
				 system("sleep 2") == 0)

			#
			# destfile size is > origfile size on tmpfs
			#
			if (size_of(destfile) >= size_orig) {
				printf("\r[")
				for (i = 0; i <= 100; i += 2)
					printf("=")
				printf("=] ")
				printf("%s\n", "100%")
				exit(0)
			}
			else {
				printf("\nFailed to copy %s to ram\n", origfile)
				exit(1)
			}
		}'

	return ${?}
}

###############################################################################
#	md5sum checker
###############################################################################
fll_md5sum_check()
{
	if [ ! -f "${1}/md5sums" ]; then
		printf "No md5sums file found on live media.\n"
		return 1
	fi

	if ( cd "${1}" && md5sum -c "md5sums" ); then
		return 0
	else
		printf "*******************************\n"
		printf "***** MD5SUM CHECK FAILED *****\n"
		printf "*******************************\n"
		return 1
	fi
}

###############################################################################
#       find and copy firmware
###############################################################################
fll_firmware() {
	# FLL_MOUNT_LOOP skips sleeping the first loop round
	unset FLL_MOUNT_LOOP

	# set FROMHD so hd's will be fingered
	FLL_FROMHD_REAL="${FLL_FROMHD}"
	FLL_FROMHD="yes"

	# don't wait longer for firmware then root
	# FLL_ROOTDELAY may already have been decreased
	while [ "${FLL_ROOTDELAY}" -gt 0 ]; do
		# sleep time, we skip sleeping for first pass
		if [ "${FLL_MOUNT_LOOP}" ]; then
			if [ "${FLL_MOUNT_LOOP}" -eq 1 ]; then
				printf "Waiting for up to ${FLL_ROOTDELAY}s for devices to settle...\n"
			fi
			sleep "${FLL_MOUNT_INTERVAL}"
			FLL_ROOTDELAY=$(( ${FLL_ROOTDELAY} - ${FLL_MOUNT_INTERVAL} ))
		fi

		FLL_MOUNT_LOOP=$(( ${FLL_MOUNT_LOOP} + 1 ))

		# FLL_FIRMDEVS holds the list of new devices to scan
		unset FLL_FIRMDEVS
		for dev in $(fll_finger_blockdevs); do
			unset firmdevdone
			for check in ${FLL_FIRMMNTS} ; do
				if [ ${check} = ${dev##*/} ]; then
					firmdevdone="Y"
					break
				fi
			done
			if [ -z "${firmdevdone}" ]; then
				FLL_FIRMDEVS="${FLL_FIRMDEVS} ${dev}"
			fi
		done

		# end loop if there is nothing new to check
		if [ -z "${FLL_FIRMDEVS}" ]; then
			continue
		fi

		# try each new device
		for dev in ${FLL_FIRMDEVS}; do
			# FLL_BLOCKMNT		- tracks current mount point
			unset FLL_BLOCKMNT

			# determine filesystem type of block device
			if fll_finger_fstype "${dev}" >/dev/null 2>&1; then
				# setup mount point
				FLL_BLOCKMNT="/fll/${dev##*/}"
				mkdir -p "${FLL_BLOCKMNT}"

				# mount block device on FLL_BLOCKMNT for probe
				if fll_mount "${FSTYPE}" "${dev}" "${FLL_BLOCKMNT}" ro; then
					# add dev name to FLL_FIRMMNTS to supress rechecking
					FLL_FIRMMNTS="${FLL_FIRMMNTS} ${dev##*/}"

					# check for firmware dir
					if [ -d "${FLL_BLOCKMNT}/${FLL_IMAGE_DIR}/firmware" ]; then
						FLL_FIRMWARE_FOUND="${dev}"

						if cp -R ${FLL_BLOCKMNT}/${FLL_IMAGE_DIR}/firmware/* /${rootmnt}/lib/firmware ; then
							printf "Copied firmware from ${dev##*/}\n"
						fi
					fi

					fll_umount "${FLL_BLOCKMNT}"
				fi
			fi

			# break dev and sleep loops if you have firmware
			if [ "${FLL_FIRMWARE_FOUND}" ]; then
				break 2
			fi
		done
	done

	# Restore real FROMHD value in case something else decides to use it
	FLL_FROMHD="${FLL_FROMHD_REAL}"
	if [ -z "${FLL_FIRMWARE_FOUND}" ]; then
		echo "Failed to find firmware directory"
	fi
}

###############################################################################
# 	define mountroot() for init
###############################################################################
mountroot()
{
	# source distro-defaults
	. /etc/default/distro

	# parse fll options given on cmdline
	for opt in $(cat /proc/cmdline); do
		case "${opt}" in
			blacklist=*)
				BLACKLIST="${BLACKLIST} ${opt#blacklist=}"
				;;
			firmware)
				FLL_FIRMWARE="yes"
				;;
			fll=debug)
				set -x
				;;
			fromhd*)
				FLL_FROMHD="yes"

				# define dev node
				case "${opt}" in
					fromhd=*)
						FLL_FROMHD_DEV="${opt#fromhd=}"
						;;
				esac
				;;
			fromiso*)
				FLL_FROMHD="yes"
				FLL_FROMISO="yes"

				# define iso name
				case "${opt}" in
					fromiso=*)
						FLL_MEDIA_NAME="${opt#fromiso=}"
						;;
				esac
				;;
			hostname=*)
				CUSTOM_HOSTNAME="${opt#hostname=}"
				;;
			image_dir=*)
				FLL_IMAGE_DIR="${opt#image_dir=}"
				FLL_IMAGE_LOCATION="${FLL_IMAGE_DIR}/${FLL_IMAGE_FILE}"
				;;
			image_name=*)
				FLL_IMAGE_FILE="${opt#image_name=}"
				FLL_IMAGE_LOCATION="${FLL_IMAGE_DIR}/${FLL_IMAGE_FILE}"
				;;
			nbdroot=*)
				# triggers finger_blockdevs
				FLL_FROMHD="yes"

				# need to know to tell finger_blockdevs to check nbd*
				FLL_NBD="${opt#nbdroot=}"
				;;
			quiet)
				if [ -f /proc/sys/kernel/printk ]; then
					echo "0" > /proc/sys/kernel/printk
				fi
				;;
			testcd|testdvd|md5sum)
				FLL_MD5SUM_CHECK="yes"
				;;
			toram)
				FLL_TORAM="yes"
				;;
			console=ttyS*,*)
				# parse out port and speed
				FLL_SERIAL="${opt#console=}"
				FLL_SERIAL_PORT="${FLL_SERIAL%,*}"
				FLL_SERIAL_OPTS="${FLL_SERIAL#*,}"
				FLL_SERIAL_SPEED="${FLL_SERIAL_OPTS%[a-z]*}"
				;;
		esac
	done

	#
	# Run scripts in local-top, like lvm2
	#
	[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/local-top"
	run_scripts /scripts/local-top
	[ "$quiet" != "y" ] && log_end_msg

	# debug checkpoint
	maybe_break fll-premount

	# total time in seconds to do device scans for; rootdelay= on kernel cmdline
	# upper limit on how long we wait for devices to show up before bailing out
	if [ "${ROOTDELAY}" ] && [ "${ROOTDELAY}" -gt 0 ]; then
		FLL_ROOTDELAY="${ROOTDELAY}"
	else
		FLL_ROOTDELAY="30"
	fi

	# interval between device scans
	FLL_MOUNT_INTERVAL="3"

	# these variables are created during $FLL_ROOTDELAY time loop
	unset FLL_MOUNT_LOOP

	while [ "${FLL_ROOTDELAY}" -gt 0 ]; do
		# refresh these for each loop
		unset FLL_BLOCKDEVS

		# sleep time, we skip FLL_ROOTDELAY interval for first pass for extra fast boot
		if [ "${FLL_MOUNT_LOOP}" ]; then
			if [ "${FLL_MOUNT_LOOP}" -eq 1 ]; then
				printf "Waiting for up to ${FLL_ROOTDELAY}s for devices to settle...\n"
			fi

			sleep "${FLL_MOUNT_INTERVAL}"
			FLL_ROOTDELAY=$(( ${FLL_ROOTDELAY} - ${FLL_MOUNT_INTERVAL} ))
		fi

		# mount loop counter, it enforces sleep interval on next loop
		FLL_MOUNT_LOOP=$(( ${FLL_MOUNT_LOOP} + 1 ))

		#
		# determine block devices to be probed on this loop
		#
		if [ "${FLL_FROMHD_DEV}" ]; then
			# user defined block device
			case "${FLL_FROMHD_DEV}" in
				LABEL=*)
					FLL_BLOCKDEVS="/dev/disk/by-label/${FLL_FROMHD_DEV#LABEL=}"
					;;
				UUID=*)
					FLL_BLOCKDEVS="/dev/disk/by-uuid/${FLL_FROMHD_DEV#UUID=}"
					;;
				/dev/mapper/*)
					lvm vgchange -aly --ignorelockingfailure
					FLL_BLOCKDEVS="${FLL_FROMHD_DEV}"
					;;
				/dev/nbd*)
					# retry nbd if not running
					# nbd not safe to re-call without test
					# can hang if FLL_FROMHD_DEV is not from nbdroot
					# strip partition, only care if rootdev is alive
					FLL_NBD_DEV=${FLL_FROMHD_DEV#/dev/}
					/sbin/nbd-client -c ${FLL_NBD_DEV%p*} > /dev/null || /scripts/local-top/nbd
					FLL_BLOCKDEVS="${FLL_FROMHD_DEV}"
					;;
				*)
					FLL_BLOCKDEVS="${FLL_FROMHD_DEV}"
					;;
			esac
		else
			# current list of block devices that may be probed for live media
			FLL_BLOCKDEVS=$(fll_finger_blockdevs)
		fi

		if [ -z "${FLL_BLOCKDEVS}" ]; then
			continue
		fi

		FLL_BLOCKDEVS_PROBED=false

		for dev in ${FLL_BLOCKDEVS}; do
			# FLL_BLOCKMNT		- tracks current mount point
			# FLL_DOUMOUNT		- stack of mount points to be umounted at end of
			#			  loop
			# FLL_DOMOVEMOUNT	- stack of mount points to be move mounted if the
			#			  loop is broken
			# FLL_MEDIA_FOUND	- set only when we have the live media compressed
			#			  filesystem located and mounted
			unset FLL_BLOCKMNT FLL_PROBE FLL_DOUMOUNT FLL_DOMOVEMOUNT FLL_MEDIA_FOUND FLL_2FS

			#
			# determine filesystem type of block device
			#
			if fll_finger_fstype "${dev}" >/dev/null 2>&1; then
				if ! ${FLL_BLOCKDEVS_PROBED}; then
					printf "Probing devices\t"
					FLL_BLOCKDEVS_PROBED=true
				fi
				printf "[ ${dev#/dev/} ] "

				# we have determined FSTYPE of the wrapper device, set FLL_BLOCKMNT
				FLL_BLOCKMNT="/fll/${dev##*/}"
				FLL_PROBE="${FLL_BLOCKMNT}/${FLL_IMAGE_LOCATION}"
				if [ "${FSTYPE}" = "squashfs" ]; then
					fll_finger_label "${dev}" >/dev/null 2>&1
					if [ "${LABEL}" = "${FLL_UUID}.${FLL_IMAGE_LOCATION#*\.}" ]; then
						FLL_BLOCKMNT="${FLL_MOUNTPOINT}"
						FLL_PROBE="${dev}"
					elif [ "${LABEL}" = "${FLL_UUID}.2" ]; then
						FLL_2FS="${dev}"
					fi
				fi
				mkdir -p "${FLL_BLOCKMNT}"
				# mount block device on FLL_BLOCKMNT for probe
				if fll_mount "${FSTYPE}" "${dev}" "${FLL_BLOCKMNT}" ro; then
					# add current FLL_BLOCKMNT to FLL_DOMOVEMOUNT stack
					FLL_DOMOVEMOUNT="${FLL_BLOCKMNT} ${FLL_DOMOVEMOUNT}"

					#
					# probe for iso image unless already have media
					#
					if [ ! -e "${FLL_PROBE}" ] && [ "${FLL_FROMISO}" = "yes" ]; then
						if [ -f "${FLL_BLOCKMNT}/${FLL_MEDIA_NAME#/}" ]; then
							# located iso, update dev description
							FLL_MEDIA_FOUND="${dev}"
							printf "\n\n"
							printf "Detected ${FLL_MEDIA_NAME} "
							printf "on ${FLL_MEDIA_FOUND#/dev/}.\n"

							if fll_finger_fstype "${FLL_BLOCKMNT}/${FLL_MEDIA_NAME}" >/dev/null 2>&1; then
								if [ "${FSTYPE}" != "iso9660" ] && [ "${FSTYPE}" != "udf" ]; then
									panic "${FLL_MEDIA_NAME} not an iso9660/ udf filesystem"
								fi

								mkdir -p /fll/fromiso

								# loop mount iso on FLL_MEDIA_MOUNTPOINT
								if fll_mount "${FSTYPE}" "${FLL_BLOCKMNT}/${FLL_MEDIA_NAME}" /fll/fromiso; then
									if [ "${FLL_TORAM}" ]; then
										# add block device mount point to umount stack
										FLL_DOUMOUNT="${FLL_BLOCKMNT} ${FLL_DOUMOUNT}"
									fi

									# update FLL_BLOCKMNT, FLL_DOMOVEMOUNT stack
									FLL_BLOCKMNT="/fll/fromiso"
									FLL_DOMOVEMOUNT="${FLL_BLOCKMNT} ${FLL_DOMOVEMOUNT}"
									# update FLL_PROBE as FLL_BLOCKMNT changed
									FLL_PROBE="${FLL_BLOCKMNT}/${FLL_IMAGE_LOCATION}"
								else
									fll_umount ${FLL_DOUMOUNT}
									panic "Failed to mount ${FSTYPE} filesystem from iso image"
								fi
							else
								fll_umount ${FLL_DOUMOUNT}
								panic "Failed to identify filesystem type of iso"
							fi
							printf "\n"
						fi
					fi
					#
					# probe for compressed filesystem
					#
					if [ -e "${FLL_PROBE}" ] ; then
						# found FLL compressed filesystem
						# set/update FLL_MEDIA_FOUND variable (possibly already set via fromiso)
						if [ -z "${FLL_MEDIA_FOUND}" ]; then
							printf "\n\n"
							FLL_MEDIA_FOUND="${dev}"
						fi

						# debug checkpoint
						maybe_break fll-found

						# md5sum check
						if [ "${FLL_MD5SUM_CHECK}" = "yes" ]; then
							MAINFS="${FSTYPE}"
							if [ -b "${FLL_PROBE}" ]; then
								umount "${FLL_BLOCKMNT}"
								if fll_finger_fstype "${FLL_PROBE%%[1234567890]*}" >dev/null 2>&1; then
									fll_mount "${FSTYPE}" "${FLL_PROBE%%[1234567890]*}" "${FLL_BLOCKMNT}" ro
									FSTYPE="${MAINFS}"
									MAINFS=""
								fi
							fi
							printf "Performing md5sum integrity check of live media...\n\n"
							if fll_md5sum_check ${FLL_BLOCKMNT}; then
								printf "All md5sums verified successfully.\n\n"
							else
								fll_umount ${FLL_DOUMOUNT}
								panic "Failed md5sum check!"
							fi
							if [ -z "${MAINFS}" ]; then
								umount "${FLL_BLOCKMNT}"
								fll_mount "${FSTYPE}" "${FLL_PROBE}" "${FLL_BLOCKMNT}" ro
							fi
						fi

						#
						# prepare live filesystem
						#
						if fll_finger_fstype "${FLL_PROBE}" >/dev/null 2>&1; then
							printf "Detected live ${FSTYPE} "
							if [ "${FLL_FROMISO}" = "yes" ]; then
								printf "filesystem on ${FLL_MEDIA_NAME}\n"
							else
								printf "filesystem on ${FLL_MEDIA_FOUND}\n"
							fi

							# find secondary squashfs if we didn't pick it up already
							if [ -z "${FLL_2FS}" ]; then
								if [ -f "${FLL_PROBE}" ]; then
									FLL_2FS="${FLL_PROBE%.*}.2"
								else
									MAINFS="${FSTYPE}"
									printf "Checking ${FLL_PROBE%%[1234567890]*}* for 2nd squashfs..."
									for testfs in ${FLL_PROBE%%[1234567890]*}*; do
										if fll_finger_fstype "${testfs}" >/dev/null 2>&1; then
											if [ "${FSTYPE}" = "squashfs" ]; then
												fll_finger_label "${testfs}" >/dev/null 2>&1
												if [ "${LABEL}" = "${FLL_UUID}.2" ]; then
													FLL_2FS="${testfs}"
													printf "found on ${testfs}"
													continue
												fi
											fi
										fi
									done
									FSTYPE="${MAINFS}"
									if [ -z "${FLL_2FS}" ]; then
										printf "none present"
									fi
									printf "\n"
								fi
							fi

							# copy to tmpfs
							if [ "${FLL_TORAM}" = "yes" ]; then
								# required tmpfs size: compressed filesystem size + 10% buffer
								eval $(fll_meminfo)

								if [ -f "${FLL_PROBE}" ]; then
									FLL_TORAM_FSSIZE=$(du -m "${FLL_PROBE}" "${FLL_2FS}" | awk -v c=0 '{c+=$1}END{print int(c * 1.1) }')
								else
									FLL_TORAM_FSSIZE=$(df -m "${FLL_PROBE}" | awk '/^\//{print int($2 * 1.2) }')
								fi

								# memory required to operate normally after iso has consumed memory
								# at least 256M is required for sanity
								FLL_TORAM_MEMREQ=$(( ${FLL_TORAM_FSSIZE} + 256 ))

								# do we have enough free memory for toram?
								if [ "${MEMFREE}" -gt "${FLL_TORAM_MEMREQ}" ]; then
									# prepare tmpfs, aka ramdisk
									mkdir -p /fll/toram
									mount -n -t tmpfs -o size="${FLL_TORAM_FSSIZE}m" tmpfs /fll/toram
									mkdir -p "/fll/toram/${FLL_IMAGE_DIR}"

									# copy compressed filesystem to tmpfs
									printf "Copying live filesystem from ${dev#/dev/} to ram...\n"
									if [ -f "${FLL_PROBE}" ]; then
										toramcmd="fll_copy_with_perc ${FLL_PROBE} /fll/toram/${FLL_IMAGE_DIR}"
									else
										toramcmd="cp ${FLL_PROBE} /fll/toram/${FLL_IMAGE_LOCATION}"
										printf "... copy about to start without progress, please wait.\n"
									fi
									if $toramcmd; then
										toramcmd=""
										if [ -f "${FLL_2FS}" ]; then
											printf "Copying 2nd filesystem from ${FLL_2FS} to ram..."
											fll_copy_with_perc "${FLL_2FS}" "/fll/toram/${FLL_IMAGE_DIR}/"
											printf "done\n"
											FLL_2FS="/fll/toram/${FLL_IMAGE_DIR}/${FLL_IMAGE_FILE%.*}.2"
										elif [ -n "${FLL_2FS}" ]; then
											printf "Copying 2nd filesystem from ${FLL_2FS} to ram..."
											cp "${FLL_2FS}" "/fll/toram/${FLL_IMAGE_DIR}/${FLL_IMAGE_FILE%.*}.2"
											printf "done\n"
											FLL_2FS="/fll/toram/${FLL_IMAGE_DIR}/${FLL_IMAGE_FILE%.*}.2"
										fi
										if [ "${FLL_BLOCKMNT}" = "${FLL_MOUNTPOINT}" ]; then
											# umount the direct squashfs partition
											fll_umount "${FLL_BLOCKMNT}"
										else
											# add old mount point to umount stack
											FLL_DOUMOUNT="${FLL_BLOCKMNT} ${FLL_DOUMOUNT}"
										fi
										# update FLL_BLOCKMNT
										FLL_BLOCKMNT="/fll/toram"
										FLL_PROBE="${FLL_BLOCKMNT}/${FLL_IMAGE_LOCATION}"
										FLL_DOMOVEMOUNT="${FLL_BLOCKMNT} ${FLL_DOMOVEMOUNT}"
									else
										if [ "${FLL_FROMISO}" = "yes" ]; then
											# we failed to copy iso contents to ram: reset FLL_DOUMOUNT stack
											unset FLL_DOUMOUNT
										fi

										# umount the tmpfs or run-init will not be happy
										rm -rf "/fll/toram/${FLL_IMAGE_DIR}"
										fll_umount /fll/toram
										printf "\nContinuing boot from live media...\n"
									fi
								else
									printf "Insufficient free memory to copy live media into memory\n"
									printf "Required free memory: ${FLL_TORAM_MEMREQ}M\n"
									printf "\nContinuing boot from live media...\n"

									if [ "${FLL_FROMISO}" = "yes" ]; then
										# we failed to copy iso contents to ram: reset FLL_DOUMOUNT stack
										unset FLL_DOUMOUNT
									fi
								fi
							fi

							# mount compressed filesystem, source directory: FLL_BLOCKMNT
							if [ ! -d "${FLL_MOUNTPOINT}" ]; then
								mkdir -p "${FLL_MOUNTPOINT}"
								if fll_mount "${FSTYPE}" "${FLL_PROBE}" "${FLL_MOUNTPOINT}"; then
									printf "Mounted ${FSTYPE} filesystem on ${FLL_MOUNTPOINT}\n\n"
									FLL_DOMOVEMOUNT="${FLL_MOUNTPOINT} ${FLL_DOMOVEMOUNT}"
								else
									fll_umount ${FLL_DOUMOUNT}
									panic "Failed to mount ${FSTYPE} filesystem"
								fi
							fi
						else
							fll_umount ${FLL_DOUMOUNT}
							panic "Failed to identify filesystem type of live media image"
						fi
					else
						# probe next device, add current mount to FLL_DOUMOUNT stack
						FLL_DOUMOUNT="${FLL_BLOCKMNT} ${FLL_DOUMOUNT}"
					fi
				fi
			fi
			# umount mount points in FLL_DOUMOUNT stack
			fll_umount ${FLL_DOUMOUNT}

			# break upon successful mount of live-media
			if [ "${FLL_MEDIA_FOUND}" ]; then
				break 2
			fi
		done

		# if we got here, we failed to find live media
		if [ "${FLL_FROMHD_DEV}" ]; then
			continue
		fi

		# start new probe line
		printf "\n"
	done

	# panic on failure to detect live media
	if [ -z "${FLL_MEDIA_FOUND}" ]; then
		panic "Failed to detect live media"
	fi

	# debug checkpoint
	maybe_break fll-postmount

	# mount secondary squashfs if we have one
	if [ -n "${FLL_2FS}" ]; then
		mkdir "${FLL_MOUNTPOINT}.2"
		FLL_DOMOVEMOUNT="${FLL_MOUNTPOINT}.2 ${FLL_DOMOVEMOUNT}"
		fll_mount squashfs "${FLL_2FS}" "${FLL_MOUNTPOINT}.2" ro
		#mount -t squashfs "${FLL_2FS}" "${FLL_MOUNTPOINT}.2"
	fi


	#
	# prepare COW union filesystem
	#

	# aufs || overlay
	if [ ! -d /sys/module/overlay ] && [ ! -d /sys/module/aufs ]; then
		modprobe "${MODPROBE_OPTIONS}" overlay >/dev/null 2>&1 || \
			modprobe "${MODPROBE_OPTIONS}" aufs >/dev/null 2>&1
	fi

	if [ -d /sys/module/overlay ]; then
		FLL_UNION_MODULE="overlay"
	elif [ -d /sys/module/aufs ]; then
		FLL_UNION_MODULE="aufs"
	fi

	# unioned filesystem mount points
	FLL_UNION_COWDIR="/fll/cow"

	# prepare COW union filesystem
	mkdir -p "${FLL_UNION_COWDIR}"
	#
	# mount a tmpfs over FLL_UNION_COWDIR, reserving 10% system memory
	# Note: $rootmnt must be mounted with a fs != rootfs prior to run-init
	#
	printf "Mounting virtual memory tmpfs filesystem on ${FLL_UNION_COWDIR}..."
	if mount -n -t tmpfs -o size=90%,mode=755 tmpfs "${FLL_UNION_COWDIR}"; then
		printf "\n"
	else
		panic "Failed to mount tmpfs over ${rootmnt}"
	fi
	FLL_DOMOVEMOUNT="${FLL_DOMOVEMOUNT} ${FLL_UNION_COWDIR}"

	# union module specific options
	case "${FLL_UNION_MODULE}" in
		aufs)
			FLL_UNION_OPTIONS="br:${FLL_UNION_COWDIR}:${FLL_MOUNTPOINT}"
			;;
		overlay)
			FLL_UNION_OPTIONS="lowerdir=${FLL_MOUNTPOINT},upperdir=${FLL_UNION_COWDIR}/upper,workdir=${FLL_UNION_COWDIR}/work"
			mkdir ${FLL_UNION_COWDIR}/upper ${FLL_UNION_COWDIR}/work
			;;
	esac

	# mount the union COW filesystem
	printf "Mounting ${FLL_UNION_MODULE} union filesystem..."
	if mount -t "${FLL_UNION_MODULE}" -o "${FLL_UNION_OPTIONS}" "${FLL_UNION_MODULE}" "${rootmnt}"; then
		printf "\n"
	else
		panic "Failed to prepare ${FLL_UNION_MODULE} union filesystem"
	fi

	# move all mounts in FLL_DOMOVEMOUNT stack to $rootmnt
	if [ "${FLL_DOMOVEMOUNT}" ]; then
		printf "Moving all mounted filesystems to ${rootmnt}..."
		for mnt in ${FLL_DOMOVEMOUNT}; do
			if grep -q "${mnt}" /proc/mounts; then
				mkdir -p "${rootmnt}${mnt}"
				mount -n -o move "${mnt}" "${rootmnt}${mnt}"
			fi
		done
		printf "\n"
	fi

	# TODO Make a sane test and kill FLL_MOUNTPOINT
	# sanity check, ${rootmnt}${FLL_MOUNTPOINT} _must_ exist
	if [ ! -d "${rootmnt}${FLL_MOUNTPOINT}" ]; then
		panic "Failed to find ${FLL_MOUNTPOINT} on ${rootmnt}"
	fi

	# debug checkpoint
	maybe_break fll-unionmount

	printf "Preparing live filesystem on ${rootmnt}...\n"

	# Scan for firmware if requested
	if [ -n "$FLL_FIRMWARE" ]; then
		# FLL_FIRMWARE_FOUND is set once the firmware is found (and copied)
		unset FLL_FIRMWARE_FOUND

		# start by checking mounted fs
		# they have been moved by now to ${rootmnt}/fll/
		# FLL_FIRMMNTS holds "devices" which have been checked
		unset FLL_FIRMMNTS

		for dev in $(ls -d ${rootmnt}/fll/*) ; do
			if [ -d ${dev} ]; then
				FLL_FIRMMNTS="${FLL_FIRMMNTS} ${dev##*/}"

				if [ -d ${dev}/${FLL_IMAGE_DIR}/firmware ]; then
					FLL_FIRMWARE_FOUND="${dev}"

					if cp -r ${dev}/${FLL_IMAGE_DIR}/firmware/* ${rootmnt}/lib/firmware/ ; then
						printf "Copied firmware from ${dev##*/}\n"
					fi

					break
				fi
			fi
		done

		# means firmware is not on a mounted fs
		if [ -z "${FLL_FIRMWARE_FOUND}" ]; then
			fll_firmware
		fi
	fi

	# debug checkpoint
	maybe_break fll-bindmount

	# prepare /dev /media /proc and /sys
	for dir in dev media proc run sys; do
		mkdir -p -m 0755 "${rootmnt}/${dir}"
	done

	# tmp with correct permissions for users
	mkdir -p -m 1777 "${rootmnt}/tmp"

	# debug checkpoint
	maybe_break fll-bottom

	# reset alsa state
	rm -f ${rootmnt}/var/lib/alsa/asound.state

	# remove persistent udev rules
	rm -f ${rootmnt}/etc/udev/rules.d/70-persistent-*.rules

	# create udev rule for persistent symlink to device live media was found on
	printf "KERNEL==\"%s\", SYMLINK+=\"fll\"\n" \
		"${FLL_MEDIA_FOUND#/dev/}" >  ${rootmnt}/etc/udev/rules.d/70-fll-live.rules
	printf "KERNEL==\"%s\", ENV{ID_CDROM}==\"?*\", SYMLINK+=\"fll-cdrom\"\n" \
		"${FLL_MEDIA_FOUND#/dev/}" >> ${rootmnt}/etc/udev/rules.d/70-fll-live.rules

	# disable movement of static $rootmnt/dev by scripts/init-bottom/udev
	export no_static_dev="1"

	# live-only (reverted by fll-installer): enable serial console access
	if [ -n "${FLL_SERIAL_PORT}" ]; then
		echo "T0:2345:respawn:/sbin/getty -n -i -l /usr/bin/fll_login -L ${FLL_SERIAL_PORT} ${FLL_SERIAL_SPEED} vt100" >> "${rootmnt}/etc/inittab"
	fi

	# disable checkroot/checkfs
	touch ${rootmnt}/fastboot

	# custom hostname given on cmdline
	if [ "${CUSTOM_HOSTNAME}" ]; then
		echo "${CUSTOM_HOSTNAME}" > "${rootmnt}/etc/hostname"
		echo "${CUSTOM_HOSTNAME}" > "${rootmnt}/etc/mailname"
		# update /etc/hosts
		sed -i '/localhost/!s/^\(127.0.0.1[ \t]\+\)\(.\+\)$/\1'"${CUSTOM_HOSTNAME}"'/' \
			"${rootmnt}/etc/hosts"
	fi

	# honour blacklist= cheatcode to disable naughty modules before udev
	if [ "${BLACKLIST}" ]; then
		for module in ${BLACKLIST}; do
			if ! grep -s -q "^blacklist ${module}" "${rootmnt}/etc/modprobe.d/*"; then
				echo "blacklist ${module}" >> "${rootmnt}/etc/modprobe.d/fll-blacklist.conf"
			fi
		done
	fi
	printf "\nStarting init process...\n\n"
}

