1/*
2 * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24// cddafs_util.c created by CJS on Mon 10-Apr-2000
25
26
27//-----------------------------------------------------------------------------
28//	Includes
29//-----------------------------------------------------------------------------
30
31// System Includes
32#include <stdio.h>
33#include <unistd.h>
34#include <string.h>
35#include <stdlib.h>
36#include <fcntl.h>
37#include <dirent.h>
38#include <libgen.h>
39#include <mntopts.h>
40#include <mach/mach_init.h>
41#include <servers/netname.h>
42#include <sys/types.h>
43#include <sys/wait.h>
44#include <sys/sysctl.h>
45#include <sys/disk.h>
46#include <sys/errno.h>
47#include <sys/param.h>
48#include <sys/paths.h>
49#include <sys/stat.h>
50#include <sys/time.h>
51#include <sys/mount.h>
52#include <sys/loadable_fs.h>
53
54// Libkern includes
55#include <libkern/OSByteOrder.h>
56
57// CoreFoundation Includes
58#include <CoreFoundation/CoreFoundation.h>
59#include <CoreFoundation/CFPriv.h>
60
61// IOKit Includes
62#include <IOKit/IOKitLib.h>
63#include <IOKit/storage/IOCDTypes.h>
64#include <IOKit/storage/IOCDMedia.h>
65
66// Project includes
67#include "cddafs_util.h"
68#include "AppleCDDAFileSystemDefines.h"
69
70	#include "CDDATrackName.h"
71
72
73//-----------------------------------------------------------------------------
74//	Macros
75//-----------------------------------------------------------------------------
76
77#define DEBUG							0
78#define DEBUG_LEVEL						0
79
80#ifndef DEBUG_ASSERT_COMPONENT_NAME_STRING
81	#define DEBUG_ASSERT_COMPONENT_NAME_STRING "cddafs.util"
82#endif
83
84#include <AssertMacros.h>
85
86#if (DEBUG_LEVEL > 3)
87#define DebugLog(x)		printf x
88#else
89#define DebugLog(x)
90#endif
91
92static int
93UtilityMain ( int argc, const char * argv[] );
94
95static int
96MountMain ( int argc, const char * argv[] );
97
98static CFDataRef
99GetTrackData ( const char * 				bsdDevNode,
100			   const QTOCDataFormat10Ptr	TOCData );
101
102//-----------------------------------------------------------------------------
103//	Globals
104//-----------------------------------------------------------------------------
105
106struct mntopt gMountOptions[] =
107{
108	MOPT_STDOPTS,
109	{ NULL }
110};
111
112static char		gAppleCDDAName[MFSNAMELEN] = "cddafs";
113static char		gFileSuffix[] = ".aiff";
114
115#define			kMaxPrefixSize		3
116#define			kASCIINumberZero	0x30
117#define			kASCIISpace			0x20
118
119//-----------------------------------------------------------------------------
120//	main -	This our main entry point to this utility.  We get called by
121//			autodiskmount.
122//-----------------------------------------------------------------------------
123
124int
125main ( int argc, const char * argv[] )
126{
127
128	int			result			= -1;
129	char *		executableName	= NULL;
130
131	#if DEBUG
132	int		index	= 0;
133
134	for ( index = 0; index < argc; index++ )
135	{
136		printf ( "[%d] = %s\n", index, argv[index] );
137	}
138	#endif
139
140	executableName = basename ( ( char * ) argv[0] );
141	if ( executableName == NULL )
142		exit ( 1 );
143
144	if ( strcmp ( executableName, kUtilExecutableName ) == 0 )
145	{
146
147		result = UtilityMain ( argc, argv );
148
149	}
150
151	else
152	{
153		result = MountMain ( argc, argv );
154	}
155
156	return result;
157
158}
159
160
161#if 0
162#pragma mark -
163#pragma mark - Utility Code
164#pragma mark -
165#endif
166
167
168//-----------------------------------------------------------------------------
169//	UtilityMain -	Returns FSUR_IO_SUCCESS if everything works, else it
170//					returns one of the FSUR_XXX errors in loadable_fs.h
171//-----------------------------------------------------------------------------
172
173static int
174UtilityMain ( int argc, const char * argv[] )
175{
176
177	char				rawDeviceName[MAXPATHLEN];
178	char				blockDeviceName[MAXPATHLEN];
179	const char *		actionPtr 						= NULL;
180	const char *		mountPointPtr 					= NULL;
181	int					result 							= FSUR_IO_SUCCESS;
182	boolean_t			isLocked 						= 0;
183	boolean_t			isEjectable 					= 0;
184	int					mountFlags						= MNT_RDONLY;
185
186	// Verify our arguments
187	result = ParseUtilityArgs ( argc, argv, &actionPtr, &mountPointPtr, &isEjectable, &isLocked );
188	require ( ( result == 0 ), Exit );
189
190	// Build our device name (full path), should end up with something like:
191	// -- "/dev/disk1" or "/dev/disk2" or "/dev/disk3"
192
193	snprintf ( rawDeviceName, MAXPATHLEN, "/dev/r%s", argv[2] );
194	snprintf ( blockDeviceName, MAXPATHLEN, "/dev/%s", argv[2] );
195
196	// call the appropriate routine to handle the given action argument after becoming root
197	result = seteuid ( 0 );
198	require_action ( ( result == 0 ), Exit, result = FSUR_INVAL );
199
200	result = setegid ( 0 );
201
202	if ( result )
203	{
204		DebugLog ( ( "cddafs.util: ERROR: setegid: %s\n", strerror ( errno ) ) );
205	}
206
207	DebugLog ( ( "Entering the switch with action = %s\n", actionPtr ) );
208
209    switch ( *actionPtr )
210	{
211
212		case FSUC_PROBE:
213			result = Probe ( rawDeviceName );
214			break;
215
216		case FSUC_MOUNT:
217		case FSUC_MOUNT_FORCE:
218			result = Mount ( blockDeviceName, mountPointPtr, mountFlags );
219			break;
220
221		case FSUC_UNMOUNT:
222			result = Unmount ( mountPointPtr );
223			break;
224
225        default:
226			// should never get here since DoVerifyArgs should handle this situation
227			DisplayUsage ( kUsageTypeUtility, argv );
228			result = FSUR_INVAL;
229			break;
230
231	}
232
233
234Exit:
235
236
237	DebugLog ( ( "cddafs.util: EXIT: %d = ", result ) );
238	switch ( result )
239	{
240
241		case FSUR_LOADERR:
242			DebugLog ( ( "FSUR_LOADERR\n" ) );
243			break;
244
245		case FSUR_INVAL:
246			DebugLog ( ( "FSUR_INVAL\n" ) );
247			break;
248
249		case FSUR_IO_SUCCESS:
250			DebugLog ( ( "FSUR_IO_SUCCESS\n" ) );
251			break;
252
253		case FSUR_IO_FAIL:
254			DebugLog ( ( "FSUR_IO_FAIL\n" ) );
255			break;
256
257		case FSUR_RECOGNIZED:
258			DebugLog ( ( "FSUR_RECOGNIZED\n" ) );
259			break;
260
261		case FSUR_MOUNT_HIDDEN:
262			DebugLog ( ( "FSUR_MOUNT_HIDDEN\n" ) );
263			break;
264
265		case FSUR_UNRECOGNIZED:
266			DebugLog ( ( "FSUR_UNRECOGNIZED\n" ) );
267			break;
268
269		default:
270			DebugLog ( ( "default\n" ) );
271			break;
272
273	}
274
275	check ( result == FSUR_IO_SUCCESS );
276	exit ( result );
277
278	return result;	// ...and make main fit the ANSI spec.
279
280}
281
282
283//-----------------------------------------------------------------------------
284//	ParseUtilityArgs -	This routine will make sure the arguments passed
285//						in to us are copacetic. Here is how this utility is used:
286//
287//	usage: cddafs.util actionArg deviceArg [mountPointArg] [flagsArg]
288//	actionArg:
289//		-p (Probe for mounting)
290//		-P (Probe for initializing - not supported)
291//		-m (Mount)
292//		-r (Repair - not supported)
293//		-u (Unmount)
294//		-M (Force Mount)
295//		-i (Initialize - not supported)
296//
297//	deviceArg:
298//		sd2 (for example)
299//
300//	mountPointArg:
301//		/foo/bar/ (required for Mount and Force Mount actions)
302//
303//	flagsArg:
304//		(these are ignored for CDROMs)
305//
306//	examples:
307//		cddafs.util -p sd2 removable writable
308//		cddafs.util -p sd2 removable readonly
309//		cddafs.util -m sd2 /my/cddafs
310//
311//	Returns FSUR_INVAL if we find a bad argument, else 0.
312//-----------------------------------------------------------------------------
313
314int
315ParseUtilityArgs ( 	int				argc,
316					const char *	argv[],
317					const char **	actionPtr,
318					const char **	mountPointPtr,
319					boolean_t *		isEjectablePtr,
320					boolean_t *		isLockedPtr )
321{
322
323	int			result 			= FSUR_INVAL;
324	int			deviceLength	= 0;
325	int			index			= 0;
326
327	// Must have at least 3 arguments and the action argument must start with a '-'
328	require_action ( ( argc >= 3 ), Exit, DisplayUsage ( kUsageTypeUtility, argv ) );
329	require_action ( ( argv[1][0] == '-' ), Exit, DisplayUsage ( kUsageTypeUtility, argv ) );
330
331	// we only support actions Probe, Mount, Force Mount, and Unmount
332	*actionPtr = &argv[1][1];
333
334	switch ( argv[1][1] )
335	{
336
337		case FSUC_PROBE:
338			// action Probe and requires 5 arguments (need the flags)
339			require_action ( ( argc >= 5 ), Exit, DisplayUsage ( kUsageTypeUtility, argv ) );
340			index = 3;
341			break;
342
343		case FSUC_UNMOUNT:
344			*mountPointPtr = argv[3];
345			index = 0; // No isEjectable/isLocked flags for unmount.
346			break;
347
348		case FSUC_MOUNT:
349		case FSUC_MOUNT_FORCE:
350			// action Mount and ForceMount require 6 arguments
351			// ( need the mountpoint and the flags )
352			require_action ( ( argc >= 6 ), Exit, DisplayUsage ( kUsageTypeUtility, argv ) );
353
354			*mountPointPtr = argv[3];
355			index = 4;
356			break;
357
358		default:
359			DisplayUsage ( kUsageTypeUtility, argv );
360			goto Exit;
361			break;
362
363	}
364
365	// Make sure device (argv[2]) is something reasonable
366	// (we expect something like "disk1")
367	deviceLength = ( int ) strlen ( argv[2] );
368	require ( ( deviceLength >= 5 ), Exit );
369
370	result = 0;
371
372	// If index is zero, no more work to do...
373	require ( ( index != 0 ), Exit );
374
375	// Flags: removable/fixed
376	if ( !strcmp ( argv[index], "removable" ) )
377	{
378
379		*isEjectablePtr = 1;
380
381	}
382
383	else if ( !strcmp ( argv[index], "fixed" ) )
384	{
385
386		*isEjectablePtr = 0;
387
388	}
389
390	else
391	{
392
393		DebugLog ( ( "cddafs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",
394					index, argv[index] ) );
395
396	}
397
398	// Flags: readonly/writable
399	if ( !strcmp ( argv[index + 1], "readonly" ) )
400	{
401		*isLockedPtr = 1;
402	}
403
404	else if ( !strcmp ( argv[index + 1], "writable" ) )
405	{
406		*isLockedPtr = 0;
407	}
408
409	else
410	{
411		DebugLog ( ( "cddafs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",
412					index, argv[index + 1] ) );
413	}
414
415
416Exit:
417
418	return result;
419
420}
421
422
423//-----------------------------------------------------------------------------
424//	Probe -		This routine will open the given raw device and check to
425//				make sure there is media that looks like an Audio CD. Returns
426//				FSUR_MOUNT_HIDDEN if everything works, else FSUR_IO_FAIL.
427//
428//	deviceNamePtr - pointer to the raw device name (full path, like /dev/rdisk1)
429//-----------------------------------------------------------------------------
430
431int
432Probe ( char * deviceNamePtr )
433{
434
435	int				result 	= FSUR_UNRECOGNIZED;
436	UInt8 *			ptr 	= NULL;
437
438	DebugLog ( ( "ENTER: Probe('%s')\n", deviceNamePtr ) );
439
440	ptr = GetTOCDataPtr ( deviceNamePtr );
441	if ( ptr != NULL )
442	{
443
444		// Parse the TOC for Audio Tracks
445		result = ParseTOC ( ptr );
446
447	}
448
449	else
450	{
451
452		DebugLog ( ( "GetTOCDataPtr returned NULL.\n" ) );
453
454	}
455
456	// if we recognized the disc, create the name and suffix files
457	if ( result == FSUR_RECOGNIZED )
458	{
459
460		CFStringRef			albumName 			= 0;
461		CDDATrackName *		database 			= NULL;
462
463		database = new CDDATrackName;
464
465		if ( database != NULL )
466		{
467
468			DebugLog ( ( "database != NULL\n" ) );
469
470			database->Init ( deviceNamePtr, ptr );
471
472			DebugLog ( ( "Init called\n" ) );
473
474			albumName = database->GetAlbumName ( );
475			DebugLog ( ( "GetAlbumName called\n" ) );
476
477		}
478
479		if ( albumName != 0 )
480		{
481
482			Boolean		success				= false;
483			char		buffer[MAXNAMLEN];
484
485			#if DEBUG
486			CFShow ( albumName );
487			#endif
488
489			success = _CFStringGetFileSystemRepresentation ( albumName,
490														     ( UInt8 * ) buffer,
491															 MAXNAMLEN );
492
493			if ( success == true )
494			{
495
496				WriteDiskLabel ( buffer );
497
498			}
499
500			else
501			{
502
503				// Good old "Audio CD" should work...
504				WriteDiskLabel ( ( char * ) kMountPointName );
505
506			}
507
508			// release it
509			CFRelease ( albumName );
510			albumName = 0;
511
512		}
513
514		else
515		{
516
517			// Good old "Audio CD" should work...
518			WriteDiskLabel ( ( char * ) kMountPointName );
519
520		}
521
522		if ( database != NULL )
523		{
524
525			delete database;
526			database = NULL;
527
528		}
529
530	}
531
532	if ( ptr != NULL )
533	{
534
535		// free the memory
536		free ( ptr );
537		ptr = NULL;
538
539	}
540
541	DebugLog ( ( "Probe: returns " ) );
542
543    switch ( result )
544	{
545
546		case FSUR_IO_FAIL:
547			DebugLog ( ( "FSUR_IO_FAIL\n" ) );
548			break;
549
550		case FSUR_RECOGNIZED:
551			DebugLog ( ( "FSUR_RECOGNIZED\n" ) );
552			break;
553
554		case FSUR_MOUNT_HIDDEN:
555			DebugLog ( ( "FSUR_MOUNT_HIDDEN\n" ) );
556			break;
557
558		case FSUR_UNRECOGNIZED:
559			DebugLog ( ( "FSUR_UNRECOGNIZED\n" ) );
560			break;
561
562		default:
563			DebugLog ( ( "default\n" ) );
564			break;
565
566	}
567
568	return result;
569
570}
571
572
573//-----------------------------------------------------------------------------
574//	Unmount -	This routine will fire off a system command to unmount the
575//				given device. Returns FSUR_IO_SUCCESS if everything works,
576//				else FSUR_IO_FAIL.
577//
578//	theDeviceNamePtr - pointer to the device name (full path, like /dev/disk1s2).
579//-----------------------------------------------------------------------------
580
581
582int
583Unmount ( const char * theMountPointPtr )
584{
585
586	int		result;
587	int		mountflags = 0;
588
589	result = unmount ( theMountPointPtr, mountflags );
590	require_action ( ( result == 0 ), Exit, result = FSUR_IO_FAIL );
591
592	result = FSUR_IO_SUCCESS;
593
594
595Exit:
596
597
598	return result;
599
600}
601
602
603#if 0
604#pragma mark -
605#pragma mark - Mount Code
606#pragma mark -
607#endif
608
609
610//-----------------------------------------------------------------------------
611//	MountMain -	returns 0 if successful
612//-----------------------------------------------------------------------------
613
614static int
615MountMain ( int argc, const char * argv[] )
616{
617
618	int		error 		= 0;
619	int		mountFlags 	= 0;
620
621	error = ParseMountArgs ( &argc, &argv, &mountFlags );
622	require_action ( ( error == 0 ), Exit, DisplayUsage ( kUsageTypeMount, argv ) );
623
624	error = Mount ( argv[0], argv[1], mountFlags );
625	check ( error == FSUR_IO_SUCCESS );
626
627	// mount_cddafs must return 0 for successful mounts
628	if ( error == FSUR_IO_SUCCESS )
629		error = 0;
630
631Exit:
632
633
634	return error;
635
636}
637
638
639//-----------------------------------------------------------------------------
640//	ParseMountArgs - Parses mount arguments
641//-----------------------------------------------------------------------------
642
643int
644ParseMountArgs ( int * argc, const char ** argv[], int * mountFlags )
645{
646
647	int				error		= 0;
648	int				ch			= 0;
649	int				altFlags	= 0;
650	mntoptparse_t	parse		= NULL;
651
652	*mountFlags = 0;
653
654	// Audio CD's are read-only
655	*mountFlags |= MNT_RDONLY;
656
657	// Must have at least 3 arguments and the action argument must start with a '-'
658	require_action ( ( *argc > 2 ), Exit, error = 1 );
659
660	// Check command line args
661	while ( ( ch = getopt ( *argc, ( char * const * ) *argv, "o:" ) ) != -1 )
662	{
663
664		switch ( ch )
665		{
666
667            case 'o':
668				parse = getmntopts ( optarg, gMountOptions, mountFlags, &altFlags );
669				if ( parse != NULL )
670				{
671					freemntopts ( parse );
672				}
673
674				else
675				{
676					error = 1;
677				}
678				break;
679
680            default:
681				error = 1;
682				break;
683
684		}
685
686	}
687
688	*argc -= optind;
689	*argv += optind;
690
691
692Exit:
693
694
695	return error;
696
697}
698
699
700//-----------------------------------------------------------------------------
701//	WriteDiskLabel -	This routine will create a file system info file that
702//						is used by autodiskmount.  After creating the file it will
703//						write whatever contentsPtr points to the new file.
704//
705//	We end up with a file something like:
706//			/usr/filesystems/cddafs.fs/cddafs.name
707//
708//	when our file system name is "cddafs" and suffixPtr points
709//	to ".name" or ".label"
710//-----------------------------------------------------------------------------
711
712void
713WriteDiskLabel ( char * contentsPtr )
714{
715
716	StripTrailingSpaces ( contentsPtr );
717	write ( STDOUT_FILENO, contentsPtr, strlen ( contentsPtr ) );
718
719}
720
721
722//-----------------------------------------------------------------------------
723//	StripTrailingSpaces -	Strips trailing white spaces from character array
724//-----------------------------------------------------------------------------
725
726void
727StripTrailingSpaces ( char * theContentsPtr )
728{
729
730	check ( theContentsPtr );
731	check ( strlen ( theContentsPtr ) > 0 );
732
733	if ( strlen ( theContentsPtr ) > 0 )
734	{
735
736		char    	*myPtr;
737
738		myPtr = theContentsPtr + strlen ( theContentsPtr ) - 1;
739		while ( *myPtr == kASCIISpace && myPtr >= theContentsPtr )
740		{
741
742			*myPtr = 0x00;
743			myPtr--;
744
745		}
746
747	}
748
749}
750
751
752//-----------------------------------------------------------------------------
753//	Mount -	Attempts to mount on our filesystem if possible
754//-----------------------------------------------------------------------------
755
756int
757Mount ( const char * 	deviceNamePtr,
758		const char * 	mountPointPtr,
759		int				mountFlags )
760{
761
762	AppleCDDAArguments 		args		= { 0 };
763	struct vfsconf			vfc			= { 0 };
764	int						result		= FSUR_IO_FAIL;
765	int						error 		= 0;
766	CFDataRef				nameDataRef	= NULL;
767	CFDataRef				xmlDataRef	= NULL;
768	QTOCDataFormat10Ptr		TOCDataPtr 	= NULL;
769	UInt8 *					xmlDataPtr 	= NULL;
770	UInt8 *					nameDataPtr	= NULL;
771	char					realMountPoint[PATH_MAX];
772	char *					realMountPointPtr;
773
774	DebugLog ( ( "Mount('%s','%s')\n", deviceNamePtr, mountPointPtr ) );
775
776	require ( ( mountPointPtr != NULL ), Exit );
777	require ( ( *mountPointPtr != '\0' ), Exit );
778
779	args.device			= ( char * ) deviceNamePtr;
780	args.fileType		= 0x41494643; // 'AIFC'
781	args.fileCreator	= 0x3F3F3F3F; // '????'
782
783
784	// Check if we're loaded into vfs or not.
785	error = getvfsbyname ( gAppleCDDAName, &vfc );
786	if ( error != 0 )
787	{
788		// Kernel extension wasn't loaded, so try to load it...
789		error = LoadKernelExtension ( );
790		require ( ( error == 0 ), Exit );
791
792		// Now try again since we loaded our extension
793		error = getvfsbyname ( gAppleCDDAName, &vfc );
794		require ( ( error == 0 ), Exit );
795
796	}
797
798	TOCDataPtr = ( QTOCDataFormat10Ptr ) GetTOCDataPtr ( deviceNamePtr );
799	require ( ( TOCDataPtr != NULL ), Exit );
800
801	nameDataRef = GetTrackData ( deviceNamePtr, TOCDataPtr );
802	require ( ( nameDataRef != NULL ), ReleaseTOCData );
803
804	// Get the number of audio tracks
805	args.numTracks = FindNumberOfAudioTracks ( TOCDataPtr );
806
807	// Build the XML file ".TOC.plist"
808	xmlDataRef	= CreateXMLFileInPListFormat ( TOCDataPtr );
809	require ( ( xmlDataRef != NULL ), ReleaseNameData );
810
811	// Get the pointers.
812	nameDataPtr	= ( UInt8 * ) CFDataGetBytePtr ( nameDataRef );
813	xmlDataPtr	= ( UInt8 * ) CFDataGetBytePtr ( xmlDataRef );
814
815	args.nameData		= ( user_addr_t ) nameDataPtr;
816	args.nameDataSize 	= ( uint32_t ) CFDataGetLength ( nameDataRef );
817	args.xmlData 		= ( user_addr_t ) xmlDataPtr;
818	args.xmlFileSize	= ( uint32_t ) CFDataGetLength ( xmlDataRef );
819
820#if ( DEBUG_LEVEL > 3 )
821	{
822		UInt32	count = 0;
823
824		for ( ; count < args.xmlFileSize; count = count + 8 )
825		{
826
827			DebugLog ( ("%02x:%02x:%02x:%02x %02x:%02x:%02x:%02x\n",
828						xmlDataPtr[count],
829						xmlDataPtr[count+1],
830						xmlDataPtr[count+2],
831						xmlDataPtr[count+3],
832						xmlDataPtr[count+4],
833						xmlDataPtr[count+5],
834						xmlDataPtr[count+6],
835						xmlDataPtr[count+7] ) );
836
837		}
838
839		DebugLog ( ( "\n" ) );
840		DebugLog ( ( "XML File Size = %d\n", ( int ) args.xmlFileSize ) );
841
842	}
843#endif
844
845	// Print out the device name for debug purposes
846	DebugLog ( ( "DeviceName = %s\n", deviceNamePtr ) );
847	DebugLog ( ( "numTracks = %d\n", ( int ) args.numTracks ) );
848
849	require ( ( args.nameData != 0 ), ReleaseXMLData );
850	require ( ( args.nameDataSize != 0 ), ReleaseXMLData );
851	require ( ( args.xmlData != 0 ), ReleaseXMLData );
852
853	DebugLog ( ( "args.nameData = %qx\n", args.nameData ) );
854	DebugLog ( ( "args.xmlData = %qx\n", args.xmlData ) );
855	DebugLog ( ( "sizeof(args) = %ld\n", sizeof ( args ) ) );
856
857	// Obtain the real path.
858	realMountPointPtr = realpath ( mountPointPtr, realMountPoint );
859	require ( ( realMountPointPtr != NULL ), ReleaseXMLData );
860
861	// Issue the system mount command
862	result = mount ( vfc.vfc_name, realMountPoint, mountFlags, &args );
863	require ( ( result == 0 ), ReleaseXMLData );
864
865	result = FSUR_IO_SUCCESS;
866
867
868ReleaseXMLData:
869
870
871	CFRelease ( xmlDataRef );
872	xmlDataRef = NULL;
873
874
875ReleaseNameData:
876
877
878	CFRelease ( nameDataRef );
879	nameDataRef = NULL;
880
881
882ReleaseTOCData:
883
884
885	// free the memory
886	require_quiet ( ( TOCDataPtr != NULL ), Exit );
887	free ( ( char * ) TOCDataPtr );
888	TOCDataPtr = NULL;
889
890
891Exit:
892
893
894	return result;
895
896}
897
898
899//-----------------------------------------------------------------------------
900//	ParseTOC - 	Parses the TOC to find audio tracks. If it finds one or more
901//				audio tracks, it returns FSUR_RECOGNIZED,
902//				else FSUR_UNRECOGNIZED
903//
904//-----------------------------------------------------------------------------
905
906int
907ParseTOC ( UInt8 * TOCInfoPtr )
908{
909
910	int						result				= FSUR_UNRECOGNIZED;
911	int						error				= 0;
912	QTOCDataFormat10Ptr		TOCDataPtr			= NULL;
913	UInt8					index				= 0;
914	UInt8					numberOfDescriptors = 0;
915
916	require ( ( TOCInfoPtr != NULL ), Exit );
917
918	// Set our pointer to the TOCInfoPtr
919	TOCDataPtr = ( QTOCDataFormat10Ptr ) TOCInfoPtr;
920
921	error = GetNumberOfTrackDescriptors ( TOCDataPtr, &numberOfDescriptors );
922	require_quiet ( ( error == 0 ), Exit );
923
924	for ( index = 0; index < numberOfDescriptors; index++ )
925	{
926
927		if ( IsAudioTrack ( index, TOCDataPtr ) )
928		{
929
930			// Found an audio cd
931			return FSUR_RECOGNIZED;
932
933		}
934
935	}
936
937
938Exit:
939
940
941	return result;
942
943}
944
945
946//-----------------------------------------------------------------------------
947// GetTrackData - Loads databases and calls them for Track Name info.
948//-----------------------------------------------------------------------------
949
950CFDataRef
951GetTrackData ( const char * 				bsdDevNode,
952			   const QTOCDataFormat10Ptr	TOCData )
953{
954
955	CFMutableDataRef	data			= 0;
956	CFStringRef			trackString		= 0;
957	UInt8				numTracks		= 0;
958	UInt32				trackIndex		= 0;
959	UInt8				currentTrack	= 0;
960	SInt32				error			= 0;
961	CDDATrackName *		database		= NULL;
962
963	data 		= CFDataCreateMutable ( kCFAllocatorDefault, 0 );
964
965	database	= new CDDATrackName;
966
967	database->Init ( bsdDevNode, TOCData );
968
969	error = GetNumberOfTrackDescriptors ( ( QTOCDataFormat10Ptr ) TOCData, &numTracks );
970	if ( error != 0 )
971	{
972
973		DebugLog ( ( "Error = %d on GetNumberOfTrackDescriptors\n", ( int ) error ) );
974		exit ( 1 );
975
976	}
977
978	for ( trackIndex = 0; trackIndex < numTracks; trackIndex++ )
979	{
980
981		if ( IsAudioTrack ( trackIndex, TOCData ) == false )
982			continue;
983
984		currentTrack = GetPointValue ( trackIndex, TOCData );
985		trackString = ( database )->GetTrackName ( currentTrack );
986
987		if ( trackString != 0 )
988		{
989
990			UInt8		size 				= 0;
991			UInt8		suffixSize			= 0;
992			UInt8		prefixSize			= 2;
993			UInt8		bufferSize			= 0;
994			UInt8		offset				= 0;
995			Boolean		success				= false;
996			char *		buffer				= NULL;
997			char		prefix[kMaxPrefixSize];
998
999			if ( currentTrack > 9 )
1000			{
1001				prefixSize++;
1002				prefix[offset++] = currentTrack / 10 + kASCIINumberZero;
1003			}
1004
1005			prefix[offset++] = currentTrack % 10 + kASCIINumberZero;
1006			prefix[offset] = kASCIISpace;
1007
1008			suffixSize = strlen ( gFileSuffix );
1009			bufferSize = MAXNAMLEN - prefixSize - suffixSize - 1;
1010
1011			buffer = ( char * ) calloc ( 1, bufferSize );
1012
1013			success = _CFStringGetFileSystemRepresentation ( trackString,
1014														     ( UInt8 * ) buffer,
1015															 bufferSize );
1016
1017			buffer[bufferSize - 1] = 0;
1018			size = strlen ( buffer ) + prefixSize + suffixSize;
1019
1020			DebugLog ( ( "size = %d\n", size ) );
1021			DebugLog ( ( "buffer = %s\n", buffer ) );
1022
1023			// Add the track number to the data object
1024			CFDataAppendBytes ( data,
1025								&currentTrack,
1026								1 );
1027
1028			// Add the size to the data object
1029			CFDataAppendBytes ( data,
1030								&size,
1031								1 );
1032
1033			// Add the prefix to the data object
1034			CFDataAppendBytes ( data,
1035								( UInt8 * ) prefix,
1036								prefixSize );
1037
1038			// add the string to the data object
1039			CFDataAppendBytes ( data,
1040								( UInt8 * ) buffer,
1041								strlen ( buffer ) );
1042
1043			// add the suffix to the data object
1044			CFDataAppendBytes ( data,
1045								( UInt8 * ) gFileSuffix,
1046								suffixSize );
1047
1048			free ( buffer );
1049
1050			// release it
1051			CFRelease ( trackString );
1052
1053		}
1054
1055	}
1056
1057	if ( database != NULL )
1058	{
1059
1060		delete database;
1061		database = NULL;
1062
1063	}
1064
1065	return data;
1066
1067}
1068
1069
1070//-----------------------------------------------------------------------------
1071// LoadKernelExtension - 	Loads our filesystem kernel extension.
1072//-----------------------------------------------------------------------------
1073
1074int
1075LoadKernelExtension ( void )
1076{
1077
1078	int 			pid;
1079	int 			result 	= -1;
1080	union wait 		status;
1081
1082	pid = fork ( );
1083
1084	if ( pid == 0 )
1085	{
1086
1087		result = execl ( kLoadCommand, kLoadCommand,
1088						 kCDDAFileSystemExtensionPath, NULL );
1089
1090		// We can only get here if the exec failed
1091		goto Exit;
1092
1093	}
1094
1095	if ( pid == -1 )
1096	{
1097
1098		// fork() didn't work, so we grab the error from errno
1099		// to return as our error
1100		result = errno;
1101		goto Exit;
1102
1103	}
1104
1105	// Success!
1106	if ( ( wait4 ( pid, ( int * ) &status, 0, NULL ) == pid ) &&
1107		 ( WIFEXITED ( status ) ) )
1108	{
1109
1110		// Stuff the status return code into our result
1111		result = status.w_retcode;
1112
1113	}
1114
1115	else
1116	{
1117
1118		// else return -1
1119		result = -1;
1120
1121	}
1122
1123
1124	check ( result == 0 );
1125
1126
1127Exit:
1128
1129
1130	return result;
1131
1132}
1133
1134
1135//-----------------------------------------------------------------------------
1136//	CreateXMLFileInPListFormat -	Makes a plist-style XML file which has a
1137//									parsed TOC. This makes it easy for
1138//									applications to get TOC info without having
1139//									to deal with the IOKit registry.
1140//
1141//	TOCDataPtr - pointer to a QTOCDataFormat10 structure
1142//-----------------------------------------------------------------------------
1143
1144CFDataRef
1145CreateXMLFileInPListFormat ( QTOCDataFormat10Ptr TOCDataPtr )
1146{
1147
1148	SubQTOCInfoPtr			trackDescriptorPtr		= NULL;
1149	SubQTOCInfoPtr			lastTrackDescriptorPtr	= NULL;
1150	UInt16					numSessions				= 0;
1151	UInt16					length					= 0;
1152	UInt16					numberOfDescriptors 	= 0;
1153	CFMutableDictionaryRef	theCDDictionaryRef		= 0;
1154	CFMutableArrayRef		theSessionArrayRef		= 0;
1155	CFDataRef				theRawTOCDataRef		= 0;
1156	UInt32 					index 					= 0;
1157	CFDataRef				xmlData					= 0;
1158
1159	DebugLog ( ( "CreateXMLFileInPListFormat called\n" ) );
1160
1161	if ( TOCDataPtr != NULL )
1162	{
1163
1164		// Create the master dictionary inside which all elements go
1165		theCDDictionaryRef = CFDictionaryCreateMutable ( 	kCFAllocatorDefault,
1166															0,
1167															&kCFTypeDictionaryKeyCallBacks,
1168															&kCFTypeDictionaryValueCallBacks );
1169
1170		// Grab the length and advance
1171		length = OSSwapBigToHostInt16 ( TOCDataPtr->TOCDataLength );
1172
1173		// Add the Raw TOC Data
1174		theRawTOCDataRef = CFDataCreate (	kCFAllocatorDefault,
1175											( UInt8 * ) TOCDataPtr,
1176											length + sizeof ( TOCDataPtr->TOCDataLength ) );
1177
1178		CFDictionarySetValue ( 	theCDDictionaryRef,
1179								CFSTR ( kRawTOCDataString ),
1180								theRawTOCDataRef );
1181
1182		CFRelease ( theRawTOCDataRef );
1183
1184		length -= ( sizeof ( TOCDataPtr->firstSessionNumber ) +
1185					sizeof ( TOCDataPtr->lastSessionNumber ) );
1186
1187		numberOfDescriptors = length / ( sizeof ( SubQTOCInfo ) );
1188		numSessions = TOCDataPtr->lastSessionNumber - TOCDataPtr->firstSessionNumber + 1;
1189
1190		if ( numberOfDescriptors <= 0 )
1191		{
1192
1193			DebugLog ( ( "No tracks on this CD...\n" ) );
1194
1195		}
1196
1197		// Create the array of sessions
1198		theSessionArrayRef 		= CFArrayCreateMutable ( kCFAllocatorDefault, numSessions, &kCFTypeArrayCallBacks );
1199		trackDescriptorPtr 		= TOCDataPtr->trackDescriptors;
1200		lastTrackDescriptorPtr	= TOCDataPtr->trackDescriptors + numberOfDescriptors - 1;
1201
1202		for ( index = 0; trackDescriptorPtr <= lastTrackDescriptorPtr; index++ )
1203		{
1204
1205			CFMutableDictionaryRef		theSessionDictionaryRef = 0;
1206			CFMutableArrayRef			theTrackArrayRef 		= 0;
1207			UInt32						trackIndex				= 0;
1208
1209			theSessionDictionaryRef = CFDictionaryCreateMutable ( 	kCFAllocatorDefault,
1210																	0,
1211																	&kCFTypeDictionaryKeyCallBacks,
1212																	&kCFTypeDictionaryValueCallBacks );
1213
1214			theTrackArrayRef = CFArrayCreateMutable ( 	kCFAllocatorDefault,
1215														0,
1216														&kCFTypeArrayCallBacks );
1217
1218			while ( ( trackDescriptorPtr <= lastTrackDescriptorPtr ) &&
1219					( trackDescriptorPtr->sessionNumber == ( index + 1 ) ) )
1220			{
1221
1222				CFMutableDictionaryRef	theTrackRef 	= 0;
1223				CFBooleanRef			isDigitalData	= kCFBooleanFalse;
1224				CFBooleanRef			preEmphasis		= kCFBooleanFalse;
1225				CFNumberRef				startBlock;
1226				CFNumberRef				sessionNumber;
1227				CFNumberRef				point;
1228				SInt16					pointValue;
1229				UInt32					blockAddress;
1230
1231				pointValue = trackDescriptorPtr->point;
1232
1233				if ( pointValue == 0x00A0 )
1234				{
1235
1236					CFNumberRef		sessionType		= 0;
1237					CFNumberRef		firstTrackNum	= 0;
1238					CFNumberRef		sessionNumber	= 0;
1239
1240					firstTrackNum = CFNumberCreate ( 	kCFAllocatorDefault,
1241														kCFNumberCharType,
1242														&trackDescriptorPtr->PMSF.A0PMSF.firstTrackNum );
1243
1244					sessionType = CFNumberCreate ( 	kCFAllocatorDefault,
1245													kCFNumberCharType,
1246													&trackDescriptorPtr->PMSF.A0PMSF.discType );
1247
1248					sessionNumber = CFNumberCreate ( 	kCFAllocatorDefault,
1249														kCFNumberCharType,
1250														&trackDescriptorPtr->sessionNumber );
1251
1252					CFDictionarySetValue ( 	theSessionDictionaryRef,
1253											CFSTR ( kFirstTrackInSessionString ),
1254											firstTrackNum );
1255
1256					CFDictionarySetValue ( 	theSessionDictionaryRef,
1257											CFSTR ( kSessionTypeString ),
1258											sessionType );
1259
1260					CFDictionarySetValue ( 	theSessionDictionaryRef,
1261											CFSTR ( kSessionNumberString ),
1262											sessionNumber );
1263
1264					CFRelease ( firstTrackNum );
1265					CFRelease ( sessionType );
1266					CFRelease ( sessionNumber );
1267
1268					goto nextIteration;
1269
1270				}
1271
1272				if ( pointValue == 0x00A1 )
1273				{
1274
1275					CFNumberRef		lastTrackNum	= 0;
1276
1277					lastTrackNum = CFNumberCreate ( 	kCFAllocatorDefault,
1278														kCFNumberCharType,
1279														&trackDescriptorPtr->PMSF.A1PMSF.lastTrackNum );
1280
1281					CFDictionarySetValue ( 	theSessionDictionaryRef,
1282											CFSTR ( kLastTrackInSessionString ),
1283											lastTrackNum );
1284
1285					CFRelease ( lastTrackNum );
1286
1287					goto nextIteration;
1288
1289				}
1290
1291				if ( pointValue == 0x00A2 )
1292				{
1293
1294					CFNumberRef		leadoutBlock	= 0;
1295					UInt32			blockAddress	= 0;
1296
1297					blockAddress = ( ( trackDescriptorPtr->PMSF.leadOutStartPosition.minutes * 60 ) +
1298									 trackDescriptorPtr->PMSF.leadOutStartPosition.seconds ) * 75 +
1299									 trackDescriptorPtr->PMSF.leadOutStartPosition.frames;
1300
1301					leadoutBlock = CFNumberCreate ( 	kCFAllocatorDefault,
1302														kCFNumberIntType,
1303														&blockAddress );
1304
1305					CFDictionarySetValue ( 	theSessionDictionaryRef,
1306											CFSTR ( kLeadoutBlockString ),
1307											leadoutBlock );
1308
1309					CFRelease ( leadoutBlock );
1310
1311					goto nextIteration;
1312
1313				}
1314
1315				if ( pointValue > 0x63 )
1316				{
1317
1318					// Skip the B0-C1 identifiers
1319					goto nextIteration;
1320
1321				}
1322
1323
1324				theTrackRef = CFDictionaryCreateMutable ( 	kCFAllocatorDefault,
1325															0,
1326															&kCFTypeDictionaryKeyCallBacks,
1327															&kCFTypeDictionaryValueCallBacks );
1328
1329				point = CFNumberCreate ( 	kCFAllocatorDefault,
1330											kCFNumberSInt16Type,
1331											&pointValue );
1332
1333				CFDictionarySetValue ( 	theTrackRef,
1334										CFSTR ( kPointString ),
1335										point );
1336
1337				CFRelease ( point );
1338
1339
1340				blockAddress = ( (  trackDescriptorPtr->PMSF.startPosition.minutes * 60 ) +
1341									trackDescriptorPtr->PMSF.startPosition.seconds ) * 75 +
1342									trackDescriptorPtr->PMSF.startPosition.frames;
1343
1344				DebugLog ( ( "track = %d, blockAddress = %d\n", pointValue, ( int ) blockAddress ) );
1345
1346				startBlock = CFNumberCreate ( 	kCFAllocatorDefault,
1347												kCFNumberSInt32Type,
1348												&blockAddress );
1349
1350				CFDictionarySetValue ( 	theTrackRef,
1351										CFSTR ( kStartBlockString ),
1352										startBlock );
1353
1354				CFRelease ( startBlock );
1355
1356				sessionNumber = CFNumberCreate ( 	kCFAllocatorDefault,
1357													kCFNumberCharType,
1358													&trackDescriptorPtr->sessionNumber );
1359
1360				CFDictionarySetValue ( 	theTrackRef,
1361										CFSTR ( kSessionNumberString ),
1362										sessionNumber );
1363
1364				CFRelease ( sessionNumber );
1365
1366				if ( ( trackDescriptorPtr->control & kDigitalDataMask ) == kDigitalDataMask )
1367				{
1368
1369					isDigitalData = kCFBooleanTrue;
1370
1371				}
1372
1373				CFDictionarySetValue ( 	theTrackRef,
1374										CFSTR ( kDataString ),
1375										isDigitalData );
1376
1377				if ( ( trackDescriptorPtr->control & kPreEmphasisMask ) == kPreEmphasisMask )
1378				{
1379
1380					preEmphasis = kCFBooleanTrue;
1381
1382				}
1383
1384				CFDictionarySetValue ( theTrackRef,
1385									   CFSTR ( kPreEmphasisString ),
1386									   preEmphasis );
1387
1388				// Add the dictionary to the array
1389				CFArraySetValueAtIndex ( theTrackArrayRef, trackIndex, theTrackRef );
1390
1391				CFRelease ( theTrackRef );
1392				trackIndex++;
1393
1394
1395nextIteration:
1396
1397
1398				// Advance to next track
1399				trackDescriptorPtr++;
1400
1401			}
1402
1403			// Set the array inside of the dictionary for the session
1404			CFDictionarySetValue ( theSessionDictionaryRef, CFSTR ( kTrackArrayString ), theTrackArrayRef );
1405			CFArraySetValueAtIndex ( theSessionArrayRef, index, theSessionDictionaryRef );
1406
1407			CFRelease ( theSessionDictionaryRef );
1408			CFRelease ( theTrackArrayRef );
1409
1410		}
1411
1412		CFDictionarySetValue ( theCDDictionaryRef, CFSTR ( kSessionsString ), theSessionArrayRef );
1413
1414		CFRelease ( theSessionArrayRef );
1415
1416		xmlData = CFPropertyListCreateXMLData ( kCFAllocatorDefault, theCDDictionaryRef );
1417
1418		CFRelease ( theCDDictionaryRef );
1419
1420	}
1421
1422	return xmlData;
1423
1424}
1425
1426
1427#if 0
1428#pragma mark -
1429#pragma mark - Shared Code
1430#pragma mark -
1431#endif
1432
1433
1434//-----------------------------------------------------------------------------
1435//	DisplayUsage -	This routine will do a printf of the correct usage
1436//					for whichever utility was launched.
1437//-----------------------------------------------------------------------------
1438
1439void
1440DisplayUsage ( int usageType, const char * argv[] )
1441{
1442
1443	if ( usageType == kUsageTypeMount )
1444	{
1445
1446		printf ( "usage: mount_cddafs [-o options] device-name mount-point\n" );
1447
1448	}
1449
1450	else if ( usageType == kUsageTypeUtility )
1451	{
1452
1453		printf ( "usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv[0] );
1454		printf ( "action_arg:\n" );
1455		printf ( "       -%c (Probe for mounting)\n", FSUC_PROBE );
1456		printf ( "       -%c (Mount)\n", FSUC_MOUNT );
1457		printf ( "       -%c (Unmount)\n", FSUC_UNMOUNT );
1458		printf ( "       -%c (Force Mount)\n", FSUC_MOUNT_FORCE );
1459		printf ( "device_arg:\n" );
1460		printf ( "       device we are acting upon (for example, 'sd2')\n" );
1461		printf ( "mount_point_arg:\n" );
1462		printf ( "       required for Mount and Force Mount \n" );
1463		printf ( "Flags:\n" );
1464		printf ( "       required for Mount, Force Mount and Probe\n" );
1465		printf ( "       indicates removable or fixed (for example 'fixed')\n" );
1466		printf ( "       indicates readonly or writable (for example 'readonly')\n" );
1467		printf ( "Examples:\n");
1468		printf ( "       %s -p sd2 fixed writable\n", argv[0] );
1469		printf ( "       %s -m sd2 /my/hfs removable readonly\n", argv[0] );
1470
1471	}
1472
1473	return;
1474
1475}
1476
1477
1478//-----------------------------------------------------------------------------
1479//	GetTOCDataPtr -	Gets a pointer to the TOCData
1480//
1481//	deviceNamePtr - pointer to the device name (full path, like /dev/rdisk1)
1482//-----------------------------------------------------------------------------
1483
1484UInt8 *
1485GetTOCDataPtr ( const char * deviceNamePtr )
1486{
1487
1488	UInt8 *					ptr 			= NULL;
1489	IOReturn				error			= 0;
1490	io_iterator_t			iterator		= MACH_PORT_NULL;
1491	io_registry_entry_t		registryEntry	= MACH_PORT_NULL;
1492	CFMutableDictionaryRef	properties 		= 0;
1493	CFDataRef     			data			= 0;
1494	char *					bsdName 		= NULL;
1495
1496	if ( !strncmp ( deviceNamePtr, "/dev/r", 6 ) )
1497	{
1498
1499		// Strip off the /dev/ from /dev/rdiskX
1500		bsdName = ( char * ) &deviceNamePtr[6];
1501
1502	}
1503
1504	else if ( !strncmp ( deviceNamePtr, "/dev/", 5 ) )
1505	{
1506
1507		// Strip off the /dev/ from /dev/diskX
1508		bsdName = ( char * ) &deviceNamePtr[5];
1509
1510	}
1511
1512	else
1513	{
1514
1515		DebugLog ( ( "GetTOCDataPtr: ERROR: not /dev/something...\n" ) );
1516		goto Exit;
1517
1518	}
1519
1520	error = IOServiceGetMatchingServices ( 	kIOMasterPortDefault,
1521											IOBSDNameMatching ( kIOMasterPortDefault, 0, bsdName ),
1522											&iterator );
1523	require ( ( error == kIOReturnSuccess ), Exit );
1524
1525	// Only expect one entry since there is a 1:1 correspondence between bsd names
1526	// and IOKit storage objects
1527	registryEntry = IOIteratorNext ( iterator );
1528	require ( ( registryEntry != MACH_PORT_NULL ), ReleaseIterator );
1529
1530	require ( IOObjectConformsTo ( registryEntry, kIOCDMediaString ), ReleaseEntry );
1531
1532	error = IORegistryEntryCreateCFProperties ( registryEntry,
1533												&properties,
1534												kCFAllocatorDefault,
1535												kNilOptions );
1536	require ( ( error == kIOReturnSuccess ), ReleaseEntry );
1537
1538	// Get the TOCInfo
1539	data = ( CFDataRef ) CFDictionaryGetValue ( properties,
1540												CFSTR ( kIOCDMediaTOCKey ) );
1541	if ( data != NULL )
1542	{
1543
1544		ptr = CreateBufferFromCFData ( data );
1545
1546	}
1547
1548	// Release the properties
1549	CFRelease ( properties );
1550
1551
1552ReleaseEntry:
1553
1554
1555	// release the object
1556	error = IOObjectRelease ( registryEntry );
1557
1558
1559ReleaseIterator:
1560
1561
1562	// relese the iterator
1563	error = IOObjectRelease ( iterator );
1564
1565
1566Exit:
1567
1568
1569	return ptr;
1570
1571}
1572
1573
1574//-----------------------------------------------------------------------------
1575// IsAudioTrack - Figures out if a track is audio or not.
1576//-----------------------------------------------------------------------------
1577
1578Boolean
1579IsAudioTrack ( UInt32 trackNumber, QTOCDataFormat10Ptr TOCData )
1580{
1581
1582	SubQTOCInfoPtr	trackDescriptorPtr;
1583
1584	trackDescriptorPtr = TOCData->trackDescriptors;
1585	trackDescriptorPtr = trackDescriptorPtr + trackNumber;
1586
1587	if ( trackDescriptorPtr->point < 100 && trackDescriptorPtr->point > 0 )
1588	{
1589
1590		if ( ( trackDescriptorPtr->control & kDigitalDataMask ) == 0 )
1591		{
1592
1593			// Found an audio track
1594			return true;
1595
1596		}
1597
1598	}
1599
1600	return false;
1601
1602}
1603
1604
1605//-----------------------------------------------------------------------------
1606// GetNumberOfTrackDescriptors - Gets the number of track descriptors
1607//-----------------------------------------------------------------------------
1608
1609SInt32
1610GetNumberOfTrackDescriptors ( 	QTOCDataFormat10Ptr	TOCDataPtr,
1611								UInt8 * 			numberOfDescriptors )
1612{
1613
1614	UInt16	length	= 0;
1615	SInt32	result	= 0;
1616
1617	require_action ( ( TOCDataPtr != NULL ), Exit, result = -1 );
1618	require_action ( ( numberOfDescriptors != NULL ), Exit, result = -1 );
1619
1620	// Grab the length and advance
1621	length = OSSwapBigToHostInt16 ( TOCDataPtr->TOCDataLength );
1622	DebugLog ( ( "Length = %d\n", length ) );
1623
1624	require_action ( ( length > sizeof ( CDTOC ) ), Exit, result = -1 );
1625
1626	length -= ( sizeof ( TOCDataPtr->firstSessionNumber ) +
1627				sizeof ( TOCDataPtr->lastSessionNumber ) );
1628
1629	*numberOfDescriptors = length / ( sizeof ( SubQTOCInfo ) );
1630	DebugLog ( ( "Number of descriptors = %d\n", *numberOfDescriptors ) );
1631
1632
1633Exit:
1634
1635
1636	return result;
1637
1638}
1639
1640
1641//-----------------------------------------------------------------------------
1642// GetPointValue - Gets the track's point value
1643//-----------------------------------------------------------------------------
1644
1645UInt8
1646GetPointValue ( UInt32 trackIndex, QTOCDataFormat10Ptr TOCData )
1647{
1648
1649	SubQTOCInfoPtr	trackDescriptorPtr;
1650
1651	trackDescriptorPtr = TOCData->trackDescriptors;
1652	trackDescriptorPtr = trackDescriptorPtr + trackIndex;
1653
1654	return trackDescriptorPtr->point;
1655
1656}
1657
1658
1659//-----------------------------------------------------------------------------
1660//	CreateBufferFromCFData - Allocates memory for a chunk of memory and copies
1661//							 the contents of the CFData to it.
1662//
1663//	NB:	The calling function should dispose of the memory
1664//-----------------------------------------------------------------------------
1665
1666UInt8 *
1667CreateBufferFromCFData ( CFDataRef theData )
1668{
1669
1670	CFRange				range;
1671	CFIndex          	bufferLength 	= 0;
1672	UInt8 *           	buffer			= NULL;
1673
1674	bufferLength	= CFDataGetLength ( theData );
1675	buffer			= ( UInt8 * ) malloc ( bufferLength );
1676
1677	range = CFRangeMake ( 0, bufferLength );
1678
1679	if ( buffer != NULL )
1680		CFDataGetBytes ( theData, range, buffer );
1681
1682	return buffer;
1683
1684}
1685
1686
1687//-----------------------------------------------------------------------------
1688//	FindNumberOfAudioTracks - 	Parses the TOC to find the number of audio
1689//								tracks.
1690//-----------------------------------------------------------------------------
1691
1692UInt32
1693FindNumberOfAudioTracks ( QTOCDataFormat10Ptr TOCDataPtr )
1694{
1695
1696	UInt32					result				= 0;
1697	SubQTOCInfoPtr			trackDescriptorPtr	= NULL;
1698	UInt16					length				= 0;
1699	UInt16					numberOfDescriptors = 0;
1700
1701	DebugLog ( ( "FindNumberOfAudioTracks called\n" ) );
1702
1703	require ( ( TOCDataPtr != NULL ), Exit );
1704
1705	// Grab the length and advance
1706	length = OSSwapBigToHostInt16 ( TOCDataPtr->TOCDataLength );
1707	require ( ( length > sizeof ( CDTOC ) ), Exit );
1708
1709	length -= ( sizeof ( TOCDataPtr->firstSessionNumber ) +
1710				sizeof ( TOCDataPtr->lastSessionNumber ) );
1711
1712	numberOfDescriptors = length / ( sizeof ( SubQTOCInfo ) );
1713	require ( ( numberOfDescriptors != 0 ), Exit );
1714
1715	DebugLog ( ( "numberOfDescriptors = %d\n", numberOfDescriptors ) );
1716
1717	trackDescriptorPtr = TOCDataPtr->trackDescriptors;
1718
1719	while ( numberOfDescriptors > 0 )
1720	{
1721
1722		if ( trackDescriptorPtr->point < 100 && trackDescriptorPtr->point > 0 )
1723		{
1724
1725			if ( ( trackDescriptorPtr->control & kDigitalDataMask ) == 0 )
1726			{
1727
1728				// Found an audio track
1729				result++;
1730
1731			}
1732
1733		}
1734
1735		trackDescriptorPtr++;
1736		numberOfDescriptors--;
1737
1738	}
1739
1740
1741Exit:
1742
1743
1744	DebugLog ( ( "numberOfTracks = %d\n", ( int ) result ) );
1745
1746	return result;
1747
1748}
1749
1750
1751//-----------------------------------------------------------------------------
1752//				End				Of			File
1753//-----------------------------------------------------------------------------
1754