diff --git a/board/common/image/image-ddi/rootfs/etc/fstab b/board/common/image/image-ddi/rootfs/etc/fstab new file mode 100644 index 00000000..f93d1f4f --- /dev/null +++ b/board/common/image/image-ddi/rootfs/etc/fstab @@ -0,0 +1,15 @@ +tmpfs /tmp tmpfs mode=0755,nosuid,nodev 0 0 +tmpfs /run tmpfs mode=0755,nosuid,nodev 0 0 +tmpfs /media tmpfs mode=1755,nosuid,nodev 0 0 +tmpfs /mnt/tmp tmpfs defaults 0 0 + +# Locate and mount /cfg, /home, /root and /var from the internal +# volume, wherever that is located - or, as a last resort, setup tmpfs +# mounts at these points. Setup an ephemeral writable overlay on /etc. +/usr/libexec/infix/mnt# /mnt/rw helper none 0 0 + +# Optional, nice-to-have mounts +debugfs /sys/kernel/debug debugfs nofail 0 0 +tracefs /sys/kernel/tracing tracefs nofail 0 0 +cfgfs /config configfs nofail,noauto 0 0 +host /mnt/host 9p nofail,noauto,cache=none,msize=16384 0 0 diff --git a/board/common/image/image-ddi/rootfs/mnt/rw/.empty b/board/common/image/image-ddi/rootfs/mnt/rw/.empty new file mode 100644 index 00000000..e69de29b diff --git a/board/common/image/image-ddi/rootfs/usr/libexec/infix/mnt b/board/common/image/image-ddi/rootfs/usr/libexec/infix/mnt new file mode 100755 index 00000000..e59d0593 --- /dev/null +++ b/board/common/image/image-ddi/rootfs/usr/libexec/infix/mnt @@ -0,0 +1,211 @@ +#!/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