From c20c806344a8af6082c3bd712faa95771a2a99c1 Mon Sep 17 00:00:00 2001 From: Mustafa Date: Thu, 7 Nov 2019 15:57:46 -0500 Subject: [PATCH 1/5] Use UIUD to create random tempname on Windows --- base/file.jl | 49 +++++++++++++++++++++++++++++++++++++------------ test/file.jl | 12 ++++++++++++ 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/base/file.jl b/base/file.jl index 094327922dcc5..96a170f77f782 100644 --- a/base/file.jl +++ b/base/file.jl @@ -517,20 +517,45 @@ function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true) return (filename, Base.open(filename, "r+")) end +# GUID win32 api (consider # TODO: refactor) +struct GUID + l1::Culong + w1::Cushort + w2::Cushort + b::NTuple{8,Cuchar} +end + +# generates a random temporary string based on a UUID +function _uiud_string() + id = Ref{GUID}() + r = ccall((:UuidCreate,:Rpcrt4), stdcall, Cint, (Ref{GUID},), id) + # ignore expected errors that we don't care about (these shouldn't even be an issue after Vista) + RPC_S_UUID_LOCAL_ONLY = 1824 + RPC_S_UUID_NO_ADDRESS = 1739 + if !(r == 0 || r == RPC_S_UUID_LOCAL_ONLY || r == RPC_S_UUID_NO_ADDRESS) + windowserror("UuidCreate", r % UInt32) # Throw on unexpected errors. Note, a RPC_STATUS is just a WINERROR with a different signedness of the type. + end + + nameptr = Ref{Ptr{Cwchar_t}}() + r = ccall((:UuidToStringW, :Rpcrt4), stdcall, Cint, (Ref{GUID}, Ref{Ptr{Cwchar_t}}), id, nameptr) + r == 0 || windowserror("UuidToString", r % UInt32) # a RPC_STATUS is just a WINERROR with a different signedness of the type + + namebuf = unsafe_wrap(Vector{Cwchar_t}, nameptr[], ccall(:wcslen, UInt, (Ptr{Cwchar_t},), nameptr[])) + name = transcode(String, namebuf) + + ccall((:RpcStringFreeW, :Rpcrt4), stdcall, Cint, (Ref{Ptr{Cwchar_t}},), nameptr) + + name = filter(!=('-'), name) + return name +end + function tempname(parent::AbstractString=tempdir(); cleanup::Bool=true) isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory")) - seed::UInt32 = rand(UInt32) - while true - if (seed & typemax(UInt16)) == 0 - seed += 1 - end - filename = _win_tempname(parent, seed) - if !ispath(filename) - cleanup && temp_cleanup_later(filename) - return filename - end - seed += 1 - end + name = _uiud_string() + filename = joinpath(parent, temp_prefix * name * ".tmp") + @assert !ispath(filename) + cleanup && temp_cleanup_later(filename) + return filename end else # !windows diff --git a/test/file.jl b/test/file.jl index e15807c0adccb..ae3ce717060e0 100644 --- a/test/file.jl +++ b/test/file.jl @@ -50,6 +50,18 @@ end using Random +@testset "that temp names are actually unique" begin + temps = [tempname(cleanup=false) for _ = 1:100] + @test allunique(temps) + temps = map(1:100) do _ + path, io = mktemp(cleanup=false) + close(io) + rm(path, force=true) + return path + end + @test allunique(temps) +end + @testset "tempname with parent" begin t = tempname() @test dirname(t) == tempdir() From cc43229b5eec7241221a3034807be6b03d37e5a6 Mon Sep 17 00:00:00 2001 From: Mustafa M Date: Thu, 5 Dec 2019 10:28:13 -0500 Subject: [PATCH 2/5] Use underscores and remove extension --- base/file.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/file.jl b/base/file.jl index 96a170f77f782..1e0e765e826c5 100644 --- a/base/file.jl +++ b/base/file.jl @@ -545,14 +545,14 @@ function _uiud_string() ccall((:RpcStringFreeW, :Rpcrt4), stdcall, Cint, (Ref{Ptr{Cwchar_t}},), nameptr) - name = filter(!=('-'), name) + name = replace(name, '-' => '_') return name end function tempname(parent::AbstractString=tempdir(); cleanup::Bool=true) isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory")) name = _uiud_string() - filename = joinpath(parent, temp_prefix * name * ".tmp") + filename = joinpath(parent, temp_prefix * name) @assert !ispath(filename) cleanup && temp_cleanup_later(filename) return filename From 4f8ddac13765113fec98a9885660515d8519a45e Mon Sep 17 00:00:00 2001 From: Mustafa Date: Thu, 16 Jan 2020 17:56:21 -0500 Subject: [PATCH 3/5] Truncate to 10 chars the UUID --- base/file.jl | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/base/file.jl b/base/file.jl index 1e0e765e826c5..116b0fbeea9aa 100644 --- a/base/file.jl +++ b/base/file.jl @@ -517,18 +517,10 @@ function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true) return (filename, Base.open(filename, "r+")) end -# GUID win32 api (consider # TODO: refactor) -struct GUID - l1::Culong - w1::Cushort - w2::Cushort - b::NTuple{8,Cuchar} -end - # generates a random temporary string based on a UUID function _uiud_string() - id = Ref{GUID}() - r = ccall((:UuidCreate,:Rpcrt4), stdcall, Cint, (Ref{GUID},), id) + id = Ref{UInt128}() + r = ccall((:UuidCreate,:Rpcrt4), stdcall, Cint, (Ref{UInt128},), id) # ignore expected errors that we don't care about (these shouldn't even be an issue after Vista) RPC_S_UUID_LOCAL_ONLY = 1824 RPC_S_UUID_NO_ADDRESS = 1739 @@ -536,16 +528,9 @@ function _uiud_string() windowserror("UuidCreate", r % UInt32) # Throw on unexpected errors. Note, a RPC_STATUS is just a WINERROR with a different signedness of the type. end - nameptr = Ref{Ptr{Cwchar_t}}() - r = ccall((:UuidToStringW, :Rpcrt4), stdcall, Cint, (Ref{GUID}, Ref{Ptr{Cwchar_t}}), id, nameptr) - r == 0 || windowserror("UuidToString", r % UInt32) # a RPC_STATUS is just a WINERROR with a different signedness of the type - - namebuf = unsafe_wrap(Vector{Cwchar_t}, nameptr[], ccall(:wcslen, UInt, (Ptr{Cwchar_t},), nameptr[])) - name = transcode(String, namebuf) - - ccall((:RpcStringFreeW, :Rpcrt4), stdcall, Cint, (Ref{Ptr{Cwchar_t}},), nameptr) + name = string(id[] % UInt64, base=36, pad=5) # between 5 and 13 characters, to avoid special file names like COM1 + name = name[1:min(end, 10)] # truncate to max of 10 characters (why 10? I have no idea, it came from the random number generator) - name = replace(name, '-' => '_') return name end From 201cf012ab82ecba1e6256afbb4a2dd241480e67 Mon Sep 17 00:00:00 2001 From: Mustafa Date: Fri, 17 Jan 2020 13:58:01 -0500 Subject: [PATCH 4/5] Generate the random name from a random byte array --- base/file.jl | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/base/file.jl b/base/file.jl index 116b0fbeea9aa..44e9b1dc6911c 100644 --- a/base/file.jl +++ b/base/file.jl @@ -517,26 +517,23 @@ function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true) return (filename, Base.open(filename, "r+")) end -# generates a random temporary string based on a UUID -function _uiud_string() - id = Ref{UInt128}() - r = ccall((:UuidCreate,:Rpcrt4), stdcall, Cint, (Ref{UInt128},), id) - # ignore expected errors that we don't care about (these shouldn't even be an issue after Vista) - RPC_S_UUID_LOCAL_ONLY = 1824 - RPC_S_UUID_NO_ADDRESS = 1739 - if !(r == 0 || r == RPC_S_UUID_LOCAL_ONLY || r == RPC_S_UUID_NO_ADDRESS) - windowserror("UuidCreate", r % UInt32) # Throw on unexpected errors. Note, a RPC_STATUS is just a WINERROR with a different signedness of the type. - end - - name = string(id[] % UInt64, base=36, pad=5) # between 5 and 13 characters, to avoid special file names like COM1 - name = name[1:min(end, 10)] # truncate to max of 10 characters (why 10? I have no idea, it came from the random number generator) +# generate a random string from random bytes +function _rand_string() + nchars = 10 + A = Vector{UInt8}(undef, nchars) + ccall((:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Cvoid}, UInt32), A, sizeof(A)) - return name + slug = StringVector(10) + chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + for i = 1:nchars + slug[i] = chars[(A[i] % length(chars)) + 1] + end + return name = String(slug) end function tempname(parent::AbstractString=tempdir(); cleanup::Bool=true) isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory")) - name = _uiud_string() + name = _rand_string() filename = joinpath(parent, temp_prefix * name) @assert !ispath(filename) cleanup && temp_cleanup_later(filename) From 50fc59c508f52791f11234b5796d3d158869360f Mon Sep 17 00:00:00 2001 From: Mustafa M Date: Fri, 17 Jan 2020 15:21:07 -0500 Subject: [PATCH 5/5] Update file.jl --- base/file.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/file.jl b/base/file.jl index 44e9b1dc6911c..6d4fca5fda8c5 100644 --- a/base/file.jl +++ b/base/file.jl @@ -523,7 +523,7 @@ function _rand_string() A = Vector{UInt8}(undef, nchars) ccall((:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Cvoid}, UInt32), A, sizeof(A)) - slug = StringVector(10) + slug = Base.StringVector(10) chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" for i = 1:nchars slug[i] = chars[(A[i] % length(chars)) + 1]