diff options
Diffstat (limited to 'rdiff-backup/rdiff_backup/user_group.py')
-rw-r--r-- | rdiff-backup/rdiff_backup/user_group.py | 202 |
1 files changed, 116 insertions, 86 deletions
diff --git a/rdiff-backup/rdiff_backup/user_group.py b/rdiff-backup/rdiff_backup/user_group.py index 9209daf..186e1b6 100644 --- a/rdiff-backup/rdiff_backup/user_group.py +++ b/rdiff-backup/rdiff_backup/user_group.py @@ -25,13 +25,16 @@ this. On the destination connection only, if necessary have a separate dictionary of mappings, which specify how to map users/groups on one -connection to the users/groups on the other. +connection to the users/groups on the other. The UserMap and GroupMap +objects should only be used on the destination. """ import grp, pwd import log, Globals +############ "Private" section - don't use outside user_group ########### + # This should be set to the user UserMap class object if using # user-defined user mapping, and a Map class object otherwise. UserMap = None @@ -40,73 +43,55 @@ UserMap = None # user-defined group mapping, and a Map class object otherwise. GroupMap = None - +# Used to cache by uid2uname and gid2gname below uid2uname_dict = {}; gid2gname_dict = {} -def uid2uname(uid): - """Given uid, return uname or None if cannot find""" - try: return uid2uname_dict[uid] - except KeyError: - try: uname = pwd.getpwuid(uid)[0] - except (KeyError, OverflowError), e: uname = None - uid2uname_dict[uid] = uname - return uname - -def gid2gname(gid): - """Given gid, return group name or None if cannot find""" - try: return gid2gname_dict[gid] - except KeyError: - try: gname = grp.getgrgid(gid)[0] - except (KeyError, OverflowError), e: gname = None - gid2gname_dict[gid] = gname - return gname +uname2uid_dict = {} def uname2uid(uname): """Given uname, return uid or None if cannot find""" - try: uname = pwd.getpwnam(uname)[2] - except KeyError: return None + try: return uname2uid_dict[uname] + except KeyError: + try: uid = pwd.getpwnam(uname)[2] + except KeyError: uid = None + uname2uid_dict[uname] = uid + return uid +gname2gid_dict = {} def gname2gid(gname): """Given gname, return gid or None if cannot find""" - try: gname = grp.getgrnam(gname)[2] - except KeyError: return None + try: return gname2gid_dict[gname] + except KeyError: + try: gid = grp.getgrnam(gname)[2] + except KeyError: gid = None + gname2gid_dict[gname] = gid + return gid class Map: """Used for mapping names and id on source side to dest side""" - def __init__(self, name2id_func): - """Map initializer, set dictionaries""" - self.name2id_dict = {} - self.name2id_func = name2id_func + def __init__(self, is_user): + """Initialize, user is true for users, false for groups""" + self.name2id = (is_user and uname2uid) or gname2gid - def get_id(self, id, name = None): + def __call__(self, id, name = None): """Return mapped id from id and, if available, name""" - if not name: return self.get_id_from_id(id) - try: return self.name2id_dict[name] - except KeyError: - out_id = self.find_id(id, name) - 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 - + if not name: return id + newid = self.name2id(name) + if newid is None: return id + else: return newid + + def map_acl(self, id, name = None): + """Like get_id, but use this for ACLs. Return id or None + + Unlike ordinary user/group ownership, ACLs are not required + and can be dropped. If we cannot map the name over, return + None. + + """ + if not name: return id + return self.name2id(name) + + class DefinedMap(Map): """Map names and ids on source side to appropriate ids on dest side @@ -114,7 +99,7 @@ class DefinedMap(Map): supersedes Map. """ - def __init__(self, name2id_func, mapping_string): + def __init__(self, is_user, mapping_string): """Initialize object with given mapping string The mapping_string should consist of a number of lines, each which @@ -122,7 +107,7 @@ class DefinedMap(Map): mapping unless user is false, then do group. """ - Map.__init__(self, name2id_func) + Map.__init__(self, is_user) self.name_mapping_dict = {}; self.id_mapping_dict = {} for line in mapping_string.split('\n'): @@ -142,44 +127,89 @@ class DefinedMap(Map): """Return id of id_or_name, failing if cannot. Used in __init__""" try: return int(id_or_name) except ValueError: - try: id = self.name2id_func(id_or_name) + try: return self.name2id(id_or_name) except KeyError: log.Log.FatalError("Cannot get id for user or group name " + id_or_name) - return id - def get_id_from_id(self, id): return self.id_mapping_dict.get(id, id) + def __call__(self, id, name = None): + """Return new id given old id and name""" + newid = self.map_acl(id, name) + if newid is None: return id + else: return newid + + def map_acl(self, id, name = None): + """Return new id or None given old and name (used for ACLs)""" + if name: + try: return self.name_mapping_dict[name] + except KeyError: pass + newid = self.name2id(name) + if newid is not None: return newid + try: return self.id_mapping_dict[id] + except KeyError: return None + + +class NumericalMap: + """Simple Map replacement that just keeps numerical uid or gid""" + def __call__(self, id, name = None): return id + def map_acl(self, id, name = None): return id + - def find_id(self, id, name): - """Find proper id to use when source file has give id and name""" - try: return self.name_mapping_dict[name] - except KeyError: - try: return self.id_mapping_dict[id] - except KeyError: return Map.find_id(self, id, name) +############ Public section - don't use outside user_group ########### - 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""" +def uid2uname(uid): + """Given uid, return uname from passwd file, or None if cannot find""" + try: return uid2uname_dict[uid] + except KeyError: + try: uname = pwd.getpwuid(uid)[0] + except (KeyError, OverflowError), e: uname = None + uid2uname_dict[uid] = uname + return uname + +def gid2gname(gid): + """Given gid, return group name from group file or None if cannot find""" + try: return gid2gname_dict[gid] + except KeyError: + try: gname = grp.getgrgid(gid)[0] + except (KeyError, OverflowError), e: gname = None + gid2gname_dict[gid] = gname + return gname + + +def init_user_mapping(mapping_string = None, numerical_ids = None): + """Initialize user mapping with given mapping string + + If numerical_ids is set, just keep the same uid. If either + argument is None, default to preserving uname where possible. + + """ global UserMap - name2id_func = lambda name: pwd.getpwnam(name)[2] - if mapping_string: UserMap = DefinedMap(name2id_func, mapping_string) - else: UserMap = Map(name2id_func) + if numerical_ids: UserMap = NumericalMap() + elif mapping_string: UserMap = DefinedMap(1, mapping_string) + else: UserMap = Map(1) + +def init_group_mapping(mapping_string = None, numerical_ids = None): + """Initialize group mapping with given mapping string -def init_group_mapping(mapping_string = None): - """Initialize the group mapping dictionary with given mapping string""" + If numerical_ids is set, just keep the same gid. If either + argument is None, default to preserving gname where possible. + + """ global GroupMap - name2id_func = lambda name: grp.getgrnam(name)[2] - if mapping_string: GroupMap = DefinedMap(name2id_func, mapping_string) - else: GroupMap = Map(name2id_func) + if numerical_ids: GroupMap = NumericalMap() + elif mapping_string: GroupMap = DefinedMap(0, mapping_string) + else: GroupMap = Map(0) + - def map_rpath(rp): - """Return (uid, gid) of mapped ownership of given rpath""" - old_uid, old_gid = rp.getuidgid() - new_uid = UserMap.get_id(old_uid, rp.getuname()) - new_gid = GroupMap.get_id(old_gid, rp.getgname()) - return (new_uid, new_gid) + """Return mapped (newuid, newgid) from rpath's initial info + + This is the main function exported by the user_group module. Note + that it is connection specific. + + """ + uid, gid = rp.getuidgid() + uname, gname = rp.getuname(), rp.getgname() + return (UserMap(uid, uname), GroupMap(gid, gname)) + |