Skip to content

Commit 0a2ecf0

Browse files
authored
Merge pull request swiftlang#1403 from hartbit/filesystem-exists-symlink
Allow FileSystem.exists to follow symlinks or not
2 parents 458b6ba + 95bd923 commit 0a2ecf0

File tree

5 files changed

+48
-11
lines changed

5 files changed

+48
-11
lines changed

Sources/Basic/FileSystem.swift

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public enum FileMode {
115115
// FIXME: Design an asynchronous story?
116116
public protocol FileSystem: class {
117117
/// Check whether the given path exists and is accessible.
118-
func exists(_ path: AbsolutePath) -> Bool
118+
func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool
119119

120120
/// Check whether the given path is accessible and a directory.
121121
func isDirectory(_ path: AbsolutePath) -> Bool
@@ -168,6 +168,11 @@ public protocol FileSystem: class {
168168
/// Convenience implementations (default arguments aren't permitted in protocol
169169
/// methods).
170170
public extension FileSystem {
171+
/// exists override with default value.
172+
func exists(_ path: AbsolutePath) -> Bool {
173+
return exists(path, followSymlink: true)
174+
}
175+
171176
/// Default implementation of createDirectory(_:)
172177
func createDirectory(_ path: AbsolutePath) throws {
173178
try createDirectory(path, recursive: false)
@@ -197,8 +202,8 @@ private class LocalFileSystem: FileSystem {
197202
return filestat.st_mode & libc.S_IXUSR != 0
198203
}
199204

200-
func exists(_ path: AbsolutePath) -> Bool {
201-
return Basic.exists(path)
205+
func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool {
206+
return Basic.exists(path, followSymlink: followSymlink)
202207
}
203208

204209
func isDirectory(_ path: AbsolutePath) -> Bool {
@@ -332,7 +337,7 @@ private class LocalFileSystem: FileSystem {
332337
}
333338

334339
func removeFileTree(_ path: AbsolutePath) throws {
335-
if self.exists(path) {
340+
if self.exists(path, followSymlink: false) {
336341
try Basic.removeFileTree(path)
337342
}
338343
}
@@ -509,7 +514,7 @@ public class InMemoryFileSystem: FileSystem {
509514

510515
// MARK: FileSystem Implementation
511516

512-
public func exists(_ path: AbsolutePath) -> Bool {
517+
public func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool {
513518
do {
514519
return try getNode(path) != nil
515520
} catch {
@@ -706,8 +711,8 @@ public class RerootedFileSystemView: FileSystem {
706711

707712
// MARK: FileSystem Implementation
708713

709-
public func exists(_ path: AbsolutePath) -> Bool {
710-
return underlyingFileSystem.exists(formUnderlyingPath(path))
714+
public func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool {
715+
return underlyingFileSystem.exists(formUnderlyingPath(path), followSymlink: followSymlink)
711716
}
712717

713718
public func isDirectory(_ path: AbsolutePath) -> Bool {

Sources/Basic/PathShims.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public func makeDirectories(_ path: AbsolutePath) throws {
8989
}
9090

9191
/// Recursively deletes the file system entity at `path`. If there is no file system entity at `path`, this function
92-
/// does nothing (in particular, this is not considered to be an error).
92+
/// throws an error.
9393
public func removeFileTree(_ path: AbsolutePath) throws {
9494
try FileManager.default.removeItem(atPath: path.asString)
9595
}

Sources/SourceControl/GitRepository.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ private class GitFileSystemView: FileSystem {
561561
return tree
562562
}
563563

564-
func exists(_ path: AbsolutePath) -> Bool {
564+
func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool {
565565
do {
566566
return try getEntry(path) != nil
567567
} catch {

Sources/SourceControl/InMemoryGitRepository.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ public final class InMemoryGitRepository {
183183

184184
extension InMemoryGitRepository: FileSystem {
185185

186-
public func exists(_ path: AbsolutePath) -> Bool {
187-
return head.fileSystem.exists(path)
186+
public func exists(_ path: AbsolutePath, followSymlink: Bool) -> Bool {
187+
return head.fileSystem.exists(path, followSymlink: followSymlink)
188188
}
189189

190190
public func isDirectory(_ path: AbsolutePath) -> Bool {

Tests/BasicTests/FileSystemTests.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,37 @@ class FileSystemTests: XCTestCase {
7171
XCTAssertTrue(thisDirectoryContents.contains(where: { $0 == AbsolutePath(#file).basename }))
7272
}
7373

74+
func testLocalExistsSymlink() throws {
75+
mktmpdir { path in
76+
let fs = Basic.localFileSystem
77+
78+
let source = path.appending(component: "source")
79+
let target = path.appending(component: "target")
80+
try fs.writeFileContents(target, bytes: "source")
81+
82+
// Source and target exist.
83+
84+
try createSymlink(source, pointingAt: target)
85+
XCTAssertEqual(fs.exists(source), true)
86+
XCTAssertEqual(fs.exists(source, followSymlink: true), true)
87+
XCTAssertEqual(fs.exists(source, followSymlink: false), true)
88+
89+
// Source only exists.
90+
91+
try fs.removeFileTree(target)
92+
XCTAssertEqual(fs.exists(source), false)
93+
XCTAssertEqual(fs.exists(source, followSymlink: true), false)
94+
XCTAssertEqual(fs.exists(source, followSymlink: false), true)
95+
96+
// None exist.
97+
98+
try fs.removeFileTree(source)
99+
XCTAssertEqual(fs.exists(source), false)
100+
XCTAssertEqual(fs.exists(source, followSymlink: true), false)
101+
XCTAssertEqual(fs.exists(source, followSymlink: false), false)
102+
}
103+
}
104+
74105
func testLocalCreateDirectory() throws {
75106
let fs = Basic.localFileSystem
76107

@@ -378,6 +409,7 @@ class FileSystemTests: XCTestCase {
378409
("testLocalBasics", testLocalBasics),
379410
("testLocalCreateDirectory", testLocalCreateDirectory),
380411
("testLocalReadWriteFile", testLocalReadWriteFile),
412+
("testLocalExistsSymlink", testLocalExistsSymlink),
381413
("testInMemoryBasics", testInMemoryBasics),
382414
("testInMemoryCreateDirectory", testInMemoryCreateDirectory),
383415
("testInMemoryFsCopy", testInMemoryFsCopy),

0 commit comments

Comments
 (0)