diff --git a/changelogs/fragments/73024-unarchive-apply-permissions-top-folder.yml b/changelogs/fragments/73024-unarchive-apply-permissions-top-folder.yml new file mode 100644 index 00000000000..5a9a87d05df --- /dev/null +++ b/changelogs/fragments/73024-unarchive-apply-permissions-top-folder.yml @@ -0,0 +1,2 @@ +bugfixes: + - unarchive - apply ``owner`` and ``group`` permissions to top folder (https://github.com/ansible/ansible/issues/35426) diff --git a/lib/ansible/modules/unarchive.py b/lib/ansible/modules/unarchive.py index 7d00f2425a8..3dccb219179 100644 --- a/lib/ansible/modules/unarchive.py +++ b/lib/ansible/modules/unarchive.py @@ -1044,6 +1044,7 @@ def main(): # Run only if we found differences (idempotence) or diff was missing if res_args.get('diff', True) and not module.check_mode: # do we need to change perms? + top_folders = [] for filename in handler.files_in_archive: file_args['path'] = os.path.join(b_dest, to_bytes(filename, errors='surrogate_or_strict')) @@ -1052,6 +1053,21 @@ def main(): except (IOError, OSError) as e: module.fail_json(msg="Unexpected error when accessing exploded file: %s" % to_native(e), **res_args) + if '/' in filename: + top_folder_path = filename.split('/')[0] + if top_folder_path not in top_folders: + top_folders.append(top_folder_path) + + # make sure top folders have the right permissions + # https://github.com/ansible/ansible/issues/35426 + if top_folders: + for f in top_folders: + file_args['path'] = "%s/%s" % (dest, f) + try: + res_args['changed'] = module.set_fs_attributes_if_different(file_args, res_args['changed'], expand=False) + except (IOError, OSError) as e: + module.fail_json(msg="Unexpected error when accessing exploded file: %s" % to_native(e), **res_args) + if module.params['list_files']: res_args['files'] = handler.files_in_archive diff --git a/test/integration/targets/unarchive/tasks/main.yml b/test/integration/targets/unarchive/tasks/main.yml index 60bdd1b08f3..148e583f2b0 100644 --- a/test/integration/targets/unarchive/tasks/main.yml +++ b/test/integration/targets/unarchive/tasks/main.yml @@ -19,3 +19,4 @@ - import_tasks: test_unprivileged_user.yml - import_tasks: test_different_language_var.yml - import_tasks: test_invalid_options.yml +- import_tasks: test_ownership_top_folder.yml diff --git a/test/integration/targets/unarchive/tasks/test_ownership_top_folder.yml b/test/integration/targets/unarchive/tasks/test_ownership_top_folder.yml new file mode 100644 index 00000000000..75bd125b6ba --- /dev/null +++ b/test/integration/targets/unarchive/tasks/test_ownership_top_folder.yml @@ -0,0 +1,73 @@ +- name: Create unarchivetest3 user + user: + name: unarchivetest3 + group: "{{ group_table[ansible_facts['distribution']] | default(omit) }}" + register: user + vars: + group_table: + MacOSX: staff + +- name: Test unarchiving as root and apply different ownership to top folder + become: yes + become_user: root + block: + + - name: Create top folder owned by root + file: + path: "{{ user.home }}/tarball-top-folder" + state: directory + owner: root + + - name: Add a file owned by root + copy: + src: foo.txt + dest: "{{ user.home }}/tarball-top-folder/foo-unarchive.txt" + mode: preserve + + - name: Create a tarball as root. This tarball won't list the top folder when doing "tar tvf test-tarball.tar.gz" + shell: tar -czf test-tarball.tar.gz tarball-top-folder/foo-unarchive.txt + args: + chdir: "{{ user.home }}" + creates: "{{ user.home }}/test-tarball.tar.gz" + + - name: Create unarchive destination folder in /home/unarchivetest3/unarchivetest3-unarchive + file: + path: "{{ user.home }}/unarchivetest3-unarchive" + state: directory + owner: unarchivetest3 + group: "{{ user.group }}" + + - name: unarchive the tarball as root. apply ownership for unarchivetest3 + unarchive: + src: "{{ user.home }}/test-tarball.tar.gz" + dest: "{{ user.home }}/unarchivetest3-unarchive" + remote_src: yes + list_files: True + owner: unarchivetest3 + group: "{{ user.group }}" + + - name: Stat the extracted top folder + stat: + path: "{{ user.home }}/unarchivetest3-unarchive/tarball-top-folder" + register: top_folder_info + + - name: verify that extracted top folder is owned by unarchivetest3 + assert: + that: + - top_folder_info.stat.pw_name == "unarchivetest3" + - top_folder_info.stat.gid == {{ user.group }} + + always: + - name: remove our unarchivetest3 user and files + user: + name: unarchivetest3 + state: absent + remove: yes + become: no + + - name: Remove user home directory on macOS + file: + path: /Users/unarchivetest3 + state: absent + become: no + when: ansible_facts.distribution == 'MacOSX'