]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/BTree/BTreeScanner.c
xnu-792.6.61.tar.gz
[apple/xnu.git] / bsd / hfs / hfscommon / BTree / BTreeScanner.c
1 /*
2 * Copyright (c) 1996-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 *
22 * @(#)BTreeScanner.c
23 */
24 #include <sys/kernel.h>
25 #include "../../hfs_endian.h"
26
27 #include "../headers/BTreeScanner.h"
28
29 static int FindNextLeafNode( BTScanState *scanState, Boolean avoidIO );
30 static int ReadMultipleNodes( BTScanState *scanState );
31
32
33 //_________________________________________________________________________________
34 //
35 // Routine: BTScanNextRecord
36 //
37 // Purpose: Return the next leaf record in a scan.
38 //
39 // Inputs:
40 // scanState Scanner's current state
41 // avoidIO If true, don't do any I/O to refill the buffer
42 //
43 // Outputs:
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
47 //
48 // Result:
49 // noErr Found a valid record
50 // btNotFound No more records
51 // ??? Needed to do I/O to get next node, but avoidIO set
52 //
53 // Notes:
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 //_________________________________________________________________________________
58
59 int BTScanNextRecord( BTScanState * scanState,
60 Boolean avoidIO,
61 void * * key,
62 void * * data,
63 u_int32_t * dataSize )
64 {
65 int err;
66 u_int16_t dataSizeShort;
67
68 err = noErr;
69
70 //
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).
73 //
74 if ( scanState->nodesLeftInBuffer == 0 )
75 {
76 err = FindNextLeafNode( scanState, avoidIO );
77 }
78
79 while ( err == noErr )
80 {
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 );
85
86 if ( err == noErr )
87 {
88 ++scanState->recordsFound;
89 ++scanState->recordNum;
90 if (dataSize != NULL)
91 *dataSize = dataSizeShort;
92 return noErr;
93 }
94 else if (err > 0)
95 {
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?
98 return err;
99 }
100
101 // We're done with the current node. See if we've returned all the records
102 if ( scanState->recordsFound >= scanState->btcb->leafRecords )
103 {
104 return btNotFound;
105 }
106
107 // Move to the first record of the next leaf node
108 scanState->recordNum = 0;
109 err = FindNextLeafNode( scanState, avoidIO );
110 }
111
112 //
113 // If we got an EOF error from FindNextLeafNode, then there are no more leaf
114 // records to be found.
115 //
116 if ( err == fsEndOfIterationErr )
117 err = btNotFound;
118
119 return err;
120
121 } /* BTScanNextRecord */
122
123
124 //_________________________________________________________________________________
125 //
126 // Routine: FindNextLeafNode
127 //
128 // Purpose: Point to the next leaf node in the buffer. Read more nodes
129 // into the buffer if needed (and allowed).
130 //
131 // Inputs:
132 // scanState Scanner's current state
133 // avoidIO If true, don't do any I/O to refill the buffer
134 //
135 // Result:
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 //_________________________________________________________________________________
140
141 static int FindNextLeafNode( BTScanState *scanState, Boolean avoidIO )
142 {
143 int err;
144 BlockDescriptor block;
145 FileReference fref;
146
147 err = noErr; // Assume everything will be OK
148
149 while ( 1 )
150 {
151 if ( scanState->nodesLeftInBuffer == 0 )
152 {
153 // Time to read some more nodes into the buffer
154 if ( avoidIO )
155 {
156 return fsBTTimeOutErr;
157 }
158 else
159 {
160 // read some more nodes into buffer
161 err = ReadMultipleNodes( scanState );
162 if ( err != noErr )
163 break;
164 }
165 }
166 else
167 {
168 // Adjust the node counters and point to the next node in the buffer
169 ++scanState->nodeNum;
170 --scanState->nodesLeftInBuffer;
171
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;
175
176 if ( scanState->nodesLeftInBuffer == 0 )
177 {
178 scanState->recordNum = 0;
179 continue;
180 }
181
182 (u_int8_t *) scanState->currentNodePtr += scanState->btcb->nodeSize;
183 }
184
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;
192
193 fref = scanState->btcb->fileRefNum;
194
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);
199 continue;
200 }
201
202 if ( scanState->currentNodePtr->kind == kBTLeafNode )
203 break;
204 }
205
206 return err;
207
208 } /* FindNextLeafNode */
209
210
211 //_________________________________________________________________________________
212 //
213 // Routine: ReadMultipleNodes
214 //
215 // Purpose: Read one or more nodes into the buffer.
216 //
217 // Inputs:
218 // theScanStatePtr Scanner's current state
219 //
220 // Result:
221 // noErr One or nodes were read
222 // fsEndOfIterationErr No nodes left in file, none in buffer
223 //_________________________________________________________________________________
224
225 static int ReadMultipleNodes( BTScanState *theScanStatePtr )
226 {
227 int myErr = E_NONE;
228 BTreeControlBlockPtr myBTreeCBPtr;
229 daddr64_t myPhyBlockNum;
230 u_int32_t myBufferSize;
231 struct vnode * myDevPtr;
232 int myBlockRun;
233 u_int32_t myBlocksInBufferCount;
234
235 // release old buffer if we have one
236 if ( theScanStatePtr->bufferPtr != NULL )
237 {
238 buf_markinvalid(theScanStatePtr->bufferPtr);
239 buf_brelse( theScanStatePtr->bufferPtr );
240 theScanStatePtr->bufferPtr = NULL;
241 theScanStatePtr->currentNodePtr = NULL;
242 }
243
244 myBTreeCBPtr = theScanStatePtr->btcb;
245
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 )
250 {
251 goto ExitThisRoutine;
252 }
253
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 )
260 {
261 myBufferSize = (myBlockRun + 1) * myBTreeCBPtr->nodeSize;
262 }
263
264 // now read blocks from the device
265 myErr = (int)buf_bread(myDevPtr,
266 myPhyBlockNum,
267 myBufferSize,
268 NOCRED,
269 &theScanStatePtr->bufferPtr );
270 if ( myErr != E_NONE )
271 {
272 goto ExitThisRoutine;
273 }
274
275 theScanStatePtr->nodesLeftInBuffer = buf_count(theScanStatePtr->bufferPtr) / theScanStatePtr->btcb->nodeSize;
276 theScanStatePtr->currentNodePtr = (BTNodeDescriptor *) buf_dataptr(theScanStatePtr->bufferPtr);
277
278 ExitThisRoutine:
279 return myErr;
280
281 } /* ReadMultipleNodes */
282
283
284
285 //_________________________________________________________________________________
286 //
287 // Routine: BTScanInitialize
288 //
289 // Purpose: Prepare to start a new BTree scan, or resume a previous one.
290 //
291 // Inputs:
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
297 //
298 // Outputs:
299 // scanState Scanner's current state; pass to other scanner calls
300 //
301 // Notes:
302 // To begin a new scan and see all records in the B-Tree, pass zeroes for
303 // startingNode, startingRecord, and recordsFound.
304 //
305 // To resume a scan from the point of a previous BTScanTerminate, use the
306 // values returned by BTScanTerminate as input for startingNode, startingRecord,
307 // and recordsFound.
308 //
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.
315 //
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 //_________________________________________________________________________________
320
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 )
327 {
328 BTreeControlBlock *btcb;
329
330 //
331 // Make sure this is a valid B-Tree file
332 //
333 btcb = (BTreeControlBlock *) btreeFile->fcbBTCBPtr;
334 if (btcb == NULL)
335 return fsBTInvalidFileErr;
336
337 //
338 // Make sure buffer size is big enough, and a multiple of the
339 // B-Tree node size
340 //
341 if ( bufferSize < btcb->nodeSize )
342 return paramErr;
343 bufferSize = (bufferSize / btcb->nodeSize) * btcb->nodeSize;
344
345 //
346 // Set up the scanner's state
347 //
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
357
358 return noErr;
359
360 } /* BTScanInitialize */
361
362
363 //_________________________________________________________________________________
364 //
365 // Routine: BTScanTerminate
366 //
367 // Purpose: Return state information about a scan so that it can be resumed
368 // later via BTScanInitialize.
369 //
370 // Inputs:
371 // scanState Scanner's current state
372 //
373 // Outputs:
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 //_________________________________________________________________________________
378
379 int BTScanTerminate( BTScanState * scanState,
380 u_int32_t * startingNode,
381 u_int32_t * startingRecord,
382 u_int32_t * recordsFound )
383 {
384 *startingNode = scanState->nodeNum;
385 *startingRecord = scanState->recordNum;
386 *recordsFound = scanState->recordsFound;
387
388 if ( scanState->bufferPtr != NULL )
389 {
390 buf_markinvalid(scanState->bufferPtr);
391 buf_brelse( scanState->bufferPtr );
392 scanState->bufferPtr = NULL;
393 scanState->currentNodePtr = NULL;
394 }
395
396 return noErr;
397
398 } /* BTScanTerminate */
399
400