/**
* 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];
}
- (HBDVDDetector *)initWithPath: (NSString *)aPath
{
NSAssert(aPath, @"nil string passed to drive detector.");
if( self = [super init] )
{
path = aPath;
bsdName = nil;
}
return self;
}
- (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;
}
struct statfs s;
statfs([path fileSystemRepresentation], &s);
bsdName = @(s.f_mntfromname);
if ([bsdName hasPrefix:@"/dev/"])
{
bsdName = [bsdName substringFromIndex:5];
}
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