Saturday, February 23, 2008

Recovering a (Lost) ReiserFS Partition

The partition table on my sister's laptop always seemed fishy to me. It just didn't have the right feel about it. The cylinder numbers and sizes and all, but I never cared. As the laptop went through a series of installations, Ubuntu or Windows, the abnormality showed up.

After installing Windows, I found hard to install GRUB on MBR. The grub-install would refuse, and so would the grub. I was troubled when the output of cfdisk said that 30G is unusable space, out of my 60G disk.

But, I thought its just another routine partition table corruption, and I had a tool in my swiss-army knife - testdisk (http://www.cgsecurity.org/wiki/TestDisk). Testdisk analyses your disk for lost partitions and tries to guess the correct partition table. Then it shows the guess to the user, who can then choose to write the table or not.

In my case, though testdisk correctly guessed the partitions (it found more than actual ones), it could not guess a proper partition table. Instead, it asked me to specify which partition is primary, logical or bootable primary. Now that was difficult for me. And I didn't want to take risks.

One good thing about testdisk is that it lets you peek inside partitions and read filenames. I was particularly interested in recovering a ReiserFS partition, which was 20G in size. Fortunately, testdisk was able to see this partition, and the filenames too. It was found lying from cylinders 3856 to 6287.

Anybody who knows a bit about recovery knows that if you know the cylinder numbers, you've solved the problem. Recovering a partition becomes an easy job. I started by dd'ing out from cylinder 3856 about 1MB. But the result was not as expected. It wasn't a superblock. It was just 'data'. Just data? How could it be that testdisk was lying?

$ file mydump
mydump: data

I checked the calculations again, yet to no avail. It was data there. One thing was for sure -- the superblock must begin somewhere near cylinder 3856. I got a weird idea. I quickly made a dummy file of 200M using dd. I made a loop device out of it, and did a reiserfs format over it.

$ dd if=/dev/zero of=check-rfs bs=1M count=200
$ sudo losetup -f check-rfs
$ sudo mkfs.reiserfs /dev/loop0
$ hd check-rfs | less

I intended to find out the pattern with which a reiserfs superblock starts. The output of hexdump showed something like this:

00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00010000 00 32 00 00 ec 11 00 00 13 20 00 00 12 00 00 00 |.2....... ......|
00010010 00 00 00 00 00 20 00 00 00 04 00 00 93 98 2f 43 |..... ......../C|
00010020 84 03 00 00 1e 00 00 00 00 00 00 00 00 10 cc 03 |................|
00010030 02 00 01 00 52 65 49 73 45 72 32 46 73 00 00 00 |....ReIsEr2Fs...|
00010040 03 00 00 00 02 00 01 00 02 00 00 00 00 00 00 00 |................|

Look how smartly the filesystem developers have written ReIsEr2Fs there! The third line, which has 2 and a space, actually contains disk label.

With this amount of information, I tried to find the superblock in the 10MB dump.

$ hd mydump | grep -i reiser2fs

And fortunately, it was there! At an offset of about 1d200h from cylinder 3856.

0001d200 02 00 01 00 52 65 49 73 45 72 32 46 73 00 00 00 |....ReIsEr2Fs...|

Looking a few 100 bytes before this pattern, I found exactly the same fingerprint of ReiserFS superblock! After calculating and adding, I did a dd dump from the correct byes, and here it was -- the superblock!

$ file dump-again
dump-again: ReiserFS V3.6 block size 4096 num blocks 4883760 r5 hash


I took a bigger dump (2G), and tried to mount it. But mount refused, simply because there was not enough space on the loop device than the partition required (20G).
I looked for some IRC help, and enouf on ##linux suggested the offset option of the mount command. It can do just what I wanted -- to mount a range of bytes of a disk as a device.

$ sudo mount /dev/sda recovered/ -o ro,loop,offset=31716679680
$ ls recovered/
bin cdrom etc initrd initrd.img.old lost+found mnt proc sbin sys tools var vmlinuz.old
boot dev home initrd.img lib media opt root srv tmp usr vmlinuz

And there it was! Recovered intact. The next thing I did was to backup the 15G data it had. And I got a treat from my sister, too. :-P

Simplicity is often wonderful. And so is Linux.