How to distinguish USB hard drives and SSDs from USB keys / pen drives on macOS

362 views Asked by At

How is it possible for code to distinguish between external USB hard drives and solid-state drives on the one hand versus USB sticks on the other hand?

I'm not familiar with macOS APIs (or system calls, interrupts, messaging, and other things) but I'm guessing it would be in I/O Kit or Disk Arbitration?

On the Terminal command line you can use system_profiler SPUSBDataType and see this information listed under "Removable Media".

2

There are 2 answers

3
hippietrail On

I've never written macOS or Swift code before. I learned just enough to get this proof of concept together. It only makes the two-way generalization as in my question: Removable and/or ejectable media vs non-removable non-ejectable media. DMGs are lumped with USB sticks and SD cards. Optical and floppy disks surely are too. I have no idea if there's such thing as a storage type where only one of "removable" and "ejectable" is true but not both...

import Cocoa
import DiskArbitration

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)
            
            if let descDict = DADiskCopyDescription(disk) as? [String: CFTypeRef] {
                let removable : Bool, ejectable : Bool
                if let val = descDict["DAMediaRemovable"] as? Bool {
                    removable = val
                    if let val = descDict["DAMediaEjectable"] as? Bool {
                        ejectable = val

                        var type = ""
                        
                        type += removable || ejectable ? "USB stick, SD card, etc" : "hard drive, SSD, etc";
                        
                        type += " ("
                        
                        type += removable ? "" : "not "
                        type += "removable"
                        type += ", "
                        type += ejectable ? "" : "not "
                        type += "ejectable"
                        
                        type += ")"

                        print(" ", type)
                    }
                }
            }
            print("\n")
        }
    }
}
4
vadian On

You can get the removable/ejectable information directly from the URL, ejectable is sufficient for the differentiation

let mountedVolumeURLs = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: [.nameKey, .volumeIsEjectableKey])!
for volumeURL in mountedVolumeURLs where volumeURL.path == "/" || volumeURL.path.hasPrefix("/Volumes") {
    let resources = try! volumeURL.resourceValues(forKeys: [.nameKey, .volumeIsEjectableKey])
    
    let ejectable = resources.volumeIsEjectable!
    let name = resources.name!
    
    var type = name + " is "
    type += ejectable ? "USB stick, SD card, etc" : "hard drive, SSD, etc";
    type += " ("
    type += ejectable ? "" : "not "
    type += "ejectable)"
    print(" ", type)
}