]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/hfs/hfscommon/BTree/BTreeScanner.c
xnu-517.12.7.tar.gz
[apple/xnu.git] / bsd / hfs / hfscommon / BTree / BTreeScanner.c
... / ...
CommitLineData
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#include "../../hfs_endian.h"
26
27#include "../headers/BTreeScanner.h"
28
29static int FindNextLeafNode( BTScanState *scanState, Boolean avoidIO );
30static 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
59int 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
141static 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
229static int ReadMultipleNodes( BTScanState *theScanStatePtr )
230{
231 int myErr = E_NONE;
232 BTreeControlBlockPtr myBTreeCBPtr;
233 daddr_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 theScanStatePtr->bufferPtr->b_flags |= (B_INVAL | B_AGE);
243 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 = VOP_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 = 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 = theScanStatePtr->bufferPtr->b_bcount / theScanStatePtr->btcb->nodeSize;
280 theScanStatePtr->currentNodePtr = (BTNodeDescriptor *) theScanStatePtr->bufferPtr->b_data;
281
282ExitThisRoutine:
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