]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/BTree/BTreeScanner.c
37d574894dc13f9ccb84add72ca85c684f9fb70e
[apple/xnu.git] / bsd / hfs / hfscommon / BTree / BTreeScanner.c
1 /*
2 * Copyright (c) 1996-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 *
28 * @(#)BTreeScanner.c
29 */
30 #include <sys/kernel.h>
31 #include "../../hfs_endian.h"
32
33 #include "../headers/BTreeScanner.h"
34
35 static int FindNextLeafNode( BTScanState *scanState, Boolean avoidIO );
36 static int ReadMultipleNodes( BTScanState *scanState );
37
38
39 //_________________________________________________________________________________
40 //
41 // Routine: BTScanNextRecord
42 //
43 // Purpose: Return the next leaf record in a scan.
44 //
45 // Inputs:
46 // scanState Scanner's current state
47 // avoidIO If true, don't do any I/O to refill the buffer
48 //
49 // Outputs:
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
53 //
54 // Result:
55 // noErr Found a valid record
56 // btNotFound No more records
57 // ??? Needed to do I/O to get next node, but avoidIO set
58 //
59 // Notes:
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 //_________________________________________________________________________________
64
65 int BTScanNextRecord( BTScanState * scanState,
66 Boolean avoidIO,
67 void * * key,
68 void * * data,
69 u_int32_t * dataSize )
70 {
71 int err;
72 u_int16_t dataSizeShort;
73
74 err = noErr;
75
76 //
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).
79 //
80 if ( scanState->nodesLeftInBuffer == 0 )
81 {
82 err = FindNextLeafNode( scanState, avoidIO );
83 }
84
85 while ( err == noErr )
86 {
87 // See if we have a record in the current node
88 err = GetRecordByIndex( scanState->btcb, scanState->currentNodePtr,
89 scanState->recordNum, (KeyPtr *) key,
90 (UInt8 **) data, &dataSizeShort );
91
92 if ( err == noErr )
93 {
94 ++scanState->recordsFound;
95 ++scanState->recordNum;
96 if (dataSize != NULL)
97 *dataSize = dataSizeShort;
98 return noErr;
99 }
100 else if (err > 0)
101 {
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?
104 return err;
105 }
106
107 // We're done with the current node. See if we've returned all the records
108 if ( scanState->recordsFound >= scanState->btcb->leafRecords )
109 {
110 return btNotFound;
111 }
112
113 // Move to the first record of the next leaf node
114 scanState->recordNum = 0;
115 err = FindNextLeafNode( scanState, avoidIO );
116 }
117
118 //
119 // If we got an EOF error from FindNextLeafNode, then there are no more leaf
120 // records to be found.
121 //
122 if ( err == fsEndOfIterationErr )
123 err = btNotFound;
124
125 return err;
126
127 } /* BTScanNextRecord */
128
129
130 //_________________________________________________________________________________
131 //
132 // Routine: FindNextLeafNode
133 //
134 // Purpose: Point to the next leaf node in the buffer. Read more nodes
135 // into the buffer if needed (and allowed).
136 //
137 // Inputs:
138 // scanState Scanner's current state
139 // avoidIO If true, don't do any I/O to refill the buffer
140 //
141 // Result:
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 //_________________________________________________________________________________
146
147 static int FindNextLeafNode( BTScanState *scanState, Boolean avoidIO )
148 {
149 int err;
150 BlockDescriptor block;
151 FileReference fref;
152
153 err = noErr; // Assume everything will be OK
154
155 while ( 1 )
156 {
157 if ( scanState->nodesLeftInBuffer == 0 )
158 {
159 // Time to read some more nodes into the buffer
160 if ( avoidIO )
161 {
162 return fsBTTimeOutErr;
163 }
164 else
165 {
166 // read some more nodes into buffer
167 err = ReadMultipleNodes( scanState );
168 if ( err != noErr )
169 break;
170 }
171 }
172 else
173 {
174 // Adjust the node counters and point to the next node in the buffer
175 ++scanState->nodeNum;
176 --scanState->nodesLeftInBuffer;
177
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;
181
182 if ( scanState->nodesLeftInBuffer == 0 )
183 {
184 scanState->recordNum = 0;
185 continue;
186 }
187
188 (u_int8_t *) scanState->currentNodePtr += scanState->btcb->nodeSize;
189 }
190
191 /* Fake a BlockDescriptor */
192 block.blockHeader = NULL; /* No buffer cache buffer */
193 block.buffer = scanState->currentNodePtr;
194 block.blockNum = scanState->nodeNum;
195 block.blockSize = scanState->btcb->nodeSize;
196 block.blockReadFromDisk = 1;
197 block.isModified = 0;
198
199 fref = scanState->btcb->fileRefNum;
200
201 /* This node was read from disk, so it must be swapped/checked. */
202 err = hfs_swap_BTNode(&block, fref, kSwapBTNodeBigToHost);
203 if ( err != noErr ) {
204 printf("FindNextLeafNode: Error from hfs_swap_BTNode (node %u)\n", scanState->nodeNum);
205 continue;
206 }
207
208 if ( scanState->currentNodePtr->kind == kBTLeafNode )
209 break;
210 }
211
212 return err;
213
214 } /* FindNextLeafNode */
215
216
217 //_________________________________________________________________________________
218 //
219 // Routine: ReadMultipleNodes
220 //
221 // Purpose: Read one or more nodes into the buffer.
222 //
223 // Inputs:
224 // theScanStatePtr Scanner's current state
225 //
226 // Result:
227 // noErr One or nodes were read
228 // fsEndOfIterationErr No nodes left in file, none in buffer
229 //_________________________________________________________________________________
230
231 static int ReadMultipleNodes( BTScanState *theScanStatePtr )
232 {
233 int myErr = E_NONE;
234 BTreeControlBlockPtr myBTreeCBPtr;
235 daddr64_t myPhyBlockNum;
236 u_int32_t myBufferSize;
237 struct vnode * myDevPtr;
238 int myBlockRun;
239 u_int32_t myBlocksInBufferCount;
240
241 // release old buffer if we have one
242 if ( theScanStatePtr->bufferPtr != NULL )
243 {
244 buf_markinvalid(theScanStatePtr->bufferPtr);
245 buf_brelse( theScanStatePtr->bufferPtr );
246 theScanStatePtr->bufferPtr = NULL;
247 theScanStatePtr->currentNodePtr = NULL;
248 }
249
250 myBTreeCBPtr = theScanStatePtr->btcb;
251
252 // map logical block in catalog btree file to physical block on volume
253 myErr = hfs_bmap(myBTreeCBPtr->fileRefNum, theScanStatePtr->nodeNum,
254 &myDevPtr, &myPhyBlockNum, &myBlockRun);
255 if ( myErr != E_NONE )
256 {
257 goto ExitThisRoutine;
258 }
259
260 // bmap block run gives us the remaining number of valid blocks (number of blocks
261 // minus the first). so if there are 10 valid blocks our run number will be 9.
262 // blocks, in our case is the same as nodes (both are 4K)
263 myBlocksInBufferCount = (theScanStatePtr->bufferSize / myBTreeCBPtr->nodeSize );
264 myBufferSize = theScanStatePtr->bufferSize;
265 if ( (myBlockRun + 1) < myBlocksInBufferCount )
266 {
267 myBufferSize = (myBlockRun + 1) * myBTreeCBPtr->nodeSize;
268 }
269
270 // now read blocks from the device
271 myErr = (int)buf_bread(myDevPtr,
272 myPhyBlockNum,
273 myBufferSize,
274 NOCRED,
275 &theScanStatePtr->bufferPtr );
276 if ( myErr != E_NONE )
277 {
278 goto ExitThisRoutine;
279 }
280
281 theScanStatePtr->nodesLeftInBuffer = buf_count(theScanStatePtr->bufferPtr) / theScanStatePtr->btcb->nodeSize;
282 theScanStatePtr->currentNodePtr = (BTNodeDescriptor *) buf_dataptr(theScanStatePtr->bufferPtr);
283
284 ExitThisRoutine:
285 return myErr;
286
287 } /* ReadMultipleNodes */
288
289
290
291 //_________________________________________________________________________________
292 //
293 // Routine: BTScanInitialize
294 //
295 // Purpose: Prepare to start a new BTree scan, or resume a previous one.
296 //
297 // Inputs:
298 // btreeFile The B-Tree's file control block
299 // startingNode Initial node number
300 // startingRecord Initial record number within node
301 // recordsFound Number of valid records found so far
302 // bufferSize Size (in bytes) of buffer
303 //
304 // Outputs:
305 // scanState Scanner's current state; pass to other scanner calls
306 //
307 // Notes:
308 // To begin a new scan and see all records in the B-Tree, pass zeroes for
309 // startingNode, startingRecord, and recordsFound.
310 //
311 // To resume a scan from the point of a previous BTScanTerminate, use the
312 // values returned by BTScanTerminate as input for startingNode, startingRecord,
313 // and recordsFound.
314 //
315 // When resuming a scan, the caller should check the B-tree's write count. If
316 // it is different from the write count when the scan was terminated, then the
317 // tree may have changed and the current state may be incorrect. In particular,
318 // you may see some records more than once, or never see some records. Also,
319 // the scanner may not be able to detect when all leaf records have been seen,
320 // and will have to scan through many empty nodes.
321 //
322 // XXXÊPerhaps the write count should be managed by BTScanInitialize and
323 // XXX BTScanTerminate? This would avoid the caller having to peek at
324 // XXX internal B-Tree structures.
325 //_________________________________________________________________________________
326
327 int BTScanInitialize( const FCB * btreeFile,
328 u_int32_t startingNode,
329 u_int32_t startingRecord,
330 u_int32_t recordsFound,
331 u_int32_t bufferSize,
332 BTScanState * scanState )
333 {
334 BTreeControlBlock *btcb;
335
336 //
337 // Make sure this is a valid B-Tree file
338 //
339 btcb = (BTreeControlBlock *) btreeFile->fcbBTCBPtr;
340 if (btcb == NULL)
341 return fsBTInvalidFileErr;
342
343 //
344 // Make sure buffer size is big enough, and a multiple of the
345 // B-Tree node size
346 //
347 if ( bufferSize < btcb->nodeSize )
348 return paramErr;
349 bufferSize = (bufferSize / btcb->nodeSize) * btcb->nodeSize;
350
351 //
352 // Set up the scanner's state
353 //
354 scanState->bufferSize = bufferSize;
355 scanState->bufferPtr = NULL;
356 scanState->btcb = btcb;
357 scanState->nodeNum = startingNode;
358 scanState->recordNum = startingRecord;
359 scanState->currentNodePtr = NULL;
360 scanState->nodesLeftInBuffer = 0; // no nodes currently in buffer
361 scanState->recordsFound = recordsFound;
362 microuptime(&scanState->startTime); // initialize our throttle
363
364 return noErr;
365
366 } /* BTScanInitialize */
367
368
369 //_________________________________________________________________________________
370 //
371 // Routine: BTScanTerminate
372 //
373 // Purpose: Return state information about a scan so that it can be resumed
374 // later via BTScanInitialize.
375 //
376 // Inputs:
377 // scanState Scanner's current state
378 //
379 // Outputs:
380 // nextNode Node number to resume a scan (pass to BTScanInitialize)
381 // nextRecord Record number to resume a scan (pass to BTScanInitialize)
382 // recordsFound Valid records seen so far (pass to BTScanInitialize)
383 //_________________________________________________________________________________
384
385 int BTScanTerminate( BTScanState * scanState,
386 u_int32_t * startingNode,
387 u_int32_t * startingRecord,
388 u_int32_t * recordsFound )
389 {
390 *startingNode = scanState->nodeNum;
391 *startingRecord = scanState->recordNum;
392 *recordsFound = scanState->recordsFound;
393
394 if ( scanState->bufferPtr != NULL )
395 {
396 buf_markinvalid(scanState->bufferPtr);
397 buf_brelse( scanState->bufferPtr );
398 scanState->bufferPtr = NULL;
399 scanState->currentNodePtr = NULL;
400 }
401
402 return noErr;
403
404 } /* BTScanTerminate */
405
406