#!/usr/bin/ash
# SPDX-License-Identifier: GPL-2.0-only

# teardown a single device by node name
#   $1: device node name, e.g. md0, dm-2
stop_device() {
    local devtype='' devname=''

    # the device must still be active
    [ -e "/sys/class/block/$1" ] || return 1

    # this can sometimes fail on stopped md devices, when the
    # sysfs node doesn't go away as quickly as i'd like it to.
    devtype="$(lsblk -drno TYPE "/dev/$1" 2>/dev/null)" || return 1
    case "$devtype" in
        crypt)
            # dmsetup could be used here, but we don't know that it
            # exists.
            read -r devname <"$1/dm/name"
            cryptsetup remove "$devname"
            ;;
        lvm | dm)
            read -r devname <"$1/dm/name"
            dmsetup --noudevsync remove "$devname"
            ;;
        raid*)
            # wait for arrays with external metadata to be marked as
            # clean. unfortunately, there isn't a whole lot we can do
            # if this fails, and the array may need a full rebuild on
            # the next reboot.
            IFS=: read -r metadata _ <"/sys/class/block/$1/md/metadata_version"
            if [ "$metadata" = external ]; then
                mdadm --wait-clean "/dev/$1"
            fi
            mdadm --stop "/dev/$1"
            ;;
        dmraid)
            read -r devname <"$1/dm/name"
            dmraid -an "$devname"
            ;;
        # silently ignore unstacked devices
    esac
}

# recursively disassemble a device chain
#   $1: device node name, e.g. md0, dm-2
disassemble() {
    local holder=''

    for holder in "$1"/holders/*; do
        [ -e "$holder" ] && disassemble "${holder##*/}"
        stop_device "$1"
    done
}

copy_binary_from_host() {
    local bin="$1"

    # hardcode a sane PATH
    for p in '/usr/sbin' '/usr/bin' '/sbin' '/bin'; do
        if [ -e "/oldroot/$p/$bin" ]; then
            cp "/oldroot/$p/$bin" "/usr/bin/$1"
            return 0
        fi
    done

    return 1
}

# XXX: Discourage libdevmapper from thinking that udev
# might be in a useful state. FS#30995.
rm -rf /run/udev

if [ "$1" = kexec ] && ! command -v kexec >/dev/null; then
    copy_binary_from_host kexec
fi

# chdir, so that we can avoid a lot of path chopping
cd /sys/class/block || exit

printf '%s\n' "Unmounting all devices."

umount --recursive /oldroot

printf '%s\n' 'Detaching loop devices.'

for loop in loop*/loop; do
    [ -e "$loop" ] && losetup -d "${loop%/loop}"
done

printf '%s\n' "Disassembling stacked devices."

# iterate over devices with holders
for part in */holders/*; do
    [ -e "$part" ] && disassemble "${part%%/*}"
done

case "$1" in
    poweroff | shutdown | halt)
        "$1" -f
        ;;
    *)
        type kexec >/dev/null && kexec -e
        reboot -f
        ;;
esac

# vim: ft=sh ts=4 sw=4
