-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Leverage fileapi
for opening files on windows
#13178
Changes from 5 commits
a7688c8
c96bc28
8cedeb6
dfc0148
bce559b
3886053
3da1fdf
3be6087
dd12001
e3bf5a0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,11 +26,85 @@ module Crystal::System::File | |
end | ||
|
||
def self.open(filename : String, flags : Int32, perm : ::File::Permissions) : {LibC::Int, Errno} | ||
flags |= LibC::O_BINARY | LibC::O_NOINHERIT | ||
access, disposition, attributes, share = self.posix_to_open_opts flags, perm | ||
|
||
fd = LibC._wopen(System.to_wstr(filename), flags, perm) | ||
handle = LibC.CreateFileW( | ||
System.to_wstr(filename), | ||
access, | ||
share, # UNIX semantics | ||
Blacksmoke16 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
nil, | ||
disposition, | ||
attributes, | ||
LibC::HANDLE.null | ||
) | ||
|
||
if handle == LibC::INVALID_HANDLE_VALUE | ||
# Map ERROR_FILE_EXISTS to Errno::EEXIST to avoid changing semantics of other systems | ||
if WinError.value.error_file_exists? | ||
Errno.value = Errno::EEXIST | ||
end | ||
|
||
return {-1, Errno.value} | ||
Blacksmoke16 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end | ||
|
||
fd = LibC._open_osfhandle handle, flags | ||
|
||
if fd == -1 | ||
return {-1, Errno.value} | ||
end | ||
|
||
if flags & LibC::O_BINARY > 0 | ||
Blacksmoke16 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
LibC._setmode fd, LibC::O_BINARY | ||
elsif flags & LibC::O_TEXT > 0 | ||
LibC._setmode fd, LibC::O_TEXT | ||
end | ||
|
||
{fd, Errno::NONE} | ||
end | ||
|
||
private def self.posix_to_open_opts(flags : Int32, perm : ::File::Permissions) | ||
disposition = 0 | ||
|
||
Blacksmoke16 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
access = if flags & LibC::O_WRONLY > 0 | ||
LibC::GENERIC_WRITE | ||
elsif flags & LibC::O_RDWR > 0 | ||
LibC::GENERIC_READ | LibC::GENERIC_WRITE | ||
else | ||
LibC::GENERIC_READ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Windows only says:
So this check is fine on Windows, but to be precise, these flags are not bit flags at all. POSIX defines also access =
case flags & LibC::O_ACCMODE # unavailable on MSVC
when LibC::O_RDONLY
when LibC::O_WRONLY
when LibC::O_RDWR
when LibC::O_SEARCH # unavailable on most platforms
when LibC::O_EXEC # unavailable on most platforms
else
raise "not allowed"
end
Blacksmoke16 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end | ||
|
||
if flags & LibC::O_APPEND > 0 | ||
access |= LibC::FILE_APPEND_DATA | ||
end | ||
|
||
case flags & (LibC::O_CREAT | LibC::O_EXCL | LibC::O_TRUNC) | ||
when 0, LibC::O_EXCL then disposition = LibC::OPEN_EXISTING | ||
when LibC::O_CREAT then disposition = LibC::OPEN_ALWAYS | ||
when LibC::O_CREAT | LibC::O_EXCL, LibC::O_CREAT | LibC::O_TRUNC | LibC::O_EXCL then disposition = LibC::CREATE_NEW | ||
when LibC::O_TRUNC, LibC::O_TRUNC | LibC::O_EXCL then disposition = LibC::TRUNCATE_EXISTING | ||
when LibC::O_CREAT | LibC::O_TRUNC then disposition = LibC::CREATE_ALWAYS | ||
end | ||
Blacksmoke16 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
attributes = LibC::FILE_ATTRIBUTE_NORMAL | ||
unless perm.owner_write? | ||
attributes |= LibC::FILE_ATTRIBUTE_READONLY | ||
end | ||
|
||
if flags & LibC::O_TEMPORARY > 0 | ||
attributes |= LibC::FILE_FLAG_DELETE_ON_CLOSE | LibC::FILE_ATTRIBUTE_TEMPORARY | ||
access |= LibC::DELETE | ||
end | ||
|
||
if flags & LibC::O_SHORT_LIVED > 0 | ||
attributes |= LibC::FILE_ATTRIBUTE_TEMPORARY | ||
end | ||
|
||
case flags & (LibC::O_SEQUENTIAL | LibC::O_RANDOM) | ||
when LibC::O_SEQUENTIAL then attributes |= LibC::FILE_FLAG_SEQUENTIAL_SCAN | ||
when LibC::O_RANDOM then attributes |= LibC::FILE_FLAG_RANDOM_ACCESS | ||
end | ||
|
||
{fd, fd == -1 ? Errno.value : Errno::NONE} | ||
{access, disposition, attributes, LibC::DEFAULT_SHARE_MODE} | ||
end | ||
|
||
NOT_FOUND_ERRORS = { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should keep these mandatory flags
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensuring
O_BINARY
is added should address your below comment.EDIT: But to make the code cleaner, might just hardcode/inline the
_setmode
call.O_NOINHERIT
seems to be controlled via thebInheritHandle
property of https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa379560(v=vs.85) which is the 4th argument toCreateFileW
. However, the documentation also says:So given we'd always be applying
O_NOINHERIT
, I think we'll be fine as it is.