From cbb6f581de4dbacbb2c0300f06fb8f213bf1dcdb Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 2 Jun 2023 08:39:36 -0700 Subject: [PATCH] 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. --- Sources/Foundation/FileManager+Win32.swift | 4 +++- Tests/Foundation/Tests/TestFileManager.swift | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Sources/Foundation/FileManager+Win32.swift b/Sources/Foundation/FileManager+Win32.swift index 48c5acd36a..8d486a1f28 100644 --- a/Sources/Foundation/FileManager+Win32.swift +++ b/Sources/Foundation/FileManager+Win32.swift @@ -986,7 +986,9 @@ extension FileManager { ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN == FILE_ATTRIBUTE_HIDDEN { continue } - _stack.append(URL(fileURLWithPath: file, relativeTo: _lastReturned)) + + let isDirectory = ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY && ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != FILE_ATTRIBUTE_REPARSE_POINT + _stack.append(_lastReturned.appendingPathComponent(file, isDirectory: isDirectory)) } while FindNextFileW(handle, &ffd) } return firstValidItem() diff --git a/Tests/Foundation/Tests/TestFileManager.swift b/Tests/Foundation/Tests/TestFileManager.swift index b90b5d17a9..a20c133340 100644 --- a/Tests/Foundation/Tests/TestFileManager.swift +++ b/Tests/Foundation/Tests/TestFileManager.swift @@ -754,7 +754,22 @@ class TestFileManager : XCTestCase { XCTFail("Failed to clean up files") } } - + + func test_contentsOfDirectoryEnumeration() throws { + let fm = FileManager.default + + let root = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(NSUUID().uuidString, isDirectory: true) + let subdirectory = root.appendingPathComponent("subdirectory", isDirectory: true) + let file = subdirectory.appendingPathComponent("file", isDirectory: false) + try? fm.removeItem(at: root) + + try XCTAssertNoThrow(fm.createDirectory(at: subdirectory, withIntermediateDirectories: true, attributes: nil)) + try XCTAssertNoThrow(fm.createFile(atPath: file.path, contents: Data(), attributes: nil)) + let contents = try XCTUnwrap(fm.contentsOfDirectory(at: root, includingPropertiesForKeys: [.isDirectoryKey], options: [.skipsHiddenFiles])) + XCTAssertEqual(contents.count, 1) + XCTAssertEqual(contents, [subdirectory]) + } + func test_subpathsOfDirectoryAtPath() { let fm = FileManager.default let path = NSTemporaryDirectory() + "testdir" @@ -2032,6 +2047,7 @@ VIDEOS=StopgapVideos ("test_directoryEnumerator", test_directoryEnumerator), ("test_pathEnumerator",test_pathEnumerator), ("test_contentsOfDirectoryAtPath", test_contentsOfDirectoryAtPath), + ("test_contentsOfDirectoryEnumeration", test_contentsOfDirectoryEnumeration), ("test_subpathsOfDirectoryAtPath", test_subpathsOfDirectoryAtPath), ("test_copyItemAtPathToPath", test_copyItemAtPathToPath), ("test_linkItemAtPathToPath", testExpectedToFailOnAndroid(test_linkItemAtPathToPath, "Android doesn't allow hard links")),