paul_venezia
Senior Contributing Editor

Rsync file recovery

analysis
Jan 29, 20064 mins

It happens to the best of us. I was rsyncing about 500MB of data from a holding directory on a Linux box to a FreeBSD 5.4 developement server and inadvertently rsync'd a much older version of a parsing script into the development hierarchy. The current version of the script hadn't been checked into Subversion yet, and wasn't something easily rewritten. It was one of those Perl scripts where every regex was not l

It happens to the best of us.

I was rsyncing about 500MB of data from a holding directory on a Linux box to a FreeBSD 5.4 developement server and inadvertently rsync’d a much older version of a parsing script into the development hierarchy. The current version of the script hadn’t been checked into Subversion yet, and wasn’t something easily rewritten. It was one of those Perl scripts where every regex was not like the other, and would have taken several painful hours to rewrite.

What to do? Rsync synchronized the file without changing the inode, so there wasn’t a deleted file to look for, or was there? It was worth it to spend 30 minutes trying to recover the file, and I had recently edited the file with vim, which creates (and more importantly, deletes) a swapfile containing the contents of the edited file before editing.

The first order of business was to install Sleuthkit and Foremost from ports on the FreeBSD box. Being lazy, I didn’t take the disks offline, but rather ran the Sleuthkit tools on the mounted partition, /dev/da0s2a.

So, fls -rd /dev/da0s2a | grep scriptname gave me hope:

root@dev# fls -rd /dev/da0s2a | grep scriptname r/r * 1271828: usr/local/sbin/scriptname.pl.swp

Ah HAH! Even though rsync hadn’t deleted the file, the last vi swap file was still on the disk and those blocks hadn’t been reallocated! There was hope!

So, from the fls output, I knew that the file had inode 1271828. Where was that on the disk?

root@dev# fsstat /dev/da0s2a | more ...truncated... Group 54: Last Written: Mon Jan 30 00:22:27 2006 Inode Range: 1271808 - 1295359 Fragment Range: 5080752 - 5174839 Super Block: 5080792 - 5080799 Group Desc: 5080800 - 5080807 Inode Table: 5080808 - 5083751 Data Fragments: 5080752 - 5080791, 5083752 - 5174839 Global Summary (from the superblock summary area): Num of Dirs: 7 Num of Avail Blocks: 5501 Num of Avail Inodes: 23333 Num of Avail Frags: 548 Local Summary (from the group descriptor): Num of Dirs: 7 Num of Avail Blocks: 5501 Num of Avail Inodes: 23333 Num of Avail Frags: 548 Last Block Allocated: 5114936 Last Fragment Allocated: 5114936 Last Inode Allocated: 1271828

Looks like Group 54. The block range is 5080752 – 5174839, so let’s grab those:

root@dev# dls /dev/da0s2a 5080752-5174839 > recover-image.dls

Now, we have a block range where the deleted file should be. Let’s carve it with foremost. First, I needed to add a definition to the foremost.conf file to cull out perl and specifically .swp files:

# Perl

pl n 10000 #!/usr/bin/perl swp n 10000 #!/usr/bin/perl

The filesize of the script I was looking for wasn’t larger than 10k, so this should work. Now, to find it.

root@dev# foremost -c ./foremost.conf -o tmprec ./recover-image.dls

This dumped everything matching the above lines into the tmprec directory. In there were two subdirs containing .pl and .swp files. Grepping for a known code snippet in the .swp directory brought me to the file containing the missing script. There was a bunch of garbage at the end of the file since my filesize estimate was slightly generous, but the missing lines of code were there, saved by vim during my last edit. I lost the last changes I made to the script, but it was far better than completely rewriting it.

Normally you would perform these actions on either a disk image or an offline, unmounted volume, so don’t try this at home. However, if you’re in a pinch you can recover nearly anything from a UFS2 or ext3 volume if you act quickly enough. If too much time passes the blocks will be overwritten and the data will truly be gone. In my case, I was just lucky.