Method is similar to restoring a corrupt btrfs filesystem.

Trying to restore a file or folder or subvolume that you accidentally deleted and you dont have a snapshot (perhaps because you accidentally delete the snapshot[s] with your operation). No fear here is a guide on how to use btrfs-find-root along with btrfs restore to try and find your files. First we generate a list of roots to look through with btrfs-find-root, then we look through those roots with a dry (no writing) run of btrfs restore, and finally once we find the root that has our data we will do an actual run of btrfs restore (not dry). SIDENOTE: I refer to roots as Well block numbers as thats how they appear in the output of btrfs-find-root.

This process is not guaranteed to restore your data. Its just one way to try. Here is another method: http://comments.gmane.org/gmane.comp.file-systems.btrfs/22560

Well lucky for you that snapshots are not the only thing that can help you go back in time with BTRFS. There is also all of those Copy on Write transactions.

Method I am covering is also covered here: http://superuser.com/questions/603708/btrfs-undelete-file

We will be using BTRFS RESTORE, read up on this: https://github.com/kdave/btrfs-wiki/wiki/btrfs-restore

(First Step) GENERATE LIST OF WELL BLOCK NUMBER / TREE ROOTS

find all of the “Well blocks”

btrfs-find-root /dev/md127 &> /root/bfr.txt
cat /root/bfr.txt | grep "Well block"

You will see it be of the form:

Well block #, have: #, want: #.

More exactly its like this:

..skip (thousands of lines above that look similar)..
Well block 1170219008 seems great, but generation doesn't match, have=11910, want=67431 level 0
Well block 1170284544 seems great, but generation doesn't match, have=11910, want=67431 level 0
Well block 1170288640 seems great, but generation doesn't match, have=11910, want=67431 level 0
..skip (thousands of lines below that look similar)..

There should be many of these lines. Unless of course you dont take snapshots and dont have Copy on Write enabled.

We need the # next to the Well block.

Lets pretend for this example I lost a subvolume called “Pictures” & we are trying to recover it

 

(Second step) FIND ROOT WHERE DATA EXISTS

Start using this method to look thru the FS old points in time. This step is harmless as its a dry run. It just preforms a read operation but no writes.

From the bottom pick at random any Well block line. We will start with 1170288640

Well block #, have: #, want: #.

Here we are looking through the BTRFS filesystem /dev/sda3, and also we ask it to dump to /tmp. However it will not dump anywhere because of the -D. By the syntax rules we still need to specify a dump location even though nothing happens to it (we could also pick /dev/null). -v is verbose output to list files etc. -F is powerful helper (but might not be in your version of “btrfsprogs” although by now It might). Without -F you will get repeating messages “btrfs restore is looping through this file, we are at offset #, click Y to continue” so -F just clicks Y for us (or something along those lines – I might of got the message wrong, and it mmight say click N to continue. But the point is that -F automatically continues it for us so that we dont have to interfere). With -t we put that well block number, and btrfs restore will try to restore the data of that well block. Not sure if I said that correctly.

btrfs restore /dev/sda3 /tmp -D -v -F -i -t 1170288640

Now you saw there is a million well block, we can look through all of them, but it will take time and it will probably be pretty intense operation so we can simply nice it and loop it. HEre are two scripts that can help with that. One looks through every well block number. The 2nd script that one will look through X number of well blocks above a certain well block number (that perhaps you found sommething at) and Y number of well blocks below a certain well block number. The 2nd script is good if you found a well block number with some enteries and you want to look above and below.

NOTE: These scripts are copy pasteable, just change the variables to match your needs.

(
# WHAT THIS DOES: Look through all Well Blocks for our MISSING FILE or FOLDER
# NOTE: edit script variables before running
# ---- start of variables ---#
VOL=/dev/sda3
FINDROOTOUTPUTFILE=/root/bfr.txt
# modify MISSINGFILEFOLDER to something cool
MISSINGFILEFOLDER="Pictures";
# ---- end of variables ---#
# First generate the Well block number list with btrfs-find-root.
btrfs-find-root $VOL &> /root/bfr.txt
# loop thru every Well Block in bfr.txt and run dry btrfs restore on it.
# Also count & show each iteration with WBN. WBTOTAL lists total number of iterations that we will run thru. So we can have a sense when this will be over when we run this.
WBTOTAL=$(cat ${FINDROOTOUTPUTFILE} | grep "Well block" | wc -l); WBN=0;
for i in $(cat ${FINDROOTOUTPUTFILE} | grep "Well block" | sed -n 's/^Well block \([0-9]*\) seems.*/\1/p'); do
WBN=$((WBN+1))
echo "---- (${WBN}/${WBTOTAL}) Testing With Well block # $i ($REGEXP) [`date` / `date +%s`] -----";
# below is the BTRFS RESTORE command that does all the magic, you mmight need to remove -F if your version of BTRFS RESTORE doesnt have it (you might need to watch the output and click a keyobard button to continue over and over without the -F option)
# we nice it, we also ask errors to go to /dev/null, or else output is huge (you can change that if you want). Also we just want to get wc -l/count. Then we know the ones with the most files/folders counted is probably what we are looking for
ionice -c2 -n7 nice -n +15 btrfs restore $VOL /tmp -D -v -F -i -t ${i} 2> /dev/null | grep "$MISSINGFILEFOLDER" | wc -l;
# if you dont care about being "nice" and "ionice", uncomment below and comment above
# btrfs restore $VOL /tmp -D -v -F -i -t ${i} 2> /dev/null | grep "$MISSINGFILEFOLDER" | wc -l;
done;
)

THe next script looks around a well block number that you found something on. Lets assume that we found the Pictures folder around 1170288640 but it didnt have all of the pics. So lets look above and below. Above will be older transactions (so maybe before some delete). Below will be after some transactions so perhaps the writes of the pictures are still to happen.
(
# WHAT THIS DOES: Look through a few Well Blocks up and down from a specific Good Well Block number, where you probably found something, to find a MISSING FILE or FOLDER. We want to look at neighbouring well block numbers perhaps they might have more files.
# NOTE: edit script variables before running
# ---- start of variables ---#
VOL=/dev/sda3
FINDROOTOUTPUTFILE=/root/bfr.txt
# modify MISSINGFILEFOLDER to something cool and put the well block number that you found some data in.
# LOOKABOVE is how many well block transactions above to look (how many transaction before to look thru), LOOKBELOW is how many Well Block transactions below to look (how many transactions after to look thru). GOODWELLBLOCKNUMBER is the well block number you found some of your missing data at, so you want to look at the neighbouring well blocks to see if you can find more data.
GOODWELLBLOCKNUMBER=1170288640;
MISSINGFILEFOLDER="Pictures";
LOOKABOVE=100 
LOOKBELOW=100 
# LOOKABOVE: this is the same as "before", if you want to look from the beginning to the good well block number, change to "LOOKABOVE=100000000000000". If you dont want to look above/before change to "LOOKABOVE=0"
# LOOKBELOW: this is the same as "after". If you want to look from the good well block number to the end, change to "LOOKBELOW=100000000000000". If you dont want to look below/after the good well block number change it to "LOOKBELOW=0"
# ---- end of variables ---#
# First generate the Well block number list with btrfs-find-root.
btrfs-find-root $VOL &> /root/bfr.txt
# loop thru every Well Block in bfr.txt and run dry btrfs restore on it
# generating look list (dont touch this variable - you can delete it after the script is done):
LOOKLIST=${FINDROOTOUTPUTFILE}.look.list.`date +%s`
cat ${FINDROOTOUTPUTFILE}| grep "Well block" | grep -A ${LOOKBELOW} -B ${LOOKABOVE} "${GOODWELLBLOCKNUMBER}" > ${LOOKLIST}
# Also count & show each iteration with WBN. WBTOTAL lists total number of iterations that we will run thru. So we can have a sense when this will be over when we run this.
WBTOTAL=$(cat ${LOOKLIST} | grep "Well block" | wc -l); WBN=0;
for i in $(cat ${LOOKLIST} | sed -n 's/^Well block \([0-9]*\) seems.*/\1/p'); do
WBN=$((WBN+1))
echo "---- (${WBN}/${WBTOTAL}) Testing With Well block # $i ($REGEXP) [`date` / `date +%s`] -----";
# below is the BTRFS RESTORE command that does all the magic, you mmight need to remove -F if your version of BTRFS RESTORE doesnt have it (you might need to watch the output and click a keyobard button to continue over and over without the -F option)
# we nice it, we also ask errors to go to /dev/null, or else output is huge (you can change that if you want). Also we just want to get wc -l/count. Then we know the ones with the most files/folders counted is probably what we are looking for
ionice -c2 -n7 nice -n +15 btrfs restore $VOL /tmp -D -v -F -i -t ${i} 2> /dev/null | grep "$MISSINGFILEFOLDER" | wc -l;
# if you dont care about being "nice" and "ionice", uncomment below and comment above
# btrfs restore $VOL /tmp -D -v -F -i -t ${i} 2> /dev/null | grep "$MISSINGFILEFOLDER" | wc -l;
done;
)

After finding out which well block produces the most numbers. You should rerun it like this to see what files it found
btrfs restore /dev/sda3 /tmp -D -v -F -i -t #

TIP: making a file every now and again in your filesystem can be helpful. you could look for that file like a marker. Then you will know the exact date and time of the “Well block” numbers. And if needed can interpolate for the Well block numbers in between.

 

(Third step) DUMPING THE DATA

Now that we identified the well block number that we want, we can go ahead and dump the data out. Lets assume we mounted a giant storage location to /mnt for backup purposes.

btrfs restore /dev/sda3 /mnt -v -F -i -t 1170288640

That will dump out all of /dev/sda3 at wellblock number 1170288640

So there is a way to ask btrfs restore to restore only what you want. From the output of “btrfs restore … -D …” above we should know how our restored files look like.

You have to use the –path-regex argument with a regular expressions of this form: –path-regex¬†‘^/(|home(|/username(|/Desktop(|/.*))))$’

If my Pictures folder was on the root of the BTRFS filesystem so it was simply /Pictures (of course when its mounted, assuming sda3 mounted to /data, then it would be like this /data/Pictures). Then the regular expression will look like this: ‘^/(|Pictures(|/.*))$’
Below will only restore the “/Pictures/” Subvolume/folder in the BTRFS volume.

btrfs restore /dev/sda3 /mnt -v -F -i -t 1170288640 --path-regex '^/(|Pictures(|/.*))$'

So if my Pictures folder was in a folder called /Media. So it looked like this /Media/Pictures (of course when its mounted, assuming sda3 mounted to /data, then it would be like this /data/Media/Pictures). Then the regular expression will look like this: ‘^/(|Media(|/Pictures(|/.*)))$’
Below will only restore the “/Media/Pictures” Subvolume/folder in the BTRFS volume.
btrfs restore /dev/sda3 /mnt -v -F -i -t 1170288640 --path-regex '^/(|Media(|/Pictures(|/.*)))$'

 

3 thoughts on “BTRFS UNDELETE – Recovering Deleted folder/file without snapshots

  1. Can I use this method from a windows 10 mashine with a BTRFS disk connected into it?
    I am looking for a way to recover a deleted folder in my Netgear ReadyNAS server…
    And in this time I have connected the disks to my windows computer.

  2. Accidentally overwrote my Picture archive folder. This article saved my butt and 10 years of picture. RAID with disk redundancy is all well and good until you just run a wrong “rm” command…

    Thanks a bunch for the guideline, it helped me to recover almost all files!

Leave a Reply

Your email address will not be published. Required fields are marked *