#!/bin/bash # # very simple restore script # Usage: # restore filename # where filename can be a part of a filename # This is not (yet) thought to restore complete directories. here="`/bin/pwd`" host="`hostname -f`" BACK=/backup/md5backup for a in . md5 log out do t="$BACK/$a" if [ ! -d "$t" ] then echo "directory $t is missing" exit 1 fi done for a in log md5 do t="$BACK/$a/$host" if [ ! -s "$t" ] then echo "logfile $t is missing" exit 1 fi done # fixes old log file type fixup() { # old: YYYY-MM-DD HH:MM:SS md5 MD5SUMHERE 0 /PATH # new: YYYY-MM-DD HH:MM:SS md5 xKB MD5SUMHERE 0 /PATH sed 's/^\([^ ]* [^ ]* md5 \)\([0-9a-f]*\) /\1?KB \2 /' } # Compare file with the contents of the backup # Hunt it in all out dirs. md5cmp() { cmpdiff=false md5t="`expr "$4" : '\(..\)'`/`expr "$4" : '..\(..\)'`/$4.$5.md5" for a in "$BACK"/out* do [ -d "$a" ] || continue [ -f "$a/$md5t" ] || continue [ -r "$a/$md5t" ] || continue cmp -s "$1" "$a/$md5t" && return cmpdiff=: done $cmpdiff && return 1 return 2 } # Compare file with the contents of the backup # Hunt it in all out dirs. : md5restore md5restore() { md5t="`expr "$4" : '\(..\)'`/`expr "$4" : '..\(..\)'`/$4.$5.md5" for a in "$BACK"/out* do [ -d "$a" ] || continue [ -f "$a/$md5t" ] || continue [ -r "$a/$md5t" ] || continue cp --backup=t "$a/$md5t" "$1" return done echo "$l $md5t not present in archive" false } # Diff file with the contents of the backup # Hunt it in all out dirs. : md5diff md5diff() { md5t="`expr "$5" : '\(..\)'`/`expr "$5" : '..\(..\)'`/$5.$6.md5" for a in "$BACK"/out* do [ -d "$a" ] || continue [ -f "$a/$md5t" ] || continue [ -r "$a/$md5t" ] || continue diff $2 -- "$a/$md5t" "$1" | less return done echo "$l $md5t not present in archive" false } # Escape the information, such that it is conforming to shell syntax : escape escape() { sed " s/'/'\\\\''/g s/\\\\'''\\\\'/\\\\'\\\\'/g s/\\\\'''\\\\'/\\\\'\\\\'/g s/^\\(.*\\)$/'\\1'/ " } : escapeargs escapeargs() { for escapes do echo "$escapes" done | escape } : huntname huntname() { sed -n 's/^[0-9a-f]* \*//p' "$BACK/md5/$host" | fgrep -- "$1" | sort -u | escape } getversions() { fgrep " $1" "$BACK/log/$host" | fixup | while read -r date time what kb md5 nr file do case "$what:$kb" in md5:*) ;; known:md5) ;; *) continue;; esac [ ".$1" = ".$file" ] || continue echo "'$date $time $md5 $nr'" done | awk 'known[$3,$4] { next } { known[$3,$4]=1; print }' } : process process() { echo "$1" } echo "+- WARNING! This is a preliminary script!" echo "! Expect BUGs!" echo "! And this script is dog slow! Sorry!" echo "+- restore script started" for arg do echo "+- processing $arg" declare -a names eval names=\(`huntname "$arg"`\) if [ -z "${#names[@]}" ] then echo "OOPS $arg" continue fi if [ 0 -eq ${#names[@]} ] then echo "! +- unknown file argument" continue fi declare -i i i=-1 while let i++ [ $i -lt ${#names[@]} ] do echo "! +- $i/${#names[@]} check versions on ${names[$i]}" l='! ! ' [ $[$i+1] -ge ${#names[@]} ] && l='! ' declare -a versions eval versions=\(`getversions "${names[$i]}"`\) if [ 0 -ge "${#versions[@]}" ] then echo "$l+- ${names[$i]} is not in the log" echo "$l currently, you must do a manual restore!" unset names[$i] continue fi declare -i ok ok=0 if [ ! -f "${names[$i]}" ] then echo "$l+- file is missing" elif [ ! -r "${names[$i]}" ] then echo "$l+- file is unreadable" else declare -i j j=0 while [ $j -lt ${#versions[@]} ] do echo -n "$l+- comparing version ${versions[$j]}: " md5cmp "${names[$i]}" ${versions[$j]} case "$?" in 0) echo "ok" versions[$j]= let ok++ ;; 1) echo "differ" ;; *) echo "missing or unreadable" versions[$j]= let ok++ ;; esac let j++ done if [ $ok -ge "${#versions[@]}" ] then echo "$l+- nothing left to do" continue fi fi while echo "$l+- restoreable versions:" do declare -i j j=-1 while let j++ [ $j -lt ${#versions[@]} ] do [ -n "${versions[$j]}" ] && echo "$l! +- $j) ${versions[$j]}" done echo "$l! \\- target to restore: ${names[$i]}" echo -n "$l+- restore which version (N/diff N [args]/EOF to skip)? " if ! read -r ans nr args then echo EOF echo "$l+- not restored" break fi if [ diff = "$ans" ] then if [ -z "${versions[$nr]}" ] then echo "$l+- wrong input" continue fi [ -z "$args" ] && args="-ub" md5diff "${names[$i]}" "$args" ${versions[$ans]} continue fi if [ -z "${versions[$ans]}" ] then echo "$l+- wrong input" continue fi echo "$l+- restoring ${versions[$ans]}" echo "$l+- to ${names[$i]}" if md5restore "${names[$i]}" ${versions[$ans]} then echo "$l\\- done" break else echo "$l+- failed" fi done done done echo "+- restore script finished"