IBMDO YOU?Hi, I'm MBO!

Settings

Make the site feel at home on your screen.

Theme

Loading your theme preference.

Keyboard shortcuts

Open search from anywhere, then move through the results without leaving the keyboard.

Open settings
Ctrl,or⌘,
Open search
CtrlKor⌘K
Select a search result
↑↓
Open the selected result
Enter
Close an open dialog
Esc

Linux

Recovering a friend's photos from a failing drive with ddrescue

How ddrescue, a second FAT32 allocation table and PhotoRec helped recover photos from a failing 500 GB USB drive.

Solved — confirmed solution

A failing drive full of photos

A friend's 500 GB FAT32 USB drive contained a large collection of photos and other files. Some regions still read at normal speeds; others were painfully slow or returned read errors.

I recovered 12,825 files through the filesystem, keeping their original names and directories. PhotoRec then found more material to sort through, although its output needed careful comparison with the files already recovered.

The biggest improvement came from the second copy of the FAT32 allocation table. Using it in the working image made another 4,081 files accessible. Almost all the disk's bytes had been recovered by that point; finding the files within them was a separate problem.

I worked through the recovery in three stages:

  1. Create the most complete possible image of the failing disk with ddrescue.
  2. Recover files through the FAT32 filesystem, including using the second copy of the FAT.
  3. Use PhotoRec to carve files directly from the image where filesystem-level recovery was insufficient.

The priority was to minimise work against the failing physical disk. Once I had an image, I kept the filesystem investigation and recovery on that image.

The device names, paths and FAT offsets below belong to this particular recovery. In particular, the FAT replacement command uses this disk's geometry and writes to a working image with a backup retained. It is not a generic repair command to copy onto another disk.

Image the disk before investigating the filesystem

The source was /dev/sdb. I stored sonic.img and its companion map file on a healthy disk under /media/mediaTwo/Rescue/, then started with:

sudo ddrescue -n \
  /dev/sdb \
  /media/mediaTwo/Rescue/sonic.img \
  /media/mediaTwo/Rescue/sonic.map

The -n option skips the scraping phase. The aim was to secure readable data before spending more time on the difficult regions. The GNU ddrescue manual explains the recovery phases and map-file behaviour.

The map file is important because it records the state of every region of the source disk. Subsequent ddrescue runs can therefore continue using the same image without rereading areas that have already been recovered.

You can check the recovery's progress with:

ddrescuelog -t /media/mediaTwo/Rescue/sonic.map

The map distinguishes the following states:

  • rescued — successfully read and written to the image.
  • non-tried — not yet attempted.
  • non-trimmed — an area surrounding a failed read that still needs narrowing down.
  • non-scraped — failed areas that have not yet been attempted sector-by-sector.
  • bad-sector — sectors that ddrescue has attempted and could not read.

Skip the slow region and come back to it

The beginning of this disk was particularly difficult to read. Rather than keep hammering that region, I restarted recovery from 100 GiB:

sudo ddrescue -n \
  -i 100GiB \
  /dev/sdb \
  /media/mediaTwo/Rescue/sonic.img \
  /media/mediaTwo/Rescue/sonic.map

-i 100GiB specifies the starting input position.

I reused the same image and map file. With no separate output position specified, the output position follows the input position, so recovered data stays at the corresponding offset in sonic.img. The map keeps track of which regions have already been read.

From approximately 100 GiB onwards, the drive read consistently at around 9–10 MB/s with no read errors for most of the remaining disk.

That let me save the large healthy portion before returning to the troublesome region.

Approach the first 100 GiB backwards

After recovering the area above 100 GiB, I approached the first 100 GiB from the opposite direction. ddrescue can copy backwards, letting it enter a problematic region from the other side rather than repeatedly approaching the same failing boundary.

The map file continued to track the recovered areas as ddrescue worked through several passes, including forward and backward copying and trimming failed blocks.

The result eventually reached approximately:

mapfile extent:  500107 MB
 
non-tried:             0 B
rescued:       500099 MB
non-trimmed:           0 B
non-scraped:       8528 kB
bad-sector:       176640 B

Almost the entire 500 GB disk was now in the image.

Stop when the remaining reads stop paying off

With the easy data secured, I ran ddrescue without -n:

sudo ddrescue \
  /dev/sdb \
  /media/mediaTwo/Rescue/sonic.img \
  /media/mediaTwo/Rescue/sonic.map

The existing map let ddrescue continue with the outstanding areas instead of recopying the recovered data.

At this point it entered:

Scraping failed blocks... (forwards)

Scraping attempts to recover the remaining failed regions at a much finer granularity.

In this case the drive was producing very little additional data and an increasing number of read errors. For example:

non-scraped:    8335 kB
bad-sector:   294400 B
read errors:       225

I stopped the scraping run: the extra reads were producing very little useful data. The rest of the work could happen on the image.

Keep an untouched copy of the recovery

Before modifying the recovered filesystem, I retained copies of both the image and map.

At this stage the recovery directory contained:

sonic.img
sonic.img.after-trim
sonic.map
sonic.map.after-trim
sonic.map.bak

That gave me an untouched copy to return to if a filesystem experiment went wrong. From this point onwards, the failing physical disk could be disconnected.

Inspect and mount the image read-only

I checked the recovered image with fdisk:

sudo fdisk -l /media/mediaTwo/Rescue/sonic.img

This showed:

Disk sonic.img: 465.76 GiB
Disklabel type: dos
 
Device      Start       End   Sectors   Size Id Type
sonic.img1   2048 976750591 976748544 465.8G  b W95 FAT32

parted confirmed the same layout:

sudo parted /media/mediaTwo/Rescue/sonic.img unit s print

Result:

Number  Start  End         Size        Type     File system
1       2048s  976750591s  976748544s primary  fat32

The tools could read the partition table, and both placed the FAT32 partition at sector 2048.

Attach the image to a loop device

I attached the image to a loop device:

sudo losetup --find --show --partscan --read-only \
  /media/mediaTwo/Rescue/sonic.img

In this session, the command returned /dev/loop0. Use the device returned on your own system; it may have a different number.

--partscan causes Linux to discover partitions within the image, resulting in:

/dev/loop0
/dev/loop0p1

I identified the filesystem with:

sudo blkid -p /dev/loop0p1

which reported:

TYPE="vfat"
VERSION="FAT32"
FSBLOCKSIZE="32768"

I also inspected the boot sector:

sudo file -s /dev/loop0p1

and:

sudo hexdump -C -n 512 /dev/loop0p1

The output was consistent with a FAT32 boot sector; it did not establish that the rest of the filesystem was intact.

I then mounted the partition read-only:

sudo mkdir -p /mnt/sonic
 
sudo mount -o ro \
  /dev/loop0p1 \
  /mnt/sonic

Mounting it read-only prevented the operating system from modifying filesystem metadata inside the recovery image.

A mountable filesystem is not necessarily a healthy one

The filesystem mounted and the directory structure appeared. That looked promising, until I tried listing its directories:

find /mnt/sonic -maxdepth 2 -type d

produced several errors including:

find: '/mnt/sonic/2012': Input/output error
find: '/mnt/sonic/photos/Edinburgh 2014': Input/output error
find: '/mnt/sonic/PHONE PICS 2017': Input/output error
find: '/mnt/sonic/PHONE 2016': Input/output error

This showed that the disk image was mountable but that portions of the FAT32 filesystem metadata or file data were damaged.

Recover the readable files with rsync

I copied everything the mounted filesystem would let me read:

sudo rsync -a --info=progress2 \
  /mnt/sonic/ \
  /mnt/newDrive/sonic-recovery/ \
  2> ~/sonic-rsync-errors.log

rsync was useful here because a failure reading one file did not prevent the remainder of the filesystem from being processed.

Errors were captured separately in:

~/sonic-rsync-errors.log

The first pass recovered 8,744 files, totalling about 24 GB. Plenty of others, including JPEG photographs, returned:

Input/output error (5)

I needed to work out whether those errors meant the file data was missing or the FAT32 allocation metadata was damaged.

Investigate the two FAT copies

The FAT32 boot sector provided the following relevant values:

Partition start:       sector 2048
Bytes per sector:      512
Sectors per cluster:   64
Reserved sectors:      32
Number of FATs:        2
Sectors per FAT:       119203

The filesystem therefore used:

64 × 512 = 32768 bytes

per cluster.

More importantly, there were two copies of the File Allocation Table. FAT32 uses the FAT to record the cluster chains belonging to files. If a file occupies clusters:

1000 -> 1001 -> 4500 -> 4501

the FAT contains the links required to reconstruct that chain.

Damage to the FAT can therefore make a file inaccessible even when its actual data clusters were successfully recovered by ddrescue.

Extract the allocation tables

FAT1 starts immediately after the 32 reserved sectors. With the partition starting at disk sector 2048, that puts FAT1 at:

FAT1 start = 2048 + 32
           = 2080

I extracted FAT1 with:

sudo dd \
  if=/media/mediaTwo/Rescue/sonic.img \
  of=/mnt/newDrive/fat1.bin \
  bs=512 \
  skip=$((2048 + 32)) \
  count=119203 \
  status=progress

FAT2 starts immediately after FAT1:

FAT2 start = 2048 + 32 + 119203

I extracted FAT2 with:

sudo dd \
  if=/media/mediaTwo/Rescue/sonic.img \
  of=/mnt/newDrive/fat2.bin \
  bs=512 \
  skip=$((2048 + 32 + 119203)) \
  count=119203 \
  status=progress

Each extracted FAT was about 58.2 MiB (119203 × 512 bytes).

Compare the two copies

Hashes immediately established that the FATs were different:

sha256sum /mnt/newDrive/fat1.bin /mnt/newDrive/fat2.bin

I then counted the differing bytes:

cmp -l /mnt/newDrive/fat1.bin /mnt/newDrive/fat2.bin | wc -l

Result:

899063

The two supposedly mirrored FATs therefore differed in almost 900,000 byte positions.

I also checked where the differences started and ended:

Differing bytes: 899063
First difference: 24
Last difference: 5357566

The differences covered a substantial range near the beginning of the tables.

Look at the allocation entries

Each FAT32 entry occupies four bytes. I used a small Python script to count zero and non-zero entries. This was a rough comparison of the tables, not a full validation of cluster chains: non-zero entries can also include reserved values, bad-cluster markers and end-of-chain markers.

The results were:

                         FAT1          FAT2
 
Entries              15,257,984    15,257,984
Zero                 14,206,410    13,898,850
Non-zero              1,051,574     1,359,134

FAT2 contained 307,560 more non-zero entries than FAT1. That suggested it retained allocation information missing from FAT1, although the counts alone could not prove which copy was correct.

That provided a plausible explanation for the large number of inaccessible files despite ddrescue having recovered almost all physical sectors.

Try FAT2 in the working image

With a backup retained, I tried replacing FAT1 in the working image with FAT2. This was an experiment based on this recovery's findings, not an assumption that the second FAT is always the better copy.

First, I unmounted the filesystem and detached the loop device:

sudo umount /mnt/sonic
sudo losetup -d /dev/loop0

I then wrote FAT2 into the FAT1 location in the working image:

sudo dd \
  if=/mnt/newDrive/fat2.bin \
  of=/media/mediaTwo/Rescue/sonic.img \
  bs=512 \
  seek=2080 \
  conv=notrunc \
  status=progress

conv=notrunc is important because the destination is the complete disk image. The command replaces only the FAT region rather than truncating the image.

After:

sync

the image was attached again:

sudo losetup --find --show --partscan --read-only \
  /media/mediaTwo/Rescue/sonic.img

and mounted again, using the loop-device number returned by losetup (still /dev/loop0 in this example):

sudo mount -t vfat -o ro \
  /dev/loop0p1 \
  /mnt/sonic

Check that the files can actually be read

I tried listing the directories that had previously returned I/O errors:

ls -lah "/mnt/sonic/2012"
ls -lah "/mnt/sonic/PHONE PICS 2017"
ls -lah "/mnt/sonic/PHONE 2016"
ls -lah "/mnt/sonic/other photos"
ls -lah "/mnt/sonic/photos/Edinburgh 2014"

Four of the five previously inaccessible directories became accessible. Edinburgh 2014 continued to fail.

Seeing filenames was encouraging, but I wanted to know whether I could read the files themselves. I read several files from start to finish:

find "/mnt/sonic/PHONE PICS 2017" -type f -print0 \
  | head -z -n 10 \
  | xargs -0 -n1 sh -c \
  'printf "%s\n" "$0"; cat "$0" >/dev/null && echo "  OK" || echo "  FAILED"'

The tested photographs all returned:

OK

The sampled files could now be read all the way through. That supported the FAT2 approach, although a successful read does not by itself prove that a photograph is undamaged.

Copy the newly accessible files

I ran rsync again against the same destination, this time with --ignore-existing:

sudo rsync -a \
  --ignore-existing \
  --info=progress2 \
  /mnt/sonic/ \
  /mnt/newDrive/sonic-recovery/ \
  2> ~/sonic-rsync-fat2-errors.log

This preserved files already present at the destination and added missing ones. --ignore-existing skips any existing destination file; it does not check that the earlier copy is complete or correct.

The filesystem recovery grew from 8,744 files / 24 GB to 12,825 files / 40 GB: another 4,081 files and roughly 16 GB, with their original filenames and directories.

Only a few rsync errors remained, mainly for:

FOUND.000
photos/Edinburgh 2014

Recover more candidates with PhotoRec

I then tried PhotoRec to look for files without relying on the FAT filesystem.

PhotoRec performs file carving: it looks for known file signatures in the underlying data rather than depending on the directory structure or FAT cluster chains.

I ran it against the recovered image:

sudo photorec /media/mediaTwo/Rescue/sonic.img

I scanned the FAT32 partition and wrote the results to a separate recovery directory, /media/mediaTwo/Rescue/photorec.

The final PhotoRec recovery contained:

21,521 JPG
   160 PNG
   139 MOV
   121 MP3
   120 MP4
    74 3GP
    34 PLIST
    33 WMA
    33 M4P
    24 PDF
    15 DOC
     7 TXT
     3 ODT
     2 DOCX
     1 XML
     1 INI
     1 BMP

Total:

22,289 files

PhotoRec does not generally preserve the original filenames or directory structure, so I kept these files separate from the filesystem recovery.

Compare the recoveries by content

I expected many PhotoRec files to be copies of files already recovered through FAT1 or FAT2. To compare their contents, I generated SHA-256 hashes for the filesystem recovery:

mkdir -p ~/sonic-analysis
 
find /mnt/newDrive/sonic-recovery -type f -print0 \
  | xargs -0 sha256sum \
  > ~/sonic-analysis/sonic-files.sha256

and for the PhotoRec recovery:

find /media/mediaTwo/Rescue/photorec -type f -print0 \
  | xargs -0 sha256sum \
  > ~/sonic-analysis/photorec-files.sha256

I extracted the unique hashes:

cut -d' ' -f1 ~/sonic-analysis/sonic-files.sha256 \
  | sort -u \
  > ~/sonic-analysis/sonic-hashes
 
cut -d' ' -f1 ~/sonic-analysis/photorec-files.sha256 \
  | sort -u \
  > ~/sonic-analysis/photorec-hashes

To count exact content present in both recoveries, I used:

comm -12 \
  ~/sonic-analysis/sonic-hashes \
  ~/sonic-analysis/photorec-hashes \
  | wc -l

To count PhotoRec content missing from the filesystem recovery, I used:

comm -13 \
  ~/sonic-analysis/sonic-hashes \
  ~/sonic-analysis/photorec-hashes \
  | wc -l

The results were:

Unique hashes in filesystem recovery:  11,454
Unique hashes in PhotoRec recovery:    19,545
 
Exact hashes present in both:           9,579
PhotoRec-only hashes:                   9,966

These counts compare unique file contents, not numbers of photographs. Matching hashes identify byte-for-byte duplicates; a different hash only means different content, which may still be another version or a damaged copy of the same photo. No files were deleted by these commands.

Extra JPEGs are candidates, not a final photo count

My PhotoRec-only file list contained 11,087 JPEG candidates, totalling about 2.75 GB. This is a file count rather than a unique-hash count, so duplicates can occur within it. It does not mean 11,087 additional lost photographs.

Raw carving can also recover:

  • thumbnails;
  • cached images;
  • deleted files;
  • multiple copies of the same image;
  • embedded images;
  • partial or damaged files.

The PhotoRec output still gave me another place to look for files I could not recover through either FAT.

What I recovered

The failing disk ultimately produced:

Recovery stage Result
Physical image ~500 GB
Files recovered using FAT1 8,744
Additional files recovered using FAT2 4,081
Combined filesystem recovery 12,825 files / ~40 GB
PhotoRec carved files 22,289
PhotoRec JPEGs 21,521
Unique filesystem hashes 11,454
Unique PhotoRec hashes 19,545
Exact hashes shared by both 9,579
PhotoRec-only hashes 9,966
PhotoRec-only JPEG candidates 11,087 / ~2.75 GB

The lesson was that recovering the disk's bytes and recovering its files are separate jobs. ddrescue got almost the entire disk into an image, but damaged FAT metadata still left thousands of files inaccessible. Trying the second FAT restored access to another 4,081 files with their original names and directories. PhotoRec gave me more candidates to inspect, although its output still needed sorting and checking.

All filesystem changes stayed on the working image. The original failing disk was used only as the source for imaging. That left me free to investigate the filesystem without asking the failing hardware to survive every experiment.

Find the fix

Search articles

Esc

Search titles, technical terms or error codes.