-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Fix File#truncate
and #lock
for Win32 append-mode files
#14706
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
Changes from 1 commit
b43f23e
cabdcbd
2322c3d
061ae30
2fce106
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 |
---|---|---|
|
@@ -9,6 +9,8 @@ require "c/ntifs" | |
require "c/winioctl" | ||
|
||
module Crystal::System::File | ||
@system_append = false | ||
|
||
def self.open(filename : String, mode : String, perm : Int32 | ::File::Permissions) : FileDescriptor::Handle | ||
perm = ::File::Permissions.new(perm) if perm.is_a? Int32 | ||
# Only the owner writable bit is used, since windows only supports | ||
|
@@ -52,10 +54,9 @@ module Crystal::System::File | |
LibC::FILE_GENERIC_READ | ||
end | ||
|
||
if flags.bits_set? LibC::O_APPEND | ||
access |= LibC::FILE_APPEND_DATA | ||
access &= ~LibC::FILE_WRITE_DATA | ||
end | ||
# do not handle `O_APPEND`, because Win32 append mode relies on removing | ||
# `FILE_WRITE_DATA` which breaks file truncation and locking; instead, | ||
# simply set the end of the file as the write offset in `#write_blocking` | ||
|
||
if flags.bits_set? LibC::O_TRUNC | ||
if flags.bits_set? LibC::O_CREAT | ||
|
@@ -96,6 +97,24 @@ module Crystal::System::File | |
{access, disposition, attributes} | ||
end | ||
|
||
protected def system_set_mode(mode : String) | ||
@system_append = true if mode.starts_with?('a') | ||
straight-shoota marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end | ||
|
||
private def write_blocking(handle, slice) | ||
if @system_append | ||
write_blocking(handle, slice) do | ||
overlapped = LibC::OVERLAPPED.new | ||
overlapped.union.offset.offset = 0xFFFFFFFF_u32 | ||
overlapped.union.offset.offsetHigh = 0xFFFFFFFF_u32 | ||
ret = LibC.WriteFile(handle, slice, slice.size, out bytes_written, pointerof(overlapped)) | ||
{ret, bytes_written} | ||
end | ||
else | ||
super | ||
end | ||
end | ||
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. thought: Maybe we could reduce this duplication? The lib call is always the same, except for the overlapped struct. module Crystal::System::FileDescriptor
private def write_blocking(handle, slice, append = false)
if append
overlapped = LibC::OVERLAPPED.new
overlapped.union.offset.offset = 0xFFFFFFFF_u32
overlapped.union.offset.offsetHigh = 0xFFFFFFFF_u32
end
write_blocking(handle, slice) do
ret = LibC.WriteFile(handle, slice, slice.size, out bytes_written, pointerof(overlapped))
{ret, bytes_written}
end
end
end
module Crystal::System::File
private def write_blocking(handle, slice)
write_blocking(handle, slice, @system_append)
end
end This should make the diff smaller, the code has less duplication and is easier to comprehend. 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. We could also consider moving 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. That snippet wouldn't work as 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. You're probably right that 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. Sorry, I scribbled the code down too quickly. 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's probably no way around it. We could try to be smart and query the access mode of the file descriptor to figure out the intention. But we cannot change it anyway, so it's probably a mute point. |
||
|
||
NOT_FOUND_ERRORS = { | ||
WinError::ERROR_FILE_NOT_FOUND, | ||
WinError::ERROR_PATH_NOT_FOUND, | ||
|
Uh oh!
There was an error while loading. Please reload this page.