マウントされたボリュームの、ファイルシステムの情報を表示する

# 概要
- マウントされたボリュームの一覧を取得し、それぞれのファイルシステムの情報を表示します。
    - ファイルシステムの情報を、`NSWorkspace`の[getFileSystemInfo\(forPath:isRemovable:isWritable:isUnmountable:description:type:\)](https://developer.apple.com/documentation/appkit/nsworkspace/1526987-getfilesysteminfo)で取得します。
    - またマウントされたボリュームの一覧を、`FileManager`の[mountedVolumeURLs\(includingResourceValuesForKeys:options:\)](https://developer.apple.com/documentation/foundation/filemanager/1409626-mountedvolumeurls)で取得します。

# 参考
- [CocoaのためのCポインタの取り扱いその3 \[Swift\] \- Cocoa API解説\(macOS/iOS\)](https://cocoaapi.hatenablog.com/entry/2015/04/16/Cocoa%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AEC%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E3%81%AE%E5%8F%96%E3%82%8A%E6%89%B1%E3%81%84%E3%81%9D%E3%81%AE3)
- [Swift: How to get the value from a AutoreleasingUnsafePointer<NSString?> from a NSScanner?](https://stackoverflow.com/questions/24205768/swift-how-to-get-the-value-from-a-autoreleasingunsafepointernsstring-from-a/24206064#24206064)

# 結果
- 色々見たいので、いくつか機器をつないだり、サーバに接続した状態で実行しています。
- 下記が原因で、macOS 11.0よりルートフォルダ`/`の書き込みが不可になっていることが確認できます。
- [macOS Catalina の読み取り専用のシステムボリュームについて](https://support.apple.com/ja-jp/HT210650)
    - `このボリュームは読み取り専用で、「Macintosh HD」`:`/`
    - `ファイルとデータは、「Macintosh HD - Data」`:`/System/Volumes/Data/`

## macOS 11.0

```
********* 
/  /* ルートフォルダ */
is not removal media
is not writable
is not unmountable
apfs apfs
********* 
/Volumes/BOOTCAMP
is not removal media
is writable
is not unmountable
ntfs ntfs
********* 
/Volumes/Untitled 1  /* USBメモリ */
is removal media
is writable
is unmountable
msdos msdos
********* 
/Volumes/<サーバ名>  /* smb接続しているサーバ */
is not removal media
is writable
is unmountable
smbfs smbfs
********* 
/Volumes/<DVD名>  /* DVD */
is removal media
is not writable
is unmountable
hfs hfs
```

## macOS 10.15

```
********* 
/  /* ルートフォルダ */
is not removal media
is writable
is not unmountable
apfs apfs
********* 
/Volumes/<サーバ名>  /* smb接続しているサーバ */
is not removal media
is writable
is unmountable
smbfs smbfs
********* 
/Volumes/Untitled  /* USBメモリ */
is removal media
is writable
is unmountable
msdos msdos
```

# 実装

```swift
printVolumesInfo()

private func printVolumesInfo() {
    guard let volumeUrls = FileManager.default.mountedVolumeURLs(
            includingResourceValuesForKeys: nil,
            options: FileManager.VolumeEnumerationOptions.skipHiddenVolumes) else {
        return
    }
    
    for volumeUrl in volumeUrls {
        // Swiftにおけるポインタ参考
        // [CocoaのためのCポインタの取り扱いその3 \[Swift\] \- Cocoa API解説\(macOS/iOS\)](https://cocoaapi.hatenablog.com/entry/2015/04/16/Cocoa%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AEC%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E3%81%AE%E5%8F%96%E3%82%8A%E6%89%B1%E3%81%84%E3%81%9D%E3%81%AE3)
        var isRemoval = ObjCBool(false)
        var isWritable = ObjCBool(false)
        var isUnmountable = ObjCBool(false)
        
        // AutoreleasingUnsafePointer<NSString?>の取得
        // [Swift: How to get the value from a AutoreleasingUnsafePointer<NSString?> from a NSScanner?](https://stackoverflow.com/questions/24205768/swift-how-to-get-the-value-from-a-autoreleasingunsafepointernsstring-from-a/24206064#24206064)
        var description: NSString? = nil
        var type: NSString? = nil
        
        NSWorkspace.shared.getFileSystemInfo(forPath: volumeUrl.path,
                                             isRemovable: &isRemoval,
                                             isWritable: &isWritable,
                                             isUnmountable: &isUnmountable,
                                             description: &description,
                                             type: &type)
        
        let descriptionForPrint = description as String?
        let typeForPrint = type as String?
        var diskInfo = ""
        if let descriptionForPrint = descriptionForPrint {
            diskInfo = diskInfo.appending(descriptionForPrint)
        }
        if let typeForPrint = typeForPrint {
            diskInfo = diskInfo.appendingFormat("%@%@", diskInfo.isEmpty ? "" : ", " , typeForPrint)
        }
        print(
            """
*******
\(volumeUrl.path)
\(FileManager.default.displayName(atPath: volumeUrl.path))
\(isRemoval.boolValue ? "is removal media" : "is not removal media")
\(isWritable.boolValue ? "is writable" : "is not writable")
\(isUnmountable.boolValue ? "is unmountable" : "is not unmountable")
\(diskInfo)
""")
        
    }
}
```