Skip to content

Instantly share code, notes, and snippets.

@Changaco
Last active December 2, 2024 04:36
Show Gist options
  • Save Changaco/45f8d171027ea2655d74 to your computer and use it in GitHub Desktop.
Save Changaco/45f8d171027ea2655d74 to your computer and use it in GitHub Desktop.
btrfs-undelete
#!/bin/bash
# btrfs-undelete
# Copyright (C) 2013 Jörg Walter <info@syntax-k.de>
# This program is free software; you can redistribute it and/or modify it under
# the term of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or any later version.
if [ ! -b "$1" -o -z "$2" -o -z "$3" ]; then
echo "Usage: $0 <dev> <file/dir> <dest>" 1>&2
echo
echo "This program tries to recover the most recent version of the"
echo "given file or directory (recursively)"
echo
echo "<dev> must not be mounted, otherwise this program may appear"
echo "to work but find nothing."
echo
echo "<file/dir> must be specified relative to the filesystem root,"
echo "obviously. It may contain * and ? as wildcards, but in that"
echo "case, empty files might be 'recovered'. If <file/dir> is a"
echo "single file name, this program tries to recover the most"
echo "recent non-empty version of the file."
echo
echo "<dest> must be a writable directory with enough free space"
echo "to hold the files you're trying to restore."
exit 1
fi
dev="$1"
file="$2"
file="${file#/}"
file="${file%/}"
regex="${file//\\/\\\\}"
# quote regex special characters
regex="${regex//./\.}"
regex="${regex//+/\+}"
regex="${regex//|/\|}"
regex="${regex//(/\(}"
regex="${regex//)/\)}"
regex="${regex//\[/\[}"
regex="${regex//]/\]}"
regex="${regex//\{/\{}"
regex="${regex//\}/\}}"
# treat shell wildcards specially
regex="${regex//\*/.*}"
regex="${regex//\?/.}"
# extract number of slashes in order to get correct number of closing parens
slashes="${regex//[^\/]/}"
# build final regex
regex="^/(|${regex//\//(|/}(|/.*${slashes//?/)}))\$"
roots="$(mktemp --tmpdir btrfs-undelete.roots.XXXXX)"
out="$(mktemp --tmpdir="$3" -d btrfs-undelete.XXXXX)"
cd $out
trap "rm $roots" EXIT
trap "rm -r $out &> /dev/null; exit 1" SIGINT
echo -ne "Searching roots..."
btrfs-find-root -a "$dev" 2>&1 \
| grep ^Well \
| sed -r -e 's/Well block ([0-9]+).*/\1/' \
| sort -rn >$roots || exit 1
echo
i=0
max="$(wc -l <$roots)"
while read id; do
((i+=1))
echo -e "Trying root $id... ($i/$max)"
btrfs restore -t $id --path-regex "$regex" "$dev" . &>/dev/null
if [ "$?" = 0 ]; then
found=$(find . -type f ! -size 0c | wc -l)
if [ $found -gt 0 ]; then
echo "Recovered $found non-empty file(s) into $out"
exit 0
fi
find . -type f -size 0c -exec echo "Found {} but it's empty" \; -delete
fi
done <$roots
rm -r $out
echo "Didn't find '$file'"
exit 1
@janos666
Copy link

find-root now needs the -a option to list more than one roots

@kiptanoi
Copy link

Can anyone tell me how to use this?
Do I need to download something? Where to put the downloaded stuff?
And if I want to have back deleted files from this folder: "Dokument"
That have this info:
Location: "/media/jonas/0e362876:data"
Filesystem: "btrfs"
Mounted on: "/media/jonas/0e362876:data"
Mounted from: "/dev/md127"

And I want to save data collected by this code here:
Name: "nas_tmp"
Location: "/media/jonas/M.2 - Recover"
Mounted on: "/media/jonas/M.2 - Recover"
Mounted from: "/dev/nvmeOn1p6"

How can I the use this "btrfs-undelete" with my info?

@endolith
Copy link

endolith commented Jan 19, 2021

Can you write some examples with different <file/dir> formats? wildcards, etc? No matter what format I enter, it says "Didn't find ..."

@endolith
Copy link

endolith commented Jan 19, 2021

OH IT NEEDS TO BE RUN AS ROOT using sudo. Then it says

...
Trying root 14769717477376... (192/213)
Trying root 14769717379072... (193/213)
Trying root 14769716805632... (194/213)
Trying root 14769716510720... (195/213)
...

But still doesn't find anything, even with a very broad search like sudo ./btrfs-undelete /dev/sdc "/Foldername/*" ~/restore which is a folder that definitely still exists on that volume...

Edit: Nevermind it somehow re-mounted itself, so it wasn't working even though it looked like it was. The above command does work! Yayyyy

@mgutt
Copy link

mgutt commented Jan 23, 2021

Does this script really work unattended? Because I executed the restore command manually and I needed to confirm a "looping a lot" question:

btrfs restore -i /dev/nvme1n1p1 /mnt/disk2/nvme1n1p1_restore
No valid Btrfs found on /dev/nvme1n1p1
Could not open root, trying backup super
We seem to be looping a lot on /mnt/disk2/nvme1n1p1_restore/domains/Win10/vdisk1.img, do you want to keep going on ? (y/N/a)

I was not able to find a flag which allowed unattended execution?!

@illwieckz
Copy link

illwieckz commented Jan 24, 2021

@mgutt, the restore command you did is not exactly the same as the one used in the script, anyway if you want to avoid answering y indefinitely you can do that:

yes | btrfs restore -i /dev/nvme1n1p1 /mnt/disk2/nvme1n1p1_restore

@mgutt
Copy link

mgutt commented Jan 24, 2021

I know. Its only a hint. Maybe the same can happen for the btrfs-undelete script, so it become stuck?!

Regarding your piping idea. I think "a" for "all" would be better. But does it return "a" + "enter", which is needed? (I can't test it anymore)

PS
I hit "N" because the file was not important for me. Anyhow, it was restored ^^

@illwieckz
Copy link

illwieckz commented Jan 24, 2021

To always answer a you can do:

yes a | btrfs restore -i /dev/nvme1n1p1 /mnt/disk2/nvme1n1p1_restore

Anyhow, it was restored ^^

Maybe it's incomplete?

@Fr-Dae
Copy link

Fr-Dae commented Mar 9, 2021

(Lubuntu 18.04x64)

hello, I would like to have some pressision on your script

To execute it, I have to copy and paste it into a text editor

saved it under the .sh extension and make it executable right?

and

I have rescement I deleted by mistake a very important folder on my secondary hard drive (/ dev / sda3) an HDD
The file contained only zip that I want to receive
/dev/sda3 = /mnt/38b05da3-7068-45b9-bc0a-0b944f15487f

A friend recommended me the following monate options to make the disk read alone thus avoiding the old data, for limiting the loss.
nosuid,nodev,nofail,x-gvfs-show,noatime,nodiratime,ro
that right for you ?

I plugged in an external hard drive, because my systeme ssd are prety small
/dev/sdc1 = /media/dae-rog/data-backup

My question is, are your tools like?
Where is it asking for data once executed?
Or do I manually be edited?

Dae#5125 on discord

@Fogapod
Copy link

Fogapod commented Apr 11, 2021

Thank you

@alvarlagerlof
Copy link

Thank you so much! This saved me 12 days.

@idreamerhx
Copy link

amazing works! saved my monthes!

@kulak
Copy link

kulak commented Jul 17, 2023

When working with subvolumes make sure to include subvolume data. For example, to extract tree of files from @data subvolume and user /home/accident use command:

 btrfs-undelete /dev/mapper/container '@data/home/accident' /tmp/extraction-location

This will include all fines under /home/accident.

@sirus20x6
Copy link

" must not be mounted, otherwise this program may appear
to work but find nothing."

can I unmount dev on a running system?

@sirus20x6
Copy link

sirus20x6 commented Oct 26, 2023

oh I get it. please change the wording of dev to source device

@sirus20x6
Copy link

can this also find the next most recent deleted file? I think I got most of what I wanted back except for a few lines that had been touched more recently

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment