Source to bsd/hfs/hfscommon/Catalog/CatalogUtilities.c


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 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:		CatalogUtilities.c

	Contains:	Private Catalog Manager support routines.

	Version:	HFS Plus 1.0

	Copyright:	� 1997-1999 by Apple Computer, Inc., all rights reserved.

	File Ownership:

		DRI:				Don Brady

		Other Contact:		Mark Day

		Technology:			xxx put technology here xxx

	Writers:

		(DSH)	Deric Horn
		(msd)	Mark Day
		(djb)	Don Brady

	Change History (most recent first):
 	  <Rhap>	  1/8/99	djb		Fixing LocateCatalogNodeByMangledName...
 	  <Rhap>	  1/7/99	djb		In BuildCatalogKeyUTF8 check name length against NAME_MAX.
 	  <Rhap>	 12/7/98	djb		Add ExtractTextEncoding routine to get text encodings.
 	  <Rhap>	11/20/98	djb		Add support for UTF-8 names.
 	  <Rhap>	 8/31/98	djb		GetTimeLocal now takes an input.
	  <Rhap>	 4/17/98	djb		Add VCB locking.
	  <Rhap>	  4/3/98	djb		Removed last name conversion cache from LocateCatalogNodeWithRetry.
	  <Rhap>	  4/2/98	djb		InvalidateCatalogNodeCache and TrashCatalogNodeCache are not used in Rhapsody.
	  <Rhap>	03/31/98	djb		Sync up with final HFSVolumes.h header file.

	  <CS24>	 1/29/98	DSH		Add TrashCatalogNodeCache for TrashAllFSCaches API support.
	  <CS23>	12/15/97	djb		Radar #2202860, In LocateCatalogNodeByMangledName remap
									cmParentNotFound error code to cmNotFound.
	  <CS22>	12/10/97	DSH		2201501, Pin the leof and peof to multiple of allocation blocks
									under 2 Gig.
	  <CS21>	 12/9/97	DSH		2201501, Pin returned leof values to 2^31-1 (SInt32), instead of
									2^32-1
	  <CS20>	11/26/97	djb		Radar #2005688, 2005461 - need to handle kTextMalformedInputErr.
	  <CS19>	11/25/97	djb		Radar #2002357 (again) fix new bug introduced in <CS18>.
	  <CS18>	11/17/97	djb		PrepareInputName routine now returns an error.
	  <CS17>	10/19/97	msd		Bug 1684586. GetCatInfo and SetCatInfo use only contentModDate.
	  <CS16>	10/17/97	djb		Add ConvertInputNameToUnicode for Catalog Create/Rename.
	  <CS15>	10/14/97	djb		Fix LocateCatalogNode's MakeFSSpec optimization (radar #1683166)
	  <CS14>	10/13/97	djb		Copy text encoding in CopyCatalogNodeData. Fix cut/paste error
									in VolumeHasEncodings macro. When accessing encoding bitmap use
									the MapEncodingToIndex and MapIndexToEncoding macros.
	  <CS13>	 10/1/97	djb		Remove old Catalog Iterator code...
	  <CS12>	  9/8/97	msd		Make sure a folder's modifyDate is set whenever its
									contentModDate is set.
	  <CS11>	  9/4/97	djb		Add MakeFSSpec optimization.
	  <CS10>	  9/4/97	msd		In CatalogNodeData, change attributeModDate to modifyDate.
	   <CS9>	 8/26/97	djb		Back out <CS4> (UpdateFolderCount must maintain vcbNmFls for HFS
									Plus volumes too).
	   <CS8>	 8/14/97	djb		Remove hard link support.
	   <CS7>	 7/18/97	msd		Include LowMemPriv.h.
	   <CS6>	 7/16/97	DSH		FilesInternal.i renamed FileMgrInternal.i to avoid name
									collision
	   <CS5>	  7/8/97	DSH		Loading PrecompiledHeaders from define passed in on C line
	   <CS4>	 6/27/97	msd		UpdateFolderCount should update number of root files/folders for
									HFS volumes, not HFS Plus.
	   <CS3>	 6/24/97	djb		LocateCatalogNodeWithRetry did not always set result code.
	   <CS2>	 6/24/97	djb		Add LocateCatalogNodeByMangledName routine
	   <CS1>	 6/24/97	djb		first checked in
*/

#if ( PRAGMA_LOAD_SUPPORTED )
        #pragma	load	PrecompiledHeaders
#else
        #if TARGET_OS_MAC
        #include	<Errors.h>
        #include	<Files.h>
        #include	<FSM.h>
        #include	<Memory.h>
        #include	<TextUtils.h>
        #include	<Types.h>
        #include	<LowMemPriv.h>
        #else
			#include "../headers/system/MacOSStubs.h"
			#include <sys/syslimits.h>
        #endif 	/* TARGET_OS_MAC */
#endif	/* PRAGMA_LOAD_SUPPORTED */


#include	"../headers/FileMgrInternal.h"
#include	"../headers/BTreesInternal.h"
#include	"../headers/HFSVolumes.h"
#include	"../headers/system/HFSUnicodeWrappers.h"
#include	"../headers/CatalogPrivate.h"
#include	<string.h>

static void ExtractTextEncoding (ItemCount length, ConstUniCharArrayPtr string, UInt32 * textEncoding);

//�������������������������������������������������������������������������������
//	Routine:	LocateCatalogNode
//
// Function: 	Locates the catalog record for an existing folder or file
//				CNode and returns pointers to the key and data records.
//
//�������������������������������������������������������������������������������

OSErr
LocateCatalogNode(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name,
					UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint)
{
	OSErr				result;
	CatalogName 		*nodeName = NULL;	/* To ward off uninitialized use warnings from compiler */
	HFSCatalogNodeID	threadParentID;


	result = LocateCatalogRecord(volume, folderID, name, hint, keyPtr, dataPtr, newHint);

	#if TARGET_OS_MAC
	//
	// MakeFSSpec Optimization...report a missing parent
	//
	if ( (result == cmNotFound) && (LMGetHFSFlags() & (1 << reportMissingParent)) )
	{
		HFSCatalogNodeID	parentID;
		UInt16				dataSize;
		OSErr				err;
		Boolean				isHFSPlus = (volume->vcbSigWord == kHFSPlusSigWord);
		
		result = cmParentNotFound;	// assume directory is missing
		
		if ( (name != NULL) && (CatalogNameLength(name, isHFSPlus) > 0) )
		{
			// Look at the previous record's parent ID to determine if the
			// parent of the CNode we were looking for actually exists
	
			err = GetBTreeRecord(volume->catalogRefNum, -1, keyPtr, dataPtr, &dataSize, newHint);
			if (err == noErr)
			{
				if ( isHFSPlus )
					parentID = keyPtr->hfsPlus.parentID;
				else
					parentID = keyPtr->hfs.parentID;
				
				if ( folderID == parentID )		// did parent exists?
					result = cmNotFound;	// yes, so report a missing file to MakeFSSpec
			}
		}
	}
	#endif /* TARGET_OS_MAC */

	ReturnIfError(result);
	
	// if we got a thread record, then go look up real record
	switch ( dataPtr->recordType )
	{
		case kHFSFileThreadRecord:
		case kHFSFolderThreadRecord:
			threadParentID = dataPtr->hfsThread.parentID;
			nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName;
			break;

		case kHFSPlusFileThreadRecord:
		case kHFSPlusFolderThreadRecord:
			threadParentID = dataPtr->hfsPlusThread.parentID;
			nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName;	
			break;

		default:
			threadParentID = 0;
			break;
	}
	
	if ( threadParentID )		// found a thread
		result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint);
	
	return result;
}

//
//	Routine:	LocateCatalogNodeByKey
//
// Function: 	Locates the catalog record for an existing folder or file
//				CNode and returns the key and data records.
//

OSErr
LocateCatalogNodeByKey(const ExtendedVCB *volume, UInt32 hint, CatalogKey *keyPtr,
						CatalogRecord *dataPtr, UInt32 *newHint)
{
	OSErr				result;
	CatalogName 		*nodeName = NULL;	/* To ward off uninitialized use warnings from compiler */
	HFSCatalogNodeID	threadParentID;
	UInt16 tempSize;


	result = SearchBTreeRecord(volume->catalogRefNum, keyPtr, hint, keyPtr,
								dataPtr, &tempSize, newHint);
	if (result == btNotFound)
		result = cmNotFound;	
	ReturnIfError(result);
	
	// if we got a thread record, then go look up real record
	switch ( dataPtr->recordType )
	{
		case kHFSFileThreadRecord:
		case kHFSFolderThreadRecord:
			threadParentID = dataPtr->hfsThread.parentID;
			nodeName = (CatalogName *) &dataPtr->hfsThread.nodeName;
			break;

		case kHFSPlusFileThreadRecord:
		case kHFSPlusFolderThreadRecord:
			threadParentID = dataPtr->hfsPlusThread.parentID;
			nodeName = (CatalogName *) &dataPtr->hfsPlusThread.nodeName;	
			break;

		default:
			threadParentID = 0;
			break;
	}
	
	if ( threadParentID )		// found a thread
		result = LocateCatalogRecord(volume, threadParentID, nodeName, kNoHint, keyPtr, dataPtr, newHint);
	
	return result;
}


#if 0
//�������������������������������������������������������������������������������
//	Routine:	LocateCatalogNodeWithRetry
//
// Function: 	Locates the catalog record for an existing folder or file node.
//				For HFS Plus volumes a retry is performed when a catalog node is
//				not found and the volume contains more than one text encoding.
//
//�������������������������������������������������������������������������������

#define VolumeHasEncodings(v) \
	( ((v)->encodingsBitmap.lo | (v)->encodingsBitmap.hi) != 0 )

#define	EncodingInstalled(i) \
	( (fsVars)->gConversionContext[(i)].toUnicode != 0 )

#define	EncodingUsedByVolume(v,i) \
	( i < 32 ? ((v)->encodingsBitmap.lo & (1 << (i))) : ((v)->encodingsBitmap.hi & (1 << (i-32))) )


OSErr
LocateCatalogNodeWithRetry (const ExtendedVCB *volume, HFSCatalogNodeID folderID, ConstStr31Param pascalName, CatalogName *unicodeName,
							UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint)
{
	TextEncoding		defaultEncoding;
	TextEncoding		encoding;
	ItemCount			encodingsToTry;
	FSVarsRec			*fsVars;
	OSErr				result = cmNotFound;

	fsVars = (FSVarsRec*) LMGetFSMVars();	// used by macros

	defaultEncoding = GetDefaultTextEncoding();
	encodingsToTry = CountInstalledEncodings();

	// 1. Try finding file using default encoding (typical case)

	{
		--encodingsToTry;
		result = PrepareInputName(pascalName, true, defaultEncoding, unicodeName);
		if (result == noErr)
			result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
		else
			result = cmNotFound;

		if ( result != cmNotFound || encodingsToTry == 0)
			return result;
	}

	//
	// XXX if the pascal string contains all 7-bit ascii then we don't need to do anymore retries
	//

	// 2. Try finding file using Mac Roman (if not already tried above)

	if ( defaultEncoding != kTextEncodingMacRoman )
	{
		--encodingsToTry;
		result = PrepareInputName(pascalName, true, kTextEncodingMacRoman, unicodeName);
		if (result == noErr)
			result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
		else
			result = cmNotFound;

		if ( result != cmNotFound || encodingsToTry == 0 )
			return result;
	}

	// 3. Try with encodings from disk (if any)

	if ( VolumeHasEncodings(volume) )	// any left to try?
	{
		UInt32	index;

		index = 0;	// since we pre increment this will skip MacRoman (which was already tried above)

		while ( index < kMacBaseEncodingCount )
		{
			++index;
			
			encoding = MapIndexToEncoding(index);

			if ( encoding == defaultEncoding )
				continue;		// we did this one already

			if ( EncodingInstalled(index) && EncodingUsedByVolume(volume, index) )
			{
				--encodingsToTry;
				result = PrepareInputName(pascalName, true, encoding, unicodeName);
				if (result == noErr)
					result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
				else
					result = cmNotFound;

				if ( result != cmNotFound || encodingsToTry == 0 )
					return result;
			}
		}
	}

	// 4. Try any remaining encodings (if any)

	{
		UInt32	index;

		index = 0;	// since we pre increment this will skip MacRoman (which was already tried above)
	
		while ( (encodingsToTry > 0) && (index < kMacBaseEncodingCount) )
		{
			++index;
	
			encoding = MapIndexToEncoding(index);
	
			if ( encoding == defaultEncoding )
				continue;		// we did this one already
	
			if ( EncodingInstalled(index) && EncodingUsedByVolume(volume, index) == false )
			{
				--encodingsToTry;
				result = PrepareInputName(pascalName, true, encoding, unicodeName);
				if (result == noErr)
					result = LocateCatalogNode(volume, folderID, unicodeName, hint, keyPtr, dataPtr, newHint);
				else
					result = cmNotFound;

				if ( result != cmNotFound || encodingsToTry == 0 )
					return result;
			}
		}
	}

	return cmNotFound;
}
#endif

//�������������������������������������������������������������������������������
//	Routine:	LocateCatalogNodeByMangledName
//
// Function: 	Locates the catalog record associated with a mangled name (if any)
//
//�������������������������������������������������������������������������������

OSErr
LocateCatalogNodeByMangledName( const ExtendedVCB *volume, HFSCatalogNodeID folderID,
								const unsigned char * name, CatalogKey *keyPtr,
								CatalogRecord *dataPtr, UInt32 *hintPtr )
{
	HFSCatalogNodeID 	fileID;
	unsigned char		nodeName[64];
	OSErr				result;
	ByteCount			actualDstLen;
	ByteCount			prefixlen;


	if (name == NULL || name[0] == '\0')
		return cmNotFound;

	fileID = GetEmbeddedFileID(name, &prefixlen);

	if ( fileID < kHFSFirstUserCatalogNodeID )
		return cmNotFound;
		
	result = LocateCatalogNode(volume, fileID, NULL, kNoHint, keyPtr, dataPtr, hintPtr);
	if ( result == cmParentNotFound )	// GetCatalogNode already handled cmParentNotFound case 	<CS23>
		result = cmNotFound;			// so remap													<CS23>
	ReturnIfError(result);
		
	// first make sure that the parents match
	if ( folderID != keyPtr->hfsPlus.parentID )
		return cmNotFound;			// not the same folder so this is a false match

	result =  ConvertUnicodeToUTF8( keyPtr->hfsPlus.nodeName.length * sizeof (UniChar),
									keyPtr->hfsPlus.nodeName.unicode,
									64,
									&actualDstLen,
									nodeName);

	if ( (actualDstLen < prefixlen) || bcmp(nodeName, name, prefixlen) != 0)
		return cmNotFound;	// mangled names didn't match so this is a false match
	
	return noErr;	// we found it
}


//�������������������������������������������������������������������������������
//	Routine:	LocateCatalogRecord
//
// Function: 	Locates the catalog record associated with folderID and name
//
//�������������������������������������������������������������������������������

OSErr
LocateCatalogRecord(const ExtendedVCB *volume, HFSCatalogNodeID folderID, const CatalogName *name,
					UInt32 hint, CatalogKey *keyPtr, CatalogRecord *dataPtr, UInt32 *newHint)
{
	OSErr			result;
	CatalogKey		tempKey;	// 518 bytes
	UInt16			tempSize;

	BuildCatalogKey(folderID, name, (volume->vcbSigWord == kHFSPlusSigWord), &tempKey);

	if ( name == NULL )
		hint = kNoHint;			// no CName given so clear the hint

	result = SearchBTreeRecord(volume->catalogRefNum, &tempKey, hint, keyPtr, dataPtr, &tempSize, newHint);
	
	return (result == btNotFound ? cmNotFound : result);	
}


//�������������������������������������������������������������������������������
//	Routine:	LocateCatalogThread
//
//	Function: 	Locates a catalog thread record in the catalog BTree file and 
//				returns a pointer to the data record.
//
//�������������������������������������������������������������������������������

OSErr
LocateCatalogThread(const ExtendedVCB *volume, HFSCatalogNodeID nodeID, CatalogRecord *threadData, UInt16 *threadSize, UInt32 *threadHint)
{
	CatalogKey	threadKey;	// 518 bytes
	OSErr		result;

	//--- build key record

	BuildCatalogKey(nodeID, NULL, (volume->vcbSigWord == kHFSPlusSigWord), &threadKey);

	//--- locate thread record in BTree

	result = SearchBTreeRecord( volume->catalogRefNum, &threadKey, kNoHint, &threadKey,
								threadData, threadSize, threadHint);
	
	return (result == btNotFound ? cmNotFound : result);	
}


/*
 *	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
		}
	}
}

/*
 * for HFS, only MacRoman is supported. If a non-MacRoman character is found, an error is returned
 */
OSErr
BuildCatalogKeyUTF8(HFSCatalogNodeID parentID, const char *name,
				Boolean isHFSPlus, CatalogKey *key, UInt32 *textEncoding)
{
	OSErr err = 0;
	UInt32 nameLength;

	if ( name != NULL )
		nameLength = strlen(name);
	else
		nameLength = 0;

	if (nameLength > NAME_MAX)
		err = bdNamErr;	/* name is too long */

	if ( isHFSPlus ) {
		ByteCount unicodeBytes = 0;

		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 ( nameLength > 0 ) {
			err = ConvertUTF8ToUnicode(nameLength, name, sizeof(key->hfsPlus.nodeName.unicode),
								&unicodeBytes, key->hfsPlus.nodeName.unicode);
			key->hfsPlus.nodeName.length = unicodeBytes / sizeof(UniChar);
			key->hfsPlus.keyLength += unicodeBytes;
		}

		ExtractTextEncoding(key->hfsPlus.nodeName.length, key->hfsPlus.nodeName.unicode, textEncoding);
	}
	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 ( nameLength > 0 ) {
			err = ConvertUTF8ToMacRoman(nameLength, name, &key->hfs.nodeName[0]);
			key->hfs.keyLength += key->hfs.nodeName[0];		// add CName size to key length
		}
		*textEncoding = 0;
	}

	if (err) {
		if (err == kTECOutputBufferFullStatus)
			err = bdNamErr;	/* name is too long */
		else
			err = paramErr;	/* name has invalid characters */
	}

	return err;
}


/*
 * make a guess at the text encoding value that coresponds to the Unicode characters
 */
static void
ExtractTextEncoding(ItemCount length, ConstUniCharArrayPtr string, UInt32 * textEncoding)
{
	int i;
	UniChar ch;

	*textEncoding = 0;

	for (i = 0; i < length; ++i) {
		ch = string[i];
		/* CJK codepoints are 0x3000 thru 0x9FFF */
		if (ch >= 0x3000) {
			if (ch < 0xa000) {
				*textEncoding = kTextEncodingMacJapanese;
				break;
			}

			/* fullwidth character codepoints are 0xFF00 thru 0xFFEF */
			if (ch >= 0xff00 && ch <= 0xffef) {
				*textEncoding = kTextEncodingMacJapanese;
				break;	
			}
		}
	}
}


//�������������������������������������������������������������������������������
//	Routine:	FlushCatalog
//
// Function: 	Flushes the catalog for a specified volume.
//
//�������������������������������������������������������������������������������

OSErr
FlushCatalog(ExtendedVCB *volume)
{
	FCB *	fcb;
	OSErr	result;
	
	fcb = GetFileControlBlock(volume->catalogRefNum);
	result = BTFlushPath(fcb);

	if (result == noErr)
	{
		//--- check if catalog's fcb is dirty...
		
		if ( fcb->fcbFlags & fcbModifiedMask )
		{
			VCB_LOCK(volume);
			volume->vcbFlags |= 0xFF00;			// Mark the VCB dirty
			volume->vcbLsMod = GetTimeLocal(volume->vcbSigWord == kHFSSigWord);	// update last modified date
			VCB_UNLOCK(volume);

			result = FlushVolumeControlBlock(volume);
		}
	}
	
	return result;
}


//�������������������������������������������������������������������������������
//	Routine:	InvalidateCatalogNodeCache
//
//	Function: 	Invalidates the Catalog node cache if volume and folderID match
//
//�������������������������������������������������������������������������������
#if TARGET_OS_MAC
void
InvalidateCatalogNodeCache(ExtendedVCB *volume, HFSCatalogNodeID parentID)
{
	FSVarsRec	*fsVars;

	fsVars = (FSVarsRec*) LMGetFSMVars();
	
	if ( (fsVars->gCatalogFSSpec.parID == parentID) && (volume->vcbVRefNum == fsVars->gCatalogFSSpec.vRefNum) )
	{
		fsVars->gCatalogFSSpec.parID = 0;
		fsVars->gCatalogFSSpec.vRefNum = 0;
	}
}
#endif

//�������������������������������������������������������������������������������
//	Routine:	TrashCatalogNodeCache
//
//	Function: 	Invalidates the Catalog node cache.
//
//�������������������������������������������������������������������������������
#if TARGET_OS_MAC
void
TrashCatalogNodeCache( void )
{
	FSVarsRec	*fsVars	= (FSVarsRec*) LMGetFSMVars();
	
	fsVars->gCatalogFSSpec.parID	= 0;
	fsVars->gCatalogFSSpec.vRefNum	= 0;
}
#endif


//�������������������������������������������������������������������������������
//	Routine:	UpdateCatalogName
//
//	Function: 	Updates a CName.
//
//�������������������������������������������������������������������������������

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

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


//�������������������������������������������������������������������������������
//	Routine:	AdjustVolumeCounts
//
//	Function: 	Adjusts the folder and file counts in the VCB
//
//�������������������������������������������������������������������������������

void
AdjustVolumeCounts(ExtendedVCB *volume, SInt16 type, SInt16 delta)
{
	//�� also update extended VCB fields...

	VCB_LOCK(volume);

	if (type == kHFSFolderRecord || type == kHFSPlusFolderRecord)
		volume->vcbDirCnt += delta;			// adjust volume folder count, �� worry about overflow?
	else
		volume->vcbFilCnt += delta;			// adjust volume file count
	
	volume->vcbFlags |= 0xFF00;			// Mark the VCB dirty
	volume->vcbLsMod = GetTimeLocal(volume->vcbSigWord == kHFSSigWord);	// update last modified date

	VCB_UNLOCK(volume);
}


//�������������������������������������������������������������������������������

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

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

	VCB_LOCK(volume);

	if ( index < 32 )
		volume->encodingsBitmap.lo |= (1 << index);
	else
		volume->encodingsBitmap.hi |= (1 << (index - 32));

	VCB_UNLOCK(volume);
		
	// vcb should already be marked dirty
}


//�������������������������������������������������������������������������������

OSErr
UpdateFolderCount( ExtendedVCB *volume, HFSCatalogNodeID parentID, const CatalogName *name, SInt16 newType,
					UInt32 hint, SInt16 valenceDelta)
{
	CatalogKey			tempKey;	// 518 bytes
	CatalogRecord		tempData;	// 520 bytes
	UInt32				tempHint;
	HFSCatalogNodeID	folderID;
	UInt16				recordSize;
	OSErr				result;

#if 0
	result = SearchBTreeRecord(volume->catalogRefNum, parentKey, hint,
								&tempKey, &tempData, &recordSize, &tempHint);
	if (result)
		return (result == btNotFound ? cmNotFound : result);	
#else

	result = LocateCatalogNode(volume, parentID, name, hint, &tempKey, &tempData, &tempHint);
	ReturnIfError(result);
#endif

	if ( volume->vcbSigWord == kHFSPlusSigWord ) // HFS Plus
	{
		UInt32		timeStamp;
		
		if ( DEBUG_BUILD && tempData.recordType != kHFSPlusFolderRecord )
			DebugStr("\p UpdateFolder: found HFS folder on HFS+ volume!");

		timeStamp = GetTimeUTC();
		tempData.hfsPlusFolder.valence += valenceDelta;		// adjust valence
		tempData.hfsPlusFolder.contentModDate = timeStamp;	// set date/time last modified
		folderID = tempData.hfsPlusFolder.folderID;
		recordSize = sizeof(tempData.hfsPlusFolder);
	}
	else // classic HFS
	{
		if ( DEBUG_BUILD && tempData.recordType != kHFSFolderRecord )
			DebugStr("\p UpdateFolder: found HFS+ folder on HFS volume!");

		tempData.hfsFolder.valence += valenceDelta;				// adjust valence
		tempData.hfsFolder.modifyDate = GetTimeLocal(true);		// set date/time last modified
		folderID = tempData.hfsFolder.folderID;
		recordSize = sizeof(tempData.hfsFolder);
	}
	
	result = ReplaceBTreeRecord(volume->catalogRefNum, &tempKey, tempHint,
								&tempData, recordSize, &tempHint);
	ReturnIfError(result);

	if ( folderID == kHFSRootFolderID )
	{
		if (newType == kHFSFolderRecord || newType == kHFSPlusFolderRecord)
		{
			VCB_LOCK(volume);
			volume->vcbNmRtDirs += valenceDelta;	// adjust root folder count (undefined for HFS Plus)
			VCB_UNLOCK(volume);
		}
		else
		{
			VCB_LOCK(volume);
			volume->vcbNmFls += valenceDelta;		// adjust root file count (used by GetVolInfo)
			VCB_UNLOCK(volume);
		}
	}

	//XXX also update extended VCB fields...

	return result;
}


//�������������������������������������������������������������������������������

UInt16	
GetCatalogRecordSize(const CatalogRecord *dataRecord)
{
	switch (dataRecord->recordType)
	{
		case kHFSFileRecord:
			return sizeof(HFSCatalogFile);

		case kHFSFolderRecord:
			return sizeof(HFSCatalogFolder);

		case kHFSPlusFileRecord:
			return sizeof(HFSPlusCatalogFile);

		case kHFSPlusFolderRecord:
			return sizeof(HFSPlusCatalogFolder);
			
		case kHFSFolderThreadRecord:
		case kHFSFileThreadRecord:
			return sizeof(HFSCatalogThread);

		case kHFSPlusFolderThreadRecord:
		case kHFSPlusFileThreadRecord:
			return sizeof(HFSPlusCatalogThread);

		default:
			return 0;
	}
}


//�������������������������������������������������������������������������������

void
CopyCatalogNodeData(const ExtendedVCB *volume, const CatalogRecord *dataPtr, CatalogNodeData *nodeData)
{	
	switch (dataPtr->recordType)
	{
		case kHFSFolderRecord:
		{
			nodeData->nodeType			= kCatalogFolderNode;
			nodeData->nodeFlags			= dataPtr->hfsFolder.flags;
			nodeData->nodeID			= dataPtr->hfsFolder.folderID;
			nodeData->createDate		= dataPtr->hfsFolder.createDate;
			nodeData->contentModDate	= dataPtr->hfsFolder.modifyDate;
			nodeData->backupDate		= dataPtr->hfsFolder.backupDate;
			nodeData->valence			= dataPtr->hfsFolder.valence;

			BlockMoveData(&dataPtr->hfsFolder.userInfo, &nodeData->finderInfo, 32);		// copy 32 bytes of finder data	
			break;
		}

		case kHFSFileRecord:
		{
			UInt32	i;

			nodeData->nodeType			= kCatalogFileNode;
			nodeData->nodeFlags			= dataPtr->hfsFile.flags;
			nodeData->nodeID			= dataPtr->hfsFile.fileID;
			nodeData->createDate		= dataPtr->hfsFile.createDate;
			nodeData->contentModDate	= dataPtr->hfsFile.modifyDate;
			nodeData->backupDate		= dataPtr->hfsFile.backupDate;
			nodeData->valence			= 0;
	
			BlockMoveData(&dataPtr->hfsFile.userInfo, &nodeData->finderInfo, 16);		// copy finder data
			BlockMoveData(&dataPtr->hfsFile.finderInfo, &nodeData->extFinderInfo, 16);	// copy finder data

			nodeData->dataLogicalSize  = dataPtr->hfsFile.dataLogicalSize;
			nodeData->dataPhysicalSize = dataPtr->hfsFile.dataPhysicalSize;
			nodeData->rsrcLogicalSize  = dataPtr->hfsFile.rsrcLogicalSize;
			nodeData->rsrcPhysicalSize = dataPtr->hfsFile.rsrcPhysicalSize;

			for (i = 0; i < kHFSExtentDensity; ++i)
			{
				nodeData->dataExtents[i].startBlock = (UInt32) (dataPtr->hfsFile.dataExtents[i].startBlock);
				nodeData->dataExtents[i].blockCount = (UInt32) (dataPtr->hfsFile.dataExtents[i].blockCount);

				nodeData->rsrcExtents[i].startBlock = (UInt32) (dataPtr->hfsFile.rsrcExtents[i].startBlock);
				nodeData->rsrcExtents[i].blockCount = (UInt32) (dataPtr->hfsFile.rsrcExtents[i].blockCount);
			}
			break;
		}

		case kHFSPlusFolderRecord:
		{
			nodeData->nodeType			= kCatalogFolderNode;
			nodeData->nodeFlags			= dataPtr->hfsPlusFolder.flags;
			nodeData->nodeID			= dataPtr->hfsPlusFolder.folderID;
			nodeData->textEncoding		= dataPtr->hfsPlusFolder.textEncoding;
			nodeData->createDate		= UTCToLocal(dataPtr->hfsPlusFolder.createDate);
			nodeData->contentModDate	= UTCToLocal(dataPtr->hfsPlusFolder.contentModDate);
			nodeData->backupDate		= UTCToLocal(dataPtr->hfsPlusFolder.backupDate);
			if (dataPtr->hfsPlusFolder.valence > 0xffff)
				nodeData->valence = 0xffff;		// pass maximum 16-bit value
			else
				nodeData->valence = dataPtr->hfsPlusFolder.valence;

			BlockMoveData(&dataPtr->hfsPlusFolder.userInfo, &nodeData->finderInfo, 32);	// copy finder data
#if TARGET_OS_RHAPSODY
            nodeData->accessDate		= UTCToLocal(dataPtr->hfsPlusFolder.accessDate);
            nodeData->attributeModDate		= UTCToLocal(dataPtr->hfsPlusFolder.attributeModDate);
            nodeData->permissions		= dataPtr->hfsPlusFolder.permissions; 
#endif
			break;
		}

		case kHFSPlusFileRecord:
		{
			UInt32	largestFileSizeUnder2Gig;
			
			nodeData->nodeType			= kCatalogFileNode;
			nodeData->nodeFlags			= dataPtr->hfsPlusFile.flags;
			nodeData->nodeID			= dataPtr->hfsPlusFile.fileID;
			nodeData->textEncoding		= dataPtr->hfsPlusFile.textEncoding;
			nodeData->createDate		= UTCToLocal(dataPtr->hfsPlusFile.createDate);
			nodeData->contentModDate	= UTCToLocal(dataPtr->hfsPlusFile.contentModDate);
			nodeData->backupDate		= UTCToLocal(dataPtr->hfsPlusFile.backupDate);
			nodeData->valence			= 0;

			BlockMoveData(&dataPtr->hfsPlusFile.userInfo, &nodeData->finderInfo, 32);	// copy finder data
		
			//	2201501, Pin values to 2^31-1 (SInt32)
			largestFileSizeUnder2Gig	=  (0x7FFFFFFF / volume->blockSize) * volume->blockSize;
			
			if ( (dataPtr->hfsPlusFile.dataFork.logicalSize.hi == 0) && (dataPtr->hfsPlusFile.dataFork.logicalSize.lo <= largestFileSizeUnder2Gig) )
			{
				nodeData->dataLogicalSize	= dataPtr->hfsPlusFile.dataFork.logicalSize.lo;
				nodeData->dataPhysicalSize	= (dataPtr->hfsPlusFile.dataFork.totalBlocks * volume->blockSize);
			}
			else
			{
				nodeData->dataPhysicalSize	=  largestFileSizeUnder2Gig;
				nodeData->dataLogicalSize	=  nodeData->dataPhysicalSize;		// signal to Open that this file is too big
				nodeData->valence			|= kLargeDataForkMask;				// overload the valence for files over 2Gig
			}

			if ( (dataPtr->hfsPlusFile.resourceFork.logicalSize.hi == 0) && (dataPtr->hfsPlusFile.resourceFork.logicalSize.lo <= largestFileSizeUnder2Gig) )
			{
				nodeData->rsrcLogicalSize	= dataPtr->hfsPlusFile.resourceFork.logicalSize.lo;
				nodeData->rsrcPhysicalSize	= (SInt32) ((SInt32)dataPtr->hfsPlusFile.resourceFork.totalBlocks * (SInt32)volume->blockSize);
			}
			else
			{
				nodeData->rsrcPhysicalSize	=  largestFileSizeUnder2Gig;
				nodeData->rsrcLogicalSize	=  nodeData->rsrcPhysicalSize;		// signal to Open that this file is too big
				nodeData->valence			|= kLargeRsrcForkMask;				// overload the valence for files over 2Gig
			}

			// copy data and rsrc extents
			BlockMoveData(&dataPtr->hfsPlusFile.dataFork.extents, &nodeData->dataExtents, sizeof(HFSPlusExtentRecord));
			BlockMoveData(&dataPtr->hfsPlusFile.resourceFork.extents, &nodeData->rsrcExtents, sizeof(HFSPlusExtentRecord));
#if TARGET_OS_RHAPSODY
            nodeData->accessDate		= UTCToLocal(dataPtr->hfsPlusFile.accessDate);
            nodeData->attributeModDate		= UTCToLocal(dataPtr->hfsPlusFile.attributeModDate);
            nodeData->permissions		= dataPtr->hfsPlusFile.permissions;
#endif
			break;
		}
		
		default:
			nodeData->nodeType = '????';	// must have hit a thread record
	}
}


//�������������������������������������������������������������������������������
#if 0
OSErr
PrepareInputName(ConstStr31Param name, Boolean unicode, TextEncoding encoding, CatalogName *catalogName)
{
	OSErr	result = noErr;
	
	if (name == NULL)
	{
		catalogName->ustr.length = 0;	// set length byte (works for both unicode and pascal)
	}
	else
	{
		Size length = name[0];
	
		if (length > CMMaxCName)
			length = CMMaxCName;	// truncate to max
			
		if ( length == 0 )
		{
			catalogName->ustr.length = 0;
		}
		else if (unicode)
		{
			Str31		truncatedName;
			StringPtr	pName;
	
			if (name[0] <= CMMaxCName)	//�� should be CMMaxCName
			{
				pName = (StringPtr)name;
			}
			else
			{
				BlockMoveData(&name[1], &truncatedName[1], CMMaxCName);
				truncatedName[0] = CMMaxCName;
				pName = truncatedName;
			}
	
			//���need to pass pascal string length (could be clipped above)
			result = ConvertHFSNameToUnicode (	pName, encoding, &catalogName->ustr );
		}
		else
		{
			BlockMoveData(&name[1], &catalogName->pstr[1], length);
			catalogName->pstr[0] = length;					// set length byte (might be smaller than name[0]
		}
	}
	
	return result;
}
#endif

//_______________________________________________________________________

#if 0
void
ConvertInputNameToUnicode(ConstStr31Param name, TextEncoding encodingHint, TextEncoding *actualEncoding, CatalogName *catalogName)
{
	Size		length;
	OSErr		result;
	
	catalogName->ustr.length = 0;	// setup defaults...
	*actualEncoding = encodingHint;

	if (name == NULL)
		return;		// catalogName is empty string

	length = name[0];

	if (length == 0 || length > CMMaxCName)
		return;		// catalogName is empty string

	result = ConvertHFSNameToUnicode ( name, encodingHint, &catalogName->ustr );
	
	if (result != noErr)
	{
		// The converter didn't like our string, try again with roman encoding
		// This takes care of errors like:	kTextMalformedInputErr
		//									kTECPartialCharErr
		//									kTECUnmappableElementErr
		//									kTECIncompleteElementErr
		//
		if ( encodingHint != kTextEncodingMacRoman )
		{
			*actualEncoding = kTextEncodingMacRoman;
			result = ConvertHFSNameToUnicode ( name, kTextEncodingMacRoman, &catalogName->ustr );
			
		}

		if (DEBUG_BUILD && result != noErr)
			DebugStr("\p ConvertHFSNameToUnicode could not convert string!");
	}
}
#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 )
		BlockMoveData(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];
}