SUtils.c   [plain text]


/*
 * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
	File:		SUtils.c

	Contains:	xxx put contents here xxx

	Version:	xxx put version here xxx

	Copyright:	© 1997-1999 by Apple Computer, Inc., all rights reserved.
*/

#include "Scavenger.h"

static void 	CompareVolHeaderBTreeSizes(	SGlobPtr GPtr,
											VolumeObjectPtr theVOPtr, 
											HFSPlusVolumeHeader * thePriVHPtr, 
											HFSPlusVolumeHeader * theAltVHPtr );
static void 	GetEmbeddedVolumeHeaders( 	SGlobPtr GPtr, 
											HFSMasterDirectoryBlock * myMDBPtr,
											Boolean isPrimaryMDB );
static OSErr 	GetVolumeObjectBlock( VolumeObjectPtr theVOPtr,
									  UInt64 theBlockNum,
								      BlockDescriptor * theBlockDescPtr );
static OSErr 	VolumeObjectFixPrimaryBlock( void );
		
/*
 * utf_encodestr
 *
 * Encode a UCS-2 (Unicode) string to UTF-8
 */
int utf_encodestr(ucsp, ucslen, utf8p, utf8len)
	const u_int16_t * ucsp;
	size_t ucslen;
	u_int8_t * utf8p;
	size_t * utf8len;
{
	u_int8_t * bufstart;
	u_int16_t ucs_ch;
	int charcnt;
	
	bufstart = utf8p;
	charcnt = ucslen / 2;

	while (charcnt-- > 0) {
		ucs_ch = *ucsp++;

		if (ucs_ch < 0x0080) {
			if (ucs_ch == '\0')
				continue;	/* skip over embedded NULLs */

			*utf8p++ = ucs_ch;
		} else if (ucs_ch < 0x800) {
			*utf8p++ = (ucs_ch >> 6)   | 0xc0;
			*utf8p++ = (ucs_ch & 0x3f) | 0x80;
		} else {	
			*utf8p++ = (ucs_ch >> 12)         | 0xe0;
			*utf8p++ = ((ucs_ch >> 6) & 0x3f) | 0x80;
			*utf8p++ = ((ucs_ch) & 0x3f)      | 0x80;
		}
	}
	
	*utf8len = utf8p - bufstart;

	return (0);
}


/*
 * utf_decodestr
 *
 * Decode a UTF-8 string back to UCS-2 (Unicode)
 */
int
utf_decodestr(utf8p, utf8len, ucsp, ucslen)
	const u_int8_t* utf8p;
	size_t utf8len;
	u_int16_t* ucsp;
	size_t *ucslen;
{
	u_int16_t* bufstart;
	u_int16_t ucs_ch;
	u_int8_t byte;

	bufstart = ucsp;

	while (utf8len-- > 0 && (byte = *utf8p++) != '\0') {
		/* check for ascii */
		if (byte < 0x80) {
			*ucsp++ = byte;
			continue;
		}

		switch (byte & 0xf0) {
		/*  2 byte sequence*/
		case 0xc0:
		case 0xd0:
			/* extract bits 6 - 10 from first byte */
			ucs_ch = (byte & 0x1F) << 6;  
			if (ucs_ch < 0x0080)
				return (-1); /* seq not minimal */
			break;
		/* 3 byte sequence*/
		case 0xe0:
			/* extract bits 12 - 15 from first byte */
			ucs_ch = (byte & 0x0F) << 6;

			/* extract bits 6 - 11 from second byte */
			if (((byte = *utf8p++) & 0xc0) != 0x80)
				return (-1);

			utf8len--;
			ucs_ch += (byte & 0x3F);
			ucs_ch <<= 6;
			if (ucs_ch < 0x0800)
				return (-1); /* seq not minimal */
			break;
		default:
			return (-1);
		}

		/* extract bits 0 - 5 from final byte */
		if (((byte = *utf8p++) & 0xc0) != 0x80)
			return (-1);

		utf8len--;
		ucs_ch += (byte & 0x3F);  
		*ucsp++ = ucs_ch;
	}

	*ucslen = (u_int8_t*)ucsp - (u_int8_t*)bufstart;

	return (0);
}


OSErr GetFBlk( SGlobPtr GPtr, SInt16 fileRefNum, SInt32 blockNumber, void **bufferH );


UInt32 gDFAStage;

UInt32	GetDFAStage( void )
{	
	return (gDFAStage);
}

void	SetDFAStage( UInt32 stage )
{
	gDFAStage = stage;
}


/*------------------------------------------------------------------------------

Routine:	RcdError

Function:	Record errors detetected by scavenging operation.
			
Input:		GPtr		-	pointer to scavenger global area.
			ErrCode		-	error code

Output:		None			
------------------------------------------------------------------------------*/

void RcdError( SGlobPtr GPtr, OSErr errorCode )
{
	GPtr->ErrCode = errorCode;
	
	WriteError( GPtr, errorCode, GPtr->TarID, GPtr->TarBlock );	//	log to summary window
}


/*------------------------------------------------------------------------------

Routine:	IntError

Function:	Records an internal Scavenger error.
			
Input:		GPtr		-	pointer to scavenger global area.
			ErrCode		-	internal error code

Output:		IntError	-	function result:			
								(E_IntErr for now)
------------------------------------------------------------------------------*/

int IntError( SGlobPtr GPtr, OSErr errorCode )
{
	GPtr->RepLevel = repairLevelUnrepairable;
	
	if ( errorCode == ioErr )				//	Cast I/O errors as read errors
		errorCode	= R_RdErr;
		
	if( (errorCode == R_RdErr) || (errorCode == R_WrErr) )
	{
		GPtr->ErrCode	= GPtr->volumeErrorCode;
		GPtr->IntErr	= 0;
		return( errorCode );		
	}
	else
	{
		GPtr->ErrCode	= R_IntErr;
		GPtr->IntErr	= errorCode;
		return( R_IntErr );
	}
	
}	//	End of IntError



/*------------------------------------------------------------------------------

Routine:	AllocBTN (Allocate BTree Node)

Function:	Allocates an BTree node in a Scavenger BTree bit map.
			
Input:		GPtr		-	pointer to scavenger global area.
			StABN		-	starting allocation block number.
			NmABlks		-	number of allocation blocks.

Output:		AllocBTN	-	function result:			
								0 = no error
								n = error
------------------------------------------------------------------------------*/

int AllocBTN( SGlobPtr GPtr, SInt16 fileRefNum, UInt32 nodeNumber )
{
	UInt16				bitPos;
	unsigned char		mask;
	char				*byteP;
	BTreeControlBlock	*calculatedBTCB	= GetBTreeControlBlock( fileRefNum );

	//	Allocate the node 
	if ( calculatedBTCB->refCon == NULL )
		return( noErr );
		
	byteP = ( (BTreeExtensionsRec*)calculatedBTCB->refCon)->BTCBMPtr + (nodeNumber / 8 );	//	ptr to starting byte
	bitPos = nodeNumber % 8;						//	bit offset
	mask = ( 0x80 >> bitPos );
	if ( (*byteP & mask) != 0 )
	{	
		RcdError( GPtr, E_OvlNode );
		return( E_OvlNode );					//	node already allocated
	}
	*byteP = *byteP | mask;						//	allocate it
	calculatedBTCB->freeNodes--;		//	decrement free count
	
	return( noErr );
}


OSErr	GetBTreeHeader( SGlobPtr GPtr, SFCB *fcb, BTHeaderRec *header )
{
	OSErr err;
	BTHeaderRec *headerRec;
	BlockDescriptor block;

	GPtr->TarBlock = kHeaderNodeNum;

	if (fcb->fcbBlockSize == 0)
		(void) SetFileBlockSize(fcb, 512);

	err = GetFileBlock(fcb, kHeaderNodeNum, kGetBlock, &block);
	ReturnIfError(err);

	SWAP_BT_NODE(&block, (fcb->fcbVolume->vcbSignature == kHFSPlusSigWord),
		fcb->fcbFileID, 3);

	headerRec = (BTHeaderRec *)((char*)block.buffer + sizeof(BTNodeDescriptor));
	CopyMemory(headerRec, header, sizeof(BTHeaderRec));

	SWAP_BT_NODE(&block, (fcb->fcbVolume->vcbSignature == kHFSPlusSigWord),
		fcb->fcbFileID, 3);
	
	err = ReleaseFileBlock (fcb, &block, kReleaseBlock);
	ReturnIfError(err);
	
	/* Validate Node Size */
	switch (header->nodeSize) {
		case   512:
		case  1024:
		case  2048:
		case  4096:
		case  8192:
		case 16384:
		case 32768:
			break;

		default:
			RcdError( GPtr, E_InvalidNodeSize );
			err = E_InvalidNodeSize;
	}

	return( err );
}



/*------------------------------------------------------------------------------

Routine:	Alloc[Minor/Major]RepairOrder

Function:	Allocate a repair order node and link into the 'GPtr->RepairXxxxxP" list.
			These are descriptions of minor/major repairs that need to be performed;
			they are compiled during verification, and executed during minor/major repair.

Input:		GPtr	- scavenger globals
			n		- number of extra bytes needed, in addition to standard node size.

Output:		Ptr to node, or NULL if out of memory or other error.
------------------------------------------------------------------------------*/

RepairOrderPtr AllocMinorRepairOrder( SGlobPtr GPtr, int n )						/* #extra bytes needed */
{
	RepairOrderPtr	p;							//	the node we allocate
	
	n += sizeof( RepairOrder );					//	add in size of basic node
	
	p = (RepairOrderPtr) AllocateClearMemory( n );		//	get the node
	
	if ( p != NULL )							//	if we got one...
	{
		p->link = GPtr->MinorRepairsP;			//	then link into list of repairs
		GPtr->MinorRepairsP = p;
	}
    else if ( GPtr->logLevel >= kDebugLog )
        printf( "\t%s - AllocateClearMemory failed to allocate %d bytes \n", __FUNCTION__, n);
	
	if ( GPtr->RepLevel == repairLevelNoProblemsFound )
		GPtr->RepLevel = repairLevelVolumeRecoverable;

	return( p );								//	return ptr to node
}



void	InvalidateCalculatedVolumeBitMap( SGlobPtr GPtr )
{

}



//------------------------------------------------------------------------------
//	Routine:	GetVolumeFeatures
//
//	Function:	Sets up some OS and volume specific flags
//
//	Input:		GPtr->DrvNum			The volume to check
//			
//	Output:		GPtr->volumeFeatures	Bit vector
//				GPtr->realVCB			Real in-memory vcb
//------------------------------------------------------------------------------

#if !BSD	
OSErr GetVolumeFeatures( SGlobPtr GPtr )
{
	OSErr					err;
	HParamBlockRec			pb;
	GetVolParmsInfoBuffer	buffer;
	long					response;

	GPtr->volumeFeatures	= 0;					//	Initialize to zero

	//	Get the "real" vcb
	err = GetVCBDriveNum( &GPtr->realVCB, GPtr->DrvNum );
	ReturnIfError( err );

	if ( GPtr->realVCB != nil )
	{
		GPtr->volumeFeatures	|= volumeIsMountedMask;

		pb.ioParam.ioNamePtr	= nil;
		pb.ioParam.ioVRefNum	= GPtr->realVCB->vcbVRefNum;
		pb.ioParam.ioBuffer		= (Ptr) &buffer;
		pb.ioParam.ioReqCount	= sizeof( buffer );
		
		if ( PBHGetVolParms( &pb, false ) == noErr )
		{
			if ( buffer.vMAttrib & (1 << bSupportsTrashVolumeCache) )
				GPtr->volumeFeatures	|= supportsTrashVolumeCacheFeatureMask;
		}
	}
	//	Check if the running system is HFS+ savy
	err = Gestalt( gestaltFSAttr, &response );
	ReturnIfError( err );
	if ( (response & (1 << gestaltFSSupportsHFSPlusVols)) != 0 )
		GPtr->volumeFeatures |= supportsHFSPlusVolsFeatureMask;
	
	return( noErr );
}
#endif



/*-------------------------------------------------------------------------------
Routine:	ClearMemory	-	clear a block of memory

-------------------------------------------------------------------------------*/
#if !BSD
void ClearMemory( void* start, UInt32 length )
{
	UInt32		zero = 0;
	UInt32*		dataPtr;
	UInt8*		bytePtr;
	UInt32		fragCount;		// serves as both a length and quadlong count
								// for the beginning and main fragment
	
	if ( length == 0 )
		return;

	// is request less than 4 bytes?
	if ( length < 4 )				// length = 1,2 or 3
	{
		bytePtr = (UInt8 *) start;
		
		do
		{
			*bytePtr++ = zero;		// clear one byte at a time
		}
		while ( --length );

		return;
	}

	// are we aligned on an odd boundry?
	fragCount = (UInt32) start & 3;

	if ( fragCount )				// fragCount = 1,2 or 3
	{
		bytePtr = (UInt8 *) start;
		
		do
		{
			*bytePtr++ = zero;		// clear one byte at a time
			++fragCount;
			--length;
		}
		while ( (fragCount < 4) && (length > 0) );

		if ( length == 0 )
			return;

		dataPtr = (UInt32*) (((UInt32) start & 0xFFFFFFFC) + 4);	// make it long word aligned
	}
	else
	{
		dataPtr = (UInt32*) ((UInt32) start & 0xFFFFFFFC);			// make it long word aligned
	}

	// At this point dataPtr is long aligned

	// are there odd bytes to copy?
	fragCount = length & 3;
	
	if ( fragCount )
	{
		bytePtr = (UInt8 *) ((UInt32) dataPtr + (UInt32) length - 1);	// point to last byte
		
		length -= fragCount;		// adjust remaining length
		
		do
		{
			*bytePtr-- = zero;		// clear one byte at a time
		}
		while ( --fragCount );

		if ( length == 0 )
			return;
	}

	// At this point length is a multiple of 4

	#if DEBUG_BUILD
	  if ( length < 4 )
		 DebugStr("\p ClearMemory: length < 4");
	#endif

	// fix up beginning to get us on a 64 byte boundary
	fragCount = length & (64-1);
	
	#if DEBUG_BUILD
	  if ( fragCount < 4 && fragCount > 0 )
		  DebugStr("\p ClearMemory: fragCount < 4");
	#endif
	
	if ( fragCount )
	{
		length -= fragCount; 		// subtract fragment from length now
		fragCount >>= 2; 			// divide by 4 to get a count, for DBRA loop
		do
		{
			// clear 4 bytes at a time...
			*dataPtr++ = zero;		
		}
		while (--fragCount);
	}

	// Are we finished yet?
	if ( length == 0 )
		return;
	
	// Time to turn on the fire hose
	length >>= 6;		// divide by 64 to get count
	do
	{
		// spray 64 bytes at a time...
		*dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero;
		*dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero;
		*dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero;
		*dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero; *dataPtr++ = zero;
	}
	while (--length);
}
#endif



void
CopyCatalogName(const CatalogName *srcName, CatalogName *dstName, Boolean isHFSPLus)
{
	UInt32	length;
	
	if ( srcName == NULL )
	{
		if ( dstName != NULL )
			dstName->ustr.length = 0;	// set length byte to zero (works for both unicode and pascal)		
		return;
	}
	
	if (isHFSPLus)
		length = sizeof(UniChar) * (srcName->ustr.length + 1);
	else
		length = sizeof(UInt8) + srcName->pstr[0];

	if ( length > 1 )
		CopyMemory(srcName, dstName, length);
	else
		dstName->ustr.length = 0;	// set length byte to zero (works for both unicode and pascal)		
}


UInt32
CatalogNameLength(const CatalogName *name, Boolean isHFSPlus)
{
	if (isHFSPlus)
		return name->ustr.length;
	else
		return name->pstr[0];
}


UInt32	CatalogNameSize( const CatalogName *name, Boolean isHFSPlus)
{
	UInt32	length = CatalogNameLength( name, isHFSPlus );
	
	if ( isHFSPlus )
		length *= sizeof(UniChar);
	
	return( length );
}


//******************************************************************************
//	Routine:	BuildCatalogKey
//
//	Function: 	Constructs a catalog key record (ckr) given the parent
//				folder ID and CName.  Works for both classic and extended
//				HFS volumes.
//
//******************************************************************************

void
BuildCatalogKey(HFSCatalogNodeID parentID, const CatalogName *cName, Boolean isHFSPlus, CatalogKey *key)
{
	if ( isHFSPlus )
	{
		key->hfsPlus.keyLength		= kHFSPlusCatalogKeyMinimumLength;	// initial key length (4 + 2)
		key->hfsPlus.parentID			= parentID;		// set parent ID
		key->hfsPlus.nodeName.length	= 0;			// null CName length
		if ( cName != NULL )
		{
			CopyCatalogName(cName, (CatalogName *) &key->hfsPlus.nodeName, isHFSPlus);
			key->hfsPlus.keyLength += sizeof(UniChar) * cName->ustr.length;	// add CName size to key length
		}
	}
	else
	{
		key->hfs.keyLength	= kHFSCatalogKeyMinimumLength;	// initial key length (1 + 4 + 1)
		key->hfs.reserved		= 0;				// clear unused byte
		key->hfs.parentID		= parentID;			// set parent ID
		key->hfs.nodeName[0]	= 0;				// null CName length
		if ( cName != NULL )
		{
			UpdateCatalogName(cName->pstr, key->hfs.nodeName);
			key->hfs.keyLength += key->hfs.nodeName[0];		// add CName size to key length
		}
	}
}


//		Defined in BTreesPrivate.h, but not implemented in the BTree code?
//		So... here's the implementation
SInt32	CompareKeys( BTreeControlBlockPtr btreePtr, KeyPtr searchKey, KeyPtr trialKey )
{
	KeyCompareProcPtr	compareProc = (KeyCompareProcPtr)btreePtr->keyCompareProc;
	
	return( compareProc(searchKey, trialKey) );
}


void
UpdateCatalogName(ConstStr31Param srcName, Str31 destName)
{
	Size length = srcName[0];
	
	if (length > kHFSMaxFileNameChars)
		length = kHFSMaxFileNameChars;		// truncate to max

	destName[0] = length;					// set length byte
	
	CopyMemory(&srcName[1], &destName[1], length);
}


void
UpdateVolumeEncodings(SVCB *volume, TextEncoding encoding)
{
	UInt32	index;

	encoding &= 0x7F;
	
	index = MapEncodingToIndex(encoding);

	volume->vcbEncodingsBitmap |= (u_int64_t)(1ULL << index);
		
	// vcb should already be marked dirty
}


//******************************************************************************
//	Routine:	VolumeObjectFixPrimaryBlock
//
//	Function:	Use the alternate Volume Header or Master Directory block (depending
//				on the type of volume) to restore the primary block.  This routine
//				depends upon our intialization code to set up where are blocks are
//				located.  
//
// 	Result:		0 if all is well, noMacDskErr when we do not have a primary block 
//				number or whatever GetVolumeObjectAlternateBlock returns.
//******************************************************************************

static OSErr VolumeObjectFixPrimaryBlock( void )
{
	OSErr				err;
	VolumeObjectPtr		myVOPtr;
	UInt64				myPrimaryBlockNum;
	BlockDescriptor  	myPrimary;
	BlockDescriptor  	myAlternate;

	myVOPtr = GetVolumeObjectPtr( );
	myPrimary.buffer = NULL;
	myAlternate.buffer = NULL;
		
	GetVolumeObjectPrimaryBlockNum( &myPrimaryBlockNum );
	if ( myPrimaryBlockNum == 0 )
		return( noMacDskErr );
		
	// we don't care if this is a valid primary block since we're
	// about to write over it
	err = GetVolumeObjectPrimaryBlock( &myPrimary );
	if ( !(err == noErr || err == badMDBErr || err == noMacDskErr) )
		goto ExitThisRoutine;

	// restore the primary block from the alternate
	err = GetVolumeObjectAlternateBlock( &myAlternate );
	
	// invalidate if we have not marked the alternate as OK
	if ( VolumeObjectIsHFS( ) ) {
		if ( (myVOPtr->flags & kVO_AltMDBOK) == 0 )
			err = badMDBErr;
	}
	else if ( (myVOPtr->flags & kVO_AltVHBOK) == 0 ) {
		err = badMDBErr;
	}
	
	if ( err == noErr ) {
		CopyMemory( myAlternate.buffer, myPrimary.buffer, Blk_Size );
		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kForceWriteBlock );		
		myPrimary.buffer = NULL;
		if ( myVOPtr->volumeType == kHFSVolumeType )
			myVOPtr->flags |= kVO_PriMDBOK;
		else
			myVOPtr->flags |= kVO_PriVHBOK;
	}

ExitThisRoutine:
	if ( myPrimary.buffer != NULL )
		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kReleaseBlock );
	if ( myAlternate.buffer != NULL )
		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kReleaseBlock );

	return( err );
	
} /* VolumeObjectFixPrimaryBlock */


//******************************************************************************
//	Routine:	GetVolumeObjectVHBorMDB
//
//	Function:	Get the Volume Header block or Master Directory block (depending
//				on type of volume).  This will normally return the alternate, but
//				it may return the primary when the alternate is damaged or cannot
//				be found.
//
// 	Result:		returns 0 when all is well.
//******************************************************************************
OSErr GetVolumeObjectVHBorMDB( BlockDescriptor * theBlockDescPtr )
{
	UInt64				myBlockNum;
	VolumeObjectPtr		myVOPtr;
	OSErr  				err;

	myVOPtr = GetVolumeObjectPtr( );
	GetVolumeObjectBlockNum( &myBlockNum );

	err = GetVolumeObjectBlock( myVOPtr, myBlockNum, theBlockDescPtr );
	if ( err == noErr ) 
	{
		if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType || 
			 myVOPtr->volumeType == kPureHFSPlusVolumeType )
		{
			err = ValidVolumeHeader( (HFSPlusVolumeHeader*) theBlockDescPtr->buffer );
		}
		else if ( myVOPtr->volumeType == kHFSVolumeType ) 
		{
			HFSMasterDirectoryBlock *	myMDBPtr;
			myMDBPtr = (HFSMasterDirectoryBlock	*) theBlockDescPtr->buffer;
			if ( myMDBPtr->drSigWord != kHFSSigWord )
				err = noMacDskErr;
		}
		else
			err = noMacDskErr;
	}

	return( err );

} /* GetVolumeObjectVHBorMDB */


//******************************************************************************
//	Routine:	GetVolumeObjectAlternateBlock
//
//	Function:	Get the alternate Volume Header block or Master Directory block
//				(depending on type of volume).
// 	Result:		returns 0 when all is well.
//******************************************************************************
OSErr GetVolumeObjectAlternateBlock( BlockDescriptor * theBlockDescPtr )
{
	UInt64				myBlockNum;
	VolumeObjectPtr		myVOPtr;
	OSErr  				err;

	myVOPtr = GetVolumeObjectPtr( );
	GetVolumeObjectAlternateBlockNum( &myBlockNum );

	err = GetVolumeObjectBlock( myVOPtr, myBlockNum, theBlockDescPtr );
	if ( err == noErr ) 
	{
		if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType || 
			 myVOPtr->volumeType == kPureHFSPlusVolumeType )
		{
			err = ValidVolumeHeader( (HFSPlusVolumeHeader*) theBlockDescPtr->buffer );
		}
		else if ( myVOPtr->volumeType == kHFSVolumeType ) 
		{
			HFSMasterDirectoryBlock *	myMDBPtr;
			myMDBPtr = (HFSMasterDirectoryBlock	*) theBlockDescPtr->buffer;
			if ( myMDBPtr->drSigWord != kHFSSigWord )
				err = noMacDskErr;
		}
		else
			err = noMacDskErr;
	}

	return( err );

} /* GetVolumeObjectAlternateBlock */


//******************************************************************************
//	Routine:	GetVolumeObjectPrimaryBlock
//
//	Function:	Get the primary Volume Header block or Master Directory block
//				(depending on type of volume).
// 	Result:		returns 0 when all is well.
//******************************************************************************
OSErr GetVolumeObjectPrimaryBlock( BlockDescriptor * theBlockDescPtr )
{
	UInt64				myBlockNum;
	VolumeObjectPtr		myVOPtr;
	OSErr  				err;

	myVOPtr = GetVolumeObjectPtr( );
	GetVolumeObjectPrimaryBlockNum( &myBlockNum );

	err = GetVolumeObjectBlock( myVOPtr, myBlockNum, theBlockDescPtr );
	if ( err == noErr ) 
	{
		if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType || 
			 myVOPtr->volumeType == kPureHFSPlusVolumeType )
		{
			err = ValidVolumeHeader( (HFSPlusVolumeHeader*) theBlockDescPtr->buffer );
		}
		else if ( myVOPtr->volumeType == kHFSVolumeType ) 
		{
			HFSMasterDirectoryBlock *	myMDBPtr;
			myMDBPtr = (HFSMasterDirectoryBlock	*) theBlockDescPtr->buffer;
			if ( myMDBPtr->drSigWord != kHFSSigWord )
				err = noMacDskErr;
		}
		else
			err = noMacDskErr;
	}

	return( err );

} /* GetVolumeObjectPrimaryBlock */


//******************************************************************************
//	Routine:	GetVolumeObjectVHB
//
//	Function:	Get the Volume Header block using either the primary or alternate
//				block number as set up by InitializeVolumeObject.  This will normally
//				return the alternate, but it may return the primary when the
//				alternate is damaged or cannot be found.
//
// 	Result:		returns 0 when all is well or passes results of GetVolumeBlock or
//				ValidVolumeHeader.
//******************************************************************************
OSErr GetVolumeObjectVHB( BlockDescriptor * theBlockDescPtr )
{
	UInt64				myBlockNum;
	VolumeObjectPtr		myVOPtr;
	OSErr  				err;

	myVOPtr = GetVolumeObjectPtr( );
	myBlockNum = ((myVOPtr->flags & kVO_AltVHBOK) != 0) ? myVOPtr->alternateVHB : myVOPtr->primaryVHB;
	err = GetVolumeObjectBlock( myVOPtr, myBlockNum, theBlockDescPtr );
	if ( err == noErr )
		err = ValidVolumeHeader( (HFSPlusVolumeHeader*) theBlockDescPtr->buffer );

	return( err );

} /* GetVolumeObjectVHB */


//******************************************************************************
//	Routine:	GetVolumeObjectAlternateMDB
//
//	Function:	Get the Master Directory Block using the alternate master directory
//				block number as set up by InitializeVolumeObject.
//
// 	Result:		returns 0 when all is well.
//******************************************************************************
OSErr GetVolumeObjectAlternateMDB( BlockDescriptor * theBlockDescPtr )
{
	VolumeObjectPtr		myVOPtr;
	OSErr  				err;

	myVOPtr = GetVolumeObjectPtr( );
	err = GetVolumeObjectBlock( NULL, myVOPtr->alternateMDB, theBlockDescPtr );
	if ( err == noErr ) 
	{
		HFSMasterDirectoryBlock *	myMDBPtr;
		myMDBPtr = (HFSMasterDirectoryBlock	*) theBlockDescPtr->buffer;
		if ( myMDBPtr->drSigWord != kHFSSigWord )
			err = noMacDskErr;
	}

	return( err );

} /* GetVolumeObjectAlternateMDB */


//******************************************************************************
//	Routine:	GetVolumeObjectPrimaryMDB
//
//	Function:	Get the Master Directory Block using the primary master directory
//				block number as set up by InitializeVolumeObject.
//
// 	Result:		returns 0 when all is well.
//******************************************************************************
OSErr GetVolumeObjectPrimaryMDB( BlockDescriptor * theBlockDescPtr )
{
	VolumeObjectPtr		myVOPtr;
	OSErr  				err;

	myVOPtr = GetVolumeObjectPtr( );
	err = GetVolumeObjectBlock( NULL, myVOPtr->primaryMDB, theBlockDescPtr );
	if ( err == noErr ) 
	{
		HFSMasterDirectoryBlock *	myMDBPtr;
		myMDBPtr = (HFSMasterDirectoryBlock	*) theBlockDescPtr->buffer;
		if ( myMDBPtr->drSigWord != kHFSSigWord )
			err = noMacDskErr;
	}

	return( err );

} /* GetVolumeObjectPrimaryMDB */


//******************************************************************************
//	Routine:	GetVolumeObjectBlock
//
//	Function:	Get the Volume Header block or Master Directory block using the
//				given block number.
// 	Result:		returns 0 when all is well or passes results of GetVolumeBlock or
//				ValidVolumeHeader.
//******************************************************************************
static OSErr GetVolumeObjectBlock( VolumeObjectPtr theVOPtr,
								   UInt64 theBlockNum,
								   BlockDescriptor * theBlockDescPtr )
{
	OSErr  			err;

	if ( theVOPtr == NULL )
		theVOPtr = GetVolumeObjectPtr( );
		
	err = GetVolumeBlock( theVOPtr->vcbPtr, theBlockNum, kGetBlock, theBlockDescPtr );

	return( err );

} /* GetVolumeObjectBlock */


//******************************************************************************
//	Routine:	GetVolumeObjectBlockNum
//
//	Function:	Extract the appropriate block number for the volume header or
//				master directory (depanding on volume type) from the VolumeObject.
//				NOTE - this routine may return the primary or alternate block
//				depending on which one is valid.  Preference is always given to
//				the alternate.
//
// 	Result:		returns block number of MDB or VHB or 0 if none are valid or
//				if volume type is unknown.
//******************************************************************************
void GetVolumeObjectBlockNum( UInt64 * theBlockNumPtr )
{
	VolumeObjectPtr				myVOPtr;

	myVOPtr = GetVolumeObjectPtr( );
	*theBlockNumPtr = 0;	// default to none

	// NOTE - we use alternate volume header or master directory
	// block before the primary because it is less likely to be damaged.
	if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType ||
	     myVOPtr->volumeType == kPureHFSPlusVolumeType ) {
		if ( (myVOPtr->flags & kVO_AltVHBOK) != 0 )
			*theBlockNumPtr = myVOPtr->alternateVHB;
		else
			*theBlockNumPtr = myVOPtr->primaryVHB;
	}
	else if ( myVOPtr->volumeType == kHFSVolumeType ) {
		if ( (myVOPtr->flags & kVO_AltMDBOK) != 0 )
			*theBlockNumPtr = myVOPtr->alternateMDB;
		else
			*theBlockNumPtr = myVOPtr->primaryMDB;
	}

	return;

} /* GetVolumeObjectBlockNum */


//******************************************************************************
//	Routine:	GetVolumeObjectAlternateBlockNum
//
//	Function:	Extract the alternate block number for the volume header or
//				master directory (depanding on volume type) from the VolumeObject.
//
// 	Result:		returns block number of alternate MDB or VHB or 0 if none are 
//				valid or if volume type is unknown.
//******************************************************************************
void GetVolumeObjectAlternateBlockNum( UInt64 * theBlockNumPtr )
{
	VolumeObjectPtr				myVOPtr;

	myVOPtr = GetVolumeObjectPtr( );
	*theBlockNumPtr = 0;	// default to none
	
	if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType ||
	     myVOPtr->volumeType == kPureHFSPlusVolumeType ) {
		*theBlockNumPtr = myVOPtr->alternateVHB;
	}
	else if ( myVOPtr->volumeType == kHFSVolumeType ) {
		*theBlockNumPtr = myVOPtr->alternateMDB;
	}

	return;

} /* GetVolumeObjectAlternateBlockNum */


//******************************************************************************
//	Routine:	GetVolumeObjectPrimaryBlockNum
//
//	Function:	Extract the primary block number for the volume header or
//				master directory (depanding on volume type) from the VolumeObject.
//
// 	Result:		returns block number of primary MDB or VHB or 0 if none are valid
//				or if volume type is unknown.
//******************************************************************************
void GetVolumeObjectPrimaryBlockNum( UInt64 * theBlockNumPtr )
{
	VolumeObjectPtr				myVOPtr;

	myVOPtr = GetVolumeObjectPtr( );
	*theBlockNumPtr = 0;	// default to none
	
	if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType ||
	     myVOPtr->volumeType == kPureHFSPlusVolumeType ) {
		*theBlockNumPtr = myVOPtr->primaryVHB;
	}
	else if ( myVOPtr->volumeType == kHFSVolumeType ) {
		*theBlockNumPtr = myVOPtr->primaryMDB;
	}

	return;

} /* GetVolumeObjectPrimaryBlockNum */


//******************************************************************************
//	Routine:	InitializeVolumeObject
//
//	Function:	Locate volume headers and / or master directory blocks for this
//				volume and fill where they are located on the volume and the type
//				of volume we are dealing with.  We have three types of HFS volumes:
//				¥ HFS - standard (old format) where primary MDB is 2nd block into
//					the volume and alternate MDB is 2nd to last block on the volume.
//				¥ pure HFS+ - where primary volume header is 2nd block into
//					the volume and alternate volume header is 2nd to last block on
//					the volume.
//				¥ wrapped HFS+ - where primary MDB is 2nd block into the volume and 
//					alternate MDB is 2nd to last block on the volume.   The embedded 
//					HFS+ volume header locations are calculated from drEmbedExtent 
//					(in the MDB).
//
// 	Result:		returns nothing.  Will fill in SGlob.VolumeObject data
//******************************************************************************
void	InitializeVolumeObject( SGlobPtr GPtr )
{
	OSErr						err;
	HFSMasterDirectoryBlock *	myMDBPtr;
	HFSPlusVolumeHeader *		myVHPtr;
	VolumeObjectPtr				myVOPtr;
	HFSPlusVolumeHeader			myPriVolHeader;
	BlockDescriptor				myBlockDescriptor;

	myBlockDescriptor.buffer = NULL;
	myVOPtr = GetVolumeObjectPtr( );
	myVOPtr->flags |= kVO_Inited;
	myVOPtr->vcbPtr = GPtr->calculatedVCB;

	// Determine volume size in sectors
	err = GetDeviceSize( 	GPtr->calculatedVCB->vcbDriveNumber, 
							&myVOPtr->totalDeviceSectors, 
							&myVOPtr->sectorSize );
	if ( (myVOPtr->totalDeviceSectors < 3) || (err != noErr) ) {
		if ( GPtr->logLevel >= kDebugLog ) {
			printf("\tinvalid device information for volume - total sectors = %qd sector size = %d \n",
				myVOPtr->totalDeviceSectors, myVOPtr->sectorSize);
		}
		goto ExitRoutine;
	}
	
	// get the primary volume header or master directory block (depending on volume type)
	// should always be block 2 (relative to 0) into the volume.
	err = GetVolumeObjectBlock( myVOPtr, MDB_BlkN, &myBlockDescriptor );
	if ( err == noErr ) {
		myMDBPtr = (HFSMasterDirectoryBlock	*) myBlockDescriptor.buffer;
		if ( myMDBPtr->drSigWord == kHFSPlusSigWord || myMDBPtr->drSigWord == kHFSXSigWord) {
			myVHPtr	= (HFSPlusVolumeHeader *) myMDBPtr;
			
			myVOPtr->primaryVHB = MDB_BlkN;								// save location
			myVOPtr->alternateVHB = myVOPtr->totalDeviceSectors - 2;	// save location
			err = ValidVolumeHeader( myVHPtr );
			if ( err == noErr ) {
				myVOPtr->flags |= kVO_PriVHBOK;
				bcopy( myVHPtr, &myPriVolHeader, sizeof( *myVHPtr ) );
			}
			else {
				if ( GPtr->logLevel >= kDebugLog ) {
					printf( "\tInvalid primary volume header - error %d \n", err );
				}
			}
		}
		else if ( myMDBPtr->drSigWord == kHFSSigWord ) {
			// we could have an HFS or wrapped HFS+ volume
			myVOPtr->primaryMDB = MDB_BlkN;								// save location
			myVOPtr->alternateMDB = myVOPtr->totalDeviceSectors - 2;	// save location
			myVOPtr->flags |= kVO_PriMDBOK;
		}
		else {
			if ( GPtr->logLevel >= kDebugLog ) {
				printf( "\tBlock %d is not an MDB or Volume Header \n", MDB_BlkN );
			}
		}
		(void) ReleaseVolumeBlock( GPtr->calculatedVCB, &myBlockDescriptor, kReleaseBlock );
	} 
	else {
		if ( GPtr->logLevel >= kDebugLog ) {
			printf( "\tcould not get volume block %d, err %d \n", MDB_BlkN, err );
		}
	}
	
	// get the alternate volume header or master directory block (depending on volume type)
	// should always be 2nd to last sector.
	err = GetVolumeObjectBlock( myVOPtr, myVOPtr->totalDeviceSectors - 2, &myBlockDescriptor );
	if ( err == noErr ) {
		myMDBPtr = (HFSMasterDirectoryBlock	*) myBlockDescriptor.buffer;
		if ( myMDBPtr->drSigWord == kHFSPlusSigWord || myMDBPtr->drSigWord == kHFSXSigWord ) {
			myVHPtr	= (HFSPlusVolumeHeader *) myMDBPtr;
			
			myVOPtr->primaryVHB = MDB_BlkN;								// save location
			myVOPtr->alternateVHB = myVOPtr->totalDeviceSectors - 2;	// save location
			err = ValidVolumeHeader( myVHPtr );
			if ( err == noErr ) {
				// check to see if the primary and alternates are in sync.  3137809
				myVOPtr->flags |= kVO_AltVHBOK;
				CompareVolHeaderBTreeSizes( GPtr, myVOPtr, &myPriVolHeader, myVHPtr );
			}
			else {
				if ( GPtr->logLevel >= kDebugLog ) {
					printf( "\tInvalid alternate volume header - error %d \n", err );
				}
			}
		}
		else if ( myMDBPtr->drSigWord == kHFSSigWord ) {
			myVOPtr->primaryMDB = MDB_BlkN;								// save location
			myVOPtr->alternateMDB = myVOPtr->totalDeviceSectors - 2;	// save location
			myVOPtr->flags |= kVO_AltMDBOK;
		}
		else {
			if ( GPtr->logLevel >= kDebugLog ) {
				printf( "\tBlock %qd is not an MDB or Volume Header \n", myVOPtr->totalDeviceSectors - 2 );
			}
		}

		(void) ReleaseVolumeBlock( GPtr->calculatedVCB, &myBlockDescriptor, kReleaseBlock );
	}
	else {
		if ( GPtr->logLevel >= kDebugLog ) {
			printf( "\tcould not get alternate volume header at %qd, err %d \n", 
					myVOPtr->totalDeviceSectors - 2, err );
		}
	}

	// get the embedded volume header (if applicable).
	if ( (myVOPtr->flags & kVO_AltMDBOK) != 0 ) {
		err = GetVolumeObjectBlock( myVOPtr, myVOPtr->alternateMDB, &myBlockDescriptor );
		if ( err == noErr ) {
			myMDBPtr = (HFSMasterDirectoryBlock	*) myBlockDescriptor.buffer;
			GetEmbeddedVolumeHeaders( GPtr, myMDBPtr, false );
			(void) ReleaseVolumeBlock( GPtr->calculatedVCB, &myBlockDescriptor, kReleaseBlock );
		}
	}
	
	// Now we will look for embedded HFS+ volume headers using the primary MDB if 
	// we haven't already located them.
	if ( (myVOPtr->flags & kVO_PriMDBOK) != 0 && 
		 ((myVOPtr->flags & kVO_PriVHBOK) == 0 || (myVOPtr->flags & kVO_AltVHBOK) == 0) ) {
		err = GetVolumeObjectBlock( myVOPtr, myVOPtr->primaryMDB, &myBlockDescriptor );
		if ( err == noErr ) {
			myMDBPtr = (HFSMasterDirectoryBlock	*) myBlockDescriptor.buffer;
			GetEmbeddedVolumeHeaders( GPtr, myMDBPtr, true );
			(void) ReleaseVolumeBlock( GPtr->calculatedVCB, &myBlockDescriptor, kReleaseBlock );
		}
		else {
			if ( GPtr->logLevel >= kDebugLog ) {
				printf( "\tcould not get primary MDB at block %qd, err %d \n", myVOPtr->primaryMDB, err );
			}
		}
	}

ExitRoutine:
	// set the type of volume using the flags we set as we located the various header / master
	// blocks.  
	if ( ((myVOPtr->flags & kVO_PriVHBOK) != 0 || (myVOPtr->flags & kVO_AltVHBOK) != 0)  &&
		 ((myVOPtr->flags & kVO_PriMDBOK) != 0 || (myVOPtr->flags & kVO_AltMDBOK) != 0) ) {
		myVOPtr->volumeType = kEmbededHFSPlusVolumeType;
	}
	else if ( ((myVOPtr->flags & kVO_PriVHBOK) != 0 || (myVOPtr->flags & kVO_AltVHBOK) != 0) &&
			  (myVOPtr->flags & kVO_PriMDBOK) == 0 && (myVOPtr->flags & kVO_AltMDBOK) == 0 ) {
		myVOPtr->volumeType = kPureHFSPlusVolumeType;
	}
	else if ( (myVOPtr->flags & kVO_PriVHBOK) == 0 && (myVOPtr->flags & kVO_AltVHBOK) == 0 &&
			  ((myVOPtr->flags & kVO_PriMDBOK) != 0 || (myVOPtr->flags & kVO_AltMDBOK) != 0) ) {
		myVOPtr->volumeType = kHFSVolumeType;
	}
	else
		myVOPtr->volumeType = kUnknownVolumeType;

	return;
	
} /* InitializeVolumeObject */


//******************************************************************************
//	Routine:	PrintVolumeObject
//
//	Function:	Print out some helpful info about the state of our VolumeObject.
//
// 	Result:		returns nothing. 
//******************************************************************************
void PrintVolumeObject( void )
{
	VolumeObjectPtr				myVOPtr;

	myVOPtr = GetVolumeObjectPtr( );

	if ( myVOPtr->volumeType == kHFSVolumeType )
		printf( "\tvolume type is HFS \n" );
	else if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType )
		printf( "\tvolume type is embedded HFS+ \n" );
	else if ( myVOPtr->volumeType == kPureHFSPlusVolumeType )
		printf( "\tvolume type is pure HFS+ \n" );
	else
		printf( "\tunknown volume type \n" );
	
	printf( "\tprimary MDB is at block %qd 0x%02qx \n", myVOPtr->primaryMDB, myVOPtr->primaryMDB );
	printf( "\talternate MDB is at block %qd 0x%02qx \n", myVOPtr->alternateMDB, myVOPtr->alternateMDB );
	printf( "\tprimary VHB is at block %qd 0x%02qx \n", myVOPtr->primaryVHB, myVOPtr->primaryVHB );
	printf( "\talternate VHB is at block %qd 0x%02qx \n", myVOPtr->alternateVHB, myVOPtr->alternateVHB );
	printf( "\tsector size = %d 0x%02x \n", myVOPtr->sectorSize, myVOPtr->sectorSize );
	printf( "\tVolumeObject flags = 0x%02X \n", myVOPtr->flags );
	printf( "\ttotal sectors for volume = %qd 0x%02qx \n", 
			myVOPtr->totalDeviceSectors, myVOPtr->totalDeviceSectors );
	printf( "\ttotal sectors for embedded volume = %qd 0x%02qx \n", 
			myVOPtr->totalEmbeddedSectors, myVOPtr->totalEmbeddedSectors );
	
	return;
	
} /* PrintVolumeObject */


//******************************************************************************
//	Routine:	GetEmbeddedVolumeHeaders
//
//	Function:	Given a MDB (Master Directory Block) from an HFS volume, check
//				to see if there is an embedded HFS+ volume.  If we find an 
//				embedded HFS+ volume fill in relevant SGlob.VolumeObject data.
//
// 	Result:		returns nothing.  Will fill in VolumeObject data
//******************************************************************************

static void GetEmbeddedVolumeHeaders( 	SGlobPtr GPtr, 
										HFSMasterDirectoryBlock * theMDBPtr,
										Boolean isPrimaryMDB )
{
	OSErr						err;
	HFSPlusVolumeHeader *		myVHPtr;
	VolumeObjectPtr				myVOPtr;
	UInt64  					myHFSPlusSectors;
	UInt64  					myPrimaryBlockNum;
	UInt64  					myAlternateBlockNum;
	HFSPlusVolumeHeader			myAltVolHeader;
	BlockDescriptor				myBlockDescriptor;

	myBlockDescriptor.buffer = NULL;
	myVOPtr = GetVolumeObjectPtr( );

	// NOTE - If all of the embedded volume information is zero, then assume
	// this really is a plain HFS disk like it says.  There could be ghost
	// volume headers left over when someone reinitializes a large HFS Plus 
	// volume as HFS.  The original embedded primary volume header and 
	// alternate volume header are not zeroed out.
	if ( theMDBPtr->drEmbedSigWord == 0 && 
		 theMDBPtr->drEmbedExtent.blockCount == 0 && 
		 theMDBPtr->drEmbedExtent.startBlock == 0 ) {
		 goto ExitRoutine;
	}
	
	// number of sectors in our embedded HFS+ volume
	myHFSPlusSectors = (theMDBPtr->drAlBlkSiz / Blk_Size) * theMDBPtr->drEmbedExtent.blockCount;
		
	// offset of embedded HFS+ volume (in bytes) into HFS wrapper volume
	// NOTE - UInt32 is OK since we don't support HFS Wrappers on TB volumes
	myVOPtr->embeddedOffset = 
		(theMDBPtr->drEmbedExtent.startBlock * theMDBPtr->drAlBlkSiz) + 
		(theMDBPtr->drAlBlSt * Blk_Size);

	// Embedded alternate volume header is always 2nd to last sector
	myAlternateBlockNum =
		theMDBPtr->drAlBlSt + 
		((theMDBPtr->drAlBlkSiz / Blk_Size) * theMDBPtr->drEmbedExtent.startBlock) + 
		myHFSPlusSectors - 2;

	// Embedded primary volume header should always be block 2 (relative to 0) 
	// into the embedded volume
	myPrimaryBlockNum = (theMDBPtr->drEmbedExtent.startBlock * theMDBPtr->drAlBlkSiz / Blk_Size) + 
						theMDBPtr->drAlBlSt + 2;

	// get the embedded alternate volume header
	err = GetVolumeObjectBlock( myVOPtr, myAlternateBlockNum, &myBlockDescriptor );
	if ( err == noErr ) {
		myVHPtr = (HFSPlusVolumeHeader	*) myBlockDescriptor.buffer;
		if ( myVHPtr->signature == kHFSPlusSigWord ) {

			myVOPtr->alternateVHB = myAlternateBlockNum;	// save location
			myVOPtr->primaryVHB = myPrimaryBlockNum;		// save location
			err = ValidVolumeHeader( myVHPtr );
			if ( err == noErr ) {
				myVOPtr->flags |= kVO_AltVHBOK;
				myVOPtr->totalEmbeddedSectors = myHFSPlusSectors;
				bcopy( myVHPtr, &myAltVolHeader, sizeof( *myVHPtr ) );
			}
			else {
				if ( GPtr->logLevel >= kDebugLog ) {
					printf( "\tInvalid embedded alternate volume header at block %qd - error %d \n", myAlternateBlockNum, err );
				}
			}
		}
		else {
			if ( GPtr->logLevel >= kDebugLog ) {
				printf( "\tBlock number %qd is not embedded alternate volume header \n", myAlternateBlockNum );
			}
		}
		(void) ReleaseVolumeBlock( GPtr->calculatedVCB, &myBlockDescriptor, kReleaseBlock );
	}
	else {
		if ( GPtr->logLevel >= kDebugLog ) {
			printf( "\tcould not get embedded alternate volume header at %qd, err %d \n", 
					myAlternateBlockNum, err );
		}
	}
	
	// get the embedded primary volume header
	err = GetVolumeObjectBlock( myVOPtr, myPrimaryBlockNum, &myBlockDescriptor );
	if ( err == noErr ) {
		myVHPtr = (HFSPlusVolumeHeader	*) myBlockDescriptor.buffer;
		if ( myVHPtr->signature == kHFSPlusSigWord ) {

			myVOPtr->primaryVHB = myPrimaryBlockNum;  		// save location
			myVOPtr->alternateVHB = myAlternateBlockNum;	// save location
			err = ValidVolumeHeader( myVHPtr );
			if ( err == noErr ) {
				myVOPtr->flags |= kVO_PriVHBOK;
				myVOPtr->totalEmbeddedSectors = myHFSPlusSectors;

				// check to see if the primary and alternates are in sync.  3137809
				CompareVolHeaderBTreeSizes( GPtr, myVOPtr, myVHPtr, &myAltVolHeader );
			}
			else {
				if ( GPtr->logLevel >= kDebugLog ) {
					printf( "\tInvalid embedded primary volume header at block %qd - error %d \n", myPrimaryBlockNum, err );
				}
			}
		}
		else {
			if ( GPtr->logLevel >= kDebugLog ) {
				printf( "\tBlock number %qd is not embedded primary volume header \n", myPrimaryBlockNum );
			}
		}
		(void) ReleaseVolumeBlock( GPtr->calculatedVCB, &myBlockDescriptor, kReleaseBlock );
	}
	else {
		if ( GPtr->logLevel >= kDebugLog ) {
			printf( "\tcould not get embedded primary volume header at %qd, err %d \n", 
					myPrimaryBlockNum, err );
		}
	}

ExitRoutine:
	return;
	
} /* GetEmbeddedVolumeHeaders */


//******************************************************************************
//	Routine:	CompareVolHeaderBTreeSizes
//
//	Function:	checks to see if the primary and alternate volume headers are in
//				sync with regards to the catalog and extents btree file size.  If
//				we find an anomaly we will give preference to the volume header 
//				with the larger of the btree files since these files never shrink.
//				Added for radar #3137809.
//
// 	Result:		returns nothing. 
//******************************************************************************
static void CompareVolHeaderBTreeSizes(	SGlobPtr GPtr,
										VolumeObjectPtr theVOPtr, 
										HFSPlusVolumeHeader * thePriVHPtr, 
										HFSPlusVolumeHeader * theAltVHPtr )
{
	int			weDisagree;
	int			usePrimary;
	int			useAlternate;
	
	weDisagree = usePrimary = useAlternate = 0;
	
	// we only check if both volume headers appear to be OK
	if ( (theVOPtr->flags & kVO_PriVHBOK) == 0 || (theVOPtr->flags & kVO_AltVHBOK) == 0  )
		return;

	if ( thePriVHPtr->catalogFile.totalBlocks != theAltVHPtr->catalogFile.totalBlocks ) {
		// only continue if the B*Tree files both start at the same block number
		if ( thePriVHPtr->catalogFile.extents[0].startBlock == theAltVHPtr->catalogFile.extents[0].startBlock ) {
			weDisagree = 1;
			if ( thePriVHPtr->catalogFile.totalBlocks > theAltVHPtr->catalogFile.totalBlocks )
				usePrimary = 1;
			else
				useAlternate = 1;
			if ( GPtr->logLevel >= kDebugLog ) {
				printf( "\tvolume headers disagree on catalog file total blocks - primary %d alternate %d \n", 
						thePriVHPtr->catalogFile.totalBlocks, theAltVHPtr->catalogFile.totalBlocks );
			}
		}
	}

	if ( thePriVHPtr->extentsFile.totalBlocks != theAltVHPtr->extentsFile.totalBlocks ) {
		// only continue if the B*Tree files both start at the same block number
		if ( thePriVHPtr->extentsFile.extents[0].startBlock == theAltVHPtr->extentsFile.extents[0].startBlock ) {
			weDisagree = 1;
			if ( thePriVHPtr->extentsFile.totalBlocks > theAltVHPtr->extentsFile.totalBlocks )
				usePrimary = 1;
			else
				useAlternate = 1;
			if ( GPtr->logLevel >= kDebugLog ) {
				printf( "\tvolume headers disagree on extents file total blocks - primary %d alternate %d \n", 
						thePriVHPtr->extentsFile.totalBlocks, theAltVHPtr->extentsFile.totalBlocks );
			}
		}
	}
	
	if ( weDisagree == 0 )
		return;
		
	// we have a disagreement.  we resolve the issue by using the larger of the two.
	if ( usePrimary == 1 && useAlternate == 1 ) {
		// this should never happen, but if it does, bail without choosing a preference
		if ( GPtr->logLevel >= kDebugLog ) {
			printf( "\tvolume headers disagree but there is confusion on which to use \n" );
		}
		return; 
	}
	
	if ( usePrimary == 1 ) {
		// mark alternate as bogus
		theVOPtr->flags &= ~kVO_AltVHBOK;
	}
	else if ( useAlternate == 1 ) {
		// mark primary as bogus
		theVOPtr->flags &= ~kVO_PriVHBOK;
	}

	return;

} /* CompareVolHeaderBTreeSizes */


//******************************************************************************
//	Routine:	VolumeObjectIsHFSPlus
//
//	Function:	determine if the volume represented by our VolumeObject is an
//				HFS+ volume (pure or embedded).
//
// 	Result:		returns true if volume is pure HFS+ or embedded HFS+ else false.
//******************************************************************************
Boolean VolumeObjectIsHFSPlus( void )
{
	VolumeObjectPtr				myVOPtr;

	myVOPtr = GetVolumeObjectPtr( );
	
	if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType ||
	     myVOPtr->volumeType == kPureHFSPlusVolumeType ) {
		return( true );
	}

	return( false );

} /* VolumeObjectIsHFSPlus */


//******************************************************************************
//	Routine:	VolumeObjectIsHFS
//
//	Function:	determine if the volume represented by our VolumeObject is an
//				HFS (standard) volume.
//
// 	Result:		returns true if HFS (standard) volume.
//******************************************************************************
Boolean VolumeObjectIsHFS( void )
{
	VolumeObjectPtr				myVOPtr;

	myVOPtr = GetVolumeObjectPtr( );
	
	if ( myVOPtr->volumeType == kHFSVolumeType )
		return( true );

	return( false );

} /* VolumeObjectIsHFS */


//******************************************************************************
//	Routine:	VolumeObjectIsEmbeddedHFSPlus
//
//	Function:	determine if the volume represented by our VolumeObject is an
//				embedded HFS plus volume.
//
// 	Result:		returns true if embedded HFS plus volume.
//******************************************************************************
Boolean VolumeObjectIsEmbeddedHFSPlus( void )
{
	VolumeObjectPtr				myVOPtr;

	myVOPtr = GetVolumeObjectPtr( );
	
	if ( myVOPtr->volumeType == kEmbededHFSPlusVolumeType )
		return( true );

	return( false );

} /* VolumeObjectIsEmbeddedHFSPlus */


//******************************************************************************
//	Routine:	VolumeObjectIsPureHFSPlus
//
//	Function:	determine if the volume represented by our VolumeObject is an
//				pure HFS plus volume.
//
// 	Result:		returns true if pure HFS plus volume.
//******************************************************************************
Boolean VolumeObjectIsPureHFSPlus( void )
{
	VolumeObjectPtr				myVOPtr;

	myVOPtr = GetVolumeObjectPtr( );
	
	if ( myVOPtr->volumeType == kPureHFSPlusVolumeType )
		return( true );

	return( false );

} /* VolumeObjectIsPureHFSPlus */


//******************************************************************************
//	Routine:	GetVolumeObjectPtr
//
//	Function:	Accessor routine to get a pointer to our VolumeObject structure.
//
// 	Result:		returns pointer to our VolumeObject.
//******************************************************************************
VolumeObjectPtr GetVolumeObjectPtr( void )
{
	static VolumeObject		myVolumeObject;
	static int				myInited = 0;
	
	if ( myInited == 0 ) {
		myInited++;
		bzero( &myVolumeObject, sizeof(myVolumeObject) );
	}
	
	return( &myVolumeObject );
	 
} /* GetVolumeObjectPtr */


//******************************************************************************
//	Routine:	CheckEmbeddedVolInfoInMDBs
//
//	Function:	Check the primary and alternate MDB to see if the embedded volume
//				information (drEmbedSigWord and drEmbedExtent) match.
//
// 	Result:		NA
//******************************************************************************
void CheckEmbeddedVolInfoInMDBs( SGlobPtr GPtr )
{
	OSErr						err;
	Boolean						primaryIsDamaged = false;
	Boolean						alternateIsDamaged = false;
	VolumeObjectPtr				myVOPtr;
	HFSMasterDirectoryBlock *	myPriMDBPtr;
	HFSMasterDirectoryBlock *	myAltMDBPtr;
	UInt64						myOffset;
	UInt64						mySectors;
	BlockDescriptor  			myPrimary;
	BlockDescriptor  			myAlternate;

	myVOPtr = GetVolumeObjectPtr( );
	myPrimary.buffer = NULL;
	myAlternate.buffer = NULL;

	// we only check this if primary and alternate are OK at this point.  OK means
	// that the primary and alternate MDBs have the correct signature and at least
	// one of them points to a valid embedded HFS+ volume.
	if ( VolumeObjectIsEmbeddedHFSPlus( ) == false || 
		 (myVOPtr->flags & kVO_PriMDBOK) == 0 || (myVOPtr->flags & kVO_AltMDBOK) == 0 )
		return;

	err = GetVolumeObjectPrimaryMDB( &myPrimary );
	if ( err != noErr ) {
		if ( GPtr->logLevel >= kDebugLog ) {
			printf( "\tcould not get primary MDB \n" );
		}
		goto ExitThisRoutine;
	}
	myPriMDBPtr = (HFSMasterDirectoryBlock	*) myPrimary.buffer;
	err = GetVolumeObjectAlternateMDB( &myAlternate );
	if ( err != noErr ) {
		if ( GPtr->logLevel >= kDebugLog ) {
			printf( "\tcould not get alternate MDB \n" );
		}
		goto ExitThisRoutine;
	}
	myAltMDBPtr = (HFSMasterDirectoryBlock	*) myAlternate.buffer;

	// bail if everything looks good.  NOTE - we can bail if drEmbedExtent info
	// is the same in the primary and alternate MDB because we know one of them is
	// valid (or VolumeObjectIsEmbeddedHFSPlus would be false and we would not be
	// here).
	if ( myPriMDBPtr->drEmbedSigWord == kHFSPlusSigWord && 
		 myAltMDBPtr->drEmbedSigWord == kHFSPlusSigWord &&
		 myPriMDBPtr->drEmbedExtent.blockCount == myAltMDBPtr->drEmbedExtent.blockCount &&
		 myPriMDBPtr->drEmbedExtent.startBlock == myAltMDBPtr->drEmbedExtent.startBlock )
		goto ExitThisRoutine;

	// we know that VolumeObject.embeddedOffset and VolumeObject.totalEmbeddedSectors 
	// are correct so we will verify the info in each MDB calculates to these values.
	myOffset = (myPriMDBPtr->drEmbedExtent.startBlock * myPriMDBPtr->drAlBlkSiz) + 
			   (myPriMDBPtr->drAlBlSt * Blk_Size);
	mySectors = (myPriMDBPtr->drAlBlkSiz / Blk_Size) * myPriMDBPtr->drEmbedExtent.blockCount;

	if ( myOffset != myVOPtr->embeddedOffset || mySectors != myVOPtr->totalEmbeddedSectors ) 
		primaryIsDamaged = true;
	
	myOffset = (myAltMDBPtr->drEmbedExtent.startBlock * myAltMDBPtr->drAlBlkSiz) + 
			   (myAltMDBPtr->drAlBlSt * Blk_Size);
	mySectors = (myAltMDBPtr->drAlBlkSiz / Blk_Size) * myAltMDBPtr->drEmbedExtent.blockCount;

	if ( myOffset != myVOPtr->embeddedOffset || mySectors != myVOPtr->totalEmbeddedSectors ) 
		alternateIsDamaged = true;
	
	// now check drEmbedSigWord if everything else is OK
	if ( primaryIsDamaged == false && alternateIsDamaged == false ) {
		if ( myPriMDBPtr->drEmbedSigWord != kHFSPlusSigWord )
			primaryIsDamaged = true;
		else if ( myAltMDBPtr->drEmbedSigWord != kHFSPlusSigWord )
			alternateIsDamaged = true;
	}

	if ( primaryIsDamaged || alternateIsDamaged ) {
		GPtr->VIStat |= S_WMDB;
		WriteError( GPtr, E_MDBDamaged, 7, 0 );
		if ( primaryIsDamaged ) {
			myVOPtr->flags &= ~kVO_PriMDBOK; // mark the primary MDB as damaged
			if ( GPtr->logLevel >= kDebugLog )
				printf("\tinvalid primary wrapper MDB \n");
		}
		else {
			myVOPtr->flags &= ~kVO_AltMDBOK; // mark the alternate MDB as damaged
			if ( GPtr->logLevel >= kDebugLog )
				printf("\tinvalid alternate wrapper MDB \n");
		}
	}
		
ExitThisRoutine:
	if ( myPrimary.buffer != NULL )
		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kReleaseBlock );
	if ( myAlternate.buffer != NULL )
		(void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kReleaseBlock );

	return;

} /* CheckEmbeddedVolInfoInMDBs */


//******************************************************************************
//	Routine:	ValidVolumeHeader
//
//	Function:	Run some sanity checks to make sure the HFSPlusVolumeHeader is valid
//
// 	Result:		error
//******************************************************************************
OSErr	ValidVolumeHeader( HFSPlusVolumeHeader *volumeHeader )
{
	OSErr	err;
	
	if ((volumeHeader->signature == kHFSPlusSigWord && volumeHeader->version == kHFSPlusVersion) ||
	    (volumeHeader->signature == kHFSXSigWord && volumeHeader->version == kHFSXVersion))
	{
		if ( (volumeHeader->blockSize != 0) && ((volumeHeader->blockSize & 0x01FF) == 0) )			//	non zero multiple of 512
			err = noErr;
		else
			err = badMDBErr;							//¥¥	I want badVolumeHeaderErr in Errors.i
	}
	else
	{
		err = noMacDskErr;
	}
	
	return( err );
}


//_______________________________________________________________________
//
//	InitBTreeHeader
//	
//	This routine initializes a B-Tree header.
//
//	Note: Since large volumes will have bigger b-trees they need to
//	have map nodes setup.
//_______________________________________________________________________

void InitBTreeHeader (UInt32 fileSize, UInt32 clumpSize, UInt16 nodeSize, UInt16 recordCount, UInt16 keySize,
						UInt32 attributes, UInt32 *mapNodes, void *buffer)
{
	UInt32		 nodeCount;
	UInt32		 usedNodes;
	UInt32		 nodeBitsInHeader;
	BTHeaderRec	 *bth;
	BTNodeDescriptor *ndp;
	UInt32		*bitMapPtr;
	SInt16		*offsetPtr;


	ClearMemory(buffer, nodeSize);			// start out with clean node
	
	nodeCount = fileSize / nodeSize;
	nodeBitsInHeader = 8 * (nodeSize - sizeof(BTNodeDescriptor) - sizeof(BTHeaderRec) - kBTreeHeaderUserBytes - 4*sizeof(SInt16));
	
	usedNodes =	1;							// header takes up one node
	*mapNodes = 0;							// number of map nodes initially (0)


	// FILL IN THE NODE DESCRIPTOR:
	ndp = (BTNodeDescriptor*) buffer;	// point to node descriptor

	ndp->kind = kBTHeaderNode;		// this node contains the B-tree header
	ndp->numRecords = 3;			// there are 3 records (header, map, and user)

	if (nodeCount > nodeBitsInHeader)		// do we need additional map nodes?
	{
		UInt32	nodeBitsInMapNode;
		
		nodeBitsInMapNode = 8 * (nodeSize - sizeof(BTNodeDescriptor) - 2*sizeof(SInt16) - 2);	//¥¥ why (-2) at end???

		if (recordCount > 0)			// catalog B-tree?
			ndp->fLink = 2;			// link points to initial map node
											//¥¥ Assumes all records will fit in one node.  It would be better
											//¥¥ to put the map node(s) first, then the records.
		else
			ndp->fLink = 1;			// link points to initial map node

		*mapNodes = (nodeCount - nodeBitsInHeader + (nodeBitsInMapNode - 1)) / nodeBitsInMapNode;
		usedNodes += *mapNodes;
	}

	// FILL IN THE HEADER RECORD:
	bth = (BTHeaderRec*) ((char*)buffer + sizeof(BTNodeDescriptor));	// point to header

	if (recordCount > 0)
	{
		++usedNodes;								// one more node will be used

		bth->treeDepth = 1;							// tree depth is one level (leaf)
		bth->rootNode  = 1;							// root node is also leaf
		bth->firstLeafNode = 1;						// first leaf node
		bth->lastLeafNode = 1;						// last leaf node
	}

	bth->attributes	  = attributes;					// flags for 16-bit key lengths, and variable sized index keys
	bth->leafRecords  = recordCount;				// total number of data records
	bth->nodeSize	  = nodeSize;					// size of a node
	bth->maxKeyLength = keySize;					// maximum length of a key
	bth->totalNodes	  = nodeCount;					// total number of nodes
	bth->freeNodes	  = nodeCount - usedNodes;		// number of free nodes
	bth->clumpSize	  = clumpSize;					//
//	bth->btreeType	  = 0;						// 0 = meta data B-tree


	// FILL IN THE MAP RECORD:
	bitMapPtr = (UInt32*) ((Byte*) buffer + sizeof(BTNodeDescriptor) + sizeof(BTHeaderRec) + kBTreeHeaderUserBytes); // point to bitmap

	// MARK NODES THAT ARE IN USE:
	// Note - worst case (32MB alloc blk) will have only 18 nodes in use.
	*bitMapPtr = ~((UInt32) 0xFFFFFFFF >> usedNodes);
	

	// PLACE RECORD OFFSETS AT THE END OF THE NODE:
	offsetPtr = (SInt16*) ((Byte*) buffer + nodeSize - 4*sizeof(SInt16));

	*offsetPtr++ = sizeof(BTNodeDescriptor) + sizeof(BTHeaderRec) + kBTreeHeaderUserBytes + nodeBitsInHeader/8;	// offset to free space
	*offsetPtr++ = sizeof(BTNodeDescriptor) + sizeof(BTHeaderRec) + kBTreeHeaderUserBytes;						// offset to allocation map
	*offsetPtr++ = sizeof(BTNodeDescriptor) + sizeof(BTHeaderRec);												// offset to user space
	*offsetPtr   = sizeof(BTNodeDescriptor);										// offset to BTH
}

/*------------------------------------------------------------------------------

Routine:	CalculateItemCount

Function:	determines number of items for progress feedback

Input:		vRefNum:  the volume to count items

Output:		number of items

------------------------------------------------------------------------------*/

void	CalculateItemCount( SGlob *GPtr, UInt64 *itemCount, UInt64 *onePercent )
{
	BTreeControlBlock	*btcb;
	VolumeObjectPtr		myVOPtr;
	UInt64				items;
	UInt32				realFreeNodes;
	SVCB				*vcb  = GPtr->calculatedVCB;
	
	/* each bitmap segment is an item */
	myVOPtr = GetVolumeObjectPtr( );
	items = GPtr->calculatedVCB->vcbTotalBlocks / 1024;
	
	//
	// Items is the used node count and leaf record count for each btree...
	//

	btcb = (BTreeControlBlock*) vcb->vcbCatalogFile->fcbBtree;
	realFreeNodes = ((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount;
	items += (2 * btcb->leafRecords) + (btcb->totalNodes - realFreeNodes);

	btcb = (BTreeControlBlock*) vcb->vcbExtentsFile->fcbBtree;
	realFreeNodes = ((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount;
	items += btcb->leafRecords + (btcb->totalNodes - realFreeNodes);

	if ( vcb->vcbAttributesFile != NULL )
	{
		btcb = (BTreeControlBlock*) vcb->vcbAttributesFile->fcbBtree;
		realFreeNodes = ((BTreeExtensionsRec*)btcb->refCon)->realFreeNodeCount;
	
		items += (btcb->leafRecords + (btcb->totalNodes - realFreeNodes));
	}
	
	*onePercent = items/ 100;
	
	//
	//	[2239291]  We're calculating the progress for the wrapper and the embedded volume separately, which
	//	confuses the caller (since they see the progress jump to a large percentage while checking the wrapper,
	//	then jump to a small percentage when starting to check the embedded volume).  To avoid this behavior,
	//	we pretend the wrapper has 100 times as many items as it really does.  This means the progress will
	//	never exceed 1% for the wrapper.
	//
/* fsck_hfs doesn't deal wih the wrapper at this time (8.29.2002)
	if ( (myVOPtr->volumeType == kEmbededHFSPlusVolumeType) && (GPtr->inputFlags & examineWrapperMask) )
		items *= 100; */
	
	//	Add en extra Å 5% to smooth the progress
	items += *onePercent * 5;
	
	*itemCount = items;
}


SFCB* ResolveFCB(short fileRefNum)
{
	return( (SFCB*)((unsigned long)GetFCBSPtr() + (unsigned long)fileRefNum)  );
}


//******************************************************************************
//	Routine:	SetupFCB fills in the FCB info
//
//	Returns:	The filled up FCB
//******************************************************************************
void	SetupFCB( SVCB *vcb, SInt16 refNum, UInt32 fileID, UInt32 fileClumpSize )
{
	SFCB *fcb;

	fcb = ResolveFCB(refNum);
	
	fcb->fcbFileID		= fileID;
	fcb->fcbVolume		= vcb;
	fcb->fcbClumpSize	= fileClumpSize;
}


//******************************************************************************
//
//	Routine:	ResolveFileRefNum
//
//	Purpose:	Return a file reference number for a given file control block
//				pointer.
//
//	Input:
//		fileCtrlBlockPtr	Pointer to the SFCB
//
//	Output:
//		result				File reference number,
//							or 0 if fileCtrlBlockPtr is invalid
//
pascal short ResolveFileRefNum(SFCB * fileCtrlBlockPtr)
{
	return( (unsigned long)fileCtrlBlockPtr - (unsigned long)GetFCBSPtr() );
}



Ptr gFCBSPtr;

void	SetFCBSPtr( Ptr value )
{
	gFCBSPtr = value;
}

Ptr	GetFCBSPtr( void )
{
	return (gFCBSPtr);
}


//_______________________________________________________________________
//
//	Routine:	FlushVolumeControlBlock
//	Arguments:	SVCB		*vcb
//	Output:		OSErr			err
//
//	Function: 	Flush volume information to either the HFSPlusVolumeHeader 
//	of the Master Directory Block
//_______________________________________________________________________

OSErr	FlushVolumeControlBlock( SVCB *vcb )
{
	OSErr				err;
	HFSPlusVolumeHeader	*volumeHeader;
	SFCB				*fcb;
	BlockDescriptor  	block;
	
	if ( ! IsVCBDirty( vcb ) )			//	if it's not dirty
		return( noErr );

	block.buffer = NULL;
	err = GetVolumeObjectPrimaryBlock( &block );
	if ( err != noErr )
	{
		// attempt to fix the primary with alternate
		if ( block.buffer != NULL ) {
			(void) ReleaseVolumeBlock( vcb, &block, kReleaseBlock );
			block.buffer = NULL;
		}
			
		err = VolumeObjectFixPrimaryBlock( );
		ReturnIfError( err );
		
		// should be able to get it now
		err = GetVolumeObjectPrimaryBlock( &block );
		ReturnIfError( err );
	}

	if ( vcb->vcbSignature == kHFSPlusSigWord )
	{		
		volumeHeader = (HFSPlusVolumeHeader *) block.buffer;
		
		// 2005507, Keep the MDB creation date and HFSPlusVolumeHeader creation date in sync.
		if ( vcb->vcbEmbeddedOffset != 0 )  // It's a wrapped HFS+ volume
		{
			HFSMasterDirectoryBlock		*mdb;
			BlockDescriptor  			mdb_block;

			mdb_block.buffer = NULL;
			err = GetVolumeObjectPrimaryMDB( &mdb_block );
			if ( err == noErr )
			{
				mdb = (HFSMasterDirectoryBlock	*) mdb_block.buffer;
				if ( mdb->drCrDate != vcb->vcbCreateDate )  // The creation date changed
				{
					mdb->drCrDate = vcb->vcbCreateDate;
					(void) ReleaseVolumeBlock(vcb, &mdb_block, kForceWriteBlock);
					mdb_block.buffer = NULL;
				}
			}
			if ( mdb_block.buffer != NULL )
				(void) ReleaseVolumeBlock(vcb, &mdb_block, kReleaseBlock);
		}

		volumeHeader->attributes		= vcb->vcbAttributes;
		volumeHeader->lastMountedVersion = kFSCKMountVersion;
		volumeHeader->createDate		= vcb->vcbCreateDate;  // NOTE: local time, not GMT!
		volumeHeader->modifyDate		= vcb->vcbModifyDate;
		volumeHeader->backupDate		= vcb->vcbBackupDate;
		volumeHeader->checkedDate		= vcb->vcbCheckedDate;
		volumeHeader->fileCount			= vcb->vcbFileCount;
		volumeHeader->folderCount		= vcb->vcbFolderCount;
		volumeHeader->blockSize			= vcb->vcbBlockSize;
		volumeHeader->totalBlocks		= vcb->vcbTotalBlocks;
		volumeHeader->freeBlocks		= vcb->vcbFreeBlocks;
		volumeHeader->nextAllocation		= vcb->vcbNextAllocation;
		volumeHeader->rsrcClumpSize		= vcb->vcbRsrcClumpSize;
		volumeHeader->dataClumpSize		= vcb->vcbDataClumpSize;
		volumeHeader->nextCatalogID		= vcb->vcbNextCatalogID;
		volumeHeader->writeCount		= vcb->vcbWriteCount;
		volumeHeader->encodingsBitmap		= vcb->vcbEncodingsBitmap;

		//¥¥Êshould we use the vcb or fcb clumpSize values ????? -djb
		volumeHeader->allocationFile.clumpSize	= vcb->vcbAllocationFile->fcbClumpSize;
		volumeHeader->extentsFile.clumpSize	= vcb->vcbExtentsFile->fcbClumpSize;
		volumeHeader->catalogFile.clumpSize	= vcb->vcbCatalogFile->fcbClumpSize;
		
		CopyMemory( vcb->vcbFinderInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo) );
	
		fcb = vcb->vcbExtentsFile;
		CopyMemory( fcb->fcbExtents32, volumeHeader->extentsFile.extents, sizeof(HFSPlusExtentRecord) );
		volumeHeader->extentsFile.logicalSize = fcb->fcbLogicalSize;
		volumeHeader->extentsFile.totalBlocks = fcb->fcbPhysicalSize / vcb->vcbBlockSize;
	
		fcb = vcb->vcbCatalogFile;
		CopyMemory( fcb->fcbExtents32, volumeHeader->catalogFile.extents, sizeof(HFSPlusExtentRecord) );
		volumeHeader->catalogFile.logicalSize = fcb->fcbLogicalSize;
		volumeHeader->catalogFile.totalBlocks = fcb->fcbPhysicalSize / vcb->vcbBlockSize;

		fcb = vcb->vcbAllocationFile;
		CopyMemory( fcb->fcbExtents32, volumeHeader->allocationFile.extents, sizeof(HFSPlusExtentRecord) );
		volumeHeader->allocationFile.logicalSize = fcb->fcbLogicalSize;
		volumeHeader->allocationFile.totalBlocks = fcb->fcbPhysicalSize / vcb->vcbBlockSize;
		
		if (vcb->vcbAttributesFile != NULL)	//	Only update fields if an attributes file existed and was open
		{
			fcb = vcb->vcbAttributesFile;
			CopyMemory( fcb->fcbExtents32, volumeHeader->attributesFile.extents, sizeof(HFSPlusExtentRecord) );
			volumeHeader->attributesFile.logicalSize = fcb->fcbLogicalSize;
			volumeHeader->attributesFile.clumpSize = fcb->fcbClumpSize;
			volumeHeader->attributesFile.totalBlocks = fcb->fcbPhysicalSize / vcb->vcbBlockSize;
		}
	}
	else
	{
		HFSMasterDirectoryBlock	*mdbP;

		mdbP = (HFSMasterDirectoryBlock	*) block.buffer;

		mdbP->drCrDate    = vcb->vcbCreateDate;
		mdbP->drLsMod     = vcb->vcbModifyDate;
		mdbP->drAtrb      = (UInt16)vcb->vcbAttributes;
		mdbP->drClpSiz    = vcb->vcbDataClumpSize;
		mdbP->drNxtCNID   = vcb->vcbNextCatalogID;
		mdbP->drFreeBks   = vcb->vcbFreeBlocks;
		mdbP->drXTClpSiz  = vcb->vcbExtentsFile->fcbClumpSize;
		mdbP->drCTClpSiz  = vcb->vcbCatalogFile->fcbClumpSize;

		mdbP->drNmFls     = vcb->vcbNmFls;
		mdbP->drNmRtDirs  = vcb->vcbNmRtDirs;
		mdbP->drFilCnt    = vcb->vcbFileCount;
		mdbP->drDirCnt    = vcb->vcbFolderCount;

		fcb = vcb->vcbExtentsFile;
		CopyMemory( fcb->fcbExtents16, mdbP->drXTExtRec, sizeof( mdbP->drXTExtRec ) );

		fcb = vcb->vcbCatalogFile;
		CopyMemory( fcb->fcbExtents16, mdbP->drCTExtRec, sizeof( mdbP->drCTExtRec ) );
	}
		
	//--	Write the VHB/MDB out by releasing the block dirty
	if ( block.buffer != NULL ) {
		err = ReleaseVolumeBlock(vcb, &block, kForceWriteBlock);	
		block.buffer = NULL;
	}
	MarkVCBClean( vcb );

	return( err );
}


//_______________________________________________________________________
//
//	Routine:	FlushAlternateVolumeControlBlock
//	Arguments:	SVCB		*vcb
//				Boolean			ifHFSPlus
//	Output:		OSErr			err
//
//	Function: 	Flush volume information to either the Alternate HFSPlusVolumeHeader or the 
//				Alternate Master Directory Block.  Called by the BTree when the catalog
//				or extent files grow.  Simply BlockMoves the original to the alternate
//				location.
//_______________________________________________________________________

OSErr	FlushAlternateVolumeControlBlock( SVCB *vcb, Boolean isHFSPlus )
{
	OSErr  				err;
	VolumeObjectPtr		myVOPtr;
	UInt64				myBlockNum;
	BlockDescriptor  	pri_block, alt_block;
	
	pri_block.buffer = NULL;
	alt_block.buffer = NULL;
	myVOPtr = GetVolumeObjectPtr( );

	err = FlushVolumeControlBlock( vcb );
	err = GetVolumeObjectPrimaryBlock( &pri_block );
	
	// invalidate if we have not marked the primary as OK
	if ( VolumeObjectIsHFS( ) ) {
		if ( (myVOPtr->flags & kVO_PriMDBOK) == 0 )
			err = badMDBErr;
	}
	else if ( (myVOPtr->flags & kVO_PriVHBOK) == 0 ) {
		err = badMDBErr;
	}
	if ( err != noErr )
		goto ExitThisRoutine;

	GetVolumeObjectAlternateBlockNum( &myBlockNum );
	if ( myBlockNum != 0 ) {
		// we don't care if this is an invalid MDB / VHB since we will write over it
		err = GetVolumeObjectAlternateBlock( &alt_block );
		if ( err == noErr || err == badMDBErr || err == noMacDskErr ) {
			CopyMemory( pri_block.buffer, alt_block.buffer, Blk_Size );
			(void) ReleaseVolumeBlock(vcb, &alt_block, kForceWriteBlock);		
			alt_block.buffer = NULL;
		}
	}

ExitThisRoutine:
	if ( pri_block.buffer != NULL )
		(void) ReleaseVolumeBlock( vcb, &pri_block, kReleaseBlock );
	if ( alt_block.buffer != NULL )
		(void) ReleaseVolumeBlock( vcb, &alt_block, kReleaseBlock );

	return( err );
}

void
ConvertToHFSPlusExtent( const HFSExtentRecord oldExtents, HFSPlusExtentRecord newExtents)
{
	UInt16	i;

	// go backwards so we can convert in place!
	
	for (i = kHFSPlusExtentDensity-1; i > 2; --i)
	{
		newExtents[i].blockCount = 0;
		newExtents[i].startBlock = 0;
	}

	newExtents[2].blockCount = oldExtents[2].blockCount;
	newExtents[2].startBlock = oldExtents[2].startBlock;
	newExtents[1].blockCount = oldExtents[1].blockCount;
	newExtents[1].startBlock = oldExtents[1].startBlock;
	newExtents[0].blockCount = oldExtents[0].blockCount;
	newExtents[0].startBlock = oldExtents[0].startBlock;
}



OSErr	CacheWriteInPlace( SVCB *vcb, UInt32 fileRefNum,  HIOParam *iopb, UInt64 currentPosition, UInt32 maximumBytes, UInt32 *actualBytes )
{
	OSErr err;
	UInt64 diskBlock;
	UInt32 contiguousBytes;
	void* buffer;
	
	*actualBytes = 0;
	buffer = (char*)iopb->ioBuffer + iopb->ioActCount;

	err = MapFileBlockC(vcb, ResolveFCB(fileRefNum), maximumBytes, (currentPosition >> kSectorShift),
				&diskBlock, &contiguousBytes );
	if (err)
		return (err);

	err = DeviceWrite(vcb->vcbDriverWriteRef, vcb->vcbDriveNumber, buffer, (diskBlock << Log2BlkLo), contiguousBytes, actualBytes);
	
	return( err );
}


void PrintName( int theCount, const UInt8 *theNamePtr, Boolean isUnicodeString )
{
    int			myCount;
 	int			i;
    
    myCount = (isUnicodeString) ? (theCount * 2) : theCount;
    for ( i = 0; i < myCount; i++ ) 
        printf( "%02X ", *(theNamePtr + i) );
    printf( "\n" );

} /* PrintName */

#if DEBUG_XATTR
#define MAX_PRIMES 11	
int primes[] = {32, 27, 25, 7, 11, 13, 17, 19, 23, 29, 31};
void print_prime_buckets(PrimeBuckets *cur); 
#endif

/* Function:	RecordXAttrBits
 *
 * Description:
 * This function increments the prime number buckets for the associated 
 * prime bucket set based on the flags and btreetype to determine 
 * the discrepancy between the attribute btree and catalog btree for
 * extended attribute data consistency.  This function is based on
 * Chinese Remainder Theorem.  
 * 
 * Alogrithm:
 * 1. If none of kHFSHasAttributesMask or kHFSHasSecurity mask is set, 
 *    return.
 * 2. Based on btreetype and the flags, determine which prime number
 *    bucket should be updated.  Initialize pointers accordingly. 
 * 3. Divide the fileID with pre-defined prime numbers. Store the 
 *    remainder.
 * 4. Increment each prime number bucket at an offset of the 
 *    corresponding remainder with one.
 *
 * Input:	1. GPtr - pointer to global scavenger area
 *        	2. flags - can include kHFSHasAttributesMask and/or kHFSHasSecurityMask
 *        	3. fileid - fileID for which particular extended attribute is seen
 *     	   	4. btreetye - can be kHFSPlusCatalogRecord or kHFSPlusAttributeRecord
 *                            indicates which btree prime number bucket should be incremented
 *
 * Output:	nil
 */
void RecordXAttrBits(SGlobPtr GPtr, UInt16 flags, HFSCatalogNodeID fileid, UInt16 btreetype) 
{
	PrimeBuckets *cur_attr = NULL;
	PrimeBuckets *cur_sec = NULL;
	int r32, r27, r25, r7, r11, r13, r17, r19, r23, r29, r31;

	if ( ((flags & kHFSHasAttributesMask) == 0) && 
	     ((flags & kHFSHasSecurityMask) == 0) ) {
		/* No attributes exists for this fileID */
		goto out;
	}
	
	/* Determine which bucket are we updating */
	if (btreetype ==  kCalculatedCatalogRefNum) {
		/* Catalog BTree buckets */
		if (flags & kHFSHasAttributesMask) {
			cur_attr = &(GPtr->CBTAttrBucket); 
		}
		if (flags & kHFSHasSecurityMask) {
			cur_sec = &(GPtr->CBTSecurityBucket); 
		}
	} else if (btreetype ==  kCalculatedAttributesRefNum) {
		/* Attribute BTree buckets */
		if (flags & kHFSHasAttributesMask) {
			cur_attr = &(GPtr->ABTAttrBucket); 
		}
		if (flags & kHFSHasSecurityMask) {
			cur_sec = &(GPtr->ABTSecurityBucket); 
		}
	} else {
		/* Incorrect btreetype found */
		goto out;
	}

	/* Perform the necessary divisions here */
	r32 = fileid % 32;
	r27 = fileid % 27;
	r25 = fileid % 25;
	r7  = fileid % 7;
	r11 = fileid % 11;
	r13 = fileid % 13;
	r17 = fileid % 17;
	r19 = fileid % 19;
	r23 = fileid % 23;
	r29 = fileid % 29;
	r31 = fileid % 31;
	
	/* Update bucket for attribute bit */
	if (cur_attr) {
		cur_attr->n32[r32]++;
		cur_attr->n27[r27]++;
		cur_attr->n25[r25]++;
		cur_attr->n7[r7]++;
		cur_attr->n11[r11]++;
		cur_attr->n13[r13]++;
		cur_attr->n17[r17]++;
		cur_attr->n19[r19]++;
		cur_attr->n23[r23]++;
		cur_attr->n29[r29]++;
		cur_attr->n31[r31]++;
	}

	/* Update bucket for security bit */
	if (cur_sec) {
		cur_sec->n32[r32]++;
		cur_sec->n27[r27]++;
		cur_sec->n25[r25]++;
		cur_sec->n7[r7]++;
		cur_sec->n11[r11]++;
		cur_sec->n13[r13]++;
		cur_sec->n17[r17]++;
		cur_sec->n19[r19]++;
		cur_sec->n23[r23]++;
		cur_sec->n29[r29]++;
		cur_sec->n31[r31]++;
	}
	
#if 0
	printf ("\nFor fileID = %d\n", fileid);
	if (cur_attr) {
		printf ("Attributes bucket:\n");
		print_prime_buckets(cur_attr);
	}
	if (cur_sec) {
		printf ("Security bucket:\n");
		print_prime_buckets(cur_sec);
	}
#endif 
out:
	return;
}

/* Function:	ComparePrimeBuckets
 *
 * Description:
 * This function compares the prime number buckets for catalog btree
 * and attribute btree for the given attribute type (normal attribute
 * bit or security bit).
 *
 * Input:	1. GPtr - pointer to global scavenger area
 *         	2. BitMask - indicate which attribute type should be compared.
 *        	             can include kHFSHasAttributesMask and/or kHFSHasSecurityMask
 * Output:	zero - the buckets are equal
 *            	non-zero - the buckets are unqual
 */
int ComparePrimeBuckets(SGlobPtr GPtr, UInt16 BitMask) 
{
	int result = 0;
	int i;
	PrimeBuckets *cat;	/* Catalog BTree */
	PrimeBuckets *attr;	/* Attribute BTree */
	
	/* Find the correct PrimeBuckets to compare */
	if (BitMask & kHFSHasAttributesMask) {
		/* Compare buckets for attribute bit */
		cat = &(GPtr->CBTAttrBucket);
		attr = &(GPtr->ABTAttrBucket); 
	} else if (BitMask & kHFSHasSecurityMask) {
		/* Compare buckets for security bit */
		cat = &(GPtr->CBTSecurityBucket);
		attr = &(GPtr->ABTSecurityBucket); 
	} else {
		printf ("%s: Incorrect BitMask found.\n", __FUNCTION__);
		goto out;
	}

	for (i=0; i<32; i++) {
		if (cat->n32[i] != attr->n32[i]) {
			goto unequal_out;
		}
	}
	
	for (i=0; i<27; i++) {
		if (cat->n27[i] != attr->n27[i]) {
			goto unequal_out;
		}
	}

	for (i=0; i<25; i++) {
		if (cat->n25[i] != attr->n25[i]) {
			goto unequal_out;
		}
	}

	for (i=0; i<7; i++) {
		if (cat->n7[i] != attr->n7[i]) {
			goto unequal_out;
		}
	}
	
	for (i=0; i<11; i++) {
		if (cat->n11[i] != attr->n11[i]) {
			goto unequal_out;
		}
	}

	for (i=0; i<13; i++) {
		if (cat->n13[i] != attr->n13[i]) {
			goto unequal_out;
		}
	}

	for (i=0; i<17; i++) {
		if (cat->n17[i] != attr->n17[i]) {
			goto unequal_out;
		}
	}

	for (i=0; i<19; i++) {
		if (cat->n19[i] != attr->n19[i]) {
			goto unequal_out;
		}
	}

	for (i=0; i<23; i++) {
		if (cat->n23[i] != attr->n23[i]) {
			goto unequal_out;
		}
	}

	for (i=0; i<29; i++) {
		if (cat->n29[i] != attr->n29[i]) {
			goto unequal_out;
		}
	}

	for (i=0; i<31; i++) {
		if (cat->n31[i] != attr->n31[i]) {
			goto unequal_out;
		}
	}

	goto out;

unequal_out:
	/* Unequal values found, set the error bit in ABTStat */
	if (BitMask & kHFSHasAttributesMask) {
		RcdError (GPtr, E_IncorrectAttrCount);
		GPtr->ABTStat |= S_AttributeCount; 
	} else {
		RcdError (GPtr, E_IncorrectSecurityCount);
		GPtr->ABTStat |= S_SecurityCount; 
	}
#if DEBUG_XATTR
	if (BitMask & kHFSHasAttributesMask) {
		printf ("For kHFSHasAttributesMask:\n");
	} else {
		printf ("For kHFSHasSecurityMask:\n");
	}
	printf("Catalog BTree bucket:\n");
	print_prime_buckets(cat);
	printf("Attribute BTree bucket\n");
	print_prime_buckets(attr);
#endif
out:
	return result;
}

#if DEBUG_XATTR
/* Prints the prime number bucket for the passed pointer */
void print_prime_buckets(PrimeBuckets *cur) 
{
	int i;
	
	printf ("n32 = { ");
	for (i=0; i<32; i++) {
		printf ("%d,", cur->n32[i]);
	}
	printf ("}\n");

	printf ("n27 = { ");
	for (i=0; i<27; i++) {
		printf ("%d,", cur->n27[i]);
	}
	printf ("}\n");

	printf ("n25 = { ");
	for (i=0; i<25; i++) {
		printf ("%d,", cur->n25[i]);
	}
	printf ("}\n");

	printf ("n7 = { ");
	for (i=0; i<7; i++) {
		printf ("%d,", cur->n7[i]);
	}
	printf ("}\n");
	
	printf ("n11 = { ");
	for (i=0; i<11; i++) {
		printf ("%d,", cur->n11[i]);
	}
	printf ("}\n");

	printf ("n13 = { ");
	for (i=0; i<13; i++) {
		printf ("%d,", cur->n13[i]);
	}
	printf ("}\n");

	printf ("n17 = { ");
	for (i=0; i<17; i++) {
		printf ("%d,", cur->n17[i]);
	}
	printf ("}\n");

	printf ("n19 = { ");
	for (i=0; i<19; i++) {
		printf ("%d,", cur->n19[i]);
	}
	printf ("}\n");

	printf ("n23 = { ");
	for (i=0; i<23; i++) {
		printf ("%d,", cur->n23[i]);
	}
	printf ("}\n");

	printf ("n29 = { ");
	for (i=0; i<29; i++) {
		printf ("%d,", cur->n29[i]);
	}
	printf ("}\n");

	printf ("n31 = { ");
	for (i=0; i<31; i++) {
		printf ("%d,", cur->n31[i]);
	}
	printf ("}\n");
}
#endif