@@ -271,48 +271,36 @@ extension _FileManagerImpl {
271271 SECURITY_ATTRIBUTES ( nLength: DWORD ( MemoryLayout< SECURITY_ATTRIBUTES> . size) ,
272272 lpSecurityDescriptor: nil ,
273273 bInheritHandle: false )
274- // `CreateDirectoryW` does not create intermediate directories, so we need to handle that manually.
275- // Note: `SHCreateDirectoryExW` seems to have issues with long paths.
274+ // `SHCreateDirectoryExW` creates intermediate directories while `CreateDirectoryW` does not.
276275 if createIntermediates {
277- // Create intermediate directories recursively
278- func _createDirectoryRecursively( at directoryPath: String ) throws {
279- try directoryPath. withNTPathRepresentation { pwszPath in
280- // Create this directory
281- guard CreateDirectoryW ( pwszPath, & saAttributes) else {
282- let lastError = GetLastError ( )
283- if lastError == ERROR_ALREADY_EXISTS {
284- var isDir : Bool = false
285- if fileExists ( atPath: directoryPath, isDirectory: & isDir) , isDir {
286- return // Directory now exists, success
287- }
288- } else if lastError == ERROR_PATH_NOT_FOUND {
289- let parentPath = directoryPath. deletingLastPathComponent ( )
290- if !parentPath. isEmpty && parentPath != directoryPath {
291- // Recursively create parent directory
292- try _createDirectoryRecursively ( at: parentPath)
293- // Now try creating this one again.
294- guard CreateDirectoryW ( pwszPath, & saAttributes) else {
295- let lastError = GetLastError ( )
296- if lastError == ERROR_ALREADY_EXISTS {
297- var isDir : Bool = false
298- if fileExists ( atPath: directoryPath, isDirectory: & isDir) , isDir {
299- return // Directory now exists, success
300- }
301- }
302- throw CocoaError . errorWithFilePath ( directoryPath, win32: lastError, reading: false )
303- }
304- return
305- }
306- }
307- throw CocoaError . errorWithFilePath ( directoryPath, win32: lastError, reading: false )
276+ // `SHCreateDirectoryExW` requires an absolute path while `CreateDirectoryW` works based on the current working
277+ // directory.
278+ try path. withNTPathRepresentation { pwszPath in
279+ let errorCode = SHCreateDirectoryExW ( nil , pwszPath, & saAttributes)
280+ guard let errorCode = DWORD ( exactly: errorCode) else {
281+ // `SHCreateDirectoryExW` returns `Int` but all error codes are defined in terms of `DWORD`, aka
282+ // `UInt`. We received an unknown error code.
283+ throw CocoaError . errorWithFilePath ( . fileWriteUnknown, path)
284+ }
285+ switch errorCode {
286+ case ERROR_SUCCESS:
287+ if let attributes {
288+ try ? fileManager. setAttributes ( attributes, ofItemAtPath: path)
289+ }
290+ case ERROR_ALREADY_EXISTS:
291+ var isDirectory : Bool = false
292+ if fileExists ( atPath: path, isDirectory: & isDirectory) , isDirectory {
293+ // A directory already exists at this path, which is not an error if we have
294+ // `createIntermediates == true`.
295+ break
308296 }
297+ // A file (not a directory) exists at the given path or the file creation failed and the item
298+ // at this path has been deleted before the call to `fileExists`. Throw the original error.
299+ fallthrough
300+ default :
301+ throw CocoaError . errorWithFilePath ( path, win32: errorCode, reading: false )
309302 }
310303 }
311-
312- try _createDirectoryRecursively ( at: path)
313- if let attributes {
314- try ? fileManager. setAttributes ( attributes, ofItemAtPath: path)
315- }
316304 } else {
317305 try path. withNTPathRepresentation { pwszPath in
318306 guard CreateDirectoryW ( pwszPath, & saAttributes) else {
@@ -509,14 +497,9 @@ extension _FileManagerImpl {
509497 // This is solely to minimize the number of allocations and number of bytes allocated versus starting with a hardcoded value like MAX_PATH.
510498 // We should NOT early-return if this returns 0, in order to avoid TOCTOU issues.
511499 let dwSize = GetCurrentDirectoryW ( 0 , nil )
512- let cwd = try ? FillNullTerminatedWideStringBuffer ( initialSize: dwSize >= 0 ? dwSize : DWORD ( MAX_PATH) , maxSize: DWORD ( Int16 . max) ) {
500+ return try ? FillNullTerminatedWideStringBuffer ( initialSize: dwSize >= 0 ? dwSize : DWORD ( MAX_PATH) , maxSize: DWORD ( Int16 . max) ) {
513501 GetCurrentDirectoryW ( DWORD ( $0. count) , $0. baseAddress)
514502 }
515-
516- // Handle Windows NT object namespace prefix
517- // The \\?\ prefix is used by Windows NT for device paths and may appear
518- // in current working directory paths. We strip it to return a standard path.
519- return cwd? . removingNTPathPrefix ( )
520503#else
521504 withUnsafeTemporaryAllocation ( of: CChar . self, capacity: FileManager . MAX_PATH_SIZE) { buffer in
522505 guard getcwd ( buffer. baseAddress!, FileManager . MAX_PATH_SIZE) != nil else {
0 commit comments