# Copyright 2002, 2003, 2004, 2005 Ben Escoto # # This file is part of rdiff-backup. # # rdiff-backup is free software; you can redistribute it and/or modify # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # rdiff-backup is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with rdiff-backup; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA """Read increment files and restore to original""" from __future__ import generators import tempfile, os, cStringIO import static, rorpiter, FilenameMapping class RestoreError(Exception): pass def Restore(mirror_rp, inc_rpath, target, restore_to_time): """Recursively restore mirror and inc_rpath to target at rest_time""" MirrorS = mirror_rp.conn.restore.MirrorStruct TargetS = target.conn.restore.TargetStruct MirrorS.set_mirror_and_rest_times(restore_to_time) MirrorS.initialize_rf_cache(mirror_rp, inc_rpath) target_iter = TargetS.get_initial_iter(target) diff_iter = MirrorS.get_diffs(target_iter) TargetS.patch(target, diff_iter) MirrorS.close_rf_cache() def get_inclist(inc_rpath): """Returns increments with given base""" dirname, basename = inc_rpath.dirsplit() if Globals.chars_to_quote: basename = FilenameMapping.unquote(basename) parent_dir = inc_rpath.__class__(inc_rpath.conn, dirname, ()) if not parent_dir.isdir(): return [] # inc directory not created yet index = inc_rpath.index inc_list = [] for filename in parent_dir.listdir(): inc_info = rpath.get_incfile_info(filename) if inc_info and inc_info[3] == basename: inc = parent_dir.append(filename) assert inc.isincfile() inc_list.append(inc) return inc_list def ListChangedSince(mirror_rp, inc_rp, restore_to_time): """List the changed files under mirror_rp since rest time Notice the output is an iterator of RORPs. We do this because we want to give the remote connection the data in buffered increments, and this is done automatically for rorp iterators. Encode the lines in the first element of the rorp's index. """ assert mirror_rp.conn is Globals.local_connection, "Run locally only" MirrorStruct.set_mirror_and_rest_times(restore_to_time) MirrorStruct.initialize_rf_cache(mirror_rp, inc_rp) old_iter = MirrorStruct.get_mirror_rorp_iter(MirrorStruct._rest_time, 1) cur_iter = MirrorStruct.get_mirror_rorp_iter(MirrorStruct._mirror_time, 1) collated = rorpiter.Collate2Iters(old_iter, cur_iter) for old_rorp, cur_rorp in collated: if not old_rorp: change = "new" elif not cur_rorp: change = "deleted" elif old_rorp == cur_rorp: continue else: change = "changed" path_desc = (old_rorp and old_rorp.get_indexpath() or cur_rorp.get_indexpath()) yield rpath.RORPath(("%-7s %s" % (change, path_desc),)) MirrorStruct.close_rf_cache() def ListAtTime(mirror_rp, inc_rp, time): """List the files in archive at the given time Output is a RORP Iterator with info in index. See ListChangedSince. """ assert mirror_rp.conn is Globals.local_connection, "Run locally only" MirrorStruct.set_mirror_and_rest_times(time) MirrorStruct.initialize_rf_cache(mirror_rp, inc_rp) old_iter = MirrorStruct.get_mirror_rorp_iter() for rorp in old_iter: yield rorp class MirrorStruct: """Hold functions to be run on the mirror side""" # If selection command line arguments given, use Select here _select = None # This will be set to the time of the current mirror _mirror_time = None # This will be set to the exact time to restore to (not restore_to_time) _rest_time = None def set_mirror_and_rest_times(cls, restore_to_time): """Set class variabels _mirror_time and _rest_time on mirror conn""" MirrorStruct._mirror_time = cls.get_mirror_time() MirrorStruct._rest_time = cls.get_rest_time(restore_to_time) def get_mirror_time(cls): """Return time (in seconds) of latest mirror""" cur_mirror_incs = get_inclist(Globals.rbdir.append("current_mirror")) if not cur_mirror_incs: log.Log.FatalError("Could not get time of current mirror") elif len(cur_mirror_incs) > 1: log.Log("Warning, two different times for current mirror found", 2) return cur_mirror_incs[0].getinctime() def get_rest_time(cls, restore_to_time): """Return older time, if restore_to_time is in between two inc times There is a slightly tricky reason for doing this: The rest of the code just ignores increments that are older than restore_to_time. But sometimes we want to consider the very next increment older than rest time, because rest_time will be between two increments, and what was actually on the mirror side will correspond to the older one. So if restore_to_time is inbetween two increments, return the older one. """ inctimes = cls.get_increment_times() older_times = filter(lambda time: time <= restore_to_time, inctimes) if older_times: return max(older_times) else: # restore time older than oldest increment, just return that return min(inctimes) def get_increment_times(cls, rp = None): """Return list of times of backups, including current mirror Take the total list of times from the increments.