Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Dir#info on Windows #13395

Merged
merged 1 commit into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions spec/std/dir_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,14 @@ describe "Dir" do
end
end

# TODO: do we even want this?
pending_win32 "tests empty? on a directory path to a file" do
it "tests empty? on a directory path to a file" do
expect_raises(File::Error, "Error opening directory: '#{datapath("dir", "f1.txt", "/").inspect_unquoted}'") do
Dir.empty?(datapath("dir", "f1.txt", "/"))
end
end
end

pending_win32 "tests info on existing directory" do
it "tests info on existing directory" do
Dir.open(datapath) do |dir|
info = dir.info
info.directory?.should be_true
Expand Down
51 changes: 35 additions & 16 deletions src/crystal/system/win32/dir.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ require "c/processenv"

module Crystal::System::Dir
private class DirHandle
property handle : LibC::HANDLE
property iter_handle : LibC::HANDLE
property file_handle : LibC::HANDLE = LibC::INVALID_HANDLE_VALUE
getter query : LibC::LPWSTR

def initialize(@handle, @query)
def initialize(@iter_handle, @query)
end
end

Expand All @@ -22,11 +23,11 @@ module Crystal::System::Dir
end

def self.next_entry(dir : DirHandle, path : String) : Entry?
if dir.handle == LibC::INVALID_HANDLE_VALUE
if dir.iter_handle == LibC::INVALID_HANDLE_VALUE
# Directory is at start, use FindFirstFile
handle = LibC.FindFirstFileW(dir.query, out data)
if handle != LibC::INVALID_HANDLE_VALUE
dir.handle = handle
dir.iter_handle = handle
data_to_entry(data)
else
error = WinError.value
Expand All @@ -38,7 +39,7 @@ module Crystal::System::Dir
end
else
# Use FindNextFile
if LibC.FindNextFileW(dir.handle, out data_) != 0
if LibC.FindNextFileW(dir.iter_handle, out data_) != 0
data_to_entry(data_)
else
error = WinError.value
Expand All @@ -64,31 +65,49 @@ module Crystal::System::Dir
end

def self.info(dir : DirHandle, path) : ::File::Info
if dir.handle == LibC::INVALID_HANDLE_VALUE
handle = LibC.FindFirstFileW(dir.query, out data)
begin
Crystal::System::FileDescriptor.system_info handle, LibC::FILE_TYPE_DISK
ensure
close(handle, path) rescue nil
if dir.file_handle == LibC::INVALID_HANDLE_VALUE
handle = LibC.CreateFileW(
System.to_wstr(path),
LibC::FILE_READ_ATTRIBUTES,
LibC::FILE_SHARE_READ | LibC::FILE_SHARE_WRITE | LibC::FILE_SHARE_DELETE,
nil,
LibC::OPEN_EXISTING,
LibC::FILE_FLAG_BACKUP_SEMANTICS,
LibC::HANDLE.null,
)

if handle == LibC::INVALID_HANDLE_VALUE
raise ::File::Error.from_winerror("Unable to get directory info", file: path)
end
else
Crystal::System::FileDescriptor.system_info dir.handle, LibC::FILE_TYPE_DISK

dir.file_handle = handle
end

Crystal::System::FileDescriptor.system_info dir.file_handle, LibC::FILE_TYPE_DISK
end

def self.close(dir : DirHandle, path : String) : Nil
close(dir.handle, path)
dir.handle = LibC::INVALID_HANDLE_VALUE
close_iter(dir.iter_handle, path)
close_file(dir.file_handle, path)
dir.iter_handle = dir.file_handle = LibC::INVALID_HANDLE_VALUE
end

def self.close(handle : LibC::HANDLE, path : String) : Nil
private def self.close_iter(handle : LibC::HANDLE, path : String) : Nil
return if handle == LibC::INVALID_HANDLE_VALUE

if LibC.FindClose(handle) == 0
raise ::File::Error.from_winerror("Error closing directory", file: path)
end
end

private def self.close_file(handle : LibC::HANDLE, path : String) : Nil
return if handle == LibC::INVALID_HANDLE_VALUE

if LibC.CloseHandle(handle) == 0
raise ::File::Error.from_winerror("CloseHandle", file: path)
end
end

def self.current : String
System.retry_wstr_buffer do |buffer, small_buf|
len = LibC.GetCurrentDirectoryW(buffer.size, buffer)
Expand Down