From 5eb272cab822b4f8ec54605c562c554223faad18 Mon Sep 17 00:00:00 2001 From: bescoto Date: Sun, 24 Aug 2003 03:46:13 +0000 Subject: Make restoring from broken archive easier git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/branches/r0-12@402 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109 --- rdiff-backup/CHANGELOG | 4 ++++ rdiff-backup/rdiff_backup/Main.py | 6 ++--- rdiff-backup/rdiff_backup/restore.py | 33 +++++++++++++++++++++----- rdiff-backup/testing/finaltest.py | 45 ++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 9 deletions(-) diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG index 0946d3b..11f3035 100644 --- a/rdiff-backup/CHANGELOG +++ b/rdiff-backup/CHANGELOG @@ -8,6 +8,10 @@ Fixed bug backing up unreadable regular files when rdiff-backup is run by root on the source site and non-root on the destination side. (Reported by Troels Arvin and Arkadiusz Miskiewicz.) +If there is data missing from the destination dir (for instance if a +user mistakenly deletes it), only warn when restoring, instead of +exiting with error. + New in v0.12.3 (2003/08/08) --------------------------- diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py index 7edfdc3..da1e596 100644 --- a/rdiff-backup/rdiff_backup/Main.py +++ b/rdiff-backup/rdiff_backup/Main.py @@ -527,7 +527,7 @@ def CalculateAverage(rps): def RemoveOlderThan(rootrp): """Remove all increment files older than a certain time""" - rom_check_dir(rootrp) + rot_check_dir(rootrp) try: time = Time.genstrtotime(remove_older_than_string) except Time.TimeException, exc: Log.FatalError(str(exc)) timep = Time.timetopretty(time) @@ -552,13 +552,13 @@ def RemoveOlderThan(rootrp): else: Log("Deleting increments at times:\n" + inc_pretty_time, 3) manage.delete_earlier_than(Globals.rbdir, time) -def rom_check_dir(rootrp): +def rot_check_dir(rootrp): """Check destination dir before RemoveOlderThan""" SetConnections.UpdateGlobal('rbdir', rootrp.append_path("rdiff-backup-data")) if not Globals.rbdir.isdir(): Log.FatalError("Unable to open rdiff-backup-data dir %s" % - (datadir.path,)) + (Globals.rbdir.path,)) checkdest_if_necessary(rootrp) diff --git a/rdiff-backup/rdiff_backup/restore.py b/rdiff-backup/rdiff_backup/restore.py index 26bc499..f73dbc1 100644 --- a/rdiff-backup/rdiff_backup/restore.py +++ b/rdiff-backup/rdiff_backup/restore.py @@ -287,28 +287,43 @@ class CachedRF: return "\n".join((s1, s2, s3)) def get_rf(self, index): - """Return RestoreFile of given index""" + """Return RestoreFile of given index, or None""" while 1: - if not self.rf_list: self.add_rfs(index) + if not self.rf_list: + if not self.add_rfs(index): return None rf = self.rf_list.pop(0) if rf.index < index: continue elif rf.index == index: return rf self.rf_list.insert(0, rf) - self.add_rfs(index) + if not self.add_rfs(index): return None def get_fp(self, index): """Return the file object (for reading) of given index""" + rf = self.get_rf(index) + if not rf: + log.Log("""Error: Unable to retrieve data for file %s! +The cause is probably data loss from the destination directory.""" % + (index and "/".join(index) or '.',), 2) + return cStringIO.StringIO('') return self.get_rf(index).get_restore_fp() def add_rfs(self, index): - """Given index, add the rfs in that same directory""" + """Given index, add the rfs in that same directory + + Returns false if no rfs are available, which usually indicates + an error. + + """ if not index: return self.root_rf parent_index = index[:-1] temp_rf = RestoreFile(self.root_rf.mirror_rp.new_index(parent_index), self.root_rf.inc_rp.new_index(parent_index), []) new_rfs = list(temp_rf.yield_sub_rfs()) - assert new_rfs, "No RFs added for index %s" % index + if not new_rfs: + log.Log("Warning: No RFs added for index %s" % (index,), 2) + return 0 self.rf_list[0:0] = new_rfs + return 1 class RestoreFile: @@ -432,7 +447,13 @@ rdiff-backup destination directory, or a bug in rdiff-backup""" % def yield_sub_rfs(self): """Return RestoreFiles under current RestoreFile (which is dir)""" - assert self.mirror_rp.isdir() or self.inc_rp.isdir() + if not self.mirror_rp.isdir() and not self.inc_rp.isdir(): + log.Log("""Warning: directory %s seems to be missing from backup! + +This is probably due to files being deleted manually from the +rdiff-backup destination directory. In general you shouldn't do this, +as data loss may result.\n""" % (self.mirror_rp.get_indexpath(),), 2) + return if self.mirror_rp.isdir(): mirror_iter = self.yield_mirrorrps(self.mirror_rp) else: mirror_iter = iter([]) diff --git a/rdiff-backup/testing/finaltest.py b/rdiff-backup/testing/finaltest.py index c45bd62..82d03a7 100644 --- a/rdiff-backup/testing/finaltest.py +++ b/rdiff-backup/testing/finaltest.py @@ -410,5 +410,50 @@ testfiles/increment2/changed_dir""") self.assertRaises(OSError, os.lstat, 'testfiles/restoretarget1/executable2') + +class FinalCorrupt(PathSetter): + """Test messing with things a bit and making sure they still work""" + def make_dir(self): + self.delete_tmpdirs() + rp1 = rpath.RPath(Globals.local_connection, 'testfiles/final_deleted1') + if rp1.lstat(): Myrm(rp1.path) + rp1.mkdir() + rp1_1 = rp1.append('regfile') + rp1_1.touch() + rp1_2 = rp1.append('dir') + rp1_2.mkdir() + rp1_2_1 = rp1_2.append('regfile2') + rp1_2_1.write_string('foo') + + rp2 = rpath.RPath(Globals.local_connection, 'testfiles/final_deleted2') + if rp2.lstat(): Myrm(rp2.path) + os.system('cp -a %s %s' % (rp1.path, rp2.path)) + rp2_2_1 = rp2.append('dir').append('regfile2') + assert rp2_2_1.lstat() + rp2_2_1.delete() + rp2_2_1.touch() + return rp1, rp1_2, rp2 + + def test_dest_delete(self): + """Test deleting a directory from the destination dir + + Obviously that directory can no longer be restored, but the + rest of the files should be OK. Just runs locally for now. + + """ + in_dir1, in_subdir, in_dir2 = self.make_dir() + self.set_connections(None, None, None, None) + self.exec_rb(10000, in_dir1.path, 'testfiles/output') + + out_subdir = rpath.RPath(Globals.local_connection, + 'testfiles/output/%s' % + (in_subdir.index[-1],)) + log.Log("Deleting %s" % (out_subdir.path,), 3) + out_subdir.delete() + self.exec_rb(20000, in_dir2.path, 'testfiles/output') + + self.exec_rb_restore(10000, 'testfiles/output', + 'testfiles/restoretarget1') + if __name__ == "__main__": unittest.main() -- cgit v1.2.1