2 * Copyright (c) 1999-2009 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 Contains: This file contains the Scavenger repair routines.
28 Written by: Bill Bruffey
30 Copyright: © 1986, 1990, 1992-1999 by Apple Computer, Inc., all rights reserved.
34 #include "Scavenger.h"
47 /* internal routine prototypes */
49 static int MRepair( SGlobPtr GPtr
);
50 void SetOffset (void *buffer
, UInt16 btNodeSize
, SInt16 recOffset
, SInt16 vecOffset
);
51 #define SetOffset(buffer,nodesize,offset,record) (*(SInt16 *) ((Byte *) (buffer) + (nodesize) + (-2 * (record))) = (offset))
52 static OSErr
UpdateBTreeHeader( SFCB
* fcbPtr
);
53 static OSErr
FixBTreeHeaderReservedFields( SGlobPtr GPtr
, short refNum
);
54 static OSErr
UpdBTM( SGlobPtr GPtr
, short refNum
);
55 static OSErr
UpdateVolumeBitMap( SGlobPtr GPtr
, Boolean preAllocateOverlappedExtents
);
56 static OSErr
DoMinorOrders( SGlobPtr GPtr
);
57 static OSErr
UpdVal( SGlobPtr GPtr
, RepairOrderPtr rP
);
58 static int DelFThd( SGlobPtr GPtr
, UInt32 fid
);
59 static OSErr
FixDirThread( SGlobPtr GPtr
, UInt32 did
);
60 static OSErr
FixOrphanedFiles ( SGlobPtr GPtr
);
61 static OSErr
RepairReservedBTreeFields ( SGlobPtr GPtr
);
62 static OSErr
GetCatalogRecord(SGlobPtr GPtr
, UInt32 fileID
, Boolean isHFSPlus
, CatalogKey
*catKey
, CatalogRecord
*catRecord
, UInt16
*recordSize
);
63 static OSErr
RepairAttributesCheckABT(SGlobPtr GPtr
, Boolean isHFSPlus
);
64 static OSErr
RepairAttributesCheckCBT(SGlobPtr GPtr
, Boolean isHFSPlus
);
65 static OSErr
RepairAttributes( SGlobPtr GPtr
);
66 static OSErr
FixFinderFlags( SGlobPtr GPtr
, RepairOrderPtr p
);
67 static OSErr
FixLinkCount( SGlobPtr GPtr
, RepairOrderPtr p
);
68 static OSErr
FixLinkChainNext( SGlobPtr GPtr
, RepairOrderPtr p
);
69 static OSErr
FixLinkChainPrev( SGlobPtr GPtr
, RepairOrderPtr p
);
70 static OSErr
FixBSDInfo( SGlobPtr GPtr
, RepairOrderPtr p
);
71 static OSErr
DeleteUnlinkedFile( SGlobPtr GPtr
, RepairOrderPtr p
);
72 static OSErr
FixOrphanedExtent( SGlobPtr GPtr
);
73 static OSErr
FixFileSize(SGlobPtr GPtr
, RepairOrderPtr p
);
74 static OSErr
VolumeObjectFixVHBorMDB( Boolean
* fixedIt
);
75 static OSErr
VolumeObjectRestoreWrapper( void );
76 static OSErr
FixBloatedThreadRecords( SGlob
*GPtr
);
77 static OSErr
FixMissingThreadRecords( SGlob
*GPtr
);
78 static OSErr
FixEmbededVolDescription( SGlobPtr GPtr
, RepairOrderPtr p
);
79 static OSErr
FixWrapperExtents( SGlobPtr GPtr
, RepairOrderPtr p
);
80 static OSErr
FixIllegalNames( SGlobPtr GPtr
, RepairOrderPtr roPtr
);
81 static HFSCatalogNodeID
GetObjectID( CatalogRecord
* theRecPtr
);
82 static OSErr
FixMissingDirectory( SGlob
*GPtr
, UInt32 theObjID
, UInt32 theParID
);
83 static OSErr
FixAttrSize(SGlobPtr GPtr
, RepairOrderPtr p
);
84 static OSErr
FixOrphanAttrRecord(SGlobPtr GPtr
);
85 static OSErr
FixBadExtent(SGlobPtr GPtr
, RepairOrderPtr p
);
86 static OSErr
FixHardLinkFinderInfo(SGlobPtr
, RepairOrderPtr
);
87 static OSErr
FixOrphanLink(SGlobPtr GPtr
, RepairOrderPtr p
);
88 static OSErr
FixOrphanInode(SGlobPtr GPtr
, RepairOrderPtr p
);
89 static OSErr
FixDirLinkOwnerFlags(SGlobPtr GPtr
, RepairOrderPtr p
);
90 static int DeleteCatalogRecordByID(SGlobPtr GPtr
, uint32_t id
, Boolean for_rename
);
91 static int MoveCatalogRecordByID(SGlobPtr GPtr
, uint32_t id
, uint32_t new_parentid
);
92 static int DeleteAllAttrsByID(SGlobPtr GPtr
, uint32_t id
);
93 static int delete_attr_record(SGlobPtr GPtr
, HFSPlusAttrKey
*attr_key
, HFSPlusAttrRecord
*attr_record
);
94 static int ZeroFillUnusedNodes(SGlobPtr GPtr
, short fileRefNum
);
96 /* Functions to fix overlapping extents */
97 static OSErr
FixOverlappingExtents(SGlobPtr GPtr
);
98 static int CompareExtentBlockCount(const void *first
, const void *second
);
99 static OSErr
MoveExtent(SGlobPtr GPtr
, ExtentInfo
*extentInfo
);
100 static OSErr
CreateCorruptFileSymlink(SGlobPtr GPtr
, UInt32 fileID
);
101 static OSErr
SearchExtentInAttributeBT(SGlobPtr GPtr
, ExtentInfo
*extentInfo
, HFSPlusAttrKey
*attrKey
, HFSPlusAttrRecord
*attrRecord
, UInt16
*recordSize
, UInt32
*foundExtentIndex
);
102 static OSErr
UpdateExtentInAttributeBT (SGlobPtr GPtr
, ExtentInfo
*extentInfo
, HFSPlusAttrKey
*attrKey
, HFSPlusAttrRecord
*attrRecord
, UInt16
*recordSize
, UInt32 foundInExtentIndex
);
103 static OSErr
SearchExtentInVH(SGlobPtr GPtr
, ExtentInfo
*extentInfo
, UInt32
*foundExtentIndex
, Boolean
*noMoreExtents
);
104 static OSErr
UpdateExtentInVH (SGlobPtr GPtr
, ExtentInfo
*extentInfo
, UInt32 foundExtentIndex
);
105 static OSErr
SearchExtentInCatalogBT(SGlobPtr GPtr
, ExtentInfo
*extentInfo
, CatalogKey
*catKey
, CatalogRecord
*catRecord
, UInt16
*recordSize
, UInt32
*foundExtentIndex
, Boolean
*noMoreExtents
);
106 static OSErr
UpdateExtentInCatalogBT (SGlobPtr GPtr
, ExtentInfo
*extentInfo
, CatalogKey
*catKey
, CatalogRecord
*catRecord
, UInt16
*recordSize
, UInt32 foundExtentIndex
);
107 static OSErr
SearchExtentInExtentBT(SGlobPtr GPtr
, ExtentInfo
*extentInfo
, HFSPlusExtentKey
*extentKey
, HFSPlusExtentRecord
*extentRecord
, UInt16
*recordSize
, UInt32
*foundExtentIndex
);
108 static OSErr
FindExtentInExtentRec (Boolean isHFSPlus
, UInt32 startBlock
, UInt32 blockCount
, const HFSPlusExtentRecord extentData
, UInt32
*foundExtentIndex
, Boolean
*noMoreExtents
);
110 /* Functions to copy disk blocks or data buffer to disk */
111 static OSErr
CopyDiskBlocks(SGlobPtr GPtr
, const UInt32 startAllocationBlock
, const UInt32 blockCount
, const UInt32 newStartAllocationBlock
);
112 static OSErr
WriteBufferToDisk(SGlobPtr GPtr
, UInt32 startBlock
, UInt32 blockCount
, u_char
*buffer
, int buflen
);
114 /* Functions to create file and directory by name */
115 static OSErr
CreateFileByName(SGlobPtr GPtr
, UInt32 parentID
, UInt16 fileType
, u_char
*fileName
, unsigned int filenameLen
, u_char
*data
, unsigned int dataLen
);
116 static UInt32
CreateDirByName(SGlob
*GPtr
, const u_char
*dirName
, const UInt32 parentID
);
118 static int BuildFolderRec( SGlob
*, u_int16_t theMode
, UInt32 theObjID
, Boolean isHFSPlus
, CatalogRecord
* theRecPtr
);
119 static int BuildThreadRec( CatalogKey
* theKeyPtr
, CatalogRecord
* theRecPtr
, Boolean isHFSPlus
, Boolean isDirectory
);
120 static int BuildFileRec(UInt16 fileType
, UInt16 fileMode
, UInt32 fileID
, Boolean isHFSPlus
, CatalogRecord
*catRecord
);
121 static void BuildAttributeKey(u_int32_t fileID
, u_int32_t startBlock
, unsigned char *attrName
, u_int16_t attrNameLen
, HFSPlusAttrKey
*key
);
124 OSErr
RepairVolume( SGlobPtr GPtr
)
128 SetDFAStage( kAboutToRepairStage
); // Notify callers repair is starting...
129 err
= CheckForStop( GPtr
); ReturnIfError( err
); // Permit the user to interrupt
134 SetDFAStage( kRepairStage
); // Stops GNE from being called, and changes behavior of MountCheck
136 err
= MRepair( GPtr
);
142 /*------------------------------------------------------------------------------
143 Routine: MRepair - (Minor Repair)
144 Function: Performs minor repair operations.
145 Input: GPtr - pointer to scavenger global area
146 Output: MRepair - function result:
147 ------------------------------------------------------------------------------*/
149 static int MRepair( SGlobPtr GPtr
)
152 SVCB
*calculatedVCB
= GPtr
->calculatedVCB
;
154 Boolean didRebuild
= false;
156 isHFSPlus
= VolumeObjectIsHFSPlus( );
158 if ( GPtr
->EBTStat
& S_RebuildBTree
)
160 fsckPrint(GPtr
->context
, hfsRebuildExtentBTree
);
161 err
= RebuildBTree( GPtr
, kHFSExtentsFileID
);
167 if ( GPtr
->CBTStat
& S_RebuildBTree
)
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
);
178 if ( GPtr
->ABTStat
& S_RebuildBTree
)
180 fsckPrint(GPtr
->context
, hfsRebuildAttrBTree
);
181 err
= RebuildBTree( GPtr
, kHFSAttributesFileID
);
188 return noErr
; // Need to restart the verification
191 * If there were unused nodes in the B-trees which were non-zero-filled,
192 * then zero fill them.
194 if (GPtr
->ABTStat
& S_UnusedNodesNotZero
)
196 err
= ZeroFillUnusedNodes(GPtr
, kCalculatedAttributesRefNum
);
199 if (GPtr
->EBTStat
& S_UnusedNodesNotZero
)
201 err
= ZeroFillUnusedNodes(GPtr
, kCalculatedExtentRefNum
);
204 if (GPtr
->CBTStat
& S_UnusedNodesNotZero
)
206 err
= ZeroFillUnusedNodes(GPtr
, kCalculatedCatalogRefNum
);
209 if ((calculatedVCB
->vcbAttributes
& kHFSUnusedNodeFixMask
) == 0)
211 calculatedVCB
->vcbAttributes
|= kHFSUnusedNodeFixMask
;
212 MarkVCBDirty(calculatedVCB
);
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.
220 * After it's done, DoMinorOrders() will take care of any requests that have been
223 if (GPtr
->CatStat
& S_FileHardLinkChain
) {
224 err
= RepairHardLinkChains(GPtr
, false);
228 err
= CheckForStop( GPtr
); ReturnIfError( err
); // Permit the user to interrupt
230 if (GPtr
->CatStat
& S_DirHardLinkChain
) {
231 err
= RepairHardLinkChains(GPtr
, true);
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
);
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
);
245 * Fix missing thread records
247 if (GPtr
->CatStat
& S_MissingThread
) {
248 err
= FixMissingThreadRecords(GPtr
);
251 GPtr
->CatStat
&= ~S_MissingThread
;
252 GPtr
->CBTStat
|= S_BTH
; /* leaf record count changed */
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
)
259 (void) FixBloatedThreadRecords( GPtr
);
260 GPtr
->VeryMinorErrorsStat
&= ~S_BloatedThreadRecordFound
;
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.
269 // Isolate and fix Overlapping Extents
271 err
= CheckForStop( GPtr
); ReturnIfError( err
); // Permit the user to interrupt
273 if ( (GPtr
->VIStat
& S_OverlappingExtents
) != 0 )
275 if (embedded
== 1 && debug
== 0)
278 err
= FixOverlappingExtents( GPtr
); // Isolate and fix Overlapping Extents
279 ReturnIfError( err
);
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
289 err
= CheckForStop( GPtr
); ReturnIfError( err
); // Permit the user to interrupt
291 if ( (GPtr
->CBTStat
& S_Orphan
) != 0 )
293 err
= FixOrphanedFiles ( GPtr
); // Orphaned file were found
294 ReturnIfError( err
);
295 GPtr
->CBTStat
|= S_BTH
; // leaf record count may change - 2913311
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.
306 if (GPtr
->MinorRepairsP
) {
307 err
= DoMinorOrders(GPtr
);
308 ReturnIfError( err
);
312 // FixOrphanedExtent records
314 if ( (GPtr
->EBTStat
& S_OrphanedExtent
) != 0 ) // Orphaned extents were found
316 err
= FixOrphanedExtent( GPtr
);
317 GPtr
->EBTStat
&= ~S_OrphanedExtent
;
318 // if ( err == errRebuildBtree )
319 // goto RebuildBtrees;
320 ReturnIfError( err
);
323 err
= CheckForStop( GPtr
); ReturnIfError( err
); // Permit the user to interrupt
326 // Update the extent BTree header and bit map
328 err
= CheckForStop( GPtr
); ReturnIfError( err
); // Permit the user to interrupt
330 if ( (GPtr
->EBTStat
& S_BTH
) || (GPtr
->EBTStat
& S_ReservedBTH
) )
332 err
= UpdateBTreeHeader( GPtr
->calculatedExtentsFCB
); // update extent file BTH
334 if ( (err
== noErr
) && (GPtr
->EBTStat
& S_ReservedBTH
) )
336 err
= FixBTreeHeaderReservedFields( GPtr
, kCalculatedExtentRefNum
);
339 ReturnIfError( err
);
343 if ( (GPtr
->EBTStat
& S_BTM
) != 0 )
345 err
= UpdBTM( GPtr
, kCalculatedExtentRefNum
); // update extent file BTM
346 ReturnIfError( err
);
350 // Update the catalog BTree header and bit map
353 err
= CheckForStop( GPtr
); ReturnIfError( err
); // Permit the user to interrupt
355 if ( (GPtr
->CBTStat
& S_BTH
) || (GPtr
->CBTStat
& S_ReservedBTH
) )
357 err
= UpdateBTreeHeader( GPtr
->calculatedCatalogFCB
); // update catalog BTH
359 if ( (err
== noErr
) && (GPtr
->CBTStat
& S_ReservedBTH
) )
361 err
= FixBTreeHeaderReservedFields( GPtr
, kCalculatedCatalogRefNum
);
364 ReturnIfError( err
);
367 if ( GPtr
->CBTStat
& S_BTM
)
369 err
= UpdBTM( GPtr
, kCalculatedCatalogRefNum
); // update catalog BTM
370 ReturnIfError( err
);
373 if ( (GPtr
->CBTStat
& S_ReservedNotZero
) != 0 )
375 err
= RepairReservedBTreeFields( GPtr
); // update catalog fields
376 ReturnIfError( err
);
379 // Repair orphaned/invalid attribute records
380 if ( (GPtr
->ABTStat
& S_AttrRec
) )
382 err
= FixOrphanAttrRecord( GPtr
);
383 ReturnIfError( err
);
386 // Repair inconsistency of attribute btree and corresponding bits in
388 if ( (GPtr
->ABTStat
& S_AttributeCount
) ||
389 (GPtr
->ABTStat
& S_SecurityCount
))
391 err
= RepairAttributes( GPtr
);
392 ReturnIfError( err
);
395 // Update the attribute BTree header and bit map
396 if ( (GPtr
->ABTStat
& S_BTH
) )
398 err
= UpdateBTreeHeader( GPtr
->calculatedAttributesFCB
); // update attribute BTH
399 ReturnIfError( err
);
402 if ( GPtr
->ABTStat
& S_BTM
)
404 err
= UpdBTM( GPtr
, kCalculatedAttributesRefNum
); // update attribute BTM
405 ReturnIfError( err
);
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
416 if ( (GPtr
->CBTStat
& S_Orphan
) != 0 )
418 err
= FixOrphanedFiles ( GPtr
);
419 ReturnIfError( err
);
423 // Update the volume bit map
425 // Note, moved volume bit map update to end after other repairs
426 // (except the MDB / VolumeHeader) have been completed
428 err
= CheckForStop( GPtr
); ReturnIfError( err
); // Permit the user to interrupt
430 if ( (GPtr
->VIStat
& S_VBM
) != 0 )
432 err
= UpdateVolumeBitMap( GPtr
, false ); // update VolumeBitMap
433 ReturnIfError( err
);
434 InvalidateCalculatedVolumeBitMap( GPtr
); // Invalidate our BitMap
438 // Fix missing Primary or Alternate VHB or MDB
441 err
= CheckForStop( GPtr
); ReturnIfError( err
); // Permit the user to interrupt
443 if ( (GPtr
->VIStat
& S_MDB
) != 0 ) // fix MDB / VolumeHeader
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.
451 GPtr
->VIStat
&= ~S_MDB
;
452 MarkVCBClean( calculatedVCB
);
456 err
= CheckForStop( GPtr
); ReturnIfError( err
); // Permit the user to interrupt
458 if ( (GPtr
->VIStat
& S_WMDB
) != 0 ) // fix wrapper MDB
460 err
= VolumeObjectRestoreWrapper();
461 ReturnIfError( err
);
465 // Update the MDB / VolumeHeader
467 // Note, moved MDB / VolumeHeader update to end
468 // after all other repairs have been completed.
471 err
= CheckForStop( GPtr
); ReturnIfError( err
); // Permit the user to interrupt
473 if ( (GPtr
->VIStat
& S_MDB
) != 0 || IsVCBDirty(calculatedVCB
) ) // update MDB / VolumeHeader
475 MarkVCBDirty(calculatedVCB
); // make sure its dirty
476 calculatedVCB
->vcbAttributes
|= kHFSVolumeUnmountedMask
;
477 err
= FlushAlternateVolumeControlBlock( calculatedVCB
, isHFSPlus
); // Writes real & alt blocks
478 ReturnIfError( err
);
481 err
= CheckForStop( GPtr
); ReturnIfError( err
); // Permit the user to interrupt
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
)
488 return( err
); // all done
497 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
498 // Routine: VolumeObjectFixVHBorMDB
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 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
505 static OSErr
VolumeObjectFixVHBorMDB( Boolean
* fixedItPtr
)
509 VolumeObjectPtr myVOPtr
;
510 BlockDescriptor myPrimary
;
511 BlockDescriptor myAlternate
;
513 myVOPtr
= GetVolumeObjectPtr( );
514 myPrimary
.buffer
= NULL
;
515 myAlternate
.buffer
= NULL
;
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
;
525 if ( (myVOPtr
->flags
& kVO_PriVHBOK
) != 0 &&
526 (myVOPtr
->flags
& kVO_AltVHBOK
) != 0 )
527 goto ExitThisRoutine
;
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
;
535 // invalidate if we have not marked the primary as OK
536 if ( VolumeObjectIsHFS( ) ) {
537 if ( (myVOPtr
->flags
& kVO_PriMDBOK
) == 0 )
540 else if ( (myVOPtr
->flags
& kVO_PriVHBOK
) == 0 ) {
544 err2
= GetVolumeObjectAlternateBlock( &myAlternate
);
545 if ( !(err2
== noErr
|| err2
== badMDBErr
|| err2
== noMacDskErr
) )
546 goto ExitThisRoutine
;
548 // invalidate if we have not marked the alternate as OK
549 if ( VolumeObjectIsHFS( ) ) {
550 if ( (myVOPtr
->flags
& kVO_AltMDBOK
) == 0 )
553 else if ( (myVOPtr
->flags
& kVO_AltVHBOK
) == 0 ) {
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
;
563 if ( VolumeObjectIsHFS( ) )
564 myVOPtr
->flags
|= kVO_AltMDBOK
;
566 myVOPtr
->flags
|= kVO_AltVHBOK
;
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
;
574 if ( VolumeObjectIsHFS( ) )
575 myVOPtr
->flags
|= kVO_PriMDBOK
;
577 myVOPtr
->flags
|= kVO_PriVHBOK
;
584 if ( myPrimary
.buffer
!= NULL
)
585 (void) ReleaseVolumeBlock( myVOPtr
->vcbPtr
, &myPrimary
, kReleaseBlock
);
586 if ( myAlternate
.buffer
!= NULL
)
587 (void) ReleaseVolumeBlock( myVOPtr
->vcbPtr
, &myAlternate
, kReleaseBlock
);
591 } /* VolumeObjectFixVHBorMDB */
594 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
595 // Routine: VolumeObjectRestoreWrapper
597 // Function: When the primary or alternate Master Directory Block is damaged
598 // or missing use the undamaged one to restore the other.
599 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
601 static OSErr
VolumeObjectRestoreWrapper( void )
605 VolumeObjectPtr myVOPtr
;
606 BlockDescriptor myPrimary
;
607 BlockDescriptor myAlternate
;
609 myVOPtr
= GetVolumeObjectPtr( );
610 myPrimary
.buffer
= NULL
;
611 myAlternate
.buffer
= NULL
;
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
;
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
;
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
;
640 if ( myPrimary
.buffer
!= NULL
)
641 (void) ReleaseVolumeBlock( myVOPtr
->vcbPtr
, &myPrimary
, kReleaseBlock
);
642 if ( myAlternate
.buffer
!= NULL
)
643 (void) ReleaseVolumeBlock( myVOPtr
->vcbPtr
, &myAlternate
, kReleaseBlock
);
647 } /* VolumeObjectRestoreWrapper */
650 /*------------------------------------------------------------------------------
651 Routine: UpdateBTreeHeader - (Update BTree Header)
653 Function: Replaces a BTH on disk with info from a scavenger BTCB.
655 Input: GPtr - pointer to scavenger global area
658 Output: UpdateBTreeHeader - function result:
661 ------------------------------------------------------------------------------*/
663 static OSErr
UpdateBTreeHeader( SFCB
* fcbPtr
)
667 M_BTreeHeaderDirty( ((BTreeControlBlockPtr
) fcbPtr
->fcbBtree
) );
668 err
= BTFlushPath( fcbPtr
);
672 } /* End UpdateBTreeHeader */
676 /*------------------------------------------------------------------------------
677 Routine: FixBTreeHeaderReservedFields
679 Function: Fix reserved fields in BTree Header
681 Input: GPtr - pointer to scavenger global area
686 ------------------------------------------------------------------------------*/
688 static OSErr
FixBTreeHeaderReservedFields( SGlobPtr GPtr
, short refNum
)
693 err
= GetBTreeHeader(GPtr
, ResolveFCB(refNum
), &header
);
694 ReturnIfError( err
);
696 if ( (header
.clumpSize
% GPtr
->calculatedVCB
->vcbBlockSize
) != 0 )
697 header
.clumpSize
= GPtr
->calculatedVCB
->vcbBlockSize
;
699 header
.reserved1
= 0;
700 header
.btreeType
= kHFSBTreeType
; // control file
702 * TBD - we'll need to repair an invlid keyCompareType field.
706 header
.keyCompareType
= kHFSBinaryCompare
;
708 ClearMemory( header
.reserved3
, sizeof(header
.reserved3
) );
716 /*------------------------------------------------------------------------------
718 Routine: UpdBTM - (Update BTree Map)
720 Function: Replaces a BTM on disk with a scavenger BTM.
722 Input: GPtr - pointer to scavenger global area
725 Output: UpdBTM - function result:
728 ------------------------------------------------------------------------------*/
730 static OSErr
UpdBTM( SGlobPtr GPtr
, short refNum
)
743 BTreeControlBlock
*calculatedBTCB
= GetBTreeControlBlock( refNum
);
746 mapSize
= ((BTreeExtensionsRec
*)calculatedBTCB
->refCon
)->BTCBMSize
;
749 // update the map records
755 sbtmP
= ((BTreeExtensionsRec
*)calculatedBTCB
->refCon
)->BTCBMPtr
;
759 GPtr
->TarBlock
= nodeNum
; // set target node number
761 err
= GetNode( calculatedBTCB
, nodeNum
, &node
);
762 ReturnIfError( err
); // could't get map node
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
;
770 CopyMemory( sbtmP
, btmP
, size
); // update it
772 err
= UpdateNode( calculatedBTCB
, &node
); // write it, and unlock buffer
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?
779 RcdError( GPtr
, E_ShortBTM
);
780 (void) ReleaseNode(calculatedBTCB
, &node
);
781 return( E_ShortBTM
);
788 } while ( mapSize
> 0 );
790 // clear the unused portion of the map record
791 for ( p
= btmP
+ size
; p
< btmP
+ recSize
; p
++ )
794 err
= UpdateNode( calculatedBTCB
, &node
); // Write it, and unlock buffer
797 return( noErr
); // All done
803 /*------------------------------------------------------------------------------
805 Routine: UpdateVolumeBitMap - (Update Volume Bit Map)
807 Function: Replaces the VBM on disk with the scavenger VBM.
809 Input: GPtr - pointer to scavenger global area
811 Output: UpdateVolumeBitMap - function result:
814 GPtr->VIStat - S_VBM flag set if VBM is damaged.
815 ------------------------------------------------------------------------------*/
817 static OSErr
UpdateVolumeBitMap( SGlobPtr GPtr
, Boolean preAllocateOverlappedExtents
)
819 GPtr
->TarID
= VBM_FNum
;
821 return ( CheckVolumeBitMap(GPtr
, true) );
825 Routine: FixBadLinkChainFirst - fix the first link in a hard link chain
827 Input: GPtr -- pointer to scavenger global data
828 p -- pointer to a minor repair order
830 Output: funciton result:
835 OSErr
FixBadLinkChainFirst(SGlobPtr GPtr
, RepairOrderPtr p
)
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;
847 ClearMemory(&iterator
, sizeof(iterator
));
848 retval
= GetCatalogRecordByID(GPtr
, (UInt32
)p
->parid
, true, (CatalogKey
*)&iterator
.key
, &rec
, &recsize
);
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.
856 GPtr
->minorRepairFalseSuccess
= true;
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
;
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
= (u_int32_t
)(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;
886 retval
= BTInsertRecord(GPtr
->calculatedAttributesFCB
, &iterator
, &bt_data
, recsize
);
887 if (retval
== btExists
) {
888 retval
= BTReplaceRecord(GPtr
->calculatedAttributesFCB
, &iterator
, &bt_data
, recsize
);
892 /* If there is error on inserting a new attribute record
893 * because attribute btree does not exists, print message.
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");
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
);
911 retval
= IntError(GPtr
, R_IntErr
);
920 Routine: FixHardLinkBadDate - fix the date of an indirect-node
922 Input: GPtr -- pointer to scavenger global data
923 p -- pointer to a minor repair order
925 Output: function result:
930 OSErr
FixHardLinkBadDate(SGlobPtr GPtr
, RepairOrderPtr p
)
938 retval
= GetCatalogRecordByID(GPtr
, (UInt32
)p
->parid
, true, &key
, &rec
, &recsize
);
941 if (rec
.recordType
!= kHFSPlusFileRecord
) {
942 retval
= IntError(GPtr
, R_IntErr
);
944 rec
.hfsPlusFile
.createDate
= (u_int32_t
)p
->correct
;
945 retval
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &rec
, recsize
, &hint
);
954 Routine: FixFileHardLinkFlag - clear the HardLinkChain flag in a file record
956 Input: GPtr -- pointer to scavenger global data
957 p -- pointer to minor repair order
959 Output: function result:
964 OSErr
FixFileHardLinkFlag(SGlobPtr GPtr
, RepairOrderPtr p
)
972 retval
= GetCatalogRecordByID(GPtr
, (UInt32
)p
->parid
, true, &key
, &rec
, &recsize
);
975 if (rec
.recordType
!= kHFSPlusFileRecord
) {
976 retval
= IntError(GPtr
, R_IntErr
);
978 rec
.hfsPlusFile
.flags
&= ~kHFSHasLinkChainMask
;
979 retval
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &rec
, recsize
, &hint
);
986 Routine: FixPrivDirBadPerms - fix the permissions of the directory hard-link private dir
988 Input: GPtr -- pointer to scavenger global data
989 p -- poitner to a minor repair order
991 Output: function result:
996 static OSErr
FixPrivDirBadPerms(SGlobPtr GPtr
, RepairOrderPtr p
)
1004 retval
= GetCatalogRecordByID(GPtr
, (UInt32
)p
->parid
, true, &key
, &rec
, &recsize
);
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.
1013 GPtr
->minorRepairFalseSuccess
= true;
1018 if (rec
.recordType
!= kHFSPlusFolderRecord
) {
1019 retval
= IntError(GPtr
, R_IntErr
);
1023 rec
.hfsPlusFolder
.bsdInfo
.ownerFlags
|= UF_IMMUTABLE
;
1024 rec
.hfsPlusFolder
.bsdInfo
.fileMode
|= S_ISVTX
;
1026 retval
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &rec
, recsize
, &hint
);
1032 /*------------------------------------------------------------------------------
1033 Routine: FixOrphanLink
1035 Function: Delete the orphan directory/file hard link as no corresponding
1036 directory/file inode was found.
1038 Input: GPtr - ptr to scavenger global data
1039 p - pointer to a minor repair order
1041 Output: function returns -
1042 0 - no error, success
1044 -------------------------------------------------------------------------------*/
1045 static OSErr
FixOrphanLink(SGlobPtr GPtr
, RepairOrderPtr p
)
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.
1056 GPtr
->minorRepairFalseSuccess
= true;
1063 /*------------------------------------------------------------------------------
1064 Routine: FixOrphanInode
1066 Function: Repair orphan file/directory inode, i.e. no hard links point
1067 to this file/directory inode by moving them to lost+found.
1069 Input: GPtr - ptr to scavenger global data
1070 p - pointer to a minor repair order
1072 Output: function returns -
1073 0 - no error, success
1075 -------------------------------------------------------------------------------*/
1076 static OSErr
FixOrphanInode(SGlobPtr GPtr
, RepairOrderPtr p
)
1079 uint32_t lost_found_id
;
1080 static int msg_display
= 0;
1082 if (embedded
== 1 && debug
== 0) {
1087 /* Make sure that lost+found exists */
1088 lost_found_id
= CreateDirByName(GPtr
, (u_char
*)"lost+found",
1090 if (lost_found_id
== 0) {
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.
1102 GPtr
->minorRepairFalseSuccess
= true;
1105 if (msg_display
== 0) {
1106 fsckPrint(GPtr
->context
, fsckLostFoundDirectory
, "lost+found");
1114 /*------------------------------------------------------------------------------
1115 Routine: FixDirLinkOwnerFlags
1117 Function: Fix the owner flags for directory hard link.
1119 Input: GPtr - ptr to scavenger global data
1120 p - pointer to a minor repair order
1122 Output: function returns -
1123 0 - no error, success
1125 -------------------------------------------------------------------------------*/
1126 static OSErr
FixDirLinkOwnerFlags(SGlobPtr GPtr
, RepairOrderPtr p
)
1134 retval
= GetCatalogRecordByID(GPtr
, p
->parid
, true, &key
, &rec
, &recsize
);
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.
1142 GPtr
->minorRepairFalseSuccess
= true;
1148 rec
.hfsPlusFile
.bsdInfo
.ownerFlags
= p
->correct
;
1150 retval
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
, kNoHint
,
1151 &rec
, recsize
, &hint
);
1157 /*------------------------------------------------------------------------------
1158 Routine: FixBadFlags
1160 Function: Update the flags field of a directory or file node
1162 Input: GPtr -- ptr to scavenger global data
1163 p -- pointer to a minor repair order
1165 Output: function result:
1169 static OSErr
FixBadFlags(SGlobPtr GPtr
, RepairOrderPtr p
)
1177 retval
= GetCatalogRecordByID(GPtr
, p
->parid
, true, &key
, &rec
, &recsize
);
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.
1185 GPtr
->minorRepairFalseSuccess
= true;
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
);
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
);
1200 rec
.hfsPlusFolder
.flags
= p
->correct
;
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
);
1205 rec
.hfsPlusFile
.flags
= p
->correct
;
1208 retval
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
, kNoHint
,
1209 &rec
, recsize
, &hint
);
1217 /*------------------------------------------------------------------------------
1218 Routine: UpdFolderCount
1220 Function: Update the folder count in an HFS+ folder record
1222 Input: GPtr -- ptr to scavenger global data
1223 p -- pointer to minor repair order
1225 Output: function result:
1229 ------------------------------------------------------------------------------*/
1230 OSErr
UpdFolderCount( SGlobPtr GPtr
, RepairOrderPtr p
)
1233 CatalogRecord record
;
1234 CatalogKey key
, foundKey
;
1238 #define DPRINT(where, fmt, ...) \
1239 if (fsckGetVerbosity(GPtr->context) >= kDebugLog) \
1240 fplog(where, fmt, ## __VA_ARGS__);
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.
1247 BuildCatalogKey( p
->parid
, NULL
, true, &key
);
1248 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
,
1249 &foundKey
, &record
, &recSize
, &hint
);
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.
1257 GPtr
->minorRepairFalseSuccess
= true;
1260 DPRINT(stderr
, "\tUpdFolderCount: first SearchBTreeRecord failed, parid = %u, result = %d\n", p
->parid
, result
);
1261 return IntError(GPtr
, R_IntErr
);
1265 if (record
.recordType
!= kHFSPlusFolderThreadRecord
) {
1266 GPtr
->CBTStat
|= S_Orphan
;
1267 GPtr
->minorRepairFalseSuccess
= true;
1271 BuildCatalogKey( record
.hfsPlusThread
.parentID
, (const CatalogName
*)&record
.hfsPlusThread
.nodeName
, true, &key
);
1272 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
,
1273 &foundKey
, &record
, &recSize
, &hint
);
1276 DPRINT(stderr
, "UpdFolderCount: second SearchBTreeRecord failed (thread.parentID = %u, result = %d), just returning without complaint\n", record
.hfsPlusThread
.parentID
, result
);
1280 if (record
.recordType
!= kHFSPlusFolderRecord
) {
1281 DPRINT(stderr
, "UpdFolderCount: actual record type (%d) != FolderRecord\n", record
.recordType
);
1282 return IntError(GPtr
, R_IntErr
);
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.
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
);
1295 if (record
.hfsPlusFolder
.folderCount
== p
->correct
) {
1296 /* We've gotten it already, no need to do anything */
1301 record
.hfsPlusFolder
.folderCount
= (u_int32_t
)p
->correct
;
1302 result
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
,
1303 &record
, recSize
, &hint
);
1305 DPRINT(stderr
, "UpdFolderCount: ReplaceBTreeRecord failed (%d)\n", result
);
1306 return IntError( GPtr
, R_IntErr
);
1312 /*------------------------------------------------------------------------------
1313 Routine: UpdHasFolderCount
1315 Function: Update the HasFolderCount flag on an HFS+ folder's flags
1317 Input: GPtr -- ptr to scavenger global data
1318 p -- pointer to minor repair order
1320 Output: function result:
1324 ------------------------------------------------------------------------------*/
1325 OSErr
UpdHasFolderCount( SGlobPtr GPtr
, RepairOrderPtr p
)
1328 CatalogRecord record
;
1329 CatalogKey key
, foundKey
;
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.
1338 BuildCatalogKey( p
->parid
, NULL
, true, &key
);
1339 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
,
1340 &foundKey
, &record
, &recSize
, &hint
);
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.
1349 GPtr
->minorRepairFalseSuccess
= true;
1352 return IntError(GPtr
, R_IntErr
);
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;
1363 BuildCatalogKey( record
.hfsPlusThread
.parentID
, (const CatalogName
*)&record
.hfsPlusThread
.nodeName
, true, &key
);
1364 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
,
1365 &foundKey
, &record
, &recSize
, &hint
);
1368 return IntError(GPtr
, R_IntErr
);
1371 if (record
.recordType
!= kHFSPlusFolderRecord
) {
1372 return IntError(GPtr
, R_IntErr
);
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
);
1381 return IntError( GPtr
, R_IntErr
);
1388 /*------------------------------------------------------------------------------
1390 Routine: DoMinorOrders
1392 Function: Execute minor repair orders.
1394 Input: GPtr - ptr to scavenger global data
1396 Outut: function result:
1399 ------------------------------------------------------------------------------*/
1401 static OSErr
DoMinorOrders( SGlobPtr GPtr
) // the globals
1405 OSErr err
= noErr
; // initialize to "no error"
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.
1412 cur
= GPtr
->MinorRepairsP
;
1413 GPtr
->MinorRepairsP
= NULL
;
1415 while( (p
= cur
) && (err
== noErr
) ) // loop over each repair order
1419 GPtr
->minorRepairFalseSuccess
= false;
1421 switch( p
->type
) // branch on repair type
1423 case E_FldCount
: // folderCount needs to be updated
1424 err
= UpdFolderCount( GPtr
, p
);
1427 case E_HsFldCount
: // HasFolderCount bit needs to be set
1428 err
= UpdHasFolderCount( GPtr
, p
);
1431 case E_RtDirCnt
: // the valence errors
1432 case E_RtFilCnt
: // (of which there are several)
1436 err
= UpdVal( GPtr
, p
); // handle a valence error
1439 case E_LockedDirName
:
1440 err
= FixFinderFlags( GPtr
, p
);
1443 case E_UnlinkedFile
:
1444 err
= DeleteUnlinkedFile( GPtr
, p
);
1447 case E_FileLinkCountError
:
1448 case E_InvalidLinkCount
:
1449 err
= FixLinkCount( GPtr
, p
);
1452 case E_InvalidLinkChainPrev
:
1453 err
= FixLinkChainPrev( GPtr
, p
);
1456 case E_InvalidLinkChainNext
:
1457 err
= FixLinkChainNext( GPtr
, p
);
1460 case E_DirHardLinkFinderInfo
:
1461 case E_FileHardLinkFinderInfo
:
1462 err
= FixHardLinkFinderInfo( GPtr
, p
);
1465 case E_InvalidPermissions
:
1466 err
= FixBSDInfo( GPtr
, p
);
1469 case E_NoFile
: // dangling file thread
1470 err
= DelFThd( GPtr
, p
->parid
); // delete the dangling thread
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
;
1479 //¥¥ Same with E_NoDir
1480 case E_NoDir
: // missing directory record
1481 err
= FixDirThread( GPtr
, p
->parid
); // fix the directory thread record
1484 case E_InvalidMDBdrAlBlSt
:
1485 err
= FixEmbededVolDescription( GPtr
, p
);
1488 case E_InvalidWrapperExtents
:
1489 err
= FixWrapperExtents(GPtr
, p
);
1493 err
= FixIllegalNames( GPtr
, p
);
1498 err
= FixFileSize(GPtr
, p
);
1503 err
= FixAttrSize(GPtr
, p
);
1507 err
= FixBadExtent(GPtr
, p
);
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
);
1518 case E_BadPermPrivDir
:
1519 err
= FixPrivDirBadPerms(GPtr
, p
);
1522 case E_InvalidLinkChainFirst
:
1523 err
= FixBadLinkChainFirst(GPtr
, p
);
1526 case E_OrphanFileLink
:
1527 case E_OrphanDirLink
:
1528 err
= FixOrphanLink(GPtr
, p
);
1531 case E_OrphanFileInode
:
1532 case E_OrphanDirInode
:
1533 err
= FixOrphanInode(GPtr
, p
);
1536 case E_DirHardLinkOwnerFlags
:
1537 err
= FixDirLinkOwnerFlags(GPtr
, p
);
1540 case E_BadHardLinkDate
:
1541 err
= FixHardLinkBadDate(GPtr
, p
);
1544 case E_LinkChainNonLink
:
1545 err
= FixFileHardLinkFlag(GPtr
, p
);
1548 default: // unknown repair type
1549 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1550 plog ("\tUnknown repair order found (type = %d)\n", p
->type
);
1552 err
= IntError( GPtr
, R_IntErr
); // treat as an internal error
1556 if ((err
!= 0) && (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)) {
1557 plog ("\tDoMinorRepair: Repair for type=%d failed (err=%d).\n", p
->type
, err
);
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.
1574 if (GPtr
->minorRepairFalseSuccess
== true) {
1575 p
->link
= GPtr
->MinorRepairsP
;
1576 GPtr
->MinorRepairsP
= p
;
1578 DisposeMemory( p
); // free the node
1582 return( err
); // return error code to our caller
1587 /*------------------------------------------------------------------------------
1589 Routine: DelFThd - (delete file thread)
1591 Function: 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.
1595 Input: GPtr - global data
1596 fid - the thread record's key's parent-ID
1598 Output: 0 - no error
1600 Modification History:
1601 29Oct90 KST CBTDelete was using "k" as key which points to cache buffer.
1602 -------------------------------------------------------------------------------*/
1604 static int DelFThd( SGlobPtr GPtr
, UInt32 fid
) // the file ID
1606 CatalogRecord record
;
1607 CatalogKey foundKey
;
1609 UInt32 hint
; // as returned by CBTSearch
1610 OSErr result
; // status return
1613 ExtentRecord zeroExtents
;
1615 isHFSPlus
= VolumeObjectIsHFSPlus( );
1617 BuildCatalogKey( fid
, (const CatalogName
*) nil
, isHFSPlus
, &key
);
1618 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &foundKey
, &record
, &recSize
, &hint
);
1620 if ( result
) return ( IntError( GPtr
, result
) );
1622 if ( (record
.recordType
!= kHFSFileThreadRecord
) && (record
.recordType
!= kHFSPlusFileThreadRecord
) ) // quit if not a file thread
1623 return ( IntError( GPtr
, R_IntErr
) );
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
) );
1630 result
= DeleteBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
);
1631 if ( result
) return ( IntError( GPtr
, result
) );
1633 // After deleting a record, we'll need to write back the BT header and map,
1634 // to reflect the updated record count etc.
1636 GPtr
->CBTStat
|= S_BTH
+ S_BTM
; // set flags to write back hdr and map
1638 return( noErr
); // successful return
1642 /*------------------------------------------------------------------------------
1644 Routine: FixDirThread - (fix directory thread record's parent ID info)
1646 Function: 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.
1652 Input: GPtr - global data
1653 did - the thread record's key's parent-ID
1655 Output: 0 - no error
1657 -------------------------------------------------------------------------------*/
1659 static OSErr
FixDirThread( SGlobPtr GPtr
, UInt32 did
) // the dir ID
1662 UInt32 hint
; // as returned by CBTSearch
1663 OSErr result
; // status return
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
;
1676 NodeDescPtr nodeDescP
;
1677 UInt32 newParDirID
= 0; // the parent ID where the dir record is really located
1679 BTreeControlBlock
*calculatedBTCB
= GetBTreeControlBlock( kCalculatedCatalogRefNum
);
1681 isHFSPlus
= VolumeObjectIsHFSPlus( );
1683 BuildCatalogKey( did
, (const CatalogName
*) nil
, isHFSPlus
, &key
);
1684 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &foundKey
, &record
, &recSize
, &hint
);
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
) );
1691 curLeafNode
= calculatedBTCB
->freeNodes
;
1693 while ( curLeafNode
)
1695 result
= GetNode( calculatedBTCB
, curLeafNode
, &node
);
1696 if ( result
!= noErr
) return( IntError( GPtr
, result
) );
1698 nodeDescP
= node
.buffer
;
1700 // loop on number of records in node
1701 for ( index
= 0 ; index
< nodeDescP
->numRecords
; index
++ )
1703 GetRecordByIndex( calculatedBTCB
, (NodeDescPtr
)nodeDescP
, index
, (BTreeKey
**)&keyP
, &dataPtr
, &recSize
);
1705 recordType
= ((CatalogRecord
*)dataPtr
)->recordType
;
1706 folderID
= recordType
== kHFSPlusFolderRecord
? ((HFSPlusCatalogFolder
*)dataPtr
)->folderID
: ((HFSCatalogFolder
*)dataPtr
)->folderID
;
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
) )
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
);
1718 if ( newParDirID
) {
1719 (void) ReleaseNode(calculatedBTCB
, &node
);
1723 curLeafNode
= nodeDescP
->fLink
; // sibling of this leaf node
1725 (void) ReleaseNode(calculatedBTCB
, &node
);
1728 if ( newParDirID
== 0 )
1730 return ( IntError( GPtr
, R_IntErr
) ); // ¥¥ Try fixing by creating a new directory record?
1734 (void) SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &foundKey
, &record
, &recSize
, &hint
);
1738 HFSPlusCatalogThread
*largeCatalogThreadP
= (HFSPlusCatalogThread
*) &record
;
1740 largeCatalogThreadP
->parentID
= newParDirID
;
1741 CopyCatalogName( &catalogName
, (CatalogName
*) &largeCatalogThreadP
->nodeName
, isHFSPlus
);
1745 HFSCatalogThread
*smallCatalogThreadP
= (HFSCatalogThread
*) &record
;
1747 smallCatalogThreadP
->parentID
= newParDirID
;
1748 CopyCatalogName( &catalogName
, (CatalogName
*)&smallCatalogThreadP
->nodeName
, isHFSPlus
);
1751 result
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recSize
, &hint
);
1754 return( noErr
); // successful return
1758 /*------------------------------------------------------------------------------
1760 Routine: UpdVal - (Update Valence)
1762 Function: Replaces out of date valences with correct vals computed during scavenge.
1764 Input: GPtr - pointer to scavenger global area
1765 p - pointer to the repair order
1767 Output: UpdVal - function result:
1770 ------------------------------------------------------------------------------*/
1772 static OSErr
UpdVal( SGlobPtr GPtr
, RepairOrderPtr p
) // the valence repair order
1774 OSErr result
; // status return
1776 UInt32 hint
; // as returned by CBTSearch
1778 CatalogRecord record
;
1779 CatalogKey foundKey
;
1781 SVCB
*calculatedVCB
= GPtr
->calculatedVCB
;
1783 isHFSPlus
= VolumeObjectIsHFSPlus( );
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
;
1795 if ( (UInt16
)p
->incorrect
!= calculatedVCB
->vcbNmFls
)
1796 return ( IntError( GPtr
, R_IntErr
) );
1797 calculatedVCB
->vcbNmFls
= (UInt16
)p
->correct
;
1798 GPtr
->VIStat
|= S_MDB
;
1802 if ( (UInt32
)p
->incorrect
!= calculatedVCB
->vcbFolderCount
)
1803 return ( IntError( GPtr
, R_IntErr
) );
1804 calculatedVCB
->vcbFolderCount
= (UInt32
)p
->correct
;
1805 GPtr
->VIStat
|= S_MDB
;
1809 if ( (UInt32
)p
->incorrect
!= calculatedVCB
->vcbFileCount
)
1810 return ( IntError( GPtr
, R_IntErr
) );
1811 calculatedVCB
->vcbFileCount
= (UInt32
)p
->correct
;
1812 GPtr
->VIStat
|= S_MDB
;
1816 BuildCatalogKey( p
->parid
, (CatalogName
*)&p
->name
, isHFSPlus
, &key
);
1817 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
,
1818 &foundKey
, &record
, &recSize
, &hint
);
1820 return ( IntError( GPtr
, result
) );
1822 if ( record
.recordType
== kHFSPlusFolderRecord
)
1824 if ( (UInt32
)p
->incorrect
!= record
.hfsPlusFolder
.valence
)
1825 return ( IntError( GPtr
, R_IntErr
) );
1826 record
.hfsPlusFolder
.valence
= (UInt32
)p
->correct
;
1830 if ( (UInt16
)p
->incorrect
!= record
.hfsFolder
.valence
)
1831 return ( IntError( GPtr
, R_IntErr
) );
1832 record
.hfsFolder
.valence
= (UInt16
)p
->correct
;
1835 result
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, hint
,\
1836 &record
, recSize
, &hint
);
1838 return ( IntError( GPtr
, result
) );
1842 return( noErr
); // no error
1845 /*------------------------------------------------------------------------------
1847 Routine: FixFinderFlags
1849 Function: Changes some of the Finder flag bits for directories.
1851 Input: GPtr - pointer to scavenger global area
1852 p - pointer to the repair order
1854 Output: FixFinderFlags - function result:
1857 ------------------------------------------------------------------------------*/
1859 static OSErr
FixFinderFlags( SGlobPtr GPtr
, RepairOrderPtr p
) // the repair order
1861 CatalogRecord record
;
1862 CatalogKey foundKey
;
1864 UInt32 hint
; // as returned by CBTSearch
1865 OSErr result
; // status return
1869 isHFSPlus
= VolumeObjectIsHFSPlus( );
1871 BuildCatalogKey( p
->parid
, (CatalogName
*)&p
->name
, isHFSPlus
, &key
);
1873 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &foundKey
, &record
, &recSize
, &hint
);
1875 return ( IntError( GPtr
, result
) );
1877 if ( record
.recordType
== kHFSPlusFolderRecord
)
1879 HFSPlusCatalogFolder
*largeCatalogFolderP
= (HFSPlusCatalogFolder
*) &record
;
1880 if ( (UInt16
) p
->incorrect
!= largeCatalogFolderP
->userInfo
.frFlags
)
1882 // Another repair order may have affected the flags
1883 if ( p
->correct
< p
->incorrect
)
1884 largeCatalogFolderP
->userInfo
.frFlags
&= ~((UInt16
)p
->maskBit
);
1886 largeCatalogFolderP
->userInfo
.frFlags
|= (UInt16
)p
->maskBit
;
1890 largeCatalogFolderP
->userInfo
.frFlags
= (UInt16
)p
->correct
;
1892 // largeCatalogFolderP->contentModDate = timeStamp;
1896 HFSCatalogFolder
*smallCatalogFolderP
= (HFSCatalogFolder
*) &record
;
1897 if ( p
->incorrect
!= smallCatalogFolderP
->userInfo
.frFlags
) // do we know what we're doing?
1899 // Another repair order may have affected the flags
1900 if ( p
->correct
< p
->incorrect
)
1901 smallCatalogFolderP
->userInfo
.frFlags
&= ~((UInt16
)p
->maskBit
);
1903 smallCatalogFolderP
->userInfo
.frFlags
|= (UInt16
)p
->maskBit
;
1907 smallCatalogFolderP
->userInfo
.frFlags
= (UInt16
)p
->correct
;
1910 // smallCatalogFolderP->modifyDate = timeStamp; // also update the modify date! -DJB
1913 result
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recSize
, &hint
); // write the node back to the file
1915 return( IntError( GPtr
, result
) );
1917 return( noErr
); // no error
1920 /*------------------------------------------------------------------------------
1921 FixHardLinkFinderInfo: Set the Finder Info contents to be correct for the type
1922 of hard link (directory or file).
1924 ------------------------------------------------------------------------------*/
1925 static OSErr
FixHardLinkFinderInfo(SGlobPtr GPtr
, RepairOrderPtr p
)
1933 retval
= GetCatalogRecordByID(GPtr
, (UInt32
)p
->parid
, true, &key
, &rec
, &recsize
);
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.
1941 GPtr
->minorRepairFalseSuccess
= true;
1947 if (rec
.recordType
!= kHFSPlusFileRecord
) {
1948 retval
= IntError(GPtr
, R_IntErr
);
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
;
1960 retval
= IntError(GPtr
, R_IntErr
);
1964 retval
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &rec
, recsize
, &hint
);
1970 /*------------------------------------------------------------------------------
1971 FixLinkChainNext: Adjust the link chain in a hard link
1972 (HFS Plus volumes only)
1973 ------------------------------------------------------------------------------*/
1975 FixLinkChainPrev(SGlobPtr GPtr
, RepairOrderPtr p
)
1979 CatalogKey key
, foundKey
;
1985 isHFSPlus
= VolumeObjectIsHFSPlus( );
1988 fcb
= GPtr
->calculatedCatalogFCB
;
1990 BuildCatalogKey( p
->parid
, NULL
, true, &key
);
1991 result
= SearchBTreeRecord( fcb
, &key
, kNoHint
, &foundKey
, &rec
, &recSize
, &hint
);
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.
2000 GPtr
->minorRepairFalseSuccess
= true;
2003 return IntError(GPtr
, R_IntErr
);
2007 if (rec
.recordType
!= kHFSPlusFileThreadRecord
) {
2008 return IntError(GPtr
, R_IntErr
);
2011 BuildCatalogKey( rec
.hfsPlusThread
.parentID
, (const CatalogName
*)&rec
.hfsPlusThread
.nodeName
, true, &key
);
2012 result
= SearchBTreeRecord( fcb
, &key
, kNoHint
, &foundKey
, &rec
, &recSize
, &hint
);
2015 return IntError(GPtr
, R_IntErr
);
2018 if (rec
.recordType
!= kHFSPlusFileRecord
) {
2019 return IntError(GPtr
, R_IntErr
);
2022 if ((UInt32
)p
->incorrect
!= rec
.hfsPlusFile
.hl_prevLinkID
) {
2023 return IntError(GPtr
, R_IntErr
);
2026 rec
.hfsPlusFile
.hl_prevLinkID
= (UInt32
)p
->correct
;
2027 result
= ReplaceBTreeRecord(fcb
, &foundKey
, hint
, &rec
, recSize
, &hint
);
2029 return IntError(GPtr
, R_IntErr
);
2035 /*------------------------------------------------------------------------------
2036 FixLinkChainNext: Adjust the link chain in a hard link
2037 (HFS Plus volumes only)
2038 ------------------------------------------------------------------------------*/
2040 FixLinkChainNext(SGlobPtr GPtr
, RepairOrderPtr p
)
2044 CatalogKey key
, foundKey
;
2050 isHFSPlus
= VolumeObjectIsHFSPlus( );
2053 fcb
= GPtr
->calculatedCatalogFCB
;
2055 BuildCatalogKey( p
->parid
, NULL
, true, &key
);
2056 result
= SearchBTreeRecord( fcb
, &key
, kNoHint
, &foundKey
, &rec
, &recSize
, &hint
);
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.
2065 GPtr
->minorRepairFalseSuccess
= true;
2068 return IntError(GPtr
, R_IntErr
);
2072 if (rec
.recordType
!= kHFSPlusFileThreadRecord
) {
2073 return IntError(GPtr
, R_IntErr
);
2076 BuildCatalogKey( rec
.hfsPlusThread
.parentID
, (const CatalogName
*)&rec
.hfsPlusThread
.nodeName
, true, &key
);
2077 result
= SearchBTreeRecord( fcb
, &key
, kNoHint
, &foundKey
, &rec
, &recSize
, &hint
);
2080 return IntError(GPtr
, R_IntErr
);
2083 if (rec
.recordType
!= kHFSPlusFileRecord
) {
2084 return IntError(GPtr
, R_IntErr
);
2087 if ((UInt32
)p
->incorrect
!= rec
.hfsPlusFile
.hl_nextLinkID
) {
2088 return IntError(GPtr
, R_IntErr
);
2091 rec
.hfsPlusFile
.hl_nextLinkID
= (UInt32
)p
->correct
;
2092 result
= ReplaceBTreeRecord(fcb
, &foundKey
, hint
, &rec
, recSize
, &hint
);
2094 return IntError(GPtr
, R_IntErr
);
2100 /*------------------------------------------------------------------------------
2101 FixLinkCount: Adjust a data node link count (BSD hard link)
2102 (HFS Plus volumes only)
2103 ------------------------------------------------------------------------------*/
2105 FixLinkCount(SGlobPtr GPtr
, RepairOrderPtr p
)
2114 int lc
; // linkcount
2116 isHFSPlus
= VolumeObjectIsHFSPlus( );
2120 result
= GetCatalogRecordByID(GPtr
, p
->parid
, isHFSPlus
, &key
, &rec
, &recSize
);
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.
2128 GPtr
->minorRepairFalseSuccess
= true;
2134 if (rec
.recordType
!= kHFSPlusFileRecord
&& rec
.recordType
!= kHFSPlusFolderRecord
)
2137 isdir
= (rec
.recordType
== kHFSPlusFolderRecord
);
2139 lc
= (isdir
? rec
.hfsPlusFolder
.bsdInfo
.special
.linkCount
: rec
.hfsPlusFile
.bsdInfo
.special
.linkCount
);
2140 if ((UInt32
)p
->correct
!= lc
) {
2142 rec
.hfsPlusFolder
.bsdInfo
.special
.linkCount
= (UInt32
)p
->correct
;
2144 rec
.hfsPlusFile
.bsdInfo
.special
.linkCount
= (UInt32
)p
->correct
;
2146 result
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
,
2147 kNoHint
, &rec
, recSize
, &hint
);
2149 return (IntError(GPtr
, result
));
2156 /*------------------------------------------------------------------------------
2157 FixIllegalNames: 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 ------------------------------------------------------------------------------*/
2167 FixIllegalNames( SGlobPtr GPtr
, RepairOrderPtr roPtr
)
2171 Boolean isDirectory
;
2174 CatalogName
* oldNamePtr
;
2175 CatalogName
* newNamePtr
;
2177 CatalogRecord record
;
2181 isHFSPlus
= VolumeObjectIsHFSPlus( );
2182 fcbPtr
= GPtr
->calculatedCatalogFCB
;
2184 oldNamePtr
= (CatalogName
*) &roPtr
->name
;
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
;
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
;
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 );
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
) {
2222 result
= DeleteBTreeRecord( fcbPtr
, &key
);
2223 if ( result
!= noErr
) {
2227 // insert record back into the catalog using the new name
2228 result
= InsertBTreeRecord( fcbPtr
, &newKey
, &record
, recSize
, &hint
);
2229 if ( result
!= noErr
) {
2233 isDirectory
= false;
2234 switch( record
.recordType
)
2236 case kHFSFolderRecord
:
2237 case kHFSPlusFolderRecord
:
2238 isDirectory
= true; break;
2241 // now we need to remove the old thread record and create a new one with
2243 BuildCatalogKey( GetObjectID( &record
), NULL
, isHFSPlus
, &key
);
2244 result
= SearchBTreeRecord( fcbPtr
, &key
, kNoHint
, NULL
, &record
, &recSize
, &hint
);
2245 if ( result
!= noErr
) {
2249 result
= DeleteBTreeRecord( fcbPtr
, &key
);
2250 if ( result
!= noErr
) {
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
) {
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
2269 } /* FixIllegalNames */
2272 /*------------------------------------------------------------------------------
2273 FixBSDInfo: Reset or repair BSD info
2274 (HFS Plus volumes only)
2275 ------------------------------------------------------------------------------*/
2277 FixBSDInfo(SGlobPtr GPtr
, RepairOrderPtr p
)
2281 FSBufferDescriptor btRecord
;
2282 BTreeIterator btIterator
;
2287 unsigned char filename
[256];
2289 isHFSPlus
= VolumeObjectIsHFSPlus( );
2292 fcb
= GPtr
->calculatedCatalogFCB
;
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
);
2302 result
= BTSearchRecord(fcb
, &btIterator
, kInvalidMRUCacheKey
,
2303 &btRecord
, &recSize
, &btIterator
);
2305 return (IntError(GPtr
, result
));
2307 if (rec
.recordType
!= kHFSPlusFileRecord
&&
2308 rec
.recordType
!= kHFSPlusFolderRecord
)
2311 utf_encodestr(((HFSUniStr255
*)&p
->name
)->unicode
,
2312 ((HFSUniStr255
*)&p
->name
)->length
<< 1, filename
, &namelen
, sizeof(filename
));
2313 filename
[namelen
] = '\0';
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
);
2321 rec
.hfsPlusFile
.bsdInfo
.fileMode
= (UInt16
)p
->correct
;
2322 result
= BTReplaceRecord(fcb
, &btIterator
, &btRecord
, recSize
);
2326 return (IntError(GPtr
, result
));
2332 /*------------------------------------------------------------------------------
2333 DeleteUnlinkedFile: Delete orphaned data node (BSD unlinked file)
2334 Also used to delete empty "HFS+ Private Data" directories
2335 (HFS Plus volumes only)
2336 ------------------------------------------------------------------------------*/
2338 DeleteUnlinkedFile(SGlobPtr GPtr
, RepairOrderPtr p
)
2342 CatalogName
*cNameP
;
2346 CatalogRecord record
;
2351 isHFSPlus
= VolumeObjectIsHFSPlus( );
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;
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
);
2370 if (result
== btNotFound
) {
2376 result
= DeleteCatalogNode(GPtr
->calculatedVCB
, p
->parid
, cNameP
, p
->hint
, false);
2381 GPtr
->VIStat
|= S_MDB
;
2382 GPtr
->VIStat
|= S_VBM
;
2384 if (record
.recordType
== kHFSPlusFileRecord
) {
2385 id
= record
.hfsPlusFile
.fileID
;
2386 } else if (record
.recordType
== kHFSPlusFolderRecord
) {
2387 id
= record
.hfsPlusFolder
.folderID
;
2390 /* Delete all extended attributes associated with this file/folder */
2391 result
= DeleteAllAttrsByID(GPtr
, id
);
2398 * Fix a file's PEOF or LEOF (truncate)
2399 * (HFS Plus volumes only)
2402 FixFileSize(SGlobPtr GPtr
, RepairOrderPtr p
)
2406 HFSPlusCatalogKey
* keyp
;
2407 FSBufferDescriptor btRecord
;
2408 BTreeIterator btIterator
;
2416 isHFSPlus
= VolumeObjectIsHFSPlus( );
2419 fcb
= GPtr
->calculatedCatalogFCB
;
2422 ClearMemory(&btIterator
, sizeof(btIterator
));
2423 btIterator
.hint
.nodeNum
= p
->hint
;
2424 keyp
= (HFSPlusCatalogKey
*)&btIterator
.key
;
2425 keyp
->parentID
= p
->parid
;
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
;
2432 btRecord
.bufferAddress
= &rec
;
2433 btRecord
.itemCount
= 1;
2434 btRecord
.itemSize
= sizeof(rec
);
2436 result
= BTSearchRecord(fcb
, &btIterator
, kInvalidMRUCacheKey
,
2437 &btRecord
, &recSize
, &btIterator
);
2439 return (IntError(GPtr
, result
));
2441 if (rec
.recordType
!= kHFSPlusFileRecord
)
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
)) {
2449 rec
.hfsPlusFile
.resourceFork
.totalBlocks
= (UInt32
)p
->correct
;
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.
2456 if (rec
.hfsPlusFile
.resourceFork
.logicalSize
> bytes
) {
2457 rec
.hfsPlusFile
.resourceFork
.logicalSize
= bytes
;
2459 } else if ((p
->forkType
== kDataFork
) &&
2460 ((UInt32
)p
->incorrect
== rec
.hfsPlusFile
.dataFork
.totalBlocks
)) {
2462 rec
.hfsPlusFile
.dataFork
.totalBlocks
= (UInt32
)p
->correct
;
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.
2469 if (rec
.hfsPlusFile
.dataFork
.logicalSize
> bytes
) {
2470 rec
.hfsPlusFile
.dataFork
.logicalSize
= bytes
;
2473 } else /* E_LEOF */ {
2474 if ((p
->forkType
== kRsrcFork
) &&
2475 (p
->incorrect
== rec
.hfsPlusFile
.resourceFork
.logicalSize
)) {
2477 rec
.hfsPlusFile
.resourceFork
.logicalSize
= p
->correct
;
2480 } else if ((p
->forkType
== kDataFork
) &&
2481 (p
->incorrect
== rec
.hfsPlusFile
.dataFork
.logicalSize
)) {
2483 rec
.hfsPlusFile
.dataFork
.logicalSize
= p
->correct
;
2489 result
= BTReplaceRecord(fcb
, &btIterator
, &btRecord
, recSize
);
2491 return (IntError(GPtr
, result
));
2497 /*------------------------------------------------------------------------------
2499 Routine: FixEmbededVolDescription
2501 Function: 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.
2506 Input: GPtr - pointer to scavenger global area
2507 p - pointer to the repair order
2509 Output: FixMDBdrAlBlSt - function result:
2512 ------------------------------------------------------------------------------*/
2514 static OSErr
FixEmbededVolDescription( SGlobPtr GPtr
, RepairOrderPtr p
)
2517 HFSMasterDirectoryBlock
*mdb
;
2518 EmbededVolDescription
*desc
;
2519 SVCB
*vcb
= GPtr
->calculatedVCB
;
2520 BlockDescriptor block
;
2522 desc
= (EmbededVolDescription
*) &(p
->name
);
2523 block
.buffer
= NULL
;
2525 /* Fix the Alternate MDB */
2526 err
= GetVolumeObjectAlternateMDB( &block
);
2528 goto ExitThisRoutine
;
2529 mdb
= (HFSMasterDirectoryBlock
*) block
.buffer
;
2531 mdb
->drAlBlSt
= desc
->drAlBlSt
;
2532 mdb
->drEmbedSigWord
= desc
->drEmbedSigWord
;
2533 mdb
->drEmbedExtent
.startBlock
= desc
->drEmbedExtent
.startBlock
;
2534 mdb
->drEmbedExtent
.blockCount
= desc
->drEmbedExtent
.blockCount
;
2536 err
= ReleaseVolumeBlock( vcb
, &block
, kForceWriteBlock
);
2537 block
.buffer
= NULL
;
2539 goto ExitThisRoutine
;
2542 err
= GetVolumeObjectPrimaryMDB( &block
);
2544 goto ExitThisRoutine
;
2545 mdb
= (HFSMasterDirectoryBlock
*) block
.buffer
;
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
;
2555 if ( block
.buffer
!= NULL
)
2556 err
= ReleaseVolumeBlock( vcb
, &block
, kReleaseBlock
);
2564 /*------------------------------------------------------------------------------
2566 Routine: FixWrapperExtents
2568 Function: 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.
2574 Input: GPtr - pointer to scavenger global area
2575 p - pointer to the repair order
2580 ------------------------------------------------------------------------------*/
2582 static OSErr
FixWrapperExtents( SGlobPtr GPtr
, RepairOrderPtr p
)
2587 HFSMasterDirectoryBlock
*mdb
;
2588 SVCB
*vcb
= GPtr
->calculatedVCB
;
2589 BlockDescriptor block
;
2591 /* Get the Alternate MDB */
2592 block
.buffer
= NULL
;
2593 err
= GetVolumeObjectAlternateMDB( &block
);
2595 goto ExitThisRoutine
;
2596 mdb
= (HFSMasterDirectoryBlock
*) block
.buffer
;
2598 /* Fix the wrapper catalog's first (and only) extent */
2599 mdb
->drCTExtRec
[0].startBlock
= mdb
->drXTExtRec
[0].startBlock
+
2600 mdb
->drXTExtRec
[0].blockCount
;
2602 err
= ReleaseVolumeBlock(vcb
, &block
, kForceWriteBlock
);
2603 block
.buffer
= NULL
;
2605 goto ExitThisRoutine
;
2608 err
= GetVolumeObjectPrimaryMDB( &block
);
2610 goto ExitThisRoutine
;
2611 mdb
= (HFSMasterDirectoryBlock
*) block
.buffer
;
2613 mdb
->drCTExtRec
[0].startBlock
= mdb
->drXTExtRec
[0].startBlock
+
2614 mdb
->drXTExtRec
[0].blockCount
;
2616 err
= ReleaseVolumeBlock(vcb
, &block
, kForceWriteBlock
);
2617 block
.buffer
= NULL
;
2620 if ( block
.buffer
!= NULL
)
2621 (void) ReleaseVolumeBlock( vcb
, &block
, kReleaseBlock
);
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.
2632 static OSErr
FixOrphanedExtent( SGlobPtr GPtr
)
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
;
2650 HFSCatalogNodeID lastFileID
= -1;
2651 UInt32 recordsFound
= 0;
2652 Boolean mustRebuildBTree
= false;
2654 SVCB
*calculatedVCB
= GPtr
->calculatedVCB
;
2655 UInt32
**dataHandle
= GPtr
->validFilesList
;
2656 SFCB
* fcb
= GPtr
->calculatedExtentsFCB
;
2659 isHFSPlus
= VolumeObjectIsHFSPlus( );
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
);
2665 ClearMemory( (Ptr
)&zeroExtents
, sizeof(ExtentRecord
) );
2669 maxRecords
= fcb
->fcbLogicalSize
/ sizeof(HFSPlusExtentRecord
);
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
2679 while ( recordsFound
< maxRecords
)
2681 err
= BTScanNextRecord( &scanState
, false, (void **) &extentKeyPtr
, (void **) &extentDataPtr
, &recordSize
);
2685 if ( err
== btNotFound
)
2691 fileID
= (isHFSPlus
== true) ? extentKeyPtr
->hfsPlus
.fileID
: extentKeyPtr
->hfs
.fileID
;
2693 if ( (fileID
> kHFSBadBlockFileID
) && (lastFileID
!= fileID
) ) // Keep us out of reserved file trouble
2695 lastFileID
= fileID
;
2699 err
= LocateCatalogThread( calculatedVCB
, fileID
, &threadData
, (UInt16
*)&recordSize
, &hint
); // This call returns nodeName as either Str31 or HFSUniStr255, no need to call PrepareInputName()
2701 if ( err
== noErr
) // Thread is found, just verify actual record exists.
2703 err
= LocateCatalogNode( calculatedVCB
, threadData
.hfsPlusThread
.parentID
, (const CatalogName
*) &(threadData
.hfsPlusThread
.nodeName
), kNoHint
, &foundExtentKey
, &catalogData
, &hint
);
2705 else if ( err
== cmNotFound
)
2707 err
= SearchBTreeRecord( GPtr
->calculatedExtentsFCB
, extentKeyPtr
, kNoHint
, &foundExtentKey
, &extents
, (UInt16
*)&recordSize
, &hint
);
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
2716 mustRebuildBTree
= true; // if we have errors here we should rebuild the extents btree
2720 if ( ! bsearch( &fileID
, *dataHandle
, numberOfFilesInList
, sizeof(UInt32
), cmpLongs
) )
2722 err
= SearchBTreeRecord( GPtr
->calculatedExtentsFCB
, extentKeyPtr
, kNoHint
, &foundExtentKey
, &extents
, (UInt16
*)&recordSize
, &hint
);
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
2730 mustRebuildBTree
= true; // if we have errors here we should rebuild the extents btree
2736 if ( mustRebuildBTree
== true )
2738 GPtr
->EBTStat
|= S_RebuildBTree
;
2739 err
= errRebuildBtree
;
2748 /* Function: FixAttrSize
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.
2756 * GPtr - global scavenger structure pointer
2757 * p - current repair order
2760 * result - zero indicates success, non-zero failure.
2762 static OSErr
FixAttrSize(SGlobPtr GPtr
, RepairOrderPtr p
)
2764 OSErr result
= noErr
;
2765 HFSPlusAttrKey
*key
;
2766 HFSPlusAttrRecord record
;
2767 BTreeIterator iterator
;
2768 FSBufferDescriptor btRecord
;
2771 Boolean doReplace
= false;
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
);
2778 btRecord
.bufferAddress
= &record
;
2779 btRecord
.itemCount
= 1;
2780 btRecord
.itemSize
= sizeof(record
);
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.
2787 result
= BTSearchRecord(GPtr
->calculatedAttributesFCB
, &iterator
,
2788 kInvalidMRUCacheKey
, &btRecord
, &recSize
, &iterator
);
2790 DPRINTF (d_error
|d_xattr
, "%s: Cannot find attribute record (err = %d)\n", __FUNCTION__
, result
);
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
;
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
;
2807 /* Make sure that new block count is large enough to
2808 * cover the current LEOAttr. If not, truncate it.
2810 bytes
= p
->correct
* (u_int64_t
)GPtr
->calculatedVCB
->vcbBlockSize
;
2811 if (record
.forkData
.theFork
.logicalSize
> bytes
) {
2812 record
.forkData
.theFork
.logicalSize
= bytes
;
2815 } else if (p
->type
== E_LEOAttr
) {
2816 if (p
->incorrect
== record
.forkData
.theFork
.logicalSize
) {
2817 record
.forkData
.theFork
.logicalSize
= p
->correct
;
2822 /* Replace the attribute record, if required */
2823 if (doReplace
== true) {
2824 result
= BTReplaceRecord(GPtr
->calculatedAttributesFCB
, &iterator
,
2825 &btRecord
, recSize
);
2827 DPRINTF (d_error
|d_xattr
, "%s: Cannot replace attribute record (err=%d)\n", __FUNCTION__
, result
);
2836 /* Function: FixBadExtent
2838 * Description: The function repairs bad extent entry by zeroing out the
2839 * bad extent entry and truncating all extent information found after the
2842 * 1. The information for repair is retrieved from the repair order passed
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
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.
2859 * GPtr - global scavenger structure pointer
2860 * p - current repair order
2863 * result - zero indicates success, non-zero failure.
2865 static OSErr
FixBadExtent(SGlobPtr GPtr
, RepairOrderPtr p
)
2868 UInt32 badExtentIndex
;
2869 UInt32 extentStartBlock
, foundStartBlock
;
2875 ExtentRecord extentRecord
;
2876 ExtentKey extentKey
;
2882 badExtentIndex
= (UInt32
)p
->correct
;
2883 extentStartBlock
= p
->hint
;
2884 forkType
= p
->forkType
;
2886 isHFSPlus
= VolumeObjectIsHFSPlus();
2889 assert (forkType
!= kEAData
);
2891 /* extentStartBlock = 0, the bad extent exists in catalog record */
2892 if (extentStartBlock
== 0) {
2894 CatalogRecord catRecord
;
2896 HFSPlusExtentDescriptor
*hfsPlusExtent
;
2897 HFSExtentDescriptor
*hfsExtent
;
2899 /* Lookup record in catalog BTree */
2900 err
= GetCatalogRecord(GPtr
, fileID
, isHFSPlus
, &catKey
, &catRecord
, &recordSize
);
2902 if (debug
) plog("%s: Could not get catalog record for fileID %u\n", __FUNCTION__
, fileID
);
2906 /* Check record type */
2907 assert ((catRecord
.recordType
== kHFSPlusFileRecord
) ||
2908 (catRecord
.recordType
== kHFSFileRecord
));
2910 /* Zero out the bad extent entry and all entries after it */
2912 if (forkType
== kDataFork
) {
2913 hfsPlusExtent
= catRecord
.hfsPlusFile
.dataFork
.extents
;
2915 hfsPlusExtent
= catRecord
.hfsPlusFile
.resourceFork
.extents
;
2918 for (i
= badExtentIndex
; i
< GPtr
->numExtents
; i
++) {
2919 hfsPlusExtent
[i
].startBlock
= 0;
2920 hfsPlusExtent
[i
].blockCount
= 0;
2923 if (forkType
== kDataFork
) {
2924 hfsExtent
= catRecord
.hfsFile
.dataExtents
;
2926 hfsExtent
= catRecord
.hfsFile
.rsrcExtents
;
2928 for (i
= badExtentIndex
; i
< GPtr
->numExtents
; i
++) {
2929 hfsExtent
[i
].startBlock
= 0;
2930 hfsExtent
[i
].blockCount
= 0;
2934 /* Write the catalog record back */
2935 err
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &catKey
, kNoHint
,
2936 &catRecord
, recordSize
, &hint
);
2938 if (debug
) plog("%s: Could not replace catalog record for fileID %u\n", __FUNCTION__
, fileID
);
2943 } else { /* bad extent exists in overflow extent record */
2945 /* First entry in overflow extent record is bad, delete entire record */
2946 if (badExtentIndex
== 0) {
2947 goto del_overflow_extents
;
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
);
2955 if (debug
) plog("%s: Could not get overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__
, fileID
, forkType
,
2960 /* Zero out the bad extent entry and all entries after it */
2962 for (i
= badExtentIndex
; i
< GPtr
->numExtents
; i
++) {
2963 extentRecord
.hfsPlus
[i
].startBlock
= 0;
2964 extentRecord
.hfsPlus
[i
].blockCount
= 0;
2967 for (i
= badExtentIndex
; i
< GPtr
->numExtents
; i
++) {
2968 extentRecord
.hfs
[i
].startBlock
= 0;
2969 extentRecord
.hfs
[i
].blockCount
= 0;
2973 /* Write the extent record back */
2974 err
= ReplaceBTreeRecord(GPtr
->calculatedExtentsFCB
, &extentKey
, hint
,
2975 &extentRecord
, recordSize
, &hint
);
2977 if (debug
) plog("%s: Could not replace overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__
, fileID
,
2978 forkType
, extentStartBlock
);
2983 /* The startBlock for extent record with bad extent entry is updated
2984 * because we use this startBlock later to lookup next extent record
2985 * for this file and forktype in overflow extent btree which should
2986 * be deleted. By incrementing the startBlock by one, we ensure that
2987 * we find the next record, if any, that should be deleted instead of
2988 * finding the same record that was updated above.
2993 del_overflow_extents
:
2994 /* Search for overflow extent records. We should get a valid record only
2995 * if the bad extent entry was the first entry in the extent overflow
2996 * record. For all other cases, the search record will return an error
2998 BuildExtentKey (isHFSPlus
, forkType
, fileID
, extentStartBlock
, &extentKey
);
2999 err
= SearchBTreeRecord (GPtr
->calculatedExtentsFCB
, &extentKey
, kNoHint
,
3000 &extentKey
, &extentRecord
, &recordSize
, &hint
);
3001 if ((err
!= noErr
) && (err
!= btNotFound
)) {
3002 goto create_symlink
;
3005 /* If we got error, check the next record */
3006 if (err
== btNotFound
) {
3007 err
= GetBTreeRecord(GPtr
->calculatedExtentsFCB
, 1, &extentKey
, &extentRecord
,
3008 &recordSize
, &hint
);
3011 while (err
== noErr
) {
3012 /* Check if the record has correct fileID, forkType */
3014 if ((fileID
!= extentKey
.hfsPlus
.fileID
) ||
3015 (forkType
!= extentKey
.hfsPlus
.forkType
)) {
3018 foundStartBlock
= extentKey
.hfsPlus
.startBlock
;
3020 if ((fileID
!= extentKey
.hfs
.fileID
) ||
3021 (forkType
!= extentKey
.hfs
.forkType
)) {
3024 foundStartBlock
= extentKey
.hfs
.startBlock
;
3027 /* Delete the extent record */
3028 err
= DeleteBTreeRecord(GPtr
->calculatedExtentsFCB
, &extentKey
);
3029 DPRINTF (d_info
, "%s: Deleting extent overflow for fileID=%u, forkType=%u, startBlock=%u\n", __FUNCTION__
, fileID
, forkType
, foundStartBlock
);
3031 goto create_symlink
;
3035 /* Get the next extent record */
3036 err
= GetBTreeRecord(GPtr
->calculatedExtentsFCB
, 1, &extentKey
, &extentRecord
,
3037 &recordSize
, &hint
);
3040 if (err
== btNotFound
) {
3044 UpdateBTreeHeader(GPtr
->calculatedExtentsFCB
);
3047 /* Create symlink for repaired files in damaged files folder */
3048 if (didRepair
== true) {
3049 /* Create symlink for damaged files */
3050 (void) CreateCorruptFileSymlink(GPtr
, fileID
);
3057 /* Function: FixOrphanedFiles
3060 * Incorrect number of thread records get fixed in this function.
3062 * The function traverses the entire catalog Btree.
3064 * For a file/folder record, it tries to lookup its corresponding thread
3065 * record. If the thread record does not exist, or is not correct, a new
3066 * thread record is created. The parent ID, record type, and the name of
3067 * the file/folder are compared for correctness.
3068 * For plain HFS, a thread record is only looked-up if kHFSThreadExistsMask is set.
3070 * For a thread record, it tries to lookup its corresponding file/folder
3071 * record. If its does not exist or is not correct, the thread record
3072 * is deleted. The file/folder ID is compared for correctness.
3074 * Input: 1. GPtr - pointer to global scavenger area
3077 * zero means success
3078 * non-zero means failure
3080 static OSErr
FixOrphanedFiles ( SGlobPtr GPtr
)
3083 CatalogKey foundKey
;
3085 CatalogRecord record
;
3086 CatalogRecord threadRecord
;
3087 CatalogRecord record2
;
3088 HFSCatalogNodeID parentID
;
3089 HFSCatalogNodeID cNodeID
= 0;
3090 BTreeIterator savedIterator
;
3096 UInt16 threadRecordSize
;
3098 SInt16 foundRecType
;
3099 SInt16 selCode
= 0x8001; /* Get first record */
3101 BTreeControlBlock
*btcb
= GetBTreeControlBlock( kCalculatedCatalogRefNum
);
3102 Boolean isDirectory
;
3104 isHFSPlus
= VolumeObjectIsHFSPlus( );
3105 CopyMemory( &btcb
->lastIterator
, &savedIterator
, sizeof(BTreeIterator
) );
3109 // Save/Restore Iterator around calls to GetBTreeRecord
3110 CopyMemory( &savedIterator
, &btcb
->lastIterator
, sizeof(BTreeIterator
) );
3111 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &foundKey
, &record
, &recordSize
, &hint
);
3112 if ( err
!= noErr
) break;
3113 CopyMemory( &btcb
->lastIterator
, &savedIterator
, sizeof(BTreeIterator
) );
3115 selCode
= 1; // kNextRecord
3116 recordType
= record
.recordType
;
3118 isDirectory
= false;
3120 switch( recordType
)
3122 case kHFSFileRecord
:
3123 // If the small file is not supposed to have a thread, just break
3124 if ( ( record
.hfsFile
.flags
& kHFSThreadExistsMask
) == 0 )
3127 case kHFSFolderRecord
:
3128 case kHFSPlusFolderRecord
:
3129 case kHFSPlusFileRecord
:
3131 // Locate the thread associated with this record
3133 (void) CheckForStop( GPtr
); // rotate cursor
3135 parentID
= isHFSPlus
== true ? foundKey
.hfsPlus
.parentID
: foundKey
.hfs
.parentID
;
3138 switch( recordType
)
3140 case kHFSFolderRecord
:
3141 cNodeID
= record
.hfsFolder
.folderID
;
3144 case kHFSFileRecord
:
3145 cNodeID
= record
.hfsFile
.fileID
;
3147 case kHFSPlusFolderRecord
:
3148 cNodeID
= record
.hfsPlusFolder
.folderID
;
3151 case kHFSPlusFileRecord
:
3152 cNodeID
= record
.hfsPlusFile
.fileID
;
3156 //-- Build the key for the file thread
3157 BuildCatalogKey( cNodeID
, nil
, isHFSPlus
, &key
);
3159 err
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
,
3160 &tempKey
, &threadRecord
, &threadRecordSize
, &hint2
);
3162 /* We found a thread record for this file/folder record. */
3164 /* Check if the parent ID and nodeName are same, and recordType is as
3165 * expected. If not, we are missing a correct thread record. Force
3166 * btNotFound in such case.
3169 /* Check thread's recordType */
3170 foundRecType
= threadRecord
.hfsPlusThread
.recordType
;
3171 if (isDirectory
== true) {
3172 if (foundRecType
!= kHFSPlusFolderThreadRecord
) {
3174 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3175 plog ("\t%s: Folder thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3179 if (foundRecType
!= kHFSPlusFileThreadRecord
) {
3181 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3182 plog ("\t%s: File thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3187 /* Compare parent ID */
3188 if (parentID
!= threadRecord
.hfsPlusThread
.parentID
) {
3190 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3191 plog ("\t%s: parentID for id=%u do not match (fileKey=%u threadRecord=%u)\n", __FUNCTION__
, cNodeID
, parentID
, threadRecord
.hfsPlusThread
.parentID
);
3195 /* Compare nodeName from file/folder key and thread reocrd */
3196 if (!((foundKey
.hfsPlus
.nodeName
.length
== threadRecord
.hfsPlusThread
.nodeName
.length
)
3197 && (!bcmp(foundKey
.hfsPlus
.nodeName
.unicode
,
3198 threadRecord
.hfsPlusThread
.nodeName
.unicode
,
3199 foundKey
.hfsPlus
.nodeName
.length
* 2)))) {
3201 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3202 unsigned maxLength
= foundKey
.hfsPlus
.nodeName
.length
;
3203 if (maxLength
< threadRecord
.hfsPlusThread
.nodeName
.length
)
3204 maxLength
= threadRecord
.hfsPlusThread
.nodeName
.length
;
3206 plog ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__
, cNodeID
);
3207 if (cur_debug_level
& d_dump_record
)
3209 plog("\tFile/Folder record:\n");
3210 HexDump(&foundKey
, foundKey
.hfsPlus
.keyLength
+ 2, FALSE
);
3212 HexDump(&record
, recordSize
, FALSE
);
3214 plog("\tThread record:\n");
3215 HexDump(&tempKey
, tempKey
.hfsPlus
.keyLength
+ 2, FALSE
);
3217 HexDump(&threadRecord
, threadRecordSize
, FALSE
);
3223 /* If any of the above checks failed, delete the bad thread record */
3224 if (err
== btNotFound
) {
3225 (void) DeleteBTreeRecord(GPtr
->calculatedCatalogFCB
, &tempKey
);
3227 } else { /* plain HFS */
3228 /* Check thread's recordType */
3229 foundRecType
= threadRecord
.hfsThread
.recordType
;
3230 if (isDirectory
== true) {
3231 if (foundRecType
!= kHFSFolderThreadRecord
) {
3233 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3234 plog ("\t%s: Folder thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3238 if (foundRecType
!= kHFSFileThreadRecord
) {
3240 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3241 plog ("\t%s: File thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3246 /* Compare parent ID */
3247 if (parentID
!= threadRecord
.hfsThread
.parentID
) {
3249 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3250 plog ("\t%s: parentID for id=%u do not match (fileKey=%u threadRecord=%u)\n", __FUNCTION__
, cNodeID
, parentID
, threadRecord
.hfsThread
.parentID
);
3254 /* Compare nodeName from file/folder key and thread reocrd */
3255 if (!((foundKey
.hfs
.nodeName
[0] == threadRecord
.hfsThread
.nodeName
[0])
3256 && (!bcmp(&foundKey
.hfs
.nodeName
[1],
3257 &threadRecord
.hfsThread
.nodeName
[1],
3258 foundKey
.hfs
.nodeName
[0])))) {
3260 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3261 plog ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__
, cNodeID
);
3265 /* If any of the above checks failed, delete the bad thread record */
3266 if (err
== btNotFound
) {
3267 (void) DeleteBTreeRecord(GPtr
->calculatedCatalogFCB
, &tempKey
);
3270 } /* err == noErr */
3272 // For missing thread records, just create the thread
3273 if ( err
== btNotFound
)
3275 // Create the missing thread record.
3277 isDirectory
= false;
3278 switch( recordType
)
3280 case kHFSFolderRecord
:
3281 case kHFSPlusFolderRecord
:
3286 //-- Fill out the data for the new file thread from the key
3287 // of catalog file/folder record
3288 recordSize
= BuildThreadRec( &foundKey
, &threadRecord
, isHFSPlus
,
3290 err
= InsertBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
,
3291 &threadRecord
, recordSize
, &threadHint
);
3292 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3293 plog ("\t%s: Created thread record for id=%u (err=%u)\n", __FUNCTION__
, cNodeID
, err
);
3300 case kHFSFolderThreadRecord
:
3301 case kHFSPlusFolderThreadRecord
:
3304 case kHFSFileThreadRecord
:
3305 case kHFSPlusFileThreadRecord
:
3307 // Find the catalog record, if it does not exist, delete the existing thread.
3309 BuildCatalogKey( record
.hfsPlusThread
.parentID
, (const CatalogName
*)&record
.hfsPlusThread
.nodeName
, isHFSPlus
, &key
);
3311 BuildCatalogKey( record
.hfsThread
.parentID
, (const CatalogName
*)&record
.hfsThread
.nodeName
, isHFSPlus
, &key
);
3313 err
= SearchBTreeRecord ( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &tempKey
, &record2
, &recordSize
, &hint2
);
3315 /* We found a file/folder record for this thread record. */
3317 /* Check if the file/folder ID are same and if the recordType is as
3318 * expected. If not, we are missing a correct file/folder record.
3319 * Delete the extra thread record
3322 /* Check recordType */
3323 foundRecType
= record2
.hfsPlusFile
.recordType
;
3324 if (isDirectory
== true) {
3325 if (foundRecType
!= kHFSPlusFolderRecord
) {
3327 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3328 plog ("\t%s: Folder recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3332 if (foundRecType
!= kHFSPlusFileRecord
) {
3334 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3335 plog ("\t%s: File recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3340 /* Compare file/folder ID */
3341 if (foundKey
.hfsPlus
.parentID
!= record2
.hfsPlusFile
.fileID
) {
3343 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3344 plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__
, foundKey
.hfsPlus
.parentID
, record2
.hfsPlusFile
.fileID
, record
.hfsPlusThread
.parentID
);
3347 } else { /* plain HFS */
3348 /* Check recordType */
3349 foundRecType
= record2
.hfsFile
.recordType
;
3350 if (isDirectory
== true) {
3351 if (foundRecType
!= kHFSFolderRecord
) {
3353 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3354 plog ("\t%s: Folder recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3358 if (foundRecType
!= kHFSFileRecord
) {
3360 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3361 plog ("\t%s: File recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3366 /* Compare file/folder ID */
3367 if (foundKey
.hfs
.parentID
!= record2
.hfsFile
.fileID
) {
3369 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3370 if (recordType
== kHFSFolderThreadRecord
) {
3371 plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__
, foundKey
.hfs
.parentID
, record2
.hfsFolder
.folderID
, record
.hfsThread
.parentID
);
3373 plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__
, foundKey
.hfs
.parentID
, record2
.hfsFile
.fileID
, record
.hfsThread
.parentID
);
3378 } /* if (err == noErr) */
3382 err
= DeleteBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
);
3383 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3385 plog ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__
, foundKey
.hfsPlus
.parentID
, err
);
3387 plog ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__
, foundKey
.hfs
.parentID
, err
);
3395 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3396 plog ("\t%s: Unknown record type.\n", __FUNCTION__
);
3401 } while ( err
== noErr
);
3403 if ( err
== btNotFound
)
3404 err
= noErr
; // all done, no more catalog records
3406 // if (err == noErr)
3407 // err = BTFlushPath( GPtr->calculatedCatalogFCB );
3413 static OSErr
RepairReservedBTreeFields ( SGlobPtr GPtr
)
3415 CatalogRecord record
;
3416 CatalogKey foundKey
;
3423 selCode
= 0x8001; // start with 1st record
3425 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &foundKey
, &record
, &recordSize
, &hint
);
3426 if ( err
!= noErr
) goto EXIT
;
3428 selCode
= 1; // get next record from now on
3432 switch( record
.recordType
)
3434 case kHFSPlusFolderRecord
:
3435 /* XXX -- this should not always be cleared out (but doesn't appear to being called) */
3436 if ( record
.hfsPlusFolder
.flags
!= 0 )
3438 record
.hfsPlusFolder
.flags
= 0;
3439 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recordSize
, &hint
);
3443 case kHFSPlusFileRecord
:
3444 // Note: bit 7 (mask 0x80) of flags is unused in HFS or HFS Plus. However, Inside Macintosh: Files
3445 // describes it as meaning the file record is in use. Some non-Apple implementations end up setting
3446 // this bit, so we just ignore it.
3447 if ( ( record
.hfsPlusFile
.flags
& (UInt16
) ~(0X83) ) != 0 )
3449 record
.hfsPlusFile
.flags
&= 0X83;
3450 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recordSize
, &hint
);
3454 case kHFSFolderRecord
:
3455 if ( record
.hfsFolder
.flags
!= 0 )
3457 record
.hfsFolder
.flags
= 0;
3458 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recordSize
, &hint
);
3462 case kHFSFolderThreadRecord
:
3463 case kHFSFileThreadRecord
:
3464 reserved
= (UInt32
*) &(record
.hfsThread
.reserved
);
3465 if ( reserved
[0] || reserved
[1] )
3469 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recordSize
, &hint
);
3473 case kHFSFileRecord
:
3474 // Note: bit 7 (mask 0x80) of flags is unused in HFS or HFS Plus. However, Inside Macintosh: Files
3475 // describes it as meaning the file record is in use. Some non-Apple implementations end up setting
3476 // this bit, so we just ignore it.
3477 if ( ( ( record
.hfsFile
.flags
& (UInt8
) ~(0X83) ) != 0 )
3478 || ( record
.hfsFile
.dataStartBlock
!= 0 )
3479 || ( record
.hfsFile
.rsrcStartBlock
!= 0 )
3480 || ( record
.hfsFile
.reserved
!= 0 ) )
3482 record
.hfsFile
.flags
&= 0X83;
3483 record
.hfsFile
.dataStartBlock
= 0;
3484 record
.hfsFile
.rsrcStartBlock
= 0;
3485 record
.hfsFile
.reserved
= 0;
3486 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recordSize
, &hint
);
3494 if ( err
!= noErr
) goto EXIT
;
3496 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &foundKey
, &record
, &recordSize
, &hint
);
3498 } while ( err
== noErr
);
3500 if ( err
== btNotFound
)
3501 err
= noErr
; // all done, no more catalog records
3508 /* Function: FixOrphanAttrRecord
3511 * The function traverses the attribute BTree completely and for every
3512 * leaf record, calls CheckAttributeRecord. CheckAttributeRecord function
3513 * is common function for verify and repair stage. CheckAttributeRecord
3514 * deletes invalid/orphaned extended attribute records under following
3516 * 1. record is overflow extents with no valid fork data or overflow extents
3518 * 2. record type is unknown.
3521 * GPtr - global scavenger structure pointer
3524 * error code - zero on success, non-zero on failure.
3526 static OSErr
FixOrphanAttrRecord(SGlobPtr GPtr
)
3532 HFSPlusAttrRecord record
;
3536 /* Zero out last attribute information from global scavenger structure */
3537 bzero (&(GPtr
->lastAttrInfo
), sizeof(GPtr
->lastAttrInfo
));
3539 /* Warning: Attribute record of type kHFSPlusAttrInlineData may be
3540 * truncated on read! (4425232). CheckAttributeRecord only uses recordType
3541 * field from inline attribute record.
3544 err
= GetBTreeRecord(GPtr
->calculatedAttributesFCB
, selCode
, &key
,
3545 &record
, &recordSize
, &hint
);
3552 err
= CheckAttributeRecord(GPtr
, &key
, &record
, recordSize
);
3557 /* Access the next record.
3558 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
3559 * truncated on read! (4425232). CheckAttributeRecord only uses recordType
3560 * field from inline attribute record.
3562 err
= GetBTreeRecord(GPtr
->calculatedAttributesFCB
, selCode
, &key
,
3563 &record
, &recordSize
, &hint
);
3564 } while (err
== noErr
);
3566 if (err
== btNotFound
) {
3574 /* Function: GetCatalogRecord
3577 * This function returns a catalog file/folder record for a given
3578 * file/folder ID from the catalog BTree.
3580 * Input: 1. GPtr - pointer to global scavenger area
3581 * 2. fileID - file ID to search the file/folder record
3582 * 3. isHFSPlus - boolean value to indicate if volume is HFSPlus
3584 * Output: 1. catKey - catalog key
3585 * 2. catRecord - catalog record for given ID
3586 * 3. recordSize - size of catalog record return back
3589 * zero means success
3590 * non-zero means failure
3592 static OSErr
GetCatalogRecord(SGlobPtr GPtr
, UInt32 fileID
, Boolean isHFSPlus
, CatalogKey
*catKey
, CatalogRecord
*catRecord
, UInt16
*recordSize
)
3595 CatalogKey catThreadKey
;
3596 CatalogName catalogName
;
3598 uint32_t thread_key_parentID
;
3600 /* Look up for catalog thread record for the file that owns attribute */
3601 BuildCatalogKey(fileID
, NULL
, isHFSPlus
, &catThreadKey
);
3602 err
= SearchBTreeRecord(GPtr
->calculatedCatalogFCB
, &catThreadKey
, kNoHint
, catKey
, catRecord
, recordSize
, &hint
);
3604 if (debug
) plog ("%s: No matching catalog thread record found\n", __FUNCTION__
);
3609 plog ("%s(%s,%d):1 recordType=%x, flags=%x\n", __FUNCTION__
, __FILE__
, __LINE__
,
3610 catRecord
->hfsPlusFile
.recordType
,
3611 catRecord
->hfsPlusFile
.flags
);
3614 /* We were expecting a thread record. The recordType says it is a file
3615 * record or folder record. Return error.
3617 if ((catRecord
->hfsPlusFile
.recordType
== kHFSPlusFolderRecord
) ||
3618 (catRecord
->hfsPlusFile
.recordType
== kHFSPlusFileRecord
)) {
3619 err
= fsBTRecordNotFoundErr
;
3622 thread_key_parentID
= catKey
->hfsPlus
.parentID
;
3624 /* It is either a file thread record or folder thread record.
3625 * Look up for catalog record for the file that owns attribute */
3626 CopyCatalogName((CatalogName
*)&(catRecord
->hfsPlusThread
.nodeName
), &catalogName
, isHFSPlus
);
3627 BuildCatalogKey(catRecord
->hfsPlusThread
.parentID
, &catalogName
, isHFSPlus
, catKey
);
3628 err
= SearchBTreeRecord(GPtr
->calculatedCatalogFCB
, catKey
, kNoHint
, catKey
, catRecord
, recordSize
, &hint
);
3630 if (debug
) plog ("%s: No matching catalog record found\n", __FUNCTION__
);
3631 if (cur_debug_level
& d_dump_record
)
3633 plog ("Searching for key:\n");
3634 HexDump(catKey
, CalcKeySize(GPtr
->calculatedCatalogBTCB
, (BTreeKey
*)catKey
), FALSE
);
3640 plog ("%s(%s,%d):2 recordType=%x, flags=%x\n", __FUNCTION__
, __FILE__
, __LINE__
,
3641 catRecord
->hfsPlusFile
.recordType
,
3642 catRecord
->hfsPlusFile
.flags
);
3645 /* For catalog file or folder record, the parentID in the thread
3646 * record's key should be equal to the fileID in the file/folder
3647 * record --- which is equal to the ID of the file/folder record
3648 * that is being looked up. If not, mark the volume for repair.
3650 if (thread_key_parentID
!= catRecord
->hfsPlusFile
.fileID
) {
3651 RcdError(GPtr
, E_IncorrectNumThdRcd
);
3652 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3653 plog("\t%s: fileID=%u, thread.key.parentID=%u, record.fileID=%u\n",
3654 __FUNCTION__
, fileID
, thread_key_parentID
, catRecord
->hfsPlusFile
.fileID
);
3656 GPtr
->CBTStat
|= S_Orphan
;
3662 /* Function: RepairAttributesCheckABT
3665 * This function is called from RepairAttributes (to repair extended
3666 * attributes) during repair stage of fsck_hfs.
3668 * 1. Make full pass through attribute BTree.
3669 * 2. For every unique fileID, lookup its catalog record in Catalog BTree.
3670 * 3. If found, check the attributes/security bit in catalog record.
3671 * If not set correctly, set it and replace the catalog record.
3672 * 4. If not found, return error
3674 * Input: 1. GPtr - pointer to global scavenger area
3675 * 2. isHFSPlus - boolean value to indicate if volume is HFSPlus
3677 * Output: err - Function result
3678 * zero means success
3679 * non-zero means failure
3681 static OSErr
RepairAttributesCheckABT(SGlobPtr GPtr
, Boolean isHFSPlus
)
3684 UInt16 selCode
; /* select access pattern for BTree */
3687 HFSPlusAttrRecord attrRecord
;
3688 HFSPlusAttrKey attrKey
;
3689 UInt16 attrRecordSize
;
3690 CatalogRecord catRecord
;
3692 UInt16 catRecordSize
;
3694 attributeInfo lastID
; /* fileID for the last attribute searched */
3695 Boolean didRecordChange
= false; /* whether catalog record was changed after checks */
3698 char attrName
[XATTR_MAXNAMELEN
];
3703 lastID
.hasSecurity
= false;
3705 selCode
= 0x8001; /* Get first record from BTree */
3706 /* Warning: Attribute record of type kHFSPlusAttrInlineData may be
3707 * truncated on read! (4425232). This function only uses recordType
3708 * field from inline attribute record.
3710 err
= GetBTreeRecord(GPtr
->calculatedAttributesFCB
, selCode
, &attrKey
, &attrRecord
, &attrRecordSize
, &hint
);
3711 if (err
== btNotFound
) {
3712 /* The attributes B-tree is empty, which is OK; nothing to do. */
3716 if (err
!= noErr
) goto out
;
3718 selCode
= 1; /* Get next record */
3721 /* Convert unicode attribute name to char for ACL check */
3722 (void) utf_encodestr(attrKey
.attrName
, attrKey
.attrNameLen
* 2, attrName
, &len
, sizeof(attrName
));
3723 attrName
[len
] = '\0';
3724 plog ("%s(%s,%d): Found attrName=%s for fileID=%d\n", __FUNCTION__
, __FILE__
, __LINE__
, attrName
, attrKey
.fileID
);
3727 if (attrKey
.fileID
!= lastID
.fileID
) {
3728 /* We found an attribute with new file ID */
3730 /* Replace the previous catalog record only if we updated the flags */
3731 if (didRecordChange
== true) {
3732 err
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &catKey
, kNoHint
, &catRecord
, catRecordSize
, &hint
);
3734 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3735 plog ("\t%s: Error in replacing catalog record for id=%u\n", __FUNCTION__
, lastID
.fileID
);
3741 didRecordChange
= false; /* reset to indicate new record has not changed */
3743 /* Get the catalog record for the new fileID */
3744 err
= GetCatalogRecord(GPtr
, attrKey
.fileID
, isHFSPlus
, &catKey
, &catRecord
, &catRecordSize
);
3746 /* No catalog record was found for this fileID. */
3747 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3748 plog ("\t%s: No matching catalog record found for id=%u\n", __FUNCTION__
, attrKey
.fileID
);
3751 /* 3984119 - Do not delete extended attributes for file IDs less
3752 * kHFSFirstUserCatalogNodeID but not equal to kHFSRootFolderID
3753 * in prime modulus checksum. These file IDs do not have
3754 * any catalog record
3756 if ((attrKey
.fileID
< kHFSFirstUserCatalogNodeID
) &&
3757 (attrKey
.fileID
!= kHFSRootFolderID
)) {
3759 plog ("%s: Ignore catalog check for fileID=%d for attribute=%s\n", __FUNCTION__
, attrKey
.fileID
, attrName
);
3764 /* Delete this orphan extended attribute */
3765 err
= delete_attr_record(GPtr
, &attrKey
, &attrRecord
);
3767 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3768 plog ("\t%s: Error in deleting attribute record for id=%u\n", __FUNCTION__
, attrKey
.fileID
);
3775 lastID
.fileID
= attrKey
.fileID
; /* set last fileID to the new ID */
3776 lastID
.hasSecurity
= false; /* reset to indicate new fileID does not have security */
3778 /* Check the Attribute bit */
3779 if (!(catRecord
.hfsPlusFile
.flags
& kHFSHasAttributesMask
)) {
3780 /* kHFSHasAttributeBit should be set */
3781 catRecord
.hfsPlusFile
.flags
|= kHFSHasAttributesMask
;
3782 didRecordChange
= true;
3785 /* Check if attribute is ACL */
3786 if (!bcmp(attrKey
.attrName
, GPtr
->securityAttrName
, GPtr
->securityAttrLen
)) {
3787 lastID
.hasSecurity
= true;
3788 /* Check the security bit */
3789 if (!(catRecord
.hfsPlusFile
.flags
& kHFSHasSecurityMask
)) {
3790 /* kHFSHasSecurityBit should be set */
3791 catRecord
.hfsPlusFile
.flags
|= kHFSHasSecurityMask
;
3792 didRecordChange
= true;
3796 /* We have seen attribute for fileID in past */
3798 /* If last time we saw this fileID did not have an ACL and this
3799 * extended attribute is an ACL, ONLY check consistency of
3800 * security bit from Catalog record
3802 if ((lastID
.hasSecurity
== false) && !bcmp(attrKey
.attrName
, GPtr
->securityAttrName
, GPtr
->securityAttrLen
)) {
3803 lastID
.hasSecurity
= true;
3804 /* Check the security bit */
3805 if (!(catRecord
.hfsPlusFile
.flags
& kHFSHasSecurityMask
)) {
3806 /* kHFSHasSecurityBit should be set */
3807 catRecord
.hfsPlusFile
.flags
|= kHFSHasSecurityMask
;
3808 didRecordChange
= true;
3814 /* Access the next record
3815 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
3816 * truncated on read! (4425232). This function only uses recordType
3817 * field from inline attribute record.
3819 err
= GetBTreeRecord(GPtr
->calculatedAttributesFCB
, selCode
, &attrKey
, &attrRecord
, &attrRecordSize
, &hint
);
3820 } while (err
== noErr
);
3824 /* Replace the catalog record for last extended attribute in the attributes BTree
3825 * only if we updated the flags
3827 if (didRecordChange
== true) {
3828 err
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &catKey
, kNoHint
, &catRecord
, catRecordSize
, &hint
);
3831 plog ("%s: Error in replacing Catalog Record\n", __FUNCTION__
);
3841 /* Function: RepairAttributesCheckCBT
3844 * This function is called from RepairAttributes (to repair extended
3845 * attributes) during repair stage of fsck_hfs.
3847 * NOTE: The case where attribute exists and bit is not set is being taken care in
3848 * RepairAttributesCheckABT. This function determines relationship from catalog
3849 * Btree to attribute Btree, and not vice-versa.
3851 * 1. Make full pass through catalog BTree.
3852 * 2. For every fileID, if the attributes/security bit is set,
3853 * lookup all the extended attributes in the attributes BTree.
3854 * 3. If found, check that if bits are set correctly.
3855 * 4. If not found, clear the bits.
3857 * Input: 1. GPtr - pointer to global scavenger area
3858 * 2. isHFSPlus - boolean value to indicate if volume is HFSPlus
3860 * Output: err - Function result
3861 * zero means success
3862 * non-zero means failure
3864 static OSErr
RepairAttributesCheckCBT(SGlobPtr GPtr
, Boolean isHFSPlus
)
3867 UInt16 selCode
; /* select access pattern for BTree */
3871 HFSPlusAttrKey
*attrKey
;
3872 CatalogRecord catRecord
;
3875 Boolean didRecordChange
= false; /* whether catalog record was changed after checks */
3877 BTreeIterator iterator
;
3880 Boolean curRecordHasAttributes
= false;
3881 Boolean curRecordHasSecurity
= false;
3883 selCode
= 0x8001; /* Get first record from BTree */
3884 err
= GetBTreeRecord(GPtr
->calculatedCatalogFCB
, selCode
, &catKey
, &catRecord
, &recordSize
, &hint
);
3885 if ( err
!= noErr
) goto out
;
3887 selCode
= 1; /* Get next record */
3889 /* Check only file record and folder record, else skip to next record */
3890 if ( (catRecord
.hfsPlusFile
.recordType
!= kHFSPlusFileRecord
) &&
3891 (catRecord
.hfsPlusFile
.recordType
!= kHFSPlusFolderRecord
)) {
3895 /* Check if catalog record has attribute and/or security bit set, else
3896 * skip to next record
3898 if ( ((catRecord
.hfsPlusFile
.flags
& kHFSHasAttributesMask
) == 0) &&
3899 ((catRecord
.hfsPlusFile
.flags
& kHFSHasSecurityMask
) == 0) ) {
3903 /* Initialize some flags */
3904 didRecordChange
= false;
3905 curRecordHasSecurity
= false;
3906 curRecordHasAttributes
= false;
3908 /* Access all extended attributes for this fileID */
3909 curFileID
= catRecord
.hfsPlusFile
.fileID
;
3911 /* Initialize the iterator and attribute key */
3912 ClearMemory(&iterator
, sizeof(BTreeIterator
));
3913 attrKey
= (HFSPlusAttrKey
*)&iterator
.key
;
3914 attrKey
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
3915 attrKey
->fileID
= curFileID
;
3917 /* Search for attribute with NULL name. This will place the iterator at correct fileID location in BTree */
3918 err
= BTSearchRecord(GPtr
->calculatedAttributesFCB
, &iterator
, kInvalidMRUCacheKey
, NULL
, NULL
, &iterator
);
3919 if (err
&& (err
!= btNotFound
)) {
3921 plog ("%s: No matching attribute record found\n", __FUNCTION__
);
3926 /* Iterate over to all extended attributes for given fileID */
3927 err
= BTIterateRecord(GPtr
->calculatedAttributesFCB
, kBTreeNextRecord
, &iterator
, NULL
, NULL
);
3929 /* Check only if we did _find_ an attribute record for the current fileID */
3930 while ((err
== noErr
) && (attrKey
->fileID
== curFileID
)) {
3931 /* Current record should have attribute bit set */
3932 curRecordHasAttributes
= true;
3934 /* Current record should have security bit set */
3935 if (!bcmp(attrKey
->attrName
, GPtr
->securityAttrName
, GPtr
->securityAttrLen
)) {
3936 curRecordHasSecurity
= true;
3939 /* Get the next record */
3940 err
= BTIterateRecord(GPtr
->calculatedAttributesFCB
, kBTreeNextRecord
, &iterator
, NULL
, NULL
);
3943 /* Determine if we need to update the catalog record */
3944 if ((curRecordHasAttributes
== false) && (catRecord
.hfsPlusFile
.flags
& kHFSHasAttributesMask
)) {
3945 /* If no attribute exists and attributes bit is set, clear it */
3946 catRecord
.hfsPlusFile
.flags
&= ~kHFSHasAttributesMask
;
3947 didRecordChange
= true;
3950 if ((curRecordHasSecurity
== false) && (catRecord
.hfsPlusFile
.flags
& kHFSHasSecurityMask
)) {
3951 /* If no security attribute exists and security bit is set, clear it */
3952 catRecord
.hfsPlusFile
.flags
&= ~kHFSHasSecurityMask
;
3953 didRecordChange
= true;
3956 /* If there was any change in catalog record, write it back to disk */
3957 if (didRecordChange
== true) {
3958 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &catKey
, kNoHint
, &catRecord
, recordSize
, &hint
);
3961 plog ("%s: Error writing catalog record\n", __FUNCTION__
);
3968 /* Access the next record */
3969 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &catKey
, &catRecord
, &recordSize
, &hint
);
3970 } while (err
== noErr
);
3978 /* Function: RepairAttributes
3981 * This function fixes the extended attributes consistency by
3982 * calling two functions:
3983 * 1. RepairAttributesCheckABT: Traverses attributes Btree and
3984 * checks if each attribute has correct bits set in its corresponding
3986 * 2. RepairAttributesCheckCBT: Traverses catalog Btree and checks
3987 * if each catalog record that has attribute/security bit set have
3988 * corresponding extended attributes.
3990 * Input: 1. GPtr - pointer to global scavenger area
3992 * Output: err - Function result
3993 * zero means success
3994 * non-zero means failure
3996 static OSErr
RepairAttributes(SGlobPtr GPtr
)
4001 /* Check if the volume is HFS Plus volume */
4002 isHFSPlus
= VolumeObjectIsHFSPlus();
4007 /* Traverse Attributes BTree and access required records in Catalog BTree */
4008 err
= RepairAttributesCheckABT(GPtr
, isHFSPlus
);
4013 /* Traverse Catalog BTree and access required records in Attributes BTree */
4014 err
= RepairAttributesCheckCBT(GPtr
, isHFSPlus
);
4023 /*------------------------------------------------------------------------------
4027 Function: compares two longs.
4029 Input: *a: pointer to first number
4030 *b: pointer to second number
4032 Output: <0 if *a < *b
4035 ------------------------------------------------------------------------------*/
4037 int cmpLongs ( const void *a
, const void *b
)
4039 return (int)( *(long*)a
- *(long*)b
);
4042 /* Function: FixOverlappingExtents
4044 * Description: Fix overlapping extents problem. The implementation copies all
4045 * the extents existing in overlapping extents to a new location and updates the
4046 * extent record to point to the new extent. At the end of repair, symlinks are
4047 * created with name "fileID filename" to point to the file involved in
4048 * overlapping extents problem. Note that currently only HFS Plus volumes are
4051 * PARTIAL SUCCESS: This function handles partial success in the following
4053 * a. The function pre-allocates space for the all the extents. If the
4054 * allocation fails, it proceeds to allocate space for other extents
4055 * instead of returning error.
4056 * b. If moving an extent succeeds and symlink creation fails, the function
4057 * proceeds to another extent.
4058 * If the repair encounters either a or b condition, appropriate message is
4059 * printed at the end of the function.
4060 * If even a single repair operation succeeds (moving of extent), the function
4063 * Current limitations:
4064 * 1. A regular file instead of symlink is created under following conditions:
4065 * a. The volume is plain HFS volume. HFS does not support symlink
4067 * b. The path the new symlink points to is greater than PATH_MAX bytes.
4068 * c. The path the new symlink points has some intermediate component
4069 * greater than NAME_MAX bytes.
4070 * 2. Contiguous disk space for every new extent is expected. The extent is
4071 * not broken into multiple extents if contiguous space is not available on the
4073 * 3. The current fix for overlapping extent only works for HFS Plus volumes.
4074 * Plain HFS volumes have problem in accessing the catalog record by fileID.
4075 * 4. Plain HFS volumes might have encoding issues with the newly created
4076 * symlink and its data.
4079 * GPtr - global scavenger pointer
4082 * returns zero on success/partial success (moving of one extent succeeds),
4083 * non-zero on failure.
4085 static OSErr
FixOverlappingExtents(SGlobPtr GPtr
)
4090 unsigned int numOverlapExtents
= 0;
4091 ExtentInfo
*extentInfo
;
4092 ExtentsTable
**extentsTableH
= GPtr
->overlappedExtents
;
4094 unsigned int status
= 0;
4095 #define S_DISKFULL 0x01 /* error due to disk full */
4096 #define S_MOVEEXTENT 0x02 /* moving extent succeeded */
4098 isHFSPlus
= VolumeObjectIsHFSPlus();
4099 if (isHFSPlus
== false) {
4100 /* Do not repair plain HFS volumes */
4105 if (extentsTableH
== NULL
) {
4106 /* No overlapping extents exist */
4110 numOverlapExtents
= (**extentsTableH
).count
;
4112 /* Optimization - sort the overlap extent list based on blockCount to
4113 * start allocating contiguous space for largest number of blocks first
4115 qsort((**extentsTableH
).extentInfo
, numOverlapExtents
, sizeof(ExtentInfo
),
4116 CompareExtentBlockCount
);
4119 /* Print all overlapping extents structure */
4120 for (i
=0; i
<numOverlapExtents
; i
++) {
4121 extentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4122 plog ("%d: fileID = %d, startBlock = %d, blockCount = %d\n", i
, extentInfo
->fileID
, extentInfo
->startBlock
, extentInfo
->blockCount
);
4126 /* Pre-allocate free space for all overlapping extents */
4127 for (i
=0; i
<numOverlapExtents
; i
++) {
4128 extentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4129 err
= AllocateContigBitmapBits (GPtr
->calculatedVCB
, extentInfo
->blockCount
, &(extentInfo
->newStartBlock
));
4130 if ((err
!= noErr
)) {
4131 /* Not enough disk space */
4132 status
|= S_DISKFULL
;
4134 plog ("%s: Not enough disk space to allocate extent for fileID = %d (start=%d, count=%d)\n", __FUNCTION__
, extentInfo
->fileID
, extentInfo
->startBlock
, extentInfo
->blockCount
);
4139 /* For every extent info, copy the extent into new location and create symlink */
4140 for (i
=0; i
<numOverlapExtents
; i
++) {
4141 extentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4143 /* Do not repair this extent as no new extent was allocated */
4144 if (extentInfo
->newStartBlock
== 0) {
4148 /* Move extent data to new location */
4149 err
= MoveExtent(GPtr
, extentInfo
);
4151 extentInfo
->didRepair
= false;
4153 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
);
4156 /* Mark the overlapping extent as repaired */
4157 extentInfo
->didRepair
= true;
4158 status
|= S_MOVEEXTENT
;
4160 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
);
4164 /* Create symlink for the corrupt file */
4165 err
= CreateCorruptFileSymlink(GPtr
, extentInfo
->fileID
);
4168 plog ("%s: Error in creating symlink for fileID = %d (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4172 plog ("%s: Created symlink for fileID = %u (old=%u, new=%u, count=%u)\n", __FUNCTION__
, extentInfo
->fileID
, extentInfo
->startBlock
, extentInfo
->newStartBlock
, extentInfo
->blockCount
);
4178 /* Release all blocks used by overlap extents that are repaired */
4179 for (i
=0; i
<numOverlapExtents
; i
++) {
4180 extentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4181 if (extentInfo
->didRepair
== true) {
4182 ReleaseBitmapBits (extentInfo
->startBlock
, extentInfo
->blockCount
);
4186 /* For all un-repaired extents,
4187 * 1. Release all blocks allocated for new extent.
4188 * 2. Mark all blocks used for the old extent (since the overlapping region
4189 * have been marked free in the for loop above.
4191 for (i
=0; i
<numOverlapExtents
; i
++) {
4192 extentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4193 if (extentInfo
->didRepair
== false) {
4194 CaptureBitmapBits (extentInfo
->startBlock
, extentInfo
->blockCount
);
4196 if (extentInfo
->newStartBlock
!= 0) {
4197 ReleaseBitmapBits (extentInfo
->newStartBlock
, extentInfo
->blockCount
);
4202 /* Update the volume free block count since the release and alloc above might
4203 * have worked on same bit multiple times.
4205 UpdateFreeBlockCount (GPtr
);
4207 /* Print correct status messages */
4208 if (status
& S_DISKFULL
) {
4209 fsckPrint(GPtr
->context
, E_DiskFull
);
4212 /* If moving of even one extent succeeds, return success */
4213 if (status
& S_MOVEEXTENT
) {
4218 } /* FixOverlappingExtents */
4220 /* Function: CompareExtentBlockCount
4222 * Description: Compares the blockCount from two ExtentInfo and return the
4223 * comparison result. (since we have to arrange in descending order)
4226 * first and second - void pointers to ExtentInfo structure.
4229 * <0 if first > second
4230 * =0 if first == second
4231 * >0 if first < second
4233 static int CompareExtentBlockCount(const void *first
, const void *second
)
4235 return (((ExtentInfo
*)second
)->blockCount
-
4236 ((ExtentInfo
*)first
)->blockCount
);
4237 } /* CompareExtentBlockCount */
4239 /* Function: MoveExtent
4241 * Description: Move data from old extent to new extent and update corresponding
4243 * 1. Search the extent record for the overlapping extent.
4244 * If the fileID < kHFSFirstUserCatalogNodeID,
4245 * Ignore repair for BadBlock, RepairCatalog, BogusExtent files.
4246 * Search for extent record in volume header.
4248 * Search for extent record in catalog BTree. If the extent list does
4249 * not end in catalog record and extent record not found in catalog
4250 * record, search in extents BTree.
4251 * 2. If found, copy disk blocks from old extent to new extent.
4252 * 3. If it succeeds, update extent record with new start block and write back
4254 * This function does not take care to deallocate blocks from old start block.
4257 * GPtr - Global Scavenger structure pointer
4258 * extentInfo - Current overlapping extent details.
4261 * err: zero on success, non-zero on failure
4262 * paramErr - Invalid paramter, ex. file ID is less than
4263 * kHFSFirstUserCatalogNodeID.
4265 static OSErr
MoveExtent(SGlobPtr GPtr
, ExtentInfo
*extentInfo
)
4270 CatalogRecord catRecord
;
4272 HFSPlusExtentKey extentKey
;
4273 HFSPlusExtentRecord extentData
;
4274 HFSPlusAttrKey attrKey
;
4275 HFSPlusAttrRecord attrRecord
;
4278 enum locationTypes
{volumeHeader
, catalogBTree
, extentsBTree
, attributeBTree
} foundLocation
;
4280 UInt32 foundExtentIndex
= 0;
4281 Boolean noMoreExtents
= true;
4283 isHFSPlus
= VolumeObjectIsHFSPlus();
4285 /* Find correct location of this overlapping extent */
4286 if (extentInfo
->forkType
== kEAData
) {
4287 assert(isHFSPlus
== true);
4289 /* Search extent in attribute btree */
4290 err
= SearchExtentInAttributeBT (GPtr
, extentInfo
, &attrKey
, &attrRecord
,
4291 &recordSize
, &foundExtentIndex
);
4295 foundLocation
= attributeBTree
;
4296 } else { /* kDataFork or kRsrcFork */
4297 if (extentInfo
->fileID
< kHFSFirstUserCatalogNodeID
) {
4298 /* Ignore these fileIDs in repair. Bad block file blocks should
4299 * never be moved. kHFSRepairCatalogFileID and kHFSBogusExtentFileID
4300 * are temporary runtime files. We need to return error to the caller
4301 * to deallocate disk blocks preallocated during preflight
4302 * to move the overlapping extents. Any other extent that overlaps
4303 * with these extents might have moved successfully, thus repairing
4306 if ((extentInfo
->fileID
== kHFSBadBlockFileID
) ||
4307 (extentInfo
->fileID
== kHFSBogusExtentFileID
) ||
4308 (extentInfo
->fileID
== kHFSRepairCatalogFileID
)) {
4313 /* Search for extent record in the volume header */
4314 err
= SearchExtentInVH (GPtr
, extentInfo
, &foundExtentIndex
, &noMoreExtents
);
4315 foundLocation
= volumeHeader
;
4317 /* Search the extent record from the catalog btree */
4318 err
= SearchExtentInCatalogBT (GPtr
, extentInfo
, &catKey
, &catRecord
,
4319 &recordSize
, &foundExtentIndex
, &noMoreExtents
);
4320 foundLocation
= catalogBTree
;
4323 if (noMoreExtents
== false) {
4324 /* search extent in extents overflow btree */
4325 err
= SearchExtentInExtentBT (GPtr
, extentInfo
, &extentKey
,
4326 &extentData
, &recordSize
, &foundExtentIndex
);
4327 foundLocation
= extentsBTree
;
4329 DPRINTF (d_error
|d_overlap
, "%s: No matching extent record found in extents btree for fileID = %d (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4333 /* No more extents exist for this file */
4334 DPRINTF (d_error
|d_overlap
, "%s: No matching extent record found for fileID = %d\n", __FUNCTION__
, extentInfo
->fileID
);
4339 /* Copy disk blocks from old extent to new extent */
4340 err
= CopyDiskBlocks(GPtr
, extentInfo
->startBlock
, extentInfo
->blockCount
,
4341 extentInfo
->newStartBlock
);
4343 DPRINTF (d_error
|d_overlap
, "%s: Error in copying disk blocks for fileID = %d (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4347 /* Replace the old start block in extent record with new start block */
4348 if (foundLocation
== catalogBTree
) {
4349 err
= UpdateExtentInCatalogBT(GPtr
, extentInfo
, &catKey
, &catRecord
,
4350 &recordSize
, foundExtentIndex
);
4351 } else if (foundLocation
== volumeHeader
) {
4352 err
= UpdateExtentInVH(GPtr
, extentInfo
, foundExtentIndex
);
4353 } else if (foundLocation
== extentsBTree
) {
4354 extentData
[foundExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4355 err
= UpdateExtentRecord(GPtr
->calculatedVCB
, NULL
, &extentKey
, extentData
, kNoHint
);
4356 } else if (foundLocation
== attributeBTree
) {
4357 err
= UpdateExtentInAttributeBT(GPtr
, extentInfo
, &attrKey
, &attrRecord
,
4358 &recordSize
, foundExtentIndex
);
4362 DPRINTF (d_error
|d_overlap
, "%s: Error in updating extent record for fileID = %d (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4370 /* Function: CreateCorruptFileSymlink
4372 * Description: Create symlink to point to the corrupt files that might
4373 * have data loss due to repair (overlapping extents, bad extents)
4375 * The function looks up directory with name "DamagedFiles" in the
4376 * root of the file system being repaired. If it does not exists, it
4377 * creates the directory. The symlink to damaged file is created in this
4380 * If fileID >= kHFSFirstUserCatalogNodeID,
4381 * Lookup the filename and path to the file based on file ID. Create the
4382 * new file name as "fileID filename" and data as the relative path of the file
4383 * from the root of the volume.
4385 * the volume is plain HFS, or
4386 * the length of the path pointed by data is greater than PATH_MAX, or
4387 * the length of any intermediate path component is greater than NAME_MAX,
4388 * Create a plain file with given data.
4392 * Find the name of file based on ID (ie. Catalog BTree, etc), and create plain
4393 * regular file with name "fileID filename" and data as "System File:
4397 * 1. GPtr - global scavenger structure pointer.
4398 * 2. fileID - fileID of the source for creating symlink
4401 * returns zero on success, non-zero on failure.
4402 * memFullErr - Not enough memory
4404 static OSErr
CreateCorruptFileSymlink(SGlobPtr GPtr
, UInt32 fileID
)
4408 char *filename
= NULL
;
4409 unsigned int filenamelen
;
4411 unsigned int datalen
;
4412 unsigned int filenameoffset
;
4413 unsigned int dataoffset
;
4414 UInt32 damagedDirID
;
4418 isHFSPlus
= VolumeObjectIsHFSPlus();
4420 /* Lookup and create, if required, the DamagedFiles folder */
4421 damagedDirID
= CreateDirByName(GPtr
, (u_char
*)"DamagedFiles", kHFSRootFolderID
);
4422 if (damagedDirID
== 0) {
4426 /* Allocate (kHFSPlusMaxFileNameChars * 4) for unicode - utf8 conversion */
4427 filenamelen
= kHFSPlusMaxFileNameChars
* 4;
4428 filename
= malloc(filenamelen
);
4434 /* Allocate (PATH_MAX * 4) instead of PATH_MAX for unicode - utf8 conversion */
4435 datalen
= PATH_MAX
* 4;
4436 data
= malloc(datalen
);
4442 /* Find filename, path for fileID >= 16 and determine new fileType */
4443 if (fileID
>= kHFSFirstUserCatalogNodeID
) {
4447 /* construct symlink data with .. prefix */
4448 dataoffset
= sprintf (data
, "..");
4449 path
= data
+ dataoffset
;
4450 datalen
-= dataoffset
;
4452 /* construct new filename prefix with fileID<space> */
4453 filenameoffset
= sprintf (filename
, "%08x ", fileID
);
4454 name
= filename
+ filenameoffset
;
4455 filenamelen
-= filenameoffset
;
4457 /* find file name and path (data for symlink) for fileID */
4458 err
= GetFileNamePathByID(GPtr
, fileID
, path
, &datalen
,
4459 name
, &filenamelen
, &status
);
4462 plog ("%s: Error in getting name/path for fileID = %d (err=%d)\n", __FUNCTION__
, fileID
, err
);
4467 /* update length of path and filename */
4468 filenamelen
+= filenameoffset
;
4469 datalen
+= dataoffset
;
4472 * (1) the volume is plain HFS volume, or
4473 * (2) any intermediate component in path was more than NAME_MAX bytes, or
4474 * (3) the entire path was greater than PATH_MAX bytes
4475 * then create regular file
4476 * else create symlink.
4478 if (!isHFSPlus
|| (status
& FPATH_BIGNAME
) || (datalen
> PATH_MAX
)) {
4482 /* create symlink */
4486 /* for fileID < 16, create regular file */
4489 /* construct the name of the file */
4490 filenameoffset
= sprintf (filename
, "%08x ", fileID
);
4491 filenamelen
-= filenameoffset
;
4492 err
= GetSystemFileName (fileID
, (filename
+ filenameoffset
), &filenamelen
);
4493 filenamelen
+= filenameoffset
;
4495 /* construct the data of the file */
4496 dataoffset
= sprintf (data
, "System File: ");
4497 datalen
-= dataoffset
;
4498 err
= GetSystemFileName (fileID
, (data
+ dataoffset
), &datalen
);
4499 datalen
+= dataoffset
;
4502 /* Create new file */
4503 err
= CreateFileByName (GPtr
, damagedDirID
, fileType
, (u_char
*)filename
,
4504 filenamelen
, (u_char
*)data
, datalen
);
4505 /* Mask error if file already exists */
4506 if (err
== EEXIST
) {
4511 plog ("%s: Error in creating fileType = %d for fileID = %d (err=%d)\n", __FUNCTION__
, fileType
, fileID
, err
);
4518 if ((GPtr
->PrintStat
& S_SymlinkCreate
) == 0) {
4519 fsckPrint(GPtr
->context
, E_SymlinkCreate
);
4520 GPtr
->PrintStat
|= S_SymlinkCreate
;
4523 if ((GPtr
->PrintStat
& S_DamagedDir
) == 0) {
4524 fsckPrint(GPtr
->context
, fsckCorruptFilesDirectory
, "DamagedFiles");
4525 GPtr
->PrintStat
|= S_DamagedDir
;
4537 } /* CreateCorruptFileSymlink */
4539 /* Function: SearchExtentInAttributeBT
4541 * Description: Search extent with given information (fileID, attribute name,
4542 * startBlock, blockCount) in the attribute BTree.
4545 * 1. GPtr - global scavenger structure pointer.
4546 * 2. extentInfo - Information about extent to be searched.
4549 * Returns zero on success, fnfErr on failure.
4550 * 1. *attrKey - Attribute key for given fileID and attribute name, if found.
4551 * 2. *attrRecord - Attribute record for given fileID and attribute name, if found.
4552 * 3. *recordSize - Size of the record being returned.
4553 * 4. *foundExtentIndex - Index in extent record which matches the input data.
4555 static OSErr
SearchExtentInAttributeBT(SGlobPtr GPtr
, ExtentInfo
*extentInfo
,
4556 HFSPlusAttrKey
*attrKey
, HFSPlusAttrRecord
*attrRecord
,
4557 UInt16
*recordSize
, UInt32
*foundExtentIndex
)
4559 OSErr result
= fnfErr
;
4560 BTreeIterator iterator
;
4561 FSBufferDescriptor btRecord
;
4562 HFSPlusAttrKey
*key
;
4563 Boolean noMoreExtents
;
4564 unsigned char *attrname
= NULL
;
4567 assert((extentInfo
->attrname
!= NULL
));
4569 attrname
= malloc (XATTR_MAXNAMELEN
+ 1);
4571 result
= memFullErr
;
4575 /* Initialize the iterator, attribute record buffer, and attribute key */
4576 ClearMemory(&iterator
, sizeof(BTreeIterator
));
4577 key
= (HFSPlusAttrKey
*)&iterator
.key
;
4578 attrnamelen
= strlen(extentInfo
->attrname
);
4579 BuildAttributeKey(extentInfo
->fileID
, 0, (unsigned char *)extentInfo
->attrname
, attrnamelen
, key
);
4581 btRecord
.bufferAddress
= attrRecord
;
4582 btRecord
.itemCount
= 1;
4583 btRecord
.itemSize
= sizeof(HFSPlusAttrRecord
);
4585 /* Search for the attribute record
4586 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4587 * truncated on read! (4425232). This function only uses recordType
4588 * field from inline attribute record.
4590 result
= BTSearchRecord(GPtr
->calculatedAttributesFCB
, &iterator
,
4591 kInvalidMRUCacheKey
, &btRecord
, recordSize
, &iterator
);
4593 DPRINTF (d_error
|d_overlap
, "%s: Error finding attribute record (err=%d) for fileID = %d, attrname = %d\n", __FUNCTION__
, result
, extentInfo
->fileID
, extentInfo
->attrname
);
4597 /* Search the attribute record for overlapping extent. If found, return
4598 * success. If not, iterate to the next record. If it is a valid
4599 * attribute extent record belonging to the same attribute, search
4600 * for the desired extent.
4603 if (attrRecord
->recordType
== kHFSPlusAttrForkData
) {
4604 result
= FindExtentInExtentRec(true, extentInfo
->startBlock
,
4605 extentInfo
->blockCount
, attrRecord
->forkData
.theFork
.extents
,
4606 foundExtentIndex
, &noMoreExtents
);
4607 if ((result
== noErr
) || (noMoreExtents
== true)) {
4610 } else if (attrRecord
->recordType
== kHFSPlusAttrExtents
) {
4611 result
= FindExtentInExtentRec(true, extentInfo
->startBlock
,
4612 extentInfo
->blockCount
, attrRecord
->overflowExtents
.extents
,
4613 foundExtentIndex
, &noMoreExtents
);
4614 if ((result
== noErr
) || (noMoreExtents
== true)) {
4618 /* Invalid attribute record. This function should not find any
4619 * attribute record except forkData and AttrExtents.
4625 /* Iterate to the next record
4626 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4627 * truncated on read! (4425232). This function only uses recordType
4628 * field from inline attribute record.
4630 result
= BTIterateRecord(GPtr
->calculatedAttributesFCB
, kBTreeNextRecord
,
4631 &iterator
, &btRecord
, recordSize
);
4636 (void) utf_encodestr(key
->attrName
, key
->attrNameLen
* 2, attrname
, &attrnamelen
, XATTR_MAXNAMELEN
+ 1);
4637 attrname
[attrnamelen
] = '\0';
4639 /* Check if the attribute record belongs to the same attribute */
4640 if ((key
->fileID
!= extentInfo
->fileID
) ||
4641 (strcmp((char *)attrname
, extentInfo
->attrname
))) {
4642 /* The attribute record belongs to another attribute */
4649 /* Copy the correct key to the caller */
4650 if (result
== noErr
) {
4651 CopyMemory(key
, attrKey
, sizeof(HFSPlusAttrKey
));
4654 if (attrname
!= NULL
) {
4661 /* Function: UpdateExtentInAttributeBT
4663 * Description: Update extent record with given information (fileID, startBlock,
4664 * blockCount) in attribute BTree.
4667 * 1. GPtr - global scavenger structure pointer.
4668 * 2. extentInfo - Information about extent to be searched.
4669 * 3. *attrKey - Attribute key for record to update.
4670 * 4. *attrRecord - Attribute record to update.
4671 * 5. *recordSize - Size of the record.
4672 * 6. foundExtentIndex - Index in extent record to update.
4675 * Returns zero on success, non-zero on failure.
4677 static OSErr
UpdateExtentInAttributeBT (SGlobPtr GPtr
, ExtentInfo
*extentInfo
,
4678 HFSPlusAttrKey
*attrKey
, HFSPlusAttrRecord
*attrRecord
,
4679 UInt16
*recordSize
, UInt32 foundInExtentIndex
)
4684 assert ((attrRecord
->recordType
== kHFSPlusAttrForkData
) ||
4685 (attrRecord
->recordType
== kHFSPlusAttrExtents
));
4687 /* Update the new start block count in the extent */
4688 if (attrRecord
->recordType
== kHFSPlusAttrForkData
) {
4689 attrRecord
->forkData
.theFork
.extents
[foundInExtentIndex
].startBlock
=
4690 extentInfo
->newStartBlock
;
4691 } else if (attrRecord
->recordType
== kHFSPlusAttrExtents
) {
4692 attrRecord
->overflowExtents
.extents
[foundInExtentIndex
].startBlock
=
4693 extentInfo
->newStartBlock
;
4696 /* Replace the attribute record.
4697 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4698 * truncated on read! (4425232).
4700 err
= ReplaceBTreeRecord (GPtr
->calculatedAttributesFCB
, attrKey
, kNoHint
,
4701 attrRecord
, *recordSize
, &foundHint
);
4706 /* Function: SearchExtentInVH
4708 * Description: Search extent with given information (fileID, startBlock,
4709 * blockCount) in volume header.
4712 * 1. GPtr - global scavenger structure pointer.
4713 * 2. extentInfo - Information about extent to be searched.
4716 * Returns zero on success, fnfErr on failure.
4717 * 1. *foundExtentIndex - Index in extent record which matches the input data.
4718 * 2. *noMoreExtents - Indicates that no more extents will exist for this
4719 * fileID in extents BTree.
4721 static OSErr
SearchExtentInVH(SGlobPtr GPtr
, ExtentInfo
*extentInfo
, UInt32
*foundExtentIndex
, Boolean
*noMoreExtents
)
4727 isHFSPlus
= VolumeObjectIsHFSPlus();
4728 *noMoreExtents
= true;
4730 /* Find correct FCB structure */
4731 switch (extentInfo
->fileID
) {
4732 case kHFSExtentsFileID
:
4733 fcb
= GPtr
->calculatedVCB
->vcbExtentsFile
;
4736 case kHFSCatalogFileID
:
4737 fcb
= GPtr
->calculatedVCB
->vcbCatalogFile
;
4740 case kHFSAllocationFileID
:
4741 fcb
= GPtr
->calculatedVCB
->vcbAllocationFile
;
4744 case kHFSStartupFileID
:
4745 fcb
= GPtr
->calculatedVCB
->vcbStartupFile
;
4748 case kHFSAttributesFileID
:
4749 fcb
= GPtr
->calculatedVCB
->vcbAttributesFile
;
4753 /* If extent found, find correct extent index */
4756 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
4757 extentInfo
->blockCount
, fcb
->fcbExtents32
,
4758 foundExtentIndex
, noMoreExtents
);
4760 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
4761 extentInfo
->blockCount
,
4762 (*(HFSPlusExtentRecord
*)fcb
->fcbExtents16
),
4763 foundExtentIndex
, noMoreExtents
);
4767 } /* SearchExtentInVH */
4769 /* Function: UpdateExtentInVH
4771 * Description: Update the extent record for given fileID and index in the
4772 * volume header with new start block.
4775 * 1. GPtr - global scavenger structure pointer.
4776 * 2. extentInfo - Information about extent to be searched.
4777 * 3. foundExtentIndex - Index in extent record to update.
4780 * Returns zero on success, fnfErr on failure. This function will fail an
4781 * incorrect fileID is passed.
4783 static OSErr
UpdateExtentInVH (SGlobPtr GPtr
, ExtentInfo
*extentInfo
, UInt32 foundExtentIndex
)
4789 isHFSPlus
= VolumeObjectIsHFSPlus();
4791 /* Find correct FCB structure */
4792 switch (extentInfo
->fileID
) {
4793 case kHFSExtentsFileID
:
4794 fcb
= GPtr
->calculatedVCB
->vcbExtentsFile
;
4797 case kHFSCatalogFileID
:
4798 fcb
= GPtr
->calculatedVCB
->vcbCatalogFile
;
4801 case kHFSAllocationFileID
:
4802 fcb
= GPtr
->calculatedVCB
->vcbAllocationFile
;
4805 case kHFSStartupFileID
:
4806 fcb
= GPtr
->calculatedVCB
->vcbStartupFile
;
4809 case kHFSAttributesFileID
:
4810 fcb
= GPtr
->calculatedVCB
->vcbAttributesFile
;
4814 /* If extent found, find correct extent index */
4817 fcb
->fcbExtents32
[foundExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4819 fcb
->fcbExtents16
[foundExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4821 MarkVCBDirty(GPtr
->calculatedVCB
);
4825 } /* UpdateExtentInVH */
4827 /* Function: SearchExtentInCatalogBT
4829 * Description: Search extent with given information (fileID, startBlock,
4830 * blockCount) in catalog BTree.
4833 * 1. GPtr - global scavenger structure pointer.
4834 * 2. extentInfo - Information about extent to be searched.
4837 * Returns zero on success, non-zero on failure.
4838 * 1. *catKey - Catalog key for given fileID, if found.
4839 * 2. *catRecord - Catalog record for given fileID, if found.
4840 * 3. *recordSize - Size of the record being returned.
4841 * 4. *foundExtentIndex - Index in extent record which matches the input data.
4842 * 5. *noMoreExtents - Indicates that no more extents will exist for this
4843 * fileID in extents BTree.
4845 static OSErr
SearchExtentInCatalogBT(SGlobPtr GPtr
, ExtentInfo
*extentInfo
, CatalogKey
*catKey
, CatalogRecord
*catRecord
, UInt16
*recordSize
, UInt32
*foundExtentIndex
, Boolean
*noMoreExtents
)
4850 isHFSPlus
= VolumeObjectIsHFSPlus();
4852 /* Search catalog btree for this file ID */
4853 err
= GetCatalogRecord(GPtr
, extentInfo
->fileID
, isHFSPlus
, catKey
, catRecord
,
4857 plog ("%s: No matching catalog record found for fileID = %d (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4864 if (extentInfo
->forkType
== kDataFork
) {
4866 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
4867 extentInfo
->blockCount
,
4868 catRecord
->hfsPlusFile
.dataFork
.extents
,
4869 foundExtentIndex
, noMoreExtents
);
4872 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
4873 extentInfo
->blockCount
,
4874 catRecord
->hfsPlusFile
.resourceFork
.extents
,
4875 foundExtentIndex
, noMoreExtents
);
4879 if (extentInfo
->forkType
== kDataFork
) {
4881 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
4882 extentInfo
->blockCount
,
4883 (*(HFSPlusExtentRecord
*)catRecord
->hfsFile
.dataExtents
),
4884 foundExtentIndex
, noMoreExtents
);
4887 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
4888 extentInfo
->blockCount
,
4889 (*(HFSPlusExtentRecord
*)catRecord
->hfsFile
.rsrcExtents
),
4890 foundExtentIndex
, noMoreExtents
);
4896 } /* SearchExtentInCatalogBT */
4898 /* Function: UpdateExtentInCatalogBT
4900 * Description: Update extent record with given information (fileID, startBlock,
4901 * blockCount) in catalog BTree.
4904 * 1. GPtr - global scavenger structure pointer.
4905 * 2. extentInfo - Information about extent to be searched.
4906 * 3. *catKey - Catalog key for record to update.
4907 * 4. *catRecord - Catalog record to update.
4908 * 5. *recordSize - Size of the record.
4909 * 6. foundExtentIndex - Index in extent record to update.
4912 * Returns zero on success, non-zero on failure.
4914 static OSErr
UpdateExtentInCatalogBT (SGlobPtr GPtr
, ExtentInfo
*extentInfo
, CatalogKey
*catKey
, CatalogRecord
*catRecord
, UInt16
*recordSize
, UInt32 foundInExtentIndex
)
4920 isHFSPlus
= VolumeObjectIsHFSPlus();
4922 /* Modify the catalog record */
4924 if (extentInfo
->forkType
== kDataFork
) {
4925 catRecord
->hfsPlusFile
.dataFork
.extents
[foundInExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4927 catRecord
->hfsPlusFile
.resourceFork
.extents
[foundInExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4930 if (extentInfo
->forkType
== kDataFork
) {
4931 catRecord
->hfsFile
.dataExtents
[foundInExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4933 catRecord
->hfsFile
.rsrcExtents
[foundInExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4937 /* Replace the catalog record */
4938 err
= ReplaceBTreeRecord (GPtr
->calculatedCatalogFCB
, catKey
, kNoHint
,
4939 catRecord
, *recordSize
, &foundHint
);
4942 plog ("%s: Error in replacing catalog record for fileID = %d (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4946 } /* UpdateExtentInCatalogBT */
4948 /* Function: SearchExtentInExtentBT
4950 * Description: Search extent with given information (fileID, startBlock,
4951 * blockCount) in Extent BTree.
4954 * 1. GPtr - global scavenger structure pointer.
4955 * 2. extentInfo - Information about extent to be searched.
4958 * Returns zero on success, non-zero on failure.
4959 * fnfErr - desired extent record was not found.
4960 * 1. *extentKey - Extent key, if found.
4961 * 2. *extentRecord - Extent record, if found.
4962 * 3. *recordSize - Size of the record being returned.
4963 * 4. *foundExtentIndex - Index in extent record which matches the input data.
4965 static OSErr
SearchExtentInExtentBT(SGlobPtr GPtr
, ExtentInfo
*extentInfo
, HFSPlusExtentKey
*extentKey
, HFSPlusExtentRecord
*extentRecord
, UInt16
*recordSize
, UInt32
*foundExtentIndex
)
4969 Boolean noMoreExtents
= true;
4972 isHFSPlus
= VolumeObjectIsHFSPlus();
4974 /* set up extent key */
4975 BuildExtentKey (isHFSPlus
, extentInfo
->forkType
, extentInfo
->fileID
, 0, extentKey
);
4976 err
= SearchBTreeRecord (GPtr
->calculatedExtentsFCB
, extentKey
, kNoHint
,
4977 extentKey
, extentRecord
, recordSize
, &hint
);
4978 if ((err
!= noErr
) && (err
!= btNotFound
)) {
4980 plog ("%s: Error on searching first record for fileID = %d in Extents Btree (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4985 if (err
== btNotFound
)
4987 /* Position to the first record for the given fileID */
4988 err
= GetBTreeRecord (GPtr
->calculatedExtentsFCB
, 1, extentKey
,
4989 extentRecord
, recordSize
, &hint
);
4992 while (err
== noErr
)
4994 /* Break out if we see different fileID, forkType in the BTree */
4996 if ((extentKey
->fileID
!= extentInfo
->fileID
) ||
4997 (extentKey
->forkType
!= extentInfo
->forkType
)) {
5002 if ((((HFSExtentKey
*)extentKey
)->fileID
!= extentInfo
->fileID
) ||
5003 (((HFSExtentKey
*)extentKey
)->forkType
!= extentInfo
->forkType
)) {
5009 /* Check the extents record for corresponding startBlock, blockCount */
5010 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
5011 extentInfo
->blockCount
, *extentRecord
,
5012 foundExtentIndex
, &noMoreExtents
);
5016 if (noMoreExtents
== true) {
5021 /* Try next record for this fileID and forkType */
5022 err
= GetBTreeRecord (GPtr
->calculatedExtentsFCB
, 1, extentKey
,
5023 extentRecord
, recordSize
, &hint
);
5028 } /* SearchExtentInExtentBT */
5030 /* Function: FindExtentInExtentRec
5032 * Description: Traverse the given extent record (size based on if the volume is
5033 * HFS or HFSPlus volume) and find the index location if the given startBlock
5034 * and blockCount match.
5037 * 1. isHFSPlus - If the volume is plain HFS or HFS Plus.
5038 * 2. startBlock - startBlock to be searched in extent record.
5039 * 3. blockCount - blockCount to be searched in extent record.
5040 * 4. extentData - Extent Record to be searched.
5043 * Returns zero if the match is found, else fnfErr on failure.
5044 * 1. *foundExtentIndex - Index in extent record which matches the input data.
5045 * 2. *noMoreExtents - Indicates that no more extents exist after this extent
5048 static OSErr
FindExtentInExtentRec (Boolean isHFSPlus
, UInt32 startBlock
, UInt32 blockCount
, const HFSPlusExtentRecord extentData
, UInt32
*foundExtentIndex
, Boolean
*noMoreExtents
)
5051 UInt32 numOfExtents
;
5052 Boolean foundExtent
;
5055 foundExtent
= false;
5056 *noMoreExtents
= false;
5057 *foundExtentIndex
= 0;
5060 numOfExtents
= kHFSPlusExtentDensity
;
5062 numOfExtents
= kHFSExtentDensity
;
5065 for (i
=0; i
<numOfExtents
; i
++) {
5066 if (extentData
[i
].blockCount
== 0) {
5067 /* no more extents left to check */
5068 *noMoreExtents
= true;
5071 if ((startBlock
== extentData
[i
].startBlock
) &&
5072 (blockCount
== extentData
[i
].blockCount
)) {
5074 *foundExtentIndex
= i
;
5079 if (foundExtent
== false) {
5084 } /* FindExtentInExtentRec */
5086 /* Function: GetSystemFileName
5088 * Description: Return the name of the system file based on fileID
5091 * 1. fileID - fileID whose name is to be returned.
5092 * 2. *filenamelen - length of filename buffer.
5095 * 1. *filename - filename, is limited by the length of filename buffer passed
5097 * 2. *filenamelen - length of the filename
5098 * Always returns zero.
5100 OSErr
GetSystemFileName(UInt32 fileID
, char *filename
, unsigned int *filenamelen
)
5106 len
= *filenamelen
- 1;
5108 case kHFSExtentsFileID
:
5109 strncpy (filename
, "Extents Overflow BTree", len
);
5112 case kHFSCatalogFileID
:
5113 strncpy (filename
, "Catalog BTree", len
);
5116 case kHFSAllocationFileID
:
5117 strncpy (filename
, "Allocation File", len
);
5120 case kHFSStartupFileID
:
5121 strncpy (filename
, "Startup File", len
);
5124 case kHFSAttributesFileID
:
5125 strncpy (filename
, "Attributes BTree", len
);
5128 case kHFSBadBlockFileID
:
5129 strncpy (filename
, "Bad Allocation File", len
);
5132 case kHFSRepairCatalogFileID
:
5133 strncpy (filename
, "Repair Catalog File", len
);
5136 case kHFSBogusExtentFileID
:
5137 strncpy (filename
, "Bogus Extents File", len
);
5141 strncpy (filename
, "Unknown File", len
);
5144 filename
[len
] = '\0';
5145 *filenamelen
= (unsigned int)strlen (filename
);
5150 /* structure to store the intermediate path components during BTree traversal.
5151 * This is used as a LIFO linked list
5156 unsigned int namelen
;
5157 struct fsPathString
*childPtr
;
5160 /* Function: GetFileNamePathByID
5162 * Description: Return the file/directory name and/or full path by ID. The
5163 * length of the strings returned is limited by string lengths passed as
5165 * The function lookups catalog thread record for given fileID and its parents
5166 * until it reaches the Root Folder.
5169 * 1. The path returned currently does not return mangled names.
5170 * 2. Either one or both of fullPath and fileName can be NULL.
5171 * 3. fullPath and fileName are returned as NULL-terminated UTF8 strings.
5172 * 4. Returns error if fileID < kHFSFirstUserCatalogID.
5175 * 1. GPtr - global scavenger structure pointer
5176 * 2. fileID - fileID for the target file/directory for finding the path
5177 * 3. fullPathLen - size of array to return full path
5178 * 4. fileNameLen - size of array to return file name
5181 * Return value: zero on success, non-zero on failure
5182 * memFullErr - Not enough memory
5183 * paramErr - Invalid paramter
5185 * The data in *fileNameLen and *fullPathLen is undefined on error.
5187 * 1. fullPath - If fullPath is non-NULL, full path of file/directory is
5188 * returned (size limited by fullPathLen)
5189 * 2. *fullPathLen - length of fullPath returned.
5190 * 3. fileName - If fileName is non-NULL, file name of fileID is returned (size
5191 * limited by fileNameLen).
5192 * 4. *fileNameLen - length of fileName returned.
5193 * 5. *status - status of operation, any of the following bits can be set
5194 * (defined in dfalib/Scavenger.h).
5195 * FNAME_BUF2SMALL - filename buffer is too small.
5196 * FNAME_BIGNAME - filename is more than NAME_MAX bytes.
5197 * FPATH_BUF2SMALL - path buffer is too small.
5198 * FPATH_BIGNAME - one or more intermediate path component is greater
5199 * than NAME_MAX bytes.
5200 * F_RESERVE_FILEID- fileID is less than kHFSFirstUserCatalogNodeID.
5202 OSErr
GetFileNamePathByID(SGlobPtr GPtr
, UInt32 fileID
, char *fullPath
, unsigned int *fullPathLen
, char *fileName
, unsigned int *fileNameLen
, u_int16_t
*status
)
5207 UInt16 curStatus
= 0;
5210 CatalogRecord catRecord
;
5211 struct fsPathString
*listHeadPtr
= NULL
;
5212 struct fsPathString
*listTailPtr
= NULL
;
5213 struct fsPathString
*curPtr
= NULL
;
5214 u_char
*filename
= NULL
;
5217 if (!fullPath
&& !fileName
) {
5221 if (fileID
< kHFSFirstUserCatalogNodeID
) {
5222 curStatus
= F_RESERVE_FILEID
;
5227 isHFSPlus
= VolumeObjectIsHFSPlus();
5230 filename
= malloc(kHFSPlusMaxFileNameChars
* 3 + 1);
5232 filename
= malloc(kHFSMaxFileNameChars
+ 1);
5237 plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__
, err
);
5242 while (fileID
!= kHFSRootFolderID
) {
5243 /* lookup for thread record for this fileID */
5244 BuildCatalogKey(fileID
, NULL
, isHFSPlus
, &catKey
);
5245 err
= SearchBTreeRecord(GPtr
->calculatedCatalogFCB
, &catKey
, kNoHint
,
5246 &catKey
, &catRecord
, &recordSize
, &hint
);
5249 plog ("%s: Error finding thread record for fileID = %d (err=%d)\n", __FUNCTION__
, fileID
, err
);
5254 /* Check if this is indeed a thread record */
5255 if ((catRecord
.hfsPlusThread
.recordType
!= kHFSPlusFileThreadRecord
) &&
5256 (catRecord
.hfsPlusThread
.recordType
!= kHFSPlusFolderThreadRecord
) &&
5257 (catRecord
.hfsThread
.recordType
!= kHFSFileThreadRecord
) &&
5258 (catRecord
.hfsThread
.recordType
!= kHFSFolderThreadRecord
)) {
5261 plog ("%s: Error finding valid thread record for fileID = %d\n", __FUNCTION__
, fileID
);
5266 /* Convert the name string to utf8 */
5268 (void) utf_encodestr(catRecord
.hfsPlusThread
.nodeName
.unicode
,
5269 catRecord
.hfsPlusThread
.nodeName
.length
* 2,
5270 filename
, &namelen
, kHFSPlusMaxFileNameChars
* 3 + 1);
5272 namelen
= catRecord
.hfsThread
.nodeName
[0];
5273 memcpy (filename
, catKey
.hfs
.nodeName
, namelen
);
5276 /* Store the path name in LIFO linked list */
5277 curPtr
= malloc(sizeof(struct fsPathString
));
5281 plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__
, err
);
5286 /* Do not NULL terminate the string */
5287 curPtr
->namelen
= (unsigned int)namelen
;
5288 curPtr
->name
= malloc(namelen
);
5289 if (!curPtr
->name
) {
5292 plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__
, err
);
5295 memcpy (curPtr
->name
, filename
, namelen
);
5296 curPtr
->childPtr
= listHeadPtr
;
5297 listHeadPtr
= curPtr
;
5298 if (listTailPtr
== NULL
) {
5299 listTailPtr
= curPtr
;
5302 /* lookup for parentID */
5304 fileID
= catRecord
.hfsPlusThread
.parentID
;
5306 fileID
= catRecord
.hfsThread
.parentID
;
5309 /* no need to find entire path, bail out */
5310 if (fullPath
== NULL
) {
5315 /* return the name of the file/directory */
5317 /* Do not overflow the buffer length passed */
5318 if (*fileNameLen
< (listTailPtr
->namelen
+ 1)) {
5319 *fileNameLen
= *fileNameLen
- 1;
5320 curStatus
|= FNAME_BUF2SMALL
;
5322 *fileNameLen
= listTailPtr
->namelen
;
5324 if (*fileNameLen
> NAME_MAX
) {
5325 curStatus
|= FNAME_BIGNAME
;
5327 memcpy (fileName
, listTailPtr
->name
, *fileNameLen
);
5328 fileName
[*fileNameLen
] = '\0';
5331 /* return the full path of the file/directory */
5333 /* Do not overflow the buffer length passed and reserve last byte for NULL termination */
5334 unsigned int bytesRemain
= *fullPathLen
- 1;
5337 while (listHeadPtr
!= NULL
) {
5338 if (bytesRemain
== 0) {
5341 memcpy ((fullPath
+ *fullPathLen
), "/", 1);
5345 if (bytesRemain
== 0) {
5348 if (bytesRemain
< listHeadPtr
->namelen
) {
5349 namelen
= bytesRemain
;
5350 curStatus
|= FPATH_BUF2SMALL
;
5352 namelen
= listHeadPtr
->namelen
;
5354 if (namelen
> NAME_MAX
) {
5355 curStatus
|= FPATH_BIGNAME
;
5357 memcpy ((fullPath
+ *fullPathLen
), listHeadPtr
->name
, namelen
);
5358 *fullPathLen
+= namelen
;
5359 bytesRemain
-= namelen
;
5361 curPtr
= listHeadPtr
;
5362 listHeadPtr
= listHeadPtr
->childPtr
;
5367 fullPath
[*fullPathLen
] = '\0';
5374 *status
= curStatus
;
5377 /* free any remaining allocated memory */
5378 while (listHeadPtr
!= NULL
) {
5379 curPtr
= listHeadPtr
;
5380 listHeadPtr
= listHeadPtr
->childPtr
;
5382 free (curPtr
->name
);
5391 } /* GetFileNamePathByID */
5393 /* Function: CopyDiskBlocks
5395 * Description: Copy data from source extent to destination extent
5396 * for blockCount on the disk.
5399 * 1. GPtr - pointer to global scavenger structure.
5400 * 2. startAllocationBlock - startBlock for old extent
5401 * 3. blockCount - total blocks to copy
5402 * 4. newStartAllocationBlock - startBlock for new extent
5405 * err, zero on success, non-zero on failure.
5407 OSErr
CopyDiskBlocks(SGlobPtr GPtr
, const UInt32 startAllocationBlock
, const UInt32 blockCount
, const UInt32 newStartAllocationBlock
)
5411 uint64_t old_offset
;
5412 uint64_t new_offset
;
5413 uint32_t sectorsPerBlock
;
5415 vcb
= GPtr
->calculatedVCB
;
5416 sectorsPerBlock
= vcb
->vcbBlockSize
/ Blk_Size
;
5418 old_offset
= (vcb
->vcbAlBlSt
+ (sectorsPerBlock
* startAllocationBlock
)) << Log2BlkLo
;
5419 new_offset
= (vcb
->vcbAlBlSt
+ (sectorsPerBlock
* newStartAllocationBlock
)) << Log2BlkLo
;
5421 err
= CacheCopyDiskBlocks (vcb
->vcbBlockCache
, old_offset
, new_offset
,
5422 blockCount
* vcb
->vcbBlockSize
);
5424 } /* CopyDiskBlocks */
5426 /* Function: WriteBufferToDisk
5428 * Description: Write given buffer data to disk blocks.
5429 * If the length of the buffer is not a multiple of allocation block size,
5430 * the disk is filled with zero from the length of buffer upto the
5431 * end of allocation blocks (specified by blockCount).
5434 * 1. GPtr - global scavenger structure pointer
5435 * 2. startBlock - starting block number for writing data.
5436 * 3. blockCount - total number of contiguous blocks to be written
5437 * 4. buffer - data buffer to be written to disk
5438 * 5. bufLen - length of data buffer to be written to disk.
5441 * returns zero on success, non-zero on failure.
5443 OSErr
WriteBufferToDisk(SGlobPtr GPtr
, UInt32 startBlock
, UInt32 blockCount
, u_char
*buffer
, int bufLen
)
5450 vcb
= GPtr
->calculatedVCB
;
5452 /* Calculate offset and length */
5453 offset
= (vcb
->vcbAlBlSt
+ ((vcb
->vcbBlockSize
/ Blk_Size
) * startBlock
)) << Log2BlkLo
;
5454 write_len
= blockCount
* vcb
->vcbBlockSize
;
5456 /* Write buffer to disk */
5457 err
= CacheWriteBufferToDisk (vcb
->vcbBlockCache
, offset
, write_len
, buffer
, bufLen
);
5460 } /* WriteBufferToDisk */
5462 // 2210409, in System 8.1, moving file or folder would cause HFS+ thread records to be
5463 // 520 bytes in size. We only shrink the threads if other repairs are needed.
5464 static OSErr
FixBloatedThreadRecords( SGlob
*GPtr
)
5466 CatalogRecord record
;
5467 CatalogKey foundKey
;
5472 SInt16 selCode
= 0x8001; // Start with 1st record
5474 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &foundKey
, &record
, &recordSize
, &hint
);
5475 ReturnIfError( err
);
5477 selCode
= 1; // Get next record from now on
5481 if ( ++i
> 10 ) { (void) CheckForStop( GPtr
); i
= 0; } // Spin the cursor every 10 entries
5483 if ( (recordSize
== sizeof(HFSPlusCatalogThread
)) && ((record
.recordType
== kHFSPlusFolderThreadRecord
) || (record
.recordType
== kHFSPlusFileThreadRecord
)) )
5485 // HFS Plus has varaible sized threads so adjust to actual length
5486 recordSize
-= ( sizeof(record
.hfsPlusThread
.nodeName
.unicode
) - (record
.hfsPlusThread
.nodeName
.length
* sizeof(UniChar
)) );
5488 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recordSize
, &hint
);
5489 ReturnIfError( err
);
5492 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &foundKey
, &record
, &recordSize
, &hint
);
5493 } while ( err
== noErr
);
5495 if ( err
== btNotFound
)
5503 FixMissingThreadRecords( SGlob
*GPtr
)
5505 struct MissingThread
* mtp
;
5506 FSBufferDescriptor btRecord
;
5507 BTreeIterator iterator
;
5511 UInt32 lostAndFoundDirID
;
5513 lostAndFoundDirID
= 0;
5515 for (mtp
= GPtr
->missingThreadList
; mtp
!= NULL
; mtp
= mtp
->link
) {
5516 if ( mtp
->threadID
== 0 )
5519 // if the thread record information in the MissingThread struct is not there
5520 // then we have a missing directory in addition to a missing thread record
5521 // for that directory. We will recreate the missing directory in our
5522 // lost+found directory.
5523 if ( mtp
->thread
.parentID
== 0 ) {
5524 if (embedded
== 1 && debug
== 0) {
5527 if ( lostAndFoundDirID
== 0 )
5528 lostAndFoundDirID
= CreateDirByName( GPtr
, (u_char
*)"lost+found", kHFSRootFolderID
);
5529 if ( lostAndFoundDirID
== 0 ) {
5530 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
5531 plog( "\tCould not create lost+found directory \n" );
5534 fsckPrint(GPtr
->context
, E_NoDir
, mtp
->threadID
);
5535 result
= FixMissingDirectory( GPtr
, mtp
->threadID
, lostAndFoundDirID
);
5536 if ( result
!= 0 ) {
5537 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
5538 plog( "\tCould not recreate a missing directory (error %d)\n", result
);
5546 dataSize
= 10 + (mtp
->thread
.nodeName
.length
* 2);
5547 btRecord
.bufferAddress
= (void *)&mtp
->thread
;
5548 btRecord
.itemSize
= dataSize
;
5549 btRecord
.itemCount
= 1;
5550 iterator
.hint
.nodeNum
= 0;
5551 BuildCatalogKey(mtp
->threadID
, NULL
, true, (CatalogKey
*)&iterator
.key
);
5553 result
= BTInsertRecord(GPtr
->calculatedCatalogFCB
, &iterator
, &btRecord
, dataSize
);
5555 return (IntError(GPtr
, R_IntErr
));
5559 fsckPrint(GPtr
->context
, fsckLostFoundDirectory
, "lost+found");
5566 FixMissingDirectory( SGlob
*GPtr
, UInt32 theObjID
, UInt32 theParID
)
5573 char myString
[ 32 ];
5575 CatalogRecord catRec
;
5576 CatalogKey myKey
, myThreadKey
;
5578 isHFSPlus
= VolumeObjectIsHFSPlus( );
5580 // we will use the object ID of the missing directory as the name since we have
5581 // no way to find the original name and this should make it unique within our
5582 // lost+found directory.
5583 sprintf( myString
, "%ld", (long)theObjID
);
5584 nameLen
= (int)strlen( myString
);
5589 myName
.ustr
.length
= nameLen
;
5590 for ( i
= 0; i
< myName
.ustr
.length
; i
++ )
5591 myName
.ustr
.unicode
[ i
] = (u_int16_t
) myString
[ i
];
5595 myName
.pstr
[0] = nameLen
;
5596 memcpy( &myName
.pstr
[1], &myString
[0], nameLen
);
5599 // make sure the name is not already used
5600 BuildCatalogKey( theParID
, &myName
, isHFSPlus
, &myKey
);
5601 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &myKey
, kNoHint
,
5602 NULL
, &catRec
, &recSize
, &hint
);
5603 if ( result
== noErr
)
5606 // insert new directory and thread record into the catalog
5607 recSize
= BuildThreadRec( &myKey
, &catRec
, isHFSPlus
, true );
5608 BuildCatalogKey( theObjID
, NULL
, isHFSPlus
, &myThreadKey
);
5609 result
= InsertBTreeRecord( GPtr
->calculatedCatalogFCB
, &myThreadKey
, &catRec
, recSize
, &hint
);
5610 if ( result
!= noErr
)
5613 recSize
= BuildFolderRec( GPtr
, 01777, theObjID
, isHFSPlus
, &catRec
);
5615 result
= InsertBTreeRecord( GPtr
->calculatedCatalogFCB
, &myKey
, &catRec
, recSize
, &hint
);
5616 if ( result
!= noErr
)
5619 /* update parent directory to reflect addition of new directory */
5620 result
= UpdateFolderCount( GPtr
->calculatedVCB
, theParID
, NULL
,
5621 ((isHFSPlus
) ? kHFSPlusFolderRecord
: kHFSFolderRecord
),
5624 /* update our header node on disk from our BTreeControlBlock */
5625 UpdateBTreeHeader( GPtr
->calculatedCatalogFCB
);
5629 } /* FixMissingDirectory */
5632 static HFSCatalogNodeID
5633 GetObjectID( CatalogRecord
* theRecPtr
)
5635 HFSCatalogNodeID myObjID
;
5637 switch ( theRecPtr
->recordType
) {
5638 case kHFSPlusFolderRecord
:
5639 myObjID
= theRecPtr
->hfsPlusFolder
.folderID
;
5641 case kHFSPlusFileRecord
:
5642 myObjID
= theRecPtr
->hfsPlusFile
.fileID
;
5644 case kHFSFolderRecord
:
5645 myObjID
= theRecPtr
->hfsFolder
.folderID
;
5647 case kHFSFileRecord
:
5648 myObjID
= theRecPtr
->hfsFile
.fileID
;
5658 /* Function: CreateFileByName
5660 * Description: Create a file with given fileName of type fileType containing
5661 * data of length dataLen. This function assumes that the name of symlink
5662 * to be created is passed as UTF8
5665 * 1. GPtr - pointer to global scavenger structure
5666 * 2. parentID - ID of parent directory to create the new file.
5667 * 3. fileName - name of the file to create in UTF8 format.
5668 * 4. fileNameLen - length of the filename to be created.
5669 * If the volume is HFS Plus, the filename is delimited to
5670 * kHFSPlusMaxFileNameChars characters.
5671 * If the volume is plain HFS, the filename is delimited to
5672 * kHFSMaxFileNameChars characters.
5673 * 5. fileType - file type (currently supported S_IFREG, S_IFLNK).
5674 * 6. data - data content of first data fork of the file
5675 * 7. dataLen - length of data to be written
5678 * returns zero on success, non-zero on failure.
5679 * memFullErr - Not enough memory
5680 * paramErr - Invalid paramter
5682 OSErr
CreateFileByName(SGlobPtr GPtr
, UInt32 parentID
, UInt16 fileType
, u_char
*fileName
, unsigned int filenameLen
, u_char
*data
, unsigned int dataLen
)
5686 Boolean isCatUpdated
= false;
5689 CatalogRecord catRecord
;
5691 CatalogKey threadKey
;
5695 UInt32 startBlock
= 0;
5696 UInt32 blockCount
= 0;
5699 isHFSPlus
= VolumeObjectIsHFSPlus();
5701 /* Construct unicode name for file name to construct catalog key */
5703 /* Convert utf8 filename to Unicode filename */
5706 if (filenameLen
< kHFSPlusMaxFileNameChars
) {
5707 (void) utf_decodestr (fileName
, filenameLen
, fName
.ustr
.unicode
, &namelen
, sizeof(fName
.ustr
.unicode
));
5709 fName
.ustr
.length
= namelen
;
5711 /* The resulting may result in more than kHFSPlusMaxFileNameChars chars */
5712 UInt16
*unicodename
;
5714 /* Allocate a large array to convert the utf-8 to utf-16 */
5715 unicodename
= malloc (filenameLen
* 4);
5716 if (unicodename
== NULL
) {
5721 (void) utf_decodestr (fileName
, filenameLen
, unicodename
, &namelen
, filenameLen
* 4);
5724 /* Chopping unicode string based on length might affect unicode
5725 * chars that take more than one UInt16 - very rare possiblity.
5727 if (namelen
> kHFSPlusMaxFileNameChars
) {
5728 namelen
= kHFSPlusMaxFileNameChars
;
5731 memcpy (fName
.ustr
.unicode
, unicodename
, (namelen
* 2));
5733 fName
.ustr
.length
= namelen
;
5736 if (filenameLen
> kHFSMaxFileNameChars
) {
5737 filenameLen
= kHFSMaxFileNameChars
;
5739 fName
.pstr
[0] = filenameLen
;
5740 memcpy(&fName
.pstr
[1], fileName
, filenameLen
);
5743 /* Make sure that a file with same name does not exist in parent dir */
5744 BuildCatalogKey(parentID
, &fName
, isHFSPlus
, &catKey
);
5745 err
= SearchBTreeRecord(GPtr
->calculatedCatalogFCB
, &catKey
, kNoHint
, NULL
,
5746 &catRecord
, &recordSize
, &hint
);
5747 if (err
!= fsBTRecordNotFoundErr
) {
5749 plog ("%s: %s probably exists in dirID = %d (err=%d)\n", __FUNCTION__
, fileName
, parentID
, err
);
5756 /* Calculate correct number of blocks required for data */
5757 if (dataLen
% (GPtr
->calculatedVCB
->vcbBlockSize
)) {
5758 blockCount
= (dataLen
/ (GPtr
->calculatedVCB
->vcbBlockSize
)) + 1;
5760 blockCount
= dataLen
/ (GPtr
->calculatedVCB
->vcbBlockSize
);
5764 /* Allocate disk space for the data */
5765 err
= AllocateContigBitmapBits (GPtr
->calculatedVCB
, blockCount
, &startBlock
);
5768 plog ("%s: Not enough disk space (err=%d)\n", __FUNCTION__
, err
);
5773 /* Write the data to the blocks */
5774 err
= WriteBufferToDisk(GPtr
, startBlock
, blockCount
, data
, dataLen
);
5777 plog ("%s: Error in writing data of %s to disk (err=%d)\n", __FUNCTION__
, fileName
, err
);
5784 /* Build and insert thread record */
5785 nextCNID
= GPtr
->calculatedVCB
->vcbNextCatalogID
;
5786 if (!isHFSPlus
&& nextCNID
== 0xffffFFFF) {
5789 recordSize
= BuildThreadRec(&catKey
, &catRecord
, isHFSPlus
, false);
5791 BuildCatalogKey(nextCNID
, NULL
, isHFSPlus
, &threadKey
);
5792 err
= InsertBTreeRecord(GPtr
->calculatedCatalogFCB
, &threadKey
, &catRecord
,
5793 recordSize
, &hint
);
5794 if (err
== fsBTDuplicateRecordErr
&& isHFSPlus
) {
5795 /* Allow CNIDs on HFS Plus volumes to wrap around */
5797 if (nextCNID
< kHFSFirstUserCatalogNodeID
) {
5798 GPtr
->calculatedVCB
->vcbAttributes
|= kHFSCatalogNodeIDsReusedMask
;
5799 MarkVCBDirty(GPtr
->calculatedVCB
);
5800 nextCNID
= kHFSFirstUserCatalogNodeID
;
5808 plog ("%s: Error inserting thread record for file = %s (err=%d)\n", __FUNCTION__
, fileName
, err
);
5813 /* Build file record */
5814 recordSize
= BuildFileRec(fileType
, 0666, nextCNID
, isHFSPlus
, &catRecord
);
5815 if (recordSize
== 0) {
5817 plog ("%s: Incorrect fileType\n", __FUNCTION__
);
5820 /* Remove the thread record inserted above */
5821 err
= DeleteBTreeRecord (GPtr
->calculatedCatalogFCB
, &threadKey
);
5824 plog ("%s: Error in removing thread record\n", __FUNCTION__
);
5831 /* Update startBlock, blockCount, etc */
5833 catRecord
.hfsPlusFile
.dataFork
.logicalSize
= dataLen
;
5834 catRecord
.hfsPlusFile
.dataFork
.totalBlocks
= blockCount
;
5835 catRecord
.hfsPlusFile
.dataFork
.extents
[0].startBlock
= startBlock
;
5836 catRecord
.hfsPlusFile
.dataFork
.extents
[0].blockCount
= blockCount
;
5838 catRecord
.hfsFile
.dataLogicalSize
= dataLen
;
5839 catRecord
.hfsFile
.dataPhysicalSize
= blockCount
* GPtr
->calculatedVCB
->vcbBlockSize
;
5840 catRecord
.hfsFile
.dataExtents
[0].startBlock
= startBlock
;
5841 catRecord
.hfsFile
.dataExtents
[0].blockCount
= blockCount
;
5844 /* Insert catalog file record */
5845 err
= InsertBTreeRecord(GPtr
->calculatedCatalogFCB
, &catKey
, &catRecord
, recordSize
, &hint
);
5847 isCatUpdated
= true;
5850 plog ("Created \"%s\" with ID = %d startBlock = %d, blockCount = %d, dataLen = %d\n", fileName
, nextCNID
, startBlock
, blockCount
, dataLen
);
5854 plog ("%s: Error in inserting file record for file = %s (err=%d)\n", __FUNCTION__
, fileName
, err
);
5857 /* remove the thread record inserted above */
5858 err
= DeleteBTreeRecord (GPtr
->calculatedCatalogFCB
, &threadKey
);
5861 plog ("%s: Error in removing thread record\n", __FUNCTION__
);
5868 /* Update volume header */
5869 GPtr
->calculatedVCB
->vcbNextCatalogID
= nextCNID
+ 1;
5870 if (GPtr
->calculatedVCB
->vcbNextCatalogID
< kHFSFirstUserCatalogNodeID
) {
5871 GPtr
->calculatedVCB
->vcbAttributes
|= kHFSCatalogNodeIDsReusedMask
;
5872 GPtr
->calculatedVCB
->vcbNextCatalogID
= kHFSFirstUserCatalogNodeID
;
5874 MarkVCBDirty( GPtr
->calculatedVCB
);
5876 /* update our header node on disk from our BTreeControlBlock */
5877 UpdateBTreeHeader(GPtr
->calculatedCatalogFCB
);
5879 /* update parent directory to reflect addition of new file */
5880 err
= UpdateFolderCount(GPtr
->calculatedVCB
, parentID
, NULL
, kHFSPlusFileRecord
, kNoHint
, 1);
5883 plog ("%s: Error in updating parent folder count (err=%d)\n", __FUNCTION__
, err
);
5889 /* On error, if catalog record was not inserted and disk block were allocated,
5890 * deallocate the blocks
5892 if (err
&& (isCatUpdated
== false) && startBlock
) {
5893 ReleaseBitmapBits (startBlock
, blockCount
);
5897 } /* CreateFileByName */
5899 /* Function: CreateDirByName
5901 * Description: Create directory with name dirName in a directory with ID as
5902 * parentID. The function assumes that the dirName passed is ASCII.
5905 * GPtr - global scavenger structure pointer
5906 * dirName - name of directory to be created
5907 * parentID - dirID of the parent directory for new directory
5910 * on success, ID of the new directory created.
5914 UInt32
CreateDirByName(SGlob
*GPtr
, const u_char
*dirName
, const UInt32 parentID
)
5926 CatalogRecord catRec
;
5928 isHFSPlus
= VolumeObjectIsHFSPlus( );
5929 fcbPtr
= GPtr
->calculatedCatalogFCB
;
5930 nameLen
= (int)strlen( (char *)dirName
);
5935 myName
.ustr
.length
= nameLen
;
5936 for ( i
= 0; i
< myName
.ustr
.length
; i
++ )
5937 myName
.ustr
.unicode
[ i
] = (u_int16_t
) dirName
[ i
];
5941 myName
.pstr
[0] = nameLen
;
5942 memcpy( &myName
.pstr
[1], &dirName
[0], nameLen
);
5945 // see if we already have a lost and found directory
5946 BuildCatalogKey( parentID
, &myName
, isHFSPlus
, &myKey
);
5947 result
= SearchBTreeRecord( fcbPtr
, &myKey
, kNoHint
, NULL
, &catRec
, &recSize
, &hint
);
5948 if ( result
== noErr
) {
5950 if ( catRec
.recordType
== kHFSPlusFolderRecord
)
5951 return( catRec
.hfsPlusFolder
.folderID
);
5953 else if ( catRec
.recordType
== kHFSFolderRecord
)
5954 return( catRec
.hfsFolder
.folderID
);
5955 return( 0 ); // something already there but not a directory
5958 // insert new directory and thread record into the catalog
5959 nextCNID
= GPtr
->calculatedVCB
->vcbNextCatalogID
;
5960 if ( !isHFSPlus
&& nextCNID
== 0xFFFFFFFF )
5963 recSize
= BuildThreadRec( &myKey
, &catRec
, isHFSPlus
, true );
5967 BuildCatalogKey( nextCNID
, NULL
, isHFSPlus
, &key
);
5968 result
= InsertBTreeRecord( fcbPtr
, &key
, &catRec
, recSize
, &hint
);
5969 if ( result
== fsBTDuplicateRecordErr
&& isHFSPlus
) {
5971 * Allow CNIDs on HFS Plus volumes to wrap around
5974 if ( nextCNID
< kHFSFirstUserCatalogNodeID
) {
5975 GPtr
->calculatedVCB
->vcbAttributes
|= kHFSCatalogNodeIDsReusedMask
;
5976 MarkVCBDirty( GPtr
->calculatedVCB
);
5977 nextCNID
= kHFSFirstUserCatalogNodeID
;
5986 myMode
= ( GPtr
->lostAndFoundMode
== 0 ) ? 01777 : GPtr
->lostAndFoundMode
;
5987 recSize
= BuildFolderRec( GPtr
, myMode
, nextCNID
, isHFSPlus
, &catRec
);
5988 result
= InsertBTreeRecord( fcbPtr
, &myKey
, &catRec
, recSize
, &hint
);
5992 /* Update volume header */
5993 GPtr
->calculatedVCB
->vcbNextCatalogID
= nextCNID
+ 1;
5994 if ( GPtr
->calculatedVCB
->vcbNextCatalogID
< kHFSFirstUserCatalogNodeID
) {
5995 GPtr
->calculatedVCB
->vcbAttributes
|= kHFSCatalogNodeIDsReusedMask
;
5996 GPtr
->calculatedVCB
->vcbNextCatalogID
= kHFSFirstUserCatalogNodeID
;
5998 MarkVCBDirty( GPtr
->calculatedVCB
);
6000 /* update parent directory to reflect addition of new directory */
6001 result
= UpdateFolderCount( GPtr
->calculatedVCB
, parentID
, NULL
, kHFSPlusFolderRecord
, kNoHint
, 1 );
6003 /* update our header node on disk from our BTreeControlBlock */
6004 UpdateBTreeHeader( GPtr
->calculatedCatalogFCB
);
6008 } /* CreateDirByName */
6011 CountFolderItems(SGlobPtr GPtr
, UInt32 folderID
, Boolean isHFSPlus
, UInt32
*itemCount
, UInt32
*folderCount
)
6013 SFCB
*fcb
= GPtr
->calculatedCatalogFCB
;
6015 BTreeIterator iterator
;
6016 FSBufferDescriptor btRecord
;
6018 HFSPlusCatalogFolder catRecord
;
6019 HFSPlusCatalogFile catFile
;
6021 HFSPlusCatalogKey
*key
;
6022 UInt16 recordSize
= 0;
6023 int fCount
= 0, iCount
= 0;
6025 ClearMemory(&iterator
, sizeof(iterator
));
6026 key
= (HFSPlusCatalogKey
*)&iterator
.key
;
6027 BuildCatalogKey(folderID
, NULL
, isHFSPlus
, (CatalogKey
*)key
);
6028 btRecord
.bufferAddress
= &catRecord
;
6029 btRecord
.itemCount
= 1;
6030 btRecord
.itemSize
= sizeof(catRecord
);
6032 for (err
= BTSearchRecord(fcb
, &iterator
, kNoHint
, &btRecord
, &recordSize
, &iterator
);
6034 err
= BTIterateRecord(fcb
, kBTreeNextRecord
, &iterator
, &btRecord
, &recordSize
)) {
6035 if (catRecord
.catRecord
.recordType
== kHFSPlusFolderThreadRecord
||
6036 catRecord
.catRecord
.recordType
== kHFSPlusFileThreadRecord
||
6037 catRecord
.catRecord
.recordType
== kHFSFolderThreadRecord
||
6038 catRecord
.catRecord
.recordType
== kHFSFileThreadRecord
)
6040 if (key
->parentID
!= folderID
)
6043 (catRecord
.catRecord
.recordType
== kHFSPlusFileRecord
) &&
6044 (catRecord
.catFile
.flags
& kHFSHasLinkChainMask
) &&
6045 (catRecord
.catFile
.userInfo
.fdType
== kHFSAliasType
) &&
6046 (catRecord
.catFile
.userInfo
.fdCreator
== kHFSAliasCreator
) &&
6047 (key
->parentID
!= GPtr
->filelink_priv_dir_id
)) {
6048 // It's a directory hard link, which counts as a directory here
6051 if (catRecord
.catRecord
.recordType
== kHFSPlusFolderRecord
)
6056 *itemCount
= iCount
;
6058 *folderCount
= fCount
;
6062 * Build a catalog node folder record with the given input.
6065 BuildFolderRec( SGlob
*GPtr
, u_int16_t theMode
, UInt32 theObjID
, Boolean isHFSPlus
, CatalogRecord
* theRecPtr
)
6069 UInt32 vCount
= 0, fCount
= 0;
6071 ClearMemory( (Ptr
)theRecPtr
, sizeof(*theRecPtr
) );
6073 CountFolderItems(GPtr
, theObjID
, isHFSPlus
, &vCount
, &fCount
);
6075 createTime
= GetTimeUTC();
6076 theRecPtr
->hfsPlusFolder
.recordType
= kHFSPlusFolderRecord
;
6077 theRecPtr
->hfsPlusFolder
.folderID
= theObjID
;
6078 theRecPtr
->hfsPlusFolder
.createDate
= createTime
;
6079 theRecPtr
->hfsPlusFolder
.contentModDate
= createTime
;
6080 theRecPtr
->hfsPlusFolder
.attributeModDate
= createTime
;
6081 theRecPtr
->hfsPlusFolder
.bsdInfo
.ownerID
= getuid( );
6082 theRecPtr
->hfsPlusFolder
.bsdInfo
.groupID
= getgid( );
6083 theRecPtr
->hfsPlusFolder
.bsdInfo
.fileMode
= S_IFDIR
;
6084 theRecPtr
->hfsPlusFolder
.bsdInfo
.fileMode
|= theMode
;
6085 theRecPtr
->hfsPlusFolder
.valence
= vCount
;
6086 recSize
= sizeof(HFSPlusCatalogFolder
);
6087 if (VolumeObjectIsHFSX(GPtr
)) {
6088 theRecPtr
->hfsPlusFolder
.flags
|= kHFSHasFolderCountMask
;
6089 theRecPtr
->hfsPlusFolder
.folderCount
= fCount
;
6093 createTime
= GetTimeLocal( true );
6094 theRecPtr
->hfsFolder
.recordType
= kHFSFolderRecord
;
6095 theRecPtr
->hfsFolder
.folderID
= theObjID
;
6096 theRecPtr
->hfsFolder
.createDate
= createTime
;
6097 theRecPtr
->hfsFolder
.modifyDate
= createTime
;
6098 theRecPtr
->hfsFolder
.valence
= vCount
;
6099 recSize
= sizeof(HFSCatalogFolder
);
6104 } /* BuildFolderRec */
6108 * Build a catalog node thread record from a catalog key
6109 * and return the size of the record.
6112 BuildThreadRec( CatalogKey
* theKeyPtr
, CatalogRecord
* theRecPtr
,
6113 Boolean isHFSPlus
, Boolean isDirectory
)
6118 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)theKeyPtr
;
6119 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)theRecPtr
;
6121 size
= sizeof(HFSPlusCatalogThread
);
6123 rec
->recordType
= kHFSPlusFolderThreadRecord
;
6125 rec
->recordType
= kHFSPlusFileThreadRecord
;
6127 rec
->parentID
= key
->parentID
;
6128 bcopy(&key
->nodeName
, &rec
->nodeName
,
6129 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
6131 /* HFS Plus has varaible sized thread records */
6132 size
-= (sizeof(rec
->nodeName
.unicode
) -
6133 (rec
->nodeName
.length
* sizeof(UniChar
)));
6135 else /* HFS standard */ {
6136 HFSCatalogKey
*key
= (HFSCatalogKey
*)theKeyPtr
;
6137 HFSCatalogThread
*rec
= (HFSCatalogThread
*)theRecPtr
;
6139 size
= sizeof(HFSCatalogThread
);
6142 rec
->recordType
= kHFSFolderThreadRecord
;
6144 rec
->recordType
= kHFSFileThreadRecord
;
6145 rec
->parentID
= key
->parentID
;
6146 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
6151 } /* BuildThreadRec */
6153 /* Function: BuildFileRec
6155 * Description: Build a catalog file record with given fileID, fileType
6159 * 1. fileType - currently supports S_IFREG, S_IFLNK
6160 * 2. fileMode - file mode desired.
6161 * 3. fileID - file ID
6162 * 4. isHFSPlus - indicates whether the record is being created for
6163 * HFSPlus volume or plain HFS volume.
6164 * 5. catRecord - pointer to catalog record
6167 * returns size of the catalog record.
6168 * on success, non-zero value; on failure, zero.
6170 static int BuildFileRec(UInt16 fileType
, UInt16 fileMode
, UInt32 fileID
, Boolean isHFSPlus
, CatalogRecord
*catRecord
)
6172 UInt16 recordSize
= 0;
6175 /* We only support creating S_IFREG and S_IFLNK and S_IFLNK is not supported
6178 if (((fileType
!= S_IFREG
) && (fileType
!= S_IFLNK
)) ||
6179 ((isHFSPlus
== false) && (fileType
== S_IFLNK
))) {
6183 ClearMemory((Ptr
)catRecord
, sizeof(*catRecord
));
6186 createTime
= GetTimeUTC();
6187 catRecord
->hfsPlusFile
.recordType
= kHFSPlusFileRecord
;
6188 catRecord
->hfsPlusFile
.fileID
= fileID
;
6189 catRecord
->hfsPlusFile
.createDate
= createTime
;
6190 catRecord
->hfsPlusFile
.contentModDate
= createTime
;
6191 catRecord
->hfsPlusFile
.attributeModDate
= createTime
;
6192 catRecord
->hfsPlusFile
.bsdInfo
.ownerID
= getuid();
6193 catRecord
->hfsPlusFile
.bsdInfo
.groupID
= getgid();
6194 catRecord
->hfsPlusFile
.bsdInfo
.fileMode
= fileType
;
6195 catRecord
->hfsPlusFile
.bsdInfo
.fileMode
|= fileMode
;
6196 if (fileType
== S_IFLNK
) {
6197 catRecord
->hfsPlusFile
.userInfo
.fdType
= kSymLinkFileType
;
6198 catRecord
->hfsPlusFile
.userInfo
.fdCreator
= kSymLinkCreator
;
6200 catRecord
->hfsPlusFile
.userInfo
.fdType
= kTextFileType
;
6201 catRecord
->hfsPlusFile
.userInfo
.fdCreator
= kTextFileCreator
;
6203 recordSize
= sizeof(HFSPlusCatalogFile
);
6206 createTime
= GetTimeLocal(true);
6207 catRecord
->hfsFile
.recordType
= kHFSFileRecord
;
6208 catRecord
->hfsFile
.fileID
= fileID
;
6209 catRecord
->hfsFile
.createDate
= createTime
;
6210 catRecord
->hfsFile
.modifyDate
= createTime
;
6211 catRecord
->hfsFile
.userInfo
.fdType
= kTextFileType
;
6212 catRecord
->hfsFile
.userInfo
.fdCreator
= kTextFileCreator
;
6213 recordSize
= sizeof(HFSCatalogFile
);
6218 } /* BuildFileRec */
6220 /* Function: BuildAttributeKey
6222 * Build attribute key based on given information like -
6223 * fileID, startBlock, attribute name and attribute name length.
6225 * Note that the attribute name is the UTF-8 format string.
6227 static void BuildAttributeKey(u_int32_t fileID
, u_int32_t startBlock
,
6228 unsigned char *attrName
, u_int16_t attrNameLen
, HFSPlusAttrKey
*key
)
6230 size_t attrNameLenBytes
;
6232 assert(VolumeObjectIsHFSPlus() == true);
6235 key
->fileID
= fileID
;
6236 key
->startBlock
= startBlock
;
6238 /* Convert UTF-8 attribute name to unicode */
6239 (void) utf_decodestr(attrName
, attrNameLen
, key
->attrName
, &attrNameLenBytes
, sizeof(key
->attrName
));
6240 key
->attrNameLen
= attrNameLenBytes
/ 2;
6242 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
+ attrNameLenBytes
;
6245 /* Delete catalog record and thread record for given ID. On successful
6246 * deletion, this function also updates the valence and folder count for
6247 * the parent directory and the file/folder count in the volume header.
6249 * Returns - zero on success, non-zero on failure.
6251 static int DeleteCatalogRecordByID(SGlobPtr GPtr
, uint32_t id
, Boolean for_rename
)
6259 isHFSPlus
= VolumeObjectIsHFSPlus();
6261 /* Lookup the catalog record to move */
6262 retval
= GetCatalogRecordByID(GPtr
, id
, isHFSPlus
, &key
, &rec
, &recsize
);
6267 /* Delete the record */
6269 retval
= DeleteCatalogNode(GPtr
->calculatedVCB
,
6270 key
.hfsPlus
.parentID
,
6271 (const CatalogName
*)&key
.hfsPlus
.nodeName
,
6272 kNoHint
, for_rename
);
6274 retval
= DeleteCatalogNode(GPtr
->calculatedVCB
,
6276 (const CatalogName
*)&key
.hfs
.nodeName
,
6277 kNoHint
, for_rename
);
6280 /* If deletion of record succeeded, and the operation was not
6281 * being performed for rename, and the volume is HFS+, try
6282 * deleting all extended attributes for this file/folder
6284 if ((retval
== 0) && (for_rename
== false) && (isHFSPlus
== true)) {
6285 /* Delete all attributes associated with this ID */
6286 retval
= DeleteAllAttrsByID(GPtr
, id
);
6292 /* Move a catalog record with given ID to a new parent directory with given
6293 * parentID. This function should only be called for HFS+ volumes.
6294 * This function removes the catalog record from old parent and inserts
6295 * it back with the new parent ID. It also takes care of updating the
6296 * parent directory counts. Note that this function clears kHFSHasLinkChainBit
6297 * from the catalog record flags.
6299 * On success, returns zero. On failure, returns non-zero.
6301 static int MoveCatalogRecordByID(SGlobPtr GPtr
, uint32_t id
, uint32_t new_parentid
)
6308 Boolean isFolder
= false;
6309 BTreeIterator iterator
;
6311 assert (VolumeObjectIsHFSPlus() == true);
6313 /* Lookup the catalog record to move */
6314 retval
= GetCatalogRecordByID(GPtr
, id
, true, &key
, &rec
, &recsize
);
6319 /* Delete the record and its thread from original location.
6320 * For file records, do not deallocate original extents.
6322 retval
= DeleteCatalogRecordByID(GPtr
, id
, true);
6327 key
.hfsPlus
.parentID
= new_parentid
;
6328 /* The record being moved should not have linkChainMask set */
6329 if (rec
.recordType
== kHFSPlusFolderRecord
) {
6330 rec
.hfsPlusFolder
.flags
&= ~kHFSHasLinkChainMask
;
6332 } else if (rec
.recordType
== kHFSPlusFileRecord
) {
6333 rec
.hfsPlusFile
.flags
&= ~kHFSHasLinkChainMask
;
6337 /* Insert the catalog record with new parent */
6338 retval
= InsertBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
, &rec
,
6344 /* Insert the new thread record */
6345 recsize
= BuildThreadRec(&key
, &rec
, true, isFolder
);
6346 BuildCatalogKey(id
, NULL
, true, &key
);
6347 retval
= InsertBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
, &rec
,
6353 /* Update the counts in the new parent directory and volume header */
6354 ClearMemory(&iterator
, sizeof(iterator
));
6355 retval
= GetCatalogRecordByID(GPtr
, new_parentid
, true, &key
, &rec
, &recsize
);
6357 if ((retval
== btNotFound
) && (GPtr
->CBTStat
& S_Orphan
)) {
6358 /* No need for re-repair minor repair order because
6359 * we are failing on updating the parent directory.
6365 if (rec
.recordType
!= kHFSPlusFolderRecord
) {
6369 rec
.hfsPlusFolder
.valence
++;
6370 if ((isFolder
== true) &&
6371 (rec
.hfsPlusFolder
.flags
& kHFSHasFolderCountMask
)) {
6372 rec
.hfsPlusFolder
.folderCount
++;
6375 retval
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
,
6376 kNoHint
, &rec
, recsize
, &hint
);
6381 if (isFolder
== true) {
6382 GPtr
->calculatedVCB
->vcbFolderCount
++;
6384 GPtr
->calculatedVCB
->vcbFileCount
++;
6386 GPtr
->VIStat
|= S_MDB
;
6387 GPtr
->CBTStat
|= S_BTH
; /* leaf record count changed */
6393 /* The function deletes all extended attributes associated with a given
6394 * file/folder ID. The function takes care of deallocating allocation blocks
6395 * associated with extent based attributes.
6397 * Note: This function deletes *all* attributes for a given file/folder.
6398 * To delete a single attribute record using a key, use delete_attr_record().
6400 * On success, returns zero. On failure, returns non-zero.
6402 static int DeleteAllAttrsByID(SGlobPtr GPtr
, uint32_t id
)
6405 BTreeIterator iterator
;
6406 FSBufferDescriptor btrec
;
6407 HFSPlusAttrKey
*attr_key
;
6408 HFSPlusAttrRecord attr_record
;
6411 /* Initialize the iterator, attribute key, and attribute record */
6412 ClearMemory(&iterator
, sizeof(BTreeIterator
));
6413 attr_key
= (HFSPlusAttrKey
*)&iterator
.key
;
6414 attr_key
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
6415 attr_key
->fileID
= id
;
6417 ClearMemory(&btrec
, sizeof(FSBufferDescriptor
));
6418 btrec
.bufferAddress
= &attr_record
;
6419 btrec
.itemCount
= 1;
6420 btrec
.itemSize
= sizeof(HFSPlusAttrRecord
);
6422 /* Search for attribute with NULL name which will place the
6423 * iterator just before the first record for given id.
6425 retval
= BTSearchRecord(GPtr
->calculatedAttributesFCB
, &iterator
,
6426 kInvalidMRUCacheKey
, &btrec
, &record_size
, &iterator
);
6427 if ((retval
!= 0) && (retval
!= btNotFound
)) {
6431 retval
= BTIterateRecord(GPtr
->calculatedAttributesFCB
, kBTreeNextRecord
,
6432 &iterator
, &btrec
, &record_size
);
6433 while ((retval
== 0) && (attr_key
->fileID
== id
)) {
6434 /* Delete attribute record and deallocate extents, if any */
6435 retval
= delete_attr_record(GPtr
, attr_key
, &attr_record
);
6440 retval
= BTIterateRecord(GPtr
->calculatedAttributesFCB
,
6441 kBTreeNextRecord
, &iterator
, &btrec
, &record_size
);
6444 if (retval
== btNotFound
) {
6452 /* The function deletes an extented attribute record when the corresponding
6453 * record and key are provided. If the record is an extent-based attribute,
6454 * it also takes care to deallocate all allocation blocks associated with
6457 * Note: This function does not delete all attribute records associated
6458 * with the file/folder ID in the attribute key. To delete all attributes
6459 * for given file/folder ID, use DeleteAllAttrsByID().
6461 * On success, returns zero. On failure, returns non-zero.
6463 static int delete_attr_record(SGlobPtr GPtr
, HFSPlusAttrKey
*attr_key
, HFSPlusAttrRecord
*attr_record
)
6466 UInt32 num_blocks_freed
;
6467 Boolean last_extent
;
6469 retval
= DeleteBTreeRecord(GPtr
->calculatedAttributesFCB
, attr_key
);
6471 /* Set bits to write back attribute btree header and map */
6472 GPtr
->ABTStat
|= S_BTH
+ S_BTM
;
6474 if (attr_record
->recordType
== kHFSPlusAttrForkData
) {
6475 retval
= ReleaseExtents(GPtr
->calculatedVCB
,
6476 attr_record
->forkData
.theFork
.extents
,
6477 &num_blocks_freed
, &last_extent
);
6478 } else if (attr_record
->recordType
== kHFSPlusAttrExtents
) {
6479 retval
= ReleaseExtents(GPtr
->calculatedVCB
,
6480 attr_record
->overflowExtents
.extents
,
6481 &num_blocks_freed
, &last_extent
);
6488 /*------------------------------------------------------------------------------
6490 Routine: ZeroFillUnusedNodes
6492 Function: Write zeroes to all unused nodes of a given B-tree.
6494 Input: GPtr - pointer to scavenger global area
6495 fileRefNum - refnum of BTree file
6497 Output: ZeroFillUnusedNodes - function result:
6500 ------------------------------------------------------------------------------*/
6502 static int ZeroFillUnusedNodes(SGlobPtr GPtr
, short fileRefNum
)
6504 BTreeControlBlock
*btcb
= GetBTreeControlBlock(fileRefNum
);
6505 unsigned char *bitmap
= (unsigned char *) ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
;
6506 unsigned char mask
= 0x80;
6509 BlockDescriptor node
;
6513 for (nodeNum
= 0; nodeNum
< btcb
->totalNodes
; ++nodeNum
)
6515 if ((*bitmap
& mask
) == 0)
6517 /* Read the raw node, without going through hfs_swap_BTNode. */
6518 err
= btcb
->getBlockProc(btcb
->fcbPtr
, nodeNum
, kGetBlock
|kGetEmptyBlock
, &node
);
6521 if (debug
) plog("Couldn't read node #%u\n", nodeNum
);
6525 /* Fill the node with zeroes. */
6526 bzero(node
.buffer
, node
.blockSize
);
6528 /* Release and write the node without going through hfs_swap_BTNode. */
6529 (void) btcb
->releaseBlockProc(btcb
->fcbPtr
, &node
, kReleaseBlock
|kMarkBlockDirty
);
6533 /* Move to the next bit in the bitmap. */
6543 } /* end ZeroFillUnusedNodes */