module Puppet::Util::Windows::File
Constants
- INVALID_HANDLE_VALUE
define
INVALID_HANDLE_VALUE((HANDLE)(LONG_PTR)-1)
Public Class Methods
add_attributes(path, flags)
click to toggle source
# File lib/puppet/util/windows/file.rb 98 def add_attributes(path, flags) 99 oldattrs = get_attributes(path) 100 101 if (oldattrs | flags) != oldattrs 102 set_attributes(path, oldattrs | flags) 103 end 104 end
create_file(file_name, desired_access, share_mode, security_attributes, creation_disposition, flags_and_attributes, template_file_handle)
click to toggle source
# File lib/puppet/util/windows/file.rb 126 def self.create_file(file_name, desired_access, share_mode, security_attributes, 127 creation_disposition, flags_and_attributes, template_file_handle) 128 129 result = CreateFileW(wide_string(file_name.to_s), 130 desired_access, share_mode, security_attributes, creation_disposition, 131 flags_and_attributes, template_file_handle) 132 133 return result unless result == INVALID_HANDLE_VALUE 134 raise Puppet::Util::Windows::Error.new( 135 "CreateFile(#{file_name}, #{desired_access.to_s(8)}, #{share_mode.to_s(8)}, " + 136 "#{security_attributes}, #{creation_disposition.to_s(8)}, " + 137 "#{flags_and_attributes.to_s(8)}, #{template_file_handle})") 138 end
device_io_control(handle, io_control_code, in_buffer = nil, out_buffer = nil)
click to toggle source
# File lib/puppet/util/windows/file.rb 179 def self.device_io_control(handle, io_control_code, in_buffer = nil, out_buffer = nil) 180 if out_buffer.nil? 181 raise Puppet::Util::Windows::Error.new(_("out_buffer is required")) 182 end 183 184 FFI::MemoryPointer.new(:dword, 1) do |bytes_returned_ptr| 185 result = DeviceIoControl( 186 handle, 187 io_control_code, 188 in_buffer, in_buffer.nil? ? 0 : in_buffer.size, 189 out_buffer, out_buffer.size, 190 bytes_returned_ptr, 191 nil 192 ) 193 194 if result == FFI::WIN32_FALSE 195 raise Puppet::Util::Windows::Error.new( 196 "DeviceIoControl(#{handle}, #{io_control_code}, " + 197 "#{in_buffer}, #{in_buffer ? in_buffer.size : ''}, " + 198 "#{out_buffer}, #{out_buffer ? out_buffer.size : ''}") 199 end 200 end 201 202 out_buffer 203 end
exist?(path)
click to toggle source
# File lib/puppet/util/windows/file.rb 56 def exist?(path) 57 path = path.to_str if path.respond_to?(:to_str) # support WatchedFile 58 path = path.to_s # support String and Pathname 59 60 seen_paths = [] 61 # follow up to 64 symlinks before giving up 62 0.upto(64) do |depth| 63 # return false if this path has been seen before. This is protection against circular symlinks 64 return false if seen_paths.include?(path.downcase) 65 66 result = get_attributes(path,false) 67 68 # return false for path not found 69 return false if result == INVALID_FILE_ATTRIBUTES 70 71 # return true if path exists and it's not a symlink 72 # Other file attributes are ignored. https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117(v=vs.85).aspx 73 reparse_point = (result & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT 74 if reparse_point && symlink_reparse_point?(path) 75 # walk the symlink and try again... 76 seen_paths << path.downcase 77 path = readlink(path) 78 else 79 # file was found and its not a symlink 80 return true 81 end 82 end 83 84 false 85 end
get_attributes(file_name, raise_on_invalid = true)
click to toggle source
# File lib/puppet/util/windows/file.rb 88 def get_attributes(file_name, raise_on_invalid = true) 89 result = GetFileAttributesW(wide_string(file_name.to_s)) 90 if raise_on_invalid && result == INVALID_FILE_ATTRIBUTES 91 raise Puppet::Util::Windows::Error.new("GetFileAttributes(#{file_name})") 92 end 93 94 result 95 end
get_long_pathname(path)
click to toggle source
# File lib/puppet/util/windows/file.rb 247 def get_long_pathname(path) 248 converted = String.new 249 FFI::Pointer.from_string_to_wide_string(path) do |path_ptr| 250 # includes terminating NULL 251 buffer_size = GetLongPathNameW(path_ptr, FFI::Pointer::NULL, 0) 252 FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr| 253 if GetLongPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE 254 raise Puppet::Util::Windows::Error.new(_("Failed to call GetLongPathName")) 255 end 256 257 converted = converted_ptr.read_wide_string(buffer_size - 1) 258 end 259 end 260 261 converted 262 end
get_reparse_point_data(handle) { |buffer_type| ... }
click to toggle source
# File lib/puppet/util/windows/file.rb 140 def self.get_reparse_point_data(handle, &block) 141 # must be multiple of 1024, min 10240 142 FFI::MemoryPointer.new(MAXIMUM_REPARSE_DATA_BUFFER_SIZE) do |reparse_data_buffer_ptr| 143 device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, reparse_data_buffer_ptr) 144 145 reparse_tag = reparse_data_buffer_ptr.read_win32_ulong 146 buffer_type = case reparse_tag 147 when IO_REPARSE_TAG_SYMLINK 148 SYMLINK_REPARSE_DATA_BUFFER 149 when IO_REPARSE_TAG_MOUNT_POINT 150 MOUNT_POINT_REPARSE_DATA_BUFFER 151 when IO_REPARSE_TAG_NFS 152 raise Puppet::Util::Windows::Error.new("Retrieving NFS reparse point data is unsupported") 153 else 154 raise Puppet::Util::Windows::Error.new("DeviceIoControl(#{handle}, " + 155 "FSCTL_GET_REPARSE_POINT) returned unknown tag 0x#{reparse_tag.to_s(16).upcase}") 156 end 157 158 yield buffer_type.new(reparse_data_buffer_ptr) 159 end 160 161 # underlying struct MemoryPointer has been cleaned up by this point, nothing to return 162 nil 163 end
get_reparse_point_tag(handle)
click to toggle source
# File lib/puppet/util/windows/file.rb 165 def self.get_reparse_point_tag(handle) 166 reparse_tag = nil 167 168 # must be multiple of 1024, min 10240 169 FFI::MemoryPointer.new(MAXIMUM_REPARSE_DATA_BUFFER_SIZE) do |reparse_data_buffer_ptr| 170 device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, reparse_data_buffer_ptr) 171 172 # DWORD ReparseTag is the first member of the struct 173 reparse_tag = reparse_data_buffer_ptr.read_win32_ulong 174 end 175 176 reparse_tag 177 end
get_short_pathname(path)
click to toggle source
# File lib/puppet/util/windows/file.rb 265 def get_short_pathname(path) 266 converted = String.new 267 FFI::Pointer.from_string_to_wide_string(path) do |path_ptr| 268 # includes terminating NULL 269 buffer_size = GetShortPathNameW(path_ptr, FFI::Pointer::NULL, 0) 270 FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr| 271 if GetShortPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE 272 raise Puppet::Util::Windows::Error.new("Failed to call GetShortPathName") 273 end 274 275 converted = converted_ptr.read_wide_string(buffer_size - 1) 276 end 277 end 278 279 converted 280 end
lstat(file_name)
click to toggle source
# File lib/puppet/util/windows/file.rb 307 def lstat(file_name) 308 file_name = file_name.to_s # accommodate PathName or String 309 # monkey'ing around! 310 stat = File.lstat(file_name) 311 312 singleton_class = class << stat; self; end 313 singleton_class.send(:define_method, :mode) do 314 Puppet::Util::Windows::Security.get_mode(file_name) 315 end 316 317 if symlink?(file_name) 318 def stat.ftype 319 "link" 320 end 321 end 322 stat 323 end
move_file_ex(source, target, flags = 0)
click to toggle source
# File lib/puppet/util/windows/file.rb 35 def move_file_ex(source, target, flags = 0) 36 result = MoveFileExW(wide_string(source.to_s), 37 wide_string(target.to_s), 38 flags) 39 40 return true if result != FFI::WIN32_FALSE 41 raise Puppet::Util::Windows::Error. 42 new("MoveFileEx(#{source}, #{target}, #{flags.to_s(8)})") 43 end
open_symlink(link_name) { |handle = create_file( link_name, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0) ensure WIN32.CloseHandle(handle)| ... }
click to toggle source
# File lib/puppet/util/windows/file.rb 219 def self.open_symlink(link_name) 220 begin 221 yield handle = create_file( 222 link_name, 223 GENERIC_READ, 224 FILE_SHARE_READ, 225 nil, # security_attributes 226 OPEN_EXISTING, 227 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 228 0) # template_file 229 ensure 230 FFI::WIN32.CloseHandle(handle) if handle 231 end 232 233 # handle has had CloseHandle called against it, so nothing to return 234 nil 235 end
readlink(link_name)
click to toggle source
# File lib/puppet/util/windows/file.rb 237 def readlink(link_name) 238 link = nil 239 open_symlink(link_name) do |handle| 240 link = resolve_symlink(handle) 241 end 242 243 link 244 end
remove_attributes(path, flags)
click to toggle source
# File lib/puppet/util/windows/file.rb 107 def remove_attributes(path, flags) 108 oldattrs = get_attributes(path) 109 110 if (oldattrs & ~flags) != oldattrs 111 set_attributes(path, oldattrs & ~flags) 112 end 113 end
reparse_point?(file_name)
click to toggle source
# File lib/puppet/util/windows/file.rb 205 def reparse_point?(file_name) 206 attributes = get_attributes(file_name, false) 207 208 return false if (attributes == INVALID_FILE_ATTRIBUTES) 209 (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT 210 end
replace_file(target, source)
click to toggle source
# File lib/puppet/util/windows/file.rb 15 def replace_file(target, source) 16 target_encoded = wide_string(target.to_s) 17 source_encoded = wide_string(source.to_s) 18 19 flags = REPLACEFILE_IGNORE_MERGE_ERRORS 20 backup_file = nil 21 result = ReplaceFileW( 22 target_encoded, 23 source_encoded, 24 backup_file, 25 flags, 26 FFI::Pointer::NULL, 27 FFI::Pointer::NULL 28 ) 29 30 return true if result != FFI::WIN32_FALSE 31 raise Puppet::Util::Windows::Error.new("ReplaceFile(#{target}, #{source})") 32 end
set_attributes(path, flags)
click to toggle source
# File lib/puppet/util/windows/file.rb 116 def set_attributes(path, flags) 117 success = SetFileAttributesW(wide_string(path), flags) != FFI::WIN32_FALSE 118 raise Puppet::Util::Windows::Error.new(_("Failed to set file attributes")) if !success 119 120 success 121 end
stat(file_name)
click to toggle source
# File lib/puppet/util/windows/file.rb 283 def stat(file_name) 284 file_name = file_name.to_s # accommodate PathName or String 285 stat = File.stat(file_name) 286 singleton_class = class << stat; self; end 287 target_path = file_name 288 289 if symlink?(file_name) 290 target_path = readlink(file_name) 291 link_ftype = File.stat(target_path).ftype 292 293 # sigh, monkey patch instance method for instance, and close over link_ftype 294 singleton_class.send(:define_method, :ftype) do 295 link_ftype 296 end 297 end 298 299 singleton_class.send(:define_method, :mode) do 300 Puppet::Util::Windows::Security.get_mode(target_path) 301 end 302 303 stat 304 end
symlink(target, symlink)
click to toggle source
# File lib/puppet/util/windows/file.rb 46 def symlink(target, symlink) 47 flags = File.directory?(target) ? 0x1 : 0x0 48 result = CreateSymbolicLinkW(wide_string(symlink.to_s), 49 wide_string(target.to_s), flags) 50 return true if result != FFI::WIN32_FALSE 51 raise Puppet::Util::Windows::Error.new( 52 "CreateSymbolicLink(#{symlink}, #{target}, #{flags.to_s(8)})") 53 end
symlink?(file_name)
click to toggle source
# File lib/puppet/util/windows/file.rb 213 def symlink?(file_name) 214 # Puppet currently only handles mount point and symlink reparse points, ignores others 215 reparse_point?(file_name) && symlink_reparse_point?(file_name) 216 end
Private Class Methods
resolve_symlink(handle)
click to toggle source
# File lib/puppet/util/windows/file.rb 326 def self.resolve_symlink(handle) 327 path = nil 328 get_reparse_point_data(handle) do |reparse_data| 329 offset = reparse_data[:PrintNameOffset] 330 length = reparse_data[:PrintNameLength] 331 332 ptr = reparse_data.pointer + reparse_data.offset_of(:PathBuffer) + offset 333 path = ptr.read_wide_string(length / 2) # length is bytes, need UTF-16 wchars 334 end 335 336 path 337 end
symlink_reparse_point?(path)
click to toggle source
these reparse point types are the only ones Puppet currently understands so rather than raising an exception in readlink, prefer to not consider the path a symlink when stat'ing later
# File lib/puppet/util/windows/file.rb 343 def self.symlink_reparse_point?(path) 344 symlink = false 345 346 open_symlink(path) do |handle| 347 symlink = [ 348 IO_REPARSE_TAG_SYMLINK, 349 IO_REPARSE_TAG_MOUNT_POINT 350 ].include?(get_reparse_point_tag(handle)) 351 end 352 353 symlink 354 end
Private Instance Methods
add_attributes(path, flags)
click to toggle source
# File lib/puppet/util/windows/file.rb 98 def add_attributes(path, flags) 99 oldattrs = get_attributes(path) 100 101 if (oldattrs | flags) != oldattrs 102 set_attributes(path, oldattrs | flags) 103 end 104 end
exist?(path)
click to toggle source
# File lib/puppet/util/windows/file.rb 56 def exist?(path) 57 path = path.to_str if path.respond_to?(:to_str) # support WatchedFile 58 path = path.to_s # support String and Pathname 59 60 seen_paths = [] 61 # follow up to 64 symlinks before giving up 62 0.upto(64) do |depth| 63 # return false if this path has been seen before. This is protection against circular symlinks 64 return false if seen_paths.include?(path.downcase) 65 66 result = get_attributes(path,false) 67 68 # return false for path not found 69 return false if result == INVALID_FILE_ATTRIBUTES 70 71 # return true if path exists and it's not a symlink 72 # Other file attributes are ignored. https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117(v=vs.85).aspx 73 reparse_point = (result & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT 74 if reparse_point && symlink_reparse_point?(path) 75 # walk the symlink and try again... 76 seen_paths << path.downcase 77 path = readlink(path) 78 else 79 # file was found and its not a symlink 80 return true 81 end 82 end 83 84 false 85 end
get_attributes(file_name, raise_on_invalid = true)
click to toggle source
# File lib/puppet/util/windows/file.rb 88 def get_attributes(file_name, raise_on_invalid = true) 89 result = GetFileAttributesW(wide_string(file_name.to_s)) 90 if raise_on_invalid && result == INVALID_FILE_ATTRIBUTES 91 raise Puppet::Util::Windows::Error.new("GetFileAttributes(#{file_name})") 92 end 93 94 result 95 end
get_long_pathname(path)
click to toggle source
# File lib/puppet/util/windows/file.rb 247 def get_long_pathname(path) 248 converted = String.new 249 FFI::Pointer.from_string_to_wide_string(path) do |path_ptr| 250 # includes terminating NULL 251 buffer_size = GetLongPathNameW(path_ptr, FFI::Pointer::NULL, 0) 252 FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr| 253 if GetLongPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE 254 raise Puppet::Util::Windows::Error.new(_("Failed to call GetLongPathName")) 255 end 256 257 converted = converted_ptr.read_wide_string(buffer_size - 1) 258 end 259 end 260 261 converted 262 end
get_short_pathname(path)
click to toggle source
# File lib/puppet/util/windows/file.rb 265 def get_short_pathname(path) 266 converted = String.new 267 FFI::Pointer.from_string_to_wide_string(path) do |path_ptr| 268 # includes terminating NULL 269 buffer_size = GetShortPathNameW(path_ptr, FFI::Pointer::NULL, 0) 270 FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr| 271 if GetShortPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE 272 raise Puppet::Util::Windows::Error.new("Failed to call GetShortPathName") 273 end 274 275 converted = converted_ptr.read_wide_string(buffer_size - 1) 276 end 277 end 278 279 converted 280 end
lstat(file_name)
click to toggle source
# File lib/puppet/util/windows/file.rb 307 def lstat(file_name) 308 file_name = file_name.to_s # accommodate PathName or String 309 # monkey'ing around! 310 stat = File.lstat(file_name) 311 312 singleton_class = class << stat; self; end 313 singleton_class.send(:define_method, :mode) do 314 Puppet::Util::Windows::Security.get_mode(file_name) 315 end 316 317 if symlink?(file_name) 318 def stat.ftype 319 "link" 320 end 321 end 322 stat 323 end
move_file_ex(source, target, flags = 0)
click to toggle source
# File lib/puppet/util/windows/file.rb 35 def move_file_ex(source, target, flags = 0) 36 result = MoveFileExW(wide_string(source.to_s), 37 wide_string(target.to_s), 38 flags) 39 40 return true if result != FFI::WIN32_FALSE 41 raise Puppet::Util::Windows::Error. 42 new("MoveFileEx(#{source}, #{target}, #{flags.to_s(8)})") 43 end
readlink(link_name)
click to toggle source
# File lib/puppet/util/windows/file.rb 237 def readlink(link_name) 238 link = nil 239 open_symlink(link_name) do |handle| 240 link = resolve_symlink(handle) 241 end 242 243 link 244 end
remove_attributes(path, flags)
click to toggle source
# File lib/puppet/util/windows/file.rb 107 def remove_attributes(path, flags) 108 oldattrs = get_attributes(path) 109 110 if (oldattrs & ~flags) != oldattrs 111 set_attributes(path, oldattrs & ~flags) 112 end 113 end
reparse_point?(file_name)
click to toggle source
# File lib/puppet/util/windows/file.rb 205 def reparse_point?(file_name) 206 attributes = get_attributes(file_name, false) 207 208 return false if (attributes == INVALID_FILE_ATTRIBUTES) 209 (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT 210 end
replace_file(target, source)
click to toggle source
# File lib/puppet/util/windows/file.rb 15 def replace_file(target, source) 16 target_encoded = wide_string(target.to_s) 17 source_encoded = wide_string(source.to_s) 18 19 flags = REPLACEFILE_IGNORE_MERGE_ERRORS 20 backup_file = nil 21 result = ReplaceFileW( 22 target_encoded, 23 source_encoded, 24 backup_file, 25 flags, 26 FFI::Pointer::NULL, 27 FFI::Pointer::NULL 28 ) 29 30 return true if result != FFI::WIN32_FALSE 31 raise Puppet::Util::Windows::Error.new("ReplaceFile(#{target}, #{source})") 32 end
set_attributes(path, flags)
click to toggle source
# File lib/puppet/util/windows/file.rb 116 def set_attributes(path, flags) 117 success = SetFileAttributesW(wide_string(path), flags) != FFI::WIN32_FALSE 118 raise Puppet::Util::Windows::Error.new(_("Failed to set file attributes")) if !success 119 120 success 121 end
stat(file_name)
click to toggle source
# File lib/puppet/util/windows/file.rb 283 def stat(file_name) 284 file_name = file_name.to_s # accommodate PathName or String 285 stat = File.stat(file_name) 286 singleton_class = class << stat; self; end 287 target_path = file_name 288 289 if symlink?(file_name) 290 target_path = readlink(file_name) 291 link_ftype = File.stat(target_path).ftype 292 293 # sigh, monkey patch instance method for instance, and close over link_ftype 294 singleton_class.send(:define_method, :ftype) do 295 link_ftype 296 end 297 end 298 299 singleton_class.send(:define_method, :mode) do 300 Puppet::Util::Windows::Security.get_mode(target_path) 301 end 302 303 stat 304 end
symlink(target, symlink)
click to toggle source
# File lib/puppet/util/windows/file.rb 46 def symlink(target, symlink) 47 flags = File.directory?(target) ? 0x1 : 0x0 48 result = CreateSymbolicLinkW(wide_string(symlink.to_s), 49 wide_string(target.to_s), flags) 50 return true if result != FFI::WIN32_FALSE 51 raise Puppet::Util::Windows::Error.new( 52 "CreateSymbolicLink(#{symlink}, #{target}, #{flags.to_s(8)})") 53 end
symlink?(file_name)
click to toggle source
# File lib/puppet/util/windows/file.rb 213 def symlink?(file_name) 214 # Puppet currently only handles mount point and symlink reparse points, ignores others 215 reparse_point?(file_name) && symlink_reparse_point?(file_name) 216 end