#!/usr/bin/bash
#
# Copyright (c) 2022, 2024, Oracle and/or its affiliates.
#
# Check the disk label and if it is ASM disk then add it to iofilter map.
#

ORACLE_ASM_IOFILTER_MAP_PATH="/sys/fs/bpf/iofilter_asm"
IOFILTER_ASM="/usr/lib/oracleasm/iofilter_asm"
ORACLE_ASM_CONFIG_FILE="/etc/sysconfig/oracleasm"

#
# Log command output and errors to syslog file
#

asm_log()
{
    while read -r line; do
        echo "$line" | logger
    done
}

iofilter_map_add_path ()
{
    # Check if iofilter_asm program is available
    if [ ! -f "${IOFILTER_ASM}" ]
    then
	return  0
    fi

    if [ "$#" -lt "3" ]
    then
        echo "iofilter_map_add_path(): Requires arguments"
	return 0
    fi

    disk_path="$1"
    dev_major="$2"
    dev_minor="$3"

    if [ ! -b "$disk_path" ]
    then
        echo "iofilter_map_add_path: invalid argument $disk_path" | asm_log
	return  1
    fi

    # Get disk type (disk or partition)
    disk_type=$(lsblk -dn -o type ${disk_path})
    # Get size of the disk
    disk_size=$(blockdev --getsz ${disk_path})
    if [ $? != 0 ]
    then
        echo "iofilter_map_add_path: failed to get disk size for $1" | asm_log
        return 1
    fi
    range_end_sec=$(expr $disk_size - 1)

    if [ "${disk_type}" = "part" ]
    then
	dev_dir=$(dirname $disk_path)
	if [ "${dev_dir}" = "/dev/mapper" ]
	then
	    dm_path=$disk_path
	    info=$(dmsetup table $disk_path 2>/dev/null)
	else
	    dev=$(basename ${disk_path})
	    info=$(blockdev --report ${disk_path} 2>/dev/null | grep "$dev")
	fi
	if [ -z "${info}" ]
	then
	    echo "iofilter_map_add: failed to get disk offset $1" | \
	        asm_log
	    return 1
	else
	    offset="$(echo $info | awk '{print $5}')"
	fi
    else
	offset=0
    fi

    # For the whole disk or for the DM device path just add
    # the entry in the iofilter map. If it is a partition
    # then the parent entry is also to be updated in the map.
    if [ "${disk_type}" != "part" -o "${disk_path}" = "${dm_path}" ]
    then
        # regular whole disk or DM device
	${IOFILTER_ASM} --add "${disk_path}" --range "0-${range_end_sec}" \
	    --offset "${offset}" --type write \
	    --pin "${ORACLE_ASM_IOFILTER_MAP_PATH}" \
	    --devnum "$dev_major:$dev_minor" | asm_log
    else
	parent_disk=$(lsblk -no PKNAME ${disk_path})
	${IOFILTER_ASM} --add "${disk_path}" --range "0-${range_end_sec}" \
	    --offset "${offset}" --type write \
	    --pin "${ORACLE_ASM_IOFILTER_MAP_PATH}" \
	    --parent "/dev/${parent_disk}" \
	    --devnum "$dev_major:$dev_minor" | asm_log
    fi

    if [ ${PIPESTATUS[0]} != 0 ]
    then
        #DEBUG echo "Failed to add disk ${disk_path} to iofilter map"
        return 1
    fi

    echo "Added disk ${disk_path} to iofilter map" | asm_log

    return 0
}

# Set the appropriate permissions on the disk paths for this disk.
# Returns 0 on success, 1 on error, 2 on already correct
#
perm_disk_path()
{
    if [ "$#" -lt "1" -o -z "$1" ]
    then
        die "perm_disk_path(): Requires an argument"
    fi

    disk_path="$1"

    OUTPUT=$(ls -Hld "$disk_path" 2>/dev/null)
    if [ $? != 0 ]
    then
        return 1
    fi

    OUID=$(echo "$OUTPUT" | awk '{print $3}')
    OGID=$(echo "$OUTPUT" | awk '{print $4}')

    if [ "$OUID" = "${ORACLEASM_UID:-root}" -a "$OGID" = "${ORACLEASM_GID:-root}" ]
    then
	return 0
    fi

    chown "${ORACLEASM_UID:-root}:${ORACLEASM_GID:-root}" "$disk_path" 2>&3
    if [ $? != 0 ]
    then
        return 1
    fi

    chmod 0660 "$disk_path" 2>&3
    if [ $? != 0 ]
    then
        return 1
    fi

    return 0
}

# Load config parameters file
[ -f "${ORACLE_ASM_CONFIG_FILE}" ] && . "${ORACLE_ASM_CONFIG_FILE}"

# Force LC_ALL=C
export LC_ALL=C
 
USAGE="<disk-label> <device-path> <device-major> <device-minor>"

version=

exec 3>/dev/null

while case "$#" in 0) break ;; esac
do
    case "$1" in
    -h|--help)
        help=t
        ;;
    -V|--version)
        version=t
        ;;
    -*)
        usage=t
        ;;
    *)
        break
        ;;
    esac
    shift
done

if [ "$help" = "t" -o "$usage" = "t" ]
then
    usage
fi

if [ "$version" = "t" ]
then
    version
fi

if [ ! -e "${ORACLE_ASM_IOFILTER_MAP_PATH}" ]
then
	# No iofilter map exists
	exit 0
fi

if [ $# -lt 4 ]
then
    usage
    exit 1
fi

LABEL="$1"
DISK_NAME="$2"
DEV_MAJOR="$3"
DEV_MINOR="$4"

# Add the ASM disk element to the iofilter map.
iofilter_map_add_path "${DISK_NAME}" "${DEV_MAJOR}" "${DEV_MINOR}"

# fix permissions to the device file
perm_disk_path "${DISK_NAME}"

exit 0
