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

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

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

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

commented Jan 10, 2018

doesn't seem to work.

@Omnicraft

This comment has been minimized.

Copy link

commented Mar 6, 2018

This script totally saved my movie collection folder!

@fjf2002

This comment has been minimized.

Copy link

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

commented Sep 30, 2018

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

@Payden-Pringle

This comment has been minimized.

Copy link

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

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.

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.