/** * HBDriveDetector.m * 8/17/2007 * * This file is part of the HandBrake source code. * Homepage: . * It may be used under the terms of the GNU General Public License. */ #include #include #include #include #import "HBDVDDetector.h" @interface HBDVDDetector (Private) - (NSString *)bsdName; - (BOOL)pathHasVideoTS; - (BOOL)deviceIsDVD; - (io_service_t)getIOKitServiceForBSDName; - (BOOL)isDVDService: (io_service_t)service; - (BOOL)isWholeMediaService: (io_service_t)service; @end @implementation HBDVDDetector + (HBDVDDetector *)detectorForPath: (NSString *)aPath { return [[[self alloc] initWithPath:aPath] autorelease]; } - (HBDVDDetector *)initWithPath: (NSString *)aPath { NSAssert(aPath, @"nil string passed to drive detector."); if( self = [super init] ) { path = [aPath retain]; bsdName = nil; } return self; } - (void)dealloc { [path release]; path = nil; [bsdName release]; bsdName = nil; [super dealloc]; } - (BOOL)isVideoDVD { return ( [self pathHasVideoTS] && [self deviceIsDVD] ); } - (NSString *)devicePath { return [NSString stringWithFormat:@"/dev/%@", [self bsdName]]; } @end @implementation HBDVDDetector (Private) - (NSString *)bsdName { if ( bsdName ) { return bsdName; } NSURL *volumeURL = [NSURL fileURLWithPath:path]; // Create a DADiskRef DASessionRef session = DASessionCreate(kCFAllocatorDefault); DADiskRef disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, (__bridge CFURLRef)volumeURL); if ( disk ) { CFDictionaryRef desc = DADiskCopyDescription(disk); if ( desc ) { // Get the bsd name from it bsdName = [(NSString *)CFDictionaryGetValue(desc, kDADiskDescriptionMediaBSDNameKey) retain]; CFRelease(desc); } CFRelease(disk); } CFRelease(session); return bsdName; } - (BOOL)pathHasVideoTS { // Check one level under the path if( [[NSFileManager defaultManager] fileExistsAtPath: [path stringByAppendingPathComponent:@"VIDEO_TS"]] ) { return YES; } // Now check above the path return [[path pathComponents] containsObject:@"VIDEO_TS"]; } - (BOOL)deviceIsDVD { io_service_t service = [self getIOKitServiceForBSDName]; if( service == IO_OBJECT_NULL ) { return NO; } BOOL result = [self isDVDService:service]; IOObjectRelease(service); return result; } - (io_service_t)getIOKitServiceForBSDName { CFMutableDictionaryRef matchingDict; matchingDict = IOBSDNameMatching( kIOMasterPortDefault, 0, [[self bsdName] UTF8String] ); if( matchingDict == NULL ) { return IO_OBJECT_NULL; } // Fetch the object with the matching BSD node name. There should only be // one match, so IOServiceGetMatchingService is used instead of // IOServiceGetMatchingServices to simplify the code. return IOServiceGetMatchingService( kIOMasterPortDefault, matchingDict ); } - (BOOL)isDVDService: (io_service_t)service { // Find the IOMedia object that represents the entire (whole) media that the // volume is on. // // If the volume is on partitioned media, the whole media object will be a // parent of the volume's media object. If the media is not partitioned, the // volume's media object will be the whole media object. // // The whole media object is indicated in the IORegistry by the presence of // a property with the key "Whole" and value "Yes". // Create an iterator across all parents of the service object passed in. kern_return_t kernResult; io_iterator_t iter; kernResult = IORegistryEntryCreateIterator( service, kIOServicePlane, kIORegistryIterateRecursively | kIORegistryIterateParents, &iter ); if( kernResult != KERN_SUCCESS ) { return NO; } if( iter == IO_OBJECT_NULL ) { return NO; } // A reference on the initial service object is released in the do-while loop below, // so add a reference to balance. IOObjectRetain( service ); BOOL isDVD = NO; do { isDVD = ( [self isWholeMediaService:service] && IOObjectConformsTo(service, kIODVDMediaClass) ); IOObjectRelease(service); } while( !isDVD && (service = IOIteratorNext(iter)) ); IOObjectRelease( iter ); return isDVD; } - (BOOL)isWholeMediaService: (io_service_t)service { // // Determine if the object passed in represents an IOMedia (or subclass) object. // If it does, test the "Whole" property. // Boolean isWholeMedia = NO; if( IOObjectConformsTo(service, kIOMediaClass) ) { CFTypeRef wholeMedia; wholeMedia = IORegistryEntryCreateCFProperty( service, CFSTR(kIOMediaWholeKey), kCFAllocatorDefault, 0); if( !wholeMedia ) { return NO; } isWholeMedia = CFBooleanGetValue( (CFBooleanRef)wholeMedia ); CFRelease(wholeMedia); } return isWholeMedia; } @end