]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2002-2005, 2007-2009 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_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. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | /* | |
24 | File: SRebuildBTree.c | |
25 | ||
26 | Contains: This file contains BTree rebuilding code. | |
27 | ||
28 | Written by: Jerry Cottingham | |
29 | ||
30 | Copyright: � 1986, 1990, 1992-2002 by Apple Computer, Inc., all rights reserved. | |
31 | ||
32 | */ | |
33 | ||
34 | #define SHOW_ELAPSED_TIMES 0 | |
35 | #define DEBUG_REBUILD 0 | |
36 | ||
37 | extern void MyIndirectLog(const char *); | |
38 | ||
39 | #if SHOW_ELAPSED_TIMES | |
40 | #include <sys/time.h> | |
41 | #endif | |
42 | ||
43 | #include "Scavenger.h" | |
44 | #include "../cache.h" | |
45 | ||
46 | /* internal routine prototypes */ | |
47 | ||
48 | /*static*/ OSErr CreateNewBTree( SGlobPtr theSGlobPtr, int FileID ); | |
49 | static OSErr DeleteBTree( SGlobPtr theSGlobPtr, SFCB * theFCBPtr ); | |
50 | static OSErr InitializeBTree( BTreeControlBlock * theBTreeCBPtr, | |
51 | UInt32 * theBytesUsedPtr, | |
52 | UInt32 * theMapNodeCountPtr ); | |
53 | static OSErr ReleaseExtentsInExtentsBTree( SGlobPtr theSGlobPtr, | |
54 | SFCB * theFCBPtr ); | |
55 | static OSErr ValidateCatalogRecordLength( SGlobPtr theSGlobPtr, | |
56 | CatalogRecord * theRecPtr, | |
57 | UInt32 theRecSize ); | |
58 | static OSErr ValidateAttributeRecordLength (SGlobPtr s, HFSPlusAttrRecord * theRecPtr, UInt32 theRecSize); | |
59 | static OSErr ValidateExtentRecordLength (SGlobPtr s, ExtentRecord * theRecPtr, UInt32 theRecSize); | |
60 | static OSErr WriteMapNodes( BTreeControlBlock * theBTreeCBPtr, | |
61 | UInt32 theFirstMapNode, | |
62 | UInt32 theNodeCount ); | |
63 | ||
64 | #if DEBUG_REBUILD | |
65 | static void PrintBTHeaderRec( BTHeaderRec * thePtr ); | |
66 | static void PrintNodeDescriptor( NodeDescPtr thePtr ); | |
67 | static void PrintBTreeKey( KeyPtr thePtr, BTreeControlBlock * theBTreeCBPtr ); | |
68 | static void PrintBTreeData(void *data, UInt32 length); | |
69 | static void PrintIndexNodeRec( UInt32 theNodeNum ); | |
70 | static void PrintLeafNodeRec( HFSPlusCatalogFolder * thePtr ); | |
71 | #endif | |
72 | ||
73 | void SETOFFSET ( void *buffer, UInt16 btNodeSize, SInt16 recOffset, SInt16 vecOffset ); | |
74 | #define SETOFFSET( buf,ndsiz,offset,rec ) \ | |
75 | ( *(SInt16 *)((UInt8 *)(buf) + (ndsiz) + (-2 * (rec))) = (offset) ) | |
76 | ||
77 | ||
78 | //_________________________________________________________________________________ | |
79 | // | |
80 | // Routine: RebuildBTree | |
81 | // | |
82 | // Purpose: Attempt to rebuild a B-Tree file using an existing B-Tree | |
83 | // file. When successful a new BT-ree file will exist and | |
84 | // the old one will be deleted. The MDB an alternate MDB | |
85 | // will be updated to point to the new file. | |
86 | // | |
87 | // The tree is rebuilt by walking through every record. We use | |
88 | // BTScanNextRecord(), which iterates sequentially through the | |
89 | // nodes in the tree (starting at the first node), and extracts | |
90 | // each record from each leaf node. It does not use the node | |
91 | // forward or backward links; this allows it to rebuild the tree | |
92 | // when the index nodes are non-reliable, or the leaf node links | |
93 | // are damaged. | |
94 | // | |
95 | // The rebuild will be aborted (leaving the existing btree | |
96 | // as it was found) if there are errors retreiving the nodes or | |
97 | // records, or if there are errors inserting the records into | |
98 | // the new tree. | |
99 | // | |
100 | // Inputs: | |
101 | // SGlobPtr->calculatedCatalogBTCB or SGlobPtr->calculatedAttributesBTCB | |
102 | // need this as a model and in order to extract leaf records. | |
103 | // SGlobPtr->calculatedCatalogFCB or SGlobPtr->calculatedAttributesFCB | |
104 | // need this as a model and in order to extract leaf records. | |
105 | // SGlobPtr->calculatedRepairFCB | |
106 | // pointer to our repair FCB. | |
107 | // SGlobPtr->calculatedRepairBTCB | |
108 | // pointer to our repair BTreeControlBlock. | |
109 | // | |
110 | // Outputs: | |
111 | // SGlobPtr->calculatedVCB | |
112 | // this will get mostly filled in here. On input it is not fully | |
113 | // set up. | |
114 | // SGlobPtr->calculatedCatalogFCB or SGlobPtr->calculatedAttributesFCB | |
115 | // tis will refer to the new catalog file. | |
116 | // | |
117 | // Result: | |
118 | // various error codes when problem occur or noErr if rebuild completed | |
119 | // | |
120 | // to do: | |
121 | // have an option where we get back what we can. | |
122 | // | |
123 | // Notes: | |
124 | // - requires input BTCB and FCBs to be valid! | |
125 | //_________________________________________________________________________________ | |
126 | ||
127 | OSErr RebuildBTree( SGlobPtr theSGlobPtr, int FileID ) | |
128 | { | |
129 | BlockDescriptor myBlockDescriptor; | |
130 | BTreeKeyPtr myCurrentKeyPtr; | |
131 | void * myCurrentDataPtr; | |
132 | SFCB * myFCBPtr = NULL; | |
133 | SFCB * oldFCBPtr = NULL; | |
134 | SVCB * myVCBPtr; | |
135 | UInt32 myDataSize; | |
136 | UInt32 myHint; | |
137 | OSErr myErr; | |
138 | Boolean isHFSPlus; | |
139 | UInt32 numRecords = 0; | |
140 | ||
141 | #if SHOW_ELAPSED_TIMES | |
142 | struct timeval myStartTime; | |
143 | struct timeval myEndTime; | |
144 | struct timeval myElapsedTime; | |
145 | struct timezone zone; | |
146 | #endif | |
147 | ||
148 | theSGlobPtr->TarID = FileID; | |
149 | theSGlobPtr->TarBlock = 0; | |
150 | myBlockDescriptor.buffer = NULL; | |
151 | myVCBPtr = theSGlobPtr->calculatedVCB; | |
152 | if (kHFSCatalogFileID == FileID) { | |
153 | oldFCBPtr = theSGlobPtr->calculatedCatalogFCB; | |
154 | } else if (kHFSAttributesFileID == FileID) { | |
155 | oldFCBPtr = theSGlobPtr->calculatedAttributesFCB; | |
156 | /* | |
157 | * 12447845 | |
158 | * If we don't have an attributes btree, then we should just | |
159 | * quit now -- nothing to rebuild. | |
160 | */ | |
161 | if (oldFCBPtr->fcbLogicalSize == 0) { | |
162 | if (debug) | |
163 | plog("Requested attributes btree rebuild, but attributes file size is 0\n"); | |
164 | myErr = 0; | |
165 | goto ExitThisRoutine; | |
166 | } | |
167 | } else if (kHFSExtentsFileID == FileID) { | |
168 | oldFCBPtr = theSGlobPtr->calculatedExtentsFCB; | |
169 | } else { | |
170 | abort(); | |
171 | } | |
172 | ||
173 | myErr = BTScanInitialize( oldFCBPtr, &theSGlobPtr->scanState ); | |
174 | if ( noErr != myErr ) | |
175 | goto ExitThisRoutine; | |
176 | ||
177 | // some VCB fields that we need may not have been calculated so we get it from the MDB. | |
178 | // this can happen because the fsck_hfs code path to fully set up the VCB may have been | |
179 | // aborted if an error was found that would trigger a rebuild. For example, | |
180 | // if a leaf record was found to have a keys out of order then the verify phase of the | |
181 | // B-Tree check would be aborted and we would come directly (if allowable) to here. | |
182 | isHFSPlus = ( myVCBPtr->vcbSignature == kHFSPlusSigWord ); | |
183 | ||
184 | if (!isHFSPlus) { | |
185 | myErr = noMacDskErr; | |
186 | goto ExitThisRoutine; | |
187 | } | |
188 | ||
189 | myErr = GetVolumeObjectVHBorMDB( &myBlockDescriptor ); | |
190 | if ( noErr != myErr ) | |
191 | goto ExitThisRoutine; | |
192 | ||
193 | if ( isHFSPlus ) | |
194 | { | |
195 | HFSPlusVolumeHeader * myVHBPtr; | |
196 | ||
197 | myVHBPtr = (HFSPlusVolumeHeader *) myBlockDescriptor.buffer; | |
198 | myVCBPtr->vcbFreeBlocks = myVHBPtr->freeBlocks; | |
199 | myVCBPtr->vcbFileCount = myVHBPtr->fileCount; | |
200 | myVCBPtr->vcbFolderCount = myVHBPtr->folderCount; | |
201 | myVCBPtr->vcbEncodingsBitmap = myVHBPtr->encodingsBitmap; | |
202 | myVCBPtr->vcbRsrcClumpSize = myVHBPtr->rsrcClumpSize; | |
203 | myVCBPtr->vcbDataClumpSize = myVHBPtr->dataClumpSize; | |
204 | ||
205 | // check out creation and last mod dates | |
206 | myVCBPtr->vcbCreateDate = myVHBPtr->createDate; | |
207 | myVCBPtr->vcbModifyDate = myVHBPtr->modifyDate; | |
208 | myVCBPtr->vcbCheckedDate = myVHBPtr->checkedDate; | |
209 | myVCBPtr->vcbBackupDate = myVHBPtr->backupDate; | |
210 | myVCBPtr->vcbCatalogFile->fcbClumpSize = myVHBPtr->catalogFile.clumpSize; | |
211 | if (myVCBPtr->vcbAttributesFile != NULL) { | |
212 | myVCBPtr->vcbAttributesFile->fcbClumpSize = myVHBPtr->attributesFile.clumpSize; | |
213 | } | |
214 | myVCBPtr->vcbExtentsFile->fcbClumpSize = myVHBPtr->extentsFile.clumpSize; | |
215 | ||
216 | // 3882639: Removed check for volume attributes in HFS Plus | |
217 | myVCBPtr->vcbAttributes = myVHBPtr->attributes; | |
218 | ||
219 | CopyMemory( myVHBPtr->finderInfo, myVCBPtr->vcbFinderInfo, sizeof(myVCBPtr->vcbFinderInfo) ); | |
220 | } | |
221 | else | |
222 | { | |
223 | HFSMasterDirectoryBlock * myMDBPtr; | |
224 | myMDBPtr = (HFSMasterDirectoryBlock *) myBlockDescriptor.buffer; | |
225 | myVCBPtr->vcbFreeBlocks = myMDBPtr->drFreeBks; | |
226 | myVCBPtr->vcbFileCount = myMDBPtr->drFilCnt; | |
227 | myVCBPtr->vcbFolderCount = myMDBPtr->drDirCnt; | |
228 | myVCBPtr->vcbDataClumpSize = myMDBPtr->drClpSiz; | |
229 | myVCBPtr->vcbCatalogFile->fcbClumpSize = myMDBPtr->drCTClpSiz; | |
230 | myVCBPtr->vcbNmRtDirs = myMDBPtr->drNmRtDirs; | |
231 | ||
232 | // check out creation and last mod dates | |
233 | myVCBPtr->vcbCreateDate = myMDBPtr->drCrDate; | |
234 | myVCBPtr->vcbModifyDate = myMDBPtr->drLsMod; | |
235 | ||
236 | // verify volume attribute flags | |
237 | if ( (myMDBPtr->drAtrb & VAtrb_Msk) == 0 ) | |
238 | myVCBPtr->vcbAttributes = myMDBPtr->drAtrb; | |
239 | else | |
240 | myVCBPtr->vcbAttributes = VAtrb_DFlt; | |
241 | myVCBPtr->vcbBackupDate = myMDBPtr->drVolBkUp; | |
242 | myVCBPtr->vcbVSeqNum = myMDBPtr->drVSeqNum; | |
243 | CopyMemory( myMDBPtr->drFndrInfo, myVCBPtr->vcbFinderInfo, sizeof(myMDBPtr->drFndrInfo) ); | |
244 | } | |
245 | (void) ReleaseVolumeBlock( myVCBPtr, &myBlockDescriptor, kReleaseBlock ); | |
246 | myBlockDescriptor.buffer = NULL; | |
247 | ||
248 | // create the new BTree file | |
249 | if (FileID == kHFSCatalogFileID || FileID == kHFSAttributesFileID || FileID == kHFSExtentsFileID) { | |
250 | myErr = CreateNewBTree( theSGlobPtr, FileID ); | |
251 | } else { | |
252 | myErr = EINVAL; | |
253 | } | |
254 | if ( noErr != myErr ) { | |
255 | #if DEBUG_REBUILD | |
256 | plog("CreateNewBTree returned %d\n", myErr); | |
257 | #endif | |
258 | if (myErr == dskFulErr) { | |
259 | fsckPrint(theSGlobPtr->context, E_DiskFull); | |
260 | } | |
261 | goto ExitThisRoutine; | |
262 | } | |
263 | myFCBPtr = theSGlobPtr->calculatedRepairFCB; | |
264 | ||
265 | #if SHOW_ELAPSED_TIMES | |
266 | gettimeofday( &myStartTime, &zone ); | |
267 | #endif | |
268 | ||
269 | #if DEBUG_REBUILD | |
270 | if (debug) { | |
271 | int i; | |
272 | HFSPlusExtentDescriptor *te = (HFSPlusExtentDescriptor*)&theSGlobPtr->calculatedRepairFCB->fcbExtents32; | |
273 | printf("Extent records for rebuilt file %u:\n", FileID); | |
274 | for (i = 0; i < kHFSPlusExtentDensity; i++) { | |
275 | printf("\t[ %u, %u ]\n", te[i].startBlock, te[i].blockCount); | |
276 | } | |
277 | } | |
278 | #endif | |
279 | ||
280 | while ( true ) | |
281 | { | |
282 | /* scan the btree for leaf records */ | |
283 | myErr = BTScanNextRecord( &theSGlobPtr->scanState, | |
284 | (void **) &myCurrentKeyPtr, | |
285 | (void **) &myCurrentDataPtr, | |
286 | &myDataSize ); | |
287 | if ( noErr != myErr ) | |
288 | break; | |
289 | ||
290 | /* do some validation on the record */ | |
291 | theSGlobPtr->TarBlock = theSGlobPtr->scanState.nodeNum; | |
292 | if (FileID == kHFSCatalogFileID) { | |
293 | myErr = ValidateCatalogRecordLength( theSGlobPtr, myCurrentDataPtr, myDataSize ); | |
294 | } else if (FileID == kHFSAttributesFileID) { | |
295 | myErr = ValidateAttributeRecordLength( theSGlobPtr, myCurrentDataPtr, myDataSize ); | |
296 | } else if (FileID == kHFSExtentsFileID) { | |
297 | myErr = ValidateExtentRecordLength( theSGlobPtr, myCurrentDataPtr, myDataSize ); | |
298 | } | |
299 | if ( noErr != myErr ) | |
300 | { | |
301 | #if DEBUG_REBUILD | |
302 | { | |
303 | plog( "%s - Record length validation (file %d) failed! \n", __FUNCTION__, FileID ); | |
304 | plog( "%s - record %d in node %d is not recoverable. \n", | |
305 | __FUNCTION__, (theSGlobPtr->scanState.recordNum - 1), | |
306 | theSGlobPtr->scanState.nodeNum ); | |
307 | } | |
308 | #endif | |
309 | myErr = R_RFail; | |
310 | break; // this implementation does not handle partial rebuilds (all or none) | |
311 | } | |
312 | ||
313 | /* insert this record into the new btree file */ | |
314 | myErr = InsertBTreeRecord( myFCBPtr, myCurrentKeyPtr, | |
315 | myCurrentDataPtr, myDataSize, &myHint ); | |
316 | if ( noErr != myErr ) | |
317 | { | |
318 | #if DEBUG_REBUILD | |
319 | { | |
320 | plog( "%s - InsertBTreeRecord failed with err %d 0x%02X \n", | |
321 | __FUNCTION__, myErr, myErr ); | |
322 | plog( "%s - numRecords = %d\n", __FUNCTION__, numRecords); | |
323 | plog( "%s - record %d in node %d is not recoverable. \n", | |
324 | __FUNCTION__, (theSGlobPtr->scanState.recordNum - 1), | |
325 | theSGlobPtr->scanState.nodeNum ); | |
326 | PrintBTreeKey( myCurrentKeyPtr, theSGlobPtr->calculatedCatalogBTCB ); | |
327 | PrintBTreeData( myCurrentDataPtr, myDataSize ); | |
328 | } | |
329 | if (myErr == btExists) | |
330 | continue; | |
331 | #endif | |
332 | if (dskFulErr == myErr) | |
333 | { | |
334 | fsckPrint(theSGlobPtr->context, E_DiskFull); | |
335 | } | |
336 | myErr = R_RFail; | |
337 | break; // this implementation does not handle partial rebuilds (all or none) | |
338 | } | |
339 | numRecords++; | |
340 | #if DEBUG_REBUILD | |
341 | if (debug && ((numRecords % 1000) == 0)) | |
342 | plog("btree file %d: %u records\n", FileID, numRecords); | |
343 | #endif | |
344 | } | |
345 | ||
346 | #if SHOW_ELAPSED_TIMES | |
347 | gettimeofday( &myEndTime, &zone ); | |
348 | timersub( &myEndTime, &myStartTime, &myElapsedTime ); | |
349 | plog( "\n%s - rebuild btree %u %u records elapsed time \n", __FUNCTION__, FileID, numRecords ); | |
350 | plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", myElapsedTime.tv_sec, myElapsedTime.tv_usec ); | |
351 | #endif | |
352 | ||
353 | if ( btNotFound == myErr ) | |
354 | myErr = noErr; | |
355 | if ( noErr != myErr ) | |
356 | goto ExitThisRoutine; | |
357 | ||
358 | /* update our header node on disk from our BTreeControlBlock */ | |
359 | myErr = BTFlushPath( myFCBPtr ); | |
360 | if ( noErr != myErr ) | |
361 | goto ExitThisRoutine; | |
362 | myErr = CacheFlush( myVCBPtr->vcbBlockCache ); | |
363 | if ( noErr != myErr ) | |
364 | goto ExitThisRoutine; | |
365 | ||
366 | /* switch old file with our new one */ | |
367 | if (FileID == kHFSCatalogFileID) { | |
368 | theSGlobPtr->calculatedRepairFCB = theSGlobPtr->calculatedCatalogFCB; | |
369 | theSGlobPtr->calculatedCatalogFCB = myFCBPtr; | |
370 | myVCBPtr->vcbCatalogFile = myFCBPtr; | |
371 | theSGlobPtr->calculatedCatalogFCB->fcbFileID = kHFSCatalogFileID; | |
372 | theSGlobPtr->calculatedRepairBTCB = theSGlobPtr->calculatedCatalogBTCB; | |
373 | } else if (FileID == kHFSAttributesFileID) { | |
374 | theSGlobPtr->calculatedRepairFCB = theSGlobPtr->calculatedAttributesFCB; | |
375 | theSGlobPtr->calculatedAttributesFCB = myFCBPtr; | |
376 | myVCBPtr->vcbAttributesFile = myFCBPtr; | |
377 | if (theSGlobPtr->calculatedAttributesFCB == NULL) { | |
378 | if (debug) { | |
379 | plog("Can't rebuilt attributes btree when there is no attributes file\n"); | |
380 | } | |
381 | myErr = noErr; | |
382 | goto ExitThisRoutine; | |
383 | } | |
384 | theSGlobPtr->calculatedAttributesFCB->fcbFileID = kHFSAttributesFileID; | |
385 | theSGlobPtr->calculatedRepairBTCB = theSGlobPtr->calculatedAttributesBTCB; | |
386 | } else if (FileID == kHFSExtentsFileID) { | |
387 | theSGlobPtr->calculatedRepairFCB = theSGlobPtr->calculatedExtentsFCB; | |
388 | theSGlobPtr->calculatedExtentsFCB = myFCBPtr; | |
389 | myVCBPtr->vcbExtentsFile = myFCBPtr; | |
390 | theSGlobPtr->calculatedExtentsFCB->fcbFileID = kHFSExtentsFileID; | |
391 | theSGlobPtr->calculatedRepairBTCB = theSGlobPtr->calculatedExtentsBTCB; | |
392 | } | |
393 | ||
394 | // todo - add code to allow new btree file to be allocated in extents. | |
395 | // Note when we do allow this the swap of btree files gets even more | |
396 | // tricky since extent record key contains the file ID. The rebuilt | |
397 | // file has file ID kHFSCatalogFileID/kHFSCatalogFileID when it is created. | |
398 | ||
399 | MarkVCBDirty( myVCBPtr ); | |
400 | myErr = FlushAlternateVolumeControlBlock( myVCBPtr, isHFSPlus ); | |
401 | if ( noErr != myErr ) | |
402 | { | |
403 | // we may be totally screwed if we get here, try to recover | |
404 | if (FileID == kHFSCatalogFileID) { | |
405 | theSGlobPtr->calculatedCatalogFCB = theSGlobPtr->calculatedRepairFCB; | |
406 | theSGlobPtr->calculatedRepairFCB = myFCBPtr; | |
407 | myVCBPtr->vcbCatalogFile = theSGlobPtr->calculatedCatalogFCB; | |
408 | } else if (FileID == kHFSAttributesFileID) { | |
409 | theSGlobPtr->calculatedAttributesFCB = theSGlobPtr->calculatedRepairFCB; | |
410 | theSGlobPtr->calculatedRepairFCB = myFCBPtr; | |
411 | myVCBPtr->vcbAttributesFile = theSGlobPtr->calculatedAttributesFCB; | |
412 | } else if (FileID == kHFSExtentsFileID) { | |
413 | theSGlobPtr->calculatedExtentsFCB = theSGlobPtr->calculatedRepairFCB; | |
414 | theSGlobPtr->calculatedRepairFCB = myFCBPtr; | |
415 | myVCBPtr->vcbExtentsFile = theSGlobPtr->calculatedExtentsFCB; | |
416 | } | |
417 | MarkVCBDirty( myVCBPtr ); | |
418 | (void) FlushAlternateVolumeControlBlock( myVCBPtr, isHFSPlus ); | |
419 | goto ExitThisRoutine; | |
420 | } | |
421 | ||
422 | /* release space occupied by old BTree file */ | |
423 | (void) DeleteBTree( theSGlobPtr, theSGlobPtr->calculatedRepairFCB ); | |
424 | if (FileID == kHFSExtentsFileID) | |
425 | (void)FlushExtentFile(myVCBPtr); | |
426 | ||
427 | ExitThisRoutine: | |
428 | if ( myBlockDescriptor.buffer != NULL ) | |
429 | (void) ReleaseVolumeBlock( myVCBPtr, &myBlockDescriptor, kReleaseBlock ); | |
430 | ||
431 | if ( myErr != noErr && myFCBPtr != NULL ) | |
432 | (void) DeleteBTree( theSGlobPtr, myFCBPtr ); | |
433 | BTScanTerminate( &theSGlobPtr->scanState ); | |
434 | ||
435 | return( myErr ); | |
436 | ||
437 | } /* RebuildBTree */ | |
438 | ||
439 | //_________________________________________________________________________________ | |
440 | // | |
441 | // Routine: CreateNewBTree | |
442 | // | |
443 | // Purpose: Create and Initialize a new B-Tree on the target volume | |
444 | // using the physical size of the old (being rebuilt) file as an initial | |
445 | // size. | |
446 | // | |
447 | // NOTES: we force this to be contiguous in order to get this into Jaguar. | |
448 | // Allowing the new file to go into extents makes the swap | |
449 | // of the old and new files complicated. The extent records | |
450 | // are keyed by file ID and the new (rebuilt) btree file starts out as | |
451 | // file Id kHFSCatalogFileID/kHFSCatalogFileID/kHFSCatalogFileID. | |
452 | // If there were extents then we would have to fix up the extent records in the extent B-Tree. | |
453 | // | |
454 | // todo: Don't force new file to be contiguous | |
455 | // | |
456 | // Inputs: | |
457 | // SGlobPtr global state set up by fsck_hfs. We depend upon the | |
458 | // manufactured and repair FCBs. | |
459 | // | |
460 | // Outputs: | |
461 | // calculatedRepairBTCB fill in the BTreeControlBlock for new B-Tree file. | |
462 | // calculatedRepairFCB fill in the SFCB for the new B-Tree file | |
463 | // | |
464 | // Result: | |
465 | // various error codes when problems occur or noErr if all is well | |
466 | // | |
467 | //_________________________________________________________________________________ | |
468 | ||
469 | /*static*/ OSErr CreateNewBTree( SGlobPtr theSGlobPtr, int FileID ) | |
470 | { | |
471 | OSErr myErr; | |
472 | BTreeControlBlock * myBTreeCBPtr, * oldBCBPtr; | |
473 | SVCB * myVCBPtr; | |
474 | SFCB * myFCBPtr, * oldFCBPtr; | |
475 | UInt32 myBytesUsed = 0; | |
476 | UInt32 myMapNodeCount; | |
477 | UInt32 myNumBlocks; | |
478 | FSSize myNewEOF; | |
479 | BTHeaderRec myHeaderRec; | |
480 | ||
481 | myBTreeCBPtr = theSGlobPtr->calculatedRepairBTCB; | |
482 | myFCBPtr = theSGlobPtr->calculatedRepairFCB; | |
483 | ClearMemory( (Ptr) myFCBPtr, sizeof( *myFCBPtr ) ); | |
484 | ClearMemory( (Ptr) myBTreeCBPtr, sizeof( *myBTreeCBPtr ) ); | |
485 | ||
486 | if (FileID == kHFSCatalogFileID) { | |
487 | oldFCBPtr = theSGlobPtr->calculatedCatalogFCB; | |
488 | oldBCBPtr = theSGlobPtr->calculatedCatalogBTCB; | |
489 | } else if (FileID == kHFSAttributesFileID) { | |
490 | oldFCBPtr = theSGlobPtr->calculatedAttributesFCB; | |
491 | oldBCBPtr = theSGlobPtr->calculatedAttributesBTCB; | |
492 | } else if (FileID == kHFSExtentsFileID) { | |
493 | oldFCBPtr = theSGlobPtr->calculatedExtentsFCB; | |
494 | oldBCBPtr = theSGlobPtr->calculatedExtentsBTCB; | |
495 | } else | |
496 | abort(); | |
497 | ||
498 | // Create new FCB | |
499 | myVCBPtr = oldFCBPtr->fcbVolume; | |
500 | if (FileID == kHFSCatalogFileID) | |
501 | myFCBPtr->fcbFileID = kHFSCatalogFileID; | |
502 | else if (FileID == kHFSAttributesFileID) | |
503 | myFCBPtr->fcbFileID = kHFSAttributesFileID; | |
504 | else if (FileID == kHFSExtentsFileID) | |
505 | myFCBPtr->fcbFileID = kHFSExtentsFileID; | |
506 | ||
507 | myFCBPtr->fcbVolume = myVCBPtr; | |
508 | myFCBPtr->fcbBtree = myBTreeCBPtr; | |
509 | myFCBPtr->fcbBlockSize = oldBCBPtr->nodeSize; | |
510 | ||
511 | // Create new BTree Control Block | |
512 | myBTreeCBPtr->fcbPtr = myFCBPtr; | |
513 | myBTreeCBPtr->btreeType = kHFSBTreeType; | |
514 | myBTreeCBPtr->keyCompareType = oldBCBPtr->keyCompareType; | |
515 | myBTreeCBPtr->keyCompareProc = oldBCBPtr->keyCompareProc; | |
516 | myBTreeCBPtr->nodeSize = oldBCBPtr->nodeSize; | |
517 | myBTreeCBPtr->maxKeyLength = oldBCBPtr->maxKeyLength; | |
518 | if (myVCBPtr->vcbSignature == kHFSPlusSigWord) { | |
519 | if (FileID == kHFSExtentsFileID) | |
520 | myBTreeCBPtr->attributes = kBTBigKeysMask; | |
521 | else | |
522 | myBTreeCBPtr->attributes = ( kBTBigKeysMask + kBTVariableIndexKeysMask ); | |
523 | } | |
524 | ||
525 | myBTreeCBPtr->getBlockProc = GetFileBlock; | |
526 | myBTreeCBPtr->releaseBlockProc = ReleaseFileBlock; | |
527 | myBTreeCBPtr->setEndOfForkProc = SetEndOfForkProc; | |
528 | ||
529 | myNewEOF = oldFCBPtr->fcbPhysicalSize; | |
530 | ||
531 | myNumBlocks = myNewEOF / myVCBPtr->vcbBlockSize; | |
532 | myErr = BlockFindAll( myBTreeCBPtr->fcbPtr, myNumBlocks); | |
533 | ReturnIfError( myErr ); | |
534 | myBTreeCBPtr->fcbPtr->fcbPhysicalSize = myNewEOF; | |
535 | myErr = ZeroFileBlocks( myVCBPtr, myBTreeCBPtr->fcbPtr, 0, (UInt32)(myNewEOF >> kSectorShift) ); | |
536 | ReturnIfError( myErr ); | |
537 | ||
538 | /* now set real values in our BTree Control Block */ | |
539 | myFCBPtr->fcbLogicalSize = myFCBPtr->fcbPhysicalSize; // new B-tree looks at fcbLogicalSize | |
540 | if (FileID == kHFSCatalogFileID) | |
541 | myFCBPtr->fcbClumpSize = myVCBPtr->vcbCatalogFile->fcbClumpSize; | |
542 | else if (FileID == kHFSAttributesFileID) | |
543 | myFCBPtr->fcbClumpSize = myVCBPtr->vcbAttributesFile->fcbClumpSize; | |
544 | else if (FileID == kHFSExtentsFileID) | |
545 | myFCBPtr->fcbClumpSize = myVCBPtr->vcbExtentsFile->fcbClumpSize; | |
546 | ||
547 | myBTreeCBPtr->totalNodes = (UInt32)( myFCBPtr->fcbPhysicalSize / myBTreeCBPtr->nodeSize ); | |
548 | myBTreeCBPtr->freeNodes = myBTreeCBPtr->totalNodes; | |
549 | ||
550 | // Initialize our new BTree (write out header node and an empty leaf node) | |
551 | myErr = InitializeBTree( myBTreeCBPtr, &myBytesUsed, &myMapNodeCount ); | |
552 | ReturnIfError( myErr ); | |
553 | ||
554 | // Update our BTreeControlBlock from BTHeaderRec we just wrote out | |
555 | myErr = GetBTreeHeader( theSGlobPtr, myFCBPtr, &myHeaderRec ); | |
556 | ReturnIfError( myErr ); | |
557 | ||
558 | myBTreeCBPtr->treeDepth = myHeaderRec.treeDepth; | |
559 | myBTreeCBPtr->rootNode = myHeaderRec.rootNode; | |
560 | myBTreeCBPtr->leafRecords = myHeaderRec.leafRecords; | |
561 | myBTreeCBPtr->firstLeafNode = myHeaderRec.firstLeafNode; | |
562 | myBTreeCBPtr->lastLeafNode = myHeaderRec.lastLeafNode; | |
563 | myBTreeCBPtr->totalNodes = myHeaderRec.totalNodes; | |
564 | myBTreeCBPtr->freeNodes = myHeaderRec.freeNodes; | |
565 | myBTreeCBPtr->maxKeyLength = myHeaderRec.maxKeyLength; | |
566 | ||
567 | if ( myMapNodeCount > 0 ) | |
568 | { | |
569 | myErr = WriteMapNodes( myBTreeCBPtr, (myBytesUsed / myBTreeCBPtr->nodeSize ), myMapNodeCount ); | |
570 | ReturnIfError( myErr ); | |
571 | } | |
572 | ||
573 | return( myErr ); | |
574 | ||
575 | } /* CreateNewBTree */ | |
576 | ||
577 | ||
578 | /* | |
579 | * InitializeBTree | |
580 | * | |
581 | * This routine manufactures and writes out a B-Tree header | |
582 | * node and an empty leaf node. | |
583 | * | |
584 | * Note: Since large volumes can have bigger b-trees they | |
585 | * might need to have map nodes setup. | |
586 | * | |
587 | * this routine originally came from newfs_hfs.tproj ( see | |
588 | * WriteCatalogFile in file makehfs.c) and was modified for fsck_hfs. | |
589 | */ | |
590 | static OSErr InitializeBTree( BTreeControlBlock * theBTreeCBPtr, | |
591 | UInt32 * theBytesUsedPtr, | |
592 | UInt32 * theMapNodeCountPtr ) | |
593 | { | |
594 | OSErr myErr; | |
595 | BlockDescriptor myNode; | |
596 | Boolean isHFSPlus = false; | |
597 | SVCB * myVCBPtr; | |
598 | BTNodeDescriptor * myNodeDescPtr; | |
599 | BTHeaderRec * myHeaderRecPtr; | |
600 | UInt8 * myBufferPtr; | |
601 | UInt8 * myBitMapPtr; | |
602 | UInt32 myNodeBitsInHeader; | |
603 | UInt32 temp; | |
604 | SInt16 myOffset; | |
605 | ||
606 | myVCBPtr = theBTreeCBPtr->fcbPtr->fcbVolume; | |
607 | isHFSPlus = ( myVCBPtr->vcbSignature == kHFSPlusSigWord) ; | |
608 | *theMapNodeCountPtr = 0; | |
609 | ||
610 | myErr = GetNewNode( theBTreeCBPtr, kHeaderNodeNum, &myNode ); | |
611 | ReturnIfError( myErr ); | |
612 | ||
613 | myBufferPtr = (UInt8 *) myNode.buffer; | |
614 | ||
615 | /* FILL IN THE NODE DESCRIPTOR: */ | |
616 | myNodeDescPtr = (BTNodeDescriptor *) myBufferPtr; | |
617 | myNodeDescPtr->kind = kBTHeaderNode; | |
618 | myNodeDescPtr->numRecords = 3; | |
619 | myOffset = sizeof( BTNodeDescriptor ); | |
620 | ||
621 | SETOFFSET( myBufferPtr, theBTreeCBPtr->nodeSize, myOffset, 1 ); | |
622 | ||
623 | /* FILL IN THE HEADER RECORD: */ | |
624 | myHeaderRecPtr = (BTHeaderRec *)((UInt8 *)myBufferPtr + myOffset); | |
625 | myHeaderRecPtr->treeDepth = 0; | |
626 | myHeaderRecPtr->rootNode = 0; | |
627 | myHeaderRecPtr->firstLeafNode = 0; | |
628 | myHeaderRecPtr->lastLeafNode = 0; | |
629 | myHeaderRecPtr->nodeSize = theBTreeCBPtr->nodeSize; | |
630 | myHeaderRecPtr->totalNodes = theBTreeCBPtr->totalNodes; | |
631 | myHeaderRecPtr->freeNodes = myHeaderRecPtr->totalNodes - 1; /* header node */ | |
632 | myHeaderRecPtr->clumpSize = theBTreeCBPtr->fcbPtr->fcbClumpSize; | |
633 | ||
634 | myHeaderRecPtr->attributes = theBTreeCBPtr->attributes; | |
635 | myHeaderRecPtr->maxKeyLength = theBTreeCBPtr->maxKeyLength; | |
636 | myHeaderRecPtr->keyCompareType = theBTreeCBPtr->keyCompareType; | |
637 | ||
638 | myOffset += sizeof( BTHeaderRec ); | |
639 | SETOFFSET( myBufferPtr, theBTreeCBPtr->nodeSize, myOffset, 2 ); | |
640 | ||
641 | myOffset += kBTreeHeaderUserBytes; | |
642 | SETOFFSET( myBufferPtr, theBTreeCBPtr->nodeSize, myOffset, 3 ); | |
643 | ||
644 | /* FIGURE OUT HOW MANY MAP NODES (IF ANY): */ | |
645 | myNodeBitsInHeader = 8 * (theBTreeCBPtr->nodeSize | |
646 | - sizeof(BTNodeDescriptor) | |
647 | - sizeof(BTHeaderRec) | |
648 | - kBTreeHeaderUserBytes | |
649 | - (4 * sizeof(SInt16)) ); | |
650 | ||
651 | if ( myHeaderRecPtr->totalNodes > myNodeBitsInHeader ) | |
652 | { | |
653 | UInt32 nodeBitsInMapNode; | |
654 | ||
655 | myNodeDescPtr->fLink = myHeaderRecPtr->lastLeafNode + 1; | |
656 | nodeBitsInMapNode = 8 * (theBTreeCBPtr->nodeSize | |
657 | - sizeof(BTNodeDescriptor) | |
658 | - (2 * sizeof(SInt16)) | |
659 | - 2 ); | |
660 | *theMapNodeCountPtr = (myHeaderRecPtr->totalNodes - myNodeBitsInHeader + | |
661 | (nodeBitsInMapNode - 1)) / nodeBitsInMapNode; | |
662 | myHeaderRecPtr->freeNodes = myHeaderRecPtr->freeNodes - *theMapNodeCountPtr; | |
663 | } | |
664 | ||
665 | /* | |
666 | * FILL IN THE MAP RECORD, MARKING NODES THAT ARE IN USE. | |
667 | * Note - worst case (32MB alloc blk) will have only 18 nodes in use. | |
668 | */ | |
669 | myBitMapPtr = ((UInt8 *)myBufferPtr + myOffset); | |
670 | temp = myHeaderRecPtr->totalNodes - myHeaderRecPtr->freeNodes; | |
671 | ||
672 | /* Working a byte at a time is endian safe */ | |
673 | while ( temp >= 8 ) | |
674 | { | |
675 | *myBitMapPtr = 0xFF; | |
676 | temp -= 8; | |
677 | myBitMapPtr++; | |
678 | } | |
679 | *myBitMapPtr = ~(0xFF >> temp); | |
680 | myOffset += myNodeBitsInHeader / 8; | |
681 | ||
682 | SETOFFSET( myBufferPtr, theBTreeCBPtr->nodeSize, myOffset, 4 ); | |
683 | ||
684 | *theBytesUsedPtr = | |
685 | ( myHeaderRecPtr->totalNodes - myHeaderRecPtr->freeNodes - *theMapNodeCountPtr ) | |
686 | * theBTreeCBPtr->nodeSize; | |
687 | ||
688 | /* write header node */ | |
689 | myErr = UpdateNode( theBTreeCBPtr, &myNode ); | |
690 | M_ExitOnError( myErr ); | |
691 | ||
692 | return noErr; | |
693 | ||
694 | ErrorExit: | |
695 | (void) ReleaseNode( theBTreeCBPtr, &myNode ); | |
696 | ||
697 | return( myErr ); | |
698 | ||
699 | } /* InitializeBTree */ | |
700 | ||
701 | ||
702 | /* | |
703 | * WriteMapNodes | |
704 | * | |
705 | * This routine manufactures and writes out a B-Tree map | |
706 | * node (or nodes if there are more than one). | |
707 | * | |
708 | * this routine originally came from newfs_hfs.tproj ( see | |
709 | * WriteMapNodes in file makehfs.c) and was modified for fsck_hfs. | |
710 | */ | |
711 | ||
712 | static OSErr WriteMapNodes( BTreeControlBlock * theBTreeCBPtr, | |
713 | UInt32 theFirstMapNode, | |
714 | UInt32 theNodeCount ) | |
715 | { | |
716 | OSErr myErr; | |
717 | UInt16 i; | |
718 | UInt32 mapRecordBytes; | |
719 | BTNodeDescriptor * myNodeDescPtr; | |
720 | BlockDescriptor myNode; | |
721 | ||
722 | myNode.buffer = NULL; | |
723 | ||
724 | /* | |
725 | * Note - worst case (32MB alloc blk) will have | |
726 | * only 18 map nodes. So don't bother optimizing | |
727 | * this section to do multiblock writes! | |
728 | */ | |
729 | for ( i = 0; i < theNodeCount; i++ ) | |
730 | { | |
731 | myErr = GetNewNode( theBTreeCBPtr, theFirstMapNode, &myNode ); | |
732 | M_ExitOnError( myErr ); | |
733 | ||
734 | myNodeDescPtr = (BTNodeDescriptor *) myNode.buffer; | |
735 | myNodeDescPtr->kind = kBTMapNode; | |
736 | myNodeDescPtr->numRecords = 1; | |
737 | ||
738 | /* note: must be long word aligned (hence the extra -2) */ | |
739 | mapRecordBytes = theBTreeCBPtr->nodeSize - sizeof(BTNodeDescriptor) - 2 * sizeof(SInt16) - 2; | |
740 | ||
741 | SETOFFSET( myNodeDescPtr, theBTreeCBPtr->nodeSize, sizeof(BTNodeDescriptor), 1 ); | |
742 | SETOFFSET( myNodeDescPtr, theBTreeCBPtr->nodeSize, sizeof(BTNodeDescriptor) + mapRecordBytes, 2) ; | |
743 | ||
744 | if ( (i + 1) < theNodeCount ) | |
745 | myNodeDescPtr->fLink = ++theFirstMapNode; /* point to next map node */ | |
746 | else | |
747 | myNodeDescPtr->fLink = 0; /* this is the last map node */ | |
748 | ||
749 | myErr = UpdateNode( theBTreeCBPtr, &myNode ); | |
750 | M_ExitOnError( myErr ); | |
751 | } | |
752 | ||
753 | return noErr; | |
754 | ||
755 | ErrorExit: | |
756 | (void) ReleaseNode( theBTreeCBPtr, &myNode ); | |
757 | ||
758 | return( myErr ); | |
759 | ||
760 | } /* WriteMapNodes */ | |
761 | ||
762 | ||
763 | /* | |
764 | * DeleteBTree | |
765 | * | |
766 | * This routine will realease all space associated with the BTree | |
767 | * file represented by the FCB passed in. | |
768 | * | |
769 | */ | |
770 | ||
771 | enum | |
772 | { | |
773 | kDataForkType = 0, | |
774 | kResourceForkType = 0xFF | |
775 | }; | |
776 | ||
777 | static OSErr DeleteBTree( SGlobPtr theSGlobPtr, SFCB * theFCBPtr ) | |
778 | { | |
779 | OSErr myErr; | |
780 | SVCB * myVCBPtr; | |
781 | int i; | |
782 | Boolean isHFSPlus; | |
783 | Boolean checkExtentsBTree = true; | |
784 | ||
785 | myVCBPtr = theFCBPtr->fcbVolume; | |
786 | isHFSPlus = ( myVCBPtr->vcbSignature == kHFSPlusSigWord) ; | |
787 | ||
788 | if ( isHFSPlus ) | |
789 | { | |
790 | for ( i = 0; i < kHFSPlusExtentDensity; ++i ) | |
791 | { | |
792 | if ( theFCBPtr->fcbExtents32[ i ].blockCount == 0 ) | |
793 | { | |
794 | checkExtentsBTree = false; | |
795 | break; | |
796 | } | |
797 | (void) BlockDeallocate( myVCBPtr, | |
798 | theFCBPtr->fcbExtents32[ i ].startBlock, | |
799 | theFCBPtr->fcbExtents32[ i ].blockCount ); | |
800 | theFCBPtr->fcbExtents32[ i ].startBlock = 0; | |
801 | theFCBPtr->fcbExtents32[ i ].blockCount = 0; | |
802 | } | |
803 | } | |
804 | else | |
805 | { | |
806 | for ( i = 0; i < kHFSExtentDensity; ++i ) | |
807 | { | |
808 | if ( theFCBPtr->fcbExtents16[ i ].blockCount == 0 ) | |
809 | { | |
810 | checkExtentsBTree = false; | |
811 | break; | |
812 | } | |
813 | (void) BlockDeallocate( myVCBPtr, | |
814 | theFCBPtr->fcbExtents16[ i ].startBlock, | |
815 | theFCBPtr->fcbExtents16[ i ].blockCount ); | |
816 | theFCBPtr->fcbExtents16[ i ].startBlock = 0; | |
817 | theFCBPtr->fcbExtents16[ i ].blockCount = 0; | |
818 | } | |
819 | } | |
820 | ||
821 | if ( checkExtentsBTree ) | |
822 | { | |
823 | (void) ReleaseExtentsInExtentsBTree( theSGlobPtr, theFCBPtr ); | |
824 | (void) FlushExtentFile( myVCBPtr ); | |
825 | } | |
826 | ||
827 | (void) MarkVCBDirty( myVCBPtr ); | |
828 | (void) FlushAlternateVolumeControlBlock( myVCBPtr, isHFSPlus ); | |
829 | myErr = noErr; | |
830 | ||
831 | return( myErr ); | |
832 | ||
833 | } /* DeleteBTree */ | |
834 | ||
835 | ||
836 | /* | |
837 | * ReleaseExtentsInExtentsBTree | |
838 | * | |
839 | * This routine will locate extents in the extent BTree then release the space | |
840 | * associated with the extents. It will also delete the BTree record for the | |
841 | * extent. | |
842 | * | |
843 | */ | |
844 | ||
845 | static OSErr ReleaseExtentsInExtentsBTree( SGlobPtr theSGlobPtr, | |
846 | SFCB * theFCBPtr ) | |
847 | { | |
848 | BTreeIterator myIterator; | |
849 | ExtentRecord myExtentRecord; | |
850 | FSBufferDescriptor myBTRec; | |
851 | ExtentKey * myKeyPtr; | |
852 | SVCB * myVCBPtr; | |
853 | UInt16 myRecLen; | |
854 | UInt16 i; | |
855 | OSErr myErr; | |
856 | Boolean isHFSPlus; | |
857 | ||
858 | myVCBPtr = theFCBPtr->fcbVolume; | |
859 | isHFSPlus = ( myVCBPtr->vcbSignature == kHFSPlusSigWord ); | |
860 | ||
861 | // position just before the first extent record for the given File ID. We | |
862 | // pass in the file ID and a start block of 0 which will put us in a | |
863 | // position for BTIterateRecord (with kBTreeNextRecord) to get the first | |
864 | // extent record. | |
865 | ClearMemory( &myIterator, sizeof(myIterator) ); | |
866 | myBTRec.bufferAddress = &myExtentRecord; | |
867 | myBTRec.itemCount = 1; | |
868 | myBTRec.itemSize = sizeof(myExtentRecord); | |
869 | myKeyPtr = (ExtentKey *) &myIterator.key; | |
870 | ||
871 | BuildExtentKey( isHFSPlus, kDataForkType, theFCBPtr->fcbFileID, | |
872 | 0, (void *) myKeyPtr ); | |
873 | ||
874 | // it is now a simple process of getting the next extent record and | |
875 | // cleaning up the allocated space for each one until we hit a | |
876 | // different file ID. | |
877 | for ( ;; ) | |
878 | { | |
879 | myErr = BTIterateRecord( theSGlobPtr->calculatedExtentsFCB, | |
880 | kBTreeNextRecord, &myIterator, | |
881 | &myBTRec, &myRecLen ); | |
882 | if ( noErr != myErr ) | |
883 | { | |
884 | myErr = noErr; | |
885 | break; | |
886 | } | |
887 | ||
888 | /* deallocate space for the extents we found */ | |
889 | if ( isHFSPlus ) | |
890 | { | |
891 | // we're done if this is a different File ID | |
892 | if ( myKeyPtr->hfsPlus.fileID != theFCBPtr->fcbFileID || | |
893 | myKeyPtr->hfsPlus.forkType != kDataForkType ) | |
894 | break; | |
895 | ||
896 | for ( i = 0; i < kHFSPlusExtentDensity; ++i ) | |
897 | { | |
898 | if ( myExtentRecord.hfsPlus[ i ].blockCount == 0 ) | |
899 | break; | |
900 | ||
901 | (void) BlockDeallocate( myVCBPtr, | |
902 | myExtentRecord.hfsPlus[ i ].startBlock, | |
903 | myExtentRecord.hfsPlus[ i ].blockCount ); | |
904 | } | |
905 | } | |
906 | else | |
907 | { | |
908 | // we're done if this is a different File ID | |
909 | if ( myKeyPtr->hfs.fileID != theFCBPtr->fcbFileID || | |
910 | myKeyPtr->hfs.forkType != kDataForkType ) | |
911 | break; | |
912 | ||
913 | for ( i = 0; i < kHFSExtentDensity; ++i ) | |
914 | { | |
915 | if ( myExtentRecord.hfs[ i ].blockCount == 0 ) | |
916 | break; | |
917 | ||
918 | (void) BlockDeallocate( myVCBPtr, | |
919 | myExtentRecord.hfs[ i ].startBlock, | |
920 | myExtentRecord.hfs[ i ].blockCount ); | |
921 | } | |
922 | } | |
923 | ||
924 | /* get rid of this extent BTree record */ | |
925 | myErr = DeleteBTreeRecord( theSGlobPtr->calculatedExtentsFCB, myKeyPtr ); | |
926 | } | |
927 | ||
928 | return( myErr ); | |
929 | ||
930 | } /* ReleaseExtentsInExtentsBTree */ | |
931 | ||
932 | ||
933 | /* | |
934 | * ValidateExtentRecordLength | |
935 | * This routine will ensure that an extent record is the right size. | |
936 | * This should always be the size of HFSPlusExtentRecord. | |
937 | */ | |
938 | static OSErr ValidateExtentRecordLength (SGlobPtr s, ExtentRecord * theRecPtr, UInt32 theRecSize) | |
939 | { | |
940 | Boolean isHFSPlus = ( s->calculatedVCB->vcbSignature == kHFSPlusSigWord ); | |
941 | if (isHFSPlus) { | |
942 | if (theRecSize != sizeof(HFSPlusExtentRecord)) | |
943 | return -1; | |
944 | } else { | |
945 | if (theRecSize != sizeof(HFSExtentRecord)) | |
946 | return -1; | |
947 | } | |
948 | ||
949 | return noErr; | |
950 | } | |
951 | ||
952 | /* | |
953 | * ValidateAttributeRecordLength | |
954 | * | |
955 | * This routine will make sure that the given HFS+ attributes file record | |
956 | * is of the correct length. | |
957 | * | |
958 | */ | |
959 | static OSErr ValidateAttributeRecordLength (SGlobPtr s, HFSPlusAttrRecord * theRecPtr, UInt32 theRecSize) | |
960 | { | |
961 | OSErr retval = noErr; | |
962 | static UInt32 maxInlineSize; | |
963 | ||
964 | if (maxInlineSize == 0) { | |
965 | /* The maximum size of an inline attribute record is nodesize / 2 minus a bit */ | |
966 | /* These calculations taken from hfs_xattr.c:getmaxinlineattrsize */ | |
967 | maxInlineSize = s->calculatedAttributesBTCB->nodeSize; | |
968 | maxInlineSize -= sizeof(BTNodeDescriptor); // Minus node descriptor | |
969 | maxInlineSize -= 3 * sizeof(u_int16_t); // Minus 3 index slots | |
970 | maxInlineSize /= 2; // 2 key/rec pairs minimum | |
971 | maxInlineSize -= sizeof(HFSPlusAttrKey); // Minus maximum key size | |
972 | maxInlineSize &= 0xFFFFFFFE; // Multiple of two | |
973 | } | |
974 | switch (theRecPtr->recordType) { | |
975 | case kHFSPlusAttrInlineData: | |
976 | if (theRecSize > maxInlineSize) { | |
977 | if (debug) | |
978 | plog("Inline Attribute size %u is larger than maxsize %u\n", theRecSize, maxInlineSize); | |
979 | retval = -1; | |
980 | } | |
981 | break; | |
982 | case kHFSPlusAttrForkData: | |
983 | if (theRecSize != sizeof(HFSPlusAttrForkData)) { | |
984 | if (debug) | |
985 | plog("Fork Data attribute size %u is larger then HFSPlusAttrForkData size %u\n", theRecSize, sizeof(HFSPlusAttrForkData)); | |
986 | retval = -1; | |
987 | } | |
988 | break; | |
989 | case kHFSPlusAttrExtents: | |
990 | if (theRecSize != sizeof(HFSPlusAttrExtents)) { | |
991 | if (debug) | |
992 | plog("Extents Data attribute size %u is larger than HFSPlusAttrExtents size %u\n", theRecSize, sizeof(HFSPlusAttrExtents)); | |
993 | retval = -1; | |
994 | } | |
995 | break; | |
996 | default: | |
997 | // Right now, we don't support any other kind | |
998 | if (debug) | |
999 | plog("Unknown attribute type %u\n", theRecPtr->recordType); | |
1000 | retval = -1; | |
1001 | break; | |
1002 | } | |
1003 | return retval; | |
1004 | } | |
1005 | ||
1006 | /* | |
1007 | * ValidateCatalogRecordLength | |
1008 | * | |
1009 | * This routine will make sure the given HFS (plus and standard) catalog record | |
1010 | * is of the correct length. | |
1011 | * | |
1012 | */ | |
1013 | ||
1014 | static OSErr ValidateCatalogRecordLength( SGlobPtr theSGlobPtr, | |
1015 | CatalogRecord * theRecPtr, | |
1016 | UInt32 theRecSize ) | |
1017 | { | |
1018 | SVCB * myVCBPtr; | |
1019 | Boolean isHFSPlus = false; | |
1020 | ||
1021 | myVCBPtr = theSGlobPtr->calculatedVCB; | |
1022 | isHFSPlus = ( myVCBPtr->vcbSignature == kHFSPlusSigWord ); | |
1023 | ||
1024 | if ( isHFSPlus ) | |
1025 | { | |
1026 | switch ( theRecPtr->recordType ) | |
1027 | { | |
1028 | case kHFSPlusFolderRecord: | |
1029 | if ( theRecSize != sizeof( HFSPlusCatalogFolder ) ) | |
1030 | { | |
1031 | return( -1 ); | |
1032 | } | |
1033 | break; | |
1034 | ||
1035 | case kHFSPlusFileRecord: | |
1036 | if ( theRecSize != sizeof(HFSPlusCatalogFile) ) | |
1037 | { | |
1038 | return( -1 ); | |
1039 | } | |
1040 | break; | |
1041 | ||
1042 | case kHFSPlusFolderThreadRecord: | |
1043 | /* Fall through */ | |
1044 | ||
1045 | case kHFSPlusFileThreadRecord: | |
1046 | if ( theRecSize > sizeof(HFSPlusCatalogThread) || | |
1047 | theRecSize < sizeof(HFSPlusCatalogThread) - sizeof(HFSUniStr255) + sizeof(UniChar) ) | |
1048 | { | |
1049 | return( -1 ); | |
1050 | } | |
1051 | break; | |
1052 | ||
1053 | default: | |
1054 | return( -1 ); | |
1055 | } | |
1056 | } | |
1057 | else | |
1058 | { | |
1059 | switch ( theRecPtr->recordType ) | |
1060 | { | |
1061 | case kHFSFolderRecord: | |
1062 | if ( theRecSize != sizeof(HFSCatalogFolder) ) | |
1063 | return( -1 ); | |
1064 | break; | |
1065 | ||
1066 | case kHFSFileRecord: | |
1067 | if ( theRecSize != sizeof(HFSCatalogFile) ) | |
1068 | return( -1 ); | |
1069 | break; | |
1070 | ||
1071 | case kHFSFolderThreadRecord: | |
1072 | /* Fall through */ | |
1073 | case kHFSFileThreadRecord: | |
1074 | if ( theRecSize != sizeof(HFSCatalogThread)) | |
1075 | return( -1 ); | |
1076 | break; | |
1077 | ||
1078 | default: | |
1079 | return( -1 ); | |
1080 | } | |
1081 | } | |
1082 | ||
1083 | return( noErr ); | |
1084 | ||
1085 | } /* ValidateCatalogRecordLength */ | |
1086 | ||
1087 | ||
1088 | #if DEBUG_REBUILD | |
1089 | static void PrintNodeDescriptor( NodeDescPtr thePtr ) | |
1090 | { | |
1091 | plog( "\n xxxxxxxx BTNodeDescriptor xxxxxxxx \n" ); | |
1092 | plog( " fLink %d \n", thePtr->fLink ); | |
1093 | plog( " bLink %d \n", thePtr->bLink ); | |
1094 | plog( " kind %d ", thePtr->kind ); | |
1095 | if ( thePtr->kind == kBTLeafNode ) | |
1096 | plog( "%s \n", "kBTLeafNode" ); | |
1097 | else if ( thePtr->kind == kBTIndexNode ) | |
1098 | plog( "%s \n", "kBTIndexNode" ); | |
1099 | else if ( thePtr->kind == kBTHeaderNode ) | |
1100 | plog( "%s \n", "kBTHeaderNode" ); | |
1101 | else if ( thePtr->kind == kBTMapNode ) | |
1102 | plog( "%s \n", "kBTMapNode" ); | |
1103 | else | |
1104 | plog( "do not know?? \n" ); | |
1105 | plog( " height %d \n", thePtr->height ); | |
1106 | plog( " numRecords %d \n", thePtr->numRecords ); | |
1107 | ||
1108 | } /* PrintNodeDescriptor */ | |
1109 | ||
1110 | ||
1111 | static void PrintBTHeaderRec( BTHeaderRec * thePtr ) | |
1112 | { | |
1113 | plog( "\n xxxxxxxx BTHeaderRec xxxxxxxx \n" ); | |
1114 | plog( " treeDepth %d \n", thePtr->treeDepth ); | |
1115 | plog( " rootNode %d \n", thePtr->rootNode ); | |
1116 | plog( " leafRecords %d \n", thePtr->leafRecords ); | |
1117 | plog( " firstLeafNode %d \n", thePtr->firstLeafNode ); | |
1118 | plog( " lastLeafNode %d \n", thePtr->lastLeafNode ); | |
1119 | plog( " nodeSize %d \n", thePtr->nodeSize ); | |
1120 | plog( " maxKeyLength %d \n", thePtr->maxKeyLength ); | |
1121 | plog( " totalNodes %d \n", thePtr->totalNodes ); | |
1122 | plog( " freeNodes %d \n", thePtr->freeNodes ); | |
1123 | plog( " clumpSize %d \n", thePtr->clumpSize ); | |
1124 | plog( " btreeType 0x%02X \n", thePtr->btreeType ); | |
1125 | plog( " attributes 0x%02X \n", thePtr->attributes ); | |
1126 | ||
1127 | } /* PrintBTHeaderRec */ | |
1128 | ||
1129 | ||
1130 | static void PrintBTreeKey( KeyPtr thePtr, BTreeControlBlock * theBTreeCBPtr ) | |
1131 | { | |
1132 | int myKeyLength, i; | |
1133 | UInt8 * myPtr = (UInt8 *)thePtr; | |
1134 | char ascii[17]; | |
1135 | UInt8 byte; | |
1136 | ||
1137 | ascii[16] = '\0'; | |
1138 | ||
1139 | myKeyLength = CalcKeySize( theBTreeCBPtr, thePtr) ; | |
1140 | plog( "\n xxxxxxxx BTreeKey (length %d) xxxxxxxx \n", myKeyLength ); | |
1141 | for ( i = 0; i < myKeyLength; i++ ) | |
1142 | { | |
1143 | byte = *(myPtr + i); | |
1144 | plog( "%02X ", byte ); | |
1145 | if (byte < 32 || byte > 126) | |
1146 | ascii[i & 0xF] = '.'; | |
1147 | else | |
1148 | ascii[i & 0xF] = byte; | |
1149 | ||
1150 | if ((i & 0xF) == 0xF) | |
1151 | { | |
1152 | plog(" %s\n", ascii); | |
1153 | } | |
1154 | } | |
1155 | ||
1156 | if (i & 0xF) | |
1157 | { | |
1158 | int j; | |
1159 | for (j = i & 0xF; j < 16; ++j) | |
1160 | plog(" "); | |
1161 | ascii[i & 0xF] = 0; | |
1162 | plog(" %s\n", ascii); | |
1163 | } | |
1164 | } /* PrintBTreeKey */ | |
1165 | ||
1166 | static void PrintBTreeData(void *data, UInt32 length) | |
1167 | { | |
1168 | UInt32 i; | |
1169 | UInt8 * myPtr = (UInt8 *)data; | |
1170 | char ascii[17]; | |
1171 | UInt8 byte; | |
1172 | ||
1173 | ascii[16] = '\0'; | |
1174 | ||
1175 | plog( "\n xxxxxxxx BTreeData (length %d) xxxxxxxx \n", length ); | |
1176 | for ( i = 0; i < length; i++ ) | |
1177 | { | |
1178 | byte = *(myPtr + i); | |
1179 | plog( "%02X ", byte ); | |
1180 | if (byte < 32 || byte > 126) | |
1181 | ascii[i & 0xF] = '.'; | |
1182 | else | |
1183 | ascii[i & 0xF] = byte; | |
1184 | ||
1185 | if ((i & 0xF) == 0xF) | |
1186 | { | |
1187 | plog(" %s\n", ascii); | |
1188 | } | |
1189 | } | |
1190 | ||
1191 | if (i & 0xF) | |
1192 | { | |
1193 | int j; | |
1194 | for (j = i & 0xF; j < 16; ++j) | |
1195 | plog(" "); | |
1196 | ascii[i & 0xF] = 0; | |
1197 | plog(" %s\n", ascii); | |
1198 | } | |
1199 | } | |
1200 | ||
1201 | static void PrintIndexNodeRec( UInt32 theNodeNum ) | |
1202 | { | |
1203 | plog( "\n xxxxxxxx IndexNodeRec xxxxxxxx \n" ); | |
1204 | plog( " node number %d \n", theNodeNum ); | |
1205 | ||
1206 | } /* PrintIndexNodeRec */ | |
1207 | ||
1208 | static void PrintLeafNodeRec( HFSPlusCatalogFolder * thePtr ) | |
1209 | { | |
1210 | plog( "\n xxxxxxxx LeafNodeRec xxxxxxxx \n" ); | |
1211 | plog( " recordType %d ", thePtr->recordType ); | |
1212 | if ( thePtr->recordType == kHFSPlusFolderRecord ) | |
1213 | plog( "%s \n", "kHFSPlusFolderRecord" ); | |
1214 | else if ( thePtr->recordType == kHFSPlusFileRecord ) | |
1215 | plog( "%s \n", "kHFSPlusFileRecord" ); | |
1216 | else if ( thePtr->recordType == kHFSPlusFolderThreadRecord ) | |
1217 | plog( "%s \n", "kHFSPlusFolderThreadRecord" ); | |
1218 | else if ( thePtr->recordType == kHFSPlusFileThreadRecord ) | |
1219 | plog( "%s \n", "kHFSPlusFileThreadRecord" ); | |
1220 | else | |
1221 | plog( "do not know?? \n" ); | |
1222 | ||
1223 | } /* PrintLeafNodeRec */ | |
1224 | ||
1225 | ||
1226 | #endif // DEBUG_REBUILD |