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