Skip to content

Commit 0302cd5

Browse files
committed
FileManager.fileExists(atPath:) should follow symlinks
1 parent c15a5e1 commit 0302cd5

File tree

2 files changed

+19
-4
lines changed

2 files changed

+19
-4
lines changed

Sources/FoundationEssentials/FileManager/FileManager+Files.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -380,12 +380,19 @@ extension _FileManagerImpl {
380380
private func _fileExists(_ path: String) -> (exists: Bool, isDirectory: Bool) {
381381
#if os(Windows)
382382
guard !path.isEmpty else { return (false, false) }
383-
return (try? path.withNTPathRepresentation {
384-
var faAttributes: WIN32_FILE_ATTRIBUTE_DATA = .init()
385-
guard GetFileAttributesExW($0, GetFileExInfoStandard, &faAttributes) else {
383+
return (try? path.withNTPathRepresentation { pwszPath in
384+
let handle = CreateFileW(pwszPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nil)
385+
if handle == INVALID_HANDLE_VALUE {
386+
return (false, false)
387+
}
388+
defer { CloseHandle(handle) }
389+
390+
var info: BY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION()
391+
guard GetFileInformationByHandle(handle, &info) else {
386392
return (false, false)
387393
}
388-
return (true, faAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY)
394+
395+
return (true, info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY)
389396
}) ?? (false, false)
390397
#else
391398
path.withFileSystemRepresentation { rep -> (Bool, Bool) in

Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,9 @@ final class FileManagerTests : XCTestCase {
526526
"bar"
527527
}
528528
"other"
529+
SymbolicLink("link_to_file", destination: "other")
530+
SymbolicLink("link_to_dir", destination: "dir")
531+
SymbolicLink("link_to_nonexistent", destination: "does_not_exist")
529532
}.test {
530533
#if FOUNDATION_FRAMEWORK
531534
var isDir: ObjCBool = false
@@ -546,7 +549,12 @@ final class FileManagerTests : XCTestCase {
546549
XCTAssertTrue(isDirBool())
547550
XCTAssertTrue($0.fileExists(atPath: "other", isDirectory: &isDir))
548551
XCTAssertFalse(isDirBool())
552+
XCTAssertTrue($0.fileExists(atPath: "link_to_file", isDirectory: &isDir))
553+
XCTAssertFalse(isDirBool())
554+
XCTAssertTrue($0.fileExists(atPath: "link_to_dir", isDirectory: &isDir))
555+
XCTAssertTrue(isDirBool())
549556
XCTAssertFalse($0.fileExists(atPath: "does_not_exist"))
557+
XCTAssertFalse($0.fileExists(atPath: "link_to_nonexistent"))
550558
}
551559
}
552560

0 commit comments

Comments
 (0)