|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights ! 7: * Reserved. This file contains Original Code and/or Modifications of ! 8: * Original Code as defined in and that are subject to the Apple Public ! 9: * Source License Version 1.1 (the "License"). You may not use this file ! 10: * except in compliance with the License. Please obtain a copy of the ! 11: * License at http://www.apple.com/publicsource and read it before using ! 12: * this file. ! 13: * ! 14: * The Original Code and all software distributed under the License are ! 15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 19: * License for the specific language governing rights and limitations ! 20: * under the License. ! 21: * ! 22: * @APPLE_LICENSE_HEADER_END@ ! 23: */ ! 24: ! 25: /* ! 26: File: BTreeScanner.c ! 27: ! 28: Contains: Routines for quick reading of B-Tree records (not in sorted order). ! 29: ! 30: Using a caller-supplied buffer, these routines read in a buffer full ! 31: of B-tree nodes. Pointers to the leaf records' key and data are ! 32: returned one at a time. The buffer is refilled as needed. It is the ! 33: caller's responsibility to flush any changes to the B-tree before ! 34: starting a scan. ! 35: ! 36: These routines were designed for use by CatSearch and MountCheck. ! 37: ! 38: NOTE: Records will not be returned in sorted order. Within a given node, ! 39: they will be in order, but the nodes may not be read in sorted order. ! 40: ! 41: Version: HFS Plus 1.0 ! 42: ! 43: Written by: Mark Day ! 44: ! 45: Copyright: � 1997 by Apple Computer, Inc., all rights reserved. ! 46: ! 47: File Ownership: ! 48: ! 49: DRI: Mark Day ! 50: ! 51: Other Contact: Don Brady ! 52: ! 53: Technology: File Systems ! 54: ! 55: Writers: ! 56: ! 57: (msd) Mark Day ! 58: (DSH) Deric Horn ! 59: (djb) Don Brady ! 60: ! 61: Change History (most recent first): ! 62: ! 63: <CS3> 8/1/97 msd Don't prefill node buffer in BTScanInitialize (fixes MountCheck ! 64: error with empty extents B-Tree). Remove extraneous enum for ! 65: timeOutErr. ! 66: <CS2> 7/30/97 DSH Casting for SC, needed for DFA compiles. ! 67: <CS1> 7/28/97 msd first checked in ! 68: <0> 7/24/97 msd Begin implementation. ! 69: ! 70: */ ! 71: ! 72: #include "../headers/FileMgrInternal.h" ! 73: #include "../headers/HFSBtreesPriv.h" ! 74: #include "../headers/BTreesPrivate.h" ! 75: #include "../headers/BTreeScanner.h" ! 76: ! 77: ! 78: //====================================================================== ! 79: // Exported Routines: ! 80: // ! 81: // BTScanInitialize - Set up to start or resume a scan ! 82: // BTScanTerminate - Obtain state to later resume a scan ! 83: // BTScanNextRecord - Get the next record ! 84: // BTScanCacheNode - Make sure the current node is in the cache ! 85: // ! 86: // Internal Routines: ! 87: // ! 88: // FindNextLeafNode - Point to the next leaf node after the current ! 89: // node. More nodes will be read if needed (and ! 90: // allowed by the caller). ! 91: // ReadMultipleNodes - Read one or more nodes into a buffer. ! 92: //====================================================================== ! 93: ! 94: ! 95: ! 96: ! 97: OSErr FindNextLeafNode(BTScanState *scanState, Boolean avoidIO); ! 98: OSErr ReadMultipleNodes(BTScanState *scanState); ! 99: ! 100: ! 101: ! 102: ! 103: //_________________________________________________________________________________ ! 104: // ! 105: // Routine: BTScanInitialize ! 106: // ! 107: // Purpose: Prepare to start a new BTree scan, or resume a previous one. ! 108: // ! 109: // Inputs: ! 110: // btreeFile The B-Tree's file control block ! 111: // startingNode Initial node number ! 112: // startingRecord Initial record number within node ! 113: // recordsFound Number of valid records found so far ! 114: // buffer Start of buffer used to hold B-Tree data ! 115: // bufferSize Size (in bytes) of buffer ! 116: // ! 117: // Outputs: ! 118: // scanState Scanner's current state; pass to other scanner calls ! 119: // ! 120: // Notes: ! 121: // To begin a new scan and see all records in the B-Tree, pass zeroes for ! 122: // startingNode, startingRecord, and recordsFound. ! 123: // ! 124: // To resume a scan from the point of a previous BTScanTerminate, use the ! 125: // values returned by BTScanTerminate as input for startingNode, startingRecord, ! 126: // and recordsFound. ! 127: // ! 128: // When resuming a scan, the caller should check the B-tree's write count. If ! 129: // it is different from the write count when the scan was terminated, then the ! 130: // tree may have changed and the current state may be incorrect. In particular, ! 131: // you may see some records more than once, or never see some records. Also, ! 132: // the scanner may not be able to detect when all leaf records have been seen, ! 133: // and will have to scan through many empty nodes. ! 134: // ! 135: // ���Perhaps the write count should be managed by BTScanInitialize and ! 136: // �� BTScanTerminate? This would avoid the caller having to peek at ! 137: // �� internal B-Tree structures. ! 138: //_________________________________________________________________________________ ! 139: ! 140: OSErr BTScanInitialize( ! 141: const FCB * btreeFile, ! 142: UInt32 startingNode, ! 143: UInt32 startingRecord, ! 144: UInt32 recordsFound, ! 145: void * buffer, ! 146: UInt32 bufferSize, ! 147: BTScanState * scanState) ! 148: { ! 149: BTreeControlBlock *btcb; ! 150: ! 151: // ! 152: // Make sure this is a valid B-Tree file ! 153: // ! 154: btcb = (BTreeControlBlock *) btreeFile->fcbBTCBPtr; ! 155: if (btcb == NULL) ! 156: return fsBTInvalidFileErr; ! 157: ! 158: // ! 159: // Make sure buffer size is big enough, and a multiple of the ! 160: // B-Tree node size ! 161: // ! 162: if (buffer == NULL || bufferSize < btcb->nodeSize) ! 163: return paramErr; ! 164: bufferSize = (bufferSize / btcb->nodeSize) * btcb->nodeSize; ! 165: ! 166: // ! 167: // Set up the scanner's state ! 168: // ! 169: scanState->buffer = buffer; ! 170: scanState->bufferSize = bufferSize; ! 171: scanState->btcb = btcb; ! 172: scanState->nodeNum = startingNode; ! 173: scanState->recordNum = startingRecord; ! 174: scanState->currentNode = buffer; ! 175: scanState->nodesLeftInBuffer = 0; // no nodes currently in buffer ! 176: scanState->recordsFound = recordsFound; ! 177: scanState->lastCachedNode = 0; // no nodes have been cached ! 178: ! 179: ! 180: return noErr; ! 181: } ! 182: ! 183: ! 184: ! 185: //_________________________________________________________________________________ ! 186: // ! 187: // Routine: BTScanTerminate ! 188: // ! 189: // Purpose: Return state information about a scan so that it can be resumed ! 190: // later via BTScanInitialize. ! 191: // ! 192: // Inputs: ! 193: // scanState Scanner's current state ! 194: // ! 195: // Outputs: ! 196: // nextNode Node number to resume a scan (pass to BTScanInitialize) ! 197: // nextRecord Record number to resume a scan (pass to BTScanInitialize) ! 198: // recordsFound Valid records seen so far (pass to BTScanInitialize) ! 199: //_________________________________________________________________________________ ! 200: ! 201: OSErr BTScanTerminate( ! 202: const BTScanState * scanState, ! 203: UInt32 * startingNode, ! 204: UInt32 * startingRecord, ! 205: UInt32 * recordsFound) ! 206: { ! 207: *startingNode = scanState->nodeNum; ! 208: *startingRecord = scanState->recordNum; ! 209: *recordsFound = scanState->recordsFound; ! 210: ! 211: return noErr; ! 212: } ! 213: ! 214: ! 215: ! 216: //_________________________________________________________________________________ ! 217: // ! 218: // Routine: BTScanNextRecord ! 219: // ! 220: // Purpose: Return the next leaf record in a scan. ! 221: // ! 222: // Inputs: ! 223: // scanState Scanner's current state ! 224: // avoidIO If true, don't do any I/O to refill the buffer ! 225: // ! 226: // Outputs: ! 227: // key Key of found record (points into buffer) ! 228: // data Data of found record (points into buffer) ! 229: // dataSize Size of data in found record ! 230: // ! 231: // Result: ! 232: // noErr Found a valid record ! 233: // btNotFound No more records ! 234: // ??? Needed to do I/O to get next node, but avoidIO set ! 235: // ! 236: // Notes: ! 237: // This routine returns pointers to the found record's key and data. It ! 238: // does not copy the key or data to a caller-supplied buffer (like ! 239: // GetBTreeRecord would). The caller must not modify the key or data. ! 240: //_________________________________________________________________________________ ! 241: ! 242: OSErr BTScanNextRecord( ! 243: BTScanState * scanState, ! 244: Boolean avoidIO, ! 245: void * * key, ! 246: void * * data, ! 247: UInt32 * dataSize) ! 248: { ! 249: OSStatus err; ! 250: UInt16 dataSizeShort; ! 251: ! 252: err = noErr; ! 253: ! 254: // ! 255: // If this is the first call, there won't be any nodes in the buffer, so go ! 256: // find the first first leaf node (if any). ! 257: // ! 258: if (scanState->nodesLeftInBuffer == 0) ! 259: err = FindNextLeafNode(scanState, avoidIO); ! 260: ! 261: while (err == noErr) { ! 262: // See if we have a record in the current node ! 263: err = GetRecordByIndex(scanState->btcb, scanState->currentNode, scanState->recordNum, ! 264: (KeyPtr *) key, (UInt8 **) data, &dataSizeShort); ! 265: if (err == noErr) { ! 266: ++scanState->recordsFound; ! 267: ++scanState->recordNum; ! 268: *dataSize = dataSizeShort; ! 269: return noErr; ! 270: } ! 271: ! 272: // We're done with the current node. See if we've returned all the records ! 273: if (scanState->recordsFound >= scanState->btcb->leafRecords) ! 274: return btNotFound; ! 275: ! 276: // Move to the first record of the next leaf node ! 277: scanState->recordNum = 0; ! 278: err = FindNextLeafNode(scanState, avoidIO); ! 279: } ! 280: ! 281: // ! 282: // If we got an EOF error from FindNextLeafNode, then there are no more leaf ! 283: // records to be found. ! 284: // ! 285: if (err == eofErr) ! 286: err = btNotFound; ! 287: ! 288: return err; ! 289: } ! 290: ! 291: ! 292: ! 293: //_________________________________________________________________________________ ! 294: // ! 295: // Routine: BTScanCacheNode ! 296: // ! 297: // Purpose: Make sure that the node just returned is in the cache. ! 298: // ! 299: // Inputs: ! 300: // scanState Scanner's current state ! 301: //_________________________________________________________________________________ ! 302: ! 303: OSErr BTScanCacheNode(BTScanState *scanState) ! 304: { ! 305: OSStatus err; ! 306: NodeRec nodeRec; ! 307: ! 308: err = noErr; ! 309: ! 310: if (scanState->lastCachedNode != scanState->nodeNum) { ! 311: err = GetNode(scanState->btcb, scanState->nodeNum, &nodeRec); ! 312: if (err == noErr) ! 313: err = ReleaseNode(scanState->btcb, &nodeRec); ! 314: scanState->lastCachedNode = scanState->nodeNum; ! 315: } ! 316: ! 317: return err; ! 318: } ! 319: ! 320: ! 321: ! 322: //_________________________________________________________________________________ ! 323: // ! 324: // Routine: FindNextLeafNode ! 325: // ! 326: // Purpose: Point to the next leaf node in the buffer. Read more nodes ! 327: // into the buffer if needed (and allowed). ! 328: // ! 329: // Inputs: ! 330: // scanState Scanner's current state ! 331: // avoidIO If true, don't do any I/O to refill the buffer ! 332: // ! 333: // Result: ! 334: // noErr Found a valid record ! 335: // eofErr No more nodes in file ! 336: // ??? Needed to do I/O to get next node, but avoidIO set ! 337: //_________________________________________________________________________________ ! 338: ! 339: OSErr FindNextLeafNode(BTScanState *scanState, Boolean avoidIO) ! 340: { ! 341: OSErr err; ! 342: ! 343: err = noErr; // Assume everything will be OK ! 344: ! 345: while (1) { ! 346: if (scanState->nodesLeftInBuffer == 0) { ! 347: // Time to read some more nodes into the buffer ! 348: if (avoidIO) { ! 349: return fsBTTimeOutErr; ! 350: } ! 351: else { ! 352: // read some more nodes into buffer ! 353: scanState->currentNode = scanState->buffer; ! 354: err = ReadMultipleNodes(scanState); ! 355: if (err != noErr) break; ! 356: } ! 357: } ! 358: else { ! 359: // Adjust the node counters and point to the next node in the buffer ! 360: ++scanState->nodeNum; ! 361: --scanState->nodesLeftInBuffer; ! 362: (UInt8 *) scanState->currentNode += scanState->btcb->nodeSize; ! 363: ! 364: // If we've looked at all nodes in the tree, then we're done ! 365: if (scanState->nodeNum > scanState->btcb->totalNodes) ! 366: return eofErr; ! 367: } ! 368: ! 369: //�� CheckNode returns an error on empty nodes (all zeros) ! 370: // err = CheckNode(scanState->btcb, scanState->currentNode); ! 371: // if (err != noErr) break; ! 372: ! 373: if (scanState->currentNode->type == kLeafNode) ! 374: break; ! 375: } ! 376: ! 377: return err; ! 378: } ! 379: ! 380: ! 381: ! 382: //_________________________________________________________________________________ ! 383: // ! 384: // Routine: ReadMultipleNodes ! 385: // ! 386: // Purpose: Read one or more nodes into the buffer. ! 387: // ! 388: // Inputs: ! 389: // scanState Scanner's current state ! 390: // ! 391: // Result: ! 392: // noErr One or nodes were read ! 393: // eofErr No nodes left in file, none in buffer ! 394: //_________________________________________________________________________________ ! 395: ! 396: OSErr ReadMultipleNodes(BTScanState *scanState) ! 397: { ! 398: #if TARGET_OS_MAC ! 399: OSErr err = noErr; ! 400: UInt32 nodeSize; // size of one B-Tree node ! 401: UInt32 currentPosition; // current offset in bytes from start of B-Tree file ! 402: UInt32 bytesToRead; // number of bytes left to read into buffer ! 403: ExtendedVCB *vcb; // volume that B-Tree resides on ! 404: UInt32 actualBytes; // bytes actually read by CacheReadInPlace ! 405: HIOParam iopb; // used by CacheReadInPlace ! 406: ! 407: nodeSize = scanState->btcb->nodeSize; ! 408: vcb = GetFileControlBlock(scanState->btcb->fileRefNum)->fcbVPtr; ! 409: ! 410: iopb.ioRefNum = scanState->btcb->fileRefNum; ! 411: iopb.ioPermssn = fsRdPerm; ! 412: iopb.ioBuffer = scanState->buffer; ! 413: iopb.ioPosMode = fsAtMark | (noCacheMask << 8); ! 414: iopb.ioActCount = 0; ! 415: ! 416: currentPosition = scanState->nodeNum * nodeSize; ! 417: bytesToRead = scanState->bufferSize; ! 418: ! 419: while (bytesToRead > 0) { ! 420: err = CacheReadInPlace(vcb, &iopb, currentPosition, bytesToRead, &actualBytes); ! 421: ! 422: if (err != noErr || actualBytes == 0) ! 423: break; ! 424: ! 425: bytesToRead -= actualBytes; ! 426: iopb.ioActCount += actualBytes; ! 427: currentPosition += actualBytes; ! 428: } ! 429: ! 430: scanState->nodesLeftInBuffer = iopb.ioActCount / nodeSize; ! 431: ! 432: if (err == fxRangeErr && iopb.ioActCount == 0) ! 433: err = eofErr; ! 434: else ! 435: err = noErr; ! 436: ! 437: if (scanState->nodesLeftInBuffer == 0 && err == noErr) ! 438: err = eofErr; ! 439: ! 440: return err; ! 441: #else ! 442: #pragma unused(scanState) ! 443: return noErr; ! 444: #endif ! 445: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.