2 * Copyright (c) 1996-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_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 License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 #include <sys/kernel.h>
31 #include "../../hfs_endian.h"
33 #include "../headers/BTreeScanner.h"
35 static int FindNextLeafNode( BTScanState
*scanState
, Boolean avoidIO
);
36 static int ReadMultipleNodes( BTScanState
*scanState
);
39 //_________________________________________________________________________________
41 // Routine: BTScanNextRecord
43 // Purpose: Return the next leaf record in a scan.
46 // scanState Scanner's current state
47 // avoidIO If true, don't do any I/O to refill the buffer
50 // key Key of found record (points into buffer)
51 // data Data of found record (points into buffer)
52 // dataSize Size of data in found record
55 // noErr Found a valid record
56 // btNotFound No more records
57 // ??? Needed to do I/O to get next node, but avoidIO set
60 // This routine returns pointers to the found record's key and data. It
61 // does not copy the key or data to a caller-supplied buffer (like
62 // GetBTreeRecord would). The caller must not modify the key or data.
63 //_________________________________________________________________________________
65 int BTScanNextRecord( BTScanState
* scanState
,
69 u_int32_t
* dataSize
)
72 u_int16_t dataSizeShort
;
77 // If this is the first call, there won't be any nodes in the buffer, so go
78 // find the first first leaf node (if any).
80 if ( scanState
->nodesLeftInBuffer
== 0 )
82 err
= FindNextLeafNode( scanState
, avoidIO
);
85 while ( err
== noErr
)
87 // See if we have a record in the current node
88 err
= GetRecordByIndex( scanState
->btcb
, scanState
->currentNodePtr
,
89 scanState
->recordNum
, (KeyPtr
*) key
,
90 (u_int8_t
**) data
, &dataSizeShort
);
94 ++scanState
->recordsFound
;
95 ++scanState
->recordNum
;
97 *dataSize
= dataSizeShort
;
102 // We didn't get the node through the cache, so we can't invalidate it.
103 //XXX Should we do something else to avoid seeing the same record again?
107 // We're done with the current node. See if we've returned all the records
108 if ( scanState
->recordsFound
>= scanState
->btcb
->leafRecords
)
113 // Move to the first record of the next leaf node
114 scanState
->recordNum
= 0;
115 err
= FindNextLeafNode( scanState
, avoidIO
);
119 // If we got an EOF error from FindNextLeafNode, then there are no more leaf
120 // records to be found.
122 if ( err
== fsEndOfIterationErr
)
127 } /* BTScanNextRecord */
130 //_________________________________________________________________________________
132 // Routine: FindNextLeafNode
134 // Purpose: Point to the next leaf node in the buffer. Read more nodes
135 // into the buffer if needed (and allowed).
138 // scanState Scanner's current state
139 // avoidIO If true, don't do any I/O to refill the buffer
142 // noErr Found a valid record
143 // fsEndOfIterationErr No more nodes in file
144 // ??? Needed to do I/O to get next node, but avoidIO set
145 //_________________________________________________________________________________
147 static int FindNextLeafNode( BTScanState
*scanState
, Boolean avoidIO
)
150 BlockDescriptor block
;
153 err
= noErr
; // Assume everything will be OK
157 if ( scanState
->nodesLeftInBuffer
== 0 )
159 // Time to read some more nodes into the buffer
162 return fsBTTimeOutErr
;
166 // read some more nodes into buffer
167 err
= ReadMultipleNodes( scanState
);
174 // Adjust the node counters and point to the next node in the buffer
175 ++scanState
->nodeNum
;
176 --scanState
->nodesLeftInBuffer
;
178 // If we've looked at all nodes in the tree, then we're done
179 if ( scanState
->nodeNum
>= scanState
->btcb
->totalNodes
)
180 return fsEndOfIterationErr
;
182 if ( scanState
->nodesLeftInBuffer
== 0 )
184 scanState
->recordNum
= 0;
188 scanState
->currentNodePtr
= (BTNodeDescriptor
*)(((u_int8_t
*)scanState
->currentNodePtr
)
189 + scanState
->btcb
->nodeSize
);
192 /* Fake a BlockDescriptor */
193 block
.blockHeader
= NULL
; /* No buffer cache buffer */
194 block
.buffer
= scanState
->currentNodePtr
;
195 block
.blockNum
= scanState
->nodeNum
;
196 block
.blockSize
= scanState
->btcb
->nodeSize
;
197 block
.blockReadFromDisk
= 1;
198 block
.isModified
= 0;
200 fref
= scanState
->btcb
->fileRefNum
;
202 /* This node was read from disk, so it must be swapped/checked. */
203 err
= hfs_swap_BTNode(&block
, fref
, kSwapBTNodeBigToHost
);
204 if ( err
!= noErr
) {
205 printf("FindNextLeafNode: Error from hfs_swap_BTNode (node %u)\n", scanState
->nodeNum
);
209 if ( scanState
->currentNodePtr
->kind
== kBTLeafNode
)
215 } /* FindNextLeafNode */
218 //_________________________________________________________________________________
220 // Routine: ReadMultipleNodes
222 // Purpose: Read one or more nodes into the buffer.
225 // theScanStatePtr Scanner's current state
228 // noErr One or nodes were read
229 // fsEndOfIterationErr No nodes left in file, none in buffer
230 //_________________________________________________________________________________
232 static int ReadMultipleNodes( BTScanState
*theScanStatePtr
)
235 BTreeControlBlockPtr myBTreeCBPtr
;
236 daddr64_t myPhyBlockNum
;
237 u_int32_t myBufferSize
;
238 struct vnode
* myDevPtr
;
239 unsigned int myBlockRun
;
240 u_int32_t myBlocksInBufferCount
;
242 // release old buffer if we have one
243 if ( theScanStatePtr
->bufferPtr
!= NULL
)
245 buf_markinvalid(theScanStatePtr
->bufferPtr
);
246 buf_brelse( theScanStatePtr
->bufferPtr
);
247 theScanStatePtr
->bufferPtr
= NULL
;
248 theScanStatePtr
->currentNodePtr
= NULL
;
251 myBTreeCBPtr
= theScanStatePtr
->btcb
;
253 // map logical block in catalog btree file to physical block on volume
254 myErr
= hfs_bmap(myBTreeCBPtr
->fileRefNum
, theScanStatePtr
->nodeNum
,
255 &myDevPtr
, &myPhyBlockNum
, &myBlockRun
);
256 if ( myErr
!= E_NONE
)
258 goto ExitThisRoutine
;
261 // bmap block run gives us the remaining number of valid blocks (number of blocks
262 // minus the first). so if there are 10 valid blocks our run number will be 9.
263 // blocks, in our case is the same as nodes (both are 4K)
264 myBlocksInBufferCount
= (theScanStatePtr
->bufferSize
/ myBTreeCBPtr
->nodeSize
);
265 myBufferSize
= theScanStatePtr
->bufferSize
;
266 if ( (myBlockRun
+ 1) < myBlocksInBufferCount
)
268 myBufferSize
= (myBlockRun
+ 1) * myBTreeCBPtr
->nodeSize
;
271 // now read blocks from the device
272 myErr
= (int)buf_bread(myDevPtr
,
276 &theScanStatePtr
->bufferPtr
);
277 if ( myErr
!= E_NONE
)
279 goto ExitThisRoutine
;
282 theScanStatePtr
->nodesLeftInBuffer
= buf_count(theScanStatePtr
->bufferPtr
) / theScanStatePtr
->btcb
->nodeSize
;
283 theScanStatePtr
->currentNodePtr
= (BTNodeDescriptor
*) buf_dataptr(theScanStatePtr
->bufferPtr
);
288 } /* ReadMultipleNodes */
292 //_________________________________________________________________________________
294 // Routine: BTScanInitialize
296 // Purpose: Prepare to start a new BTree scan, or resume a previous one.
299 // btreeFile The B-Tree's file control block
300 // startingNode Initial node number
301 // startingRecord Initial record number within node
302 // recordsFound Number of valid records found so far
303 // bufferSize Size (in bytes) of buffer
306 // scanState Scanner's current state; pass to other scanner calls
309 // To begin a new scan and see all records in the B-Tree, pass zeroes for
310 // startingNode, startingRecord, and recordsFound.
312 // To resume a scan from the point of a previous BTScanTerminate, use the
313 // values returned by BTScanTerminate as input for startingNode, startingRecord,
316 // When resuming a scan, the caller should check the B-tree's write count. If
317 // it is different from the write count when the scan was terminated, then the
318 // tree may have changed and the current state may be incorrect. In particular,
319 // you may see some records more than once, or never see some records. Also,
320 // the scanner may not be able to detect when all leaf records have been seen,
321 // and will have to scan through many empty nodes.
323 // XXXĂPerhaps the write count should be managed by BTScanInitialize and
324 // XXX BTScanTerminate? This would avoid the caller having to peek at
325 // XXX internal B-Tree structures.
326 //_________________________________________________________________________________
328 int BTScanInitialize( const FCB
* btreeFile
,
329 u_int32_t startingNode
,
330 u_int32_t startingRecord
,
331 u_int32_t recordsFound
,
332 u_int32_t bufferSize
,
333 BTScanState
* scanState
)
335 BTreeControlBlock
*btcb
;
338 // Make sure this is a valid B-Tree file
340 btcb
= (BTreeControlBlock
*) btreeFile
->fcbBTCBPtr
;
342 return fsBTInvalidFileErr
;
345 // Make sure buffer size is big enough, and a multiple of the
348 if ( bufferSize
< btcb
->nodeSize
)
350 bufferSize
= (bufferSize
/ btcb
->nodeSize
) * btcb
->nodeSize
;
353 // Set up the scanner's state
355 scanState
->bufferSize
= bufferSize
;
356 scanState
->bufferPtr
= NULL
;
357 scanState
->btcb
= btcb
;
358 scanState
->nodeNum
= startingNode
;
359 scanState
->recordNum
= startingRecord
;
360 scanState
->currentNodePtr
= NULL
;
361 scanState
->nodesLeftInBuffer
= 0; // no nodes currently in buffer
362 scanState
->recordsFound
= recordsFound
;
363 microuptime(&scanState
->startTime
); // initialize our throttle
367 } /* BTScanInitialize */
370 //_________________________________________________________________________________
372 // Routine: BTScanTerminate
374 // Purpose: Return state information about a scan so that it can be resumed
375 // later via BTScanInitialize.
378 // scanState Scanner's current state
381 // nextNode Node number to resume a scan (pass to BTScanInitialize)
382 // nextRecord Record number to resume a scan (pass to BTScanInitialize)
383 // recordsFound Valid records seen so far (pass to BTScanInitialize)
384 //_________________________________________________________________________________
386 int BTScanTerminate( BTScanState
* scanState
,
387 u_int32_t
* startingNode
,
388 u_int32_t
* startingRecord
,
389 u_int32_t
* recordsFound
)
391 *startingNode
= scanState
->nodeNum
;
392 *startingRecord
= scanState
->recordNum
;
393 *recordsFound
= scanState
->recordsFound
;
395 if ( scanState
->bufferPtr
!= NULL
)
397 buf_markinvalid(scanState
->bufferPtr
);
398 buf_brelse( scanState
->bufferPtr
);
399 scanState
->bufferPtr
= NULL
;
400 scanState
->currentNodePtr
= NULL
;
405 } /* BTScanTerminate */