diff -u duplicity-0.6.18/debian/changelog duplicity-0.6.18/debian/changelog --- duplicity-0.6.18/debian/changelog +++ duplicity-0.6.18/debian/changelog @@ -1,3 +1,12 @@ +duplicity (0.6.18-0ubuntu3.4) precise; urgency=low + + * debian/patches/11-dont-skip-first-chunk-on-restart.dpatch: + - When restarting a backup, if the file we were in the middle of + backing up is now deleted, don't skip the first 65k chunk of the + next file. Patch backported from upstream trunk. LP: #1252484 + + -- Michael Terry Tue, 19 Nov 2013 10:16:52 -0500 + duplicity (0.6.18-0ubuntu3.3) precise-proposed; urgency=low * debian/patches/10fixignoremissing.dpatch diff -u duplicity-0.6.18/debian/patches/00list duplicity-0.6.18/debian/patches/00list --- duplicity-0.6.18/debian/patches/00list +++ duplicity-0.6.18/debian/patches/00list @@ -8,0 +9 @@ +11-dont-skip-first-chunk-on-restart.dpatch only in patch2: unchanged: --- duplicity-0.6.18.orig/debian/patches/11-dont-skip-first-chunk-on-restart.dpatch +++ duplicity-0.6.18/debian/patches/11-dont-skip-first-chunk-on-restart.dpatch @@ -0,0 +1,108 @@ +#! /bin/sh /usr/share/dpatch/dpatch-run +## 03-dont-skip-first-chunk-on-restart.dpatch by > +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: No description. + +@DPATCH@ +diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' duplicity-0.6.18~/bin/duplicity duplicity-0.6.18/bin/duplicity +--- duplicity-0.6.18~/bin/duplicity 2013-11-19 10:59:21.000000000 -0500 ++++ duplicity-0.6.18/bin/duplicity 2013-11-19 10:59:21.000000000 -0500 +@@ -219,7 +219,8 @@ + last_block = globals.restart.last_block + try: + # Just spin our wheels +- while tarblock_iter.next(): ++ iter_result = tarblock_iter.next() ++ while iter_result: + if (tarblock_iter.previous_index == last_index): + # If both the previous index and this index are done, exit now + # before we hit the next index, to prevent skipping its first +@@ -234,13 +235,15 @@ + "Continuing restart on file %s.") % + ("/".join(last_index), "/".join(tarblock_iter.previous_index)), + log.ErrorCode.restart_file_not_found) ++ # We went too far! Stuff the data back into place before restarting ++ tarblock_iter.queue_index_data(iter_result) + break ++ iter_result = tarblock_iter.next() + except StopIteration: + log.Warn(_("File %s missing in backup set.\n" + "Continuing restart on file %s.") % + ("/".join(last_index), "/".join(tarblock_iter.previous_index)), + log.ErrorCode.restart_file_not_found) +- return 0 + + + def write_multivol(backup_type, tarblock_iter, man_outfp, sig_outfp, backend): +diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' duplicity-0.6.18~/duplicity/diffdir.py duplicity-0.6.18/duplicity/diffdir.py +--- duplicity-0.6.18~/duplicity/diffdir.py 2013-11-19 10:59:21.000000000 -0500 ++++ duplicity-0.6.18/duplicity/diffdir.py 2013-11-19 10:59:21.000000000 -0500 +@@ -465,6 +465,7 @@ + self.remember_next = False # see remember_next_index() + self.remember_value = None # holds index of next block + self.remember_block = None # holds block of next block ++ self.queued_data = None # data to return in next next() call + + def tarinfo2tarblock(self, index, tarinfo, file_data = ""): + """ +@@ -500,6 +501,12 @@ + """ + Return next block and update offset + """ ++ if self.queued_data is not None: ++ result = self.queued_data ++ self.queued_data = None ++ # Keep rest of metadata as is (like previous_index) ++ return result ++ + if self.process_waiting: + result = self.process_continued() + else: +@@ -528,6 +535,12 @@ + """ + return self.previous_index, self.previous_block + ++ def queue_index_data(self, data): ++ """ ++ Next time next() is called, we will return data instead of processing ++ """ ++ self.queued_data = data ++ + def remember_next_index(self): + """ + When called, remember the index of the next block iterated +diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' duplicity-0.6.18~/testing/tests/restarttest.py duplicity-0.6.18/testing/tests/restarttest.py +--- duplicity-0.6.18~/testing/tests/restarttest.py 2013-11-19 10:59:21.000000000 -0500 ++++ duplicity-0.6.18/testing/tests/restarttest.py 2013-11-19 10:59:56.459351413 -0500 +@@ -410,6 +410,30 @@ + assert not os.system("diff %s/file1 testfiles/restore_out/file1" % source) + assert not os.system("diff %s/z testfiles/restore_out/z" % source) + ++ def test_changed_source_file_disappears(self): ++ """ ++ Make sure we correctly handle restarting a backup when a file ++ disappears when we had been in the middle of backing it up. It's ++ possible that the first chunk of the next file will be skipped unless ++ we're careful. ++ """ ++ source = 'testfiles/largefiles' ++ assert not os.system("mkdir -p testfiles/largefiles") ++ assert not os.system("dd if=/dev/urandom of=testfiles/largefiles/file1 bs=1024 count=2048 > /dev/null 2>&1") ++ # intentionally interrupt initial backup ++ try: ++ self.backup("full", source, options = ["--vol 1", "--fail 2"]) ++ except CmdError: ++ pass ++ # now remove starting source data and make sure we add something after ++ assert not os.system("rm %s/*" % source) ++ assert not os.system("echo hello > %s/z" % source) ++ # finish backup ++ self.backup("full", source) ++ # and verify we can restore ++ self.restore() ++ assert not os.system("diff %s/z testfiles/restore_out/z" % source) ++ + + # Note that this class duplicates all the tests in RestartTest + class RestartTestWithoutEncryption(RestartTest):