Module: Puppet::Util::Windows::File

Extended by:
FFI::Library, String
Defined in:
lib/puppet/util/windows.rb,
lib/puppet/util/windows/file.rb

Defined Under Namespace

Classes: REPARSE_DATA_BUFFER

Constant Summary collapse

FILE_ATTRIBUTE_READONLY =
0x00000001
SYNCHRONIZE =
0x100000
STANDARD_RIGHTS_REQUIRED =
0xf0000
STANDARD_RIGHTS_READ =
0x20000
STANDARD_RIGHTS_WRITE =
0x20000
STANDARD_RIGHTS_EXECUTE =
0x20000
STANDARD_RIGHTS_ALL =
0x1F0000
SPECIFIC_RIGHTS_ALL =
0xFFFF
FILE_READ_DATA =
1
FILE_WRITE_DATA =
2
FILE_APPEND_DATA =
4
FILE_READ_EA =
8
FILE_WRITE_EA =
16
FILE_EXECUTE =
32
FILE_DELETE_CHILD =
64
FILE_READ_ATTRIBUTES =
128
FILE_WRITE_ATTRIBUTES =
256
FILE_ALL_ACCESS =
STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF
FILE_GENERIC_READ =
STANDARD_RIGHTS_READ |
FILE_READ_DATA |
FILE_READ_ATTRIBUTES |
FILE_READ_EA |
SYNCHRONIZE
FILE_GENERIC_WRITE =
STANDARD_RIGHTS_WRITE |
FILE_WRITE_DATA |
FILE_WRITE_ATTRIBUTES |
FILE_WRITE_EA |
FILE_APPEND_DATA |
SYNCHRONIZE
FILE_GENERIC_EXECUTE =
STANDARD_RIGHTS_EXECUTE |
FILE_READ_ATTRIBUTES |
FILE_EXECUTE |
SYNCHRONIZE
REPLACEFILE_WRITE_THROUGH =
0x1
REPLACEFILE_IGNORE_MERGE_ERRORS =
0x2
REPLACEFILE_IGNORE_ACL_ERRORS =
0x3
INVALID_FILE_ATTRIBUTES =

define INVALID_FILE_ATTRIBUTES (DWORD (-1))

0xFFFFFFFF
INVALID_HANDLE_VALUE =

define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)

FFI::Pointer.new(-1).address
FILE_ATTRIBUTE_REPARSE_POINT =
0x400
GENERIC_READ =
0x80000000
GENERIC_WRITE =
0x40000000
GENERIC_EXECUTE =
0x20000000
GENERIC_ALL =
0x10000000
FILE_SHARE_READ =
1
FILE_SHARE_WRITE =
2
OPEN_EXISTING =
3
FILE_FLAG_OPEN_REPARSE_POINT =
0x00200000
FILE_FLAG_BACKUP_SEMANTICS =
0x02000000
ERROR_FILE_NOT_FOUND =
2
ERROR_PATH_NOT_FOUND =
3

Class Method Summary collapse

Methods included from FFI::Library

attach_function_private

Methods included from String

wide_string

Class Method Details

.add_attributes(path, flags) ⇒ Object



138
139
140
141
142
143
144
# File 'lib/puppet/util/windows/file.rb', line 138

def add_attributes(path, flags)
  oldattrs = get_attributes(path)

  if (oldattrs | flags) != oldattrs
    set_attributes(path, oldattrs | flags)
  end
end

.create_file(file_name, desired_access, share_mode, security_attributes, creation_disposition, flags_and_attributes, template_file_handle) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/puppet/util/windows/file.rb', line 166

def self.create_file(file_name, desired_access, share_mode, security_attributes,
  creation_disposition, flags_and_attributes, template_file_handle)

  result = CreateFileW(wide_string(file_name.to_s),
    desired_access, share_mode, security_attributes, creation_disposition,
    flags_and_attributes, template_file_handle)

  return result unless result == INVALID_HANDLE_VALUE
  raise Puppet::Util::Windows::Error.new(
    "CreateFile(#{file_name}, #{desired_access.to_s(8)}, #{share_mode.to_s(8)}, " +
      "#{security_attributes}, #{creation_disposition.to_s(8)}, " +
      "#{flags_and_attributes.to_s(8)}, #{template_file_handle})")
end

.device_io_control(handle, io_control_code, in_buffer = nil, out_buffer = nil) ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/puppet/util/windows/file.rb', line 191

def self.device_io_control(handle, io_control_code, in_buffer = nil, out_buffer = nil)
  if out_buffer.nil?
    raise Puppet::Util::Windows::Error.new(_("out_buffer is required"))
  end

  FFI::MemoryPointer.new(:dword, 1) do |bytes_returned_ptr|
    result = DeviceIoControl(
      handle,
      io_control_code,
      in_buffer, in_buffer.nil? ? 0 : in_buffer.size,
      out_buffer, out_buffer.size,
      bytes_returned_ptr,
      nil
    )

    if result == FFI::WIN32_FALSE
      raise Puppet::Util::Windows::Error.new(
        "DeviceIoControl(#{handle}, #{io_control_code}, " +
        "#{in_buffer}, #{in_buffer ? in_buffer.size : ''}, " +
        "#{out_buffer}, #{out_buffer ? out_buffer.size : ''}")
    end
  end

  out_buffer
end

.exist?(path) ⇒ Boolean

Returns:

  • (Boolean)


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
# File 'lib/puppet/util/windows/file.rb', line 97

def exist?(path)
  path = path.to_str if path.respond_to?(:to_str) # support WatchedFile
  path = path.to_s # support String and Pathname

  seen_paths = []
  # follow up to 64 symlinks before giving up
  0.upto(64) do |depth|
    # return false if this path has been seen before.  This is protection against circular symlinks
    return false if seen_paths.include?(path.downcase)

    result = get_attributes(path,false)

    # return false for path not found
    return false if result == INVALID_FILE_ATTRIBUTES

    # return true if path exists and it's not a symlink
    # Other file attributes are ignored. https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117(v=vs.85).aspx
    return true if (result & FILE_ATTRIBUTE_REPARSE_POINT) != FILE_ATTRIBUTE_REPARSE_POINT

    # walk the symlink and try again...
    seen_paths << path.downcase
    path = readlink(path)
  end

  false
end

.get_attributes(file_name, raise_on_invalid = true) ⇒ Object



128
129
130
131
132
133
134
135
# File 'lib/puppet/util/windows/file.rb', line 128

def get_attributes(file_name, raise_on_invalid = true)
  result = GetFileAttributesW(wide_string(file_name.to_s))
  if raise_on_invalid && result == INVALID_FILE_ATTRIBUTES
    raise Puppet::Util::Windows::Error.new("GetFileAttributes(#{file_name})")
  end

  result
end

.get_long_pathname(path) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/puppet/util/windows/file.rb', line 267

def get_long_pathname(path)
  converted = ''
  FFI::Pointer.from_string_to_wide_string(path) do |path_ptr|
    # includes terminating NULL
    buffer_size = GetLongPathNameW(path_ptr, FFI::Pointer::NULL, 0)
    FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr|
      if GetLongPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE
        raise Puppet::Util::Windows::Error.new(_("Failed to call GetLongPathName"))
      end

      converted = converted_ptr.read_wide_string(buffer_size - 1)
    end
  end

  converted
end

.get_reparse_point_data(handle, &block) ⇒ Object



180
181
182
183
184
185
186
187
188
189
# File 'lib/puppet/util/windows/file.rb', line 180

def self.get_reparse_point_data(handle, &block)
  # must be multiple of 1024, min 10240
  FFI::MemoryPointer.new(REPARSE_DATA_BUFFER.size) do |reparse_data_buffer_ptr|
    device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, reparse_data_buffer_ptr)
    yield REPARSE_DATA_BUFFER.new(reparse_data_buffer_ptr)
  end

  # underlying struct MemoryPointer has been cleaned up by this point, nothing to return
  nil
end

.get_short_pathname(path) ⇒ Object



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/puppet/util/windows/file.rb', line 285

def get_short_pathname(path)
  converted = ''
  FFI::Pointer.from_string_to_wide_string(path) do |path_ptr|
    # includes terminating NULL
    buffer_size = GetShortPathNameW(path_ptr, FFI::Pointer::NULL, 0)
    FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr|
      if GetShortPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE
        raise Puppet::Util::Windows::Error.new("Failed to call GetShortPathName")
      end

      converted = converted_ptr.read_wide_string(buffer_size - 1)
    end
  end

  converted
end

.lstat(file_name) ⇒ Object



327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/puppet/util/windows/file.rb', line 327

def lstat(file_name)
  file_name = file_name.to_s # accommodate PathName or String
  # monkey'ing around!
  stat = File.lstat(file_name)

  singleton_class = class << stat; self; end
  singleton_class.send(:define_method, :mode) do
    Puppet::Util::Windows::Security.get_mode(file_name)
  end

  if symlink?(file_name)
    def stat.ftype
      "link"
    end
  end
  stat
end

.move_file_ex(source, target, flags = 0) ⇒ Object



75
76
77
78
79
80
81
82
83
# File 'lib/puppet/util/windows/file.rb', line 75

def move_file_ex(source, target, flags = 0)
  result = MoveFileExW(wide_string(source.to_s),
                       wide_string(target.to_s),
                       flags)

  return true if result != FFI::WIN32_FALSE
  raise Puppet::Util::Windows::Error.
    new("MoveFileEx(#{source}, #{target}, #{flags.to_s(8)})")
end


236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/puppet/util/windows/file.rb', line 236

def self.open_symlink(link_name)
  begin
    yield handle = create_file(
    link_name,
    GENERIC_READ,
    FILE_SHARE_READ,
    nil, # security_attributes
    OPEN_EXISTING,
    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
    0) # template_file
  ensure
    FFI::WIN32.CloseHandle(handle) if handle
  end

  # handle has had CloseHandle called against it, so nothing to return
  nil
end


254
255
256
257
258
259
260
261
# File 'lib/puppet/util/windows/file.rb', line 254

def readlink(link_name)
  link = nil
  open_symlink(link_name) do |handle|
    link = resolve_symlink(handle)
  end

  link
end

.remove_attributes(path, flags) ⇒ Object



147
148
149
150
151
152
153
# File 'lib/puppet/util/windows/file.rb', line 147

def remove_attributes(path, flags)
  oldattrs = get_attributes(path)

  if (oldattrs & ~flags) != oldattrs
    set_attributes(path, oldattrs & ~flags)
  end
end

.replace_file(target, source) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/puppet/util/windows/file.rb', line 55

def replace_file(target, source)
  target_encoded = wide_string(target.to_s)
  source_encoded = wide_string(source.to_s)

  flags = REPLACEFILE_IGNORE_MERGE_ERRORS
  backup_file = nil
  result = ReplaceFileW(
    target_encoded,
    source_encoded,
    backup_file,
    flags,
    FFI::Pointer::NULL,
    FFI::Pointer::NULL
  )

  return true if result != FFI::WIN32_FALSE
  raise Puppet::Util::Windows::Error.new("ReplaceFile(#{target}, #{source})")
end

.set_attributes(path, flags) ⇒ Object



156
157
158
159
160
161
# File 'lib/puppet/util/windows/file.rb', line 156

def set_attributes(path, flags)
  success = SetFileAttributesW(wide_string(path), flags) != FFI::WIN32_FALSE
  raise Puppet::Util::Windows::Error.new(_("Failed to set file attributes")) if !success

  success
end

.stat(file_name) ⇒ Object



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/puppet/util/windows/file.rb', line 303

def stat(file_name)
  file_name = file_name.to_s # accommodate PathName or String
  stat = File.stat(file_name)
  singleton_class = class << stat; self; end
  target_path = file_name

  if symlink?(file_name)
    target_path = readlink(file_name)
    link_ftype = File.stat(target_path).ftype

    # sigh, monkey patch instance method for instance, and close over link_ftype
    singleton_class.send(:define_method, :ftype) do
      link_ftype
    end
  end

  singleton_class.send(:define_method, :mode) do
    Puppet::Util::Windows::Security.get_mode(target_path)
  end

  stat
end


86
87
88
89
90
91
92
93
# File 'lib/puppet/util/windows/file.rb', line 86

def symlink(target, symlink)
  flags = File.directory?(target) ? 0x1 : 0x0
  result = CreateSymbolicLinkW(wide_string(symlink.to_s),
    wide_string(target.to_s), flags)
  return true if result != FFI::WIN32_FALSE
  raise Puppet::Util::Windows::Error.new(
    "CreateSymbolicLink(#{symlink}, #{target}, #{flags.to_s(8)})")
end

.symlink?(file_name) ⇒ Boolean

Returns:

  • (Boolean)


218
219
220
221
222
223
# File 'lib/puppet/util/windows/file.rb', line 218

def symlink?(file_name)
  attributes = get_attributes(file_name, false)

  return false if (attributes == INVALID_FILE_ATTRIBUTES)
  (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT
end