From 94da66c4015094e2c5a71e945ef3562644543c0f Mon Sep 17 00:00:00 2001 From: bescoto Date: Sat, 23 Aug 2003 23:42:57 +0000 Subject: Added tests for and fixed problem with backing up unreadable regular files as root on source and non-root on dest. git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@399 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109 --- rdiff-backup/CHANGELOG | 4 +++ rdiff-backup/rdiff_backup/Main.py | 2 ++ rdiff-backup/rdiff_backup/backup.py | 23 ++++++++---- rdiff-backup/rdiff_backup/increment.py | 8 ++++- rdiff-backup/rdiff_backup/rpath.py | 8 +++++ rdiff-backup/testing/roottest.py | 66 ++++++++++++++++++++++++++++++---- 6 files changed, 98 insertions(+), 13 deletions(-) diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG index 98f2e5f..71c1e07 100644 --- a/rdiff-backup/CHANGELOG +++ b/rdiff-backup/CHANGELOG @@ -4,6 +4,10 @@ New in v0.13.2 (??????????) Specified socket type as SOCK_STREAM. (Error reported by Erik Forsberg.) +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.) + New in v0.13.1 (2003/08/08) --------------------------- diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py index e8167d6..638f137 100644 --- a/rdiff-backup/rdiff_backup/Main.py +++ b/rdiff-backup/rdiff_backup/Main.py @@ -423,8 +423,10 @@ def restore_set_fs_globals(target): target_fsa = fs_abilities.FSAbilities('destination').init_readwrite( target, 0) + Log(str(target_fsa), 3) mirror_fsa = fs_abilities.FSAbilities('source').init_readwrite( Globals.rbdir) + Log(str(mirror_fsa), 3) update_bool_global('read_acls', target_fsa.acls) update_bool_global('write_acls', target_fsa.acls) diff --git a/rdiff-backup/rdiff_backup/backup.py b/rdiff-backup/rdiff_backup/backup.py index 33e97bf..0909722 100644 --- a/rdiff-backup/rdiff_backup/backup.py +++ b/rdiff-backup/rdiff_backup/backup.py @@ -184,15 +184,26 @@ class DestinationStruct: elif dest_rorp: dest_sig = dest_rorp.getRORPath() if dest_rorp.isreg(): - dest_rp = dest_base_rpath.new_index(index) - if not dest_rp.isreg(): - log.ErrorLog.write_if_open("UpdateError", dest_rp, - "File changed from regular file before signature") - return None - dest_sig.setfile(Rdiff.get_signature(dest_rp)) + sig_fp = cls.get_one_sig_fp(dest_base_rpath.new_index(index)) + if sig_fp is None: return None + dest_sig.setfile(sig_fp) else: dest_sig = rpath.RORPath(index) return dest_sig + def get_one_sig_fp(cls, dest_rp): + """Return a signature fp of given index, corresponding to reg file""" + if not dest_rp.isreg(): + log.ErrorLog.write_if_open("UpdateError", dest_rp, + "File changed from regular file before signature") + return None + if Globals.process_uid != 0 and not dest_rp.readable(): + # This branch can happen with root source and non-root + # destination. Permissions are changed permanently, which + # should propogate to the diffs + assert dest_rp.isowner(), 'no ownership of %s' % (dest_rp.path,) + dest_rp.chmod(0400 | dest_rp.getperms()) + return Rdiff.get_signature(dest_rp) + def patch(cls, dest_rpath, source_diffiter, start_index = ()): """Patch dest_rpath with an rorpiter of diffs""" ITR = rorpiter.IterTreeReducer(PatchITRB, [dest_rpath, cls.CCPP]) diff --git a/rdiff-backup/rdiff_backup/increment.py b/rdiff-backup/rdiff_backup/increment.py index 8d829e2..ea7a81d 100644 --- a/rdiff-backup/rdiff_backup/increment.py +++ b/rdiff-backup/rdiff_backup/increment.py @@ -79,7 +79,13 @@ def makediff(new, mirror, incpref): if compress: diff = get_inc(incpref, "diff.gz") else: diff = get_inc(incpref, "diff") - Rdiff.write_delta(new, mirror, diff, compress) + if Globals.process_uid != 0 and not new.readable(): + # Check for unreadable files + old_new_perms = new.getperms() + new.chmod(0400 | old_new_perms) + Rdiff.write_delta(new, mirror, diff, compress) + new.chmod(old_new_perms) + else: Rdiff.write_delta(new, mirror, diff, compress) rpath.copy_attribs(mirror, diff) return diff diff --git a/rdiff-backup/rdiff_backup/rpath.py b/rdiff-backup/rdiff_backup/rpath.py index 247dc40..43f14e3 100644 --- a/rdiff-backup/rdiff_backup/rpath.py +++ b/rdiff-backup/rdiff_backup/rpath.py @@ -885,6 +885,14 @@ class RPath(RORPath): raise RPathException("Error closing file") self.setdata() + def write_string(self, s, compress = None): + """Write string s into rpath""" + assert not self.lstat(), "File %s already exists" % (self.path,) + outfp = self.open("wb", compress = compress) + outfp.write(s) + assert not outfp.close() + self.setdata() + def isincfile(self): """Return true if path looks like an increment file diff --git a/rdiff-backup/testing/roottest.py b/rdiff-backup/testing/roottest.py index 6193893..ab0c580 100644 --- a/rdiff-backup/testing/roottest.py +++ b/rdiff-backup/testing/roottest.py @@ -2,15 +2,19 @@ import unittest, os from commontest import * from rdiff_backup import Globals, log -"""Root tests +"""Root tests - contain tests which need to be run as root. + +Some of the quoting here may not work with csh (works on bash). Also, +if you aren't me, check out the 'user' global variable. -This is mainly a copy of regressiontest.py, but contains the two tests -that are meant to be run as root. """ Globals.set('change_source_perms', None) Globals.counter = 0 -log.Log.setverbosity(6) +verbosity = 3 +log.Log.setverbosity(verbosity) +user = 'ben' # Non-root user to su to +assert os.getuid() == 0, "Run this test as root!" def Run(cmd): print "Running: ", cmd @@ -24,6 +28,56 @@ class RootTest(unittest.TestCase): def testLocal2(self): BackupRestoreSeries(1, 1, self.dirlist2) def testRemote(self): BackupRestoreSeries(None, None, self.dirlist1) +class HalfRoot(unittest.TestCase): + """Backing up files where origin is root and destination is non-root""" + def make_dirs(self): + """Make source directories, return rpaths + + These make a directory with a changing file that is not + self-readable. (Caused problems earlier.) + + """ + rp1 = rpath.RPath(Globals.local_connection, "testfiles/root_half1") + if rp1.lstat(): Myrm(rp1.path) + rp1.mkdir() + rp1_1 = rp1.append('foo') + rp1_1.write_string('hello') + rp1_1.chmod(0) + rp1_2 = rp1.append('to be deleted') + rp1_2.write_string('aosetuhaosetnuhontu') + rp1_2.chmod(0) + + rp2 = rpath.RPath(Globals.local_connection, "testfiles/root_half2") + if rp2.lstat(): Myrm(rp2.path) + rp2.mkdir() + rp2_1 = rp2.append('foo') + rp2_1.write_string('goodbye') + rp2_1.chmod(0) + return rp1, rp2 + + def test_backup(self): + """Right now just test backing up""" + in_rp1, in_rp2 = self.make_dirs() + outrp = rpath.RPath(Globals.local_connection, "testfiles/output") + if outrp.lstat(): outrp.delete() + remote_schema = 'su -c "rdiff-backup --server" %s' % (user,) + cmd_schema = ("rdiff-backup -v" + str(verbosity) + + " --current-time %s --remote-schema '%%s' %s '%s'::%s") + + cmd1 = cmd_schema % (10000, in_rp1.path, remote_schema, outrp.path) + print "Executing: ", cmd1 + assert not os.system(cmd1) + in_rp1.setdata() + outrp.setdata() + assert CompareRecursive(in_rp1, outrp) + + cmd2 = cmd_schema % (20000, in_rp2.path, remote_schema, outrp.path) + print "Executing: ", cmd2 + assert not os.system(cmd2) + in_rp2.setdata() + outrp.setdata() + assert CompareRecursive(in_rp2, outrp) + class NonRoot(unittest.TestCase): """Test backing up as non-root user @@ -32,7 +86,6 @@ class NonRoot(unittest.TestCase): root, everything should be restored normally. """ - user = 'ben' def make_root_dirs(self): """Make directory createable only by root""" rp = rpath.RPath(Globals.local_connection, "testfiles/root_out1") @@ -61,10 +114,11 @@ class NonRoot(unittest.TestCase): return rp, sp def backup(self, input_rp, output_rp, time): + global user backup_cmd = ("rdiff-backup --no-compare-inode " "--current-time %s %s %s" % (time, input_rp.path, output_rp.path)) - Run("su %s -c '%s'" % (self.user, backup_cmd)) + Run("su %s -c '%s'" % (user, backup_cmd)) def restore(self, dest_rp, restore_rp, time = None): assert restore_rp.path == "testfiles/rest_out" -- cgit v1.2.1