UNIX File Undelete "Tool"

Yes, there's a very good reason why the word "tool" is in quotes, as you shall see.

Ever delete a file on a UNIX machine and then say, "oh, wait, I didn't want to delete that" only to find out that there's no way to undelete it?  Sure you have.  And normally, in a case like that, what people tell you is that you should alias the rm(1) command over to the mv(1) command, and then have all files moved to a temporary dumpster directory before being permanently deleted off the disk.  I agree with this completely, and as such, this "tool" isn't designed to help you in those situations.

However, there are times when a file gets deleted anyhow, through no fault of your own.  One such example was when I was working on my ramdisk driver.  As I'm sure isn't an altogether uncommon event for people developing their first loadable kernel module, my driver had bugs in it that caused the machine to crash.  And, as crashes do from time to time, some of my data got eaten (although I now make it a point to manually issue a sync(1M) command before loading anything that might crash the machine to help prevent this data loss).  Well, in this case, not some... just one specific file.  That file, of course, was the source code for the entire ramdisk driver.

I tried to put a good face on this situation.  First, I joked and said "My computer is just telling me that I'm not cut out for this, and it wants me to stop hurting it."  Next, I tried to accept it by saying "I suppose it's for the better, I should stop working on drivers nobody wants and go try to get a job."  I was able to delude myself in this capacity for almost exactly 24 hours.  It was at that point that I said "Dammit, I want my friggin' driver back.  I'm not going to let some stupid system crash get in my way."

And thus, this "tool" was born.

Not bad for about 20 minutes worth of work (I was watching TV at the time, something us jobless people like to do), especially since it recovered the file.  This would have been easier if I could have retrieved the free block list for my drives, but again, since the IRIX source is hard to come by (and SGI isn't making any visible progress on open-sourcing their XFS filesystem) I did the best I could.  I named the script "dread.pl" because 1) it's a Data READing script and 2) you DREAD having to run it.  Think of it as grepping the raw partition data for a unique string of data.  And you want that string to be small, because the data is read in 512 byte blocks, and you run the risk of the string straddling a block boundary.

I haven't put a downloadable version of this "tool" up because if you're going to use it, you should really understand what it does and how it does it first.  It's not complicated or anything, but you have to run it as root, and you have to make sure that you understand you are tinkering with a raw filesystem.  Also, you really should send the output to a separate disk and have this disk unmounted at the time, because if you don't the program will write out data that could possibly overwrite the data you are looking for, plus since the output data contains the string you are searching for, the script will match the output data and output the data again.  Basically, it will recurse until your drive is full.

This script was written in desperation, and it shows.  But, it saved my bacon, taught me to be especially certain to make frequent (like hourly) backups of critical in-use files, and showed me that as long as you have some understanding of how the underlying system works, it's sometimes possible to do things other people tell you can't be done.

A couple important notes about what this little script does: It was written under IRIX, and the raw disk device specified in here follows IRIX 6.5 conventions for the raw block device for a disk.  I could have used the character device interface for the disk as well, and I assume it would have worked in pretty much the same way, but since the data on the disk is stored in 512 byte blocks as it is, it made more sense to do it this way.  In that regard I could be given a block number where the data was located, and I could use dd(1M) to go back later and extract the data, plus a chunk of data on either side of that block as well.  Ultimately this worked really well.  Not only did I pull out the desired file, I also pulled out 20 or so old versions of it scattered around the disk.  This script is in no way optimized or cleaned up, or even well tested.  I just put it here as a demonstration of how such a task is done, plus since this little bugger saved me from a large amount of hair pulling over the loss of my driver, I figured it deserved its own little space on my Bad Ideas page.


"dread.pl"

#!/usr/bin/perl

my $block;
my $searchtext = "k_ini";
my $foundit = 1;
my $blockcount = 0;

open(RAWDISK, "/dev/dsk/dks0d1s0");

while ($foundit)
{
        if (read(RAWDISK, $block, 512))
        {
                if ($block =~ /$searchtext/i)
                {
                        print "Found at block $blockcount:\n";
                        print "###########################\n";
                        print $block;
                        print "\n###########################\n";
                        $foundit = 0;
                }
                ++$blockcount;
        }
}

close(RAWDISK);


Back to the Bad Idea page