2 * Copyright (c) 1996-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
27 #include <sys/kernel.h>
28 #include "../../hfs_endian.h"
30 #include "../headers/BTreeScanner.h"
32 static int FindNextLeafNode( BTScanState
*scanState
, Boolean avoidIO
);
33 static int ReadMultipleNodes( BTScanState
*scanState
);
36 //_________________________________________________________________________________
38 // Routine: BTScanNextRecord
40 // Purpose: Return the next leaf record in a scan.
43 // scanState Scanner's current state
44 // avoidIO If true, don't do any I/O to refill the buffer
47 // key Key of found record (points into buffer)
48 // data Data of found record (points into buffer)
49 // dataSize Size of data in found record
52 // noErr Found a valid record
53 // btNotFound No more records
54 // ??? Needed to do I/O to get next node, but avoidIO set
57 // This routine returns pointers to the found record's key and data. It
58 // does not copy the key or data to a caller-supplied buffer (like
59 // GetBTreeRecord would). The caller must not modify the key or data.
60 //_________________________________________________________________________________
62 int BTScanNextRecord( BTScanState
* scanState
,
66 u_int32_t
* dataSize
)
69 u_int16_t dataSizeShort
;
74 // If this is the first call, there won't be any nodes in the buffer, so go
75 // find the first first leaf node (if any).
77 if ( scanState
->nodesLeftInBuffer
== 0 )
79 err
= FindNextLeafNode( scanState
, avoidIO
);
82 while ( err
== noErr
)
84 // See if we have a record in the current node
85 err
= GetRecordByIndex( scanState
->btcb
, scanState
->currentNodePtr
,
86 scanState
->recordNum
, (KeyPtr
*) key
,
87 (UInt8
**) data
, &dataSizeShort
);
91 ++scanState
->recordsFound
;
92 ++scanState
->recordNum
;
94 *dataSize
= dataSizeShort
;
99 // We didn't get the node through the cache, so we can't invalidate it.
100 //XXX Should we do something else to avoid seeing the same record again?
104 // We're done with the current node. See if we've returned all the records
105 if ( scanState
->recordsFound
>= scanState
->btcb
->leafRecords
)
110 // Move to the first record of the next leaf node
111 scanState
->recordNum
= 0;
112 err
= FindNextLeafNode( scanState
, avoidIO
);
116 // If we got an EOF error from FindNextLeafNode, then there are no more leaf
117 // records to be found.
119 if ( err
== fsEndOfIterationErr
)
124 } /* BTScanNextRecord */
127 //_________________________________________________________________________________
129 // Routine: FindNextLeafNode
131 // Purpose: Point to the next leaf node in the buffer. Read more nodes
132 // into the buffer if needed (and allowed).
135 // scanState Scanner's current state
136 // avoidIO If true, don't do any I/O to refill the buffer
139 // noErr Found a valid record
140 // fsEndOfIterationErr No more nodes in file
141 // ??? Needed to do I/O to get next node, but avoidIO set
142 //_________________________________________________________________________________
144 static int FindNextLeafNode( BTScanState
*scanState
, Boolean avoidIO
)
148 err
= noErr
; // Assume everything will be OK
152 if ( scanState
->nodesLeftInBuffer
== 0 )
154 // Time to read some more nodes into the buffer
157 return fsBTTimeOutErr
;
161 // read some more nodes into buffer
162 err
= ReadMultipleNodes( scanState
);
169 // Adjust the node counters and point to the next node in the buffer
170 ++scanState
->nodeNum
;
171 --scanState
->nodesLeftInBuffer
;
173 // If we've looked at all nodes in the tree, then we're done
174 if ( scanState
->nodeNum
>= scanState
->btcb
->totalNodes
)
175 return fsEndOfIterationErr
;
177 if ( scanState
->nodesLeftInBuffer
== 0 )
179 scanState
->recordNum
= 0;
183 (u_int8_t
*) scanState
->currentNodePtr
+= scanState
->btcb
->nodeSize
;
186 #if BYTE_ORDER == LITTLE_ENDIAN
188 BlockDescriptor block
;
191 /* Fake a BlockDescriptor */
192 block
.buffer
= scanState
->currentNodePtr
;
193 block
.blockSize
= scanState
->btcb
->nodeSize
;
194 block
.blockReadFromDisk
= 1;
195 block
.isModified
= 0;
197 fref
= scanState
->btcb
->fileRefNum
;
199 SWAP_BT_NODE(&block
, ISHFSPLUS(VTOVCB(fref
)), VTOC(fref
)->c_fileid
, 0);
203 // Make sure this is a valid node
204 if ( CheckNode( scanState
->btcb
, scanState
->currentNodePtr
) != noErr
)
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 daddr_t myPhyBlockNum
;
237 u_int32_t myBufferSize
;
238 struct vnode
* myDevPtr
;
240 u_int32_t myBlocksInBufferCount
;
242 // release old buffer if we have one
243 if ( theScanStatePtr
->bufferPtr
!= NULL
)
245 theScanStatePtr
->bufferPtr
->b_flags
|= (B_INVAL
| B_AGE
);
246 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
= VOP_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
= bread( myDevPtr
,
276 &theScanStatePtr
->bufferPtr
);
277 if ( myErr
!= E_NONE
)
279 goto ExitThisRoutine
;
282 theScanStatePtr
->nodesLeftInBuffer
= theScanStatePtr
->bufferPtr
->b_bcount
/ theScanStatePtr
->btcb
->nodeSize
;
283 theScanStatePtr
->currentNodePtr
= (BTNodeDescriptor
*) theScanStatePtr
->bufferPtr
->b_data
;
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 scanState
->startTime
= time
; // 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 scanState
->bufferPtr
->b_flags
|= (B_INVAL
| B_AGE
);
398 brelse( scanState
->bufferPtr
);
399 scanState
->bufferPtr
= NULL
;
400 scanState
->currentNodePtr
= NULL
;
405 } /* BTScanTerminate */