Skip to content
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
4 changes: 2 additions & 2 deletions spec/compiler/macro/macro_methods_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3732,7 +3732,7 @@ module Crystal
assert_error <<-CRYSTAL,
{{read_file("#{__DIR__}/../data/build_foo")}}
CRYSTAL
"No such file or directory"
"Error opening file with mode 'r'"
end
end

Expand All @@ -3747,7 +3747,7 @@ module Crystal
assert_error <<-CRYSTAL,
{{read_file("spec/compiler/data/build_foo")}}
CRYSTAL
"No such file or directory"
"Error opening file with mode 'r'"
end
end
end
Expand Down
14 changes: 9 additions & 5 deletions src/crystal/system/file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ module Crystal::System::File

LOWER_ALPHANUM = "0123456789abcdefghijklmnopqrstuvwxyz".to_slice

def self.mktemp(prefix : String?, suffix : String?, dir : String, random : ::Random = ::Random::DEFAULT) : {LibC::Int, String}
def self.mktemp(prefix : String?, suffix : String?, dir : String, random : ::Random = ::Random::DEFAULT) : {FileDescriptor::Handle, String}
mode = LibC::O_RDWR | LibC::O_CREAT | LibC::O_EXCL
perm = ::File::Permissions.new(0o600)

Expand All @@ -65,10 +65,10 @@ module Crystal::System::File
io << suffix
end

fd, errno = open(path, mode, perm)
handle, errno = open(path, mode, perm)

if errno.none?
return {fd, path}
if error_is_none?(errno)
return {handle, path}
elsif error_is_file_exists?(errno)
# retry
next
Expand All @@ -80,8 +80,12 @@ module Crystal::System::File
raise ::File::AlreadyExistsError.new("Error creating temporary file", file: "#{prefix}********#{suffix}")
end

private def self.error_is_none?(errno)
errno.in?(Errno::NONE, WinError::ERROR_SUCCESS)
end

private def self.error_is_file_exists?(errno)
errno.in?(Errno::EEXIST, WinError::ERROR_ALREADY_EXISTS)
errno.in?(Errno::EEXIST, WinError::ERROR_FILE_EXISTS)
end

# Closes the internal file descriptor without notifying libevent.
Expand Down
6 changes: 3 additions & 3 deletions src/crystal/system/process.cr
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ struct Crystal::System::Process
end

module Crystal::System
ORIGINAL_STDIN = IO::FileDescriptor.new(0, blocking: true)
ORIGINAL_STDOUT = IO::FileDescriptor.new(1, blocking: true)
ORIGINAL_STDERR = IO::FileDescriptor.new(2, blocking: true)
ORIGINAL_STDIN = IO::FileDescriptor.new(Crystal::System::FileDescriptor::STDIN_HANDLE, blocking: true)
ORIGINAL_STDOUT = IO::FileDescriptor.new(Crystal::System::FileDescriptor::STDOUT_HANDLE, blocking: true)
ORIGINAL_STDERR = IO::FileDescriptor.new(Crystal::System::FileDescriptor::STDERR_HANDLE, blocking: true)
Comment on lines +87 to +89
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be shortened to:

Suggested change
ORIGINAL_STDIN = IO::FileDescriptor.new(Crystal::System::FileDescriptor::STDIN_HANDLE, blocking: true)
ORIGINAL_STDOUT = IO::FileDescriptor.new(Crystal::System::FileDescriptor::STDOUT_HANDLE, blocking: true)
ORIGINAL_STDERR = IO::FileDescriptor.new(Crystal::System::FileDescriptor::STDERR_HANDLE, blocking: true)
ORIGINAL_STDIN = IO::FileDescriptor.new(FileDescriptor::STDIN_HANDLE, blocking: true)
ORIGINAL_STDOUT = IO::FileDescriptor.new(FileDescriptor::STDOUT_HANDLE, blocking: true)
ORIGINAL_STDERR = IO::FileDescriptor.new(FileDescriptor::STDERR_HANDLE, blocking: true)

end

{% if flag?(:wasi) %}
Expand Down
4 changes: 4 additions & 0 deletions src/crystal/system/unix/file_descriptor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ module Crystal::System::FileDescriptor
# system.
alias Handle = Int32

STDIN_HANDLE = 0
STDOUT_HANDLE = 1
STDERR_HANDLE = 2

private def unbuffered_read(slice : Bytes) : Int32
evented_read(slice, "Error reading file") do
LibC.read(fd, slice, slice.size).tap do |return_code|
Expand Down
24 changes: 7 additions & 17 deletions src/crystal/system/win32/file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require "c/ntifs"
require "c/winioctl"

module Crystal::System::File
def self.open(filename : String, mode : String, perm : Int32 | ::File::Permissions) : LibC::Int
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
# the read only attribute.
Expand All @@ -19,15 +19,15 @@ module Crystal::System::File
perm = LibC::S_IREAD
end

fd, errno = open(filename, open_flag(mode), ::File::Permissions.new(perm))
unless errno.none?
raise ::File::Error.from_os_error("Error opening file with mode '#{mode}'", errno, file: filename)
handle, error = open(filename, open_flag(mode), ::File::Permissions.new(perm))
unless error.error_success?
raise ::File::Error.from_os_error("Error opening file with mode '#{mode}'", error, file: filename)
end

fd
handle
end

def self.open(filename : String, flags : Int32, perm : ::File::Permissions) : {LibC::Int, Errno}
def self.open(filename : String, flags : Int32, perm : ::File::Permissions) : {FileDescriptor::Handle, WinError}
access, disposition, attributes = self.posix_to_open_opts flags, perm

handle = LibC.CreateFileW(
Expand All @@ -40,17 +40,7 @@ module Crystal::System::File
LibC::HANDLE.null
)

if handle == LibC::INVALID_HANDLE_VALUE
return {-1, WinError.value.to_errno}
end

fd = LibC._open_osfhandle handle, flags

if fd == -1
return {-1, Errno.value}
end

{fd, Errno::NONE}
{handle.address, handle == LibC::INVALID_HANDLE_VALUE ? WinError.value : WinError::ERROR_SUCCESS}
end

private def self.posix_to_open_opts(flags : Int32, perm : ::File::Permissions)
Expand Down
55 changes: 27 additions & 28 deletions src/crystal/system/win32/file_descriptor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ module Crystal::System::FileDescriptor

# Platform-specific type to represent a file descriptor handle to the operating
# system.
alias Handle = ::LibC::Int
# NOTE: this should really be `LibC::HANDLE`, here it is an integer type of
# the same size so that `IO::FileDescriptor#fd` continues to return an `Int`
alias Handle = ::LibC::UIntPtrT

STDIN_HANDLE = LibC.GetStdHandle(LibC::STD_INPUT_HANDLE).address
STDOUT_HANDLE = LibC.GetStdHandle(LibC::STD_OUTPUT_HANDLE).address
STDERR_HANDLE = LibC.GetStdHandle(LibC::STD_ERROR_HANDLE).address

@system_blocking = true

Expand Down Expand Up @@ -92,21 +98,12 @@ module Crystal::System::FileDescriptor
raise NotImplementedError.new "Crystal::System::FileDescriptor.fcntl"
end

private def windows_handle
FileDescriptor.windows_handle!(fd)
protected def windows_handle
FileDescriptor.windows_handle(fd)
end

def self.windows_handle(fd)
ret = LibC._get_osfhandle(fd)
return LibC::INVALID_HANDLE_VALUE if ret == -1 || ret == -2
LibC::HANDLE.new(ret)
end

def self.windows_handle!(fd)
ret = LibC._get_osfhandle(fd)
raise RuntimeError.from_errno("_get_osfhandle") if ret == -1
raise RuntimeError.new("_get_osfhandle returned -2") if ret == -2
LibC::HANDLE.new(ret)
LibC::HANDLE.new(fd)
end

def self.system_info(handle, file_type = nil)
Expand Down Expand Up @@ -152,10 +149,11 @@ module Crystal::System::FileDescriptor
end

private def system_reopen(other : IO::FileDescriptor)
# Windows doesn't implement the CLOEXEC flag
if LibC._dup2(other.fd, self.fd) == -1
raise IO::Error.from_errno("Could not reopen file descriptor")
cur_proc = LibC.GetCurrentProcess
if LibC.DuplicateHandle(cur_proc, other.windows_handle, cur_proc, out new_handle, 0, true, LibC::DUPLICATE_SAME_ACCESS) == 0
raise IO::Error.from_winerror("Could not reopen file descriptor")
end
@volatile_fd.set(new_handle.address)

# Mark the handle open, since we had to have dup'd a live handle.
@closed = false
Expand All @@ -168,13 +166,8 @@ module Crystal::System::FileDescriptor
end

def file_descriptor_close
if LibC._close(fd) != 0
case Errno.value
when Errno::EINTR
# ignore
else
raise IO::Error.from_errno("Error closing file", target: self)
end
if LibC.CloseHandle(windows_handle) == 0
raise IO::Error.from_winerror("Error closing file", target: self)
end
end

Expand Down Expand Up @@ -253,15 +246,15 @@ module Crystal::System::FileDescriptor
raise IO::Error.from_winerror("CreateFileW") if r_pipe == LibC::INVALID_HANDLE_VALUE
Crystal::Scheduler.event_loop.create_completion_port(r_pipe) unless read_blocking

r = IO::FileDescriptor.new(LibC._open_osfhandle(r_pipe, 0), read_blocking)
w = IO::FileDescriptor.new(LibC._open_osfhandle(w_pipe, 0), write_blocking)
r = IO::FileDescriptor.new(r_pipe.address, read_blocking)
w = IO::FileDescriptor.new(w_pipe.address, write_blocking)
w.sync = true

{r, w}
end

def self.pread(fd, buffer, offset)
handle = windows_handle!(fd)
handle = windows_handle(fd)

overlapped = LibC::OVERLAPPED.new
overlapped.union.offset.offset = LibC::DWORD.new(offset)
Expand All @@ -276,8 +269,14 @@ module Crystal::System::FileDescriptor
end

def self.from_stdio(fd)
handle = case fd
when 0 then LibC.GetStdHandle(LibC::STD_INPUT_HANDLE)
when 1 then LibC.GetStdHandle(LibC::STD_OUTPUT_HANDLE)
when 2 then LibC.GetStdHandle(LibC::STD_ERROR_HANDLE)
else LibC::INVALID_HANDLE_VALUE
end

console_handle = false
handle = windows_handle(fd)
if handle != LibC::INVALID_HANDLE_VALUE
# TODO: use `out old_mode` after implementing interpreter out closured var
old_mode = uninitialized LibC::DWORD
Expand All @@ -291,7 +290,7 @@ module Crystal::System::FileDescriptor
end
end

io = IO::FileDescriptor.new(fd, blocking: true)
io = IO::FileDescriptor.new(handle.address, blocking: true)
# Set sync or flush_on_newline as described in STDOUT and STDERR docs.
# See https://crystal-lang.org/api/toplevel.html#STDERR
if console_handle
Expand Down
2 changes: 1 addition & 1 deletion src/crystal/system/win32/process.cr
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ struct Crystal::System::Process
end

private def self.handle_from_io(io : IO::FileDescriptor, parent_io)
source_handle = FileDescriptor.windows_handle!(io.fd)
source_handle = io.windows_handle

cur_proc = LibC.GetCurrentProcess
if LibC.DuplicateHandle(cur_proc, source_handle, cur_proc, out new_handle, 0, true, LibC::DUPLICATE_SAME_ACCESS) == 0
Expand Down
4 changes: 2 additions & 2 deletions src/io/file_descriptor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class IO::FileDescriptor < IO
write_timeout
end

def initialize(fd, blocking = nil, *, @close_on_finalize = true)
def initialize(fd : Handle, blocking = nil, *, @close_on_finalize = true)
@volatile_fd = Atomic.new(fd)
@closed = system_closed?

Expand All @@ -57,7 +57,7 @@ class IO::FileDescriptor < IO
end

# :nodoc:
def self.from_stdio(fd) : self
def self.from_stdio(fd : Handle) : self
Crystal::System::FileDescriptor.from_stdio(fd)
end

Expand Down
8 changes: 4 additions & 4 deletions src/lib_c/x86_64-windows-msvc/c/io.cr
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
require "c/stdint"

lib LibC
fun _close(fd : Int) : Int
fun _wexecvp(cmdname : WCHAR*, argv : WCHAR**) : IntPtrT
fun _get_osfhandle(fd : Int) : IntPtrT
fun _dup2(fd1 : Int, fd2 : Int) : Int
fun _open_osfhandle(osfhandle : HANDLE, flags : LibC::Int) : LibC::Int

# unused
fun _open_osfhandle(osfhandle : HANDLE, flags : LibC::Int) : LibC::Int
fun _get_osfhandle(fd : Int) : IntPtrT
fun _close(fd : Int) : Int
fun _dup2(fd1 : Int, fd2 : Int) : Int
fun _isatty(fd : Int) : Int
fun _write(fd : Int, buffer : UInt8*, count : UInt) : Int
fun _read(fd : Int, buffer : UInt8*, count : UInt) : Int
Expand Down
4 changes: 3 additions & 1 deletion src/lib_c/x86_64-windows-msvc/c/winbase.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ lib LibC
FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000_u32
FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF_u32

STD_ERROR_HANDLE = 0xFFFFFFF4_u32
STD_INPUT_HANDLE = 0xFFFFFFF6_u32
STD_OUTPUT_HANDLE = 0xFFFFFFF5_u32
STD_ERROR_HANDLE = 0xFFFFFFF4_u32

fun FormatMessageA(dwFlags : DWORD, lpSource : Void*, dwMessageId : DWORD, dwLanguageId : DWORD,
lpBuffer : LPSTR, nSize : DWORD, arguments : Void*) : DWORD
Expand Down