#!/bin/bash
# SPDX-Version: 3.0
# SPDX-CreationInfo: 2025-06-17; WEIDNER, Marc S.; <msw@coresecret.dev>
# SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.installer.git
# SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency
# SPDX-FileCopyrightText: 2024-2025; WEIDNER, Marc S.; <msw@coresecret.dev>
# SPDX-FileType: SOURCE
# SPDX-License-Identifier: EUPL-1.2 OR LicenseRef-CCLA-1.0
# SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework.
# SPDX-PackageName: CISS.debian.installer
# SPDX-Security-Contact: security@coresecret.eu

guard_sourcing

#######################################
# '/etc/fstab' entry writer and logger.
# Globals:
#   TARGET
# Arguments:
#   1: UUID
#   2: Mount Path
#   3: Filesystem
#   4: Mount Options
#   5: Pass value, while Dump value is hardcoded always "0", e.g., "1"
# Returns:
#   0: on success
#######################################
write_fstab() {
  declare _uuid="$1" _path="$2" _fs="$3" _opts="$4" _pass="$5"
  printf "UUID=%s %s %s %s 0 %s\n" "${_uuid}" "${_path}" "${_fs}" "${_opts}" "${_pass}" >> "${TARGET}/etc/fstab"
  do_log "info" "true" "fstab entry generated: 'UUID=${_uuid}    ${_path}    ${_fs}    ${_opts}    0 ${_pass}'."
  return 0
}

#######################################
# Generate target '/etc/fstab' entries.
# Globals:
#   HMP_EPHEMERAL_ENCLABEL
#   HMP_PATH_FSUUID
#   MAP_MOUNTPATH_DEV
#   TARGET
#   VAR_RECIPE_STRING
#   VAR_SETUP_PART
# Arguments:
#   None
# Returns:
#   0: on success
#######################################
generate_fstab() {
  ### Generate '${TARGET}/etc/fstab' header.
  : >| "${TARGET}/etc/fstab"
  chmod 0600 "${TARGET}/etc/fstab"

  cat << 'EOF' >> "${TARGET}/etc/fstab"
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# systemd generates mount units based on this file, see systemd.mount(5).
# Please run 'systemctl daemon-reload' after making changes here.
#
# <file system>                            <mount point>  <type>       <options>                                  <dump>  <pass>

# Secure tmpfs mounts for a hardened system
# Mount the proc filesystem to provide process and kernel information
proc                                       /proc          proc         nodev,nosuid,noexec,hidepid=2                         0 0
# Mount sysfs to expose kernel device information to user space
sysfs                                      /sys           sysfs        defaults                                              0 0
# Mount the devpts filesystem to enable pseudo-terminal support for user sessions
devpts                                     /dev/pts       devpts       gid=5,mode=620                                        0 0
# Restrict /dev/shm to shared memory, limit size, prevent code execution
tmpfs                                      /dev/shm       tmpfs        rw,nodev,noexec,nosuid,relatime,size=1G               0 0
# System runtime directory in RAM; do not set noexec here for compatibility
tmpfs                                      /run           tmpfs        mode=0755,nodev,nosuid                                0 0

EOF

  ### Generate '${TARGET}/etc/fstab' special entries '/' '/boot' '/boot/efi'.
  ### Define the order of the special paths.
  declare -a ary_path_order; ary_path_order=("/" "/boot" "/boot/efi")
  declare -a ary_skip=("/" "/boot" "/boot/efi" "/recovery")
  declare var_path var_entry var_part var_dev var_key var_uuid var_fs_btrfs_compress var_fs_btrfs_level var_fs_btrfs_subvolume \
    var_fs_version var_mount_options var_mount_optsnap var_btrfs_compression

  for var_path in "${ary_path_order[@]}"; do

    var_entry=$(yq e -r '.recipe.*.dev.* | select(.mount.path == "'"${var_path}"'")' "${VAR_SETUP_PART}")

    if [[ -z "${var_entry}" ]]; then
      continue
    fi

    var_part=$(echo "${var_entry}" | yq e 'path | .[-2]' -)
    var_dev=$(echo "${var_entry}" | yq e 'path | .[-3]' -)
    var_key="UUID_${var_path}"
    var_uuid="${HMP_PATH_FSUUID[${var_key}]}"
    var_fs_btrfs_compress=$(yq_val ".recipe.${VAR_RECIPE_STRING}.dev.${var_dev}.${var_part}.filesystem.btrfs.compress"   "${VAR_SETUP_PART}")
    var_fs_btrfs_level=$(yq_val ".recipe.${VAR_RECIPE_STRING}.dev.${var_dev}.${var_part}.filesystem.btrfs.level"         "${VAR_SETUP_PART}")
    var_fs_btrfs_subvolume=$(yq_val ".recipe.${VAR_RECIPE_STRING}.dev.${var_dev}.${var_part}.filesystem.btrfs.subvolume" "${VAR_SETUP_PART}")
    var_fs_version=$(yq_val ".recipe.${VAR_RECIPE_STRING}.dev.${var_dev}.${var_part}.filesystem.version"                 "${VAR_SETUP_PART}")
    var_mount_options=$(yq_val ".recipe.${VAR_RECIPE_STRING}.dev.${var_dev}.${var_part}.mount.options"                   "${VAR_SETUP_PART}")
    var_mount_optsnap=$(yq_val ".recipe.${VAR_RECIPE_STRING}.dev.${var_dev}.${var_part}.mount.optsnap"                   "${VAR_SETUP_PART}")

    if [[ "${var_fs_version,,}" == btrfs ]]; then

      if [[ "${var_fs_btrfs_level:-0}" == 0 ]]; then
        var_btrfs_compression="compress=no"
      else
        var_btrfs_compression="compress=${var_fs_btrfs_compress}:${var_fs_btrfs_level}"
      fi

      write_fstab "${var_uuid}" "${var_path}" "${var_fs_version}" "${var_mount_options},${var_btrfs_compression}" "1"

      if [[ -n "${var_fs_btrfs_subvolume}" ]]; then

        if [[ "${var_path}" == "/" ]]; then

          write_fstab "${var_uuid}" "/.snapshots" "${var_fs_version}" "${var_mount_optsnap}" "0"

        else

          write_fstab "${var_uuid}" "${var_path}/.snapshots" "${var_fs_version}" "${var_mount_optsnap}" "0"

        fi

      fi
      continue

    elif [[ "${var_fs_version,,}" == ext4 ]]; then

      write_fstab "${var_uuid}" "${var_path}" "${var_fs_version}" "${var_mount_options}" "1"
      continue

    elif [[ "${var_fs_version,,}" == fat32 ]]; then

      write_fstab "${var_uuid}" "${var_path}" "vfat" "${var_mount_options}" "2"
      continue

    fi

  done

  ### Generate '${TARGET}/etc/fstab' remaining entries.
  for var_path in "${!MAP_MOUNTPATH_DEV[@]}"; do

    if validation_array "${var_path}" "${ary_skip[@]}"; then
      continue
    fi

    var_entry=$(yq e -r '.recipe.*.dev.* | select(.mount.path == "'"${var_path}"'")' "${VAR_SETUP_PART}")

    if [[ -z "${var_entry}" ]]; then
      continue
    fi

    var_part=$(echo "${var_entry}" | yq e 'path | .[-2]' -)
    var_dev=$(echo "${var_entry}" | yq e 'path | .[-3]' -)
    var_key="UUID_${var_path}"
    var_uuid="${HMP_PATH_FSUUID[${var_key}]}"
    var_fs_btrfs_compress=$(yq_val ".recipe.${VAR_RECIPE_STRING}.dev.${var_dev}.${var_part}.filesystem.btrfs.compress"   "${VAR_SETUP_PART}")
    var_fs_btrfs_level=$(yq_val ".recipe.${VAR_RECIPE_STRING}.dev.${var_dev}.${var_part}.filesystem.btrfs.level"         "${VAR_SETUP_PART}")
    var_fs_btrfs_subvolume=$(yq_val ".recipe.${VAR_RECIPE_STRING}.dev.${var_dev}.${var_part}.filesystem.btrfs.subvolume" "${VAR_SETUP_PART}")
    var_fs_version=$(yq_val ".recipe.${VAR_RECIPE_STRING}.dev.${var_dev}.${var_part}.filesystem.version"                 "${VAR_SETUP_PART}")
    var_mount_options=$(yq_val ".recipe.${VAR_RECIPE_STRING}.dev.${var_dev}.${var_part}.mount.options"                   "${VAR_SETUP_PART}")
    var_mount_optsnap=$(yq_val ".recipe.${VAR_RECIPE_STRING}.dev.${var_dev}.${var_part}.mount.optsnap"                   "${VAR_SETUP_PART}")

    if [[ "${var_fs_version,,}" == btrfs ]]; then

      if [[ "${var_fs_btrfs_level:-0}" == 0 ]]; then
        var_btrfs_compression="compress=no"
      else
        var_btrfs_compression="compress=${var_fs_btrfs_compress}:${var_fs_btrfs_level}"
      fi

      write_fstab "${var_uuid}" "${var_path}" "${var_fs_version}" "${var_mount_options},${var_btrfs_compression}" "2"

      if [[ -n "${var_fs_btrfs_subvolume}" ]]; then
        write_fstab "${var_uuid}" "${var_path}/.snapshots" "${var_fs_version}" "${var_mount_optsnap}" "0"
      fi
      continue

    elif [[ "${var_fs_version,,}" == ext4 ]]; then

      write_fstab "${var_uuid}" "${var_path}" "${var_fs_version}" "${var_mount_options}" "2"
      continue

    elif [[ "${var_fs_version,,}" == fat32 ]]; then

      write_fstab "${var_uuid}" "${var_path}" "vfat" "${var_mount_options}" "2"
      continue

    fi

  done

  cat << 'EOF' >> "${TARGET}/etc/fstab"

/dev/sr0                                   /media/cdrom0  udf,iso9660  user,noauto                                           0 0

EOF
  do_log "info" "true" "fstab entry generated: '/dev/sr0  /media/cdrom0  udf,iso9660  user,noauto  0 0'."

  ### Add entry for 'SWAP' device.
  var_path="SWAP"
  write_fstab "/dev/mapper/${HMP_EPHEMERAL_ENCLABEL["${var_path}"]}" "none" "swap" "defaults" "0"

  ### Add entry for '/tmp' device.
  var_path="/tmp"
  write_fstab "/dev/mapper/${HMP_EPHEMERAL_ENCLABEL["${var_path}"]}" "/tmp" "ext4" "defaults,rw,nodev,nosuid,relatime" "0"

  return 0
}
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
