Skip to content

Commit f3eac9a

Browse files
authored
Merge pull request swiftlang#1496 from spevans/pr_fm_mountedvolumeurls
2 parents 397eaed + 83aef4e commit f3eac9a

File tree

2 files changed

+78
-3
lines changed

2 files changed

+78
-3
lines changed

Foundation/FileManager.swift

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,66 @@ open class FileManager : NSObject {
3232
}
3333
}
3434

35-
/* Returns an NSArray of NSURLs locating the mounted volumes available on the computer. The property keys that can be requested are available in NSURL.
36-
*/
35+
/// Returns an array of URLs that identify the mounted volumes available on the device.
3736
open func mountedVolumeURLs(includingResourceValuesForKeys propertyKeys: [URLResourceKey]?, options: VolumeEnumerationOptions = []) -> [URL]? {
38-
NSUnimplemented()
37+
var urls: [URL]
38+
39+
#if os(Linux)
40+
guard let procMounts = try? String(contentsOfFile: "/proc/mounts", encoding: .utf8) else {
41+
return nil
42+
}
43+
urls = []
44+
for line in procMounts.components(separatedBy: "\n") {
45+
let mountPoint = line.components(separatedBy: " ")
46+
if mountPoint.count > 2 {
47+
urls.append(URL(fileURLWithPath: mountPoint[1], isDirectory: true))
48+
}
49+
}
50+
#elseif canImport(Darwin)
51+
52+
func mountPoints(_ statBufs: UnsafePointer<statfs>, _ fsCount: Int) -> [URL] {
53+
var urls: [URL] = []
54+
55+
for fsIndex in 0..<fsCount {
56+
var fs = statBufs.advanced(by: fsIndex).pointee
57+
58+
if options.contains(.skipHiddenVolumes) && fs.f_flags & UInt32(MNT_DONTBROWSE) != 0 {
59+
continue
60+
}
61+
62+
let mountPoint = withUnsafePointer(to: &fs.f_mntonname.0) { (ptr: UnsafePointer<Int8>) -> String in
63+
return string(withFileSystemRepresentation: ptr, length: strlen(ptr))
64+
}
65+
urls.append(URL(fileURLWithPath: mountPoint, isDirectory: true))
66+
}
67+
return urls
68+
}
69+
70+
if #available(OSX 10.13, *) {
71+
var statBufPtr: UnsafeMutablePointer<statfs>?
72+
let fsCount = getmntinfo_r_np(&statBufPtr, MNT_WAIT)
73+
guard let statBuf = statBufPtr, fsCount > 0 else {
74+
return nil
75+
}
76+
urls = mountPoints(statBuf, Int(fsCount))
77+
free(statBufPtr)
78+
} else {
79+
var fsCount = getfsstat(nil, 0, MNT_WAIT)
80+
guard fsCount > 0 else {
81+
return nil
82+
}
83+
let statBuf = UnsafeMutablePointer<statfs>.allocate(capacity: Int(fsCount))
84+
defer { statBuf.deallocate() }
85+
fsCount = getfsstat(statBuf, fsCount * Int32(MemoryLayout<statfs>.stride), MNT_WAIT)
86+
guard fsCount > 0 else {
87+
return nil
88+
}
89+
urls = mountPoints(statBuf, Int(fsCount))
90+
}
91+
#else
92+
#error("Requires a platform-specific implementation")
93+
#endif
94+
return urls
3995
}
4096

4197
/* Returns an NSArray of NSURLs identifying the the directory entries.

TestFoundation/TestFileManager.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class TestFileManager : XCTestCase {
3434
("test_homedirectoryForUser", test_homedirectoryForUser),
3535
("test_temporaryDirectoryForUser", test_temporaryDirectoryForUser),
3636
("test_creatingDirectoryWithShortIntermediatePath", test_creatingDirectoryWithShortIntermediatePath),
37+
("test_mountedVolumeURLs", test_mountedVolumeURLs)
3738
]
3839
}
3940

@@ -601,4 +602,22 @@ class TestFileManager : XCTestCase {
601602
XCTFail("Unable to write a file to the temporary directory: \(tmpDir), err: \(error)")
602603
}
603604
}
605+
606+
func test_mountedVolumeURLs() {
607+
guard let volumes = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys:[], options: []) else {
608+
XCTFail("mountedVolumeURLs returned nil")
609+
return
610+
}
611+
XCTAssertNotEqual(0, volumes.count)
612+
XCTAssertTrue(volumes.contains(URL(fileURLWithPath: "/")))
613+
#if os(macOS)
614+
// On macOS, .skipHiddenVolumes should hide 'nobrowse' volumes of which there should be at least one
615+
guard let visibleVolumes = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: [], options: [.skipHiddenVolumes]) else {
616+
XCTFail("mountedVolumeURLs returned nil")
617+
return
618+
}
619+
XCTAssertTrue(visibleVolumes.count > 0)
620+
XCTAssertTrue(visibleVolumes.count < volumes.count)
621+
#endif
622+
}
604623
}

0 commit comments

Comments
 (0)