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