Skip to content

Commit 8cea9ea

Browse files
committed
Foundation: correct directory iteration on Windows
The path that was being constructed would elide the penultimate arc in the path as the base URL was not marked as a directory. As such, it was assumed to be a file URL, and making a URL relative to it would truncate the previously last arc. Append the path component instead and explicitly indicate if it is a directory component when building the URL as we already have the information on hand. This repairs the directory traversal on Windows. The bug was identified by the DocC test suite on Windows.
1 parent b70418c commit 8cea9ea

File tree

2 files changed

+19
-1
lines changed

2 files changed

+19
-1
lines changed

Sources/Foundation/FileManager+Win32.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,9 @@ extension FileManager {
989989
ffd.dwFileAttributes & DWORD(FILE_ATTRIBUTE_HIDDEN) == DWORD(FILE_ATTRIBUTE_HIDDEN) {
990990
continue
991991
}
992-
_stack.append(URL(fileURLWithPath: file, relativeTo: _lastReturned))
992+
993+
let isDirectory = ffd.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) == DWORD(FILE_ATTRIBUTE_DIRECTORY)
994+
_stack.append(_lastReturned.appendingPathComponent(file, isDirectory: isDirectory))
993995
} while FindNextFileW(handle, &ffd)
994996
}
995997
return firstValidItem()

Tests/Foundation/Tests/TestFileManager.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,21 @@ class TestFileManager : XCTestCase {
750750
XCTFail("Failed to clean up files")
751751
}
752752
}
753+
754+
func test_contentsOfDirectoryEnumeration() {
755+
let fm = FileManager.default
756+
757+
let root = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(NSUUID().uuidString, isDirectory: true)
758+
let subdirectory = root.appendingPathComponent("subdirectory", isDirectory: true)
759+
let file = subdirectory.appendingPathComponent("file", isDirectory: false)
760+
try? fm.removeItem(at: root)
761+
762+
XCTAssertNoThrow(fm.createDirectory(at: subdirectory, withIntermediateDirectories: true, attributes: nil))
763+
XCTAssertNoThrow(fm.createFile(atPath: file.path, contents: Data(), attributes: nil))
764+
let contents = try XCTUnwrap(fm.contentsOfDirectory(at: root, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles]))
765+
XCTAssertEqual(contents.count, 2)
766+
XCTAssertEqual(contents, [subdirectory, file])
767+
}
753768

754769
func test_subpathsOfDirectoryAtPath() {
755770
let fm = FileManager.default
@@ -2028,6 +2043,7 @@ VIDEOS=StopgapVideos
20282043
("test_directoryEnumerator", test_directoryEnumerator),
20292044
("test_pathEnumerator",test_pathEnumerator),
20302045
("test_contentsOfDirectoryAtPath", test_contentsOfDirectoryAtPath),
2046+
("test_contentsOfDirectoryEnumeration", test_contentsOfDirectoryEnumeration),
20312047
("test_subpathsOfDirectoryAtPath", test_subpathsOfDirectoryAtPath),
20322048
("test_copyItemAtPathToPath", test_copyItemAtPathToPath),
20332049
("test_linkItemAtPathToPath", testExpectedToFailOnAndroid(test_linkItemAtPathToPath, "Android doesn't allow hard links")),

0 commit comments

Comments
 (0)