/* * Copyright (c) 2000-2008 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ // cddafs_util.c created by CJS on Mon 10-Apr-2000 //----------------------------------------------------------------------------- // Includes //----------------------------------------------------------------------------- // System Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Libkern includes #include // CoreFoundation Includes #include #include // IOKit Includes #include #include #include // Project includes #include "cddafs_util.h" #include "AppleCDDAFileSystemDefines.h" #include "CDDATrackName.h" //----------------------------------------------------------------------------- // Macros //----------------------------------------------------------------------------- #define DEBUG 0 #define DEBUG_LEVEL 0 #ifndef DEBUG_ASSERT_COMPONENT_NAME_STRING #define DEBUG_ASSERT_COMPONENT_NAME_STRING "cddafs.util" #endif #include #if (DEBUG_LEVEL > 3) #define DebugLog(x) printf x #else #define DebugLog(x) #endif static int UtilityMain ( int argc, const char * argv[] ); static int MountMain ( int argc, const char * argv[] ); static CFDataRef GetTrackData ( const char * bsdDevNode, const QTOCDataFormat10Ptr TOCData ); //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- struct mntopt gMountOptions[] = { MOPT_STDOPTS, { NULL } }; static char gAppleCDDAName[MFSNAMELEN] = "cddafs"; static char gFileSuffix[] = ".aiff"; #define kMaxPrefixSize 3 #define kASCIINumberZero 0x30 #define kASCIISpace 0x20 //----------------------------------------------------------------------------- // main - This our main entry point to this utility. We get called by // autodiskmount. //----------------------------------------------------------------------------- int main ( int argc, const char * argv[] ) { int result = -1; char * executableName = NULL; #if DEBUG int index = 0; for ( index = 0; index < argc; index++ ) { printf ( "[%d] = %s\n", index, argv[index] ); } #endif executableName = basename ( ( char * ) argv[0] ); if ( executableName == NULL ) exit ( 1 ); if ( strcmp ( executableName, kUtilExecutableName ) == 0 ) { result = UtilityMain ( argc, argv ); } else { result = MountMain ( argc, argv ); } return result; } #if 0 #pragma mark - #pragma mark - Utility Code #pragma mark - #endif //----------------------------------------------------------------------------- // UtilityMain - Returns FSUR_IO_SUCCESS if everything works, else it // returns one of the FSUR_XXX errors in loadable_fs.h //----------------------------------------------------------------------------- static int UtilityMain ( int argc, const char * argv[] ) { char rawDeviceName[MAXPATHLEN]; char blockDeviceName[MAXPATHLEN]; const char * actionPtr = NULL; const char * mountPointPtr = NULL; int result = FSUR_IO_SUCCESS; boolean_t isLocked = 0; boolean_t isEjectable = 0; int mountFlags = MNT_RDONLY; // Verify our arguments result = ParseUtilityArgs ( argc, argv, &actionPtr, &mountPointPtr, &isEjectable, &isLocked ); require ( ( result == 0 ), Exit ); // Build our device name (full path), should end up with something like: // -- "/dev/disk1" or "/dev/disk2" or "/dev/disk3" snprintf ( rawDeviceName, MAXPATHLEN, "/dev/r%s", argv[2] ); snprintf ( blockDeviceName, MAXPATHLEN, "/dev/%s", argv[2] ); // call the appropriate routine to handle the given action argument after becoming root result = seteuid ( 0 ); require_action ( ( result == 0 ), Exit, result = FSUR_INVAL ); result = setegid ( 0 ); if ( result ) { DebugLog ( ( "cddafs.util: ERROR: setegid: %s\n", strerror ( errno ) ) ); } DebugLog ( ( "Entering the switch with action = %s\n", actionPtr ) ); switch ( *actionPtr ) { case FSUC_PROBE: result = Probe ( rawDeviceName ); break; case FSUC_MOUNT: case FSUC_MOUNT_FORCE: result = Mount ( blockDeviceName, mountPointPtr, mountFlags ); break; case FSUC_UNMOUNT: result = Unmount ( mountPointPtr ); break; default: // should never get here since DoVerifyArgs should handle this situation DisplayUsage ( kUsageTypeUtility, argv ); result = FSUR_INVAL; break; } Exit: DebugLog ( ( "cddafs.util: EXIT: %d = ", result ) ); switch ( result ) { case FSUR_LOADERR: DebugLog ( ( "FSUR_LOADERR\n" ) ); break; case FSUR_INVAL: DebugLog ( ( "FSUR_INVAL\n" ) ); break; case FSUR_IO_SUCCESS: DebugLog ( ( "FSUR_IO_SUCCESS\n" ) ); break; case FSUR_IO_FAIL: DebugLog ( ( "FSUR_IO_FAIL\n" ) ); break; case FSUR_RECOGNIZED: DebugLog ( ( "FSUR_RECOGNIZED\n" ) ); break; case FSUR_MOUNT_HIDDEN: DebugLog ( ( "FSUR_MOUNT_HIDDEN\n" ) ); break; case FSUR_UNRECOGNIZED: DebugLog ( ( "FSUR_UNRECOGNIZED\n" ) ); break; default: DebugLog ( ( "default\n" ) ); break; } check ( result == FSUR_IO_SUCCESS ); exit ( result ); return result; // ...and make main fit the ANSI spec. } //----------------------------------------------------------------------------- // ParseUtilityArgs - This routine will make sure the arguments passed // in to us are copacetic. Here is how this utility is used: // // usage: cddafs.util actionArg deviceArg [mountPointArg] [flagsArg] // actionArg: // -p (Probe for mounting) // -P (Probe for initializing - not supported) // -m (Mount) // -r (Repair - not supported) // -u (Unmount) // -M (Force Mount) // -i (Initialize - not supported) // // deviceArg: // sd2 (for example) // // mountPointArg: // /foo/bar/ (required for Mount and Force Mount actions) // // flagsArg: // (these are ignored for CDROMs) // // examples: // cddafs.util -p sd2 removable writable // cddafs.util -p sd2 removable readonly // cddafs.util -m sd2 /my/cddafs // // Returns FSUR_INVAL if we find a bad argument, else 0. //----------------------------------------------------------------------------- int ParseUtilityArgs ( int argc, const char * argv[], const char ** actionPtr, const char ** mountPointPtr, boolean_t * isEjectablePtr, boolean_t * isLockedPtr ) { int result = FSUR_INVAL; int deviceLength = 0; int index = 0; // Must have at least 3 arguments and the action argument must start with a '-' require_action ( ( argc >= 3 ), Exit, DisplayUsage ( kUsageTypeUtility, argv ) ); require_action ( ( argv[1][0] == '-' ), Exit, DisplayUsage ( kUsageTypeUtility, argv ) ); // we only support actions Probe, Mount, Force Mount, and Unmount *actionPtr = &argv[1][1]; switch ( argv[1][1] ) { case FSUC_PROBE: // action Probe and requires 5 arguments (need the flags) require_action ( ( argc >= 5 ), Exit, DisplayUsage ( kUsageTypeUtility, argv ) ); index = 3; break; case FSUC_UNMOUNT: *mountPointPtr = argv[3]; index = 0; // No isEjectable/isLocked flags for unmount. break; case FSUC_MOUNT: case FSUC_MOUNT_FORCE: // action Mount and ForceMount require 6 arguments // ( need the mountpoint and the flags ) require_action ( ( argc >= 6 ), Exit, DisplayUsage ( kUsageTypeUtility, argv ) ); *mountPointPtr = argv[3]; index = 4; break; default: DisplayUsage ( kUsageTypeUtility, argv ); goto Exit; break; } // Make sure device (argv[2]) is something reasonable // (we expect something like "disk1") deviceLength = ( int ) strlen ( argv[2] ); require ( ( deviceLength >= 5 ), Exit ); result = 0; // If index is zero, no more work to do... require ( ( index != 0 ), Exit ); // Flags: removable/fixed if ( !strcmp ( argv[index], "removable" ) ) { *isEjectablePtr = 1; } else if ( !strcmp ( argv[index], "fixed" ) ) { *isEjectablePtr = 0; } else { DebugLog ( ( "cddafs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n", index, argv[index] ) ); } // Flags: readonly/writable if ( !strcmp ( argv[index + 1], "readonly" ) ) { *isLockedPtr = 1; } else if ( !strcmp ( argv[index + 1], "writable" ) ) { *isLockedPtr = 0; } else { DebugLog ( ( "cddafs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n", index, argv[index + 1] ) ); } Exit: return result; } //----------------------------------------------------------------------------- // Probe - This routine will open the given raw device and check to // make sure there is media that looks like an Audio CD. Returns // FSUR_MOUNT_HIDDEN if everything works, else FSUR_IO_FAIL. // // deviceNamePtr - pointer to the raw device name (full path, like /dev/rdisk1) //----------------------------------------------------------------------------- int Probe ( char * deviceNamePtr ) { int result = FSUR_UNRECOGNIZED; UInt8 * ptr = NULL; DebugLog ( ( "ENTER: Probe('%s')\n", deviceNamePtr ) ); ptr = GetTOCDataPtr ( deviceNamePtr ); if ( ptr != NULL ) { // Parse the TOC for Audio Tracks result = ParseTOC ( ptr ); } else { DebugLog ( ( "GetTOCDataPtr returned NULL.\n" ) ); } // if we recognized the disc, create the name and suffix files if ( result == FSUR_RECOGNIZED ) { CFStringRef albumName = 0; CDDATrackName * database = NULL; database = new CDDATrackName; if ( database != NULL ) { DebugLog ( ( "database != NULL\n" ) ); database->Init ( deviceNamePtr, ptr ); DebugLog ( ( "Init called\n" ) ); albumName = database->GetAlbumName ( ); DebugLog ( ( "GetAlbumName called\n" ) ); } if ( albumName != 0 ) { Boolean success = false; char buffer[MAXNAMLEN]; #if DEBUG CFShow ( albumName ); #endif success = _CFStringGetFileSystemRepresentation ( albumName, ( UInt8 * ) buffer, MAXNAMLEN ); if ( success == true ) { WriteDiskLabel ( buffer ); } else { // Good old "Audio CD" should work... WriteDiskLabel ( ( char * ) kMountPointName ); } // release it CFRelease ( albumName ); albumName = 0; } else { // Good old "Audio CD" should work... WriteDiskLabel ( ( char * ) kMountPointName ); } if ( database != NULL ) { delete database; database = NULL; } } if ( ptr != NULL ) { // free the memory free ( ptr ); ptr = NULL; } DebugLog ( ( "Probe: returns " ) ); switch ( result ) { case FSUR_IO_FAIL: DebugLog ( ( "FSUR_IO_FAIL\n" ) ); break; case FSUR_RECOGNIZED: DebugLog ( ( "FSUR_RECOGNIZED\n" ) ); break; case FSUR_MOUNT_HIDDEN: DebugLog ( ( "FSUR_MOUNT_HIDDEN\n" ) ); break; case FSUR_UNRECOGNIZED: DebugLog ( ( "FSUR_UNRECOGNIZED\n" ) ); break; default: DebugLog ( ( "default\n" ) ); break; } return result; } //----------------------------------------------------------------------------- // Unmount - This routine will fire off a system command to unmount the // given device. Returns FSUR_IO_SUCCESS if everything works, // else FSUR_IO_FAIL. // // theDeviceNamePtr - pointer to the device name (full path, like /dev/disk1s2). //----------------------------------------------------------------------------- int Unmount ( const char * theMountPointPtr ) { int result; int mountflags = 0; result = unmount ( theMountPointPtr, mountflags ); require_action ( ( result == 0 ), Exit, result = FSUR_IO_FAIL ); result = FSUR_IO_SUCCESS; Exit: return result; } #if 0 #pragma mark - #pragma mark - Mount Code #pragma mark - #endif //----------------------------------------------------------------------------- // MountMain - returns 0 if successful //----------------------------------------------------------------------------- static int MountMain ( int argc, const char * argv[] ) { int error = 0; int mountFlags = 0; error = ParseMountArgs ( &argc, &argv, &mountFlags ); require_action ( ( error == 0 ), Exit, DisplayUsage ( kUsageTypeMount, argv ) ); error = Mount ( argv[0], argv[1], mountFlags ); check ( error == FSUR_IO_SUCCESS ); // mount_cddafs must return 0 for successful mounts if ( error == FSUR_IO_SUCCESS ) error = 0; Exit: return error; } //----------------------------------------------------------------------------- // ParseMountArgs - Parses mount arguments //----------------------------------------------------------------------------- int ParseMountArgs ( int * argc, const char ** argv[], int * mountFlags ) { int error = 0; int ch = 0; int altFlags = 0; mntoptparse_t parse = NULL; *mountFlags = 0; // Audio CD's are read-only *mountFlags |= MNT_RDONLY; // Must have at least 3 arguments and the action argument must start with a '-' require_action ( ( *argc > 2 ), Exit, error = 1 ); // Check command line args while ( ( ch = getopt ( *argc, ( char * const * ) *argv, "o:" ) ) != -1 ) { switch ( ch ) { case 'o': parse = getmntopts ( optarg, gMountOptions, mountFlags, &altFlags ); if ( parse != NULL ) { freemntopts ( parse ); } else { error = 1; } break; default: error = 1; break; } } *argc -= optind; *argv += optind; Exit: return error; } //----------------------------------------------------------------------------- // WriteDiskLabel - This routine will create a file system info file that // is used by autodiskmount. After creating the file it will // write whatever contentsPtr points to the new file. // // We end up with a file something like: // /usr/filesystems/cddafs.fs/cddafs.name // // when our file system name is "cddafs" and suffixPtr points // to ".name" or ".label" //----------------------------------------------------------------------------- void WriteDiskLabel ( char * contentsPtr ) { StripTrailingSpaces ( contentsPtr ); write ( STDOUT_FILENO, contentsPtr, strlen ( contentsPtr ) ); } //----------------------------------------------------------------------------- // StripTrailingSpaces - Strips trailing white spaces from character array //----------------------------------------------------------------------------- void StripTrailingSpaces ( char * theContentsPtr ) { check ( theContentsPtr ); check ( strlen ( theContentsPtr ) > 0 ); if ( strlen ( theContentsPtr ) > 0 ) { char *myPtr; myPtr = theContentsPtr + strlen ( theContentsPtr ) - 1; while ( *myPtr == kASCIISpace && myPtr >= theContentsPtr ) { *myPtr = 0x00; myPtr--; } } } //----------------------------------------------------------------------------- // Mount - Attempts to mount on our filesystem if possible //----------------------------------------------------------------------------- int Mount ( const char * deviceNamePtr, const char * mountPointPtr, int mountFlags ) { AppleCDDAArguments args = { 0 }; struct vfsconf vfc = { 0 }; int result = FSUR_IO_FAIL; int error = 0; CFDataRef nameDataRef = NULL; CFDataRef xmlDataRef = NULL; QTOCDataFormat10Ptr TOCDataPtr = NULL; UInt8 * xmlDataPtr = NULL; UInt8 * nameDataPtr = NULL; char realMountPoint[PATH_MAX]; char * realMountPointPtr; DebugLog ( ( "Mount('%s','%s')\n", deviceNamePtr, mountPointPtr ) ); require ( ( mountPointPtr != NULL ), Exit ); require ( ( *mountPointPtr != '\0' ), Exit ); args.device = ( char * ) deviceNamePtr; args.fileType = 0x41494643; // 'AIFC' args.fileCreator = 0x3F3F3F3F; // '????' // Check if we're loaded into vfs or not. error = getvfsbyname ( gAppleCDDAName, &vfc ); if ( error != 0 ) { // Kernel extension wasn't loaded, so try to load it... error = LoadKernelExtension ( ); require ( ( error == 0 ), Exit ); // Now try again since we loaded our extension error = getvfsbyname ( gAppleCDDAName, &vfc ); require ( ( error == 0 ), Exit ); } TOCDataPtr = ( QTOCDataFormat10Ptr ) GetTOCDataPtr ( deviceNamePtr ); require ( ( TOCDataPtr != NULL ), Exit ); nameDataRef = GetTrackData ( deviceNamePtr, TOCDataPtr ); require ( ( nameDataRef != NULL ), ReleaseTOCData ); // Get the number of audio tracks args.numTracks = FindNumberOfAudioTracks ( TOCDataPtr ); // Build the XML file ".TOC.plist" xmlDataRef = CreateXMLFileInPListFormat ( TOCDataPtr ); require ( ( xmlDataRef != NULL ), ReleaseNameData ); // Get the pointers. nameDataPtr = ( UInt8 * ) CFDataGetBytePtr ( nameDataRef ); xmlDataPtr = ( UInt8 * ) CFDataGetBytePtr ( xmlDataRef ); args.nameData = ( user_addr_t ) nameDataPtr; args.nameDataSize = ( uint32_t ) CFDataGetLength ( nameDataRef ); args.xmlData = ( user_addr_t ) xmlDataPtr; args.xmlFileSize = ( uint32_t ) CFDataGetLength ( xmlDataRef ); #if ( DEBUG_LEVEL > 3 ) { UInt32 count = 0; for ( ; count < args.xmlFileSize; count = count + 8 ) { DebugLog ( ("%02x:%02x:%02x:%02x %02x:%02x:%02x:%02x\n", xmlDataPtr[count], xmlDataPtr[count+1], xmlDataPtr[count+2], xmlDataPtr[count+3], xmlDataPtr[count+4], xmlDataPtr[count+5], xmlDataPtr[count+6], xmlDataPtr[count+7] ) ); } DebugLog ( ( "\n" ) ); DebugLog ( ( "XML File Size = %d\n", ( int ) args.xmlFileSize ) ); } #endif // Print out the device name for debug purposes DebugLog ( ( "DeviceName = %s\n", deviceNamePtr ) ); DebugLog ( ( "numTracks = %d\n", ( int ) args.numTracks ) ); require ( ( args.nameData != 0 ), ReleaseXMLData ); require ( ( args.nameDataSize != 0 ), ReleaseXMLData ); require ( ( args.xmlData != 0 ), ReleaseXMLData ); DebugLog ( ( "args.nameData = %qx\n", args.nameData ) ); DebugLog ( ( "args.xmlData = %qx\n", args.xmlData ) ); DebugLog ( ( "sizeof(args) = %ld\n", sizeof ( args ) ) ); // Obtain the real path. realMountPointPtr = realpath ( mountPointPtr, realMountPoint ); require ( ( realMountPointPtr != NULL ), ReleaseXMLData ); // Issue the system mount command result = mount ( vfc.vfc_name, realMountPoint, mountFlags, &args ); require ( ( result == 0 ), ReleaseXMLData ); result = FSUR_IO_SUCCESS; ReleaseXMLData: CFRelease ( xmlDataRef ); xmlDataRef = NULL; ReleaseNameData: CFRelease ( nameDataRef ); nameDataRef = NULL; ReleaseTOCData: // free the memory require_quiet ( ( TOCDataPtr != NULL ), Exit ); free ( ( char * ) TOCDataPtr ); TOCDataPtr = NULL; Exit: return result; } //----------------------------------------------------------------------------- // ParseTOC - Parses the TOC to find audio tracks. If it finds one or more // audio tracks, it returns FSUR_RECOGNIZED, // else FSUR_UNRECOGNIZED // //----------------------------------------------------------------------------- int ParseTOC ( UInt8 * TOCInfoPtr ) { int result = FSUR_UNRECOGNIZED; int error = 0; QTOCDataFormat10Ptr TOCDataPtr = NULL; UInt8 index = 0; UInt8 numberOfDescriptors = 0; require ( ( TOCInfoPtr != NULL ), Exit ); // Set our pointer to the TOCInfoPtr TOCDataPtr = ( QTOCDataFormat10Ptr ) TOCInfoPtr; error = GetNumberOfTrackDescriptors ( TOCDataPtr, &numberOfDescriptors ); require_quiet ( ( error == 0 ), Exit ); for ( index = 0; index < numberOfDescriptors; index++ ) { if ( IsAudioTrack ( index, TOCDataPtr ) ) { // Found an audio cd return FSUR_RECOGNIZED; } } Exit: return result; } //----------------------------------------------------------------------------- // GetTrackData - Loads databases and calls them for Track Name info. //----------------------------------------------------------------------------- CFDataRef GetTrackData ( const char * bsdDevNode, const QTOCDataFormat10Ptr TOCData ) { CFMutableDataRef data = 0; CFStringRef trackString = 0; UInt8 numTracks = 0; UInt32 trackIndex = 0; UInt8 currentTrack = 0; SInt32 error = 0; CDDATrackName * database = NULL; data = CFDataCreateMutable ( kCFAllocatorDefault, 0 ); database = new CDDATrackName; database->Init ( bsdDevNode, TOCData ); error = GetNumberOfTrackDescriptors ( ( QTOCDataFormat10Ptr ) TOCData, &numTracks ); if ( error != 0 ) { DebugLog ( ( "Error = %d on GetNumberOfTrackDescriptors\n", ( int ) error ) ); exit ( 1 ); } for ( trackIndex = 0; trackIndex < numTracks; trackIndex++ ) { if ( IsAudioTrack ( trackIndex, TOCData ) == false ) continue; currentTrack = GetPointValue ( trackIndex, TOCData ); trackString = ( database )->GetTrackName ( currentTrack ); if ( trackString != 0 ) { UInt8 size = 0; UInt8 suffixSize = 0; UInt8 prefixSize = 2; UInt8 bufferSize = 0; UInt8 offset = 0; Boolean success = false; char * buffer = NULL; char prefix[kMaxPrefixSize]; if ( currentTrack > 9 ) { prefixSize++; prefix[offset++] = currentTrack / 10 + kASCIINumberZero; } prefix[offset++] = currentTrack % 10 + kASCIINumberZero; prefix[offset] = kASCIISpace; suffixSize = strlen ( gFileSuffix ); bufferSize = MAXNAMLEN - prefixSize - suffixSize - 1; buffer = ( char * ) calloc ( 1, bufferSize ); success = _CFStringGetFileSystemRepresentation ( trackString, ( UInt8 * ) buffer, bufferSize ); buffer[bufferSize - 1] = 0; size = strlen ( buffer ) + prefixSize + suffixSize; DebugLog ( ( "size = %d\n", size ) ); DebugLog ( ( "buffer = %s\n", buffer ) ); // Add the track number to the data object CFDataAppendBytes ( data, ¤tTrack, 1 ); // Add the size to the data object CFDataAppendBytes ( data, &size, 1 ); // Add the prefix to the data object CFDataAppendBytes ( data, ( UInt8 * ) prefix, prefixSize ); // add the string to the data object CFDataAppendBytes ( data, ( UInt8 * ) buffer, strlen ( buffer ) ); // add the suffix to the data object CFDataAppendBytes ( data, ( UInt8 * ) gFileSuffix, suffixSize ); free ( buffer ); // release it CFRelease ( trackString ); } } if ( database != NULL ) { delete database; database = NULL; } return data; } //----------------------------------------------------------------------------- // LoadKernelExtension - Loads our filesystem kernel extension. //----------------------------------------------------------------------------- int LoadKernelExtension ( void ) { int pid; int result = -1; union wait status; pid = fork ( ); if ( pid == 0 ) { result = execl ( kLoadCommand, kLoadCommand, kCDDAFileSystemExtensionPath, NULL ); // We can only get here if the exec failed goto Exit; } if ( pid == -1 ) { // fork() didn't work, so we grab the error from errno // to return as our error result = errno; goto Exit; } // Success! if ( ( wait4 ( pid, ( int * ) &status, 0, NULL ) == pid ) && ( WIFEXITED ( status ) ) ) { // Stuff the status return code into our result result = status.w_retcode; } else { // else return -1 result = -1; } check ( result == 0 ); Exit: return result; } //----------------------------------------------------------------------------- // CreateXMLFileInPListFormat - Makes a plist-style XML file which has a // parsed TOC. This makes it easy for // applications to get TOC info without having // to deal with the IOKit registry. // // TOCDataPtr - pointer to a QTOCDataFormat10 structure //----------------------------------------------------------------------------- CFDataRef CreateXMLFileInPListFormat ( QTOCDataFormat10Ptr TOCDataPtr ) { SubQTOCInfoPtr trackDescriptorPtr = NULL; SubQTOCInfoPtr lastTrackDescriptorPtr = NULL; UInt16 numSessions = 0; UInt16 length = 0; UInt16 numberOfDescriptors = 0; CFMutableDictionaryRef theCDDictionaryRef = 0; CFMutableArrayRef theSessionArrayRef = 0; CFDataRef theRawTOCDataRef = 0; UInt32 index = 0; CFDataRef xmlData = 0; DebugLog ( ( "CreateXMLFileInPListFormat called\n" ) ); if ( TOCDataPtr != NULL ) { // Create the master dictionary inside which all elements go theCDDictionaryRef = CFDictionaryCreateMutable ( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); // Grab the length and advance length = OSSwapBigToHostInt16 ( TOCDataPtr->TOCDataLength ); // Add the Raw TOC Data theRawTOCDataRef = CFDataCreate ( kCFAllocatorDefault, ( UInt8 * ) TOCDataPtr, length + sizeof ( TOCDataPtr->TOCDataLength ) ); CFDictionarySetValue ( theCDDictionaryRef, CFSTR ( kRawTOCDataString ), theRawTOCDataRef ); CFRelease ( theRawTOCDataRef ); length -= ( sizeof ( TOCDataPtr->firstSessionNumber ) + sizeof ( TOCDataPtr->lastSessionNumber ) ); numberOfDescriptors = length / ( sizeof ( SubQTOCInfo ) ); numSessions = TOCDataPtr->lastSessionNumber - TOCDataPtr->firstSessionNumber + 1; if ( numberOfDescriptors <= 0 ) { DebugLog ( ( "No tracks on this CD...\n" ) ); } // Create the array of sessions theSessionArrayRef = CFArrayCreateMutable ( kCFAllocatorDefault, numSessions, &kCFTypeArrayCallBacks ); trackDescriptorPtr = TOCDataPtr->trackDescriptors; lastTrackDescriptorPtr = TOCDataPtr->trackDescriptors + numberOfDescriptors - 1; for ( index = 0; trackDescriptorPtr <= lastTrackDescriptorPtr; index++ ) { CFMutableDictionaryRef theSessionDictionaryRef = 0; CFMutableArrayRef theTrackArrayRef = 0; UInt32 trackIndex = 0; theSessionDictionaryRef = CFDictionaryCreateMutable ( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); theTrackArrayRef = CFArrayCreateMutable ( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); while ( ( trackDescriptorPtr <= lastTrackDescriptorPtr ) && ( trackDescriptorPtr->sessionNumber == ( index + 1 ) ) ) { CFMutableDictionaryRef theTrackRef = 0; CFBooleanRef isDigitalData = kCFBooleanFalse; CFBooleanRef preEmphasis = kCFBooleanFalse; CFNumberRef startBlock; CFNumberRef sessionNumber; CFNumberRef point; SInt16 pointValue; UInt32 blockAddress; pointValue = trackDescriptorPtr->point; if ( pointValue == 0x00A0 ) { CFNumberRef sessionType = 0; CFNumberRef firstTrackNum = 0; CFNumberRef sessionNumber = 0; firstTrackNum = CFNumberCreate ( kCFAllocatorDefault, kCFNumberCharType, &trackDescriptorPtr->PMSF.A0PMSF.firstTrackNum ); sessionType = CFNumberCreate ( kCFAllocatorDefault, kCFNumberCharType, &trackDescriptorPtr->PMSF.A0PMSF.discType ); sessionNumber = CFNumberCreate ( kCFAllocatorDefault, kCFNumberCharType, &trackDescriptorPtr->sessionNumber ); CFDictionarySetValue ( theSessionDictionaryRef, CFSTR ( kFirstTrackInSessionString ), firstTrackNum ); CFDictionarySetValue ( theSessionDictionaryRef, CFSTR ( kSessionTypeString ), sessionType ); CFDictionarySetValue ( theSessionDictionaryRef, CFSTR ( kSessionNumberString ), sessionNumber ); CFRelease ( firstTrackNum ); CFRelease ( sessionType ); CFRelease ( sessionNumber ); goto nextIteration; } if ( pointValue == 0x00A1 ) { CFNumberRef lastTrackNum = 0; lastTrackNum = CFNumberCreate ( kCFAllocatorDefault, kCFNumberCharType, &trackDescriptorPtr->PMSF.A1PMSF.lastTrackNum ); CFDictionarySetValue ( theSessionDictionaryRef, CFSTR ( kLastTrackInSessionString ), lastTrackNum ); CFRelease ( lastTrackNum ); goto nextIteration; } if ( pointValue == 0x00A2 ) { CFNumberRef leadoutBlock = 0; UInt32 blockAddress = 0; blockAddress = ( ( trackDescriptorPtr->PMSF.leadOutStartPosition.minutes * 60 ) + trackDescriptorPtr->PMSF.leadOutStartPosition.seconds ) * 75 + trackDescriptorPtr->PMSF.leadOutStartPosition.frames; leadoutBlock = CFNumberCreate ( kCFAllocatorDefault, kCFNumberIntType, &blockAddress ); CFDictionarySetValue ( theSessionDictionaryRef, CFSTR ( kLeadoutBlockString ), leadoutBlock ); CFRelease ( leadoutBlock ); goto nextIteration; } if ( pointValue > 0x63 ) { // Skip the B0-C1 identifiers goto nextIteration; } theTrackRef = CFDictionaryCreateMutable ( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); point = CFNumberCreate ( kCFAllocatorDefault, kCFNumberSInt16Type, &pointValue ); CFDictionarySetValue ( theTrackRef, CFSTR ( kPointString ), point ); CFRelease ( point ); blockAddress = ( ( trackDescriptorPtr->PMSF.startPosition.minutes * 60 ) + trackDescriptorPtr->PMSF.startPosition.seconds ) * 75 + trackDescriptorPtr->PMSF.startPosition.frames; DebugLog ( ( "track = %d, blockAddress = %d\n", pointValue, ( int ) blockAddress ) ); startBlock = CFNumberCreate ( kCFAllocatorDefault, kCFNumberSInt32Type, &blockAddress ); CFDictionarySetValue ( theTrackRef, CFSTR ( kStartBlockString ), startBlock ); CFRelease ( startBlock ); sessionNumber = CFNumberCreate ( kCFAllocatorDefault, kCFNumberCharType, &trackDescriptorPtr->sessionNumber ); CFDictionarySetValue ( theTrackRef, CFSTR ( kSessionNumberString ), sessionNumber ); CFRelease ( sessionNumber ); if ( ( trackDescriptorPtr->control & kDigitalDataMask ) == kDigitalDataMask ) { isDigitalData = kCFBooleanTrue; } CFDictionarySetValue ( theTrackRef, CFSTR ( kDataString ), isDigitalData ); if ( ( trackDescriptorPtr->control & kPreEmphasisMask ) == kPreEmphasisMask ) { preEmphasis = kCFBooleanTrue; } CFDictionarySetValue ( theTrackRef, CFSTR ( kPreEmphasisString ), preEmphasis ); // Add the dictionary to the array CFArraySetValueAtIndex ( theTrackArrayRef, trackIndex, theTrackRef ); CFRelease ( theTrackRef ); trackIndex++; nextIteration: // Advance to next track trackDescriptorPtr++; } // Set the array inside of the dictionary for the session CFDictionarySetValue ( theSessionDictionaryRef, CFSTR ( kTrackArrayString ), theTrackArrayRef ); CFArraySetValueAtIndex ( theSessionArrayRef, index, theSessionDictionaryRef ); CFRelease ( theSessionDictionaryRef ); CFRelease ( theTrackArrayRef ); } CFDictionarySetValue ( theCDDictionaryRef, CFSTR ( kSessionsString ), theSessionArrayRef ); CFRelease ( theSessionArrayRef ); xmlData = CFPropertyListCreateXMLData ( kCFAllocatorDefault, theCDDictionaryRef ); CFRelease ( theCDDictionaryRef ); } return xmlData; } #if 0 #pragma mark - #pragma mark - Shared Code #pragma mark - #endif //----------------------------------------------------------------------------- // DisplayUsage - This routine will do a printf of the correct usage // for whichever utility was launched. //----------------------------------------------------------------------------- void DisplayUsage ( int usageType, const char * argv[] ) { if ( usageType == kUsageTypeMount ) { printf ( "usage: mount_cddafs [-o options] device-name mount-point\n" ); } else if ( usageType == kUsageTypeUtility ) { printf ( "usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv[0] ); printf ( "action_arg:\n" ); printf ( " -%c (Probe for mounting)\n", FSUC_PROBE ); printf ( " -%c (Mount)\n", FSUC_MOUNT ); printf ( " -%c (Unmount)\n", FSUC_UNMOUNT ); printf ( " -%c (Force Mount)\n", FSUC_MOUNT_FORCE ); printf ( "device_arg:\n" ); printf ( " device we are acting upon (for example, 'sd2')\n" ); printf ( "mount_point_arg:\n" ); printf ( " required for Mount and Force Mount \n" ); printf ( "Flags:\n" ); printf ( " required for Mount, Force Mount and Probe\n" ); printf ( " indicates removable or fixed (for example 'fixed')\n" ); printf ( " indicates readonly or writable (for example 'readonly')\n" ); printf ( "Examples:\n"); printf ( " %s -p sd2 fixed writable\n", argv[0] ); printf ( " %s -m sd2 /my/hfs removable readonly\n", argv[0] ); } return; } //----------------------------------------------------------------------------- // GetTOCDataPtr - Gets a pointer to the TOCData // // deviceNamePtr - pointer to the device name (full path, like /dev/rdisk1) //----------------------------------------------------------------------------- UInt8 * GetTOCDataPtr ( const char * deviceNamePtr ) { UInt8 * ptr = NULL; IOReturn error = 0; io_iterator_t iterator = MACH_PORT_NULL; io_registry_entry_t registryEntry = MACH_PORT_NULL; CFMutableDictionaryRef properties = 0; CFDataRef data = 0; char * bsdName = NULL; if ( !strncmp ( deviceNamePtr, "/dev/r", 6 ) ) { // Strip off the /dev/ from /dev/rdiskX bsdName = ( char * ) &deviceNamePtr[6]; } else if ( !strncmp ( deviceNamePtr, "/dev/", 5 ) ) { // Strip off the /dev/ from /dev/diskX bsdName = ( char * ) &deviceNamePtr[5]; } else { DebugLog ( ( "GetTOCDataPtr: ERROR: not /dev/something...\n" ) ); goto Exit; } error = IOServiceGetMatchingServices ( kIOMasterPortDefault, IOBSDNameMatching ( kIOMasterPortDefault, 0, bsdName ), &iterator ); require ( ( error == kIOReturnSuccess ), Exit ); // Only expect one entry since there is a 1:1 correspondence between bsd names // and IOKit storage objects registryEntry = IOIteratorNext ( iterator ); require ( ( registryEntry != MACH_PORT_NULL ), ReleaseIterator ); require ( IOObjectConformsTo ( registryEntry, kIOCDMediaString ), ReleaseEntry ); error = IORegistryEntryCreateCFProperties ( registryEntry, &properties, kCFAllocatorDefault, kNilOptions ); require ( ( error == kIOReturnSuccess ), ReleaseEntry ); // Get the TOCInfo data = ( CFDataRef ) CFDictionaryGetValue ( properties, CFSTR ( kIOCDMediaTOCKey ) ); if ( data != NULL ) { ptr = CreateBufferFromCFData ( data ); } // Release the properties CFRelease ( properties ); ReleaseEntry: // release the object error = IOObjectRelease ( registryEntry ); ReleaseIterator: // relese the iterator error = IOObjectRelease ( iterator ); Exit: return ptr; } //----------------------------------------------------------------------------- // IsAudioTrack - Figures out if a track is audio or not. //----------------------------------------------------------------------------- Boolean IsAudioTrack ( UInt32 trackNumber, QTOCDataFormat10Ptr TOCData ) { SubQTOCInfoPtr trackDescriptorPtr; trackDescriptorPtr = TOCData->trackDescriptors; trackDescriptorPtr = trackDescriptorPtr + trackNumber; if ( trackDescriptorPtr->point < 100 && trackDescriptorPtr->point > 0 ) { if ( ( trackDescriptorPtr->control & kDigitalDataMask ) == 0 ) { // Found an audio track return true; } } return false; } //----------------------------------------------------------------------------- // GetNumberOfTrackDescriptors - Gets the number of track descriptors //----------------------------------------------------------------------------- SInt32 GetNumberOfTrackDescriptors ( QTOCDataFormat10Ptr TOCDataPtr, UInt8 * numberOfDescriptors ) { UInt16 length = 0; SInt32 result = 0; require_action ( ( TOCDataPtr != NULL ), Exit, result = -1 ); require_action ( ( numberOfDescriptors != NULL ), Exit, result = -1 ); // Grab the length and advance length = OSSwapBigToHostInt16 ( TOCDataPtr->TOCDataLength ); DebugLog ( ( "Length = %d\n", length ) ); require_action ( ( length > sizeof ( CDTOC ) ), Exit, result = -1 ); length -= ( sizeof ( TOCDataPtr->firstSessionNumber ) + sizeof ( TOCDataPtr->lastSessionNumber ) ); *numberOfDescriptors = length / ( sizeof ( SubQTOCInfo ) ); DebugLog ( ( "Number of descriptors = %d\n", *numberOfDescriptors ) ); Exit: return result; } //----------------------------------------------------------------------------- // GetPointValue - Gets the track's point value //----------------------------------------------------------------------------- UInt8 GetPointValue ( UInt32 trackIndex, QTOCDataFormat10Ptr TOCData ) { SubQTOCInfoPtr trackDescriptorPtr; trackDescriptorPtr = TOCData->trackDescriptors; trackDescriptorPtr = trackDescriptorPtr + trackIndex; return trackDescriptorPtr->point; } //----------------------------------------------------------------------------- // CreateBufferFromCFData - Allocates memory for a chunk of memory and copies // the contents of the CFData to it. // // NB: The calling function should dispose of the memory //----------------------------------------------------------------------------- UInt8 * CreateBufferFromCFData ( CFDataRef theData ) { CFRange range; CFIndex bufferLength = 0; UInt8 * buffer = NULL; bufferLength = CFDataGetLength ( theData ); buffer = ( UInt8 * ) malloc ( bufferLength ); range = CFRangeMake ( 0, bufferLength ); if ( buffer != NULL ) CFDataGetBytes ( theData, range, buffer ); return buffer; } //----------------------------------------------------------------------------- // FindNumberOfAudioTracks - Parses the TOC to find the number of audio // tracks. //----------------------------------------------------------------------------- UInt32 FindNumberOfAudioTracks ( QTOCDataFormat10Ptr TOCDataPtr ) { UInt32 result = 0; SubQTOCInfoPtr trackDescriptorPtr = NULL; UInt16 length = 0; UInt16 numberOfDescriptors = 0; DebugLog ( ( "FindNumberOfAudioTracks called\n" ) ); require ( ( TOCDataPtr != NULL ), Exit ); // Grab the length and advance length = OSSwapBigToHostInt16 ( TOCDataPtr->TOCDataLength ); require ( ( length > sizeof ( CDTOC ) ), Exit ); length -= ( sizeof ( TOCDataPtr->firstSessionNumber ) + sizeof ( TOCDataPtr->lastSessionNumber ) ); numberOfDescriptors = length / ( sizeof ( SubQTOCInfo ) ); require ( ( numberOfDescriptors != 0 ), Exit ); DebugLog ( ( "numberOfDescriptors = %d\n", numberOfDescriptors ) ); trackDescriptorPtr = TOCDataPtr->trackDescriptors; while ( numberOfDescriptors > 0 ) { if ( trackDescriptorPtr->point < 100 && trackDescriptorPtr->point > 0 ) { if ( ( trackDescriptorPtr->control & kDigitalDataMask ) == 0 ) { // Found an audio track result++; } } trackDescriptorPtr++; numberOfDescriptors--; } Exit: DebugLog ( ( "numberOfTracks = %d\n", ( int ) result ) ); return result; } //----------------------------------------------------------------------------- // End Of File //-----------------------------------------------------------------------------