test-microsd.sh

·Bash / shell · · 6 views ·225 lines

Script to test a microSD card by mounting /dev/sdb partitions and logging results, run with sudo.

#!/usr/bin/env bash
set -uo pipefail

DEV="/dev/sdb"
BOOT="${DEV}1"
ROOT="${DEV}2"
LOG="$HOME/microsd-test-$(date +%Y%m%d-%H%M%S).log"

if [[ $EUID -ne 0 ]]; then
    echo "Run this script with sudo:"
    echo "  sudo $0"
    exit 1
fi

# If invoked with sudo, save the logfile in the real user's home.
if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
    USER_HOME="$(getent passwd "$SUDO_USER" | cut -d: -f6)"
    LOG="$USER_HOME/microsd-test-$(date +%Y%m%d-%H%M%S).log"
fi

exec > >(tee -a "$LOG") 2>&1

echo "======================================================"
echo " Raspberry Pi microSD diagnostic"
echo "======================================================"
echo
echo "Device: $DEV"
echo "Log:    $LOG"
echo

# -------------------------------------------------------
# SAFETY CHECKS
# -------------------------------------------------------

if [[ ! -b "$DEV" ]]; then
    echo "ERROR: $DEV does not exist."
    exit 1
fi

ROOTDEV="$(findmnt -n -o SOURCE / | sed -E 's/p?[0-9]+$//')"

if [[ "$ROOTDEV" == "$DEV" ]]; then
    echo "REFUSING TO CONTINUE."
    echo "$DEV appears to contain the currently running OS."
    exit 1
fi

echo "Detected device:"
lsblk -o NAME,PATH,SIZE,RM,RO,TYPE,FSTYPE,LABEL,MODEL,MOUNTPOINTS "$DEV"
echo

echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo " Verify that THIS is your 238 GB microSD card:"
echo
lsblk -d -o NAME,SIZE,RM,MODEL,SERIAL "$DEV"
echo
echo " This script performs READ-ONLY diagnostics."
echo " It WILL NOT format or overwrite the card."
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo

read -rp "Type YES to test $DEV: " CONFIRM

if [[ "$CONFIRM" != "YES" ]]; then
    echo "Cancelled."
    exit 1
fi

echo
echo "======================================================"
echo " 1. Unmounting SD partitions"
echo "======================================================"

while read -r PART MOUNTPOINT; do
    [[ -z "$MOUNTPOINT" ]] && continue

    echo "Unmounting $PART from $MOUNTPOINT..."
    umount "$PART" || {
        echo "ERROR: Could not unmount $PART"
        exit 1
    }
done < <(lsblk -nrpo NAME,MOUNTPOINT "$DEV")

sync

echo
lsblk -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINTS "$DEV"

echo
echo "======================================================"
echo " 2. Checking kernel/device information"
echo "======================================================"

udevadm info --query=property --name="$DEV" 2>/dev/null |
    grep -E 'ID_MODEL=|ID_VENDOR=|ID_SERIAL=|ID_BUS=|ID_DRIVE_FLASH|ID_DRIVE_REMOVABLE' ||
    true

echo
echo "Partition table:"
fdisk -l "$DEV"

echo
echo "======================================================"
echo " 3. FAT boot partition filesystem check"
echo "======================================================"

if [[ -b "$BOOT" ]]; then
    echo "Running READ-ONLY FAT check on $BOOT..."
    echo

    fsck.vfat -n -v "$BOOT"
    FAT_RESULT=$?

    echo
    echo "fsck.vfat exit code: $FAT_RESULT"
else
    echo "WARNING: $BOOT does not exist."
    FAT_RESULT=99
fi

echo
echo "======================================================"
echo " 4. ext4 root partition filesystem check"
echo "======================================================"

if [[ -b "$ROOT" ]]; then
    echo "Running READ-ONLY forced ext4 check on $ROOT..."
    echo

    e2fsck -f -n -v "$ROOT"
    EXT_RESULT=$?

    echo
    echo "e2fsck exit code: $EXT_RESULT"
else
    echo "WARNING: $ROOT does not exist."
    EXT_RESULT=99
fi

echo
echo "======================================================"
echo " 5. Full physical READ scan of microSD"
echo "======================================================"
echo
echo "This reads the ENTIRE ~238 GiB card."
echo "Nothing will be written."
echo
echo "This can take a while depending on the USB adapter."
echo

START_TIME=$(date +%s)

# -s = progress
# -v = verbose summary
# -b 4096 = 4 KiB blocks
#
# IMPORTANT: no -w or -n flags are used.
# This is read-only.
badblocks -s -v -b 4096 "$DEV"

BADBLOCK_RESULT=$?

END_TIME=$(date +%s)
ELAPSED=$((END_TIME - START_TIME))

echo
echo "badblocks exit code: $BADBLOCK_RESULT"
printf "Physical scan duration: %02d:%02d:%02d\n" \
    $((ELAPSED / 3600)) \
    $(((ELAPSED % 3600) / 60)) \
    $((ELAPSED % 60))

echo
echo "======================================================"
echo " 6. Looking for USB / SD / I/O errors in kernel log"
echo "======================================================"

dmesg --color=never |
    grep -Ei \
    'sdb|usb|uas|scsi|mmc|I/O error|buffer I/O|medium error|sense key|reset|timeout|CRC|failed command' |
    tail -n 150 ||
    true

echo
echo "======================================================"
echo " RESULTS"
echo "======================================================"

echo
printf "%-30s %s\n" "FAT boot filesystem:" "$FAT_RESULT"
printf "%-30s %s\n" "ext4 root filesystem:" "$EXT_RESULT"
printf "%-30s %s\n" "physical read scan:" "$BADBLOCK_RESULT"

echo

if [[ "$BADBLOCK_RESULT" -eq 0 ]]; then
    echo "[PASS] Full physical card read completed."
else
    echo "[FAIL] Physical read scan reported a problem."
fi

echo

if [[ "$FAT_RESULT" -eq 0 ]]; then
    echo "[PASS] bootfs FAT filesystem appears clean."
else
    echo "[WARN] bootfs filesystem reported errors or inconsistencies."
fi

if [[ "$EXT_RESULT" -eq 0 ]]; then
    echo "[PASS] rootfs ext4 filesystem appears clean."
else
    echo "[WARN] rootfs filesystem reported errors or inconsistencies."
fi

echo
echo "Log saved to:"
echo "$LOG"

if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
    chown "$SUDO_USER":"$SUDO_USER" "$LOG" 2>/dev/null || true
fi

echo
echo "NO DATA WAS INTENTIONALLY WRITTEN TO THE MICROSD."