Skip to content

Commit 5efac49

Browse files
authored
Merge pull request #2408 from gmittert/OverwriteDest
[Windows] Overwrite the destination if it exists in _copySymlink
2 parents a8651fb + 8210ccf commit 5efac49

File tree

1 file changed

+28
-19
lines changed

1 file changed

+28
-19
lines changed

Foundation/FileManager+Win32.swift

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -276,19 +276,30 @@ extension FileManager {
276276
return result
277277
}
278278

279-
internal func _createSymbolicLink(atPath path: String, withDestinationPath destPath: String) throws {
279+
internal func _createSymbolicLink(atPath path: String, withDestinationPath destPath: String, isDirectory: Bool? = nil) throws {
280280
var dwFlags = DWORD(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
281-
// Note: windowsfileAttributes will throw if the destPath is not found.
282-
// Since on Windows, you are required to know the type of the symlink
283-
// target (file or directory) during creation, and assuming one or the
284-
// other doesn't make a lot of sense, we allow it to throw, thus
285-
// disallowing the creation of broken symlinks on Windows (unlike with
286-
// POSIX).
287-
guard let faAttributes = try? windowsFileAttributes(atPath: destPath) else {
288-
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path, destPath])
289-
}
290-
if faAttributes.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) == DWORD(FILE_ATTRIBUTE_DIRECTORY) {
291-
dwFlags |= DWORD(SYMBOLIC_LINK_FLAG_DIRECTORY)
281+
// If destPath is relative, we should look for it relative to `path`, not our current working directory
282+
switch isDirectory {
283+
case .some(true):
284+
dwFlags |= DWORD(SYMBOLIC_LINK_FLAG_DIRECTORY)
285+
case .some(false):
286+
break;
287+
case .none:
288+
let resolvedDest = destPath.isAbsolutePath
289+
? destPath
290+
: try joinPath(prefix: path.deletingLastPathComponent, suffix: destPath)
291+
guard let faAttributes = try? windowsFileAttributes(atPath: resolvedDest) else {
292+
// Note: windowsfileAttributes will throw if the destPath is not found.
293+
// Since on Windows, you are required to know the type of the symlink
294+
// target (file or directory) during creation, and assuming one or the
295+
// other doesn't make a lot of sense, we allow it to throw, thus
296+
// disallowing the creation of broken symlinks on Windows is the target
297+
// is of unknown type.
298+
throw _NSErrorWithWindowsError(GetLastError(), reading: true, paths: [path, resolvedDest])
299+
}
300+
if faAttributes.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) == DWORD(FILE_ATTRIBUTE_DIRECTORY) {
301+
dwFlags |= DWORD(SYMBOLIC_LINK_FLAG_DIRECTORY)
302+
}
292303
}
293304

294305
try path.withCString(encodedAs: UTF16.self) { name in
@@ -349,14 +360,12 @@ extension FileManager {
349360
throw _NSErrorWithErrno(EINVAL, reading: true, path: srcPath, extraUserInfo: extraErrorInfo(srcPath: srcPath, dstPath: dstPath, userVariant: variant))
350361
}
351362

352-
let destination = try FileManager.default.destinationOfSymbolicLink(atPath: srcPath)
353-
354-
var dwFlags: DWORD = DWORD(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
355-
if try windowsFileAttributes(atPath: destination).dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) == DWORD(FILE_ATTRIBUTE_DIRECTORY) {
356-
dwFlags |= DWORD(SYMBOLIC_LINK_FLAG_DIRECTORY)
363+
let destination = try destinationOfSymbolicLink(atPath: srcPath)
364+
let isDir = try windowsFileAttributes(atPath: srcPath).dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) == DWORD(FILE_ATTRIBUTE_DIRECTORY)
365+
if fileExists(atPath: dstPath) {
366+
try removeItem(atPath: dstPath)
357367
}
358-
359-
try FileManager.default.createSymbolicLink(atPath: dstPath, withDestinationPath: destination)
368+
try _createSymbolicLink(atPath: dstPath, withDestinationPath: destination, isDirectory: isDir)
360369
}
361370

362371
internal func _copyOrLinkDirectoryHelper(atPath srcPath: String, toPath dstPath: String, variant: String = "Copy", _ body: (String, String, FileAttributeType) throws -> ()) throws {

0 commit comments

Comments
 (0)