Earlier today, my officemate and I were busy working on a big project, when he suddenly slammed his hands down on his keyboard, shouted 'F***!', spun his chair around, and kicked a cardboard box into the corner of the office.

He explained that he had accidentally deleted the script that he had been working on for the past four days. (He meant to delete a similarly named file with a different extension.) Of course, he wanted to know if there was any way to recover the file.

Before I go any further, I should say that it should have been possible to recover the file from CVS or a backup, but he hadn't checked the file into CVS yet, and the file was created after the most recent back up. Additionally, he had already deleted the emacs backup file. So, in the absence of those obvious and more reliable solutions...

I did a Google search for "undelete linux". One site described an unrm utility, but the download server wasn't reachable. On another site, I found a Linux FAQ with the question How Can I Undelete Files? It suggested searching the raw disk device to find the deleted file.

So, I turned to Perl, writing two quick scripts. These are just one-offs; I was more concerned at the time with recovering the file quickly rather than coding for strictness or reusability. BTW, these have to be run as root.

 

The first script reads the raw bytes of the device (`df` told me the file I wanted was in a directory mounted on /dev/sda6) looking for the string 'littleWindow', the name of a subroutine from the script. It prints out the byte offset and the text every time it finds a match.

#!/usr/local/bin/perl open(DEV, '/dev/sda6') or die "Can't open: $!\n"; while (read(DEV, $buf, 4096)) { print tell(DEV), "\n", $buf, "\n" if $buf =~ /littleWindow/; }

 

The second script uses byte offsets from the first script (matches were found in two separate locations), and reads in a big chunk around each one. My coworker said the file was around 20K, so I subtracted 20000 from each offset and read in 40000 bytes.

#!/usr/local/bin/perl open(DEV, '/dev/sda6') or die "Can't open /dev/sda6: $!\n"; open(OUT, '>tmp1.txt') or die "Can't open tmp1.txt: $!\n"; seek(DEV, 946192864, 0) or die "Can't seek: $!\n"; read(DEV, $buf, 40000) or die "Can't read: $!\n"; print OUT "$buf\n"; open(OUT, '>tmp2.txt') or die "Can't open tmp2.txt: $!\n"; seek(DEV, 970662368, 0) or die "Can't seek: $!\n"; read(DEV, $buf, 40000) or die "Can't read: $!\n"; print OUT "$buf\n";

 

Then I just opened up tmp1.txt and tmp2.txt in emacs and grabbed the contents of the missing file. It turned out that the file in tmp2.txt had already been partly overwritten, but the file in tmp1.txt was intact. Yay Perl!

When a file is deleted, all references to it in the file system are removed, but the actual bytes remain on disk until the space is used for a new file. I was fortunate that the deleted file was not a very busy file system, so I had time to grab the file before it was overwritten.

This is an ugly and unreliable solution; version control systems and frequent backups are much preferable. Nonetheless, Perl got the job done, and it was a great relief to recover the file.


In reply to Restoring deleted files under Linux by chipmunk

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.