#!/bin/bash
####################################################################################
# File.......: /usr/lib/setup/armedslack-SeTpartitions
# Called from: (Sourced rather than forked) /usr/lib/setup/SeTpartitions
# Applied via a patch: installer-patches/SeTpartitions-add-nofscheck.diff
# Purpose....: Detect a Slackware ARM/AArch64 bootable SD card and append it to
#              the /etc/fstab of the new installation.
#              Mount this SD card under /mnt/boot (/mnt being the location that
#              the Slackware installer mounts the new OS's root filesystem).
#              Updates the U-Boot configuration.
#
#              This is to support the boot process where we ship SD card images
#              that provide a bootable U-Boot header and an ext4 file system containing
#              the U-Boot configuration, Linux Kernel and Slackware installer and OS
#              initial RAM disks ('initrd').
# Version....: 1.02
# Date.......: 25-Nov-2023
# Author.....: Stuart Winter <mozes@slackware.com>
###################################################################################
# Change log
# v1.02, 25-Nov-2023
# * Moved U-Boot configuration into
#  /usr/share/hwm-configure/platform/aarch64/installer/helper.scr/000-uboot-configure
# v1.01, 02-Jan-2022
# * Added file system labeling as the default.
# v1.00, 08-Apr-2021
# * Initial version
###################################################################################
#
# Copyright 2021, 2022, 2023,2024  Stuart Winter, Donostia, Spain.
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
#  EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Config files:
ARMEDSLACK_EXTLINUXCONF=extlinux/extlinux.conf
# Experimentation with INCLUDE
#ARMEDSLACKOS_EXTLINUXCONF=extlinux/slkos

# Copy the 'ROOT_DEVICE' variable into one that we can manipulate within this
# script.  This is because this script is sourced from SeTpartitions, and we
# don't want to overwrite the original values if we switch to use file system
# labeling.
ARMEDSLACK_ROOT_DEVICE=${ROOT_DEVICE}

# Store the root file system type (e.g. 'ext4') so that it can be used in other
# post installation Boot Loader configuration tools such as for GRUB.
echo ${ROOT_SYS_TYPE} > $TMP/root_sys_type

###################################################################################
################### Functions #####################################################
###################################################################################

# Ensure that the device has a single partition, which we will assume means the user
# wrote the image directly to the SD card and made no modifications.
# Takes full device name as input - e.g. /dev/mmcblk1p1
#
#function sanitycheckbootpart() {
#   local dev=${1%%p*} # lop off the partition number
#   local devbase=${dev#/dev/} # lop off /dev/
#   # If 1 partition found, we're good.
#   [ $( grep -c "${devbase}p[0-9]" /proc/partitions 2> /dev/null ) -eq 1 ] && return 0
#   # Else we're not:
#   return 1
#}

# Ensure that the device has no partitions after the one that will house /boot
# Takes full device name as input - e.g. /dev/mmcblk1p1
function sanitycheckbootpart() {
   local devbase=${1%%p*} # lop off the partition number to find the base name of the block device
   # If the last partition on the block device is the same as the one we're supplied with,
   # it indicates that we potentially have room to resize.
   # However, the last partition isn't the one we're looking for, we cannot offer to
   # resize since it'd overlap!
   [ $( fdisk -l ${devbase} | grep -E '^/dev/' | tail -n1 | awk '{print $1}' ) = "${1}" ] && return 0
   # There's another partition subsequent to Slackware OS'
   # /boot - resizing isn't possible:
   return 1
}

function offerresizebootsuccess() {
   dialog \
      --backtitle "/boot partition management" \
      --title "RESIZE /boot PARTITION ON SD CARD" --ok-button "OK" \
      --msgbox "\n\nResizing was successful.\n" 9 60
   clear
}

function offerresizebootfail() {
   dialog \
      --backtitle "/boot partition management" \
      --title "RESIZE /boot PARTITION ON SD CARD" --ok-button "OK" \
      --msgbox '\nResizing failed!\n\n
The installation of Slackware cannot continue.
\n\nYou must investigate manually.\n
\nOne option may be to re-write the Installer image to the SD card again and
reboot the installer.\n' 14 79
   clear
}

# Offer the user the option of resizing /boot:
function offerresizebootpart() {
   dialog \
      --backtitle "/boot partition management" \
      --title "RESIZE /boot PARTITION ON SD CARD" --yesno \
"\nBy default Slackware AArch64 supplies the Slackware Installer image that presently
occupies a 1GB partition on the SD card within this machine.\n\n
This SD card will be transformed into the Operating System's /boot partition
as part of the installation process.\n
\n
It is recommended that this SD card is used for nothing more than /boot, however this
is not mandatory, and some users may prefer to create additional partitions subsequently (post installation).
\n
\nDo you want to resize the /boot partition to fill all available space? (recommendation is 'Yes')\n \

\n" 17 77
  return $?
}

# Resize /boot:
# Take full device name including partition number as input.
# e.g. /dev/mmcblk1p1
function resizebootpart(){
   local dev=${1%%p*} # lop off the partition number
   local devpart=${1}

   # Cause the Kernel to become aware of the new size.
   # Unnecessary but I'll keep it in case:
   # echo 1 > /sys/block/mmcblk1/device/rescan
   # [ "${mmcdev:(-2)}" = "p1" ] && echo y

   # Grow the partition to fill all available space:
   echo "EXISTING SIZE: $( /sbin/fdisk -l ${devpart} | grep "Disk ${devpart}" )"
   echo "OPERATION: Growing partition ${devpart}"
   /sbin/e2fsck -yf ${devpart}
#   /usr/sbin/parted -s -a opt ${dev} "resizepart 1 100%"
   /usr/sbin/parted -s -a opt ${dev} "resizepart ${devpart#*p} 100%"
   estat=$?
   if [ $estat -gt 0 ]; then
      read -p "An error occurred.  Press ENTER to continue"
      return $estat
   fi

   # Resize the ext4 filesystem for /boot on partition 1:
   echo "OPERATION: Resizing ${devpart}"
   /sbin/e2fsck -yf ${devpart}
   /sbin/resize2fs ${devpart}
   estat=$?
   if [ $estat -gt 0 ]; then
      read -p "An error occurred.  Press ENTER to continue"
      return $estat
   fi
   echo "NEW SIZE: $( /sbin/fdisk -l ${devpart} | grep "Disk ${devpart}" )"

   # Wait a moment to see the output:
   sleep 6
   return 0
}

# umount everything that has been configured by /usr/lib/setup/SeTpartitions
# If we merge this upstream, we can dispense with this.
# We cannot umount just $T_PX because the operator may have chosen to mount
# other file systems, which which are now mounted under $T_PX (/mnt within the
# Slackware Installer).
# This prevents us from umounting to conduct labeling operations.
function umount_all_tpx() {
  local blkdev
  # Reverse the order here, since we cannot umount /mnt first as any other
  # mount points are mounted under it.
  # Since the lines in SeTnative are added in the order in which they are mounted,
  # we'll assume we can umount in the reverse order.
  # Not the best assumption, granted, but it'll do until labeling can be upstreamed
  # behind a feature flag.
  grep -E '^/' ${TMP}/SeTnative | awk '{print $1}' | tac | while read blkdev ; do
    umount ${blkdev} || { echo "Failed umount ${blkdev}" ; exit 1 ;}
  done
}

# Re-mount everything that has been configured by /usr/lib/setup/SeTpartitions
# These are re-mounted in the same order as they were configured, since the
# mount points build atop one another (with '/mnt' as the base):
function remount_all_tpx() {
  local mntlint
  grep '^/' ${TMP}/SeTnative | awk -v tpx=$T_PX '{print $1,tpx$2,"-t "$3'} | while read mntline ; do
    mount $mntline || { echo "Failed to mount $mntline" ; exit 1 ;}
  done
}

###################################################################################

# In Slackware x86 /etc/fstab and the Boot Loader directly reference the block device.
# This works because the OS partitions typically reside on storage buses that are static
# (e.g. SATA, IDE, SCSI), where as on ARM the primary storage is often on a hot-plug bus
# such as USB.  This lends itself to inconsistent numbering across boots, which may cause
# the machine not to boot.
if [ ! -f /.no-labeling ]; then
   # This label is used within the Boot Loader (e.g. extlinux.conf/grub.conf) and
   # the OS' /etc/fstab:
   ARMEDSLACK_ROOT_DEVICE="LABEL=SLKroot"
   # Store the root device label so it can be used within the Boot Loader config
   # scripts within the post install phase.
   echo "${ARMEDSLACK_ROOT_DEVICE}" > ${TMP}/SeTrootdev_labeled

   ( # Determine the labeling command required for the file system type:
     case ${ROOT_SYS_TYPE} in
        ext2|ext3|ext4) labcmd="e2label ${ROOT_DEVICE} SLKroot" ;;
        reiserfs) labcmd="reiserfstune --label SLKroot ${ROOT_DEVICE}" ;;
        btrfs)labcmd="btrfs filesystem label ${ROOT_DEVICE} SLKroot" ;;
        f2fs) labcmd="f2fs_label ${T_PX} SLKroot"
              # f2fs_label requires that the f2fs file system is mounted:
              labnoumount=yes ;;
        jfs) labcmd="jfs_tune -L SLKroot ${ROOT_DEVICE}" ;;
        xfs) labcmd="xfs_admin -L SLKroot ${ROOT_DEVICE}" ;;
     esac

     # Some file systems cannot label when the FS is mounted.
     # We'll umount by default as the safest choice, and remain mounted
     # by exception:
     #[ -z "${labnoumount}" ] && { umount ${T_PX} || exit 1 ;} # ${T_PX} = OS mount point, /mnt
     if [ ! -z "${labcmd}" ]; then
        [ -z "${labnoumount}" ] && { umount_all_tpx || exit 1 ;}
     fi
     # Label the file system:
     if [ ! -z "${labcmd}" ]; then
        eval ${labcmd} || { echo "Failed to label root device" ; exit 1 ; }
     fi
     # Re-mount the root FS if we umounted it above:
     #[ -z "${labnoumount}" ] && { mount $ROOT_DEVICE $T_PX -t $ROOT_SYS_TYPE 1> $REDIR 2> $REDIR || exit 1 ;}
     if [ ! -z "${labcmd}" ]; then
        [ -z "${labnoumount}" ] && { remount_all_tpx || exit 1 ;}
     fi
     # Switch the OS' /etc/fstab to use a label for the root fs:
     #sed -i 's?^'"${ROOT_DEVICE}"'?'"${ARMEDSLACK_ROOT_DEVICE}"'?g' ${TMP}/SeTnative || exit 1
     # Fix the spacing:
     #sed -i 's?^'"${ROOT_DEVICE} "'?'"${ARMEDSLACK_ROOT_DEVICE}"'?g' ${TMP}/SeTnative || { echo "Failed to switch to labeling in ${TMP}/SeTnative" ; exit 1 ;}
     if [ ! -z "${labcmd}" ]; then
        sed -i 's?^'"${ROOT_DEVICE} "'?'"${ARMEDSLACK_ROOT_DEVICE} "'?g' ${TMP}/SeTnative || { echo "Failed to switch to labeling in ${TMP}/SeTnative" ; exit 1 ;}
     fi
     # Ensure that there's a single file system labeled 'SLKroot'
     # This may occur if the user had a file system from a previous installation
     # which they didn't format, and didn't select as their root file system.
     if [ $( lsblk -o label -Mrin | grep -E '^SLKroot' -c ) -gt 1 ]; then
        echo "ERROR: More than one file system label found for 'SLKroot'"
        echo "       You must re-label the file system which you did not"
        echo "       select as your root file system."
        exit 1
     fi
   ) || { read -p "ERROR: Failed labeling. This needs to be resolved manually." ; exit 1 ;}
fi

# Scan the MMC block devices for the filesystem label 'SLKboot'
# If there is >1, we'll pick the last.
for mmcdev in /dev/mmcblk* ; do
   { ( /sbin/e2label ${mmcdev} 2>/dev/null | grep -Eq '^SLKboot$' ) && ARMEDSLACK_MMCBOOTPART=${mmcdev} ;}
done

# If a Slackware ARM Installer and OS /boot partition was found,
# we (no longer: offer the user the option to grow it, then)
# proceed to configure the boot loader.
if [ ! -z "${ARMEDSLACK_MMCBOOTPART}" ]; then
#
###
# RESIZING OPTION - commented out.  If you want to re-add it, uncomment this block below.
###
# Don't offer the resize option for /boot because it doesn't make sense.
# Having users use /boot for anything other than boot assets is silly, and there's
# plenty of space remaining on the default 4GB partition for extra kernels, initrd's etc.
# The code below and all of the resizing works perfectly though, so this may be re-purposed
# for the all-in-one Slackware Installer SD card images once those are developed.
#   # By default Slackware AArch64's installer image contains a 4GB single partition.
#   # Offer to resize it if seems like the 'stock' image, and handle failures.
#   sanitycheckbootpart ${ARMEDSLACK_MMCBOOTPART} && {
#      offerresizebootpart && {
#         # There's no possibility to retry here.
#         clear
#         resizebootpart ${ARMEDSLACK_MMCBOOTPART}
#         estat=$?
#         if [ $estat -gt 0 ]; then
#           offerresizebootfail ; exit 1
#          else
#           offerresizebootsuccess
#         fi
#       }
#   }

  # By default, we will repurpose the SD card as the OS's /boot. Some users
  # want to handle /boot themselves.
  BOOTSELECTION=SD
  [ -f /.bootsel_alt ] && {
     dialog --title "Boot Device Selection" \
        --yes-label "SD Card" \
        --no-label "Alternate Storage" \
        --yesno \
        "Default: The installer SD card is repurposed as /boot.
U-Boot (on SPI flash) reliably boots from SD on all
supported hardware.

Alternative: Place /boot on your target storage. U-boot
will attempt to locate extlinux.conf by scanning its
configured device list. Not all U-Boot builds can read
every storage type (e.g. PCIe SATA).

WARNING: If your U-Boot cannot access the chosen /boot
device, the system will not boot. Proceed only if you
know your U-Boot supports it." 17 60
   [ $? -eq 1 ] && BOOTSELECTION=alt ;}

   clear
   echo "Detected Slackware bootable SD card: ${ARMEDSLACK_MMCBOOTPART}"
   echo "Attempting to mount ..." # to ${T_PX}/boot"
   # Mount it:
   # For HWMs with this configuration profile (/boot on SD card), we need /boot mounted
   # so that the Kernel package installs its components into the correct place.
   mkdir -pm755 ${T_PX}/boot
   mount -v ${ARMEDSLACK_MMCBOOTPART} ${T_PX}/boot
   if [ $? -gt 0 ]; then
      # Perhaps turn this into a dialog? not that it helps ;-)
      echo    "ERROR: Unable to mount.  This will cause a boot failure."
      read -p "       You need to fix this manually."
      # Abort the installation:
      exit 1
   fi

   # If the user has chosen SD card storage, we need to adjust the OS's /etc/fstab
   # to mount by label.  Users who choose alternate storage may wish to also use labels,
   # but that's for them to configure manually where necessary.
   if [ "${BOOTSELECTION}" = "SD" ]; then
      cat << EOF >> ${TMP}/SeTnative
# This is the SD card that contains an ext4 file system, housing the
# Linux Kernel and Slackware OS InitRD (Operating System Initial RAM Disk).
$( printf "%-16s %-16s %-11s %-16s %-3s %s\n" LABEL=SLKboot /boot ext4 errors=remount-ro 0 1 )
EOF
   fi

   # If the user has opted to use alternate storage for their /boot, we need to
   # prevent U-Boot finding the extlinux.conf on the SD card - because mmc devices
   # are (by default) early in U-Boot's search list. Users could remove the SD card
   # upon completion of the Slackware installation, but this author doesn't remove
   # SD cards during development, so we just need to deactivate it -- by renaming it.
   [ "${BOOTSELECTION}" = "alt" ] && {
      echo "Deactivating the SD card's U-Boot extlinux.conf ..."
      mv -f ${T_PX}/boot/extlinux/extlinux.conf{,_deactivated}
      echo "Umounting SD card"
      umount ${T_PX}/boot
      if [ $? -gt 0 ]; then
         # Perhaps turn this into a dialog? not that it helps ;-)
         echo    "ERROR: Unable to umount.  This will cause an installation failure."
         read -p "       You need to fix this manually."
         # Abort the installation:
         exit 1
      fi ;}

fi

# Search for 'SLKhwm_bw' partition.
# This contains the Hardware Model's initial boot loader and other
# boot assets such as Firmware.
# Hardware Models such as the RK3399 have the Boot Loader within
# flash, so don't have a 'SLKhwm_bw' partition.
# An example that does have a 'bootware' partition is the Raspberry Pi4.
#
# Scan MMC sub system first, as this is the preferred option since we
# ship this file system on the Slackware Installer disk/SD image:
for mmcdev in /dev/mmcblk* ; do
   { ( e2label ${mmcdev} 2>/dev/null | tail -n1 | rev \
       | awk '{print $1}' | rev | tr -d "'" | \
       grep -Eq '^SLKhwm_bw$' ) && ARMEDSLACK_HWMBW_PART=${mmcdev} ;}
done
# If we didn't find one on the MMC sub system, widen the search to
# all block devices:
# If we still don't have one, the variable is null. This is acceptable
# since only the RPi has this file system presently.
# If the user wants to put this file system on their non-MMC storage,
# they will need to rename/remove the SD card first to avoid it being
# detected here.
[ -z "${ARMEDSLACK_HWMBW_PART}" ] && ARMEDSLACK_HWMBW_PART=$( blkid -L SLKhwm_bw )

# If we found the 'SLKhwm_bw' partition, add it to the OS' /etc/fstab
# and mount it within the Installer.
#
# This isn't required to be mounted at install time,
# but it's handy for the user if they perform any post OS installation
# tasks from within the Installer prior to booting the OS.
#
[ ! -z "${ARMEDSLACK_HWMBW_PART}" ] && {
   # This will be mounted within the OS as /boot/platform/hwm_bw
   # Add it to the OS' /etc/fstab:
cat << EOF >> ${TMP}/SeTnative
# Hardware Model's initial Boot Loader assets:
$(  printf "%-16s %-16s %-6s %-16s %-3s %s\n" LABEL=SLKhwm_bw /boot/platform/hwm_bw vfat errors=remount-ro 0 1 )
EOF

   echo "Detected Hardware Model Bootware partition: ${ARMEDSLACK_HWMBW_PART}"
   echo "Attempting to mount ..."
   # Mount it:
   # If it fails to mount, it's not an immediate issue (although it'll cause
   # errors when the OS boots), so we'll not stall progress.
   mkdir -pm755 ${T_PX}/boot/platform/hwm_bw
   mount -v ${ARMEDSLACK_HWMBW_PART} ${T_PX}/boot/platform/hwm_bw ;}

# Wait a few seconds, otherwise the user sees some text flash on the screen
# momentarily which may cause concern, as often that's what happens to
# error messages.
sleep 4
clear
