Skip to content

Commit cc75ff5

Browse files
committed
Foundation: make _moveItem resilient to multiple volumes
`MoveFileExW` does not permit moving directories across volumes. Account for this by checking the source and fallback to copy and delete.
1 parent 8fbe624 commit cc75ff5

File tree

1 file changed

+34
-4
lines changed

1 file changed

+34
-4
lines changed

Sources/Foundation/FileManager+Win32.swift

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -604,14 +604,44 @@ extension FileManager {
604604
}
605605

606606
try withNTPathRepresentation(of: dstPath) { wszDestination in
607-
var faAttributes: WIN32_FILE_ATTRIBUTE_DATA = .init()
608-
if GetFileAttributesExW(wszDestination, GetFileExInfoStandard, &faAttributes) {
607+
var faDestinationnAttributes: WIN32_FILE_ATTRIBUTE_DATA = .init()
608+
if GetFileAttributesExW(wszDestination, GetFileExInfoStandard, &faDestinationnAttributes) {
609609
throw CocoaError.error(.fileWriteFileExists, userInfo: [NSFilePathErrorKey:dstPath])
610610
}
611611

612612
try withNTPathRepresentation(of: srcPath) { wszSource in
613-
if !MoveFileExW(wszSource, wszDestination, DWORD(MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
614-
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
613+
var faSourceAttributes: WIN32_FILE_ATTRIBUTE_DATA = .init()
614+
guard GetFileAttributesExW(wszSource, GetFileExInfoStandard, &faSourceAttributes) else {
615+
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [srcPath])
616+
}
617+
618+
// MoveFileExW does not work if the source and destination are
619+
// on different volumes and the source is a directory. In that
620+
// case, we need to do a recursive copy & remove.
621+
if PathIsSameRootW(wszSource, wszDestination) ||
622+
faSourceAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == 0 {
623+
if !MoveFileExW(wszSource, wszDestination, DWORD(MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH)) {
624+
throw _NSErrorWithWindowsError(GetLastError(), reading: false, paths: [srcPath, dstPath])
625+
}
626+
} else {
627+
try _copyOrLinkDirectoryHelper(atPath: srcPath, toPath: dstPath, variant: "Move") { (src, dst, type) in
628+
do {
629+
switch type {
630+
case .typeRegular:
631+
try _copyRegularFile(atPath: src, toPath: dst, variant: "Move")
632+
case .typeSymbolicLink:
633+
try _copySymlink(atPath: src, toPath: dst, variant: "Move")
634+
default:
635+
break
636+
}
637+
} catch {
638+
if !shouldProceedAfterError(error, movingItemAtPath: src, toPath: dst, isURL: isURL) {
639+
throw error
640+
}
641+
}
642+
643+
try _removeItem(atPath: src, isURL: isURL, alreadyConfirmed: true)
644+
}
615645
}
616646
}
617647
}

0 commit comments

Comments
 (0)