@@ -387,44 +387,88 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
387387 // TODO: Somehow avoid copying back and forth to a String to hold the path
388388
389389#if os(Windows)
390- try inPath. path. withNTPathRepresentation { pwszPath in
391- var ( fd, auxPath, temporaryDirectoryPath) = try createProtectedTemporaryFile ( at: inPath. path, inPath: inPath, options: options, variant: " Folder " )
390+ var ( fd, auxPath, temporaryDirectoryPath) = try createProtectedTemporaryFile ( at: inPath. path, inPath: inPath, options: options, variant: " Folder " )
392391
393- // Cleanup temporary directory
394- defer { cleanupTemporaryDirectory ( at: temporaryDirectoryPath) }
392+ // Cleanup temporary directory
393+ defer { cleanupTemporaryDirectory ( at: temporaryDirectoryPath) }
395394
396- guard fd >= 0 else {
395+ guard fd >= 0 else {
396+ throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false )
397+ }
398+
399+ defer { if fd >= 0 { _close ( fd) } }
400+
401+ let callback = ( reportProgress && Progress . current ( ) != nil ) ? Progress ( totalUnitCount: Int64 ( buffer. count) ) : nil
402+
403+ do {
404+ try write ( buffer: buffer, toFileDescriptor: fd, path: inPath, parentProgress: callback)
405+ } catch {
406+ try auxPath. withNTPathRepresentation { pwszAuxPath in
407+ _ = DeleteFileW ( pwszAuxPath)
408+ }
409+
410+ if callback? . isCancelled ?? false {
411+ throw CocoaError ( . userCancelled)
412+ } else {
397413 throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false )
398414 }
415+ }
399416
400- defer { if fd >= 0 { _close ( fd ) } }
417+ writeExtendedAttributes ( fd : fd , attributes : attributes )
401418
402- let callback = ( reportProgress && Progress . current ( ) != nil ) ? Progress ( totalUnitCount: Int64 ( buffer. count) ) : nil
419+ _close ( fd)
420+ fd = - 1
403421
404- do {
405- try write ( buffer: buffer, toFileDescriptor: fd, path: inPath, parentProgress: callback)
406- } catch {
407- try auxPath. withNTPathRepresentation { pwszAuxPath in
408- _ = DeleteFileW ( pwszAuxPath)
409- }
422+ try auxPath. withNTPathRepresentation { pwszAuxiliaryPath in
423+ defer { _ = DeleteFileW ( pwszAuxiliaryPath) }
410424
411- if callback? . isCancelled ?? false {
412- throw CocoaError ( . userCancelled)
413- } else {
414- throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false )
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+ throw CocoaError . errorWithFilePath ( inPath, win32: GetLastError ( ) , reading: false )
432+ }
433+
434+ defer {
435+ switch hFile {
436+ case INVALID_HANDLE_VALUE:
437+ break
438+ default :
439+ _ = CloseHandle ( hFile)
415440 }
416441 }
417442
418- writeExtendedAttributes ( fd: fd, attributes: attributes)
443+ try inPath. path. withNTPathRepresentation { pwszPath in
444+ let cchLength = wcslen ( pwszPath)
445+ let cbSize = cchLength * MemoryLayout< WCHAR> . size
446+ let dwSize = DWORD ( MemoryLayout < FILE_RENAME_INFO > . size + cbSize + MemoryLayout < WCHAR > . size)
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+ pBuffer. baseAddress? . advanced ( by: MemoryLayout < FILE_RENAME_INFO > . offset ( of: \. FileName) !)
454+ . withMemoryRebound ( to: WCHAR . self, capacity: cchLength + 1 ) {
455+ wcscpy_s ( $0, cchLength + 1 , pwszPath)
456+ }
419457
420- _close ( fd)
421- fd = - 1
458+ if !SetFileInformationByHandle( hFile, FileRenameInfoEx, pInfo, dwSize) {
459+ let dwError = GetLastError ( )
460+ guard dwError == ERROR_NOT_SAME_DEVICE else {
461+ throw CocoaError . errorWithFilePath ( inPath, win32: dwError, reading: false )
462+ }
422463
423- try auxPath. 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 )
464+ _ = CloseHandle ( hFile)
465+ hFile = INVALID_HANDLE_VALUE
466+
467+ // The move is across volumes.
468+ guard MoveFileExW ( pwszAuxiliaryPath, pwszPath, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) else {
469+ throw CocoaError . errorWithFilePath ( inPath, win32: GetLastError ( ) , reading: false )
470+ }
471+ }
428472 }
429473 }
430474 }
0 commit comments