Source to bsd/hfs/hfscommon/Misc/Attributes.c


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

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @[email protected]
 * 
 * "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."
 * 
 * @[email protected]
 */

/*
	File:		Attributes.c

	Contains:	Extended Attributes Manager
				Routines for managing attributes (additional catalog-like data) associated
				with files or folders on HFS Plus volumes.

	Version:	HFS Plus 1.0

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

	File Ownership:

		DRI:				Mark Day

		Other Contact:		Greg Parks

		Technology:			HFS Plus

	Writers:

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

	Change History (most recent first):
	  <Rhap>	 4/17/98	djb		Add VCB locking.
	  <Rhap>	 3/31/98	djb		Sync up with final HFSVolumes.h header file.

	  <CS13>	 11/7/97	msd		Change calls to the wrapper routine CompareUnicodeNames() to use
									the underlying routine FastUnicodeCompare() instead.
	  <CS12>	10/17/97	msd		Remove DebugStrs.
	  <CS11>	10/13/97	DSH		Added InitBTreeHeader() fileSize parameter.
	  <CS10>	  9/4/97	msd		Remove unneeded/unused routines.
	   <CS9>	 8/22/97	djb		Add readFromDisk flag to GetCacheBlock.
	   <CS8>	 7/25/97	DSH		Pass kInvalidMRUCacheKey to BTSearchRecord as the heuristicHint.
	   <CS7>	 7/24/97	djb		SetEndOfForkProc takes a refNum instead of an FCB.
	   <CS6>	 7/18/97	msd		Include LowMemPriv.h.
	   <CS5>	 7/16/97	DSH		FilesInternal.i renamed FileMgrInternal.i to avoid name
									collision
	   <CS4>	  7/8/97	DSH		Loading PrecompiledHeaders from define passed in on C line
	   <CS3>	  6/2/97	DSH		On creation make sure we call through the SetEndOfFileProc so
									the AlternateVolumeHeader is updated.
	   <CS2>	 5/28/97	msd		Change keys to {cnid, flags, creator, selector}. Prototype an
									SPI for testing.
	   <CS1>	 4/24/97	djb		first checked in
	  <HFS5>	 4/10/97	msd		In PropertyDeleteObject, use the global buffer instead of an
									automatic variable.
	  <HFS4>	  4/8/97	msd		Add AttributesOpenVolume to allocate appropriate buffer space.
	  <HFS3>	  4/7/97	msd		Start adding real code. Removed PropertyOpenVolume; that code
									now resides in Volumes.c.
	  <HFS2>	 3/17/97	DSH		SC needs parameters to the stub functions.
	  <HFS1>	  3/5/97	msd		first checked in
*/

/*
Public routines:
	AttributesCloseVolume
				Clean up and finalize for a given volume.  Close a property database
				file if needed.  In general, clean up after PropertyOpenVolume.
				This routine should be called before closing the Catalog and Extents
				files on the volume.
*/

#if ( PRAGMA_LOAD_SUPPORTED )
        #pragma	load	PrecompiledHeaders
#else
	#if TARGET_OS_MAC
		#include <Types.h>
		#include <LowMemPriv.h>
	#else
		#include "../headers/system/MacOSStubs.h"
	#endif 	/* TARGET_OS_MAC */
#endif	/* PRAGMA_LOAD_SUPPORTED */

#include "../headers/HFSVolumes.h"
#include "../headers/FileMgrInternal.h"
#include "../headers/BTreesInternal.h"
#include "../headers/HFSBtreesPriv.h"
#include "../headers/system/HFSUnicodeWrappers.h"

#if HFSInstrumentation
        #include "Instrumentation.h"
#endif

#define min(x,y)	((x < y) ? x : y)

//
//	Values for flags in AttributesKey.flags
//
enum {
	kAttributeNodeSize		= 4096			// Size of our Btree node
};


#if TARGET_OS_MAC
OSErr AttributesCloseVolume(ExtendedVCB *		vcb)
{
	OSErr	err;
	
	err = noErr;
	
	if (vcb->vcbSigWord == kHFSPlusSigWord && vcb->attributesRefNum != 0) {
		err = CloseFile( vcb, vcb->attributesRefNum, LMGetFCBSPtr() );
		vcb->attributesRefNum = 0;
	}
	
	return err;
}
#endif	/* TARGET_OS_MAC */

SInt32 CompareAttributeKeys( const void *inSearchKey, const void *inTrialKey )
{
	const AttributeKey *searchKey	= inSearchKey;
	const AttributeKey *trialKey	= inTrialKey;
	SInt32				temp;
		
	//
	//	First, compare the CNID's
	//
	if (searchKey->cnid != trialKey->cnid) {
		return searchKey->cnid < trialKey->cnid ? -1 : 1;
	}

	//
	//	CNID's are equal; compare names
	//
	if (searchKey->attributeName.length == 0 || trialKey->attributeName.length == 0)
		return searchKey->attributeName.length < trialKey->attributeName.length ? -1 : 1;
	temp = FastUnicodeCompare(&searchKey->attributeName.unicode[0], searchKey->attributeName.length,
							  &trialKey->attributeName.unicode[0], trialKey->attributeName.length);
	if (temp != 0)
		return temp;

	//
	//	Names are equal; compare startBlock
	//
	if (searchKey->startBlock == trialKey->startBlock)
		return 0;
	else
		return searchKey->startBlock < trialKey->startBlock ? -1 : 1;
}


#if TARGET_OS_MAC
static OSErr CreateAndOpenBtree(ExtendedVCB *vcb)
{
	OSStatus		err;
	UInt16			refnum;				//	attribute file's refnum
	Ptr				fcbHeader;			//	points to start of FCBs (unused)
	FCB				*fcb;				//	attribute file's FCB
	ExtendedFCB		*extendedFCB;		//	attribute file's extended FCB
	UInt32			clumpSize;
	UInt32			mapNodes;
	LogicalAddress	header;
	Boolean			readFromDisk;
	
	clumpSize = 32 * vcb->blockSize;			// Need a better value.

    //	Find a free FCB for the attributes file
#if TARGET_OS_MAC
	err = FindFileControlBlock(&refnum, &fcbHeader);
#else	/* TARGET_OS_MAC */
    err = GetNewFCB(vcb, &refnum );
#endif	/* TARGET_OS_MAC */

	ReturnIfError(err);
//	fcb = GetFileControlBlock(refnum);
	fcb = SetupFCB(vcb, refnum, kHFSAttributesFileID, clumpSize);

	//	Mark file as empty.
	fcb->fcbEOF = 0;
	fcb->fcbPLen = 0;
	vcb->attributesRefNum = refnum;
	ClearMemory( (Ptr)fcb->fcbExtRec, sizeof(HFSExtentRecord) );
/* XXX PPD: Is there any reason this isn't the same for EVERY platform? */
#if TARGET_OS_MAC
	extendedFCB = ParallelFCBFromRefnum( refnum );
#else
	extendedFCB = GetParallelFCB( refnum );
#endif
	ClearMemory( (Ptr)extendedFCB->extents, sizeof(HFSPlusExtentRecord) );

	//	Allocate some space to the file
	err = SetEndOfForkProc(refnum, clumpSize, clumpSize);
	if (err) {
		ClearMemory(fcb, sizeof(FCB));
		goto ErrorExit;
	}
	fcb->fcbEOF = fcb->fcbPLen;		// grow the file to include the allocated space
	
	//	Initialize the b-tree.  Write out the header.
	err = GetCacheBlock(refnum, 0, kAttributeNodeSize, gbDefault, &header, &readFromDisk);
	ClearMemory(header, kAttributeNodeSize);
	InitBTreeHeader(fcb->fcbEOF, clumpSize, kAttributeNodeSize, 0, kAttributeKeyMaximumLength, kBTBigKeysMask, &mapNodes, header);
	err = ReleaseCacheBlock(header, rbWriteMask);
	
	//	Finally, prepare for using the B-tree		
	err = OpenBTree( refnum, (KeyCompareProcPtr) CompareAttributeKeys );
	if (err) {
		ClearMemory(fcb, sizeof(FCB));
		goto ErrorExit;
	}
	
	// Should we update the volume header and alternate volume header?

ErrorExit:	
	return err;
}
#endif	/* #if TARGET_OS_MAC */

#if TARGET_OS_MAC
static OSErr PreflightAttributeCall(AttributeParam *pb, FindFileNameGlueRec *fileInfo, Boolean needsWrite)
{
	OSErr				err;
	ExtendedVCB			*vcb;

	//
	//	See if the file exists.  Get its FileID.
	//
	err = FindFileName((ParamBlockRec *) pb, fileInfo);
	ReturnIfError( err );
	
	//
	//	Make sure the volume is on-line and writable.
	//
	vcb = fileInfo->vcb;
	
	if (needsWrite) {
		err = VolumeWritable( vcb );
		ReturnIfError( err );
	}
	
	err = CheckVolumeOffLine( vcb );
	ReturnIfError( err );
	
	//
	//	And make sure it is HFS Plus.
	//
	if (vcb->vcbSigWord != kHFSPlusSigWord)
		return wrgVolTypErr;
	
	//
	//	Make sure there is an attribute file.  Create and open it if needed.
	//
	if (vcb->attributesRefNum == 0) {
		
		if (needsWrite == false)		//	If we only wanted to read,
			return notBTree;			//	tell caller there is no b-tree
		//
		//	Create and open b-tree
		//
		err = CreateAndOpenBtree(vcb);
	}
	
	return err;
}
#endif	/* TARGET_OS_MAC */


#if TARGET_OS_MAC
OSErr CreateAttribute(AttributeParam *pb)
{
	OSErr				err;
	ExtendedVCB			*vcb;
	UInt32				length;
	UInt32				numberOfExtents;
	UInt32				i,extentsThisRecord;
	UInt32				blocksAllocated = 0;
	UInt32				hint;
	AttributeKey		key;
	FindFileNameGlueRec	fileInfo;

	err = PreflightAttributeCall(pb, &fileInfo, true);
	ReturnIfError( err );
	
	vcb = fileInfo.vcb;
	
	//
	//	Set up the first key
	//
	length = sizeof(UniChar) * (pb->ioAttributeName->length+1);		// length in bytes of Unicode name

	key.keyLength	= length + offsetof(AttributeKey, attributeName);
	key.pad			= 0;
	key.cnid		= fileInfo.data->nodeID;
	key.startBlock	= 0;
	BlockMoveData(pb->ioAttributeName, &key.attributeName, length);	// copy attributeName
	
	//
	//	Determine the first record type based on the number of extents
	//
	numberOfExtents = pb->filler1;
	if (numberOfExtents == 0) {
		AttributeInlineData		record;
		
		record.recordType = kAttributeInlineData;
		record.logicalSize = 0;
		length = offsetof(AttributeInlineData, userData);
		err = InsertBTreeRecord(vcb->attributesRefNum, &key, &record, length, &hint);
	}
	else {
		{
			AttributeForkData		record;
			
			//	Set up first record, with ForkData
			ClearMemory(&record, sizeof(record));
			record.recordType				= kAttributeForkData;
			record.theFork.logicalSize.lo	= numberOfExtents * vcb->blockSize;		// each extent is one block
			record.theFork.logicalSize.hi	= 0;
			record.theFork.clumpSize		= vcb->attributesClumpSize;
			record.theFork.totalBlocks		= numberOfExtents;						// one block per extent
			extentsThisRecord = min(numberOfExtents, kHFSPlusExtentDensity);
			for (i=0; i<extentsThisRecord; i++) {
				err = BlockAllocateAny(vcb, 0, vcb->totalBlocks, 1, &record.theFork.extents[i].startBlock,
																	&record.theFork.extents[i].blockCount);
				ReturnIfError(err);
				++blocksAllocated;
			}
			err = InsertBTreeRecord(vcb->attributesRefNum, &key, &record, sizeof(record), &hint);
			ReturnIfError(err);
		}
		
		{
			AttributeExtents		record;
			
			//	Handle overflow extent records
			ClearMemory(&record, sizeof(record));
			numberOfExtents -= extentsThisRecord;
			record.recordType	= kAttributeExtents;
			while (numberOfExtents) {
				extentsThisRecord = min(numberOfExtents, kHFSPlusExtentDensity);
				key.startBlock += kHFSPlusExtentDensity;
				for (i=0; i<extentsThisRecord; i++) {
					err = BlockAllocateAny(vcb, 0, vcb->totalBlocks, 1, &record.extents[i].startBlock,
																		&record.extents[i].blockCount);
					ReturnIfError(err);
					++blocksAllocated;
				}
				for (; i<kHFSPlusExtentDensity; i++) {
					record.extents[i].startBlock = 0;
					record.extents[i].blockCount = 0;
				}
				err = InsertBTreeRecord(vcb->attributesRefNum, &key, &record, sizeof(record), &hint);
				ReturnIfError(err);
				numberOfExtents -= extentsThisRecord;
			}
		}
	}
	
	if (blocksAllocated) {
		VCB_LOCK(vcb);
		vcb->freeBlocks -= blocksAllocated;
		VCB_UNLOCK(vcb);
		UpdateVCBFreeBlks( vcb );
		MarkVCBDirty(vcb);
	}
	
	if (err == fsBTDuplicateRecordErr)
		err = btDupRecErr;
			
	return err;
}
#endif	/* TARGET_OS_MAC */