#!/bin/bash # For information about the usage of this script type # sh undel-ext3.sh --help # # This script is for people who have deleted something # on their ext3 partition and have no backup, like me :) # # Basically i started work on this script as i deleted # something on my ext3 partition and for recovering files # from Virtuozzo partitions which are broken. # # Author: Maik Broemme # # $Id: undel-ext3.sh,v 1.2 2004/05/27 03:20:27 mbroemme Exp $ SCRIPTNAME="undel-ext3.sh" OPTION_DEVICE="FALSE" OPTION_DEVICE_TYPE="FILE" OPTION_HELP="FALSE" OPTION_OUTPUT="TRUE" OPTION_BLOCKSIZE="0" OPTION_BLOCKSIZE_VALUE="0" OPTION_DIRECTORY="0" OPTION_DIRECTORY_VALUE="0" # reading argument list and save given values. for i in $@ ; do if [ "$OPTION_BLOCKSIZE" == "1" ]; then if [ `expr match $i "[0-9]*"` -ne 0 ]; then OPTION_BLOCKSIZE_VALUE="$i" fi OPTION_BLOCKSIZE="0" continue fi if [ "$OPTION_DIRECTORY" == "1" ]; then OPTION_DIRECTORY_VALUE="$i" OPTION_DIRECTORY="0" continue fi if [ "$i" == "-h" ] || [ "$i" == "--help" ]; then OPTION_HELP="TRUE" continue fi if [ "$i" == "-s" ] || [ "$i" == "--silent" ]; then OPTION_OUTPUT="FALSE" continue fi if [ -b "$i" ]; then OPTION_DEVICE_TYPE="BLOCK" OPTION_DEVICE="$i" continue fi if [ -f "$i" ]; then OPTION_DEVICE_TYPE="FILE" OPTION_DEVICE="$i" continue fi if [ "$i" == "-b" ] || [ "$i" == "--blocksize" ]; then OPTION_BLOCKSIZE="1" continue fi if [ "$i" == "-o" ] || [ "$i" == "--output" ]; then OPTION_DIRECTORY="1" continue fi done # do some useful argument checking. if [ "$OPTION_DEVICE" == "FALSE" ] && [ "$OPTION_HELP" == "FALSE" ] then echo "No device given" echo "Try \`$SCRIPTNAME --help' for more information." exit 1 fi if [ "$OPTION_BLOCKSIZE_VALUE" == "0" ] && [ "$OPTION_HELP" == "FALSE" ] then echo "No blocksize given" echo "Try \`$SCRIPTNAME --help' for more information." exit 1 fi if [ "$OPTION_DIRECTORY_VALUE" == "0" ] && [ "$OPTION_HELP" == "FALSE" ] then echo "No output directory given" echo "Try \`$SCRIPTNAME --help' for more information." exit 1 fi # help for the options. if [ "$1" == "" ] || [ "$OPTION_HELP" == "TRUE" ] then echo "Usage: $SCRIPTNAME [DEVICE] [PATH]..." echo "Undeletes files from ext3 devices. (Example: sh $SCRIPTNAME -b 4096 /dev/hda5 -o /tmp/hda5)" echo echo "Mandatory arguments to long options are mandatory for short options too." echo " -h, --help shows you this help screen." echo " -b, --blocksize specifies the ext3 blocksize." echo " -o, --output directory to store files." echo " -s, --silent shows you nothing." echo echo "By default, a normal verbose mode is enabled to show some statistics." echo echo "Report bugs " exit 0 fi # shows the given text on STDOUT function undel_ext3_printf() { local text="${1}" local stderr="${2}" if [ "$OPTION_OUTPUT" == "TRUE" ] || [ "$stderr" == "STDERR" ]; then echo -e "$text" fi } # search all needed binaries function undel_ext3_check() { check_bin=`which "${1}" 2>&1` if [ "$?" != "0" ]; then undel_ext3_printf "ERROR: ${1} is missing on your system" "STDERR" exit 1 else undel_ext3_printf "Using binary: $check_bin" fi } # shows the given parameters function undel_ext3_device() { local device_type="${1}" local device_path="${2}" local device_bs="${3}" local device_output="${4}" undel_ext3_printf "Device: $device_path" # check what type of device we handle if [ "$device_type" == "BLOCK" ]; then undel_ext3_printf "Type: block device" fi if [ "$device_type" == "FILE" ]; then undel_ext3_printf "Type: image file" fi undel_ext3_printf "Blocksize: $device_bs" undel_ext3_printf "Output directory: $device_output" } # creates output directory function undel_ext3_output() { local device_output="${1}" undel_ext3_printf "Creating output directory..." mkdir -p $device_output 2> /dev/null } # the undelete god :) function undel_ext3_undelete() { local device_path="${1}" local device_bs="${2}" local device_output="${3}" local i=0 # threshold local delta=4030 # calculate the number of blocks in local blocks=$(echo $(du -b $device_path | awk '{print $1}' ) / $device_bs | bc) # create temporary file local temp=`mktemp` while [ "$i" -lt "$blocks" ]; do { dd if=$device_path bs=$device_bs count=1 skip=$i 2>/dev/null > $temp [ "$(dd if=$temp bs=128 skip=31 2>/dev/null)" = "" ] && \ [ "$(dd if=$temp bs=1 skip=7 count=1 2>/dev/null | hexdump -b | awk '{print $2}')" == "$(dd if=$temp bs=1 skip=3 count=1 2>/dev/null | hexdump -b | awk '{print $2}')" ] && \ [ "$(dd if=$temp bs=1 skip=15 count=1 2>/dev/null | hexdump -b | awk '{print $2}')" == "$(dd if=$temp bs=1 skip=11 count=1 2>/dev/null | hexdump -b | awk '{print $2}')" ] && \ i=$(echo "$i+1" | bc) # get filetype local dir=$(dd if=$device_path bs=$device_bs count=1 skip=$i 2>/dev/null | file -b -) local file_type="$dir" local dir=$device_output/$(echo $dir | awk '{print $2}')/$(echo $dir | awk '{print $1}') # check if directory already exists, if not create the directory with filetype retrieved from file test -d $dir || mkdir -p $dir # our filename with the filenumber local file=$dir/file$i.raw local file_name="file$i.raw" dd if=$device_path bs=$device_bs count=1 skip=$i 2>/dev/null > $file local end=$(dd if=$file count=$device_bs skip=$delta bs=1 2> /dev/null) # copy until we think eof comes, notice eof must be a multiple of blocksize while test -n "$end"; do { i=$(echo "$i+1"|bc) dd if=$device_path bs=$device_bs count=1 skip=$i 2>/dev/null > $temp [ "" == "$(dd if=$temp bs=128 skip=31 2>/dev/null)" ] && \ [ "$(dd if=$temp bs=1 skip=7 count=1 2>/dev/null | hexdump -b | awk '{print $2}')" == "$(dd if=$temp bs=1 skip=3 count=1 2>/dev/null | hexdump -b | awk '{print $2}' )" ] && [ "$(dd if=$temp bs=1 skip=15 count=1 2>/dev/null | hexdump -b | awk '{print $2}')" == "$(dd if=$temp bs=1 skip=11 count=1 2>/dev/null | hexdump -b | awk '{print $2}')" ] && \ i=$(echo "$i+1" | bc) && \ dd if=$device_path bs=$device_bs count=1 skip=$i 2>/dev/null > $temp local end=$(dd if=$temp count=66 skip=$delta bs=1 2> /dev/null) cat $temp >> $file }; done # shows the file and type on which script is working undel_ext3_printf "File: $file_name from type $file_type" i=$(echo "$i+1" | bc) }; done } # main() starts here for i in awk bc cat expr dd file hexdump mkdir mktemp ; do undel_ext3_check "$i" done undel_ext3_device "$OPTION_DEVICE_TYPE" "$OPTION_DEVICE" "$OPTION_BLOCKSIZE_VALUE" "$OPTION_DIRECTORY_VALUE" undel_ext3_output "$OPTION_DIRECTORY_VALUE" undel_ext3_undelete "$OPTION_DEVICE" "$OPTION_BLOCKSIZE_VALUE" "$OPTION_DIRECTORY_VALUE"