@@ -358,44 +358,88 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
358358 // TODO: Somehow avoid copying back and forth to a String to hold the path
359359
360360#if os(Windows)
361- try inPath. path. withNTPathRepresentation { pwszPath in
362- var ( fd, auxPath, temporaryDirectoryPath) = try createProtectedTemporaryFile ( at: inPath. path, inPath: inPath, options: options, variant: " Folder " )
361+ var ( fd, auxPath, temporaryDirectoryPath) = try createProtectedTemporaryFile ( at: inPath. path, inPath: inPath, options: options, variant: " Folder " )
363362
364- // Cleanup temporary directory
365- defer { cleanupTemporaryDirectory ( at: temporaryDirectoryPath) }
363+ // Cleanup temporary directory
364+ defer { cleanupTemporaryDirectory ( at: temporaryDirectoryPath) }
366365
367- guard fd >= 0 else {
366+ guard fd >= 0 else {
367+ throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false )
368+ }
369+
370+ defer { if fd >= 0 { _close ( fd) } }
371+
372+ let callback = ( reportProgress && Progress . current ( ) != nil ) ? Progress ( totalUnitCount: Int64 ( buffer. count) ) : nil
373+
374+ do {
375+ try write ( buffer: buffer, toFileDescriptor: fd, path: inPath, parentProgress: callback)
376+ } catch {
377+ try auxPath. withNTPathRepresentation { pwszAuxPath in
378+ _ = DeleteFileW ( pwszAuxPath)
379+ }
380+
381+ if callback? . isCancelled ?? false {
382+ throw CocoaError ( . userCancelled)
383+ } else {
368384 throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false )
369385 }
386+ }
370387
371- defer { if fd >= 0 { _close ( fd ) } }
388+ writeExtendedAttributes ( fd : fd , attributes : attributes )
372389
373- let callback = ( reportProgress && Progress . current ( ) != nil ) ? Progress ( totalUnitCount: Int64 ( buffer. count) ) : nil
390+ _close ( fd)
391+ fd = - 1
374392
375- do {
376- try write ( buffer: buffer, toFileDescriptor: fd, path: inPath, parentProgress: callback)
377- } catch {
378- try auxPath. withNTPathRepresentation { pwszAuxPath in
379- _ = DeleteFileW ( pwszAuxPath)
380- }
393+ try auxPath. withNTPathRepresentation { pwszAuxiliaryPath in
394+ defer { _ = DeleteFileW ( pwszAuxiliaryPath) }
381395
382- if callback? . isCancelled ?? false {
383- throw CocoaError ( . userCancelled)
384- } else {
385- throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false )
396+ var hFile = CreateFileW ( pwszAuxiliaryPath, DELETE,
397+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
398+ nil , OPEN_EXISTING,
399+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
400+ nil )
401+ if hFile == INVALID_HANDLE_VALUE {
402+ throw CocoaError . errorWithFilePath ( inPath, win32: GetLastError ( ) , reading: false )
403+ }
404+
405+ defer {
406+ switch hFile {
407+ case INVALID_HANDLE_VALUE:
408+ break
409+ default :
410+ _ = CloseHandle ( hFile)
386411 }
387412 }
388413
389- writeExtendedAttributes ( fd: fd, attributes: attributes)
414+ try inPath. path. withNTPathRepresentation { pwszPath in
415+ let cchLength = wcslen ( pwszPath)
416+ let cbSize = cchLength * MemoryLayout< WCHAR> . size
417+ let dwSize = DWORD ( MemoryLayout < FILE_RENAME_INFO > . size + cbSize + MemoryLayout < WCHAR > . size)
418+ try withUnsafeTemporaryAllocation ( byteCount: Int ( dwSize) ,
419+ alignment: MemoryLayout< FILE_RENAME_INFO> . alignment) { pBuffer in
420+ var pInfo = pBuffer. baseAddress? . bindMemory ( to: FILE_RENAME_INFO . self, capacity: 1 )
421+ pInfo? . pointee. Flags = FILE_RENAME_FLAG_POSIX_SEMANTICS | FILE_RENAME_FLAG_REPLACE_IF_EXISTS
422+ pInfo? . pointee. RootDirectory = nil
423+ pInfo? . pointee. FileNameLength = DWORD ( cbSize)
424+ pBuffer. baseAddress? . advanced ( by: MemoryLayout < FILE_RENAME_INFO > . offset ( of: \. FileName) !)
425+ . withMemoryRebound ( to: WCHAR . self, capacity: cchLength + 1 ) {
426+ wcscpy_s ( $0, cchLength + 1 , pwszPath)
427+ }
390428
391- _close ( fd)
392- fd = - 1
429+ if !SetFileInformationByHandle( hFile, FileRenameInfoEx, pInfo, dwSize) {
430+ let dwError = GetLastError ( )
431+ guard dwError == ERROR_NOT_SAME_DEVICE else {
432+ throw CocoaError . errorWithFilePath ( inPath, win32: dwError, reading: false )
433+ }
393434
394- try auxPath. withNTPathRepresentation { pwszAuxiliaryPath in
395- guard MoveFileExW ( pwszAuxiliaryPath, pwszPath, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) else {
396- let dwError = GetLastError ( )
397- _ = DeleteFileW ( pwszAuxiliaryPath)
398- throw CocoaError . errorWithFilePath ( inPath, win32: dwError, reading: false )
435+ _ = CloseHandle ( hFile)
436+ hFile = INVALID_HANDLE_VALUE
437+
438+ // The move is across volumes.
439+ guard MoveFileExW ( pwszAuxiliaryPath, pwszPath, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) else {
440+ throw CocoaError . errorWithFilePath ( inPath, win32: GetLastError ( ) , reading: false )
441+ }
442+ }
399443 }
400444 }
401445 }
0 commit comments