#!/bin/sh
# Called from /etc/fstab to ensure we have something writable mounted
# at /cfg, /home, /root, and /var.
#
# In the normal case, the "rw" LV from the "internal" LVM VG is used
# to back all persistant storage. In the event that "internal" is
# present, but the "rw" LV is missing, one will be provisioned, along
# with the required BTRFS subvolumes.
#
# In all other scenarios where the subvolumes are not available, the
# system will fall back to temporary storage for all mount points.
#
# For /etc, a writable, tmpfs backed, layer is always overlayed on top
# of the read-only contents from the OS image.

set -e

nm=$(basename "$0")
need_restore=
code=0

logmsg()
{
    logger -k -t "$nm" -p user.crit "$@"
}

status()
{
    GREEN="$(printf '\033[1;32m')"
    RED="$(printf '\033[1;31m')"
    YELLOW="$(printf '\033[1;33m')"
    BOLD="$(printf '\033[1m')"
    RESET="$(printf '\033[0m')"

    case $1 in
        0) color=$GREEN; text=' OK ' ;;
        1) color=$RED;   text='FAIL' ;;
        2) color=$YELLOW;text='WARN' ;;
        *) color=$YELLOW;text=' ⋯  ' ;;
    esac

    printf '%s[%s%s%s%s]%s ' "$BOLD" "$color" "$text" "$RESET" "$BOLD" "$RESET"
}

print_start()
{
    printf '\r\033[K%s%s' "$(status 3)" "$*" > /dev/console
    need_restore=YES
}

print_end()
{
    rc=$1; shift
    if [ $# -gt 0 ]; then
        printf '\r\033[K%s%s\n' "$(status "$rc")" "$*" > /dev/console
    else
        printf '\r%s\n' "$(status "$rc")" > /dev/console
    fi
}

# Restore Finit's original progress message so its [ OK ] appears correctly
print_restore()
{
    [ "$need_restore" ] || return 0
    print_start "Mounting filesystems from /etc/fstab"
}

# # External button or bootloader changed kernel command line
# check_factory()
# {
#     if [ -f /mnt/cfg/infix/.reset ]; then
# 	return 0;
#     fi

#     if grep -q 'finit.cond=factory-reset' /proc/cmdline; then
# 	return 0;
#     fi

#     # Add to your br2-external to extend factory-reset check
#     if [ ! -x /usr/libexec/infix/check-factory ]; then
# 	return 1;
#     fi

#     /usr/libexec/infix/check-factory
# }

# factory_reset()
# {
#     find /sys/class/leds/ -type l -exec sh -c 'echo 100 > $0/brightness' {} \;
#     print_start "Resetting to factory defaults."
#     logger $opt -p user.notice -t "$nm" "Resetting to factory defaults."

#     rm -rf /mnt/cfg/* /mnt/var/*
#     sync

#     logger $opt -p user.notice -t "$nm" "Factory reset complete."
#     print_end 0 "Factory reset complete."
#     print_restore
# }

subvols="cfg var root home"

internal_rw_provision()
{
    print_start "Provisioning persistent storage on internal volume"

    lvm lvcreate --name rw --extents 50%FREE internal || {
	logmsg "No rw LV available, nor could it be created"
	print_end 1
	return 1
    }

    mkfs.btrfs -L rw /dev/internal/rw || {
	logmsg "Could not create BTRFS on rw LV"
	print_end 1
	return 1
    }

    mount -t btrfs /dev/internal/rw /mnt/rw || {
	logmsg "Failed to mount rw"
	print_end 1
	return 1
    }

    for subvol in $subvols; do
	btrfs subvolume create /mnt/rw/@"$subvol" || {
	    logmsg "Failed to create @$subvol on rw"
	    print_end 1
	    return 1
	}
    done

    umount /mnt/rw || {
	logmsg "Failed to unmount rw"
	print_end 1
	return 1
    }

    print_end 0
}

internal_rw_activate()
{
    lvm vgs internal || {
	logmsg "No internal volume available"
	return 1
    }

    lvm vgchange -ay internal || {
	logmsg "Unable to bring internal volume online"
	return 1
    }

    # The only condition under which we do BTRFS provisioning is when
    # the LV is absent. In all other failure scenarios: fall back to
    # tmpfs and let the user sort it out. This way we do not risk any
    # loss of data.
    [ -b /dev/internal/rw ] && return 0

    internal_rw_provision
}

internal_rw_mount()
{
    internal_rw_activate || return 1

    for subvol in $subvols; do
	mount -t btrfs /dev/internal/rw /"$subvol" -o subvol=@"$subvol" || {
	    logmsg "Failed to mount /$subvol"
	    return 1
	}
    done
}

tmpfs_rw_mount()
{
    for subvol in $subvols; do
	mkdir -p -m 0755 /mnt/tmp/"$subvol"
	mount /mnt/tmp/"$subvol" /"$subvol" -o bind || {
	    logmsg "Failed to mount temporary /$subvol"
	    return 1
	}
    done
}

tmpfs_etc_mount()
{
    mkdir -p -m 0755 /mnt/tmp/etc/
    tar c -C /etc . | tar x -C /mnt/tmp/etc
    mount /mnt/tmp/etc /etc -o bind
}

exec >/mnt/tmp/mnt.log 2>&1

tmpfs_etc_mount || {
    code=1
    logmsg "Failed to make /etc writable"
}

if ! internal_rw_mount; then
    code=1
    print_end 1 "Mounting persistent storage from internal volume"
    logmsg "Internal volume not found, falling back to tmpfs"

    print_start "Falling back to ephemeral tmpfs storage!"
    tmpfs_rw_mount
    print_end $?
fi

print_restore
exit $code
