Current File : //usr/local/jetapps/usr/share/rear/layout/recreate/default/120_confirm_wipedisk_disks.sh
# Show the user what disks will be completely overwritten:
# The disks that will be completely overwritten are those disks
# where in diskrestore.sh the create_disk_label function is called
# (the create_disk_label function calls "parted -s $disk mklabel $label")
# for example like
#   create_disk_label /dev/sda gpt
#   create_disk_label /dev/sdb msdos
#   create_disk_label /dev/md127 gpt
# so in this example disks_to_be_recreated="/dev/sda /dev/sdb /dev/md127 "
local disks_to_be_recreated=""
disks_to_be_recreated="$( grep '^ *create_disk_label /dev/' $LAYOUT_CODE | grep -o '/dev/[^ ]*' | sort -u | tr -s '[:space:]' ' ' )"
LogPrint "Disks to be completely overwritten and recreated by $LAYOUT_CODE:"
LogPrint "  $disks_to_be_recreated"

# Skip wiping disks when the user has explicitly specified to not wipe disks:
is_false "$DISKS_TO_BE_WIPED" && return 0

# In migration mode let the user confirm the disks
# that will be completely wiped (as far as possible)
# so that the disk layout recreation code (diskrestore.sh)
# can run on clean disks that behave like pristine new disks.
LogPrint "Determining disks to be wiped ..."

local disk_to_be_wiped
local disks_to_be_wiped=""
if test "$DISKS_TO_BE_WIPED" ; then
    # If the user has specified DISKS_TO_BE_WIPED (i.e. when it is not empty)
    # use only those that actually exist as block devices in the recovery system.
    # Bash pathname expansion happens here (e.g. when DISKS_TO_BE_WIPED="/dev/sd[a-z]"):
    for disk_to_be_wiped in $DISKS_TO_BE_WIPED ; do
        # 'test -b' succeeds when there is no argument but fails when the argument is empty:
        test -b "$disk_to_be_wiped" || continue
        # Write-protection for the disks in DISKS_TO_BE_WIPED
        # cf. https://github.com/rear/rear/pull/2703#issuecomment-979928423
        if is_write_protected "$disk_to_be_wiped" ; then
            LogPrint "Excluding $disk_to_be_wiped from DISKS_TO_BE_WIPED ($disk_to_be_wiped is write-protected)"
            continue
        fi
        # Have a trailing space delimiter to get e.g. disks_to_be_wiped="/dev/sda /dev/sdb "
        # (a trailing space looks better in user messages than a preceding space):
        disks_to_be_wiped+="$disk_to_be_wiped "
    done
else
    # The above automatism that determines disks_to_be_recreated
    # cannot work when the create_disk_label function is called
    # for higher level block devices like RAID devices e.g. as 'create_disk_label /dev/md127 gpt'
    # that do not exist as disks on the bare hardware or on a bare virtual machine:
    for disk_to_be_wiped in $disks_to_be_recreated ; do
        # 'test -b' succeeds when there is no argument but fails when the argument is empty:
        if test -b "$disk_to_be_wiped" ; then
            # Write-protection for the disks in disks_to_be_recreated
            # cf. https://github.com/rear/rear/pull/2703#issuecomment-979928423
            if is_write_protected "$disk_to_be_wiped" ; then
                DebugPrint "Excluding $disk_to_be_wiped to be wiped ($disk_to_be_wiped is write-protected)"
                continue
            fi
            # Have a trailing space delimiter to get e.g. disks_to_be_wiped="/dev/sda /dev/sdb "
            # (a trailing space looks better in user messages than a preceding space):
            disks_to_be_wiped+="$disk_to_be_wiped "
        else
            # Handle RAID devices like /dev/md127 that do not (yet) exist in the currently running recovery system
            # (so the above 'test -b "$disk_to_be_wiped"' results false):
            # When the create_disk_label function is called for higher level block devices like RAID devices
            # e.g. as 'create_disk_label /dev/md127 gpt' the RAID device /dev/md127 is a child of a disk like /dev/sdc
            # or the RAID device /dev/md127 is a child of a partition like /dev/sdc1 that is a child of the disk /dev/sdc
            # so we need to find out the parent disk of the RAID device. Because the RAID device does not (yet) exist
            # in the currently running recovery system we check disklayout.conf that tells about the original system
            # but here the devices in disklayout.conf are already migrated to what they are on the recovery system
            # so we can check disklayout.conf what the parent disk of the RAID device is on the current recovery system,
            # cf. the code in layout/prepare/GNU/Linux/120_include_raid_code.sh
            local keyword raiddevice options
            # The disklayout.conf keyword for a RAID array is 'raidarray' and $raiddevice is e.g. /dev/md127
            # and $options is a string that should contain a word like devices=/dev/sda,/dev/sdb,/dev/sdc
            read keyword raiddevice options < <( grep "^raidarray $disk_to_be_wiped " "$LAYOUT_FILE" )
            if ! test "$raiddevice" = "$disk_to_be_wiped" ; then
                # Continue with the next disk_to_be_wiped when the current one is no RAID device.
                # We are in the 'else' clause of the outer 'if' so disk_to_be_wiped does not exist as block device:
                DebugPrint "Skipping $disk_to_be_wiped to be wiped ($disk_to_be_wiped does not exist as block device)"
                continue
            fi
            # The current disk_to_be_wiped is a RAID device like /dev/md127 that does not (yet) exist
            # as block device in the currently running ReaR recovery system:
            DebugPrint "RAID device $raiddevice does not exist - trying to determine the parent disks of its component devices"
            local component_devices=()
            local option
            for option in $options ; do
                case "$option" in
                    (devices=*)
                        # E.g. when option is "devices=/dev/sda,/dev/sdb,/dev/sdc"
                        # then ${option#devices=} is "/dev/sda,/dev/sdb,/dev/sdc"
                        # so that echo ${option#devices=} | tr ',' ' '
                        # results "/dev/sda /dev/sdb /dev/sdc"
                        component_devices=( $( echo ${option#devices=} | tr ',' ' ' ) )
                        ;;
                esac
            done
            local component_device parent_device added_parent_device="no"
            for component_device in "${component_devices[@]}" ; do
                # component_device is a disk like /dev/sdc or a partition like /dev/sdc1 (cf. above)
                # so we get the parent device of it (the parent of a disk will be the disk itself)
                # cf. the code of the function write_protection_ids() in lib/write-protect-functions.sh
                # Older Linux distributions do not contain lsblk (e.g. SLES10)
                # and older lsblk versions do not support the output column PKNAME
                # so we ignore lsblk failures and error messages
                # and we skip empty lines in the output via 'awk NF'
                # and we use only the topmost reported PKNAME.
                # For example in a recovery system with RAID1 of /dev/sda and /dev/sdb
                #   # lsblk -ipo NAME,KNAME,PKNAME,TYPE,FSTYPE                
                #   NAME        KNAME     PKNAME   TYPE FSTYPE
                #   /dev/sda    /dev/sda           disk linux_raid_member
                #   /dev/sdb    /dev/sdb           disk linux_raid_member
                #   /dev/sdc    /dev/sdc           disk 
                #   `-/dev/sdc1 /dev/sdc1 /dev/sdc part
                # There is no PKNAME for disks so we use KNAME (so the parent of a disk is the disk itself)
                # and we also use KNAME as fallback when lsblk does not support PKNAME and proceed bona fide
                # (so we wipe only KNAME of a partition but not its parent disk when PKNAME is not supported):
                parent_device="$( lsblk -inpo PKNAME "$component_device" 2>/dev/null | awk NF | head -n1 )"
                test $parent_device || parent_device="$( lsblk -inpo KNAME "$component_device" 2>/dev/null | awk NF | head -n1 )"
                # Without quoting an empty parent_device would result plain "test -b" which would (falsely) succeed:
                if test -b "$parent_device" ; then
                    # parent_device is usually a disk but in the KNAME fallback case it could be a partition:
                    DebugPrint "$parent_device is a parent of component device $component_device of $raiddevice that should be wiped"
                    # Write-protection for the disks in disks_to_be_recreated (see above).
                    # When parent_device is a partition the function write_protection_ids() in lib/write-protect-functions.sh
                    # also tries to determine its parent disk if possible to check the disk device in disks_to_be_recreated:
                    if is_write_protected "$parent_device" ; then
                        DebugPrint "Excluding parent $parent_device to be wiped ($parent_device is write-protected)"
                        # Continue with the next component_device
                        continue
                    fi
                    DebugPrint "Adding parent $parent_device to be wiped ($parent_device is not write-protected)"
                    # Have a trailing space delimiter to get e.g. disks_to_be_wiped="/dev/sda /dev/sdb "
                    # (a trailing space looks better in user messages than a preceding space):
                    disks_to_be_wiped+="$parent_device "
                    added_parent_device="yes"
                fi
            done
            if is_false $added_parent_device ; then
                DebugPrint "Skipping RAID device $raiddevice to be wiped (no parent disk found for its component devices)"
            fi
        fi
    done
fi
DISKS_TO_BE_WIPED="$disks_to_be_wiped"
# The DISKS_TO_BE_WIPED string is needed in the subsequent layout/recreate/default/150_wipe_disks.sh script

# Show the user confirmation dialog in any case but when not in migration mode
# automatically proceed with less timeout USER_INPUT_INTERRUPT_TIMEOUT (by default 30 seconds)
# to avoid longer delays (USER_INPUT_TIMEOUT is by default 300 seconds) in case of unattended recovery:
local timeout="$USER_INPUT_TIMEOUT"
is_true "$MIGRATION_MODE" || timeout="$USER_INPUT_INTERRUPT_TIMEOUT"

rear_workflow="rear $WORKFLOW"
rear_shell_history="lsblk"
unset choices
choices[0]="Confirm disks to be wiped and continue '$rear_workflow'"
choices[1]="Skip wiping disks and continue '$rear_workflow'"
choices[2]="Use Relax-and-Recover shell and return back to here"
choices[3]="Abort '$rear_workflow'"
prompt="Disks to be wiped: $DISKS_TO_BE_WIPED"
choice=""
wilful_input=""
# When USER_INPUT_WIPE_DISKS_CONFIRMATION has any 'true' value be liberal in what you accept and
# assume choices[0] 'Confirm disk layout' was actually meant:
is_true "$USER_INPUT_WIPE_DISKS_CONFIRMATION" && USER_INPUT_WIPE_DISKS_CONFIRMATION="${choices[0]}"
while true ; do
    choice="$( UserInput -I WIPE_DISKS_CONFIRMATION -t "$timeout" -p "$prompt" -D "${choices[0]}" "${choices[@]}" )" && wilful_input="yes" || wilful_input="no"
    case "$choice" in
        (${choices[0]})
            # Confirm disks to be wiped and continue:
            is_true "$wilful_input" && LogPrint "User confirmed disks to be wiped" || LogPrint "Continuing '$rear_workflow' by default"
            break
            ;;
        (${choices[1]})
            # Skip wiping disks:
            DISKS_TO_BE_WIPED="false"
            break
            ;;
        (${choices[2]})
            # rear_shell runs 'bash' with the original STDIN STDOUT and STDERR when 'rear' was launched by the user:
            rear_shell "" "$rear_shell_history"
            ;;
        (${choices[3]})
            abort_recreate
            Error "User chose to abort '$rear_workflow' in ${BASH_SOURCE[0]}"
            ;;
    esac
done