@@ -387,44 +387,88 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
387
387
// TODO: Somehow avoid copying back and forth to a String to hold the path
388
388
389
389
#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 " )
392
391
393
- // Cleanup temporary directory
394
- defer { cleanupTemporaryDirectory ( at: temporaryDirectoryPath) }
392
+ // Cleanup temporary directory
393
+ defer { cleanupTemporaryDirectory ( at: temporaryDirectoryPath) }
395
394
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 {
397
413
throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false )
398
414
}
415
+ }
399
416
400
- defer { if fd >= 0 { _close ( fd ) } }
417
+ writeExtendedAttributes ( fd : fd , attributes : attributes )
401
418
402
- let callback = ( reportProgress && Progress . current ( ) != nil ) ? Progress ( totalUnitCount: Int64 ( buffer. count) ) : nil
419
+ _close ( fd)
420
+ fd = - 1
403
421
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) }
410
424
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)
415
440
}
416
441
}
417
442
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
+ }
419
457
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
+ }
422
463
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
+ }
428
472
}
429
473
}
430
474
}
0 commit comments