#!/bin/sh
# Filename:      forensic-mark-readonly
# Purpose:       force block devices to read-only mode when booting with boot option read-only
# Authors:       grml-team (grml.org), (c) Michael Prokop <mika@grml.org>
# Bug-Reports:   see http://grml.org/bugs/
# License:       This file is licensed under the GPL v2 or any later version.
################################################################################

# check for read-only bootoption
if ! grep -q read-only /proc/cmdline ; then
  exit 0
fi

# see linux source -> Documentation/admin-guide/sysfs-rules.rst
get_blockdev_dir() {
  for dir in /sys/subsystem/block /sys/class/block /sys/block ; do
    [ -d "${dir}" ] && echo "${dir}" && return
  done
}

base() {
  echo "${1##*/}"
}

dir() {
  echo "${1%/*}"
}

is_ro() {
  [ "$(blockdev --getro "$1")" = "1" ] && return 0 || return 1
}

if [ -z "${1:-}" ] ; then
  echo "Error: usage: <$0> <blockdevice>" >&2
  exit 1
fi

# accept /dev/foo from command line but also just "foo" from udev
case "$1" in
  /dev/*)
    BLOCK_DEVICE="$1"
    ;;
  *)
    BLOCK_DEVICE="/dev/$1"
    ;;
esac

SYS_DIR="$(get_blockdev_dir)"

base_device=$(base "${BLOCK_DEVICE}")
if [ -n "${SYS_DIR}" ] && [ -n "${base_device}" ] ; then
  tmp_parent="$(readlink -f "${SYS_DIR}"/*/"${base_device}")"
  if [ -d "${tmp_parent}" ] ; then
    parent_device=$(dir "${tmp_parent}")
    parent_device=$(base "${parent_device}")
    parent_device="/dev/${parent_device}"
  fi
  unset tmp_parent
fi

# support configuration file
if [ -r /etc/grml/forensic.conf ] ; then
  READONLY_MODE=""
  READONLY_IGNORE=""

  . /etc/grml/forensic.conf

  if [ "${READONLY_MODE:-}" = "disable" ] ; then
    logger -t forensic-mark-readonly "not setting '${BLOCK_DEVICE}' to read-only as disabled via config"
    exit 0
  fi

  if [ -n "${READONLY_IGNORE:-}" ] ; then
    if printf "%s\n" "${READONLY_IGNORE:-}" | grep -qw "${parent_device}" ; then
      if [ -n "${parent_device:-}" ] ; then
        logger -t forensic-mark-readonly "not setting '${BLOCK_DEVICE}' (parent device: '${parent_device}') to read-only as present in ignore list"
      else
        logger -t forensic-mark-readonly "not setting '${BLOCK_DEVICE}' to read-only as present in ignore list"
      fi
      exit 0
    fi
  fi
fi

if is_ro "${BLOCK_DEVICE}" ; then
  logger -t forensic-mark-readonly "device ${BLOCK_DEVICE} already set to read-only mode, nothing to do"
elif [ -n "${parent_device}" ] && ! is_ro "${parent_device}" ; then
  logger -t forensic-mark-readonly "parent device ${parent_device} is set read-write, not modifying"
  logger -t forensic-mark-readonly "use blockdev --setro ${BLOCK_DEVICE} to set it manually"
else
  logger -t forensic-mark-readonly "setting ${BLOCK_DEVICE} [${ID_SERIAL}] to read-only"

  if blockdev --setro "${BLOCK_DEVICE}" ; then
    logger -t forensic-mark-readonly "|-> done; execute 'blockdev --setrw ${BLOCK_DEVICE}' to unlock"
  else
    logger -t forensic-mark-readonly "|-> error while executing blockdev: $(blockdev --setro "${BLOCK_DEVICE}" 2>&1)"
  fi
fi

## END OF FILE #################################################################
