Repository: ansible/ansible
Pull request: win_find - Fix deduped files mistaken for directories
##### SUMMARY
Fixes: #58511

##### ISSUE TYPE

- Bugfix Pull Request
##### COMPONENT NAME
win_find.ps1

##### ADDITIONAL INFORMATION
This was a fun one


Problem statement:
win_find module not returning deduplicated files


##### SUMMARY
Using the win_find module against a Windows share it is not returning some files. In this case .exe files.

The .exe files are deduplicated and looking at their properties in Windows explorer, they appear as something like this:

'Size: 100MB'
'Size on disk: 0 bytes'.

win_find is successfully returning other (non deduplicated) files from the same directory and it is showing the correct number of files in the directory (the number of files including the deduplicated .exe files).

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
win_find.py

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.8.1
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /root/playbooks/ansible-venv/lib/python2.7/site-packages/ansible
  executable location = /root/playbooks/ansible-venv/bin/ansible
  python version = 2.7.5 (default, Jun 11 2019, 12:19:05) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below

```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->
Ansible Server: RHEL7
Target Host: Win20126
Share Server: Win2016

##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
   - name: Get list of exes from share with deduplication
     debugger: always
     win_find:
      paths: \\server\share\
      patterns: '*.exe' # When uncommented this returns no files from the share. 
      # When commented it returns everything but the deduplicated files (exes in this case)
      # The debug output then shows the list of files found plus; "examined": 29, "matched": 13
      # Still missing the exe files from the output...
      user_name: user
      user_password: pass
     register: list_of_exes
     become: true
     become_method: runas
     vars:
      ansible_become_user: '{{ ansible_user }}'
      ansible_become_pass: '{{ ansible_password }}'

   - debug:
      var: list_of_exes
```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
Would expect the results to include the deduplicated files
```
TASK [debug] ********************************************************************************************************
ok: [169.254.0.1] => {
    "list_of_exes": {
        "changed": false,
        "examined": 29,
        "failed": false,
        "files": [...],
        "matched": 13
    }
}
```

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
Playbook executes ok but does not return the deduplicated files.
<!--- Paste verbatim command output between quotes -->
```paste below
TASK [debug] ********************************************************************************************************
ok: [169.254.0.1] => {
    "list_of_exes": {
        "changed": false,
        "examined": 29,
        "failed": false,
        "files": [],
        "matched": 0
    }
}
```

Hints from issue discussion:
Files identified in the description:
* [`lib/ansible/modules/windows/win_find.py`](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/windows/win_find.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @ShachafGoldstein @if-meaton @jborean93 @jhawkesworth @nitzmahone
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Please add a verbose `-vvv` output of the playbook
Verbose output as requested. Ip addresses, file names and computers etc have been changed in the output, and I've removed some of the repeated file results.

https://gist.github.com/MarkJB/b84c8fa5c76e69bdeecaa28b5f8e8f8f


The output you gave here is a working one... not the case for what you wrote in the description
> The output you gave here is a working one... not the case for what you wrote in the description

The output is from the playbook that does not return deduplicated exes.

If I copy some of the files into a local folder or into another folder on the share then the same playbook returns the exes.
I ran the playbook with verbose output with the `patterns: '*.exe'` line commented out so the output more clearly showed that it does find some files, just not the deduplicated ones.
Ok found the issue, deduped files have  SparseFile, ReparsePoint in their attributes hich makes their info be isdir=true which in turn fails n assert-filetype
https://github.com/ansible/ansible/blob/737dbefd78cab9a4cad8ea32237fe9fc88798258/lib/ansible/modules/windows/win_find.ps1#L244-L254
can you test https://github.com/ansible/ansible/pull/58680?
Thanks, that works but took ~40s vs ~3s without the fix. (I've run it a few times now and seems to be consistently around 40s)

The results show all the expected files:

```ok: [127.0.0.1] => {
    "list_of_installers": {
        "changed": false,
        "examined": 29,
        "failed": false,
        "files": [
            {
                "attributes": "Archive, SparseFile, ReparsePoint",
                "checksum": "",
                "creationtime": 1550848790.6796088,
                "extension": ".exe",
                "filename": "file1.exe",
                "isarchive": true,
                "isdir": false,
                "ishidden": false,
                "islnk": false,
                "isreadonly": false,
                "isshared": false,
                "lastaccesstime": 1550848788.394998,
                "lastwritetime": 1550848790.7662134,
                "owner": "",
                "path": "\\\\shares.local\\folder\\file1.exe",
                "size": 12345
            },
            ... a bunch more output of files found ...
            {
                "attributes": "Archive, SparseFile, ReparsePoint",
                "checksum": "",
                "creationtime": 1556647413.7757668,
                "extension": ".exe",
                "filename": "file2.exe",
                "isarchive": true,
                "isdir": false,
                "ishidden": false,
                "islnk": false,
                "isreadonly": false,
                "isshared": false,
                "lastaccesstime": 1556647412.6277752,
                "lastwritetime": 1556647413.8913832,
                "owner": "",
                "path": "\\\\shares.local\\folder\\file2.exe",
                "size": 12345
            }
        ],
        "matched": 28
    }
}```

Commit message: Update win_find.py

Patch to review:
diff --git a/changelogs/fragments/win_find-fix-ignore-of-deduped-files.yml b/changelogs/fragments/win_find-fix-ignore-of-deduped-files.yml
new file mode 100644
index 00000000000000..1f976214337be3
--- /dev/null
+++ b/changelogs/fragments/win_find-fix-ignore-of-deduped-files.yml
@@ -0,0 +1,2 @@
+bugfixes:
+  - "win_find - Fix deduped files mistaken for directories (https://github.com/ansible/ansible/issues/58511)"
\ No newline at end of file
diff --git a/lib/ansible/modules/windows/win_find.ps1 b/lib/ansible/modules/windows/win_find.ps1
index da8c62842de452..19313b7b1445c7 100644
--- a/lib/ansible/modules/windows/win_find.ps1
+++ b/lib/ansible/modules/windows/win_find.ps1
@@ -244,13 +244,16 @@ Function Get-FileStat($file) {
     if ($attributes -contains 'ReparsePoint') {
         # TODO: Find a way to differenciate between soft and junction links
         $islnk = $true
-        $isdir = $true
 
-        # Try and get the symlink source, can result in failure if link is broken
-        try {
-            $lnk_source = [Ansible.Command.SymLinkHelper]::GetSymbolicLinkTarget($file)
-            $file_stat.lnk_source = $lnk_source
-        } catch {}
+        if ($attributes -contains 'Directory') {
+            $isdir = $true
+
+            # Try and get the symlink source, can result in failure if link is broken
+            try {
+                $lnk_source = [Ansible.Command.SymLinkHelper]::GetSymbolicLinkTarget($file)
+                $file_stat.lnk_source = $lnk_source
+            } catch {}
+        }
     } elseif ($file.PSIsContainer) {
         $isdir = $true
 
diff --git a/lib/ansible/modules/windows/win_find.py b/lib/ansible/modules/windows/win_find.py
index 31ffb799b668bc..d57b876e0e6868 100644
--- a/lib/ansible/modules/windows/win_find.py
+++ b/lib/ansible/modules/windows/win_find.py
@@ -256,7 +256,7 @@
             sample: true
         islnk:
             description: If the path is a symbolic link or junction or not.
-            returned: success, path exists
+            returned: success, path exists or deduped files
             type: bool
             sample: true
         isreadonly:


Code review comments:
diff_hunk: @@ -244,13 +244,16 @@ Function Get-FileStat($file) {
     if ($attributes -contains 'ReparsePoint') {
         # TODO: Find a way to differenciate between soft and junction links
         $islnk = $true
-        $isdir = $true
 
-        # Try and get the symlink source, can result in failure if link is broken
-        try {
-            $lnk_source = [Ansible.Command.SymLinkHelper]::GetSymbolicLinkTarget($file)
-            $file_stat.lnk_source = $lnk_source
-        } catch {}
+        if ($attributes -contains 'Directory') {
+            $isdir = $true
+
+            # Try and get the symlink source, can result in failure if link is broken
+            try {
path: lib/ansible/modules/windows/win_find.ps1
text: @user1:
We should still have this outside of the Directory check, you can have a file symbolic link.

@author:
the original code assumed all links are directories, maybe changing the assumption should come in a diffreant PR?

@author:
NVM - i see what you mean, ill change it