#14634: symbolic permissions error (#14994)

This fixes the symbolic notation of the chmod modes, as stated in the man page of chmod (in Linux). This also takes into account that chmod a+x is different from chmod +x. As the second one should take the current umask into account.

Fixes #14634
pull/27235/head
Ton Kersten 7 years ago committed by Toshio Kuratomi
parent 4fd7ab11ff
commit 134852e686

@ -1303,22 +1303,43 @@ class AnsibleModule(object):
def _symbolic_mode_to_octal(self, path_stat, symbolic_mode): def _symbolic_mode_to_octal(self, path_stat, symbolic_mode):
new_mode = stat.S_IMODE(path_stat.st_mode) new_mode = stat.S_IMODE(path_stat.st_mode)
mode_re = re.compile(r'^(?P<users>[ugoa]+)(?P<operator>[-+=])(?P<perms>[rwxXst-]*|[ugo])$') # 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"
# Now parse all symbolic modes
for mode in symbolic_mode.split(','): for mode in symbolic_mode.split(','):
match = mode_re.match(mode) # Per single mode. This always contains a '+', '-' or '='
if match: # Split it on that
users = match.group('users') permlist = re.split(r'[+=-]', mode)
operator = match.group('operator')
perms = match.group('perms') # And find all the operators
opers = re.findall(r'[+=-]', 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.
# An empty user or 'a' means 'all'.
users = permlist.pop(0)
use_umask = (users == '')
if users == 'a' or users == '':
users = 'ugo'
# 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):
raise ValueError("bad symbolic permission for mode: %s" % mode)
if users == 'a': # Now we have two list of equal length, one contains the requested
users = 'ugo' # 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):
raise ValueError("bad symbolic permission for mode: %s" % mode)
for user in users: for user in users:
mode_to_apply = self._get_octal_mode_from_symbolic_perms(path_stat, user, perms) mode_to_apply = self._get_octal_mode_from_symbolic_perms(path_stat, user, perms, use_umask)
new_mode = self._apply_operation_to_mode(user, operator, mode_to_apply, new_mode) new_mode = self._apply_operation_to_mode(user, opers[idx], mode_to_apply, new_mode)
else:
raise ValueError("bad symbolic permission for mode: %s" % mode)
return new_mode return new_mode
def _apply_operation_to_mode(self, user, operator, mode_to_apply, current_mode): def _apply_operation_to_mode(self, user, operator, mode_to_apply, current_mode):
@ -1339,13 +1360,27 @@ class AnsibleModule(object):
new_mode = current_mode - (current_mode & mode_to_apply) new_mode = current_mode - (current_mode & mode_to_apply)
return new_mode return new_mode
def _get_octal_mode_from_symbolic_perms(self, path_stat, user, perms): 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
prev_mode = stat.S_IMODE(path_stat.st_mode) prev_mode = stat.S_IMODE(path_stat.st_mode)
is_directory = stat.S_ISDIR(path_stat.st_mode) is_directory = stat.S_ISDIR(path_stat.st_mode)
has_x_permissions = (prev_mode & EXEC_PERM_BITS) > 0 has_x_permissions = (prev_mode & EXEC_PERM_BITS) > 0
apply_X_permission = is_directory or has_x_permissions apply_X_permission = is_directory or has_x_permissions
# Get the umask, if the 'user' part is empty, the effect is as if (a) were
# given, but bits that are set in the umask are not affected.
# We also need the "reversed umask" for masking
umask = os.umask(0)
os.umask(umask)
rev_umask = PERM_BITS - umask
# Permission bits constants documented at: # Permission bits constants documented at:
# http://docs.python.org/2/library/stat.html#stat.S_ISUID # http://docs.python.org/2/library/stat.html#stat.S_ISUID
if apply_X_permission: if apply_X_permission:
@ -1363,35 +1398,32 @@ class AnsibleModule(object):
user_perms_to_modes = { user_perms_to_modes = {
'u': { 'u': {
'r': stat.S_IRUSR, 'r': _apply_umask(stat.S_IRUSR, use_umask, rev_umask),
'w': stat.S_IWUSR, 'w': _apply_umask(stat.S_IWUSR, use_umask, rev_umask),
'x': stat.S_IXUSR, 'x': _apply_umask(stat.S_IXUSR, use_umask, rev_umask),
's': stat.S_ISUID, 's': stat.S_ISUID,
't': 0, 't': 0,
'u': prev_mode & stat.S_IRWXU, 'u': prev_mode & stat.S_IRWXU,
'g': (prev_mode & stat.S_IRWXG) << 3, 'g': (prev_mode & stat.S_IRWXG) << 3,
'o': (prev_mode & stat.S_IRWXO) << 6, 'o': (prev_mode & stat.S_IRWXO) << 6},
},
'g': { 'g': {
'r': stat.S_IRGRP, 'r': _apply_umask(stat.S_IRGRP, use_umask, rev_umask),
'w': stat.S_IWGRP, 'w': _apply_umask(stat.S_IWGRP, use_umask, rev_umask),
'x': stat.S_IXGRP, 'x': _apply_umask(stat.S_IXGRP, use_umask, rev_umask),
's': stat.S_ISGID, 's': stat.S_ISGID,
't': 0, 't': 0,
'u': (prev_mode & stat.S_IRWXU) >> 3, 'u': (prev_mode & stat.S_IRWXU) >> 3,
'g': prev_mode & stat.S_IRWXG, 'g': prev_mode & stat.S_IRWXG,
'o': (prev_mode & stat.S_IRWXO) << 3, 'o': (prev_mode & stat.S_IRWXO) << 3},
},
'o': { 'o': {
'r': stat.S_IROTH, 'r': _apply_umask(stat.S_IROTH, use_umask, rev_umask),
'w': stat.S_IWOTH, 'w': _apply_umask(stat.S_IWOTH, use_umask, rev_umask),
'x': stat.S_IXOTH, 'x': _apply_umask(stat.S_IXOTH, use_umask, rev_umask),
's': 0, 's': 0,
't': stat.S_ISVTX, 't': stat.S_ISVTX,
'u': (prev_mode & stat.S_IRWXU) >> 6, 'u': (prev_mode & stat.S_IRWXU) >> 6,
'g': (prev_mode & stat.S_IRWXG) >> 3, 'g': (prev_mode & stat.S_IRWXG) >> 3,
'o': prev_mode & stat.S_IRWXO, 'o': prev_mode & stat.S_IRWXO},
}
} }
# Insert X_perms into user_perms_to_modes # Insert X_perms into user_perms_to_modes

Loading…
Cancel
Save