@@ -358,44 +358,88 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
358
358
// TODO: Somehow avoid copying back and forth to a String to hold the path
359
359
360
360
#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 " )
363
362
364
- // Cleanup temporary directory
365
- defer { cleanupTemporaryDirectory ( at: temporaryDirectoryPath) }
363
+ // Cleanup temporary directory
364
+ defer { cleanupTemporaryDirectory ( at: temporaryDirectoryPath) }
366
365
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 {
368
384
throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false )
369
385
}
386
+ }
370
387
371
- defer { if fd >= 0 { _close ( fd ) } }
388
+ writeExtendedAttributes ( fd : fd , attributes : attributes )
372
389
373
- let callback = ( reportProgress && Progress . current ( ) != nil ) ? Progress ( totalUnitCount: Int64 ( buffer. count) ) : nil
390
+ _close ( fd)
391
+ fd = - 1
374
392
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) }
381
395
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)
386
411
}
387
412
}
388
413
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
+ }
390
428
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
+ }
393
434
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
+ }
399
443
}
400
444
}
401
445
}
0 commit comments