1/*
2 * Copyright (c) 1999-2003, 2005-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	File:		SUtils.c
25
26	Contains:	xxx put contents here xxx
27
28	Version:	xxx put version here xxx
29
30	Copyright:	� 1997-1999 by Apple Computer, Inc., all rights reserved.
31*/
32
33#include "Scavenger.h"
34
35static void 	CompareVolHeaderBTreeSizes(	SGlobPtr GPtr,
36											VolumeObjectPtr theVOPtr,
37											HFSPlusVolumeHeader * thePriVHPtr,
38											HFSPlusVolumeHeader * theAltVHPtr );
39static void 	GetEmbeddedVolumeHeaders( 	SGlobPtr GPtr,
40											HFSMasterDirectoryBlock * myMDBPtr,
41											Boolean isPrimaryMDB );
42static OSErr 	GetVolumeObjectBlock( VolumeObjectPtr theVOPtr,
43									  UInt64 theBlockNum,
44								      BlockDescriptor * theBlockDescPtr );
45static OSErr 	VolumeObjectFixPrimaryBlock( void );
46
47/*
48 * utf_encodestr
49 *
50 * Encode a UCS-2 (Unicode) string to UTF-8
51 */
52int utf_encodestr(ucsp, ucslen, utf8p, utf8len, utf8plen)
53	const u_int16_t * ucsp;
54	size_t ucslen;
55	unsigned char * utf8p;
56	size_t * utf8len;
57	size_t utf8plen;
58{
59	unsigned char * bufstart;
60	u_int16_t ucs_ch;
61	size_t charcnt;
62
63	bufstart = utf8p;
64	charcnt = ucslen / 2;
65
66	while (charcnt-- > 0 && utf8plen > 0) {
67		ucs_ch = *ucsp++;
68
69		if (ucs_ch < 0x0080) {
70			if (ucs_ch == '\0')
71				continue;	/* skip over embedded NULLs */
72
73			*utf8p++ = ucs_ch;
74			utf8plen--;
75		} else if (ucs_ch < 0x800) {
76			if (utf8plen < 2)	/* We're about to over-flow the buffer */
77				break;
78			utf8plen -= 2;
79			*utf8p++ = (ucs_ch >> 6)   | 0xc0;
80			*utf8p++ = (ucs_ch & 0x3f) | 0x80;
81		} else {
82			if (utf8plen < 3)	/* We're about to over-flow the buffer */
83				break;
84			utf8plen -= 3;
85			*utf8p++ = (ucs_ch >> 12)         | 0xe0;
86			*utf8p++ = ((ucs_ch >> 6) & 0x3f) | 0x80;
87			*utf8p++ = ((ucs_ch) & 0x3f)      | 0x80;
88		}
89	}
90
91	*utf8len = utf8p - bufstart;
92
93	return (0);
94}
95
96
97/*
98 * utf_decodestr
99 *
100 * Decode a UTF-8 string back to UCS-2 (Unicode)
101 *
102 * N.B.:  ucslen on input describes the length of the buffer;
103 * on return, it describes how many bytes were used.
104 */
105int
106utf_decodestr(utf8p, utf8len, ucsp, ucslen, ucsplen)
107	const unsigned char * utf8p;
108	size_t utf8len;
109	u_int16_t* ucsp;
110	size_t *ucslen;
111	size_t ucsplen;
112{
113	u_int16_t* bufstart;
114	u_int16_t ucs_ch;
115	u_int8_t byte;
116
117	bufstart = ucsp;
118
119	while (utf8len-- > 0 && (byte = *utf8p++) != '\0' && ucsplen > 0) {
120		/* check for ascii */
121		if (byte < 0x80) {
122			*ucsp++ = byte;
123			ucsplen--;
124			continue;
125		}
126
127		switch (byte & 0xf0) {
128		/*  2 byte sequence*/
129		case 0xc0:
130		case 0xd0:
131			/* extract bits 6 - 10 from first byte */
132			ucs_ch = (byte & 0x1F) << 6;
133			if (ucs_ch < 0x0080)
134				return (-1); /* seq not minimal */
135			break;
136		/* 3 byte sequence*/
137		case 0xe0:
138			/* extract bits 12 - 15 from first byte */
139			ucs_ch = (byte & 0x0F) << 6;
140
141			/* extract bits 6 - 11 from second byte */
142			if (((byte = *utf8p++) & 0xc0) != 0x80)
143				return (-1);
144
145			utf8len--;
146			ucs_ch += (byte & 0x3F);
147			ucs_ch <<= 6;
148			if (ucs_ch < 0x0800)
149				return (-1); /* seq not minimal */
150			break;
151		default:
152			return (-1);
153		}
154
155		/* extract bits 0 - 5 from final byte */
156		if (((byte = *utf8p++) & 0xc0) != 0x80)
157			return (-1);
158
159		utf8len--;
160		ucs_ch += (byte & 0x3F);
161		*ucsp++ = ucs_ch;
162		ucsplen--;
163	}
164
165	*ucslen = (u_int8_t*)ucsp - (u_int8_t*)bufstart;
166
167	return (0);
168}
169
170
171OSErr GetFBlk( SGlobPtr GPtr, SInt16 fileRefNum, SInt32 blockNumber, void **bufferH );
172
173
174UInt32 gDFAStage;
175
176UInt32	GetDFAStage( void )
177{
178	return (gDFAStage);
179}
180
181void	SetDFAStage( UInt32 stage )
182{
183	gDFAStage = stage;
184}
185
186
187/*------------------------------------------------------------------------------
188
189Routine:	RcdError
190
191Function:	Record errors detetected by scavenging operation.
192
193Input:		GPtr		-	pointer to scavenger global area.
194			ErrCode		-	error code
195
196Output:		None
197------------------------------------------------------------------------------*/
198
199void RcdError( SGlobPtr GPtr, OSErr errorCode )
200{
201	GPtr->ErrCode = errorCode;
202
203	WriteError( GPtr, errorCode, GPtr->TarID, GPtr->TarBlock );	//	log to summary window
204}
205
206
207/*------------------------------------------------------------------------------
208
209Routine:	IntError
210
211Function:	Records an internal Scavenger error.
212
213Input:		GPtr		-	pointer to scavenger global area.
214			ErrCode		-	internal error code
215
216Output:		IntError	-	function result:
217								(E_IntErr for now)
218------------------------------------------------------------------------------*/
219
220int IntError( SGlobPtr GPtr, OSErr errorCode )
221{
222	GPtr->RepLevel = repairLevelUnrepairable;
223
224	if ( errorCode == ioErr )				//	Cast I/O errors as read errors
225		errorCode	= R_RdErr;
226
227	if( (errorCode == R_RdErr) || (errorCode == R_WrErr) )
228	{
229		GPtr->ErrCode	= GPtr->volumeErrorCode;
230		GPtr->IntErr	= 0;
231		return( errorCode );
232	}
233	else
234	{
235		GPtr->ErrCode	= R_IntErr;
236		GPtr->IntErr	= errorCode;
237		return( R_IntErr );
238	}
239
240}	//	End of IntError
241
242
243
244/*------------------------------------------------------------------------------
245
246Routine:	AllocBTN (Allocate BTree Node)
247
248Function:	Allocates an BTree node in a Scavenger BTree bit map.
249
250Input:		GPtr		-	pointer to scavenger global area.
251			StABN		-	starting allocation block number.
252			NmABlks		-	number of allocation blocks.
253
254Output:		AllocBTN	-	function result:
255								0 = no error
256								n = error
257------------------------------------------------------------------------------*/
258
259int AllocBTN( SGlobPtr GPtr, SInt16 fileRefNum, UInt32 nodeNumber )
260{
261	UInt16				bitPos;
262	unsigned char		mask;
263	char				*byteP;
264	BTreeControlBlock	*calculatedBTCB	= GetBTreeControlBlock( fileRefNum );
265
266	//	Allocate the node
267	if ( calculatedBTCB->refCon == 0)
268		return( noErr );
269
270	byteP = ( (BTreeExtensionsRec*)calculatedBTCB->refCon)->BTCBMPtr + (nodeNumber / 8 );	//	ptr to starting byte
271	bitPos = nodeNumber % 8;						//	bit offset
272	mask = ( 0x80 >> bitPos );
273	if ( (*byteP & mask) != 0 )
274	{
275		RcdError( GPtr, E_OvlNode );
276		return( E_OvlNode );					//	node already allocated
277	}
278	*byteP = *byteP | mask;						//	allocate it
279	calculatedBTCB->freeNodes--;		//	decrement free count
280
281	return( noErr );
282}
283
284
285OSErr	GetBTreeHeader( SGlobPtr GPtr, SFCB *fcb, BTHeaderRec *header )
286{
287	OSErr err;
288	BTHeaderRec *headerRec;
289	BlockDescriptor block;
290
291	GPtr->TarBlock = kHeaderNodeNum;
292
293	if (fcb->fcbBlockSize == 0)
294		(void) SetFileBlockSize(fcb, 512);
295
296	err = GetFileBlock(fcb, kHeaderNodeNum, kGetBlock, &block);
297	ReturnIfError(err);
298
299	err = hfs_swap_BTNode(&block, fcb, kSwapBTNodeHeaderRecordOnly);
300	if (err != noErr)
301	{
302		(void) ReleaseFileBlock(fcb, &block, kReleaseBlock | kTrashBlock);
303		return err;
304	}
305
306	headerRec = (BTHeaderRec *)((char*)block.buffer + sizeof(BTNodeDescriptor));
307	CopyMemory(headerRec, header, sizeof(BTHeaderRec));
308
309	err = hfs_swap_BTNode(&block, fcb, kSwapBTNodeHeaderRecordOnly);
310	if (err != noErr)
311	{
312		(void) ReleaseFileBlock(fcb, &block, kReleaseBlock | kTrashBlock);
313		return err;
314	}
315
316	err = ReleaseFileBlock (fcb, &block, kReleaseBlock);
317	ReturnIfError(err);
318
319	/* Validate Node Size */
320	switch (header->nodeSize) {
321		case   512:
322		case  1024:
323		case  2048:
324		case  4096:
325		case  8192:
326		case 16384:
327		case 32768:
328			break;
329
330		default:
331			RcdError( GPtr, E_InvalidNodeSize );
332			err = E_InvalidNodeSize;
333	}
334
335	return( err );
336}
337
338
339/*------------------------------------------------------------------------------
340
341Routine:	IsDuplicateRepairOrder
342
343Function:	Search a duplicate repair order node in the GPtr->MinorRepairP
344		list.  This function traverses the entire list of minor
345		repairs, and compares the following fields to determine a
346		match - type, forkType, correct, incorrect, maskBit, hint,
347		and parentID.
348
349Input:		GPtr	- scavenger globals
350		orig	- repair order to search and compare
351
352Output:		0 - no duplicate was found,
353		1 - duplicate was found.
354------------------------------------------------------------------------------*/
355int IsDuplicateRepairOrder(SGlobPtr GPtr, RepairOrderPtr orig)
356{
357	RepairOrderPtr cur;
358	int retval = 0;
359
360	cur = GPtr->MinorRepairsP;
361	while (cur) {
362		if (cur != orig) {
363			/* If all these values match, this is a duplicate */
364			if ((orig->type == cur->type) &&
365			    (orig->correct == cur->correct) &&
366			    (orig->incorrect == cur->incorrect) &&
367			    (orig->parid == cur->parid) &&
368			    (orig->forkType == cur->forkType) &&
369			    (orig->maskBit == cur->maskBit) &&
370			    (orig->hint == cur->hint)) {
371				retval = 1;
372				break;
373			}
374		}
375		cur = cur->link;
376	}
377
378	return retval;
379}
380
381/*------------------------------------------------------------------------------
382
383Routine:	DeleteRepairOrder
384
385Function:	Deletes the minor repair order that matches the repair order
386		provided from the list.  This function should be called when
387		a duplicate repair order is detected.
388
389Input:		GPtr	- scavenger globals
390		orig	- repair order to remove
391
392Output:		Nothing
393------------------------------------------------------------------------------*/
394void DeleteRepairOrder(SGlobPtr GPtr, RepairOrderPtr orig)
395{
396	RepairOrderPtr cur;
397	RepairOrderPtr prev = NULL;
398
399	cur = GPtr->MinorRepairsP;
400	while (cur) {
401		if (cur == orig) {
402			if (prev) {
403				prev->link = cur->link;
404			}
405			if (cur == GPtr->MinorRepairsP) {
406				GPtr->MinorRepairsP = cur->link;
407			}
408			DisposeMemory(cur);
409		}
410		prev = cur;
411		cur = cur->link;
412	}
413
414	return;
415}
416
417
418/*------------------------------------------------------------------------------
419
420Routine:	Alloc[Minor/Major]RepairOrder
421
422Function:	Allocate a repair order node and link into the 'GPtr->RepairXxxxxP" list.
423			These are descriptions of minor/major repairs that need to be performed;
424			they are compiled during verification, and executed during minor/major repair.
425
426Input:		GPtr	- scavenger globals
427			n		- number of extra bytes needed, in addition to standard node size.
428
429Output:		Ptr to node, or NULL if out of memory or other error.
430------------------------------------------------------------------------------*/
431
432RepairOrderPtr AllocMinorRepairOrder( SGlobPtr GPtr, size_t n )						/* #extra bytes needed */
433{
434	RepairOrderPtr	p;							//	the node we allocate
435
436	n += sizeof( RepairOrder );					//	add in size of basic node
437
438	p = (RepairOrderPtr) AllocateClearMemory( n );		//	get the node
439
440	if ( p != NULL )							//	if we got one...
441	{
442		p->link = GPtr->MinorRepairsP;			//	then link into list of repairs
443		GPtr->MinorRepairsP = p;
444	}
445    else if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
446       	plog( "\t%s - AllocateClearMemory failed to allocate %d bytes \n", __FUNCTION__, n);
447
448	if ( GPtr->RepLevel == repairLevelNoProblemsFound )
449		GPtr->RepLevel = repairLevelVolumeRecoverable;
450
451	return( p );								//	return ptr to node
452}
453
454
455
456void	InvalidateCalculatedVolumeBitMap( SGlobPtr GPtr )
457{
458
459}
460
461
462
463//------------------------------------------------------------------------------
464//	Routine:	GetVolumeFeatures
465//
466//	Function:	Sets up some OS and volume specific flags
467//
468//	Input:		GPtr->DrvNum			The volume to check
469//
470//	Output:		GPtr->volumeFeatures	Bit vector
471//				GPtr->realVCB			Real in-memory vcb
472//------------------------------------------------------------------------------
473
474#if !BSD
475OSErr GetVolumeFeatures( SGlobPtr GPtr )
476{
477	OSErr					err;
478	HParamBlockRec			pb;
479	GetVolParmsInfoBuffer	buffer;
480	long					response;
481
482	GPtr->volumeFeatures	= 0;					//	Initialize to zero
483
484	//	Get the "real" vcb
485	err = GetVCBDriveNum( &GPtr->realVCB, GPtr->DrvNum );
486	ReturnIfError( err );
487
488	if ( GPtr->realVCB != nil )
489	{
490		GPtr->volumeFeatures	|= volumeIsMountedMask;
491
492		pb.ioParam.ioNamePtr	= nil;
493		pb.ioParam.ioVRefNum	= GPtr->realVCB->vcbVRefNum;
494		pb.ioParam.ioBuffer		= (Ptr) &buffer;
495		pb.ioParam.ioReqCount	= sizeof( buffer );
496
497		if ( PBHGetVolParms( &pb, false ) == noErr )
498		{
499			if ( buffer.vMAttrib & (1 << bSupportsTrashVolumeCache) )
500				GPtr->volumeFeatures	|= supportsTrashVolumeCacheFeatureMask;
501		}
502	}
503	//	Check if the running system is HFS+ savy
504	err = Gestalt( gestaltFSAttr, &response );
505	ReturnIfError( err );
506	if ( (response & (1 << gestaltFSSupportsHFSPlusVols)) != 0 )
507		GPtr->volumeFeatures |= supportsHFSPlusVolsFeatureMask;
508
509	return( noErr );
510}
511#endif
512
513
514
515/*-------------------------------------------------------------------------------
516Routine:	ClearMemory	-	clear a block of memory
517
518-------------------------------------------------------------------------------*/
519#if !BSD
520void ClearMemory( void* start, UInt32 length )
521{
522	UInt32		zero = 0;
523	UInt32*		dataPtr;
524	UInt8*		bytePtr;
525	UInt32		fragCount;		// serves as both a length and quadlong count
526								// for the beginning and main fragment
527
528	if ( length == 0 )
529		return;
530
531	// is request less than 4 bytes?
532	if ( length < 4 )				// length = 1,2 or 3
533	{
534		bytePtr = (UInt8 *) start;
535
536		do
537		{
538			*bytePtr++ = zero;		// clear one byte at a time
539		}
540		while ( --length );
541
542		return;
543	}
544
545	// are we aligned on an odd boundry?
546	fragCount = (UInt32) start & 3;
547
548	if ( fragCount )				// fragCount = 1,2 or 3
549	{
550		bytePtr = (UInt8 *) start;
551
552		do
553		{
554			*bytePtr++ = zero;		// clear one byte at a time
555			++fragCount;
556			--length;
557		}
558		while ( (fragCount < 4) && (length > 0) );
559
560		if ( length == 0 )
561			return;
562
563		dataPtr = (UInt32*) (((UInt32) start & 0xFFFFFFFC) + 4);	// make it long word aligned
564	}
565	else
566	{
567		dataPtr = (UInt32*) ((UInt32) start & 0xFFFFFFFC);			// make it long word aligned
568	}
569
570	// At this point dataPtr is long aligned
571
572	// are there odd bytes to copy?
573	fragCount = length & 3;
574
575	if ( fragCount )
576	{
577		bytePtr = (UInt8 *) ((UInt32) dataPtr + (UInt32) length - 1);	// point to last byte
578
579		length -= fragCount;		// adjust remaining length
580
581		do
582		{
583			*bytePtr-- = zero;		// clear one byte at a time
584		}
585		while ( --fragCount );
586
587		if ( length == 0 )
588			return;
589	}
590
591	// At this point length is a multiple of 4
592
593	#if DEBUG_BUILD
594	  if ( length < 4 )
595		 DebugStr("\p ClearMemory: length < 4");
596	#endif
597
598	// fix up beginning to get us on a 64 byte boundary
599	fragCount = length & (64-1);
600
601	#if DEBUG_BUILD
602	  if ( fragCount < 4 && fragCount > 0 )
603		  DebugStr("\p ClearMemory: fragCount < 4");
604	#endif
605
606	if ( fragCount )
607	{
608		length -= fragCount; 		// subtract fragment from length now
609		fragCount >>= 2; 			// divide by 4 to get a count, for DBRA loop
610		do
611		{
612			// clear 4 bytes at a time...
613			*dataPtr++ = zero;
614		}
615		while (--fragCount);
616	}
617
618	// Are we finished yet?
619	if ( length == 0 )
620		return;
621
622	// Time to turn on the fire hose
623	length >>= 6;		// divide by 64 to get count
624	do
625	{
626		// spray 64 bytes at a time...
627		*dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero;
628		*dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero;
629		*dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero;
630		*dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero;
631	}
632	while (--length);
633}
634#endif
635
636
637
638void
639CopyCatalogName(const CatalogName *srcName, CatalogName *dstName, Boolean isHFSPLus)
640{
641	size_t	length;
642
643	if ( srcName == NULL )
644	{
645		if ( dstName != NULL )
646			dstName->ustr.length = 0;	// set length byte to zero (works for both unicode and pascal)
647		return;
648	}
649
650	if (isHFSPLus)
651		length = sizeof(UniChar) * (srcName->ustr.length + 1);
652	else
653		length = sizeof(UInt8) + srcName->pstr[0];
654
655	if ( length > 1 )
656		CopyMemory(srcName, dstName, length);
657	else
658		dstName->ustr.length = 0;	// set length byte to zero (works for both unicode and pascal)
659}
660
661
662UInt32
663CatalogNameLength(const CatalogName *name, Boolean isHFSPlus)
664{
665	if (isHFSPlus)
666		return name->ustr.length;
667	else
668		return name->pstr[0];
669}
670
671
672UInt32	CatalogNameSize( const CatalogName *name, Boolean isHFSPlus)
673{
674	UInt32	length = CatalogNameLength( name, isHFSPlus );
675
676	if ( isHFSPlus )
677		length *= sizeof(UniChar);
678
679	return( length );
680}
681
682
683//******************************************************************************
684//	Routine:	BuildCatalogKey
685//
686//	Function: 	Constructs a catalog key record (ckr) given the parent
687//				folder ID and CName.  Works for both classic and extended
688//				HFS volumes.
689//
690//******************************************************************************
691
692void
693BuildCatalogKey(HFSCatalogNodeID parentID, const CatalogName *cName, Boolean isHFSPlus, CatalogKey *key)
694{
695	if ( isHFSPlus )
696	{
697		key->hfsPlus.keyLength		= kHFSPlusCatalogKeyMinimumLength;	// initial key length (4 + 2)
698		key->hfsPlus.parentID			= parentID;		// set parent ID
699		key->hfsPlus.nodeName.length	= 0;			// null CName length
700		if ( cName != NULL )
701		{
702			CopyCatalogName(cName, (CatalogName *) &key->hfsPlus.nodeName, isHFSPlus);
703			key->hfsPlus.keyLength += sizeof(UniChar) * cName->ustr.length;	// add CName size to key length
704		}
705	}
706	else
707	{
708		key->hfs.keyLength	= kHFSCatalogKeyMinimumLength;	// initial key length (1 + 4 + 1)
709		key->hfs.reserved		= 0;				// clear unused byte
710		key->hfs.parentID		= parentID;			// set parent ID
711		key->hfs.nodeName[0]	= 0;				// null CName length
712		if ( cName != NULL )
713		{
714			UpdateCatalogName(cName->pstr, key->hfs.nodeName);
715			key->hfs.keyLength += key->hfs.nodeName[0];		// add CName size to key length
716		}
717	}
718}
719
720
721//		Defined in BTreesPrivate.h, but not implemented in the BTree code?
722//		So... here's the implementation
723SInt32	CompareKeys( BTreeControlBlockPtr btreePtr, KeyPtr searchKey, KeyPtr trialKey )
724{
725	KeyCompareProcPtr	compareProc = (KeyCompareProcPtr)btreePtr->keyCompareProc;
726
727	return( compareProc(searchKey, trialKey) );
728}
729
730
731void
732UpdateCatalogName(ConstStr31Param srcName, Str31 destName)
733{
734	Size length = srcName[0];
735
736	if (length > kHFSMaxFileNameChars)
737		length = kHFSMaxFileNameChars;		// truncate to max
738
739	destName[0] = length;					// set length byte
740
741	CopyMemory(&srcName[1], &destName[1], length);
742}
743
744
745void
746UpdateVolumeEncodings(SVCB *volume, TextEncoding encoding)
747{
748	UInt32	index;
749
750	encoding &= 0x7F;
751
752	index = MapEncodingToIndex(encoding);
753
754	volume->vcbEncodingsBitmap |= (u_int64_t)(1ULL << index);
755
756	// vcb should already be marked dirty
757}
758
759
760//******************************************************************************
761//	Routine:	VolumeObjectFixPrimaryBlock
762//
763//	Function:	Use the alternate Volume Header or Master Directory block (depending
764//				on the type of volume) to restore the primary block.  This routine
765//				depends upon our intialization code to set up where are blocks are
766//				located.
767//
768// 	Result:		0 if all is well, noMacDskErr when we do not have a primary block
769//				number or whatever GetVolumeObjectAlternateBlock returns.
770//******************************************************************************
771
772static OSErr VolumeObjectFixPrimaryBlock( void )
773{
774	OSErr				err;
775	VolumeObjectPtr		myVOPtr;
776	UInt64				myPrimaryBlockNum;
777	BlockDescriptor  	myPrimary;
778	BlockDescriptor  	myAlternate;
779
780	myVOPtr = GetVolumeObjectPtr( );
781	myPrimary.buffer = NULL;
782	myAlternate.buffer = NULL;
783
784	GetVolumeObjectPrimaryBlockNum( &myPrimaryBlockNum );
785	if ( myPrimaryBlockNum == 0 )
786		return( noMacDskErr );
787
788	// we don't care if this is a valid primary block since we're
789	// about to write over it
790	err = GetVolumeObjectPrimaryBlock( &myPrimary );
791	if ( !(err == noErr || err == badMDBErr || err == noMacDskErr) )
792		goto ExitThisRoutine;
793
794	// restore the primary block from the alternate
795	err = GetVolumeObjectAlternateBlock( &myAlternate );
796
797	// invalidate if we have not marked the alternate as OK
798	if ( VolumeObjectIsHFS( ) ) {
799		if ( (myVOPtr->flags & kVO_AltMDBOK) == 0 )
800			err = badMDBErr;
801	}
802	else if ( (myVOPtr->flags & kVO_AltVHBOK) == 0 ) {
803		err = badMDBErr;
804	}
805
806	if ( err == noErr ) {
807		CopyMemory( myAlternate.buffer, myPrimary.buffer, Blk_Size );
808		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kForceWriteBlock );
809		myPrimary.buffer = NULL;
810		if ( myVOPtr->volumeType == kHFSVolumeType )
811			myVOPtr->flags |= kVO_PriMDBOK;
812		else
813			myVOPtr->flags |= kVO_PriVHBOK;
814	}
815
816ExitThisRoutine:
817	if ( myPrimary.buffer != NULL )
818		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kReleaseBlock );
819	if ( myAlternate.buffer != NULL )
820		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kReleaseBlock );
821
822	return( err );
823
824} /* VolumeObjectFixPrimaryBlock */
825
826
827//******************************************************************************
828//	Routine:	GetVolumeObjectVHBorMDB
829//
830//	Function:	Get the Volume Header block or Master Directory block (depending
831//				on type of volume).  This will normally return the alternate, but
832//				it may return the primary when the alternate is damaged or cannot
833//				be found.
834//
835// 	Result:		returns 0 when all is well.
836//******************************************************************************
837OSErr GetVolumeObjectVHBorMDB( BlockDescriptor * theBlockDescPtr )
838{
839	UInt64				myBlockNum;
840	VolumeObjectPtr		myVOPtr;
841	OSErr  				err;
842
843	myVOPtr = GetVolumeObjectPtr( );
844	GetVolumeObjectBlockNum( &myBlockNum );
845
846	err = GetVolumeObjectBlock( myVOPtr, myBlockNum, theBlockDescPtr );
847	if ( err == noErr )
848	{
849		if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType ||
850			 myVOPtr->volumeType == kPureHFSPlusVolumeType )
851		{
852			err = ValidVolumeHeader( (HFSPlusVolumeHeader*) theBlockDescPtr->buffer );
853		}
854		else if ( myVOPtr->volumeType == kHFSVolumeType )
855		{
856			HFSMasterDirectoryBlock *	myMDBPtr;
857			myMDBPtr = (HFSMasterDirectoryBlock	*) theBlockDescPtr->buffer;
858			if ( myMDBPtr->drSigWord != kHFSSigWord )
859				err = noMacDskErr;
860		}
861		else
862			err = noMacDskErr;
863	}
864
865	return( err );
866
867} /* GetVolumeObjectVHBorMDB */
868
869
870//******************************************************************************
871//	Routine:	GetVolumeObjectAlternateBlock
872//
873//	Function:	Get the alternate Volume Header block or Master Directory block
874//				(depending on type of volume).
875// 	Result:		returns 0 when all is well.
876//******************************************************************************
877OSErr GetVolumeObjectAlternateBlock( BlockDescriptor * theBlockDescPtr )
878{
879	UInt64				myBlockNum;
880	VolumeObjectPtr		myVOPtr;
881	OSErr  				err;
882
883	myVOPtr = GetVolumeObjectPtr( );
884	GetVolumeObjectAlternateBlockNum( &myBlockNum );
885
886	err = GetVolumeObjectBlock( myVOPtr, myBlockNum, theBlockDescPtr );
887	if ( err == noErr )
888	{
889		if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType ||
890			 myVOPtr->volumeType == kPureHFSPlusVolumeType )
891		{
892			err = ValidVolumeHeader( (HFSPlusVolumeHeader*) theBlockDescPtr->buffer );
893		}
894		else if ( myVOPtr->volumeType == kHFSVolumeType )
895		{
896			HFSMasterDirectoryBlock *	myMDBPtr;
897			myMDBPtr = (HFSMasterDirectoryBlock	*) theBlockDescPtr->buffer;
898			if ( myMDBPtr->drSigWord != kHFSSigWord )
899				err = noMacDskErr;
900		}
901		else
902			err = noMacDskErr;
903	}
904
905	return( err );
906
907} /* GetVolumeObjectAlternateBlock */
908
909
910//******************************************************************************
911//	Routine:	GetVolumeObjectPrimaryBlock
912//
913//	Function:	Get the primary Volume Header block or Master Directory block
914//				(depending on type of volume).
915// 	Result:		returns 0 when all is well.
916//******************************************************************************
917OSErr GetVolumeObjectPrimaryBlock( BlockDescriptor * theBlockDescPtr )
918{
919	UInt64				myBlockNum;
920	VolumeObjectPtr		myVOPtr;
921	OSErr  				err;
922
923	myVOPtr = GetVolumeObjectPtr( );
924	GetVolumeObjectPrimaryBlockNum( &myBlockNum );
925
926	err = GetVolumeObjectBlock( myVOPtr, myBlockNum, theBlockDescPtr );
927	if ( err == noErr )
928	{
929		if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType ||
930			 myVOPtr->volumeType == kPureHFSPlusVolumeType )
931		{
932			err = ValidVolumeHeader( (HFSPlusVolumeHeader*) theBlockDescPtr->buffer );
933		}
934		else if ( myVOPtr->volumeType == kHFSVolumeType )
935		{
936			HFSMasterDirectoryBlock *	myMDBPtr;
937			myMDBPtr = (HFSMasterDirectoryBlock	*) theBlockDescPtr->buffer;
938			if ( myMDBPtr->drSigWord != kHFSSigWord )
939				err = noMacDskErr;
940		}
941		else
942			err = noMacDskErr;
943	}
944
945	return( err );
946
947} /* GetVolumeObjectPrimaryBlock */
948
949
950//******************************************************************************
951//	Routine:	GetVolumeObjectVHB
952//
953//	Function:	Get the Volume Header block using either the primary or alternate
954//				block number as set up by InitializeVolumeObject.  This will normally
955//				return the alternate, but it may return the primary when the
956//				alternate is damaged or cannot be found.
957//
958// 	Result:		returns 0 when all is well or passes results of GetVolumeBlock or
959//				ValidVolumeHeader.
960//******************************************************************************
961OSErr GetVolumeObjectVHB( BlockDescriptor * theBlockDescPtr )
962{
963	UInt64				myBlockNum;
964	VolumeObjectPtr		myVOPtr;
965	OSErr  				err;
966
967	myVOPtr = GetVolumeObjectPtr( );
968	myBlockNum = ((myVOPtr->flags & kVO_AltVHBOK) != 0) ? myVOPtr->alternateVHB : myVOPtr->primaryVHB;
969	err = GetVolumeObjectBlock( myVOPtr, myBlockNum, theBlockDescPtr );
970	if ( err == noErr )
971		err = ValidVolumeHeader( (HFSPlusVolumeHeader*) theBlockDescPtr->buffer );
972
973	return( err );
974
975} /* GetVolumeObjectVHB */
976
977
978//******************************************************************************
979//	Routine:	GetVolumeObjectAlternateMDB
980//
981//	Function:	Get the Master Directory Block using the alternate master directory
982//				block number as set up by InitializeVolumeObject.
983//
984// 	Result:		returns 0 when all is well.
985//******************************************************************************
986OSErr GetVolumeObjectAlternateMDB( BlockDescriptor * theBlockDescPtr )
987{
988	VolumeObjectPtr		myVOPtr;
989	OSErr  				err;
990
991	myVOPtr = GetVolumeObjectPtr( );
992	err = GetVolumeObjectBlock( NULL, myVOPtr->alternateMDB, theBlockDescPtr );
993	if ( err == noErr )
994	{
995		HFSMasterDirectoryBlock *	myMDBPtr;
996		myMDBPtr = (HFSMasterDirectoryBlock	*) theBlockDescPtr->buffer;
997		if ( myMDBPtr->drSigWord != kHFSSigWord )
998			err = noMacDskErr;
999	}
1000
1001	return( err );
1002
1003} /* GetVolumeObjectAlternateMDB */
1004
1005
1006//******************************************************************************
1007//	Routine:	GetVolumeObjectPrimaryMDB
1008//
1009//	Function:	Get the Master Directory Block using the primary master directory
1010//				block number as set up by InitializeVolumeObject.
1011//
1012// 	Result:		returns 0 when all is well.
1013//******************************************************************************
1014OSErr GetVolumeObjectPrimaryMDB( BlockDescriptor * theBlockDescPtr )
1015{
1016	VolumeObjectPtr		myVOPtr;
1017	OSErr  				err;
1018
1019	myVOPtr = GetVolumeObjectPtr( );
1020	err = GetVolumeObjectBlock( NULL, myVOPtr->primaryMDB, theBlockDescPtr );
1021	if ( err == noErr )
1022	{
1023		HFSMasterDirectoryBlock *	myMDBPtr;
1024		myMDBPtr = (HFSMasterDirectoryBlock	*) theBlockDescPtr->buffer;
1025		if ( myMDBPtr->drSigWord != kHFSSigWord )
1026			err = noMacDskErr;
1027	}
1028
1029	return( err );
1030
1031} /* GetVolumeObjectPrimaryMDB */
1032
1033
1034//******************************************************************************
1035//	Routine:	GetVolumeObjectBlock
1036//
1037//	Function:	Get the Volume Header block or Master Directory block using the
1038//				given block number.
1039// 	Result:		returns 0 when all is well or passes results of GetVolumeBlock or
1040//				ValidVolumeHeader.
1041//******************************************************************************
1042static OSErr GetVolumeObjectBlock( VolumeObjectPtr theVOPtr,
1043								   UInt64 theBlockNum,
1044								   BlockDescriptor * theBlockDescPtr )
1045{
1046	OSErr  			err;
1047
1048	if ( theVOPtr == NULL )
1049		theVOPtr = GetVolumeObjectPtr( );
1050
1051	err = GetVolumeBlock( theVOPtr->vcbPtr, theBlockNum, kGetBlock, theBlockDescPtr );
1052
1053	return( err );
1054
1055} /* GetVolumeObjectBlock */
1056
1057
1058//******************************************************************************
1059//	Routine:	GetVolumeObjectBlockNum
1060//
1061//	Function:	Extract the appropriate block number for the volume header or
1062//				master directory (depanding on volume type) from the VolumeObject.
1063//				NOTE - this routine may return the primary or alternate block
1064//				depending on which one is valid.  Preference is always given to
1065//				the alternate.
1066//
1067// 	Result:		returns block number of MDB or VHB or 0 if none are valid or
1068//				if volume type is unknown.
1069//******************************************************************************
1070void GetVolumeObjectBlockNum( UInt64 * theBlockNumPtr )
1071{
1072	VolumeObjectPtr				myVOPtr;
1073
1074	myVOPtr = GetVolumeObjectPtr( );
1075	*theBlockNumPtr = 0;	// default to none
1076
1077	// NOTE - we use alternate volume header or master directory
1078	// block before the primary because it is less likely to be damaged.
1079	if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType ||
1080	     myVOPtr->volumeType == kPureHFSPlusVolumeType ) {
1081		if ( (myVOPtr->flags & kVO_AltVHBOK) != 0 )
1082			*theBlockNumPtr = myVOPtr->alternateVHB;
1083		else
1084			*theBlockNumPtr = myVOPtr->primaryVHB;
1085	}
1086	else if ( myVOPtr->volumeType == kHFSVolumeType ) {
1087		if ( (myVOPtr->flags & kVO_AltMDBOK) != 0 )
1088			*theBlockNumPtr = myVOPtr->alternateMDB;
1089		else
1090			*theBlockNumPtr = myVOPtr->primaryMDB;
1091	}
1092
1093	return;
1094
1095} /* GetVolumeObjectBlockNum */
1096
1097
1098//******************************************************************************
1099//	Routine:	GetVolumeObjectAlternateBlockNum
1100//
1101//	Function:	Extract the alternate block number for the volume header or
1102//				master directory (depanding on volume type) from the VolumeObject.
1103//
1104// 	Result:		returns block number of alternate MDB or VHB or 0 if none are
1105//				valid or if volume type is unknown.
1106//******************************************************************************
1107void GetVolumeObjectAlternateBlockNum( UInt64 * theBlockNumPtr )
1108{
1109	VolumeObjectPtr				myVOPtr;
1110
1111	myVOPtr = GetVolumeObjectPtr( );
1112	*theBlockNumPtr = 0;	// default to none
1113
1114	if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType ||
1115	     myVOPtr->volumeType == kPureHFSPlusVolumeType ) {
1116		*theBlockNumPtr = myVOPtr->alternateVHB;
1117	}
1118	else if ( myVOPtr->volumeType == kHFSVolumeType ) {
1119		*theBlockNumPtr = myVOPtr->alternateMDB;
1120	}
1121
1122	return;
1123
1124} /* GetVolumeObjectAlternateBlockNum */
1125
1126
1127//******************************************************************************
1128//	Routine:	GetVolumeObjectPrimaryBlockNum
1129//
1130//	Function:	Extract the primary block number for the volume header or
1131//				master directory (depanding on volume type) from the VolumeObject.
1132//
1133// 	Result:		returns block number of primary MDB or VHB or 0 if none are valid
1134//				or if volume type is unknown.
1135//******************************************************************************
1136void GetVolumeObjectPrimaryBlockNum( UInt64 * theBlockNumPtr )
1137{
1138	VolumeObjectPtr				myVOPtr;
1139
1140	myVOPtr = GetVolumeObjectPtr( );
1141	*theBlockNumPtr = 0;	// default to none
1142
1143	if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType ||
1144	     myVOPtr->volumeType == kPureHFSPlusVolumeType ) {
1145		*theBlockNumPtr = myVOPtr->primaryVHB;
1146	}
1147	else if ( myVOPtr->volumeType == kHFSVolumeType ) {
1148		*theBlockNumPtr = myVOPtr->primaryMDB;
1149	}
1150
1151	return;
1152
1153} /* GetVolumeObjectPrimaryBlockNum */
1154
1155
1156//******************************************************************************
1157//	Routine:	InitializeVolumeObject
1158//
1159//	Function:	Locate volume headers and / or master directory blocks for this
1160//				volume and fill where they are located on the volume and the type
1161//				of volume we are dealing with.  We have three types of HFS volumes:
1162//				� HFS - standard (old format) where primary MDB is 2nd block into
1163//					the volume and alternate MDB is 2nd to last block on the volume.
1164//				� pure HFS+ - where primary volume header is 2nd block into
1165//					the volume and alternate volume header is 2nd to last block on
1166//					the volume.
1167//				� wrapped HFS+ - where primary MDB is 2nd block into the volume and
1168//					alternate MDB is 2nd to last block on the volume.   The embedded
1169//					HFS+ volume header locations are calculated from drEmbedExtent
1170//					(in the MDB).
1171//
1172// 	Result:		returns nothing.  Will fill in SGlob.VolumeObject data
1173//******************************************************************************
1174void	InitializeVolumeObject( SGlobPtr GPtr )
1175{
1176	OSErr						err;
1177	HFSMasterDirectoryBlock *	myMDBPtr;
1178	HFSPlusVolumeHeader *		myVHPtr;
1179	VolumeObjectPtr				myVOPtr;
1180	HFSPlusVolumeHeader			myPriVolHeader;
1181	BlockDescriptor				myBlockDescriptor;
1182
1183	myBlockDescriptor.buffer = NULL;
1184	myVOPtr = GetVolumeObjectPtr( );
1185	myVOPtr->flags |= kVO_Inited;
1186	myVOPtr->vcbPtr = GPtr->calculatedVCB;
1187
1188	// Determine volume size in sectors
1189	err = GetDeviceSize( 	GPtr->calculatedVCB->vcbDriveNumber,
1190							&myVOPtr->totalDeviceSectors,
1191							&myVOPtr->sectorSize );
1192	if ( (myVOPtr->totalDeviceSectors < 3) || (err != noErr) ) {
1193		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1194			plog("\tinvalid device information for volume - total sectors = %qd sector size = %d \n",
1195				myVOPtr->totalDeviceSectors, myVOPtr->sectorSize);
1196		}
1197		goto ExitRoutine;
1198	}
1199
1200	// get the primary volume header or master directory block (depending on volume type)
1201	// should always be block 2 (relative to 0) into the volume.
1202	err = GetVolumeObjectBlock( myVOPtr, MDB_BlkN, &myBlockDescriptor );
1203	if ( err == noErr ) {
1204		myMDBPtr = (HFSMasterDirectoryBlock	*) myBlockDescriptor.buffer;
1205		if ( myMDBPtr->drSigWord == kHFSPlusSigWord || myMDBPtr->drSigWord == kHFSXSigWord) {
1206			myVHPtr	= (HFSPlusVolumeHeader *) myMDBPtr;
1207
1208			myVOPtr->primaryVHB = MDB_BlkN;								// save location
1209			myVOPtr->alternateVHB = myVOPtr->totalDeviceSectors - 2;	// save location
1210			err = ValidVolumeHeader( myVHPtr );
1211			if ( err == noErr ) {
1212				myVOPtr->flags |= kVO_PriVHBOK;
1213				bcopy( myVHPtr, &myPriVolHeader, sizeof( *myVHPtr ) );
1214			}
1215			else {
1216				if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1217					plog( "\tInvalid primary volume header - error %d \n", err );
1218				}
1219			}
1220		}
1221		else if ( myMDBPtr->drSigWord == kHFSSigWord ) {
1222			// we could have an HFS or wrapped HFS+ volume
1223			myVOPtr->primaryMDB = MDB_BlkN;								// save location
1224			myVOPtr->alternateMDB = myVOPtr->totalDeviceSectors - 2;	// save location
1225			myVOPtr->flags |= kVO_PriMDBOK;
1226		}
1227		else {
1228			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1229				plog( "\tBlock %d is not an MDB or Volume Header \n", MDB_BlkN );
1230			}
1231		}
1232		(void) ReleaseVolumeBlock( GPtr->calculatedVCB, &myBlockDescriptor, kReleaseBlock );
1233	}
1234	else {
1235		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1236			plog( "\tcould not get volume block %d, err %d \n", MDB_BlkN, err );
1237		}
1238	}
1239
1240	// get the alternate volume header or master directory block (depending on volume type)
1241	// should always be 2nd to last sector.
1242	err = GetVolumeObjectBlock( myVOPtr, myVOPtr->totalDeviceSectors - 2, &myBlockDescriptor );
1243	if ( err == noErr ) {
1244		myMDBPtr = (HFSMasterDirectoryBlock	*) myBlockDescriptor.buffer;
1245		if ( myMDBPtr->drSigWord == kHFSPlusSigWord || myMDBPtr->drSigWord == kHFSXSigWord ) {
1246			myVHPtr	= (HFSPlusVolumeHeader *) myMDBPtr;
1247
1248			myVOPtr->primaryVHB = MDB_BlkN;								// save location
1249			myVOPtr->alternateVHB = myVOPtr->totalDeviceSectors - 2;	// save location
1250			err = ValidVolumeHeader( myVHPtr );
1251			if ( err == noErr ) {
1252				// check to see if the primary and alternates are in sync.  3137809
1253				myVOPtr->flags |= kVO_AltVHBOK;
1254				CompareVolHeaderBTreeSizes( GPtr, myVOPtr, &myPriVolHeader, myVHPtr );
1255			}
1256			else {
1257				if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1258					plog( "\tInvalid alternate volume header - error %d \n", err );
1259				}
1260			}
1261		}
1262		else if ( myMDBPtr->drSigWord == kHFSSigWord ) {
1263			myVOPtr->primaryMDB = MDB_BlkN;								// save location
1264			myVOPtr->alternateMDB = myVOPtr->totalDeviceSectors - 2;	// save location
1265			myVOPtr->flags |= kVO_AltMDBOK;
1266		}
1267		else {
1268			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1269				plog( "\tBlock %qd is not an MDB or Volume Header \n", myVOPtr->totalDeviceSectors - 2 );
1270			}
1271		}
1272
1273		(void) ReleaseVolumeBlock( GPtr->calculatedVCB, &myBlockDescriptor, kReleaseBlock );
1274	}
1275	else {
1276		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1277			plog( "\tcould not get alternate volume header at %qd, err %d \n",
1278					myVOPtr->totalDeviceSectors - 2, err );
1279		}
1280	}
1281
1282	// get the embedded volume header (if applicable).
1283	if ( (myVOPtr->flags & kVO_AltMDBOK) != 0 ) {
1284		err = GetVolumeObjectBlock( myVOPtr, myVOPtr->alternateMDB, &myBlockDescriptor );
1285		if ( err == noErr ) {
1286			myMDBPtr = (HFSMasterDirectoryBlock	*) myBlockDescriptor.buffer;
1287			GetEmbeddedVolumeHeaders( GPtr, myMDBPtr, false );
1288			(void) ReleaseVolumeBlock( GPtr->calculatedVCB, &myBlockDescriptor, kReleaseBlock );
1289		}
1290	}
1291
1292	// Now we will look for embedded HFS+ volume headers using the primary MDB if
1293	// we haven't already located them.
1294	if ( (myVOPtr->flags & kVO_PriMDBOK) != 0 &&
1295		 ((myVOPtr->flags & kVO_PriVHBOK) == 0 || (myVOPtr->flags & kVO_AltVHBOK) == 0) ) {
1296		err = GetVolumeObjectBlock( myVOPtr, myVOPtr->primaryMDB, &myBlockDescriptor );
1297		if ( err == noErr ) {
1298			myMDBPtr = (HFSMasterDirectoryBlock	*) myBlockDescriptor.buffer;
1299			GetEmbeddedVolumeHeaders( GPtr, myMDBPtr, true );
1300			(void) ReleaseVolumeBlock( GPtr->calculatedVCB, &myBlockDescriptor, kReleaseBlock );
1301		}
1302		else {
1303			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1304				plog( "\tcould not get primary MDB at block %qd, err %d \n", myVOPtr->primaryMDB, err );
1305			}
1306		}
1307	}
1308
1309ExitRoutine:
1310	// set the type of volume using the flags we set as we located the various header / master
1311	// blocks.
1312	if ( ((myVOPtr->flags & kVO_PriVHBOK) != 0 || (myVOPtr->flags & kVO_AltVHBOK) != 0)  &&
1313		 ((myVOPtr->flags & kVO_PriMDBOK) != 0 || (myVOPtr->flags & kVO_AltMDBOK) != 0) ) {
1314		myVOPtr->volumeType = kEmbededHFSPlusVolumeType;
1315	}
1316	else if ( ((myVOPtr->flags & kVO_PriVHBOK) != 0 || (myVOPtr->flags & kVO_AltVHBOK) != 0) &&
1317			  (myVOPtr->flags & kVO_PriMDBOK) == 0 && (myVOPtr->flags & kVO_AltMDBOK) == 0 ) {
1318		myVOPtr->volumeType = kPureHFSPlusVolumeType;
1319	}
1320	else if ( (myVOPtr->flags & kVO_PriVHBOK) == 0 && (myVOPtr->flags & kVO_AltVHBOK) == 0 &&
1321			  ((myVOPtr->flags & kVO_PriMDBOK) != 0 || (myVOPtr->flags & kVO_AltMDBOK) != 0) ) {
1322		myVOPtr->volumeType = kHFSVolumeType;
1323	}
1324	else
1325		myVOPtr->volumeType = kUnknownVolumeType;
1326
1327	return;
1328
1329} /* InitializeVolumeObject */
1330
1331
1332//******************************************************************************
1333//	Routine:	PrintVolumeObject
1334//
1335//	Function:	Print out some helpful info about the state of our VolumeObject.
1336//
1337// 	Result:		returns nothing.
1338//******************************************************************************
1339void PrintVolumeObject( void )
1340{
1341	VolumeObjectPtr				myVOPtr;
1342
1343	myVOPtr = GetVolumeObjectPtr( );
1344
1345	if ( myVOPtr->volumeType == kHFSVolumeType )
1346		plog( "\tvolume type is HFS \n" );
1347	else if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType )
1348		plog( "\tvolume type is embedded HFS+ \n" );
1349	else if ( myVOPtr->volumeType == kPureHFSPlusVolumeType )
1350		plog( "\tvolume type is pure HFS+ \n" );
1351	else
1352		plog( "\tunknown volume type \n" );
1353
1354	plog( "\tprimary MDB is at block %qd 0x%02qx \n", myVOPtr->primaryMDB, myVOPtr->primaryMDB );
1355	plog( "\talternate MDB is at block %qd 0x%02qx \n", myVOPtr->alternateMDB, myVOPtr->alternateMDB );
1356	plog( "\tprimary VHB is at block %qd 0x%02qx \n", myVOPtr->primaryVHB, myVOPtr->primaryVHB );
1357	plog( "\talternate VHB is at block %qd 0x%02qx \n", myVOPtr->alternateVHB, myVOPtr->alternateVHB );
1358	plog( "\tsector size = %d 0x%02x \n", myVOPtr->sectorSize, myVOPtr->sectorSize );
1359	plog( "\tVolumeObject flags = 0x%02X \n", myVOPtr->flags );
1360	plog( "\ttotal sectors for volume = %qd 0x%02qx \n",
1361			myVOPtr->totalDeviceSectors, myVOPtr->totalDeviceSectors );
1362	plog( "\ttotal sectors for embedded volume = %qd 0x%02qx \n",
1363			myVOPtr->totalEmbeddedSectors, myVOPtr->totalEmbeddedSectors );
1364
1365	return;
1366
1367} /* PrintVolumeObject */
1368
1369
1370//******************************************************************************
1371//	Routine:	GetEmbeddedVolumeHeaders
1372//
1373//	Function:	Given a MDB (Master Directory Block) from an HFS volume, check
1374//				to see if there is an embedded HFS+ volume.  If we find an
1375//				embedded HFS+ volume fill in relevant SGlob.VolumeObject data.
1376//
1377// 	Result:		returns nothing.  Will fill in VolumeObject data
1378//******************************************************************************
1379
1380static void GetEmbeddedVolumeHeaders( 	SGlobPtr GPtr,
1381										HFSMasterDirectoryBlock * theMDBPtr,
1382										Boolean isPrimaryMDB )
1383{
1384	OSErr						err;
1385	HFSPlusVolumeHeader *		myVHPtr;
1386	VolumeObjectPtr				myVOPtr;
1387	UInt64  					myHFSPlusSectors;
1388	UInt64  					myPrimaryBlockNum;
1389	UInt64  					myAlternateBlockNum;
1390	HFSPlusVolumeHeader			myAltVolHeader;
1391	BlockDescriptor				myBlockDescriptor;
1392
1393	myBlockDescriptor.buffer = NULL;
1394	myVOPtr = GetVolumeObjectPtr( );
1395
1396	// NOTE - If all of the embedded volume information is zero, then assume
1397	// this really is a plain HFS disk like it says.  There could be ghost
1398	// volume headers left over when someone reinitializes a large HFS Plus
1399	// volume as HFS.  The original embedded primary volume header and
1400	// alternate volume header are not zeroed out.
1401	if ( theMDBPtr->drEmbedSigWord == 0 &&
1402		 theMDBPtr->drEmbedExtent.blockCount == 0 &&
1403		 theMDBPtr->drEmbedExtent.startBlock == 0 ) {
1404		 goto ExitRoutine;
1405	}
1406
1407	// number of sectors in our embedded HFS+ volume
1408	myHFSPlusSectors = (theMDBPtr->drAlBlkSiz / Blk_Size) * theMDBPtr->drEmbedExtent.blockCount;
1409
1410	// offset of embedded HFS+ volume (in bytes) into HFS wrapper volume
1411	// NOTE - UInt32 is OK since we don't support HFS Wrappers on TB volumes
1412	myVOPtr->embeddedOffset =
1413		(theMDBPtr->drEmbedExtent.startBlock * theMDBPtr->drAlBlkSiz) +
1414		(theMDBPtr->drAlBlSt * Blk_Size);
1415
1416	// Embedded alternate volume header is always 2nd to last sector
1417	myAlternateBlockNum =
1418		theMDBPtr->drAlBlSt +
1419		((theMDBPtr->drAlBlkSiz / Blk_Size) * theMDBPtr->drEmbedExtent.startBlock) +
1420		myHFSPlusSectors - 2;
1421
1422	// Embedded primary volume header should always be block 2 (relative to 0)
1423	// into the embedded volume
1424	myPrimaryBlockNum = (theMDBPtr->drEmbedExtent.startBlock * theMDBPtr->drAlBlkSiz / Blk_Size) +
1425						theMDBPtr->drAlBlSt + 2;
1426
1427	// get the embedded alternate volume header
1428	err = GetVolumeObjectBlock( myVOPtr, myAlternateBlockNum, &myBlockDescriptor );
1429	if ( err == noErr ) {
1430		myVHPtr = (HFSPlusVolumeHeader	*) myBlockDescriptor.buffer;
1431		if ( myVHPtr->signature == kHFSPlusSigWord ) {
1432
1433			myVOPtr->alternateVHB = myAlternateBlockNum;	// save location
1434			myVOPtr->primaryVHB = myPrimaryBlockNum;		// save location
1435			err = ValidVolumeHeader( myVHPtr );
1436			if ( err == noErr ) {
1437				myVOPtr->flags |= kVO_AltVHBOK;
1438				myVOPtr->totalEmbeddedSectors = myHFSPlusSectors;
1439				bcopy( myVHPtr, &myAltVolHeader, sizeof( *myVHPtr ) );
1440			}
1441			else {
1442				if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1443					plog( "\tInvalid embedded alternate volume header at block %qd - error %d \n", myAlternateBlockNum, err );
1444				}
1445			}
1446		}
1447		else {
1448			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1449				plog( "\tBlock number %qd is not embedded alternate volume header \n", myAlternateBlockNum );
1450			}
1451		}
1452		(void) ReleaseVolumeBlock( GPtr->calculatedVCB, &myBlockDescriptor, kReleaseBlock );
1453	}
1454	else {
1455		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1456			plog( "\tcould not get embedded alternate volume header at %qd, err %d \n",
1457					myAlternateBlockNum, err );
1458		}
1459	}
1460
1461	// get the embedded primary volume header
1462	err = GetVolumeObjectBlock( myVOPtr, myPrimaryBlockNum, &myBlockDescriptor );
1463	if ( err == noErr ) {
1464		myVHPtr = (HFSPlusVolumeHeader	*) myBlockDescriptor.buffer;
1465		if ( myVHPtr->signature == kHFSPlusSigWord ) {
1466
1467			myVOPtr->primaryVHB = myPrimaryBlockNum;  		// save location
1468			myVOPtr->alternateVHB = myAlternateBlockNum;	// save location
1469			err = ValidVolumeHeader( myVHPtr );
1470			if ( err == noErr ) {
1471				myVOPtr->flags |= kVO_PriVHBOK;
1472				myVOPtr->totalEmbeddedSectors = myHFSPlusSectors;
1473
1474				// check to see if the primary and alternates are in sync.  3137809
1475				CompareVolHeaderBTreeSizes( GPtr, myVOPtr, myVHPtr, &myAltVolHeader );
1476			}
1477			else {
1478				if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1479					plog( "\tInvalid embedded primary volume header at block %qd - error %d \n", myPrimaryBlockNum, err );
1480				}
1481			}
1482		}
1483		else {
1484			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1485				plog( "\tBlock number %qd is not embedded primary volume header \n", myPrimaryBlockNum );
1486			}
1487		}
1488		(void) ReleaseVolumeBlock( GPtr->calculatedVCB, &myBlockDescriptor, kReleaseBlock );
1489	}
1490	else {
1491		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1492			plog( "\tcould not get embedded primary volume header at %qd, err %d \n",
1493					myPrimaryBlockNum, err );
1494		}
1495	}
1496
1497ExitRoutine:
1498	return;
1499
1500} /* GetEmbeddedVolumeHeaders */
1501
1502
1503//******************************************************************************
1504//	Routine:	CompareVolHeaderBTreeSizes
1505//
1506//	Function:	checks to see if the primary and alternate volume headers are in
1507//				sync with regards to the catalog and extents btree file size.  If
1508//				we find an anomaly we will give preference to the volume header
1509//				with the larger of the btree files since these files never shrink.
1510//				Added for radar #3137809.
1511//
1512// 	Result:		returns nothing.
1513//******************************************************************************
1514static void CompareVolHeaderBTreeSizes(	SGlobPtr GPtr,
1515										VolumeObjectPtr theVOPtr,
1516										HFSPlusVolumeHeader * thePriVHPtr,
1517										HFSPlusVolumeHeader * theAltVHPtr )
1518{
1519	int			weDisagree;
1520	int			usePrimary;
1521	int			useAlternate;
1522
1523	weDisagree = usePrimary = useAlternate = 0;
1524
1525	// we only check if both volume headers appear to be OK
1526	if ( (theVOPtr->flags & kVO_PriVHBOK) == 0 || (theVOPtr->flags & kVO_AltVHBOK) == 0  )
1527		return;
1528
1529	if ( thePriVHPtr->catalogFile.totalBlocks != theAltVHPtr->catalogFile.totalBlocks ) {
1530		// only continue if the B*Tree files both start at the same block number
1531		if ( thePriVHPtr->catalogFile.extents[0].startBlock == theAltVHPtr->catalogFile.extents[0].startBlock ) {
1532			weDisagree = 1;
1533			if ( thePriVHPtr->catalogFile.totalBlocks > theAltVHPtr->catalogFile.totalBlocks )
1534				usePrimary = 1;
1535			else
1536				useAlternate = 1;
1537			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1538				plog( "\tvolume headers disagree on catalog file total blocks - primary %d alternate %d \n",
1539						thePriVHPtr->catalogFile.totalBlocks, theAltVHPtr->catalogFile.totalBlocks );
1540			}
1541		}
1542	}
1543
1544	if ( thePriVHPtr->extentsFile.totalBlocks != theAltVHPtr->extentsFile.totalBlocks ) {
1545		// only continue if the B*Tree files both start at the same block number
1546		if ( thePriVHPtr->extentsFile.extents[0].startBlock == theAltVHPtr->extentsFile.extents[0].startBlock ) {
1547			weDisagree = 1;
1548			if ( thePriVHPtr->extentsFile.totalBlocks > theAltVHPtr->extentsFile.totalBlocks )
1549				usePrimary = 1;
1550			else
1551				useAlternate = 1;
1552			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1553				plog( "\tvolume headers disagree on extents file total blocks - primary %d alternate %d \n",
1554						thePriVHPtr->extentsFile.totalBlocks, theAltVHPtr->extentsFile.totalBlocks );
1555			}
1556		}
1557	}
1558
1559	if ( weDisagree == 0 )
1560		return;
1561
1562	// we have a disagreement.  we resolve the issue by using the larger of the two.
1563	if ( usePrimary == 1 && useAlternate == 1 ) {
1564		// this should never happen, but if it does, bail without choosing a preference
1565		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1566			plog( "\tvolume headers disagree but there is confusion on which to use \n" );
1567		}
1568		return;
1569	}
1570
1571	if ( usePrimary == 1 ) {
1572		// mark alternate as bogus
1573		theVOPtr->flags &= ~kVO_AltVHBOK;
1574	}
1575	else if ( useAlternate == 1 ) {
1576		// mark primary as bogus
1577		theVOPtr->flags &= ~kVO_PriVHBOK;
1578	}
1579
1580	return;
1581
1582} /* CompareVolHeaderBTreeSizes */
1583
1584
1585/*
1586 * This code should be removed after debugging is completed.
1587 */
1588#include <ctype.h>
1589
1590#ifndef MIN
1591#define MIN(a, b) \
1592	({ __typeof(a) _a = (a); __typeof(b) _b = (b);	\
1593		(_a < _b) ? _a : _b; })
1594#endif
1595
1596
1597enum { WIDTH = 16, };
1598
1599void
1600DumpData(const void *data, size_t len)
1601{
1602	unsigned char *base = (unsigned char*)data;
1603	unsigned char *end = base + len;
1604	unsigned char *cp = base;
1605	int allzeroes = 0;
1606
1607	while (cp < end) {
1608		unsigned char *tend = MIN(end, cp + WIDTH);
1609		unsigned char *tmp;
1610		int i;
1611		size_t gap = (cp + WIDTH) - tend;
1612
1613		if (gap != 0 || tend == end)
1614			allzeroes = 0;
1615		if (allzeroes) {
1616			for (tmp = cp; tmp < tend; tmp++) {
1617				if (*tmp) {
1618					allzeroes = 0;
1619					break;
1620				}
1621			}
1622			if (allzeroes == 1) {
1623				fprintf(stderr, ". . .\n");
1624				allzeroes = 2;
1625			}
1626			if (allzeroes) {
1627				cp += WIDTH;
1628				continue;
1629			}
1630		}
1631		allzeroes = 1;
1632
1633		fprintf(stderr, "%04x:  ", (int)(cp - base));
1634		for (i = 0, tmp = cp; tmp < tend; tmp++) {
1635			fprintf(stderr, "%02x", *tmp);
1636			if (++i % 2 == 0)
1637				fprintf(stderr, " ");
1638			if (*tmp)
1639				allzeroes = 0;
1640		}
1641		for (i = gap; i >= 0; i--) {
1642			fprintf(stderr, "  ");
1643			if (i % 2 == 1)
1644				fprintf(stderr, " ");
1645		}
1646		fprintf(stderr, "    |");
1647		for (tmp = cp; tmp < tend; tmp++) {
1648			fprintf(stderr, "%c", isalnum(*tmp) ? *tmp : '.');
1649		}
1650		for (i = 0; i < gap; i++) {
1651			fprintf(stderr, " ");
1652		}
1653		fprintf(stderr, "|\n");
1654		cp += WIDTH;
1655	}
1656
1657	return;
1658
1659}
1660//******************************************************************************
1661//	Routine:	VolumeObjectIsValid
1662//
1663//	Function:	determine if the volume represented by our VolumeObject is a
1664//				valid volume type (i.e. not unknown type)
1665//
1666// 	Result:		returns true if volume is known volume type (i.e. HFS, HFS+)
1667//				false otherwise.
1668//******************************************************************************
1669Boolean VolumeObjectIsValid(void)
1670{
1671	VolumeObjectPtr	myVOPtr = GetVolumeObjectPtr();
1672	Boolean retval = false;
1673
1674	/* Check if the type is unknown type */
1675	if (myVOPtr->volumeType == kUnknownVolumeType) {
1676		pwarn("volumeType is %d\n", kUnknownVolumeType);
1677		goto done;
1678	}
1679
1680	/* Check if it is HFS+ volume */
1681	if (VolumeObjectIsHFSPlus() == true) {
1682		retval = true;
1683		goto done;
1684	}
1685
1686	/* Check if it is HFS volume */
1687	if (VolumeObjectIsHFS() == true) {
1688		retval = true;
1689		goto done;
1690	}
1691
1692done:
1693	/*
1694	 * This code should be removed after debugging is done.
1695	 */
1696	if (retval == false) {
1697		UInt64 myBlockNum;
1698		VolumeObjectPtr myVOPtr;
1699		BlockDescriptor theBlockDesc;
1700		OSErr err;
1701
1702		myVOPtr = GetVolumeObjectPtr();
1703		GetVolumeObjectBlockNum(&myBlockNum);
1704		err = GetVolumeBlock(myVOPtr->vcbPtr, myBlockNum, kGetBlock, &theBlockDesc);
1705		if (err != noErr) {
1706			fprintf(stderr, "%s: Cannot GetVolumetBlock: %d\n", __FUNCTION__, err);
1707		} else {
1708			uint8_t *ptr = (uint8_t*)theBlockDesc.buffer;
1709			DumpData(ptr, theBlockDesc.blockSize);
1710			ReleaseVolumeBlock(myVOPtr->vcbPtr, &theBlockDesc, kReleaseBlock);
1711		}
1712	}
1713	return retval;
1714} /* VolumeObjectIsValid */
1715
1716//******************************************************************************
1717//	Routine:	VolumeObjectIsHFSPlus
1718//
1719//	Function:	determine if the volume represented by our VolumeObject is an
1720//				HFS+ volume (pure or embedded).
1721//
1722// 	Result:		returns true if volume is pure HFS+ or embedded HFS+ else false.
1723//******************************************************************************
1724Boolean VolumeObjectIsHFSPlus( void )
1725{
1726	VolumeObjectPtr				myVOPtr;
1727
1728	myVOPtr = GetVolumeObjectPtr( );
1729
1730	if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType ||
1731	     myVOPtr->volumeType == kPureHFSPlusVolumeType ) {
1732		return( true );
1733	}
1734
1735	return( false );
1736
1737} /* VolumeObjectIsHFSPlus */
1738
1739
1740//******************************************************************************
1741//	Routine:	VolumeObjectIsHFSX
1742//
1743//	Function:	determine if the volume represented by our VolumeObject is an
1744//				HFSX volume (pure or embedded)
1745//
1746// 	Result:		returns true if volume is pure HFSX or embedded HFSX else false.
1747//******************************************************************************
1748
1749Boolean VolumeObjectIsHFSX(SGlobPtr GPtr)
1750{
1751	OSErr err;
1752	int result = false;
1753	HFSMasterDirectoryBlock *mdbp;
1754	SVCB *vcb = GPtr->calculatedVCB;
1755	BlockDescriptor block;
1756
1757#define kIDSector 2
1758	err = GetVolumeBlock(vcb, kIDSector, kGetBlock, &block);
1759	if (err) return (false);
1760
1761	mdbp = (HFSMasterDirectoryBlock *)block.buffer;
1762	if (mdbp->drSigWord == kHFSXSigWord) {
1763		result = true;
1764	} else if (mdbp->drSigWord == kHFSSigWord) {
1765		if (mdbp->drEmbedSigWord == kHFSXSigWord) {
1766			result = true;
1767		}
1768	}
1769
1770	(void) ReleaseVolumeBlock(vcb, &block, kReleaseBlock);
1771
1772	return( result );
1773} /* VolumeObjectIsHFSX */
1774
1775//******************************************************************************
1776//	Routine:	VolumeObjectIsHFS
1777//
1778//	Function:	determine if the volume represented by our VolumeObject is an
1779//				HFS (standard) volume.
1780//
1781// 	Result:		returns true if HFS (standard) volume.
1782//******************************************************************************
1783Boolean VolumeObjectIsHFS( void )
1784{
1785	VolumeObjectPtr				myVOPtr;
1786
1787	myVOPtr = GetVolumeObjectPtr( );
1788
1789	if ( myVOPtr->volumeType == kHFSVolumeType )
1790		return( true );
1791
1792	return( false );
1793
1794} /* VolumeObjectIsHFS */
1795
1796
1797//******************************************************************************
1798//	Routine:	VolumeObjectIsEmbeddedHFSPlus
1799//
1800//	Function:	determine if the volume represented by our VolumeObject is an
1801//				embedded HFS plus volume.
1802//
1803// 	Result:		returns true if embedded HFS plus volume.
1804//******************************************************************************
1805Boolean VolumeObjectIsEmbeddedHFSPlus( void )
1806{
1807	VolumeObjectPtr				myVOPtr;
1808
1809	myVOPtr = GetVolumeObjectPtr( );
1810
1811	if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType )
1812		return( true );
1813
1814	return( false );
1815
1816} /* VolumeObjectIsEmbeddedHFSPlus */
1817
1818
1819//******************************************************************************
1820//	Routine:	VolumeObjectIsPureHFSPlus
1821//
1822//	Function:	determine if the volume represented by our VolumeObject is an
1823//				pure HFS plus volume.
1824//
1825// 	Result:		returns true if pure HFS plus volume.
1826//******************************************************************************
1827Boolean VolumeObjectIsPureHFSPlus( void )
1828{
1829	VolumeObjectPtr				myVOPtr;
1830
1831	myVOPtr = GetVolumeObjectPtr( );
1832
1833	if ( myVOPtr->volumeType == kPureHFSPlusVolumeType )
1834		return( true );
1835
1836	return( false );
1837
1838} /* VolumeObjectIsPureHFSPlus */
1839
1840
1841//******************************************************************************
1842//	Routine:	GetVolumeObjectPtr
1843//
1844//	Function:	Accessor routine to get a pointer to our VolumeObject structure.
1845//
1846// 	Result:		returns pointer to our VolumeObject.
1847//******************************************************************************
1848VolumeObjectPtr GetVolumeObjectPtr( void )
1849{
1850	static VolumeObject		myVolumeObject;
1851	static int				myInited = 0;
1852
1853	if ( myInited == 0 ) {
1854		myInited++;
1855		bzero( &myVolumeObject, sizeof(myVolumeObject) );
1856	}
1857
1858	return( &myVolumeObject );
1859
1860} /* GetVolumeObjectPtr */
1861
1862
1863//******************************************************************************
1864//	Routine:	CheckEmbeddedVolInfoInMDBs
1865//
1866//	Function:	Check the primary and alternate MDB to see if the embedded volume
1867//				information (drEmbedSigWord and drEmbedExtent) match.
1868//
1869// 	Result:		NA
1870//******************************************************************************
1871void CheckEmbeddedVolInfoInMDBs( SGlobPtr GPtr )
1872{
1873	OSErr						err;
1874	Boolean						primaryIsDamaged = false;
1875	Boolean						alternateIsDamaged = false;
1876	VolumeObjectPtr				myVOPtr;
1877	HFSMasterDirectoryBlock *	myPriMDBPtr;
1878	HFSMasterDirectoryBlock *	myAltMDBPtr;
1879	UInt64						myOffset;
1880	UInt64						mySectors;
1881	BlockDescriptor  			myPrimary;
1882	BlockDescriptor  			myAlternate;
1883
1884	myVOPtr = GetVolumeObjectPtr( );
1885	myPrimary.buffer = NULL;
1886	myAlternate.buffer = NULL;
1887
1888	// we only check this if primary and alternate are OK at this point.  OK means
1889	// that the primary and alternate MDBs have the correct signature and at least
1890	// one of them points to a valid embedded HFS+ volume.
1891	if ( VolumeObjectIsEmbeddedHFSPlus( ) == false ||
1892		 (myVOPtr->flags & kVO_PriMDBOK) == 0 || (myVOPtr->flags & kVO_AltMDBOK) == 0 )
1893		return;
1894
1895	err = GetVolumeObjectPrimaryMDB( &myPrimary );
1896	if ( err != noErr ) {
1897		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1898			plog( "\tcould not get primary MDB \n" );
1899		}
1900		goto ExitThisRoutine;
1901	}
1902	myPriMDBPtr = (HFSMasterDirectoryBlock	*) myPrimary.buffer;
1903	err = GetVolumeObjectAlternateMDB( &myAlternate );
1904	if ( err != noErr ) {
1905		if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1906			plog( "\tcould not get alternate MDB \n" );
1907		}
1908		goto ExitThisRoutine;
1909	}
1910	myAltMDBPtr = (HFSMasterDirectoryBlock	*) myAlternate.buffer;
1911
1912	// bail if everything looks good.  NOTE - we can bail if drEmbedExtent info
1913	// is the same in the primary and alternate MDB because we know one of them is
1914	// valid (or VolumeObjectIsEmbeddedHFSPlus would be false and we would not be
1915	// here).
1916	if ( myPriMDBPtr->drEmbedSigWord == kHFSPlusSigWord &&
1917		 myAltMDBPtr->drEmbedSigWord == kHFSPlusSigWord &&
1918		 myPriMDBPtr->drEmbedExtent.blockCount == myAltMDBPtr->drEmbedExtent.blockCount &&
1919		 myPriMDBPtr->drEmbedExtent.startBlock == myAltMDBPtr->drEmbedExtent.startBlock )
1920		goto ExitThisRoutine;
1921
1922	// we know that VolumeObject.embeddedOffset and VolumeObject.totalEmbeddedSectors
1923	// are correct so we will verify the info in each MDB calculates to these values.
1924	myOffset = (myPriMDBPtr->drEmbedExtent.startBlock * myPriMDBPtr->drAlBlkSiz) +
1925			   (myPriMDBPtr->drAlBlSt * Blk_Size);
1926	mySectors = (myPriMDBPtr->drAlBlkSiz / Blk_Size) * myPriMDBPtr->drEmbedExtent.blockCount;
1927
1928	if ( myOffset != myVOPtr->embeddedOffset || mySectors != myVOPtr->totalEmbeddedSectors )
1929		primaryIsDamaged = true;
1930
1931	myOffset = (myAltMDBPtr->drEmbedExtent.startBlock * myAltMDBPtr->drAlBlkSiz) +
1932			   (myAltMDBPtr->drAlBlSt * Blk_Size);
1933	mySectors = (myAltMDBPtr->drAlBlkSiz / Blk_Size) * myAltMDBPtr->drEmbedExtent.blockCount;
1934
1935	if ( myOffset != myVOPtr->embeddedOffset || mySectors != myVOPtr->totalEmbeddedSectors )
1936		alternateIsDamaged = true;
1937
1938	// now check drEmbedSigWord if everything else is OK
1939	if ( primaryIsDamaged == false && alternateIsDamaged == false ) {
1940		if ( myPriMDBPtr->drEmbedSigWord != kHFSPlusSigWord )
1941			primaryIsDamaged = true;
1942		else if ( myAltMDBPtr->drEmbedSigWord != kHFSPlusSigWord )
1943			alternateIsDamaged = true;
1944	}
1945
1946	if ( primaryIsDamaged || alternateIsDamaged ) {
1947		GPtr->VIStat |= S_WMDB;
1948		WriteError( GPtr, E_MDBDamaged, 7, 0 );
1949		if ( primaryIsDamaged ) {
1950			myVOPtr->flags &= ~kVO_PriMDBOK; // mark the primary MDB as damaged
1951			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
1952				plog("\tinvalid primary wrapper MDB \n");
1953		}
1954		else {
1955			myVOPtr->flags &= ~kVO_AltMDBOK; // mark the alternate MDB as damaged
1956			if ( fsckGetVerbosity(GPtr->context) >= kDebugLog )
1957				plog("\tinvalid alternate wrapper MDB \n");
1958		}
1959	}
1960
1961ExitThisRoutine:
1962	if ( myPrimary.buffer != NULL )
1963		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kReleaseBlock );
1964	if ( myAlternate.buffer != NULL )
1965		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kReleaseBlock );
1966
1967	return;
1968
1969} /* CheckEmbeddedVolInfoInMDBs */
1970
1971
1972//******************************************************************************
1973//	Routine:	ValidVolumeHeader
1974//
1975//	Function:	Run some sanity checks to make sure the HFSPlusVolumeHeader is valid
1976//
1977// 	Result:		error
1978//******************************************************************************
1979OSErr	ValidVolumeHeader( HFSPlusVolumeHeader *volumeHeader )
1980{
1981	OSErr	err;
1982
1983	if ((volumeHeader->signature == kHFSPlusSigWord && volumeHeader->version == kHFSPlusVersion) ||
1984	    (volumeHeader->signature == kHFSXSigWord && volumeHeader->version == kHFSXVersion))
1985	{
1986		if ( (volumeHeader->blockSize != 0) && ((volumeHeader->blockSize & 0x01FF) == 0) )			//	non zero multiple of 512
1987			err = noErr;
1988		else
1989			err = badMDBErr;							//��	I want badVolumeHeaderErr in Errors.i
1990	}
1991	else
1992	{
1993		err = noMacDskErr;
1994	}
1995
1996	return( err );
1997}
1998
1999
2000//_______________________________________________________________________
2001//
2002//	InitBTreeHeader
2003//
2004//	This routine initializes a B-Tree header.
2005//
2006//	Note: Since large volumes will have bigger b-trees they need to
2007//	have map nodes setup.
2008//_______________________________________________________________________
2009
2010void InitBTreeHeader (UInt32 fileSize, UInt32 clumpSize, UInt16 nodeSize, UInt16 recordCount, UInt16 keySize,
2011						UInt32 attributes, UInt32 *mapNodes, void *buffer)
2012{
2013	UInt32		 nodeCount;
2014	UInt32		 usedNodes;
2015	UInt32		 nodeBitsInHeader;
2016	BTHeaderRec	 *bth;
2017	BTNodeDescriptor *ndp;
2018	UInt32		*bitMapPtr;
2019	SInt16		*offsetPtr;
2020
2021
2022	ClearMemory(buffer, nodeSize);			// start out with clean node
2023
2024	nodeCount = fileSize / nodeSize;
2025	nodeBitsInHeader = 8 * (nodeSize - sizeof(BTNodeDescriptor) - sizeof(BTHeaderRec) - kBTreeHeaderUserBytes - 4*sizeof(SInt16));
2026
2027	usedNodes =	1;							// header takes up one node
2028	*mapNodes = 0;							// number of map nodes initially (0)
2029
2030
2031	// FILL IN THE NODE DESCRIPTOR:
2032	ndp = (BTNodeDescriptor*) buffer;	// point to node descriptor
2033
2034	ndp->kind = kBTHeaderNode;		// this node contains the B-tree header
2035	ndp->numRecords = 3;			// there are 3 records (header, map, and user)
2036
2037	if (nodeCount > nodeBitsInHeader)		// do we need additional map nodes?
2038	{
2039		UInt32	nodeBitsInMapNode;
2040
2041		nodeBitsInMapNode = 8 * (nodeSize - sizeof(BTNodeDescriptor) - 2*sizeof(SInt16) - 2);	//�� why (-2) at end???
2042
2043		if (recordCount > 0)			// catalog B-tree?
2044			ndp->fLink = 2;			// link points to initial map node
2045											//�� Assumes all records will fit in one node.  It would be better
2046											//�� to put the map node(s) first, then the records.
2047		else
2048			ndp->fLink = 1;			// link points to initial map node
2049
2050		*mapNodes = (nodeCount - nodeBitsInHeader + (nodeBitsInMapNode - 1)) / nodeBitsInMapNode;
2051		usedNodes += *mapNodes;
2052	}
2053
2054	// FILL IN THE HEADER RECORD:
2055	bth = (BTHeaderRec*) ((char*)buffer + sizeof(BTNodeDescriptor));	// point to header
2056
2057	if (recordCount > 0)
2058	{
2059		++usedNodes;								// one more node will be used
2060
2061		bth->treeDepth = 1;							// tree depth is one level (leaf)
2062		bth->rootNode  = 1;							// root node is also leaf
2063		bth->firstLeafNode = 1;						// first leaf node
2064		bth->lastLeafNode = 1;						// last leaf node
2065	}
2066
2067	bth->attributes	  = attributes;					// flags for 16-bit key lengths, and variable sized index keys
2068	bth->leafRecords  = recordCount;				// total number of data records
2069	bth->nodeSize	  = nodeSize;					// size of a node
2070	bth->maxKeyLength = keySize;					// maximum length of a key
2071	bth->totalNodes	  = nodeCount;					// total number of nodes
2072	bth->freeNodes	  = nodeCount - usedNodes;		// number of free nodes
2073	bth->clumpSize	  = clumpSize;					//
2074//	bth->btreeType	  = 0;						// 0 = meta data B-tree
2075
2076
2077	// FILL IN THE MAP RECORD:
2078	bitMapPtr = (UInt32*) ((Byte*) buffer + sizeof(BTNodeDescriptor) + sizeof(BTHeaderRec) + kBTreeHeaderUserBytes); // point to bitmap
2079
2080	// MARK NODES THAT ARE IN USE:
2081	// Note - worst case (32MB alloc blk) will have only 18 nodes in use.
2082	*bitMapPtr = ~((UInt32) 0xFFFFFFFF >> usedNodes);
2083
2084
2085	// PLACE RECORD OFFSETS AT THE END OF THE NODE:
2086	offsetPtr = (SInt16*) ((Byte*) buffer + nodeSize - 4*sizeof(SInt16));
2087
2088	*offsetPtr++ = sizeof(BTNodeDescriptor) + sizeof(BTHeaderRec) + kBTreeHeaderUserBytes + nodeBitsInHeader/8;	// offset to free space
2089	*offsetPtr++ = sizeof(BTNodeDescriptor) + sizeof(BTHeaderRec) + kBTreeHeaderUserBytes;						// offset to allocation map
2090	*offsetPtr++ = sizeof(BTNodeDescriptor) + sizeof(BTHeaderRec);												// offset to user space
2091	*offsetPtr   = sizeof(BTNodeDescriptor);										// offset to BTH
2092}
2093
2094/*------------------------------------------------------------------------------
2095
2096Routine:	CalculateItemCount
2097
2098Function:	determines number of items for progress feedback
2099
2100Input:		vRefNum:  the volume to count items
2101
2102Output:		number of items
2103
2104------------------------------------------------------------------------------*/
2105
2106void	CalculateItemCount( SGlob *GPtr, UInt64 *itemCount, UInt64 *onePercent )
2107{
2108	BTreeControlBlock	*btcb;
2109	VolumeObjectPtr		myVOPtr;
2110	UInt64				items;
2111	UInt32				realFreeNodes;
2112	SVCB				*vcb  = GPtr->calculatedVCB;
2113
2114	/* each bitmap segment is an item */
2115	myVOPtr = GetVolumeObjectPtr( );
2116	items = GPtr->calculatedVCB->vcbTotalBlocks / 1024;
2117
2118	//
2119	// Items is the used node count and leaf record count for each btree...
2120	//
2121
2122	btcb = (BTreeControlBlock*) vcb->vcbCatalogFile->fcbBtree;
2123	realFreeNodes = ((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount;
2124	items += (2 * btcb->leafRecords) + (btcb->totalNodes - realFreeNodes);
2125
2126	btcb = (BTreeControlBlock*) vcb->vcbExtentsFile->fcbBtree;
2127	realFreeNodes = ((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount;
2128	items += btcb->leafRecords + (btcb->totalNodes - realFreeNodes);
2129
2130	if ( vcb->vcbAttributesFile != NULL )
2131	{
2132		btcb = (BTreeControlBlock*) vcb->vcbAttributesFile->fcbBtree;
2133		realFreeNodes = ((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount;
2134
2135		items += (btcb->leafRecords + (btcb->totalNodes - realFreeNodes));
2136	}
2137
2138	*onePercent = items/ 100;
2139
2140	//
2141	//	[2239291]  We're calculating the progress for the wrapper and the embedded volume separately, which
2142	//	confuses the caller (since they see the progress jump to a large percentage while checking the wrapper,
2143	//	then jump to a small percentage when starting to check the embedded volume).  To avoid this behavior,
2144	//	we pretend the wrapper has 100 times as many items as it really does.  This means the progress will
2145	//	never exceed 1% for the wrapper.
2146	//
2147/* fsck_hfs doesn't deal wih the wrapper at this time (8.29.2002)
2148	if ( (myVOPtr->volumeType == kEmbededHFSPlusVolumeType) && (GPtr->inputFlags & examineWrapperMask) )
2149		items *= 100; */
2150
2151	//	Add en extra � 5% to smooth the progress
2152	items += *onePercent * 5;
2153
2154	*itemCount = items;
2155}
2156
2157
2158SFCB* ResolveFCB(short fileRefNum)
2159{
2160	return( (SFCB*)((unsigned long)GetFCBSPtr() + (unsigned long)fileRefNum)  );
2161}
2162
2163
2164//******************************************************************************
2165//	Routine:	SetupFCB fills in the FCB info
2166//
2167//	Returns:	The filled up FCB
2168//******************************************************************************
2169void	SetupFCB( SVCB *vcb, SInt16 refNum, UInt32 fileID, UInt32 fileClumpSize )
2170{
2171	SFCB *fcb;
2172
2173	fcb = ResolveFCB(refNum);
2174
2175	fcb->fcbFileID		= fileID;
2176	fcb->fcbVolume		= vcb;
2177	fcb->fcbClumpSize	= fileClumpSize;
2178}
2179
2180
2181//******************************************************************************
2182//
2183//	Routine:	ResolveFileRefNum
2184//
2185//	Purpose:	Return a file reference number for a given file control block
2186//				pointer.
2187//
2188//	Input:
2189//		fileCtrlBlockPtr	Pointer to the SFCB
2190//
2191//	Output:
2192//		result				File reference number,
2193//							or 0 if fileCtrlBlockPtr is invalid
2194//
2195pascal short ResolveFileRefNum(SFCB * fileCtrlBlockPtr)
2196{
2197	return( (unsigned long)fileCtrlBlockPtr - (unsigned long)GetFCBSPtr() );
2198}
2199
2200
2201
2202Ptr gFCBSPtr;
2203
2204void	SetFCBSPtr( Ptr value )
2205{
2206	gFCBSPtr = value;
2207}
2208
2209Ptr	GetFCBSPtr( void )
2210{
2211	return (gFCBSPtr);
2212}
2213
2214
2215//_______________________________________________________________________
2216//
2217//	Routine:	FlushVolumeControlBlock
2218//	Arguments:	SVCB		*vcb
2219//	Output:		OSErr			err
2220//
2221//	Function: 	Flush volume information to either the HFSPlusVolumeHeader
2222//	of the Master Directory Block
2223//_______________________________________________________________________
2224
2225OSErr	FlushVolumeControlBlock( SVCB *vcb )
2226{
2227	OSErr				err;
2228	HFSPlusVolumeHeader	*volumeHeader;
2229	SFCB				*fcb;
2230	BlockDescriptor  	block;
2231
2232	if ( ! IsVCBDirty( vcb ) )			//	if it's not dirty
2233		return( noErr );
2234
2235	block.buffer = NULL;
2236	err = GetVolumeObjectPrimaryBlock( &block );
2237	if ( err != noErr )
2238	{
2239		// attempt to fix the primary with alternate
2240		if ( block.buffer != NULL ) {
2241			(void) ReleaseVolumeBlock( vcb, &block, kReleaseBlock );
2242			block.buffer = NULL;
2243		}
2244
2245		err = VolumeObjectFixPrimaryBlock( );
2246		ReturnIfError( err );
2247
2248		// should be able to get it now
2249		err = GetVolumeObjectPrimaryBlock( &block );
2250		ReturnIfError( err );
2251	}
2252
2253	if ( vcb->vcbSignature == kHFSPlusSigWord )
2254	{
2255		volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
2256
2257		// 2005507, Keep the MDB creation date and HFSPlusVolumeHeader creation date in sync.
2258		if ( vcb->vcbEmbeddedOffset != 0 )  // It's a wrapped HFS+ volume
2259		{
2260			HFSMasterDirectoryBlock		*mdb;
2261			BlockDescriptor  			mdb_block;
2262
2263			mdb_block.buffer = NULL;
2264			err = GetVolumeObjectPrimaryMDB( &mdb_block );
2265			if ( err == noErr )
2266			{
2267				mdb = (HFSMasterDirectoryBlock	*) mdb_block.buffer;
2268				if ( mdb->drCrDate != vcb->vcbCreateDate )  // The creation date changed
2269				{
2270					mdb->drCrDate = vcb->vcbCreateDate;
2271					(void) ReleaseVolumeBlock(vcb, &mdb_block, kForceWriteBlock);
2272					mdb_block.buffer = NULL;
2273				}
2274			}
2275			if ( mdb_block.buffer != NULL )
2276				(void) ReleaseVolumeBlock(vcb, &mdb_block, kReleaseBlock);
2277		}
2278
2279		volumeHeader->attributes		= vcb->vcbAttributes;
2280		volumeHeader->lastMountedVersion = kFSCKMountVersion;
2281		volumeHeader->createDate		= vcb->vcbCreateDate;  // NOTE: local time, not GMT!
2282		volumeHeader->modifyDate		= vcb->vcbModifyDate;
2283		volumeHeader->backupDate		= vcb->vcbBackupDate;
2284		volumeHeader->checkedDate		= vcb->vcbCheckedDate;
2285		volumeHeader->fileCount			= vcb->vcbFileCount;
2286		volumeHeader->folderCount		= vcb->vcbFolderCount;
2287		volumeHeader->blockSize			= vcb->vcbBlockSize;
2288		volumeHeader->totalBlocks		= vcb->vcbTotalBlocks;
2289		volumeHeader->freeBlocks		= vcb->vcbFreeBlocks;
2290		volumeHeader->nextAllocation		= vcb->vcbNextAllocation;
2291		volumeHeader->rsrcClumpSize		= vcb->vcbRsrcClumpSize;
2292		volumeHeader->dataClumpSize		= vcb->vcbDataClumpSize;
2293		volumeHeader->nextCatalogID		= vcb->vcbNextCatalogID;
2294		volumeHeader->writeCount		= vcb->vcbWriteCount;
2295		volumeHeader->encodingsBitmap		= vcb->vcbEncodingsBitmap;
2296
2297		//���should we use the vcb or fcb clumpSize values ????? -djb
2298		volumeHeader->allocationFile.clumpSize	= vcb->vcbAllocationFile->fcbClumpSize;
2299		volumeHeader->extentsFile.clumpSize	= vcb->vcbExtentsFile->fcbClumpSize;
2300		volumeHeader->catalogFile.clumpSize	= vcb->vcbCatalogFile->fcbClumpSize;
2301
2302		CopyMemory( vcb->vcbFinderInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo) );
2303
2304		fcb = vcb->vcbExtentsFile;
2305		CopyMemory( fcb->fcbExtents32, volumeHeader->extentsFile.extents, sizeof(HFSPlusExtentRecord) );
2306		volumeHeader->extentsFile.logicalSize = fcb->fcbLogicalSize;
2307		volumeHeader->extentsFile.totalBlocks = fcb->fcbPhysicalSize / vcb->vcbBlockSize;
2308
2309		fcb = vcb->vcbCatalogFile;
2310		CopyMemory( fcb->fcbExtents32, volumeHeader->catalogFile.extents, sizeof(HFSPlusExtentRecord) );
2311		volumeHeader->catalogFile.logicalSize = fcb->fcbLogicalSize;
2312		volumeHeader->catalogFile.totalBlocks = fcb->fcbPhysicalSize / vcb->vcbBlockSize;
2313
2314		fcb = vcb->vcbAllocationFile;
2315		CopyMemory( fcb->fcbExtents32, volumeHeader->allocationFile.extents, sizeof(HFSPlusExtentRecord) );
2316		volumeHeader->allocationFile.logicalSize = fcb->fcbLogicalSize;
2317		volumeHeader->allocationFile.totalBlocks = fcb->fcbPhysicalSize / vcb->vcbBlockSize;
2318
2319		if (vcb->vcbAttributesFile != NULL)	//	Only update fields if an attributes file existed and was open
2320		{
2321			fcb = vcb->vcbAttributesFile;
2322			CopyMemory( fcb->fcbExtents32, volumeHeader->attributesFile.extents, sizeof(HFSPlusExtentRecord) );
2323			volumeHeader->attributesFile.logicalSize = fcb->fcbLogicalSize;
2324			volumeHeader->attributesFile.clumpSize = fcb->fcbClumpSize;
2325			volumeHeader->attributesFile.totalBlocks = fcb->fcbPhysicalSize / vcb->vcbBlockSize;
2326		}
2327	}
2328	else
2329	{
2330		HFSMasterDirectoryBlock	*mdbP;
2331
2332		mdbP = (HFSMasterDirectoryBlock	*) block.buffer;
2333
2334		mdbP->drCrDate    = vcb->vcbCreateDate;
2335		mdbP->drLsMod     = vcb->vcbModifyDate;
2336		mdbP->drAtrb      = (UInt16)vcb->vcbAttributes;
2337		mdbP->drClpSiz    = vcb->vcbDataClumpSize;
2338		mdbP->drNxtCNID   = vcb->vcbNextCatalogID;
2339		mdbP->drFreeBks   = vcb->vcbFreeBlocks;
2340		mdbP->drXTClpSiz  = vcb->vcbExtentsFile->fcbClumpSize;
2341		mdbP->drCTClpSiz  = vcb->vcbCatalogFile->fcbClumpSize;
2342
2343		mdbP->drNmFls     = vcb->vcbNmFls;
2344		mdbP->drNmRtDirs  = vcb->vcbNmRtDirs;
2345		mdbP->drFilCnt    = vcb->vcbFileCount;
2346		mdbP->drDirCnt    = vcb->vcbFolderCount;
2347
2348		fcb = vcb->vcbExtentsFile;
2349		CopyMemory( fcb->fcbExtents16, mdbP->drXTExtRec, sizeof( mdbP->drXTExtRec ) );
2350
2351		fcb = vcb->vcbCatalogFile;
2352		CopyMemory( fcb->fcbExtents16, mdbP->drCTExtRec, sizeof( mdbP->drCTExtRec ) );
2353	}
2354
2355	//--	Write the VHB/MDB out by releasing the block dirty
2356	if ( block.buffer != NULL ) {
2357		err = ReleaseVolumeBlock(vcb, &block, kForceWriteBlock);
2358		block.buffer = NULL;
2359	}
2360	MarkVCBClean( vcb );
2361
2362	return( err );
2363}
2364
2365
2366//_______________________________________________________________________
2367//
2368//	Routine:	FlushAlternateVolumeControlBlock
2369//	Arguments:	SVCB		*vcb
2370//				Boolean			ifHFSPlus
2371//	Output:		OSErr			err
2372//
2373//	Function: 	Flush volume information to either the Alternate HFSPlusVolumeHeader or the
2374//				Alternate Master Directory Block.  Called by the BTree when the catalog
2375//				or extent files grow.  Simply BlockMoves the original to the alternate
2376//				location.
2377//_______________________________________________________________________
2378
2379OSErr	FlushAlternateVolumeControlBlock( SVCB *vcb, Boolean isHFSPlus )
2380{
2381	OSErr  				err;
2382	VolumeObjectPtr		myVOPtr;
2383	UInt64				myBlockNum;
2384	BlockDescriptor  	pri_block, alt_block;
2385
2386	pri_block.buffer = NULL;
2387	alt_block.buffer = NULL;
2388	myVOPtr = GetVolumeObjectPtr( );
2389
2390	err = FlushVolumeControlBlock( vcb );
2391	err = GetVolumeObjectPrimaryBlock( &pri_block );
2392
2393	// invalidate if we have not marked the primary as OK
2394	if ( VolumeObjectIsHFS( ) ) {
2395		if ( (myVOPtr->flags & kVO_PriMDBOK) == 0 )
2396			err = badMDBErr;
2397	}
2398	else if ( (myVOPtr->flags & kVO_PriVHBOK) == 0 ) {
2399		err = badMDBErr;
2400	}
2401	if ( err != noErr )
2402		goto ExitThisRoutine;
2403
2404	GetVolumeObjectAlternateBlockNum( &myBlockNum );
2405	if ( myBlockNum != 0 ) {
2406		// we don't care if this is an invalid MDB / VHB since we will write over it
2407		err = GetVolumeObjectAlternateBlock( &alt_block );
2408		if ( err == noErr || err == badMDBErr || err == noMacDskErr ) {
2409			CopyMemory( pri_block.buffer, alt_block.buffer, Blk_Size );
2410			(void) ReleaseVolumeBlock(vcb, &alt_block, kForceWriteBlock);
2411			alt_block.buffer = NULL;
2412		}
2413	}
2414
2415ExitThisRoutine:
2416	if ( pri_block.buffer != NULL )
2417		(void) ReleaseVolumeBlock( vcb, &pri_block, kReleaseBlock );
2418	if ( alt_block.buffer != NULL )
2419		(void) ReleaseVolumeBlock( vcb, &alt_block, kReleaseBlock );
2420
2421	return( err );
2422}
2423
2424void
2425ConvertToHFSPlusExtent( const HFSExtentRecord oldExtents, HFSPlusExtentRecord newExtents)
2426{
2427	UInt16	i;
2428
2429	// go backwards so we can convert in place!
2430
2431	for (i = kHFSPlusExtentDensity-1; i > 2; --i)
2432	{
2433		newExtents[i].blockCount = 0;
2434		newExtents[i].startBlock = 0;
2435	}
2436
2437	newExtents[2].blockCount = oldExtents[2].blockCount;
2438	newExtents[2].startBlock = oldExtents[2].startBlock;
2439	newExtents[1].blockCount = oldExtents[1].blockCount;
2440	newExtents[1].startBlock = oldExtents[1].startBlock;
2441	newExtents[0].blockCount = oldExtents[0].blockCount;
2442	newExtents[0].startBlock = oldExtents[0].startBlock;
2443}
2444
2445
2446
2447OSErr	CacheWriteInPlace( SVCB *vcb, UInt32 fileRefNum,  HIOParam *iopb, UInt64 currentPosition, UInt32 maximumBytes, UInt32 *actualBytes )
2448{
2449	OSErr err;
2450	UInt64 diskBlock;
2451	UInt32 contiguousBytes;
2452	void* buffer;
2453
2454	*actualBytes = 0;
2455	buffer = (char*)iopb->ioBuffer + iopb->ioActCount;
2456
2457	err = MapFileBlockC(vcb, ResolveFCB(fileRefNum), maximumBytes, (currentPosition >> kSectorShift),
2458				&diskBlock, &contiguousBytes );
2459	if (err)
2460		return (err);
2461
2462	err = DeviceWrite(vcb->vcbDriverWriteRef, vcb->vcbDriveNumber, buffer, (diskBlock << Log2BlkLo), contiguousBytes, actualBytes);
2463
2464	return( err );
2465}
2466
2467
2468void PrintName( int theCount, const UInt8 *theNamePtr, Boolean isUnicodeString )
2469{
2470    int			myCount;
2471 	int			i;
2472
2473    myCount = (isUnicodeString) ? (theCount * 2) : theCount;
2474    for ( i = 0; i < myCount; i++ )
2475       	plog( "%02X ", *(theNamePtr + i) );
2476   	plog( "\n" );
2477
2478} /* PrintName */
2479
2480/* Function:	add_prime_bucket_uint32
2481 *
2482 * Description:
2483 * This function increments the prime number buckets in the prime bucket
2484 * set based on the uint32_t number provided.  This function increments
2485 * each prime number bucket by one at an offset of the corresponding
2486 * remainder of the division.  This function is based on Chinese Remainder
2487 * Theorem and adds the given number to the set to compare later.
2488 *
2489 * Input:
2490 *		1. Corresponding prime bucket to increment.
2491 *		2. uint32_t number to add to the set.
2492 *
2493 * Output:	nil
2494 */
2495void add_prime_bucket_uint32(PrimeBuckets *cur, uint32_t num)
2496{
2497	int r32, r27, r25, r7, r11, r13, r17, r19, r23, r29, r31;
2498
2499	if (!cur) {
2500		return;
2501	}
2502
2503	/* Perform the necessary divisions here */
2504	r32 = num % 32;
2505	r27 = num % 27;
2506	r25 = num % 25;
2507	r7  = num % 7;
2508	r11 = num % 11;
2509	r13 = num % 13;
2510	r17 = num % 17;
2511	r19 = num % 19;
2512	r23 = num % 23;
2513	r29 = num % 29;
2514	r31 = num % 31;
2515
2516	/* Update bucket for attribute bit */
2517	cur->n32[r32]++;
2518	cur->n27[r27]++;
2519	cur->n25[r25]++;
2520	cur->n7[r7]++;
2521	cur->n11[r11]++;
2522	cur->n13[r13]++;
2523	cur->n17[r17]++;
2524	cur->n19[r19]++;
2525	cur->n23[r23]++;
2526	cur->n29[r29]++;
2527	cur->n31[r31]++;
2528
2529	return;
2530}
2531
2532/* Function:	add_prime_bucket_uint64
2533 *
2534 * Description:
2535 * This function increments the prime number buckets in the prime bucket
2536 * set based on the uint64_t number provided.  This function increments
2537 * each prime number bucket by one at an offset of the corresponding
2538 * remainder of the division.  This function is based on Chinese Remainder
2539 * Theorem and adds the given number to the set to compare later.
2540 *
2541 * Input:
2542 *		1. Corresponding prime bucket to increment.
2543 *		2. uint64_t number to add to the set.
2544 *
2545 * Output:	nil
2546 */
2547void add_prime_bucket_uint64(PrimeBuckets *cur, uint64_t num)
2548{
2549	size_t r32, r27, r25, r7, r11, r13, r17, r19, r23, r29, r31;
2550
2551	if (!cur) {
2552		return;
2553	}
2554
2555	/* Perform the necessary divisions here */
2556	r32 = num % 32;
2557	r27 = num % 27;
2558	r25 = num % 25;
2559	r7  = num % 7;
2560	r11 = num % 11;
2561	r13 = num % 13;
2562	r17 = num % 17;
2563	r19 = num % 19;
2564	r23 = num % 23;
2565	r29 = num % 29;
2566	r31 = num % 31;
2567
2568	/* Update bucket for attribute bit */
2569	cur->n32[r32]++;
2570	cur->n27[r27]++;
2571	cur->n25[r25]++;
2572	cur->n7[r7]++;
2573	cur->n11[r11]++;
2574	cur->n13[r13]++;
2575	cur->n17[r17]++;
2576	cur->n19[r19]++;
2577	cur->n23[r23]++;
2578	cur->n29[r29]++;
2579	cur->n31[r31]++;
2580
2581	return;
2582}
2583
2584/* Compares the two prime buckets provided.
2585 * Returns -
2586 * 	zero - If the two buckets are same.
2587 *  non-zero - If the two buckets do not match.
2588 */
2589int compare_prime_buckets(PrimeBuckets *bucket1, PrimeBuckets *bucket2)
2590{
2591	int retval = 1;
2592	int i;
2593
2594	for (i=0; i<32; i++) {
2595		if (bucket1->n32[i] != bucket2->n32[i]) {
2596			goto out;
2597		}
2598	}
2599
2600	for (i=0; i<27; i++) {
2601		if (bucket1->n27[i] != bucket2->n27[i]) {
2602			goto out;
2603		}
2604	}
2605
2606	for (i=0; i<25; i++) {
2607		if (bucket1->n25[i] != bucket2->n25[i]) {
2608			goto out;
2609		}
2610	}
2611
2612	for (i=0; i<7; i++) {
2613		if (bucket1->n7[i] != bucket2->n7[i]) {
2614			goto out;
2615		}
2616	}
2617
2618	for (i=0; i<11; i++) {
2619		if (bucket1->n11[i] != bucket2->n11[i]) {
2620			goto out;
2621		}
2622	}
2623
2624	for (i=0; i<13; i++) {
2625		if (bucket1->n13[i] != bucket2->n13[i]) {
2626			goto out;
2627		}
2628	}
2629
2630	for (i=0; i<17; i++) {
2631		if (bucket1->n17[i] != bucket2->n17[i]) {
2632			goto out;
2633		}
2634	}
2635
2636	for (i=0; i<19; i++) {
2637		if (bucket1->n19[i] != bucket2->n19[i]) {
2638			goto out;
2639		}
2640	}
2641
2642	for (i=0; i<23; i++) {
2643		if (bucket1->n23[i] != bucket2->n23[i]) {
2644			goto out;
2645		}
2646	}
2647
2648	for (i=0; i<29; i++) {
2649		if (bucket1->n29[i] != bucket2->n29[i]) {
2650			goto out;
2651		}
2652	}
2653
2654	for (i=0; i<31; i++) {
2655		if (bucket1->n31[i] != bucket2->n31[i]) {
2656			goto out;
2657		}
2658	}
2659
2660	retval = 0;
2661
2662out:
2663	return retval;
2664}
2665
2666/* Prints the prime number bucket for the passed pointer */
2667void print_prime_buckets(PrimeBuckets *cur)
2668{
2669	int i;
2670
2671	plog ("n32 = { ");
2672	for (i=0; i<32; i++) {
2673		plog ("%d,", cur->n32[i]);
2674	}
2675	plog ("}\n");
2676
2677	plog ("n27 = { ");
2678	for (i=0; i<27; i++) {
2679		plog ("%d,", cur->n27[i]);
2680	}
2681	plog ("}\n");
2682
2683	plog ("n25 = { ");
2684	for (i=0; i<25; i++) {
2685		plog ("%d,", cur->n25[i]);
2686	}
2687	plog ("}\n");
2688
2689	plog ("n7 = { ");
2690	for (i=0; i<7; i++) {
2691		plog ("%d,", cur->n7[i]);
2692	}
2693	plog ("}\n");
2694
2695	plog ("n11 = { ");
2696	for (i=0; i<11; i++) {
2697		plog ("%d,", cur->n11[i]);
2698	}
2699	plog ("}\n");
2700
2701	plog ("n13 = { ");
2702	for (i=0; i<13; i++) {
2703		plog ("%d,", cur->n13[i]);
2704	}
2705	plog ("}\n");
2706
2707	plog ("n17 = { ");
2708	for (i=0; i<17; i++) {
2709		plog ("%d,", cur->n17[i]);
2710	}
2711	plog ("}\n");
2712
2713	plog ("n19 = { ");
2714	for (i=0; i<19; i++) {
2715		plog ("%d,", cur->n19[i]);
2716	}
2717	plog ("}\n");
2718
2719	plog ("n23 = { ");
2720	for (i=0; i<23; i++) {
2721		plog ("%d,", cur->n23[i]);
2722	}
2723	plog ("}\n");
2724
2725	plog ("n29 = { ");
2726	for (i=0; i<29; i++) {
2727		plog ("%d,", cur->n29[i]);
2728	}
2729	plog ("}\n");
2730
2731	plog ("n31 = { ");
2732	for (i=0; i<31; i++) {
2733		plog ("%d,", cur->n31[i]);
2734	}
2735	plog ("}\n");
2736}
2737