File Manager
Viewing File: file.rb
#
# Author:: Seth Chisamore (<schisamo@chef.io>)
# Author:: Mark Mzyk (<mmzyk@ospcode.com>)
# Copyright:: Copyright 2011-2016, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require "chef/win32/api"
require "chef/win32/api/security"
require "chef/win32/api/system"
require "chef/win32/unicode"
class Chef
module ReservedNames::Win32
module API
module File
extend Chef::ReservedNames::Win32::API
include Chef::ReservedNames::Win32::API::Security
include Chef::ReservedNames::Win32::API::System
###############################################
# Win32 API Constants
###############################################
FILE_ATTRIBUTE_READONLY = 0x00000001
FILE_ATTRIBUTE_HIDDEN = 0x00000002
FILE_ATTRIBUTE_SYSTEM = 0x00000004
FILE_ATTRIBUTE_DIRECTORY = 0x00000010
FILE_ATTRIBUTE_ARCHIVE = 0x00000020
FILE_ATTRIBUTE_DEVICE = 0x00000040
FILE_ATTRIBUTE_NORMAL = 0x00000080
FILE_ATTRIBUTE_TEMPORARY = 0x00000100
FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200
FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400
FILE_ATTRIBUTE_COMPRESSED = 0x00000800
FILE_ATTRIBUTE_OFFLINE = 0x00001000
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000
FILE_ATTRIBUTE_ENCRYPTED = 0x00004000
FILE_ATTRIBUTE_VIRTUAL = 0x00010000
INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF
FILE_FLAG_WRITE_THROUGH = 0x80000000
FILE_FLAG_OVERLAPPED = 0x40000000
FILE_FLAG_NO_BUFFERING = 0x20000000
FILE_FLAG_RANDOM_ACCESS = 0x10000000
FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000
FILE_FLAG_DELETE_ON_CLOSE = 0x04000000
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
FILE_FLAG_POSIX_SEMANTICS = 0x01000000
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
FILE_FLAG_OPEN_NO_RECALL = 0x00100000
FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000
INVALID_HANDLE_VALUE = 0xFFFFFFFF
MAX_PATH = 260
SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1
FILE_NAME_NORMALIZED = 0x0
FILE_NAME_OPENED = 0x8
# TODO add the rest of these CONSTS
FILE_SHARE_READ = 0x00000001
OPEN_EXISTING = 3
# DeviceIoControl control codes
# -----------------------------
FILE_DEVICE_BEEP = 0x00000001
FILE_DEVICE_CD_ROM = 0x00000002
FILE_DEVICE_CD_ROM_FILE_SYSTEM = 0x00000003
FILE_DEVICE_CONTROLLER = 0x00000004
FILE_DEVICE_DATALINK = 0x00000005
FILE_DEVICE_DFS = 0x00000006
FILE_DEVICE_DISK = 0x00000007
FILE_DEVICE_DISK_FILE_SYSTEM = 0x00000008
FILE_DEVICE_FILE_SYSTEM = 0x00000009
FILE_DEVICE_INPORT_PORT = 0x0000000a
FILE_DEVICE_KEYBOARD = 0x0000000b
FILE_DEVICE_MAILSLOT = 0x0000000c
FILE_DEVICE_MIDI_IN = 0x0000000d
FILE_DEVICE_MIDI_OUT = 0x0000000e
FILE_DEVICE_MOUSE = 0x0000000f
FILE_DEVICE_MULTI_UNC_PROVIDER = 0x00000010
FILE_DEVICE_NAMED_PIPE = 0x00000011
FILE_DEVICE_NETWORK = 0x00000012
FILE_DEVICE_NETWORK_BROWSER = 0x00000013
FILE_DEVICE_NETWORK_FILE_SYSTEM = 0x00000014
FILE_DEVICE_NULL = 0x00000015
FILE_DEVICE_PARALLEL_PORT = 0x00000016
FILE_DEVICE_PHYSICAL_NETCARD = 0x00000017
FILE_DEVICE_PRINTER = 0x00000018
FILE_DEVICE_SCANNER = 0x00000019
FILE_DEVICE_SERIAL_MOUSE_PORT = 0x0000001a
FILE_DEVICE_SERIAL_PORT = 0x0000001b
FILE_DEVICE_SCREEN = 0x0000001c
FILE_DEVICE_SOUND = 0x0000001d
FILE_DEVICE_STREAMS = 0x0000001e
FILE_DEVICE_TAPE = 0x0000001f
FILE_DEVICE_TAPE_FILE_SYSTEM = 0x00000020
FILE_DEVICE_TRANSPORT = 0x00000021
FILE_DEVICE_UNKNOWN = 0x00000022
FILE_DEVICE_VIDEO = 0x00000023
FILE_DEVICE_VIRTUAL_DISK = 0x00000024
FILE_DEVICE_WAVE_IN = 0x00000025
FILE_DEVICE_WAVE_OUT = 0x00000026
FILE_DEVICE_8042_PORT = 0x00000027
FILE_DEVICE_NETWORK_REDIRECTOR = 0x00000028
FILE_DEVICE_BATTERY = 0x00000029
FILE_DEVICE_BUS_EXTENDER = 0x0000002a
FILE_DEVICE_MODEM = 0x0000002b
FILE_DEVICE_VDM = 0x0000002c
FILE_DEVICE_MASS_STORAGE = 0x0000002d
FILE_DEVICE_SMB = 0x0000002e
FILE_DEVICE_KS = 0x0000002f
FILE_DEVICE_CHANGER = 0x00000030
FILE_DEVICE_SMARTCARD = 0x00000031
FILE_DEVICE_ACPI = 0x00000032
FILE_DEVICE_DVD = 0x00000033
FILE_DEVICE_FULLSCREEN_VIDEO = 0x00000034
FILE_DEVICE_DFS_FILE_SYSTEM = 0x00000035
FILE_DEVICE_DFS_VOLUME = 0x00000036
FILE_DEVICE_SERENUM = 0x00000037
FILE_DEVICE_TERMSRV = 0x00000038
FILE_DEVICE_KSEC = 0x00000039
FILE_DEVICE_FIPS = 0x0000003A
FILE_DEVICE_INFINIBAND = 0x0000003B
FILE_DEVICE_VMBUS = 0x0000003E
FILE_DEVICE_CRYPT_PROVIDER = 0x0000003F
FILE_DEVICE_WPD = 0x00000040
FILE_DEVICE_BLUETOOTH = 0x00000041
FILE_DEVICE_MT_COMPOSITE = 0x00000042
FILE_DEVICE_MT_TRANSPORT = 0x00000043
FILE_DEVICE_BIOMETRIC = 0x00000044
FILE_DEVICE_PMI = 0x00000045
# Methods
METHOD_BUFFERED = 0
METHOD_IN_DIRECT = 1
METHOD_OUT_DIRECT = 2
METHOD_NEITHER = 3
METHOD_DIRECT_TO_HARDWARE = METHOD_IN_DIRECT
METHOD_DIRECT_FROM_HARDWARE = METHOD_OUT_DIRECT
# Access
FILE_ANY_ACCESS = 0
FILE_SPECIAL_ACCESS = FILE_ANY_ACCESS
FILE_READ_ACCESS = 0x0001
FILE_WRITE_ACCESS = 0x0002
def self.CTL_CODE( device_type, function, method, access )
(device_type << 16) | (access << 14) | (function << 2) | method
end
FSCTL_GET_REPARSE_POINT = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
# Reparse point tags
IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
IO_REPARSE_TAG_HSM = 0xC0000004
IO_REPARSE_TAG_HSM2 = 0x80000006
IO_REPARSE_TAG_SIS = 0x80000007
IO_REPARSE_TAG_WIM = 0x80000008
IO_REPARSE_TAG_CSV = 0x80000009
IO_REPARSE_TAG_DFS = 0x8000000A
IO_REPARSE_TAG_SYMLINK = 0xA000000C
IO_REPARSE_TAG_DFSR = 0x80000012
MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024
###############################################
# Win32 API Bindings
###############################################
ffi_lib "kernel32", "version"
# Does not map directly to a win32 struct
# see https://msdn.microsoft.com/en-us/library/windows/desktop/ms647464(v=vs.85).aspx
class Translation < FFI::Struct
layout :w_lang, :WORD,
:w_code_page, :WORD
end
=begin
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME, *PFILETIME;
=end
class FILETIME < FFI::Struct
layout :dw_low_date_time, :DWORD,
:dw_high_date_time, :DWORD
end
=begin
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
=end
class SECURITY_ATTRIBUTES < FFI::Struct
layout :n_length, :DWORD,
:lp_security_descriptor, :LPVOID,
:b_inherit_handle, :DWORD
end
=begin
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
TCHAR cFileName[MAX_PATH];
TCHAR cAlternateFileName[14];
} WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA;
=end
class WIN32_FIND_DATA < FFI::Struct
layout :dw_file_attributes, :DWORD,
:ft_creation_time, FILETIME,
:ft_last_access_time, FILETIME,
:ft_last_write_time, FILETIME,
:n_file_size_high, :DWORD,
:n_file_size_low, :DWORD,
:dw_reserved_0, :DWORD,
:dw_reserved_1, :DWORD,
:c_file_name, [:BYTE, MAX_PATH * 2],
:c_alternate_file_name, [:BYTE, 14]
end
=begin
typedef struct _BY_HANDLE_FILE_INFORMATION {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD dwVolumeSerialNumber;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD nNumberOfLinks;
DWORD nFileIndexHigh;
DWORD nFileIndexLow;
} BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION;
=end
class BY_HANDLE_FILE_INFORMATION < FFI::Struct
layout :dw_file_attributes, :DWORD,
:ft_creation_time, FILETIME,
:ft_last_access_time, FILETIME,
:ft_last_write_time, FILETIME,
:dw_volume_serial_number, :DWORD,
:n_file_size_high, :DWORD,
:n_file_size_low, :DWORD,
:n_number_of_links, :DWORD,
:n_file_index_high, :DWORD,
:n_file_index_low, :DWORD
end
=begin
typedef struct _REPARSE_DATA_BUFFER {
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union {
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct {
UCHAR DataBuffer[1];
} GenericReparseBuffer;
};
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
=end
class REPARSE_DATA_BUFFER_SYMBOLIC_LINK < FFI::Struct
layout :SubstituteNameOffset, :ushort,
:SubstituteNameLength, :ushort,
:PrintNameOffset, :ushort,
:PrintNameLength, :ushort,
:Flags, :uint32,
:PathBuffer, :ushort
def substitute_name
string_pointer = FFI::Pointer.new(pointer.address) + offset_of(:PathBuffer) + self[:SubstituteNameOffset]
string_pointer.read_wstring(self[:SubstituteNameLength] / 2)
end
def print_name
string_pointer = FFI::Pointer.new(pointer.address) + offset_of(:PathBuffer) + self[:PrintNameOffset]
string_pointer.read_wstring(self[:PrintNameLength] / 2)
end
end
class REPARSE_DATA_BUFFER_MOUNT_POINT < FFI::Struct
layout :SubstituteNameOffset, :ushort,
:SubstituteNameLength, :ushort,
:PrintNameOffset, :ushort,
:PrintNameLength, :ushort,
:PathBuffer, :ushort
def substitute_name
string_pointer = FFI::Pointer.new(pointer.address) + offset_of(:PathBuffer) + self[:SubstituteNameOffset]
string_pointer.read_wstring(self[:SubstituteNameLength] / 2)
end
def print_name
string_pointer = FFI::Pointer.new(pointer.address) + offset_of(:PathBuffer) + self[:PrintNameOffset]
string_pointer.read_wstring(self[:PrintNameLength] / 2)
end
end
class REPARSE_DATA_BUFFER_GENERIC < FFI::Struct
layout :DataBuffer, :uchar
end
class REPARSE_DATA_BUFFER_UNION < FFI::Union
layout :SymbolicLinkReparseBuffer, REPARSE_DATA_BUFFER_SYMBOLIC_LINK,
:MountPointReparseBuffer, REPARSE_DATA_BUFFER_MOUNT_POINT,
:GenericReparseBuffer, REPARSE_DATA_BUFFER_GENERIC
end
class REPARSE_DATA_BUFFER < FFI::Struct
layout :ReparseTag, :uint32,
:ReparseDataLength, :ushort,
:Reserved, :ushort,
:ReparseBuffer, REPARSE_DATA_BUFFER_UNION
def reparse_buffer
if self[:ReparseTag] == IO_REPARSE_TAG_SYMLINK
self[:ReparseBuffer][:SymbolicLinkReparseBuffer]
elsif self[:ReparseTag] == IO_REPARSE_TAG_MOUNT_POINT
self[:ReparseBuffer][:MountPointReparseBuffer]
else
self[:ReparseBuffer][:GenericReparseBuffer]
end
end
end
=begin
HANDLE WINAPI CreateFile(
__in LPCTSTR lpFileName,
__in DWORD dwDesiredAccess,
__in DWORD dwShareMode,
__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
__in DWORD dwCreationDisposition,
__in DWORD dwFlagsAndAttributes,
__in_opt HANDLE hTemplateFile
);
=end
safe_attach_function :CreateFileW, [:LPCTSTR, :DWORD, :DWORD, :LPSECURITY_ATTRIBUTES, :DWORD, :DWORD, :pointer], :HANDLE
=begin
BOOL WINAPI FindClose(
__inout HANDLE hFindFile
);
=end
safe_attach_function :FindClose, [:HANDLE], :BOOL
=begin
DWORD WINAPI GetFileAttributes(
__in LPCTSTR lpFileName
);
=end
safe_attach_function :GetFileAttributesW, [:LPCWSTR], :DWORD
=begin
DWORD WINAPI GetFinalPathNameByHandle(
__in HANDLE hFile,
__out LPTSTR lpszFilePath,
__in DWORD cchFilePath,
__in DWORD dwFlags
);
=end
safe_attach_function :GetFinalPathNameByHandleW, [:HANDLE, :LPTSTR, :DWORD, :DWORD], :DWORD
=begin
BOOL WINAPI GetFileInformationByHandle(
__in HANDLE hFile,
__out LPBY_HANDLE_FILE_INFORMATION lpFileInformation
);
=end
safe_attach_function :GetFileInformationByHandle, [:HANDLE, :LPBY_HANDLE_FILE_INFORMATION], :BOOL
=begin
HANDLE WINAPI FindFirstFile(
__in LPCTSTR lpFileName,
__out LPWIN32_FIND_DATA lpFindFileData
);
=end
safe_attach_function :FindFirstFileW, [:LPCTSTR, :LPWIN32_FIND_DATA], :HANDLE
=begin
BOOL WINAPI CreateHardLink(
__in LPCTSTR lpFileName,
__in LPCTSTR lpExistingFileName,
__reserved LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
=end
safe_attach_function :CreateHardLinkW, [:LPCTSTR, :LPCTSTR, :LPSECURITY_ATTRIBUTES], :BOOLEAN
=begin
BOOLEAN WINAPI CreateSymbolicLink(
__in LPTSTR lpSymlinkFileName,
__in LPTSTR lpTargetFileName,
__in DWORD dwFlags
);
=end
safe_attach_function :CreateSymbolicLinkW, [:LPTSTR, :LPTSTR, :DWORD], :BOOLEAN
=begin
DWORD WINAPI GetLongPathName(
__in LPCTSTR lpszShortPath,
__out LPTSTR lpszLongPath,
__in DWORD cchBuffer
);
=end
safe_attach_function :GetLongPathNameW, [:LPCTSTR, :LPTSTR, :DWORD], :DWORD
=begin
DWORD WINAPI GetShortPathName(
__in LPCTSTR lpszLongPath,
__out LPTSTR lpszShortPath,
__in DWORD cchBuffer
);
=end
safe_attach_function :GetShortPathNameW, [:LPCTSTR, :LPTSTR, :DWORD], :DWORD
=begin
BOOL WINAPI DeviceIoControl(
__in HANDLE hDevice,
__in DWORD dwIoControlCode,
__in_opt LPVOID lpInBuffer,
__in DWORD nInBufferSize,
__out_opt LPVOID lpOutBuffer,
__in DWORD nOutBufferSize,
__out_opt LPDWORD lpBytesReturned,
__inout_opt LPOVERLAPPED lpOverlapped
);
=end
safe_attach_function :DeviceIoControl, [:HANDLE, :DWORD, :LPVOID, :DWORD, :LPVOID, :DWORD, :LPDWORD, :pointer], :BOOL
#BOOL WINAPI DeleteVolumeMountPoint(
#_In_ LPCTSTR lpszVolumeMountPoint
#);
safe_attach_function :DeleteVolumeMountPointW, [:LPCTSTR], :BOOL
#BOOL WINAPI SetVolumeMountPoint(
#_In_ LPCTSTR lpszVolumeMountPoint,
#_In_ LPCTSTR lpszVolumeName
#);
safe_attach_function :SetVolumeMountPointW, [:LPCTSTR, :LPCTSTR], :BOOL
#BOOL WINAPI GetVolumeNameForVolumeMountPoint(
#_In_ LPCTSTR lpszVolumeMountPoint,
#_Out_ LPTSTR lpszVolumeName,
#_In_ DWORD cchBufferLength
#);
safe_attach_function :GetVolumeNameForVolumeMountPointW, [:LPCTSTR, :LPTSTR, :DWORD], :BOOL
=begin
BOOL WINAPI GetFileVersionInfo(
_In_ LPCTSTR lptstrFilename,
_Reserved_ DWORD dwHandle,
_In_ DWORD dwLen,
_Out_ LPVOID lpData
);
=end
safe_attach_function :GetFileVersionInfoW, [:LPCTSTR, :DWORD, :DWORD, :LPVOID], :BOOL
=begin
DWORD WINAPI GetFileVersionInfoSize(
_In_ LPCTSTR lptstrFilename,
_Out_opt_ LPDWORD lpdwHandle
);
=end
safe_attach_function :GetFileVersionInfoSizeW, [:LPCTSTR, :LPDWORD], :DWORD
=begin
BOOL WINAPI VerQueryValue(
_In_ LPCVOID pBlock,
_In_ LPCTSTR lpSubBlock,
_Out_ LPVOID *lplpBuffer,
_Out_ PUINT puLen
);
=end
safe_attach_function :VerQueryValueW, [:LPCVOID, :LPCTSTR, :LPVOID, :PUINT], :BOOL
###############################################
# Helpers
###############################################
# takes the given path pre-pends "\\?\" and
# UTF-16LE encodes it. Used to prepare paths
# to be passed to the *W vesion of WinAPI File
# functions.
# This function is used by the "Link" resources where we need
# preserve relative paths because symbolic links can actually
# point to a relative path (relative to the link itself).
def encode_path(path)
(path_prepender << path.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR)).to_wstring
end
# Expands the path, prepends "\\?\" and UTF-16LE encodes it.
# This function is used by the "File" resources where we need
# convert relative paths to fully qualified paths.
def canonical_encode_path(path)
Chef::Util::PathHelper.canonical_path(path).to_wstring
end
def path_prepender
"\\\\?\\"
end
# retrieves a file search handle and passes it
# to +&block+ along with the find_data. also
# ensures the handle is closed on exit of the block
# FIXME: yard with @yield
def file_search_handle(path)
# Workaround for CHEF-4419:
# Make sure paths starting with "/" has a drive letter
# assigned from the current working diretory.
# Note: With CHEF-4427 this issue will be fixed with a
# broader fix to map all the paths starting with "/" to
# SYSTEM_DRIVE on windows.
path = ::File.expand_path(path) if path.start_with? "/"
path = canonical_encode_path(path)
find_data = WIN32_FIND_DATA.new
handle = FindFirstFileW(path, find_data)
if handle == INVALID_HANDLE_VALUE
Chef::ReservedNames::Win32::Error.raise!
end
yield(handle, find_data)
ensure
FindClose(handle) if handle && handle != INVALID_HANDLE_VALUE
end
# retrieves a file handle and passes it
# to +&block+ along with the find_data. also
# ensures the handle is closed on exit of the block
# FIXME: yard with @yield
def file_handle(path)
path = canonical_encode_path(path)
handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nil)
if handle == INVALID_HANDLE_VALUE
Chef::ReservedNames::Win32::Error.raise!
end
yield(handle)
ensure
CloseHandle(handle) if handle && handle != INVALID_HANDLE_VALUE
end
# FIXME: yard with @yield
def symlink_file_handle(path)
path = encode_path(path)
handle = CreateFileW(path, FILE_READ_EA, FILE_SHARE_READ,
nil, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nil)
if handle == INVALID_HANDLE_VALUE
Chef::ReservedNames::Win32::Error.raise!
end
yield(handle)
ensure
CloseHandle(handle) if handle && handle != INVALID_HANDLE_VALUE
end
def retrieve_file_info(file_name)
file_information = nil
file_handle(file_name) do |handle|
file_information = BY_HANDLE_FILE_INFORMATION.new
success = GetFileInformationByHandle(handle, file_information)
if success == 0
Chef::ReservedNames::Win32::Error.raise!
end
end
file_information
end
def retrieve_file_version_info(file_name)
file_name = encode_path(file_name)
file_size = GetFileVersionInfoSizeW(file_name, nil)
if file_size == 0
Chef::ReservedNames::Win32::Error.raise!
end
version_info = FFI::MemoryPointer.new(file_size)
unless GetFileVersionInfoW(file_name, 0, file_size, version_info)
Chef::ReservedNames::Win32::Error.raise!
end
version_info
end
end
end
end
end