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


# Force LC_ALL=C
export LC_ALL=C
 
USAGE="[-l <manager>] [-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
    -l|--manager)
        case "$#" in 1) usage=t; break ;; esac
        shift
        ORACLE_ASMMANAGER="$1"
        ;;
    -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

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 "${ORACLE_ASMMANAGER}" "${OLD}")"
    new_path="$(asm_disk_path "${ORACLE_ASMMANAGER}" "${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 -l "${ORACLE_ASMMANAGER}" "${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    

    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 -l "${ORACLE_ASMMANAGER}" \
                "${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.
	if [ "$ORACLEASM_DRIVER_SUPPORTED" = "false" ]
	then
	    # Remove the iofilter map entry for this disk
	    iofilter_map_delete ${OLD}
	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 -l "${ORACLE_ASMMANAGER}" "${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 "${ORACLE_ASMMANAGER}" "${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}
	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 -l "${ORACLE_ASMMANAGER}" "${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    

    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 "${ORACLE_ASMMANAGER}" "${OLD}")"
	    dev_major_minor="$(get_dev "$DEV")"
	    old_major_minor="$(get_dev "$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 -l "${ORACLE_ASMMANAGER}" "${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.
    if [ "$ORACLEASM_DRIVER_SUPPORTED" = "false" ]
    then
	# Remove the iofilter map entry for this disk
	iofilter_map_delete ${OLD}
    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 -l "${ORACLE_ASMMANAGER}" "${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 -l "${ORACLE_ASMMANAGER}" "${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 "${ORACLE_ASMMANAGER}" "${NEW}")"
    perm_disk "${new_path}"
    if [ $? = 1 ]
    then
        echo "failed"
        echo "Unable to change ownership of disk \"${NEW}\"" | 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
}

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 "${ORACLE_ASMMANAGER}" "${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
    echo -n "Writing disk header: "
    oracleasm-write-label -f "${DEV}" "${NEW}" 2>&1 | asm_log 2
    if [ ${PIPESTATUS[0]} != 0 ]
    then
        oracleasm-instantiate-disk -l "${ORACLE_ASMMANAGER}" "${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 -l "${ORACLE_ASMMANAGER}" "${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 [ "$force" != "t" ]
then
    cat <<EOCAT
WARNING: Changing the label of an disk marked for ASM is a very dangerous
         operation.  If this is really what you mean to do, you must
         ensure that all Oracle and ASM instances have ceased using
         this disk.  Otherwise, you may LOSE DATA.

If you are really sure you wish to change the label and are sure that
all of your Oracle and ASM instances have ceased using the disk,
rerun this command with the '-f' option.
EOCAT
    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
