2 * Copyright (c) 1996-2015 Apple 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 "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 * Since we are reading multiple nodes, we might have read an
204 * unused node. Therefore we allow swapping of unused nodes.
206 err
= hfs_swap_BTNode(&block
, fref
, kSwapBTNodeBigToHost
, true);
207 if ( err
!= noErr
) {
208 printf("hfs: FindNextLeafNode: Error from hfs_swap_BTNode (node %u)\n", scanState
->nodeNum
);
212 if ( scanState
->currentNodePtr
->kind
== kBTLeafNode
)
218 } /* FindNextLeafNode */
221 //_________________________________________________________________________________
223 // Routine: ReadMultipleNodes
225 // Purpose: Read one or more nodes into the buffer.
228 // theScanStatePtr Scanner's current state
231 // noErr One or nodes were read
232 // fsEndOfIterationErr No nodes left in file, none in buffer
233 //_________________________________________________________________________________
235 static int ReadMultipleNodes( BTScanState
*theScanStatePtr
)
238 BTreeControlBlockPtr myBTreeCBPtr
;
239 daddr64_t myPhyBlockNum
;
240 u_int32_t myBufferSize
;
241 struct vnode
* myDevPtr
;
242 unsigned int myBlockRun
;
243 u_int32_t myBlocksInBufferCount
;
245 // release old buffer if we have one
246 if ( theScanStatePtr
->bufferPtr
!= NULL
)
248 buf_markinvalid(theScanStatePtr
->bufferPtr
);
249 buf_brelse( theScanStatePtr
->bufferPtr
);
250 theScanStatePtr
->bufferPtr
= NULL
;
251 theScanStatePtr
->currentNodePtr
= NULL
;
254 myBTreeCBPtr
= theScanStatePtr
->btcb
;
256 // map logical block in catalog btree file to physical block on volume
257 myErr
= hfs_bmap(myBTreeCBPtr
->fileRefNum
, theScanStatePtr
->nodeNum
,
258 &myDevPtr
, &myPhyBlockNum
, &myBlockRun
);
259 if ( myErr
!= E_NONE
)
261 goto ExitThisRoutine
;
264 // bmap block run gives us the remaining number of valid blocks (number of blocks
265 // minus the first). so if there are 10 valid blocks our run number will be 9.
266 // blocks, in our case is the same as nodes (both are 4K)
267 myBlocksInBufferCount
= (theScanStatePtr
->bufferSize
/ myBTreeCBPtr
->nodeSize
);
268 myBufferSize
= theScanStatePtr
->bufferSize
;
269 if ( (myBlockRun
+ 1) < myBlocksInBufferCount
)
271 myBufferSize
= (myBlockRun
+ 1) * myBTreeCBPtr
->nodeSize
;
274 // now read blocks from the device
275 myErr
= (int)buf_meta_bread(myDevPtr
,
279 &theScanStatePtr
->bufferPtr
);
280 if ( myErr
!= E_NONE
)
282 goto ExitThisRoutine
;
285 theScanStatePtr
->nodesLeftInBuffer
= buf_count(theScanStatePtr
->bufferPtr
) / theScanStatePtr
->btcb
->nodeSize
;
286 theScanStatePtr
->currentNodePtr
= (BTNodeDescriptor
*) buf_dataptr(theScanStatePtr
->bufferPtr
);
291 } /* ReadMultipleNodes */
295 //_________________________________________________________________________________
297 // Routine: BTScanInitialize
299 // Purpose: Prepare to start a new BTree scan, or resume a previous one.
302 // btreeFile The B-Tree's file control block
303 // startingNode Initial node number
304 // startingRecord Initial record number within node
305 // recordsFound Number of valid records found so far
306 // bufferSize Size (in bytes) of buffer
309 // scanState Scanner's current state; pass to other scanner calls
312 // To begin a new scan and see all records in the B-Tree, pass zeroes for
313 // startingNode, startingRecord, and recordsFound.
315 // To resume a scan from the point of a previous BTScanTerminate, use the
316 // values returned by BTScanTerminate as input for startingNode, startingRecord,
319 // When resuming a scan, the caller should check the B-tree's write count. If
320 // it is different from the write count when the scan was terminated, then the
321 // tree may have changed and the current state may be incorrect. In particular,
322 // you may see some records more than once, or never see some records. Also,
323 // the scanner may not be able to detect when all leaf records have been seen,
324 // and will have to scan through many empty nodes.
326 // XXXĂPerhaps the write count should be managed by BTScanInitialize and
327 // XXX BTScanTerminate? This would avoid the caller having to peek at
328 // XXX internal B-Tree structures.
329 //_________________________________________________________________________________
331 int BTScanInitialize( const FCB
* btreeFile
,
332 u_int32_t startingNode
,
333 u_int32_t startingRecord
,
334 u_int32_t recordsFound
,
335 u_int32_t bufferSize
,
336 BTScanState
* scanState
)
338 BTreeControlBlock
*btcb
;
341 // Make sure this is a valid B-Tree file
343 btcb
= (BTreeControlBlock
*) btreeFile
->fcbBTCBPtr
;
345 return fsBTInvalidFileErr
;
348 // Make sure buffer size is big enough, and a multiple of the
351 if ( bufferSize
< btcb
->nodeSize
)
353 bufferSize
= (bufferSize
/ btcb
->nodeSize
) * btcb
->nodeSize
;
356 // Set up the scanner's state
358 scanState
->bufferSize
= bufferSize
;
359 scanState
->bufferPtr
= NULL
;
360 scanState
->btcb
= btcb
;
361 scanState
->nodeNum
= startingNode
;
362 scanState
->recordNum
= startingRecord
;
363 scanState
->currentNodePtr
= NULL
;
364 scanState
->nodesLeftInBuffer
= 0; // no nodes currently in buffer
365 scanState
->recordsFound
= recordsFound
;
366 microuptime(&scanState
->startTime
); // initialize our throttle
370 } /* BTScanInitialize */
373 //_________________________________________________________________________________
375 // Routine: BTScanTerminate
377 // Purpose: Return state information about a scan so that it can be resumed
378 // later via BTScanInitialize.
381 // scanState Scanner's current state
384 // nextNode Node number to resume a scan (pass to BTScanInitialize)
385 // nextRecord Record number to resume a scan (pass to BTScanInitialize)
386 // recordsFound Valid records seen so far (pass to BTScanInitialize)
387 //_________________________________________________________________________________
389 int BTScanTerminate( BTScanState
* scanState
,
390 u_int32_t
* startingNode
,
391 u_int32_t
* startingRecord
,
392 u_int32_t
* recordsFound
)
394 *startingNode
= scanState
->nodeNum
;
395 *startingRecord
= scanState
->recordNum
;
396 *recordsFound
= scanState
->recordsFound
;
398 if ( scanState
->bufferPtr
!= NULL
)
400 buf_markinvalid(scanState
->bufferPtr
);
401 buf_brelse( scanState
->bufferPtr
);
402 scanState
->bufferPtr
= NULL
;
403 scanState
->currentNodePtr
= NULL
;
408 } /* BTScanTerminate */