Commit 2525563c authored by bescoto's avatar bescoto

Make restoring from broken archive easier


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@402 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent 767dc3f1
...@@ -8,6 +8,10 @@ Fixed bug backing up unreadable regular files when rdiff-backup is run ...@@ -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. by root on the source site and non-root on the destination side.
(Reported by Troels Arvin and Arkadiusz Miskiewicz.) (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) New in v0.12.3 (2003/08/08)
--------------------------- ---------------------------
......
...@@ -527,7 +527,7 @@ def CalculateAverage(rps): ...@@ -527,7 +527,7 @@ def CalculateAverage(rps):
def RemoveOlderThan(rootrp): def RemoveOlderThan(rootrp):
"""Remove all increment files older than a certain time""" """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) try: time = Time.genstrtotime(remove_older_than_string)
except Time.TimeException, exc: Log.FatalError(str(exc)) except Time.TimeException, exc: Log.FatalError(str(exc))
timep = Time.timetopretty(time) timep = Time.timetopretty(time)
...@@ -552,13 +552,13 @@ def RemoveOlderThan(rootrp): ...@@ -552,13 +552,13 @@ def RemoveOlderThan(rootrp):
else: Log("Deleting increments at times:\n" + inc_pretty_time, 3) else: Log("Deleting increments at times:\n" + inc_pretty_time, 3)
manage.delete_earlier_than(Globals.rbdir, time) manage.delete_earlier_than(Globals.rbdir, time)
def rom_check_dir(rootrp): def rot_check_dir(rootrp):
"""Check destination dir before RemoveOlderThan""" """Check destination dir before RemoveOlderThan"""
SetConnections.UpdateGlobal('rbdir', SetConnections.UpdateGlobal('rbdir',
rootrp.append_path("rdiff-backup-data")) rootrp.append_path("rdiff-backup-data"))
if not Globals.rbdir.isdir(): if not Globals.rbdir.isdir():
Log.FatalError("Unable to open rdiff-backup-data dir %s" % Log.FatalError("Unable to open rdiff-backup-data dir %s" %
(datadir.path,)) (Globals.rbdir.path,))
checkdest_if_necessary(rootrp) checkdest_if_necessary(rootrp)
......
...@@ -287,28 +287,43 @@ class CachedRF: ...@@ -287,28 +287,43 @@ class CachedRF:
return "\n".join((s1, s2, s3)) return "\n".join((s1, s2, s3))
def get_rf(self, index): def get_rf(self, index):
"""Return RestoreFile of given index""" """Return RestoreFile of given index, or None"""
while 1: 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) rf = self.rf_list.pop(0)
if rf.index < index: continue if rf.index < index: continue
elif rf.index == index: return rf elif rf.index == index: return rf
self.rf_list.insert(0, rf) self.rf_list.insert(0, rf)
self.add_rfs(index) if not self.add_rfs(index): return None
def get_fp(self, index): def get_fp(self, index):
"""Return the file object (for reading) of given 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() return self.get_rf(index).get_restore_fp()
def add_rfs(self, index): 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 if not index: return self.root_rf
parent_index = index[:-1] parent_index = index[:-1]
temp_rf = RestoreFile(self.root_rf.mirror_rp.new_index(parent_index), temp_rf = RestoreFile(self.root_rf.mirror_rp.new_index(parent_index),
self.root_rf.inc_rp.new_index(parent_index), []) self.root_rf.inc_rp.new_index(parent_index), [])
new_rfs = list(temp_rf.yield_sub_rfs()) 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 self.rf_list[0:0] = new_rfs
return 1
class RestoreFile: class RestoreFile:
...@@ -432,7 +447,13 @@ rdiff-backup destination directory, or a bug in rdiff-backup""" % ...@@ -432,7 +447,13 @@ rdiff-backup destination directory, or a bug in rdiff-backup""" %
def yield_sub_rfs(self): def yield_sub_rfs(self):
"""Return RestoreFiles under current RestoreFile (which is dir)""" """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(): if self.mirror_rp.isdir():
mirror_iter = self.yield_mirrorrps(self.mirror_rp) mirror_iter = self.yield_mirrorrps(self.mirror_rp)
else: mirror_iter = iter([]) else: mirror_iter = iter([])
......
...@@ -410,5 +410,50 @@ testfiles/increment2/changed_dir""") ...@@ -410,5 +410,50 @@ testfiles/increment2/changed_dir""")
self.assertRaises(OSError, os.lstat, self.assertRaises(OSError, os.lstat,
'testfiles/restoretarget1/executable2') '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() if __name__ == "__main__": unittest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment