Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I obtain disk identifier in Swift

Tags:

macos

swift

I'd like to find the identifiers of attached drives - as seen in the Terminal command diskutil list

   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *1.0 TB     disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:                  Apple_HFS SSHD OSX                511.7 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
   4:                  Apple_HFS SSHD OSX NEW            511.0 GB   disk0s4
   5:                 Apple_Boot Recovery HD             650.0 MB   disk0s5

So far I've managed to write this code:

var volume_stack = [NSURL]()

volume_stack = NSFileManager.defaultManager().mountedVolumeURLsIncludingResourceValuesForKeys([NSURLVolumeNameKey, NSURLVolumeIdentifierKey], options: [])!
for disk: NSURL in volume_stack {
    do {
        var info: Dictionary = [String : AnyObject]()
        info = try disk.resourceValuesForKeys([NSURLVolumeIdentifierKey])
        for (key, value) in info {
            var s_value = String(data: value as! NSData, encoding: NSUTF8StringEncoding)
            print("key: \(key) value: \(s_value)")
        }
    }
    catch {
        print ("ERROR")
    }
}

but the output is:

key: NSURLVolumeIdentifierKey value: Optional("gEd\0\0\0\0\0")
key: NSURLVolumeIdentifierKey value: nil
key: NSURLVolumeIdentifierKey value: nil
key: NSURLVolumeIdentifierKey value: Optional("sHg\0\0\0\0\0")

a log of the ivar value instead of s_value gives

key: NSURLVolumeIdentifierKey value: <67456400 00000000>
key: NSURLVolumeIdentifierKey value: <c6236500 00000000>
key: NSURLVolumeIdentifierKey value: <69986600 00000000>
key: NSURLVolumeIdentifierKey value: <73486700 00000000>

and from what I can tell it's NSData. Apologies for the badly written code but it's my first Swift app and I've been going around in circles so it's become a little unsophisticated. Is there a way to obtain what's being listed in the identifier column as a String?

like image 471
Todd Avatar asked Feb 17 '16 21:02

Todd


2 Answers

Use DiskArbitration.framework

At a high level, you can use the Disk Arbitration framework to:

  • Receive notification of disk-related events (disk ejection, for example) and participate in the arbitration process (preventing disk ejection, for example).
  • Obtain information about disks and manipulate disks (requesting that a disk be ejected, for example).

First create a global session with DASessionCreate, then create a disk reference with DADiskCreateFromVolumePath for each mounted volume and get the BSD name (identifier) with DADiskGetBSDName. Convert the C-string to String and you are done.

if let session = DASessionCreate(kCFAllocatorDefault) {
    let mountedVolumeURLs = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: nil)!
    for volumeURL in mountedVolumeURLs {
        if let disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, volumeURL as CFURL),
            let bsdName = DADiskGetBSDName(disk) {
            let bsdString = String(cString : bsdName)
            print(volumeURL.path, bsdString)
        }
    }
}

Another suitable way is to parse IORegistry.

like image 112
vadian Avatar answered Sep 20 '22 22:09

vadian


Another (possibly easier) way would be to call the BSD function statfs(). In plain C, you'd use:

struct statfs fsinfo;
statfs("/", &fsinfo);
printf("root device path: %s\n", fsinfo.f_mntfromname);

That'll print something like "/dev/disk1s3"

Here's an example calling statfs from Swift

import Darwin

extension String {
    /// See https://oleb.net/blog/2017/12/swift-imports-fixed-size-c-arrays-as-tuples/
    init<T>(tupleOfCChars: T, length: Int = Int.max) {
        self = withUnsafePointer(to: tupleOfCChars) { pointer -> String in
            let count = MemoryLayout<T>.size / MemoryLayout<CChar>.size

            return pointer.withMemoryRebound(to: CChar.self, capacity: count) { cCharPointer in
                return String(cString: cCharPointer)
            }
        }
    }
}

var fsInfo = statfs()
statfs("/", &fsInfo)
let fileSystemMountName = String(tupleOfCChars: fsInfo.f_mntfromname)

print(fileSystemMountName) // => /dev/disk1s5
like image 42
Thomas Tempelmann Avatar answered Sep 20 '22 22:09

Thomas Tempelmann