@@ -168,32 +168,41 @@ public struct FileIterator: Sequence, IteratorProtocol {
168
168
/// exist or is not a supported file type. If `followSymlinks` is `true`, the returned URL may be
169
169
/// different from the given URL; otherwise, it will be the same.
170
170
private func fileAndType( at url: URL , followSymlinks: Bool ) -> ( URL , FileAttributeType ) ? {
171
- func typeOfFile( at url: URL ) -> FileAttributeType ? {
172
- // We cannot use `URL.resourceValues(forKeys:)` here because it appears to behave incorrectly on
173
- // Linux.
174
- return try ? FileManager . default. attributesOfItem ( atPath: url. path) [ . type] as? FileAttributeType
171
+ var url = url
172
+ if followSymlinks {
173
+ url = realPath ( of: url)
175
174
}
176
175
177
- guard var fileType = typeOfFile ( at: url) else {
176
+ // We cannot use `URL.resourceValues(forKeys:)` here because it appears to behave incorrectly on
177
+ // Linux.
178
+ guard
179
+ let type =
180
+ try ? FileManager . default. attributesOfItem ( atPath: url. path) [ . type] as? FileAttributeType
181
+ else {
178
182
return nil
179
183
}
180
184
181
- // We would use `standardizedFileURL.path` here as we do in the iterator above to ensure that
182
- // path components like `.` and `..` are resolved, but the standardized URLs returned by
183
- // Foundation pre-Swift-6.0 resolve symlinks. This causes the file type of a URL and its
184
- // standardized path to not match .
185
- var visited : Set < String > = [ url . absoluteString ]
186
- var url = url
187
- while followSymlinks && fileType == . typeSymbolicLink ,
188
- let destination = try ? FileManager . default . destinationOfSymbolicLink ( atPath : url . path )
189
- {
190
- url = URL ( fileURLWithPath : destination , relativeTo : url )
191
- // If this URL is in the visited set, we must have a symlink cycle. Ignore it gracefully.
192
- guard !visited . contains ( url . absoluteString ) , let newType = typeOfFile ( at : url ) else {
193
- return nil
185
+ return ( url , type )
186
+ }
187
+
188
+ /// Assuming the given URL is a file URL, resolves all symlinks in its path .
189
+ ///
190
+ /// - Note: We need this because `URL.resolvingSymlinksInPath()` not only resolves symlinks but also standardizes the
191
+ /// path by stripping away `private` prefixes.
192
+ @ _spi ( Internal )
193
+ public func realPath ( of url : URL ) -> URL {
194
+ #if canImport(Darwin )
195
+ return url . path . withCString { path in
196
+ guard let realpath = Darwin . realpath ( path , nil ) else {
197
+ return url
194
198
}
195
- visited. insert ( url. absoluteString)
196
- fileType = newType
199
+ let result = URL ( fileURLWithPath: String ( cString: realpath) )
200
+ free ( realpath)
201
+ return result
197
202
}
198
- return ( url, fileType)
203
+ #else
204
+ // Non-Darwin platforms don't have the `/private` stripping issue, so we can just use `self.resolvingSymlinksInPath`
205
+ // here.
206
+ return url. resolvingSymlinksInPath ( )
207
+ #endif
199
208
}
0 commit comments