From 1c885cb84815c1005f340df34a40a5411b5fb5a4 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Thu, 9 Feb 2017 12:49:49 +1000 Subject: [PATCH] Added fix for win_file with broken symlinks (#19146) --- lib/ansible/modules/windows/win_file.ps1 | 48 ++++++++++++++++++- .../targets/win_file/tasks/main.yml | 18 +++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/lib/ansible/modules/windows/win_file.ps1 b/lib/ansible/modules/windows/win_file.ps1 index 68df390c92c..f151497f7bd 100644 --- a/lib/ansible/modules/windows/win_file.ps1 +++ b/lib/ansible/modules/windows/win_file.ps1 @@ -30,6 +30,52 @@ $result = @{ changed = $false } +# Used to delete symlinks as powershell cannot delete broken symlinks +$symlink_util = @" +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace Ansible.Command { + public class SymLinkHelper { + [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] + public static extern bool RemoveDirectory(string lpPathName); + + public static void DeleteSymLink(string linkPathName) { + bool result = RemoveDirectory(linkPathName); + if (result == false) + throw new Exception(String.Format("Error deleting symlink: {0}", new Win32Exception(Marshal.GetLastWin32Error()).Message)); + } + } +} +"@ +Add-Type -TypeDefinition $symlink_util + +# Used to delete directories and files with logic on handling symbolic links +function Remove-File($file) { + try { + if ($file.Attributes -band [System.IO.FileAttributes]::ReparsePoint) { + # Bug with powershell, if you try and delete a symbolic link that is pointing + # to an invalid path it will fail, using Win32 API to do this instead + [Ansible.Command.SymLinkHelper]::DeleteSymLink($file.FullName) + } elseif ($file.PSIsContainer) { + Remove-Directory -directory $file + } else { + Remove-Item -Path $file.FullName -Force + } + } catch [Exception] { + Fail-Json (New-Object psobject) "Failed to delete $($file.FullName): $($_.Exception.Message)" + } +} + +function Remove-Directory($directory) { + foreach ($file in Get-ChildItem $directory.FullName) { + Remove-File -file $file + } + Remove-Item -Path $directory.FullName -Force -Recurse +} + + if ($state -eq "touch") { if (-not $check_mode) { if (Test-Path $path) { @@ -45,7 +91,7 @@ if (Test-Path $path) { $fileinfo = Get-Item $path if ($state -eq "absent") { if (-not $check_mode) { - Remove-Item -Recurse -Force $fileinfo + Remove-File -file $fileinfo } $result.changed = $true } else { diff --git a/test/integration/targets/win_file/tasks/main.yml b/test/integration/targets/win_file/tasks/main.yml index d46bcbeb700..4332d053fb1 100644 --- a/test/integration/targets/win_file/tasks/main.yml +++ b/test/integration/targets/win_file/tasks/main.yml @@ -496,6 +496,24 @@ - file_result.changed - "stat_result.stat.exists == False" +- name: create folder to point set symbolic link for + win_file: + path: "{{win_output_dir}}/link-test/link-target" + state: directory + +- name: create symbolic link + win_command: cmd.exe /c mklink /d "{{win_output_dir}}\link-test\link" "{{win_output_dir}}\link-test\link-target" + +- name: remove symbolic link target + win_file: + path: "{{win_output_dir}}/link-test/link-target" + state: absent + +- name: remove parent folder with broken link + win_file: + path: "{{win_output_dir}}/link-test" + state: absent + - name: clean up sub1 win_file: path={{win_output_dir}}/sub1 state=absent