Skip to content

Instantly share code, notes, and snippets.

@Changaco
Last active April 22, 2024 20:06
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
@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