13 Commits
dev ... ai

Author SHA1 Message Date
matmoul ab346342d5 docs: update usage and shell style conventions 2026-04-28 01:03:19 +02:00
matmoul bf9cbe918f chore: remove VS Code ignore rules 2026-04-28 00:35:05 +02:00
matmoul 62ac956836 fix: reorder wipe method fallback sequence 2026-04-28 00:33:27 +02:00
matmoul 07c210164a feat: add optional wipe tool fallbacks by device type 2026-04-28 00:28:50 +02:00
matmoul c56aec350d docs: tighten project conventions for mtm-ddwipe 2026-04-27 23:46:51 +02:00
matmoul 956cfd7325 fix: tighten mtm-ddwipe usage and device checks
Add an explicit mounted-device check with findmnt, shorten confirmation prompts, and align the usage text and log wording with the current script name.
2026-04-27 23:40:28 +02:00
matmoul 55c55a4a08 feat: strengthen wipe safety checks and confirmation flow
Add mounted/in-use device detection, show detailed device info before confirmation, and require an exact wipe phrase to proceed. Also move status output to stderr and refresh the usage warnings for clearer destructive-action guidance.
2026-04-27 23:37:40 +02:00
matmoul ecc6e5b038 fix: harden mtm-ddwipe confirmation and wipe flow
Add strict shell options, root and block-device validation, and a typed-device confirmation prompt before wiping. Preserve the fallback wipe sequence through secure discard, zero discard, and dd, while tightening error handling and keeping messages concise.
2026-04-27 23:32:45 +02:00
matmoul 7011426381 docs: clarify mtm-ddwipe safety and prompt conventions 2026-04-27 23:20:07 +02:00
matmoul b01e72cba6 fix: improve mtm-ddwipe argument handling and timing output 2026-04-27 23:17:16 +02:00
matmoul 08d6ccde0d docs: tighten mtm-ddwipe help and project guidance 2026-04-27 23:05:22 +02:00
matmoul 7321d7e087 docs: update project rules for safer wipe behavior 2026-04-27 23:02:14 +02:00
matmoul 7609bf9c17 feat: improve release script with dry-run and version increments 2026-04-27 22:57:08 +02:00
5 changed files with 407 additions and 87 deletions
+32
View File
@@ -0,0 +1,32 @@
---
description: mtm-ddwipe project conventions
---
# Project conventions
- Use English.
- Use Bash for shell scripts.
- Keep `mtm-ddwipe` focused on wiping block devices.
- Keep `mtm-ddwipe` interactive by default.
- Require explicit confirmation before destructive actions.
- Validate real block devices and refuse mounted or in-use targets.
- Show clear device details before confirmation.
- Keep destructive safeguards strict and explicit.
- Keep messages short and clear.
- Keep help concise, usage-first, and warning-focused.
- Preserve the fallback wipe flow: secure discard, zero discard, then `dd` zero-fill.
- Keep optional tools optional.
- Use extra wipe methods only if the command is present.
- Use `ddrescue` or `dd_rescue` only after `dd` fails.
- Keep `nvme` and `hdparm` optional.
- Use `nvme` and `hdparm` only on matching device types.
- Do not make new external tools required.
- Keep changes minimal and preserve intent.
- Keep this file aligned and concise.
- Prefer tabs for indentation.
- Prefer `${var}` notation in Bash.
- If non-interactive support is added, make it an opt-in safety flag.
# Project identity
- Main script: `mtm-ddwipe`
- License: GNU GPL v3
-14
View File
@@ -1,14 +0,0 @@
# ---> VisualStudioCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
+17
View File
@@ -1,2 +1,19 @@
# mtm-ddwipe # mtm-ddwipe
Wipe a block device.
Warning: destructive and irreversible.
Target must be a real block device, not mounted, and not in use.
Usage:
mtm-ddwipe DEVICE
mtm-ddwipe -h | --help
Options:
-h, --help Show help.
Notes:
- Interactive by default.
- Confirm exactly before wiping.
- Optional tools: blkdiscard, ddrescue, dd_rescue, nvme, hdparm.
+112 -9
View File
@@ -1,32 +1,135 @@
#!/bin/bash #!/bin/bash
declare -r VERSION=${1} set -euo pipefail
declare -r MESSAGE=${2}
declare -r TAGBRANCH=main declare -r TAGBRANCH=main
declare CURRENTBRANCH="" declare CURRENTBRANCH=""
declare ORIGBRANCH=""
declare -r ARG1="${1:-}"
declare -r ARG2="${2:-}"
declare is_dry_run=false
declare release_input=""
showHelp() { showHelp() {
echo makerelease version cat <<'EOF'
Usage:
makerelease.sh VERSION
makerelease.sh +0.0.1
makerelease.sh +0.1
makerelease.sh +1
makerelease.sh --dry-run VERSION
makerelease.sh --dry-run +0.0.1
makerelease.sh --dry-run +0.1
makerelease.sh --dry-run +1
Creates an annotated Git tag from the current dev branch.
If VERSION starts with +, it is treated as an increment:
- +0.0.1 increments patch
- +0.1 increments minor
- +1 increments major
Use --dry-run to show the computed release version without running Git actions.
Requirements:
- run from a clean Git working tree
- current branch must be dev for real releases
- main branch must exist locally
EOF
} }
if [ "${VERSION}" == "" ]; then cleanup() {
if [ -n "${ORIGBRANCH}" ] && [ "${CURRENTBRANCH}" != "${ORIGBRANCH}" ]; then
git checkout "${ORIGBRANCH}" >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT
if [ "${ARG1}" = "--dry-run" ] || [ "${ARG1}" = "-n" ]; then
is_dry_run=true
release_input="${ARG2:-}"
else
release_input="${ARG1}"
fi
if [ -z "${release_input}" ]; then
showHelp showHelp
echo "" echo ""
echo "no version provided!" echo "no version provided!"
exit 1 exit 1
fi fi
CURRENTBRANCH=$(git rev-parse --abbrev-ref HEAD) if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "You are not inside a Git repository!"
exit 1
fi
if [ ! "${CURRENTBRANCH}" == "dev" ]; then if [ -n "$(git status --porcelain)" ]; then
echo "Working tree is not clean!"
echo "Commit or stash your changes before creating a release."
exit 1
fi
CURRENTBRANCH=$(git rev-parse --abbrev-ref HEAD)
ORIGBRANCH="${CURRENTBRANCH}"
if [ "${is_dry_run}" = false ] && [ "${CURRENTBRANCH}" != "dev" ]; then
echo "You are not in dev branch!" echo "You are not in dev branch!"
echo "Use dev branch to make a release!" echo "Use dev branch to make a release!"
exit 1 exit 1
fi fi
release_tag="${release_input}"
if [ "${release_input}" != "${release_input#+}" ]; then
current_tag="$(git describe --tags --abbrev=0 2>/dev/null || true)"
if [ -z "${current_tag}" ]; then
echo "No existing tag found to increment from!"
exit 1
fi
case "${release_input}" in
+1)
release_tag="$(printf '%s' "${current_tag}" | awk -F. 'BEGIN{OFS="."} { $1+=1; $2=0; $3=0; print }')"
;;
+0.1)
release_tag="$(printf '%s' "${current_tag}" | awk -F. 'BEGIN{OFS="."} { $2+=1; $3=0; print }')"
;;
+0.0.1)
release_tag="$(printf '%s' "${current_tag}" | awk -F. 'BEGIN{OFS="."} { $3+=1; print }')"
;;
*)
echo "Unsupported increment syntax: ${release_input}"
exit 1
;;
esac
fi
if git rev-parse -q --verify "refs/tags/${release_tag}" >/dev/null; then
echo "Tag ${release_tag} already exists!"
exit 1
fi
if [ "${is_dry_run}" = true ]; then
echo "Dry run: computed release tag ${release_tag}"
exit 0
fi
echo "Release tag selected: ${release_tag}"
read -r -p "Proceed with release? [y/N] " confirm
case "${confirm}" in
y|Y)
;;
*)
echo "Release cancelled."
exit 1
;;
esac
git checkout "${TAGBRANCH}" git checkout "${TAGBRANCH}"
git merge "${CURRENTBRANCH}" CURRENTBRANCH="${TAGBRANCH}"
git merge "${ORIGBRANCH}"
git push git push
git tag -a "${VERSION}" -m "${MESSAGE}" git tag -a "${release_tag}" -m "Release ${release_tag}"
git push --tags git push --tags
git checkout "${CURRENTBRANCH}"
echo "Created release tag ${release_tag}"
Executable → Regular
+244 -62
View File
@@ -1,89 +1,271 @@
#!/bin/bash #!/bin/bash
set -euo pipefail
IFS=$'\n\t'
declare -r DEV="${1}" VERSION="0.0.4"
declare STARTDATE="" STARTDATE=0
declare STARTDATESTRING="" STARTDATESTRING=""
show_help() { usage() {
echo "ddwipe dev (/dev/sdX)" cat <<EOF
echo "" Usage: mtm-ddwipe DEVICE
echo "Version : 0.0.3"
Wipe a block device.
Warnings:
- Destructive and irreversible.
- Target must be a real block device, not mounted, and not in use.
- Optional tools: blkdiscard, ddrescue, dd_rescue, nvme, hdparm.
- dd fallback may take a long time.
Version: ${VERSION}
EOF
}
log() {
echo "$*" >&2
}
die() {
echo "Error: $*" >&2
exit 1
} }
check_args() { check_args() {
if [ "${1}" == "" ]; then if [ $# -ne 1 ]; then
show_help case "${1:-}" in
-h|--help)
usage
exit 0
;;
"")
usage
exit 1
;;
-*)
die "Invalid option."
;;
esac
usage
exit 1 exit 1
fi fi
} }
check_dev_exist() { check_device() {
if [ ! -e "${1}" ]; then local dev="${1}"
echo "${1} is missing."
exit 1 [ -e "${dev}" ] || die "Missing device: ${dev}"
[ -b "${dev}" ] || die "Not a block device: ${dev}"
}
check_device_not_in_use() {
local dev="${1}"
if findmnt -rn --target "${dev}" >/dev/null 2>&1; then
die "Device is mounted: ${dev}"
fi fi
if lsblk -nrpo NAME,MOUNTPOINT "${dev}" | awk '$2 != "" { found=1 } END { exit !found }'; then
die "Device or child is mounted: ${dev}"
fi
}
is_nvme_device() {
local dev="${1}"
local sysdev
sysdev="/sys$(lsblk -ndo PATH "${dev}" 2>/dev/null | head -n1)"
[ -n "${sysdev}" ] || return 1
[ -e "${sysdev}" ] || return 1
[ -d "${sysdev}/device" ] || return 1
[ -e "${sysdev}/device/uevent" ] || return 1
grep -q '^NVME=1$' "${sysdev}/device/uevent"
}
is_ata_device() {
local dev="${1}"
local tran
tran="$(lsblk -ndo TRAN "${dev}" 2>/dev/null | head -n1)"
[ "${tran}" = "sata" ] || [ "${tran}" = "ata" ]
} }
confirm_wipe() { confirm_wipe() {
lsblk "${1}" local dev="${1}"
local choice=""
echo "Device:"
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,MODEL,SERIAL "${dev}"
echo ""
echo "Type exactly: WIPE ${dev}"
read -r -p "Confirm: " choice
[ "${choice}" = "WIPE ${dev}" ] || die "Canceled"
echo "" echo ""
read -r -p "Wipe ${1} (y/[n])?" CHOICE
case "${CHOICE}" in
y|Y ) echo "";;
* )
echo "Canceled"
exit 1
;;
esac
} }
wipe_dev() { confirm_root() {
STARTDATE=$(date +%s) if [ "${EUID:-$(id -u)}" -ne 0 ]; then
STARTDATESTRING="$(date)" die "This tool must be run as root."
echo "Begin wiping device ${1}"
echo ""
echo "Start date :"
echo "${STARTDATESTRING}"
echo ""
echo "blkdiscard secure"
if ! blkdiscard -f -p 500M -s -v "${1}"; then
echo ""
echo "blkdiscard zero"
if ! blkdiscard -f -p 500M -z -v "${1}"; then
echo ""
echo "dd zero"
if ! dd if=/dev/zero of="${1}" bs=1M status=progress; then
# Need check if dd has all writed, if yes, return no error
# echo "Error wiping device ${1}, use phisical destroy !"
echo "Wiped with dd, check if full size is writed."
echo "Otherwise use a mechanical destruction of the device."
print_time
exit 1
fi
fi
fi fi
echo "" }
echo "Device ${1} wipped !"
format_duration() {
local total="${1}"
local hours minutes seconds
hours=$((total / 3600))
minutes=$(((total % 3600) / 60))
seconds=$((total % 60))
printf '%02d:%02d:%02d\n' "${hours}" "${minutes}" "${seconds}"
} }
print_time() { print_time() {
echo "" local enddate calctime
echo "Start date :"
echo "${STARTDATESTRING}"
ENDDATE=$(date +%s)
echo "" echo ""
echo "End date :" log "Start date :"
date log "${STARTDATESTRING}"
enddate=$(date +%s)
calctime=$((enddate - STARTDATE))
CALCTIME=$((ENDDATE-STARTDATE))
echo "" echo ""
echo "Total time :" log "End date :"
date -d@${CALCTIME} -u +%H:%M:%S date >&2
echo ""
log "Total time :"
format_duration "${calctime}" >&2
} }
check_args "${DEV}" wipe_with_blkdiscard_secure() {
check_dev_exist "${DEV}" local dev="${1}"
confirm_wipe "${DEV}"
wipe_dev "${DEV}" log "blkdiscard secure"
print_time blkdiscard -f -p 500M -s -v "${dev}"
}
wipe_with_blkdiscard_zero() {
local dev="${1}"
log "blkdiscard zero"
blkdiscard -f -p 500M -z -v "${dev}"
}
wipe_with_dd() {
local dev="${1}"
log "dd zero"
dd if=/dev/zero of="${dev}" bs=1M status=progress conv=fsync
}
wipe_with_ddrescue() {
local dev="${1}"
command -v ddrescue >/dev/null 2>&1 || return 1
log "ddrescue zero"
ddrescue -f -n /dev/zero "${dev}"
}
wipe_with_dd_rescue() {
local dev="${1}"
command -v dd_rescue >/dev/null 2>&1 || return 1
log "dd_rescue zero"
dd_rescue -f /dev/zero "${dev}"
}
wipe_with_nvme() {
local dev="${1}"
command -v nvme >/dev/null 2>&1 || return 1
is_nvme_device "${dev}" || return 1
log "nvme format"
nvme format "${dev}" -s 1 >/dev/null
}
wipe_with_hdparm() {
local dev="${1}"
command -v hdparm >/dev/null 2>&1 || return 1
is_ata_device "${dev}" || return 1
log "hdparm secure erase"
hdparm --security-erase NULL "${dev}"
}
wipe_dev() {
local dev="${1}"
STARTDATE=$(date +%s)
STARTDATESTRING="$(date)"
log "Begin wiping: ${dev}"
echo ""
log "Start date :"
log "${STARTDATESTRING}"
echo ""
if wipe_with_blkdiscard_secure "${dev}"; then
echo ""
log "Device ${dev} wiped."
return
fi
echo ""
if wipe_with_blkdiscard_zero "${dev}"; then
echo ""
log "Device ${dev} wiped."
return
fi
echo ""
if wipe_with_dd "${dev}"; then
echo ""
log "Device ${dev} wiped."
return
fi
echo ""
if wipe_with_ddrescue "${dev}"; then
echo ""
log "Device ${dev} wiped."
return
fi
echo ""
if wipe_with_dd_rescue "${dev}"; then
echo ""
log "Device ${dev} wiped."
return
fi
echo ""
if wipe_with_nvme "${dev}"; then
echo ""
log "Device ${dev} wiped."
return
fi
echo ""
if wipe_with_hdparm "${dev}"; then
echo ""
log "Device ${dev} wiped."
return
fi
die "Wipe failed. The device may not be fully overwritten."
}
main() {
check_args "$@"
confirm_root
check_device "${1}"
check_device_not_in_use "${1}"
confirm_wipe "${1}"
wipe_dev "${1}"
print_time
}
main "$@"