diff --git a/changelogs/fragments/file-nonexitent-link-recurse.yaml b/changelogs/fragments/file-nonexitent-link-recurse.yaml new file mode 100644 index 00000000000..f1183d6fbfe --- /dev/null +++ b/changelogs/fragments/file-nonexitent-link-recurse.yaml @@ -0,0 +1,5 @@ +--- +bugfixes: + - file module - Fix error when recursively assigning permissions and + a symlink to a nonexistent file is present in the directory tree + (https://github.com/ansible/ansible/issues/39456) diff --git a/lib/ansible/modules/files/file.py b/lib/ansible/modules/files/file.py index 53b4575e6b4..7f2b433ad1d 100644 --- a/lib/ansible/modules/files/file.py +++ b/lib/ansible/modules/files/file.py @@ -160,16 +160,23 @@ def recursive_set_attributes(module, b_path, follow, file_args): tmp_file_args['path'] = to_native(b_fsname, errors='surrogate_or_strict') changed |= module.set_fs_attributes_if_different(tmp_file_args, changed, expand=False) else: + # Change perms on the link tmp_file_args = file_args.copy() tmp_file_args['path'] = to_native(b_fsname, errors='surrogate_or_strict') changed |= module.set_fs_attributes_if_different(tmp_file_args, changed, expand=False) + if follow: b_fsname = os.path.join(b_root, os.readlink(b_fsname)) - if os.path.isdir(b_fsname): - changed |= recursive_set_attributes(module, b_fsname, follow, file_args) - tmp_file_args = file_args.copy() - tmp_file_args['path'] = to_native(b_fsname, errors='surrogate_or_strict') - changed |= module.set_fs_attributes_if_different(tmp_file_args, changed, expand=False) + # The link target could be nonexistent + if os.path.exists(b_fsname): + if os.path.isdir(b_fsname): + # Link is a directory so change perms on the directory's contents + changed |= recursive_set_attributes(module, b_fsname, follow, file_args) + + # Change perms on the file pointed to by the link + tmp_file_args = file_args.copy() + tmp_file_args['path'] = to_native(b_fsname, errors='surrogate_or_strict') + changed |= module.set_fs_attributes_if_different(tmp_file_args, changed, expand=False) return changed diff --git a/test/integration/targets/file/tasks/main.yml b/test/integration/targets/file/tasks/main.yml index b01df23e535..f80e138f94f 100644 --- a/test/integration/targets/file/tasks/main.yml +++ b/test/integration/targets/file/tasks/main.yml @@ -610,6 +610,9 @@ - name: create a symlink to the directory file: path={{output_dir}}/test_follow_rec/test_link_dir state=link src="../test_follow_rec_target_dir" +- name: create a symlink to a nonexistent file + file: path={{output_dir}}/test_follow_rec/nonexistent state=link src=does_not_exist force=True + - name: try to change permissions without following symlinks file: path={{output_dir}}/test_follow_rec follow=False mode="a-x" recurse=True