first commit

This commit is contained in:
2026-09-05 12:14:36 +03:00
commit 5930fd7ab5
8 changed files with 217 additions and 0 deletions
@@ -0,0 +1,19 @@
[Unit]
Description=EP Eraser console
After=local-fs.target systemd-udev-settle.service
Wants=systemd-udev-settle.service
[Service]
Type=simple
ExecStart=/usr/local/sbin/ep-eraser
StandardInput=tty
StandardOutput=tty
StandardError=tty
TTYPath=/dev/tty1
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
Restart=no
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,107 @@
#!/usr/bin/env bash
set -Eeuo pipefail
LOG=/var/log/ep-eraser.log
exec > >(tee -a "$LOG") 2>&1
die() { printf '\nERROR: %s\n' "$*" >&2; read -r -p 'Press Enter to return to the menu...' _; }
mounted() {
findmnt -rn -S "$1" >/dev/null 2>&1 && return 0
lsblk -nrpo NAME,MOUNTPOINTS "$1" | awk 'NF > 1 && $2 != "" { found=1 } END { exit !found }'
}
is_live_device() {
local live parent
live=$(findmnt -rn -o SOURCE /run/live/medium 2>/dev/null || true)
[ -n "$live" ] || return 1
parent=$(lsblk -dnro PKNAME "$live" 2>/dev/null || true)
[ -n "$parent" ] && [ "$1" = "/dev/$parent" ]
}
disk_info() {
lsblk -dnpo NAME,TYPE,SIZE,MODEL,SERIAL,TRAN | awk '$2 == "disk" { print }'
}
choose_disk() {
mapfile -t disks < <(lsblk -dnpo NAME,TYPE | awk '$2 == "disk" { print $1 }')
[ "${#disks[@]}" -gt 0 ] || { die 'No whole disks were detected.'; return 1; }
printf '\nDetected disks:\n\n'
printf '%-4s %-18s %-10s %-24s %-16s %s\n' '#' DEVICE SIZE MODEL SERIAL TRAN
disk_info | nl -w2 -s' '
printf '\nEnter the number shown above, or q to quit: '
read -r choice
[ "$choice" = q ] && exit 0
[[ "$choice" =~ ^[0-9]+$ ]] || { die 'Invalid selection.'; return 1; }
target=${disks[$((choice - 1))]:-}
[ -n "$target" ] || { die 'Invalid selection.'; return 1; }
}
validate_target() {
[ -b "$target" ] || { die 'Selected path is not a block device.'; return 1; }
[ "$(lsblk -dnro TYPE "$target")" = disk ] || { die 'Only whole disks may be erased.'; return 1; }
[ "$(lsblk -dnro RO "$target")" = 0 ] || { die 'The disk is read-only.'; return 1; }
mounted "$target" && { die 'The disk or one of its partitions is mounted.'; return 1; }
is_live_device "$target" && { die 'The live boot device cannot be erased.'; return 1; }
}
confirm() {
local info
info=$(lsblk -dnpo NAME,SIZE,MODEL,SERIAL "$target")
printf '\nTARGET: %s\n%s\n\nThis permanently destroys all data. Type ERASE to continue: ' "$target" "$info"
read -r answer
[ "$answer" = ERASE ] || { printf 'Cancelled.\n'; return 1; }
}
erase_ssd() {
printf '\nSSD/NVMe erase options:\n1) Secure discard (blkdiscard --secure)\n2) NVMe sanitize (NVMe only)\n3) Cancel\nChoose: '
read -r method
case "$method" in
1)
blkdiscard --secure --force "$target" || { die 'Secure discard failed; no data was overwritten.'; return 1; }
;;
2)
[[ "$target" == /dev/nvme*n* ]] || { die 'NVMe sanitize requires an NVMe namespace.'; return 1; }
nvme sanitize "$target" -a 2 || { die 'NVMe sanitize failed.'; return 1; }
nvme sanitize-log "$target" -H || true
;;
*) printf 'Cancelled.\n'; return 1 ;;
esac
}
erase_hdd() {
printf '\nHDD erase uses three random overwrite passes plus a zero pass. Continue? [yes/no]: '
read -r answer
[ "$answer" = yes ] || { printf 'Cancelled.\n'; return 1; }
shred --verbose --iterations=3 --zero --force "$target"
}
verify() {
printf '\nFinal device state:\n'
lsblk -dnpo NAME,TYPE,SIZE,MODEL,SERIAL "$target"
printf 'Erase command completed. Verification is limited to command/device status.\n'
}
main() {
[ "$(id -u)" -eq 0 ] || { echo 'This tool must run as root.' >&2; exit 1; }
printf '\nEP ERASER - destructive disk sanitization\n==========================================\n'
while true; do
choose_disk || continue
validate_target || continue
rotational=$(lsblk -dnro ROTA "$target")
if [ "$rotational" = 1 ]; then
operation=HDD
else
operation=SSD/NVMe
fi
printf '\nDetected media type: %s\n' "$operation"
confirm || continue
if [ "$operation" = HDD ]; then erase_hdd; else erase_ssd; fi
verify
printf '\nPress Enter to return to the disk menu, or type q to quit: '
read -r answer
[ "$answer" = q ] && exit 0
done
}
main "$@"