1 #!powershell
2
3 # Copyright: (c) 2017, Ansible Project
4 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6 #AnsibleRequires -CSharpUtil Ansible.Basic
7 #Requires -Module Ansible.ModuleUtils.FileUtil
8 #Requires -Module Ansible.ModuleUtils.LinkUtil
9
10 function ConvertTo-Timestamp($start_date, $end_date) {
11 if ($start_date -and $end_date) {
12 return (New-TimeSpan -Start $start_date -End $end_date).TotalSeconds
13 }
14 }
15
16 function Get-FileChecksum($path, $algorithm) {
17 switch ($algorithm) {
18 'md5' { $sp = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider }
19 'sha1' { $sp = New-Object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider }
20 'sha256' { $sp = New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider }
21 'sha384' { $sp = New-Object -TypeName System.Security.Cryptography.SHA384CryptoServiceProvider }
22 'sha512' { $sp = New-Object -TypeName System.Security.Cryptography.SHA512CryptoServiceProvider }
23 default { Fail-Json -obj $result -message "Unsupported hash algorithm supplied '$algorithm'" }
24 }
25
26 $fp = [System.IO.File]::Open($path, [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
27 try {
28 $hash = [System.BitConverter]::ToString($sp.ComputeHash($fp)).Replace("-", "").ToLower()
29 } finally {
30 $fp.Dispose()
31 }
32
33 return $hash
34 }
35
36 function Get-FileInfo {
37 param([String]$Path, [Switch]$Follow)
38
board_early_init_f(void)39 $info = Get-AnsibleItem -Path $Path -ErrorAction SilentlyContinue
40 $link_info = $null
41 if ($null -ne $info) {
42 try {
43 $link_info = Get-Link -link_path $info.FullName
44 } catch {
45 $module.Warn("Failed to check/get link info for file: $($_.Exception.Message)")
46 }
47
48 # If follow=true we want to follow the link all the way back to root object
49 if ($Follow -and $null -ne $link_info -and $link_info.Type -in @("SymbolicLink", "JunctionPoint")) {
50 $info, $link_info = Get-FileInfo -Path $link_info.AbsolutePath -Follow
51 }
52 }
53
54 return $info, $link_info
board_init(void)55 }
56
57 $spec = @{
58 options = @{
59 path = @{ type='path'; required=$true; aliases=@( 'dest', 'name' ) }
60 get_checksum = @{ type='bool'; default=$true }
61 checksum_algorithm = @{ type='str'; default='sha1'; choices=@( 'md5', 'sha1', 'sha256', 'sha384', 'sha512' ) }
62 follow = @{ type='bool'; default=$false }
63 }
64 supports_check_mode = $true
65 }
66
67 $module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
68
69 $path = $module.Params.path
70 $get_checksum = $module.Params.get_checksum
71 $checksum_algorithm = $module.Params.checksum_algorithm
72 $follow = $module.Params.follow
73
74 $module.Result.stat = @{ exists=$false }
75
76 Load-LinkUtils
77 $info, $link_info = Get-FileInfo -Path $path -Follow:$follow
reset_cpu(void)78 If ($null -ne $info) {
79 $epoch_date = Get-Date -Date "01/01/1970"
80 $attributes = @()
81 foreach ($attribute in ($info.Attributes -split ',')) {
82 $attributes += $attribute.Trim()
83 }
84
85 # default values that are always set, specific values are set below this
86 # but are kept commented for easier readability
87 $stat = @{
88 exists = $true
89 attributes = $info.Attributes.ToString()
90 isarchive = ($attributes -contains "Archive")
91 isdir = $false
92 ishidden = ($attributes -contains "Hidden")
93 isjunction = $false
94 islnk = $false
95 isreadonly = ($attributes -contains "ReadOnly")
96 isreg = $false
97 isshared = $false
98 nlink = 1 # Number of links to the file (hard links), overriden below if islnk
99 # lnk_target = islnk or isjunction Target of the symlink. Note that relative paths remain relative
100 # lnk_source = islnk os isjunction Target of the symlink normalized for the remote filesystem
101 hlnk_targets = @()
102 creationtime = (ConvertTo-Timestamp -start_date $epoch_date -end_date $info.CreationTime)
103 lastaccesstime = (ConvertTo-Timestamp -start_date $epoch_date -end_date $info.LastAccessTime)
104 lastwritetime = (ConvertTo-Timestamp -start_date $epoch_date -end_date $info.LastWriteTime)
105 # size = a file and directory - calculated below
106 path = $info.FullName
107 filename = $info.Name
108 # extension = a file
109 # owner = set outsite this dict in case it fails
110 # sharename = a directory and isshared is True
111 # checksum = a file and get_checksum: True
112 }
113 try {
114 $stat.owner = $info.GetAccessControl().Owner
115 } catch {
116 # may not have rights, historical behaviour was to just set to $null
117 # due to ErrorActionPreference being set to "Continue"
118 $stat.owner = $null
119 }
120
121 # values that are set according to the type of file
122 if ($info.Attributes.HasFlag([System.IO.FileAttributes]::Directory)) {
123 $stat.isdir = $true
124 $share_info = Get-CimInstance -ClassName Win32_Share -Filter "Path='$($stat.path -replace '\\', '\\')'"
125 if ($null -ne $share_info) {
126 $stat.isshared = $true
127 $stat.sharename = $share_info.Name
128 }
129
130 try {
131 $size = 0
132 foreach ($file in $info.EnumerateFiles("*", [System.IO.SearchOption]::AllDirectories)) {
133 $size += $file.Length
134 }
135 $stat.size = $size
136 } catch {
137 $stat.size = 0
138 }
139 } else {
140 $stat.extension = $info.Extension
141 $stat.isreg = $true
142 $stat.size = $info.Length
143
144 if ($get_checksum) {
145 try {
146 $stat.checksum = Get-FileChecksum -path $path -algorithm $checksum_algorithm
147 } catch {
148 $module.FailJson("Failed to get hash of file, set get_checksum to False to ignore this error: $($_.Exception.Message)", $_)
149 }
150 }
151 }
152
153 # Get symbolic link, junction point, hard link info
154 if ($null -ne $link_info) {
155 switch ($link_info.Type) {
156 "SymbolicLink" {
157 $stat.islnk = $true
158 $stat.isreg = $false
159 $stat.lnk_target = $link_info.TargetPath
160 $stat.lnk_source = $link_info.AbsolutePath
161 break
162 }
163 "JunctionPoint" {
164 $stat.isjunction = $true
165 $stat.isreg = $false
166 $stat.lnk_target = $link_info.TargetPath
167 $stat.lnk_source = $link_info.AbsolutePath
168 break
169 }
170 "HardLink" {
171 $stat.lnk_type = "hard"
172 $stat.nlink = $link_info.HardTargets.Count
173
174 # remove current path from the targets
175 $hlnk_targets = $link_info.HardTargets | Where-Object { $_ -ne $stat.path }
176 $stat.hlnk_targets = @($hlnk_targets)
177 break
178 }
179 }
180 }
181
182 $module.Result.stat = $stat
183 }
184
185 $module.ExitJson()
186
187