#!/bin/sh
# Copyright (c) 2012-2019, Piotr Karbowski <piotr.karbowski@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
#
#    * Redistributions of source code must retain the above copyright notice, this list
#      of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright notice, this list
#      of conditions and the following disclaimer in the documentation and/or other
#      materials provided with the distribution.
#    * Neither the name of the Piotr Karbowski nor the names of its contributors may be
#      used to endorse or promote products derived from this software without specific
#      prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 US
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# This script meant to create /dev/disk/by-* and /dev/mapper/* symlinks.
# and remove them after storage device is removed.
# the /dev/disk/by-* handling based on the idea and proof of concept from BitJam.

# debug
#exec >> /run/debug-mdev 2>&1
#set -x
#echo '### ENV:'
#env
#echo '### CODE:'
#

umask 077

storage_dir="/dev/.mdev-like-a-boss"
[ -d "${storage_dir}" ] || mkdir "${storage_dir}"

[ "${MDEV}" ] || exit 2

create_uuid_label_symlink() {
	local target_dir="/dev/disk/by-${1}"
	local target_symlink="${target_dir}/${2}"
	[ -e "${target_symlink}" ] && return
	mkdir -p "${target_dir}"
	ln -snf "/dev/${MDEV}" "${target_symlink}"
	echo "${target_symlink}" >"${storage_dir}/storage_symlink_${1}_${MDEV}"
}

add_symlinks() {
	# Skip temp cryptsetup nodes.
	case "${MDEV}" in
		'dm-'[0-9]*)
			case "$(cat "/sys/block/${MDEV}/dm/name")" in
				'temporary-cryptsetup-'[0-9]*)
					return 0
				;;
			esac
		;;
	esac

	if command -v blkid >/dev/null 2>&1; then
		local field name value UUID LABEL TYPE PTTYPE PARTUUID
		local blkid_output="$(blkid "/dev/${MDEV}")"
		eval "${blkid_output#*: }"

		[ "${UUID}" ] && create_uuid_label_symlink 'uuid' "${UUID}"
		[ "${LABEL}" ] && create_uuid_label_symlink 'label' "${LABEL}"
		[ "${PARTUUID}" ] && create_uuid_label_symlink 'partuuid' "${PARTUUID}"
	fi

	if [ -f "/sys/block/${MDEV}/dm/name" ]; then
		[ -d '/dev/mapper' ] || mkdir '/dev/mapper'
		if ! [ -c '/dev/mapper/control' ]; then
			awk '$2 == "device-mapper" { foo = system("mknod /dev/mapper/control c 10 " $1); exit foo }' /proc/misc || exit 1
		fi
		local dmname="$(cat "/sys/block/${MDEV}/dm/name")"
		if [ "${dmname}" ]; then
			local target_symlink="/dev/mapper/${dmname}"
			[ -e "${target_symlink}" ] && return
			ln -snf "/dev/${MDEV}" "${target_symlink}"
			echo "${target_symlink}" >"${storage_dir}/storage_symlink_mapper_${MDEV}"
		fi
	fi
}

set_readahead() {
	read_ahead_kb_control="/sys/class/block/${MDEV}/queue/read_ahead_kb"
	new_read_ahead_kb="2048"
	if [ -f "${read_ahead_kb_control}" ]; then
		read_ahead_kb="$(cat "${read_ahead_kb_control}")"
		if [ "${read_ahead_kb}" -lt "${new_read_ahead_kb}" ]; then
			logger -t mdev "Changing ${MDEV} read_ahead_kb from ${read_ahead_kb} to ${new_read_ahead_kb}"
			echo -n "${new_read_ahead_kb}" >"${read_ahead_kb_control}"
		fi
	fi
}

drop_symlinks() {
	local type
	for type in uuid label mapper; do
		[ -f "${storage_dir}/storage_symlink_${type}_${MDEV}" ] || continue
		local target_symlink="$(cat "${storage_dir}/storage_symlink_${type}_${MDEV}" 2>/dev/null)"
		[ "${target_symlink}" ] || continue

		local target_symlink_device="$(readlink "${target_symlink}")"
		if [ "${target_symlink_device}" = "/dev/${MDEV}" ]; then
			rm "${target_symlink}"
		fi
		rm "${storage_dir}/storage_symlink_${type}_${MDEV}"
	done
}

case "${ACTION}" in
	'add'|'')
		add_symlinks
		set_readahead
	;;
	'remove')
		drop_symlinks
	;;
esac