aboutsummaryrefslogtreecommitdiff
path: root/scripts/wipedisk
blob: 9b1b4ab18fdadc3140b8993438a31965c1c03239 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-3.0-or-later
# Craig Jennings <c@cjennings.net>
# identify disk and erase

# Overridable so the test suite can point at a fixture directory, the way
# nvidia_preflight_report takes NVIDIA_DRM_GLOB.
by_id_dir="${WIPEDISK_BY_ID:-/dev/disk/by-id}"

# Whole disks only. /dev/disk/by-id publishes a -partN entry for every
# partition alongside the disks (18 entries on this machine, 12 of them
# partitions), and the prompt below promises a disk. Globbed rather than
# `ls | grep` so a name with whitespace can't split into two menu entries.
all_disk_ids=()
for entry in "$by_id_dir"/*; do
    [ -e "$entry" ] || continue          # unmatched glob stays literal
    name="${entry##*/}"
    [[ "$name" =~ -part[0-9]+$ ]] && continue
    all_disk_ids+=("$name")
done

if [ "${#all_disk_ids[@]}" -eq 0 ]; then
    echo "No disks found in $by_id_dir." >&2
    exit 1
fi

echo ""; echo "Select the disk id to use. All data will be erased."
select disk_id in "${all_disk_ids[@]}"; do
    # ensure valid selection
    if [[ -n $disk_id ]]; then
        selection=$disk_id
        break
    else
        echo "Invalid. Try again."
    fi
done

# Confirm the selected disk
read -r -p "Confirm: '$selection' [y/n]? " choice
if [[ "$choice" != "y" ]]; then
    echo "Exiting..."
    exit 1
fi

DISK="$by_id_dir/$selection"
echo ""; echo "### Erasing Disk"

# blkdiscard opens the device O_EXCL by default (util-linux >= 2.36) and
# refuses a disk something is holding -- a mounted filesystem, an md member, an
# LVM PV. That refusal is this script's safety gate, and it has to run BEFORE
# anything destructive. Passing -f disables the exclusive open, which turned
# "refuse the wrong disk" into "discard a live filesystem, then let sgdisk fail
# and tell the user nothing happened". The gate is the kernel's answer, not a
# heuristic of ours, so no -f.
discarded=true
discard_err=$(blkdiscard "${DISK}" 2>&1) || discarded=false

# A discard can fail for two very different reasons. "Operation not supported"
# means the hardware has no discard -- the run continues and the closing
# message says the data is still there. Anything else means the device was
# refused, and nothing has been written yet, so stop while that is still true.
if [ "$discarded" = false ] \
   && ! printf '%s' "$discard_err" | grep -qi 'not supported'; then
    echo "" >&2
    echo "REFUSED: '$selection' is in use. Nothing was erased." >&2
    [ -n "$discard_err" ] && echo "  $discard_err" >&2
    echo "  Unmount its filesystems and stop any md/LVM/ZFS holder, then run" >&2
    echo "  this again." >&2
    exit 1
fi

# sgdisk refuses a busy device too. Reaching here means blkdiscard already
# accepted the disk, so a failure now is something else -- report it rather
# than announcing an erase that did not happen.
if ! sgdisk --zap-all "${DISK}"; then
    echo "" >&2
    echo "FAILED: could not clear the partition table on '$selection'." >&2
    echo "  The sectors were discarded but the partition table was not" >&2
    echo "  rewritten. Check the device and run this again." >&2
    exit 1
fi

echo ""
if [ "$discarded" = true ]; then
    echo "Disk erased. Sectors discarded and the partition table cleared."
else
    # Say what actually happened. sgdisk --zap-all destroys partition tables,
    # not data, so without a successful discard every byte is still readable.
    echo "Partition table cleared on '$selection'."
    echo "NOTE: this device did not support discard, so the data is still"
    echo "      present and recoverable. For a disposal-grade wipe use the"
    echo "      drive's own secure erase (nvme format, hdparm) or overwrite it."
fi