diff options
Diffstat (limited to 'rdiff-backup/rdiff_backup/backup.py')
-rw-r--r-- | rdiff-backup/rdiff_backup/backup.py | 121 |
1 files changed, 95 insertions, 26 deletions
diff --git a/rdiff-backup/rdiff_backup/backup.py b/rdiff-backup/rdiff_backup/backup.py index 6e87048..9e24e56 100644 --- a/rdiff-backup/rdiff_backup/backup.py +++ b/rdiff-backup/rdiff_backup/backup.py @@ -20,6 +20,7 @@ """High level functions for mirroring and mirror+incrementing""" from __future__ import generators +import errno import Globals, metadata, rorpiter, TempFile, Hardlink, robust, increment, \ rpath, static, log, selection, Time, Rdiff, statistics @@ -153,7 +154,7 @@ class DestinationStruct: Hardlink.islinked(src_rorp or dest_rorp)): dest_sig = rpath.RORPath(index) dest_sig.flaglinked(Hardlink.get_link_index(dest_sig)) - elif dest_rorp: + elif dest_rorp: dest_sig = dest_rorp.getRORPath() if dest_rorp.isreg(): dest_rp = dest_base_rpath.new_index(index) @@ -196,10 +197,11 @@ class CacheCollatedPostProcess: receives. 2. The metadata must match what is stored in the destination - directory. If there is an error we do not update the dest - directory for that file, and the old metadata is used. Thus - we cannot write any metadata until we know the file has been - procesed correctly. + directory. If there is an error, either we do not update the + dest directory for that file and the old metadata is used, or + the file is deleted on the other end.. Thus we cannot write + any metadata until we know the file has been procesed + correctly. The class caches older source_rorps and dest_rps so the patch function can retrieve them if necessary. The patch function can @@ -218,8 +220,9 @@ class CacheCollatedPostProcess: # the following should map indicies to lists [source_rorp, # dest_rorp, changed_flag, success_flag] where changed_flag # should be true if the rorps are different, and success_flag - # should be true if dest_rorp has been successfully updated to - # source_rorp. They both default to false. + # should be 1 if dest_rorp has been successfully updated to + # source_rorp, and 2 if the destination file is deleted + # entirely. They both default to false (0). self.cache_dict = {} self.cache_indicies = [] @@ -268,17 +271,26 @@ class CacheCollatedPostProcess: if not changed or success: if source_rorp: self.statfileobj.add_source_file(source_rorp) if dest_rorp: self.statfileobj.add_dest_file(dest_rorp) - if success: + if success == 0: metadata_rorp = dest_rorp + elif success == 1: self.statfileobj.add_changed(source_rorp, dest_rorp) metadata_rorp = source_rorp - else: metadata_rorp = dest_rorp + else: metadata_rorp = None if metadata_rorp and metadata_rorp.lstat(): metadata.WriteMetadata(metadata_rorp) + def in_cache(self, index): + """Return true if given index is cached""" + return self.cache_dict.has_key(index) + def flag_success(self, index): """Signal that the file with given index was updated successfully""" self.cache_dict[index][3] = 1 + def flag_deleted(self, index): + """Signal that the destination file was deleted""" + self.cache_dict[index][3] = 2 + def flag_changed(self, index): """Signal that the file with given index has changed""" self.cache_dict[index][2] = 1 @@ -291,6 +303,10 @@ class CacheCollatedPostProcess: """Retrieve source_rorp with given index from cache""" return self.cache_dict[index][0] + def get_mirror_rorp(self, index): + """Retrieve mirror_rorp with given index from cache""" + return self.cache_dict[index][1] + def close(self): """Process the remaining elements in the cache""" while self.cache_indicies: self.shorten_cache() @@ -335,9 +351,12 @@ class PatchITRB(rorpiter.ITRBranch): rp = self.get_rp_from_root(index) tf = TempFile.new(rp) if self.patch_to_temp(rp, diff_rorp, tf): - if tf.lstat(): rpath.rename(tf, rp) - elif rp.lstat(): rp.delete() - self.CCPP.flag_success(index) + if tf.lstat(): + rpath.rename(tf, rp) + self.CCPP.flag_success(index) + elif rp.lstat(): + rp.delete() + self.CCPP.flag_deleted(index) else: tf.setdata() if tf.lstat(): tf.delete() @@ -355,7 +374,23 @@ class PatchITRB(rorpiter.ITRBranch): if robust.check_common_error(self.error_handler, Rdiff.patch_local, (basis_rp, diff_rorp, new)) == 0: return 0 if new.lstat(): rpath.copy_attribs(diff_rorp, new) - return 1 + return self.matches_cached_rorp(diff_rorp, new) + + def matches_cached_rorp(self, diff_rorp, new_rp): + """Return true if new_rp matches cached src rorp + + This is a final check to make sure the temp file just written + matches the stats which we got earlier. If it doesn't it + could confuse the regress operation. This is only necessary + for regular files. + + """ + if not new_rp.isreg(): return 1 + cached_rorp = self.CCPP.get_source_rorp(diff_rorp.index) + if cached_rorp.equal_loose(new_rp): return 1 + log.ErrorLog.write_if_open("UpdateError", diff_rorp, "Updated mirror " + "temp file %s does not match source" % (new_rp.path,)) + return 0 def write_special(self, diff_rorp, new): """Write diff_rorp (which holds special file) to new""" @@ -370,7 +405,8 @@ class PatchITRB(rorpiter.ITRBranch): base_rp = self.base_rp = self.get_rp_from_root(index) assert diff_rorp.isdir() or base_rp.isdir() or not base_rp.index if diff_rorp.isdir(): self.prepare_dir(diff_rorp, base_rp) - else: self.set_dir_replacement(diff_rorp, base_rp) + elif self.set_dir_replacement(diff_rorp, base_rp): + self.CCPP.flag_success(index) def set_dir_replacement(self, diff_rorp, base_rp): """Set self.dir_replacement, which holds data until done with dir @@ -380,8 +416,15 @@ class PatchITRB(rorpiter.ITRBranch): """ assert diff_rorp.get_attached_filetype() == 'snapshot' self.dir_replacement = TempFile.new(base_rp) - rpath.copy_with_attribs(diff_rorp, self.dir_replacement) + if not self.patch_to_temp(None, diff_rorp, self.dir_replacement): + if self.dir_replacement.lstat(): self.dir_replacement.delete() + # Was an error, so now restore original directory + rpath.copy_with_attribs(self.CCPP.get_mirror_rorp(diff_rorp.index), + self.dir_replacement) + success = 0 + else: success = 1 if base_rp.isdir(): base_rp.chmod(0700) + return success def prepare_dir(self, diff_rorp, base_rp): """Prepare base_rp to turn into a directory""" @@ -389,6 +432,10 @@ class PatchITRB(rorpiter.ITRBranch): if not base_rp.isdir(): if base_rp.lstat(): base_rp.delete() base_rp.mkdir() + self.CCPP.flag_success(diff_rorp.index) + else: # maybe no change, so query CCPP before tagging success + if self.CCPP.in_cache(diff_rorp.index): + self.CCPP.flag_success(diff_rorp.index) base_rp.chmod(0700) def end_process(self): @@ -401,7 +448,6 @@ class PatchITRB(rorpiter.ITRBranch): self.base_rp.rmdir() if self.dir_replacement.lstat(): rpath.rename(self.dir_replacement, self.base_rp) - self.CCPP.flag_success(self.base_rp.index) class IncrementITRB(PatchITRB): @@ -421,25 +467,48 @@ class IncrementITRB(PatchITRB): self.cached_incrp = self.inc_root_rp.new_index(index) return self.cached_incrp + def inc_with_checking(self, new, old, inc_rp): + """Produce increment taking new to old checking for errors""" + try: inc = increment.Increment(new, old, inc_rp) + except OSError, exc: + if (errno.errorcode.has_key(exc[0]) and + errno.errorcode[exc[0]] == 'ENAMETOOLONG'): + self.error_handler(exc, old) + return None + else: raise + return inc + def fast_process(self, index, diff_rorp): """Patch base_rp with diff_rorp and write increment (neither is dir)""" rp = self.get_rp_from_root(index) tf = TempFile.new(rp) - self.patch_to_temp(rp, diff_rorp, tf) - increment.Increment(tf, rp, self.get_incrp(index)) - if tf.lstat(): rpath.rename(tf, rp) - else: rp.delete() - self.CCPP.flag_success(index) + if self.patch_to_temp(rp, diff_rorp, tf): + inc = self.inc_with_checking(tf, rp, self.get_incrp(index)) + if inc is not None: + if inc.isreg(): + inc.fsync_with_dir() # Write inc before rp changed + if tf.lstat(): + rpath.rename(tf, rp) + self.CCPP.flag_success(index) + elif rp.lstat(): + rp.delete() + self.CCPP.flag_deleted(index) + return # normal return, otherwise error occurred + tf.setdata() + if tf.lstat(): tf.delete() def start_process(self, index, diff_rorp): """Start processing directory""" base_rp = self.base_rp = self.get_rp_from_root(index) assert diff_rorp.isdir() or base_rp.isdir() if diff_rorp.isdir(): - increment.Increment(diff_rorp, base_rp, self.get_incrp(index)) + inc = self.inc_with_checking(diff_rorp, base_rp, + self.get_incrp(index)) + if inc and inc.isreg(): + inc.fsync_with_dir() # must writte inc before rp changed self.prepare_dir(diff_rorp, base_rp) - else: - self.set_dir_replacement(diff_rorp, base_rp) - increment.Increment(self.dir_replacement, base_rp, - self.get_incrp(index)) + elif (self.set_dir_replacement(diff_rorp, base_rp) and + self.inc_with_checking(self.dir_replacement, base_rp, + self.get_incrp(index))): + self.CCPP.flag_success(index) |