]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/BTree/BTreeScanner.c
66521dbbd247fd5cba9b763f75dc73a835db9ae6
[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
145 err = noErr; // Assume everything will be OK
146
147 while ( 1 )
148 {
149 if ( scanState->nodesLeftInBuffer == 0 )
150 {
151 // Time to read some more nodes into the buffer
152 if ( avoidIO )
153 {
154 return fsBTTimeOutErr;
155 }
156 else
157 {
158 // read some more nodes into buffer
159 err = ReadMultipleNodes( scanState );
160 if ( err != noErr )
161 break;
162 }
163 }
164 else
165 {
166 // Adjust the node counters and point to the next node in the buffer
167 ++scanState->nodeNum;
168 --scanState->nodesLeftInBuffer;
169
170 // If we've looked at all nodes in the tree, then we're done
171 if ( scanState->nodeNum >= scanState->btcb->totalNodes )
172 return fsEndOfIterationErr;
173
174 if ( scanState->nodesLeftInBuffer == 0 )
175 {
176 scanState->recordNum = 0;
177 continue;
178 }
179
180 (u_int8_t *) scanState->currentNodePtr += scanState->btcb->nodeSize;
181 }
182
183 #if BYTE_ORDER == LITTLE_ENDIAN
184 {
185 BlockDescriptor block;
186 FileReference fref;
187
188 /* Fake a BlockDescriptor */
189 block.buffer = scanState->currentNodePtr;
190 block.blockSize = scanState->btcb->nodeSize;
191 block.blockReadFromDisk = 1;
192 block.isModified = 0;
193
194 fref = scanState->btcb->fileRefNum;
195
196 SWAP_BT_NODE(&block, ISHFSPLUS(VTOVCB(fref)), VTOC(fref)->c_fileid, 0);
197 }
198 #endif
199
200 // Make sure this is a valid node
201 if ( CheckNode( scanState->btcb, scanState->currentNodePtr ) != noErr )
202 {
203 continue;
204 }
205
206 if ( scanState->currentNodePtr->kind == kBTLeafNode )
207 break;
208 }
209
210 return err;
211
212 } /* FindNextLeafNode */
213
214
215 //_________________________________________________________________________________
216 //
217 // Routine: ReadMultipleNodes
218 //
219 // Purpose: Read one or more nodes into the buffer.
220 //
221 // Inputs:
222 // theScanStatePtr Scanner's current state
223 //
224 // Result:
225 // noErr One or nodes were read
226 // fsEndOfIterationErr No nodes left in file, none in buffer
227 //_________________________________________________________________________________
228
229 static int ReadMultipleNodes( BTScanState *theScanStatePtr )
230 {
231 int myErr = E_NONE;
232 BTreeControlBlockPtr myBTreeCBPtr;
233 daddr64_t myPhyBlockNum;
234 u_int32_t myBufferSize;
235 struct vnode * myDevPtr;
236 int myBlockRun;
237 u_int32_t myBlocksInBufferCount;
238
239 // release old buffer if we have one
240 if ( theScanStatePtr->bufferPtr != NULL )
241 {
242 buf_markinvalid(theScanStatePtr->bufferPtr);
243 buf_brelse( theScanStatePtr->bufferPtr );
244 theScanStatePtr->bufferPtr = NULL;
245 theScanStatePtr->currentNodePtr = NULL;
246 }
247
248 myBTreeCBPtr = theScanStatePtr->btcb;
249
250 // map logical block in catalog btree file to physical block on volume
251 myErr = hfs_bmap(myBTreeCBPtr->fileRefNum, theScanStatePtr->nodeNum,
252 &myDevPtr, &myPhyBlockNum, &myBlockRun);
253 if ( myErr != E_NONE )
254 {
255 goto ExitThisRoutine;
256 }
257
258 // bmap block run gives us the remaining number of valid blocks (number of blocks
259 // minus the first). so if there are 10 valid blocks our run number will be 9.
260 // blocks, in our case is the same as nodes (both are 4K)
261 myBlocksInBufferCount = (theScanStatePtr->bufferSize / myBTreeCBPtr->nodeSize );
262 myBufferSize = theScanStatePtr->bufferSize;
263 if ( (myBlockRun + 1) < myBlocksInBufferCount )
264 {
265 myBufferSize = (myBlockRun + 1) * myBTreeCBPtr->nodeSize;
266 }
267
268 // now read blocks from the device
269 myErr = (int)buf_bread(myDevPtr,
270 myPhyBlockNum,
271 myBufferSize,
272 NOCRED,
273 &theScanStatePtr->bufferPtr );
274 if ( myErr != E_NONE )
275 {
276 goto ExitThisRoutine;
277 }
278
279 theScanStatePtr->nodesLeftInBuffer = buf_count(theScanStatePtr->bufferPtr) / theScanStatePtr->btcb->nodeSize;
280 theScanStatePtr->currentNodePtr = (BTNodeDescriptor *) buf_dataptr(theScanStatePtr->bufferPtr);
281
282 ExitThisRoutine:
283 return myErr;
284
285 } /* ReadMultipleNodes */
286
287
288
289 //_________________________________________________________________________________
290 //
291 // Routine: BTScanInitialize
292 //
293 // Purpose: Prepare to start a new BTree scan, or resume a previous one.
294 //
295 // Inputs:
296 // btreeFile The B-Tree's file control block
297 // startingNode Initial node number
298 // startingRecord Initial record number within node
299 // recordsFound Number of valid records found so far
300 // bufferSize Size (in bytes) of buffer
301 //
302 // Outputs:
303 // scanState Scanner's current state; pass to other scanner calls
304 //
305 // Notes:
306 // To begin a new scan and see all records in the B-Tree, pass zeroes for
307 // startingNode, startingRecord, and recordsFound.
308 //
309 // To resume a scan from the point of a previous BTScanTerminate, use the
310 // values returned by BTScanTerminate as input for startingNode, startingRecord,
311 // and recordsFound.
312 //
313 // When resuming a scan, the caller should check the B-tree's write count. If
314 // it is different from the write count when the scan was terminated, then the
315 // tree may have changed and the current state may be incorrect. In particular,
316 // you may see some records more than once, or never see some records. Also,
317 // the scanner may not be able to detect when all leaf records have been seen,
318 // and will have to scan through many empty nodes.
319 //
320 // XXXÊPerhaps the write count should be managed by BTScanInitialize and
321 // XXX BTScanTerminate? This would avoid the caller having to peek at
322 // XXX internal B-Tree structures.
323 //_________________________________________________________________________________
324
325 int BTScanInitialize( const FCB * btreeFile,
326 u_int32_t startingNode,
327 u_int32_t startingRecord,
328 u_int32_t recordsFound,
329 u_int32_t bufferSize,
330 BTScanState * scanState )
331 {
332 BTreeControlBlock *btcb;
333
334 //
335 // Make sure this is a valid B-Tree file
336 //
337 btcb = (BTreeControlBlock *) btreeFile->fcbBTCBPtr;
338 if (btcb == NULL)
339 return fsBTInvalidFileErr;
340
341 //
342 // Make sure buffer size is big enough, and a multiple of the
343 // B-Tree node size
344 //
345 if ( bufferSize < btcb->nodeSize )
346 return paramErr;
347 bufferSize = (bufferSize / btcb->nodeSize) * btcb->nodeSize;
348
349 //
350 // Set up the scanner's state
351 //
352 scanState->bufferSize = bufferSize;
353 scanState->bufferPtr = NULL;
354 scanState->btcb = btcb;
355 scanState->nodeNum = startingNode;
356 scanState->recordNum = startingRecord;
357 scanState->currentNodePtr = NULL;
358 scanState->nodesLeftInBuffer = 0; // no nodes currently in buffer
359 scanState->recordsFound = recordsFound;
360 microuptime(&scanState->startTime); // initialize our throttle
361
362 return noErr;
363
364 } /* BTScanInitialize */
365
366
367 //_________________________________________________________________________________
368 //
369 // Routine: BTScanTerminate
370 //
371 // Purpose: Return state information about a scan so that it can be resumed
372 // later via BTScanInitialize.
373 //
374 // Inputs:
375 // scanState Scanner's current state
376 //
377 // Outputs:
378 // nextNode Node number to resume a scan (pass to BTScanInitialize)
379 // nextRecord Record number to resume a scan (pass to BTScanInitialize)
380 // recordsFound Valid records seen so far (pass to BTScanInitialize)
381 //_________________________________________________________________________________
382
383 int BTScanTerminate( BTScanState * scanState,
384 u_int32_t * startingNode,
385 u_int32_t * startingRecord,
386 u_int32_t * recordsFound )
387 {
388 *startingNode = scanState->nodeNum;
389 *startingRecord = scanState->recordNum;
390 *recordsFound = scanState->recordsFound;
391
392 if ( scanState->bufferPtr != NULL )
393 {
394 buf_markinvalid(scanState->bufferPtr);
395 buf_brelse( scanState->bufferPtr );
396 scanState->bufferPtr = NULL;
397 scanState->currentNodePtr = NULL;
398 }
399
400 return noErr;
401
402 } /* BTScanTerminate */
403
404