2

I have a mapfile produced by ddrescue which lists 418 bad sectors where each line looks like this (the minus indicates a bad block):

Position      Size
0x1CC7C68000  0x00001000  -

By converting the position in bytes to the partition relative sector number, I can use debugfs to query the inode number and then find the path of the broken file. Doing this manually is not feasible for almost 2000 bad blocks, so I would like to automate this, is there a way to script debugfs to execute a sequence of commands on a file system?

Here is what I am currently doing to get the file name for a broken sector:

  1. The positions in the ddrescue mapfile are in bytes relative to the start of the disk. First, I convert the position to the sector number by dividing with 512 and then I subtract the start sektor position of the partition:

    Partition of first sector: 91914240 Bad block position: 0x1CC7C68000 In decimal: 123610759168 Absolute sector position: 123610759168 / 512 = 241427264 Relative block position on partition: 241427264 - 91914240 = 149513024

So the bad sector is at 149513024 relative to the partition start, using debugfs I can now find the inode:

$ debugfs
debugfs: open /dev/sdd3
debugfs: icheck 149513024
Block       Inode number
149513024   1183169
debugfs: ncheck 1183169
Inode   Pathname
1183169 /username/foo/bar/baz

I would like to automate this process so I can pass a list of block positions to debugfs, resolve these blocks into inodes, filter the inodes to exclude unmapped inodes and then use ncheck to resolve the path names for the remaining inodes. Is this possible with debugfs and some shell scripting?

2 Answers 2

1

I believe I have found a solution for my problem. However, I am still curious if anyone can come up with a more elegant solution or possibly find a mistake in my solution.

As it turns out, I could write to stdin of debugfs, so I only needed to generate a sequence of commands for debugfs, to analyze the output of ddrescue.

The following bash script assumes that a file named mapfile.ddrescue is present in the current directory which has been produced by ddrescue.

for line in \
    $(cat mapfile.ddrescue | \
      grep -e "-$" | \
      awk -F" " '{print $1}' | \
      awk -F"0x" '{print $2}'); \
do \
    position=$(( 16#$line / 512 - 91914240 )); \
    result="$result $position"; \
done; \
echo -e "open /dev/sdd3\nicheck $result\nquit\n" | sudo debugfs

Here is what this script does:

  1. I parse the mapfile from ddrescue, which I have named mapfile.ddrescue.
  2. I filter it to keep only lines which end with a hyphen. Those are the positions with bad blocks.
  3. I use awk to split at whitespace and print the first token which is the position. This will contain a hexadecimal number such as 0x34A933F000.
  4. I remove the Ox prefix.
  5. The result is returned by the $(...) call hand serves as input to the for-loop, thus line will always contain one position.
  6. I use the $(( ... )) expression to do math computation on the position, divide the position by 512 (e.g. bytes per sector) and subtract the partition start which in my case is 91914240. This gives a position in sektors relative to the partition start.
  7. I concatenate each position into a space separated list which is stored in $result.
  8. Finally, I generate newline-separated command list which I pipe to stdin of the debugfs command which I run with sudo. The command opens the device (in my case /dev/sdd3). It then runs icheck on the $result and quits from debugfs.

When I ran this script, debugfs took a long time to find all the inodes for those blocks, in my case it seemed to hang for several minutes until it printed the output.

When the script completed I copied the result into a text file and analyzed it. Luckily, most sectors were pointing to unallocated blocks and of the remaining ones most pointed to the same few inode numbers. After removing lines with <block not found> and removing duplicates only four inodes remained which I could manually check with debugfs using ncheck. This gave me four file paths, those are the files that I will now try to restore from a backup.

Background I originally started out, with dd and wanted to copy the contents of a 256GB SSD to a bigger SSD. dd aborted with I/O errors at about 45/185 GB of the last partition. However, with ddrescue I could save 99.99% of the drive. Finally, with the above solution, I was able to check to which files the remaining 1700kb or 418 bad areas belong and found only four files that are broken. This has sufficiently increased my trust in the restored data since I now know which files are broken and can restore them from an older backup.

1

I had to do this today after my rpi3b+ SD card died. I ended up writing a python script doing what you did in your bash script above, except it will spit out the file paths automatically. You can check it out here: https://github.com/zkrx/rescue2path

In this solution, I also check for not-yet-scraped chunks of data ('/'). Scraping took a very long time here and I just stopped before it completed.

This entry in the Arch wiki was particularly helpful: https://wiki.archlinux.org/index.php/Identify_damaged_files

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .