diff --git a/spec/std/file_spec.cr b/spec/std/file_spec.cr index 104cf3e16186..8513b3bebacf 100644 --- a/spec/std/file_spec.cr +++ b/spec/std/file_spec.cr @@ -517,6 +517,15 @@ describe "File" do end end + it "deletes an open file" do + with_tempfile("delete-file.txt") do |filename| + file = File.open filename, "w" + File.exists?(file.path).should be_true + file.delete + File.exists?(file.path).should be_false + end + end + it "deletes? a file" do with_tempfile("delete-file.txt") do |filename| File.open(filename, "w") { } diff --git a/src/crystal/system/file.cr b/src/crystal/system/file.cr index 0f645fd8a3ec..89d29512c28e 100644 --- a/src/crystal/system/file.cr +++ b/src/crystal/system/file.cr @@ -81,7 +81,7 @@ module Crystal::System::File end private def self.error_is_file_exists?(errno) - Errno.value.in?(Errno::EEXIST, WinError::ERROR_ALREADY_EXISTS) + errno.in?(Errno::EEXIST, WinError::ERROR_ALREADY_EXISTS) end # Closes the internal file descriptor without notifying libevent. diff --git a/src/crystal/system/win32/file.cr b/src/crystal/system/win32/file.cr index 225a345723cc..49ebf92702ae 100644 --- a/src/crystal/system/win32/file.cr +++ b/src/crystal/system/win32/file.cr @@ -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 = self.posix_to_open_opts flags, perm - fd = LibC._wopen(System.to_wstr(filename), flags, perm) + handle = LibC.CreateFileW( + System.to_wstr(filename), + access, + LibC::DEFAULT_SHARE_MODE, # UNIX semantics + 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 + return {-1, WinError.value.error_file_exists? ? Errno::EEXIST : Errno.value} + end + + fd = LibC._open_osfhandle handle, flags + + if fd == -1 + return {-1, Errno.value} + end + + # Only binary mode is supported + LibC._setmode fd, LibC::O_BINARY + + {fd, Errno::NONE} + end + + private def self.posix_to_open_opts(flags : Int32, perm : ::File::Permissions) + access = if flags.bits_set? LibC::O_WRONLY + LibC::GENERIC_WRITE + elsif flags.bits_set? LibC::O_RDWR + LibC::GENERIC_READ | LibC::GENERIC_WRITE + else + LibC::GENERIC_READ + end + + if flags.bits_set? LibC::O_APPEND + access |= LibC::FILE_APPEND_DATA + end + + if flags.bits_set? LibC::O_TRUNC + if flags.bits_set? LibC::O_CREAT + disposition = LibC::CREATE_ALWAYS + else + disposition = LibC::TRUNCATE_EXISTING + end + elsif flags.bits_set? LibC::O_CREAT + if flags.bits_set? LibC::O_EXCL + disposition = LibC::CREATE_NEW + else + disposition = LibC::OPEN_ALWAYS + end + else + disposition = LibC::OPEN_EXISTING + end + + attributes = LibC::FILE_ATTRIBUTE_NORMAL + unless perm.owner_write? + attributes |= LibC::FILE_ATTRIBUTE_READONLY + end + + if flags.bits_set? LibC::O_TEMPORARY + attributes |= LibC::FILE_FLAG_DELETE_ON_CLOSE | LibC::FILE_ATTRIBUTE_TEMPORARY + access |= LibC::DELETE + end + + if flags.bits_set? LibC::O_SHORT_LIVED + attributes |= LibC::FILE_ATTRIBUTE_TEMPORARY + end + + if flags.bits_set? LibC::O_SEQUENTIAL + attributes |= LibC::FILE_FLAG_SEQUENTIAL_SCAN + elsif flags.bits_set? LibC::O_RANDOM + attributes |= LibC::FILE_FLAG_RANDOM_ACCESS + end - {fd, fd == -1 ? Errno.value : Errno::NONE} + {access, disposition, attributes} end NOT_FOUND_ERRORS = { diff --git a/src/lib_c/x86_64-windows-msvc/c/fileapi.cr b/src/lib_c/x86_64-windows-msvc/c/fileapi.cr index 701a9590ca22..9b88b5341c09 100644 --- a/src/lib_c/x86_64-windows-msvc/c/fileapi.cr +++ b/src/lib_c/x86_64-windows-msvc/c/fileapi.cr @@ -41,15 +41,30 @@ lib LibC fun SetFileAttributesW(lpFileName : LPWSTR, dwFileAttributes : DWORD) : BOOL fun GetFileAttributesExW(lpFileName : LPWSTR, fInfoLevelId : GET_FILEEX_INFO_LEVELS, lpFileInformation : Void*) : BOOL - OPEN_EXISTING = 3 + CREATE_NEW = 1 + CREATE_ALWAYS = 2 + OPEN_EXISTING = 3 + OPEN_ALWAYS = 4 + TRUNCATE_EXISTING = 5 - FILE_ATTRIBUTE_NORMAL = 0x80 - FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 + FILE_ATTRIBUTE_NORMAL = 0x80 + FILE_ATTRIBUTE_TEMPORARY = 0x100 + + FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 + FILE_FLAG_DELETE_ON_CLOSE = 0x04000000 + FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000 + FILE_FLAG_RANDOM_ACCESS = 0x10000000 + FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000 FILE_SHARE_READ = 0x1 FILE_SHARE_WRITE = 0x2 FILE_SHARE_DELETE = 0x4 + DEFAULT_SHARE_MODE = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE + + GENERIC_READ = 0x80000000 + GENERIC_WRITE = 0x40000000 + fun CreateFileW(lpFileName : LPWSTR, dwDesiredAccess : DWORD, dwShareMode : DWORD, lpSecurityAttributes : SECURITY_ATTRIBUTES*, dwCreationDisposition : DWORD, dwFlagsAndAttributes : DWORD, hTemplateFile : HANDLE) : HANDLE diff --git a/src/lib_c/x86_64-windows-msvc/c/io.cr b/src/lib_c/x86_64-windows-msvc/c/io.cr index dc3dfd345523..94a5b7b582a3 100644 --- a/src/lib_c/x86_64-windows-msvc/c/io.cr +++ b/src/lib_c/x86_64-windows-msvc/c/io.cr @@ -16,4 +16,6 @@ lib LibC 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 diff --git a/src/lib_c/x86_64-windows-msvc/c/winnt.cr b/src/lib_c/x86_64-windows-msvc/c/winnt.cr index edd247c2c723..36c887f78ba7 100644 --- a/src/lib_c/x86_64-windows-msvc/c/winnt.cr +++ b/src/lib_c/x86_64-windows-msvc/c/winnt.cr @@ -19,8 +19,11 @@ lib LibC FILE_ATTRIBUTE_READONLY = 0x1 FILE_ATTRIBUTE_REPARSE_POINT = 0x400 - FILE_READ_ATTRIBUTES = 0x80 - FILE_WRITE_ATTRIBUTES = 0x0100 + FILE_APPEND_DATA = 0x00000004 + + DELETE = 0x00010000 + FILE_READ_ATTRIBUTES = 0x80 + FILE_WRITE_ATTRIBUTES = 0x0100 # Memory protection constants PAGE_READWRITE = 0x04