#!/bin/sh

. /etc/initrd.d/00-common.sh
. /etc/initrd.d/00-splash.sh
. /etc/initrd.d/00-zfs.sh

_fstype_init() {
    local fstype="${1}"
    if [ "${fstype}" = "btrfs" ]; then
        # start BTRFS volume detection, if available
        [ -x /sbin/btrfs ] && /sbin/btrfs device scan
    elif [ -z "${fstype}" ]; then
        warn_msg "Unable to detect the filesystem type (empty variable)"
    fi
}

_rootdev_detect() {
    local got_good_root=0
    while [ "${got_good_root}" != "1" ]; do

        case "${REAL_ROOT}" in
            LABEL=*|UUID=*)
                local root_dev=$(find_real_device "${REAL_ROOT}")
                if [ -n "${root_dev}" ]; then
                    REAL_ROOT="${root_dev}"
                    good_msg "Detected root: ${REAL_ROOT}"
                else
                    bad_msg "Unable to resolve root: ${REAL_ROOT}"

                    got_good_root=0
                    prompt_user "REAL_ROOT" "root block device"
                    continue
                fi
                ;;

            ZFS*)
                # zfs_rootdev_init will tweak ${REAL_ROOT}
                zfs_rootdev_init
                if [ "${?}" = "0" ]; then
                    got_good_root=1
                else
                    got_good_root=0
                    prompt_user "REAL_ROOT" "root block device"
                    continue
                fi
                ;;
        esac

        if [ -z "${REAL_ROOT}" ]; then
            # No REAL_ROOT determined/specified.
            # Prompt user for root block device.
            prompt_user "REAL_ROOT" "root block device"
            got_good_root=0

        # Check for a block device, NFS or ZFS
        # Here we assume that zfs_rootdev_init has correctly
        # initialized ZFS volumes
        elif [ -b "${REAL_ROOT}" ] || is_nfs || is_zfs; then
            got_good_root=1
        else
            bad_msg "${REAL_ROOT} is an invalid root device..."
            REAL_ROOT=""
            got_good_root=0
        fi
    done

    return 0
}

_rootdev_mount() {
    local mount_opts=ro
    local mount_fstype="${ROOTFSTYPE}"
    local fstype=$(get_device_fstype "${REAL_ROOT}")

    # handle ZFS special case. Thanks to Jordan Patterson
    # for reporting this.
    if [ -z "${fstype}" ] && is_zfs; then
        # here we assume that if ${fstype} is empty
        # and ZFS is enabled, we may well force the
        # fstype value to zfs_member
        fstype=$(zfs_member_fstype)
    fi

    if is_zfs_fstype "${fstype}"; then
        [ -z "${mount_fstype}" ] && mount_fstype=zfs
        mount_opts=$(zfs_get_real_root_mount_flags)
    fi

    [ -z "${mount_fstype}" ] && mount_fstype="${fstype}"

    good_msg "Detected fstype: ${fstype}"
    good_msg "Using mount fstype: ${mount_fstype}"
    _fstype_init "${fstype}"

    local mopts="${mount_opts}"
    [ -n "${REAL_ROOTFLAGS}" ] && \
        mopts="${mopts},${REAL_ROOTFLAGS}"
    good_msg "Using mount opts: -o ${mopts}"

    mount -t "${mount_fstype}" -o "${mopts}" \
        "${REAL_ROOT}" "${NEW_ROOT}" && return 0

    bad_msg "Cannot mount ${REAL_ROOT}, trying with -t auto"
    mount -t "auto" -o "${mopts}" \
        "${REAL_ROOT}" "${NEW_ROOT}" && return 0

    bad_msg "Cannot mount ${REAL_ROOT} with -t auto, giving up"

    return 1
}

_get_mounts_list() {
    awk '
        /^[[:blank:]]*#/ { next }
        { print $1 }
        ' ${NEW_ROOT}/etc/initramfs.mounts
}

_get_mount_fstype() {
    [ -e "${NEW_ROOT}"/etc/fstab ] || return 1
    awk -v fs="$1" '
        /^[[:blank:]]*#/ { next }
        $2 == fs { print $3 }
        ' ${NEW_ROOT}/etc/fstab
}

_get_mount_options() {
    [ -e "${NEW_ROOT}"/etc/fstab ] || return 1
    awk -v fs="$1" '
        /^[[:blank:]]*#/ { next }
        $2 == fs { print $4 }
        ' ${NEW_ROOT}/etc/fstab
}

_get_mount_device() {
    [ -e "${NEW_ROOT}"/etc/fstab ] || return 1
    awk -v fs="$1" '
        /^[[:blank:]]*#/ { next }
        $2 == fs { print $1 }
        ' ${NEW_ROOT}/etc/fstab
}

# If the kernel is handed a mount option is does not recognize, it
# WILL fail to mount. util-linux handles auto/noauto, but busybox
# passes it straight to the kernel which then rejects the mount.  To
# make like a little easier, busybox mount does not care about
# leading, trailing or duplicate commas.
_strip_mount_options() {
  sed -r \
        -e 's/(,|^)(no)?auto(,|$)/,/g' \
        -e 's/(,|^)iversion(,|$)/,/g'
}

real_root_init() {
    if [ -z "${REAL_ROOT}" ] && [ "${FAKE_ROOT}" != "/dev/ram0" ]; then
        REAL_ROOT="${FAKE_ROOT}"
    fi

    if [ -z "${REAL_ROOTFLAGS}" ]; then
        REAL_ROOTFLAGS="${FAKE_ROOTFLAGS}"
    fi
}

real_init_init() {
    local default_init="/sbin/init"
    if [ -z "${REAL_INIT}" ] && [ -z "${FAKE_INIT}" ]; then
        # if none of REAL_INIT and FAKE_INIT are set, default
        # to ${default_init}
        REAL_INIT="${default_init}"
    elif [ -z "${REAL_INIT}" ]; then
        if [ "${FAKE_INIT}" = "/linuxrc" ]; then
            # if init=/linuxrc is given, ignore linuxrc
            # this is for backward compatibility with very old setups
            REAL_INIT="${default_init}"
        else
            REAL_INIT="${FAKE_INIT}"
        fi
    fi
}

# Read /etc/initramfs.mounts from ${NEW_ROOT} and mount the
# listed filesystem mountpoints. For instance, /usr, which is
# required by udev & systemd.
ensure_initramfs_mounts() {
    local fslist=

    if [ -f "${NEW_ROOT}/etc/initramfs.mounts" ]; then
        fslist="$(_get_mounts_list)"
    else
        fslist="/usr"
    fi

    local dev= fstype= opts= mnt= cmd=
    for fs in ${fslist}; do

        mnt="${NEW_ROOT}${fs}"
        if mountpoint -q "${mnt}"; then
            good_msg "${fs} already mounted, skipping..."
            continue
        fi

        dev=$(_get_mount_device "${fs}")
        [ -z "${dev}" ] && continue
        # Resolve it like util-linux mount does
        [ -L "${dev}" ] && dev=$(realpath "${dev}")
        # In this case, it's probably part of the filesystem
        # and not a mountpoint
        [ -z "${dev}" ] && continue

        fstype=$(_get_mount_fstype "${fs}")
        if _get_mount_options "${fs}" | fgrep -q bind; then
            opts="bind"
            dev="${NEW_ROOT}${dev}"
        else
            # ro must be trailing, and the options will always
            # contain at least 'defaults'
            opts="$(_get_mount_options ${fs} | _strip_mount_options)"
            opts="${opts},ro"
        fi

        cmd="mount -t ${fstype} -o ${opts} ${dev} ${mnt}"
        good_msg "Mounting ${dev} as ${fs}: ${cmd}"
        if ! ${cmd}; then
            bad_msg "Unable to mount ${dev} for ${fs}"
        fi
    done
}

rootdev_init() {
    good_msg "Initializing root device..."

    while true; do

        if ! _rootdev_detect; then
            bad_msg "Could not mount specified ROOT, try again"
            prompt_user "REAL_ROOT" "root block device"
            continue
        fi

        if is_livecd && ! is_nfs; then
            # CD already mounted; no further checks necessary
            break
        fi
        if [ "${LOOPTYPE}" = "sgimips" ]; then
            # sgimips mounts the livecd root partition directly
            # there is no isofs filesystem to worry about
            break
        fi

        good_msg "Mounting ${REAL_ROOT} as root..."

        # Try to mount the device as ${NEW_ROOT}
        local out=1
        if is_nfs; then
            find_nfs && out=0
        else
            _rootdev_mount && out=0
        fi

        if [ "${out}" != "0" ]; then
            bad_msg "Could not mount specified ROOT, try again"
            prompt_user "REAL_ROOT" "root block device"
            continue
        fi

        # now that the root filesystem is mounted, before
        # checking the validity of ${NEW_ROOT} and ${REAL_INIT},
        # ensure that ${NEW_ROOT}/etc/initramfs.mounts entries
        # are mounted.
        ensure_initramfs_mounts

        # NFS does not need further checks here.
        is_nfs && break

        if [ ! -d "${NEW_ROOT}/dev" ]; then
            _msg="The filesystem ${REAL_ROOT},"
            _msg="${_msg} mounted at ${NEW_ROOT}"
            _msg="${_msg} does not contain /dev"
            _msg="${_msg}, init will likely fail..."
            bad_msg "${_msg}"
            prompt_user "REAL_ROOT" "root block device"
            continue
        fi
        if [ ! -x "${NEW_ROOT}${REAL_INIT}" ]; then
            _msg="The filesystem ${REAL_ROOT},"
            _msg="${_msg} mounted at ${NEW_ROOT}"
            _msg="${_msg} does not contain a valid"
            _msg="${_msg} init=${REAL_INIT}"
            bad_msg "${_msg}"
            prompt_user "REAL_ROOT" "root block device"
            continue
        fi
        break
    done
}
