Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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 "$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
@breznak

This comment has been minimized.

Copy link

breznak commented Sep 16, 2016

👍 saved my day (and ass/data) today! :) Do you plan to turn it into a small repo or package it, so it doesnt get lost?

@breznak

This comment has been minimized.

Copy link

breznak commented Sep 16, 2016

Q1: does it recover only delete, or all files in given directory?
Q2: is there a possibility to speed the script up (quit early safely)? It recovered most of my files on (16/173) and now I'm just sitting here..is it safe to kill the process early?
Thanks!

@Jipok

This comment has been minimized.

Copy link

Jipok commented Nov 13, 2017

Thank you! It saved me! For some reason, the file began to occupy 0 bytes. This script is the only thing that helped to restore.

@MetaPhaze

This comment has been minimized.

Copy link

MetaPhaze commented Jan 10, 2018

doesn't seem to work.

@Omnicraft

This comment has been minimized.

Copy link

Omnicraft commented Mar 6, 2018

This script totally saved my movie collection folder!

@fjf2002

This comment has been minimized.

Copy link

fjf2002 commented Apr 2, 2018

You are sorting byte-addresses descending instead of generation ids descending, aren't you?

@MerloRodrigo

This comment has been minimized.

Copy link

MerloRodrigo commented Sep 30, 2018

Wow! perfect. It's was a beatful work. Thanks a lot

@Payden-Pringle

This comment has been minimized.

Copy link

Payden-Pringle commented Dec 28, 2018

This script is successfully creating files that appear to be the correct size, but they cannot be opened or read correctly with the relevant programs. Not sure what could be wrong here, as the script seems to be working?

@splurben

This comment has been minimized.

Copy link

splurben commented Apr 26, 2019

This worked perfectly, better than I ever could have expected, for me. The files I was recovering had only been deleted about 2 minutes before I used the script and I was able to unmount the drive 10 seconds after I accidentally deleted (rm -r) the files.

@minhng99

This comment has been minimized.

Copy link

minhng99 commented Oct 13, 2019

Perfect!

@jamalroger

This comment has been minimized.

Copy link

jamalroger commented Mar 1, 2020

I try to restore my file but i got this errors

recover.sh: 32: Bad substitution

command
sudo sh recover.sh /dev/sda /home/jamal/project/work_payments/* /home/jamal/restore/

@cpbotha

This comment has been minimized.

Copy link

cpbotha commented Mar 18, 2020

Thank you very much for this script! Did something silly with dvc remove on a directory containing some files that were not committed yet, and your script was able to recover the files.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.