Skip to content

Commit de755eb

Browse files
committed
[Windows] Fix Symbolic Link Creation
- Don't error out if the target doesn't exist - Check the correct path when determining if it's a directory
1 parent 845feec commit de755eb

File tree

1 file changed

+18
-4
lines changed

1 file changed

+18
-4
lines changed

Foundation/FileManager+Win32.swift

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,14 @@ extension FileManager {
264264
}
265265

266266
internal func _createSymbolicLink(atPath path: String, withDestinationPath destPath: String) throws {
267-
let faAttributes: WIN32_FILE_ATTRIBUTE_DATA = try windowsFileAttributes(atPath: path)
268-
var dwFlags: DWORD = DWORD(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
267+
var dwFlags = DWORD(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
268+
// Note: windowsfileAttributes will throw if the destPath is not found.
269+
// Since on Windows, you are required to know the type of the symlink
270+
// target (file or directory) during creation, and assuming one or the
271+
// other doesn't make a lot of sense, we allow it to throw, thus
272+
// disallowing the creation of broken symlinks on Windows (unlike with
273+
// POSIX).
274+
let faAttributes = try windowsFileAttributes(atPath: destPath)
269275
if faAttributes.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) == DWORD(FILE_ATTRIBUTE_DIRECTORY) {
270276
dwFlags |= DWORD(SYMBOLIC_LINK_FLAG_DIRECTORY)
271277
}
@@ -286,12 +292,20 @@ extension FileManager {
286292
internal func _canonicalizedPath(toFileAtPath path: String) throws -> String {
287293
var hFile: HANDLE = INVALID_HANDLE_VALUE
288294
path.withCString(encodedAs: UTF16.self) { link in
289-
hFile = CreateFileW(link, GENERIC_READ, DWORD(FILE_SHARE_WRITE), nil, DWORD(OPEN_EXISTING), DWORD(FILE_FLAG_BACKUP_SEMANTICS), nil)
295+
hFile = CreateFileW(link,
296+
0,
297+
DWORD(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
298+
nil,
299+
DWORD(OPEN_EXISTING),
300+
// BACKUP_SEMANTICS are (confusingly) required
301+
// in order to receive a handle to a directory
302+
DWORD(FILE_FLAG_BACKUP_SEMANTICS),
303+
nil)
290304
}
291-
defer { CloseHandle(hFile) }
292305
if hFile == INVALID_HANDLE_VALUE {
293306
throw _NSErrorWithWindowsError(GetLastError(), reading: true)
294307
}
308+
defer { CloseHandle(hFile) }
295309

296310
let dwLength: DWORD = GetFinalPathNameByHandleW(hFile, nil, 0, DWORD(FILE_NAME_NORMALIZED))
297311
var szPath: [WCHAR] = Array<WCHAR>(repeating: 0, count: Int(dwLength + 1))

0 commit comments

Comments
 (0)