1/* 2 * Copyright (c) 1999-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: SRepair.c 25 26 Contains: This file contains the Scavenger repair routines. 27 28 Written by: Bill Bruffey 29 30 Copyright: � 1986, 1990, 1992-1999 by Apple Computer, Inc., all rights reserved. 31 32*/ 33 34#include "Scavenger.h" 35#include <unistd.h> 36#include <sys/stat.h> 37#include <stdlib.h> 38#include <stddef.h> 39#include "../cache.h" 40 41enum { 42 clearBlocks, 43 addBitmapBit, 44 deleteExtents 45}; 46 47/* internal routine prototypes */ 48 49static int MRepair( SGlobPtr GPtr ); 50void SetOffset (void *buffer, UInt16 btNodeSize, SInt16 recOffset, SInt16 vecOffset); 51#define SetOffset(buffer,nodesize,offset,record) (*(SInt16 *) ((Byte *) (buffer) + (nodesize) + (-2 * (record))) = (offset)) 52static OSErr UpdateBTreeHeader( SFCB * fcbPtr ); 53static OSErr FixBTreeHeaderReservedFields( SGlobPtr GPtr, short refNum ); 54static OSErr UpdBTM( SGlobPtr GPtr, short refNum); 55static OSErr UpdateVolumeBitMap( SGlobPtr GPtr, Boolean preAllocateOverlappedExtents ); 56static OSErr DoMinorOrders( SGlobPtr GPtr ); 57static OSErr UpdVal( SGlobPtr GPtr, RepairOrderPtr rP ); 58static int DelFThd( SGlobPtr GPtr, UInt32 fid ); 59static OSErr FixDirThread( SGlobPtr GPtr, UInt32 did ); 60static OSErr FixOrphanedFiles ( SGlobPtr GPtr ); 61static OSErr RepairReservedBTreeFields ( SGlobPtr GPtr ); 62static OSErr GetCatalogRecord(SGlobPtr GPtr, UInt32 fileID, Boolean isHFSPlus, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize); 63static OSErr RepairAttributesCheckABT(SGlobPtr GPtr, Boolean isHFSPlus); 64static OSErr RepairAttributesCheckCBT(SGlobPtr GPtr, Boolean isHFSPlus); 65static OSErr RepairAttributes( SGlobPtr GPtr ); 66static OSErr FixFinderFlags( SGlobPtr GPtr, RepairOrderPtr p ); 67static OSErr FixLinkCount( SGlobPtr GPtr, RepairOrderPtr p ); 68static OSErr FixLinkChainNext( SGlobPtr GPtr, RepairOrderPtr p ); 69static OSErr FixLinkChainPrev( SGlobPtr GPtr, RepairOrderPtr p ); 70static OSErr FixBSDInfo( SGlobPtr GPtr, RepairOrderPtr p ); 71static OSErr DeleteUnlinkedFile( SGlobPtr GPtr, RepairOrderPtr p ); 72static OSErr FixOrphanedExtent( SGlobPtr GPtr ); 73static OSErr FixFileSize(SGlobPtr GPtr, RepairOrderPtr p); 74static OSErr VolumeObjectFixVHBorMDB( Boolean * fixedIt ); 75static OSErr VolumeObjectRestoreWrapper( void ); 76static OSErr FixBloatedThreadRecords( SGlob *GPtr ); 77static OSErr FixMissingThreadRecords( SGlob *GPtr ); 78static OSErr FixEmbededVolDescription( SGlobPtr GPtr, RepairOrderPtr p ); 79static OSErr FixWrapperExtents( SGlobPtr GPtr, RepairOrderPtr p ); 80static OSErr FixIllegalNames( SGlobPtr GPtr, RepairOrderPtr roPtr ); 81static HFSCatalogNodeID GetObjectID( CatalogRecord * theRecPtr ); 82static OSErr FixMissingDirectory( SGlob *GPtr, UInt32 theObjID, UInt32 theParID ); 83static OSErr FixAttrSize(SGlobPtr GPtr, RepairOrderPtr p); 84static OSErr FixOrphanAttrRecord(SGlobPtr GPtr); 85static OSErr FixBadExtent(SGlobPtr GPtr, RepairOrderPtr p); 86static OSErr FixHardLinkFinderInfo(SGlobPtr, RepairOrderPtr); 87static OSErr FixOrphanLink(SGlobPtr GPtr, RepairOrderPtr p); 88static OSErr FixOrphanInode(SGlobPtr GPtr, RepairOrderPtr p); 89static OSErr FixDirLinkOwnerFlags(SGlobPtr GPtr, RepairOrderPtr p); 90static int DeleteCatalogRecordByID(SGlobPtr GPtr, uint32_t id, Boolean for_rename); 91static int MoveCatalogRecordByID(SGlobPtr GPtr, uint32_t id, uint32_t new_parentid); 92static int DeleteAllAttrsByID(SGlobPtr GPtr, uint32_t id); 93static int delete_attr_record(SGlobPtr GPtr, HFSPlusAttrKey *attr_key, HFSPlusAttrRecord *attr_record); 94static int ZeroFillUnusedNodes(SGlobPtr GPtr, short fileRefNum); 95 96/* Functions to fix overlapping extents */ 97static OSErr FixOverlappingExtents(SGlobPtr GPtr); 98static int CompareExtentBlockCount(const void *first, const void *second); 99static OSErr MoveExtent(SGlobPtr GPtr, ExtentInfo *extentInfo); 100static OSErr CreateCorruptFileSymlink(SGlobPtr GPtr, UInt32 fileID); 101static OSErr SearchExtentInAttributeBT(SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord, UInt16 *recordSize, UInt32 *foundExtentIndex); 102static OSErr UpdateExtentInAttributeBT (SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord, UInt16 *recordSize, UInt32 foundInExtentIndex); 103static OSErr SearchExtentInVH(SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 *foundExtentIndex, Boolean *noMoreExtents); 104static OSErr UpdateExtentInVH (SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 foundExtentIndex); 105static OSErr SearchExtentInCatalogBT(SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 *foundExtentIndex, Boolean *noMoreExtents); 106static OSErr UpdateExtentInCatalogBT (SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 foundExtentIndex); 107static OSErr SearchExtentInExtentBT(SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusExtentKey *extentKey, HFSPlusExtentRecord *extentRecord, UInt16 *recordSize, UInt32 *foundExtentIndex); 108static OSErr FindExtentInExtentRec (Boolean isHFSPlus, UInt32 startBlock, UInt32 blockCount, const HFSPlusExtentRecord extentData, UInt32 *foundExtentIndex, Boolean *noMoreExtents); 109 110/* Functions to copy disk blocks or data buffer to disk */ 111static OSErr CopyDiskBlocks(SGlobPtr GPtr, const UInt32 startAllocationBlock, const UInt32 blockCount, const UInt32 newStartAllocationBlock ); 112static OSErr WriteBufferToDisk(SGlobPtr GPtr, UInt32 startBlock, UInt32 blockCount, u_char *buffer, int buflen); 113 114/* Functions to create file and directory by name */ 115static OSErr CreateFileByName(SGlobPtr GPtr, UInt32 parentID, UInt16 fileType, u_char *fileName, unsigned int filenameLen, u_char *data, unsigned int dataLen); 116static UInt32 CreateDirByName(SGlob *GPtr , const u_char *dirName, const UInt32 parentID); 117 118static int BuildFolderRec( SGlob*, u_int16_t theMode, UInt32 theObjID, Boolean isHFSPlus, CatalogRecord * theRecPtr ); 119static int BuildThreadRec( CatalogKey * theKeyPtr, CatalogRecord * theRecPtr, Boolean isHFSPlus, Boolean isDirectory ); 120static int BuildFileRec(UInt16 fileType, UInt16 fileMode, UInt32 fileID, Boolean isHFSPlus, CatalogRecord *catRecord); 121static void BuildAttributeKey(u_int32_t fileID, u_int32_t startBlock, unsigned char *attrName, u_int16_t attrNameLen, HFSPlusAttrKey *key); 122 123 124OSErr RepairVolume( SGlobPtr GPtr ) 125{ 126 OSErr err; 127 128 SetDFAStage( kAboutToRepairStage ); // Notify callers repair is starting... 129 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 130 131 // 132 // Do the repair 133 // 134 SetDFAStage( kRepairStage ); // Stops GNE from being called, and changes behavior of MountCheck 135 136 err = MRepair( GPtr ); 137 138 return( err ); 139} 140 141 142/*------------------------------------------------------------------------------ 143Routine: MRepair - (Minor Repair) 144Function: Performs minor repair operations. 145Input: GPtr - pointer to scavenger global area 146Output: MRepair - function result: 147------------------------------------------------------------------------------*/ 148 149static int MRepair( SGlobPtr GPtr ) 150{ 151 OSErr err; 152 SVCB *calculatedVCB = GPtr->calculatedVCB; 153 Boolean isHFSPlus; 154 Boolean didRebuild = false; 155 156 isHFSPlus = VolumeObjectIsHFSPlus( ); 157 158 if ( GPtr->EBTStat & S_RebuildBTree ) 159 { 160 fsckPrint(GPtr->context, hfsRebuildExtentBTree); 161 err = RebuildBTree( GPtr, kHFSExtentsFileID ); 162 if (err) 163 return (err); 164 didRebuild = true; 165 } 166 167 if ( GPtr->CBTStat & S_RebuildBTree ) 168 { 169 /* once we do the rebuild we will force another verify since the */ 170 /* first verify was aborted when we determined a rebuild was necessary */ 171 fsckPrint(GPtr->context, hfsRebuildCatalogBTree); 172 err = RebuildBTree( GPtr, kHFSCatalogFileID ); 173 if (err) 174 return (err); 175 didRebuild = true; 176 } 177 178 if ( GPtr->ABTStat & S_RebuildBTree ) 179 { 180 fsckPrint(GPtr->context, hfsRebuildAttrBTree); 181 err = RebuildBTree( GPtr, kHFSAttributesFileID ); 182 if (err) 183 return (err); 184 didRebuild = true; 185 } 186 187 if (didRebuild) 188 return noErr; // Need to restart the verification 189 190 /* 191 * If there were unused nodes in the B-trees which were non-zero-filled, 192 * then zero fill them. 193 */ 194 if (GPtr->ABTStat & S_UnusedNodesNotZero) 195 { 196 err = ZeroFillUnusedNodes(GPtr, kCalculatedAttributesRefNum); 197 ReturnIfError(err); 198 } 199 if (GPtr->EBTStat & S_UnusedNodesNotZero) 200 { 201 err = ZeroFillUnusedNodes(GPtr, kCalculatedExtentRefNum); 202 ReturnIfError(err); 203 } 204 if (GPtr->CBTStat & S_UnusedNodesNotZero) 205 { 206 err = ZeroFillUnusedNodes(GPtr, kCalculatedCatalogRefNum); 207 ReturnIfError(err); 208 } 209 if ((calculatedVCB->vcbAttributes & kHFSUnusedNodeFixMask) == 0) 210 { 211 calculatedVCB->vcbAttributes |= kHFSUnusedNodeFixMask; 212 MarkVCBDirty(calculatedVCB); 213 } 214 215 /* 216 * We do this check here because it may make set up some minor repair orders; 217 * however, because determining the repairs to be done is expensive, we have only 218 * checked to see if there is any sort of problem so far. 219 * 220 * After it's done, DoMinorOrders() will take care of any requests that have been 221 * set up. 222 */ 223 if (GPtr->CatStat & S_FileHardLinkChain) { 224 err = RepairHardLinkChains(GPtr, false); 225 ReturnIfError(err); 226 } 227 228 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 229 230 if (GPtr->CatStat & S_DirHardLinkChain) { 231 err = RepairHardLinkChains(GPtr, true); 232 ReturnIfError(err); 233 } 234 235 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 236 // Handle repair orders. Note that these must be done *BEFORE* the MDB is updated. 237 err = DoMinorOrders( GPtr ); 238 ReturnIfError( err ); 239 err = CheckForStop( GPtr ); ReturnIfError( err ); 240 241 /* Clear Catalog status for things repaired by DoMinorOrders */ 242 GPtr->CatStat &= ~(S_FileAllocation | S_Permissions | S_UnlinkedFile | S_LinkCount | S_IllName | S_BadExtent | S_LinkErrRepair | S_FileHardLinkChain | S_DirHardLinkChain); 243 244 /* 245 * Fix missing thread records 246 */ 247 if (GPtr->CatStat & S_MissingThread) { 248 err = FixMissingThreadRecords(GPtr); 249 ReturnIfError(err); 250 251 GPtr->CatStat &= ~S_MissingThread; 252 GPtr->CBTStat |= S_BTH; /* leaf record count changed */ 253 } 254 255 // 2210409, in System 8.1, moving file or folder would cause HFS+ thread records to be 256 // 520 bytes in size. We only shrink the threads if other repairs are needed. 257 if ( GPtr->VeryMinorErrorsStat & S_BloatedThreadRecordFound ) 258 { 259 (void) FixBloatedThreadRecords( GPtr ); 260 GPtr->VeryMinorErrorsStat &= ~S_BloatedThreadRecordFound; 261 } 262 263 // 264 // we will update the following data structures regardless of whether we have done 265 // major or minor repairs, so we might end up doing this multiple times. Look into this. 266 // 267 268 // 269 // Isolate and fix Overlapping Extents 270 // 271 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 272 273 if ( (GPtr->VIStat & S_OverlappingExtents) != 0 ) 274 { 275 if (embedded == 1 && debug == 0) 276 return R_RFail; 277 278 err = FixOverlappingExtents( GPtr ); // Isolate and fix Overlapping Extents 279 ReturnIfError( err ); 280 281 GPtr->VIStat &= ~S_OverlappingExtents; 282 GPtr->VIStat |= S_VBM; // Now that we changed the extents, we need to rebuild the bitmap 283 InvalidateCalculatedVolumeBitMap( GPtr ); // Invalidate our BitMap 284 } 285 286 // 287 // FixOrphanedFiles 288 // 289 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 290 291 if ( (GPtr->CBTStat & S_Orphan) != 0 ) 292 { 293 err = FixOrphanedFiles ( GPtr ); // Orphaned file were found 294 ReturnIfError( err ); 295 GPtr->CBTStat |= S_BTH; // leaf record count may change - 2913311 296 } 297 298 /* Some minor repairs would have failed at the first 299 * attempt because of missing thread record or missing 300 * file/folder record because of ordering of repairs 301 * (example, deletion of file/folder before setting 302 * the flag). If any minor repairs orders are left, 303 * try to repair them again after fixing incorrect 304 * number of thread records. 305 */ 306 if (GPtr->MinorRepairsP) { 307 err = DoMinorOrders(GPtr); 308 ReturnIfError( err ); 309 } 310 311 // 312 // FixOrphanedExtent records 313 // 314 if ( (GPtr->EBTStat & S_OrphanedExtent) != 0 ) // Orphaned extents were found 315 { 316 err = FixOrphanedExtent( GPtr ); 317 GPtr->EBTStat &= ~S_OrphanedExtent; 318 // if ( err == errRebuildBtree ) 319 // goto RebuildBtrees; 320 ReturnIfError( err ); 321 } 322 323 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 324 325 // 326 // Update the extent BTree header and bit map 327 // 328 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 329 330 if ( (GPtr->EBTStat & S_BTH) || (GPtr->EBTStat & S_ReservedBTH) ) 331 { 332 err = UpdateBTreeHeader( GPtr->calculatedExtentsFCB ); // update extent file BTH 333 334 if ( (err == noErr) && (GPtr->EBTStat & S_ReservedBTH) ) 335 { 336 err = FixBTreeHeaderReservedFields( GPtr, kCalculatedExtentRefNum ); 337 } 338 339 ReturnIfError( err ); 340 } 341 342 343 if ( (GPtr->EBTStat & S_BTM) != 0 ) 344 { 345 err = UpdBTM( GPtr, kCalculatedExtentRefNum ); // update extent file BTM 346 ReturnIfError( err ); 347 } 348 349 // 350 // Update the catalog BTree header and bit map 351 // 352 353 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 354 355 if ( (GPtr->CBTStat & S_BTH) || (GPtr->CBTStat & S_ReservedBTH) ) 356 { 357 err = UpdateBTreeHeader( GPtr->calculatedCatalogFCB ); // update catalog BTH 358 359 if ( (err == noErr) && (GPtr->CBTStat & S_ReservedBTH) ) 360 { 361 err = FixBTreeHeaderReservedFields( GPtr, kCalculatedCatalogRefNum ); 362 } 363 364 ReturnIfError( err ); 365 } 366 367 if ( GPtr->CBTStat & S_BTM ) 368 { 369 err = UpdBTM( GPtr, kCalculatedCatalogRefNum ); // update catalog BTM 370 ReturnIfError( err ); 371 } 372 373 if ( (GPtr->CBTStat & S_ReservedNotZero) != 0 ) 374 { 375 err = RepairReservedBTreeFields( GPtr ); // update catalog fields 376 ReturnIfError( err ); 377 } 378 379 // Repair orphaned/invalid attribute records 380 if ( (GPtr->ABTStat & S_AttrRec) ) 381 { 382 err = FixOrphanAttrRecord( GPtr ); 383 ReturnIfError( err ); 384 } 385 386 // Repair inconsistency of attribute btree and corresponding bits in 387 // catalog btree 388 if ( (GPtr->ABTStat & S_AttributeCount) || 389 (GPtr->ABTStat & S_SecurityCount)) 390 { 391 err = RepairAttributes( GPtr ); 392 ReturnIfError( err ); 393 } 394 395 // Update the attribute BTree header and bit map 396 if ( (GPtr->ABTStat & S_BTH) ) 397 { 398 err = UpdateBTreeHeader( GPtr->calculatedAttributesFCB ); // update attribute BTH 399 ReturnIfError( err ); 400 } 401 402 if ( GPtr->ABTStat & S_BTM ) 403 { 404 err = UpdBTM( GPtr, kCalculatedAttributesRefNum ); // update attribute BTM 405 ReturnIfError( err ); 406 } 407 408 /* Extended attribute repair can also detect incorrect number 409 * of thread records, so trigger thread records repair now and 410 * come back again in next pass for any fallouts and/or repairing 411 * extended attribute inconsistency. 412 * Note: This should be removed when Chinese Remainder Theorem 413 * is used for detecting incorrect number of thread records 414 * (rdar://3968148). 415 */ 416 if ( (GPtr->CBTStat & S_Orphan) != 0 ) 417 { 418 err = FixOrphanedFiles ( GPtr ); 419 ReturnIfError( err ); 420 } 421 422 // 423 // Update the volume bit map 424 // 425 // Note, moved volume bit map update to end after other repairs 426 // (except the MDB / VolumeHeader) have been completed 427 // 428 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 429 430 if ( (GPtr->VIStat & S_VBM) != 0 ) 431 { 432 err = UpdateVolumeBitMap( GPtr, false ); // update VolumeBitMap 433 ReturnIfError( err ); 434 InvalidateCalculatedVolumeBitMap( GPtr ); // Invalidate our BitMap 435 } 436 437 // 438 // Fix missing Primary or Alternate VHB or MDB 439 // 440 441 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 442 443 if ( (GPtr->VIStat & S_MDB) != 0 ) // fix MDB / VolumeHeader 444 { 445 Boolean fixedIt = false; 446 err = VolumeObjectFixVHBorMDB( &fixedIt ); 447 ReturnIfError( err ); 448 // don't call FlushAlternateVolumeControlBlock if we fixed it since that would 449 // mean our calculated VCB has not been completely set up. 450 if ( fixedIt ) { 451 GPtr->VIStat &= ~S_MDB; 452 MarkVCBClean( calculatedVCB ); 453 } 454 } 455 456 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 457 458 if ( (GPtr->VIStat & S_WMDB) != 0 ) // fix wrapper MDB 459 { 460 err = VolumeObjectRestoreWrapper(); 461 ReturnIfError( err ); 462 } 463 464 // 465 // Update the MDB / VolumeHeader 466 // 467 // Note, moved MDB / VolumeHeader update to end 468 // after all other repairs have been completed. 469 // 470 471 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 472 473 if ( (GPtr->VIStat & S_MDB) != 0 || IsVCBDirty(calculatedVCB) ) // update MDB / VolumeHeader 474 { 475 MarkVCBDirty(calculatedVCB); // make sure its dirty 476 calculatedVCB->vcbAttributes |= kHFSVolumeUnmountedMask; 477 err = FlushAlternateVolumeControlBlock( calculatedVCB, isHFSPlus ); // Writes real & alt blocks 478 ReturnIfError( err ); 479 } 480 481 err = CheckForStop( GPtr ); ReturnIfError( err ); // Permit the user to interrupt 482 483 // if we had minor repairs that failed we still want to fix as much as possible 484 // so we wait until now to indicate the volume still has problems 485 if ( GPtr->minorRepairErrors ) 486 err = R_RFail; 487 488 return( err ); // all done 489} 490 491 492 493// 494// Internal Routines 495// 496 497//������������������������������������������������������������������������������� 498// Routine: VolumeObjectFixVHBorMDB 499// 500// Function: When the primary or alternate Volume Header Block or Master 501// Directory Block is damaged or missing use the undamaged one to 502// restore the other. 503//������������������������������������������������������������������������������� 504 505static OSErr VolumeObjectFixVHBorMDB( Boolean* fixedItPtr ) 506{ 507 OSErr err; 508 OSErr err2; 509 VolumeObjectPtr myVOPtr; 510 BlockDescriptor myPrimary; 511 BlockDescriptor myAlternate; 512 513 myVOPtr = GetVolumeObjectPtr( ); 514 myPrimary.buffer = NULL; 515 myAlternate.buffer = NULL; 516 err = noErr; 517 518 // bail if both are OK 519 if ( VolumeObjectIsHFS() ) { 520 if ( (myVOPtr->flags & kVO_PriMDBOK) != 0 && 521 (myVOPtr->flags & kVO_AltMDBOK) != 0 ) 522 goto ExitThisRoutine; 523 } 524 else { 525 if ( (myVOPtr->flags & kVO_PriVHBOK) != 0 && 526 (myVOPtr->flags & kVO_AltVHBOK) != 0 ) 527 goto ExitThisRoutine; 528 } 529 530 // it's OK if one of the primary or alternate is invalid 531 err = GetVolumeObjectPrimaryBlock( &myPrimary ); 532 if ( !(err == noErr || err == badMDBErr || err == noMacDskErr) ) 533 goto ExitThisRoutine; 534 535 // invalidate if we have not marked the primary as OK 536 if ( VolumeObjectIsHFS( ) ) { 537 if ( (myVOPtr->flags & kVO_PriMDBOK) == 0 ) 538 err = badMDBErr; 539 } 540 else if ( (myVOPtr->flags & kVO_PriVHBOK) == 0 ) { 541 err = badMDBErr; 542 } 543 544 err2 = GetVolumeObjectAlternateBlock( &myAlternate ); 545 if ( !(err2 == noErr || err2 == badMDBErr || err2 == noMacDskErr) ) 546 goto ExitThisRoutine; 547 548 // invalidate if we have not marked the alternate as OK 549 if ( VolumeObjectIsHFS( ) ) { 550 if ( (myVOPtr->flags & kVO_AltMDBOK) == 0 ) 551 err2 = badMDBErr; 552 } 553 else if ( (myVOPtr->flags & kVO_AltVHBOK) == 0 ) { 554 err2 = badMDBErr; 555 } 556 557 // primary is OK so use it to restore alternate 558 if ( err == noErr ) { 559 CopyMemory( myPrimary.buffer, myAlternate.buffer, Blk_Size ); 560 (void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kForceWriteBlock ); 561 myAlternate.buffer = NULL; 562 *fixedItPtr = true; 563 if ( VolumeObjectIsHFS( ) ) 564 myVOPtr->flags |= kVO_AltMDBOK; 565 else 566 myVOPtr->flags |= kVO_AltVHBOK; 567 } 568 // alternate is OK so use it to restore the primary 569 else if ( err2 == noErr ) { 570 CopyMemory( myAlternate.buffer, myPrimary.buffer, Blk_Size ); 571 (void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kForceWriteBlock ); 572 myPrimary.buffer = NULL; 573 *fixedItPtr = true; 574 if ( VolumeObjectIsHFS( ) ) 575 myVOPtr->flags |= kVO_PriMDBOK; 576 else 577 myVOPtr->flags |= kVO_PriVHBOK; 578 err = noErr; 579 } 580 else 581 err = noMacDskErr; 582 583ExitThisRoutine: 584 if ( myPrimary.buffer != NULL ) 585 (void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kReleaseBlock ); 586 if ( myAlternate.buffer != NULL ) 587 (void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kReleaseBlock ); 588 589 return( err ); 590 591} /* VolumeObjectFixVHBorMDB */ 592 593 594//������������������������������������������������������������������������������� 595// Routine: VolumeObjectRestoreWrapper 596// 597// Function: When the primary or alternate Master Directory Block is damaged 598// or missing use the undamaged one to restore the other. 599//������������������������������������������������������������������������������� 600 601static OSErr VolumeObjectRestoreWrapper( void ) 602{ 603 OSErr err; 604 OSErr err2; 605 VolumeObjectPtr myVOPtr; 606 BlockDescriptor myPrimary; 607 BlockDescriptor myAlternate; 608 609 myVOPtr = GetVolumeObjectPtr( ); 610 myPrimary.buffer = NULL; 611 myAlternate.buffer = NULL; 612 613 // it's OK if one of the MDB is invalid 614 err = GetVolumeObjectPrimaryMDB( &myPrimary ); 615 if ( !(err == noErr || err == badMDBErr || err == noMacDskErr) ) 616 goto ExitThisRoutine; 617 err2 = GetVolumeObjectAlternateMDB( &myAlternate ); 618 if ( !(err2 == noErr || err2 == badMDBErr || err2 == noMacDskErr) ) 619 goto ExitThisRoutine; 620 621 // primary is OK so use it to restore alternate 622 if ( err == noErr && (myVOPtr->flags & kVO_PriMDBOK) != 0 ) { 623 CopyMemory( myPrimary.buffer, myAlternate.buffer, Blk_Size ); 624 (void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kForceWriteBlock ); 625 myAlternate.buffer = NULL; 626 myVOPtr->flags |= kVO_AltMDBOK; 627 } 628 // alternate is OK so use it to restore the primary 629 else if ( err2 == noErr && (myVOPtr->flags & kVO_AltMDBOK) != 0 ) { 630 CopyMemory( myAlternate.buffer, myPrimary.buffer, Blk_Size ); 631 (void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kForceWriteBlock ); 632 myPrimary.buffer = NULL; 633 myVOPtr->flags |= kVO_PriMDBOK; 634 err = noErr; 635 } 636 else 637 err = noMacDskErr; 638 639ExitThisRoutine: 640 if ( myPrimary.buffer != NULL ) 641 (void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myPrimary, kReleaseBlock ); 642 if ( myAlternate.buffer != NULL ) 643 (void) ReleaseVolumeBlock( myVOPtr->vcbPtr, &myAlternate, kReleaseBlock ); 644 645 return( err ); 646 647} /* VolumeObjectRestoreWrapper */ 648 649 650/*------------------------------------------------------------------------------ 651Routine: UpdateBTreeHeader - (Update BTree Header) 652 653Function: Replaces a BTH on disk with info from a scavenger BTCB. 654 655Input: GPtr - pointer to scavenger global area 656 refNum - file refnum 657 658Output: UpdateBTreeHeader - function result: 659 0 = no error 660 n = error 661------------------------------------------------------------------------------*/ 662 663static OSErr UpdateBTreeHeader( SFCB * fcbPtr ) 664{ 665 OSErr err; 666 667 M_BTreeHeaderDirty( ((BTreeControlBlockPtr) fcbPtr->fcbBtree) ); 668 err = BTFlushPath( fcbPtr ); 669 670 return( err ); 671 672} /* End UpdateBTreeHeader */ 673 674 675 676/*------------------------------------------------------------------------------ 677Routine: FixBTreeHeaderReservedFields 678 679Function: Fix reserved fields in BTree Header 680 681Input: GPtr - pointer to scavenger global area 682 refNum - file refnum 683 684Output: 0 = no error 685 n = error 686------------------------------------------------------------------------------*/ 687 688static OSErr FixBTreeHeaderReservedFields( SGlobPtr GPtr, short refNum ) 689{ 690 OSErr err; 691 BTHeaderRec header; 692 693 err = GetBTreeHeader(GPtr, ResolveFCB(refNum), &header); 694 ReturnIfError( err ); 695 696 if ( (header.clumpSize % GPtr->calculatedVCB->vcbBlockSize) != 0 ) 697 header.clumpSize = GPtr->calculatedVCB->vcbBlockSize; 698 699 header.reserved1 = 0; 700 header.btreeType = kHFSBTreeType; // control file 701/* 702 * TBD - we'll need to repair an invlid keyCompareType field. 703 */ 704#if 0 705 if (-->TBD<--) 706 header.keyCompareType = kHFSBinaryCompare; 707#endif 708 ClearMemory( header.reserved3, sizeof(header.reserved3) ); 709 710 return( err ); 711} 712 713 714 715 716/*------------------------------------------------------------------------------ 717 718Routine: UpdBTM - (Update BTree Map) 719 720Function: Replaces a BTM on disk with a scavenger BTM. 721 722Input: GPtr - pointer to scavenger global area 723 refNum - file refnum 724 725Output: UpdBTM - function result: 726 0 = no error 727 n = error 728------------------------------------------------------------------------------*/ 729 730static OSErr UpdBTM( SGlobPtr GPtr, short refNum ) 731{ 732 OSErr err; 733 UInt16 recSize; 734 SInt32 mapSize; 735 SInt16 size; 736 SInt16 recIndx; 737 Ptr p; 738 Ptr btmP; 739 Ptr sbtmP; 740 UInt32 nodeNum; 741 NodeRec node; 742 UInt32 fLink; 743 BTreeControlBlock *calculatedBTCB = GetBTreeControlBlock( refNum ); 744 745 // Set up 746 mapSize = ((BTreeExtensionsRec*)calculatedBTCB->refCon)->BTCBMSize; 747 748 // 749 // update the map records 750 // 751 if ( mapSize > 0 ) 752 { 753 nodeNum = 0; 754 recIndx = 2; 755 sbtmP = ((BTreeExtensionsRec*)calculatedBTCB->refCon)->BTCBMPtr; 756 757 do 758 { 759 GPtr->TarBlock = nodeNum; // set target node number 760 761 err = GetNode( calculatedBTCB, nodeNum, &node ); 762 ReturnIfError( err ); // could't get map node 763 764 // Locate the map record 765 recSize = GetRecordSize( calculatedBTCB, (BTNodeDescriptor *)node.buffer, recIndx ); 766 btmP = (Ptr)GetRecordAddress( calculatedBTCB, (BTNodeDescriptor *)node.buffer, recIndx ); 767 fLink = ((NodeDescPtr)node.buffer)->fLink; 768 size = ( recSize > mapSize ) ? mapSize : recSize; 769 770 CopyMemory( sbtmP, btmP, size ); // update it 771 772 err = UpdateNode( calculatedBTCB, &node ); // write it, and unlock buffer 773 774 mapSize -= size; // move to next map record 775 if ( mapSize == 0 ) // more to go? 776 break; // no, zero remainder of record 777 if ( fLink == 0 ) // out of bitmap blocks in file? 778 { 779 RcdError( GPtr, E_ShortBTM ); 780 (void) ReleaseNode(calculatedBTCB, &node); 781 return( E_ShortBTM ); 782 } 783 784 nodeNum = fLink; 785 sbtmP += size; 786 recIndx = 0; 787 788 } while ( mapSize > 0 ); 789 790 // clear the unused portion of the map record 791 for ( p = btmP + size ; p < btmP + recSize ; p++ ) 792 *p = 0; 793 794 err = UpdateNode( calculatedBTCB, &node ); // Write it, and unlock buffer 795 } 796 797 return( noErr ); // All done 798} // end UpdBTM 799 800 801 802 803/*------------------------------------------------------------------------------ 804 805Routine: UpdateVolumeBitMap - (Update Volume Bit Map) 806 807Function: Replaces the VBM on disk with the scavenger VBM. 808 809Input: GPtr - pointer to scavenger global area 810 811Output: UpdateVolumeBitMap - function result: 812 0 = no error 813 n = error 814 GPtr->VIStat - S_VBM flag set if VBM is damaged. 815------------------------------------------------------------------------------*/ 816 817static OSErr UpdateVolumeBitMap( SGlobPtr GPtr, Boolean preAllocateOverlappedExtents ) 818{ 819 GPtr->TarID = VBM_FNum; 820 821 return ( CheckVolumeBitMap(GPtr, true) ); 822} 823 824/* 825Routine: FixBadLinkChainFirst - fix the first link in a hard link chain 826 827Input: GPtr -- pointer to scavenger global data 828 p -- pointer to a minor repair order 829 830Output: funciton result: 831 0 -- no error 832 n -- error 833*/ 834 835OSErr FixBadLinkChainFirst(SGlobPtr GPtr, RepairOrderPtr p) 836{ 837 CatalogRecord rec; 838 uint16_t recsize; 839 OSErr retval = 0; 840 HFSPlusAttrData *attrRec; 841 HFSPlusAttrKey *attrKey; 842 BTreeIterator iterator; 843 FSBufferDescriptor bt_data; 844 u_int8_t attrdata[FIRST_LINK_XATTR_REC_SIZE]; 845 size_t unicode_bytes = 0; 846 847 ClearMemory(&iterator, sizeof(iterator)); 848 retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, (CatalogKey*)&iterator.key, &rec, &recsize); 849 if (retval != 0) { 850 if (retval == btNotFound) { 851 /* If the record was not found because either the thread 852 * record is missing or the file/folder record was deleted by 853 * another repair order, return false success to retry again 854 * after thread repair code. 855 */ 856 GPtr->minorRepairFalseSuccess = true; 857 retval = 0; 858 } 859 goto done; 860 } 861 862 switch (rec.recordType) { 863 case kHFSPlusFolderRecord: // directory hard link 864 attrKey = (HFSPlusAttrKey*)&iterator.key; 865 utf_decodestr((unsigned char *)FIRST_LINK_XATTR_NAME, 866 strlen(FIRST_LINK_XATTR_NAME), attrKey->attrName, 867 &unicode_bytes, sizeof(attrKey->attrName)); 868 attrKey->attrNameLen = unicode_bytes / sizeof(UniChar); 869 attrKey->keyLength = kHFSPlusAttrKeyMinimumLength + unicode_bytes; 870 attrKey->pad = 0; 871 attrKey->fileID = p->parid; 872 attrKey->startBlock = 0; 873 attrRec = (HFSPlusAttrData*)&attrdata[0]; 874 attrRec->recordType = kHFSPlusAttrInlineData; 875 attrRec->reserved[0] = 0; 876 attrRec->reserved[1] = 0; 877 (void)snprintf((char*)&attrRec->attrData[0], 878 sizeof(attrdata) - offsetof(HFSPlusAttrData, attrData), 879 "%lu", (unsigned long)(p->correct)); 880 attrRec->attrSize = 1 + strlen((char*)&attrRec->attrData[0]); 881 bt_data.bufferAddress = attrRec; 882 recsize = sizeof(HFSPlusAttrData) - 2 + attrRec->attrSize + ((attrRec->attrSize & 1) ? 1 : 0); 883 bt_data.itemSize = recsize; 884 bt_data.itemCount = 1; 885 886 retval = BTInsertRecord(GPtr->calculatedAttributesFCB, &iterator, &bt_data, recsize); 887 if (retval == btExists) { 888 retval = BTReplaceRecord(GPtr->calculatedAttributesFCB, &iterator, &bt_data, recsize); 889 } 890 891 if (retval) { 892 /* If there is error on inserting a new attribute record 893 * because attribute btree does not exists, print message. 894 */ 895 if ((GPtr->calculatedAttributesFCB->fcbPhysicalSize == 0) && 896 (GPtr->calculatedAttributesFCB->fcbLogicalSize == 0) && 897 (GPtr->calculatedAttributesFCB->fcbClumpSize == 0) && 898 (fsckGetVerbosity(GPtr->context) >= kDebugLog)) { 899 plog ("\tFixBadLinkChainFirst: Attribute btree does not exists.\n"); 900 } 901 } 902 break; 903 case kHFSPlusFileRecord: // file hard link 904 rec.hfsPlusFile.hl_firstLinkID = (UInt32)p->correct; 905 bt_data.bufferAddress = &rec; 906 bt_data.itemSize = recsize; 907 bt_data.itemCount = 1; 908 retval = BTReplaceRecord(GPtr->calculatedCatalogFCB, &iterator, &bt_data, recsize); 909 break; 910 default: 911 retval = IntError(GPtr, R_IntErr); 912 break; 913 } 914done: 915 return retval; 916} 917 918 919/* 920Routine: FixHardLinkBadDate - fix the date of an indirect-node 921 922Input: GPtr -- pointer to scavenger global data 923 p -- pointer to a minor repair order 924 925Output: function result: 926 0 -- no error 927 n -- error 928*/ 929 930OSErr FixHardLinkBadDate(SGlobPtr GPtr, RepairOrderPtr p) 931{ 932 CatalogKey key; 933 CatalogRecord rec; 934 uint16_t recsize; 935 OSErr retval = 0; 936 UInt32 hint; 937 938 retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, &key, &rec, &recsize); 939 940 if (retval == 0) { 941 if (rec.recordType != kHFSPlusFileRecord) { 942 retval = IntError(GPtr, R_IntErr); 943 } else { 944 rec.hfsPlusFile.createDate = p->correct; 945 retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint, &rec, recsize, &hint); 946 } 947 } 948 949 return retval; 950 951} 952 953/* 954Routine: FixFileHardLinkFlag - clear the HardLinkChain flag in a file record 955 956Input: GPtr -- pointer to scavenger global data 957 p -- pointer to minor repair order 958 959Output: function result: 960 0 -- no error 961 n -- error 962*/ 963 964OSErr FixFileHardLinkFlag(SGlobPtr GPtr, RepairOrderPtr p) 965{ 966 CatalogKey key; 967 CatalogRecord rec; 968 uint16_t recsize; 969 OSErr retval = 0; 970 UInt32 hint; 971 972 retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, &key, &rec, &recsize); 973 974 if (retval == 0) { 975 if (rec.recordType != kHFSPlusFileRecord) { 976 retval = IntError(GPtr, R_IntErr); 977 } else { 978 rec.hfsPlusFile.flags &= ~kHFSHasLinkChainMask; 979 retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint, &rec, recsize, &hint); 980 } 981 } 982 return retval; 983} 984 985/* 986Routine: FixPrivDirBadPerms - fix the permissions of the directory hard-link private dir 987 988Input: GPtr -- pointer to scavenger global data 989 p -- poitner to a minor repair order 990 991Output: function result: 992 0 -- no error 993 n -- error 994*/ 995 996static OSErr FixPrivDirBadPerms(SGlobPtr GPtr, RepairOrderPtr p) 997{ 998 CatalogKey key; 999 CatalogRecord rec; 1000 uint16_t recsize; 1001 OSErr retval = 0; 1002 UInt32 hint; 1003 1004 retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, &key, &rec, &recsize); 1005 1006 if (retval != 0) { 1007 if (retval == btNotFound) { 1008 /* If the record was not found because either the thread 1009 * record is missing or the file/folder record was deleted by 1010 * another repair order, return false success to retry again 1011 * after thread repair code. 1012 */ 1013 GPtr->minorRepairFalseSuccess = true; 1014 retval = 0; 1015 } 1016 goto done; 1017 } 1018 if (rec.recordType != kHFSPlusFolderRecord) { 1019 retval = IntError(GPtr, R_IntErr); 1020 goto done; 1021 } 1022 1023 rec.hfsPlusFolder.bsdInfo.ownerFlags |= UF_IMMUTABLE; 1024 rec.hfsPlusFolder.bsdInfo.fileMode |= S_ISVTX; 1025 1026 retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint, &rec, recsize, &hint); 1027 1028done: 1029 return retval; 1030} 1031 1032/*------------------------------------------------------------------------------ 1033Routine: FixOrphanLink 1034 1035Function: Delete the orphan directory/file hard link as no corresponding 1036 directory/file inode was found. 1037 1038Input: GPtr - ptr to scavenger global data 1039 p - pointer to a minor repair order 1040 1041Output: function returns - 1042 0 - no error, success 1043 n - error 1044-------------------------------------------------------------------------------*/ 1045static OSErr FixOrphanLink(SGlobPtr GPtr, RepairOrderPtr p) 1046{ 1047 int retval; 1048 1049 retval = DeleteCatalogRecordByID(GPtr, p->parid, false); 1050 if (retval == btNotFound) { 1051 /* If the record was not found because either the thread 1052 * record is missing or the file/folder record was deleted by 1053 * another repair order, return false success to retry again 1054 * after thread repair code. 1055 */ 1056 GPtr->minorRepairFalseSuccess = true; 1057 retval = 0; 1058 } 1059 1060 return retval; 1061} 1062 1063/*------------------------------------------------------------------------------ 1064Routine: FixOrphanInode 1065 1066Function: Repair orphan file/directory inode, i.e. no hard links point 1067 to this file/directory inode by moving them to lost+found. 1068 1069Input: GPtr - ptr to scavenger global data 1070 p - pointer to a minor repair order 1071 1072Output: function returns - 1073 0 - no error, success 1074 n - error 1075-------------------------------------------------------------------------------*/ 1076static OSErr FixOrphanInode(SGlobPtr GPtr, RepairOrderPtr p) 1077{ 1078 int retval; 1079 uint32_t lost_found_id; 1080 static int msg_display = 0; 1081 1082 if (embedded == 1 && debug == 0) { 1083 retval = EPERM; 1084 goto out; 1085 } 1086 1087 /* Make sure that lost+found exists */ 1088 lost_found_id = CreateDirByName(GPtr, (u_char *)"lost+found", 1089 kHFSRootFolderID); 1090 if (lost_found_id == 0) { 1091 retval = ENOENT; 1092 goto out; 1093 } 1094 1095 retval = MoveCatalogRecordByID(GPtr, p->parid, lost_found_id); 1096 if (retval == btNotFound) { 1097 /* If the record was not found because either the thread 1098 * record is missing or the file/folder record was deleted by 1099 * another repair order, return false success to retry again 1100 * after thread repair code. 1101 */ 1102 GPtr->minorRepairFalseSuccess = true; 1103 retval = 0; 1104 } 1105 if (msg_display == 0) { 1106 fsckPrint(GPtr->context, fsckLostFoundDirectory, "lost+found"); 1107 msg_display = 1; 1108 } 1109 1110out: 1111 return retval; 1112} 1113 1114/*------------------------------------------------------------------------------ 1115Routine: FixDirLinkOwnerFlags 1116 1117Function: Fix the owner flags for directory hard link. 1118 1119Input: GPtr - ptr to scavenger global data 1120 p - pointer to a minor repair order 1121 1122Output: function returns - 1123 0 - no error, success 1124 n - error 1125-------------------------------------------------------------------------------*/ 1126static OSErr FixDirLinkOwnerFlags(SGlobPtr GPtr, RepairOrderPtr p) 1127{ 1128 CatalogKey key; 1129 CatalogRecord rec; 1130 uint16_t recsize; 1131 OSErr retval = 0; 1132 UInt32 hint; 1133 1134 retval = GetCatalogRecordByID(GPtr, p->parid, true, &key, &rec, &recsize); 1135 if (retval != 0) { 1136 if (retval == btNotFound) { 1137 /* If the record was not found because either the thread 1138 * record is missing or the file/folder record was deleted by 1139 * another repair order, return false success to retry again 1140 * after thread repair code. 1141 */ 1142 GPtr->minorRepairFalseSuccess = true; 1143 retval = 0; 1144 } 1145 goto done; 1146 } 1147 1148 rec.hfsPlusFile.bsdInfo.ownerFlags = p->correct; 1149 1150 retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint, 1151 &rec, recsize, &hint); 1152 1153done: 1154 return retval; 1155} 1156 1157/*------------------------------------------------------------------------------ 1158Routine: FixBadFlags 1159 1160Function: Update the flags field of a directory or file node 1161 1162Input: GPtr -- ptr to scavenger global data 1163 p -- pointer to a minor repair order 1164 1165Output: function result: 1166 0 - no error 1167 n - error 1168*/ 1169static OSErr FixBadFlags(SGlobPtr GPtr, RepairOrderPtr p) 1170{ 1171 CatalogKey key; 1172 CatalogRecord rec; 1173 uint16_t recsize; 1174 OSErr retval = 0; 1175 UInt32 hint; 1176 1177 retval = GetCatalogRecordByID(GPtr, p->parid, true, &key, &rec, &recsize); 1178 if (retval != 0) { 1179 if (retval == btNotFound) { 1180 /* If the record was not found because either the thread 1181 * record is missing or the file/folder record was deleted by 1182 * another repair order, return false success to retry again 1183 * after thread repair code. 1184 */ 1185 GPtr->minorRepairFalseSuccess = true; 1186 retval = 0; 1187 } 1188 goto done; 1189 } 1190 1191 if (p->type == E_DirInodeBadFlags) { 1192 if ((rec.hfsPlusFolder.flags != p->incorrect) && (fsckGetVerbosity(GPtr->context) >= kDebugLog)) { 1193 fplog(stderr, "\tFixBadFlags (folder): old = %#x, incorrect = %#x, correct = %#x\n", rec.hfsPlusFolder.flags, (int)p->incorrect, (int)p->correct); 1194 } 1195 rec.hfsPlusFolder.flags = p->correct; 1196 } else if (p->type == E_DirLinkAncestorFlags) { 1197 if ((rec.hfsPlusFolder.flags != p->incorrect) && (fsckGetVerbosity(GPtr->context) >= kDebugLog)) { 1198 fplog(stderr, "\tFixBadFlags (parent folder): old = %#x, incorrect = %#x, correct = %#x\n", rec.hfsPlusFolder.flags, (int)p->incorrect, (int)p->correct); 1199 } 1200 rec.hfsPlusFolder.flags = p->correct; 1201 } else { 1202 if ((rec.hfsPlusFolder.flags != p->incorrect) && (fsckGetVerbosity(GPtr->context) >= kDebugLog)) { 1203 fplog(stderr, "\tFixBadFlags (file): old = %#x, incorrect = %#x, correct = %#x\n", rec.hfsPlusFolder.flags, (int)p->incorrect, (int)p->correct); 1204 } 1205 rec.hfsPlusFile.flags = p->correct; 1206 } 1207 1208 retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint, 1209 &rec, recsize, &hint); 1210 1211done: 1212 1213 return retval; 1214 1215} 1216 1217/*------------------------------------------------------------------------------ 1218Routine: UpdFolderCount 1219 1220Function: Update the folder count in an HFS+ folder record 1221 1222Input: GPtr -- ptr to scavenger global data 1223 p -- pointer to minor repair order 1224 1225Output: function result: 1226 0 - no error 1227 n - error 1228 1229------------------------------------------------------------------------------*/ 1230OSErr UpdFolderCount( SGlobPtr GPtr, RepairOrderPtr p) 1231{ 1232 OSErr result = -1; 1233 CatalogRecord record; 1234 CatalogKey key, foundKey; 1235 UInt16 recSize = 0; 1236 UInt32 hint = 0; 1237 1238#define DPRINT(where, fmt, ...) \ 1239 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) \ 1240 fplog(where, fmt, ## __VA_ARGS__); 1241 1242 /* 1243 * We do the search in two stages. First, we look for just the 1244 * catalog ID we get from the repair order; this SHOULD give us 1245 * a thread record, which we can then use to get the real record. 1246 */ 1247 BuildCatalogKey( p->parid, NULL, true, &key); 1248 result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, 1249 &foundKey, &record, &recSize, &hint); 1250 if (result) { 1251 if (result == btNotFound) { 1252 /* If the record was not found because either the thread 1253 * record is missing or the file/folder record was deleted by 1254 * another repair order, return false success to retry again 1255 * after thread repair code. 1256 */ 1257 GPtr->minorRepairFalseSuccess = true; 1258 return 0; 1259 } else { 1260 DPRINT(stderr, "\tUpdFolderCount: first SearchBTreeRecord failed, parid = %u, result = %d\n", p->parid, result); 1261 return IntError(GPtr, R_IntErr); 1262 } 1263 } 1264 1265 if (record.recordType != kHFSPlusFolderThreadRecord) { 1266 GPtr->CBTStat |= S_Orphan; 1267 GPtr->minorRepairFalseSuccess = true; 1268 return 0; 1269 } 1270 1271 BuildCatalogKey( record.hfsPlusThread.parentID, (const CatalogName *)&record.hfsPlusThread.nodeName, true, &key); 1272 result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, 1273 &foundKey, &record, &recSize, &hint); 1274 1275 if (result) { 1276 DPRINT(stderr, "UpdFolderCount: second SearchBTreeRecord failed (thread.parentID = %u, result = %d), just returning without complaint\n", record.hfsPlusThread.parentID, result); 1277 return 0; 1278 } 1279 1280 if (record.recordType != kHFSPlusFolderRecord) { 1281 DPRINT(stderr, "UpdFolderCount: actual record type (%d) != FolderRecord\n", record.recordType); 1282 return IntError(GPtr, R_IntErr); 1283 } 1284 1285#if 0 1286 /* 1287 * If we've had to make a folder on an HFSX volume, we set the folderCount to what 1288 * it should be -- which may not be what it found at a different part of the pass. 1289 */ 1290 if ((UInt32)p->incorrect != record.hfsPlusFolder.folderCount) { 1291 DPRINT(stderr, "UpdFolderCount: incorrect (%u) != expected folderCount (%u)\n", (UInt32)p->incorrect, record.hfsPlusFolder.folderCount); 1292 return IntError( GPtr, R_IntErr); 1293 } 1294#else 1295 if (record.hfsPlusFolder.folderCount == p->correct) { 1296 /* We've gotten it already, no need to do anything */ 1297 return noErr; 1298 } 1299#endif 1300 1301 record.hfsPlusFolder.folderCount = p->correct; 1302 result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, 1303 &record, recSize, &hint); 1304 if (result) { 1305 DPRINT(stderr, "UpdFolderCount: ReplaceBTreeRecord failed (%d)\n", result); 1306 return IntError( GPtr, R_IntErr ); 1307 } 1308 return noErr; 1309} 1310#undef DPRINT 1311 1312/*------------------------------------------------------------------------------ 1313Routine: UpdHasFolderCount 1314 1315Function: Update the HasFolderCount flag on an HFS+ folder's flags 1316 1317Input: GPtr -- ptr to scavenger global data 1318 p -- pointer to minor repair order 1319 1320Output: function result: 1321 0 - no error 1322 n - error 1323 1324------------------------------------------------------------------------------*/ 1325OSErr UpdHasFolderCount( SGlobPtr GPtr, RepairOrderPtr p) 1326{ 1327 OSErr result = -1; 1328 CatalogRecord record; 1329 CatalogKey key, foundKey; 1330 UInt16 recSize = 0; 1331 UInt32 hint = 0; 1332 1333 /* 1334 * As above, we do the search in two stages: first to get the 1335 * thread record (based solely on the CNID); second, to get the 1336 * folder record based from the thread record. 1337 */ 1338 BuildCatalogKey( p->parid, NULL, true, &key); 1339 result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, 1340 &foundKey, &record, &recSize, &hint); 1341 1342 if (result) { 1343 if (result == btNotFound) { 1344 /* If the record was not found because either the thread 1345 * record is missing or the file/folder record was deleted by 1346 * another repair order, return false success to retry again 1347 * after thread repair code. 1348 */ 1349 GPtr->minorRepairFalseSuccess = true; 1350 return 0; 1351 } else { 1352 return IntError(GPtr, R_IntErr); 1353 } 1354 } 1355 1356 /* If it's not a folder thread record, we've got a problem */ 1357 if (record.recordType != kHFSPlusFolderThreadRecord) { 1358 GPtr->CBTStat |= S_Orphan; 1359 GPtr->minorRepairFalseSuccess = true; 1360 return 0; 1361 } 1362 1363 BuildCatalogKey( record.hfsPlusThread.parentID, (const CatalogName *)&record.hfsPlusThread.nodeName, true, &key); 1364 result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, 1365 &foundKey, &record, &recSize, &hint); 1366 1367 if (result) { 1368 return IntError(GPtr, R_IntErr); 1369 } 1370 1371 if (record.recordType != kHFSPlusFolderRecord) { 1372 return IntError(GPtr, R_IntErr); 1373 } 1374 1375 /* Verify that the kHFSHasFolderCountMask bit hasn't been set, and set if necessary */ 1376 if ((record.hfsPlusFolder.flags & kHFSHasFolderCountMask) == 0) { 1377 record.hfsPlusFolder.flags |= kHFSHasFolderCountMask; 1378 result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, 1379 &record, recSize, &hint); 1380 if (result) { 1381 return IntError( GPtr, R_IntErr ); 1382 } 1383 } 1384 1385 return noErr; 1386} 1387 1388/*------------------------------------------------------------------------------ 1389 1390Routine: DoMinorOrders 1391 1392Function: Execute minor repair orders. 1393 1394Input: GPtr - ptr to scavenger global data 1395 1396Outut: function result: 1397 0 - no error 1398 n - error 1399------------------------------------------------------------------------------*/ 1400 1401static OSErr DoMinorOrders( SGlobPtr GPtr ) // the globals 1402{ 1403 RepairOrderPtr p; 1404 RepairOrderPtr cur; 1405 OSErr err = noErr; // initialize to "no error" 1406 1407 /* Manipulate the list for minor repairs separately from the global 1408 * list head because global list head will be used to store repair 1409 * orders which returned false success in anticipation of re-repair 1410 * after other corruptioins on the disk. 1411 */ 1412 cur = GPtr->MinorRepairsP; 1413 GPtr->MinorRepairsP = NULL; 1414 1415 while( (p = cur) && (err == noErr) ) // loop over each repair order 1416 { 1417 cur = p->link; 1418 1419 GPtr->minorRepairFalseSuccess = false; 1420 1421 switch( p->type ) // branch on repair type 1422 { 1423 case E_FldCount: // folderCount needs to be updated 1424 err = UpdFolderCount( GPtr, p ); 1425 break; 1426 1427 case E_HsFldCount: // HasFolderCount bit needs to be set 1428 err = UpdHasFolderCount( GPtr, p ); 1429 break; 1430 1431 case E_RtDirCnt: // the valence errors 1432 case E_RtFilCnt: // (of which there are several) 1433 case E_DirCnt: 1434 case E_FilCnt: 1435 case E_DirVal: 1436 err = UpdVal( GPtr, p ); // handle a valence error 1437 break; 1438 1439 case E_LockedDirName: 1440 err = FixFinderFlags( GPtr, p ); 1441 break; 1442 1443 case E_UnlinkedFile: 1444 err = DeleteUnlinkedFile( GPtr, p ); 1445 break; 1446 1447 case E_FileLinkCountError: 1448 case E_InvalidLinkCount: 1449 err = FixLinkCount( GPtr, p ); 1450 break; 1451 1452 case E_InvalidLinkChainPrev: 1453 err = FixLinkChainPrev( GPtr, p ); 1454 break; 1455 1456 case E_InvalidLinkChainNext: 1457 err = FixLinkChainNext( GPtr, p ); 1458 break; 1459 1460 case E_DirHardLinkFinderInfo: 1461 case E_FileHardLinkFinderInfo: 1462 err = FixHardLinkFinderInfo( GPtr, p ); 1463 break; 1464 1465 case E_InvalidPermissions: 1466 err = FixBSDInfo( GPtr, p ); 1467 break; 1468 1469 case E_NoFile: // dangling file thread 1470 err = DelFThd( GPtr, p->parid ); // delete the dangling thread 1471 break; 1472 1473 //�� E_NoFile case is never hit since VLockedChk() registers the error, 1474 //�� and returns the error causing the verification to quit. 1475 case E_EntryNotFound: 1476 GPtr->EBTStat |= S_OrphanedExtent; 1477 break; 1478 1479 //�� Same with E_NoDir 1480 case E_NoDir: // missing directory record 1481 err = FixDirThread( GPtr, p->parid ); // fix the directory thread record 1482 break; 1483 1484 case E_InvalidMDBdrAlBlSt: 1485 err = FixEmbededVolDescription( GPtr, p ); 1486 break; 1487 1488 case E_InvalidWrapperExtents: 1489 err = FixWrapperExtents(GPtr, p); 1490 break; 1491 1492 case E_IllegalName: 1493 err = FixIllegalNames( GPtr, p ); 1494 break; 1495 1496 case E_PEOF: 1497 case E_LEOF: 1498 err = FixFileSize(GPtr, p); 1499 break; 1500 1501 case E_PEOAttr: 1502 case E_LEOAttr: 1503 err = FixAttrSize(GPtr, p); 1504 break; 1505 1506 case E_ExtEnt: 1507 err = FixBadExtent(GPtr, p); 1508 break; 1509 1510 case E_DirInodeBadFlags: 1511 case E_DirLinkAncestorFlags: 1512 case E_FileInodeBadFlags: 1513 case E_DirLinkBadFlags: 1514 case E_FileLinkBadFlags: 1515 err = FixBadFlags(GPtr, p); 1516 break; 1517 1518 case E_BadPermPrivDir: 1519 err = FixPrivDirBadPerms(GPtr, p); 1520 break; 1521 1522 case E_InvalidLinkChainFirst: 1523 err = FixBadLinkChainFirst(GPtr, p); 1524 break; 1525 1526 case E_OrphanFileLink: 1527 case E_OrphanDirLink: 1528 err = FixOrphanLink(GPtr, p); 1529 break; 1530 1531 case E_OrphanFileInode: 1532 case E_OrphanDirInode: 1533 err = FixOrphanInode(GPtr, p); 1534 break; 1535 1536 case E_DirHardLinkOwnerFlags: 1537 err = FixDirLinkOwnerFlags(GPtr, p); 1538 break; 1539 1540 case E_BadHardLinkDate: 1541 err = FixHardLinkBadDate(GPtr, p); 1542 break; 1543 1544 case E_LinkChainNonLink: 1545 err = FixFileHardLinkFlag(GPtr, p); 1546 break; 1547 1548 default: // unknown repair type 1549 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 1550 plog ("\tUnknown repair order found (type = %d)\n", p->type); 1551 } 1552 err = IntError( GPtr, R_IntErr ); // treat as an internal error 1553 break; 1554 } 1555 1556 if ((err != 0) && (fsckGetVerbosity(GPtr->context) >= kDebugLog)) { 1557 plog ("\tDoMinorRepair: Repair for type=%d failed (err=%d).\n", p->type, err); 1558 } 1559 1560 /* A repair order can return false success if lookup of a 1561 * record failed --- which can happen if the corresponding 1562 * thread record is missing or a file/folder record was 1563 * deleted as part of another repair order. If repair 1564 * order returned false success, do not free it up, instead 1565 * add it back to the global minor repair list to retry 1566 * repair after repairing incorrect number of thread records. 1567 * Note: We do not return error when repair of minor 1568 * repair orders fail second time due to missing record 1569 * because if we did not find the catalog record second time, 1570 * it is already deleted and the minor repair order is invalid. 1571 * The minor repair order list is later freed up in clean up 1572 * for the scavenger. 1573 */ 1574 if (GPtr->minorRepairFalseSuccess == true) { 1575 p->link = GPtr->MinorRepairsP; 1576 GPtr->MinorRepairsP = p; 1577 } else { 1578 DisposeMemory( p ); // free the node 1579 } 1580 } 1581 1582 return( err ); // return error code to our caller 1583} 1584 1585 1586 1587/*------------------------------------------------------------------------------ 1588 1589Routine: DelFThd - (delete file thread) 1590 1591Function: Executes the delete dangling file thread repair orders. These are typically 1592 threads left after system 6 deletes an aliased file, since system 6 is not 1593 aware of aliases and thus will not delete the thread along with the file. 1594 1595Input: GPtr - global data 1596 fid - the thread record's key's parent-ID 1597 1598Output: 0 - no error 1599 n - deletion failed 1600Modification History: 1601 29Oct90 KST CBTDelete was using "k" as key which points to cache buffer. 1602-------------------------------------------------------------------------------*/ 1603 1604static int DelFThd( SGlobPtr GPtr, UInt32 fid ) // the file ID 1605{ 1606 CatalogRecord record; 1607 CatalogKey foundKey; 1608 CatalogKey key; 1609 UInt32 hint; // as returned by CBTSearch 1610 OSErr result; // status return 1611 UInt16 recSize; 1612 Boolean isHFSPlus; 1613 ExtentRecord zeroExtents; 1614 1615 isHFSPlus = VolumeObjectIsHFSPlus( ); 1616 1617 BuildCatalogKey( fid, (const CatalogName*) nil, isHFSPlus, &key ); 1618 result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &record, &recSize, &hint ); 1619 1620 if ( result ) return ( IntError( GPtr, result ) ); 1621 1622 if ( (record.recordType != kHFSFileThreadRecord) && (record.recordType != kHFSPlusFileThreadRecord) ) // quit if not a file thread 1623 return ( IntError( GPtr, R_IntErr ) ); 1624 1625 // Zero the record on disk 1626 ClearMemory( (Ptr)&zeroExtents, sizeof(ExtentRecord) ); 1627 result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &key, hint, &zeroExtents, recSize, &hint ); 1628 if ( result ) return ( IntError( GPtr, result ) ); 1629 1630 result = DeleteBTreeRecord( GPtr->calculatedCatalogFCB, &key ); 1631 if ( result ) return ( IntError( GPtr, result ) ); 1632 1633 // After deleting a record, we'll need to write back the BT header and map, 1634 // to reflect the updated record count etc. 1635 1636 GPtr->CBTStat |= S_BTH + S_BTM; // set flags to write back hdr and map 1637 1638 return( noErr ); // successful return 1639} 1640 1641 1642/*------------------------------------------------------------------------------ 1643 1644Routine: FixDirThread - (fix directory thread record's parent ID info) 1645 1646Function: Executes the missing directory record repair orders most likely caused by 1647 disappearing folder bug. This bug causes some folders to jump to Desktop 1648 from the root window. The catalog directory record for such a folder has 1649 the Desktop folder as the parent but its thread record still the root 1650 directory as its parent. 1651 1652Input: GPtr - global data 1653 did - the thread record's key's parent-ID 1654 1655Output: 0 - no error 1656 n - deletion failed 1657-------------------------------------------------------------------------------*/ 1658 1659static OSErr FixDirThread( SGlobPtr GPtr, UInt32 did ) // the dir ID 1660{ 1661 UInt8 *dataPtr; 1662 UInt32 hint; // as returned by CBTSearch 1663 OSErr result; // status return 1664 UInt16 recSize; 1665 CatalogName catalogName; // temporary name record 1666 CatalogName *keyName; // temporary name record 1667 register short index; // loop index for all records in the node 1668 UInt32 curLeafNode; // current leaf node being checked 1669 CatalogRecord record; 1670 CatalogKey foundKey; 1671 CatalogKey key; 1672 CatalogKey *keyP; 1673 SInt16 recordType; 1674 UInt32 folderID; 1675 NodeRec node; 1676 NodeDescPtr nodeDescP; 1677 UInt32 newParDirID = 0; // the parent ID where the dir record is really located 1678 Boolean isHFSPlus; 1679 BTreeControlBlock *calculatedBTCB = GetBTreeControlBlock( kCalculatedCatalogRefNum ); 1680 1681 isHFSPlus = VolumeObjectIsHFSPlus( ); 1682 1683 BuildCatalogKey( did, (const CatalogName*) nil, isHFSPlus, &key ); 1684 result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &record, &recSize, &hint ); 1685 1686 if ( result ) 1687 return( IntError( GPtr, result ) ); 1688 if ( (record.recordType != kHFSFolderThreadRecord) && (record.recordType != kHFSPlusFolderThreadRecord) ) // quit if not a directory thread 1689 return ( IntError( GPtr, R_IntErr ) ); 1690 1691 curLeafNode = calculatedBTCB->freeNodes; 1692 1693 while ( curLeafNode ) 1694 { 1695 result = GetNode( calculatedBTCB, curLeafNode, &node ); 1696 if ( result != noErr ) return( IntError( GPtr, result ) ); 1697 1698 nodeDescP = node.buffer; 1699 1700 // loop on number of records in node 1701 for ( index = 0 ; index < nodeDescP->numRecords ; index++ ) 1702 { 1703 GetRecordByIndex( calculatedBTCB, (NodeDescPtr)nodeDescP, index, (BTreeKey **)&keyP, &dataPtr, &recSize ); 1704 1705 recordType = ((CatalogRecord *)dataPtr)->recordType; 1706 folderID = recordType == kHFSPlusFolderRecord ? ((HFSPlusCatalogFolder *)dataPtr)->folderID : ((HFSCatalogFolder *)dataPtr)->folderID; 1707 1708 // did we locate a directory record whode dirID match the the thread's key's parent dir ID? 1709 if ( (folderID == did) && ( recordType == kHFSPlusFolderRecord || recordType == kHFSFolderRecord ) ) 1710 { 1711 newParDirID = recordType == kHFSPlusFolderRecord ? keyP->hfsPlus.parentID : keyP->hfs.parentID; 1712 keyName = recordType == kHFSPlusFolderRecord ? (CatalogName *)&keyP->hfsPlus.nodeName : (CatalogName *)&keyP->hfs.nodeName; 1713 CopyCatalogName( keyName, &catalogName, isHFSPlus ); 1714 break; 1715 } 1716 } 1717 1718 if ( newParDirID ) { 1719 (void) ReleaseNode(calculatedBTCB, &node); 1720 break; 1721 } 1722 1723 curLeafNode = nodeDescP->fLink; // sibling of this leaf node 1724 1725 (void) ReleaseNode(calculatedBTCB, &node); 1726 } 1727 1728 if ( newParDirID == 0 ) 1729 { 1730 return ( IntError( GPtr, R_IntErr ) ); // �� Try fixing by creating a new directory record? 1731 } 1732 else 1733 { 1734 (void) SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &record, &recSize, &hint ); 1735 1736 if ( isHFSPlus ) 1737 { 1738 HFSPlusCatalogThread *largeCatalogThreadP = (HFSPlusCatalogThread *) &record; 1739 1740 largeCatalogThreadP->parentID = newParDirID; 1741 CopyCatalogName( &catalogName, (CatalogName *) &largeCatalogThreadP->nodeName, isHFSPlus ); 1742 } 1743 else 1744 { 1745 HFSCatalogThread *smallCatalogThreadP = (HFSCatalogThread *) &record; 1746 1747 smallCatalogThreadP->parentID = newParDirID; 1748 CopyCatalogName( &catalogName, (CatalogName *)&smallCatalogThreadP->nodeName, isHFSPlus ); 1749 } 1750 1751 result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recSize, &hint ); 1752 } 1753 1754 return( noErr ); // successful return 1755} 1756 1757 1758/*------------------------------------------------------------------------------ 1759 1760Routine: UpdVal - (Update Valence) 1761 1762Function: Replaces out of date valences with correct vals computed during scavenge. 1763 1764Input: GPtr - pointer to scavenger global area 1765 p - pointer to the repair order 1766 1767Output: UpdVal - function result: 1768 0 = no error 1769 n = error 1770------------------------------------------------------------------------------*/ 1771 1772static OSErr UpdVal( SGlobPtr GPtr, RepairOrderPtr p ) // the valence repair order 1773{ 1774 OSErr result; // status return 1775 Boolean isHFSPlus; 1776 UInt32 hint; // as returned by CBTSearch 1777 UInt16 recSize; 1778 CatalogRecord record; 1779 CatalogKey foundKey; 1780 CatalogKey key; 1781 SVCB *calculatedVCB = GPtr->calculatedVCB; 1782 1783 isHFSPlus = VolumeObjectIsHFSPlus( ); 1784 1785 switch( p->type ) 1786 { 1787 case E_RtDirCnt: // invalid count of Dirs in Root 1788 if ( (UInt16)p->incorrect != calculatedVCB->vcbNmRtDirs ) 1789 return ( IntError( GPtr, R_IntErr ) ); 1790 calculatedVCB->vcbNmRtDirs = (UInt16)p->correct; 1791 GPtr->VIStat |= S_MDB; 1792 break; 1793 1794 case E_RtFilCnt: 1795 if ( (UInt16)p->incorrect != calculatedVCB->vcbNmFls ) 1796 return ( IntError( GPtr, R_IntErr ) ); 1797 calculatedVCB->vcbNmFls = (UInt16)p->correct; 1798 GPtr->VIStat |= S_MDB; 1799 break; 1800 1801 case E_DirCnt: 1802 if ( (UInt32)p->incorrect != calculatedVCB->vcbFolderCount ) 1803 return ( IntError( GPtr, R_IntErr ) ); 1804 calculatedVCB->vcbFolderCount = (UInt32)p->correct; 1805 GPtr->VIStat |= S_MDB; 1806 break; 1807 1808 case E_FilCnt: 1809 if ( (UInt32)p->incorrect != calculatedVCB->vcbFileCount ) 1810 return ( IntError( GPtr, R_IntErr ) ); 1811 calculatedVCB->vcbFileCount = (UInt32)p->correct; 1812 GPtr->VIStat |= S_MDB; 1813 break; 1814 1815 case E_DirVal: 1816 BuildCatalogKey( p->parid, (CatalogName *)&p->name, isHFSPlus, &key ); 1817 result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, 1818 &foundKey, &record, &recSize, &hint ); 1819 if ( result ) 1820 return ( IntError( GPtr, result ) ); 1821 1822 if ( record.recordType == kHFSPlusFolderRecord ) 1823 { 1824 if ( (UInt32)p->incorrect != record.hfsPlusFolder.valence) 1825 return ( IntError( GPtr, R_IntErr ) ); 1826 record.hfsPlusFolder.valence = (UInt32)p->correct; 1827 } 1828 else 1829 { 1830 if ( (UInt16)p->incorrect != record.hfsFolder.valence ) 1831 return ( IntError( GPtr, R_IntErr ) ); 1832 record.hfsFolder.valence = (UInt16)p->correct; 1833 } 1834 1835 result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &key, hint,\ 1836 &record, recSize, &hint ); 1837 if ( result ) 1838 return ( IntError( GPtr, result ) ); 1839 break; 1840 } 1841 1842 return( noErr ); // no error 1843} 1844 1845/*------------------------------------------------------------------------------ 1846 1847Routine: FixFinderFlags 1848 1849Function: Changes some of the Finder flag bits for directories. 1850 1851Input: GPtr - pointer to scavenger global area 1852 p - pointer to the repair order 1853 1854Output: FixFinderFlags - function result: 1855 0 = no error 1856 n = error 1857------------------------------------------------------------------------------*/ 1858 1859static OSErr FixFinderFlags( SGlobPtr GPtr, RepairOrderPtr p ) // the repair order 1860{ 1861 CatalogRecord record; 1862 CatalogKey foundKey; 1863 CatalogKey key; 1864 UInt32 hint; // as returned by CBTSearch 1865 OSErr result; // status return 1866 UInt16 recSize; 1867 Boolean isHFSPlus; 1868 1869 isHFSPlus = VolumeObjectIsHFSPlus( ); 1870 1871 BuildCatalogKey( p->parid, (CatalogName *)&p->name, isHFSPlus, &key ); 1872 1873 result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, &foundKey, &record, &recSize, &hint ); 1874 if ( result ) 1875 return ( IntError( GPtr, result ) ); 1876 1877 if ( record.recordType == kHFSPlusFolderRecord ) 1878 { 1879 HFSPlusCatalogFolder *largeCatalogFolderP = (HFSPlusCatalogFolder *) &record; 1880 if ( (UInt16) p->incorrect != largeCatalogFolderP->userInfo.frFlags) 1881 { 1882 // Another repair order may have affected the flags 1883 if ( p->correct < p->incorrect ) 1884 largeCatalogFolderP->userInfo.frFlags &= ~((UInt16)p->maskBit); 1885 else 1886 largeCatalogFolderP->userInfo.frFlags |= (UInt16)p->maskBit; 1887 } 1888 else 1889 { 1890 largeCatalogFolderP->userInfo.frFlags = (UInt16)p->correct; 1891 } 1892 // largeCatalogFolderP->contentModDate = timeStamp; 1893 } 1894 else 1895 { 1896 HFSCatalogFolder *smallCatalogFolderP = (HFSCatalogFolder *) &record; 1897 if ( p->incorrect != smallCatalogFolderP->userInfo.frFlags) // do we know what we're doing? 1898 { 1899 // Another repair order may have affected the flags 1900 if ( p->correct < p->incorrect ) 1901 smallCatalogFolderP->userInfo.frFlags &= ~((UInt16)p->maskBit); 1902 else 1903 smallCatalogFolderP->userInfo.frFlags |= (UInt16)p->maskBit; 1904 } 1905 else 1906 { 1907 smallCatalogFolderP->userInfo.frFlags = (UInt16)p->correct; 1908 } 1909 1910 // smallCatalogFolderP->modifyDate = timeStamp; // also update the modify date! -DJB 1911 } 1912 1913 result = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recSize, &hint ); // write the node back to the file 1914 if ( result ) 1915 return( IntError( GPtr, result ) ); 1916 1917 return( noErr ); // no error 1918} 1919 1920/*------------------------------------------------------------------------------ 1921FixHardLinkFinderInfo: Set the Finder Info contents to be correct for the type 1922 of hard link (directory or file). 1923 (HFS+ volumes only) 1924------------------------------------------------------------------------------*/ 1925static OSErr FixHardLinkFinderInfo(SGlobPtr GPtr, RepairOrderPtr p) 1926{ 1927 CatalogRecord rec; 1928 CatalogKey key; 1929 uint16_t recsize; 1930 OSErr retval = 0; 1931 UInt32 hint; 1932 1933 retval = GetCatalogRecordByID(GPtr, (UInt32)p->parid, true, &key, &rec, &recsize); 1934 if (retval != 0) { 1935 if (retval == btNotFound) { 1936 /* If the record was not found because either the thread 1937 * record is missing or the file/folder record was deleted by 1938 * another repair order, return false success to retry again 1939 * after thread repair code. 1940 */ 1941 GPtr->minorRepairFalseSuccess = true; 1942 retval = 0; 1943 } 1944 goto done; 1945 } 1946 1947 if (rec.recordType != kHFSPlusFileRecord) { 1948 retval = IntError(GPtr, R_IntErr); 1949 goto done; 1950 } 1951 1952 if (p->type == E_DirHardLinkFinderInfo) { 1953 rec.hfsPlusFile.userInfo.fdType = kHFSAliasType; 1954 rec.hfsPlusFile.userInfo.fdCreator = kHFSAliasCreator; 1955 rec.hfsPlusFile.userInfo.fdFlags |= kIsAlias; 1956 } else if (p->type == E_FileHardLinkFinderInfo) { 1957 rec.hfsPlusFile.userInfo.fdType = kHardLinkFileType; 1958 rec.hfsPlusFile.userInfo.fdCreator = kHFSPlusCreator; 1959 } else { 1960 retval = IntError(GPtr, R_IntErr); 1961 goto done; 1962 } 1963 1964 retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, kNoHint, &rec, recsize, &hint); 1965 1966done: 1967 return retval; 1968} 1969 1970/*------------------------------------------------------------------------------ 1971FixLinkChainNext: Adjust the link chain in a hard link 1972 (HFS Plus volumes only) 1973------------------------------------------------------------------------------*/ 1974static OSErr 1975FixLinkChainPrev(SGlobPtr GPtr, RepairOrderPtr p) 1976{ 1977 SFCB *fcb; 1978 CatalogRecord rec; 1979 CatalogKey key, foundKey; 1980 UInt16 recSize; 1981 OSErr result; 1982 Boolean isHFSPlus; 1983 UInt32 hint = 0; 1984 1985 isHFSPlus = VolumeObjectIsHFSPlus( ); 1986 if (!isHFSPlus) 1987 return (0); 1988 fcb = GPtr->calculatedCatalogFCB; 1989 1990 BuildCatalogKey( p->parid, NULL, true, &key ); 1991 result = SearchBTreeRecord( fcb, &key, kNoHint, &foundKey, &rec, &recSize, &hint ); 1992 1993 if (result) { 1994 if (result == btNotFound) { 1995 /* If the record was not found because either the thread 1996 * record is missing or the file/folder record was deleted by 1997 * another repair order, return false success to retry again 1998 * after thread repair code. 1999 */ 2000 GPtr->minorRepairFalseSuccess = true; 2001 return 0; 2002 } else { 2003 return IntError(GPtr, R_IntErr); 2004 } 2005 } 2006 2007 if (rec.recordType != kHFSPlusFileThreadRecord) { 2008 return IntError(GPtr, R_IntErr); 2009 } 2010 2011 BuildCatalogKey( rec.hfsPlusThread.parentID, (const CatalogName*)&rec.hfsPlusThread.nodeName, true, &key); 2012 result = SearchBTreeRecord( fcb, &key, kNoHint, &foundKey, &rec, &recSize, &hint); 2013 2014 if (result) { 2015 return IntError(GPtr, R_IntErr); 2016 } 2017 2018 if (rec.recordType != kHFSPlusFileRecord) { 2019 return IntError(GPtr, R_IntErr); 2020 } 2021 2022 if ((UInt32)p->incorrect != rec.hfsPlusFile.hl_prevLinkID) { 2023 return IntError(GPtr, R_IntErr); 2024 } 2025 2026 rec.hfsPlusFile.hl_prevLinkID = (UInt32)p->correct; 2027 result = ReplaceBTreeRecord(fcb, &foundKey, hint, &rec, recSize, &hint); 2028 if (result) { 2029 return IntError(GPtr, R_IntErr); 2030 } 2031 2032 return noErr; 2033} 2034 2035/*------------------------------------------------------------------------------ 2036FixLinkChainNext: Adjust the link chain in a hard link 2037 (HFS Plus volumes only) 2038------------------------------------------------------------------------------*/ 2039static OSErr 2040FixLinkChainNext(SGlobPtr GPtr, RepairOrderPtr p) 2041{ 2042 SFCB *fcb; 2043 CatalogRecord rec; 2044 CatalogKey key, foundKey; 2045 UInt16 recSize; 2046 OSErr result; 2047 Boolean isHFSPlus; 2048 UInt32 hint = 0; 2049 2050 isHFSPlus = VolumeObjectIsHFSPlus( ); 2051 if (!isHFSPlus) 2052 return (0); 2053 fcb = GPtr->calculatedCatalogFCB; 2054 2055 BuildCatalogKey( p->parid, NULL, true, &key ); 2056 result = SearchBTreeRecord( fcb, &key, kNoHint, &foundKey, &rec, &recSize, &hint ); 2057 2058 if (result) { 2059 if (result == btNotFound) { 2060 /* If the record was not found because either the thread 2061 * record is missing or the file/folder record was deleted by 2062 * another repair order, return false success to retry again 2063 * after thread repair code. 2064 */ 2065 GPtr->minorRepairFalseSuccess = true; 2066 return 0; 2067 } else { 2068 return IntError(GPtr, R_IntErr); 2069 } 2070 } 2071 2072 if (rec.recordType != kHFSPlusFileThreadRecord) { 2073 return IntError(GPtr, R_IntErr); 2074 } 2075 2076 BuildCatalogKey( rec.hfsPlusThread.parentID, (const CatalogName*)&rec.hfsPlusThread.nodeName, true, &key); 2077 result = SearchBTreeRecord( fcb, &key, kNoHint, &foundKey, &rec, &recSize, &hint); 2078 2079 if (result) { 2080 return IntError(GPtr, R_IntErr); 2081 } 2082 2083 if (rec.recordType != kHFSPlusFileRecord) { 2084 return IntError(GPtr, R_IntErr); 2085 } 2086 2087 if ((UInt32)p->incorrect != rec.hfsPlusFile.hl_nextLinkID) { 2088 return IntError(GPtr, R_IntErr); 2089 } 2090 2091 rec.hfsPlusFile.hl_nextLinkID = (UInt32)p->correct; 2092 result = ReplaceBTreeRecord(fcb, &foundKey, hint, &rec, recSize, &hint); 2093 if (result) { 2094 return IntError(GPtr, R_IntErr); 2095 } 2096 2097 return noErr; 2098} 2099 2100/*------------------------------------------------------------------------------ 2101FixLinkCount: Adjust a data node link count (BSD hard link) 2102 (HFS Plus volumes only) 2103------------------------------------------------------------------------------*/ 2104static OSErr 2105FixLinkCount(SGlobPtr GPtr, RepairOrderPtr p) 2106{ 2107 CatalogRecord rec; 2108 CatalogKey key; 2109 OSErr result; 2110 UInt16 recSize; 2111 UInt32 hint; 2112 Boolean isHFSPlus; 2113 Boolean isdir = 0; 2114 int lc; // linkcount 2115 2116 isHFSPlus = VolumeObjectIsHFSPlus( ); 2117 if (!isHFSPlus) 2118 return (0); 2119 2120 result = GetCatalogRecordByID(GPtr, p->parid, isHFSPlus, &key, &rec, &recSize); 2121 if (result) { 2122 if (result == btNotFound) { 2123 /* If the record was not found because either the thread 2124 * record is missing or the file/folder record was deleted by 2125 * another repair order, return false success to retry again 2126 * after thread repair code. 2127 */ 2128 GPtr->minorRepairFalseSuccess = true; 2129 result = 0; 2130 } 2131 return result; 2132 } 2133 2134 if (rec.recordType != kHFSPlusFileRecord && rec.recordType != kHFSPlusFolderRecord) 2135 return (noErr); 2136 2137 isdir = (rec.recordType == kHFSPlusFolderRecord); 2138 2139 lc = (isdir ? rec.hfsPlusFolder.bsdInfo.special.linkCount : rec.hfsPlusFile.bsdInfo.special.linkCount); 2140 if ((UInt32)p->correct != lc) { 2141 if (isdir) 2142 rec.hfsPlusFolder.bsdInfo.special.linkCount = (UInt32)p->correct; 2143 else 2144 rec.hfsPlusFile.bsdInfo.special.linkCount = (UInt32)p->correct; 2145 2146 result = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, 2147 kNoHint, &rec, recSize, &hint); 2148 if (result) 2149 return (IntError(GPtr, result)); 2150 } 2151 2152 return (noErr); 2153} 2154 2155 2156/*------------------------------------------------------------------------------ 2157FixIllegalNames: Fix catalog enties that have illegal names. 2158 RepairOrder.name[] holds the old (illegal) name followed by the new name. 2159 The new name has been checked to make sure it is unique within the target 2160 directory. The names will look like this: 2161 2 byte length of old name (in unicode characters not bytes) 2162 unicode characters for old name 2163 2 byte length of new name (in unicode characters not bytes) 2164 unicode characters for new name 2165------------------------------------------------------------------------------*/ 2166static OSErr 2167FixIllegalNames( SGlobPtr GPtr, RepairOrderPtr roPtr ) 2168{ 2169 OSErr result; 2170 Boolean isHFSPlus; 2171 Boolean isDirectory; 2172 UInt16 recSize; 2173 SFCB * fcbPtr; 2174 CatalogName * oldNamePtr; 2175 CatalogName * newNamePtr; 2176 UInt32 hint; 2177 CatalogRecord record; 2178 CatalogKey key; 2179 CatalogKey newKey; 2180 2181 isHFSPlus = VolumeObjectIsHFSPlus( ); 2182 fcbPtr = GPtr->calculatedCatalogFCB; 2183 2184 oldNamePtr = (CatalogName *) &roPtr->name; 2185 if ( isHFSPlus ) 2186 { 2187 int myLength; 2188 u_int16_t * myPtr = (u_int16_t *) oldNamePtr; 2189 myLength = *myPtr; // get length of old name 2190 myPtr += (1 + myLength); // bump past length of old name and old name 2191 newNamePtr = (CatalogName *) myPtr; 2192 } 2193 else 2194 { 2195 int myLength; 2196 u_char * myPtr = (u_char *) oldNamePtr; 2197 myLength = *myPtr; // get length of old name 2198 myPtr += (1 + myLength); // bump past length of old name and old name 2199 newNamePtr = (CatalogName *) myPtr; 2200 } 2201 2202 // make sure new name isn't already there 2203 BuildCatalogKey( roPtr->parid, newNamePtr, isHFSPlus, &newKey ); 2204 result = SearchBTreeRecord( fcbPtr, &newKey, kNoHint, NULL, &record, &recSize, NULL ); 2205 if ( result == noErr ) { 2206 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) { 2207 plog( "\treplacement name already exists \n" ); 2208 plog( "\tduplicate name is 0x" ); 2209 PrintName( newNamePtr->ustr.length, (UInt8 *) &newNamePtr->ustr.unicode, true ); 2210 } 2211 goto ErrorExit; 2212 } 2213 2214 // get catalog record for object with the illegal name. We will restore this 2215 // info with our new (valid) name. 2216 BuildCatalogKey( roPtr->parid, oldNamePtr, isHFSPlus, &key ); 2217 result = SearchBTreeRecord( fcbPtr, &key, kNoHint, NULL, &record, &recSize, &hint ); 2218 if ( result != noErr ) { 2219 goto ErrorExit; 2220 } 2221 2222 result = DeleteBTreeRecord( fcbPtr, &key ); 2223 if ( result != noErr ) { 2224 goto ErrorExit; 2225 } 2226 2227 // insert record back into the catalog using the new name 2228 result = InsertBTreeRecord( fcbPtr, &newKey, &record, recSize, &hint ); 2229 if ( result != noErr ) { 2230 goto ErrorExit; 2231 } 2232 2233 isDirectory = false; 2234 switch( record.recordType ) 2235 { 2236 case kHFSFolderRecord: 2237 case kHFSPlusFolderRecord: 2238 isDirectory = true; break; 2239 } 2240 2241 // now we need to remove the old thread record and create a new one with 2242 // our new name. 2243 BuildCatalogKey( GetObjectID( &record ), NULL, isHFSPlus, &key ); 2244 result = SearchBTreeRecord( fcbPtr, &key, kNoHint, NULL, &record, &recSize, &hint ); 2245 if ( result != noErr ) { 2246 goto ErrorExit; 2247 } 2248 2249 result = DeleteBTreeRecord( fcbPtr, &key ); 2250 if ( result != noErr ) { 2251 goto ErrorExit; 2252 } 2253 2254 // insert thread record with new name as thread data 2255 recSize = BuildThreadRec( &newKey, &record, isHFSPlus, isDirectory ); 2256 result = InsertBTreeRecord( fcbPtr, &key, &record, recSize, &hint ); 2257 if ( result != noErr ) { 2258 goto ErrorExit; 2259 } 2260 2261 return( noErr ); 2262 2263ErrorExit: 2264 GPtr->minorRepairErrors = true; 2265 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) 2266 plog( "\t%s - repair failed for type 0x%02X %d \n", __FUNCTION__, roPtr->type, roPtr->type ); 2267 return( noErr ); // errors in this routine should not be fatal 2268 2269} /* FixIllegalNames */ 2270 2271 2272/*------------------------------------------------------------------------------ 2273FixBSDInfo: Reset or repair BSD info 2274 (HFS Plus volumes only) 2275------------------------------------------------------------------------------*/ 2276static OSErr 2277FixBSDInfo(SGlobPtr GPtr, RepairOrderPtr p) 2278{ 2279 SFCB *fcb; 2280 CatalogRecord rec; 2281 FSBufferDescriptor btRecord; 2282 BTreeIterator btIterator; 2283 Boolean isHFSPlus; 2284 OSErr result; 2285 UInt16 recSize; 2286 size_t namelen; 2287 unsigned char filename[256]; 2288 2289 isHFSPlus = VolumeObjectIsHFSPlus( ); 2290 if (!isHFSPlus) 2291 return (0); 2292 fcb = GPtr->calculatedCatalogFCB; 2293 2294 ClearMemory(&btIterator, sizeof(btIterator)); 2295 btIterator.hint.nodeNum = p->hint; 2296 BuildCatalogKey(p->parid, (CatalogName *)&p->name, true, 2297 (CatalogKey*)&btIterator.key); 2298 btRecord.bufferAddress = &rec; 2299 btRecord.itemCount = 1; 2300 btRecord.itemSize = sizeof(rec); 2301 2302 result = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey, 2303 &btRecord, &recSize, &btIterator); 2304 if (result) { 2305 return (IntError(GPtr, result)); 2306 } 2307 if (rec.recordType != kHFSPlusFileRecord && 2308 rec.recordType != kHFSPlusFolderRecord) 2309 return (noErr); 2310 2311 utf_encodestr(((HFSUniStr255 *)&p->name)->unicode, 2312 ((HFSUniStr255 *)&p->name)->length << 1, filename, &namelen, sizeof(filename)); 2313 filename[namelen] = '\0'; 2314 2315 if (p->type == E_InvalidPermissions && 2316 ((UInt16)p->incorrect == rec.hfsPlusFile.bsdInfo.fileMode)) { 2317 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) 2318 plog("\t\"%s\": fixing mode from %07o to %07o\n", 2319 filename, (int)p->incorrect, (int)p->correct); 2320 2321 rec.hfsPlusFile.bsdInfo.fileMode = (UInt16)p->correct; 2322 result = BTReplaceRecord(fcb, &btIterator, &btRecord, recSize); 2323 } 2324 2325 if (result) 2326 return (IntError(GPtr, result)); 2327 else 2328 return (noErr); 2329} 2330 2331 2332/*------------------------------------------------------------------------------ 2333DeleteUnlinkedFile: Delete orphaned data node (BSD unlinked file) 2334 Also used to delete empty "HFS+ Private Data" directories 2335 (HFS Plus volumes only) 2336------------------------------------------------------------------------------*/ 2337static OSErr 2338DeleteUnlinkedFile(SGlobPtr GPtr, RepairOrderPtr p) 2339{ 2340 OSErr result = -1; 2341 CatalogName name; 2342 CatalogName *cNameP; 2343 Boolean isHFSPlus; 2344 size_t len; 2345 CatalogKey key; 2346 CatalogRecord record; 2347 uint32_t id = 0; 2348 UInt16 recSize; 2349 UInt32 hint; 2350 2351 isHFSPlus = VolumeObjectIsHFSPlus( ); 2352 if (!isHFSPlus) 2353 return (0); 2354 2355 if (p->name[0] > 0) { 2356 /* name was stored in UTF-8 */ 2357 (void) utf_decodestr(&p->name[1], p->name[0], name.ustr.unicode, &len, sizeof(name.ustr.unicode)); 2358 name.ustr.length = len / 2; 2359 cNameP = &name; 2360 } else { 2361 cNameP = NULL; 2362 goto out; 2363 } 2364 2365 /* Lookup the record to find out file/folder ID for attribute deletion */ 2366 BuildCatalogKey(p->parid, cNameP, true, &key); 2367 result = SearchBTreeRecord (GPtr->calculatedCatalogFCB, &key, kNoHint, 2368 NULL, &record, &recSize, &hint); 2369 if (result) { 2370 if (result == btNotFound) { 2371 result = 0; 2372 } 2373 goto out; 2374 } 2375 2376 result = DeleteCatalogNode(GPtr->calculatedVCB, p->parid, cNameP, p->hint, false); 2377 if (result) { 2378 goto out; 2379 } 2380 2381 GPtr->VIStat |= S_MDB; 2382 GPtr->VIStat |= S_VBM; 2383 2384 if (record.recordType == kHFSPlusFileRecord) { 2385 id = record.hfsPlusFile.fileID; 2386 } else if (record.recordType == kHFSPlusFolderRecord) { 2387 id = record.hfsPlusFolder.folderID; 2388 } 2389 2390 /* Delete all extended attributes associated with this file/folder */ 2391 result = DeleteAllAttrsByID(GPtr, id); 2392 2393out: 2394 return result; 2395} 2396 2397/* 2398 * Fix a file's PEOF or LEOF (truncate) 2399 * (HFS Plus volumes only) 2400 */ 2401static OSErr 2402FixFileSize(SGlobPtr GPtr, RepairOrderPtr p) 2403{ 2404 SFCB *fcb; 2405 CatalogRecord rec; 2406 HFSPlusCatalogKey * keyp; 2407 FSBufferDescriptor btRecord; 2408 BTreeIterator btIterator; 2409 size_t len; 2410 Boolean isHFSPlus; 2411 Boolean replace; 2412 OSErr result; 2413 UInt16 recSize; 2414 UInt64 bytes; 2415 2416 isHFSPlus = VolumeObjectIsHFSPlus( ); 2417 if (!isHFSPlus) 2418 return (0); 2419 fcb = GPtr->calculatedCatalogFCB; 2420 replace = false; 2421 2422 ClearMemory(&btIterator, sizeof(btIterator)); 2423 btIterator.hint.nodeNum = p->hint; 2424 keyp = (HFSPlusCatalogKey*)&btIterator.key; 2425 keyp->parentID = p->parid; 2426 2427 /* name was stored in UTF-8 */ 2428 (void) utf_decodestr(&p->name[1], p->name[0], keyp->nodeName.unicode, &len, sizeof(keyp->nodeName.unicode)); 2429 keyp->nodeName.length = len / 2; 2430 keyp->keyLength = kHFSPlusCatalogKeyMinimumLength + len; 2431 2432 btRecord.bufferAddress = &rec; 2433 btRecord.itemCount = 1; 2434 btRecord.itemSize = sizeof(rec); 2435 2436 result = BTSearchRecord(fcb, &btIterator, kInvalidMRUCacheKey, 2437 &btRecord, &recSize, &btIterator); 2438 if (result) 2439 return (IntError(GPtr, result)); 2440 2441 if (rec.recordType != kHFSPlusFileRecord) 2442 return (noErr); 2443 2444 if (p->type == E_PEOF) { 2445 bytes = p->correct * (UInt64)GPtr->calculatedVCB->vcbBlockSize; 2446 if ((p->forkType == kRsrcFork) && 2447 ((UInt32)p->incorrect == rec.hfsPlusFile.resourceFork.totalBlocks)) { 2448 2449 rec.hfsPlusFile.resourceFork.totalBlocks = (UInt32)p->correct; 2450 replace = true; 2451 /* 2452 * Make sure our new block count is large 2453 * enough to cover the current LEOF. If 2454 * its not we must truncate the fork. 2455 */ 2456 if (rec.hfsPlusFile.resourceFork.logicalSize > bytes) { 2457 rec.hfsPlusFile.resourceFork.logicalSize = bytes; 2458 } 2459 } else if ((p->forkType == kDataFork) && 2460 ((UInt32)p->incorrect == rec.hfsPlusFile.dataFork.totalBlocks)) { 2461 2462 rec.hfsPlusFile.dataFork.totalBlocks = (UInt32)p->correct; 2463 replace = true; 2464 /* 2465 * Make sure our new block count is large 2466 * enough to cover the current LEOF. If 2467 * its not we must truncate the fork. 2468 */ 2469 if (rec.hfsPlusFile.dataFork.logicalSize > bytes) { 2470 rec.hfsPlusFile.dataFork.logicalSize = bytes; 2471 } 2472 } 2473 } else /* E_LEOF */ { 2474 if ((p->forkType == kRsrcFork) && 2475 (p->incorrect == rec.hfsPlusFile.resourceFork.logicalSize)) { 2476 2477 rec.hfsPlusFile.resourceFork.logicalSize = p->correct; 2478 replace = true; 2479 2480 } else if ((p->forkType == kDataFork) && 2481 (p->incorrect == rec.hfsPlusFile.dataFork.logicalSize)) { 2482 2483 rec.hfsPlusFile.dataFork.logicalSize = p->correct; 2484 replace = true; 2485 } 2486 } 2487 2488 if (replace) { 2489 result = BTReplaceRecord(fcb, &btIterator, &btRecord, recSize); 2490 if (result) 2491 return (IntError(GPtr, result)); 2492 } 2493 2494 return (noErr); 2495} 2496 2497/*------------------------------------------------------------------------------ 2498 2499Routine: FixEmbededVolDescription 2500 2501Function: If the "mdb->drAlBlSt" field has been modified, i.e. Norton Disk Doctor 2502 3.5 tried to "Fix" an HFS+ volume, it reduces the value in the 2503 "mdb->drAlBlSt" field. If this field is changed, the file system can 2504 no longer find the VolumeHeader or AltVolumeHeader. 2505 2506Input: GPtr - pointer to scavenger global area 2507 p - pointer to the repair order 2508 2509Output: FixMDBdrAlBlSt - function result: 2510 0 = no error 2511 n = error 2512------------------------------------------------------------------------------*/ 2513 2514static OSErr FixEmbededVolDescription( SGlobPtr GPtr, RepairOrderPtr p ) 2515{ 2516 OSErr err; 2517 HFSMasterDirectoryBlock *mdb; 2518 EmbededVolDescription *desc; 2519 SVCB *vcb = GPtr->calculatedVCB; 2520 BlockDescriptor block; 2521 2522 desc = (EmbededVolDescription *) &(p->name); 2523 block.buffer = NULL; 2524 2525 /* Fix the Alternate MDB */ 2526 err = GetVolumeObjectAlternateMDB( &block ); 2527 if ( err != noErr ) 2528 goto ExitThisRoutine; 2529 mdb = (HFSMasterDirectoryBlock *) block.buffer; 2530 2531 mdb->drAlBlSt = desc->drAlBlSt; 2532 mdb->drEmbedSigWord = desc->drEmbedSigWord; 2533 mdb->drEmbedExtent.startBlock = desc->drEmbedExtent.startBlock; 2534 mdb->drEmbedExtent.blockCount = desc->drEmbedExtent.blockCount; 2535 2536 err = ReleaseVolumeBlock( vcb, &block, kForceWriteBlock ); 2537 block.buffer = NULL; 2538 if ( err != noErr ) 2539 goto ExitThisRoutine; 2540 2541 /* Fix the MDB */ 2542 err = GetVolumeObjectPrimaryMDB( &block ); 2543 if ( err != noErr ) 2544 goto ExitThisRoutine; 2545 mdb = (HFSMasterDirectoryBlock *) block.buffer; 2546 2547 mdb->drAlBlSt = desc->drAlBlSt; 2548 mdb->drEmbedSigWord = desc->drEmbedSigWord; 2549 mdb->drEmbedExtent.startBlock = desc->drEmbedExtent.startBlock; 2550 mdb->drEmbedExtent.blockCount = desc->drEmbedExtent.blockCount; 2551 err = ReleaseVolumeBlock( vcb, &block, kForceWriteBlock ); 2552 block.buffer = NULL; 2553 2554ExitThisRoutine: 2555 if ( block.buffer != NULL ) 2556 err = ReleaseVolumeBlock( vcb, &block, kReleaseBlock ); 2557 2558 return( err ); 2559} 2560 2561 2562 2563 2564/*------------------------------------------------------------------------------ 2565 2566Routine: FixWrapperExtents 2567 2568Function: When Norton Disk Doctor 2.0 tries to repair an HFS Plus volume, it 2569 assumes that the first catalog extent must be a fixed number of 2570 allocation blocks after the start of the first extents extent (in the 2571 wrapper). In reality, the first catalog extent should start immediately 2572 after the first extents extent. 2573 2574Input: GPtr - pointer to scavenger global area 2575 p - pointer to the repair order 2576 2577Output: 2578 0 = no error 2579 n = error 2580------------------------------------------------------------------------------*/ 2581 2582static OSErr FixWrapperExtents( SGlobPtr GPtr, RepairOrderPtr p ) 2583{ 2584#pragma unused (p) 2585 2586 OSErr err; 2587 HFSMasterDirectoryBlock *mdb; 2588 SVCB *vcb = GPtr->calculatedVCB; 2589 BlockDescriptor block; 2590 2591 /* Get the Alternate MDB */ 2592 block.buffer = NULL; 2593 err = GetVolumeObjectAlternateMDB( &block ); 2594 if ( err != noErr ) 2595 goto ExitThisRoutine; 2596 mdb = (HFSMasterDirectoryBlock *) block.buffer; 2597 2598 /* Fix the wrapper catalog's first (and only) extent */ 2599 mdb->drCTExtRec[0].startBlock = mdb->drXTExtRec[0].startBlock + 2600 mdb->drXTExtRec[0].blockCount; 2601 2602 err = ReleaseVolumeBlock(vcb, &block, kForceWriteBlock); 2603 block.buffer = NULL; 2604 if ( err != noErr ) 2605 goto ExitThisRoutine; 2606 2607 /* Fix the MDB */ 2608 err = GetVolumeObjectPrimaryMDB( &block ); 2609 if ( err != noErr ) 2610 goto ExitThisRoutine; 2611 mdb = (HFSMasterDirectoryBlock *) block.buffer; 2612 2613 mdb->drCTExtRec[0].startBlock = mdb->drXTExtRec[0].startBlock + 2614 mdb->drXTExtRec[0].blockCount; 2615 2616 err = ReleaseVolumeBlock(vcb, &block, kForceWriteBlock); 2617 block.buffer = NULL; 2618 2619ExitThisRoutine: 2620 if ( block.buffer != NULL ) 2621 (void) ReleaseVolumeBlock( vcb, &block, kReleaseBlock ); 2622 2623 return( err ); 2624} 2625 2626 2627// 2628// Entries in the extents BTree which do not have a corresponding catalog entry get fixed here 2629// This routine will run slowly if the extents file is large because we require a Catalog BTree 2630// search for each extent record. 2631// 2632static OSErr FixOrphanedExtent( SGlobPtr GPtr ) 2633{ 2634#if 0 2635 OSErr err; 2636 UInt32 hint; 2637 UInt32 recordSize; 2638 UInt32 maxRecords; 2639 UInt32 numberOfFilesInList; 2640 ExtentKey *extentKeyPtr; 2641 ExtentRecord *extentDataPtr; 2642 ExtentRecord extents; 2643 ExtentRecord zeroExtents; 2644 CatalogKey foundExtentKey; 2645 CatalogRecord catalogData; 2646 CatalogRecord threadData; 2647 HFSCatalogNodeID fileID; 2648 BTScanState scanState; 2649 2650 HFSCatalogNodeID lastFileID = -1; 2651 UInt32 recordsFound = 0; 2652 Boolean mustRebuildBTree = false; 2653 Boolean isHFSPlus; 2654 SVCB *calculatedVCB = GPtr->calculatedVCB; 2655 UInt32 **dataHandle = GPtr->validFilesList; 2656 SFCB * fcb = GPtr->calculatedExtentsFCB; 2657 2658 // Set Up 2659 isHFSPlus = VolumeObjectIsHFSPlus( ); 2660 // 2661 // Use the BTree scanner since we use MountCheck to find orphaned extents, and MountCheck uses the scanner 2662 err = BTScanInitialize( fcb, 0, 0, 0, gFSBufferPtr, gFSBufferSize, &scanState ); 2663 if ( err != noErr ) return( badMDBErr ); 2664 2665 ClearMemory( (Ptr)&zeroExtents, sizeof(ExtentRecord) ); 2666 2667 if ( isHFSPlus ) 2668 { 2669 maxRecords = fcb->fcbLogicalSize / sizeof(HFSPlusExtentRecord); 2670 } 2671 else 2672 { 2673 maxRecords = fcb->fcbLogicalSize / sizeof(HFSExtentRecord); 2674 numberOfFilesInList = GetHandleSize((Handle) dataHandle) / sizeof(UInt32); 2675 qsort( *dataHandle, numberOfFilesInList, sizeof (UInt32), cmpLongs ); // Sort the list of found file IDs 2676 } 2677 2678 2679 while ( recordsFound < maxRecords ) 2680 { 2681 err = BTScanNextRecord( &scanState, false, (void **) &extentKeyPtr, (void **) &extentDataPtr, &recordSize ); 2682 2683 if ( err != noErr ) 2684 { 2685 if ( err == btNotFound ) 2686 err = noErr; 2687 break; 2688 } 2689 2690 ++recordsFound; 2691 fileID = (isHFSPlus == true) ? extentKeyPtr->hfsPlus.fileID : extentKeyPtr->hfs.fileID; 2692 2693 if ( (fileID > kHFSBadBlockFileID) && (lastFileID != fileID) ) // Keep us out of reserved file trouble 2694 { 2695 lastFileID = fileID; 2696 2697 if ( isHFSPlus ) 2698 { 2699 err = LocateCatalogThread( calculatedVCB, fileID, &threadData, (UInt16*)&recordSize, &hint ); // This call returns nodeName as either Str31 or HFSUniStr255, no need to call PrepareInputName() 2700 2701 if ( err == noErr ) // Thread is found, just verify actual record exists. 2702 { 2703 err = LocateCatalogNode( calculatedVCB, threadData.hfsPlusThread.parentID, (const CatalogName *) &(threadData.hfsPlusThread.nodeName), kNoHint, &foundExtentKey, &catalogData, &hint ); 2704 } 2705 else if ( err == cmNotFound ) 2706 { 2707 err = SearchBTreeRecord( GPtr->calculatedExtentsFCB, extentKeyPtr, kNoHint, &foundExtentKey, &extents, (UInt16*)&recordSize, &hint ); 2708 if ( err == noErr ) 2709 { //�� can't we just delete btree record? 2710 err = ReplaceBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey, hint, &zeroExtents, recordSize, &hint ); 2711 err = DeleteBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey ); // Delete the orphaned extent 2712 } 2713 } 2714 2715 if ( err != noErr ) 2716 mustRebuildBTree = true; // if we have errors here we should rebuild the extents btree 2717 } 2718 else 2719 { 2720 if ( ! bsearch( &fileID, *dataHandle, numberOfFilesInList, sizeof(UInt32), cmpLongs ) ) 2721 { 2722 err = SearchBTreeRecord( GPtr->calculatedExtentsFCB, extentKeyPtr, kNoHint, &foundExtentKey, &extents, (UInt16*)&recordSize, &hint ); 2723 if ( err == noErr ) 2724 { //�� can't we just delete btree record? 2725 err = ReplaceBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey, hint, &zeroExtents, recordSize, &hint ); 2726 err = DeleteBTreeRecord( GPtr->calculatedExtentsFCB, &foundExtentKey ); // Delete the orphaned extent 2727 } 2728 2729 if ( err != noErr ) 2730 mustRebuildBTree = true; // if we have errors here we should rebuild the extents btree 2731 } 2732 } 2733 } 2734 } 2735 2736 if ( mustRebuildBTree == true ) 2737 { 2738 GPtr->EBTStat |= S_RebuildBTree; 2739 err = errRebuildBtree; 2740 } 2741 2742 return( err ); 2743#else 2744 return (0); 2745#endif 2746} 2747 2748/* Function: FixAttrSize 2749 * 2750 * Description: 2751 * Fixes the incorrect block count or attribute size for extended attribute 2752 * detected during verify stage. This is a minor repair order function 2753 * i.e. it depends on previously created repair order to repair the disk. 2754 * 2755 * Input: 2756 * GPtr - global scavenger structure pointer 2757 * p - current repair order 2758 * 2759 * Output: 2760 * result - zero indicates success, non-zero failure. 2761 */ 2762static OSErr FixAttrSize(SGlobPtr GPtr, RepairOrderPtr p) 2763{ 2764 OSErr result = noErr; 2765 HFSPlusAttrKey *key; 2766 HFSPlusAttrRecord record; 2767 BTreeIterator iterator; 2768 FSBufferDescriptor btRecord; 2769 u_int16_t recSize; 2770 u_int64_t bytes; 2771 Boolean doReplace = false; 2772 2773 /* Initialize the iterator, attribute key, and attribute record */ 2774 ClearMemory(&iterator, sizeof(iterator)); 2775 key = (HFSPlusAttrKey *)&iterator.key; 2776 BuildAttributeKey(p->parid, 0, &p->name[1], p->name[0], key); 2777 2778 btRecord.bufferAddress = &record; 2779 btRecord.itemCount = 1; 2780 btRecord.itemSize = sizeof(record); 2781 2782 /* Search for the attribute record. 2783 * Warning: Attribute record of type kHFSPlusAttrInlineData may be 2784 * truncated on read! (4425232). This function only uses recordType 2785 * field from inline attribute record. 2786 */ 2787 result = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator, 2788 kInvalidMRUCacheKey, &btRecord, &recSize, &iterator); 2789 if (result) { 2790 DPRINTF (d_error|d_xattr, "%s: Cannot find attribute record (err = %d)\n", __FUNCTION__, result); 2791 goto out; 2792 } 2793 2794 /* We should only get record of type kHFSPlusAttrForkData */ 2795 if (record.recordType != kHFSPlusAttrForkData) { 2796 DPRINTF (d_error|d_xattr, "%s: Record found is not attribute fork data\n", __FUNCTION__); 2797 result = btNotFound; 2798 goto out; 2799 } 2800 2801 /* Manipulate necessary fields in the attribute record */ 2802 if (p->type == E_PEOAttr) { 2803 if ((u_int32_t)p->incorrect == record.forkData.theFork.totalBlocks) { 2804 record.forkData.theFork.totalBlocks = (u_int32_t)p->correct; 2805 doReplace = true; 2806 2807 /* Make sure that new block count is large enough to 2808 * cover the current LEOAttr. If not, truncate it. 2809 */ 2810 bytes = p->correct * (u_int64_t)GPtr->calculatedVCB->vcbBlockSize; 2811 if (record.forkData.theFork.logicalSize > bytes) { 2812 record.forkData.theFork.logicalSize = bytes; 2813 } 2814 } 2815 } else if (p->type == E_LEOAttr) { 2816 if (p->incorrect == record.forkData.theFork.logicalSize) { 2817 record.forkData.theFork.logicalSize = p->correct; 2818 doReplace = true; 2819 } 2820 } 2821 2822 /* Replace the attribute record, if required */ 2823 if (doReplace == true) { 2824 result = BTReplaceRecord(GPtr->calculatedAttributesFCB, &iterator, 2825 &btRecord, recSize); 2826 if (result) { 2827 DPRINTF (d_error|d_xattr, "%s: Cannot replace attribute record (err=%d)\n", __FUNCTION__, result); 2828 goto out; 2829 } 2830 } 2831 2832out: 2833 return(result); 2834} 2835 2836/* Function: FixBadExtent 2837 * 2838 * Description: The function repairs bad extent entry by zeroing out the 2839 * bad extent entry and truncating all extent information found after the 2840 * bad extent entry. 2841 * 2842 * 1. The information for repair is retrieved from the repair order passed 2843 * as parameter. 2844 * 2. If the start block of bad extent is zero, bad extent existed in 2845 * catalog record extent information. Lookup the catalog record, zero 2846 * out bad extent entry and all entries after it and update the catalog 2847 * record. 2848 * 3. If the start block of bad extent is non-zero, bad extent existed 2849 * in overflow extent. If the index of bad extent is zero, we want 2850 * to delete the record completely. If the index is non-zero, search 2851 * the extent record, zero out bad extent entry and all entries after it 2852 * and update the extent record. 2853 * 4. Search for any extent record in the overflow extent after the 2854 * the bad extent entry. If found, delete the record. 2855 * 5. If the file was truncated, create symlink in DamagedFiles folder 2856 * and display message to the user. 2857 * 2858 * Input: 2859 * GPtr - global scavenger structure pointer 2860 * p - current repair order 2861 * 2862 * Output: 2863 * result - zero indicates success, non-zero failure. 2864 */ 2865static OSErr FixBadExtent(SGlobPtr GPtr, RepairOrderPtr p) 2866{ 2867 OSErr err = noErr; 2868 UInt32 badExtentIndex; 2869 UInt32 extentStartBlock, foundStartBlock; 2870 UInt32 fileID; 2871 int i; 2872 UInt8 forkType; 2873 2874 UInt16 recordSize; 2875 ExtentRecord extentRecord; 2876 ExtentKey extentKey; 2877 UInt32 hint; 2878 Boolean isHFSPlus; 2879 Boolean didRepair; 2880 2881 fileID = p->parid; 2882 badExtentIndex = p->correct; 2883 extentStartBlock = p->hint; 2884 forkType = p->forkType; 2885 2886 isHFSPlus = VolumeObjectIsHFSPlus(); 2887 didRepair = false; 2888 2889 assert (forkType != kEAData); 2890 2891 /* extentStartBlock = 0, the bad extent exists in catalog record */ 2892 if (extentStartBlock == 0) { 2893 2894 CatalogRecord catRecord; 2895 CatalogKey catKey; 2896 HFSPlusExtentDescriptor *hfsPlusExtent; 2897 HFSExtentDescriptor *hfsExtent; 2898 2899 /* Lookup record in catalog BTree */ 2900 err = GetCatalogRecord(GPtr, fileID, isHFSPlus, &catKey, &catRecord, &recordSize); 2901 if (err) { 2902 plog("%s: Could not get catalog record for fileID %u\n", __FUNCTION__, fileID); 2903 goto out; 2904 } 2905 2906 /* Check record type */ 2907 assert ((catRecord.recordType == kHFSPlusFileRecord) || 2908 (catRecord.recordType == kHFSFileRecord)); 2909 2910 /* Zero out the bad extent entry and all entries after it */ 2911 if (isHFSPlus) { 2912 if (forkType == kDataFork) { 2913 hfsPlusExtent = catRecord.hfsPlusFile.dataFork.extents; 2914 } else { 2915 hfsPlusExtent = catRecord.hfsPlusFile.resourceFork.extents; 2916 } 2917 2918 for (i = badExtentIndex; i < GPtr->numExtents; i++) { 2919 hfsPlusExtent[i].startBlock = 0; 2920 hfsPlusExtent[i].blockCount = 0; 2921 } 2922 } else { 2923 if (forkType == kDataFork) { 2924 hfsExtent = catRecord.hfsFile.dataExtents; 2925 } else { 2926 hfsExtent = catRecord.hfsFile.rsrcExtents; 2927 } 2928 for (i = badExtentIndex; i < GPtr->numExtents; i++) { 2929 hfsExtent[i].startBlock = 0; 2930 hfsExtent[i].blockCount = 0; 2931 } 2932 } 2933 2934 /* Write the catalog record back */ 2935 err = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, kNoHint, 2936 &catRecord, recordSize, &hint); 2937 if (err) { 2938 plog("%s: Could not replace catalog record for fileID %u\n", __FUNCTION__, fileID); 2939 goto out; 2940 } 2941 didRepair = true; 2942 2943 } else { /* bad extent exists in overflow extent record */ 2944 2945 /* First entry in overflow extent record is bad, delete entire record */ 2946 if (badExtentIndex == 0) { 2947 goto del_overflow_extents; 2948 } 2949 2950 /* Lookup record in extents overflow BTree */ 2951 BuildExtentKey (isHFSPlus, forkType, fileID, extentStartBlock, &extentKey); 2952 err = SearchBTreeRecord (GPtr->calculatedExtentsFCB, &extentKey, kNoHint, 2953 &extentKey, &extentRecord, &recordSize, &hint); 2954 if (err) { 2955 plog("%s: Could not get overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__, fileID, forkType, extentStartBlock); 2956 goto out; 2957 } 2958 2959 /* Zero out the bad extent entry and all entries after it */ 2960 if (isHFSPlus) { 2961 for (i = badExtentIndex; i < GPtr->numExtents; i++) { 2962 extentRecord.hfsPlus[i].startBlock = 0; 2963 extentRecord.hfsPlus[i].blockCount = 0; 2964 } 2965 } else { 2966 for (i = badExtentIndex; i < GPtr->numExtents; i++) { 2967 extentRecord.hfs[i].startBlock = 0; 2968 extentRecord.hfs[i].blockCount = 0; 2969 } 2970 } 2971 2972 /* Write the extent record back */ 2973 err = ReplaceBTreeRecord(GPtr->calculatedExtentsFCB, &extentKey, hint, 2974 &extentRecord, recordSize, &hint); 2975 if (err) { 2976 plog("%s: Could not replace overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__, fileID, forkType, extentStartBlock); 2977 goto out; 2978 } 2979 didRepair = true; 2980 2981 /* The startBlock for extent record with bad extent entry is updated 2982 * because we use this startBlock later to lookup next extent record 2983 * for this file and forktype in overflow extent btree which should 2984 * be deleted. By incrementing the startBlock by one, we ensure that 2985 * we find the next record, if any, that should be deleted instead of 2986 * finding the same record that was updated above. 2987 */ 2988 extentStartBlock++; 2989 } 2990 2991del_overflow_extents: 2992 /* Search for overflow extent records. We should get a valid record only 2993 * if the bad extent entry was the first entry in the extent overflow 2994 * record. For all other cases, the search record will return an error 2995 */ 2996 BuildExtentKey (isHFSPlus, forkType, fileID, extentStartBlock, &extentKey); 2997 err = SearchBTreeRecord (GPtr->calculatedExtentsFCB, &extentKey, kNoHint, 2998 &extentKey, &extentRecord, &recordSize, &hint); 2999 if ((err != noErr) && (err != btNotFound)) { 3000 goto create_symlink; 3001 } 3002 3003 /* If we got error, check the next record */ 3004 if (err == btNotFound) { 3005 err = GetBTreeRecord(GPtr->calculatedExtentsFCB, 1, &extentKey, &extentRecord, 3006 &recordSize, &hint); 3007 } 3008 3009 while (err == noErr) { 3010 /* Check if the record has correct fileID, forkType */ 3011 if (isHFSPlus) { 3012 if ((fileID != extentKey.hfsPlus.fileID) || 3013 (forkType != extentKey.hfsPlus.forkType)) { 3014 break; 3015 } 3016 foundStartBlock = extentKey.hfsPlus.startBlock; 3017 } else { 3018 if ((fileID != extentKey.hfs.fileID) || 3019 (forkType != extentKey.hfs.forkType)) { 3020 break; 3021 } 3022 foundStartBlock = extentKey.hfs.startBlock; 3023 } 3024 3025 /* Delete the extent record */ 3026 err = DeleteBTreeRecord(GPtr->calculatedExtentsFCB, &extentKey); 3027 DPRINTF (d_info, "%s: Deleting extent overflow for fileID=%u, forkType=%u, startBlock=%u\n", __FUNCTION__, fileID, forkType, foundStartBlock); 3028 if (err) { 3029 goto create_symlink; 3030 } 3031 didRepair = true; 3032 3033 /* Get the next extent record */ 3034 err = GetBTreeRecord(GPtr->calculatedExtentsFCB, 1, &extentKey, &extentRecord, 3035 &recordSize, &hint); 3036 } 3037 3038 if (err == btNotFound) { 3039 err = noErr; 3040 } 3041 3042 UpdateBTreeHeader(GPtr->calculatedExtentsFCB); 3043 3044create_symlink: 3045 /* Create symlink for repaired files in damaged files folder */ 3046 if (didRepair == true) { 3047 /* Create symlink for damaged files */ 3048 (void) CreateCorruptFileSymlink(GPtr, fileID); 3049 } 3050 3051out: 3052 return err; 3053} 3054 3055/* Function: FixOrphanedFiles 3056 * 3057 * Description: 3058 * Incorrect number of thread records get fixed in this function. 3059 * 3060 * The function traverses the entire catalog Btree. 3061 * 3062 * For a file/folder record, it tries to lookup its corresponding thread 3063 * record. If the thread record does not exist, or is not correct, a new 3064 * thread record is created. The parent ID, record type, and the name of 3065 * the file/folder are compared for correctness. 3066 * For plain HFS, a thread record is only looked-up if kHFSThreadExistsMask is set. 3067 * 3068 * For a thread record, it tries to lookup its corresponding file/folder 3069 * record. If its does not exist or is not correct, the thread record 3070 * is deleted. The file/folder ID is compared for correctness. 3071 * 3072 * Input: 1. GPtr - pointer to global scavenger area 3073 * 3074 * Return value: 3075 * zero means success 3076 * non-zero means failure 3077 */ 3078static OSErr FixOrphanedFiles ( SGlobPtr GPtr ) 3079{ 3080 CatalogKey key; 3081 CatalogKey foundKey; 3082 CatalogKey tempKey; 3083 CatalogRecord record; 3084 CatalogRecord threadRecord; 3085 CatalogRecord record2; 3086 HFSCatalogNodeID parentID; 3087 HFSCatalogNodeID cNodeID = 0; 3088 BTreeIterator savedIterator; 3089 UInt32 hint; 3090 UInt32 hint2; 3091 UInt32 threadHint; 3092 OSErr err; 3093 UInt16 recordSize; 3094 UInt16 threadRecordSize; 3095 SInt16 recordType; 3096 SInt16 foundRecType; 3097 SInt16 selCode = 0x8001; /* Get first record */ 3098 Boolean isHFSPlus; 3099 BTreeControlBlock *btcb = GetBTreeControlBlock( kCalculatedCatalogRefNum ); 3100 Boolean isDirectory; 3101 3102 isHFSPlus = VolumeObjectIsHFSPlus( ); 3103 CopyMemory( &btcb->lastIterator, &savedIterator, sizeof(BTreeIterator) ); 3104 3105 do 3106 { 3107 // Save/Restore Iterator around calls to GetBTreeRecord 3108 CopyMemory( &savedIterator, &btcb->lastIterator, sizeof(BTreeIterator) ); 3109 err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint ); 3110 if ( err != noErr ) break; 3111 CopyMemory( &btcb->lastIterator, &savedIterator, sizeof(BTreeIterator) ); 3112 3113 selCode = 1; // kNextRecord 3114 recordType = record.recordType; 3115 3116 isDirectory = false; 3117 3118 switch( recordType ) 3119 { 3120 case kHFSFileRecord: 3121 // If the small file is not supposed to have a thread, just break 3122 if ( ( record.hfsFile.flags & kHFSThreadExistsMask ) == 0 ) 3123 break; 3124 3125 case kHFSFolderRecord: 3126 case kHFSPlusFolderRecord: 3127 case kHFSPlusFileRecord: 3128 3129 // Locate the thread associated with this record 3130 3131 (void) CheckForStop( GPtr ); // rotate cursor 3132 3133 parentID = isHFSPlus == true ? foundKey.hfsPlus.parentID : foundKey.hfs.parentID; 3134 threadHint = hint; 3135 3136 switch( recordType ) 3137 { 3138 case kHFSFolderRecord: 3139 cNodeID = record.hfsFolder.folderID; 3140 isDirectory = true; 3141 break; 3142 case kHFSFileRecord: 3143 cNodeID = record.hfsFile.fileID; 3144 break; 3145 case kHFSPlusFolderRecord: 3146 cNodeID = record.hfsPlusFolder.folderID; 3147 isDirectory = true; 3148 break; 3149 case kHFSPlusFileRecord: 3150 cNodeID = record.hfsPlusFile.fileID; 3151 break; 3152 } 3153 3154 //-- Build the key for the file thread 3155 BuildCatalogKey( cNodeID, nil, isHFSPlus, &key ); 3156 3157 err = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &key, kNoHint, 3158 &tempKey, &threadRecord, &threadRecordSize, &hint2 ); 3159 3160 /* We found a thread record for this file/folder record. */ 3161 if (err == noErr) { 3162 /* Check if the parent ID and nodeName are same, and recordType is as 3163 * expected. If not, we are missing a correct thread record. Force 3164 * btNotFound in such case. 3165 */ 3166 if (isHFSPlus) { 3167 /* Check thread's recordType */ 3168 foundRecType = threadRecord.hfsPlusThread.recordType; 3169 if (isDirectory == true) { 3170 if (foundRecType != kHFSPlusFolderThreadRecord) { 3171 err = btNotFound; 3172 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3173 plog ("\t%s: Folder thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType); 3174 } 3175 } 3176 } else { 3177 if (foundRecType != kHFSPlusFileThreadRecord) { 3178 err = btNotFound; 3179 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3180 plog ("\t%s: File thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType); 3181 } 3182 } 3183 } 3184 3185 /* Compare parent ID */ 3186 if (parentID != threadRecord.hfsPlusThread.parentID) { 3187 err = btNotFound; 3188 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3189 plog ("\t%s: parentID for id=%u do not match (fileKey=%u threadRecord=%u)\n", __FUNCTION__, cNodeID, parentID, threadRecord.hfsPlusThread.parentID); 3190 } 3191 } 3192 3193 /* Compare nodeName from file/folder key and thread reocrd */ 3194 if (!((foundKey.hfsPlus.nodeName.length == threadRecord.hfsPlusThread.nodeName.length) 3195 && (!bcmp(foundKey.hfsPlus.nodeName.unicode, 3196 threadRecord.hfsPlusThread.nodeName.unicode, 3197 foundKey.hfsPlus.nodeName.length * 2)))) { 3198 err = btNotFound; 3199 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3200 unsigned maxLength = foundKey.hfsPlus.nodeName.length; 3201 if (maxLength < threadRecord.hfsPlusThread.nodeName.length) 3202 maxLength = threadRecord.hfsPlusThread.nodeName.length; 3203 3204 plog ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__, cNodeID); 3205 if (cur_debug_level & d_dump_record) 3206 { 3207 plog("\tFile/Folder record:\n"); 3208 HexDump(&foundKey, foundKey.hfsPlus.keyLength + 2, FALSE); 3209 plog("--\n"); 3210 HexDump(&record, recordSize, FALSE); 3211 plog("\n"); 3212 plog("\tThread record:\n"); 3213 HexDump(&tempKey, tempKey.hfsPlus.keyLength + 2, FALSE); 3214 plog("--\n"); 3215 HexDump(&threadRecord, threadRecordSize, FALSE); 3216 plog("\n"); 3217 } 3218 } 3219 } 3220 3221 /* If any of the above checks failed, delete the bad thread record */ 3222 if (err == btNotFound) { 3223 (void) DeleteBTreeRecord(GPtr->calculatedCatalogFCB, &tempKey); 3224 } 3225 } else { /* plain HFS */ 3226 /* Check thread's recordType */ 3227 foundRecType = threadRecord.hfsThread.recordType; 3228 if (isDirectory == true) { 3229 if (foundRecType != kHFSFolderThreadRecord) { 3230 err = btNotFound; 3231 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3232 plog ("\t%s: Folder thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType); 3233 } 3234 } 3235 } else { 3236 if (foundRecType != kHFSFileThreadRecord) { 3237 err = btNotFound; 3238 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3239 plog ("\t%s: File thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType); 3240 } 3241 } 3242 } 3243 3244 /* Compare parent ID */ 3245 if (parentID != threadRecord.hfsThread.parentID) { 3246 err = btNotFound; 3247 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3248 plog ("\t%s: parentID for id=%u do not match (fileKey=%u threadRecord=%u)\n", __FUNCTION__, cNodeID, parentID, threadRecord.hfsThread.parentID); 3249 } 3250 } 3251 3252 /* Compare nodeName from file/folder key and thread reocrd */ 3253 if (!((foundKey.hfs.nodeName[0] == threadRecord.hfsThread.nodeName[0]) 3254 && (!bcmp(&foundKey.hfs.nodeName[1], 3255 &threadRecord.hfsThread.nodeName[1], 3256 foundKey.hfs.nodeName[0])))) { 3257 err = btNotFound; 3258 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3259 plog ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__, cNodeID); 3260 } 3261 } 3262 3263 /* If any of the above checks failed, delete the bad thread record */ 3264 if (err == btNotFound) { 3265 (void) DeleteBTreeRecord(GPtr->calculatedCatalogFCB, &tempKey); 3266 } 3267 } 3268 } /* err == noErr */ 3269 3270 // For missing thread records, just create the thread 3271 if ( err == btNotFound ) 3272 { 3273 // Create the missing thread record. 3274 3275 isDirectory = false; 3276 switch( recordType ) 3277 { 3278 case kHFSFolderRecord: 3279 case kHFSPlusFolderRecord: 3280 isDirectory = true; 3281 break; 3282 } 3283 3284 //-- Fill out the data for the new file thread from the key 3285 // of catalog file/folder record 3286 recordSize = BuildThreadRec( &foundKey, &threadRecord, isHFSPlus, 3287 isDirectory ); 3288 err = InsertBTreeRecord( GPtr->calculatedCatalogFCB, &key, 3289 &threadRecord, recordSize, &threadHint ); 3290 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3291 plog ("\t%s: Created thread record for id=%u (err=%u)\n", __FUNCTION__, cNodeID, err); 3292 } 3293 } 3294 3295 break; 3296 3297 3298 case kHFSFolderThreadRecord: 3299 case kHFSPlusFolderThreadRecord: 3300 isDirectory = true; 3301 3302 case kHFSFileThreadRecord: 3303 case kHFSPlusFileThreadRecord: 3304 3305 // Find the catalog record, if it does not exist, delete the existing thread. 3306 if ( isHFSPlus ) 3307 BuildCatalogKey( record.hfsPlusThread.parentID, (const CatalogName *)&record.hfsPlusThread.nodeName, isHFSPlus, &key ); 3308 else 3309 BuildCatalogKey( record.hfsThread.parentID, (const CatalogName *)&record.hfsThread.nodeName, isHFSPlus, &key ); 3310 3311 err = SearchBTreeRecord ( GPtr->calculatedCatalogFCB, &key, kNoHint, &tempKey, &record2, &recordSize, &hint2 ); 3312 3313 /* We found a file/folder record for this thread record. */ 3314 if (err == noErr) { 3315 /* Check if the file/folder ID are same and if the recordType is as 3316 * expected. If not, we are missing a correct file/folder record. 3317 * Delete the extra thread record 3318 */ 3319 if (isHFSPlus) { 3320 /* Check recordType */ 3321 foundRecType = record2.hfsPlusFile.recordType; 3322 if (isDirectory == true) { 3323 if (foundRecType != kHFSPlusFolderRecord) { 3324 err = btNotFound; 3325 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3326 plog ("\t%s: Folder recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType); 3327 } 3328 } 3329 } else { 3330 if (foundRecType != kHFSPlusFileRecord) { 3331 err = btNotFound; 3332 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3333 plog ("\t%s: File recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType); 3334 } 3335 } 3336 } 3337 3338 /* Compare file/folder ID */ 3339 if (foundKey.hfsPlus.parentID != record2.hfsPlusFile.fileID) { 3340 err = btNotFound; 3341 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3342 plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfsPlus.parentID, record2.hfsPlusFile.fileID, record.hfsPlusThread.parentID); 3343 } 3344 } 3345 } else { /* plain HFS */ 3346 /* Check recordType */ 3347 foundRecType = record2.hfsFile.recordType; 3348 if (isDirectory == true) { 3349 if (foundRecType != kHFSFolderRecord) { 3350 err = btNotFound; 3351 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3352 plog ("\t%s: Folder recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType); 3353 } 3354 } 3355 } else { 3356 if (foundRecType != kHFSFileRecord) { 3357 err = btNotFound; 3358 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3359 plog ("\t%s: File recordType mismatch for id=%u (found=%u)\n", __FUNCTION__, cNodeID, foundRecType); 3360 } 3361 } 3362 } 3363 3364 /* Compare file/folder ID */ 3365 if (foundKey.hfs.parentID != record2.hfsFile.fileID) { 3366 err = btNotFound; 3367 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3368 if (recordType == kHFSFolderThreadRecord) { 3369 plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfs.parentID, record2.hfsFolder.folderID, record.hfsThread.parentID); 3370 } else { 3371 plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__, foundKey.hfs.parentID, record2.hfsFile.fileID, record.hfsThread.parentID); 3372 } 3373 } 3374 } 3375 } 3376 } /* if (err == noErr) */ 3377 3378 if ( err != noErr ) 3379 { 3380 err = DeleteBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey ); 3381 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3382 if (isHFSPlus) { 3383 plog ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__, foundKey.hfsPlus.parentID, err); 3384 } else { 3385 plog ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__, foundKey.hfs.parentID, err); 3386 } 3387 } 3388 } 3389 3390 break; 3391 3392 default: 3393 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3394 plog ("\t%s: Unknown record type.\n", __FUNCTION__); 3395 } 3396 break; 3397 3398 } 3399 } while ( err == noErr ); 3400 3401 if ( err == btNotFound ) 3402 err = noErr; // all done, no more catalog records 3403 3404// if (err == noErr) 3405// err = BTFlushPath( GPtr->calculatedCatalogFCB ); 3406 3407 return( err ); 3408} 3409 3410 3411static OSErr RepairReservedBTreeFields ( SGlobPtr GPtr ) 3412{ 3413 CatalogRecord record; 3414 CatalogKey foundKey; 3415 UInt16 recordSize; 3416 SInt16 selCode; 3417 UInt32 hint; 3418 UInt32 *reserved; 3419 OSErr err; 3420 3421 selCode = 0x8001; // start with 1st record 3422 3423 err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint ); 3424 if ( err != noErr ) goto EXIT; 3425 3426 selCode = 1; // get next record from now on 3427 3428 do 3429 { 3430 switch( record.recordType ) 3431 { 3432 case kHFSPlusFolderRecord: 3433 /* XXX -- this should not always be cleared out (but doesn't appear to being called) */ 3434 if ( record.hfsPlusFolder.flags != 0 ) 3435 { 3436 record.hfsPlusFolder.flags = 0; 3437 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint ); 3438 } 3439 break; 3440 3441 case kHFSPlusFileRecord: 3442 // Note: bit 7 (mask 0x80) of flags is unused in HFS or HFS Plus. However, Inside Macintosh: Files 3443 // describes it as meaning the file record is in use. Some non-Apple implementations end up setting 3444 // this bit, so we just ignore it. 3445 if ( ( record.hfsPlusFile.flags & (UInt16) ~(0X83) ) != 0 ) 3446 { 3447 record.hfsPlusFile.flags &= 0X83; 3448 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint ); 3449 } 3450 break; 3451 3452 case kHFSFolderRecord: 3453 if ( record.hfsFolder.flags != 0 ) 3454 { 3455 record.hfsFolder.flags = 0; 3456 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint ); 3457 } 3458 break; 3459 3460 case kHFSFolderThreadRecord: 3461 case kHFSFileThreadRecord: 3462 reserved = (UInt32*) &(record.hfsThread.reserved); 3463 if ( reserved[0] || reserved[1] ) 3464 { 3465 reserved[0] = 0; 3466 reserved[1] = 0; 3467 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint ); 3468 } 3469 break; 3470 3471 case kHFSFileRecord: 3472 // Note: bit 7 (mask 0x80) of flags is unused in HFS or HFS Plus. However, Inside Macintosh: Files 3473 // describes it as meaning the file record is in use. Some non-Apple implementations end up setting 3474 // this bit, so we just ignore it. 3475 if ( ( ( record.hfsFile.flags & (UInt8) ~(0X83) ) != 0 ) 3476 || ( record.hfsFile.dataStartBlock != 0 ) 3477 || ( record.hfsFile.rsrcStartBlock != 0 ) 3478 || ( record.hfsFile.reserved != 0 ) ) 3479 { 3480 record.hfsFile.flags &= 0X83; 3481 record.hfsFile.dataStartBlock = 0; 3482 record.hfsFile.rsrcStartBlock = 0; 3483 record.hfsFile.reserved = 0; 3484 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint ); 3485 } 3486 break; 3487 3488 default: 3489 break; 3490 } 3491 3492 if ( err != noErr ) goto EXIT; 3493 3494 err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint ); 3495 3496 } while ( err == noErr ); 3497 3498 if ( err == btNotFound ) 3499 err = noErr; // all done, no more catalog records 3500 3501EXIT: 3502 return( err ); 3503} 3504 3505 3506/* Function: FixOrphanAttrRecord 3507 * 3508 * Description: 3509 * The function traverses the attribute BTree completely and for every 3510 * leaf record, calls CheckAttributeRecord. CheckAttributeRecord function 3511 * is common function for verify and repair stage. CheckAttributeRecord 3512 * deletes invalid/orphaned extended attribute records under following 3513 * conditions - 3514 * 1. record is overflow extents with no valid fork data or overflow extents 3515 * preceeding it. 3516 * 2. record type is unknown. 3517 * 3518 * Input: 3519 * GPtr - global scavenger structure pointer 3520 * 3521 * Output: 3522 * error code - zero on success, non-zero on failure. 3523 */ 3524static OSErr FixOrphanAttrRecord(SGlobPtr GPtr) 3525{ 3526 OSErr err = noErr; 3527 UInt16 selCode; 3528 UInt32 hint; 3529 3530 HFSPlusAttrRecord record; 3531 HFSPlusAttrKey key; 3532 UInt16 recordSize; 3533 3534 /* Zero out last attribute information from global scavenger structure */ 3535 bzero (&(GPtr->lastAttrInfo), sizeof(GPtr->lastAttrInfo)); 3536 3537 /* Warning: Attribute record of type kHFSPlusAttrInlineData may be 3538 * truncated on read! (4425232). CheckAttributeRecord only uses recordType 3539 * field from inline attribute record. 3540 */ 3541 selCode = 0x8001; 3542 err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &key, 3543 &record, &recordSize, &hint); 3544 if (err != noErr) { 3545 goto out; 3546 } 3547 3548 selCode = 1; 3549 do { 3550 err = CheckAttributeRecord(GPtr, &key, &record, recordSize); 3551 if (err) { 3552 break; 3553 } 3554 3555 /* Access the next record. 3556 * Warning: Attribute record of type kHFSPlusAttrInlineData may be 3557 * truncated on read! (4425232). CheckAttributeRecord only uses recordType 3558 * field from inline attribute record. 3559 */ 3560 err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &key, 3561 &record, &recordSize, &hint); 3562 } while (err == noErr); 3563 3564 if (err == btNotFound) { 3565 err = noErr; 3566 } 3567 3568out: 3569 return(err); 3570} 3571 3572/* Function: GetCatalogRecord 3573 * 3574 * Description: 3575 * This function returns a catalog file/folder record for a given 3576 * file/folder ID from the catalog BTree. 3577 * 3578 * Input: 1. GPtr - pointer to global scavenger area 3579 * 2. fileID - file ID to search the file/folder record 3580 * 3. isHFSPlus - boolean value to indicate if volume is HFSPlus 3581 * 3582 * Output: 1. catKey - catalog key 3583 * 2. catRecord - catalog record for given ID 3584 * 3. recordSize - size of catalog record return back 3585 * 3586 * Return value: 3587 * zero means success 3588 * non-zero means failure 3589 */ 3590static OSErr GetCatalogRecord(SGlobPtr GPtr, UInt32 fileID, Boolean isHFSPlus, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize) 3591{ 3592 OSErr err = noErr; 3593 CatalogKey catThreadKey; 3594 CatalogName catalogName; 3595 UInt32 hint; 3596 uint32_t thread_key_parentID; 3597 3598 /* Look up for catalog thread record for the file that owns attribute */ 3599 BuildCatalogKey(fileID, NULL, isHFSPlus, &catThreadKey); 3600 err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catThreadKey, kNoHint, catKey, catRecord, recordSize, &hint); 3601 if (err) { 3602 plog ("%s: No matching catalog thread record found\n", __FUNCTION__); 3603 goto out; 3604 } 3605 3606#if DEBUG_XATTR 3607 plog ("%s(%s,%d):1 recordType=%x, flags=%x\n", __FUNCTION__, __FILE__, __LINE__, 3608 catRecord->hfsPlusFile.recordType, 3609 catRecord->hfsPlusFile.flags); 3610#endif 3611 3612 /* We were expecting a thread record. The recordType says it is a file 3613 * record or folder record. Return error. 3614 */ 3615 if ((catRecord->hfsPlusFile.recordType == kHFSPlusFolderRecord) || 3616 (catRecord->hfsPlusFile.recordType == kHFSPlusFileRecord)) { 3617 err = fsBTRecordNotFoundErr; 3618 goto out; 3619 } 3620 thread_key_parentID = catKey->hfsPlus.parentID; 3621 3622 /* It is either a file thread record or folder thread record. 3623 * Look up for catalog record for the file that owns attribute */ 3624 CopyCatalogName((CatalogName *)&(catRecord->hfsPlusThread.nodeName), &catalogName, isHFSPlus); 3625 BuildCatalogKey(catRecord->hfsPlusThread.parentID, &catalogName, isHFSPlus, catKey); 3626 err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, catKey, kNoHint, catKey, catRecord, recordSize, &hint); 3627 if (err) { 3628 plog ("%s: No matching catalog record found\n", __FUNCTION__); 3629 if (cur_debug_level & d_dump_record) 3630 { 3631 plog ("Searching for key:\n"); 3632 HexDump(catKey, CalcKeySize(GPtr->calculatedCatalogBTCB, (BTreeKey *)catKey), FALSE); 3633 } 3634 goto out; 3635 } 3636 3637#if DEBUG_XATTR 3638 plog ("%s(%s,%d):2 recordType=%x, flags=%x\n", __FUNCTION__, __FILE__, __LINE__, 3639 catRecord->hfsPlusFile.recordType, 3640 catRecord->hfsPlusFile.flags); 3641#endif 3642 3643 /* For catalog file or folder record, the parentID in the thread 3644 * record's key should be equal to the fileID in the file/folder 3645 * record --- which is equal to the ID of the file/folder record 3646 * that is being looked up. If not, mark the volume for repair. 3647 */ 3648 if (thread_key_parentID != catRecord->hfsPlusFile.fileID) { 3649 RcdError(GPtr, E_IncorrectNumThdRcd); 3650 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3651 plog("\t%s: fileID=%u, thread.key.parentID=%u, record.fileID=%u\n", 3652 __FUNCTION__, fileID, thread_key_parentID, catRecord->hfsPlusFile.fileID); 3653 } 3654 GPtr->CBTStat |= S_Orphan; 3655 } 3656out: 3657 return err; 3658} 3659 3660/* Function: RepairAttributesCheckABT 3661 * 3662 * Description: 3663 * This function is called from RepairAttributes (to repair extended 3664 * attributes) during repair stage of fsck_hfs. 3665 * 3666 * 1. Make full pass through attribute BTree. 3667 * 2. For every unique fileID, lookup its catalog record in Catalog BTree. 3668 * 3. If found, check the attributes/security bit in catalog record. 3669 * If not set correctly, set it and replace the catalog record. 3670 * 4. If not found, return error 3671 * 3672 * Input: 1. GPtr - pointer to global scavenger area 3673 * 2. isHFSPlus - boolean value to indicate if volume is HFSPlus 3674 * 3675 * Output: err - Function result 3676 * zero means success 3677 * non-zero means failure 3678 */ 3679static OSErr RepairAttributesCheckABT(SGlobPtr GPtr, Boolean isHFSPlus) 3680{ 3681 OSErr err = noErr; 3682 UInt16 selCode; /* select access pattern for BTree */ 3683 UInt32 hint; 3684 3685 HFSPlusAttrRecord attrRecord; 3686 HFSPlusAttrKey attrKey; 3687 UInt16 attrRecordSize; 3688 CatalogRecord catRecord; 3689 CatalogKey catKey; 3690 UInt16 catRecordSize; 3691 3692 attributeInfo lastID; /* fileID for the last attribute searched */ 3693 Boolean didRecordChange = false; /* whether catalog record was changed after checks */ 3694 3695#if DEBUG_XATTR 3696 char attrName[XATTR_MAXNAMELEN]; 3697 size_t len; 3698#endif 3699 3700 lastID.fileID = 0; 3701 lastID.hasSecurity = false; 3702 3703 selCode = 0x8001; /* Get first record from BTree */ 3704 /* Warning: Attribute record of type kHFSPlusAttrInlineData may be 3705 * truncated on read! (4425232). This function only uses recordType 3706 * field from inline attribute record. 3707 */ 3708 err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &attrKey, &attrRecord, &attrRecordSize, &hint); 3709 if (err == btNotFound) { 3710 /* The attributes B-tree is empty, which is OK; nothing to do. */ 3711 err = noErr; 3712 goto out; 3713 } 3714 if (err != noErr) goto out; 3715 3716 selCode = 1; /* Get next record */ 3717 do { 3718#if DEBUG_XATTR 3719 /* Convert unicode attribute name to char for ACL check */ 3720 (void) utf_encodestr(attrKey.attrName, attrKey.attrNameLen * 2, attrName, &len, sizeof(attrName)); 3721 attrName[len] = '\0'; 3722 plog ("%s(%s,%d): Found attrName=%s for fileID=%d\n", __FUNCTION__, __FILE__, __LINE__, attrName, attrKey.fileID); 3723#endif 3724 3725 if (attrKey.fileID != lastID.fileID) { 3726 /* We found an attribute with new file ID */ 3727 3728 /* Replace the previous catalog record only if we updated the flags */ 3729 if (didRecordChange == true) { 3730 err = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &catKey , kNoHint, &catRecord, catRecordSize, &hint); 3731 if (err) { 3732 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3733 plog ("\t%s: Error in replacing catalog record for id=%u\n", __FUNCTION__, lastID.fileID); 3734 } 3735 goto out; 3736 } 3737 } 3738 3739 didRecordChange = false; /* reset to indicate new record has not changed */ 3740 3741 /* Get the catalog record for the new fileID */ 3742 err = GetCatalogRecord(GPtr, attrKey.fileID, isHFSPlus, &catKey, &catRecord, &catRecordSize); 3743 if (err) { 3744 /* No catalog record was found for this fileID. */ 3745 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3746 plog ("\t%s: No matching catalog record found for id=%u\n", __FUNCTION__, attrKey.fileID); 3747 } 3748 3749 /* 3984119 - Do not delete extended attributes for file IDs less 3750 * kHFSFirstUserCatalogNodeID but not equal to kHFSRootFolderID 3751 * in prime modulus checksum. These file IDs do not have 3752 * any catalog record 3753 */ 3754 if ((attrKey.fileID < kHFSFirstUserCatalogNodeID) && 3755 (attrKey.fileID != kHFSRootFolderID)) { 3756#if DEBUG_XATTR 3757 plog ("%s: Ignore catalog check for fileID=%d for attribute=%s\n", __FUNCTION__, attrKey.fileID, attrName); 3758#endif 3759 goto getnext; 3760 } 3761 3762 /* Delete this orphan extended attribute */ 3763 err = delete_attr_record(GPtr, &attrKey, &attrRecord); 3764 if (err) { 3765 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) { 3766 plog ("\t%s: Error in deleting attribute record for id=%u\n", __FUNCTION__, attrKey.fileID); 3767 } 3768 goto out; 3769 } 3770 goto getnext; 3771 } 3772 3773 lastID.fileID = attrKey.fileID; /* set last fileID to the new ID */ 3774 lastID.hasSecurity = false; /* reset to indicate new fileID does not have security */ 3775 3776 /* Check the Attribute bit */ 3777 if (!(catRecord.hfsPlusFile.flags & kHFSHasAttributesMask)) { 3778 /* kHFSHasAttributeBit should be set */ 3779 catRecord.hfsPlusFile.flags |= kHFSHasAttributesMask; 3780 didRecordChange = true; 3781 } 3782 3783 /* Check if attribute is ACL */ 3784 if (!bcmp(attrKey.attrName, GPtr->securityAttrName, GPtr->securityAttrLen)) { 3785 lastID.hasSecurity = true; 3786 /* Check the security bit */ 3787 if (!(catRecord.hfsPlusFile.flags & kHFSHasSecurityMask)) { 3788 /* kHFSHasSecurityBit should be set */ 3789 catRecord.hfsPlusFile.flags |= kHFSHasSecurityMask; 3790 didRecordChange = true; 3791 } 3792 } 3793 } else { 3794 /* We have seen attribute for fileID in past */ 3795 3796 /* If last time we saw this fileID did not have an ACL and this 3797 * extended attribute is an ACL, ONLY check consistency of 3798 * security bit from Catalog record 3799 */ 3800 if ((lastID.hasSecurity == false) && !bcmp(attrKey.attrName, GPtr->securityAttrName, GPtr->securityAttrLen)) { 3801 lastID.hasSecurity = true; 3802 /* Check the security bit */ 3803 if (!(catRecord.hfsPlusFile.flags & kHFSHasSecurityMask)) { 3804 /* kHFSHasSecurityBit should be set */ 3805 catRecord.hfsPlusFile.flags |= kHFSHasSecurityMask; 3806 didRecordChange = true; 3807 } 3808 } 3809 } 3810 3811getnext: 3812 /* Access the next record 3813 * Warning: Attribute record of type kHFSPlusAttrInlineData may be 3814 * truncated on read! (4425232). This function only uses recordType 3815 * field from inline attribute record. 3816 */ 3817 err = GetBTreeRecord(GPtr->calculatedAttributesFCB, selCode, &attrKey, &attrRecord, &attrRecordSize, &hint); 3818 } while (err == noErr); 3819 3820 err = noErr; 3821 3822 /* Replace the catalog record for last extended attribute in the attributes BTree 3823 * only if we updated the flags 3824 */ 3825 if (didRecordChange == true) { 3826 err = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &catKey , kNoHint, &catRecord, catRecordSize, &hint); 3827 if (err) { 3828#if DEBUG_XATTR 3829 plog ("%s: Error in replacing Catalog Record\n", __FUNCTION__); 3830#endif 3831 goto out; 3832 } 3833 } 3834 3835out: 3836 return err; 3837} 3838 3839/* Function: RepairAttributesCheckCBT 3840 * 3841 * Description: 3842 * This function is called from RepairAttributes (to repair extended 3843 * attributes) during repair stage of fsck_hfs. 3844 * 3845 * NOTE: The case where attribute exists and bit is not set is being taken care in 3846 * RepairAttributesCheckABT. This function determines relationship from catalog 3847 * Btree to attribute Btree, and not vice-versa. 3848 3849 * 1. Make full pass through catalog BTree. 3850 * 2. For every fileID, if the attributes/security bit is set, 3851 * lookup all the extended attributes in the attributes BTree. 3852 * 3. If found, check that if bits are set correctly. 3853 * 4. If not found, clear the bits. 3854 * 3855 * Input: 1. GPtr - pointer to global scavenger area 3856 * 2. isHFSPlus - boolean value to indicate if volume is HFSPlus 3857 * 3858 * Output: err - Function result 3859 * zero means success 3860 * non-zero means failure 3861 */ 3862static OSErr RepairAttributesCheckCBT(SGlobPtr GPtr, Boolean isHFSPlus) 3863{ 3864 OSErr err = noErr; 3865 UInt16 selCode; /* select access pattern for BTree */ 3866 UInt16 recordSize; 3867 UInt32 hint; 3868 3869 HFSPlusAttrKey *attrKey; 3870 CatalogRecord catRecord; 3871 CatalogKey catKey; 3872 3873 Boolean didRecordChange = false; /* whether catalog record was changed after checks */ 3874 3875 BTreeIterator iterator; 3876 3877 UInt32 curFileID; 3878 Boolean curRecordHasAttributes = false; 3879 Boolean curRecordHasSecurity = false; 3880 3881 selCode = 0x8001; /* Get first record from BTree */ 3882 err = GetBTreeRecord(GPtr->calculatedCatalogFCB, selCode, &catKey, &catRecord, &recordSize, &hint); 3883 if ( err != noErr ) goto out; 3884 3885 selCode = 1; /* Get next record */ 3886 do { 3887 /* Check only file record and folder record, else skip to next record */ 3888 if ( (catRecord.hfsPlusFile.recordType != kHFSPlusFileRecord) && 3889 (catRecord.hfsPlusFile.recordType != kHFSPlusFolderRecord)) { 3890 goto getnext; 3891 } 3892 3893 /* Check if catalog record has attribute and/or security bit set, else 3894 * skip to next record 3895 */ 3896 if ( ((catRecord.hfsPlusFile.flags & kHFSHasAttributesMask) == 0) && 3897 ((catRecord.hfsPlusFile.flags & kHFSHasSecurityMask) == 0) ) { 3898 goto getnext; 3899 } 3900 3901 /* Initialize some flags */ 3902 didRecordChange = false; 3903 curRecordHasSecurity = false; 3904 curRecordHasAttributes = false; 3905 3906 /* Access all extended attributes for this fileID */ 3907 curFileID = catRecord.hfsPlusFile.fileID; 3908 3909 /* Initialize the iterator and attribute key */ 3910 ClearMemory(&iterator, sizeof(BTreeIterator)); 3911 attrKey = (HFSPlusAttrKey *)&iterator.key; 3912 attrKey->keyLength = kHFSPlusAttrKeyMinimumLength; 3913 attrKey->fileID = curFileID; 3914 3915 /* Search for attribute with NULL name. This will place the iterator at correct fileID location in BTree */ 3916 err = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator, kInvalidMRUCacheKey, NULL, NULL, &iterator); 3917 if (err && (err != btNotFound)) { 3918#if DEBUG_XATTR 3919 plog ("%s: No matching attribute record found\n", __FUNCTION__); 3920#endif 3921 goto out; 3922 } 3923 3924 /* Iterate over to all extended attributes for given fileID */ 3925 err = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord, &iterator, NULL, NULL); 3926 3927 /* Check only if we did _find_ an attribute record for the current fileID */ 3928 while ((err == noErr) && (attrKey->fileID == curFileID)) { 3929 /* Current record should have attribute bit set */ 3930 curRecordHasAttributes = true; 3931 3932 /* Current record should have security bit set */ 3933 if (!bcmp(attrKey->attrName, GPtr->securityAttrName, GPtr->securityAttrLen)) { 3934 curRecordHasSecurity = true; 3935 } 3936 3937 /* Get the next record */ 3938 err = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord, &iterator, NULL, NULL); 3939 } 3940 3941 /* Determine if we need to update the catalog record */ 3942 if ((curRecordHasAttributes == false) && (catRecord.hfsPlusFile.flags & kHFSHasAttributesMask)) { 3943 /* If no attribute exists and attributes bit is set, clear it */ 3944 catRecord.hfsPlusFile.flags &= ~kHFSHasAttributesMask; 3945 didRecordChange = true; 3946 } 3947 3948 if ((curRecordHasSecurity == false) && (catRecord.hfsPlusFile.flags & kHFSHasSecurityMask)) { 3949 /* If no security attribute exists and security bit is set, clear it */ 3950 catRecord.hfsPlusFile.flags &= ~kHFSHasSecurityMask; 3951 didRecordChange = true; 3952 } 3953 3954 /* If there was any change in catalog record, write it back to disk */ 3955 if (didRecordChange == true) { 3956 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &catKey , kNoHint, &catRecord, recordSize, &hint ); 3957 if (err) { 3958#if DEBUG_XATTR 3959 plog ("%s: Error writing catalog record\n", __FUNCTION__); 3960#endif 3961 goto out; 3962 } 3963 } 3964 3965getnext: 3966 /* Access the next record */ 3967 err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &catKey, &catRecord, &recordSize, &hint ); 3968 } while (err == noErr); 3969 3970 err = noErr; 3971 3972out: 3973 return err; 3974} 3975 3976/* Function: RepairAttributes 3977 * 3978 * Description: 3979 * This function fixes the extended attributes consistency by 3980 * calling two functions: 3981 * 1. RepairAttributesCheckABT: Traverses attributes Btree and 3982 * checks if each attribute has correct bits set in its corresponding 3983 * catalog record. 3984 * 2. RepairAttributesCheckCBT: Traverses catalog Btree and checks 3985 * if each catalog record that has attribute/security bit set have 3986 * corresponding extended attributes. 3987 * 3988 * Input: 1. GPtr - pointer to global scavenger area 3989 * 3990 * Output: err - Function result 3991 * zero means success 3992 * non-zero means failure 3993 */ 3994static OSErr RepairAttributes(SGlobPtr GPtr) 3995{ 3996 OSErr err = noErr; 3997 Boolean isHFSPlus; 3998 3999 /* Check if the volume is HFS Plus volume */ 4000 isHFSPlus = VolumeObjectIsHFSPlus(); 4001 if (!isHFSPlus) { 4002 goto out; 4003 } 4004 4005 /* Traverse Attributes BTree and access required records in Catalog BTree */ 4006 err = RepairAttributesCheckABT(GPtr, isHFSPlus); 4007 if (err) { 4008 goto out; 4009 } 4010 4011 /* Traverse Catalog BTree and access required records in Attributes BTree */ 4012 err = RepairAttributesCheckCBT(GPtr, isHFSPlus); 4013 if (err) { 4014 goto out; 4015 } 4016 4017out: 4018 return err; 4019} 4020 4021/*------------------------------------------------------------------------------ 4022 4023Function: cmpLongs 4024 4025Function: compares two longs. 4026 4027Input: *a: pointer to first number 4028 *b: pointer to second number 4029 4030Output: <0 if *a < *b 4031 0 if a == b 4032 >0 if a > b 4033------------------------------------------------------------------------------*/ 4034 4035int cmpLongs ( const void *a, const void *b ) 4036{ 4037 return( *(long*)a - *(long*)b ); 4038} 4039 4040/* Function: FixOverlappingExtents 4041 * 4042 * Description: Fix overlapping extents problem. The implementation copies all 4043 * the extents existing in overlapping extents to a new location and updates the 4044 * extent record to point to the new extent. At the end of repair, symlinks are 4045 * created with name "fileID filename" to point to the file involved in 4046 * overlapping extents problem. Note that currently only HFS Plus volumes are 4047 * repaired. 4048 * 4049 * PARTIAL SUCCESS: This function handles partial success in the following 4050 * two ways: 4051 * a. The function pre-allocates space for the all the extents. If the 4052 * allocation fails, it proceeds to allocate space for other extents 4053 * instead of returning error. 4054 * b. If moving an extent succeeds and symlink creation fails, the function 4055 * proceeds to another extent. 4056 * If the repair encounters either a or b condition, appropriate message is 4057 * printed at the end of the function. 4058 * If even a single repair operation succeeds (moving of extent), the function 4059 * returns zero. 4060 * 4061 * Current limitations: 4062 * 1. A regular file instead of symlink is created under following conditions: 4063 * a. The volume is plain HFS volume. HFS does not support symlink 4064 * creation. 4065 * b. The path the new symlink points to is greater than PATH_MAX bytes. 4066 * c. The path the new symlink points has some intermediate component 4067 * greater than NAME_MAX bytes. 4068 * 2. Contiguous disk space for every new extent is expected. The extent is 4069 * not broken into multiple extents if contiguous space is not available on the 4070 * disk. 4071 * 3. The current fix for overlapping extent only works for HFS Plus volumes. 4072 * Plain HFS volumes have problem in accessing the catalog record by fileID. 4073 * 4. Plain HFS volumes might have encoding issues with the newly created 4074 * symlink and its data. 4075 * 4076 * Input: 4077 * GPtr - global scavenger pointer 4078 * 4079 * Output: 4080 * returns zero on success/partial success (moving of one extent succeeds), 4081 * non-zero on failure. 4082 */ 4083static OSErr FixOverlappingExtents(SGlobPtr GPtr) 4084{ 4085 OSErr err = noErr; 4086 Boolean isHFSPlus; 4087 unsigned int i; 4088 unsigned int numOverlapExtents = 0; 4089 ExtentInfo *extentInfo; 4090 ExtentsTable **extentsTableH = GPtr->overlappedExtents; 4091 4092 unsigned int status = 0; 4093#define S_DISKFULL 0x01 /* error due to disk full */ 4094#define S_MOVEEXTENT 0x02 /* moving extent succeeded */ 4095 4096 isHFSPlus = VolumeObjectIsHFSPlus(); 4097 if (isHFSPlus == false) { 4098 /* Do not repair plain HFS volumes */ 4099 err = R_RFail; 4100 goto out; 4101 } 4102 4103 if (extentsTableH == NULL) { 4104 /* No overlapping extents exist */ 4105 goto out; 4106 } 4107 4108 numOverlapExtents = (**extentsTableH).count; 4109 4110 /* Optimization - sort the overlap extent list based on blockCount to 4111 * start allocating contiguous space for largest number of blocks first 4112 */ 4113 qsort((**extentsTableH).extentInfo, numOverlapExtents, sizeof(ExtentInfo), 4114 CompareExtentBlockCount); 4115 4116#if DEBUG_OVERLAP 4117 /* Print all overlapping extents structure */ 4118 for (i=0; i<numOverlapExtents; i++) { 4119 extentInfo = &((**extentsTableH).extentInfo[i]); 4120 plog ("%d: fileID = %d, startBlock = %d, blockCount = %d\n", i, extentInfo->fileID, extentInfo->startBlock, extentInfo->blockCount); 4121 } 4122#endif 4123 4124 /* Pre-allocate free space for all overlapping extents */ 4125 for (i=0; i<numOverlapExtents; i++) { 4126 extentInfo = &((**extentsTableH).extentInfo[i]); 4127 err = AllocateContigBitmapBits (GPtr->calculatedVCB, extentInfo->blockCount, &(extentInfo->newStartBlock)); 4128 if ((err != noErr)) { 4129 /* Not enough disk space */ 4130 status |= S_DISKFULL; 4131#if DEBUG_OVERLAP 4132 plog ("%s: Not enough disk space to allocate extent for fileID = %d (start=%d, count=%d)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->blockCount); 4133#endif 4134 } 4135 } 4136 4137 /* For every extent info, copy the extent into new location and create symlink */ 4138 for (i=0; i<numOverlapExtents; i++) { 4139 extentInfo = &((**extentsTableH).extentInfo[i]); 4140 4141 /* Do not repair this extent as no new extent was allocated */ 4142 if (extentInfo->newStartBlock == 0) { 4143 continue; 4144 } 4145 4146 /* Move extent data to new location */ 4147 err = MoveExtent(GPtr, extentInfo); 4148 if (err != noErr) { 4149 extentInfo->didRepair = false; 4150#if DEBUG_OVERLAP 4151 plog ("%s: Extent move failed for extent for fileID = %u (old=%u, new=%u, count=%u) (err=%d)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount, err); 4152#endif 4153 } else { 4154 /* Mark the overlapping extent as repaired */ 4155 extentInfo->didRepair = true; 4156 status |= S_MOVEEXTENT; 4157#if DEBUG_OVERLAP 4158 plog ("%s: Extent move success for extent for fileID = %u (old=%u, new=%u, count=%u)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount); 4159#endif 4160 } 4161 4162 /* Create symlink for the corrupt file */ 4163 err = CreateCorruptFileSymlink(GPtr, extentInfo->fileID); 4164 if (err != noErr) { 4165#if DEBUG_OVERLAP 4166 plog ("%s: Error in creating symlink for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err); 4167#endif 4168 } else { 4169#if DEBUG_OVERLAP 4170 plog ("%s: Created symlink for fileID = %u (old=%u, new=%u, count=%u)\n", __FUNCTION__, extentInfo->fileID, extentInfo->startBlock, extentInfo->newStartBlock, extentInfo->blockCount); 4171#endif 4172 } 4173 } 4174 4175out: 4176 /* Release all blocks used by overlap extents that are repaired */ 4177 for (i=0; i<numOverlapExtents; i++) { 4178 extentInfo = &((**extentsTableH).extentInfo[i]); 4179 if (extentInfo->didRepair == true) { 4180 ReleaseBitmapBits (extentInfo->startBlock, extentInfo->blockCount); 4181 } 4182 } 4183 4184 /* For all un-repaired extents, 4185 * 1. Release all blocks allocated for new extent. 4186 * 2. Mark all blocks used for the old extent (since the overlapping region 4187 * have been marked free in the for loop above. 4188 */ 4189 for (i=0; i<numOverlapExtents; i++) { 4190 extentInfo = &((**extentsTableH).extentInfo[i]); 4191 if (extentInfo->didRepair == false) { 4192 CaptureBitmapBits (extentInfo->startBlock, extentInfo->blockCount); 4193 4194 if (extentInfo->newStartBlock != 0) { 4195 ReleaseBitmapBits (extentInfo->newStartBlock, extentInfo->blockCount); 4196 } 4197 } 4198 } 4199 4200 /* Update the volume free block count since the release and alloc above might 4201 * have worked on same bit multiple times. 4202 */ 4203 UpdateFreeBlockCount (GPtr); 4204 4205 /* Print correct status messages */ 4206 if (status & S_DISKFULL) { 4207 fsckPrint(GPtr->context, E_DiskFull); 4208 } 4209 4210 /* If moving of even one extent succeeds, return success */ 4211 if (status & S_MOVEEXTENT) { 4212 err = noErr; 4213 } 4214 4215 return err; 4216} /* FixOverlappingExtents */ 4217 4218/* Function: CompareExtentBlockCount 4219 * 4220 * Description: Compares the blockCount from two ExtentInfo and return the 4221 * comparison result. (since we have to arrange in descending order) 4222 * 4223 * Input: 4224 * first and second - void pointers to ExtentInfo structure. 4225 * 4226 * Output: 4227 * <0 if first > second 4228 * =0 if first == second 4229 * >0 if first < second 4230 */ 4231static int CompareExtentBlockCount(const void *first, const void *second) 4232{ 4233 return (((ExtentInfo *)second)->blockCount - 4234 ((ExtentInfo *)first)->blockCount); 4235} /* CompareExtentBlockCount */ 4236 4237/* Function: MoveExtent 4238 * 4239 * Description: Move data from old extent to new extent and update corresponding 4240 * records. 4241 * 1. Search the extent record for the overlapping extent. 4242 * If the fileID < kHFSFirstUserCatalogNodeID, 4243 * Ignore repair for BadBlock, RepairCatalog, BogusExtent files. 4244 * Search for extent record in volume header. 4245 * Else, 4246 * Search for extent record in catalog BTree. If the extent list does 4247 * not end in catalog record and extent record not found in catalog 4248 * record, search in extents BTree. 4249 * 2. If found, copy disk blocks from old extent to new extent. 4250 * 3. If it succeeds, update extent record with new start block and write back 4251 * to disk. 4252 * This function does not take care to deallocate blocks from old start block. 4253 * 4254 * Input: 4255 * GPtr - Global Scavenger structure pointer 4256 * extentInfo - Current overlapping extent details. 4257 * 4258 * Output: 4259 * err: zero on success, non-zero on failure 4260 * paramErr - Invalid paramter, ex. file ID is less than 4261 * kHFSFirstUserCatalogNodeID. 4262 */ 4263static OSErr MoveExtent(SGlobPtr GPtr, ExtentInfo *extentInfo) 4264{ 4265 OSErr err = noErr; 4266 Boolean isHFSPlus; 4267 4268 CatalogRecord catRecord; 4269 CatalogKey catKey; 4270 HFSPlusExtentKey extentKey; 4271 HFSPlusExtentRecord extentData; 4272 HFSPlusAttrKey attrKey; 4273 HFSPlusAttrRecord attrRecord; 4274 UInt16 recordSize; 4275 4276 enum locationTypes {volumeHeader, catalogBTree, extentsBTree, attributeBTree} foundLocation; 4277 4278 UInt32 foundExtentIndex = 0; 4279 Boolean noMoreExtents = true; 4280 4281 isHFSPlus = VolumeObjectIsHFSPlus(); 4282 4283 /* Find correct location of this overlapping extent */ 4284 if (extentInfo->forkType == kEAData) { 4285 assert(isHFSPlus == true); 4286 4287 /* Search extent in attribute btree */ 4288 err = SearchExtentInAttributeBT (GPtr, extentInfo, &attrKey, &attrRecord, 4289 &recordSize, &foundExtentIndex); 4290 if (err != noErr) { 4291 goto out; 4292 } 4293 foundLocation = attributeBTree; 4294 } else { /* kDataFork or kRsrcFork */ 4295 if (extentInfo->fileID < kHFSFirstUserCatalogNodeID) { 4296 /* Ignore these fileIDs in repair. Bad block file blocks should 4297 * never be moved. kHFSRepairCatalogFileID and kHFSBogusExtentFileID 4298 * are temporary runtime files. We need to return error to the caller 4299 * to deallocate disk blocks preallocated during preflight 4300 * to move the overlapping extents. Any other extent that overlaps 4301 * with these extents might have moved successfully, thus repairing 4302 * the problem. 4303 */ 4304 if ((extentInfo->fileID == kHFSBadBlockFileID) || 4305 (extentInfo->fileID == kHFSBogusExtentFileID) || 4306 (extentInfo->fileID == kHFSRepairCatalogFileID)) { 4307 err = paramErr; 4308 goto out; 4309 } 4310 4311 /* Search for extent record in the volume header */ 4312 err = SearchExtentInVH (GPtr, extentInfo, &foundExtentIndex, &noMoreExtents); 4313 foundLocation = volumeHeader; 4314 } else { 4315 /* Search the extent record from the catalog btree */ 4316 err = SearchExtentInCatalogBT (GPtr, extentInfo, &catKey, &catRecord, 4317 &recordSize, &foundExtentIndex, &noMoreExtents); 4318 foundLocation = catalogBTree; 4319 } 4320 if (err != noErr) { 4321 if (noMoreExtents == false) { 4322 /* search extent in extents overflow btree */ 4323 err = SearchExtentInExtentBT (GPtr, extentInfo, &extentKey, 4324 &extentData, &recordSize, &foundExtentIndex); 4325 foundLocation = extentsBTree; 4326 if (err != noErr) { 4327 DPRINTF (d_error|d_overlap, "%s: No matching extent record found in extents btree for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err); 4328 goto out; 4329 } 4330 } else { 4331 /* No more extents exist for this file */ 4332 DPRINTF (d_error|d_overlap, "%s: No matching extent record found for fileID = %d\n", __FUNCTION__, extentInfo->fileID); 4333 goto out; 4334 } 4335 } 4336 } 4337 /* Copy disk blocks from old extent to new extent */ 4338 err = CopyDiskBlocks(GPtr, extentInfo->startBlock, extentInfo->blockCount, 4339 extentInfo->newStartBlock); 4340 if (err != noErr) { 4341 DPRINTF (d_error|d_overlap, "%s: Error in copying disk blocks for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err); 4342 goto out; 4343 } 4344 4345 /* Replace the old start block in extent record with new start block */ 4346 if (foundLocation == catalogBTree) { 4347 err = UpdateExtentInCatalogBT(GPtr, extentInfo, &catKey, &catRecord, 4348 &recordSize, foundExtentIndex); 4349 } else if (foundLocation == volumeHeader) { 4350 err = UpdateExtentInVH(GPtr, extentInfo, foundExtentIndex); 4351 } else if (foundLocation == extentsBTree) { 4352 extentData[foundExtentIndex].startBlock = extentInfo->newStartBlock; 4353 err = UpdateExtentRecord(GPtr->calculatedVCB, NULL, &extentKey, extentData, kNoHint); 4354 } else if (foundLocation == attributeBTree) { 4355 err = UpdateExtentInAttributeBT(GPtr, extentInfo, &attrKey, &attrRecord, 4356 &recordSize, foundExtentIndex); 4357 4358 } 4359 if (err != noErr) { 4360 DPRINTF (d_error|d_overlap, "%s: Error in updating extent record for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err); 4361 goto out; 4362 } 4363 4364out: 4365 return err; 4366} /* MoveExtent */ 4367 4368/* Function: CreateCorruptFileSymlink 4369 * 4370 * Description: Create symlink to point to the corrupt files that might 4371 * have data loss due to repair (overlapping extents, bad extents) 4372 * 4373 * The function looks up directory with name "DamagedFiles" in the 4374 * root of the file system being repaired. If it does not exists, it 4375 * creates the directory. The symlink to damaged file is created in this 4376 * directory. 4377 * 4378 * If fileID >= kHFSFirstUserCatalogNodeID, 4379 * Lookup the filename and path to the file based on file ID. Create the 4380 * new file name as "fileID filename" and data as the relative path of the file 4381 * from the root of the volume. 4382 * If either 4383 * the volume is plain HFS, or 4384 * the length of the path pointed by data is greater than PATH_MAX, or 4385 * the length of any intermediate path component is greater than NAME_MAX, 4386 * Create a plain file with given data. 4387 * Else 4388 * Create a symlink. 4389 * Else 4390 * Find the name of file based on ID (ie. Catalog BTree, etc), and create plain 4391 * regular file with name "fileID filename" and data as "System File: 4392 * filename". 4393 * 4394 * Input: 4395 * 1. GPtr - global scavenger structure pointer. 4396 * 2. fileID - fileID of the source for creating symlink 4397 * 4398 * Output: 4399 * returns zero on success, non-zero on failure. 4400 * memFullErr - Not enough memory 4401 */ 4402static OSErr CreateCorruptFileSymlink(SGlobPtr GPtr, UInt32 fileID) 4403{ 4404 OSErr err = noErr; 4405 Boolean isHFSPlus; 4406 char *filename = NULL; 4407 unsigned int filenamelen; 4408 char *data = NULL; 4409 unsigned int datalen; 4410 unsigned int filenameoffset; 4411 unsigned int dataoffset; 4412 UInt32 damagedDirID; 4413 UInt16 status; 4414 UInt16 fileType; 4415 4416 isHFSPlus = VolumeObjectIsHFSPlus(); 4417 4418 /* Lookup and create, if required, the DamagedFiles folder */ 4419 damagedDirID = CreateDirByName(GPtr, (u_char *)"DamagedFiles", kHFSRootFolderID); 4420 if (damagedDirID == 0) { 4421 goto out; 4422 } 4423 4424 /* Allocate (kHFSPlusMaxFileNameChars * 4) for unicode - utf8 conversion */ 4425 filenamelen = kHFSPlusMaxFileNameChars * 4; 4426 filename = malloc(filenamelen); 4427 if (!filename) { 4428 err = memFullErr; 4429 goto out; 4430 } 4431 4432 /* Allocate (PATH_MAX * 4) instead of PATH_MAX for unicode - utf8 conversion */ 4433 datalen = PATH_MAX * 4; 4434 data = malloc(datalen); 4435 if (!data) { 4436 err = memFullErr; 4437 goto out; 4438 } 4439 4440 /* Find filename, path for fileID >= 16 and determine new fileType */ 4441 if (fileID >= kHFSFirstUserCatalogNodeID) { 4442 char *name; 4443 char *path; 4444 4445 /* construct symlink data with .. prefix */ 4446 dataoffset = sprintf (data, ".."); 4447 path = data + dataoffset; 4448 datalen -= dataoffset; 4449 4450 /* construct new filename prefix with fileID<space> */ 4451 filenameoffset = sprintf (filename, "%08x ", fileID); 4452 name = filename + filenameoffset; 4453 filenamelen -= filenameoffset; 4454 4455 /* find file name and path (data for symlink) for fileID */ 4456 err = GetFileNamePathByID(GPtr, fileID, path, &datalen, 4457 name, &filenamelen, &status); 4458 if (err != noErr) { 4459#if DEBUG_OVERLAP 4460 plog ("%s: Error in getting name/path for fileID = %d (err=%d)\n", __FUNCTION__, fileID, err); 4461#endif 4462 goto out; 4463 } 4464 4465 /* update length of path and filename */ 4466 filenamelen += filenameoffset; 4467 datalen += dataoffset; 4468 4469 /* If 4470 * (1) the volume is plain HFS volume, or 4471 * (2) any intermediate component in path was more than NAME_MAX bytes, or 4472 * (3) the entire path was greater than PATH_MAX bytes 4473 * then create regular file 4474 * else create symlink. 4475 */ 4476 if (!isHFSPlus || (status & FPATH_BIGNAME) || (datalen > PATH_MAX)) { 4477 /* create file */ 4478 fileType = S_IFREG; 4479 } else { 4480 /* create symlink */ 4481 fileType = S_IFLNK; 4482 } 4483 } else { 4484 /* for fileID < 16, create regular file */ 4485 fileType = S_IFREG; 4486 4487 /* construct the name of the file */ 4488 filenameoffset = sprintf (filename, "%08x ", fileID); 4489 filenamelen -= filenameoffset; 4490 err = GetSystemFileName (fileID, (filename + filenameoffset), &filenamelen); 4491 filenamelen += filenameoffset; 4492 4493 /* construct the data of the file */ 4494 dataoffset = sprintf (data, "System File: "); 4495 datalen -= dataoffset; 4496 err = GetSystemFileName (fileID, (data + dataoffset), &datalen); 4497 datalen += dataoffset; 4498 } 4499 4500 /* Create new file */ 4501 err = CreateFileByName (GPtr, damagedDirID, fileType, (u_char *)filename, 4502 filenamelen, (u_char *)data, datalen); 4503 /* Mask error if file already exists */ 4504 if (err == EEXIST) { 4505 err = noErr; 4506 } 4507 if (err != noErr) { 4508#if DEBUG_OVERLAP 4509 plog ("%s: Error in creating fileType = %d for fileID = %d (err=%d)\n", __FUNCTION__, fileType, fileID, err); 4510#endif 4511 goto out; 4512 } 4513 4514out: 4515 if (err) { 4516 if ((GPtr->PrintStat & S_SymlinkCreate) == 0) { 4517 fsckPrint(GPtr->context, E_SymlinkCreate); 4518 GPtr->PrintStat|= S_SymlinkCreate; 4519 } 4520 } else { 4521 if ((GPtr->PrintStat & S_DamagedDir) == 0) { 4522 fsckPrint(GPtr->context, fsckCorruptFilesDirectory, "DamagedFiles"); 4523 GPtr->PrintStat|= S_DamagedDir; 4524 } 4525 } 4526 4527 if (data) { 4528 free (data); 4529 } 4530 if (filename) { 4531 free (filename); 4532 } 4533 4534 return err; 4535} /* CreateCorruptFileSymlink */ 4536 4537/* Function: SearchExtentInAttributeBT 4538 * 4539 * Description: Search extent with given information (fileID, attribute name, 4540 * startBlock, blockCount) in the attribute BTree. 4541 * 4542 * Input: 4543 * 1. GPtr - global scavenger structure pointer. 4544 * 2. extentInfo - Information about extent to be searched. 4545 * 4546 * Output: 4547 * Returns zero on success, fnfErr on failure. 4548 * 1. *attrKey - Attribute key for given fileID and attribute name, if found. 4549 * 2. *attrRecord - Attribute record for given fileID and attribute name, if found. 4550 * 3. *recordSize - Size of the record being returned. 4551 * 4. *foundExtentIndex - Index in extent record which matches the input data. 4552 */ 4553static OSErr SearchExtentInAttributeBT(SGlobPtr GPtr, ExtentInfo *extentInfo, 4554 HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord, 4555 UInt16 *recordSize, UInt32 *foundExtentIndex) 4556{ 4557 OSErr result = fnfErr; 4558 BTreeIterator iterator; 4559 FSBufferDescriptor btRecord; 4560 HFSPlusAttrKey *key; 4561 Boolean noMoreExtents; 4562 unsigned char *attrname = NULL; 4563 size_t attrnamelen; 4564 4565 assert((extentInfo->attrname != NULL)); 4566 4567 attrname = malloc (XATTR_MAXNAMELEN + 1); 4568 if (!attrname) { 4569 result = memFullErr; 4570 goto out; 4571 } 4572 4573 /* Initialize the iterator, attribute record buffer, and attribute key */ 4574 ClearMemory(&iterator, sizeof(BTreeIterator)); 4575 key = (HFSPlusAttrKey *)&iterator.key; 4576 attrnamelen = strlen(extentInfo->attrname); 4577 BuildAttributeKey(extentInfo->fileID, 0, (unsigned char *)extentInfo->attrname, attrnamelen, key); 4578 4579 btRecord.bufferAddress = attrRecord; 4580 btRecord.itemCount = 1; 4581 btRecord.itemSize = sizeof(HFSPlusAttrRecord); 4582 4583 /* Search for the attribute record 4584 * Warning: Attribute record of type kHFSPlusAttrInlineData may be 4585 * truncated on read! (4425232). This function only uses recordType 4586 * field from inline attribute record. 4587 */ 4588 result = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator, 4589 kInvalidMRUCacheKey, &btRecord, recordSize, &iterator); 4590 if (result) { 4591 DPRINTF (d_error|d_overlap, "%s: Error finding attribute record (err=%d) for fileID = %d, attrname = %d\n", __FUNCTION__, result, extentInfo->fileID, extentInfo->attrname); 4592 goto out; 4593 } 4594 4595 /* Search the attribute record for overlapping extent. If found, return 4596 * success. If not, iterate to the next record. If it is a valid 4597 * attribute extent record belonging to the same attribute, search 4598 * for the desired extent. 4599 */ 4600 while (1) { 4601 if (attrRecord->recordType == kHFSPlusAttrForkData) { 4602 result = FindExtentInExtentRec(true, extentInfo->startBlock, 4603 extentInfo->blockCount, attrRecord->forkData.theFork.extents, 4604 foundExtentIndex, &noMoreExtents); 4605 if ((result == noErr) || (noMoreExtents == true)) { 4606 goto out; 4607 } 4608 } else if (attrRecord->recordType == kHFSPlusAttrExtents) { 4609 result = FindExtentInExtentRec(true, extentInfo->startBlock, 4610 extentInfo->blockCount, attrRecord->overflowExtents.extents, 4611 foundExtentIndex, &noMoreExtents); 4612 if ((result == noErr) || (noMoreExtents == true)) { 4613 goto out; 4614 } 4615 } else { 4616 /* Invalid attribute record. This function should not find any 4617 * attribute record except forkData and AttrExtents. 4618 */ 4619 result = fnfErr; 4620 goto out; 4621 } 4622 4623 /* Iterate to the next record 4624 * Warning: Attribute record of type kHFSPlusAttrInlineData may be 4625 * truncated on read! (4425232). This function only uses recordType 4626 * field from inline attribute record. 4627 */ 4628 result = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord, 4629 &iterator, &btRecord, recordSize); 4630 if (result) { 4631 goto out; 4632 } 4633 4634 (void) utf_encodestr(key->attrName, key->attrNameLen * 2, attrname, &attrnamelen, XATTR_MAXNAMELEN + 1); 4635 attrname[attrnamelen] = '\0'; 4636 4637 /* Check if the attribute record belongs to the same attribute */ 4638 if ((key->fileID != extentInfo->fileID) || 4639 (strcmp((char *)attrname, extentInfo->attrname))) { 4640 /* The attribute record belongs to another attribute */ 4641 result = fnfErr; 4642 goto out; 4643 } 4644 } 4645 4646out: 4647 /* Copy the correct key to the caller */ 4648 if (result == noErr) { 4649 CopyMemory(key, attrKey, sizeof(HFSPlusAttrKey)); 4650 } 4651 4652 if (attrname != NULL) { 4653 free (attrname); 4654 } 4655 4656 return (result); 4657} 4658 4659/* Function: UpdateExtentInAttributeBT 4660 * 4661 * Description: Update extent record with given information (fileID, startBlock, 4662 * blockCount) in attribute BTree. 4663 * 4664 * Input: 4665 * 1. GPtr - global scavenger structure pointer. 4666 * 2. extentInfo - Information about extent to be searched. 4667 * 3. *attrKey - Attribute key for record to update. 4668 * 4. *attrRecord - Attribute record to update. 4669 * 5. *recordSize - Size of the record. 4670 * 6. foundExtentIndex - Index in extent record to update. 4671 * 4672 * Output: 4673 * Returns zero on success, non-zero on failure. 4674 */ 4675static OSErr UpdateExtentInAttributeBT (SGlobPtr GPtr, ExtentInfo *extentInfo, 4676 HFSPlusAttrKey *attrKey, HFSPlusAttrRecord *attrRecord, 4677 UInt16 *recordSize, UInt32 foundInExtentIndex) 4678{ 4679 OSErr err; 4680 UInt32 foundHint; 4681 4682 assert ((attrRecord->recordType == kHFSPlusAttrForkData) || 4683 (attrRecord->recordType == kHFSPlusAttrExtents)); 4684 4685 /* Update the new start block count in the extent */ 4686 if (attrRecord->recordType == kHFSPlusAttrForkData) { 4687 attrRecord->forkData.theFork.extents[foundInExtentIndex].startBlock = 4688 extentInfo->newStartBlock; 4689 } else if (attrRecord->recordType == kHFSPlusAttrExtents) { 4690 attrRecord->overflowExtents.extents[foundInExtentIndex].startBlock = 4691 extentInfo->newStartBlock; 4692 } 4693 4694 /* Replace the attribute record. 4695 * Warning: Attribute record of type kHFSPlusAttrInlineData may be 4696 * truncated on read! (4425232). 4697 */ 4698 err = ReplaceBTreeRecord (GPtr->calculatedAttributesFCB, attrKey, kNoHint, 4699 attrRecord, *recordSize, &foundHint); 4700 4701 return (err); 4702} 4703 4704/* Function: SearchExtentInVH 4705 * 4706 * Description: Search extent with given information (fileID, startBlock, 4707 * blockCount) in volume header. 4708 * 4709 * Input: 4710 * 1. GPtr - global scavenger structure pointer. 4711 * 2. extentInfo - Information about extent to be searched. 4712 * 4713 * Output: 4714 * Returns zero on success, fnfErr on failure. 4715 * 1. *foundExtentIndex - Index in extent record which matches the input data. 4716 * 2. *noMoreExtents - Indicates that no more extents will exist for this 4717 * fileID in extents BTree. 4718 */ 4719static OSErr SearchExtentInVH(SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 *foundExtentIndex, Boolean *noMoreExtents) 4720{ 4721 OSErr err = fnfErr; 4722 Boolean isHFSPlus; 4723 SFCB *fcb = NULL; 4724 4725 isHFSPlus = VolumeObjectIsHFSPlus(); 4726 *noMoreExtents = true; 4727 4728 /* Find correct FCB structure */ 4729 switch (extentInfo->fileID) { 4730 case kHFSExtentsFileID: 4731 fcb = GPtr->calculatedVCB->vcbExtentsFile; 4732 break; 4733 4734 case kHFSCatalogFileID: 4735 fcb = GPtr->calculatedVCB->vcbCatalogFile; 4736 break; 4737 4738 case kHFSAllocationFileID: 4739 fcb = GPtr->calculatedVCB->vcbAllocationFile; 4740 break; 4741 4742 case kHFSStartupFileID: 4743 fcb = GPtr->calculatedVCB->vcbStartupFile; 4744 break; 4745 4746 case kHFSAttributesFileID: 4747 fcb = GPtr->calculatedVCB->vcbAttributesFile; 4748 break; 4749 }; 4750 4751 /* If extent found, find correct extent index */ 4752 if (fcb != NULL) { 4753 if (isHFSPlus) { 4754 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock, 4755 extentInfo->blockCount, fcb->fcbExtents32, 4756 foundExtentIndex, noMoreExtents); 4757 } else { 4758 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock, 4759 extentInfo->blockCount, 4760 (*(HFSPlusExtentRecord *)fcb->fcbExtents16), 4761 foundExtentIndex, noMoreExtents); 4762 } 4763 } 4764 return err; 4765} /* SearchExtentInVH */ 4766 4767/* Function: UpdateExtentInVH 4768 * 4769 * Description: Update the extent record for given fileID and index in the 4770 * volume header with new start block. 4771 * 4772 * Input: 4773 * 1. GPtr - global scavenger structure pointer. 4774 * 2. extentInfo - Information about extent to be searched. 4775 * 3. foundExtentIndex - Index in extent record to update. 4776 * 4777 * Output: 4778 * Returns zero on success, fnfErr on failure. This function will fail an 4779 * incorrect fileID is passed. 4780 */ 4781static OSErr UpdateExtentInVH (SGlobPtr GPtr, ExtentInfo *extentInfo, UInt32 foundExtentIndex) 4782{ 4783 OSErr err = fnfErr; 4784 Boolean isHFSPlus; 4785 SFCB *fcb = NULL; 4786 4787 isHFSPlus = VolumeObjectIsHFSPlus(); 4788 4789 /* Find correct FCB structure */ 4790 switch (extentInfo->fileID) { 4791 case kHFSExtentsFileID: 4792 fcb = GPtr->calculatedVCB->vcbExtentsFile; 4793 break; 4794 4795 case kHFSCatalogFileID: 4796 fcb = GPtr->calculatedVCB->vcbCatalogFile; 4797 break; 4798 4799 case kHFSAllocationFileID: 4800 fcb = GPtr->calculatedVCB->vcbAllocationFile; 4801 break; 4802 4803 case kHFSStartupFileID: 4804 fcb = GPtr->calculatedVCB->vcbStartupFile; 4805 break; 4806 4807 case kHFSAttributesFileID: 4808 fcb = GPtr->calculatedVCB->vcbAttributesFile; 4809 break; 4810 }; 4811 4812 /* If extent found, find correct extent index */ 4813 if (fcb != NULL) { 4814 if (isHFSPlus) { 4815 fcb->fcbExtents32[foundExtentIndex].startBlock = extentInfo->newStartBlock; 4816 } else { 4817 fcb->fcbExtents16[foundExtentIndex].startBlock = extentInfo->newStartBlock; 4818 } 4819 MarkVCBDirty(GPtr->calculatedVCB); 4820 err = noErr; 4821 } 4822 return err; 4823} /* UpdateExtentInVH */ 4824 4825/* Function: SearchExtentInCatalogBT 4826 * 4827 * Description: Search extent with given information (fileID, startBlock, 4828 * blockCount) in catalog BTree. 4829 * 4830 * Input: 4831 * 1. GPtr - global scavenger structure pointer. 4832 * 2. extentInfo - Information about extent to be searched. 4833 * 4834 * Output: 4835 * Returns zero on success, non-zero on failure. 4836 * 1. *catKey - Catalog key for given fileID, if found. 4837 * 2. *catRecord - Catalog record for given fileID, if found. 4838 * 3. *recordSize - Size of the record being returned. 4839 * 4. *foundExtentIndex - Index in extent record which matches the input data. 4840 * 5. *noMoreExtents - Indicates that no more extents will exist for this 4841 * fileID in extents BTree. 4842 */ 4843static OSErr SearchExtentInCatalogBT(SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 *foundExtentIndex, Boolean *noMoreExtents) 4844{ 4845 OSErr err; 4846 Boolean isHFSPlus; 4847 4848 isHFSPlus = VolumeObjectIsHFSPlus(); 4849 4850 /* Search catalog btree for this file ID */ 4851 err = GetCatalogRecord(GPtr, extentInfo->fileID, isHFSPlus, catKey, catRecord, 4852 recordSize); 4853 if (err != noErr) { 4854#if DEBUG_OVERLAP 4855 plog ("%s: No matching catalog record found for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err); 4856#endif 4857 goto out; 4858 } 4859 4860 if (isHFSPlus) { 4861 /* HFS Plus */ 4862 if (extentInfo->forkType == kDataFork) { 4863 /* data fork */ 4864 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock, 4865 extentInfo->blockCount, 4866 catRecord->hfsPlusFile.dataFork.extents, 4867 foundExtentIndex, noMoreExtents); 4868 } else { 4869 /* resource fork */ 4870 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock, 4871 extentInfo->blockCount, 4872 catRecord->hfsPlusFile.resourceFork.extents, 4873 foundExtentIndex, noMoreExtents); 4874 } 4875 } else { 4876 /* HFS */ 4877 if (extentInfo->forkType == kDataFork) { 4878 /* data fork */ 4879 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock, 4880 extentInfo->blockCount, 4881 (*(HFSPlusExtentRecord *)catRecord->hfsFile.dataExtents), 4882 foundExtentIndex, noMoreExtents); 4883 } else { 4884 /* resource fork */ 4885 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock, 4886 extentInfo->blockCount, 4887 (*(HFSPlusExtentRecord *)catRecord->hfsFile.rsrcExtents), 4888 foundExtentIndex, noMoreExtents); 4889 } 4890 } 4891 4892out: 4893 return err; 4894} /* SearchExtentInCatalogBT */ 4895 4896/* Function: UpdateExtentInCatalogBT 4897 * 4898 * Description: Update extent record with given information (fileID, startBlock, 4899 * blockCount) in catalog BTree. 4900 * 4901 * Input: 4902 * 1. GPtr - global scavenger structure pointer. 4903 * 2. extentInfo - Information about extent to be searched. 4904 * 3. *catKey - Catalog key for record to update. 4905 * 4. *catRecord - Catalog record to update. 4906 * 5. *recordSize - Size of the record. 4907 * 6. foundExtentIndex - Index in extent record to update. 4908 * 4909 * Output: 4910 * Returns zero on success, non-zero on failure. 4911 */ 4912static OSErr UpdateExtentInCatalogBT (SGlobPtr GPtr, ExtentInfo *extentInfo, CatalogKey *catKey, CatalogRecord *catRecord, UInt16 *recordSize, UInt32 foundInExtentIndex) 4913{ 4914 OSErr err; 4915 Boolean isHFSPlus; 4916 UInt32 foundHint; 4917 4918 isHFSPlus = VolumeObjectIsHFSPlus(); 4919 4920 /* Modify the catalog record */ 4921 if (isHFSPlus) { 4922 if (extentInfo->forkType == kDataFork) { 4923 catRecord->hfsPlusFile.dataFork.extents[foundInExtentIndex].startBlock = extentInfo->newStartBlock; 4924 } else { 4925 catRecord->hfsPlusFile.resourceFork.extents[foundInExtentIndex].startBlock = extentInfo->newStartBlock; 4926 } 4927 } else { 4928 if (extentInfo->forkType == kDataFork) { 4929 catRecord->hfsFile.dataExtents[foundInExtentIndex].startBlock = extentInfo->newStartBlock; 4930 } else { 4931 catRecord->hfsFile.rsrcExtents[foundInExtentIndex].startBlock = extentInfo->newStartBlock; 4932 } 4933 } 4934 4935 /* Replace the catalog record */ 4936 err = ReplaceBTreeRecord (GPtr->calculatedCatalogFCB, catKey, kNoHint, 4937 catRecord, *recordSize, &foundHint); 4938 if (err != noErr) { 4939#if DEBUG_OVERLAP 4940 plog ("%s: Error in replacing catalog record for fileID = %d (err=%d)\n", __FUNCTION__, extentInfo->fileID, err); 4941#endif 4942 } 4943 return err; 4944} /* UpdateExtentInCatalogBT */ 4945 4946/* Function: SearchExtentInExtentBT 4947 * 4948 * Description: Search extent with given information (fileID, startBlock, 4949 * blockCount) in Extent BTree. 4950 * 4951 * Input: 4952 * 1. GPtr - global scavenger structure pointer. 4953 * 2. extentInfo - Information about extent to be searched. 4954 * 4955 * Output: 4956 * Returns zero on success, non-zero on failure. 4957 * fnfErr - desired extent record was not found. 4958 * 1. *extentKey - Extent key, if found. 4959 * 2. *extentRecord - Extent record, if found. 4960 * 3. *recordSize - Size of the record being returned. 4961 * 4. *foundExtentIndex - Index in extent record which matches the input data. 4962 */ 4963static OSErr SearchExtentInExtentBT(SGlobPtr GPtr, ExtentInfo *extentInfo, HFSPlusExtentKey *extentKey, HFSPlusExtentRecord *extentRecord, UInt16 *recordSize, UInt32 *foundExtentIndex) 4964{ 4965 OSErr err = noErr; 4966 Boolean isHFSPlus; 4967 Boolean noMoreExtents = true; 4968 UInt32 hint; 4969 4970 isHFSPlus = VolumeObjectIsHFSPlus(); 4971 4972 /* set up extent key */ 4973 BuildExtentKey (isHFSPlus, extentInfo->forkType, extentInfo->fileID, 0, extentKey); 4974 err = SearchBTreeRecord (GPtr->calculatedExtentsFCB, extentKey, kNoHint, 4975 extentKey, extentRecord, recordSize, &hint); 4976 if ((err != noErr) && (err != btNotFound)) { 4977#if DEBUG_OVERLAP 4978 plog ("%s: Error on searching first record for fileID = %d in Extents Btree (err=%d)\n", __FUNCTION__, extentInfo->fileID, err); 4979#endif 4980 goto out; 4981 } 4982 4983 if (err == btNotFound) 4984 { 4985 /* Position to the first record for the given fileID */ 4986 err = GetBTreeRecord (GPtr->calculatedExtentsFCB, 1, extentKey, 4987 extentRecord, recordSize, &hint); 4988 } 4989 4990 while (err == noErr) 4991 { 4992 /* Break out if we see different fileID, forkType in the BTree */ 4993 if (isHFSPlus) { 4994 if ((extentKey->fileID != extentInfo->fileID) || 4995 (extentKey->forkType != extentInfo->forkType)) { 4996 err = fnfErr; 4997 break; 4998 } 4999 } else { 5000 if ((((HFSExtentKey *)extentKey)->fileID != extentInfo->fileID) || 5001 (((HFSExtentKey *)extentKey)->forkType != extentInfo->forkType)) { 5002 err = fnfErr; 5003 break; 5004 } 5005 } 5006 5007 /* Check the extents record for corresponding startBlock, blockCount */ 5008 err = FindExtentInExtentRec(isHFSPlus, extentInfo->startBlock, 5009 extentInfo->blockCount, *extentRecord, 5010 foundExtentIndex, &noMoreExtents); 5011 if (err == noErr) { 5012 break; 5013 } 5014 if (noMoreExtents == true) { 5015 err = fnfErr; 5016 break; 5017 } 5018 5019 /* Try next record for this fileID and forkType */ 5020 err = GetBTreeRecord (GPtr->calculatedExtentsFCB, 1, extentKey, 5021 extentRecord, recordSize, &hint); 5022 } 5023 5024out: 5025 return err; 5026} /* SearchExtentInExtentBT */ 5027 5028/* Function: FindExtentInExtentRec 5029 * 5030 * Description: Traverse the given extent record (size based on if the volume is 5031 * HFS or HFSPlus volume) and find the index location if the given startBlock 5032 * and blockCount match. 5033 * 5034 * Input: 5035 * 1. isHFSPlus - If the volume is plain HFS or HFS Plus. 5036 * 2. startBlock - startBlock to be searched in extent record. 5037 * 3. blockCount - blockCount to be searched in extent record. 5038 * 4. extentData - Extent Record to be searched. 5039 * 5040 * Output: 5041 * Returns zero if the match is found, else fnfErr on failure. 5042 * 1. *foundExtentIndex - Index in extent record which matches the input data. 5043 * 2. *noMoreExtents - Indicates that no more extents exist after this extent 5044 * record. 5045 */ 5046static OSErr FindExtentInExtentRec (Boolean isHFSPlus, UInt32 startBlock, UInt32 blockCount, const HFSPlusExtentRecord extentData, UInt32 *foundExtentIndex, Boolean *noMoreExtents) 5047{ 5048 OSErr err = noErr; 5049 UInt32 numOfExtents; 5050 Boolean foundExtent; 5051 int i; 5052 5053 foundExtent = false; 5054 *noMoreExtents = false; 5055 *foundExtentIndex = 0; 5056 5057 if (isHFSPlus) { 5058 numOfExtents = kHFSPlusExtentDensity; 5059 } else { 5060 numOfExtents = kHFSExtentDensity; 5061 } 5062 5063 for (i=0; i<numOfExtents; i++) { 5064 if (extentData[i].blockCount == 0) { 5065 /* no more extents left to check */ 5066 *noMoreExtents = true; 5067 break; 5068 } 5069 if ((startBlock == extentData[i].startBlock) && 5070 (blockCount == extentData[i].blockCount)) { 5071 foundExtent = true; 5072 *foundExtentIndex = i; 5073 break; 5074 } 5075 } 5076 5077 if (foundExtent == false) { 5078 err = fnfErr; 5079 } 5080 5081 return err; 5082} /* FindExtentInExtentRec */ 5083 5084/* Function: GetSystemFileName 5085 * 5086 * Description: Return the name of the system file based on fileID 5087 * 5088 * Input: 5089 * 1. fileID - fileID whose name is to be returned. 5090 * 2. *filenamelen - length of filename buffer. 5091 * 5092 * Output: 5093 * 1. *filename - filename, is limited by the length of filename buffer passed 5094 * in *filenamelen. 5095 * 2. *filenamelen - length of the filename 5096 * Always returns zero. 5097 */ 5098OSErr GetSystemFileName(UInt32 fileID, char *filename, unsigned int *filenamelen) 5099{ 5100 OSErr err = noErr; 5101 unsigned int len; 5102 5103 if (filename) { 5104 len = *filenamelen - 1; 5105 switch (fileID) { 5106 case kHFSExtentsFileID: 5107 strncpy (filename, "Extents Overflow BTree", len); 5108 break; 5109 5110 case kHFSCatalogFileID: 5111 strncpy (filename, "Catalog BTree", len); 5112 break; 5113 5114 case kHFSAllocationFileID: 5115 strncpy (filename, "Allocation File", len); 5116 break; 5117 5118 case kHFSStartupFileID: 5119 strncpy (filename, "Startup File", len); 5120 break; 5121 5122 case kHFSAttributesFileID: 5123 strncpy (filename, "Attributes BTree", len); 5124 break; 5125 5126 case kHFSBadBlockFileID: 5127 strncpy (filename, "Bad Allocation File", len); 5128 break; 5129 5130 case kHFSRepairCatalogFileID: 5131 strncpy (filename, "Repair Catalog File", len); 5132 break; 5133 5134 case kHFSBogusExtentFileID: 5135 strncpy (filename, "Bogus Extents File", len); 5136 break; 5137 5138 default: 5139 strncpy (filename, "Unknown File", len); 5140 break; 5141 }; 5142 filename[len] = '\0'; 5143 *filenamelen = strlen (filename); 5144 } 5145 return err; 5146} 5147 5148/* structure to store the intermediate path components during BTree traversal. 5149 * This is used as a LIFO linked list 5150 */ 5151struct fsPathString 5152{ 5153 char *name; 5154 unsigned int namelen; 5155 struct fsPathString *childPtr; 5156}; 5157 5158/* Function: GetFileNamePathByID 5159 * 5160 * Description: Return the file/directory name and/or full path by ID. The 5161 * length of the strings returned is limited by string lengths passed as 5162 * parameters. 5163 * The function lookups catalog thread record for given fileID and its parents 5164 * until it reaches the Root Folder. 5165 * 5166 * Note: 5167 * 1. The path returned currently does not return mangled names. 5168 * 2. Either one or both of fullPath and fileName can be NULL. 5169 * 3. fullPath and fileName are returned as NULL-terminated UTF8 strings. 5170 * 4. Returns error if fileID < kHFSFirstUserCatalogID. 5171 * 5172 * Input: 5173 * 1. GPtr - global scavenger structure pointer 5174 * 2. fileID - fileID for the target file/directory for finding the path 5175 * 3. fullPathLen - size of array to return full path 5176 * 4. fileNameLen - size of array to return file name 5177 * 5178 * Output: 5179 * Return value: zero on success, non-zero on failure 5180 * memFullErr - Not enough memory 5181 * paramErr - Invalid paramter 5182 * 5183 * The data in *fileNameLen and *fullPathLen is undefined on error. 5184 * 5185 * 1. fullPath - If fullPath is non-NULL, full path of file/directory is 5186 * returned (size limited by fullPathLen) 5187 * 2. *fullPathLen - length of fullPath returned. 5188 * 3. fileName - If fileName is non-NULL, file name of fileID is returned (size 5189 * limited by fileNameLen). 5190 * 4. *fileNameLen - length of fileName returned. 5191 * 5. *status - status of operation, any of the following bits can be set 5192 * (defined in dfalib/Scavenger.h). 5193 * FNAME_BUF2SMALL - filename buffer is too small. 5194 * FNAME_BIGNAME - filename is more than NAME_MAX bytes. 5195 * FPATH_BUF2SMALL - path buffer is too small. 5196 * FPATH_BIGNAME - one or more intermediate path component is greater 5197 * than NAME_MAX bytes. 5198 * F_RESERVE_FILEID- fileID is less than kHFSFirstUserCatalogNodeID. 5199 */ 5200OSErr GetFileNamePathByID(SGlobPtr GPtr, UInt32 fileID, char *fullPath, unsigned int *fullPathLen, char *fileName, unsigned int *fileNameLen, u_int16_t *status) 5201{ 5202 OSErr err = noErr; 5203 Boolean isHFSPlus; 5204 UInt16 recordSize; 5205 UInt16 curStatus = 0; 5206 UInt32 hint; 5207 CatalogKey catKey; 5208 CatalogRecord catRecord; 5209 struct fsPathString *listHeadPtr = NULL; 5210 struct fsPathString *listTailPtr = NULL; 5211 struct fsPathString *curPtr = NULL; 5212 u_char *filename = NULL; 5213 size_t namelen; 5214 5215 if (!fullPath && !fileName) { 5216 goto out; 5217 } 5218 5219 if (fileID < kHFSFirstUserCatalogNodeID) { 5220 curStatus = F_RESERVE_FILEID; 5221 err = paramErr; 5222 goto out; 5223 } 5224 5225 isHFSPlus = VolumeObjectIsHFSPlus(); 5226 5227 if (isHFSPlus) { 5228 filename = malloc(kHFSPlusMaxFileNameChars * 3 + 1); 5229 } else { 5230 filename = malloc(kHFSMaxFileNameChars + 1); 5231 } 5232 if (!filename) { 5233 err = memFullErr; 5234#if DEBUG_OVERLAP 5235 plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err); 5236#endif 5237 goto out; 5238 } 5239 5240 while (fileID != kHFSRootFolderID) { 5241 /* lookup for thread record for this fileID */ 5242 BuildCatalogKey(fileID, NULL, isHFSPlus, &catKey); 5243 err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, kNoHint, 5244 &catKey, &catRecord, &recordSize, &hint); 5245 if (err) { 5246#if DEBUG_OVERLAP 5247 plog ("%s: Error finding thread record for fileID = %d (err=%d)\n", __FUNCTION__, fileID, err); 5248#endif 5249 goto out; 5250 } 5251 5252 /* Check if this is indeed a thread record */ 5253 if ((catRecord.hfsPlusThread.recordType != kHFSPlusFileThreadRecord) && 5254 (catRecord.hfsPlusThread.recordType != kHFSPlusFolderThreadRecord) && 5255 (catRecord.hfsThread.recordType != kHFSFileThreadRecord) && 5256 (catRecord.hfsThread.recordType != kHFSFolderThreadRecord)) { 5257 err = paramErr; 5258#if DEBUG_OVERLAP 5259 plog ("%s: Error finding valid thread record for fileID = %d\n", __FUNCTION__, fileID); 5260#endif 5261 goto out; 5262 } 5263 5264 /* Convert the name string to utf8 */ 5265 if (isHFSPlus) { 5266 (void) utf_encodestr(catRecord.hfsPlusThread.nodeName.unicode, 5267 catRecord.hfsPlusThread.nodeName.length * 2, 5268 filename, &namelen, kHFSPlusMaxFileNameChars * 3 + 1); 5269 } else { 5270 namelen = catRecord.hfsThread.nodeName[0]; 5271 memcpy (filename, catKey.hfs.nodeName, namelen); 5272 } 5273 5274 /* Store the path name in LIFO linked list */ 5275 curPtr = malloc(sizeof(struct fsPathString)); 5276 if (!curPtr) { 5277 err = memFullErr; 5278#if DEBUG_OVERLAP 5279 plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err); 5280#endif 5281 goto out; 5282 } 5283 5284 /* Do not NULL terminate the string */ 5285 curPtr->namelen = namelen; 5286 curPtr->name = malloc(namelen); 5287 if (!curPtr->name) { 5288 err = memFullErr; 5289#if DEBUG_OVERLAP 5290 plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__, err); 5291#endif 5292 } 5293 memcpy (curPtr->name, filename, namelen); 5294 curPtr->childPtr = listHeadPtr; 5295 listHeadPtr = curPtr; 5296 if (listTailPtr == NULL) { 5297 listTailPtr = curPtr; 5298 } 5299 5300 /* lookup for parentID */ 5301 if (isHFSPlus) { 5302 fileID = catRecord.hfsPlusThread.parentID; 5303 } else { 5304 fileID = catRecord.hfsThread.parentID; 5305 } 5306 5307 /* no need to find entire path, bail out */ 5308 if (fullPath == NULL) { 5309 break; 5310 } 5311 } 5312 5313 /* return the name of the file/directory */ 5314 if (fileName) { 5315 /* Do not overflow the buffer length passed */ 5316 if (*fileNameLen < (listTailPtr->namelen + 1)) { 5317 *fileNameLen = *fileNameLen - 1; 5318 curStatus |= FNAME_BUF2SMALL; 5319 } else { 5320 *fileNameLen = listTailPtr->namelen; 5321 } 5322 if (*fileNameLen > NAME_MAX) { 5323 curStatus |= FNAME_BIGNAME; 5324 } 5325 memcpy (fileName, listTailPtr->name, *fileNameLen); 5326 fileName[*fileNameLen] = '\0'; 5327 } 5328 5329 /* return the full path of the file/directory */ 5330 if (fullPath) { 5331 /* Do not overflow the buffer length passed and reserve last byte for NULL termination */ 5332 unsigned int bytesRemain = *fullPathLen - 1; 5333 5334 *fullPathLen = 0; 5335 while (listHeadPtr != NULL) { 5336 if (bytesRemain == 0) { 5337 break; 5338 } 5339 memcpy ((fullPath + *fullPathLen), "/", 1); 5340 *fullPathLen += 1; 5341 bytesRemain--; 5342 5343 if (bytesRemain == 0) { 5344 break; 5345 } 5346 if (bytesRemain < listHeadPtr->namelen) { 5347 namelen = bytesRemain; 5348 curStatus |= FPATH_BUF2SMALL; 5349 } else { 5350 namelen = listHeadPtr->namelen; 5351 } 5352 if (namelen > NAME_MAX) { 5353 curStatus |= FPATH_BIGNAME; 5354 } 5355 memcpy ((fullPath + *fullPathLen), listHeadPtr->name, namelen); 5356 *fullPathLen += namelen; 5357 bytesRemain -= namelen; 5358 5359 curPtr = listHeadPtr; 5360 listHeadPtr = listHeadPtr->childPtr; 5361 free(curPtr->name); 5362 free(curPtr); 5363 } 5364 5365 fullPath[*fullPathLen] = '\0'; 5366 } 5367 5368 err = noErr; 5369 5370out: 5371 if (status) { 5372 *status = curStatus; 5373 } 5374 5375 /* free any remaining allocated memory */ 5376 while (listHeadPtr != NULL) { 5377 curPtr = listHeadPtr; 5378 listHeadPtr = listHeadPtr->childPtr; 5379 if (curPtr->name) { 5380 free (curPtr->name); 5381 } 5382 free (curPtr); 5383 } 5384 if (filename) { 5385 free (filename); 5386 } 5387 5388 return err; 5389} /* GetFileNamePathByID */ 5390 5391/* Function: CopyDiskBlocks 5392 * 5393 * Description: Copy data from source extent to destination extent 5394 * for blockCount on the disk. 5395 * 5396 * Input: 5397 * 1. GPtr - pointer to global scavenger structure. 5398 * 2. startAllocationBlock - startBlock for old extent 5399 * 3. blockCount - total blocks to copy 5400 * 4. newStartAllocationBlock - startBlock for new extent 5401 * 5402 * Output: 5403 * err, zero on success, non-zero on failure. 5404 */ 5405OSErr CopyDiskBlocks(SGlobPtr GPtr, const UInt32 startAllocationBlock, const UInt32 blockCount, const UInt32 newStartAllocationBlock ) 5406{ 5407 OSErr err = noErr; 5408 SVCB *vcb; 5409 uint64_t old_offset; 5410 uint64_t new_offset; 5411 uint32_t sectorsPerBlock; 5412 5413 vcb = GPtr->calculatedVCB; 5414 sectorsPerBlock = vcb->vcbBlockSize / Blk_Size; 5415 5416 old_offset = (vcb->vcbAlBlSt + (sectorsPerBlock * startAllocationBlock)) << Log2BlkLo; 5417 new_offset = (vcb->vcbAlBlSt + (sectorsPerBlock * newStartAllocationBlock)) << Log2BlkLo; 5418 5419 err = CacheCopyDiskBlocks (vcb->vcbBlockCache, old_offset, new_offset, 5420 blockCount * vcb->vcbBlockSize); 5421 return err; 5422} /* CopyDiskBlocks */ 5423 5424/* Function: WriteBufferToDisk 5425 * 5426 * Description: Write given buffer data to disk blocks. 5427 * If the length of the buffer is not a multiple of allocation block size, 5428 * the disk is filled with zero from the length of buffer upto the 5429 * end of allocation blocks (specified by blockCount). 5430 * 5431 * Input: 5432 * 1. GPtr - global scavenger structure pointer 5433 * 2. startBlock - starting block number for writing data. 5434 * 3. blockCount - total number of contiguous blocks to be written 5435 * 4. buffer - data buffer to be written to disk 5436 * 5. bufLen - length of data buffer to be written to disk. 5437 * 5438 * Output: 5439 * returns zero on success, non-zero on failure. 5440 */ 5441OSErr WriteBufferToDisk(SGlobPtr GPtr, UInt32 startBlock, UInt32 blockCount, u_char *buffer, int bufLen) 5442{ 5443 OSErr err = noErr; 5444 SVCB *vcb; 5445 uint64_t offset; 5446 uint32_t write_len; 5447 5448 vcb = GPtr->calculatedVCB; 5449 5450 /* Calculate offset and length */ 5451 offset = (vcb->vcbAlBlSt + ((vcb->vcbBlockSize / Blk_Size) * startBlock)) << Log2BlkLo; 5452 write_len = blockCount * vcb->vcbBlockSize; 5453 5454 /* Write buffer to disk */ 5455 err = CacheWriteBufferToDisk (vcb->vcbBlockCache, offset, write_len, buffer, bufLen); 5456 5457 return err; 5458} /* WriteBufferToDisk */ 5459 5460// 2210409, in System 8.1, moving file or folder would cause HFS+ thread records to be 5461// 520 bytes in size. We only shrink the threads if other repairs are needed. 5462static OSErr FixBloatedThreadRecords( SGlob *GPtr ) 5463{ 5464 CatalogRecord record; 5465 CatalogKey foundKey; 5466 UInt32 hint; 5467 UInt16 recordSize; 5468 SInt16 i = 0; 5469 OSErr err; 5470 SInt16 selCode = 0x8001; // Start with 1st record 5471 5472 err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint ); 5473 ReturnIfError( err ); 5474 5475 selCode = 1; // Get next record from now on 5476 5477 do 5478 { 5479 if ( ++i > 10 ) { (void) CheckForStop( GPtr ); i = 0; } // Spin the cursor every 10 entries 5480 5481 if ( (recordSize == sizeof(HFSPlusCatalogThread)) && ((record.recordType == kHFSPlusFolderThreadRecord) || (record.recordType == kHFSPlusFileThreadRecord)) ) 5482 { 5483 // HFS Plus has varaible sized threads so adjust to actual length 5484 recordSize -= ( sizeof(record.hfsPlusThread.nodeName.unicode) - (record.hfsPlusThread.nodeName.length * sizeof(UniChar)) ); 5485 5486 err = ReplaceBTreeRecord( GPtr->calculatedCatalogFCB, &foundKey, hint, &record, recordSize, &hint ); 5487 ReturnIfError( err ); 5488 } 5489 5490 err = GetBTreeRecord( GPtr->calculatedCatalogFCB, selCode, &foundKey, &record, &recordSize, &hint ); 5491 } while ( err == noErr ); 5492 5493 if ( err == btNotFound ) 5494 err = noErr; 5495 5496 return( err ); 5497} 5498 5499 5500static OSErr 5501FixMissingThreadRecords( SGlob *GPtr ) 5502{ 5503 struct MissingThread * mtp; 5504 FSBufferDescriptor btRecord; 5505 BTreeIterator iterator; 5506 OSStatus result; 5507 UInt16 dataSize; 5508 Boolean headsUp; 5509 UInt32 lostAndFoundDirID; 5510 5511 lostAndFoundDirID = 0; 5512 headsUp = false; 5513 for (mtp = GPtr->missingThreadList; mtp != NULL; mtp = mtp->link) { 5514 if ( mtp->threadID == 0 ) 5515 continue; 5516 5517 // if the thread record information in the MissingThread struct is not there 5518 // then we have a missing directory in addition to a missing thread record 5519 // for that directory. We will recreate the missing directory in our 5520 // lost+found directory. 5521 if ( mtp->thread.parentID == 0 ) { 5522 if (embedded == 1 && debug == 0) { 5523 return( R_RFail ); 5524 } 5525 if ( lostAndFoundDirID == 0 ) 5526 lostAndFoundDirID = CreateDirByName( GPtr , (u_char *)"lost+found", kHFSRootFolderID); 5527 if ( lostAndFoundDirID == 0 ) { 5528 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) 5529 plog( "\tCould not create lost+found directory \n" ); 5530 return( R_RFail ); 5531 } 5532 fsckPrint(GPtr->context, E_NoDir, mtp->threadID); 5533 result = FixMissingDirectory( GPtr, mtp->threadID, lostAndFoundDirID ); 5534 if ( result != 0 ) { 5535 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) 5536 plog( "\tCould not recreate a missing directory (error %d)\n", result ); 5537 return( R_RFail ); 5538 } 5539 else 5540 headsUp = true; 5541 continue; 5542 } 5543 5544 dataSize = 10 + (mtp->thread.nodeName.length * 2); 5545 btRecord.bufferAddress = (void *)&mtp->thread; 5546 btRecord.itemSize = dataSize; 5547 btRecord.itemCount = 1; 5548 iterator.hint.nodeNum = 0; 5549 BuildCatalogKey(mtp->threadID, NULL, true, (CatalogKey*)&iterator.key); 5550 5551 result = BTInsertRecord(GPtr->calculatedCatalogFCB, &iterator, &btRecord, dataSize); 5552 if (result) 5553 return (IntError(GPtr, R_IntErr)); 5554 mtp->threadID = 0; 5555 } 5556 if ( headsUp ) 5557 fsckPrint(GPtr->context, fsckLostFoundDirectory, "lost+found"); 5558 5559 return (0); 5560} 5561 5562 5563static OSErr 5564FixMissingDirectory( SGlob *GPtr, UInt32 theObjID, UInt32 theParID ) 5565{ 5566 Boolean isHFSPlus; 5567 UInt16 recSize; 5568 OSErr result; 5569 int nameLen; 5570 UInt32 hint; 5571 char myString[ 32 ]; 5572 CatalogName myName; 5573 CatalogRecord catRec; 5574 CatalogKey myKey, myThreadKey; 5575 5576 isHFSPlus = VolumeObjectIsHFSPlus( ); 5577 5578 // we will use the object ID of the missing directory as the name since we have 5579 // no way to find the original name and this should make it unique within our 5580 // lost+found directory. 5581 sprintf( myString, "%ld", (long)theObjID ); 5582 nameLen = strlen( myString ); 5583 5584 if ( isHFSPlus ) 5585 { 5586 int i; 5587 myName.ustr.length = nameLen; 5588 for ( i = 0; i < myName.ustr.length; i++ ) 5589 myName.ustr.unicode[ i ] = (u_int16_t) myString[ i ]; 5590 } 5591 else 5592 { 5593 myName.pstr[0] = nameLen; 5594 memcpy( &myName.pstr[1], &myString[0], nameLen ); 5595 } 5596 5597 // make sure the name is not already used 5598 BuildCatalogKey( theParID, &myName, isHFSPlus, &myKey ); 5599 result = SearchBTreeRecord( GPtr->calculatedCatalogFCB, &myKey, kNoHint, 5600 NULL, &catRec, &recSize, &hint ); 5601 if ( result == noErr ) 5602 return( R_IntErr ); 5603 5604 // insert new directory and thread record into the catalog 5605 recSize = BuildThreadRec( &myKey, &catRec, isHFSPlus, true ); 5606 BuildCatalogKey( theObjID, NULL, isHFSPlus, &myThreadKey ); 5607 result = InsertBTreeRecord( GPtr->calculatedCatalogFCB, &myThreadKey, &catRec, recSize, &hint ); 5608 if ( result != noErr ) 5609 return( result ); 5610 5611 recSize = BuildFolderRec( GPtr, 01777, theObjID, isHFSPlus, &catRec ); 5612 5613 result = InsertBTreeRecord( GPtr->calculatedCatalogFCB, &myKey, &catRec, recSize, &hint ); 5614 if ( result != noErr ) 5615 return( result ); 5616 5617 /* update parent directory to reflect addition of new directory */ 5618 result = UpdateFolderCount( GPtr->calculatedVCB, theParID, NULL, 5619 ((isHFSPlus) ? kHFSPlusFolderRecord : kHFSFolderRecord), 5620 kNoHint, 1 ); 5621 5622 /* update our header node on disk from our BTreeControlBlock */ 5623 UpdateBTreeHeader( GPtr->calculatedCatalogFCB ); 5624 5625 return( result ); 5626 5627} /* FixMissingDirectory */ 5628 5629 5630static HFSCatalogNodeID 5631GetObjectID( CatalogRecord * theRecPtr ) 5632{ 5633 HFSCatalogNodeID myObjID; 5634 5635 switch ( theRecPtr->recordType ) { 5636 case kHFSPlusFolderRecord: 5637 myObjID = theRecPtr->hfsPlusFolder.folderID; 5638 break; 5639 case kHFSPlusFileRecord: 5640 myObjID = theRecPtr->hfsPlusFile.fileID; 5641 break; 5642 case kHFSFolderRecord: 5643 myObjID = theRecPtr->hfsFolder.folderID; 5644 break; 5645 case kHFSFileRecord: 5646 myObjID = theRecPtr->hfsFile.fileID; 5647 break; 5648 default: 5649 myObjID = 0; 5650 } 5651 5652 return( myObjID ); 5653 5654} /* GetObjectID */ 5655 5656/* Function: CreateFileByName 5657 * 5658 * Description: Create a file with given fileName of type fileType containing 5659 * data of length dataLen. This function assumes that the name of symlink 5660 * to be created is passed as UTF8 5661 * 5662 * Input: 5663 * 1. GPtr - pointer to global scavenger structure 5664 * 2. parentID - ID of parent directory to create the new file. 5665 * 3. fileName - name of the file to create in UTF8 format. 5666 * 4. fileNameLen - length of the filename to be created. 5667 * If the volume is HFS Plus, the filename is delimited to 5668 * kHFSPlusMaxFileNameChars characters. 5669 * If the volume is plain HFS, the filename is delimited to 5670 * kHFSMaxFileNameChars characters. 5671 * 5. fileType - file type (currently supported S_IFREG, S_IFLNK). 5672 * 6. data - data content of first data fork of the file 5673 * 7. dataLen - length of data to be written 5674 * 5675 * Output: 5676 * returns zero on success, non-zero on failure. 5677 * memFullErr - Not enough memory 5678 * paramErr - Invalid paramter 5679 */ 5680OSErr CreateFileByName(SGlobPtr GPtr, UInt32 parentID, UInt16 fileType, u_char *fileName, unsigned int filenameLen, u_char *data, unsigned int dataLen) 5681{ 5682 OSErr err = noErr; 5683 Boolean isHFSPlus; 5684 Boolean isCatUpdated = false; 5685 5686 CatalogName fName; 5687 CatalogRecord catRecord; 5688 CatalogKey catKey; 5689 CatalogKey threadKey; 5690 UInt32 hint; 5691 UInt16 recordSize; 5692 5693 UInt32 startBlock = 0; 5694 UInt32 blockCount = 0; 5695 UInt32 nextCNID; 5696 5697 isHFSPlus = VolumeObjectIsHFSPlus(); 5698 5699 /* Construct unicode name for file name to construct catalog key */ 5700 if (isHFSPlus) { 5701 /* Convert utf8 filename to Unicode filename */ 5702 size_t namelen; 5703 5704 if (filenameLen < kHFSPlusMaxFileNameChars) { 5705 (void) utf_decodestr (fileName, filenameLen, fName.ustr.unicode, &namelen, sizeof(fName.ustr.unicode)); 5706 namelen /= 2; 5707 fName.ustr.length = namelen; 5708 } else { 5709 /* The resulting may result in more than kHFSPlusMaxFileNameChars chars */ 5710 UInt16 *unicodename; 5711 5712 /* Allocate a large array to convert the utf-8 to utf-16 */ 5713 unicodename = malloc (filenameLen * 4); 5714 if (unicodename == NULL) { 5715 err = memFullErr; 5716 goto out; 5717 } 5718 5719 (void) utf_decodestr (fileName, filenameLen, unicodename, &namelen, filenameLen * 4); 5720 namelen /= 2; 5721 5722 /* Chopping unicode string based on length might affect unicode 5723 * chars that take more than one UInt16 - very rare possiblity. 5724 */ 5725 if (namelen > kHFSPlusMaxFileNameChars) { 5726 namelen = kHFSPlusMaxFileNameChars; 5727 } 5728 5729 memcpy (fName.ustr.unicode, unicodename, (namelen * 2)); 5730 free (unicodename); 5731 fName.ustr.length = namelen; 5732 } 5733 } else { 5734 if (filenameLen > kHFSMaxFileNameChars) { 5735 filenameLen = kHFSMaxFileNameChars; 5736 } 5737 fName.pstr[0] = filenameLen; 5738 memcpy(&fName.pstr[1], fileName, filenameLen); 5739 } 5740 5741 /* Make sure that a file with same name does not exist in parent dir */ 5742 BuildCatalogKey(parentID, &fName, isHFSPlus, &catKey); 5743 err = SearchBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, kNoHint, NULL, 5744 &catRecord, &recordSize, &hint); 5745 if (err != fsBTRecordNotFoundErr) { 5746#if DEBUG_OVERLAP 5747 plog ("%s: %s probably exists in dirID = %d (err=%d)\n", __FUNCTION__, fileName, parentID, err); 5748#endif 5749 err = EEXIST; 5750 goto out; 5751 } 5752 5753 if (data) { 5754 /* Calculate correct number of blocks required for data */ 5755 if (dataLen % (GPtr->calculatedVCB->vcbBlockSize)) { 5756 blockCount = (dataLen / (GPtr->calculatedVCB->vcbBlockSize)) + 1; 5757 } else { 5758 blockCount = dataLen / (GPtr->calculatedVCB->vcbBlockSize); 5759 } 5760 5761 if (blockCount) { 5762 /* Allocate disk space for the data */ 5763 err = AllocateContigBitmapBits (GPtr->calculatedVCB, blockCount, &startBlock); 5764 if (err != noErr) { 5765#if DEBUG_OVERLAP 5766 plog ("%s: Not enough disk space (err=%d)\n", __FUNCTION__, err); 5767#endif 5768 goto out; 5769 } 5770 5771 /* Write the data to the blocks */ 5772 err = WriteBufferToDisk(GPtr, startBlock, blockCount, data, dataLen); 5773 if (err != noErr) { 5774#if DEBUG_OVERLAP 5775 plog ("%s: Error in writing data of %s to disk (err=%d)\n", __FUNCTION__, fileName, err); 5776#endif 5777 goto out; 5778 } 5779 } 5780 } 5781 5782 /* Build and insert thread record */ 5783 nextCNID = GPtr->calculatedVCB->vcbNextCatalogID; 5784 if (!isHFSPlus && nextCNID == 0xffffFFFF) { 5785 goto out; 5786 } 5787 recordSize = BuildThreadRec(&catKey, &catRecord, isHFSPlus, false); 5788 for (;;) { 5789 BuildCatalogKey(nextCNID, NULL, isHFSPlus, &threadKey); 5790 err = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &threadKey, &catRecord, 5791 recordSize, &hint ); 5792 if (err == fsBTDuplicateRecordErr && isHFSPlus) { 5793 /* Allow CNIDs on HFS Plus volumes to wrap around */ 5794 ++nextCNID; 5795 if (nextCNID < kHFSFirstUserCatalogNodeID) { 5796 GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask; 5797 MarkVCBDirty(GPtr->calculatedVCB); 5798 nextCNID = kHFSFirstUserCatalogNodeID; 5799 } 5800 continue; 5801 } 5802 break; 5803 } 5804 if (err != noErr) { 5805#if DEBUG_OVERLAP 5806 plog ("%s: Error inserting thread record for file = %s (err=%d)\n", __FUNCTION__, fileName, err); 5807#endif 5808 goto out; 5809 } 5810 5811 /* Build file record */ 5812 recordSize = BuildFileRec(fileType, 0666, nextCNID, isHFSPlus, &catRecord); 5813 if (recordSize == 0) { 5814#if DEBUG_OVERLAP 5815 plog ("%s: Incorrect fileType\n", __FUNCTION__); 5816#endif 5817 5818 /* Remove the thread record inserted above */ 5819 err = DeleteBTreeRecord (GPtr->calculatedCatalogFCB, &threadKey); 5820 if (err != noErr) { 5821#if DEBUG_OVERLAP 5822 plog ("%s: Error in removing thread record\n", __FUNCTION__); 5823#endif 5824 } 5825 err = paramErr; 5826 goto out; 5827 } 5828 5829 /* Update startBlock, blockCount, etc */ 5830 if (isHFSPlus) { 5831 catRecord.hfsPlusFile.dataFork.logicalSize = dataLen; 5832 catRecord.hfsPlusFile.dataFork.totalBlocks = blockCount; 5833 catRecord.hfsPlusFile.dataFork.extents[0].startBlock = startBlock; 5834 catRecord.hfsPlusFile.dataFork.extents[0].blockCount = blockCount; 5835 } else { 5836 catRecord.hfsFile.dataLogicalSize = dataLen; 5837 catRecord.hfsFile.dataPhysicalSize = blockCount * GPtr->calculatedVCB->vcbBlockSize; 5838 catRecord.hfsFile.dataExtents[0].startBlock = startBlock; 5839 catRecord.hfsFile.dataExtents[0].blockCount = blockCount; 5840 } 5841 5842 /* Insert catalog file record */ 5843 err = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &catKey, &catRecord, recordSize, &hint ); 5844 if (err == noErr) { 5845 isCatUpdated = true; 5846 5847#if DEBUG_OVERLAP 5848 plog ("Created \"%s\" with ID = %d startBlock = %d, blockCount = %d, dataLen = %d\n", fileName, nextCNID, startBlock, blockCount, dataLen); 5849#endif 5850 } else { 5851#if DEBUG_OVERLAP 5852 plog ("%s: Error in inserting file record for file = %s (err=%d)\n", __FUNCTION__, fileName, err); 5853#endif 5854 5855 /* remove the thread record inserted above */ 5856 err = DeleteBTreeRecord (GPtr->calculatedCatalogFCB, &threadKey); 5857 if (err != noErr) { 5858#if DEBUG_OVERLAP 5859 plog ("%s: Error in removing thread record\n", __FUNCTION__); 5860#endif 5861 } 5862 err = paramErr; 5863 goto out; 5864 } 5865 5866 /* Update volume header */ 5867 GPtr->calculatedVCB->vcbNextCatalogID = nextCNID + 1; 5868 if (GPtr->calculatedVCB->vcbNextCatalogID < kHFSFirstUserCatalogNodeID) { 5869 GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask; 5870 GPtr->calculatedVCB->vcbNextCatalogID = kHFSFirstUserCatalogNodeID; 5871 } 5872 MarkVCBDirty( GPtr->calculatedVCB ); 5873 5874 /* update our header node on disk from our BTreeControlBlock */ 5875 UpdateBTreeHeader(GPtr->calculatedCatalogFCB); 5876 5877 /* update parent directory to reflect addition of new file */ 5878 err = UpdateFolderCount(GPtr->calculatedVCB, parentID, NULL, kHFSPlusFileRecord, kNoHint, 1); 5879 if (err != noErr) { 5880#if DEBUG_OVERLAP 5881 plog ("%s: Error in updating parent folder count (err=%d)\n", __FUNCTION__, err); 5882#endif 5883 goto out; 5884 } 5885 5886out: 5887 /* On error, if catalog record was not inserted and disk block were allocated, 5888 * deallocate the blocks 5889 */ 5890 if (err && (isCatUpdated == false) && startBlock) { 5891 ReleaseBitmapBits (startBlock, blockCount); 5892 } 5893 5894 return err; 5895} /* CreateFileByName */ 5896 5897/* Function: CreateDirByName 5898 * 5899 * Description: Create directory with name dirName in a directory with ID as 5900 * parentID. The function assumes that the dirName passed is ASCII. 5901 * 5902 * Input: 5903 * GPtr - global scavenger structure pointer 5904 * dirName - name of directory to be created 5905 * parentID - dirID of the parent directory for new directory 5906 * 5907 * Output: 5908 * on success, ID of the new directory created. 5909 * on failure, zero. 5910 * 5911 */ 5912UInt32 CreateDirByName(SGlob *GPtr , const u_char *dirName, const UInt32 parentID) 5913{ 5914 Boolean isHFSPlus; 5915 UInt16 recSize; 5916 UInt16 myMode; 5917 int result; 5918 int nameLen; 5919 UInt32 hint; 5920 UInt32 nextCNID; 5921 SFCB * fcbPtr; 5922 CatalogKey myKey; 5923 CatalogName myName; 5924 CatalogRecord catRec; 5925 5926 isHFSPlus = VolumeObjectIsHFSPlus( ); 5927 fcbPtr = GPtr->calculatedCatalogFCB; 5928 nameLen = strlen( (char *)dirName ); 5929 5930 if ( isHFSPlus ) 5931 { 5932 int i; 5933 myName.ustr.length = nameLen; 5934 for ( i = 0; i < myName.ustr.length; i++ ) 5935 myName.ustr.unicode[ i ] = (u_int16_t) dirName[ i ]; 5936 } 5937 else 5938 { 5939 myName.pstr[0] = nameLen; 5940 memcpy( &myName.pstr[1], &dirName[0], nameLen ); 5941 } 5942 5943 // see if we already have a lost and found directory 5944 BuildCatalogKey( parentID, &myName, isHFSPlus, &myKey ); 5945 result = SearchBTreeRecord( fcbPtr, &myKey, kNoHint, NULL, &catRec, &recSize, &hint ); 5946 if ( result == noErr ) { 5947 if ( isHFSPlus ) { 5948 if ( catRec.recordType == kHFSPlusFolderRecord ) 5949 return( catRec.hfsPlusFolder.folderID ); 5950 } 5951 else if ( catRec.recordType == kHFSFolderRecord ) 5952 return( catRec.hfsFolder.folderID ); 5953 return( 0 ); // something already there but not a directory 5954 } 5955 5956 // insert new directory and thread record into the catalog 5957 nextCNID = GPtr->calculatedVCB->vcbNextCatalogID; 5958 if ( !isHFSPlus && nextCNID == 0xFFFFFFFF ) 5959 return( 0 ); 5960 5961 recSize = BuildThreadRec( &myKey, &catRec, isHFSPlus, true ); 5962 for (;;) { 5963 CatalogKey key; 5964 5965 BuildCatalogKey( nextCNID, NULL, isHFSPlus, &key ); 5966 result = InsertBTreeRecord( fcbPtr, &key, &catRec, recSize, &hint ); 5967 if ( result == fsBTDuplicateRecordErr && isHFSPlus ) { 5968 /* 5969 * Allow CNIDs on HFS Plus volumes to wrap around 5970 */ 5971 ++nextCNID; 5972 if ( nextCNID < kHFSFirstUserCatalogNodeID ) { 5973 GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask; 5974 MarkVCBDirty( GPtr->calculatedVCB ); 5975 nextCNID = kHFSFirstUserCatalogNodeID; 5976 } 5977 continue; 5978 } 5979 break; 5980 } 5981 if ( result != 0 ) 5982 return( 0 ); 5983 5984 myMode = ( GPtr->lostAndFoundMode == 0 ) ? 01777 : GPtr->lostAndFoundMode; 5985 recSize = BuildFolderRec( GPtr, myMode, nextCNID, isHFSPlus, &catRec ); 5986 result = InsertBTreeRecord( fcbPtr, &myKey, &catRec, recSize, &hint ); 5987 if ( result != 0 ) 5988 return( 0 ); 5989 5990 /* Update volume header */ 5991 GPtr->calculatedVCB->vcbNextCatalogID = nextCNID + 1; 5992 if ( GPtr->calculatedVCB->vcbNextCatalogID < kHFSFirstUserCatalogNodeID ) { 5993 GPtr->calculatedVCB->vcbAttributes |= kHFSCatalogNodeIDsReusedMask; 5994 GPtr->calculatedVCB->vcbNextCatalogID = kHFSFirstUserCatalogNodeID; 5995 } 5996 MarkVCBDirty( GPtr->calculatedVCB ); 5997 5998 /* update parent directory to reflect addition of new directory */ 5999 result = UpdateFolderCount( GPtr->calculatedVCB, parentID, NULL, kHFSPlusFolderRecord, kNoHint, 1 ); 6000 6001 /* update our header node on disk from our BTreeControlBlock */ 6002 UpdateBTreeHeader( GPtr->calculatedCatalogFCB ); 6003 6004 return( nextCNID ); 6005 6006} /* CreateDirByName */ 6007 6008static void 6009CountFolderItems(SGlobPtr GPtr, UInt32 folderID, Boolean isHFSPlus, UInt32 *itemCount, UInt32 *folderCount) 6010{ 6011 SFCB *fcb = GPtr->calculatedCatalogFCB; 6012 OSErr err = 0; 6013 BTreeIterator iterator; 6014 FSBufferDescriptor btRecord; 6015 union { 6016 HFSPlusCatalogFolder catRecord; 6017 HFSPlusCatalogFile catFile; 6018 } catRecord; 6019 HFSPlusCatalogKey *key; 6020 UInt16 recordSize = 0; 6021 int fCount = 0, iCount = 0; 6022 6023 ClearMemory(&iterator, sizeof(iterator)); 6024 key = (HFSPlusCatalogKey*)&iterator.key; 6025 BuildCatalogKey(folderID, NULL, isHFSPlus, (CatalogKey*)key); 6026 btRecord.bufferAddress = &catRecord; 6027 btRecord.itemCount = 1; 6028 btRecord.itemSize = sizeof(catRecord); 6029 6030 for (err = BTSearchRecord(fcb, &iterator, kNoHint, &btRecord, &recordSize, &iterator); 6031 err == 0; 6032 err = BTIterateRecord(fcb, kBTreeNextRecord, &iterator, &btRecord, &recordSize)) { 6033 if (catRecord.catRecord.recordType == kHFSPlusFolderThreadRecord || 6034 catRecord.catRecord.recordType == kHFSPlusFileThreadRecord || 6035 catRecord.catRecord.recordType == kHFSFolderThreadRecord || 6036 catRecord.catRecord.recordType == kHFSFileThreadRecord) 6037 continue; 6038 if (key->parentID != folderID) 6039 break; 6040 if (isHFSPlus && 6041 (catRecord.catRecord.recordType == kHFSPlusFileRecord) && 6042 (catRecord.catFile.flags & kHFSHasLinkChainMask) && 6043 (catRecord.catFile.userInfo.fdType == kHFSAliasType) && 6044 (catRecord.catFile.userInfo.fdCreator == kHFSAliasCreator) && 6045 (key->parentID != GPtr->filelink_priv_dir_id)) { 6046 // It's a directory hard link, which counts as a directory here 6047 fCount++; 6048 } 6049 if (catRecord.catRecord.recordType == kHFSPlusFolderRecord) 6050 fCount++; 6051 iCount++; 6052 } 6053 if (itemCount) 6054 *itemCount = iCount; 6055 if (folderCount) 6056 *folderCount = fCount; 6057 return; 6058} 6059/* 6060 * Build a catalog node folder record with the given input. 6061 */ 6062static int 6063BuildFolderRec( SGlob *GPtr, u_int16_t theMode, UInt32 theObjID, Boolean isHFSPlus, CatalogRecord * theRecPtr ) 6064{ 6065 UInt16 recSize; 6066 UInt32 createTime; 6067 UInt32 vCount = 0, fCount = 0; 6068 6069 ClearMemory( (Ptr)theRecPtr, sizeof(*theRecPtr) ); 6070 6071 CountFolderItems(GPtr, theObjID, isHFSPlus, &vCount, &fCount); 6072 if ( isHFSPlus ) { 6073 createTime = GetTimeUTC(); 6074 theRecPtr->hfsPlusFolder.recordType = kHFSPlusFolderRecord; 6075 theRecPtr->hfsPlusFolder.folderID = theObjID; 6076 theRecPtr->hfsPlusFolder.createDate = createTime; 6077 theRecPtr->hfsPlusFolder.contentModDate = createTime; 6078 theRecPtr->hfsPlusFolder.attributeModDate = createTime; 6079 theRecPtr->hfsPlusFolder.bsdInfo.ownerID = getuid( ); 6080 theRecPtr->hfsPlusFolder.bsdInfo.groupID = getgid( ); 6081 theRecPtr->hfsPlusFolder.bsdInfo.fileMode = S_IFDIR; 6082 theRecPtr->hfsPlusFolder.bsdInfo.fileMode |= theMode; 6083 theRecPtr->hfsPlusFolder.valence = vCount; 6084 recSize= sizeof(HFSPlusCatalogFolder); 6085 if (VolumeObjectIsHFSX(GPtr)) { 6086 theRecPtr->hfsPlusFolder.flags |= kHFSHasFolderCountMask; 6087 theRecPtr->hfsPlusFolder.folderCount = fCount; 6088 } 6089 } 6090 else { 6091 createTime = GetTimeLocal( true ); 6092 theRecPtr->hfsFolder.recordType = kHFSFolderRecord; 6093 theRecPtr->hfsFolder.folderID = theObjID; 6094 theRecPtr->hfsFolder.createDate = createTime; 6095 theRecPtr->hfsFolder.modifyDate = createTime; 6096 theRecPtr->hfsFolder.valence = vCount; 6097 recSize= sizeof(HFSCatalogFolder); 6098 } 6099 6100 return( recSize ); 6101 6102} /* BuildFolderRec */ 6103 6104 6105/* 6106 * Build a catalog node thread record from a catalog key 6107 * and return the size of the record. 6108 */ 6109static int 6110BuildThreadRec( CatalogKey * theKeyPtr, CatalogRecord * theRecPtr, 6111 Boolean isHFSPlus, Boolean isDirectory ) 6112{ 6113 int size = 0; 6114 6115 if ( isHFSPlus ) { 6116 HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)theKeyPtr; 6117 HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)theRecPtr; 6118 6119 size = sizeof(HFSPlusCatalogThread); 6120 if ( isDirectory ) 6121 rec->recordType = kHFSPlusFolderThreadRecord; 6122 else 6123 rec->recordType = kHFSPlusFileThreadRecord; 6124 rec->reserved = 0; 6125 rec->parentID = key->parentID; 6126 bcopy(&key->nodeName, &rec->nodeName, 6127 sizeof(UniChar) * (key->nodeName.length + 1)); 6128 6129 /* HFS Plus has varaible sized thread records */ 6130 size -= (sizeof(rec->nodeName.unicode) - 6131 (rec->nodeName.length * sizeof(UniChar))); 6132 } 6133 else /* HFS standard */ { 6134 HFSCatalogKey *key = (HFSCatalogKey *)theKeyPtr; 6135 HFSCatalogThread *rec = (HFSCatalogThread *)theRecPtr; 6136 6137 size = sizeof(HFSCatalogThread); 6138 bzero(rec, size); 6139 if ( isDirectory ) 6140 rec->recordType = kHFSFolderThreadRecord; 6141 else 6142 rec->recordType = kHFSFileThreadRecord; 6143 rec->parentID = key->parentID; 6144 bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1); 6145 } 6146 6147 return (size); 6148 6149} /* BuildThreadRec */ 6150 6151/* Function: BuildFileRec 6152 * 6153 * Description: Build a catalog file record with given fileID, fileType 6154 * and fileMode. 6155 * 6156 * Input: 6157 * 1. fileType - currently supports S_IFREG, S_IFLNK 6158 * 2. fileMode - file mode desired. 6159 * 3. fileID - file ID 6160 * 4. isHFSPlus - indicates whether the record is being created for 6161 * HFSPlus volume or plain HFS volume. 6162 * 5. catRecord - pointer to catalog record 6163 * 6164 * Output: 6165 * returns size of the catalog record. 6166 * on success, non-zero value; on failure, zero. 6167 */ 6168static int BuildFileRec(UInt16 fileType, UInt16 fileMode, UInt32 fileID, Boolean isHFSPlus, CatalogRecord *catRecord) 6169{ 6170 UInt16 recordSize = 0; 6171 UInt32 createTime; 6172 6173 /* We only support creating S_IFREG and S_IFLNK and S_IFLNK is not supported 6174 * on plain HFS 6175 */ 6176 if (((fileType != S_IFREG) && (fileType != S_IFLNK)) || 6177 ((isHFSPlus == false) && (fileType == S_IFLNK))) { 6178 goto out; 6179 } 6180 6181 ClearMemory((Ptr)catRecord, sizeof(*catRecord)); 6182 6183 if ( isHFSPlus ) { 6184 createTime = GetTimeUTC(); 6185 catRecord->hfsPlusFile.recordType = kHFSPlusFileRecord; 6186 catRecord->hfsPlusFile.fileID = fileID; 6187 catRecord->hfsPlusFile.createDate = createTime; 6188 catRecord->hfsPlusFile.contentModDate = createTime; 6189 catRecord->hfsPlusFile.attributeModDate = createTime; 6190 catRecord->hfsPlusFile.bsdInfo.ownerID = getuid(); 6191 catRecord->hfsPlusFile.bsdInfo.groupID = getgid(); 6192 catRecord->hfsPlusFile.bsdInfo.fileMode = fileType; 6193 catRecord->hfsPlusFile.bsdInfo.fileMode |= fileMode; 6194 if (fileType == S_IFLNK) { 6195 catRecord->hfsPlusFile.userInfo.fdType = kSymLinkFileType; 6196 catRecord->hfsPlusFile.userInfo.fdCreator = kSymLinkCreator; 6197 } else { 6198 catRecord->hfsPlusFile.userInfo.fdType = kTextFileType; 6199 catRecord->hfsPlusFile.userInfo.fdCreator = kTextFileCreator; 6200 } 6201 recordSize= sizeof(HFSPlusCatalogFile); 6202 } 6203 else { 6204 createTime = GetTimeLocal(true); 6205 catRecord->hfsFile.recordType = kHFSFileRecord; 6206 catRecord->hfsFile.fileID = fileID; 6207 catRecord->hfsFile.createDate = createTime; 6208 catRecord->hfsFile.modifyDate = createTime; 6209 catRecord->hfsFile.userInfo.fdType = kTextFileType; 6210 catRecord->hfsFile.userInfo.fdCreator = kTextFileCreator; 6211 recordSize= sizeof(HFSCatalogFile); 6212 } 6213 6214out: 6215 return(recordSize); 6216} /* BuildFileRec */ 6217 6218/* Function: BuildAttributeKey 6219 * 6220 * Build attribute key based on given information like - 6221 * fileID, startBlock, attribute name and attribute name length. 6222 * 6223 * Note that the attribute name is the UTF-8 format string. 6224 */ 6225static void BuildAttributeKey(u_int32_t fileID, u_int32_t startBlock, 6226 unsigned char *attrName, u_int16_t attrNameLen, HFSPlusAttrKey *key) 6227{ 6228 size_t attrNameLenBytes; 6229 6230 assert(VolumeObjectIsHFSPlus() == true); 6231 6232 key->pad = 0; 6233 key->fileID = fileID; 6234 key->startBlock = startBlock; 6235 6236 /* Convert UTF-8 attribute name to unicode */ 6237 (void) utf_decodestr(attrName, attrNameLen, key->attrName, &attrNameLenBytes, sizeof(key->attrName)); 6238 key->attrNameLen = attrNameLenBytes / 2; 6239 6240 key->keyLength = kHFSPlusAttrKeyMinimumLength + attrNameLenBytes; 6241} 6242 6243/* Delete catalog record and thread record for given ID. On successful 6244 * deletion, this function also updates the valence and folder count for 6245 * the parent directory and the file/folder count in the volume header. 6246 * 6247 * Returns - zero on success, non-zero on failure. 6248 */ 6249static int DeleteCatalogRecordByID(SGlobPtr GPtr, uint32_t id, Boolean for_rename) 6250{ 6251 int retval; 6252 CatalogRecord rec; 6253 CatalogKey key; 6254 UInt16 recsize; 6255 Boolean isHFSPlus; 6256 6257 isHFSPlus = VolumeObjectIsHFSPlus(); 6258 6259 /* Lookup the catalog record to move */ 6260 retval = GetCatalogRecordByID(GPtr, id, isHFSPlus, &key, &rec, &recsize); 6261 if (retval) { 6262 return retval; 6263 } 6264 6265 /* Delete the record */ 6266 if (isHFSPlus) { 6267 retval = DeleteCatalogNode(GPtr->calculatedVCB, 6268 key.hfsPlus.parentID, 6269 (const CatalogName *)&key.hfsPlus.nodeName, 6270 kNoHint, for_rename); 6271 } else { 6272 retval = DeleteCatalogNode(GPtr->calculatedVCB, 6273 key.hfs.parentID, 6274 (const CatalogName *)&key.hfs.nodeName, 6275 kNoHint, for_rename); 6276 } 6277 6278 /* If deletion of record succeeded, and the operation was not 6279 * being performed for rename, and the volume is HFS+, try 6280 * deleting all extended attributes for this file/folder 6281 */ 6282 if ((retval == 0) && (for_rename == false) && (isHFSPlus == true)) { 6283 /* Delete all attributes associated with this ID */ 6284 retval = DeleteAllAttrsByID(GPtr, id); 6285 } 6286 6287 return retval; 6288} 6289 6290/* Move a catalog record with given ID to a new parent directory with given 6291 * parentID. This function should only be called for HFS+ volumes. 6292 * This function removes the catalog record from old parent and inserts 6293 * it back with the new parent ID. It also takes care of updating the 6294 * parent directory counts. Note that this function clears kHFSHasLinkChainBit 6295 * from the catalog record flags. 6296 * 6297 * On success, returns zero. On failure, returns non-zero. 6298 */ 6299static int MoveCatalogRecordByID(SGlobPtr GPtr, uint32_t id, uint32_t new_parentid) 6300{ 6301 int retval; 6302 CatalogRecord rec; 6303 CatalogKey key; 6304 UInt32 hint; 6305 UInt16 recsize; 6306 Boolean isFolder = false; 6307 BTreeIterator iterator; 6308 6309 assert (VolumeObjectIsHFSPlus() == true); 6310 6311 /* Lookup the catalog record to move */ 6312 retval = GetCatalogRecordByID(GPtr, id, true, &key, &rec, &recsize); 6313 if (retval) { 6314 goto out; 6315 } 6316 6317 /* Delete the record and its thread from original location. 6318 * For file records, do not deallocate original extents. 6319 */ 6320 retval = DeleteCatalogRecordByID(GPtr, id, true); 6321 if (retval) { 6322 goto out; 6323 } 6324 6325 key.hfsPlus.parentID = new_parentid; 6326 /* The record being moved should not have linkChainMask set */ 6327 if (rec.recordType == kHFSPlusFolderRecord) { 6328 rec.hfsPlusFolder.flags &= ~kHFSHasLinkChainMask; 6329 isFolder = true; 6330 } else if (rec.recordType == kHFSPlusFileRecord) { 6331 rec.hfsPlusFile.flags &= ~kHFSHasLinkChainMask; 6332 isFolder = false; 6333 } 6334 6335 /* Insert the catalog record with new parent */ 6336 retval = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &key, &rec, 6337 recsize, &hint); 6338 if (retval) { 6339 goto out; 6340 } 6341 6342 /* Insert the new thread record */ 6343 recsize = BuildThreadRec(&key, &rec, true, isFolder); 6344 BuildCatalogKey(id, NULL, true, &key); 6345 retval = InsertBTreeRecord(GPtr->calculatedCatalogFCB, &key, &rec, 6346 recsize, &hint); 6347 if (retval) { 6348 goto out; 6349 } 6350 6351 /* Update the counts in the new parent directory and volume header */ 6352 ClearMemory(&iterator, sizeof(iterator)); 6353 retval = GetCatalogRecordByID(GPtr, new_parentid, true, &key, &rec, &recsize); 6354 if (retval) { 6355 if ((retval == btNotFound) && (GPtr->CBTStat & S_Orphan)) { 6356 /* No need for re-repair minor repair order because 6357 * we are failing on updating the parent directory. 6358 */ 6359 retval = 0; 6360 } 6361 goto out; 6362 } 6363 if (rec.recordType != kHFSPlusFolderRecord) { 6364 goto out; 6365 } 6366 6367 rec.hfsPlusFolder.valence++; 6368 if ((isFolder == true) && 6369 (rec.hfsPlusFolder.flags & kHFSHasFolderCountMask)) { 6370 rec.hfsPlusFolder.folderCount++; 6371 } 6372 6373 retval = ReplaceBTreeRecord(GPtr->calculatedCatalogFCB, &key, 6374 kNoHint, &rec, recsize, &hint); 6375 if (retval) { 6376 goto out; 6377 } 6378 6379 if (isFolder == true) { 6380 GPtr->calculatedVCB->vcbFolderCount++; 6381 } else { 6382 GPtr->calculatedVCB->vcbFileCount++; 6383 } 6384 GPtr->VIStat |= S_MDB; 6385 GPtr->CBTStat |= S_BTH; /* leaf record count changed */ 6386 6387out: 6388 return retval; 6389} 6390 6391/* The function deletes all extended attributes associated with a given 6392 * file/folder ID. The function takes care of deallocating allocation blocks 6393 * associated with extent based attributes. 6394 * 6395 * Note: This function deletes *all* attributes for a given file/folder. 6396 * To delete a single attribute record using a key, use delete_attr_record(). 6397 * 6398 * On success, returns zero. On failure, returns non-zero. 6399 */ 6400static int DeleteAllAttrsByID(SGlobPtr GPtr, uint32_t id) 6401{ 6402 int retval; 6403 BTreeIterator iterator; 6404 FSBufferDescriptor btrec; 6405 HFSPlusAttrKey *attr_key; 6406 HFSPlusAttrRecord attr_record; 6407 UInt16 record_size; 6408 6409 /* Initialize the iterator, attribute key, and attribute record */ 6410 ClearMemory(&iterator, sizeof(BTreeIterator)); 6411 attr_key = (HFSPlusAttrKey *)&iterator.key; 6412 attr_key->keyLength = kHFSPlusAttrKeyMinimumLength; 6413 attr_key->fileID = id; 6414 6415 ClearMemory(&btrec, sizeof(FSBufferDescriptor)); 6416 btrec.bufferAddress = &attr_record; 6417 btrec.itemCount = 1; 6418 btrec.itemSize = sizeof(HFSPlusAttrRecord); 6419 6420 /* Search for attribute with NULL name which will place the 6421 * iterator just before the first record for given id. 6422 */ 6423 retval = BTSearchRecord(GPtr->calculatedAttributesFCB, &iterator, 6424 kInvalidMRUCacheKey, &btrec, &record_size, &iterator); 6425 if ((retval != 0) && (retval != btNotFound)) { 6426 goto out; 6427 } 6428 6429 retval = BTIterateRecord(GPtr->calculatedAttributesFCB, kBTreeNextRecord, 6430 &iterator, &btrec, &record_size); 6431 while ((retval == 0) && (attr_key->fileID == id)) { 6432 /* Delete attribute record and deallocate extents, if any */ 6433 retval = delete_attr_record(GPtr, attr_key, &attr_record); 6434 if (retval) { 6435 break; 6436 } 6437 6438 retval = BTIterateRecord(GPtr->calculatedAttributesFCB, 6439 kBTreeNextRecord, &iterator, &btrec, &record_size); 6440 } 6441 6442 if (retval == btNotFound) { 6443 retval = 0; 6444 } 6445 6446out: 6447 return retval; 6448} 6449 6450/* The function deletes an extented attribute record when the corresponding 6451 * record and key are provided. If the record is an extent-based attribute, 6452 * it also takes care to deallocate all allocation blocks associated with 6453 * the record. 6454 * 6455 * Note: This function does not delete all attribute records associated 6456 * with the file/folder ID in the attribute key. To delete all attributes 6457 * for given file/folder ID, use DeleteAllAttrsByID(). 6458 * 6459 * On success, returns zero. On failure, returns non-zero. 6460 */ 6461static int delete_attr_record(SGlobPtr GPtr, HFSPlusAttrKey *attr_key, HFSPlusAttrRecord *attr_record) 6462{ 6463 int retval; 6464 UInt32 num_blocks_freed; 6465 Boolean last_extent; 6466 6467 retval = DeleteBTreeRecord(GPtr->calculatedAttributesFCB, attr_key); 6468 if (retval == 0) { 6469 /* Set bits to write back attribute btree header and map */ 6470 GPtr->ABTStat |= S_BTH + S_BTM; 6471 6472 if (attr_record->recordType == kHFSPlusAttrForkData) { 6473 retval = ReleaseExtents(GPtr->calculatedVCB, 6474 attr_record->forkData.theFork.extents, 6475 &num_blocks_freed, &last_extent); 6476 } else if (attr_record->recordType == kHFSPlusAttrExtents) { 6477 retval = ReleaseExtents(GPtr->calculatedVCB, 6478 attr_record->overflowExtents.extents, 6479 &num_blocks_freed, &last_extent); 6480 } 6481 } 6482 6483 return retval; 6484} 6485 6486/*------------------------------------------------------------------------------ 6487 6488Routine: ZeroFillUnusedNodes 6489 6490Function: Write zeroes to all unused nodes of a given B-tree. 6491 6492Input: GPtr - pointer to scavenger global area 6493 fileRefNum - refnum of BTree file 6494 6495Output: ZeroFillUnusedNodes - function result: 6496 0 = no error 6497 n = error 6498------------------------------------------------------------------------------*/ 6499 6500static int ZeroFillUnusedNodes(SGlobPtr GPtr, short fileRefNum) 6501{ 6502 BTreeControlBlock *btcb = GetBTreeControlBlock(fileRefNum); 6503 unsigned char *bitmap = (unsigned char *) ((BTreeExtensionsRec*)btcb->refCon)->BTCBMPtr; 6504 unsigned char mask = 0x80; 6505 OSErr err; 6506 UInt32 nodeNum; 6507 BlockDescriptor node; 6508 6509 node.buffer = NULL; 6510 6511 for (nodeNum = 0; nodeNum < btcb->totalNodes; ++nodeNum) 6512 { 6513 if ((*bitmap & mask) == 0) 6514 { 6515 /* Read the raw node, without going through hfs_swap_BTNode. */ 6516 err = btcb->getBlockProc(btcb->fcbPtr, nodeNum, kGetBlock|kGetEmptyBlock, &node); 6517 if (err) 6518 { 6519 if (debug) plog("Couldn't read node #%u\n", nodeNum); 6520 return err; 6521 } 6522 6523 /* Fill the node with zeroes. */ 6524 bzero(node.buffer, node.blockSize); 6525 6526 /* Release and write the node without going through hfs_swap_BTNode. */ 6527 (void) btcb->releaseBlockProc(btcb->fcbPtr, &node, kReleaseBlock|kMarkBlockDirty); 6528 node.buffer = NULL; 6529 } 6530 6531 /* Move to the next bit in the bitmap. */ 6532 mask >>= 1; 6533 if (mask == 0) 6534 { 6535 mask = 0x80; 6536 ++bitmap; 6537 } 6538 } 6539 6540 return 0; 6541} /* end ZeroFillUnusedNodes */ 6542