def _restore_orig_regfile(self, rf): """Restore original regular file This is the trickiest case for avoiding information loss, because we don't want to delete the increment before the mirror is fully written. """ assert rf.metadata_rorp.isreg(), ( "Metadata path '{mp}' can only be regular file.".format( mp=rf.metadata_rorp)) if rf.mirror_rp.isreg(): tf = rf.mirror_rp.get_temp_rpath(sibling=True) tf.write_from_fileobj(rf.get_restore_fp()) tf.fsync_with_dir() # make sure tf fully written before move rpath.copy_attribs(rf.metadata_rorp, tf) rpath.rename(tf, rf.mirror_rp) # move is atomic else: if rf.mirror_rp.lstat(): rf.mirror_rp.delete() rf.mirror_rp.write_from_fileobj(rf.get_restore_fp()) rpath.copy_attribs(rf.metadata_rorp, rf.mirror_rp) if Globals.fsync_directories: rf.mirror_rp.get_parent_rp().fsync( ) # force move before inc delete
def end_process_directory(self): """Finish processing a directory""" rf = self.rf if rf.metadata_rorp.isdir(): if rf.mirror_rp.isdir(): rf.mirror_rp.setdata() if not rf.metadata_rorp.equal_loose(rf.mirror_rp): log.Log("Regressing attributes of path {pa}".format(pa=rf), log.INFO) rpath.copy_attribs(rf.metadata_rorp, rf.mirror_rp) else: rf.mirror_rp.delete() log.Log("Regressing file {fi}".format(fi=rf.mirror_rp), log.INFO) rpath.copy_with_attribs(rf.metadata_rorp, rf.mirror_rp) else: # replacing a dir with some other kind of file assert rf.mirror_rp.isdir(), ( "Mirror '{mrp!r}' can only be a directory.".format( mrp=rf.mirror_rp)) log.Log("Replacing directory {di}".format(di=rf), log.INFO) if rf.metadata_rorp.isreg(): self._restore_orig_regfile(rf) else: rf.mirror_rp.delete() rpath.copy_with_attribs(rf.metadata_rorp, rf.mirror_rp) if rf.regress_inc: log.Log("Deleting increment {ic}".format(ic=rf), log.INFO) rf.regress_inc.delete()
def testCopyAttribs(self): """Test copying attributes""" t = RPath(self.lc, self.mainprefix, ("testattribs",)) if t.lstat(): t.delete() for rp in [self.noperms, self.nowrite, self.rf, self.exec1, self.exec2, self.hl1, self.dir]: copy(rp, t) rpath.copy_attribs(rp, t) # assert rpath.cmp_attribs(t, rp), \ assert t.equal_loose(rp), "Attributes for file %s not copied successfully" % rp.path t.delete()
def testCopyAttribs(self): """Test copying attributes""" t = RPath(self.lc, self.mainprefix, ("testattribs",)) if t.lstat(): t.delete() for rp in [self.noperms, self.nowrite, self.rf, self.exec1, self.exec2, self.hl1, self.dir]: copy(rp, t) rpath.copy_attribs(rp, t) #assert rpath.cmp_attribs(t, rp), \ assert t.equal_loose(rp), \ "Attributes for file %s not copied successfully" % rp.path t.delete()
def testCopyAttribs(self): """Test copying attributes""" t = rpath.RPath(self.lc, self.write_dir, ("testattribs", )) if t.lstat(): t.delete() for rp in [ self.noperms, self.nowrite, self.rf, self.exec1, self.exec2, self.hl1, self.dir ]: rpath.copy(rp, t) rpath.copy_attribs(rp, t) self.assertTrue(t.equal_loose(rp)) t.delete()
def end_process_directory(self): """Finish processing directory""" if self.dir_update: assert self.base_rp.isdir(), ( "Base path '{brp}' must be a directory.".format( brp=self.base_rp)) rpath.copy_attribs(self.dir_update, self.base_rp) else: assert self.dir_replacement, ( "Replacement directory must be defined.") self.base_rp.rmdir() if self.dir_replacement.lstat(): rpath.rename(self.dir_replacement, self.base_rp)
def _patch_to_temp(self, basis_rp, diff_rorp, new): """Patch basis_rp, writing output in new, which doesn't exist yet""" if diff_rorp.isflaglinked(): map_hardlinks.link_rp(diff_rorp, new, self.basis_root_rp) return if diff_rorp.get_attached_filetype() == 'snapshot': copy_report = rpath.copy(diff_rorp, new) else: assert diff_rorp.get_attached_filetype() == 'diff', ( "File '{drp}' must be of type '{dtype}'.".format( drp=diff_rorp, dtype='diff')) copy_report = Rdiff.patch_local(basis_rp, diff_rorp, new) self._check_hash(copy_report, diff_rorp) if new.lstat(): rpath.copy_attribs(diff_rorp, new)
def InternalMirror(source_local, dest_local, src_dir, dest_dir, force=False): """ Mirror src to dest internally like InternalBackup, but only mirror. Do this through InternalBackup, but then delete rdiff-backup-data directory. """ # Save attributes of root to restore later src_root = rpath.RPath(Globals.local_connection, src_dir) dest_root = rpath.RPath(Globals.local_connection, dest_dir) dest_rbdir = dest_root.append("rdiff-backup-data") InternalBackup(source_local, dest_local, src_dir, dest_dir, force=force) dest_root.setdata() Myrm(dest_rbdir.path) # Restore old attributes rpath.copy_attribs(src_root, dest_root)
def InternalMirror(source_local, dest_local, src_dir, dest_dir): """Mirror src to dest internally like InternalBackup, but only mirror. Do this through InternalBackup, but then delete rdiff-backup-data directory. """ # Save attributes of root to restore later src_root = rpath.RPath(Globals.local_connection, src_dir) dest_root = rpath.RPath(Globals.local_connection, dest_dir) dest_rbdir = dest_root.append("rdiff-backup-data") InternalBackup(source_local, dest_local, src_dir, dest_dir) dest_root.setdata() Myrm(dest_rbdir.path) # Restore old attributes rpath.copy_attribs(src_root, dest_root)
def end_process_directory(self): """Finish processing directory""" if self.dir_update: assert self.base_rp.isdir(), ( "Base directory '{rp}' isn't a directory.".format( rp=self.base_rp)) rpath.copy_attribs(self.dir_update, self.base_rp) if (Globals.process_uid != 0 and self.dir_update.getperms() % 0o1000 < 0o700): # Directory was unreadable at start -- keep it readable # until the end of the backup process. self.base_rp.chmod(0o700 | self.dir_update.getperms()) elif self.dir_replacement: self.base_rp.rmdir() if self.dir_replacement.lstat(): rpath.rename(self.dir_replacement, self.base_rp)
def _patch_snapshot_to_temp(self, diff_rorp, new): """Write diff_rorp to new, return true if successful Returns 1 if normal success, 2 if special file is written, whether or not it is successful. This is because special files either fail with a SpecialFileError, or don't need to be compared. """ if diff_rorp.isspecial(): self._write_special(diff_rorp, new) rpath.copy_attribs(diff_rorp, new) return 2 report = robust.check_common_error(self.error_handler, rpath.copy, (diff_rorp, new)) if isinstance(report, hash.Report): self.CCPP.update_hash(diff_rorp.index, report.sha1_digest) return 1 return report != 0 # if == 0, error_handler caught something
def _patch_to_temp(self, basis_rp, diff_rorp, new): """Patch basis_rp, writing output in new, which doesn't exist yet Returns true if able to write new as desired, false if UpdateError or similar gets in the way. """ if diff_rorp.isflaglinked(): self._patch_hardlink_to_temp(diff_rorp, new) elif diff_rorp.get_attached_filetype() == 'snapshot': result = self._patch_snapshot_to_temp(diff_rorp, new) if not result: return 0 elif result == 2: return 1 # SpecialFile elif not self._patch_diff_to_temp(basis_rp, diff_rorp, new): return 0 if new.lstat(): if diff_rorp.isflaglinked(): if Globals.eas_write: """ `isflaglinked() == True` implies that we are processing the 2nd (or later) file in a group of files linked to an inode. As such, we don't need to perform the usual `copy_attribs(diff_rorp, new)` for the inode because that was already done when the 1st file in the group was processed. Nonetheless, we still must perform the following task (which would have normally been performed by `copy_attribs()`). Otherwise, the subsequent call to `_matches_cached_rorp(diff_rorp, new)` will fail because the new rorp's metadata would be missing the extended attribute data. """ new.data['ea'] = diff_rorp.get_ea() else: rpath.copy_attribs(diff_rorp, new) return self._matches_cached_rorp(diff_rorp, new)
def testInnerRestore(self): """Restore part of a dir, see if hard links preserved""" MakeOutputDir() output = rpath.RPath(Globals.local_connection, abs_output_dir) hlout1_dir = os.path.join(abs_test_dir, b"out_hardlink1") hlout2_dir = os.path.join(abs_test_dir, b"out_hardlink2") # Now set up directories out_hardlink1 and out_hardlink2 hlout1 = rpath.RPath(Globals.local_connection, hlout1_dir) if hlout1.lstat(): hlout1.delete() hlout1.mkdir() hlout1_sub = hlout1.append("subdir") hlout1_sub.mkdir() hl1_1 = hlout1_sub.append("hardlink1") hl1_2 = hlout1_sub.append("hardlink2") hl1_3 = hlout1_sub.append("hardlink3") hl1_4 = hlout1_sub.append("hardlink4") # 1 and 2 are hard linked, as are 3 and 4 hl1_1.touch() hl1_2.hardlink(hl1_1.path) hl1_3.touch() hl1_4.hardlink(hl1_3.path) hlout2 = rpath.RPath(Globals.local_connection, hlout2_dir) if hlout2.lstat(): hlout2.delete() xcopytree(hlout1_dir, hlout2_dir) hlout2_sub = hlout2.append("subdir") hl2_1 = hlout2_sub.append("hardlink1") hl2_2 = hlout2_sub.append("hardlink2") hl2_3 = hlout2_sub.append("hardlink3") hl2_4 = hlout2_sub.append("hardlink4") # Now 2 and 3 are hard linked, also 1 and 4 rpath.copy_with_attribs(hl1_1, hl2_1) rpath.copy_with_attribs(hl1_2, hl2_2) hl2_3.delete() hl2_3.hardlink(hl2_2.path) hl2_4.delete() hl2_4.hardlink(hl2_1.path) rpath.copy_attribs(hlout1_sub, hlout2_sub) # Now try backing up twice, making sure hard links are preserved InternalBackup(1, 1, hlout1.path, output.path) out_subdir = output.append("subdir") self.assertEqual( out_subdir.append("hardlink1").getinode(), out_subdir.append("hardlink2").getinode()) self.assertEqual( out_subdir.append("hardlink3").getinode(), out_subdir.append("hardlink4").getinode()) self.assertNotEqual( out_subdir.append("hardlink1").getinode(), out_subdir.append("hardlink3").getinode()) time.sleep(1) InternalBackup(1, 1, hlout2.path, output.path) out_subdir.setdata() self.assertEqual( out_subdir.append("hardlink1").getinode(), out_subdir.append("hardlink4").getinode()) self.assertEqual( out_subdir.append("hardlink2").getinode(), out_subdir.append("hardlink3").getinode()) self.assertNotEqual( out_subdir.append("hardlink1").getinode(), out_subdir.append("hardlink2").getinode()) # Now try restoring, still checking hard links. sub_dir = os.path.join(abs_output_dir, b"subdir") out2_dir = os.path.join(abs_test_dir, b"out2") out2 = rpath.RPath(Globals.local_connection, out2_dir) hlout1 = out2.append("hardlink1") hlout2 = out2.append("hardlink2") hlout3 = out2.append("hardlink3") hlout4 = out2.append("hardlink4") if out2.lstat(): out2.delete() InternalRestore(1, 1, sub_dir, out2_dir, 1) out2.setdata() for rp in [hlout1, hlout2, hlout3, hlout4]: rp.setdata() self.assertEqual(hlout1.getinode(), hlout2.getinode()) self.assertEqual(hlout3.getinode(), hlout4.getinode()) self.assertNotEqual(hlout1.getinode(), hlout3.getinode()) if out2.lstat(): out2.delete() InternalRestore(1, 1, sub_dir, out2_dir, int(time.time())) out2.setdata() for rp in [hlout1, hlout2, hlout3, hlout4]: rp.setdata() self.assertEqual(hlout1.getinode(), hlout4.getinode()) self.assertEqual(hlout2.getinode(), hlout3.getinode()) self.assertNotEqual(hlout1.getinode(), hlout2.getinode())
def testInnerRestore(self): """Restore part of a dir, see if hard links preserved""" MakeOutputDir() output = rpath.RPath(Globals.local_connection, "testfiles/output") # Now set up directories out_hardlink1 and out_hardlink2 hlout1 = rpath.RPath(Globals.local_connection, "testfiles/out_hardlink1") if hlout1.lstat(): hlout1.delete() hlout1.mkdir() hlout1_sub = hlout1.append("subdir") hlout1_sub.mkdir() hl1_1 = hlout1_sub.append("hardlink1") hl1_2 = hlout1_sub.append("hardlink2") hl1_3 = hlout1_sub.append("hardlink3") hl1_4 = hlout1_sub.append("hardlink4") # 1 and 2 are hard linked, as are 3 and 4 hl1_1.touch() hl1_2.hardlink(hl1_1.path) hl1_3.touch() hl1_4.hardlink(hl1_3.path) hlout2 = rpath.RPath(Globals.local_connection, "testfiles/out_hardlink2") if hlout2.lstat(): hlout2.delete() assert not os.system("cp -a testfiles/out_hardlink1 " "testfiles/out_hardlink2") hlout2_sub = hlout2.append("subdir") hl2_1 = hlout2_sub.append("hardlink1") hl2_2 = hlout2_sub.append("hardlink2") hl2_3 = hlout2_sub.append("hardlink3") hl2_4 = hlout2_sub.append("hardlink4") # Now 2 and 3 are hard linked, also 1 and 4 rpath.copy_with_attribs(hl1_1, hl2_1) rpath.copy_with_attribs(hl1_2, hl2_2) hl2_3.delete() hl2_3.hardlink(hl2_2.path) hl2_4.delete() hl2_4.hardlink(hl2_1.path) rpath.copy_attribs(hlout1_sub, hlout2_sub) # Now try backing up twice, making sure hard links are preserved InternalBackup(1, 1, hlout1.path, output.path) out_subdir = output.append("subdir") assert out_subdir.append("hardlink1").getinode() == \ out_subdir.append("hardlink2").getinode() assert out_subdir.append("hardlink3").getinode() == \ out_subdir.append("hardlink4").getinode() assert out_subdir.append("hardlink1").getinode() != \ out_subdir.append("hardlink3").getinode() time.sleep(1) InternalBackup(1, 1, hlout2.path, output.path) out_subdir.setdata() assert out_subdir.append("hardlink1").getinode() == \ out_subdir.append("hardlink4").getinode() assert out_subdir.append("hardlink2").getinode() == \ out_subdir.append("hardlink3").getinode() assert out_subdir.append("hardlink1").getinode() != \ out_subdir.append("hardlink2").getinode() # Now try restoring, still checking hard links. out2 = rpath.RPath(Globals.local_connection, "testfiles/out2") hlout1 = out2.append("hardlink1") hlout2 = out2.append("hardlink2") hlout3 = out2.append("hardlink3") hlout4 = out2.append("hardlink4") if out2.lstat(): out2.delete() InternalRestore(1, 1, "testfiles/output/subdir", "testfiles/out2", 1) out2.setdata() for rp in [hlout1, hlout2, hlout3, hlout4]: rp.setdata() assert hlout1.getinode() == hlout2.getinode() assert hlout3.getinode() == hlout4.getinode() assert hlout1.getinode() != hlout3.getinode() if out2.lstat(): out2.delete() InternalRestore(1, 1, "testfiles/output/subdir", "testfiles/out2", int(time.time())) out2.setdata() for rp in [hlout1, hlout2, hlout3, hlout4]: rp.setdata() assert hlout1.getinode() == hlout4.getinode(), \ "%s %s" % (hlout1.path, hlout4.path) assert hlout2.getinode() == hlout3.getinode() assert hlout1.getinode() != hlout2.getinode()