summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbescoto <bescoto@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109>2003-09-28 04:34:39 +0000
committerbescoto <bescoto@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109>2003-09-28 04:34:39 +0000
commit325ba37502ba34ddb76ec10f9166d7fdd2bab024 (patch)
tree2e2703d2e128d30a34743a9f6231caa7f77cd587
parent4d274a0bdb3d27b54b946de8f930e2acfd786dcf (diff)
downloadrdiff-backup-325ba37502ba34ddb76ec10f9166d7fdd2bab024.tar.gz
Added tests and fixes for restore/regress permission bug
git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/branches/r0-12@456 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
-rw-r--r--rdiff-backup/CHANGELOG5
-rw-r--r--rdiff-backup/rdiff_backup/Security.py1
-rw-r--r--rdiff-backup/rdiff_backup/regress.py17
-rw-r--r--rdiff-backup/rdiff_backup/restore.py80
-rw-r--r--rdiff-backup/testing/regresstest.py45
-rw-r--r--rdiff-backup/testing/roottest.py46
6 files changed, 178 insertions, 16 deletions
diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG
index 3b3514a..2bb9371 100644
--- a/rdiff-backup/CHANGELOG
+++ b/rdiff-backup/CHANGELOG
@@ -13,6 +13,11 @@ memory. Thanks for Jacques Botha for report.
Fixed bug restoring some directories when mirror_metadata file was
missing (as when made by 0.10.x version).
+Regressing and restoring as non-root user now works on directories
+that contain unreadable files and directories as long as they are
+owned by that user. Bug report by Arkadiusz Miskiewicz. Hopefully
+this is the last of the unreadable file bugs...
+
New in v0.12.4 (2003/09/13)
---------------------------
diff --git a/rdiff-backup/rdiff_backup/Security.py b/rdiff-backup/rdiff_backup/Security.py
index 9254a0c..25962ed 100644
--- a/rdiff-backup/rdiff_backup/Security.py
+++ b/rdiff-backup/rdiff_backup/Security.py
@@ -133,6 +133,7 @@ def set_allowed_requests(sec_level):
allowed_requests.extend(
["restore.MirrorStruct.set_mirror_and_rest_times",
"restore.MirrorStruct.initialize_rf_cache",
+ "restore.MirrorStruct.close_rf_cache",
"restore.MirrorStruct.get_diffs",
"backup.SourceStruct.set_source_select",
"backup.SourceStruct.get_source_select",
diff --git a/rdiff-backup/rdiff_backup/regress.py b/rdiff-backup/rdiff_backup/regress.py
index b4e630f..b619fea 100644
--- a/rdiff-backup/rdiff_backup/regress.py
+++ b/rdiff-backup/rdiff_backup/regress.py
@@ -112,14 +112,29 @@ def remove_rbdir_increments():
old_current_mirror.delete()
def iterate_raw_rfs(mirror_rp, inc_rp):
- """Iterate all RegressFile objects in mirror/inc directory"""
+ """Iterate all RegressFile objects in mirror/inc directory
+
+ Also changes permissions of unreadable files to allow access and
+ then changes them back later.
+
+ """
root_rf = RegressFile(mirror_rp, inc_rp, restore.get_inclist(inc_rp))
def helper(rf):
+ mirror_rp = rf.mirror_rp
+ if (Globals.process_uid != 0 and
+ ((mirror_rp.isreg() and not mirror_rp.readable()) or
+ (mirror_rp.isdir() and not mirror_rp.hasfullperms()))):
+ unreadable, old_perms = 1, mirror_rp.getperms()
+ if mirror_rp.isreg(): mirror_rp.chmod(0400 | old_perms)
+ else: mirror_rp.chmod(0700 | old_perms)
+ else: unreadable = 0
yield rf
+ if unreadable and mirror_rp.isreg(): mirror_rp.chmod(old_perms)
if rf.mirror_rp.isdir() or rf.inc_rp.isdir():
for sub_rf in rf.yield_sub_rfs():
for sub_sub_rf in helper(sub_rf):
yield sub_sub_rf
+ if unreadable and mirror_rp.isdir(): mirror_rp.chmod(old_perms)
return helper(root_rf)
def yield_metadata():
diff --git a/rdiff-backup/rdiff_backup/restore.py b/rdiff-backup/rdiff_backup/restore.py
index 292aa42..34813df 100644
--- a/rdiff-backup/rdiff_backup/restore.py
+++ b/rdiff-backup/rdiff_backup/restore.py
@@ -48,6 +48,7 @@ def Restore(mirror_rp, inc_rpath, target, restore_to_time):
target_iter = TargetS.get_initial_iter(target)
diff_iter = MirrorS.get_diffs(target_iter)
TargetS.patch(target, diff_iter)
+ MirrorS.close_rf_cache()
def get_inclist(inc_rpath):
"""Returns increments with given base"""
@@ -87,7 +88,7 @@ def ListChangedSince(mirror_rp, inc_rp, restore_to_time):
path_desc = (old_rorp and old_rorp.get_indexpath() or
cur_rorp.get_indexpath())
yield rpath.RORPath(("%-7s %s" % (change, path_desc),))
-
+ MirrorStruct.close_rf_cache()
def ListAtTime(mirror_rp, inc_rp, time):
"""List the files in archive at the given time"""
@@ -150,6 +151,10 @@ class MirrorStruct:
cls.root_rf = rf
cls.rf_cache = CachedRF(rf)
+ def close_rf_cache(cls):
+ """Run anything remaining on CachedRF object"""
+ cls.rf_cache.close()
+
def get_mirror_rorp_iter(cls, rest_time = None, require_metadata = None):
"""Return iter of mirror rps at given restore time
@@ -287,6 +292,8 @@ class CachedRF:
"""Initialize CachedRF, self.rf_list variable"""
self.root_rf = root_rf
self.rf_list = [] # list should filled in index order
+ if Globals.process_uid != 0:
+ self.perm_changer = PermissionChanger(root_rf.mirror_rp)
def list_rfs_in_cache(self, index):
"""Used for debugging, return indicies of cache rfs for printing"""
@@ -301,7 +308,9 @@ class CachedRF:
if not self.rf_list:
if not self.add_rfs(index): return None
rf = self.rf_list[0]
- if rf.index == index: return rf
+ if rf.index == index:
+ if Globals.process_uid != 0: self.perm_changer(rf.mirror_rp)
+ return rf
elif rf.index > index:
# Try to add earlier indicies. But if first is
# already from same directory, or we can't find any
@@ -331,6 +340,7 @@ The cause is probably data loss from the destination directory.""" %
parent_index = index[:-1]
temp_rf = RestoreFile(self.root_rf.mirror_rp.new_index(parent_index),
self.root_rf.inc_rp.new_index(parent_index), [])
+ if Globals.process_uid != 0: self.perm_changer(temp_rf.mirror_rp)
new_rfs = list(temp_rf.yield_sub_rfs())
if not new_rfs:
log.Log("Warning: No RFs added for index %s" % (index,), 2)
@@ -338,6 +348,10 @@ The cause is probably data loss from the destination directory.""" %
self.rf_list[0:0] = new_rfs
return 1
+ def close(self):
+ """Finish remaining rps in PermissionChanger"""
+ if Globals.process_uid != 0: self.perm_changer.finish()
+
class RestoreFile:
"""Hold data about a single mirror file and its related increments
@@ -609,3 +623,65 @@ class PatchITRB(rorpiter.ITRBranch):
self.base_rp.rmdir()
if self.dir_replacement.lstat():
rpath.rename(self.dir_replacement, self.base_rp)
+
+
+class PermissionChanger:
+ """Change the permission of mirror files and directories
+
+ The problem is that mirror files and directories may need their
+ permissions changed in order to be read and listed, and then
+ changed back when we are done. This class hooks into the CachedRF
+ object to know when an rp is needed.
+
+ """
+ def __init__(self, root_rp):
+ self.root_rp = root_rp
+ self.current_index = ()
+ # Below is a list of (index, rp, old_perm) triples in reverse
+ # order that need clearing
+ self.open_index_list = []
+
+ def __call__(self, rp):
+ """Given rpath, change permissions up and including rp"""
+ index, old_index = rp.index, self.current_index
+ self.current_index = index
+ if not index or index == old_index: return
+ assert index > old_index, (index, old_index)
+ self.restore_old(rp, index)
+ self.add_new(rp, old_index, index)
+
+ def restore_old(self, rp, index):
+ """Restore permissions for indicies we are done with"""
+ while self.open_index_list:
+ old_index, old_rp, old_perms = self.open_index_list[0]
+ if index[:len(old_index)] > old_index: old_rp.chmod(old_perms)
+ else: break
+ del self.open_index_list[0]
+
+ def add_new(self, rp, old_index, index):
+ """Change permissions of directories between old_index and index"""
+ for rp in self.get_new_rp_list(rp, old_index, index):
+ if ((rp.isreg() and not rp.readable()) or
+ (rp.isdir() and not rp.hasfullperms())):
+ old_perms = rp.getperms()
+ self.open_index_list.insert(0, (index, rp, old_perms))
+ if rp.isreg(): rp.chmod(0400 | old_perms)
+ else: rp.chmod(0700 | old_perms)
+
+ def get_new_rp_list(self, rp, old_index, index):
+ """Return list of new rp's between old_index and index"""
+ for i in range(len(index)-1, -1, -1):
+ if old_index[:i] == index[:i]:
+ common_prefix_len = i
+ break
+ else: assert 0
+
+ new_rps = []
+ for total_len in range(common_prefix_len+1, len(index)):
+ new_rps.append(self.root_rp.new_index(index[:total_len]))
+ new_rps.append(rp)
+ return new_rps
+
+ def finish(self):
+ """Restore any remaining rps"""
+ for index, rp, perms in self.open_index_list: rp.chmod(perms)
diff --git a/rdiff-backup/testing/regresstest.py b/rdiff-backup/testing/regresstest.py
index be02e67..a94f4ae 100644
--- a/rdiff-backup/testing/regresstest.py
+++ b/rdiff-backup/testing/regresstest.py
@@ -93,4 +93,49 @@ class RegressTest(unittest.TestCase):
"""Run regress test remotely"""
self.runtest(self.regress_to_time_remote)
+ def test_unreadable(self):
+ """Run regress test when regular file is unreadable"""
+ self.output_rp.setdata()
+ if self.output_rp.lstat(): Myrm(self.output_rp.path)
+ unreadable_rp = self.make_unreadable()
+
+ rdiff_backup(1, 1, unreadable_rp.path, self.output_rp.path,
+ current_time = 1)
+ rbdir = self.output_rp.append('rdiff-backup-data')
+ marker = rbdir.append('current_mirror.2000-12-31T21:33:20-07:00.data')
+ marker.touch()
+ self.change_unreadable()
+
+ cmd = "rdiff-backup --check-destination-dir " + self.output_rp.path
+ print "Executing:", cmd
+ assert not os.system(cmd)
+
+ def make_unreadable(self):
+ """Make unreadable input directory
+
+ The directory needs to be readable initially (otherwise it
+ just won't get backed up, and then later we will turn it
+ unreadable.
+
+ """
+ rp = rpath.RPath(Globals.local_connection, "testfiles/regress")
+ if rp.lstat(): Myrm(rp.path)
+ rp.setdata()
+ rp.mkdir()
+ rp1 = rp.append('unreadable_dir')
+ rp1.mkdir()
+ rp1_1 = rp1.append('to_be_unreadable')
+ rp1_1.write_string('aensuthaoeustnahoeu')
+ return rp
+
+ def change_unreadable(self):
+ """Change attributes in directory, so regress will request fp"""
+ subdir = self.output_rp.append('unreadable_dir')
+ assert subdir.lstat()
+ filerp = subdir.append('to_be_unreadable')
+ filerp.chmod(0)
+ subdir.chmod(0)
+
+
if __name__ == "__main__": unittest.main()
+
diff --git a/rdiff-backup/testing/roottest.py b/rdiff-backup/testing/roottest.py
index a548767..3aa6d42 100644
--- a/rdiff-backup/testing/roottest.py
+++ b/rdiff-backup/testing/roottest.py
@@ -11,7 +11,7 @@ if you aren't me, check out the 'user' global variable.
Globals.set('change_source_perms', None)
Globals.counter = 0
-verbosity = 3
+verbosity = 5
log.Log.setverbosity(verbosity)
user = 'ben' # Non-root user to su to
assert os.getuid() == 0, "Run this test as root!"
@@ -50,6 +50,13 @@ class HalfRoot(unittest.TestCase):
rp1_3.mkdir()
rp1_3_1 = rp1_3.append('file_inside')
rp1_3_1.write_string('blah')
+ rp1_3_1.chmod(0)
+ rp1_3_2 = rp1_3.append('subdir_inside')
+ rp1_3_2.mkdir()
+ rp1_3_2_1 = rp1_3_2.append('foo')
+ rp1_3_2_1.write_string('saotnhu')
+ rp1_3_2_1.chmod(0)
+ rp1_3_2.chmod(0)
rp1_3.chmod(0)
rp2 = rpath.RPath(Globals.local_connection, "testfiles/root_half2")
@@ -60,8 +67,17 @@ class HalfRoot(unittest.TestCase):
rp2_1.chmod(0)
rp2_3 = rp2.append('unreadable_dir')
rp2_3.mkdir()
- rp2_3_2 = rp2_3.append('file2')
- rp2_3_2.touch()
+ rp2_3_1 = rp2_3.append('file_inside')
+ rp2_3_1.write_string('new string')
+ rp2_3_1.chmod(0)
+ rp2_3_2 = rp2_3.append('subdir_inside')
+ rp2_3_2.mkdir()
+ rp2_3_2_1 = rp2_3_2.append('foo')
+ rp2_3_2_1.write_string('asoetn;oet')
+ rp2_3_2_1.chmod(0)
+ rp2_3_2.chmod(0)
+ rp2_3_3 = rp2_3.append('file2')
+ rp2_3_3.touch()
rp2_3.chmod(0)
return rp1, rp2
@@ -90,23 +106,27 @@ class HalfRoot(unittest.TestCase):
rout_rp = rpath.RPath(Globals.local_connection,
"testfiles/restore_out")
+ restore_schema = ("rdiff-backup -v" + str(verbosity) +
+ " -r %s --remote-schema '%%s' '%s'::%s %s")
Myrm(rout_rp.path)
- cmd3 = cmd_schema % (10000,
- "-r 10000 %s/unreadable_dir" % (outrp.path,),
- remote_schema, rout_rp.path)
+ cmd3 = restore_schema % (10000, remote_schema, outrp.path,
+ rout_rp.path)
print "Executing restore: ", cmd3
assert not os.system(cmd3)
- rout_rp.setdata()
- assert rout_rp.getperms() == 0, rout_rp.getperms()
+ rout_perms = rout_rp.append('unreadable_dir').getperms()
+ outrp_perms = outrp.append('unreadable_dir').getperms()
+ assert rout_perms == 0, rout_perms
+ assert outrp_perms == 0, outrp_perms
Myrm(rout_rp.path)
- cmd4 = cmd_schema % (10000,
- "-r now %s/unreadable_dir" % (outrp.path,),
- remote_schema, rout_rp.path)
+ cmd4 = restore_schema % ("now", remote_schema, outrp.path,
+ rout_rp.path)
print "Executing restore: ", cmd4
assert not os.system(cmd4)
- rout_rp.setdata()
- assert rout_rp.getperms() == 0, rout_rp.getperms()
+ rout_perms = rout_rp.append('unreadable_dir').getperms()
+ outrp_perms = outrp.append('unreadable_dir').getperms()
+ assert rout_perms == 0, rout_perms
+ assert outrp_perms == 0, outrp_perms
class NonRoot(unittest.TestCase):