Source to bsd/hfs/hfscommon/BTree/BTreeScanner.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:		BTreeScanner.c

	Contains:	Routines for quick reading of B-Tree records (not in sorted order).
	
				Using a caller-supplied buffer, these routines read in a buffer full
				of B-tree nodes.  Pointers to the leaf records' key and data are
				returned one at a time.  The buffer is refilled as needed.  It is the
				caller's responsibility to flush any changes to the B-tree before
				starting a scan.
				
				These routines were designed for use by CatSearch and MountCheck.
				
				NOTE: Records will not be returned in sorted order.  Within a given node,
				they will be in order, but the nodes may not be read in sorted order.

	Version:	HFS Plus 1.0

	Written by:	Mark Day

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

	File Ownership:

		DRI:				Mark Day

		Other Contact:		Don Brady

		Technology:			File Systems

	Writers:

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

	Change History (most recent first):

	   <CS3>	  8/1/97	msd		Don't prefill node buffer in BTScanInitialize (fixes MountCheck
									error with empty extents B-Tree). Remove extraneous enum for
									timeOutErr.
	   <CS2>	 7/30/97	DSH		Casting for SC, needed for DFA compiles.
	   <CS1>	 7/28/97	msd		first checked in
	   <0>	 7/24/97	msd		Begin implementation.

*/

#include "../headers/FileMgrInternal.h"
#include "../headers/HFSBtreesPriv.h"
#include "../headers/BTreesPrivate.h"
#include "../headers/BTreeScanner.h"


//======================================================================
//	Exported Routines:
//
//	BTScanInitialize	- Set up to start or resume a scan
//	BTScanTerminate		- Obtain state to later resume a scan
//	BTScanNextRecord	- Get the next record
//	BTScanCacheNode		- Make sure the current node is in the cache
//
//	Internal Routines:
//
//	FindNextLeafNode	- Point to the next leaf node after the current
//						  node.  More nodes will be read if needed (and
//						  allowed by the caller).
//	ReadMultipleNodes	- Read one or more nodes into a buffer.
//======================================================================




OSErr FindNextLeafNode(BTScanState *scanState, Boolean avoidIO);
OSErr ReadMultipleNodes(BTScanState *scanState);




//_________________________________________________________________________________
//
//	Routine:	BTScanInitialize
//
//	Purpose:	Prepare to start a new BTree scan, or resume a previous one.
//
//	Inputs:
//		btreeFile		The B-Tree's file control block
//		startingNode	Initial node number
//		startingRecord	Initial record number within node
//		recordsFound	Number of valid records found so far
//		buffer			Start of buffer used to hold B-Tree data
//		bufferSize		Size (in bytes) of buffer
//
//	Outputs:
//		scanState		Scanner's current state; pass to other scanner calls
//
//	Notes:
//		To begin a new scan and see all records in the B-Tree, pass zeroes for
//		startingNode, startingRecord, and recordsFound.
//
//		To resume a scan from the point of a previous BTScanTerminate, use the
//		values returned by BTScanTerminate as input for startingNode, startingRecord,
//		and recordsFound.
//
//		When resuming a scan, the caller should check the B-tree's write count.  If
//		it is different from the write count when the scan was terminated, then the
//		tree may have changed and the current state may be incorrect.  In particular,
//		you may see some records more than once, or never see some records.  Also,
//		the scanner may not be able to detect when all leaf records have been seen,
//		and will have to scan through many empty nodes.
//
//		���Perhaps the write count should be managed by BTScanInitialize and
//		�� BTScanTerminate?  This would avoid the caller having to peek at
//		�� internal B-Tree structures.
//_________________________________________________________________________________

OSErr BTScanInitialize(
	const FCB *		btreeFile,
	UInt32			startingNode,
	UInt32			startingRecord,
	UInt32			recordsFound,
	void *			buffer,
	UInt32			bufferSize,
	BTScanState	*	scanState)
{
	BTreeControlBlock	*btcb;
	
	//
	//	Make sure this is a valid B-Tree file
	//
	btcb = (BTreeControlBlock *) btreeFile->fcbBTCBPtr;
	if (btcb == NULL)
		return fsBTInvalidFileErr;
	
	//
	//	Make sure buffer size is big enough, and a multiple of the
	//	B-Tree node size
	//
	if (buffer == NULL || bufferSize < btcb->nodeSize)
		return paramErr;
	bufferSize = (bufferSize / btcb->nodeSize) * btcb->nodeSize;

	//
	//	Set up the scanner's state
	//
	scanState->buffer				= buffer;
	scanState->bufferSize			= bufferSize;
	scanState->btcb					= btcb;
	scanState->nodeNum				= startingNode;
	scanState->recordNum			= startingRecord;
	scanState->currentNode			= buffer;
	scanState->nodesLeftInBuffer	= 0;		// no nodes currently in buffer
	scanState->recordsFound			= recordsFound;
	scanState->lastCachedNode		= 0;		// no nodes have been cached

	
	return noErr;
}



//_________________________________________________________________________________
//
//	Routine:	BTScanTerminate
//
//	Purpose:	Return state information about a scan so that it can be resumed
//				later via BTScanInitialize.
//
//	Inputs:
//		scanState		Scanner's current state
//
//	Outputs:
//		nextNode		Node number to resume a scan (pass to BTScanInitialize)
//		nextRecord		Record number to resume a scan (pass to BTScanInitialize)
//		recordsFound	Valid records seen so far (pass to BTScanInitialize)
//_________________________________________________________________________________

OSErr BTScanTerminate(
	const BTScanState *	scanState,
	UInt32 *			startingNode,
	UInt32 *			startingRecord,
	UInt32 *			recordsFound)
{
	*startingNode	= scanState->nodeNum;
	*startingRecord	= scanState->recordNum;
	*recordsFound	= scanState->recordsFound;
	
	return noErr;
}



//_________________________________________________________________________________
//
//	Routine:	BTScanNextRecord
//
//	Purpose:	Return the next leaf record in a scan.
//
//	Inputs:
//		scanState		Scanner's current state
//		avoidIO			If true, don't do any I/O to refill the buffer
//
//	Outputs:
//		key				Key of found record (points into buffer)
//		data			Data of found record (points into buffer)
//		dataSize		Size of data in found record
//
//	Result:
//		noErr			Found a valid record
//		btNotFound		No more records
//		???				Needed to do I/O to get next node, but avoidIO set
//
//	Notes:
//		This routine returns pointers to the found record's key and data.  It
//		does not copy the key or data to a caller-supplied buffer (like
//		GetBTreeRecord would).  The caller must not modify the key or data.
//_________________________________________________________________________________

OSErr BTScanNextRecord(
	BTScanState *	scanState,
	Boolean			avoidIO,
	void * *		key,
	void * *		data,
	UInt32 *		dataSize)
{
	OSStatus	err;
	UInt16		dataSizeShort;
	
	err = noErr;

	//
	//	If this is the first call, there won't be any nodes in the buffer, so go
	//	find the first first leaf node (if any).
	//	
	if (scanState->nodesLeftInBuffer == 0)
		err = FindNextLeafNode(scanState, avoidIO);

	while (err == noErr) {
		//	See if we have a record in the current node
		err = GetRecordByIndex(scanState->btcb, scanState->currentNode, scanState->recordNum,
								(KeyPtr *) key, (UInt8 **) data, &dataSizeShort);
		if (err == noErr) {
			++scanState->recordsFound;
			++scanState->recordNum;
			*dataSize = dataSizeShort;
			return noErr;
		}
		
		//	We're done with the current node.  See if we've returned all the records
		if (scanState->recordsFound >= scanState->btcb->leafRecords)
			return btNotFound;

		//	Move to the first record of the next leaf node
		scanState->recordNum = 0;
		err = FindNextLeafNode(scanState, avoidIO);
	}

	//
	//	If we got an EOF error from FindNextLeafNode, then there are no more leaf
	//	records to be found.
	//
	if (err == eofErr)
		err = btNotFound;
	
	return err;
}



//_________________________________________________________________________________
//
//	Routine:	BTScanCacheNode
//
//	Purpose:	Make sure that the node just returned is in the cache.
//
//	Inputs:
//		scanState		Scanner's current state
//_________________________________________________________________________________

OSErr BTScanCacheNode(BTScanState *scanState)
{
	OSStatus	err;
	NodeRec		nodeRec;
	
	err = noErr;
	
	if (scanState->lastCachedNode != scanState->nodeNum) {
		err = GetNode(scanState->btcb, scanState->nodeNum, &nodeRec);
		if (err == noErr)
			err = ReleaseNode(scanState->btcb, &nodeRec);
		scanState->lastCachedNode = scanState->nodeNum;
	}
	
	return err;
}



//_________________________________________________________________________________
//
//	Routine:	FindNextLeafNode
//
//	Purpose:	Point to the next leaf node in the buffer.  Read more nodes
//				into the buffer if needed (and allowed).
//
//	Inputs:
//		scanState		Scanner's current state
//		avoidIO			If true, don't do any I/O to refill the buffer
//
//	Result:
//		noErr			Found a valid record
//		eofErr			No more nodes in file
//		???				Needed to do I/O to get next node, but avoidIO set
//_________________________________________________________________________________

OSErr FindNextLeafNode(BTScanState *scanState, Boolean avoidIO)
{
	OSErr	err;
	
	err = noErr;		// Assume everything will be OK
	
	while (1) {
		if (scanState->nodesLeftInBuffer == 0) {
			//	Time to read some more nodes into the buffer
			if (avoidIO) {
				return fsBTTimeOutErr;
			}
			else {
				//	read some more nodes into buffer
				scanState->currentNode = scanState->buffer;
				err = ReadMultipleNodes(scanState);
				if (err != noErr) break;
			}
		}
		else {
			//	Adjust the node counters and point to the next node in the buffer
			++scanState->nodeNum;
			--scanState->nodesLeftInBuffer;
			(UInt8 *) scanState->currentNode += scanState->btcb->nodeSize;
			
			//	If we've looked at all nodes in the tree, then we're done
			if (scanState->nodeNum > scanState->btcb->totalNodes)
				return eofErr;
		}
		
		//�� CheckNode returns an error on empty nodes (all zeros)
//		err = CheckNode(scanState->btcb, scanState->currentNode);
//		if (err != noErr) break;
		
		if (scanState->currentNode->type == kLeafNode)
			break;
	}
	
	return err;
}



//_________________________________________________________________________________
//
//	Routine:	ReadMultipleNodes
//
//	Purpose:	Read one or more nodes into the buffer.
//
//	Inputs:
//		scanState		Scanner's current state
//
//	Result:
//		noErr			One or nodes were read
//		eofErr			No nodes left in file, none in buffer
//_________________________________________________________________________________

OSErr ReadMultipleNodes(BTScanState *scanState)
{
#if TARGET_OS_MAC
	OSErr			err = noErr;
	UInt32			nodeSize;			// size of one B-Tree node
	UInt32			currentPosition;	// current offset in bytes from start of B-Tree file
	UInt32			bytesToRead;		// number of bytes left to read into buffer
	ExtendedVCB		*vcb;				// volume that B-Tree resides on
	UInt32			actualBytes;		// bytes actually read by CacheReadInPlace
	HIOParam		iopb;				// used by CacheReadInPlace

	nodeSize = scanState->btcb->nodeSize;
	vcb = GetFileControlBlock(scanState->btcb->fileRefNum)->fcbVPtr;
	
	iopb.ioRefNum 	= scanState->btcb->fileRefNum;
	iopb.ioPermssn	= fsRdPerm;
	iopb.ioBuffer 	= scanState->buffer;
	iopb.ioPosMode	= fsAtMark | (noCacheMask << 8);
	iopb.ioActCount	= 0;
	
	currentPosition	= scanState->nodeNum * nodeSize;
	bytesToRead		= scanState->bufferSize;
	
	while (bytesToRead > 0) {
		err = CacheReadInPlace(vcb, &iopb, currentPosition, bytesToRead, &actualBytes);
		
		if (err != noErr || actualBytes == 0)
			break;
		
		bytesToRead		-= actualBytes;
		iopb.ioActCount	+= actualBytes;
		currentPosition	+= actualBytes;
	}
	
	scanState->nodesLeftInBuffer = iopb.ioActCount / nodeSize;
	
	if (err == fxRangeErr && iopb.ioActCount == 0)
		err = eofErr;
	else
		err = noErr;
		
	if (scanState->nodesLeftInBuffer == 0 && err == noErr)
		err = eofErr;
	
	return err;
#else
#pragma unused(scanState)
    return noErr;
#endif
}