@@ -19,6 +19,8 @@ internal import DarwinPrivate // for VREG
1919
2020internal  import  _FoundationCShims
2121
22+ import  WinSDK
23+ 
2224#if canImport(Darwin) 
2325import  Darwin
2426#elseif canImport(Android) 
@@ -387,44 +389,119 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
387389    // TODO: Somehow avoid copying back and forth to a String to hold the path
388390
389391#if os(Windows) 
390-     try . path. withNTPathRepresentation  {  pwszPath in 
391-         var  ( fd,  auxPath,  temporaryDirectoryPath)  =  try createProtectedTemporaryFile ( at:  inPath. path,  inPath:  inPath,  options:  options,  variant:  " Folder " ) 
392+     var  ( fd,  auxPath,  temporaryDirectoryPath)  =  try createProtectedTemporaryFile ( at:  inPath. path,  inPath:  inPath,  options:  options,  variant:  " Folder " ) 
392393
393-          // Cleanup temporary directory
394-          defer  {  cleanupTemporaryDirectory ( at:  temporaryDirectoryPath)  } 
394+     // Cleanup temporary directory
395+     defer  {  cleanupTemporaryDirectory ( at:  temporaryDirectoryPath)  } 
395396
396-         guard  fd >=  0  else  { 
397-             throw  CocoaError . errorWithFilePath ( inPath,  errno:  errno,  reading:  false ) 
397+     guard  fd >=  0  else  { 
398+         throw  CocoaError . errorWithFilePath ( inPath,  errno:  errno,  reading:  false ) 
399+     } 
400+ 
401+     defer  {  if  fd >=  0  {  _close ( fd)  }  } 
402+ 
403+     let  callback  =  ( reportProgress && Progress . current ( )  !=  nil )  ?  Progress ( totalUnitCount:  Int64 ( buffer. count) )  :  nil 
404+ 
405+     do  { 
406+         try write ( buffer:  buffer,  toFileDescriptor:  fd,  path:  inPath,  parentProgress:  callback) 
407+     }  catch  { 
408+         try . withNTPathRepresentation  {  pwszAuxPath in 
409+             _ =  DeleteFileW ( pwszAuxPath) 
398410        } 
399411
400-         defer  {  if  fd >=  0  {  _close ( fd)  }  } 
412+         if  callback? . isCancelled ??  false  { 
413+             throw  CocoaError ( . userCancelled) 
414+         }  else  { 
415+             throw  CocoaError . errorWithFilePath ( inPath,  errno:  errno,  reading:  false ) 
416+         } 
417+     } 
401418
402-          let   callback   =   ( reportProgress &&  Progress . current ( )   !=   nil )   ?   Progress ( totalUnitCount :   Int64 ( buffer . count ) )   :   nil 
419+     writeExtendedAttributes ( fd :  fd ,  attributes :  attributes ) 
403420
404-         do  { 
405-             try write ( buffer:  buffer,  toFileDescriptor:  fd,  path:  inPath,  parentProgress:  callback) 
406-         }  catch  { 
407-             try . withNTPathRepresentation  {  pwszAuxPath in 
408-                 _ =  DeleteFileW ( pwszAuxPath) 
409-             } 
421+     _close ( fd) 
422+     fd =  - 1 
410423
411-             if  callback? . isCancelled ??  false  { 
412-                 throw  CocoaError ( . userCancelled) 
413-             }  else  { 
414-                 throw  CocoaError . errorWithFilePath ( inPath,  errno:  errno,  reading:  false ) 
424+     try . withNTPathRepresentation  {  pwszAuxiliaryPath in 
425+         var  hFile  =  CreateFileW ( pwszAuxiliaryPath,  DELETE, 
426+                                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
427+                                 nil ,  OPEN_EXISTING, 
428+                                 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 
429+                                 nil ) 
430+         if  hFile ==  INVALID_HANDLE_VALUE { 
431+             let  dwError  =  GetLastError ( ) 
432+             _ =  DeleteFileW ( pwszAuxiliaryPath) 
433+             throw  CocoaError . errorWithFilePath ( inPath,  win32:  dwError,  reading:  false ) 
434+         } 
435+         defer  { 
436+             switch  hFile { 
437+             case  INVALID_HANDLE_VALUE: 
438+                 break 
439+             default : 
440+                 _ =  CloseHandle ( hFile) 
415441            } 
416442        } 
417443
418-         writeExtendedAttributes ( fd:  fd,  attributes:  attributes) 
444+         try . path. withNTPathRepresentation  {  pwszPath in 
445+             let  cbSize  =  wcslen ( pwszPath)  *  MemoryLayout< WCHAR> . size
446+             let  dwSize  =  DWORD ( MemoryLayout < FILE_RENAME_INFO > . size +  cbSize) 
447+             try withUnsafeTemporaryAllocation ( byteCount:  Int ( dwSize) , 
448+                                               alignment:  MemoryLayout< FILE_RENAME_INFO> . alignment)  {  pBuffer in 
449+                 var  pInfo  =  pBuffer. baseAddress? . bindMemory ( to:  FILE_RENAME_INFO . self,  capacity:  1 ) 
450+                 pInfo? . pointee. Flags =  FILE_RENAME_FLAG_POSIX_SEMANTICS | FILE_RENAME_FLAG_REPLACE_IF_EXISTS
451+                 pInfo? . pointee. RootDirectory =  nil 
452+                 pInfo? . pointee. FileNameLength =  DWORD ( cbSize) 
453+                 let  count  =  wcslen ( pwszPath) 
454+                 pBuffer. baseAddress? . advanced ( by:  MemoryLayout < FILE_RENAME_INFO > . offset ( of:  \. FileName) !) 
455+                                     . withMemoryRebound ( to:  WCHAR . self,  capacity:  count +  1 )  { 
456+                     wcscpy_s ( $0,  count +  1 ,  pwszPath) 
457+                     $0 [ count]  =  0 
458+                 } 
419459
420-         _close ( fd) 
421-         fd =  - 1 
460+                 if  !SetFileInformationByHandle( hFile,  FileRenameInfoEx,  pInfo,  dwSize)  { 
461+                     let  dwError  =  GetLastError ( ) 
462+                     if  dwError ==  ERROR_ACCESS_DENIED { 
463+                         let  dwAttributes  =  GetFileAttributesW ( pwszPath) 
464+                         if  dwAttributes >  0 , 
465+                                 dwAttributes &  ( FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)  !=  0  { 
466+                             // The target file is read-only, hidden or a system file. Remove those attributes and try again.
467+                             if  SetFileAttributesW ( pwszPath,  dwAttributes &  ~ ( FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM) )  { 
468+                                 if  SetFileInformationByHandle ( hFile,  FileRenameInfoEx,  pInfo,  dwSize)  { 
469+                                     return 
470+                                 } 
471+                                 // Try to restore the attributes, but ignore any error
472+                                 _ =  SetFileAttributesW ( pwszPath,  dwAttributes) 
473+                             } 
474+                         } 
475+                     } 
476+                     guard  dwError ==  ERROR_NOT_SAME_DEVICE else  { 
477+                         _ =  DeleteFileW ( pwszAuxiliaryPath) 
478+                         throw  CocoaError . errorWithFilePath ( inPath,  win32:  dwError,  reading:  false ) 
479+                     } 
422480
423-         try . withNTPathRepresentation  {  pwszAuxiliaryPath in 
424-             guard  MoveFileExW ( pwszAuxiliaryPath,  pwszPath,  MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)  else  { 
425-                 let  dwError  =  GetLastError ( ) 
426-                 _ =  DeleteFileW ( pwszAuxiliaryPath) 
427-                 throw  CocoaError . errorWithFilePath ( inPath,  win32:  dwError,  reading:  false ) 
481+                     _ =  CloseHandle ( hFile) 
482+                     hFile =  INVALID_HANDLE_VALUE
483+ 
484+                     // The move is across volumes.
485+                     guard  MoveFileExW ( pwszAuxiliaryPath,  pwszPath,  MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)  else  { 
486+                         let  dwError  =  GetLastError ( ) 
487+                         if  dwError ==  ERROR_ACCESS_DENIED { 
488+                             let  dwAttributes  =  GetFileAttributesW ( pwszPath) 
489+                             if  dwAttributes >  0 , 
490+                                     dwAttributes &  ( FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)  !=  0  { 
491+                                 // The target file is read-only, hidden or a system file. Remove those attributes and try again.
492+                                 if  SetFileAttributesW ( pwszPath,  dwAttributes &  ~ ( FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM) )  { 
493+                                     if  MoveFileExW ( pwszAuxiliaryPath,  pwszPath,  MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)  { 
494+                                         return 
495+                                     } 
496+                                     // Try to restore the attributes, but ignore any error
497+                                     _ =  SetFileAttributesW ( pwszPath,  dwAttributes) 
498+                                 } 
499+                             } 
500+                         } 
501+                         _ =  DeleteFileW ( pwszAuxiliaryPath) 
502+                         throw  CocoaError . errorWithFilePath ( inPath,  win32:  dwError,  reading:  false ) 
503+                     } 
504+                 } 
428505            } 
429506        } 
430507    } 
0 commit comments