Recovering a Weak-Head Seagate Drive with Controlled HDDSuperClone Imaging
This is a technical write-up of a real recovery workflow used on an unstable 1TB Seagate drive that would reset under sustained reads. The goal was to safely extract photos for a photographer while minimizing drive stress.
The drive would not show up in Windows on the Device Manager, and required using a SATA PCIe card to be able to hot swap the drive in after BIOS since it would lock up BIOS during hardware checks.
The drive would show up in output of lsblk and thus we took the following steps to recover data for our customer.
Tools used: Linux, smartctl, hdparm, HDDsuperclone, PhotoRec, ExifTool.
Symptoms and initial triage
The drive presented as readable for short operations (SMART and small reads), but would become unstable under sustained sequential reads.
ddrescue would stall, and imaging attempts with HDDSuperClone would trigger firmware resets and capacity/identify glitches. The error message read Source drive reports wrong size / size changed. No new clicking sounds were observed.
Quick verification commands
$ sudo smartctl -a /dev/sdX
$ sudo hdparm -I /dev/sdX
$ sudo dd if=/dev/sdX of=/dev/null bs=512 count=1
A key indicator was SMART responsiveness: after stressful imaging sessions, smartctl became slow, but returned to fast responses
after a longer cool-down. This strongly suggested thermal / sustained-load sensitivity consistent with a weak head (not a dead motor, not obvious
mechanical crash, and not immediate firmware lockup).
Why sustained imaging was failing
In this case, the drive tolerated only a limited amount of continuous reading before entering heavy internal retries and eventually resetting. The practical takeaway: avoid long, continuous reads. Use controlled reads of small sections of the drive and allow true rest between runs.
Controlled micro-burst imaging with HDDsuperclone
HDDSuperClone was used to image in small, controlled segments. The critical technique was to increase segment size gradually, only when the drive remained stable and SMART stayed responsive.
Segment sizing approach
Start conservative and increase in measured steps. Example working sizes observed during this recovery:
- 512000 sectors (~250MB)
- 640000 sectors (~320MB)
- 768000 sectors (~384MB)
If hesitation increases, SMART slows, or resets occur, drop back to the last stable size.
Operational pattern used between runs
After each successful segment, the drive was placed into standby to stop spindle rotation and park heads cleanly. This provided real rest compared to simply waiting while the drive continued spinning.
$ sudo hdparm -Y /dev/sdX
$ sudo hdparm -C /dev/sdX
The workflow was: run a segment, stop, disconnect the device handle, issue standby (hdparm -Y), rest 10–15 minutes, then resume.
After several larger segments, take a longer cool-down.
Validating the image and attempting mounts
On partial images, mounting the whole-disk image directly often fails because the filesystem lives inside a partition at an offset. First, inspect partitions within the image:
$ sudo fdisk -l badimage.imgFor a typical NTFS partition starting at sector 2048, the mount offset is 2048 * 512 = 1048576 bytes. Attempt a read-only mount via ntfs-3g:
$ sudo mkdir -p /mnt/test
$ sudo ntfs-3g -o ro,loop,offset=1048576 badimage.img /mnt/test
$ sudo umount /mnt/testIn this recovery, the image did not mount early on (likely incomplete NTFS metadata), so we pivoted to file carving.
Photo recovery with PhotoRec (JPEG-focused)
PhotoRec can recover photos from partial images even when filesystem metadata is incomplete. Run PhotoRec against the image:
$ sudo photorec badimage.img
In the PhotoRec UI, choose File Opt, press s to disable all, then enable only the photo types you care about
(JPEG/JPG and optionally PNG/RAW). This reduces junk output and speeds up the carve.
PhotoRec filenames: f######## vs t########
PhotoRec assigns generated filenames. Two common patterns:
f########.jpg– a carved file recovered by signature scant########.jpg– commonly thumbnails / small previews (often safe to separate)
If you want to isolate thumbnails for review later:
$ mkdir -p thumbs
$ mv t*.jpg thumbs/ 2>/dev/null || trueSorting recovered photos for a photographer (EXIF-based)
Carving loses original folder structure and filenames. For photographers, sorting by EXIF date is usually the fastest way to rebuild timelines. Install ExifTool:
$ sudo apt install exiftoolWe can check the contents of the metadata to see what value we want to use to sort, the best value was the Created Time value:
$ exiftool f6121520.jpg The following script was created to sort recovered photos by date based on the exif data:
#!/usr/bin/env python3
import argparse
import json
import os
import shutil
import subprocess
from pathlib import Path
from typing import Dict, List
def find_recup_dirs(src_root: Path) -> List[Path]:
if not src_root.is_dir():
raise FileNotFoundError(f"Source root not found or not a directory: {src_root}")
return sorted([p for p in src_root.iterdir() if p.is_dir() and p.name.startswith("recup")])
def iter_files_under(dirs: List[Path]) -> List[Path]:
files: List[Path] = []
for d in dirs:
for p in d.rglob("*"):
if p.is_file():
files.append(p)
return files
def chunked(lst: List[Path], n: int) -> List[List[Path]]:
return [lst[i : i + n] for i in range(0, len(lst), n)]
def exiftool_create_month(files: List[Path]) -> Dict[str, str]:
"""
Returns mapping: SourceFile -> "YYYY-MM"
Missing CreateDate will simply not exist in mapping.
Uses exiftool date formatting to output YYYY-MM directly.
"""
if not files:
return {}
cmd = ["exiftool", "-json", "-CreateDate", "-d", "%Y-%m"] + [str(f) for f in files]
try:
proc = subprocess.run(cmd, capture_output=True, text=True, check=False)
except FileNotFoundError:
raise RuntimeError("exiftool not found. Install it first (e.g., sudo apt install libimage-exiftool-perl).")
if proc.returncode != 0 and not proc.stdout.strip():
raise RuntimeError(f"exiftool failed:\n{proc.stderr.strip()}")
try:
data = json.loads(proc.stdout) if proc.stdout.strip() else []
except json.JSONDecodeError as e:
raise RuntimeError(f"Failed to parse exiftool JSON output: {e}\nStderr:\n{proc.stderr.strip()}")
out: Dict[str, str] = {}
for item in data:
src = item.get("SourceFile")
month = item.get("CreateDate") # already formatted like YYYY-MM due to -d
if src and month and isinstance(month, str) and len(month) == 7 and month[4] == "-":
out[src] = month
return out
def safe_dest_path(dest_dir: Path, filename: str) -> Path:
dest_dir.mkdir(parents=True, exist_ok=True)
base = Path(filename).stem
ext = Path(filename).suffix
candidate = dest_dir / (base + ext)
if not candidate.exists():
return candidate
i = 1
while True:
candidate = dest_dir / f"{base}_{i}{ext}"
if not candidate.exists():
return candidate
i += 1
def copy_file(src: Path, dest_dir: Path, dry_run: bool) -> Path:
dest = safe_dest_path(dest_dir, src.name)
if dry_run:
return dest
shutil.copy2(src, dest)
return dest
def move_file(src: Path, dest_dir: Path, dry_run: bool) -> Path:
dest = safe_dest_path(dest_dir, src.name)
if dry_run:
return dest
dest_dir.mkdir(parents=True, exist_ok=True)
shutil.move(str(src), str(dest))
return dest
def main():
ap = argparse.ArgumentParser(description="Sort PhotoRec recovered files into YYYY-MM folders using ExifTool CreateDate.")
ap.add_argument("--src-root", default="/mnt/samsung/photorec_out", help="Root that contains recup* directories.")
ap.add_argument("--dest-root", default="/mnt/samsung/recoveredphotos", help="Destination base directory.")
ap.add_argument("--chunk-size", type=int, default=500, help="How many files to send to exiftool per batch.")
ap.add_argument("--dry-run", action="store_true", help="Print what would happen, but don't copy/move anything.")
args = ap.parse_args()
src_root = Path(args.src_root)
dest_root = Path(args.dest_root)
thumbs_dir = dest_root / "thumbnails"
misc_dir = dest_root / "Misc"
recup_dirs = find_recup_dirs(src_root)
if not recup_dirs:
print(f"No recup* directories found under: {src_root}")
return
all_files = iter_files_under(recup_dirs)
if not all_files:
print(f"No files found under recup* directories in: {src_root}")
return
# Separate thumbnails (filename starts with 't')
thumbs = [p for p in all_files if p.name.startswith("t")]
normal = [p for p in all_files if not p.name.startswith("t")]
print(f"Found recup dirs: {len(recup_dirs)}")
print(f"Total files: {len(all_files)} | thumbnails (move): {len(thumbs)} | normal (copy): {len(normal)}")
if args.dry_run:
print("DRY RUN enabled: no changes will be made.\n")
# Move thumbnails
moved_thumbs = 0
for p in thumbs:
dest = move_file(p, thumbs_dir, args.dry_run)
moved_thumbs += 1
if moved_thumbs <= 10 or moved_thumbs % 500 == 0:
print(f"[THUMB] {p} -> {dest}")
if moved_thumbs > 10:
print(f"[THUMB] ... moved {moved_thumbs} thumbnails total")
# Copy normal files based on CreateDate -> YYYY-MM
copied = 0
to_misc = 0
for batch in chunked(normal, args.chunk_size):
month_map = exiftool_create_month(batch)
for p in batch:
month = month_map.get(str(p))
target_dir = (dest_root / month) if month else misc_dir
dest = copy_file(p, target_dir, args.dry_run)
copied += 1
if not month:
to_misc += 1
if copied <= 10 or copied % 1000 == 0:
label = month if month else "Misc"
print(f"[COPY:{label}] {p} -> {dest}")
if copied > 10:
print(f"[COPY] ... copied {copied} files total")
print("\nDone.")
print(f"Thumbnails moved: {moved_thumbs} -> {thumbs_dir}")
print(f"Normal copied: {copied} -> {dest_root}")
print(f"Missing CreateDate (copied to Misc): {to_misc} -> {misc_dir}")
if __name__ == "__main__":
main()
We use the chmod command to make the script executable, test it with the dry run parameter, and after validation we can run this to sort the photos by exif data datetime.
$ chmod +x sort_recup_photos.py
$ sudo ./sort_recup_photos.py --dry-run
$ sudo ./sort_recup_photos.py
Takeaways
- Weak-head drives can be readable in short bursts while failing under sustained reads.
- SMART responsiveness is a practical indicator of fatigue; longer cool-downs can restore stability.
- Using
hdparm -Ybetween runs provides real rest without hard power pulls. - File carving can produce usable results before a filesystem is mountable.
- For photographers, EXIF-based sorting quickly restores usable organization.
Recovering Data from 23 Unreadable CDs Using Multi-Drive ddrescue and DMDE
This is a real-world workflow used to recover data from a large batch of CDs that would not read in standard environments. The customer had 23 discs containing photos and files accumulated over years, most of which failed to open or would hang during access.
Instead of treating each disc as a full imaging job, we built a workflow that prioritized quick wins, reduced unnecessary stress on marginal media, and used multiple optical drives to maximize read success.
Tools used: Linux, ddrescue, DMDE, multiple SATA and USB optical drives.
Initial Triage with DMDE
Before committing to full imaging, each CD was tested in DMDE to quickly determine whether files were accessible without heavy read operations.
cd Downloads/dmde
sudo ./dmdeThis step allowed us to:
- Identify discs that could be recovered immediately
- Avoid unnecessary ddrescue runs on readable media
- Reduce wear on already fragile discs
A small number of discs were successfully recovered at this stage, eliminating hours of imaging work.
Building a Multi-Drive Recovery Setup
Optical drives vary significantly in how they handle damaged or degraded discs. Instead of relying on a single drive, we created an “assembly line” using multiple drives simultaneously.
- Older SATA DVD-RW drives (2008–2013 era)
- USB DVD drives
- USB Blu-ray drives
Each drive had different read tolerances. Some would fail instantly on a disc that another drive could partially read.
Devices appeared as:
/dev/sr0
/dev/sr1
/dev/sr2
/dev/sr3This allowed us to rotate discs between drives without losing progress.
Imaging Workflow with ddrescue
Each disc was labeled physically and matched to an image file and log file inside a customer directory:
mkdir ~/mike
cd ~/mikeInitial imaging always started with a no-retry pass to quickly capture readable sectors:
sudo ddrescue -b 2048 -n /dev/sr0 ddcd24.img ddcd24.logIf the drive stalled or slowed significantly, the same job was resumed on another drive using the same mapfile:
sudo ddrescue -b 2048 -n /dev/sr1 ddcd24.img ddcd24.log
sudo ddrescue -b 2048 -n /dev/sr2 ddcd24.img ddcd24.logThis is a key technique. ddrescue’s mapfile allows seamless continuation across different drives, effectively combining their strengths.
Final pass on best-performing drive
Once the majority of readable sectors were captured, the best-performing drive was selected for retry attempts:
sudo ddrescue -b 2048 -r 3 /dev/sr3 ddcd24.img ddcd24.logLimiting retries is important. CDs degrade quickly under repeated reads, and excessive retries can make things worse.
Why Multiple Drives Matter
One of the biggest takeaways from this job is how inconsistent optical drives are.
- Some drives read through scratches better
- Some handle dye degradation better
- Some fail quickly but others recover slowly
Rotating drives is often the difference between partial recovery and near-complete recovery.
Extracting Files with DMDE
Once imaging completed, DMDE was used to scan raw images and extract files.
In many cases, filesystem metadata was incomplete or damaged, so raw scanning was required.
JPEG files were recovered and organized into folders that matched the original disc labels written by the customer years ago.
This small step makes a big difference for usability. Instead of a flat dump of files, the customer received structured data that reflected how they originally organized their discs.
Final Delivery
All recovered files were consolidated and transferred to a USB drive for the customer.
- Recovered images organized by disc
- Readable files separated from partial or corrupted files
- Delivered on a single accessible device
Out of 23 discs, the majority yielded recoverable data, including photos that had not been accessible for years.
Key Takeaways
- Always triage first. Not every disc needs full imaging
- Use ddrescue mapfiles to continue across multiple drives
- Older optical drives can outperform newer ones on damaged media
- Limit retries to avoid further degradation
- Organizing output improves customer experience significantly
If you have old CDs that won’t read, especially photo archives, there is often still recoverable data even when they appear completely dead.