3
votes

I am trying to get from the USB device BSD Name to the actual mounted volume(s) for that device e.g. device has BSD name "disk2" and mounts a single volume with BSD name "disk2s1" at "/Volumes/USBSTICK".

Here is what I have been doing so far. Using

NSNotificationCenter NSWorkspaceDidMountNotification

I detect when a drive has been added. I then scan through all the USB devices and use

IORegistryEntrySearchCFProperty kIOBSDNameKey

to get the BSD name of the device.

For my USB stick this returns "disk2". Running

system_profiler SPUSBDataTypesystem_profiler SPUSBDataType

shows

      Product ID: 0x5607
      Vendor ID: 0x03f0  (Hewlett Packard)
      Serial Number: AA04012700008687
      Speed: Up to 480 Mb/sec
      Manufacturer: HP
      Location ID: 0x14200000 / 25
      Current Available (mA): 500
      Current Required (mA): 500
      Capacity: 16.04 GB (16,039,018,496 bytes)
      Removable Media: Yes
      Detachable Drive: Yes
      BSD Name: disk2
      Partition Map Type: MBR (Master Boot Record)
      S.M.A.R.T. status: Not Supported
      Volumes:
        USBSTICK:
          Capacity: 16.04 GB (16,037,879,808 bytes)
          Available: 5.22 GB (5,224,095,744 bytes)
          Writable: Yes
          File System: MS-DOS FAT32
          BSD Name: disk2s1
          Mount Point: /Volumes/USBSTICK
          Content: Windows_FAT_32

which makes sense since there could be multiple volumes for a single USB device.

I assumed I could use DiskArbitration to find the actual volumes, but

DASessionRef session = DASessionCreate(NULL);
if (session)
{
    DADiskRef disk = DADiskCreateFromBSDName(NULL,session,"disk2");
    if (disk)
    {
        CFDictionaryRef dict = DADiskCopyDescription(disk);
        if (dict)

always returns a NULL dictionary.

So, how do I get from the BSD name for a USB device to the actual mounted volume(s) for that device? I guess it should be possible to get iterate over all the volumes, get their BSD name and check if it starts with the string e.g. /Volumes/USBSTICK above is "disk2s1", but that's hacky and what if there is a disk20 etc.

1

1 Answers

3
votes

Found a solution using IOBSDNameMatching will create a dictionary to match the service with a given BSD name. Then the children of that service can be searched for their BSD names. NOTE: This is my first time doing anything on OSX. Also, the 'dict' in the above code was NULL because of bug, but that dictionary is of no use for this anyway.

Here's some cut down code with no error checking etc.

CFMutableDictionaryRef  matchingDict;

matchingDict = IOBSDNameMatching(kIOMasterPortDefault, 0, "disk2");
io_iterator_t itr;
// Might only ever be one service so, MatchingService could be used. No sure though
IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &itr);
io_object_t service;
while ((service = IOIteratorNext(itr)))
{
    io_iterator_t children;
    io_registry_entry_t child;

    // Obtain the service's children.
    IORegistryEntryGetChildIterator(service, kIOServicePlane, &children);
    while ((child = IOIteratorNext(children)))
    {
        CFTypeRef name = IORegistryEntrySearchCFProperty(child,
                                                      kIOServicePlane,
                                                      CFSTR(kIOBSDNameKey),
                                                      kCFAllocatorDefault,
                                                      kIORegistryIterateRecursively);
        if (name)
        {
            // Got child BSD Name e.g. "disk2s1"
        }
    }
}