From 8ca3cf2a1d13e07988b6b7bc7f8c27fe6d150fb4 Mon Sep 17 00:00:00 2001 From: bescoto Date: Mon, 13 Oct 2003 08:01:36 +0000 Subject: Fix bug backing up acl system to no-acl system git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@473 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109 --- rdiff-backup/CHANGELOG | 3 ++ rdiff-backup/TODO | 2 -- rdiff-backup/rdiff_backup/Globals.py | 47 +++++++++++++------------ rdiff-backup/rdiff_backup/Main.py | 68 ++++++++++++++++++++++-------------- rdiff-backup/rdiff_backup/backup.py | 14 ++++---- rdiff-backup/rdiff_backup/restore.py | 2 +- rdiff-backup/rdiff_backup/rpath.py | 52 +++++++++++++++------------ rdiff-backup/testing/commontest.py | 23 +++++++----- rdiff-backup/testing/eas_aclstest.py | 3 +- 9 files changed, 124 insertions(+), 90 deletions(-) diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG index 6c714ef..5ad560f 100644 --- a/rdiff-backup/CHANGELOG +++ b/rdiff-backup/CHANGELOG @@ -20,6 +20,9 @@ this is the last of the unreadable file bugs... Rewrote hard link tracking system. New way should use less memory. +Fixed bug causing rdiff-backup to crash when backing up from system +supporting EAs/ACLs to one that didn't. + New in v0.13.2 (2003/09/16) --------------------------- diff --git a/rdiff-backup/TODO b/rdiff-backup/TODO index 9327376..9cd6ecf 100644 --- a/rdiff-backup/TODO +++ b/rdiff-backup/TODO @@ -1,8 +1,6 @@ Figure out why files getting unnecessarily incremented when upgrading from 0.12.x. -Fix support of case when source has EAs or ACLs, dest doesn't. - Consider adding --datadir option (Jean-Sébastien GOETSCHY) See if regressing takes too much memory (large directories). diff --git a/rdiff-backup/rdiff_backup/Globals.py b/rdiff-backup/rdiff_backup/Globals.py index de334f7..ccde68a 100644 --- a/rdiff-backup/rdiff_backup/Globals.py +++ b/rdiff-backup/rdiff_backup/Globals.py @@ -63,28 +63,27 @@ change_mirror_perms = (process_uid != 0) # If true, try to reset the atimes of the source partition. preserve_atime = None -# If true, save the extended attributes when backing up. -read_eas = None - -# If true, preserve the extended attributes on the mirror directory -# when backing up, or write them to the restore directory. This -# requires read_eas. -write_eas = None - -# If true, save access control lists when backup up. -read_acls = None - -# If true, write access control list information to the destination -# when backing up or restoring. Requires read_acls. -write_acls = None - -# If true, look for and save resource fork information when backing -# up. -read_resource_forks = None - -# If true, write resource fork information to destination when backing -# up or restoring. Requires read_resource_forks. -write_resource_forks = None +# The following three attributes represent whether extended attributes +# are supported. If eas_active is true, then the current session +# supports them. If eas_write is true, then the extended attributes +# should also be written to the destination side. Finally, eas_conn +# is relative to the current connection, and should be true iff that +# particular connection supports extended attributes. +eas_active = None +eas_write = None +eas_conn = None + +# The following settings are like the extended attribute settings, but +# apply to access control lists instead. +acls_active = None +acls_write = None +acls_conn = None + +# Like above two setting groups, but applies to support of Mac OS X +# style resource forks. +resource_forks_active = None +resource_forks_write = None +resource_forks_conn = None # This will be set as soon as the LocalConnection class loads local_connection = None @@ -231,6 +230,10 @@ def set(name, val): changed_settings.append(name) globals()[name] = val +def set_local(name, val): + """Like set above, but only set current connection""" + globals()[name] = val + def set_integer(name, val): """Like set, but make sure val is an integer""" try: intval = int(val) diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py index 9152df7..dea570d 100644 --- a/rdiff-backup/rdiff_backup/Main.py +++ b/rdiff-backup/rdiff_backup/Main.py @@ -111,12 +111,12 @@ def parse_cmdlineoptions(arglist): action = "list-increments" elif opt == '--list-increment-sizes': action = 'list-increment-sizes' elif opt == "--never-drop-acls": Globals.set("never_drop_acls", 1) - elif opt == "--no-acls": Globals.set("read_acls", 0) + elif opt == "--no-acls": Globals.set("acls_active", 0) elif opt == "--no-compare-inode": Globals.set("compare_inode", 0) elif opt == "--no-compression": Globals.set("compression", None) elif opt == "--no-compression-regexp": Globals.set("no_compression_regexp_string", arg) - elif opt == "--no-eas": Globals.set("read_eas", 0) + elif opt == "--no-eas": Globals.set("eas_active", 0) elif opt == "--no-file-statistics": Globals.set('file_statistics', 0) elif opt == "--no-hard-links": Globals.set('preserve_hardlinks', 0) elif opt == "--null-separator": Globals.set("null_separator", 1) @@ -358,31 +358,38 @@ def backup_final_init(rpout): def backup_set_fs_globals(rpin, rpout): """Use fs_abilities to set the globals that depend on filesystem""" - def update_bool_global(attr, bool): - """If bool is not None, update Globals.attr accordingly""" - if Globals.get(attr) is None: - SetConnections.UpdateGlobal(attr, bool) + def update_triple(src_support, dest_support, attr_triple): + """Update global settings for feature based on fsa results""" + active_attr, write_attr, conn_attr = attr_triple + if Globals.get(active_attr) == 0: return # don't override 0 + for attr in attr_triple: SetConnections.UpdateGlobal(attr, None) + if not src_support: return # if source doesn't support, nothing + SetConnections.UpdateGlobal(active_attr, 1) + rpin.conn.Globals.set_local(conn_attr, 1) + if dest_support: + SetConnections.UpdateGlobal(write_attr, 1) + rpout.conn.Globals.set_local(conn_attr, 1) src_fsa = rpin.conn.fs_abilities.get_fsabilities_readonly('source', rpin) Log(str(src_fsa), 3) dest_fsa = rpout.conn.fs_abilities.get_fsabilities_readwrite( 'destination', Globals.rbdir, 1, Globals.chars_to_quote) Log(str(dest_fsa), 3) - if Globals.never_drop_acls and not dest_fsa.acls: + + update_triple(src_fsa.eas, dest_fsa.eas, + ('eas_active', 'eas_write', 'eas_conn')) + update_triple(src_fsa.acls, dest_fsa.acls, + ('acls_active', 'acls_write', 'acls_conn')) + update_triple(src_fsa.resource_forks, dest_fsa.resource_forks, + ('resource_forks_active', 'resource_forks_write', + 'resource_forks_conn')) + if Globals.never_drop_acls and not Globals.acls_active: Log.FatalError("--never-drop-acls specified, but ACL support\n" "disabled on destination filesystem") - if Globals.read_acls != 0: update_bool_global('read_acls', src_fsa.acls) - if Globals.read_eas != 0: update_bool_global('read_eas', src_fsa.eas) - update_bool_global('read_resource_forks', src_fsa.resource_forks) - SetConnections.UpdateGlobal('preserve_hardlinks', dest_fsa.hardlinks) SetConnections.UpdateGlobal('fsync_directories', dest_fsa.fsync_dirs) SetConnections.UpdateGlobal('change_ownership', dest_fsa.ownership) - update_bool_global('write_acls', Globals.read_acls and dest_fsa.acls) - update_bool_global('write_eas', Globals.read_eas and dest_fsa.eas) - update_bool_global('write_resource_forks', - Globals.read_resource_forks and dest_fsa.resource_forks) SetConnections.UpdateGlobal('chars_to_quote', dest_fsa.chars_to_quote) if Globals.chars_to_quote: for conn in Globals.connections: @@ -455,9 +462,16 @@ def restore_init_quoting(src_rp): def restore_set_fs_globals(target): """Use fs_abilities to set the globals that depend on filesystem""" - def update_bool_global(attr, bool): - """If bool is not None, update Globals.attr accordingly""" - if Globals.get(attr) is None: SetConnections.UpdateGlobal(attr, bool) + def update_triple(src_support, dest_support, attr_triple): + """Update global settings for feature based on fsa results""" + active_attr, write_attr, conn_attr = attr_triple + if Globals.get(active_attr) == 0: return # don't override 0 + for attr in attr_triple: SetConnections.UpdateGlobal(attr, None) + if not dest_support: return # if dest doesn't support, do nothing + SetConnections.UpdateGlobal(active_attr, 1) + target.conn.Globals.set_local(conn_attr, 1) + target.conn.Globals.set_local(write_attr, 1) + if src_support: Globals.rbdir.conn.Globals.set_local(conn_attr, 1) target_fsa = target.conn.fs_abilities.get_fsabilities_readwrite( 'destination', target, 0) @@ -465,18 +479,18 @@ def restore_set_fs_globals(target): mirror_fsa = Globals.rbdir.conn.fs_abilities.get_fsabilities_restoresource( Globals.rbdir) Log(str(mirror_fsa), 3) - if Globals.never_drop_acls and not target_fsa.acls: + + update_triple(mirror_fsa.eas, target_fsa.eas, + ('eas_active', 'eas_write', 'eas_conn')) + update_triple(mirror_fsa.acls, target_fsa.acls, + ('acls_active', 'acls_write', 'acls_conn')) + update_triple(mirror_fsa.resource_forks, target_fsa.resource_forks, + ('resource_forks_active', 'resource_forks_write', + 'resource_forks_conn')) + if Globals.never_drop_acls and not Globals.acls_active: Log.FatalError("--never-drop-acls specified, but ACL support\n" "disabled on destination filesystem") - if Globals.read_acls != 0: - update_bool_global('read_acls', target_fsa.acls) - update_bool_global('write_acls', target_fsa.acls) - if Globals.read_eas != 0: - update_bool_global('read_eas', target_fsa.eas) - update_bool_global('write_eas', target_fsa.eas) - update_bool_global('read_resource_forks', target_fsa.resource_forks) - update_bool_global('write_resource_forks', target_fsa.resource_forks) SetConnections.UpdateGlobal('preserve_hardlinks', target_fsa.hardlinks) SetConnections.UpdateGlobal('change_ownership', target_fsa.ownership) diff --git a/rdiff-backup/rdiff_backup/backup.py b/rdiff-backup/rdiff_backup/backup.py index 850e266..ed0e733 100644 --- a/rdiff-backup/rdiff_backup/backup.py +++ b/rdiff-backup/rdiff_backup/backup.py @@ -135,7 +135,7 @@ class DestinationStruct: if use_metadata: rorp_iter = eas_acls.GetCombinedMetadataIter( Globals.rbdir, Time.prevtime, - acls = Globals.read_acls, eas = Globals.read_eas) + acls = Globals.acls_active, eas = Globals.eas_active) if rorp_iter: return rorp_iter return get_iter_from_fs() @@ -268,8 +268,8 @@ class CacheCollatedPostProcess: self.statfileobj = statistics.init_statfileobj() if Globals.file_statistics: statistics.FileStats.init() metadata.MetadataFile.open_file() - if Globals.read_eas: eas_acls.ExtendedAttributesFile.open_file() - if Globals.read_acls: eas_acls.AccessControlListFile.open_file() + if Globals.eas_active: eas_acls.ExtendedAttributesFile.open_file() + if Globals.acls_active: eas_acls.AccessControlListFile.open_file() # the following should map indicies to lists # [source_rorp, dest_rorp, changed_flag, success_flag, increment] @@ -368,10 +368,10 @@ class CacheCollatedPostProcess: else: metadata_rorp = None if metadata_rorp and metadata_rorp.lstat(): metadata.MetadataFile.write_object(metadata_rorp) - if Globals.read_eas and not metadata_rorp.get_ea().empty(): + if Globals.eas_active and not metadata_rorp.get_ea().empty(): eas_acls.ExtendedAttributesFile.write_object( metadata_rorp.get_ea()) - if Globals.read_acls and not metadata_rorp.get_acl().is_basic(): + if Globals.acls_active and not metadata_rorp.get_acl().is_basic(): eas_acls.AccessControlListFile.write_object( metadata_rorp.get_acl()) if Globals.file_statistics: @@ -427,8 +427,8 @@ class CacheCollatedPostProcess: dir_rp, perms = self.dir_perms_list.pop() dir_rp.chmod(perms) metadata.MetadataFile.close_file() - if Globals.read_eas: eas_acls.ExtendedAttributesFile.close_file() - if Globals.read_acls: eas_acls.AccessControlListFile.close_file() + if Globals.eas_active: eas_acls.ExtendedAttributesFile.close_file() + if Globals.acls_active: eas_acls.AccessControlListFile.close_file() if Globals.print_statistics: statistics.print_active_stats() if Globals.file_statistics: statistics.FileStats.close() statistics.write_active_statfileobj() diff --git a/rdiff-backup/rdiff_backup/restore.py b/rdiff-backup/rdiff_backup/restore.py index 7a4f0bf..a113726 100644 --- a/rdiff-backup/rdiff_backup/restore.py +++ b/rdiff-backup/rdiff_backup/restore.py @@ -177,7 +177,7 @@ class MirrorStruct: rorp_iter = eas_acls.GetCombinedMetadataIter( Globals.rbdir, rest_time, restrict_index = cls.mirror_base.index, - acls = Globals.write_acls, eas = Globals.write_eas) + acls = Globals.acls_active, eas = Globals.eas_active) if not rorp_iter: if require_metadata: log.Log.FatalError("Mirror metadata not found") diff --git a/rdiff-backup/rdiff_backup/rpath.py b/rdiff-backup/rdiff_backup/rpath.py index 3df537a..b1f3d0b 100644 --- a/rdiff-backup/rdiff_backup/rpath.py +++ b/rdiff-backup/rdiff_backup/rpath.py @@ -153,12 +153,12 @@ def copy_attribs(rpin, rpout): log.Log("Copying attributes from %s to %s" % (rpin.index, rpout.path), 7) assert rpin.lstat() == rpout.lstat() or rpin.isspecial() if rpin.issym(): return # symlinks have no valid attributes - if Globals.write_resource_forks and rpin.isreg(): + if Globals.resource_forks_write and rpin.isreg(): rpout.write_resource_fork(rpin.get_resource_fork()) - if Globals.write_eas: rpout.write_ea(rpin.get_ea()) + if Globals.eas_write: rpout.write_ea(rpin.get_ea()) if Globals.change_ownership: rpout.chown(*user_group.map_rpath(rpin)) rpout.chmod(rpin.getperms()) - if Globals.write_acls: rpout.write_acl(rpin.get_acl()) + if Globals.acls_write: rpout.write_acl(rpin.get_acl()) if not rpin.isdev(): rpout.setmtime(rpin.getmtime()) def copy_attribs_inc(rpin, rpout): @@ -172,14 +172,14 @@ def copy_attribs_inc(rpin, rpout): log.Log("Copying inc attrs from %s to %s" % (rpin.index, rpout.path), 7) check_for_files(rpin, rpout) if rpin.issym(): return # symlinks have no valid attributes - if Globals.write_resource_forks and rpin.isreg() and rpout.isreg(): + if Globals.resource_forks_write and rpin.isreg() and rpout.isreg(): rpout.write_resource_fork(rpin.get_resource_fork()) - if Globals.write_eas: rpout.write_ea(rpin.get_ea()) + if Globals.eas_write: rpout.write_ea(rpin.get_ea()) if Globals.change_ownership: apply(rpout.chown, rpin.getuidgid()) if rpin.isdir() and not rpout.isdir(): rpout.chmod(rpin.getperms() & 0777) else: rpout.chmod(rpin.getperms()) - if Globals.write_acls: rpout.write_acl(rpin.get_acl(), map_names = 0) + if Globals.acls_write: rpout.write_acl(rpin.get_acl(), map_names = 0) if not rpin.isdev(): rpout.setmtime(rpin.getmtime()) def cmp_attribs(rp1, rp2): @@ -292,9 +292,9 @@ class RORPath: elif key == 'ctime': pass elif key == 'devloc' or key == 'nlink': pass elif key == 'size' and not self.isreg(): pass - elif key == 'ea' and not Globals.read_eas: pass - elif key == 'acl' and not Globals.read_acls: pass - elif key == 'resourcefork' and not Globals.read_resource_forks: + elif key == 'ea' and not Globals.eas_active: pass + elif key == 'acl' and not Globals.acls_active: pass + elif key == 'resourcefork' and not Globals.resource_forks_active: pass elif (key == 'inode' and (not self.isreg() or self.getnumlinks() == 1 or @@ -324,9 +324,9 @@ class RORPath: elif key == 'devloc' or key == 'nlink': pass elif key == 'size' and not self.isreg(): pass elif key == 'inode': pass - elif key == 'ea' and not Globals.write_eas: pass - elif key == 'acl' and not Globals.write_acls: pass - elif key == 'resourcefork' and not Globals.write_resource_forks: + elif key == 'ea' and not Globals.eas_write: pass + elif key == 'acl' and not Globals.acls_write: pass + elif key == 'resourcefork' and not Globals.resource_forks_write: pass elif (not other.data.has_key(key) or self.data[key] != other.data[key]): return 0 @@ -653,13 +653,7 @@ class RPath(RORPath): def setdata(self): """Set data dictionary using C extension""" self.data = self.conn.C.make_file_dict(self.path) - if not self.lstat(): return - self.data['uname'] = self.conn.user_group.uid2uname(self.data['uid']) - self.data['gname'] = self.conn.user_group.gid2gname(self.data['gid']) - if Globals.read_eas: self.data['ea'] = self.conn.rpath.ea_get(self) - if Globals.read_acls: self.data['acl'] = self.conn.rpath.acl_get(self) - if Globals.read_resource_forks and self.isreg(): - self.get_resource_fork() + if self.lstat(): self.conn.rpath.setdata_local(self) def make_file_dict_old(self): """Create the data dictionary""" @@ -1097,9 +1091,23 @@ class RPathFileHook: return result +def setdata_local(rpath): + """Set eas/acls, uid/gid, resource fork in data dictionary + + This is a global function because it must be called locally, since + these features may exist or not depending on the connection. + + """ + assert rpath.conn is Globals.local_connection + rpath.data['uname'] = user_group.uid2uname(rpath.data['uid']) + rpath.data['gname'] = user_group.gid2gname(rpath.data['gid']) + if Globals.eas_conn: rpath.data['ea'] = ea_get(rpath) + if Globals.acls_conn: rpath.data['acl'] = acl_get(rpath) + if Globals.resource_forks_conn and rpath.isreg(): + rpath.get_resource_fork() + + # These two are overwritten by the eas_acls.py module. We can't -# import that module directory because of circular dependency -# problems. +# import that module directly because of circular dependency problems. def acl_get(rp): assert 0 def ea_get(rp): assert 0 - diff --git a/rdiff-backup/testing/commontest.py b/rdiff-backup/testing/commontest.py index 9d1ee81..de2b64e 100644 --- a/rdiff-backup/testing/commontest.py +++ b/rdiff-backup/testing/commontest.py @@ -75,7 +75,7 @@ def cmd_schemas2rps(schema_list, remote_schema): SetConnections.get_cmd_pairs(schema_list, remote_schema)) def InternalBackup(source_local, dest_local, src_dir, dest_dir, - current_time = None): + current_time = None, eas = None, acls = None): """Backup src to dest internally This is like rdiff_backup but instead of running a separate @@ -96,6 +96,10 @@ def InternalBackup(source_local, dest_local, src_dir, dest_dir, % (SourceDir, dest_dir) rpin, rpout = cmd_schemas2rps([src_dir, dest_dir], remote_schema) + for attr in ('eas_active', 'eas_write', 'eas_conn'): + SetConnections.UpdateGlobal(attr, eas) + for attr in ('acls_active', 'acls_write', 'acls_conn'): + SetConnections.UpdateGlobal(attr, acls) Main.misc_setup([rpin, rpout]) Main.Backup(rpin, rpout) Main.cleanup() @@ -118,7 +122,8 @@ def InternalMirror(source_local, dest_local, src_dir, dest_dir): # Restore old attributes rpath.copy_attribs(src_root, dest_root) -def InternalRestore(mirror_local, dest_local, mirror_dir, dest_dir, time): +def InternalRestore(mirror_local, dest_local, mirror_dir, dest_dir, time, + eas = None, acls = None): """Restore mirror_dir to dest_dir at given time This will automatically find the increments.XXX.dir representing @@ -138,6 +143,10 @@ def InternalRestore(mirror_local, dest_local, mirror_dir, dest_dir, time): % (SourceDir, dest_dir) mirror_rp, dest_rp = cmd_schemas2rps([mirror_dir, dest_dir], remote_schema) + for attr in ('eas_active', 'eas_write', 'eas_conn'): + SetConnections.UpdateGlobal(attr, eas) + for attr in ('acls_active', 'acls_write', 'acls_conn'): + SetConnections.UpdateGlobal(attr, acls) Main.misc_setup([mirror_rp, dest_rp]) inc = get_increment_rp(mirror_rp, time) if inc: Main.Restore(get_increment_rp(mirror_rp, time), dest_rp) @@ -299,10 +308,6 @@ def BackupRestoreSeries(source_local, dest_local, list_of_dirnames, """ Globals.set('preserve_hardlinks', compare_hardlinks) - Globals.set('write_eas', compare_eas) - Globals.set('read_eas', compare_eas) - Globals.set('write_acls', compare_acls) - Globals.set('read_acls', compare_acls) time = 10000 dest_rp = rpath.RPath(Globals.local_connection, dest_dirname) restore_rp = rpath.RPath(Globals.local_connection, restore_dirname) @@ -313,7 +318,8 @@ def BackupRestoreSeries(source_local, dest_local, list_of_dirnames, reset_hardlink_dicts() _reset_connections(src_rp, dest_rp) - InternalBackup(source_local, dest_local, dirname, dest_dirname, time) + InternalBackup(source_local, dest_local, dirname, dest_dirname, time, + eas = compare_eas, acls = compare_acls) time += 10000 _reset_connections(src_rp, dest_rp) if compare_backups: @@ -326,7 +332,8 @@ def BackupRestoreSeries(source_local, dest_local, list_of_dirnames, reset_hardlink_dicts() Myrm(restore_dirname) InternalRestore(dest_local, source_local, dest_dirname, - restore_dirname, time) + restore_dirname, time, + eas = compare_eas, acls = compare_acls) src_rp = rpath.RPath(Globals.local_connection, dirname) assert CompareRecursive(src_rp, restore_rp, compare_eas = compare_eas, diff --git a/rdiff-backup/testing/eas_aclstest.py b/rdiff-backup/testing/eas_aclstest.py index 91e9d75..d27abe0 100644 --- a/rdiff-backup/testing/eas_aclstest.py +++ b/rdiff-backup/testing/eas_aclstest.py @@ -1,13 +1,14 @@ import unittest, os, time, cStringIO, posix1e, pwd, grp from commontest import * from rdiff_backup.eas_acls import * -from rdiff_backup import Globals, rpath, Time, user_group +from rdiff_backup import Globals, rpath, Time, user_group, log user_group.init_user_mapping() user_group.init_group_mapping() tempdir = rpath.RPath(Globals.local_connection, "testfiles/output") restore_dir = rpath.RPath(Globals.local_connection, "testfiles/restore_out") +log.Log.setverbosity(7) class EATest(unittest.TestCase): """Test extended attributes""" -- cgit v1.2.1