Skip to content

Commit 4eacf88

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 bd2e810 commit 4eacf88

File tree

2 files changed

+20
-2
lines changed

2 files changed

+20
-2
lines changed

Sources/Foundation/FileManager+Win32.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,9 @@ extension FileManager {
986986
ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN == FILE_ATTRIBUTE_HIDDEN {
987987
continue
988988
}
989-
_stack.append(URL(fileURLWithPath: file, relativeTo: _lastReturned))
989+
990+
let isDirectory = ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY && ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != FILE_ATTRIBUTE_REPARSE_POINT
991+
_stack.append(_lastReturned.appendingPathComponent(file, isDirectory: isDirectory))
990992
} while FindNextFileW(handle, &ffd)
991993
}
992994
return firstValidItem()

Tests/Foundation/Tests/TestFileManager.swift

+17-1
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,22 @@ class TestFileManager : XCTestCase {
754754
XCTFail("Failed to clean up files")
755755
}
756756
}
757-
757+
758+
func test_contentsOfDirectoryEnumeration() throws {
759+
let fm = FileManager.default
760+
761+
let root = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(NSUUID().uuidString, isDirectory: true)
762+
let subdirectory = root.appendingPathComponent("subdirectory", isDirectory: true)
763+
let file = subdirectory.appendingPathComponent("file", isDirectory: false)
764+
try? fm.removeItem(at: root)
765+
766+
try XCTAssertNoThrow(fm.createDirectory(at: subdirectory, withIntermediateDirectories: true, attributes: nil))
767+
try XCTAssertNoThrow(fm.createFile(atPath: file.path, contents: Data(), attributes: nil))
768+
let contents = try XCTUnwrap(fm.contentsOfDirectory(at: root, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles]))
769+
XCTAssertEqual(contents.count, 1)
770+
XCTAssertEqual(contents, [subdirectory])
771+
}
772+
758773
func test_subpathsOfDirectoryAtPath() {
759774
let fm = FileManager.default
760775
let path = NSTemporaryDirectory() + "testdir"
@@ -2032,6 +2047,7 @@ VIDEOS=StopgapVideos
20322047
("test_directoryEnumerator", test_directoryEnumerator),
20332048
("test_pathEnumerator",test_pathEnumerator),
20342049
("test_contentsOfDirectoryAtPath", test_contentsOfDirectoryAtPath),
2050+
("test_contentsOfDirectoryEnumeration", test_contentsOfDirectoryEnumeration),
20352051
("test_subpathsOfDirectoryAtPath", test_subpathsOfDirectoryAtPath),
20362052
("test_copyItemAtPathToPath", test_copyItemAtPathToPath),
20372053
("test_linkItemAtPathToPath", testExpectedToFailOnAndroid(test_linkItemAtPathToPath, "Android doesn't allow hard links")),

0 commit comments

Comments
 (0)