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// AppleCDDAFileSystemUtils.c created by CJS on Sun 14-May-2000
25
26#ifndef __APPLE_CDDA_FS_UTILS_H__
27#include "AppleCDDAFileSystemUtils.h"
28#endif
29
30#ifndef __APPLE_CDDA_FS_DEBUG_H__
31#include "AppleCDDAFileSystemDebug.h"
32#endif
33
34#ifndef __APPLE_CDDA_FS_DEFINES_H__
35#include "AppleCDDAFileSystemDefines.h"
36#endif
37
38#ifndef __AIFF_SUPPORT_H__
39#include "AIFFSupport.h"
40#endif
41
42#ifndef __APPLE_CDDA_FS_VFS_OPS_H__
43#include "AppleCDDAFileSystemVFSOps.h"
44#endif
45
46#include <sys/param.h>
47#include <sys/systm.h>
48#include <sys/kernel.h>
49#include <sys/stat.h>
50#include <sys/proc.h>
51#include <sys/mount.h>
52#include <sys/vnode.h>
53#include <sys/namei.h>
54#include <sys/malloc.h>
55#include <sys/attr.h>
56#include <sys/time.h>
57#include <sys/ubc.h>
58#include <sys/unistd.h>
59
60
61//-----------------------------------------------------------------------------
62//	Static Function Prototypes
63//-----------------------------------------------------------------------------
64
65static int 		BuildTrackName 					( mount_t mountPtr, AppleCDDANodeInfoPtr nodeInfoPtr );
66static UInt32	CalculateNumberOfDescriptors 	( const QTOCDataFormat10Ptr TOCDataPtr );
67static UInt32	CalculateLBA 					( SubQTOCInfoPtr trackDescriptorPtr );
68static int		FindName						( mount_t mountPtr, UInt8 trackNumber, char ** name, UInt8 * nameSize );
69
70
71//-----------------------------------------------------------------------------
72//	CreateNewCDDANode -	This routine is responsible for creating new nodes
73//-----------------------------------------------------------------------------
74
75errno_t
76CreateNewCDDANode ( mount_t 				mountPtr,
77					UInt32 					nodeID,
78					enum vtype 				vNodeType,
79					vnode_t					parentVNodePtr,
80					struct componentname *	compNamePtr,
81					vnode_t * 				vNodeHandle )
82{
83
84	errno_t 				result			= 0;
85	AppleCDDANodePtr		cddaNodePtr		= NULL;
86	vnode_t 				vNodePtr		= NULLVP;
87	struct vnode_fsparam	vfsp;
88
89	DebugAssert ( ( mountPtr != NULL ) );
90	DebugAssert ( ( vNodeHandle != NULL ) );
91
92	// Allocate the cddaNode
93	MALLOC ( cddaNodePtr, AppleCDDANodePtr, sizeof ( AppleCDDANode ), M_TEMP, M_WAITOK );
94
95	// Zero the structure
96	bzero ( cddaNodePtr, sizeof ( AppleCDDANode ) );
97
98	// Set the nodeID
99	cddaNodePtr->nodeID = nodeID;
100
101	// Zero the FS param structure
102	bzero ( &vfsp, sizeof ( vfsp ) );
103
104	vfsp.vnfs_mp		= mountPtr;
105	vfsp.vnfs_vtype 	= vNodeType;
106	vfsp.vnfs_str 		= "cddafs";
107	vfsp.vnfs_dvp 		= parentVNodePtr;
108	vfsp.vnfs_fsnode 	= cddaNodePtr;
109	vfsp.vnfs_cnp 		= compNamePtr;
110	vfsp.vnfs_vops 		= gCDDA_VNodeOp_p;
111	vfsp.vnfs_rdev 		= 0;
112
113#if DEBUG
114	if ( compNamePtr != NULL )
115	{
116
117		DebugLog ( ( "compNamePtr->cn_flags = 0x%08x\n", ( int ) compNamePtr->cn_flags ) );
118		DebugLog ( ( "compNamePtr->cn_nameiop = 0x%08x\n", ( int ) compNamePtr->cn_nameiop ) );
119		DebugLog ( ( "compNamePtr->cn_pnbuf = %s\n", compNamePtr->cn_pnbuf ) );
120		DebugLog ( ( "compNamePtr->cn_nameptr = %s\n", compNamePtr->cn_nameptr ) );
121		DebugLog ( ( "compNamePtr->cn_namelen = %ld\n", compNamePtr->cn_namelen ) );
122		DebugLog ( ( "compNamePtr->cn_hash = 0x%08x\n", ( int ) compNamePtr->cn_hash ) );
123		DebugLog ( ( "compNamePtr->cn_consume = %ld\n\n", compNamePtr->cn_consume ) );
124
125	}
126#endif /* DEBUG */
127
128	if ( ( parentVNodePtr != NULL ) && ( compNamePtr != NULL ) && ( compNamePtr->cn_flags & MAKEENTRY ) )
129		vfsp.vnfs_flags = 0;
130	else
131		vfsp.vnfs_flags = VNFS_NOCACHE;
132
133	vfsp.vnfs_markroot 	 = ( nodeID == kAppleCDDARootFileID );
134	vfsp.vnfs_marksystem = 0;
135
136	// Note that vnode_create ( ) returns the vnode with an iocount of +1;
137	// this routine returns the newly created vnode with this positive iocount.
138	result = vnode_create ( VNCREATE_FLAVOR, ( uint32_t ) VCREATESIZE, &vfsp, &vNodePtr );
139	if ( result != 0 )
140	{
141
142		DebugLog ( ( "getnewvnode failed with error code %d\n", result ) );
143		goto FREE_CDDA_NODE_ERROR;
144
145	}
146
147	// Link the cddaNode to the vnode
148	cddaNodePtr->vNodePtr = vNodePtr;
149
150	vnode_addfsref ( vNodePtr );
151
152	// Return the vnode to the caller
153	*vNodeHandle = vNodePtr;
154
155	return result;
156
157
158FREE_CDDA_NODE_ERROR:
159
160
161	// Free the allocated memory
162	FREE ( ( caddr_t ) cddaNodePtr, M_TEMP );
163	cddaNodePtr = NULL;
164
165	return result;
166
167}
168
169
170//-----------------------------------------------------------------------------
171//	DisposeCDDANode -	This routine is responsible for cleaning up cdda nodes
172//-----------------------------------------------------------------------------
173
174int
175DisposeCDDANode ( vnode_t vNodePtr )
176{
177
178	AppleCDDANodePtr	cddaNodePtr = NULL;
179
180	DebugAssert ( ( vNodePtr != NULL ) );
181
182	cddaNodePtr = VTOCDDA ( vNodePtr );
183
184	DebugAssert ( ( cddaNodePtr != NULL ) );
185
186	if ( cddaNodePtr != NULL )
187	{
188
189		// Free memory associated with our filesystem's internal data
190		FREE ( vnode_fsnode ( vNodePtr ), M_TEMP );
191		vnode_clearfsnode ( vNodePtr );
192
193	}
194
195	return ( 0 );
196
197}
198
199
200//-----------------------------------------------------------------------------
201//	CreateNewCDDAFile -	This routine is responsible for creating new
202//						files
203//-----------------------------------------------------------------------------
204
205errno_t
206CreateNewCDDAFile ( mount_t 				mountPtr,
207					UInt32 					nodeID,
208					AppleCDDANodeInfoPtr 	nodeInfoPtr,
209					vnode_t					parentVNodePtr,
210					struct componentname *	compNamePtr,
211					vnode_t * 				vNodeHandle )
212{
213
214	errno_t					result				= 0;
215	vnode_t 				vNodePtr			= NULLVP;
216	AppleCDDANodePtr		cddaNodePtr			= NULL;
217	AppleCDDANodePtr		parentCDDANodePtr	= NULL;
218	AppleCDDAMountPtr		cddaMountPtr		= NULL;
219	struct componentname	cn;
220
221	bzero ( &cn, sizeof ( cn ) );
222
223	DebugAssert ( ( mountPtr != NULL ) );
224	DebugAssert ( ( nodeInfoPtr != NULL ) );
225	DebugAssert ( ( vNodeHandle != NULL ) );
226
227	cddaMountPtr		= VFSTOCDDA ( mountPtr );
228	parentCDDANodePtr	= VTOCDDA ( cddaMountPtr->root );
229
230	DebugAssert ( ( cddaMountPtr != NULL ) );
231	DebugAssert ( ( parentCDDANodePtr != NULL ) );
232
233	if ( parentVNodePtr == NULL )
234	{
235
236		DebugLog ( ( "CreateNewCDDAFile called with NULL parentVNodePtr\n" ) );
237		parentVNodePtr = cddaMountPtr->root;
238
239	}
240
241	if ( compNamePtr == NULL )
242	{
243
244		DebugLog ( ( "CreateNewCDDAFile called with NULL compNamePtr\n" ) );
245
246		MALLOC ( cn.cn_pnbuf, caddr_t, MAXPATHLEN, M_TEMP, M_WAITOK );
247
248		cn.cn_nameiop	= LOOKUP;
249		cn.cn_flags		= ISLASTCN | MAKEENTRY;
250		cn.cn_pnlen		= MAXPATHLEN;
251		cn.cn_nameptr	= cn.cn_pnbuf;
252		cn.cn_namelen	= nodeInfoPtr->nameSize;
253		cn.cn_hash		= 0;
254		cn.cn_consume	= 0;
255
256		bcopy ( nodeInfoPtr->name, cn.cn_nameptr, nodeInfoPtr->nameSize + 1 );
257
258		compNamePtr = &cn;
259
260	}
261
262	result = CreateNewCDDANode ( mountPtr, nodeID, VREG, parentVNodePtr, compNamePtr, &vNodePtr );
263	if ( result != 0 )
264	{
265
266		DebugLog ( ( "Error = %d returned from CreatNewCDDANode\n", result ) );
267		return result;
268
269	}
270
271	if ( cn.cn_pnbuf != NULL )
272	{
273
274		DebugLog ( ( "CreateNewCDDAFile: freeing cn_pnbuf\n" ) );
275		FREE ( cn.cn_pnbuf, M_TEMP );
276
277	}
278
279	cddaNodePtr = VTOCDDA ( vNodePtr );
280
281	DebugAssert ( ( cddaNodePtr != NULL ) );
282
283	// Build the header.
284	BuildCDAIFFHeader ( &cddaNodePtr->u.file.aiffHeader, nodeInfoPtr->numBytes );
285
286	// Fill in the miscellaneous fields for the cddaNode
287	cddaNodePtr->nodeType 				= kAppleCDDATrackType;
288	cddaNodePtr->blockDeviceVNodePtr	= parentCDDANodePtr->blockDeviceVNodePtr;
289
290	// Set the back pointer
291	cddaNodePtr->u.file.nodeInfoPtr	= nodeInfoPtr;
292
293	DebugLog ( ( "LBA of %d = %ld.\n", cddaNodePtr->nodeID, nodeInfoPtr->LBA ) );
294
295	// stuff the vNode in
296	*vNodeHandle = vNodePtr;
297
298	return 0;
299
300}
301
302
303//-----------------------------------------------------------------------------
304//	CreateNewXMLFile -	This routine is responsible for creating the ".TOC.plist"
305//						file which has XML data describing the CD layout.
306//-----------------------------------------------------------------------------
307
308errno_t
309CreateNewXMLFile ( 	mount_t 				mountPtr,
310					UInt32 					xmlFileSize,
311					UInt8 * 				xmlData,
312					vnode_t					parentVNodePtr,
313					struct componentname *	compNamePtr,
314					vnode_t * 				vNodeHandle )
315{
316
317	errno_t					result				= 0;
318	vnode_t 				vNodePtr			= NULLVP;
319	AppleCDDANodePtr		cddaNodePtr			= NULL;
320	AppleCDDANodePtr		parentCDDANodePtr	= NULL;
321	AppleCDDAMountPtr		cddaMountPtr		= NULL;
322	struct componentname	cn;
323
324	bzero ( &cn, sizeof ( cn ) );
325
326	DebugAssert ( ( mountPtr != NULL ) );
327	DebugAssert ( ( vNodeHandle != NULL ) );
328
329	cddaMountPtr		= VFSTOCDDA ( mountPtr );
330	parentCDDANodePtr	= VTOCDDA ( cddaMountPtr->root );
331
332	DebugAssert ( ( cddaMountPtr != NULL ) );
333	DebugAssert ( ( parentCDDANodePtr != NULL ) );
334
335	if ( parentVNodePtr == NULL )
336	{
337
338		DebugLog ( ( "CreateNewXMLFile called with NULL parentVNodePtr\n" ) );
339		parentVNodePtr = cddaMountPtr->root;
340
341	}
342
343	if ( compNamePtr == NULL )
344	{
345
346		DebugLog ( ( "CreateNewXMLFile called with NULL compNamePtr\n" ) );
347
348		MALLOC ( cn.cn_pnbuf, caddr_t, MAXPATHLEN, M_TEMP, M_WAITOK );
349
350		cn.cn_nameiop	= LOOKUP;
351		cn.cn_flags		= ISLASTCN | MAKEENTRY;
352		cn.cn_pnlen		= MAXPATHLEN;
353		cn.cn_nameptr	= cn.cn_pnbuf;
354		cn.cn_namelen	= ( uint32_t ) strlen ( ".TOC.plist" );
355		cn.cn_hash		= 0;
356		cn.cn_consume	= 0;
357
358		snprintf ( cn.cn_nameptr, MAXPATHLEN, "%s", ".TOC.plist" );
359
360		compNamePtr = &cn;
361
362	}
363
364	result = CreateNewCDDANode ( mountPtr, kAppleCDDAXMLFileID, VREG, parentVNodePtr, compNamePtr, &vNodePtr );
365	if ( result != 0 )
366	{
367
368		DebugLog ( ( "Error = %d returned from CreatNewCDDANode\n", result ) );
369		return result;
370
371	}
372
373	if ( cn.cn_pnbuf != NULL )
374	{
375
376		DebugLog ( ( "CreateNewXMLFile: freeing cn_pnbuf\n" ) );
377		FREE ( cn.cn_pnbuf, M_TEMP );
378
379	}
380
381	cddaNodePtr = VTOCDDA ( vNodePtr );
382
383	DebugAssert ( ( cddaNodePtr != NULL ) );
384
385	// Fill in the miscellaneous fields for the cddaNode
386	cddaNodePtr->nodeType 				= kAppleCDDAXMLFileType;
387	cddaNodePtr->blockDeviceVNodePtr	= parentCDDANodePtr->blockDeviceVNodePtr;
388
389	// Point the xmlData to the correct place
390	cddaNodePtr->u.xmlFile.fileDataPtr 	= xmlData;
391	cddaNodePtr->u.xmlFile.fileSize 	= xmlFileSize;
392
393	#if 0
394	{
395		UInt32	count;
396		// Let's see if we got the right data mapped in
397		for ( count = 0; count < xmlFileSize; count = count + 8 )
398		{
399
400			DebugLog ( ( "%x:%x:%x:%x %x:%x:%x:%x\n",
401						xmlData[count],
402						xmlData[count+1],
403						xmlData[count+2],
404						xmlData[count+3],
405						xmlData[count+4],
406						xmlData[count+5],
407						xmlData[count+6],
408						xmlData[count+7] ) );
409
410		}
411
412		DebugLog ( ( "\n" ) );
413
414	}
415	#endif
416
417	// stuff the vNode in
418	*vNodeHandle = vNodePtr;
419
420	return 0;
421
422}
423
424
425//-----------------------------------------------------------------------------
426//	CreateNewCDDADirectory -	This routine is responsible for creating new
427//								directories (i.e. the root directory)
428//-----------------------------------------------------------------------------
429
430errno_t
431CreateNewCDDADirectory ( mount_t 		mountPtr,
432						 UInt32 		nodeID,
433						 vnode_t * 		vNodeHandle )
434{
435
436	errno_t					result			= 0;
437	vnode_t					vNodePtr		= NULLVP;
438	AppleCDDANodePtr		cddaNodePtr		= NULL;
439
440	DebugAssert ( ( mountPtr != NULL ) );
441	DebugAssert ( ( vNodeHandle != NULL ) );
442
443	result = CreateNewCDDANode ( mountPtr, nodeID, VDIR, NULL, NULL, &vNodePtr );
444	if ( result != 0 )
445	{
446
447		DebugLog ( ( "Error = %d returned from CreatNewCDDANode\n", result ) );
448		return result;
449
450	}
451
452	cddaNodePtr = VTOCDDA ( vNodePtr );
453	DebugAssert ( ( cddaNodePtr != NULL ) );
454
455	// Set up the directory-specific fields
456	cddaNodePtr->nodeType 					= kAppleCDDADirectoryType;
457	cddaNodePtr->u.directory.directorySize	= 0;
458	cddaNodePtr->u.directory.entryCount 	= kNumberOfFakeDirEntries; 		// ".", "..", and ".TOC.plist"
459
460	// stuff the vNode in
461	*vNodeHandle = vNodePtr;
462
463	return 0;
464
465}
466
467
468//-----------------------------------------------------------------------------
469//	IsAudioTrack -	Checks the arguments passed in to find out if specified
470//					track is audio or not
471//-----------------------------------------------------------------------------
472
473boolean_t
474IsAudioTrack ( const SubQTOCInfoPtr trackDescriptorPtr )
475{
476
477	DebugAssert ( ( trackDescriptorPtr != NULL ) );
478
479	// Check to make sure the point is between 1 and 99 (inclusive)
480	if ( trackDescriptorPtr->point < 100 && trackDescriptorPtr->point > 0 )
481	{
482
483		// Do we have digital data?
484		if ( ( trackDescriptorPtr->control & kDigitalDataMask ) == 0 )
485		{
486
487			// Found an audio track
488			return TRUE;
489
490		}
491
492	}
493
494	return FALSE;
495
496}
497
498
499//-----------------------------------------------------------------------------
500//	CalculateSize -	Calculate the file size based on number of frames
501//					(i.e. blocks) in the track
502//-----------------------------------------------------------------------------
503
504UInt32
505CalculateSize ( const QTOCDataFormat10Ptr 	TOCDataPtr,
506				UInt32 						trackDescriptorOffset,
507				UInt32 						currentA2Offset )
508{
509
510	UInt32				size					= 0;
511	UInt32				offset					= 0;
512	UInt32				numberOfDescriptors		= 0;
513	UInt32				nextOffset				= 0;
514	SubQTOCInfoPtr		trackDescriptorPtr		= NULL;
515	SubQTOCInfoPtr		nextTrackDescriptorPtr	= NULL;
516
517	DebugLog ( ( "CalculateSize: Entering...\n" ) );
518
519	DebugAssert ( ( TOCDataPtr != NULL ) );
520
521	// Find the number of descriptors
522	numberOfDescriptors = CalculateNumberOfDescriptors ( TOCDataPtr );
523
524	// Get the correct track descriptor
525	trackDescriptorPtr = &TOCDataPtr->trackDescriptors[trackDescriptorOffset];
526
527	// Are we past the total number of descriptors in TOC?
528	if ( trackDescriptorOffset + 1 >= numberOfDescriptors )
529	{
530
531		// yes, so set the descriptor to the last leadout descriptor we hit
532		nextTrackDescriptorPtr = &TOCDataPtr->trackDescriptors[currentA2Offset];
533
534	}
535
536	else
537	{
538
539		// no, so set the descriptor to the next entry in the TOC
540		nextTrackDescriptorPtr = &TOCDataPtr->trackDescriptors[trackDescriptorOffset + 1];
541
542		// Are we past the end of the session?
543		if ( trackDescriptorPtr->sessionNumber != nextTrackDescriptorPtr->sessionNumber ||
544			 !IsAudioTrack ( nextTrackDescriptorPtr ) )
545		{
546
547			// yes, so set the descriptor to the last leadout descriptor we hit
548			nextTrackDescriptorPtr = &TOCDataPtr->trackDescriptors[currentA2Offset];
549
550		}
551
552	}
553
554	// Calculate the LBAs for both tracks
555	offset 		= CalculateLBA ( trackDescriptorPtr );
556	nextOffset 	= CalculateLBA ( nextTrackDescriptorPtr );
557
558	// Multiply number of blocks by block size and add the header + pad (1 block)
559	size = ( ( nextOffset - offset ) * kPhysicalMediaBlockSize ) + kPhysicalMediaBlockSize;
560
561	DebugLog ( ( "CalculateSize: size = %ld.\n", size ) );
562
563	DebugLog ( ( "CalculateSize: exiting...\n" ) );
564
565	return size;
566
567}
568
569
570//-----------------------------------------------------------------------------
571//	FindName - 	Parses the names data that gets passed in to the
572//				filesystem, looking for the specified track's name.
573//				All names look like the following packed structure:
574//
575//	| 	1 byte 		| 		1 byte 		| 	number of bytes in 2nd byte		|
576//		Track #	 		size of String			String for Name
577//
578//	Track # of zero corresponds to the album name (the mount point name).
579//-----------------------------------------------------------------------------
580
581
582int
583FindName ( mount_t mountPtr, UInt8 trackNumber, char ** name, UInt8 * nameSize )
584{
585
586	AppleCDDAMountPtr		cddaMountPtr		= NULL;
587	UInt8 *					ptr					= NULL;
588	UInt8					length				= 0;
589
590	DebugLog ( ( "FindName: entering\n" ) );
591	DebugLog ( ( "trackNumber = %d\n" ) );
592
593	DebugAssert ( ( mountPtr != NULL ) );
594	DebugAssert ( ( name != NULL ) );
595	DebugAssert ( ( nameSize != NULL ) );
596
597	cddaMountPtr = VFSTOCDDA ( mountPtr );
598	DebugLog ( ( "cddaMountPtr->nameDataSize = %ld\n", cddaMountPtr->nameDataSize ) );
599
600	ptr = cddaMountPtr->nameData;
601
602	if ( ptr == NULL )
603	{
604
605		DebugLog ( ( "cddaMountPtr->nameData is NULL" ) );
606		return ENOENT;
607
608	}
609
610	do
611	{
612
613		DebugLog ( ( "*ptr = %d\n", *ptr ) );
614
615		if ( *ptr == trackNumber )
616		{
617
618			char	mylocalname[512];
619
620			DebugLog ( ( "Found track = %d\n", trackNumber ) );
621
622			*nameSize 	= ptr[1];
623			*name 		= ( char * ) &ptr[2];
624
625			bcopy ( &ptr[2], mylocalname, *nameSize );
626			mylocalname[*nameSize] = 0;
627
628			DebugLog ( ( "NameSize = %d\n", *nameSize ) );
629			DebugLog ( ( "Name = %s\n", mylocalname ) );
630
631			break;
632
633		}
634
635		else
636		{
637
638			length = ptr[1];
639
640			DebugLog ( ( "Didn't find it, keep looking\n" ) );
641			ptr = &ptr[length + 2];
642
643		}
644
645	} while ( ptr < ( cddaMountPtr->nameData + cddaMountPtr->nameDataSize ) );
646
647	DebugLog ( ( "FindName: exiting\n" ) );
648
649	return 0;
650
651}
652
653
654//-----------------------------------------------------------------------------
655//	ParseTOC - 	Parses the TOC to find audio tracks. It figures out which
656//				tracks are audio and what their offsets are and fills in the
657//				cddaNode structures associated with each vnode
658//-----------------------------------------------------------------------------
659
660SInt32
661ParseTOC ( mount_t 	mountPtr,
662		   UInt32 	numTracks )
663{
664
665	QTOCDataFormat10Ptr			TOCDataPtr			= NULL;
666	SubQTOCInfoPtr				trackDescriptorPtr	= NULL;
667	AppleCDDAMountPtr			cddaMountPtr		= NULL;
668	AppleCDDANodeInfoPtr		nodeInfoPtr			= NULL;
669	AppleCDDADirectoryNodePtr	rootDirNodePtr		= NULL;
670	OSStatus					error				= 0;
671	UInt16						numberOfDescriptors = 0;
672	UInt32						currentA2Offset		= 0;
673	UInt32						currentOffset		= 0;
674
675	DebugLog ( ( "ParseTOC: Entering...\n" ) );
676
677	DebugAssert ( ( mountPtr != NULL ) );
678
679	cddaMountPtr 	= VFSTOCDDA ( mountPtr );
680	rootDirNodePtr	= &( ( VTOCDDA ( cddaMountPtr->root ) )->u.directory );
681
682	DebugAssert ( ( cddaMountPtr != NULL ) );
683	DebugAssert ( ( rootDirNodePtr != NULL ) );
684
685	// Get the data from the registry entry
686	TOCDataPtr = CreateBufferFromIORegistry ( mountPtr );
687
688	if ( TOCDataPtr != NULL )
689	{
690
691		// calculate number of track descriptors
692		numberOfDescriptors = CalculateNumberOfDescriptors ( TOCDataPtr );
693		DebugLog ( ( "Number of descriptors = %d\n", numberOfDescriptors ) );
694
695		if ( numberOfDescriptors <= 0 )
696		{
697
698			// This is bad...no track descriptors, time to bail
699			error = EINVAL;
700			goto Exit;
701
702		}
703
704		trackDescriptorPtr = TOCDataPtr->trackDescriptors;
705
706		while ( numberOfDescriptors > 0 && rootDirNodePtr->entryCount < ( numTracks + kNumberOfFakeDirEntries ) )
707		{
708
709			if ( trackDescriptorPtr->point == 0xA2 )
710			{
711
712				// Set the a2 offset when we find an a2 point
713				currentA2Offset = currentOffset;
714
715			}
716
717			// Is this an audio track?
718			if ( IsAudioTrack ( trackDescriptorPtr ) )
719			{
720
721				// Make this easier to read by getting a pointer to the nodeInfo
722				nodeInfoPtr = &cddaMountPtr->nodeInfoArrayPtr[rootDirNodePtr->entryCount - kNumberOfFakeDirEntries];
723
724				// Copy this trackDescriptor into the AppleCDDANodeInfo array
725				nodeInfoPtr->trackDescriptor = *trackDescriptorPtr;
726
727				// Get the LogicalBlockAddress and number of bytes in the track
728				nodeInfoPtr->LBA 		= CalculateLBA ( trackDescriptorPtr );
729				nodeInfoPtr->numBytes 	= CalculateSize ( TOCDataPtr, currentOffset, currentA2Offset );
730
731				// Add this node's size to the root directory's directorySize field
732				rootDirNodePtr->directorySize += nodeInfoPtr->numBytes;
733
734				// Increment the number of audio tracks
735				rootDirNodePtr->entryCount++;
736
737				( void ) BuildTrackName ( mountPtr, nodeInfoPtr );
738
739				DebugLog ( ( "LBA of %d = %ld.\n", trackDescriptorPtr->point, nodeInfoPtr->LBA ) );
740
741			}
742
743			// Advance the pointers and decrement the count
744			trackDescriptorPtr++;
745			numberOfDescriptors--;
746			currentOffset++;
747
748		}
749
750		if ( ( numberOfDescriptors != 0 ) && ( rootDirNodePtr->entryCount == ( numTracks + kNumberOfFakeDirEntries )  ) )
751		{
752
753			// Oops...the parsing routine in userland must've screwed up
754			DebugLog ( ( "ParseTOC: userland utility sent wrong number of audio tracks in at mount time.\n" ) );
755
756		}
757
758	}
759
760	else
761	{
762
763		// Couldn't parse the TOC, so return an error
764		return ENOMEM;
765
766	}
767
768
769Exit:
770
771
772	if ( TOCDataPtr != NULL )
773	{
774
775		// Free the buffer allocated earlier
776		DisposeBufferFromIORegistry ( TOCDataPtr );
777
778	}
779
780	DebugLog ( ( "ParseTOC: exiting...\n" ) );
781
782	return error;
783
784}
785
786
787//-----------------------------------------------------------------------------
788//	BuildTrackName -	This routine is responsible for building a track
789//						name based on its number
790//-----------------------------------------------------------------------------
791
792int
793BuildTrackName ( mount_t mountPtr, AppleCDDANodeInfoPtr nodeInfoPtr )
794{
795
796	UInt8		trackNumber	= 0;
797	char *		name		= NULL;
798	UInt8		nameSize	= 0;
799	int			error		= 0;
800
801	DebugLog ( ( "BuildTrackName: entering.\n" ) );
802
803	DebugAssert ( ( nodeInfoPtr != NULL ) );
804
805	// Get the track number for which to find the name
806	trackNumber = nodeInfoPtr->trackDescriptor.point;
807
808	// Find the name
809	error = FindName ( mountPtr, trackNumber, &name, &nameSize );
810	if ( error != 0 )
811	{
812
813		DebugLog ( ( "cddafs : FindName returned error = %d\n", error ) );
814		// Buffer copied in was formatted incorrectly. We'll just use
815		// track numbers on this CD.
816
817	}
818
819	// Set the size of the name
820	nodeInfoPtr->nameSize = nameSize;
821
822	// If we got here, then we have a valid track and the nodeInfoArrayPtr points to our
823	// offset into the array. So, MALLOC the name here
824	MALLOC ( nodeInfoPtr->name, char *, nodeInfoPtr->nameSize + 1, M_TEMP, M_WAITOK );
825
826	// Copy the name
827	bcopy ( name, &nodeInfoPtr->name[0], nameSize );
828
829	// Don't forget NULL byte
830	nodeInfoPtr->name[nameSize] = 0;
831
832	DebugLog ( ( "BuildTrackName: fileName = %s\n", nodeInfoPtr->name ) );
833
834	return 0;
835
836}
837
838
839//-----------------------------------------------------------------------------
840//	CalculateNumberOfDescriptors -	Calculate the number of SubQTOCInfo entries
841//									in the given TOC
842//-----------------------------------------------------------------------------
843
844UInt32
845CalculateNumberOfDescriptors ( const QTOCDataFormat10Ptr TOCDataPtr )
846{
847
848	UInt32		numberOfDescriptors = 0;
849	UInt32		length				= 0;
850
851	DebugLog ( ( "CalculateNumberOfDescriptors: Entering...\n" ) );
852
853	DebugAssert ( ( TOCDataPtr != NULL ) );
854
855	// Get the length of the TOC
856	length = OSSwapBigToHostInt16 ( TOCDataPtr->TOCDataLength );
857
858	// Remove the first and last session numbers so all we are left with are track descriptors
859	length -= ( UInt32 ) ( ( sizeof ( TOCDataPtr->firstSessionNumber ) + sizeof ( TOCDataPtr->lastSessionNumber ) ) );
860
861	// Divide the length by the size of a single track descriptor to get total number
862	numberOfDescriptors =  ( length / ( ( UInt32 ) sizeof ( SubQTOCInfo ) ) );
863
864	DebugLog ( ( "CalculateNumberOfDescriptors: exiting...\n" ) );
865
866	return numberOfDescriptors;
867
868}
869
870
871//-----------------------------------------------------------------------------
872//	CalculateLBA -	Convert the frames offset of the file (from the TOC) to
873//					the LBA of the device
874//
875// NB: this is a workaround because the first 2 seconds of a CD are unreadable
876// and defined as off-limits. So we convert our absolute MSF to an actual
877// logical block which can be addressed through the BSD layer.
878//-----------------------------------------------------------------------------
879
880UInt32
881CalculateLBA ( SubQTOCInfoPtr trackDescriptorPtr )
882{
883
884	UInt32	frames = 0;
885
886	DebugAssert ( ( trackDescriptorPtr != NULL ) );
887
888	frames = ( ( ( trackDescriptorPtr->PMSF.startPosition.minutes * kSecondsPerMinute ) +
889	  		   trackDescriptorPtr->PMSF.startPosition.seconds ) * kFramesPerSecond ) +
890	  		   trackDescriptorPtr->PMSF.startPosition.frames;
891
892	if ( frames < kMSFToLBA )
893		frames = kMSFToLBA;
894
895	// Simply convert MSF to LBA
896	return frames - kMSFToLBA;
897
898}
899
900
901//-----------------------------------------------------------------------------
902//				End				Of			File
903//-----------------------------------------------------------------------------
904