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