Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 565
RobertJoseph Avatar asked Jul 24 '12 14:07

RobertJoseph


2 Answers

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;
}
like image 79
Parag Bafna Avatar answered Oct 20 '22 00:10

Parag Bafna


Unfortunately getFileSystemInfoForPath: is not really the right way to do this. What removable means is that the volume is on removable media such as a CD or DVD. In practice unmountable seems to give the same results as removable. See for example, this post on results using getFileSystemInfoForPath. So unless you want to simply know if a volume is on removable media, you'll need to use another technique.

What you really want to check is the connection bus type of the volume. Firewire, USB, Thunderbolt, etc. are unmountable in the sense you mean. You can see this information in Disk Utility if you select the volume and push the "Info" button under "Connection Bus". Getting this information programmatically is much harder and as far as I can tell is only possible using the IOKit. Details are in Apple's documentation on Accessing Hardware from Applications.

like image 45
torrey.lyons Avatar answered Oct 19 '22 23:10

torrey.lyons