class Dir
Constants
- MYDOCUMENTS
Public Class Methods
Same as the standard MRI Dir[] method except that it handles backslashes in path names.
# File lib/win32/dir.rb, line 133 def [](*glob_patterns) temp = glob_patterns.map! { |pattern| "#{pattern}".tr("\\", "/") } old_ref(*temp) end
Creates the symlink to
, linked to the existing directory from
. If the to
directory already exists, it must be empty or an error is raised.
Example:
Dir.mkdir('C:/from') Dir.create_junction('C:/to', 'C:/from')
# File lib/win32/dir.rb, line 198 def self.create_junction(to, from) to = string_check(to).wincode from = string_check(from).wincode from_path = (0.chr * 1024).encode("UTF-16LE") length = GetFullPathNameW(from, from_path.size, from_path, nil) if length == 0 || length > from_path.size raise SystemCallError.new("GetFullPathNameW", FFI.errno) else from_path.strip! end to_path = (0.chr * 1024).encode("UTF-16LE") length = GetFullPathNameW(to, to_path.size, to_path, nil) if length == 0 || length > to_path.size raise SystemCallError.new("GetFullPathNameW", FFI.errno) else to_path.strip! end # You can create a junction to a directory that already exists, so # long as it's empty. unless CreateDirectoryW(to_path, nil) if FFI.errno != ERROR_ALREADY_EXISTS raise SystemCallError.new("CreateDirectoryW", FFI.errno) end end # Generic read & write + open existing + reparse point & backup semantics handle = CreateFileW( to_path, GENERIC_READ | GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0 ) if handle == INVALID_HANDLE_VALUE raise SystemCallError.new("CreateFileW", FFI.errno) end target = "\\??\\".encode("UTF-16LE") + from_path rdb = REPARSE_JDATA_BUFFER.new rdb[:ReparseTag] = 2684354563 # IO_REPARSE_TAG_MOUNT_POINT rdb[:ReparseDataLength] = target.bytesize + 12 rdb[:Reserved] = 0 rdb[:SubstituteNameOffset] = 0 rdb[:SubstituteNameLength] = target.bytesize rdb[:PrintNameOffset] = target.bytesize + 2 rdb[:PrintNameLength] = 0 rdb[:PathBuffer] = target bytes = FFI::MemoryPointer.new(:ulong) begin bool = DeviceIoControl( handle, CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, 0), rdb, rdb[:ReparseDataLength] + rdb.header_size, nil, 0, bytes, nil ) error = FFI.errno unless bool RemoveDirectoryW(to_path) raise SystemCallError.new("DeviceIoControl", error) end ensure CloseHandle(handle) end self end
Returns whether or not path
is empty. Returns false if path
is not a directory, or contains any files other than '.' or '..'.
# File lib/win32/dir.rb, line 373 def self.empty?(path) PathIsDirectoryEmptyW("#{path}".wincode) end
Returns the present working directory. Unlike MRI, this method always normalizes the path.
Examples:
Dir.chdir("C:/Progra~1") Dir.getwd # => C:\Program Files Dir.chdir("C:/PROGRAM FILES") Dir.getwd # => C:\Program Files
# File lib/win32/dir.rb, line 154 def getwd path1 = FFI::Buffer.new(:wint_t, 1024, true) path2 = FFI::Buffer.new(:wint_t, 1024, true) path3 = FFI::Buffer.new(:wint_t, 1024, true) length = GetCurrentDirectoryW(path1.size, path1) if length == 0 || length > path1.size raise SystemCallError.new("GetCurrentDirectoryW", FFI.errno) end length = GetShortPathNameW(path1, path2, path2.size) if length == 0 || length > path2.size raise SystemCallError.new("GetShortPathNameW", FFI.errno) end length = GetLongPathNameW(path2, path3, path3.size) if length == 0 || length > path3.size raise SystemCallError.new("GetLongPathNameW", FFI.errno) end path = path3.read_bytes(length * 2).wstrip begin path.encode(Encoding.default_external) rescue Encoding::UndefinedConversionError path.encode("UTF-8") end end
Same as the standard MRI Dir.glob
method except that it handles backslashes in path names.
# File lib/win32/dir.rb, line 118 def glob(glob_pattern, flags = 0, &block) if glob_pattern.is_a?(Array) temp = glob_pattern.map! { |pattern| string_check(pattern).tr("\\", "/") } else temp = string_check(glob_pattern).tr("\\", "/") end old_glob(temp, flags, &block) end
Returns whether or not path
is a junction.
# File lib/win32/dir.rb, line 379 def self.junction?(path) string_check(path) bool = true attrib = GetFileAttributesW("#{path}".wincode) # Only directories with a reparse point attribute can be junctions if attrib == INVALID_FILE_ATTRIBUTES || attrib & FILE_ATTRIBUTE_DIRECTORY == 0 || attrib & FILE_ATTRIBUTE_REPARSE_POINT == 0 bool = false end bool end
Returns the path that a given junction points to. Raises an Errno::ENOENT error if the given path does not exist. Returns false if it is not a junction.
Example:
Dir.mkdir('C:/from') Dir.create_junction('C:/to', 'C:/from') Dir.read_junction("c:/to") # => "c:/from"
# File lib/win32/dir.rb, line 294 def self.read_junction(junction) return false unless Dir.junction?(junction) junction = string_check(junction).wincode junction_path = (0.chr * 1024).encode("UTF-16LE") length = GetFullPathNameW(junction, junction_path.size, junction_path, nil) if length == 0 || length > junction_path.size raise SystemCallError.new("GetFullPathNameW", FFI.errno) else junction_path.strip! end # Generic read & write + open existing + reparse point & backup semantics handle = CreateFileW( junction_path, GENERIC_READ | GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0 ) if handle == INVALID_HANDLE_VALUE raise SystemCallError.new("CreateFileW", FFI.errno) end rdb = REPARSE_JDATA_BUFFER.new rdb[:ReparseTag] = 0 rdb[:ReparseDataLength] = 0 rdb[:Reserved] = 0 rdb[:SubstituteNameOffset] = 0 rdb[:SubstituteNameLength] = 0 rdb[:PrintNameOffset] = 0 rdb[:PrintNameLength] = 0 rdb[:PathBuffer] = "" bytes = FFI::MemoryPointer.new(:ulong) begin bool = DeviceIoControl( handle, CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, 0), nil, 0, rdb, 1024, bytes, nil ) error = FFI.errno unless bool raise SystemCallError.new("DeviceIoControl", error) end ensure CloseHandle(handle) end # MSDN says print and substitute names can be in any order jname = (rdb[:PathBuffer].to_ptr + rdb[:SubstituteNameOffset]).read_string(rdb[:SubstituteNameLength]) jname = jname.bytes.to_a.pack("C*") jname = jname.force_encoding("UTF-16LE") raise "Invalid junction name: #{jname.encode("UTF-8")}" unless jname[0..3] == "\\??\\".encode("UTF-16LE") begin File.expand_path(jname[4..-1].encode(Encoding.default_external)) rescue Encoding::UndefinedConversionError File.expand_path(jname[4..-1].encode("UTF-8")) end end
Private Class Methods
Macro from Windows header file, used by the create_junction
method.
# File lib/win32/dir.rb, line 415 def self.CTL_CODE(device, function, method, access) ((device) << 16) | ((access) << 14) | ((function) << 2) | (method) end
Simulate MRI's contortions for a stringiness check.
# File lib/win32/dir.rb, line 405 def string_check(arg) return arg if arg.is_a?(String) return arg.send(:to_str) if arg.respond_to?(:to_str, true) # MRI honors it, even if private return arg.to_path if arg.respond_to?(:to_path) raise TypeError end