2 * Copyright (c) 1996-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
32 #include <sys/kernel.h>
33 #include "../../hfs_endian.h"
35 #include "../headers/BTreeScanner.h"
37 static int FindNextLeafNode( BTScanState
*scanState
, Boolean avoidIO
);
38 static int ReadMultipleNodes( BTScanState
*scanState
);
41 //_________________________________________________________________________________
43 // Routine: BTScanNextRecord
45 // Purpose: Return the next leaf record in a scan.
48 // scanState Scanner's current state
49 // avoidIO If true, don't do any I/O to refill the buffer
52 // key Key of found record (points into buffer)
53 // data Data of found record (points into buffer)
54 // dataSize Size of data in found record
57 // noErr Found a valid record
58 // btNotFound No more records
59 // ??? Needed to do I/O to get next node, but avoidIO set
62 // This routine returns pointers to the found record's key and data. It
63 // does not copy the key or data to a caller-supplied buffer (like
64 // GetBTreeRecord would). The caller must not modify the key or data.
65 //_________________________________________________________________________________
67 int BTScanNextRecord( BTScanState
* scanState
,
71 u_int32_t
* dataSize
)
74 u_int16_t dataSizeShort
;
79 // If this is the first call, there won't be any nodes in the buffer, so go
80 // find the first first leaf node (if any).
82 if ( scanState
->nodesLeftInBuffer
== 0 )
84 err
= FindNextLeafNode( scanState
, avoidIO
);
87 while ( err
== noErr
)
89 // See if we have a record in the current node
90 err
= GetRecordByIndex( scanState
->btcb
, scanState
->currentNodePtr
,
91 scanState
->recordNum
, (KeyPtr
*) key
,
92 (UInt8
**) data
, &dataSizeShort
);
96 ++scanState
->recordsFound
;
97 ++scanState
->recordNum
;
99 *dataSize
= dataSizeShort
;
104 // We didn't get the node through the cache, so we can't invalidate it.
105 //XXX Should we do something else to avoid seeing the same record again?
109 // We're done with the current node. See if we've returned all the records
110 if ( scanState
->recordsFound
>= scanState
->btcb
->leafRecords
)
115 // Move to the first record of the next leaf node
116 scanState
->recordNum
= 0;
117 err
= FindNextLeafNode( scanState
, avoidIO
);
121 // If we got an EOF error from FindNextLeafNode, then there are no more leaf
122 // records to be found.
124 if ( err
== fsEndOfIterationErr
)
129 } /* BTScanNextRecord */
132 //_________________________________________________________________________________
134 // Routine: FindNextLeafNode
136 // Purpose: Point to the next leaf node in the buffer. Read more nodes
137 // into the buffer if needed (and allowed).
140 // scanState Scanner's current state
141 // avoidIO If true, don't do any I/O to refill the buffer
144 // noErr Found a valid record
145 // fsEndOfIterationErr No more nodes in file
146 // ??? Needed to do I/O to get next node, but avoidIO set
147 //_________________________________________________________________________________
149 static int FindNextLeafNode( BTScanState
*scanState
, Boolean avoidIO
)
152 BlockDescriptor block
;
155 err
= noErr
; // Assume everything will be OK
159 if ( scanState
->nodesLeftInBuffer
== 0 )
161 // Time to read some more nodes into the buffer
164 return fsBTTimeOutErr
;
168 // read some more nodes into buffer
169 err
= ReadMultipleNodes( scanState
);
176 // Adjust the node counters and point to the next node in the buffer
177 ++scanState
->nodeNum
;
178 --scanState
->nodesLeftInBuffer
;
180 // If we've looked at all nodes in the tree, then we're done
181 if ( scanState
->nodeNum
>= scanState
->btcb
->totalNodes
)
182 return fsEndOfIterationErr
;
184 if ( scanState
->nodesLeftInBuffer
== 0 )
186 scanState
->recordNum
= 0;
190 (u_int8_t
*) scanState
->currentNodePtr
+= scanState
->btcb
->nodeSize
;
193 /* Fake a BlockDescriptor */
194 block
.blockHeader
= NULL
; /* No buffer cache buffer */
195 block
.buffer
= scanState
->currentNodePtr
;
196 block
.blockNum
= scanState
->nodeNum
;
197 block
.blockSize
= scanState
->btcb
->nodeSize
;
198 block
.blockReadFromDisk
= 1;
199 block
.isModified
= 0;
201 fref
= scanState
->btcb
->fileRefNum
;
203 /* This node was read from disk, so it must be swapped/checked. */
204 err
= hfs_swap_BTNode(&block
, fref
, kSwapBTNodeBigToHost
);
205 if ( err
!= noErr
) {
206 printf("FindNextLeafNode: Error from hfs_swap_BTNode (node %u)\n", scanState
->nodeNum
);
210 if ( scanState
->currentNodePtr
->kind
== kBTLeafNode
)
216 } /* FindNextLeafNode */
219 //_________________________________________________________________________________
221 // Routine: ReadMultipleNodes
223 // Purpose: Read one or more nodes into the buffer.
226 // theScanStatePtr Scanner's current state
229 // noErr One or nodes were read
230 // fsEndOfIterationErr No nodes left in file, none in buffer
231 //_________________________________________________________________________________
233 static int ReadMultipleNodes( BTScanState
*theScanStatePtr
)
236 BTreeControlBlockPtr myBTreeCBPtr
;
237 daddr64_t myPhyBlockNum
;
238 u_int32_t myBufferSize
;
239 struct vnode
* myDevPtr
;
241 u_int32_t myBlocksInBufferCount
;
243 // release old buffer if we have one
244 if ( theScanStatePtr
->bufferPtr
!= NULL
)
246 buf_markinvalid(theScanStatePtr
->bufferPtr
);
247 buf_brelse( theScanStatePtr
->bufferPtr
);
248 theScanStatePtr
->bufferPtr
= NULL
;
249 theScanStatePtr
->currentNodePtr
= NULL
;
252 myBTreeCBPtr
= theScanStatePtr
->btcb
;
254 // map logical block in catalog btree file to physical block on volume
255 myErr
= hfs_bmap(myBTreeCBPtr
->fileRefNum
, theScanStatePtr
->nodeNum
,
256 &myDevPtr
, &myPhyBlockNum
, &myBlockRun
);
257 if ( myErr
!= E_NONE
)
259 goto ExitThisRoutine
;
262 // bmap block run gives us the remaining number of valid blocks (number of blocks
263 // minus the first). so if there are 10 valid blocks our run number will be 9.
264 // blocks, in our case is the same as nodes (both are 4K)
265 myBlocksInBufferCount
= (theScanStatePtr
->bufferSize
/ myBTreeCBPtr
->nodeSize
);
266 myBufferSize
= theScanStatePtr
->bufferSize
;
267 if ( (myBlockRun
+ 1) < myBlocksInBufferCount
)
269 myBufferSize
= (myBlockRun
+ 1) * myBTreeCBPtr
->nodeSize
;
272 // now read blocks from the device
273 myErr
= (int)buf_bread(myDevPtr
,
277 &theScanStatePtr
->bufferPtr
);
278 if ( myErr
!= E_NONE
)
280 goto ExitThisRoutine
;
283 theScanStatePtr
->nodesLeftInBuffer
= buf_count(theScanStatePtr
->bufferPtr
) / theScanStatePtr
->btcb
->nodeSize
;
284 theScanStatePtr
->currentNodePtr
= (BTNodeDescriptor
*) buf_dataptr(theScanStatePtr
->bufferPtr
);
289 } /* ReadMultipleNodes */
293 //_________________________________________________________________________________
295 // Routine: BTScanInitialize
297 // Purpose: Prepare to start a new BTree scan, or resume a previous one.
300 // btreeFile The B-Tree's file control block
301 // startingNode Initial node number
302 // startingRecord Initial record number within node
303 // recordsFound Number of valid records found so far
304 // bufferSize Size (in bytes) of buffer
307 // scanState Scanner's current state; pass to other scanner calls
310 // To begin a new scan and see all records in the B-Tree, pass zeroes for
311 // startingNode, startingRecord, and recordsFound.
313 // To resume a scan from the point of a previous BTScanTerminate, use the
314 // values returned by BTScanTerminate as input for startingNode, startingRecord,
317 // When resuming a scan, the caller should check the B-tree's write count. If
318 // it is different from the write count when the scan was terminated, then the
319 // tree may have changed and the current state may be incorrect. In particular,
320 // you may see some records more than once, or never see some records. Also,
321 // the scanner may not be able to detect when all leaf records have been seen,
322 // and will have to scan through many empty nodes.
324 // XXXĂPerhaps the write count should be managed by BTScanInitialize and
325 // XXX BTScanTerminate? This would avoid the caller having to peek at
326 // XXX internal B-Tree structures.
327 //_________________________________________________________________________________
329 int BTScanInitialize( const FCB
* btreeFile
,
330 u_int32_t startingNode
,
331 u_int32_t startingRecord
,
332 u_int32_t recordsFound
,
333 u_int32_t bufferSize
,
334 BTScanState
* scanState
)
336 BTreeControlBlock
*btcb
;
339 // Make sure this is a valid B-Tree file
341 btcb
= (BTreeControlBlock
*) btreeFile
->fcbBTCBPtr
;
343 return fsBTInvalidFileErr
;
346 // Make sure buffer size is big enough, and a multiple of the
349 if ( bufferSize
< btcb
->nodeSize
)
351 bufferSize
= (bufferSize
/ btcb
->nodeSize
) * btcb
->nodeSize
;
354 // Set up the scanner's state
356 scanState
->bufferSize
= bufferSize
;
357 scanState
->bufferPtr
= NULL
;
358 scanState
->btcb
= btcb
;
359 scanState
->nodeNum
= startingNode
;
360 scanState
->recordNum
= startingRecord
;
361 scanState
->currentNodePtr
= NULL
;
362 scanState
->nodesLeftInBuffer
= 0; // no nodes currently in buffer
363 scanState
->recordsFound
= recordsFound
;
364 microuptime(&scanState
->startTime
); // initialize our throttle
368 } /* BTScanInitialize */
371 //_________________________________________________________________________________
373 // Routine: BTScanTerminate
375 // Purpose: Return state information about a scan so that it can be resumed
376 // later via BTScanInitialize.
379 // scanState Scanner's current state
382 // nextNode Node number to resume a scan (pass to BTScanInitialize)
383 // nextRecord Record number to resume a scan (pass to BTScanInitialize)
384 // recordsFound Valid records seen so far (pass to BTScanInitialize)
385 //_________________________________________________________________________________
387 int BTScanTerminate( BTScanState
* scanState
,
388 u_int32_t
* startingNode
,
389 u_int32_t
* startingRecord
,
390 u_int32_t
* recordsFound
)
392 *startingNode
= scanState
->nodeNum
;
393 *startingRecord
= scanState
->recordNum
;
394 *recordsFound
= scanState
->recordsFound
;
396 if ( scanState
->bufferPtr
!= NULL
)
398 buf_markinvalid(scanState
->bufferPtr
);
399 buf_brelse( scanState
->bufferPtr
);
400 scanState
->bufferPtr
= NULL
;
401 scanState
->currentNodePtr
= NULL
;
406 } /* BTScanTerminate */