#!/bin/bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh

# This script generates a service that manages a dm-verity device for the chosen USR partition

set -e

UNIT_DIR="${1:-/tmp}"

# env var for testing
if [[ -n "${VERITY_GENERATOR_CMDLINE}" ]]; then
    cmdline=( ${VERITY_GENERATOR_CMDLINE} )
else
    cmdline=( $(</proc/cmdline) )
fi

# Usage: cmdline_arg name default_value
cmdline_arg() {
    local name="$1" value="$2"
    for arg in "${cmdline[@]}"; do
	if [[ "${arg%%=*}" == "${name}" ]]; then
	    value="${arg#*=}"
	fi
    done
    echo "${value}"
}

usr=$(cmdline_arg verity.usr)
usrhash=$(cmdline_arg verity.usrhash)

case "${usr}" in
    LABEL=*)
	usr="$(echo $usr | sed 's,/,\\x2f,g')"
	usr="/dev/disk/by-label/${usr#LABEL=}"
	;;
    UUID=*)
	usr="${usr#UUID=}"
	usr="$(echo $usr | tr "[:upper:]" "[:lower:]")"
	usr="/dev/disk/by-uuid/${usr}"
	;;
    PARTUUID=*)
	usr="${usr#PARTUUID=}"
	usr="$(echo $usr | tr "[:upper:]" "[:lower:]")"
	usr="/dev/disk/by-partuuid/${usr}"
	;;
    PARTLABEL=*)
	usr="/dev/disk/by-partlabel/${usr#PARTLABEL=}"
	;;
esac

# Only proceed if the source is a path.
if [[ "${usr}" != /* ]]; then
    exit 0
fi


# Only generate the service if we have sufficient parameters.
if [[ -n "${usr}" && -n "${usrhash}" ]]; then
    device=$(systemd-escape --suffix=device --path "${usr}")

    cat >"${UNIT_DIR}/verity-setup.service" <<-EOF
	# Automatically generated by verity-generator

	[Unit]
	Description=Verity Setup for /dev/mapper/usr
	SourcePath=/proc/cmdline
	DefaultDependencies=no
	IgnoreOnIsolate=true
	BindsTo=dev-mapper-usr.device
	BindsTo=${device}
	After=${device}

	[Service]
	Type=oneshot
	RemainAfterExit=yes
	# Try to query the filesystem size dynamically but otherwise fall back to the expected value from the image GPT layout
	ExecStart=/bin/sh -c '/sbin/veritysetup --panic-on-corruption --hash-offset="\$(e2size ${usr} || echo 1065345024)" open "${usr}" usr "${usr}" "${usrhash}"'
	# If there's a hash mismatch during table initialization,
	# veritysetup reports it on stderr but still exits 0, and
	# dev-mapper-usr.device ends up waiting indefinitely. Manually
	# check the target status and fail if invalid.
	ExecStart=/bin/bash -c 'read off len tgt status addl <<<\$(dmsetup status usr); [[ "\$\${status}" == V ]]'
	ExecStop=/sbin/veritysetup close usr
	EOF

    requires_dir="${UNIT_DIR}/dev-mapper-usr.device.requires"
    mkdir -p "${requires_dir}"
    ln -sf "../verity-setup.service" "${requires_dir}/verity-setup.service"
fi
