Skip to content

Commit

Permalink
Use UIUD to create random tempname on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
musm committed Nov 26, 2019
1 parent 8f7855a commit 721ff1d
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 12 deletions.
49 changes: 37 additions & 12 deletions base/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -513,20 +513,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
Expand Down
12 changes: 12 additions & 0 deletions test/file.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 721ff1d

Please sign in to comment.