From 6873663d61d679604c0377579f8ad13b84a599a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 31 May 2022 22:02:31 +0200 Subject: [PATCH 1/7] Extract low-level `Crystal::System::File.open` variant --- src/crystal/system/unix/file.cr | 14 ++++++++++---- src/crystal/system/win32/file.cr | 11 ++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/crystal/system/unix/file.cr b/src/crystal/system/unix/file.cr index 19bf9df8f1f3..136cda60867e 100644 --- a/src/crystal/system/unix/file.cr +++ b/src/crystal/system/unix/file.cr @@ -3,16 +3,22 @@ require "file/error" # :nodoc: module Crystal::System::File - def self.open(filename, mode, perm) - oflag = open_flag(mode) | LibC::O_CLOEXEC - - fd = LibC.open(filename.check_no_null_byte, oflag, perm) + def self.open(filename : String, mode : String, perm : Int32 | ::File::Permissions) + perm = ::File::Permissions.new(perm) if perm.is_a? Int32 + fd = open(filename, open_flag(mode), perm) if fd < 0 raise ::File::Error.from_errno("Error opening file with mode '#{mode}'", file: filename) end fd end + def self.open(filename : String, flags : Int32, perm : ::File::Permissions) : LibC::Int + filename.check_no_null_byte + flags |= LibC::O_CLOEXEC + + LibC.open(filename, flags, perm) + end + def self.mktemp(prefix, suffix, dir) : {LibC::Int, String} prefix.try &.check_no_null_byte suffix.try &.check_no_null_byte diff --git a/src/crystal/system/win32/file.cr b/src/crystal/system/win32/file.cr index e9d6d2502cf7..432224a6ae0f 100644 --- a/src/crystal/system/win32/file.cr +++ b/src/crystal/system/win32/file.cr @@ -9,8 +9,6 @@ require "c/handleapi" module Crystal::System::File def self.open(filename : String, mode : String, perm : Int32 | ::File::Permissions) : LibC::Int perm = ::File::Permissions.new(perm) if perm.is_a? Int32 - oflag = open_flag(mode) | LibC::O_BINARY | LibC::O_NOINHERIT - # Only the owner writable bit is used, since windows only supports # the read only attribute. if perm.owner_write? @@ -19,7 +17,7 @@ module Crystal::System::File perm = LibC::S_IREAD end - fd = LibC._wopen(to_windows_path(filename), oflag, perm) + fd = open(filename, open_flag(mode), perm) if fd == -1 raise ::File::Error.from_errno("Error opening file with mode '#{mode}'", file: filename) end @@ -27,6 +25,13 @@ module Crystal::System::File fd end + def self.open(filename : String, flags : Int32, perm : ::File::Permissions) : LibC::Int + flags |= LibC::O_BINARY | LibC::O_NOINHERIT + + fd = LibC._wopen(to_windows_path(filename), flags, perm) + fd + end + def self.mktemp(prefix : String?, suffix : String?, dir : String) : {LibC::Int, String} path = "#{dir}#{::File::SEPARATOR}#{prefix}.#{::Random::Secure.hex}#{suffix}" From 18298033db17846e4f5233629d31fadd5e606205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 31 May 2022 22:08:54 +0200 Subject: [PATCH 2/7] Add specs for tempfile permissions --- spec/std/file/tempfile_spec.cr | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/spec/std/file/tempfile_spec.cr b/spec/std/file/tempfile_spec.cr index 9a69e7466bca..a671f87f3027 100644 --- a/spec/std/file/tempfile_spec.cr +++ b/spec/std/file/tempfile_spec.cr @@ -1,5 +1,16 @@ require "../spec_helper" +private def normalize_permissions(permissions, *, directory) + {% if flag?(:win32) %} + normalized_permissions = 0o444 + normalized_permissions |= 0o222 if permissions.bits_set?(0o200) + normalized_permissions |= 0o111 if directory + File::Permissions.new(normalized_permissions) + {% else %} + File::Permissions.new(permissions) + {% end %} +end + describe File do describe ".tempname" do it "creates a path without creating the file" do @@ -42,6 +53,7 @@ describe File do it "creates and writes" do tempfile = File.tempfile tempfile.print "Hello!" + tempfile.info.permissions.should eq normalize_permissions(0o600, directory: false) tempfile.close File.exists?(tempfile.path).should be_true @@ -53,6 +65,7 @@ describe File do it "accepts single suffix argument" do tempfile = File.tempfile ".bar" tempfile.print "Hello!" + tempfile.info.permissions.should eq normalize_permissions(0o600, directory: false) tempfile.close File.extname(tempfile.path).should eq(".bar") @@ -66,6 +79,7 @@ describe File do it "accepts prefix and suffix arguments" do tempfile = File.tempfile "foo", ".bar" tempfile.print "Hello!" + tempfile.info.permissions.should eq normalize_permissions(0o600, directory: false) tempfile.close File.extname(tempfile.path).should eq(".bar") From 3d74c03ce00c7d518c4c6172e2cf1f268a4b1040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Wed, 8 Jun 2022 23:38:27 +0200 Subject: [PATCH 3/7] Add `LibC::O_EXCL` --- src/lib_c/aarch64-darwin/c/fcntl.cr | 1 + src/lib_c/aarch64-linux-gnu/c/fcntl.cr | 1 + src/lib_c/aarch64-linux-musl/c/fcntl.cr | 1 + src/lib_c/arm-linux-gnueabihf/c/fcntl.cr | 1 + src/lib_c/i386-linux-gnu/c/fcntl.cr | 1 + src/lib_c/i386-linux-musl/c/fcntl.cr | 1 + src/lib_c/wasm32-wasi/c/fcntl.cr | 1 + src/lib_c/x86_64-darwin/c/fcntl.cr | 1 + src/lib_c/x86_64-dragonfly/c/fcntl.cr | 1 + src/lib_c/x86_64-freebsd/c/fcntl.cr | 1 + src/lib_c/x86_64-linux-gnu/c/fcntl.cr | 1 + src/lib_c/x86_64-linux-musl/c/fcntl.cr | 1 + src/lib_c/x86_64-netbsd/c/fcntl.cr | 1 + src/lib_c/x86_64-openbsd/c/fcntl.cr | 1 + 14 files changed, 14 insertions(+) diff --git a/src/lib_c/aarch64-darwin/c/fcntl.cr b/src/lib_c/aarch64-darwin/c/fcntl.cr index ea632f5512c4..cf6ce527a729 100644 --- a/src/lib_c/aarch64-darwin/c/fcntl.cr +++ b/src/lib_c/aarch64-darwin/c/fcntl.cr @@ -12,6 +12,7 @@ lib LibC O_CREAT = 0x0200 O_NOFOLLOW = 0x0100 O_TRUNC = 0x0400 + O_EXCL = 0x0800 O_APPEND = 0x0008 O_NONBLOCK = 0x0004 O_SYNC = 0x0080 diff --git a/src/lib_c/aarch64-linux-gnu/c/fcntl.cr b/src/lib_c/aarch64-linux-gnu/c/fcntl.cr index 9e74bbc4f536..e52f375d8dc4 100644 --- a/src/lib_c/aarch64-linux-gnu/c/fcntl.cr +++ b/src/lib_c/aarch64-linux-gnu/c/fcntl.cr @@ -10,6 +10,7 @@ lib LibC FD_CLOEXEC = 1 O_CLOEXEC = 0o2000000 O_CREAT = 0o100 + O_EXCL = 0o200 O_NOFOLLOW = 0o100000 O_TRUNC = 0o1000 O_APPEND = 0o2000 diff --git a/src/lib_c/aarch64-linux-musl/c/fcntl.cr b/src/lib_c/aarch64-linux-musl/c/fcntl.cr index 5633ae3241a6..7664c411a36c 100644 --- a/src/lib_c/aarch64-linux-musl/c/fcntl.cr +++ b/src/lib_c/aarch64-linux-musl/c/fcntl.cr @@ -10,6 +10,7 @@ lib LibC FD_CLOEXEC = 1 O_CLOEXEC = 0o2000000 O_CREAT = 0o100 + O_EXCL = 0o200 O_NOFOLLOW = 0o100000 O_TRUNC = 0o1000 O_APPEND = 0o2000 diff --git a/src/lib_c/arm-linux-gnueabihf/c/fcntl.cr b/src/lib_c/arm-linux-gnueabihf/c/fcntl.cr index 9e74bbc4f536..e52f375d8dc4 100644 --- a/src/lib_c/arm-linux-gnueabihf/c/fcntl.cr +++ b/src/lib_c/arm-linux-gnueabihf/c/fcntl.cr @@ -10,6 +10,7 @@ lib LibC FD_CLOEXEC = 1 O_CLOEXEC = 0o2000000 O_CREAT = 0o100 + O_EXCL = 0o200 O_NOFOLLOW = 0o100000 O_TRUNC = 0o1000 O_APPEND = 0o2000 diff --git a/src/lib_c/i386-linux-gnu/c/fcntl.cr b/src/lib_c/i386-linux-gnu/c/fcntl.cr index 2a12d9b4857f..cea8630785da 100644 --- a/src/lib_c/i386-linux-gnu/c/fcntl.cr +++ b/src/lib_c/i386-linux-gnu/c/fcntl.cr @@ -10,6 +10,7 @@ lib LibC FD_CLOEXEC = 1 O_CLOEXEC = 0o2000000 O_CREAT = 0o100 + O_EXCL = 0o200 O_NOFOLLOW = 0o400000 O_TRUNC = 0o1000 O_APPEND = 0o2000 diff --git a/src/lib_c/i386-linux-musl/c/fcntl.cr b/src/lib_c/i386-linux-musl/c/fcntl.cr index 6beab0d6de74..27a5cf0c22d3 100644 --- a/src/lib_c/i386-linux-musl/c/fcntl.cr +++ b/src/lib_c/i386-linux-musl/c/fcntl.cr @@ -10,6 +10,7 @@ lib LibC FD_CLOEXEC = 1 O_CLOEXEC = 0o2000000 O_CREAT = 0o100 + O_EXCL = 0o200 O_NOFOLLOW = 0o400000 O_TRUNC = 0o1000 O_APPEND = 0o2000 diff --git a/src/lib_c/wasm32-wasi/c/fcntl.cr b/src/lib_c/wasm32-wasi/c/fcntl.cr index 7bbfe10f23ec..029a5721cfdb 100644 --- a/src/lib_c/wasm32-wasi/c/fcntl.cr +++ b/src/lib_c/wasm32-wasi/c/fcntl.cr @@ -10,6 +10,7 @@ lib LibC FD_CLOEXEC = 1 O_CLOEXEC = 0 O_CREAT = 1_u16 << 12 + O_EXCL = 4_u16 << 12 O_NOFOLLOW = 0x01000000 O_TRUNC = 8_u16 << 12 O_APPEND = 1_u16 diff --git a/src/lib_c/x86_64-darwin/c/fcntl.cr b/src/lib_c/x86_64-darwin/c/fcntl.cr index ea632f5512c4..cf6ce527a729 100644 --- a/src/lib_c/x86_64-darwin/c/fcntl.cr +++ b/src/lib_c/x86_64-darwin/c/fcntl.cr @@ -12,6 +12,7 @@ lib LibC O_CREAT = 0x0200 O_NOFOLLOW = 0x0100 O_TRUNC = 0x0400 + O_EXCL = 0x0800 O_APPEND = 0x0008 O_NONBLOCK = 0x0004 O_SYNC = 0x0080 diff --git a/src/lib_c/x86_64-dragonfly/c/fcntl.cr b/src/lib_c/x86_64-dragonfly/c/fcntl.cr index ec8962397ac5..c9b832e2e919 100644 --- a/src/lib_c/x86_64-dragonfly/c/fcntl.cr +++ b/src/lib_c/x86_64-dragonfly/c/fcntl.cr @@ -9,6 +9,7 @@ lib LibC F_SETFL = 4 FD_CLOEXEC = 1 O_CLOEXEC = 0x20000 + O_EXCL = 0x0800 O_TRUNC = 0x0400 O_CREAT = 0x0200 O_NOFOLLOW = 0x0100 diff --git a/src/lib_c/x86_64-freebsd/c/fcntl.cr b/src/lib_c/x86_64-freebsd/c/fcntl.cr index b6f2912bad8c..d5c507efac29 100644 --- a/src/lib_c/x86_64-freebsd/c/fcntl.cr +++ b/src/lib_c/x86_64-freebsd/c/fcntl.cr @@ -12,6 +12,7 @@ lib LibC O_CREAT = 0x0200 O_NOFOLLOW = 0x0100 O_TRUNC = 0x0400 + O_EXCL = 0x0800 O_APPEND = 0x0008 O_NONBLOCK = 0x0004 O_SYNC = 0x0080 diff --git a/src/lib_c/x86_64-linux-gnu/c/fcntl.cr b/src/lib_c/x86_64-linux-gnu/c/fcntl.cr index 1a8fd2f1787b..7f46cb647918 100644 --- a/src/lib_c/x86_64-linux-gnu/c/fcntl.cr +++ b/src/lib_c/x86_64-linux-gnu/c/fcntl.cr @@ -10,6 +10,7 @@ lib LibC FD_CLOEXEC = 1 O_CLOEXEC = 0o2000000 O_CREAT = 0o100 + O_EXCL = 0o200 O_NOFOLLOW = 0o400000 O_TRUNC = 0o1000 O_APPEND = 0o2000 diff --git a/src/lib_c/x86_64-linux-musl/c/fcntl.cr b/src/lib_c/x86_64-linux-musl/c/fcntl.cr index 6beab0d6de74..27a5cf0c22d3 100644 --- a/src/lib_c/x86_64-linux-musl/c/fcntl.cr +++ b/src/lib_c/x86_64-linux-musl/c/fcntl.cr @@ -10,6 +10,7 @@ lib LibC FD_CLOEXEC = 1 O_CLOEXEC = 0o2000000 O_CREAT = 0o100 + O_EXCL = 0o200 O_NOFOLLOW = 0o400000 O_TRUNC = 0o1000 O_APPEND = 0o2000 diff --git a/src/lib_c/x86_64-netbsd/c/fcntl.cr b/src/lib_c/x86_64-netbsd/c/fcntl.cr index 76ff615bef36..3a1ffe9d85c6 100644 --- a/src/lib_c/x86_64-netbsd/c/fcntl.cr +++ b/src/lib_c/x86_64-netbsd/c/fcntl.cr @@ -12,6 +12,7 @@ lib LibC O_CREAT = 0x0200 O_NOFOLLOW = 0x0100 O_TRUNC = 0x0400 + O_EXCL = 0x0800 O_APPEND = 0x0008 O_NONBLOCK = 0x0004 O_SYNC = 0x0080 diff --git a/src/lib_c/x86_64-openbsd/c/fcntl.cr b/src/lib_c/x86_64-openbsd/c/fcntl.cr index ec28cf280dce..6de726e50bf5 100644 --- a/src/lib_c/x86_64-openbsd/c/fcntl.cr +++ b/src/lib_c/x86_64-openbsd/c/fcntl.cr @@ -12,6 +12,7 @@ lib LibC O_CREAT = 0x0200 O_NOFOLLOW = 0x0100 O_TRUNC = 0x0400 + O_EXCL = 0x0800 O_APPEND = 0x0008 O_NONBLOCK = 0x0004 O_SYNC = 0x0080 From f5bd3d54b9c2396bd4a5c1c1641694588402d2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Wed, 8 Jun 2022 23:39:34 +0200 Subject: [PATCH 4/7] Add `File.from_fd` --- src/file.cr | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/file.cr b/src/file.cr index 39776b5824cc..e772c4dcc4a4 100644 --- a/src/file.cr +++ b/src/file.cr @@ -94,6 +94,11 @@ class File < IO::FileDescriptor super(fd, blocking) end + # :nodoc: + def self.from_fd(path : String, fd : Int, *, blocking = false, encoding = nil, invalid = nil) + new(path, fd, blocking: blocking, encoding: encoding, invalid: invalid) + end + # Opens the file named by *filename*. # # *mode* must be one of the following file open modes: From 860b89b7ac548824dccd3b2d300b858b8dc10ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Wed, 8 Jun 2022 23:43:54 +0200 Subject: [PATCH 5/7] Implement `Crystal::System::File.mktemp` --- spec/std/file/tempfile_spec.cr | 57 ++++++++++++++++++++++++++++++++ src/crystal/system/file.cr | 37 +++++++++++++++++++++ src/crystal/system/unix/file.cr | 31 +++++------------ src/crystal/system/wasi/file.cr | 4 --- src/crystal/system/win32/file.cr | 21 +++--------- 5 files changed, 108 insertions(+), 42 deletions(-) diff --git a/spec/std/file/tempfile_spec.cr b/spec/std/file/tempfile_spec.cr index a671f87f3027..21652a40ab59 100644 --- a/spec/std/file/tempfile_spec.cr +++ b/spec/std/file/tempfile_spec.cr @@ -1,4 +1,23 @@ require "../spec_helper" +require "../../support/tempfile" + +private class TestRNG(T) + include Random + + def initialize(@data : Array(T)) + @i = 0 + end + + def next_u : T + i = @i + @i = (i + 1) % @data.size + @data[i] + end + + def reset + @i = 0 + end +end private def normalize_permissions(permissions, *, directory) {% if flag?(:win32) %} @@ -170,3 +189,41 @@ describe File do end end end + +describe Crystal::System::File do + describe ".mktemp" do + it "creates random file name" do + with_tempfile "random-path" do |tempdir| + Dir.mkdir tempdir + fd, path = Crystal::System::File.mktemp("A", "Z", dir: tempdir, random: TestRNG.new([7, 8, 9, 10, 11, 12, 13, 14])) + path.should eq Path[tempdir, "A789abcdeZ"].to_s + ensure + File.from_fd(path, fd).close if fd && path + end + end + + it "retries when file exists" do + with_tempfile "retry" do |tempdir| + Dir.mkdir tempdir + existing_path = Path[tempdir, "A789abcdeZ"] + File.touch existing_path + fd, path = Crystal::System::File.mktemp("A", "Z", dir: tempdir, random: TestRNG.new([7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23])) + path.should eq File.join(tempdir, "AfghijklmZ") + ensure + File.from_fd(path, fd).close if fd && path + end + end + + it "raises when no valid path is found" do + with_tempfile "random-path" do |tempdir| + Dir.mkdir tempdir + File.touch Path[tempdir, "A789abcdeZ"] + expect_raises(File::AlreadyExistsError, "Error creating temporary file") do + fd, path = Crystal::System::File.mktemp("A", "Z", dir: tempdir, random: TestRNG.new([7, 8, 9, 10, 11, 12, 13, 14])) + ensure + File.from_fd(path, fd).close if fd && path + end + end + end + end +end diff --git a/src/crystal/system/file.cr b/src/crystal/system/file.cr index 504b578dd1b3..3e8f82214fef 100644 --- a/src/crystal/system/file.cr +++ b/src/crystal/system/file.cr @@ -47,6 +47,43 @@ module Crystal::System::File m | o end + LOWER_ALPHANUM = "0123456789abcdefghijklmnopqrstuvwxyz".to_slice + + def self.mktemp(prefix : String?, suffix : String?, dir : String, random : ::Random = ::Random::DEFAULT) : {LibC::Int, String} + mode = LibC::O_RDWR | LibC::O_CREAT | LibC::O_EXCL + perm = ::File::Permissions.new(0o600) + + prefix = ::File.join(dir, prefix || "") + + 100.times do + bytesize = prefix.bytesize + 8 + (suffix.try(&.bytesize) || 0) + path = String.build(bytesize) do |io| + io << prefix + 8.times do + io.write_byte LOWER_ALPHANUM.sample(random) + end + io << suffix + end + + fd, errno = open(path, mode, perm) + + if errno.none? + return {fd, path} + elsif error_is_file_exists?(errno) + # retry + next + else + raise ::File::Error.from_os_error("Error creating temporary file", errno, file: path) + end + end + + raise ::File::AlreadyExistsError.new("Error creating temporary file", file: "#{prefix}********#{suffix}") + end + + private def self.error_is_file_exists?(errno) + Errno.value.in?(Errno::EEXIST, WinError::ERROR_ALREADY_EXISTS) + end + # Closes the internal file descriptor without notifying libevent. # This is directly used after the fork of a process to close the # parent's Crystal::Signal.@@pipe reference before re initializing diff --git a/src/crystal/system/unix/file.cr b/src/crystal/system/unix/file.cr index 136cda60867e..f26ce9912daa 100644 --- a/src/crystal/system/unix/file.cr +++ b/src/crystal/system/unix/file.cr @@ -5,36 +5,23 @@ require "file/error" module Crystal::System::File def self.open(filename : String, mode : String, perm : Int32 | ::File::Permissions) perm = ::File::Permissions.new(perm) if perm.is_a? Int32 - fd = open(filename, open_flag(mode), perm) - if fd < 0 - raise ::File::Error.from_errno("Error opening file with mode '#{mode}'", file: filename) + + fd, errno = open(filename, open_flag(mode), perm) + + unless errno.none? + raise ::File::Error.from_os_error("Error opening file with mode '#{mode}'", errno, file: filename) end + fd end - def self.open(filename : String, flags : Int32, perm : ::File::Permissions) : LibC::Int + def self.open(filename : String, flags : Int32, perm : ::File::Permissions) : {LibC::Int, Errno} filename.check_no_null_byte flags |= LibC::O_CLOEXEC - LibC.open(filename, flags, perm) - end - - def self.mktemp(prefix, suffix, dir) : {LibC::Int, String} - prefix.try &.check_no_null_byte - suffix.try &.check_no_null_byte - dir.check_no_null_byte - - dir = dir + ::File::SEPARATOR - path = "#{dir}#{prefix}.XXXXXX#{suffix}" - - if suffix - fd = LibC.mkstemps(path, suffix.bytesize) - else - fd = LibC.mkstemp(path) - end + fd = LibC.open(filename, flags, perm) - raise ::File::Error.from_errno("Error creating temporary file", file: path) if fd == -1 - {fd, path} + {fd, fd < 0 ? Errno.value : Errno::NONE} end def self.info?(path : String, follow_symlinks : Bool) : ::File::Info? diff --git a/src/crystal/system/wasi/file.cr b/src/crystal/system/wasi/file.cr index ef8d90458728..8c06a2a940f6 100644 --- a/src/crystal/system/wasi/file.cr +++ b/src/crystal/system/wasi/file.cr @@ -34,10 +34,6 @@ module Crystal::System::File raise NotImplementedError.new "Crystal::System::File#flock" end - def self.mktemp(prefix, suffix, dir) : {LibC::Int, String} - raise NotImplementedError.new "Crystal::System::File.mktemp" - end - def self.delete(path : String, *, raise_on_missing : Bool) : Bool raise NotImplementedError.new "Crystal::System::File.delete" end diff --git a/src/crystal/system/win32/file.cr b/src/crystal/system/win32/file.cr index 432224a6ae0f..9e7acfc38aa4 100644 --- a/src/crystal/system/win32/file.cr +++ b/src/crystal/system/win32/file.cr @@ -17,31 +17,20 @@ module Crystal::System::File perm = LibC::S_IREAD end - fd = open(filename, open_flag(mode), perm) - if fd == -1 - raise ::File::Error.from_errno("Error opening file with mode '#{mode}'", file: filename) + 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) end fd end - def self.open(filename : String, flags : Int32, perm : ::File::Permissions) : LibC::Int + def self.open(filename : String, flags : Int32, perm : ::File::Permissions) : {LibC::Int, Errno} flags |= LibC::O_BINARY | LibC::O_NOINHERIT fd = LibC._wopen(to_windows_path(filename), flags, perm) - fd - end - - def self.mktemp(prefix : String?, suffix : String?, dir : String) : {LibC::Int, String} - path = "#{dir}#{::File::SEPARATOR}#{prefix}.#{::Random::Secure.hex}#{suffix}" - - mode = LibC::O_RDWR | LibC::O_CREAT | LibC::O_EXCL | LibC::O_BINARY | LibC::O_NOINHERIT - fd = LibC._wopen(to_windows_path(path), mode, ::File::DEFAULT_CREATE_PERMISSIONS) - if fd == -1 - raise ::File::Error.from_errno("Error creating temporary file", file: path) - end - {fd, path} + {fd, fd == -1 ? Errno.value : Errno::NONE} end NOT_FOUND_ERRORS = { From 1b1fa0a5b9bb498b33377264ad9b665974afa0ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 14 Feb 2023 18:14:09 +0100 Subject: [PATCH 6/7] Update src/crystal/system/file.cr Co-authored-by: Quinton Miller --- src/crystal/system/file.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crystal/system/file.cr b/src/crystal/system/file.cr index 3e8f82214fef..b5202585d377 100644 --- a/src/crystal/system/file.cr +++ b/src/crystal/system/file.cr @@ -54,9 +54,9 @@ module Crystal::System::File perm = ::File::Permissions.new(0o600) prefix = ::File.join(dir, prefix || "") + bytesize = prefix.bytesize + 8 + (suffix.try(&.bytesize) || 0) 100.times do - bytesize = prefix.bytesize + 8 + (suffix.try(&.bytesize) || 0) path = String.build(bytesize) do |io| io << prefix 8.times do From da1491dd1e3754a7895bba033833a4c1774c15cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Tue, 14 Feb 2023 18:16:42 +0100 Subject: [PATCH 7/7] Remove excessive 23 --- spec/std/file/tempfile_spec.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/std/file/tempfile_spec.cr b/spec/std/file/tempfile_spec.cr index 21652a40ab59..aac486bacf40 100644 --- a/spec/std/file/tempfile_spec.cr +++ b/spec/std/file/tempfile_spec.cr @@ -207,7 +207,7 @@ describe Crystal::System::File do Dir.mkdir tempdir existing_path = Path[tempdir, "A789abcdeZ"] File.touch existing_path - fd, path = Crystal::System::File.mktemp("A", "Z", dir: tempdir, random: TestRNG.new([7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23])) + fd, path = Crystal::System::File.mktemp("A", "Z", dir: tempdir, random: TestRNG.new([7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22])) path.should eq File.join(tempdir, "AfghijklmZ") ensure File.from_fd(path, fd).close if fd && path