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
2 changes: 1 addition & 1 deletion spec/std/dir_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ describe "Dir" do

context "path exists" do
it "fails when path is a file" do
expect_raises(File::AlreadyExistsError, "Unable to create directory: '#{datapath("test_file.txt").inspect_unquoted}': File exists") do
expect_raises(File::AlreadyExistsError, "Unable to create directory: '#{datapath("test_file.txt").inspect_unquoted}'") do
Dir.mkdir_p(datapath("test_file.txt"))
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/crystal/system/print_error.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Crystal::System
{% if flag?(:unix) || flag?(:wasm32) %}
LibC.write 2, bytes, bytes.size
{% elsif flag?(:win32) %}
LibC._write 2, bytes, bytes.size
LibC.WriteFile(LibC.GetStdHandle(LibC::STD_ERROR_HANDLE), bytes, bytes.size, out _, nil)
{% end %}
end
end
Expand Down
8 changes: 4 additions & 4 deletions src/crystal/system/win32/dir.cr
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ module Crystal::System::Dir
end

def self.create(path : String, mode : Int32) : Nil
if LibC._wmkdir(System.to_wstr(path)) == -1
raise ::File::Error.from_errno("Unable to create directory", file: path)
if LibC.CreateDirectoryW(System.to_wstr(path), nil) == 0
raise ::File::Error.from_winerror("Unable to create directory", file: path)
end
end

Expand Down Expand Up @@ -179,8 +179,8 @@ module Crystal::System::Dir
end
end

return true if LibC._wrmdir(win_path) == 0
return true if LibC.RemoveDirectoryW(win_path) != 0
LibC.SetFileAttributesW(win_path, attributes) if read_only_removed
raise ::File::Error.from_errno("Unable to remove directory", file: path)
raise ::File::Error.from_winerror("Unable to remove directory", file: path)
end
end
29 changes: 19 additions & 10 deletions src/crystal/system/win32/file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,16 @@ module Crystal::System::File

private def self.posix_to_open_opts(flags : Int32, perm : ::File::Permissions)
access = if flags.bits_set? LibC::O_WRONLY
LibC::GENERIC_WRITE
LibC::FILE_GENERIC_WRITE
elsif flags.bits_set? LibC::O_RDWR
LibC::GENERIC_READ | LibC::GENERIC_WRITE
LibC::FILE_GENERIC_READ | LibC::FILE_GENERIC_WRITE
else
LibC::GENERIC_READ
LibC::FILE_GENERIC_READ
end

if flags.bits_set? LibC::O_APPEND
access |= LibC::FILE_APPEND_DATA
access &= ~LibC::FILE_WRITE_DATA
end

if flags.bits_set? LibC::O_TRUNC
Expand Down Expand Up @@ -277,10 +278,10 @@ module Crystal::System::File
# all reparse point directories should be deleted like a directory, not just
# symbolic links, so we don't care about the reparse tag here
is_reparse_dir = attributes.bits_set?(LibC::FILE_ATTRIBUTE_REPARSE_POINT) && attributes.bits_set?(LibC::FILE_ATTRIBUTE_DIRECTORY)
result = is_reparse_dir ? LibC._wrmdir(win_path) : LibC._wunlink(win_path)
return true if result == 0
result = is_reparse_dir ? LibC.RemoveDirectoryW(win_path) : LibC.DeleteFileW(win_path)
return true if result != 0
LibC.SetFileAttributesW(win_path, attributes) if read_only_removed
raise ::File::Error.from_errno("Error deleting file", file: path)
raise ::File::Error.from_winerror("Error deleting file", file: path)
end

private REALPATH_SYMLINK_LIMIT = 100
Expand Down Expand Up @@ -451,8 +452,16 @@ module Crystal::System::File
end

private def system_truncate(size : Int) : Nil
if LibC._chsize_s(fd, size) != 0
raise ::File::Error.from_errno("Error truncating file", file: path)
handle = windows_handle
if LibC.SetFilePointerEx(handle, size.to_i64, out old_pos, IO::Seek::Set) == 0
raise ::File::Error.from_winerror("Error truncating file", file: path)
end
begin
if LibC.SetEndOfFile(handle) == 0
raise ::File::Error.from_winerror("Error truncating file", file: path)
end
ensure
LibC.SetFilePointerEx(handle, old_pos, nil, IO::Seek::Set)
end
end

Expand Down Expand Up @@ -508,8 +517,8 @@ module Crystal::System::File
end

private def system_fsync(flush_metadata = true) : Nil
if LibC._commit(fd) != 0
raise IO::Error.from_errno("Error syncing file", target: self)
if LibC.FlushFileBuffers(windows_handle) == 0
raise IO::Error.from_winerror("Error syncing file", target: self)
end
end
end
33 changes: 18 additions & 15 deletions src/crystal/system/win32/file_descriptor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ module Crystal::System::FileDescriptor
if ConsoleUtils.console?(handle)
ConsoleUtils.read(handle, slice)
elsif system_blocking?
bytes_read = LibC._read(fd, slice, slice.size)
if bytes_read == -1
if Errno.value == Errno::EBADF
if LibC.ReadFile(handle, slice, slice.size, out bytes_read, nil) == 0
case error = WinError.value
when .error_access_denied?
raise IO::Error.new "File not open for reading", target: self
when .error_broken_pipe?
return 0_u32
else
raise IO::Error.from_errno("Error reading file", target: self)
raise IO::Error.from_os_error("Error reading file", error, target: self)
end
end
bytes_read
Expand All @@ -33,18 +35,20 @@ module Crystal::System::FileDescriptor
end

private def unbuffered_write(slice : Bytes)
handle = windows_handle
until slice.empty?
if system_blocking?
bytes_written = LibC._write(fd, slice, slice.size)
if bytes_written == -1
if Errno.value == Errno::EBADF
if LibC.WriteFile(handle, slice, slice.size, out bytes_written, nil) == 0
case error = WinError.value
when .error_access_denied?
raise IO::Error.new "File not open for writing", target: self
when .error_broken_pipe?
return 0_u32
else
raise IO::Error.from_errno("Error writing file", target: self)
raise IO::Error.from_os_error("Error writing file", error, target: self)
end
end
else
handle = windows_handle
bytes_written = overlapped_operation(handle, "WriteFile", write_timeout, writing: true) do |overlapped|
ret = LibC.WriteFile(handle, slice, slice.size, out byte_count, overlapped)
{ret, byte_count}
Expand Down Expand Up @@ -128,16 +132,15 @@ module Crystal::System::FileDescriptor
end

private def system_seek(offset, whence : IO::Seek) : Nil
seek_value = LibC._lseeki64(fd, offset, whence)

if seek_value == -1
raise IO::Error.from_errno "Unable to seek", target: self
if LibC.SetFilePointerEx(windows_handle, offset, nil, whence) == 0
raise IO::Error.from_winerror("Unable to seek", target: self)
end
end

private def system_pos
pos = LibC._lseeki64(fd, 0, IO::Seek::Current)
raise IO::Error.from_errno("Unable to tell", target: self) if pos == -1
if LibC.SetFilePointerEx(windows_handle, 0, out pos, IO::Seek::Current) == 0
raise IO::Error.from_winerror("Unable to tell", target: self)
end
pos
end

Expand Down
1 change: 1 addition & 0 deletions src/lib_c/x86_64-windows-msvc/c/direct.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require "c/winnt"

lib LibC
# unused
fun _wmkdir(dirname : WCHAR*) : Int
fun _wrmdir(dirname : WCHAR*) : Int
end
7 changes: 7 additions & 0 deletions src/lib_c/x86_64-windows-msvc/c/fileapi.cr
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,16 @@ lib LibC
fun CreateFileW(lpFileName : LPWSTR, dwDesiredAccess : DWORD, dwShareMode : DWORD,
lpSecurityAttributes : SECURITY_ATTRIBUTES*, dwCreationDisposition : DWORD,
dwFlagsAndAttributes : DWORD, hTemplateFile : HANDLE) : HANDLE
fun DeleteFileW(lpFileName : LPWSTR) : BOOL

fun ReadFile(hFile : HANDLE, lpBuffer : Void*, nNumberOfBytesToRead : DWORD, lpNumberOfBytesRead : DWORD*, lpOverlapped : OVERLAPPED*) : BOOL
fun WriteFile(hFile : HANDLE, lpBuffer : Void*, nNumberOfBytesToWrite : DWORD, lpNumberOfBytesWritten : DWORD*, lpOverlapped : OVERLAPPED*) : BOOL
fun SetFilePointerEx(hFile : HANDLE, liDistanceToMove : LARGE_INTEGER, lpNewFilePointer : LARGE_INTEGER*, dwMoveMethod : DWORD) : BOOL
fun SetEndOfFile(hFile : HANDLE) : BOOL
fun FlushFileBuffers(hFile : HANDLE) : BOOL

fun CreateDirectoryW(lpPathName : LPWSTR, lpSecurityAttributes : SECURITY_ATTRIBUTES*) : BOOL
fun RemoveDirectoryW(lpPathName : LPWSTR) : BOOL

MAX_PATH = 260

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

lib LibC
fun _isatty(fd : Int) : Int
fun _close(fd : Int) : Int
fun _waccess_s(path : WCHAR*, mode : Int) : ErrnoT
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
fun _setmode(fd : LibC::Int, mode : LibC::Int) : LibC::Int

# unused
fun _write(fd : Int, buffer : UInt8*, count : UInt) : Int
fun _read(fd : Int, buffer : UInt8*, count : UInt) : Int
fun _lseeki64(fd : Int, offset : Int64, origin : Int) : Int64
fun _isatty(fd : Int) : Int
fun _close(fd : Int) : Int
fun _wopen(filename : WCHAR*, oflag : Int, ...) : Int
fun _waccess_s(path : WCHAR*, mode : Int) : ErrnoT
fun _wchmod(filename : WCHAR*, pmode : Int) : Int
fun _wunlink(filename : WCHAR*) : Int
fun _wmktemp_s(template : WCHAR*, sizeInChars : SizeT) : ErrnoT
fun _wexecvp(cmdname : WCHAR*, argv : WCHAR**) : IntPtrT
fun _chsize_s(fd : Int, size : Int64) : ErrnoT
fun _get_osfhandle(fd : Int) : IntPtrT
fun _pipe(pfds : Int*, psize : UInt, textmode : Int) : Int
fun _dup2(fd1 : Int, fd2 : Int) : Int
fun _commit(fd : Int) : Int
fun _open_osfhandle(osfhandle : HANDLE, flags : LibC::Int) : LibC::Int
fun _setmode(fd : LibC::Int, mode : LibC::Int) : LibC::Int
end
1 change: 1 addition & 0 deletions src/lib_c/x86_64-windows-msvc/c/stdio.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ require "./stddef"

@[Link("legacy_stdio_definitions")]
lib LibC
# unused
fun printf(format : Char*, ...) : Int
fun rename(old : Char*, new : Char*) : Int
fun snprintf(buffer : Char*, count : SizeT, format : Char*, ...) : Int
Expand Down
8 changes: 5 additions & 3 deletions src/lib_c/x86_64-windows-msvc/c/stdlib.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ lib LibC
rem : Int
end

fun atof(nptr : Char*) : Double
fun div(numer : Int, denom : Int) : DivT
fun exit(status : Int) : NoReturn
fun _exit(status : Int) : NoReturn
fun free(ptr : Void*) : Void
fun malloc(size : SizeT) : Void*
fun putenv(string : Char*) : Int
fun realloc(ptr : Void*, size : SizeT) : Void*
fun strtof(nptr : Char*, endptr : Char**) : Float
fun strtod(nptr : Char*, endptr : Char**) : Double

alias InvalidParameterHandler = WCHAR*, WCHAR*, WCHAR*, UInt, UIntPtrT ->
fun _set_invalid_parameter_handler(pNew : InvalidParameterHandler) : InvalidParameterHandler

# unused
fun atof(nptr : Char*) : Double
fun div(numer : Int, denom : Int) : DivT
fun putenv(string : Char*) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/x86_64-windows-msvc/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ lib LibC
st_ctime : Time64T
end

# unused
fun _wstat64(path : WCHAR*, buffer : Stat64*) : Int
fun _fstat64(fd : Int, buffer : Stat64*) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/x86_64-windows-msvc/c/sys/utime.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ lib LibC
modtime : Time64T
end

# unused
fun _wutime64(filename : WCHAR*, times : Utimbuf64*) : Int
end
13 changes: 8 additions & 5 deletions src/lib_c/x86_64-windows-msvc/c/winnt.cr
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ lib LibC
FILE_ATTRIBUTE_REPARSE_POINT = 0x400
FILE_ATTRIBUTE_SYSTEM = 0x4

FILE_APPEND_DATA = 0x00000004

DELETE = 0x00010000
FILE_READ_ATTRIBUTES = 0x80
FILE_WRITE_ATTRIBUTES = 0x0100
DELETE = 0x00010000_u32

FILE_WRITE_DATA = 0x00000002_u32
FILE_APPEND_DATA = 0x00000004_u32
FILE_READ_ATTRIBUTES = 0x00000080_u32
FILE_WRITE_ATTRIBUTES = 0x00000100_u32
FILE_GENERIC_READ = 0x00120089_u32
FILE_GENERIC_WRITE = 0x00120116_u32

MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 0x4000

Expand Down