1/*
2 * Copyright (c) 2000-2009 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// AppleCDDAFileSystemVNodeOps.c created by CJS on Mon 10-Apr-2000
25
26// Project Includes
27#ifndef __APPLE_CDDA_FS_VNODE_OPS_H__
28#include "AppleCDDAFileSystemVNodeOps.h"
29#endif
30
31#ifndef __APPLE_CDDA_FS_DEBUG_H__
32#include "AppleCDDAFileSystemDebug.h"
33#endif
34
35#ifndef __APPLE_CDDA_FS_DEFINES_H__
36#include "AppleCDDAFileSystemDefines.h"
37#endif
38
39#ifndef __APPLE_CDDA_FS_UTILS_H__
40#include "AppleCDDAFileSystemUtils.h"
41#endif
42
43#ifndef __APPLE_CDDA_FS_VFS_OPS_H__
44#include "AppleCDDAFileSystemVFSOps.h"
45#endif
46
47// System Includes
48#include <sys/param.h>
49#include <sys/systm.h>
50#include <sys/types.h>
51#include <sys/time.h>
52#include <sys/proc.h>
53#include <sys/buf.h>
54#include <sys/vnode.h>
55#include <sys/dirent.h>
56#include <sys/stat.h>
57#include <sys/mount.h>
58#include <sys/malloc.h>
59#include <sys/paths.h>
60#include <sys/errno.h>
61#include <sys/uio.h>
62#include <sys/ubc.h>
63#include <sys/xattr.h>
64#include <vfs/vfs_support.h>
65#include <string.h>
66#include <libkern/OSByteOrder.h>
67#include <IOKit/IOLib.h>
68
69
70//-----------------------------------------------------------------------------
71//	Globals
72//-----------------------------------------------------------------------------
73
74const char gAIFFHeaderPadData[kPhysicalMediaBlockSize - sizeof(CDAIFFHeader)] = { 0 };
75
76//-----------------------------------------------------------------------------
77//	Static Function Prototypes
78//-----------------------------------------------------------------------------
79
80static SInt32
81AddDirectoryEntry ( UInt32 nodeID, UInt8 type, const char * name, uio_t uio );
82
83static inline uint64_t
84__u64min(uint64_t a, uint64_t b)
85{
86	return (a > b ? b : a);
87}
88
89//-----------------------------------------------------------------------------
90//	AddDirectoryEntry - This routine adds a directory entry to the uio buffer
91//-----------------------------------------------------------------------------
92
93static SInt32
94AddDirectoryEntry ( UInt32			nodeID,
95					UInt8			type,
96					const char *	name,
97					uio_t			uio )
98{
99
100	struct dirent		directoryEntry;
101	SInt32				nameLength				= 0;
102	UInt16				directoryEntryLength	= 0;
103
104	DebugAssert ( ( name != NULL ) );
105	DebugAssert ( ( uio != NULL ) );
106
107	DebugLog ( ( "fileName = %s\n", name ) );
108
109	nameLength = ( SInt32 ) strlen ( name );
110	DebugAssert ( ( nameLength < MAXNAMLEN + 1 ) );
111
112	directoryEntry.d_fileno = nodeID;
113	directoryEntry.d_reclen = sizeof ( directoryEntry );
114	directoryEntry.d_type	= type;
115	directoryEntry.d_namlen = nameLength;
116	directoryEntryLength	= directoryEntry.d_reclen;
117
118	// Copy the string
119	strncpy ( directoryEntry.d_name, name, MAXNAMLEN );
120
121	// Zero the rest of the array for safe-keeping
122	bzero ( &directoryEntry.d_name[nameLength], MAXNAMLEN + 1 - nameLength );
123
124	if ( uio_resid ( uio ) < directoryEntry.d_reclen )
125	{
126
127		// We can't copy because there isn't enough room in the buffer,
128		// so set the directoryEntryLength to zero so the caller knows
129		// an error occurred
130		directoryEntryLength = 0;
131
132	}
133
134	else
135	{
136
137		// Move the data
138		uiomove ( ( caddr_t ) &directoryEntry, ( int ) sizeof ( directoryEntry ), uio );
139
140	}
141
142	return directoryEntryLength;
143
144}
145
146
147//-----------------------------------------------------------------------------
148//	CDDA_Lookup -	This routine performs a lookup
149//-----------------------------------------------------------------------------
150
151int
152CDDA_Lookup ( struct vnop_lookup_args * lookupArgsPtr )
153/*
154struct vnop_lookup_args {
155	struct vnodeop_desc *a_desc;
156	vnode_t a_dvp;
157	vnode_t *a_vpp;
158	struct componentname *a_cnp;
159	vfs_context_t a_context;
160};
161*/
162{
163
164	struct mount *				mountPtr			= NULL;
165	struct componentname *		compNamePtr			= NULL;
166	vnode_t *					vNodeHandle			= NULL;
167	vnode_t						parentVNodePtr		= NULLVP;
168	AppleCDDANodePtr			parentCDDANodePtr	= NULL;
169	AppleCDDAMountPtr			cddaMountPtr		= NULL;
170	int							error				= 0;
171	int							flags				= 0;
172
173	DebugLog ( ( "CDDA_Lookup: Entering.\n" ) );
174
175	DebugAssert ( ( lookupArgsPtr != NULL ) );
176
177	compNamePtr		= lookupArgsPtr->a_cnp;
178	vNodeHandle		= lookupArgsPtr->a_vpp;
179	parentVNodePtr	= lookupArgsPtr->a_dvp;
180	mountPtr		= vnode_mount ( parentVNodePtr );
181
182	DebugAssert ( ( compNamePtr != NULL ) );
183	DebugAssert ( ( vNodeHandle != NULL ) );
184	DebugAssert ( ( parentVNodePtr != NULL ) );
185
186	parentCDDANodePtr	= VTOCDDA ( parentVNodePtr );
187	cddaMountPtr		= VFSTOCDDA ( mountPtr );
188
189	DebugAssert ( ( parentCDDANodePtr != NULL ) );
190	DebugAssert ( ( cddaMountPtr != NULL ) );
191
192	*vNodeHandle	= NULL;
193	flags			= compNamePtr->cn_flags;
194
195	if ( compNamePtr->cn_namelen > NAME_MAX )
196	{
197
198		error = ENAMETOOLONG;
199		goto Exit;
200
201	}
202
203	// Check if process wants to create, delete or rename anything
204	if ( compNamePtr->cn_nameiop == CREATE ||
205		 compNamePtr->cn_nameiop == RENAME ||
206		 compNamePtr->cn_nameiop == DELETE )
207	{
208
209		DebugLog ( ( "Can't CREATE, RENAME or DELETE %s, returning EROFS\n", compNamePtr->cn_nameptr ) );
210		error = EROFS;
211		goto Exit;
212
213	}
214
215	// Determine if we're looking for a resource fork.
216	// NB: this could cause a read off the end of the component name buffer in some rare cases.
217	if ( ( flags & ISLASTCN ) == 0 && bcmp ( &compNamePtr->cn_nameptr[compNamePtr->cn_namelen],
218											 _PATH_RSRCFORKSPEC,
219											 sizeof ( _PATH_RSRCFORKSPEC ) - 1 ) == 0 )
220	{
221
222		DebugLog ( ( "No resource forks available, return ENOTDIR.\n" ) );
223		compNamePtr->cn_consume = ( uint32_t ) sizeof ( _PATH_RSRCFORKSPEC ) - 1;
224		error = ENOTDIR;
225		goto Exit;
226
227	}
228
229	DebugLog ( ( "Looking for name = %s.\n", compNamePtr->cn_nameptr ) );
230
231	// first check for "." and ".TOC.plist"
232	if ( compNamePtr->cn_nameptr[0] == '.' )
233	{
234
235		if ( compNamePtr->cn_namelen == 1 )
236		{
237
238			DebugLog ( ( ". was requested\n" ) );
239
240			error = CDDA_VGetInternal ( mountPtr, kAppleCDDARootFileID, parentVNodePtr, compNamePtr, vNodeHandle );
241			goto Exit;
242
243		}
244
245		else if ( ( compNamePtr->cn_namelen == 10 ) && ( !strncmp ( &compNamePtr->cn_nameptr[1], "TOC.plist", 9 ) ) )
246		{
247
248			DebugLog ( ( ".TOC.plist was requested\n" ) );
249
250			error = CDDA_VGetInternal ( mountPtr, kAppleCDDAXMLFileID, parentVNodePtr, compNamePtr, vNodeHandle );
251			goto Exit;
252
253		}
254
255		else
256		{
257
258			// Not going to find anything prefixed with "." other than the above.
259			error = ENOENT;
260			goto Exit;
261
262		}
263
264	}
265
266	// At this point, we better be fetching a file which ends in ".aiff".
267	if ( strncmp ( &compNamePtr->cn_nameptr[compNamePtr->cn_namelen - 5], ".aiff", 5 ) != 0 )
268	{
269
270		error = ENOENT;
271		goto Exit;
272
273	}
274
275	// Find out which inode they want. The first two bytes will tell us the track number which
276	// we can convert to an inode number by adding the kOffsetForFiles constant. Beware lame string
277	// parsing ahead...
278	{
279
280		ino64_t inode = 0;
281
282		if ( compNamePtr->cn_nameptr[1] == ' ' )
283		{
284
285			// It's asking about track 1-9.
286			inode = ( ino64_t ) ( compNamePtr->cn_nameptr[0] - '0' );
287
288		}
289
290		else if ( compNamePtr->cn_nameptr[2] == ' ' )
291		{
292
293			// It's asking about track 10-99.
294			inode = ( ino64_t ) ( ( ( compNamePtr->cn_nameptr[0] - '0' ) * 10 ) + ( compNamePtr->cn_nameptr[1] - '0' ) );
295
296		}
297
298		DebugLog ( ( "Track %lld was requested\n", inode ) );
299
300		// Add the offset for a CD Track...
301		inode += kOffsetForFiles;
302
303		// Call the internal vget routine. Make sure to pass the parentVNode and compNamePtr so they
304		// can be passed to the CreateXXX routines if a vnode needs to be created.
305		error = CDDA_VGetInternal ( mountPtr, inode, parentVNodePtr, compNamePtr, vNodeHandle );
306		goto Exit;
307
308	}
309
310
311Exit:
312
313
314	return ( error );
315
316}
317
318
319//-----------------------------------------------------------------------------
320//	CDDA_Open - This routine opens a file
321//-----------------------------------------------------------------------------
322
323int
324CDDA_Open ( struct vnop_open_args * openArgsPtr )
325/*
326struct vnop_open_args {
327	struct vnodeop_desc *a_desc;
328	vnode_t a_vp;
329	int a_mode;
330	vfs_context_t a_context;
331};
332*/
333{
334
335	vnode_t		vNodePtr	= NULLVP;
336	int			error		= 0;
337
338	DebugLog ( ( "CDDA_Open: Entering.\n" ) );
339
340	DebugAssert ( ( openArgsPtr != NULL ) );
341
342	vNodePtr = openArgsPtr->a_vp;
343	DebugAssert ( ( vNodePtr != NULL ) );
344
345	// Set the vNodeOperationType to tell the user process if we are going to open a
346	// file or a directory
347	if ( ! vnode_isreg ( vNodePtr ) && ! vnode_isdir ( vNodePtr ) )
348	{
349
350		// This should never happen but just in case
351		DebugLog ( ( "Error = %d, wrong vnode type.\n", ENOTSUP ) );
352		error = ENOTSUP;
353		goto ERROR;
354
355	}
356
357	// Turn off speculative read-ahead for our vnodes. The cluster
358	// code can't possibly do the right thing when we have possible
359	// loss of streaming on CD media.
360	vnode_setnoreadahead ( vNodePtr );
361
362
363ERROR:
364
365
366	DebugLog ( ( "CDDA_Open: exiting with error = %d.\n", error ) );
367
368	return ( error );
369
370}
371
372
373//-----------------------------------------------------------------------------
374//	CDDA_Close -	This routine closes a file. Since we are a read-only
375//					filesystem, we don't have any cleaning up to do.
376//-----------------------------------------------------------------------------
377
378int
379CDDA_Close ( struct vnop_close_args * closeArgsPtr )
380/*
381struct vnop_close_args {
382	struct vnodeop_desc *a_desc;
383	vnode_t a_vp;
384	int a_fflag;
385	vfs_context_t a_context;
386};
387*/
388{
389#pragma unused (closeArgsPtr)
390	return ( 0 );
391}
392
393
394//-----------------------------------------------------------------------------
395//	CDDA_Read - This routine reads from a file
396//-----------------------------------------------------------------------------
397
398int
399CDDA_Read ( struct vnop_read_args * readArgsPtr )
400/*
401struct vnop_read_args {
402	struct vnodeop_desc *a_desc;
403	vnode_t a_vp;
404	uio_t a_uio;
405	int a_ioflag;
406	vfs_context_t a_context;
407};
408*/
409{
410
411	vnode_t				vNodePtr		= NULLVP;
412	uio_t				uio				= NULL;
413	AppleCDDANodePtr	cddaNodePtr		= NULL;
414	int					error			= 0;
415
416	DebugLog ( ( "CDDA_Read: Entering.\n" ) );
417
418	DebugAssert ( ( readArgsPtr ) );
419
420	vNodePtr	= readArgsPtr->a_vp;
421	uio			= readArgsPtr->a_uio;
422
423	DebugAssert ( ( vNodePtr != NULL ) );
424	DebugAssert ( ( uio != NULL ) );
425
426	cddaNodePtr = VTOCDDA ( vNodePtr );
427	DebugAssert ( ( cddaNodePtr != NULL ) );
428
429	// Check to make sure we're operating on a regular file
430	if ( ! vnode_isreg ( vNodePtr ) )
431	{
432
433		DebugLog ( ( "CDDA_Read: not a file, exiting with error = %d.\n", EISDIR ) );
434		return ( EISDIR );
435
436	}
437
438	// Check to make sure they asked for data
439	if ( uio_resid ( uio ) == 0 )
440	{
441
442		DebugLog ( ( "CDDA_Read: uio_resid = 0, no data requested" ) );
443		return ( 0 );
444
445	}
446
447	// Can't read from a negative offset
448	if ( uio_offset ( uio ) < 0 )
449	{
450
451		DebugLog ( ( "CDDA_Read: Can't read from a negative offset..." ) );
452		return ( EINVAL );
453
454	}
455
456	if ( cddaNodePtr->nodeType == kAppleCDDAXMLFileType )
457	{
458
459		off_t		offset			= uio_offset ( uio );
460		UInt32		amountToCopy	= 0;
461		UInt32		numBytes		= 0;
462
463		numBytes = cddaNodePtr->u.xmlFile.fileSize;
464
465		// Check to make sure we don't read past EOF
466		if ( uio_offset ( uio ) > numBytes )
467		{
468
469			DebugLog ( ( "CDDA_Read: Can't read past end of file..." ) );
470			return ( 0 );
471
472		}
473
474		amountToCopy = ( UInt32 ) __u64min ( uio_resid ( uio ), numBytes - offset );
475
476		error = uiomove ( ( caddr_t ) &cddaNodePtr->u.xmlFile.fileDataPtr[offset],
477				  amountToCopy,
478				  uio );
479
480		if ( error != 0 )
481		{
482
483			DebugLog ( ( "CDDA_Read: uiomove returned %d!\n", error ) );
484			return ( error );
485
486		}
487
488		return ( 0 );
489
490	}
491
492	else if ( cddaNodePtr->nodeType == kAppleCDDATrackType )
493	{
494
495		UInt32			headerSize		= 0;
496		UInt32			count			= 0;
497		UInt64			blockNumber		= 0;
498		off_t			offset			= 0;
499		off_t			sectorOffset	= 0;
500		buf_t			bufPtr			= NULL;
501
502		offset	= uio_offset ( uio );
503
504		// Check to make sure we don't read past EOF
505		if ( offset > cddaNodePtr->u.file.nodeInfoPtr->numBytes )
506		{
507
508			DebugLog ( ( "CDDA_Read: Can't read past end of file..." ) );
509			return ( 0 );
510
511		}
512
513		headerSize = ( UInt32 ) sizeof ( cddaNodePtr->u.file.aiffHeader );
514
515		// Copy any part of the header that we need to copy.
516		if ( offset < headerSize )
517		{
518
519			UInt32	amountToCopy = 0;
520			UInt8 *	bytes		 = NULL;
521
522			bytes = ( UInt8 * ) &cddaNodePtr->u.file.aiffHeader;
523
524			amountToCopy = ( UInt32 ) __u64min ( uio_resid ( uio ), headerSize - offset );
525
526			error = uiomove ( ( caddr_t ) &bytes[offset],
527				  amountToCopy,
528				  uio );
529
530			if ( error != 0 )
531			{
532
533				DebugLog ( ( "CDDA_Read: uiomove returned %d!\n", error ) );
534				return ( error );
535
536			}
537
538			offset += amountToCopy;
539
540		}
541
542		// Copy any part of the header pad that we need to copy.
543		if ( ( uio_resid ( uio ) > 0  ) &&
544			 ( offset < kPhysicalMediaBlockSize ) )
545		{
546
547			UInt32	amountToCopy = 0;
548
549			amountToCopy = ( UInt32 ) __u64min ( uio_resid ( uio ), kPhysicalMediaBlockSize - offset );
550
551			error = uiomove ( ( caddr_t ) &gAIFFHeaderPadData[offset - headerSize],
552				  amountToCopy,
553				  uio );
554
555			if ( error != 0 )
556			{
557
558				DebugLog ( ( "CDDA_Read: uiomove returned %d!\n", error ) );
559				return ( error );
560
561			}
562
563			offset += amountToCopy;
564
565		}
566
567		if ( ( uio_resid ( uio ) > 0  ) &&
568			 ( uio_offset ( uio ) < cddaNodePtr->u.file.nodeInfoPtr->numBytes ) )
569		{
570
571			// Adjust offset by the header size so we have a true offset into the media.
572			offset -= kPhysicalMediaBlockSize;
573			sectorOffset = offset % kPhysicalMediaBlockSize;
574			blockNumber = ( offset / kPhysicalMediaBlockSize ) + cddaNodePtr->u.file.nodeInfoPtr->LBA;
575
576			// Part 1
577			// We do the read in 3 parts. First, we read one sector (picks up any buffer cache entry from a
578			// previous Part 3 if it exists).
579			{
580
581				// Clip to requested transfer count and end of file.
582				count = ( UInt32 ) __u64min ( uio_resid ( uio ), ( kPhysicalMediaBlockSize - sectorOffset ) );
583				count = ( UInt32 ) __u64min ( count, cddaNodePtr->u.file.nodeInfoPtr->numBytes - uio_offset ( uio ) );
584
585				// Read the one sector
586				error = ( int ) buf_meta_bread (
587									cddaNodePtr->blockDeviceVNodePtr,
588									blockNumber,
589									kPhysicalMediaBlockSize,
590									NOCRED,
591									&bufPtr );
592
593				if ( error != 0 )
594				{
595
596					buf_brelse ( bufPtr );
597					return ( error );
598
599				}
600
601				// Move the data from the block into the buffer
602				error = uiomove ( ( caddr_t ) ( ( char * ) buf_dataptr ( bufPtr ) + sectorOffset ), count, uio );
603
604				// Make sure we mark this bp invalid as we don't need to keep it around anymore
605				buf_markinvalid ( bufPtr );
606
607				// Release this buffer back into the buffer pool.
608				buf_brelse ( bufPtr );
609
610				if ( error != 0 )
611				{
612
613					DebugLog ( ( "CDDA_Read: uiomove returned %d!\n", error ) );
614					return ( error );
615
616				}
617
618				// Update offset
619				blockNumber++;
620
621			}
622
623			// Part 2
624			// Now we execute the second part of the read. This will be the largest chunk of the read.
625			// We will read multiple disc blocks up to MAXBSIZE bytes in a loop until we hit a chunk which
626			// is less than one block size. That will be read in the third part.
627
628			while ( ( uio_resid ( uio ) > kPhysicalMediaBlockSize ) &&
629					( uio_offset ( uio ) < cddaNodePtr->u.file.nodeInfoPtr->numBytes ) )
630			{
631
632				UInt64		blocksToRead = 0;
633
634				// Read in as close to MAXBSIZE chunks as possible
635				if ( uio_resid ( uio ) > kMaxBytesPerRead )
636				{
637					blocksToRead	= kMaxBlocksPerRead;
638					count			= kMaxBytesPerRead;
639				}
640
641				else
642				{
643					blocksToRead	= uio_resid ( uio ) / kPhysicalMediaBlockSize;
644					count			= ( UInt32 ) ( blocksToRead * kPhysicalMediaBlockSize );
645				}
646
647				// Read kMaxBlocksPerRead blocks and put them in the cache.
648				error = ( int ) buf_meta_bread (
649									cddaNodePtr->blockDeviceVNodePtr,
650									blockNumber,
651									count,
652									NOCRED,
653									&bufPtr );
654
655				if ( error != 0 )
656				{
657
658					buf_brelse ( bufPtr );
659					return ( error );
660
661				}
662
663				count = ( UInt32 ) __u64min ( count, cddaNodePtr->u.file.nodeInfoPtr->numBytes - uio_offset ( uio ) );
664
665				// Move the data from the block into the buffer
666				error = uiomove ( ( caddr_t ) buf_dataptr ( bufPtr ), count, uio );
667
668				// Make sure we mark any intermediate buffers as invalid as we don't need
669				// to keep them.
670				buf_markinvalid ( bufPtr );
671
672				// Release this buffer back into the buffer pool.
673				buf_brelse ( bufPtr );
674
675				if ( error != 0 )
676				{
677
678					DebugLog ( ( "CDDA_Read: uiomove returned %d!\n", error ) );
679					return ( error );
680
681				}
682
683				// Update offset
684				blockNumber += blocksToRead;
685
686			}
687
688			// Part 3
689			// Now that we have read everything, we read the tail end which is a partial sector.
690			// Sometimes we don't need to execute this step since there isn't a tail.
691			if ( ( uio_resid ( uio ) > 0  ) &&
692				 ( uio_offset ( uio ) < cddaNodePtr->u.file.nodeInfoPtr->numBytes ) )
693			{
694
695				count = ( UInt32 ) __u64min ( uio_resid ( uio ), cddaNodePtr->u.file.nodeInfoPtr->numBytes - uio_offset ( uio ) );
696
697				// Read the one sector
698				error = ( int ) buf_meta_bread (
699									cddaNodePtr->blockDeviceVNodePtr,
700									blockNumber,
701									kPhysicalMediaBlockSize,
702									NOCRED,
703									&bufPtr );
704
705				if ( error != 0 )
706				{
707
708					buf_brelse ( bufPtr );
709					return ( error );
710
711				}
712
713				// Move the data from the block into the buffer
714				error = uiomove ( ( caddr_t ) buf_dataptr ( bufPtr ), count, uio );
715
716				// Make sure we mark any intermediate buffers as invalid as we don't need
717				// to keep them.
718				buf_markinvalid ( bufPtr );
719
720				// Release this buffer back into the buffer pool.
721				buf_brelse ( bufPtr );
722
723				if ( error != 0 )
724				{
725
726					DebugLog ( ( "CDDA_Read: uiomove returned %d!\n", error ) );
727					return ( error );
728
729				}
730
731			}
732
733		}
734
735	}
736
737	DebugLog ( ( "CDDA_Read: exiting.\n" ) );
738
739	return ( error );
740
741}
742
743
744//-----------------------------------------------------------------------------
745//	CDDA_ReadDir -	This routine reads the contents of a directory
746//-----------------------------------------------------------------------------
747
748int
749CDDA_ReadDir ( struct vnop_readdir_args * readDirArgsPtr )
750/*
751struct vnop_readdir_args {
752	struct vnodeop_desc *a_desc;
753	vnode_t a_vp;
754	uio_t a_uio;
755	int a_flags;
756	int *a_eofflag;
757	int *a_numdirent;
758	vfs_context_t a_context;
759};
760*/
761{
762
763	vnode_t					vNodePtr			= NULLVP;
764	AppleCDDANodePtr		cddaNodePtr			= NULL;
765	AppleCDDAMountPtr		cddaMountPtr		= NULL;
766	AppleCDDANodeInfoPtr	nodeInfoArrayPtr	= NULL;
767	uio_t					uio					= NULL;
768	UInt32					index				= 0;
769	int						error				= 0;
770	SInt32					offsetValue			= 0;
771	UInt32					direntSize			= 0;
772
773	DebugLog ( ( "CDDA_ReadDir: Entering.\n" ) );
774
775	DebugAssert ( ( readDirArgsPtr != NULL ) );
776
777	vNodePtr	= readDirArgsPtr->a_vp;
778	uio			= readDirArgsPtr->a_uio;
779
780	DebugAssert ( ( vNodePtr != NULL ) );
781	DebugAssert ( ( uio != NULL ) );
782
783	cddaNodePtr = VTOCDDA ( vNodePtr );
784
785	DebugAssert ( ( cddaNodePtr != NULL ) );
786
787	if ( readDirArgsPtr->a_flags & ( VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF ) )
788		return ( EINVAL );
789
790	// First make sure it is a directory we are dealing with
791	if ( ! vnode_isdir ( vNodePtr ) )
792	{
793
794		DebugLog ( ( "CDDA_ReadDir: not a directory, exiting with error = %d.\n", ENOTDIR ) );
795		return ( ENOTDIR );
796
797	}
798
799	if ( cddaNodePtr->nodeID != kAppleCDDARootFileID )
800	{
801
802		DebugLog ( ( "CDDA_ReadDir: not root directory, exiting with error = %d.\n", EINVAL ) );
803		return ( EINVAL );
804
805	}
806
807	// Make sure it's all one big buffer
808	if ( uio_iovcnt ( uio ) > 1 )
809	{
810
811		DebugLog ( ( "More than one buffer, exiting with error = %d.\n", EINVAL ) );
812		return ( EINVAL );
813
814	}
815
816	// Make sure we don't return partial entries
817	if ( ( uint32_t ) uio_resid ( uio ) < sizeof ( struct dirent ) )
818	{
819
820		DebugLog ( ( "resid < dirent size, exiting with error = %d.\n", EINVAL ) );
821		return ( EINVAL );
822
823	}
824
825	direntSize	= ( UInt32 ) sizeof ( struct dirent );
826
827	// Synthesize '.', "..", and ".TOC.plist"
828	if ( uio_offset ( uio ) == 0 )
829	{
830
831		offsetValue = AddDirectoryEntry ( cddaNodePtr->nodeID, DT_DIR, ".", uio );
832		if ( offsetValue == 0 )
833		{
834
835			DebugLog ( ( "offsetValue is zero, exiting with error = %d.\n", 0 ) );
836			return 0;
837
838		}
839
840	}
841
842	if ( uio_offset ( uio ) == direntSize )
843	{
844
845		offsetValue = AddDirectoryEntry ( cddaNodePtr->nodeID, DT_DIR, "..", uio );
846		if ( offsetValue == 0 )
847		{
848
849			DebugLog ( ( "offsetValue is zero, exiting with error = %d.\n", 0 ) );
850			return 0;
851
852		}
853
854	}
855
856	if ( uio_offset ( uio ) == direntSize * kAppleCDDARootFileID )
857	{
858
859		offsetValue += AddDirectoryEntry ( kAppleCDDAXMLFileID, DT_REG, ".TOC.plist", uio );
860		if ( offsetValue == 0 )
861		{
862
863			DebugLog ( ( "offsetValue is zero, exiting with error = %d.\n", 0 ) );
864			return 0;
865
866		}
867
868	}
869
870	nodeInfoArrayPtr	= VFSTONODEINFO ( vnode_mount ( vNodePtr ) );
871	cddaMountPtr		= VFSTOCDDA ( vnode_mount ( vNodePtr ) );
872
873	DebugAssert ( ( nodeInfoArrayPtr != NULL ) );
874	DebugAssert ( ( cddaMountPtr != NULL ) );
875
876	DebugLog ( ( "cddaMountPtr->numTracks = %ld.\n", cddaMountPtr->numTracks ) );
877	DebugLog ( ( "buffer size needed = %ld.\n", direntSize * ( cddaMountPtr->numTracks + kNumberOfFakeDirEntries ) ) );
878
879	// OK, so much for the fakes.  Now for the "real thing"
880	// Loop over all the names in the NameArray to produce directory entries
881	for ( index = 0; index < cddaMountPtr->numTracks; index++, nodeInfoArrayPtr++ )
882	{
883
884		DebugLog ( ( "uio_offset ( uio ) = %ld.\n", uio_offset ( uio ) ) );
885		DebugLog ( ( "uio_resid ( uio ) = %ld.\n", uio_resid ( uio ) ) );
886
887		if ( uio_offset ( uio ) == direntSize * ( index + kNumberOfFakeDirEntries ) )
888		{
889
890			DebugLog ( ( "index = %ld.\n", index ) );
891
892			// Return this entry
893			offsetValue = AddDirectoryEntry ( nodeInfoArrayPtr->trackDescriptor.point,
894											  DT_REG,
895											  nodeInfoArrayPtr->name,
896											  uio );
897
898			if ( offsetValue == 0 )
899			{
900
901				DebugLog ( ( "offsetValue is zero, exiting with error = %d.\n", 0 ) );
902				return 0;
903
904			}
905
906		}
907
908	}
909
910	if ( readDirArgsPtr->a_eofflag )
911	{
912
913		DebugLog ( ( "eofflag = %d.\n", ( uio_offset ( uio ) >= direntSize * ( cddaMountPtr->numTracks + kNumberOfFakeDirEntries ) ) ? 1 : 0 ) );
914
915		// If we ran all the way through the list, there are no more
916		*readDirArgsPtr->a_eofflag = ( uio_offset ( uio ) >= direntSize * ( cddaMountPtr->numTracks + kNumberOfFakeDirEntries ) ) ? 1 : 0;
917		error = 0;
918
919	}
920
921	DebugLog ( ( "CDDA_ReadDir: exiting with error = %d.\n", error ) );
922
923	return ( error );
924
925}
926
927
928//-----------------------------------------------------------------------------
929//	CDDA_PageIn -	This routine handles VM PageIn requests
930//-----------------------------------------------------------------------------
931
932int
933CDDA_PageIn ( struct vnop_pagein_args * pageInArgsPtr )
934/*
935struct vnop_pagein_args {
936	struct vnodeop_desc *a_desc;
937	vnode_t a_vp;
938	upl_t a_pl;
939	vm_offset_t a_pl_offset;
940	off_t a_f_offset;
941	size_t a_size;
942	int a_flags;
943	vfs_context_t a_context;
944};
945*/
946{
947
948	vnode_t				vNodePtr		= NULLVP;
949	AppleCDDANodePtr	cddaNodePtr		= NULL;
950	int					error			= 0;
951	int					nocommit		= 0;
952	UInt32				numBytes		= 0;
953
954	DebugLog ( ( "CDDA_PageIn: Entering.\n" ) );
955
956	DebugAssert ( ( pageInArgsPtr != NULL ) );
957
958	vNodePtr = pageInArgsPtr->a_vp;
959	nocommit = pageInArgsPtr->a_flags & UPL_NOCOMMIT;
960
961	DebugAssert ( ( vNodePtr != NULL ) );
962
963	cddaNodePtr = VTOCDDA ( vNodePtr );
964	DebugAssert ( ( cddaNodePtr != NULL ) );
965
966	if ( cddaNodePtr->nodeType == kAppleCDDAXMLFileType )
967	{
968
969		numBytes = cddaNodePtr->u.xmlFile.fileSize;
970
971	}
972
973	else if ( cddaNodePtr->nodeType == kAppleCDDATrackType )
974	{
975
976		numBytes = cddaNodePtr->u.file.nodeInfoPtr->numBytes;
977
978	}
979
980	// If they didn't ask for any data, then we are done
981	if ( pageInArgsPtr->a_size == 0 )
982	{
983
984		if ( !nocommit )
985		{
986
987			ubc_upl_abort_range ( pageInArgsPtr->a_pl,
988								  pageInArgsPtr->a_pl_offset,
989								  ( upl_size_t ) pageInArgsPtr->a_size,
990								  UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY );
991
992		}
993
994		return ( error );
995
996	}
997
998	// Make sure we aren't reading from a negative offset
999	if ( pageInArgsPtr->a_f_offset < 0 )
1000	{
1001
1002		if ( !nocommit )
1003		{
1004
1005			ubc_upl_abort_range ( pageInArgsPtr->a_pl,
1006								  pageInArgsPtr->a_pl_offset,
1007								  ( upl_size_t ) pageInArgsPtr->a_size,
1008								  UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY );
1009
1010		}
1011
1012		error = EINVAL;
1013		DebugLog ( ( "CDDA_PageIn: trying to page in from a negative offset.\n" ) );
1014
1015		return ( error );
1016
1017	}
1018
1019	// Check to make sure we don't read past EOF
1020	if ( pageInArgsPtr->a_f_offset > numBytes )
1021	{
1022
1023		if ( !nocommit )
1024		{
1025
1026			ubc_upl_abort_range ( pageInArgsPtr->a_pl,
1027								  pageInArgsPtr->a_pl_offset,
1028								  ( upl_size_t ) pageInArgsPtr->a_size,
1029								  UPL_ABORT_ERROR | UPL_ABORT_FREE_ON_EMPTY );
1030
1031		}
1032
1033		return ( error );
1034
1035	}
1036
1037	// Workaround for faked ".TOC.plist" file
1038	if ( cddaNodePtr->nodeType == kAppleCDDAXMLFileType )
1039	{
1040
1041		kern_return_t		kret			= 0;
1042		vm_offset_t			vmOffsetPtr		= 0;
1043		UInt32				amountToCopy	= 0;
1044
1045		// Map the physical page into the kernel address space
1046		kret = ubc_upl_map ( pageInArgsPtr->a_pl, &vmOffsetPtr );
1047
1048		// If we got an error or the vmOffsetPtr is zero, panic for now
1049		if ( kret != KERN_SUCCESS || vmOffsetPtr == 0 )
1050		{
1051
1052			panic ( "CDDA_PageIn: error mapping buffer into kernel space!" );
1053
1054		}
1055
1056		// Zero fill the page
1057		bzero ( ( caddr_t )( vmOffsetPtr + pageInArgsPtr->a_pl_offset ), PAGE_SIZE );
1058
1059		amountToCopy = ( UInt32 ) __u64min ( PAGE_SIZE, numBytes - pageInArgsPtr->a_f_offset );
1060
1061		// Copy the file data
1062		bcopy ( &cddaNodePtr->u.xmlFile.fileDataPtr[pageInArgsPtr->a_f_offset],
1063				( void * ) vmOffsetPtr,
1064				amountToCopy );
1065
1066		// Unmap the physical page from the kernel address space
1067		kret = ubc_upl_unmap ( pageInArgsPtr->a_pl );
1068
1069		// If we got an error, panic for now
1070		if ( kret != KERN_SUCCESS )
1071		{
1072
1073			panic ( "CDDA_PageIn: error unmapping buffer from kernel space!" );
1074
1075		}
1076
1077		if ( !nocommit )
1078		{
1079
1080			// Commit the page to the vm subsystem
1081			ubc_upl_commit_range (	pageInArgsPtr->a_pl,
1082									pageInArgsPtr->a_pl_offset,
1083									PAGE_SIZE,
1084									UPL_COMMIT_FREE_ON_EMPTY | UPL_COMMIT_CLEAR_DIRTY );
1085
1086		}
1087
1088		return 0;
1089
1090	}
1091
1092	else if ( cddaNodePtr->nodeType == kAppleCDDATrackType )
1093	{
1094
1095		UInt32			headerSize		= 0;
1096		UInt64			blockNumber		= 0;
1097		UInt32			count			= 0;
1098		off_t			offset			= 0;
1099		off_t			sectorOffset	= 0;
1100		off_t			residual		= 0;
1101		kern_return_t	kret			= 0;
1102		vm_offset_t		vmOffsetPtr		= 0;
1103		buf_t			bufPtr			= NULL;
1104
1105		residual	= pageInArgsPtr->a_size;
1106		offset		= pageInArgsPtr->a_f_offset;
1107
1108		// Check to make sure we don't read past EOF
1109		if ( offset > cddaNodePtr->u.file.nodeInfoPtr->numBytes )
1110		{
1111
1112			DebugLog ( ( "CDDA_PageIn: Can't read past end of file..." ) );
1113			return ( 0 );
1114
1115		}
1116
1117		headerSize = ( UInt32 ) sizeof ( cddaNodePtr->u.file.aiffHeader );
1118
1119		// Map the physical pages into the kernel address space
1120		kret = ubc_upl_map ( pageInArgsPtr->a_pl, &vmOffsetPtr );
1121
1122		// If we got an error or the vmOffsetPtr is zero, panic for now
1123		if ( kret != KERN_SUCCESS || vmOffsetPtr == 0 )
1124		{
1125
1126			panic ( "CDDA_PageIn: error mapping buffer into kernel space!" );
1127
1128		}
1129
1130		// Account for the offset into the UPL.
1131		vmOffsetPtr += pageInArgsPtr->a_pl_offset;
1132
1133		// Copy any part of the header that we need to copy.
1134		if ( offset < headerSize )
1135		{
1136
1137			UInt32		amountToCopy	= 0;
1138			UInt8 *		bytes			= NULL;
1139
1140			amountToCopy = ( UInt32 ) __u64min ( pageInArgsPtr->a_size, headerSize - offset );
1141
1142			bytes = ( UInt8 * ) &cddaNodePtr->u.file.aiffHeader;
1143
1144			// Copy the header data
1145			bcopy ( &bytes[offset],
1146					( void * ) vmOffsetPtr,
1147					amountToCopy );
1148
1149			offset += amountToCopy;
1150			residual -= amountToCopy;
1151			vmOffsetPtr += amountToCopy;
1152
1153		}
1154
1155		// Copy any part of the header pad that we need to copy.
1156		if ( ( residual > 0 ) &&
1157			 ( offset < kPhysicalMediaBlockSize ) )
1158		{
1159
1160			UInt32	amountToCopy = 0;
1161
1162			amountToCopy = ( UInt32 ) __u64min ( residual, kPhysicalMediaBlockSize - offset );
1163
1164			// Copy the header pad data (all zeroes).
1165			bcopy ( &gAIFFHeaderPadData[offset - headerSize],
1166					( void * ) vmOffsetPtr,
1167					amountToCopy );
1168
1169			offset += amountToCopy;
1170			residual -= amountToCopy;
1171			vmOffsetPtr += amountToCopy;
1172
1173		}
1174
1175		if ( ( residual > 0 ) &&
1176			 ( offset < cddaNodePtr->u.file.nodeInfoPtr->numBytes ) )
1177		{
1178
1179			// Adjust offset by the size of header + header pad so we have a true offset into the media.
1180			offset -= kPhysicalMediaBlockSize;
1181			sectorOffset = offset % kPhysicalMediaBlockSize;
1182			blockNumber = ( offset / kPhysicalMediaBlockSize ) + cddaNodePtr->u.file.nodeInfoPtr->LBA;
1183
1184			// Part 1
1185			// We do the read in 3 parts. First, we read one sector (picks up any buffer cache entry from a
1186			// previous Part 3 if it exists).
1187			{
1188
1189				// Clip to requested transfer count and end of file.
1190				count = ( UInt32 ) __u64min ( residual, ( kPhysicalMediaBlockSize - sectorOffset ) );
1191				count = ( UInt32 ) __u64min ( count, cddaNodePtr->u.file.nodeInfoPtr->numBytes - offset );
1192
1193				// Read the one sector
1194				error = ( int ) buf_meta_bread (
1195									cddaNodePtr->blockDeviceVNodePtr,
1196									blockNumber,
1197									kPhysicalMediaBlockSize,
1198									NOCRED,
1199									&bufPtr );
1200
1201				if ( error != 0 )
1202				{
1203
1204					buf_brelse ( bufPtr );
1205					return ( error );
1206
1207				}
1208
1209				// Copy the data
1210				bcopy ( ( void * ) ( ( char * ) buf_dataptr ( bufPtr ) + sectorOffset ),
1211						( void * ) vmOffsetPtr,
1212						count );
1213
1214				// Increment/decrement counters
1215				offset		+= count;
1216				residual	-= count;
1217				vmOffsetPtr += count;
1218
1219				// Make sure we mark this bp invalid as we don't need to keep it around anymore
1220				buf_markinvalid ( bufPtr );
1221
1222				// Release this buffer back into the buffer pool.
1223				buf_brelse ( bufPtr );
1224
1225				// Update offset
1226				blockNumber++;
1227
1228			}
1229
1230			// Part 2
1231			// Now we execute the second part of the read. This will be the largest chunk of the read.
1232			// We will read multiple disc blocks up to MAXBSIZE bytes in a loop until we hit a chunk which
1233			// is less than one block size. That will be read in the third part.
1234
1235			while ( ( residual > kPhysicalMediaBlockSize ) &&
1236					( offset < cddaNodePtr->u.file.nodeInfoPtr->numBytes ) )
1237			{
1238
1239				UInt64		blocksToRead = 0;
1240
1241				// Read in as close to MAXBSIZE chunks as possible
1242				if ( residual > kMaxBytesPerRead )
1243				{
1244					blocksToRead	= kMaxBlocksPerRead;
1245					count			= kMaxBytesPerRead;
1246				}
1247
1248				else
1249				{
1250					blocksToRead	= residual / kPhysicalMediaBlockSize;
1251					count			= ( UInt32 ) ( blocksToRead * kPhysicalMediaBlockSize );
1252				}
1253
1254				// read kMaxBlocksPerRead blocks and put them in the cache.
1255				error = ( int ) buf_meta_bread (
1256									cddaNodePtr->blockDeviceVNodePtr,
1257									blockNumber,
1258									count,
1259									NOCRED,
1260									&bufPtr );
1261
1262				if ( error != 0 )
1263				{
1264
1265					buf_brelse ( bufPtr );
1266					return ( error );
1267
1268				}
1269
1270				count = ( UInt32 ) __u64min ( count, cddaNodePtr->u.file.nodeInfoPtr->numBytes - offset );
1271
1272				// Copy the data
1273				bcopy ( ( void * ) buf_dataptr ( bufPtr ), ( void * ) vmOffsetPtr, count );
1274
1275				// Increment/decrement counters
1276				offset		+= count;
1277				residual	-= count;
1278				vmOffsetPtr += count;
1279
1280				// Make sure we mark any intermediate buffers as invalid as we don't need
1281				// to keep them.
1282				buf_markinvalid ( bufPtr );
1283
1284				// Release this buffer back into the buffer pool.
1285				buf_brelse ( bufPtr );
1286
1287				// Update offset
1288				blockNumber += blocksToRead;
1289
1290			}
1291
1292			// Part 3
1293			// Now that we have read everything, we read the tail end which is a partial sector.
1294			// Sometimes we don't need to execute this step since there isn't a tail.
1295			if ( ( residual > 0	 ) &&
1296				 ( offset < cddaNodePtr->u.file.nodeInfoPtr->numBytes ) )
1297			{
1298
1299				count = ( UInt32 ) __u64min ( residual, cddaNodePtr->u.file.nodeInfoPtr->numBytes - offset );
1300
1301				// Read the one sector
1302				error = ( int ) buf_meta_bread (
1303									cddaNodePtr->blockDeviceVNodePtr,
1304									blockNumber,
1305									kPhysicalMediaBlockSize,
1306									NOCRED,
1307									&bufPtr );
1308
1309				if ( error != 0 )
1310				{
1311
1312					buf_brelse ( bufPtr );
1313					return ( error );
1314
1315				}
1316
1317				// Copy the data
1318				bcopy ( ( void * ) buf_dataptr ( bufPtr ), ( void * ) vmOffsetPtr, count );
1319
1320				// Increment/decrement counters
1321				offset		+= count;
1322				residual	-= count;
1323				vmOffsetPtr += count;
1324
1325				// Make sure we mark any intermediate buffers as invalid as we don't need
1326				// to keep them.
1327				buf_markinvalid ( bufPtr );
1328
1329				// Release this buffer back into the buffer pool.
1330				buf_brelse ( bufPtr );
1331
1332			}
1333
1334		}
1335
1336		// Unmap the physical page from the kernel address space
1337		kret = ubc_upl_unmap ( pageInArgsPtr->a_pl );
1338
1339		// If we got an error, panic for now
1340		if ( kret != KERN_SUCCESS )
1341		{
1342
1343			panic ( "CDDA_PageIn: error unmapping buffer from kernel space!" );
1344
1345		}
1346
1347		if ( !nocommit )
1348		{
1349
1350			// Commit the page to the vm subsystem
1351			ubc_upl_commit_range (	pageInArgsPtr->a_pl,
1352									pageInArgsPtr->a_pl_offset,
1353									( upl_size_t ) pageInArgsPtr->a_size,
1354									UPL_COMMIT_FREE_ON_EMPTY | UPL_COMMIT_CLEAR_DIRTY );
1355
1356		}
1357
1358	}
1359
1360	DebugLog ( ( "CDDA_PageIn: exiting...\n" ) );
1361
1362	return ( error );
1363
1364}
1365
1366
1367//-----------------------------------------------------------------------------
1368//	CDDA_GetAttributes - This routine gets the attributes for a folder/file
1369//-----------------------------------------------------------------------------
1370
1371int
1372CDDA_GetAttributes ( struct vnop_getattr_args * getAttrArgsPtr )
1373/*
1374struct vnop_getattr_args {
1375	struct vnodeop_desc *a_desc;
1376	vnode_t a_vp;
1377	struct vnode_attr *a_vap;
1378	vfs_context_t a_context;
1379};
1380*/
1381{
1382
1383	vnode_t					vNodePtr		= NULLVP;
1384	mount_t					mountPtr		= NULL;
1385	struct vnode_attr *		attributesPtr	= NULL;
1386	AppleCDDANodePtr		cddaNodePtr		= NULL;
1387	AppleCDDAMountPtr		cddaMountPtr	= NULL;
1388	struct timespec			nullTime		= { 0, 0 };
1389
1390	DebugLog ( ( "CDDA_GetAttributes: Entering.\n" ) );
1391
1392	DebugAssert ( ( getAttrArgsPtr != NULL ) );
1393
1394	vNodePtr		= getAttrArgsPtr->a_vp;
1395	mountPtr		= vnode_mount ( vNodePtr );
1396	attributesPtr	= getAttrArgsPtr->a_vap;
1397
1398	DebugAssert ( ( vNodePtr != NULL ) );
1399	DebugAssert ( ( attributesPtr != NULL ) );
1400
1401	cddaMountPtr	= VFSTOCDDA ( mountPtr );
1402	cddaNodePtr		= VTOCDDA ( vNodePtr );
1403
1404	DebugAssert ( ( cddaNodePtr != NULL ) );
1405	DebugAssert ( ( cddaMountPtr != NULL ) );
1406
1407	DebugLog ( ( "nodeID = %ld.\n", cddaNodePtr->nodeID ) );
1408
1409	// Special case root since we know how to get its name
1410	if ( cddaNodePtr->nodeType == kAppleCDDADirectoryType )
1411	{
1412
1413		// Set the nodeID. Force root to be 2.
1414		VATTR_RETURN ( attributesPtr, va_fileid, kAppleCDDARootFileID );
1415
1416	}
1417
1418	// Special case the XMLFileNode
1419	else if ( cddaNodePtr->nodeType == kAppleCDDAXMLFileType )
1420	{
1421
1422		// Set the nodeID. Force the XMLFileNode to be 3.
1423		VATTR_RETURN ( attributesPtr, va_fileid, kAppleCDDAXMLFileID );
1424
1425	}
1426
1427	else
1428	{
1429
1430		// Set the nodeID.
1431		VATTR_RETURN ( attributesPtr, va_fileid, cddaNodePtr->nodeID );
1432
1433	}
1434
1435	if ( cddaNodePtr->nodeType == kAppleCDDADirectoryType )
1436	{
1437
1438		// If this is the root directory and they want the parent, force it to 1
1439		VATTR_RETURN ( attributesPtr, va_parentid, 1 );
1440
1441	}
1442
1443	else
1444	{
1445
1446		// Every other object has the root as its parent (flat filesystem)
1447		VATTR_RETURN ( attributesPtr, va_parentid, kAppleCDDARootFileID );
1448
1449	}
1450
1451	VATTR_RETURN ( attributesPtr, va_type, vnode_vtype ( vNodePtr ) );	// Set the VNode type (e.g. VREG, VDIR)
1452	VATTR_RETURN ( attributesPtr, va_iosize, kPhysicalMediaBlockSize );	// Set preferred block size for I/O requests
1453
1454	// Set all the time fields
1455	VATTR_RETURN ( attributesPtr, va_create_time, cddaMountPtr->mountTime );	// Creation time
1456	VATTR_RETURN ( attributesPtr, va_modify_time, cddaMountPtr->mountTime );	// Last modification time
1457	VATTR_RETURN ( attributesPtr, va_change_time, nullTime );					// Last change time
1458	VATTR_RETURN ( attributesPtr, va_access_time, nullTime );					// Last accessed time
1459	VATTR_RETURN ( attributesPtr, va_backup_time, nullTime );					// Backup time
1460
1461	// These fields are the same
1462	VATTR_RETURN ( attributesPtr, va_fsid, vfs_statfs ( mountPtr )->f_fsid.val[0] );
1463	VATTR_RETURN ( attributesPtr, va_uid, kUnknownUserID );		// "unknown"
1464	VATTR_RETURN ( attributesPtr, va_gid, kUnknownGroupID );	// "unknown"
1465	VATTR_RETURN ( attributesPtr, va_filerev, 0 );
1466	VATTR_RETURN ( attributesPtr, va_gen, 0 );
1467	VATTR_RETURN ( attributesPtr, va_flags, 0 );
1468	VATTR_RETURN ( attributesPtr, va_rdev, 0 );
1469	VATTR_RETURN ( attributesPtr, va_encoding, 0 );
1470
1471	// Set some common mode flags.
1472	// Read is ok for user, group, other.
1473	VATTR_RETURN ( attributesPtr, va_mode, S_IRUSR | S_IRGRP | S_IROTH );
1474
1475	// If it's the root, set some flags for it.
1476	if ( vnode_isvroot ( vNodePtr ) )
1477	{
1478
1479		attributesPtr->va_mode		|= S_IFDIR;							// It's a directory
1480		attributesPtr->va_mode		|= S_IXUSR | S_IXGRP | S_IXOTH;		// Execute is ok for user, group, other
1481
1482		// Number of file refs: "." and ".."
1483		VATTR_RETURN ( attributesPtr, va_nlink, kAppleCDDANumberOfRootDirReferences );
1484		VATTR_RETURN ( attributesPtr, va_nchildren, cddaNodePtr->u.directory.entryCount - kAppleCDDANumberOfRootDirReferences );
1485
1486		// Number of Tracks + ".", "..", and ".TOC.plist"
1487		VATTR_RETURN ( attributesPtr, va_data_size, ( cddaNodePtr->u.directory.entryCount ) * sizeof ( struct dirent ) );
1488
1489	}
1490
1491	// If it isn't the root vnode, it's a file.
1492	else
1493	{
1494
1495		// It's a file...
1496		attributesPtr->va_mode |= DEFFILEMODE;
1497
1498		// Just the file itself
1499		VATTR_RETURN ( attributesPtr, va_nlink, kAppleCDDANumberOfFileReferences );
1500
1501		// Is it a track?
1502		if ( cddaNodePtr->nodeType == kAppleCDDATrackType )
1503		{
1504
1505			// Set file size in bytes
1506			VATTR_RETURN ( attributesPtr, va_data_size, cddaNodePtr->u.file.nodeInfoPtr->numBytes );
1507			VATTR_RETURN ( attributesPtr, va_data_alloc, cddaNodePtr->u.file.nodeInfoPtr->numBytes );
1508
1509		}
1510
1511		// Is it the ".TOC.plist" file?
1512		else if ( cddaNodePtr->nodeType == kAppleCDDAXMLFileType )
1513		{
1514
1515			// Set file size in bytes.
1516			VATTR_RETURN ( attributesPtr, va_data_size, cddaNodePtr->u.xmlFile.fileSize );
1517			VATTR_RETURN ( attributesPtr, va_data_alloc, cddaNodePtr->u.xmlFile.fileSize );
1518
1519		}
1520
1521	}
1522
1523	DebugLog ( ( "CDDA_GetAttributes: exiting...\n" ) );
1524
1525	return ( 0 );
1526
1527}
1528
1529
1530//-----------------------------------------------------------------------------
1531//	CDDA_Inactive - This routine simply unlocks a vnode.
1532//-----------------------------------------------------------------------------
1533
1534int
1535CDDA_Inactive ( struct vnop_inactive_args * inactiveArgsPtr )
1536/*
1537struct vnop_inactive_args {
1538	struct vnodeop_desc *a_desc;
1539	vnode_t a_vp;
1540	vfs_context_t a_context;
1541};
1542*/
1543{
1544
1545	DebugLog ( ( "CDDA_Inactive: Entering.\n" ) );
1546
1547	DebugAssert ( ( inactiveArgsPtr != NULL ) );
1548
1549	// We don't do anything special, so call VFS function to handle it
1550	( void ) nop_inactive ( inactiveArgsPtr );
1551
1552	DebugLog ( ( "CDDA_Inactive: exiting...\n" ) );
1553
1554	return ( 0 );
1555
1556}
1557
1558
1559//-----------------------------------------------------------------------------
1560//	CDDA_Remove -	This routine removes a file from the name space. Since we
1561//					are a read-only volume, we release any locks if appropriate
1562//					and return EROFS
1563//-----------------------------------------------------------------------------
1564
1565int
1566CDDA_Remove ( struct vnop_remove_args * removeArgsPtr )
1567/*
1568struct vnop_remove_args {
1569	struct vnodeop_desc *a_desc;
1570	vnode_t a_dvp;
1571	vnode_t a_vp;
1572	struct componentname *a_cnp;
1573	int a_flags;
1574	vfs_context_t a_context;
1575};
1576*/
1577{
1578
1579	DebugLog ( ( "CDDA_Remove: Entering.\n" ) );
1580
1581	DebugAssert ( ( removeArgsPtr != NULL ) );
1582
1583	// We don't do anything special, so call VFS function to handle it
1584	( void ) nop_remove ( removeArgsPtr );
1585
1586	DebugLog ( ( "CDDA_Remove: exiting...\n" ) );
1587
1588	// Return the read-only filesystem error
1589	return ( EROFS );
1590
1591}
1592
1593
1594//-----------------------------------------------------------------------------
1595//	CDDA_RmDir -	This routine removes a directory from the name space.
1596//					Since we are a read-only volume, we release any locks
1597//					and return EROFS
1598//-----------------------------------------------------------------------------
1599
1600int
1601CDDA_RmDir ( struct vnop_rmdir_args * removeDirArgsPtr )
1602/*
1603struct vnop_rmdir_args {
1604	struct vnodeop_desc *a_desc;
1605	vnode_t a_dvp;
1606	vnode_t a_vp;
1607	struct componentname *a_cnp;
1608	vfs_context_t a_context;
1609};
1610*/
1611{
1612
1613	DebugLog ( ( "CDDA_RmDir: Entering.\n" ) );
1614
1615	DebugAssert ( ( removeDirArgsPtr != NULL ) );
1616
1617	// Call nop_rmdir to release locks
1618	( void ) nop_rmdir ( removeDirArgsPtr );
1619
1620	DebugLog ( ( "CDDA_RmDir: exiting...\n" ) );
1621
1622	// Return the read-only filesystem error
1623	return ( EROFS );
1624
1625}
1626
1627
1628//-----------------------------------------------------------------------------
1629//	CDDA_Reclaim - This routine reclaims a vnode for use by the system.
1630//
1631//	Remove the vnode from our node info array, drop the fs reference,
1632//	free our private data.
1633//-----------------------------------------------------------------------------
1634
1635int
1636CDDA_Reclaim ( struct vnop_reclaim_args * reclaimArgsPtr )
1637/*
1638struct vnop_reclaim_args {
1639	struct vnodeop_desc *a_desc;
1640	vnode_t a_vp;
1641	vfs_context_t a_context;
1642};
1643*/
1644{
1645
1646	vnode_t					vNodePtr			= NULLVP;
1647	AppleCDDANodePtr		cddaNodePtr			= NULL;
1648	AppleCDDAMountPtr		cddaMountPtr		= NULL;
1649	AppleCDDANodeInfoPtr	nodeInfoPtr			= NULL;
1650	int						error				= 0;
1651
1652	DebugLog ( ( "CDDA_Reclaim: Entering.\n" ) );
1653
1654	DebugAssert ( ( reclaimArgsPtr != NULL ) );
1655
1656	vNodePtr = reclaimArgsPtr->a_vp;
1657
1658	DebugAssert ( ( vNodePtr != NULL ) );
1659
1660	cddaNodePtr = VTOCDDA ( vNodePtr );
1661
1662	DebugAssert ( ( cddaNodePtr != NULL ) );
1663
1664	cddaMountPtr = VFSTOCDDA ( vnode_mount ( vNodePtr ) );
1665
1666	lck_mtx_lock ( cddaMountPtr->cddaMountLock );
1667
1668	if ( cddaNodePtr->nodeType == kAppleCDDATrackType )
1669	{
1670
1671		nodeInfoPtr = CDDATONODEINFO ( cddaNodePtr );
1672		nodeInfoPtr->vNodePtr = NULL;
1673
1674	}
1675
1676	else if ( cddaNodePtr->nodeType == kAppleCDDAXMLFileType )
1677	{
1678
1679		cddaMountPtr->xmlFileVNodePtr = NULL;
1680
1681	}
1682
1683	lck_mtx_unlock ( cddaMountPtr->cddaMountLock );
1684
1685	// Release our reference on this node
1686	vnode_removefsref ( vNodePtr );
1687
1688	error = DisposeCDDANode ( vNodePtr );
1689
1690	DebugLog ( ( "CDDA_Reclaim: exiting...\n" ) );
1691
1692	return ( error );
1693
1694}
1695
1696
1697
1698//-----------------------------------------------------------------------------
1699//	CDDA_BlockToOffset -	This routine converts logical block number to file
1700//							offset
1701//-----------------------------------------------------------------------------
1702
1703int
1704CDDA_BlockToOffset ( struct vnop_blktooff_args * blockToOffsetArgsPtr )
1705/*
1706struct vnop_blktooff_args {
1707	struct vnodeop_desc *a_desc;
1708	vnode_t a_vp;
1709	daddr64_t a_lblkno;
1710	off_t *a_offset;
1711};
1712*/
1713{
1714
1715	DebugLog ( ( "CDDA_BlockToOffset: Entering.\n" ) );
1716
1717	DebugAssert ( ( blockToOffsetArgsPtr != NULL ) );
1718
1719	if ( blockToOffsetArgsPtr->a_vp == NULL )
1720	{
1721
1722		DebugLog ( ( "CDDA_BlockToOffset: incoming vnode is NULL.\n" ) );
1723		return ( EINVAL );
1724
1725	}
1726
1727	*( blockToOffsetArgsPtr->a_offset ) =
1728			( off_t ) ( blockToOffsetArgsPtr->a_lblkno * PAGE_SIZE );
1729
1730	DebugLog ( ( "CDDA_BlockToOffset: exiting...\n" ) );
1731
1732	return ( 0 );
1733
1734}
1735
1736
1737//-----------------------------------------------------------------------------
1738//	CDDA_OffsetToBlock -	This routine converts a file offset to a logical
1739//							block number
1740//-----------------------------------------------------------------------------
1741
1742int
1743CDDA_OffsetToBlock ( struct vnop_offtoblk_args * offsetToBlockArgsPtr )
1744/*
1745struct vnop_offtoblk_args {
1746	struct vnodeop_desc *a_desc;
1747	vnode_t a_vp;
1748	off_t a_offset;
1749	daddr64_t *a_lblkno;
1750};
1751*/
1752{
1753
1754	DebugLog ( ( "CDDA_OffsetToBlock: Entering.\n" ) );
1755
1756	DebugAssert ( ( offsetToBlockArgsPtr != NULL ) );
1757
1758	if ( offsetToBlockArgsPtr->a_vp == NULL )
1759	{
1760
1761		DebugLog ( ( "CDDA_OffsetToBlock: incoming vnode is NULL.\n" ) );
1762		return ( EINVAL );
1763
1764	}
1765
1766	*( offsetToBlockArgsPtr->a_lblkno ) = ( offsetToBlockArgsPtr->a_offset / PAGE_SIZE );
1767
1768	DebugLog ( ( "CDDA_OffsetToBlock: exiting...\n" ) );
1769
1770	return ( 0 );
1771
1772}
1773
1774
1775//-----------------------------------------------------------------------------
1776//	CDDA_Pathconf - Return POSIX pathconf information applicable to
1777//					special devices
1778//-----------------------------------------------------------------------------
1779
1780int
1781CDDA_Pathconf ( struct vnop_pathconf_args * pathConfArgsPtr )
1782/*
1783struct vnop_pathconf_args {
1784	struct vnodeop_desc *a_desc;
1785	vnode_t a_vp;
1786	int a_name;
1787	register_t *a_retval;
1788	vfs_context_t a_context;
1789};
1790*/
1791{
1792
1793	int returnValue = 0;
1794
1795	DebugLog ( ( "CDDA_Pathconf: Entering.\n" ) );
1796
1797	DebugAssert ( ( pathConfArgsPtr != NULL ) );
1798
1799	switch ( pathConfArgsPtr->a_name )
1800	{
1801
1802		case _PC_LINK_MAX:
1803			*pathConfArgsPtr->a_retval = 1;
1804			break;
1805
1806		case _PC_NAME_MAX:
1807			*pathConfArgsPtr->a_retval = NAME_MAX;
1808			break;
1809
1810		case _PC_PATH_MAX:
1811			*pathConfArgsPtr->a_retval = PATH_MAX;
1812			break;
1813
1814		case _PC_CHOWN_RESTRICTED:
1815			*pathConfArgsPtr->a_retval = 1;
1816			break;
1817
1818		case _PC_NO_TRUNC:
1819			*pathConfArgsPtr->a_retval = 0;
1820			break;
1821
1822		case _PC_XATTR_SIZE_BITS:
1823			*pathConfArgsPtr->a_retval = 0;
1824			break;
1825
1826		default:
1827			returnValue = EINVAL;
1828			break;
1829
1830	}
1831
1832	DebugLog ( ( "CDDA_Pathconf: exiting with returnValue = %d.\n", returnValue ) );
1833
1834	return ( returnValue );
1835
1836}
1837
1838//-----------------------------------------------------------------------------
1839//	CDDA_GetXAttr - Handles extended attribute reads.
1840//					In this case, just for FinderInfo.
1841//-----------------------------------------------------------------------------
1842
1843int
1844CDDA_GetXAttr ( struct vnop_getxattr_args * getXAttrArgsPtr )
1845/*
1846struct vnop_getxattr_args {
1847	struct vnodeop_desc *a_desc;
1848	vnode_t a_vp;
1849	char * a_name;
1850	uio_t a_uio;
1851	size_t
1852	*a_size;
1853	int a_options;
1854	vfs_context_t a_context;
1855};
1856*/
1857{
1858
1859	AppleCDDANodePtr	cddaNodePtr		= NULL;
1860	FinderInfo *		finderInfoPtr	= NULL;
1861	char				buf[32]			= { 0 };
1862
1863	DebugLog ( ( "CDDA_GetXAttr: Entering.\n" ) );
1864	DebugAssert ( ( getXAttrArgsPtr != NULL ) );
1865
1866	if ( strncmp ( getXAttrArgsPtr->a_name, XATTR_FINDERINFO_NAME, sizeof ( XATTR_FINDERINFO_NAME ) ) != 0 )
1867	{
1868		return ( ENOATTR );
1869	}
1870
1871	cddaNodePtr		= VTOCDDA ( getXAttrArgsPtr->a_vp );
1872	finderInfoPtr	= ( FinderInfo * ) buf;
1873
1874	if ( !vnode_isvroot ( getXAttrArgsPtr->a_vp ) )
1875	{
1876
1877		if ( cddaNodePtr->nodeID == kAppleCDDAXMLFileID )
1878		{
1879
1880			DebugLog ( ( "kFinderInfoInvisibleMask\n" ) );
1881			// Make the XML file invisible
1882			finderInfoPtr->finderFlags = kFinderInfoInvisibleMask;
1883
1884		}
1885
1886		else
1887		{
1888			finderInfoPtr->finderFlags = kFinderInfoNoFileExtensionMask;
1889		}
1890
1891		finderInfoPtr->location.v	= -1;
1892		finderInfoPtr->location.h	= -1;
1893
1894		if ( vnode_isreg ( getXAttrArgsPtr->a_vp ) && ( cddaNodePtr->nodeID != kAppleCDDAXMLFileID ) )
1895		{
1896
1897			DebugLog ( ( "fileType, creator\n" ) );
1898			finderInfoPtr->fileType		= VFSTOCDDA ( vnode_mount ( cddaNodePtr->vNodePtr ) )->fileType;
1899			finderInfoPtr->fileCreator	= VFSTOCDDA ( vnode_mount ( cddaNodePtr->vNodePtr ) )->fileCreator;
1900
1901		}
1902
1903		// Swap FinderInfo into big endian. FinderInfo must always be big endian when passed
1904		// back and forth across the kernel using getattrlist/getxattr.
1905		finderInfoPtr->fileType 	= OSSwapHostToBigInt32 ( finderInfoPtr->fileType );
1906		finderInfoPtr->fileCreator 	= OSSwapHostToBigInt32 ( finderInfoPtr->fileCreator );
1907		finderInfoPtr->finderFlags 	= OSSwapHostToBigInt16 ( finderInfoPtr->finderFlags );
1908		finderInfoPtr->location.v 	= OSSwapHostToBigInt16 ( finderInfoPtr->location.v );
1909		finderInfoPtr->location.h 	= OSSwapHostToBigInt16 ( finderInfoPtr->location.h );
1910
1911	}
1912
1913	return ( uiomove ( ( caddr_t ) buf, ( int ) sizeof ( buf ), getXAttrArgsPtr->a_uio ) );
1914
1915}
1916
1917
1918//-----------------------------------------------------------------------------
1919//	Other macro'd function definitions
1920//-----------------------------------------------------------------------------
1921
1922int ( **gCDDA_VNodeOp_p )( void * );
1923typedef int (*VNOPFUNC) ( void * );
1924
1925
1926#if 0
1927#pragma mark -
1928#endif
1929
1930
1931//-----------------------------------------------------------------------------
1932//	VNode Operation Vector Entry Description (Dispatch Table)
1933//-----------------------------------------------------------------------------
1934
1935struct vnodeopv_entry_desc gCDDA_VNodeOperationEntries[] =
1936{
1937	{ &vnop_default_desc,		( VNOPFUNC ) vn_default_error },
1938	{ &vnop_lookup_desc,		( VNOPFUNC ) CDDA_Lookup },				// lookup
1939	{ &vnop_open_desc,			( VNOPFUNC ) CDDA_Open },				// open
1940	{ &vnop_close_desc,			( VNOPFUNC ) CDDA_Close },				// close
1941	{ &vnop_getattr_desc,		( VNOPFUNC ) CDDA_GetAttributes },		// getattr
1942	{ &vnop_setattr_desc,		( VNOPFUNC ) nop_setattr },				// setattr
1943	{ &vnop_read_desc,			( VNOPFUNC ) CDDA_Read },				// read
1944	{ &vnop_fsync_desc,			( VNOPFUNC ) nop_fsync },				// fsync
1945	{ &vnop_remove_desc,		( VNOPFUNC ) CDDA_Remove },				// remove
1946	{ &vnop_rmdir_desc,			( VNOPFUNC ) CDDA_RmDir },				// rmdir
1947	{ &vnop_readdir_desc,		( VNOPFUNC ) CDDA_ReadDir },			// readdir
1948	{ &vnop_inactive_desc,		( VNOPFUNC ) CDDA_Inactive },			// inactive
1949	{ &vnop_reclaim_desc,		( VNOPFUNC ) CDDA_Reclaim },			// reclaim
1950	{ &vnop_pathconf_desc,		( VNOPFUNC ) CDDA_Pathconf },			// pathconf
1951	{ &vnop_pagein_desc,		( VNOPFUNC ) CDDA_PageIn },				// pagein
1952	{ &vnop_blktooff_desc,		( VNOPFUNC ) CDDA_BlockToOffset },		// blktoff
1953	{ &vnop_offtoblk_desc,		( VNOPFUNC ) CDDA_OffsetToBlock },		// offtoblk
1954	{ &vnop_getxattr_desc,		( VNOPFUNC ) CDDA_GetXAttr },			// getxattr
1955	{ NULL,						( VNOPFUNC ) NULL }
1956};
1957
1958
1959struct vnodeopv_desc gCDDA_VNodeOperationsDesc =
1960{
1961	&gCDDA_VNodeOp_p,
1962	gCDDA_VNodeOperationEntries
1963};
1964
1965
1966//-----------------------------------------------------------------------------
1967//				End				Of			File
1968//-----------------------------------------------------------------------------
1969