diff options
author | bescoto <bescoto@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109> | 2003-09-18 02:10:14 +0000 |
---|---|---|
committer | bescoto <bescoto@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109> | 2003-09-18 02:10:14 +0000 |
commit | bfd24043e3f728e960cf8c8829df36d799505106 (patch) | |
tree | c17ceb5abdb684d868e77f38780a4a523ada322d /rdiff-backup/rdiff_backup | |
parent | 70b97a3bc174351ff9a3044414c2699070304488 (diff) | |
download | rdiff-backup-bfd24043e3f728e960cf8c8829df36d799505106.tar.gz |
Added --never-drop-acls, ACL entry dropping. Final for 0.13.2
git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@442 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
Diffstat (limited to 'rdiff-backup/rdiff_backup')
-rw-r--r-- | rdiff-backup/rdiff_backup/Globals.py | 3 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/Main.py | 14 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/eas_acls.py | 71 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/rpath.py | 16 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/user_group.py | 28 |
5 files changed, 110 insertions, 22 deletions
diff --git a/rdiff-backup/rdiff_backup/Globals.py b/rdiff-backup/rdiff_backup/Globals.py index e40fd0a..de334f7 100644 --- a/rdiff-backup/rdiff_backup/Globals.py +++ b/rdiff-backup/rdiff_backup/Globals.py @@ -207,6 +207,9 @@ compare_inode = 1 # guarantee that any changes have been committed to disk. fsync_directories = 1 +# If set, exit with error instead of dropping ACLs or ACL entries. +never_drop_acls = None + def get(name): """Return the value of something in this module""" diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py index 68832d3..d1c7b97 100644 --- a/rdiff-backup/rdiff_backup/Main.py +++ b/rdiff-backup/rdiff_backup/Main.py @@ -57,9 +57,10 @@ def parse_cmdlineoptions(arglist): "include=", "include-filelist=", "include-filelist-stdin", "include-globbing-filelist=", "include-regexp=", "list-at-time=", "list-changed-since=", "list-increments", - "list-increment-sizes", "no-compare-inode", - "no-compression", "no-compression-regexp=", - "no-file-statistics", "no-hard-links", "null-separator", + "list-increment-sizes", "never-drop-acls", + "no-compare-inode", "no-compression", + "no-compression-regexp=", "no-file-statistics", + "no-hard-links", "null-separator", "override-chars-to-quote=", "parsable-output", "print-statistics", "remote-cmd=", "remote-schema=", "remove-older-than=", "restore-as-of=", "restrict=", @@ -109,6 +110,7 @@ def parse_cmdlineoptions(arglist): elif opt == "-l" or opt == "--list-increments": 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-compare-inode": Globals.set("compare_inode", 0) elif opt == "--no-compression": Globals.set("compression", None) elif opt == "--no-compression-regexp": @@ -368,6 +370,9 @@ def backup_set_fs_globals(rpin, rpout): 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: + Log.FatalError("--never-drop-acls specified, but ACL support\n" + "disabled on destination filesystem") update_bool_global('read_acls', src_fsa.acls) update_bool_global('read_eas', src_fsa.eas) @@ -462,6 +467,9 @@ 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: + Log.FatalError("--never-drop-acls specified, but ACL support\n" + "disabled on destination filesystem") update_bool_global('read_acls', target_fsa.acls) update_bool_global('write_acls', target_fsa.acls) diff --git a/rdiff-backup/rdiff_backup/eas_acls.py b/rdiff-backup/rdiff_backup/eas_acls.py index 442bdbd..e8258ad 100644 --- a/rdiff-backup/rdiff_backup/eas_acls.py +++ b/rdiff-backup/rdiff_backup/eas_acls.py @@ -33,6 +33,11 @@ except ImportError: pass import static, Globals, metadata, connection, rorpiter, log, C, \ rpath, user_group +# When an ACL gets dropped, put name in dropped_acl_names. This is +# only used so that only the first dropped ACL for any given name +# triggers a warning. +dropped_acl_names = {} + class ExtendedAttributes: """Hold a file's extended attribute information""" def __init__(self, index, attr_dict = None): @@ -288,6 +293,7 @@ class AccessControlLists: """ assert isinstance(acl, self.__class__) if self.index != acl.index: return 0 + if self.is_basic(): return acl.is_basic() return (self.cmp_entry_list(self.entry_list, acl.entry_list) and self.cmp_entry_list(self.default_entry_list, acl.default_entry_list)) @@ -327,21 +333,23 @@ class AccessControlLists: self.entry_list, self.default_entry_list = \ rp.conn.eas_acls.get_acl_lists_from_rp(rp) - def write_to_rp(self, rp): + def write_to_rp(self, rp, map_names = 1): """Write current access control list to RPath rp""" rp.conn.eas_acls.set_rp_acl(rp, self.entry_list, - self.default_entry_list) + self.default_entry_list, map_names) -def set_rp_acl(rp, entry_list = None, default_entry_list = None): +def set_rp_acl(rp, entry_list = None, default_entry_list = None, + map_names = 1): """Set given rp with ACL that acl_text defines. rp should be local""" assert rp.conn is Globals.local_connection - if entry_list: acl = list_to_acl(entry_list) + if entry_list: acl = list_to_acl(entry_list, map_names) else: acl = posix1e.ACL() acl.applyto(rp.path) if rp.isdir(): - if default_entry_list: def_acl = list_to_acl(default_entry_list) + if default_entry_list: + def_acl = list_to_acl(default_entry_list, map_names) else: def_acl = posix1e.ACL() def_acl.applyto(rp.path, posix1e.ACL_TYPE_DEFAULT) @@ -388,18 +396,55 @@ def acl_to_list(acl): return (entry.tag_type, owner_pair, perms) return map(entry_to_tuple, acl) -def list_to_acl(entry_list): - """Return posix1e.ACL object from list representation""" +def list_to_acl(entry_list, map_names = 1): + """Return posix1e.ACL object from list representation + + If map_names is true, use user_group to update the names for the + current system, and drop if not available. Otherwise just use the + same id. + + """ + def warn_drop(name): + """Warn about acl with name getting dropped""" + global dropped_acl_names + if Globals.never_drop_acls: + log.Log.FatalError( +"--never-drop-acls specified but cannot map name\n" +"%s occurring inside an ACL." % (name,)) + if dropped_acl_names.has_key(name): return + log.Log("Warning: name %s not found on system, dropping ACL entry.\n" + "Further ACL entries dropped with this name will not " + "trigger further warnings" % (name,), 2) + dropped_acl_names[name] = name + + def map_id_name(owner_pair, group = None): + """Return id of mapped id and user given original owner_pair""" + id, name = owner_pair + Map = group and user_group.GroupMap or user_group.UserMap + if name: return Map.get_id_from_name(name) + else: + assert id is not None + return Map.get_id_from_id(id) + acl = posix1e.ACL() for tag, owner_pair, perms in entry_list: - entry = posix1e.Entry(acl) - entry.tag_type = tag + id = None if owner_pair: - if tag == posix1e.ACL_USER: - entry.qualifier = user_group.UserMap.get_id(*owner_pair) + if map_names: + if tag == posix1e.ACL_USER: id = map_id_name(owner_pair, 0) + else: + assert tag == posix1e.ACL_GROUP, (tag, owner_pair, perms) + id = map_id_name(owner_pair, 1) + if id is None: + warn_drop(owner_pair[1]) + continue else: - assert tag == posix1e.ACL_GROUP, (tag, owner_pair, perms) - entry.qualifier = user_group.GroupMap.get_id(*owner_pair) + assert owner_pair[0] is not None, (tag, owner_pair, perms) + id = owner_pair[0] + + entry = posix1e.Entry(acl) + entry.tag_type = tag + if id is not None: entry.qualifier = id entry.permset.read = perms >> 2 entry.permset.write = perms >> 1 & 1 entry.permset.execute = perms & 1 diff --git a/rdiff-backup/rdiff_backup/rpath.py b/rdiff-backup/rdiff_backup/rpath.py index de2d2f7..a834c5c 100644 --- a/rdiff-backup/rdiff_backup/rpath.py +++ b/rdiff-backup/rdiff_backup/rpath.py @@ -179,8 +179,7 @@ def copy_attribs_inc(rpin, rpout): if rpin.isdir() and not rpout.isdir(): rpout.chmod(rpin.getperms() & 0777) else: rpout.chmod(rpin.getperms()) - if Globals.write_acls and not (rpin.isdir() and not rpout.isdir()): - rpout.write_acl(rpin.get_acl()) + if Globals.write_acls: rpout.write_acl(rpin.get_acl(), map_names = 0) if not rpin.isdev(): rpout.setmtime(rpin.getmtime()) def cmp_attribs(rp1, rp2): @@ -302,7 +301,8 @@ class RORPath: not Globals.compare_inode or not Globals.preserve_hardlinks)): pass elif (not other.data.has_key(key) or - self.data[key] != other.data[key]): return None + self.data[key] != other.data[key]): + return None return 1 def equal_loose(self, other): @@ -1035,9 +1035,13 @@ class RPath(RORPath): except KeyError: acl = self.data['acl'] = acl_get(self) return acl - def write_acl(self, acl): - """Change access control list of rp""" - acl.write_to_rp(self) + def write_acl(self, acl, map_names = 1): + """Change access control list of rp + + If map_names is true, map the ids in acl by user/group names. + + """ + acl.write_to_rp(self, map_names) self.data['acl'] = acl def get_ea(self): diff --git a/rdiff-backup/rdiff_backup/user_group.py b/rdiff-backup/rdiff_backup/user_group.py index 896e60e..b34f0cf 100644 --- a/rdiff-backup/rdiff_backup/user_group.py +++ b/rdiff-backup/rdiff_backup/user_group.py @@ -60,6 +60,16 @@ def gid2gname(gid): gid2gname_dict[gid] = gname return gname +def uname2uid(uname): + """Given uname, return uid or None if cannot find""" + try: uname = pwd.getpwnam(uname)[2] + except KeyError: return None + +def gname2gid(gname): + """Given gname, return gid or None if cannot find""" + try: gname = grp.getgrnam(gname)[2] + except KeyError: return None + class Map: """Used for mapping names and id on source side to dest side""" @@ -77,12 +87,25 @@ class Map: self.name2id_dict[name] = out_id return out_id + def get_id_from_name(self, name): + """Return mapped id from name only, or None if cannot""" + try: return self.name2id_dict[name] + except KeyError: + out_id = self.find_id_from_name(name) + self.name2id_dict[name] = out_id + return out_id + def get_id_from_id(self, id): return id def find_id(self, id, name): """Find the proper id to use with given id and name""" try: return self.name2id_func(name) except KeyError: return id + + def find_id_from_name(self, name): + """Look up proper id to use with name, or None""" + try: return self.name2id_func(name) + except KeyError: return None class DefinedMap(Map): """Map names and ids on source side to appropriate ids on dest side @@ -134,6 +157,11 @@ class DefinedMap(Map): try: return self.id_mapping_dict[id] except KeyError: return Map.find_id(self, id, name) + def find_id_from_name(self, name): + """Find id to map name to, or None if we can't""" + try: return self.name_mapping_dict[name] + except KeyError: return Map.find_id_from_name(name) + def init_user_mapping(mapping_string = None): """Initialize user mapping with given mapping string or None""" global UserMap |