Just for fun, let’s say you accidentally blew away your partition table. Or perhaps on purpose, or perhaps something else went horribly wrong, but now you have a theoretically-functional filesystem, but you just can’t find it. Now what?
Finding Magic Numbers
First of all, if you know what the original values you had plugged in to fdisk
were, just put things back the way they were and it should “just work”. That’s why it’s always a good idea to print out your partition table before changing it. That way the numbers are there in the scrollback buffer.
[root]# fdisk /dev/sda Command (m for help): p Device Boot Start End Blocks Id System /dev/sda1 * 2048 3905535 1951744 83 Linux /dev/sda2 3905536 500117503 248105984 83 Linux
Now when it all hits the fan, you can just type in the numbers you see to get things back to normal. But let’s assume that’s not an option here: we need to actually find the filesystem on the disk.
Filesystems typically have a “magic number” that appears at a specific offset; find that magic number, and you often have found your filesystem. For Linux EXT-2/3/4, that magic number is 0xEF53 found at offset 1080 (0×0438).
An Example
So we have a disk at /dev/sdd
. There’s no partition table. We know that it was formatted as ext4. We tried a few common possibilities in fdisk but to no avail. So let’s just grep
the drive and look for our magic number.
Note that the EXT family of filesystems use Intel byte ordering, so the bytes appear in reverse order. So we’re looking for “53EF” instead of “EF53″. We’ll use dd
to read the disk and xxd
to hex-dump the data. In a sensible system the bytes should be aligned, but we can’t guarantee that. So most likely we’ll see something like:
.... .... 53ef .... ....
but we can’t rule out something like this:
.... ..53 ef.. .... ....
There’s the possibility of the byte sequence straddling a line-break in our display, but that’s harder to search for, so we’ll start with this:
[root]# dd if=/dev/sdd bs=1M count=10 skip=0 | xxd | grep -E '53 ?ef' 0053ef0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0153ef0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0200430: 49ad e052 0200 ffff 53ef 0100 0100 0000 I..R....S....... 0253ef0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0353ef0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
If you’re playing along at home, here’s what those options mean. if
means “input file” — our disk in this case. bs
means “block size”, which sets the context of the next options, and which we set to 1MB, count
is the number of block to actually read (so that’s 10MB in our case), and skip
tells how many blocks to skip (none). To grep
we pass the -E
option, meaning we’ll be using a real regular expression, while 53 ?ef
means match “53″ possibly followed by an optional space, followed then by ef
. We know that the hex output of xxd
is lowercase, so we don’t need to do case-insensitive matching.
In the output, we see several lines where 53ef
shows up in the address, while the first place where it shows up in the data looks like a reasonable candidate for our magic number. In the filesystem superblock, the pair of bytes right after the magic number is the filesystem state. 0100
in this case would mean “cleanly unmounted”, so that’s reasonable. The pair right before it is the number of mounts allowed between checks; ffff
means “no limit”, which sounds right. And the pair before that is the number of times the filesystem has been mounted; 0200
would mean 2 (Intel byte order). Again, looks reasonable. So let’s proceed.
The magic number was found on the line labeled 0×200430, with the sequence found 8 bytes in from there, so comes out to offset 0×200438. We were expecting the sequence to appear at offset 0×438, Subtracting 0×000438 from 0×200438 gives us the offset for the filesystem: 0×200000. That’s a pretty round number in hex, but in decimal it’s 2097152.
Now we have two options; we can either create a corresponding partition, or we can simply mount the filesystem at an offset. We’ll try the offset first as a test, and then create the partition table. You always want to mount filesystem read-only if you’re doing recovery work, hence the ro
option.
[root]# mount /dev/sdd -o ro,offset=2097152 /mnt/test/ [root]# ls /mnt/test/ . .. lost+found my_important_stuff.txt [root]# umount /mnt/test/
Looks like we have our filesystem back. Let’s create the partition table.
[root]# fdisk /dev/sdd Command (m for help): p Disk /dev/sdd: 8103 MB, 8103395328 bytes 250 heads, 62 sectors/track, 1021 cylinders, total 15826944 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x27b954b9 Device Boot Start End Blocks Id System
As mentioned, we always print out the partition table before changing it, just in case. But in this instance, we learn an important detail: all our numbers will be given in 512-byte sectors. If we divide our offset of 2097152 by 512, we get 4096. So that’s how many sectors in our partition needs to start.
Command (m for help): n Partition type: p primary (0 primary, 0 extended, 4 free) e extended Select (default p): p Partition number (1-4, default 1): 1 First sector (2048-15826943, default 2048): 4096 Last sector, +sectors or +size{K,M,G} (4096-15826943, default 15826943): Using default value 15826943
Here we used the default “last sector”, assuming that the filesystem takes the rest of the partition. If it doesn’t, there’s no harm; the filesystem can be smaller than the partition that contains it, but you don’t want the partition to be too small.
Let’s print out our new partition table for our reference, and then save it to disk.
Command (m for help): p Disk /dev/sdd: 8103 MB, 8103395328 bytes 250 heads, 62 sectors/track, 1021 cylinders, total 15826944 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x27b954b9 Device Boot Start End Blocks Id System /dev/sdd1 4096 15826943 7911424 83 Linux Command (m for help): w The partition table has been altered! Calling ioctl() to re-read partition table. Syncing disks.
Nothing left but to try it. Again, mounting read-only.
[root]# mount -o ro /dev/sdd1 /mnt/test/ [root]# ls /mnt/test/ . .. lost+found my_important_stuff.txt
Success!
If you’re confident that you’ve restored things perfectly, you can remount read-write and go about your business. Otherwise, it’s a good idea to copy all the files off this drive and onto one that you’re certain has been properly partitioned and formatted.