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
)
144 BlockDescriptor block
;
147 err
= noErr
; // Assume everything will be OK
151 if ( scanState
->nodesLeftInBuffer
== 0 )
153 // Time to read some more nodes into the buffer
156 return fsBTTimeOutErr
;
160 // read some more nodes into buffer
161 err
= ReadMultipleNodes( scanState
);
168 // Adjust the node counters and point to the next node in the buffer
169 ++scanState
->nodeNum
;
170 --scanState
->nodesLeftInBuffer
;
172 // If we've looked at all nodes in the tree, then we're done
173 if ( scanState
->nodeNum
>= scanState
->btcb
->totalNodes
)
174 return fsEndOfIterationErr
;
176 if ( scanState
->nodesLeftInBuffer
== 0 )
178 scanState
->recordNum
= 0;
182 (u_int8_t
*) scanState
->currentNodePtr
+= scanState
->btcb
->nodeSize
;
185 /* Fake a BlockDescriptor */
186 block
.blockHeader
= NULL
; /* No buffer cache buffer */
187 block
.buffer
= scanState
->currentNodePtr
;
188 block
.blockNum
= scanState
->nodeNum
;
189 block
.blockSize
= scanState
->btcb
->nodeSize
;
190 block
.blockReadFromDisk
= 1;
191 block
.isModified
= 0;
193 fref
= scanState
->btcb
->fileRefNum
;
195 /* This node was read from disk, so it must be swapped/checked. */
196 err
= hfs_swap_BTNode(&block
, fref
, kSwapBTNodeBigToHost
);
197 if ( err
!= noErr
) {
198 printf("FindNextLeafNode: Error from hfs_swap_BTNode (node %u)\n", scanState
->nodeNum
);
202 if ( scanState
->currentNodePtr
->kind
== kBTLeafNode
)
208 } /* FindNextLeafNode */
211 //_________________________________________________________________________________
213 // Routine: ReadMultipleNodes
215 // Purpose: Read one or more nodes into the buffer.
218 // theScanStatePtr Scanner's current state
221 // noErr One or nodes were read
222 // fsEndOfIterationErr No nodes left in file, none in buffer
223 //_________________________________________________________________________________
225 static int ReadMultipleNodes( BTScanState
*theScanStatePtr
)
228 BTreeControlBlockPtr myBTreeCBPtr
;
229 daddr64_t myPhyBlockNum
;
230 u_int32_t myBufferSize
;
231 struct vnode
* myDevPtr
;
233 u_int32_t myBlocksInBufferCount
;
235 // release old buffer if we have one
236 if ( theScanStatePtr
->bufferPtr
!= NULL
)
238 buf_markinvalid(theScanStatePtr
->bufferPtr
);
239 buf_brelse( theScanStatePtr
->bufferPtr
);
240 theScanStatePtr
->bufferPtr
= NULL
;
241 theScanStatePtr
->currentNodePtr
= NULL
;
244 myBTreeCBPtr
= theScanStatePtr
->btcb
;
246 // map logical block in catalog btree file to physical block on volume
247 myErr
= hfs_bmap(myBTreeCBPtr
->fileRefNum
, theScanStatePtr
->nodeNum
,
248 &myDevPtr
, &myPhyBlockNum
, &myBlockRun
);
249 if ( myErr
!= E_NONE
)
251 goto ExitThisRoutine
;
254 // bmap block run gives us the remaining number of valid blocks (number of blocks
255 // minus the first). so if there are 10 valid blocks our run number will be 9.
256 // blocks, in our case is the same as nodes (both are 4K)
257 myBlocksInBufferCount
= (theScanStatePtr
->bufferSize
/ myBTreeCBPtr
->nodeSize
);
258 myBufferSize
= theScanStatePtr
->bufferSize
;
259 if ( (myBlockRun
+ 1) < myBlocksInBufferCount
)
261 myBufferSize
= (myBlockRun
+ 1) * myBTreeCBPtr
->nodeSize
;
264 // now read blocks from the device
265 myErr
= (int)buf_bread(myDevPtr
,
269 &theScanStatePtr
->bufferPtr
);
270 if ( myErr
!= E_NONE
)
272 goto ExitThisRoutine
;
275 theScanStatePtr
->nodesLeftInBuffer
= buf_count(theScanStatePtr
->bufferPtr
) / theScanStatePtr
->btcb
->nodeSize
;
276 theScanStatePtr
->currentNodePtr
= (BTNodeDescriptor
*) buf_dataptr(theScanStatePtr
->bufferPtr
);
281 } /* ReadMultipleNodes */
285 //_________________________________________________________________________________
287 // Routine: BTScanInitialize
289 // Purpose: Prepare to start a new BTree scan, or resume a previous one.
292 // btreeFile The B-Tree's file control block
293 // startingNode Initial node number
294 // startingRecord Initial record number within node
295 // recordsFound Number of valid records found so far
296 // bufferSize Size (in bytes) of buffer
299 // scanState Scanner's current state; pass to other scanner calls
302 // To begin a new scan and see all records in the B-Tree, pass zeroes for
303 // startingNode, startingRecord, and recordsFound.
305 // To resume a scan from the point of a previous BTScanTerminate, use the
306 // values returned by BTScanTerminate as input for startingNode, startingRecord,
309 // When resuming a scan, the caller should check the B-tree's write count. If
310 // it is different from the write count when the scan was terminated, then the
311 // tree may have changed and the current state may be incorrect. In particular,
312 // you may see some records more than once, or never see some records. Also,
313 // the scanner may not be able to detect when all leaf records have been seen,
314 // and will have to scan through many empty nodes.
316 // XXXĂPerhaps the write count should be managed by BTScanInitialize and
317 // XXX BTScanTerminate? This would avoid the caller having to peek at
318 // XXX internal B-Tree structures.
319 //_________________________________________________________________________________
321 int BTScanInitialize( const FCB
* btreeFile
,
322 u_int32_t startingNode
,
323 u_int32_t startingRecord
,
324 u_int32_t recordsFound
,
325 u_int32_t bufferSize
,
326 BTScanState
* scanState
)
328 BTreeControlBlock
*btcb
;
331 // Make sure this is a valid B-Tree file
333 btcb
= (BTreeControlBlock
*) btreeFile
->fcbBTCBPtr
;
335 return fsBTInvalidFileErr
;
338 // Make sure buffer size is big enough, and a multiple of the
341 if ( bufferSize
< btcb
->nodeSize
)
343 bufferSize
= (bufferSize
/ btcb
->nodeSize
) * btcb
->nodeSize
;
346 // Set up the scanner's state
348 scanState
->bufferSize
= bufferSize
;
349 scanState
->bufferPtr
= NULL
;
350 scanState
->btcb
= btcb
;
351 scanState
->nodeNum
= startingNode
;
352 scanState
->recordNum
= startingRecord
;
353 scanState
->currentNodePtr
= NULL
;
354 scanState
->nodesLeftInBuffer
= 0; // no nodes currently in buffer
355 scanState
->recordsFound
= recordsFound
;
356 microuptime(&scanState
->startTime
); // initialize our throttle
360 } /* BTScanInitialize */
363 //_________________________________________________________________________________
365 // Routine: BTScanTerminate
367 // Purpose: Return state information about a scan so that it can be resumed
368 // later via BTScanInitialize.
371 // scanState Scanner's current state
374 // nextNode Node number to resume a scan (pass to BTScanInitialize)
375 // nextRecord Record number to resume a scan (pass to BTScanInitialize)
376 // recordsFound Valid records seen so far (pass to BTScanInitialize)
377 //_________________________________________________________________________________
379 int BTScanTerminate( BTScanState
* scanState
,
380 u_int32_t
* startingNode
,
381 u_int32_t
* startingRecord
,
382 u_int32_t
* recordsFound
)
384 *startingNode
= scanState
->nodeNum
;
385 *startingRecord
= scanState
->recordNum
;
386 *recordsFound
= scanState
->recordsFound
;
388 if ( scanState
->bufferPtr
!= NULL
)
390 buf_markinvalid(scanState
->bufferPtr
);
391 buf_brelse( scanState
->bufferPtr
);
392 scanState
->bufferPtr
= NULL
;
393 scanState
->currentNodePtr
= NULL
;
398 } /* BTScanTerminate */