Skip to content

Commit c7b50c7

Browse files
committed
Foundation: attempt a first pass symlink resolution
On Windows, rather than recursively expanding a path component by component, perform a first pass resolution at the given path. This potentially influences the path resolution as the component traversal would possibly fail to resolve cross-drive references.
1 parent 7f08595 commit c7b50c7

File tree

1 file changed

+29
-1
lines changed

1 file changed

+29
-1
lines changed

Sources/Foundation/NSURL.swift

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,35 @@ extension NSURL {
916916
#endif
917917
}
918918

919-
919+
#if os(Windows)
920+
let hFile: HANDLE = absolutePath.withCString(encodedAs: UTF16.self) {
921+
CreateFileW($0, GENERIC_READ,
922+
DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
923+
nil, DWORD(OPEN_EXISTING),
924+
DWORD(FILE_FLAG_BACKUP_SEMANTICS), nil)
925+
}
926+
guard hFile == INVALID_HANDLE_VALUE else {
927+
defer { CloseHandle(hFile) }
928+
929+
let dwLength = GetFinalPathNameByHandleW(hFile, nil, 0, 0)
930+
return withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: Int(dwLength)) {
931+
let dwLength =
932+
GetFinalPathNameByHandleW(hFile, $0.baseAddress, DWORD($0.count), 0)
933+
assert(dwLength < $0.count)
934+
935+
var resolved = String(decodingCString: $0.baseAddress!, as: UTF16.self)
936+
if preserveDirectoryFlag {
937+
var isExistingDirectory: ObjCBool = false
938+
let _ = FileManager.default.fileExists(atPath: resolved, isDirectory: &isExistingDirectory)
939+
if isExistingDirectory.boolValue && !resolved.hasSuffix("/") {
940+
resolved += "/"
941+
}
942+
}
943+
return URL(fileURLWithPath: resolved)
944+
}
945+
}
946+
#endif
947+
920948
var components = URL(fileURLWithPath: absolutePath).pathComponents
921949
guard !components.isEmpty else {
922950
return URL(string: absoluteString)

0 commit comments

Comments
 (0)