From 243aea45cea543fc1ef7c43d380a68aa1c7b338a Mon Sep 17 00:00:00 2001 From: Sean Reifschneider Date: Mon, 10 Apr 2023 16:29:10 -0600 Subject: Symbolic modes with X or =[ugo] always use original mode (#80132) * Symbolic modes with X or =[ugo] always use original mode (Fixes #80128) Here's what's happening, by way of this mode example: u=,u=rX At the first step in the loop, the "u" bits of are set to 0. On the next step in the loop, the current stat of the filesystem object is used to determine X, not the "new_mode" in the previous iteration of the loop. So while most operations kind of operate left to right, "X" is always going back to the original file to determine whether to set x bit. The Linux "chmod" (the only one I've tested) doesn't operate this way. In it, "X" operates on the current state the loop understands it is in, based on previous operations (and starting with the file permissions). This is an issue with "X" and any of the "=[ugo]" settings, because they are lookups. For example, if a file is 755 and you do "ug=rx,o=u", file module produces 0557 and chmod produces 0555. This really becomes a problem when you want to recursively change a directory of files, and the files are currently 755, but you want to change the directory to 750 and the files to 640. In chmod you can do "a=,ug=rX,u+w" (or "a=,u=rwX,g=rX"), and have it apply equally to the directory and the files. I can't come up with a single way in the ansible file module to deterministically, recursively, set a directory to 750 and the contents to 640 no matter what the current permissions are, as the code currently is. The fix is to pass in "new_mode" to _get_octal_mode_from_symbolic_perms in lib/ansible/module_utils/basic.py inside _symbolic_mode_to_octal. And then take "new_mode" as an argument and use it instead of the filesystem object stat.st_mode value. * Fixing my new unit test, fixing bug in test comments --- lib/ansible/module_utils/basic.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py index 98ce69f586..161d6e79e2 100644 --- a/lib/ansible/module_utils/basic.py +++ b/lib/ansible/module_utils/basic.py @@ -1074,7 +1074,7 @@ class AnsibleModule(object): raise ValueError("bad symbolic permission for mode: %s" % mode) for user in users: - mode_to_apply = cls._get_octal_mode_from_symbolic_perms(path_stat, user, perms, use_umask) + mode_to_apply = cls._get_octal_mode_from_symbolic_perms(path_stat, user, perms, use_umask, new_mode) new_mode = cls._apply_operation_to_mode(user, opers[idx], mode_to_apply, new_mode) return new_mode @@ -1099,9 +1099,9 @@ class AnsibleModule(object): return new_mode @staticmethod - def _get_octal_mode_from_symbolic_perms(path_stat, user, perms, use_umask): - prev_mode = stat.S_IMODE(path_stat.st_mode) - + def _get_octal_mode_from_symbolic_perms(path_stat, user, perms, use_umask, prev_mode=None): + if prev_mode is None: + prev_mode = stat.S_IMODE(path_stat.st_mode) is_directory = stat.S_ISDIR(path_stat.st_mode) has_x_permissions = (prev_mode & EXEC_PERM_BITS) > 0 apply_X_permission = is_directory or has_x_permissions -- cgit v1.2.1