#!/usr/bin/bash
#
# Copyright (c) 2006, 2025, Oracle and/or its affiliates.
#
# Return a disk to the operating system
#


# Force LC_ALL=C
export LC_ALL=C

USAGE="[-v] [-f] <label>|<device> <new_label>"

exec 3>/dev/null

help=
verbose=
version=
usage=
force=
restore=

while case "$#" in 0) break ;; esac
do
    case "$1" in
    -f|--force)
        force=t
        ;;
    -r|--restore)
        restore=t
        force=t
        ;;
    -v|--verbose)
        verbose=t
        exec 3>&2
        ;;
    -V|--version)
        version=t
        ;;
    -h|--help)
        help=t
        ;;
    -*)
        usage=t
        ;;
    *)
        break
        ;;
    esac
    shift
done


# Load configuration
. oracleasm-Xshlib
check_oracleasm_config

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

if [ $(echo $UID) != 0 ]
then
    echo "This operation requires root privileges. Please run as root"
    exit 1
fi

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

relabel_disk()
{
    if [ "$#" != "2" -o -z "$1" -o -z "$2" ]
    then
        die "relabel_disk(): Requires an argument"
    fi

    OLD="$1"
    NEW="$2"

    old_path="$(asm_disk_path "${OLD}")"
    new_path="$(asm_disk_path "${NEW}")"

    INFO="$(oracleasm-read-label "${old_path}" 2>&3)"
    ERRNO=$?
    if [ -z "$INFO" ]
    then
	if [ "$ORACLEASM_DRIVER_SUPPORTED" = "true" ]
	then
            case "$ERRNO" in
            250|237)
                # ENXIO or ENODEV means its a bad device, we should remove
                # the disk!
                oracleasm-clean-disk "${OLD}" 2>&3
                if [ $? != 0 ]
                then
                    echo "Unable to remove stale ASM disk \"${OLD}\"" | asm_log 1
                fi
                ;;
            *)
                ;;
            esac
	fi
        echo "Unable to open ASM disk \"${OLD}\"" | asm_log 1
        exit 1
    fi

    if [ "$force" != "t" ]
    then
	check_disk_group_member "${old_path}"
    fi

    LABEL="$(echo "$INFO" | cut -f1 -d:)"
    case "$LABEL" in
    "$OLD")
        old_dispos="old"
        error=

	if [ "$ORACLEASM_DRIVER_SUPPORTED" = "true" ]
	then
	    echo -n "Instantiating disk \"${NEW}\": "
            oracleasm-instantiate-disk "${old_path}" "${NEW}" 2>&3
            if [ $? != 0 ]
            then
                echo "failed"
                echo "Unable to instantiate disk \"${NEW}\"" | asm_log 1
                exit 1
            fi
            perm_disk "${new_path}"
            if [ $? = 1 ]
            then
                echo "failed"
                echo "Unable to change ownership of disk \"${NEW}\"" | asm_log 1
                exit 1
            fi
	    echo "done"
	fi

	# For ASMLIB v3, BPF iofilter map is setup which blocks any IO to
	# the block range of the whole ASM disk other than IO done through
	# ASMLIB. To update the label temporarily remove the filter entry
	# for this disk and add it back after the relabel is done.
	iofilter_map_delete ${OLD}
	if [ $? != 0 ]
	then
            echo "Failed to delete disk \"${LABEL}\" from iofilter map" | \
		asm_log 1
	fi

        echo -n "Writing disk header: "
        oracleasm-write-label -f "${old_path}" "${NEW}" 2>&1 | asm_log 2
        if [ "${PIPESTATUS[0]}" != 0 ]
        then
	    if [ "$ORACLEASM_DRIVER_SUPPORTED" = "true" ]
	    then
                oracleasm-clean-disk "${NEW}" 2>&3
	    fi
            echo "failed"
            echo "Unable to label disk \"${NEW}\"" | asm_log 1
            exit 1
        fi
        echo "done"

	if [ "$ORACLEASM_DRIVER_SUPPORTED" = "false" ]
	then
    	    new_path="$(asm_disk_path "${NEW}")"
            perm_disk "${NEW}"
            if [ $? = 1 ]
            then
                echo "failed"
                echo "Unable to change ownership of disk \"${NEW}\"" | asm_log 1
                exit 1
            fi
	    # Restore the iofilter map entry for this disk
	    iofilter_map_add ${NEW}
	    if [ $? != 0 ]
	    then
		echo "Failed to add disk \"${LABEL}\" to iofilter map" | \
		    asm_log 1
	    fi
	fi
        ;;
    "$NEW")
        old_dispos="stale"
        error="Disk \"${OLD}\" already labeled for \"${NEW}\""
        ;;
    *)
        old_dispos="$stale"
        error "Disk \"${OLD}\" isn't labeled \"${OLD}\""
        ;;
    esac

    if [ "$ORACLEASM_DRIVER_SUPPORTED" = "true" ]
    then
	echo -n "Removing ${old_dispos} ASM disk \"${OLD}\": "
        oracleasm-clean-disk "${OLD}" 2>&3
        if [ $? != 0 ]
        then
            echo "failed"
            echo "Unable to remove ${old_dispos} ASM disk \"${OLD}\"" | asm_log 1
            exit 1
	fi
	echo "done"
    fi

    if [ -n "$error" ]
    then
        echo "$error" | asm_log 1
        exit 1
    fi

    # trigger udevadm to clear stale link in /dev/disk/by-label/
    udevadm trigger 2>/dev/null
    # refresh blkid cache so it has updated info
    blkid >/dev/null

    return 0
}

relabel_device()
{
    if [ "$#" != "2" -o -z "$1" -o -z "$2" ]
    then
        die "relabel_device(): Requires an argument"
    fi

    DEV="$1"
    NEW="$2"

    INFO="$(oracleasm-read-label "${DEV}" 2>&3)"
    ERRNO=$?
    if [ -z "$INFO" ]
    then
        if [ "$ERRNO" = 0 ]
        then
            echo "Device \"${DEV}\" is not labeled for ASM" | asm_log 1
            exit 1
        else
            echo "Unable to read device \"${DEV}\"" | asm_log 1
            exit 1
        fi
    fi

    if [ "$force" != "t" ]
    then
	check_disk_group_member "${DEV}"
    fi

    OLD="$(echo "$INFO" | cut -f1 -d:)"
    if [ "${OLD}" = "${NEW}" ]
    then
        echo "Device \"${DEV}\" is already labeled \"${NEW}\"" | asm_log 1
        exit 1
    elif [ -n "$OLD" ]
    then
	if [ "$ORACLEASM_DRIVER_SUPPORTED" = "true" ]
	then
	    old_path="$(asm_disk_path "${OLD}")"
	    dev_major_minor="$(stat -Lc '%t %T' "$DEV")"
	    old_major_minor="$(stat -Lc '%t %T' "$old_path")"
	    if [ -n "$dev_major_minor" -a "$dev_major_minor" = "$old_major_minor" ]
            then
		echo -n "Removing old ASM disk \"${OLD}\": "
		oracleasm-clean-disk "${OLD}" 2>&3
		if [ $? != 0 ]
		then
		    echo "failed"
		    echo "Unable to remove ${old_dispos} ASM disk \"${OLD}\"" | asm_log 1
		    exit 1
		fi
		echo "done"
	    fi
	fi
    fi

    # For ASMLIB v3, BPF iofilter map is setup which blocks any IO to
    # the block range of the whole ASM disk other than IO done through
    # ASMLIB. To update the label temporarily remove the filter entry
    # for this disk and add it back after the relabel is done.
    iofilter_map_delete ${OLD}
    if [ $? != 0 ]
    then
        echo "Failed to delete disk \"${LABEL}\" from iofilter map" | \
	    asm_log 1
    fi

    echo -n "Writing disk header: "
    oracleasm-write-label -f "${DEV}" "${NEW}" 2>&1 | asm_log 2
    if [ ${PIPESTATUS[0]} != 0 ]
    then
	if [ "$ORACLEASM_DRIVER_SUPPORTED" = "true" ]
	then
	    oracleasm-instantiate-disk "${DEV}" "${OLD}" 2>&3
	fi
	echo "failed"
	echo "Unable to label device \"${DEV}\"" | asm_log 1
	exit 1
    fi
    echo "done"

    if [ "$ORACLEASM_DRIVER_SUPPORTED" = "true" ]
    then
	echo -n "Instantiating disk \"${NEW}\": "
	oracleasm-instantiate-disk "${DEV}" "${NEW}" 2>&3
	if [ $? != 0 ]
	then
	    echo "failed"
	    echo "Unable to instantiate disk \"${NEW}\"" | asm_log 1
	    exit 1
	fi
	echo "done"
    fi

    new_path="$(asm_disk_path "${NEW}")"
    perm_disk "${new_path}"
    if [ $? = 1 ]
    then
        echo "failed"
        echo "Unable to change ownership of disk \"${NEW}\"" | asm_log 1
        exit 1
    fi

    iofilter_map_add ${NEW}
    if [ $? != 0 ]
    then
	echo "Failed to add disk \"${LABEL}\" to iofilter map" | \
	    asm_log 1
    fi

    # trigger udevadm to clear stale link in /dev/disk/by-label/
    udevadm trigger 2>/dev/null
    # refresh blkid cache so it has updated info
    blkid >/dev/null

    return 0
}

restore_device()
{
    if [ "$#" != "2" -o -z "$1" -o -z "$2" ]
    then
        die "restore_device(): Requires an argument"
    fi

    DEV="$1"
    NEW="$2"

    new_path="$(asm_disk_path "${NEW}")"

    INFO="$(oracleasm-read-label -r "${DEV}" 2>&3)"
    ERRNO=$?
    if [ -z "$INFO" ]
    then
        if [ "$ERRNO" = 0 ]
        then
            echo "Device \"${DEV}\" is not labeled for ASM" | asm_log 1
            exit 1
        else
            echo "Unable to read device \"${DEV}\"" | asm_log 1
            exit 1
        fi
    fi
    if [ "$force" != "t" ]
    then
	check_disk_group_member "${DEV}"
    fi
    echo -n "Writing disk header: "
    oracleasm-write-label -f "${DEV}" "${NEW}" 2>&1 | asm_log 2
    if [ ${PIPESTATUS[0]} != 0 ]
    then
        oracleasm-instantiate-disk "${DEV}" "${OLD}" 2>&3
        echo "failed"
        echo "Unable to label device \"${DEV}\"" | asm_log 1
        exit 1
    fi
    echo "done"

    echo -n "Instantiating disk \"${NEW}\": "
    oracleasm-instantiate-disk "${DEV}" "${NEW}" 2>&3
    if [ $? != 0 ]
    then
        echo "failed"
        echo "Unable to instantiate disk \"${NEW}\"" | asm_log 1
        exit 1
    fi

    perm_disk "${new_path}"
    if [ $? = 1 ]
    then
        echo "failed"
        echo "Unable to change ownership of disk \"${NEW}\"" | asm_log 1
        exit 1
    fi
    echo "done"

    return 0
}

if [ $# != 2 -o -z "$1" -o -z "$2" ]
then
    usage
fi

OLD="$1"
shift
case "$OLD" in
*/*)
    ;;
*)
    OLD="$(upper_disk "$OLD")"
    if [ -z "$OLD" ]
    then
        exit 1
    fi
    ;;
esac

NEW="$(upper_disk "$1")"
shift
if [ -z "$NEW" ]
then
    exit 1
fi

if [ "$restore" = "t" ]
then
    restore_device "$OLD" "$NEW"
    exit 0
fi

case "$OLD" in
"$NEW")
    echo "New label is identical to the old one" | asm_log 1
    exit 1
    ;;
*/*)
    relabel_device "$OLD" "$NEW"
    ;;
*)
    relabel_disk "$OLD" "$NEW"
    ;;
esac

exit 0
