Skip to content

Commit 0bb1fc3

Browse files
authored
Merge pull request swiftlang#5491 from kperryua/dir-enumerator-crash
2 parents 7a3a1e3 + eba89a6 commit 0bb1fc3

File tree

4 files changed

+106
-0
lines changed

4 files changed

+106
-0
lines changed

apinotes/Foundation.apinotes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,9 @@ Classes:
659659
- Selector: 'URLForPublishingUbiquitousItemAtURL:expirationDate:error:'
660660
SwiftName: url(forPublishingUbiquitousItemAt:expiration:)
661661
MethodKind: Instance
662+
- Selector: 'enumeratorAtURL:includingPropertiesForKeys:options:errorHandler:'
663+
MethodKind: Instance
664+
SwiftPrivate: true
662665
- Name: NSFileVersion
663666
Methods:
664667
- Selector: 'versionOfItemAtURL:forPersistentIdentifier:'

stdlib/public/SDK/Foundation/FileManager.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ internal func NS_Swift_NSFileManager_replaceItemAtURL_withItemAtURL_backupItemNa
2121
_ options: FileManager.ItemReplacementOptions,
2222
_ error: NSErrorPointer) -> NSURL?
2323

24+
@_silgen_name("NS_Swift_NSFileManager_enumeratorAt_includingPropertiesForKeys_options_errorHandler")
25+
internal func NS_Swift_NSFileManager_enumeratorAt_includingPropertiesForKeys_options_errorHandler(
26+
_ self_ : AnyObject,
27+
_ url: AnyObject,
28+
_ keys: NSArray?,
29+
_ options: FileManager.DirectoryEnumerationOptions,
30+
_ errorHandler: @escaping @convention(block) (NSURL, NSError) -> Bool) -> FileManager.DirectoryEnumerator?
2431

2532
extension FileManager {
2633
/*
@@ -45,4 +52,16 @@ extension FileManager {
4552
}
4653
throw error!
4754
}
55+
56+
@available(OSX 10.6, iOS 4.0, *)
57+
public func enumerator(at url: URL, includingPropertiesForKeys keys: [URLResourceKey]?, options mask: FileManager.DirectoryEnumerationOptions = [], errorHandler handler: ((URL, Error) -> Bool)? = nil) -> FileManager.DirectoryEnumerator? {
58+
return NS_Swift_NSFileManager_enumeratorAt_includingPropertiesForKeys_options_errorHandler(self, url as NSURL, keys as NSArray?, mask, { (url, error) in
59+
var errorResult = true;
60+
if let h = handler {
61+
errorResult = h(url as URL, error)
62+
}
63+
return errorResult
64+
})
65+
}
66+
4867
}

stdlib/public/SDK/Foundation/FileManagerThunks.m

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,31 @@
3333
[backupItemName release];
3434
return success ? [result retain] : nil;
3535
}
36+
37+
extern /*"C"*/ NS_RETURNS_RETAINED id
38+
NS_Swift_NSFileManager_enumeratorAt_includingPropertiesForKeys_options_errorHandler(
39+
NSFileManager *NS_RELEASES_ARGUMENT _Nonnull self_,
40+
NSURL *NS_RELEASES_ARGUMENT _Nonnull url,
41+
NSArray *NS_RELEASES_ARGUMENT _Nullable keys,
42+
NSUInteger options,
43+
BOOL (^_Nonnull errorHandler)(NSURL * _Nonnull url, NSError * _Nonnull error) ) {
44+
45+
NSDirectoryEnumerator *result = [self_ enumeratorAtURL:url
46+
includingPropertiesForKeys:keys
47+
options:(NSDirectoryEnumerationOptions)options
48+
errorHandler:^(NSURL * url, NSError *error) {
49+
50+
NSURL *realURL = url ?: error.userInfo[NSURLErrorKey];
51+
if (!realURL) {
52+
NSString *path = error.userInfo[NSFilePathErrorKey];
53+
realURL = [NSURL fileURLWithPath:path];
54+
}
55+
return errorHandler(realURL, error);
56+
57+
}];
58+
[self_ release];
59+
[url release];
60+
[keys release];
61+
[errorHandler release];
62+
return [result retain];
63+
}

test/stdlib/TestFileManager.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,68 @@ class TestFileManager : TestFileManagerSuper {
7272
expectTrue(threw, "Should have thrown")
7373

7474
}
75+
76+
func testDirectoryEnumerator_error() {
77+
let fm = FileManager.default
78+
let nonexistantURL = URL(fileURLWithPath: "\(NSTemporaryDirectory())/nonexistant")
79+
80+
var invoked = false
81+
let e = fm.enumerator(at: nonexistantURL, includingPropertiesForKeys: []) { (url, err) in
82+
invoked = true
83+
expectEqual(nonexistantURL, url)
84+
expectEqual((err as NSError).code, NSFileReadNoSuchFileError)
85+
return true
86+
}
87+
88+
let url = e?.nextObject()
89+
expectTrue(invoked)
90+
expectTrue(url == nil)
91+
92+
}
93+
94+
func testDirectoryEnumerator_error_noHandler() {
95+
let fm = FileManager.default
96+
let nonexistantURL = URL(fileURLWithPath: "\(NSTemporaryDirectory())/nonexistant")
97+
98+
let e = fm.enumerator(at: nonexistantURL, includingPropertiesForKeys: [])
99+
let url = e?.nextObject()
100+
expectTrue(url == nil)
101+
102+
}
103+
104+
func testDirectoryEnumerator_simple() {
105+
let fm = FileManager.default
106+
let dirPath = (NSTemporaryDirectory() as NSString).appendingPathComponent(NSUUID().uuidString)
107+
try! fm.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil)
108+
defer { try! FileManager.default.removeItem(atPath: dirPath) }
109+
110+
let item1 = URL(fileURLWithPath: "\(dirPath)/1", isDirectory: false)
111+
let item2 = URL(fileURLWithPath: "\(dirPath)/2", isDirectory: false)
112+
113+
try! Data().write(to: item1)
114+
try! Data().write(to: item2)
115+
116+
let e = fm.enumerator(at: URL(fileURLWithPath: dirPath, isDirectory: true), includingPropertiesForKeys: [])
117+
let result1 = e?.nextObject()
118+
let result2 = e?.nextObject()
119+
let result3 = e?.nextObject()
120+
121+
// Avoid potential symlink discrepancy between the result and the original URL
122+
expectEqual((result1! as! URL).lastPathComponent, item1.lastPathComponent)
123+
expectEqual((result2! as! URL).lastPathComponent, item2.lastPathComponent)
124+
expectTrue(result3 == nil)
125+
126+
}
127+
75128
}
76129

77130
#if !FOUNDATION_XCTEST
78131
var FMTests = TestSuite("TestFileManager")
79132
FMTests.test("testReplaceItem") { TestFileManager().testReplaceItem() }
80133
FMTests.test("testReplaceItem_error") { TestFileManager().testReplaceItem_error() }
134+
FMTests.test("testDirectoryEnumerator_error") { TestFileManager().testDirectoryEnumerator_error() }
135+
FMTests.test("testDirectoryEnumerator_error_noHandler") { TestFileManager().testDirectoryEnumerator_error_noHandler() }
136+
FMTests.test("testDirectoryEnumerator_simple") { TestFileManager().testDirectoryEnumerator_simple() }
81137

82138
runAllTests()
83139
#endif

0 commit comments

Comments
 (0)