From 6220c4aea1012ea47d87ced9dc38639a8694a101 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Sat, 22 Jul 2017 06:47:57 -0700 Subject: [PATCH] Some enhancements to the symbolic_modes change * Mark _symbolic_mode_to_octal and helper functions as classmethod and staticmethod These helpers should be made toplevel functions in their own module. For now, make them staticmethod/classmethod so that they can be used (and tested) without instantiating an AnsibleModule. * Move regex compilation out of loops * Get rid of python-2.4 compat --- lib/ansible/module_utils/basic.py | 71 ++++++++++++++++--------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py index 39c118da30c..2c5cc4a7ccc 100644 --- a/lib/ansible/module_utils/basic.py +++ b/lib/ansible/module_utils/basic.py @@ -243,10 +243,15 @@ FILE_COMMON_ARGUMENTS = dict( PASSWD_ARG_RE = re.compile(r'^[-]{0,2}pass[-]?(word|wd)?') -# Can't use 07777 on Python 3, can't use 0o7777 on Python 2.4 -PERM_BITS = int('07777', 8) # file mode permission bits -EXEC_PERM_BITS = int('00111', 8) # execute permission bits -DEFAULT_PERM = int('0666', 8) # default file permission bits +# Used for parsing symbolic file perms +MODE_OPERATOR_RE = re.compile(r'[+=-]') +USERS_RE = re.compile(r'[^ugo]') +PERMS_RE = re.compile(r'[^rwxXstugo]') + + +PERM_BITS = 0o7777 # file mode permission bits +EXEC_PERM_BITS = 0o0111 # execute permission bits +DEFAULT_PERM = 0o0666 # default file permission bits def get_platform(): @@ -1300,21 +1305,24 @@ class AnsibleModule(object): pass return output - def _symbolic_mode_to_octal(self, path_stat, symbolic_mode): - new_mode = stat.S_IMODE(path_stat.st_mode) + @classmethod + def _symbolic_mode_to_octal(cls, path_stat, symbolic_mode): + """ + This enables symbolic chmod string parsing as stated in the chmod man-page - # Fix for issue #14634 - # This enables symbolic chmod string parsing as stated in the chmod - # man-page. This includes things like: "u=rw-x+X,g=r-x+X,o=r-x+X" + This includes things like: "u=rw-x+X,g=r-x+X,o=r-x+X" + """ + + new_mode = stat.S_IMODE(path_stat.st_mode) # Now parse all symbolic modes for mode in symbolic_mode.split(','): # Per single mode. This always contains a '+', '-' or '=' # Split it on that - permlist = re.split(r'[+=-]', mode) + permlist = MODE_OPERATOR_RE.split(mode) # And find all the operators - opers = re.findall(r'[+=-]', mode) + opers = MODE_OPERATOR_RE.findall(mode) # The user(s) where it's all about is the first element in the # 'permlist' list. Take that and remove it from the list. @@ -1326,23 +1334,24 @@ class AnsibleModule(object): # Check if there are illegal characters in the user list # They can end up in 'users' because they are not split - if re.match(r'[^ugo]', users): + if USERS_RE.match(users): raise ValueError("bad symbolic permission for mode: %s" % mode) # Now we have two list of equal length, one contains the requested # permissions and one with the corresponding operators. for idx, perms in enumerate(permlist): # Check if there are illegal characters in the permissions - if re.match(r'[^rwxXstugo]', perms): + if PERMS_RE.match(perms): raise ValueError("bad symbolic permission for mode: %s" % mode) for user in users: - mode_to_apply = self._get_octal_mode_from_symbolic_perms(path_stat, user, perms, use_umask) - new_mode = self._apply_operation_to_mode(user, opers[idx], mode_to_apply, new_mode) + mode_to_apply = cls._get_octal_mode_from_symbolic_perms(path_stat, user, perms, use_umask) + new_mode = cls._apply_operation_to_mode(user, opers[idx], mode_to_apply, new_mode) return new_mode - def _apply_operation_to_mode(self, user, operator, mode_to_apply, current_mode): + @staticmethod + def _apply_operation_to_mode(user, operator, mode_to_apply, current_mode): if operator == '=': if user == 'u': mask = stat.S_IRWXU | stat.S_ISUID @@ -1360,14 +1369,8 @@ class AnsibleModule(object): new_mode = current_mode - (current_mode & mode_to_apply) return new_mode - def _get_octal_mode_from_symbolic_perms(self, path_stat, user, perms, use_umask): - def _apply_umask(perm, use_umask, rev_umask): - """Simple function to 'fake' ternary if statement""" - if use_umask: - return perm & rev_umask - else: - return perm - + @staticmethod + def _get_octal_mode_from_symbolic_perms(path_stat, user, perms, use_umask): prev_mode = stat.S_IMODE(path_stat.st_mode) is_directory = stat.S_ISDIR(path_stat.st_mode) @@ -1379,7 +1382,7 @@ class AnsibleModule(object): # We also need the "reversed umask" for masking umask = os.umask(0) os.umask(umask) - rev_umask = PERM_BITS - umask + rev_umask = umask ^ PERM_BITS # Permission bits constants documented at: # http://docs.python.org/2/library/stat.html#stat.S_ISUID @@ -1398,27 +1401,27 @@ class AnsibleModule(object): user_perms_to_modes = { 'u': { - 'r': _apply_umask(stat.S_IRUSR, use_umask, rev_umask), - 'w': _apply_umask(stat.S_IWUSR, use_umask, rev_umask), - 'x': _apply_umask(stat.S_IXUSR, use_umask, rev_umask), + 'r': rev_umask & stat.S_IRUSR if use_umask else stat.S_IRUSR, + 'w': rev_umask & stat.S_IWUSR if use_umask else stat.S_IWUSR, + 'x': rev_umask & stat.S_IXUSR if use_umask else stat.S_IXUSR, 's': stat.S_ISUID, 't': 0, 'u': prev_mode & stat.S_IRWXU, 'g': (prev_mode & stat.S_IRWXG) << 3, 'o': (prev_mode & stat.S_IRWXO) << 6}, 'g': { - 'r': _apply_umask(stat.S_IRGRP, use_umask, rev_umask), - 'w': _apply_umask(stat.S_IWGRP, use_umask, rev_umask), - 'x': _apply_umask(stat.S_IXGRP, use_umask, rev_umask), + 'r': rev_umask & stat.S_IRGRP if use_umask else stat.S_IRGRP, + 'w': rev_umask & stat.S_IWGRP if use_umask else stat.S_IWGRP, + 'x': rev_umask & stat.S_IXGRP if use_umask else stat.S_IXGRP, 's': stat.S_ISGID, 't': 0, 'u': (prev_mode & stat.S_IRWXU) >> 3, 'g': prev_mode & stat.S_IRWXG, 'o': (prev_mode & stat.S_IRWXO) << 3}, 'o': { - 'r': _apply_umask(stat.S_IROTH, use_umask, rev_umask), - 'w': _apply_umask(stat.S_IWOTH, use_umask, rev_umask), - 'x': _apply_umask(stat.S_IXOTH, use_umask, rev_umask), + 'r': rev_umask & stat.S_IROTH if use_umask else stat.S_IROTH, + 'w': rev_umask & stat.S_IWOTH if use_umask else stat.S_IWOTH, + 'x': rev_umask & stat.S_IXOTH if use_umask else stat.S_IXOTH, 's': 0, 't': stat.S_ISVTX, 'u': (prev_mode & stat.S_IRWXU) >> 6,