Skip to content

Commit 9f011c6

Browse files
committed
Foundation: avoid temporary file leaks on Windows
Correct the implementation of `_NSCleanupTemporaryFile` to use `MoveFileExW` with `MOVEFILE_COPY_ALLOWED` to prefer to use file renames rather than copy and delete. This should improve performance in the general single volume case and still allow the correct behaviour in the multi-volume scenarios using `CopyFile` and `DeleteFile`. Thanks to @darinf for identifying the test case with 65535 temporary files!
1 parent 36a411b commit 9f011c6

File tree

3 files changed

+15
-2
lines changed

3 files changed

+15
-2
lines changed

Sources/Foundation/FileManager+Win32.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ extension FileManager {
620620
// case, we need to do a recursive copy & remove.
621621
if PathIsSameRootW(wszSource, wszDestination) ||
622622
faSourceAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == 0 {
623-
if !MoveFileExW(wszSource, wszDestination, DWORD(MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
623+
if !MoveFileExW(wszSource, wszDestination, MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH) {
624624
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
625625
}
626626
} else {

Sources/Foundation/NSPathUtilities.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,8 @@ internal func _NSCleanupTemporaryFile(_ auxFilePath: String, _ filePath: String)
813813
#if os(Windows)
814814
try withNTPathRepresentation(of: auxFilePath) { pwszSource in
815815
try withNTPathRepresentation(of: filePath) { pwszDestination in
816-
guard CopyFileW(pwszSource, pwszDestination, false) else {
816+
guard MoveFileExW(pwszSource, pwszDestination,
817+
MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) else {
817818
let dwErrorCode = GetLastError()
818819
try? FileManager.default.removeItem(atPath: auxFilePath)
819820
throw _NSErrorWithWindowsError(dwErrorCode, reading: false)

Sources/Foundation/WinSDK+Extensions.swift

+12
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ internal var GENERIC_WRITE: DWORD {
5757
DWORD(WinSDK.GENERIC_WRITE)
5858
}
5959

60+
internal var MOVEFILE_COPY_ALLOWED: DWORD {
61+
DWORD(WinSDK.MOVEFILE_COPY_ALLOWED)
62+
}
63+
64+
internal var MOVEFILE_REPLACE_EXISTING: DWORD {
65+
DWORD(WinSDK.MOVEFILE_REPLACE_EXISTING)
66+
}
67+
68+
internal var MOVEFILE_WRITE_THROUGH: DWORD {
69+
DWORD(WinSDK.MOVEFILE_WRITE_THROUGH)
70+
}
71+
6072
internal var OPEN_EXISTING: DWORD {
6173
DWORD(WinSDK.OPEN_EXISTING)
6274
}

0 commit comments

Comments
 (0)