Skip to content

Commit 31bc5e6

Browse files
committed
TSCLibc: fix mkstemps on Windows
The original implementation of `mkstemps` that I had done was actually not accounting for the template restriction. Now that it is possible to build packages with s-p-m to a certain extent, this is important to be correct. Account for the suffixing on the template.
1 parent 6f8a6d6 commit 31bc5e6

File tree

1 file changed

+36
-25
lines changed

1 file changed

+36
-25
lines changed

Sources/TSCLibc/libc.swift

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ public func realpath(
2828
fatalError("realpath is unimplemented")
2929
}
3030

31+
private func __randname(_ buffer: UnsafeMutablePointer<CChar>) {
32+
let alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
33+
_ = (0 ..< 6).map { index in
34+
buffer[index] = CChar(alpha.shuffled().randomElement()!.utf8.first!)
35+
}
36+
}
37+
3138
// char *mkdtemp(char *template);
3239
// NOTE(compnerd) this is unsafe! This assumes that the template is *ASCII*.
3340
public func mkdtemp(
@@ -50,17 +57,10 @@ public func mkdtemp(
5057
return nil
5158
}
5259

53-
let stampSuffix = { (buffer: UnsafeMutablePointer<CChar>) in
54-
let alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
55-
_ = (0 ..< 6).map { index in
56-
buffer[index] = CChar(alpha.shuffled().randomElement()!.utf8.first!)
57-
}
58-
}
59-
6060
// Attempt to create the directory
6161
var retries: Int = 100
6262
repeat {
63-
stampSuffix(template + length - 6)
63+
__randname(template + length - 6)
6464
if _mkdir(template) == 0 {
6565
return template
6666
}
@@ -75,24 +75,35 @@ public func mkstemps(
7575
_ template: UnsafeMutablePointer<CChar>?,
7676
_ suffixlen: Int32
7777
) -> Int32 {
78-
guard let template = template else { return -EINVAL }
79-
return String(cString: template).withCString(encodedAs: UTF16.self) {
80-
let capacity: Int = wcslen($0) + 1
81-
return $0.withMemoryRebound(to: wchar_t.self, capacity: capacity) {
82-
guard _wmktemp_s(UnsafeMutablePointer(mutating: $0), capacity) == 0 else {
83-
return -EINVAL
84-
}
85-
86-
var fd: Int32 = -1
87-
_wsopen_s(&fd, $0, _O_RDWR | _O_CREAT | _O_BINARY | _O_NOINHERIT,
88-
_SH_DENYNO, _S_IREAD | _S_IWRITE)
89-
90-
String(decodingCString: $0, as: UTF16.self).utf8CString.withUnsafeBytes {
91-
template.assign(from: $0.bindMemory(to: CChar.self).baseAddress!,
92-
count: $0.count)
93-
}
78+
// Although the signature of the function is `char *(*)(char *)`, the C
79+
// library treats it as `char *(*)(char * _Nonull)`. Most implementations
80+
// will simply use and trigger a segmentation fault on x86 (and similar faults
81+
// on other architectures) when the memory is accessed. This roughly emulates
82+
// that by terminating in the case even though it is possible for us to return
83+
// an error.
84+
guard let template = template else { fatalError() }
85+
86+
let length: Int = strlen(template)
87+
88+
// Validate the precondition: the template must terminate with 6 `X` which
89+
// will be filled in to generate a unique directory.
90+
guard length >= 6, memcmp(template + length - Int(suffixlen) - 6, "XXXXXX", 6) == 0 else {
91+
_set_errno(EINVAL)
92+
return -1
93+
}
94+
95+
// Attempt to create file
96+
var retries: Int = 100
97+
repeat {
98+
__randname(template + length - Int(suffixlen) - 6)
99+
var fd: CInt = -1
100+
if _sopen_s(&fd, template, _O_RDWR | _O_CREAT | _O_BINARY | _O_NOINHERIT,
101+
_SH_DENYNO, _S_IREAD | _S_IWRITE) == 0 {
94102
return fd
95103
}
96-
}
104+
retries = retries - 1
105+
} while retries > 0
106+
107+
return -1
97108
}
98109
#endif

0 commit comments

Comments
 (0)