#!/bin/sh
# Called from /etc/fstab to ensure we have something writable mounted
# at /cfg, /etc, /home, /root, and /var.
#
# /mnt/cfg is used to store device modifications made to /etc, /home,
# /root.
#
# /mnt/var is used as the backing store for /var.  If /mnt/var isn't
# available, /mnt/cfg will still be be used to persist /var/lib.
#
# If a disk partiion with the corresponding label ("cfg"/"var") is
# available, that is used to persist changes to the aforementioned
# directories. Otherwise fall back to a tmpfs based RAM disk. This
# effectively brings up the system with the default configuration, but
# obviously any subsequent configuration is ephemeral.
. /etc/os-release

set -e

nm=$(basename "$0")
err=0
opt="-k"

# 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 /libexec/infix/check-factory ]; then
	return 1;
    fi

    /libexec/infix/check-factory
}

factory_reset()
{
    # XXX: flash LEDs to confirm factory-reset in progress
    logger $opt -p user.crit -t "$nm" "Resetting to factory defaults."
    rm -rf /mnt/cfg/* /mnt/var/*
    sync
}

mount_rw()
{
    # If something is already setup, leave it be.
    mountpoint -q "/$1" && return 0

    # TODO: Also look for UBI partitions
    mount LABEL="$1" 2>/dev/null && return 0

    return 1
}

# Bind mount /var or /var/lib to ensure applications like podman can set
# up overlay mounts for containers.  Overlays cannot sit on top of other
# overlays.  We always copy the contents of /var or /var/lib from the
# rootfs to get updates, e.g., new packages added in an upgrade.
mount_bind()
{
    src="$1"
    dst="$2"

    mkdir -p "$src"
    cp -af "$dst/." "$src/" ||:

    mount -o bind "$src" "$dst"
}

# mkdir -p -m 0755 only applies to the deepest directory
# shellcheck disable=SC2174
mount_overlay()
{
    tag="$1"
    src="$2"
    dst="$3"
    u="$src/infix/$tag.u"
    w="$src/infix/$tag.w"

    mkdir -p -m 0755 "$u"
    mkdir -p -m 0755 "$w"

    # Ensure that all users in wheel can create the .reset file
    # on /cfg and upload docker images to /var
    chgrp wheel "$(dirname "$u")"

    mount -t overlay "$tag-overlay" "$dst" \
	  -o lowerdir="$dst",upperdir="$u",workdir="$w"
}

# Fall back to console logging if sysklogd is too old
if ! logger -? |grep -q "Log to kernel"; then
    opt="-c"
fi

if ! mount_rw aux >/dev/null 2>&1; then
    logger $opt -p user.warn -t "$nm" \
	   "No auxiliary partition found, software updates not supported."
fi

varsrc=/mnt/var
if ! mount_rw var >/dev/null 2>&1; then
    logger $opt -p user.warn -t "$nm" \
	   "No persistent storage found for /var, only /var/lib is persisted."
    varsrc=/mnt/tmp/infix/var
    vlibsrc=/mnt/cfg/vlib
fi

cfgsrc=/mnt/cfg
etcsrc=/mnt/tmp
if ! mount_rw cfg >/dev/null 2>&1; then
    err=1

    logger $opt -p user.crit -t "$nm" \
	   "No persistent storage found for /cfg, falling back to tmpfs."

    cfgsrc=/mnt/tmp

    # Even if /mnt/var isn't available, if /mnt/cfg isn't either, then
    # there's no point in overlaying one ramdisk on top of another.
    vlibsrc=
else
    # Classic Infix has read-write /etc across boots
    if [ "$VARIANT_ID" != "netconf" ]; then
	etcsrc=/mnt/cfg
    fi
fi

if check_factory; then
    factory_reset
fi

mount_overlay cfg  "$cfgsrc" /cfg
mount_overlay etc  "$etcsrc" /etc
mount_overlay home "$cfgsrc" /home
mount_overlay root "$cfgsrc" /root
mount_bind         "$varsrc" /var

[ "$vlibsrc" ] && mount_bind "$vlibsrc" /var/lib

for tag in $(ls /sys/bus/virtio/devices/*/mount_tag 2>/dev/null); do
    if [ "$(cat $tag | tr -d '\0')" = hostfs ]; then
	mount hostfs
	break
    fi
done

exit $err
