summaryrefslogtreecommitdiffstats
path: root/test/support/windows-integration/collections/ansible_collections/ansible/windows/plugins/modules/win_file.ps1
blob: 54427549254377cfc6bb63cfaa2cebc69ce3bef0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!powershell

# Copyright: (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

#Requires -Module Ansible.ModuleUtils.Legacy

$ErrorActionPreference = "Stop"

$params = Parse-Args $args -supports_check_mode $true

$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -default $false
$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP

$path = Get-AnsibleParam -obj $params -name "path" -type "path" -failifempty $true -aliases "dest","name"
$state = Get-AnsibleParam -obj $params -name "state" -type "str" -validateset "absent","directory","file","touch"

# used in template/copy when dest is the path to a dir and source is a file
$original_basename = Get-AnsibleParam -obj $params -name "_original_basename" -type "str"
if ((Test-Path -LiteralPath $path -PathType Container) -and ($null -ne $original_basename)) {
    $path = Join-Path -Path $path -ChildPath $original_basename
}

$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 DeleteFileW(string lpFileName);

        [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
        public static extern bool RemoveDirectoryW(string lpPathName);

        public static void DeleteDirectory(string path) {
            if (!RemoveDirectoryW(path))
                throw new Exception(String.Format("RemoveDirectoryW({0}) failed: {1}", path, new Win32Exception(Marshal.GetLastWin32Error()).Message));
        }

        public static void DeleteFile(string path) {
            if (!DeleteFileW(path))
                throw new Exception(String.Format("DeleteFileW({0}) failed: {1}", path, new Win32Exception(Marshal.GetLastWin32Error()).Message));
        }
    }
}
"@
$original_tmp = $env:TMP
$env:TMP = $_remote_tmp
Add-Type -TypeDefinition $symlink_util
$env:TMP = $original_tmp

# Used to delete directories and files with logic on handling symbolic links
function Remove-File($file, $checkmode) {
    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
            if ($file.PSIsContainer) {
                if (-not $checkmode) {
                    [Ansible.Command.SymLinkHelper]::DeleteDirectory($file.FullName)
                }
            } else {
                if (-not $checkmode) {
                    [Ansible.Command.SymlinkHelper]::DeleteFile($file.FullName)
                }
            }
        } elseif ($file.PSIsContainer) {
            Remove-Directory -directory $file -checkmode $checkmode
        } else {
            Remove-Item -LiteralPath $file.FullName -Force -WhatIf:$checkmode
        }
    } catch [Exception] {
        Fail-Json $result "Failed to delete $($file.FullName): $($_.Exception.Message)"
    }
}

function Remove-Directory($directory, $checkmode) {
    foreach ($file in Get-ChildItem -LiteralPath $directory.FullName) {
        Remove-File -file $file -checkmode $checkmode
    }
    Remove-Item -LiteralPath $directory.FullName -Force -Recurse -WhatIf:$checkmode
}


if ($state -eq "touch") {
    if (Test-Path -LiteralPath $path) {
        if (-not $check_mode) {
            (Get-ChildItem -LiteralPath $path).LastWriteTime = Get-Date
        }
        $result.changed = $true
    } else {
        Write-Output $null | Out-File -LiteralPath $path -Encoding ASCII -WhatIf:$check_mode
        $result.changed = $true
    }
}

if (Test-Path -LiteralPath $path) {
    $fileinfo = Get-Item -LiteralPath $path -Force
    if ($state -eq "absent") {
        Remove-File -file $fileinfo -checkmode $check_mode
        $result.changed = $true
    } else {
        if ($state -eq "directory" -and -not $fileinfo.PsIsContainer) {
            Fail-Json $result "path $path is not a directory"
        }

        if ($state -eq "file" -and $fileinfo.PsIsContainer) {
            Fail-Json $result "path $path is not a file"
        }
    }

} else {

    # If state is not supplied, test the $path to see if it looks like
    # a file or a folder and set state to file or folder
    if ($null -eq $state) {
        $basename = Split-Path -Path $path -Leaf
        if ($basename.length -gt 0) {
           $state = "file"
        } else {
           $state = "directory"
        }
    }

    if ($state -eq "directory") {
        try {
            New-Item -Path $path -ItemType Directory -WhatIf:$check_mode | Out-Null
        } catch {
            if ($_.CategoryInfo.Category -eq "ResourceExists") {
                $fileinfo = Get-Item -LiteralPath $_.CategoryInfo.TargetName
                if ($state -eq "directory" -and -not $fileinfo.PsIsContainer) {
                    Fail-Json $result "path $path is not a directory"
                }
            } else {
                Fail-Json $result $_.Exception.Message
            }
        }
        $result.changed = $true
    } elseif ($state -eq "file") {
        Fail-Json $result "path $path will not be created"
    }

}

Exit-Json $result