I'm currently trying hard to rescue a very big file off a HDD which is about to die (clicking sounds). The read failure is caused by just a small couple defective sectors.
As the backup I could retrieve is way too old, my first plan was to get the file off there even with the corrupt data. PhotoRec turned out to be the ideal choice for this job since it indeed managed to recover the whole file. (testdisk was an epic fail, as it would've simply cut off the file where the error was detected)
Having retrieved the (partly corrupt) file onto the intact target HDD, the next steps to take were:

  • use hdparm --read-sector <device> and create binary data files from its output
  • use the valid data from these to repair the (few) corrupt sectors using a hex editor like Okteta.

And indeed, raw-read routines of hdparm (I used v9.43) (--read-sector) could be convinced to read the data off the (few) bad sectors on that one erroneous cylinder, but only gave me back a true ascii file. There is no way I know of directly getting the output to a binary file to perform the second step described above. Moreover, all words in the output files turned out to be byte-swapped (x86-32 here on Linux; fixed in v9.45, cf. this older entry in bug tracker).

Fortunately though, there is a way to make it work even with v9.43 and earlier, by using hdparm's --verbose option, which (at the incoming_data line) will allow the hex values to get output exactly as read, in correct byte order!
So this is what I came up with so far, to read 50 sectors starting from 5000:

while [[ $((i++)) -lt 50 ]]; do
  sudo hdparm --verbose --read-sector $((5000+i)) /dev/sdb 2>&1 | 
  grep 'incoming_data' | cut -f2- -d: | sed 's/^  //' | 
  tee -a ascdata1_nl
tr '\n' ' ' < ascdata1_nl > ascdata1

Caveat: First file, ascdata1_nl, still contains newline characters. When the loop is finished, after transforming the newlines into spaces, ascdata1 will now contain the values as needed. Now byte by byte gets written into a .bin file:

 while read -d ' ' hexbyte; do printf "\x$hexbyte" | tee -a bindata.bin; done < ascdata1

The result will be a true binary file which can (e. g. with 'Okteta') be used to replace the (erroneous) zeroed areas in the file.

Short answer: it works that way.

Still, this procedure appears a bit over-complicated to me (?).

Any simpler way to obtain a binary file from a --read-sector "hex" dump?

  • Using perl, I am sure the --verbose option in hdparm can be omitted since pack() / unpack() will do a fine job. (Just I haven't managed to do this with byte pairs)
  • dd conv=swab : I also gave this dd option a try, which will unfortunately swap nibbles only, turning a6b8 d6b7 into (useless) 6a8b 6d7b.
  • What? hdparm --read-sector isn't going to have any easier time reading a bad sector than anything else. You want ddrescue, which can at least skip the bad block and come back to it later, retrying until it hopefully works.
    – psusi
    Dec 16 '14 at 2:53
  • 1
    Well, the most important thing to me was, that in the hdparm manual (I read it thoroughly several times), there was something explained about hdparm using an extremely low-level approach to read sectors when using --read-sector. So frankly, I wasn't sure (or better: much too skeptical) about whether ddrescue would use a likewise low-level approach or one or more tiers higher. Needless to say that the lowest tier was just good enough to get something useable out of those few corrupt sectors. Dec 16 '14 at 3:19
  • The man page is a bit misleading then. It doesn't do anything magic. It only crafts the command and sends it down itself instead of letting the kernel do it. The actual command the drive sees is exactly the same.
    – psusi
    Dec 16 '14 at 14:30
  • Well, this is just what I wasn't sure about two weeks ago. Dec 16 '14 at 15:31
  • Dear @psusi & @syntaxerror, The hdparm man page states in part, hdparm will issue a low-level read (completely bypassing the usual block layer read/write mechanisms) for the specified sector. This can be used to definitively check whether a given sector is bad (media error) or not (doing so through the usual mechanisms can sometimes give false positives). I recently had a drive that returned I/O errors when reading certain sectors with hexdump or dd that hdparm --read-sector was able to read successfully: asciinema.org/a/182611 . May 21 '18 at 10:22

Assuming you have GNU sed, you can try (not really tested):

 hdparm --read-sector $SECTOR /dev/sdb | \
  sed -r -e "0,/succeeded/d" -e 's/(\S\S)(\S\S)/\2\1/g' | \
  xxd -r -p >> bindata.bin

The sed call deletes everything up to the succeeded line and then byte-swaps the output. The xxd -r -p call translates plain hexadecimal code to binary data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.