Search code examples
objective-cmacoscocoaosx-lion

Get a list of unmountable drives using Cocoa


I would like to obtain a list of drives that are unmountable/ejectable using Cocoa/Objective-C under OS X.

I was hoping that NSWorkspace getFileSystemInfoForPath::::: would help me:

NSArray* listOfMedia = [[NSWorkspace sharedWorkspace] mountedLocalVolumePaths];
NSLog(@"%@", listOfMedia);

for (NSString* volumePath in listOfMedia)
{
    BOOL isRemovable = NO;
    BOOL isWritable  = NO;
    BOOL isUnmountable = NO;
    NSString* description = [NSString string];
    NSString* type = [NSString string];

    BOOL result = [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath:volumePath 
                                                             isRemovable:&isRemovable 
                                                              isWritable:&isWritable 
                                                              isUnmountable:&isUnmountable 
                                                                description:&description
                                                                       type:&type];
    NSLog(@"Result:%i Volume: %@, Removable:%i, W:%i, Unmountable:%i, Desc:%@, type:%@", result, volumePath, isRemovable, isWritable, isUnmountable, description, type);
}

Output:

...
Result:1 Volume: /Volumes/LR Photos, Removable:0, W:1, Unmountable:0, Desc:hfs, type:hfs
...

"LR Photos" is an external drive (connected via Thunderbolt) that should be removable and/or unmountable (or, at least I think it should be). :)

Should I be going about this a different way?

Thanks in advance!


Solution

  • You can use diskArbitration framework.

    #import <DiskArbitration/DiskArbitration.h>
       +(NSMutableArray *)getListOfEjectableMedia
    {
        NSArray *mountedRemovableMedia = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:nil options:NSVolumeEnumerationSkipHiddenVolumes];
        NSMutableArray *result = [NSMutableArray array];
        for(NSURL *volURL in mountedRemovableMedia)
        {
            int                 err = 0;
            DADiskRef           disk;
            DASessionRef        session;
            CFDictionaryRef     descDict;
            session = DASessionCreate(NULL);
            if (session == NULL) {
                err = EINVAL;
            }
            if (err == 0) {
                disk = DADiskCreateFromVolumePath(NULL,session,(CFURLRef)volURL);
                if (session == NULL) {
                    err = EINVAL;
                }
            }
            if (err == 0) {
                descDict = DADiskCopyDescription(disk);
                if (descDict == NULL) {
                    err = EINVAL;
                }
            }
            if (err == 0) {
                CFTypeRef mediaEjectableKey = CFDictionaryGetValue(descDict,kDADiskDescriptionMediaEjectableKey);
                CFTypeRef deviceProtocolName = CFDictionaryGetValue(descDict,kDADiskDescriptionDeviceProtocolKey);
                if (mediaEjectableKey != NULL)
                {
                    BOOL op = CFEqual(mediaEjectableKey, CFSTR("0")) || CFEqual(deviceProtocolName, CFSTR("USB"));
                    if (op) {
                        [result addObject:volURL];
                    }
                }
            }
            if (descDict != NULL) {
                CFRelease(descDict);
            }
            if (disk != NULL) {
                CFRelease(disk);
            }
            if (session != NULL) {
                CFRelease(session);
            }
        }
        return result;
    }
    

    Update:

    + (NSMutableArray *)getListOfEjectableMedia {
        NSArray *mountedRemovableMedia = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:nil options:NSVolumeEnumerationSkipHiddenVolumes];
        NSMutableArray *result = [NSMutableArray array];
        DASessionRef session = DASessionCreate(NULL); // Create the DASessionRef object outside the loop.
        for (NSURL *volURL in mountedRemovableMedia) {
            DADiskRef disk = DADiskCreateFromVolumePath(NULL, session, (__bridge CFURLRef)volURL); // Use the same session object for each disk.
            if (disk != NULL) {
                CFDictionaryRef descDict = DADiskCopyDescription(disk);
                if (descDict != NULL) {
                    CFTypeRef mediaEjectableKey = CFDictionaryGetValue(descDict,kDADiskDescriptionMediaEjectableKey);
                    CFTypeRef deviceProtocolName = CFDictionaryGetValue(descDict,kDADiskDescriptionDeviceProtocolKey);
                    if (mediaEjectableKey != NULL) {
                        BOOL op = CFEqual(mediaEjectableKey, CFSTR("0")) || CFEqual(deviceProtocolName, CFSTR("USB"));
                        if (op) {
                            [result addObject:volURL];
                        }
                    }
                    CFRelease(descDict);
                }
                CFRelease(disk);
            }
        }
        CFRelease(session); // Release the DASessionRef object after the loop.
        return result;
    }