2 * Copyright (c) 1996-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/kernel.h>
25 #include "../../hfs_endian.h"
27 #include "../headers/BTreeScanner.h"
29 static int FindNextLeafNode( BTScanState
*scanState
, Boolean avoidIO
);
30 static int ReadMultipleNodes( BTScanState
*scanState
);
33 //_________________________________________________________________________________
35 // Routine: BTScanNextRecord
37 // Purpose: Return the next leaf record in a scan.
40 // scanState Scanner's current state
41 // avoidIO If true, don't do any I/O to refill the buffer
44 // key Key of found record (points into buffer)
45 // data Data of found record (points into buffer)
46 // dataSize Size of data in found record
49 // noErr Found a valid record
50 // btNotFound No more records
51 // ??? Needed to do I/O to get next node, but avoidIO set
54 // This routine returns pointers to the found record's key and data. It
55 // does not copy the key or data to a caller-supplied buffer (like
56 // GetBTreeRecord would). The caller must not modify the key or data.
57 //_________________________________________________________________________________
59 int BTScanNextRecord( BTScanState
* scanState
,
63 u_int32_t
* dataSize
)
66 u_int16_t dataSizeShort
;
71 // If this is the first call, there won't be any nodes in the buffer, so go
72 // find the first first leaf node (if any).
74 if ( scanState
->nodesLeftInBuffer
== 0 )
76 err
= FindNextLeafNode( scanState
, avoidIO
);
79 while ( err
== noErr
)
81 // See if we have a record in the current node
82 err
= GetRecordByIndex( scanState
->btcb
, scanState
->currentNodePtr
,
83 scanState
->recordNum
, (KeyPtr
*) key
,
84 (UInt8
**) data
, &dataSizeShort
);
88 ++scanState
->recordsFound
;
89 ++scanState
->recordNum
;
91 *dataSize
= dataSizeShort
;
96 // We didn't get the node through the cache, so we can't invalidate it.
97 //XXX Should we do something else to avoid seeing the same record again?
101 // We're done with the current node. See if we've returned all the records
102 if ( scanState
->recordsFound
>= scanState
->btcb
->leafRecords
)
107 // Move to the first record of the next leaf node
108 scanState
->recordNum
= 0;
109 err
= FindNextLeafNode( scanState
, avoidIO
);
113 // If we got an EOF error from FindNextLeafNode, then there are no more leaf
114 // records to be found.
116 if ( err
== fsEndOfIterationErr
)
121 } /* BTScanNextRecord */
124 //_________________________________________________________________________________
126 // Routine: FindNextLeafNode
128 // Purpose: Point to the next leaf node in the buffer. Read more nodes
129 // into the buffer if needed (and allowed).
132 // scanState Scanner's current state
133 // avoidIO If true, don't do any I/O to refill the buffer
136 // noErr Found a valid record
137 // fsEndOfIterationErr No more nodes in file
138 // ??? Needed to do I/O to get next node, but avoidIO set
139 //_________________________________________________________________________________
141 static int FindNextLeafNode( BTScanState
*scanState
, Boolean avoidIO
)
145 err
= noErr
; // Assume everything will be OK
149 if ( scanState
->nodesLeftInBuffer
== 0 )
151 // Time to read some more nodes into the buffer
154 return fsBTTimeOutErr
;
158 // read some more nodes into buffer
159 err
= ReadMultipleNodes( scanState
);
166 // Adjust the node counters and point to the next node in the buffer
167 ++scanState
->nodeNum
;
168 --scanState
->nodesLeftInBuffer
;
170 // If we've looked at all nodes in the tree, then we're done
171 if ( scanState
->nodeNum
>= scanState
->btcb
->totalNodes
)
172 return fsEndOfIterationErr
;
174 if ( scanState
->nodesLeftInBuffer
== 0 )
176 scanState
->recordNum
= 0;
180 (u_int8_t
*) scanState
->currentNodePtr
+= scanState
->btcb
->nodeSize
;
183 #if BYTE_ORDER == LITTLE_ENDIAN
185 BlockDescriptor block
;
188 /* Fake a BlockDescriptor */
189 block
.buffer
= scanState
->currentNodePtr
;
190 block
.blockSize
= scanState
->btcb
->nodeSize
;
191 block
.blockReadFromDisk
= 1;
192 block
.isModified
= 0;
194 fref
= scanState
->btcb
->fileRefNum
;
196 SWAP_BT_NODE(&block
, ISHFSPLUS(VTOVCB(fref
)), VTOC(fref
)->c_fileid
, 0);
200 // Make sure this is a valid node
201 if ( CheckNode( scanState
->btcb
, scanState
->currentNodePtr
) != noErr
)
206 if ( scanState
->currentNodePtr
->kind
== kBTLeafNode
)
212 } /* FindNextLeafNode */
215 //_________________________________________________________________________________
217 // Routine: ReadMultipleNodes
219 // Purpose: Read one or more nodes into the buffer.
222 // theScanStatePtr Scanner's current state
225 // noErr One or nodes were read
226 // fsEndOfIterationErr No nodes left in file, none in buffer
227 //_________________________________________________________________________________
229 static int ReadMultipleNodes( BTScanState
*theScanStatePtr
)
232 BTreeControlBlockPtr myBTreeCBPtr
;
233 daddr64_t myPhyBlockNum
;
234 u_int32_t myBufferSize
;
235 struct vnode
* myDevPtr
;
237 u_int32_t myBlocksInBufferCount
;
239 // release old buffer if we have one
240 if ( theScanStatePtr
->bufferPtr
!= NULL
)
242 buf_markinvalid(theScanStatePtr
->bufferPtr
);
243 buf_brelse( theScanStatePtr
->bufferPtr
);
244 theScanStatePtr
->bufferPtr
= NULL
;
245 theScanStatePtr
->currentNodePtr
= NULL
;
248 myBTreeCBPtr
= theScanStatePtr
->btcb
;
250 // map logical block in catalog btree file to physical block on volume
251 myErr
= hfs_bmap(myBTreeCBPtr
->fileRefNum
, theScanStatePtr
->nodeNum
,
252 &myDevPtr
, &myPhyBlockNum
, &myBlockRun
);
253 if ( myErr
!= E_NONE
)
255 goto ExitThisRoutine
;
258 // bmap block run gives us the remaining number of valid blocks (number of blocks
259 // minus the first). so if there are 10 valid blocks our run number will be 9.
260 // blocks, in our case is the same as nodes (both are 4K)
261 myBlocksInBufferCount
= (theScanStatePtr
->bufferSize
/ myBTreeCBPtr
->nodeSize
);
262 myBufferSize
= theScanStatePtr
->bufferSize
;
263 if ( (myBlockRun
+ 1) < myBlocksInBufferCount
)
265 myBufferSize
= (myBlockRun
+ 1) * myBTreeCBPtr
->nodeSize
;
268 // now read blocks from the device
269 myErr
= (int)buf_bread(myDevPtr
,
273 &theScanStatePtr
->bufferPtr
);
274 if ( myErr
!= E_NONE
)
276 goto ExitThisRoutine
;
279 theScanStatePtr
->nodesLeftInBuffer
= buf_count(theScanStatePtr
->bufferPtr
) / theScanStatePtr
->btcb
->nodeSize
;
280 theScanStatePtr
->currentNodePtr
= (BTNodeDescriptor
*) buf_dataptr(theScanStatePtr
->bufferPtr
);
285 } /* ReadMultipleNodes */
289 //_________________________________________________________________________________
291 // Routine: BTScanInitialize
293 // Purpose: Prepare to start a new BTree scan, or resume a previous one.
296 // btreeFile The B-Tree's file control block
297 // startingNode Initial node number
298 // startingRecord Initial record number within node
299 // recordsFound Number of valid records found so far
300 // bufferSize Size (in bytes) of buffer
303 // scanState Scanner's current state; pass to other scanner calls
306 // To begin a new scan and see all records in the B-Tree, pass zeroes for
307 // startingNode, startingRecord, and recordsFound.
309 // To resume a scan from the point of a previous BTScanTerminate, use the
310 // values returned by BTScanTerminate as input for startingNode, startingRecord,
313 // When resuming a scan, the caller should check the B-tree's write count. If
314 // it is different from the write count when the scan was terminated, then the
315 // tree may have changed and the current state may be incorrect. In particular,
316 // you may see some records more than once, or never see some records. Also,
317 // the scanner may not be able to detect when all leaf records have been seen,
318 // and will have to scan through many empty nodes.
320 // XXXĂPerhaps the write count should be managed by BTScanInitialize and
321 // XXX BTScanTerminate? This would avoid the caller having to peek at
322 // XXX internal B-Tree structures.
323 //_________________________________________________________________________________
325 int BTScanInitialize( const FCB
* btreeFile
,
326 u_int32_t startingNode
,
327 u_int32_t startingRecord
,
328 u_int32_t recordsFound
,
329 u_int32_t bufferSize
,
330 BTScanState
* scanState
)
332 BTreeControlBlock
*btcb
;
335 // Make sure this is a valid B-Tree file
337 btcb
= (BTreeControlBlock
*) btreeFile
->fcbBTCBPtr
;
339 return fsBTInvalidFileErr
;
342 // Make sure buffer size is big enough, and a multiple of the
345 if ( bufferSize
< btcb
->nodeSize
)
347 bufferSize
= (bufferSize
/ btcb
->nodeSize
) * btcb
->nodeSize
;
350 // Set up the scanner's state
352 scanState
->bufferSize
= bufferSize
;
353 scanState
->bufferPtr
= NULL
;
354 scanState
->btcb
= btcb
;
355 scanState
->nodeNum
= startingNode
;
356 scanState
->recordNum
= startingRecord
;
357 scanState
->currentNodePtr
= NULL
;
358 scanState
->nodesLeftInBuffer
= 0; // no nodes currently in buffer
359 scanState
->recordsFound
= recordsFound
;
360 microuptime(&scanState
->startTime
); // initialize our throttle
364 } /* BTScanInitialize */
367 //_________________________________________________________________________________
369 // Routine: BTScanTerminate
371 // Purpose: Return state information about a scan so that it can be resumed
372 // later via BTScanInitialize.
375 // scanState Scanner's current state
378 // nextNode Node number to resume a scan (pass to BTScanInitialize)
379 // nextRecord Record number to resume a scan (pass to BTScanInitialize)
380 // recordsFound Valid records seen so far (pass to BTScanInitialize)
381 //_________________________________________________________________________________
383 int BTScanTerminate( BTScanState
* scanState
,
384 u_int32_t
* startingNode
,
385 u_int32_t
* startingRecord
,
386 u_int32_t
* recordsFound
)
388 *startingNode
= scanState
->nodeNum
;
389 *startingRecord
= scanState
->recordNum
;
390 *recordsFound
= scanState
->recordsFound
;
392 if ( scanState
->bufferPtr
!= NULL
)
394 buf_markinvalid(scanState
->bufferPtr
);
395 buf_brelse( scanState
->bufferPtr
);
396 scanState
->bufferPtr
= NULL
;
397 scanState
->currentNodePtr
= NULL
;
402 } /* BTScanTerminate */