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 OSErr
CreateFileByName(SGlobPtr GPtr
, UInt32 parentID
, UInt16 fileType
, u_char
*fileName
, unsigned int filenameLen
, u_char
*data
, unsigned int dataLen
);
116 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
= 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
= 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
= 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
= 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 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 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 plog("%s: Could not get overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__
, fileID
, forkType
, extentStartBlock
);
2959 /* Zero out the bad extent entry and all entries after it */
2961 for (i
= badExtentIndex
; i
< GPtr
->numExtents
; i
++) {
2962 extentRecord
.hfsPlus
[i
].startBlock
= 0;
2963 extentRecord
.hfsPlus
[i
].blockCount
= 0;
2966 for (i
= badExtentIndex
; i
< GPtr
->numExtents
; i
++) {
2967 extentRecord
.hfs
[i
].startBlock
= 0;
2968 extentRecord
.hfs
[i
].blockCount
= 0;
2972 /* Write the extent record back */
2973 err
= ReplaceBTreeRecord(GPtr
->calculatedExtentsFCB
, &extentKey
, hint
,
2974 &extentRecord
, recordSize
, &hint
);
2976 plog("%s: Could not replace overflow extents record for fileID %u, fork %u, start block %u\n", __FUNCTION__
, fileID
, forkType
, extentStartBlock
);
2981 /* The startBlock for extent record with bad extent entry is updated
2982 * because we use this startBlock later to lookup next extent record
2983 * for this file and forktype in overflow extent btree which should
2984 * be deleted. By incrementing the startBlock by one, we ensure that
2985 * we find the next record, if any, that should be deleted instead of
2986 * finding the same record that was updated above.
2991 del_overflow_extents
:
2992 /* Search for overflow extent records. We should get a valid record only
2993 * if the bad extent entry was the first entry in the extent overflow
2994 * record. For all other cases, the search record will return an error
2996 BuildExtentKey (isHFSPlus
, forkType
, fileID
, extentStartBlock
, &extentKey
);
2997 err
= SearchBTreeRecord (GPtr
->calculatedExtentsFCB
, &extentKey
, kNoHint
,
2998 &extentKey
, &extentRecord
, &recordSize
, &hint
);
2999 if ((err
!= noErr
) && (err
!= btNotFound
)) {
3000 goto create_symlink
;
3003 /* If we got error, check the next record */
3004 if (err
== btNotFound
) {
3005 err
= GetBTreeRecord(GPtr
->calculatedExtentsFCB
, 1, &extentKey
, &extentRecord
,
3006 &recordSize
, &hint
);
3009 while (err
== noErr
) {
3010 /* Check if the record has correct fileID, forkType */
3012 if ((fileID
!= extentKey
.hfsPlus
.fileID
) ||
3013 (forkType
!= extentKey
.hfsPlus
.forkType
)) {
3016 foundStartBlock
= extentKey
.hfsPlus
.startBlock
;
3018 if ((fileID
!= extentKey
.hfs
.fileID
) ||
3019 (forkType
!= extentKey
.hfs
.forkType
)) {
3022 foundStartBlock
= extentKey
.hfs
.startBlock
;
3025 /* Delete the extent record */
3026 err
= DeleteBTreeRecord(GPtr
->calculatedExtentsFCB
, &extentKey
);
3027 DPRINTF (d_info
, "%s: Deleting extent overflow for fileID=%u, forkType=%u, startBlock=%u\n", __FUNCTION__
, fileID
, forkType
, foundStartBlock
);
3029 goto create_symlink
;
3033 /* Get the next extent record */
3034 err
= GetBTreeRecord(GPtr
->calculatedExtentsFCB
, 1, &extentKey
, &extentRecord
,
3035 &recordSize
, &hint
);
3038 if (err
== btNotFound
) {
3042 UpdateBTreeHeader(GPtr
->calculatedExtentsFCB
);
3045 /* Create symlink for repaired files in damaged files folder */
3046 if (didRepair
== true) {
3047 /* Create symlink for damaged files */
3048 (void) CreateCorruptFileSymlink(GPtr
, fileID
);
3055 /* Function: FixOrphanedFiles
3058 * Incorrect number of thread records get fixed in this function.
3060 * The function traverses the entire catalog Btree.
3062 * For a file/folder record, it tries to lookup its corresponding thread
3063 * record. If the thread record does not exist, or is not correct, a new
3064 * thread record is created. The parent ID, record type, and the name of
3065 * the file/folder are compared for correctness.
3066 * For plain HFS, a thread record is only looked-up if kHFSThreadExistsMask is set.
3068 * For a thread record, it tries to lookup its corresponding file/folder
3069 * record. If its does not exist or is not correct, the thread record
3070 * is deleted. The file/folder ID is compared for correctness.
3072 * Input: 1. GPtr - pointer to global scavenger area
3075 * zero means success
3076 * non-zero means failure
3078 static OSErr
FixOrphanedFiles ( SGlobPtr GPtr
)
3081 CatalogKey foundKey
;
3083 CatalogRecord record
;
3084 CatalogRecord threadRecord
;
3085 CatalogRecord record2
;
3086 HFSCatalogNodeID parentID
;
3087 HFSCatalogNodeID cNodeID
= 0;
3088 BTreeIterator savedIterator
;
3094 UInt16 threadRecordSize
;
3096 SInt16 foundRecType
;
3097 SInt16 selCode
= 0x8001; /* Get first record */
3099 BTreeControlBlock
*btcb
= GetBTreeControlBlock( kCalculatedCatalogRefNum
);
3100 Boolean isDirectory
;
3102 isHFSPlus
= VolumeObjectIsHFSPlus( );
3103 CopyMemory( &btcb
->lastIterator
, &savedIterator
, sizeof(BTreeIterator
) );
3107 // Save/Restore Iterator around calls to GetBTreeRecord
3108 CopyMemory( &savedIterator
, &btcb
->lastIterator
, sizeof(BTreeIterator
) );
3109 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &foundKey
, &record
, &recordSize
, &hint
);
3110 if ( err
!= noErr
) break;
3111 CopyMemory( &btcb
->lastIterator
, &savedIterator
, sizeof(BTreeIterator
) );
3113 selCode
= 1; // kNextRecord
3114 recordType
= record
.recordType
;
3116 isDirectory
= false;
3118 switch( recordType
)
3120 case kHFSFileRecord
:
3121 // If the small file is not supposed to have a thread, just break
3122 if ( ( record
.hfsFile
.flags
& kHFSThreadExistsMask
) == 0 )
3125 case kHFSFolderRecord
:
3126 case kHFSPlusFolderRecord
:
3127 case kHFSPlusFileRecord
:
3129 // Locate the thread associated with this record
3131 (void) CheckForStop( GPtr
); // rotate cursor
3133 parentID
= isHFSPlus
== true ? foundKey
.hfsPlus
.parentID
: foundKey
.hfs
.parentID
;
3136 switch( recordType
)
3138 case kHFSFolderRecord
:
3139 cNodeID
= record
.hfsFolder
.folderID
;
3142 case kHFSFileRecord
:
3143 cNodeID
= record
.hfsFile
.fileID
;
3145 case kHFSPlusFolderRecord
:
3146 cNodeID
= record
.hfsPlusFolder
.folderID
;
3149 case kHFSPlusFileRecord
:
3150 cNodeID
= record
.hfsPlusFile
.fileID
;
3154 //-- Build the key for the file thread
3155 BuildCatalogKey( cNodeID
, nil
, isHFSPlus
, &key
);
3157 err
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
,
3158 &tempKey
, &threadRecord
, &threadRecordSize
, &hint2
);
3160 /* We found a thread record for this file/folder record. */
3162 /* Check if the parent ID and nodeName are same, and recordType is as
3163 * expected. If not, we are missing a correct thread record. Force
3164 * btNotFound in such case.
3167 /* Check thread's recordType */
3168 foundRecType
= threadRecord
.hfsPlusThread
.recordType
;
3169 if (isDirectory
== true) {
3170 if (foundRecType
!= kHFSPlusFolderThreadRecord
) {
3172 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3173 plog ("\t%s: Folder thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3177 if (foundRecType
!= kHFSPlusFileThreadRecord
) {
3179 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3180 plog ("\t%s: File thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3185 /* Compare parent ID */
3186 if (parentID
!= threadRecord
.hfsPlusThread
.parentID
) {
3188 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3189 plog ("\t%s: parentID for id=%u do not match (fileKey=%u threadRecord=%u)\n", __FUNCTION__
, cNodeID
, parentID
, threadRecord
.hfsPlusThread
.parentID
);
3193 /* Compare nodeName from file/folder key and thread reocrd */
3194 if (!((foundKey
.hfsPlus
.nodeName
.length
== threadRecord
.hfsPlusThread
.nodeName
.length
)
3195 && (!bcmp(foundKey
.hfsPlus
.nodeName
.unicode
,
3196 threadRecord
.hfsPlusThread
.nodeName
.unicode
,
3197 foundKey
.hfsPlus
.nodeName
.length
* 2)))) {
3199 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3200 unsigned maxLength
= foundKey
.hfsPlus
.nodeName
.length
;
3201 if (maxLength
< threadRecord
.hfsPlusThread
.nodeName
.length
)
3202 maxLength
= threadRecord
.hfsPlusThread
.nodeName
.length
;
3204 plog ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__
, cNodeID
);
3205 if (cur_debug_level
& d_dump_record
)
3207 plog("\tFile/Folder record:\n");
3208 HexDump(&foundKey
, foundKey
.hfsPlus
.keyLength
+ 2, FALSE
);
3210 HexDump(&record
, recordSize
, FALSE
);
3212 plog("\tThread record:\n");
3213 HexDump(&tempKey
, tempKey
.hfsPlus
.keyLength
+ 2, FALSE
);
3215 HexDump(&threadRecord
, threadRecordSize
, FALSE
);
3221 /* If any of the above checks failed, delete the bad thread record */
3222 if (err
== btNotFound
) {
3223 (void) DeleteBTreeRecord(GPtr
->calculatedCatalogFCB
, &tempKey
);
3225 } else { /* plain HFS */
3226 /* Check thread's recordType */
3227 foundRecType
= threadRecord
.hfsThread
.recordType
;
3228 if (isDirectory
== true) {
3229 if (foundRecType
!= kHFSFolderThreadRecord
) {
3231 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3232 plog ("\t%s: Folder thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3236 if (foundRecType
!= kHFSFileThreadRecord
) {
3238 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3239 plog ("\t%s: File thread recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3244 /* Compare parent ID */
3245 if (parentID
!= threadRecord
.hfsThread
.parentID
) {
3247 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3248 plog ("\t%s: parentID for id=%u do not match (fileKey=%u threadRecord=%u)\n", __FUNCTION__
, cNodeID
, parentID
, threadRecord
.hfsThread
.parentID
);
3252 /* Compare nodeName from file/folder key and thread reocrd */
3253 if (!((foundKey
.hfs
.nodeName
[0] == threadRecord
.hfsThread
.nodeName
[0])
3254 && (!bcmp(&foundKey
.hfs
.nodeName
[1],
3255 &threadRecord
.hfsThread
.nodeName
[1],
3256 foundKey
.hfs
.nodeName
[0])))) {
3258 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3259 plog ("\t%s: nodeName for id=%u do not match\n", __FUNCTION__
, cNodeID
);
3263 /* If any of the above checks failed, delete the bad thread record */
3264 if (err
== btNotFound
) {
3265 (void) DeleteBTreeRecord(GPtr
->calculatedCatalogFCB
, &tempKey
);
3268 } /* err == noErr */
3270 // For missing thread records, just create the thread
3271 if ( err
== btNotFound
)
3273 // Create the missing thread record.
3275 isDirectory
= false;
3276 switch( recordType
)
3278 case kHFSFolderRecord
:
3279 case kHFSPlusFolderRecord
:
3284 //-- Fill out the data for the new file thread from the key
3285 // of catalog file/folder record
3286 recordSize
= BuildThreadRec( &foundKey
, &threadRecord
, isHFSPlus
,
3288 err
= InsertBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
,
3289 &threadRecord
, recordSize
, &threadHint
);
3290 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3291 plog ("\t%s: Created thread record for id=%u (err=%u)\n", __FUNCTION__
, cNodeID
, err
);
3298 case kHFSFolderThreadRecord
:
3299 case kHFSPlusFolderThreadRecord
:
3302 case kHFSFileThreadRecord
:
3303 case kHFSPlusFileThreadRecord
:
3305 // Find the catalog record, if it does not exist, delete the existing thread.
3307 BuildCatalogKey( record
.hfsPlusThread
.parentID
, (const CatalogName
*)&record
.hfsPlusThread
.nodeName
, isHFSPlus
, &key
);
3309 BuildCatalogKey( record
.hfsThread
.parentID
, (const CatalogName
*)&record
.hfsThread
.nodeName
, isHFSPlus
, &key
);
3311 err
= SearchBTreeRecord ( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &tempKey
, &record2
, &recordSize
, &hint2
);
3313 /* We found a file/folder record for this thread record. */
3315 /* Check if the file/folder ID are same and if the recordType is as
3316 * expected. If not, we are missing a correct file/folder record.
3317 * Delete the extra thread record
3320 /* Check recordType */
3321 foundRecType
= record2
.hfsPlusFile
.recordType
;
3322 if (isDirectory
== true) {
3323 if (foundRecType
!= kHFSPlusFolderRecord
) {
3325 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3326 plog ("\t%s: Folder recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3330 if (foundRecType
!= kHFSPlusFileRecord
) {
3332 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3333 plog ("\t%s: File recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3338 /* Compare file/folder ID */
3339 if (foundKey
.hfsPlus
.parentID
!= record2
.hfsPlusFile
.fileID
) {
3341 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3342 plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__
, foundKey
.hfsPlus
.parentID
, record2
.hfsPlusFile
.fileID
, record
.hfsPlusThread
.parentID
);
3345 } else { /* plain HFS */
3346 /* Check recordType */
3347 foundRecType
= record2
.hfsFile
.recordType
;
3348 if (isDirectory
== true) {
3349 if (foundRecType
!= kHFSFolderRecord
) {
3351 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3352 plog ("\t%s: Folder recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3356 if (foundRecType
!= kHFSFileRecord
) {
3358 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3359 plog ("\t%s: File recordType mismatch for id=%u (found=%u)\n", __FUNCTION__
, cNodeID
, foundRecType
);
3364 /* Compare file/folder ID */
3365 if (foundKey
.hfs
.parentID
!= record2
.hfsFile
.fileID
) {
3367 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3368 if (recordType
== kHFSFolderThreadRecord
) {
3369 plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__
, foundKey
.hfs
.parentID
, record2
.hfsFolder
.folderID
, record
.hfsThread
.parentID
);
3371 plog ("\t%s: fileID do not match (threadKey=%u fileRecord=%u), parentID=%u\n", __FUNCTION__
, foundKey
.hfs
.parentID
, record2
.hfsFile
.fileID
, record
.hfsThread
.parentID
);
3376 } /* if (err == noErr) */
3380 err
= DeleteBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
);
3381 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3383 plog ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__
, foundKey
.hfsPlus
.parentID
, err
);
3385 plog ("\t%s: Deleted thread record for id=%d (err=%d)\n", __FUNCTION__
, foundKey
.hfs
.parentID
, err
);
3393 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3394 plog ("\t%s: Unknown record type.\n", __FUNCTION__
);
3399 } while ( err
== noErr
);
3401 if ( err
== btNotFound
)
3402 err
= noErr
; // all done, no more catalog records
3404 // if (err == noErr)
3405 // err = BTFlushPath( GPtr->calculatedCatalogFCB );
3411 static OSErr
RepairReservedBTreeFields ( SGlobPtr GPtr
)
3413 CatalogRecord record
;
3414 CatalogKey foundKey
;
3421 selCode
= 0x8001; // start with 1st record
3423 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &foundKey
, &record
, &recordSize
, &hint
);
3424 if ( err
!= noErr
) goto EXIT
;
3426 selCode
= 1; // get next record from now on
3430 switch( record
.recordType
)
3432 case kHFSPlusFolderRecord
:
3433 /* XXX -- this should not always be cleared out (but doesn't appear to being called) */
3434 if ( record
.hfsPlusFolder
.flags
!= 0 )
3436 record
.hfsPlusFolder
.flags
= 0;
3437 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recordSize
, &hint
);
3441 case kHFSPlusFileRecord
:
3442 // Note: bit 7 (mask 0x80) of flags is unused in HFS or HFS Plus. However, Inside Macintosh: Files
3443 // describes it as meaning the file record is in use. Some non-Apple implementations end up setting
3444 // this bit, so we just ignore it.
3445 if ( ( record
.hfsPlusFile
.flags
& (UInt16
) ~(0X83) ) != 0 )
3447 record
.hfsPlusFile
.flags
&= 0X83;
3448 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recordSize
, &hint
);
3452 case kHFSFolderRecord
:
3453 if ( record
.hfsFolder
.flags
!= 0 )
3455 record
.hfsFolder
.flags
= 0;
3456 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recordSize
, &hint
);
3460 case kHFSFolderThreadRecord
:
3461 case kHFSFileThreadRecord
:
3462 reserved
= (UInt32
*) &(record
.hfsThread
.reserved
);
3463 if ( reserved
[0] || reserved
[1] )
3467 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recordSize
, &hint
);
3471 case kHFSFileRecord
:
3472 // Note: bit 7 (mask 0x80) of flags is unused in HFS or HFS Plus. However, Inside Macintosh: Files
3473 // describes it as meaning the file record is in use. Some non-Apple implementations end up setting
3474 // this bit, so we just ignore it.
3475 if ( ( ( record
.hfsFile
.flags
& (UInt8
) ~(0X83) ) != 0 )
3476 || ( record
.hfsFile
.dataStartBlock
!= 0 )
3477 || ( record
.hfsFile
.rsrcStartBlock
!= 0 )
3478 || ( record
.hfsFile
.reserved
!= 0 ) )
3480 record
.hfsFile
.flags
&= 0X83;
3481 record
.hfsFile
.dataStartBlock
= 0;
3482 record
.hfsFile
.rsrcStartBlock
= 0;
3483 record
.hfsFile
.reserved
= 0;
3484 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recordSize
, &hint
);
3492 if ( err
!= noErr
) goto EXIT
;
3494 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &foundKey
, &record
, &recordSize
, &hint
);
3496 } while ( err
== noErr
);
3498 if ( err
== btNotFound
)
3499 err
= noErr
; // all done, no more catalog records
3506 /* Function: FixOrphanAttrRecord
3509 * The function traverses the attribute BTree completely and for every
3510 * leaf record, calls CheckAttributeRecord. CheckAttributeRecord function
3511 * is common function for verify and repair stage. CheckAttributeRecord
3512 * deletes invalid/orphaned extended attribute records under following
3514 * 1. record is overflow extents with no valid fork data or overflow extents
3516 * 2. record type is unknown.
3519 * GPtr - global scavenger structure pointer
3522 * error code - zero on success, non-zero on failure.
3524 static OSErr
FixOrphanAttrRecord(SGlobPtr GPtr
)
3530 HFSPlusAttrRecord record
;
3534 /* Zero out last attribute information from global scavenger structure */
3535 bzero (&(GPtr
->lastAttrInfo
), sizeof(GPtr
->lastAttrInfo
));
3537 /* Warning: Attribute record of type kHFSPlusAttrInlineData may be
3538 * truncated on read! (4425232). CheckAttributeRecord only uses recordType
3539 * field from inline attribute record.
3542 err
= GetBTreeRecord(GPtr
->calculatedAttributesFCB
, selCode
, &key
,
3543 &record
, &recordSize
, &hint
);
3550 err
= CheckAttributeRecord(GPtr
, &key
, &record
, recordSize
);
3555 /* Access the next record.
3556 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
3557 * truncated on read! (4425232). CheckAttributeRecord only uses recordType
3558 * field from inline attribute record.
3560 err
= GetBTreeRecord(GPtr
->calculatedAttributesFCB
, selCode
, &key
,
3561 &record
, &recordSize
, &hint
);
3562 } while (err
== noErr
);
3564 if (err
== btNotFound
) {
3572 /* Function: GetCatalogRecord
3575 * This function returns a catalog file/folder record for a given
3576 * file/folder ID from the catalog BTree.
3578 * Input: 1. GPtr - pointer to global scavenger area
3579 * 2. fileID - file ID to search the file/folder record
3580 * 3. isHFSPlus - boolean value to indicate if volume is HFSPlus
3582 * Output: 1. catKey - catalog key
3583 * 2. catRecord - catalog record for given ID
3584 * 3. recordSize - size of catalog record return back
3587 * zero means success
3588 * non-zero means failure
3590 static OSErr
GetCatalogRecord(SGlobPtr GPtr
, UInt32 fileID
, Boolean isHFSPlus
, CatalogKey
*catKey
, CatalogRecord
*catRecord
, UInt16
*recordSize
)
3593 CatalogKey catThreadKey
;
3594 CatalogName catalogName
;
3596 uint32_t thread_key_parentID
;
3598 /* Look up for catalog thread record for the file that owns attribute */
3599 BuildCatalogKey(fileID
, NULL
, isHFSPlus
, &catThreadKey
);
3600 err
= SearchBTreeRecord(GPtr
->calculatedCatalogFCB
, &catThreadKey
, kNoHint
, catKey
, catRecord
, recordSize
, &hint
);
3602 plog ("%s: No matching catalog thread record found\n", __FUNCTION__
);
3607 plog ("%s(%s,%d):1 recordType=%x, flags=%x\n", __FUNCTION__
, __FILE__
, __LINE__
,
3608 catRecord
->hfsPlusFile
.recordType
,
3609 catRecord
->hfsPlusFile
.flags
);
3612 /* We were expecting a thread record. The recordType says it is a file
3613 * record or folder record. Return error.
3615 if ((catRecord
->hfsPlusFile
.recordType
== kHFSPlusFolderRecord
) ||
3616 (catRecord
->hfsPlusFile
.recordType
== kHFSPlusFileRecord
)) {
3617 err
= fsBTRecordNotFoundErr
;
3620 thread_key_parentID
= catKey
->hfsPlus
.parentID
;
3622 /* It is either a file thread record or folder thread record.
3623 * Look up for catalog record for the file that owns attribute */
3624 CopyCatalogName((CatalogName
*)&(catRecord
->hfsPlusThread
.nodeName
), &catalogName
, isHFSPlus
);
3625 BuildCatalogKey(catRecord
->hfsPlusThread
.parentID
, &catalogName
, isHFSPlus
, catKey
);
3626 err
= SearchBTreeRecord(GPtr
->calculatedCatalogFCB
, catKey
, kNoHint
, catKey
, catRecord
, recordSize
, &hint
);
3628 plog ("%s: No matching catalog record found\n", __FUNCTION__
);
3629 if (cur_debug_level
& d_dump_record
)
3631 plog ("Searching for key:\n");
3632 HexDump(catKey
, CalcKeySize(GPtr
->calculatedCatalogBTCB
, (BTreeKey
*)catKey
), FALSE
);
3638 plog ("%s(%s,%d):2 recordType=%x, flags=%x\n", __FUNCTION__
, __FILE__
, __LINE__
,
3639 catRecord
->hfsPlusFile
.recordType
,
3640 catRecord
->hfsPlusFile
.flags
);
3643 /* For catalog file or folder record, the parentID in the thread
3644 * record's key should be equal to the fileID in the file/folder
3645 * record --- which is equal to the ID of the file/folder record
3646 * that is being looked up. If not, mark the volume for repair.
3648 if (thread_key_parentID
!= catRecord
->hfsPlusFile
.fileID
) {
3649 RcdError(GPtr
, E_IncorrectNumThdRcd
);
3650 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3651 plog("\t%s: fileID=%u, thread.key.parentID=%u, record.fileID=%u\n",
3652 __FUNCTION__
, fileID
, thread_key_parentID
, catRecord
->hfsPlusFile
.fileID
);
3654 GPtr
->CBTStat
|= S_Orphan
;
3660 /* Function: RepairAttributesCheckABT
3663 * This function is called from RepairAttributes (to repair extended
3664 * attributes) during repair stage of fsck_hfs.
3666 * 1. Make full pass through attribute BTree.
3667 * 2. For every unique fileID, lookup its catalog record in Catalog BTree.
3668 * 3. If found, check the attributes/security bit in catalog record.
3669 * If not set correctly, set it and replace the catalog record.
3670 * 4. If not found, return error
3672 * Input: 1. GPtr - pointer to global scavenger area
3673 * 2. isHFSPlus - boolean value to indicate if volume is HFSPlus
3675 * Output: err - Function result
3676 * zero means success
3677 * non-zero means failure
3679 static OSErr
RepairAttributesCheckABT(SGlobPtr GPtr
, Boolean isHFSPlus
)
3682 UInt16 selCode
; /* select access pattern for BTree */
3685 HFSPlusAttrRecord attrRecord
;
3686 HFSPlusAttrKey attrKey
;
3687 UInt16 attrRecordSize
;
3688 CatalogRecord catRecord
;
3690 UInt16 catRecordSize
;
3692 attributeInfo lastID
; /* fileID for the last attribute searched */
3693 Boolean didRecordChange
= false; /* whether catalog record was changed after checks */
3696 char attrName
[XATTR_MAXNAMELEN
];
3701 lastID
.hasSecurity
= false;
3703 selCode
= 0x8001; /* Get first record from BTree */
3704 /* Warning: Attribute record of type kHFSPlusAttrInlineData may be
3705 * truncated on read! (4425232). This function only uses recordType
3706 * field from inline attribute record.
3708 err
= GetBTreeRecord(GPtr
->calculatedAttributesFCB
, selCode
, &attrKey
, &attrRecord
, &attrRecordSize
, &hint
);
3709 if (err
== btNotFound
) {
3710 /* The attributes B-tree is empty, which is OK; nothing to do. */
3714 if (err
!= noErr
) goto out
;
3716 selCode
= 1; /* Get next record */
3719 /* Convert unicode attribute name to char for ACL check */
3720 (void) utf_encodestr(attrKey
.attrName
, attrKey
.attrNameLen
* 2, attrName
, &len
, sizeof(attrName
));
3721 attrName
[len
] = '\0';
3722 plog ("%s(%s,%d): Found attrName=%s for fileID=%d\n", __FUNCTION__
, __FILE__
, __LINE__
, attrName
, attrKey
.fileID
);
3725 if (attrKey
.fileID
!= lastID
.fileID
) {
3726 /* We found an attribute with new file ID */
3728 /* Replace the previous catalog record only if we updated the flags */
3729 if (didRecordChange
== true) {
3730 err
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &catKey
, kNoHint
, &catRecord
, catRecordSize
, &hint
);
3732 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3733 plog ("\t%s: Error in replacing catalog record for id=%u\n", __FUNCTION__
, lastID
.fileID
);
3739 didRecordChange
= false; /* reset to indicate new record has not changed */
3741 /* Get the catalog record for the new fileID */
3742 err
= GetCatalogRecord(GPtr
, attrKey
.fileID
, isHFSPlus
, &catKey
, &catRecord
, &catRecordSize
);
3744 /* No catalog record was found for this fileID. */
3745 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3746 plog ("\t%s: No matching catalog record found for id=%u\n", __FUNCTION__
, attrKey
.fileID
);
3749 /* 3984119 - Do not delete extended attributes for file IDs less
3750 * kHFSFirstUserCatalogNodeID but not equal to kHFSRootFolderID
3751 * in prime modulus checksum. These file IDs do not have
3752 * any catalog record
3754 if ((attrKey
.fileID
< kHFSFirstUserCatalogNodeID
) &&
3755 (attrKey
.fileID
!= kHFSRootFolderID
)) {
3757 plog ("%s: Ignore catalog check for fileID=%d for attribute=%s\n", __FUNCTION__
, attrKey
.fileID
, attrName
);
3762 /* Delete this orphan extended attribute */
3763 err
= delete_attr_record(GPtr
, &attrKey
, &attrRecord
);
3765 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
3766 plog ("\t%s: Error in deleting attribute record for id=%u\n", __FUNCTION__
, attrKey
.fileID
);
3773 lastID
.fileID
= attrKey
.fileID
; /* set last fileID to the new ID */
3774 lastID
.hasSecurity
= false; /* reset to indicate new fileID does not have security */
3776 /* Check the Attribute bit */
3777 if (!(catRecord
.hfsPlusFile
.flags
& kHFSHasAttributesMask
)) {
3778 /* kHFSHasAttributeBit should be set */
3779 catRecord
.hfsPlusFile
.flags
|= kHFSHasAttributesMask
;
3780 didRecordChange
= true;
3783 /* Check if attribute is ACL */
3784 if (!bcmp(attrKey
.attrName
, GPtr
->securityAttrName
, GPtr
->securityAttrLen
)) {
3785 lastID
.hasSecurity
= true;
3786 /* Check the security bit */
3787 if (!(catRecord
.hfsPlusFile
.flags
& kHFSHasSecurityMask
)) {
3788 /* kHFSHasSecurityBit should be set */
3789 catRecord
.hfsPlusFile
.flags
|= kHFSHasSecurityMask
;
3790 didRecordChange
= true;
3794 /* We have seen attribute for fileID in past */
3796 /* If last time we saw this fileID did not have an ACL and this
3797 * extended attribute is an ACL, ONLY check consistency of
3798 * security bit from Catalog record
3800 if ((lastID
.hasSecurity
== false) && !bcmp(attrKey
.attrName
, GPtr
->securityAttrName
, GPtr
->securityAttrLen
)) {
3801 lastID
.hasSecurity
= true;
3802 /* Check the security bit */
3803 if (!(catRecord
.hfsPlusFile
.flags
& kHFSHasSecurityMask
)) {
3804 /* kHFSHasSecurityBit should be set */
3805 catRecord
.hfsPlusFile
.flags
|= kHFSHasSecurityMask
;
3806 didRecordChange
= true;
3812 /* Access the next record
3813 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
3814 * truncated on read! (4425232). This function only uses recordType
3815 * field from inline attribute record.
3817 err
= GetBTreeRecord(GPtr
->calculatedAttributesFCB
, selCode
, &attrKey
, &attrRecord
, &attrRecordSize
, &hint
);
3818 } while (err
== noErr
);
3822 /* Replace the catalog record for last extended attribute in the attributes BTree
3823 * only if we updated the flags
3825 if (didRecordChange
== true) {
3826 err
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &catKey
, kNoHint
, &catRecord
, catRecordSize
, &hint
);
3829 plog ("%s: Error in replacing Catalog Record\n", __FUNCTION__
);
3839 /* Function: RepairAttributesCheckCBT
3842 * This function is called from RepairAttributes (to repair extended
3843 * attributes) during repair stage of fsck_hfs.
3845 * NOTE: The case where attribute exists and bit is not set is being taken care in
3846 * RepairAttributesCheckABT. This function determines relationship from catalog
3847 * Btree to attribute Btree, and not vice-versa.
3849 * 1. Make full pass through catalog BTree.
3850 * 2. For every fileID, if the attributes/security bit is set,
3851 * lookup all the extended attributes in the attributes BTree.
3852 * 3. If found, check that if bits are set correctly.
3853 * 4. If not found, clear the bits.
3855 * Input: 1. GPtr - pointer to global scavenger area
3856 * 2. isHFSPlus - boolean value to indicate if volume is HFSPlus
3858 * Output: err - Function result
3859 * zero means success
3860 * non-zero means failure
3862 static OSErr
RepairAttributesCheckCBT(SGlobPtr GPtr
, Boolean isHFSPlus
)
3865 UInt16 selCode
; /* select access pattern for BTree */
3869 HFSPlusAttrKey
*attrKey
;
3870 CatalogRecord catRecord
;
3873 Boolean didRecordChange
= false; /* whether catalog record was changed after checks */
3875 BTreeIterator iterator
;
3878 Boolean curRecordHasAttributes
= false;
3879 Boolean curRecordHasSecurity
= false;
3881 selCode
= 0x8001; /* Get first record from BTree */
3882 err
= GetBTreeRecord(GPtr
->calculatedCatalogFCB
, selCode
, &catKey
, &catRecord
, &recordSize
, &hint
);
3883 if ( err
!= noErr
) goto out
;
3885 selCode
= 1; /* Get next record */
3887 /* Check only file record and folder record, else skip to next record */
3888 if ( (catRecord
.hfsPlusFile
.recordType
!= kHFSPlusFileRecord
) &&
3889 (catRecord
.hfsPlusFile
.recordType
!= kHFSPlusFolderRecord
)) {
3893 /* Check if catalog record has attribute and/or security bit set, else
3894 * skip to next record
3896 if ( ((catRecord
.hfsPlusFile
.flags
& kHFSHasAttributesMask
) == 0) &&
3897 ((catRecord
.hfsPlusFile
.flags
& kHFSHasSecurityMask
) == 0) ) {
3901 /* Initialize some flags */
3902 didRecordChange
= false;
3903 curRecordHasSecurity
= false;
3904 curRecordHasAttributes
= false;
3906 /* Access all extended attributes for this fileID */
3907 curFileID
= catRecord
.hfsPlusFile
.fileID
;
3909 /* Initialize the iterator and attribute key */
3910 ClearMemory(&iterator
, sizeof(BTreeIterator
));
3911 attrKey
= (HFSPlusAttrKey
*)&iterator
.key
;
3912 attrKey
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
3913 attrKey
->fileID
= curFileID
;
3915 /* Search for attribute with NULL name. This will place the iterator at correct fileID location in BTree */
3916 err
= BTSearchRecord(GPtr
->calculatedAttributesFCB
, &iterator
, kInvalidMRUCacheKey
, NULL
, NULL
, &iterator
);
3917 if (err
&& (err
!= btNotFound
)) {
3919 plog ("%s: No matching attribute record found\n", __FUNCTION__
);
3924 /* Iterate over to all extended attributes for given fileID */
3925 err
= BTIterateRecord(GPtr
->calculatedAttributesFCB
, kBTreeNextRecord
, &iterator
, NULL
, NULL
);
3927 /* Check only if we did _find_ an attribute record for the current fileID */
3928 while ((err
== noErr
) && (attrKey
->fileID
== curFileID
)) {
3929 /* Current record should have attribute bit set */
3930 curRecordHasAttributes
= true;
3932 /* Current record should have security bit set */
3933 if (!bcmp(attrKey
->attrName
, GPtr
->securityAttrName
, GPtr
->securityAttrLen
)) {
3934 curRecordHasSecurity
= true;
3937 /* Get the next record */
3938 err
= BTIterateRecord(GPtr
->calculatedAttributesFCB
, kBTreeNextRecord
, &iterator
, NULL
, NULL
);
3941 /* Determine if we need to update the catalog record */
3942 if ((curRecordHasAttributes
== false) && (catRecord
.hfsPlusFile
.flags
& kHFSHasAttributesMask
)) {
3943 /* If no attribute exists and attributes bit is set, clear it */
3944 catRecord
.hfsPlusFile
.flags
&= ~kHFSHasAttributesMask
;
3945 didRecordChange
= true;
3948 if ((curRecordHasSecurity
== false) && (catRecord
.hfsPlusFile
.flags
& kHFSHasSecurityMask
)) {
3949 /* If no security attribute exists and security bit is set, clear it */
3950 catRecord
.hfsPlusFile
.flags
&= ~kHFSHasSecurityMask
;
3951 didRecordChange
= true;
3954 /* If there was any change in catalog record, write it back to disk */
3955 if (didRecordChange
== true) {
3956 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &catKey
, kNoHint
, &catRecord
, recordSize
, &hint
);
3959 plog ("%s: Error writing catalog record\n", __FUNCTION__
);
3966 /* Access the next record */
3967 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &catKey
, &catRecord
, &recordSize
, &hint
);
3968 } while (err
== noErr
);
3976 /* Function: RepairAttributes
3979 * This function fixes the extended attributes consistency by
3980 * calling two functions:
3981 * 1. RepairAttributesCheckABT: Traverses attributes Btree and
3982 * checks if each attribute has correct bits set in its corresponding
3984 * 2. RepairAttributesCheckCBT: Traverses catalog Btree and checks
3985 * if each catalog record that has attribute/security bit set have
3986 * corresponding extended attributes.
3988 * Input: 1. GPtr - pointer to global scavenger area
3990 * Output: err - Function result
3991 * zero means success
3992 * non-zero means failure
3994 static OSErr
RepairAttributes(SGlobPtr GPtr
)
3999 /* Check if the volume is HFS Plus volume */
4000 isHFSPlus
= VolumeObjectIsHFSPlus();
4005 /* Traverse Attributes BTree and access required records in Catalog BTree */
4006 err
= RepairAttributesCheckABT(GPtr
, isHFSPlus
);
4011 /* Traverse Catalog BTree and access required records in Attributes BTree */
4012 err
= RepairAttributesCheckCBT(GPtr
, isHFSPlus
);
4021 /*------------------------------------------------------------------------------
4025 Function: compares two longs.
4027 Input: *a: pointer to first number
4028 *b: pointer to second number
4030 Output: <0 if *a < *b
4033 ------------------------------------------------------------------------------*/
4035 int cmpLongs ( const void *a
, const void *b
)
4037 return( *(long*)a
- *(long*)b
);
4040 /* Function: FixOverlappingExtents
4042 * Description: Fix overlapping extents problem. The implementation copies all
4043 * the extents existing in overlapping extents to a new location and updates the
4044 * extent record to point to the new extent. At the end of repair, symlinks are
4045 * created with name "fileID filename" to point to the file involved in
4046 * overlapping extents problem. Note that currently only HFS Plus volumes are
4049 * PARTIAL SUCCESS: This function handles partial success in the following
4051 * a. The function pre-allocates space for the all the extents. If the
4052 * allocation fails, it proceeds to allocate space for other extents
4053 * instead of returning error.
4054 * b. If moving an extent succeeds and symlink creation fails, the function
4055 * proceeds to another extent.
4056 * If the repair encounters either a or b condition, appropriate message is
4057 * printed at the end of the function.
4058 * If even a single repair operation succeeds (moving of extent), the function
4061 * Current limitations:
4062 * 1. A regular file instead of symlink is created under following conditions:
4063 * a. The volume is plain HFS volume. HFS does not support symlink
4065 * b. The path the new symlink points to is greater than PATH_MAX bytes.
4066 * c. The path the new symlink points has some intermediate component
4067 * greater than NAME_MAX bytes.
4068 * 2. Contiguous disk space for every new extent is expected. The extent is
4069 * not broken into multiple extents if contiguous space is not available on the
4071 * 3. The current fix for overlapping extent only works for HFS Plus volumes.
4072 * Plain HFS volumes have problem in accessing the catalog record by fileID.
4073 * 4. Plain HFS volumes might have encoding issues with the newly created
4074 * symlink and its data.
4077 * GPtr - global scavenger pointer
4080 * returns zero on success/partial success (moving of one extent succeeds),
4081 * non-zero on failure.
4083 static OSErr
FixOverlappingExtents(SGlobPtr GPtr
)
4088 unsigned int numOverlapExtents
= 0;
4089 ExtentInfo
*extentInfo
;
4090 ExtentsTable
**extentsTableH
= GPtr
->overlappedExtents
;
4092 unsigned int status
= 0;
4093 #define S_DISKFULL 0x01 /* error due to disk full */
4094 #define S_MOVEEXTENT 0x02 /* moving extent succeeded */
4096 isHFSPlus
= VolumeObjectIsHFSPlus();
4097 if (isHFSPlus
== false) {
4098 /* Do not repair plain HFS volumes */
4103 if (extentsTableH
== NULL
) {
4104 /* No overlapping extents exist */
4108 numOverlapExtents
= (**extentsTableH
).count
;
4110 /* Optimization - sort the overlap extent list based on blockCount to
4111 * start allocating contiguous space for largest number of blocks first
4113 qsort((**extentsTableH
).extentInfo
, numOverlapExtents
, sizeof(ExtentInfo
),
4114 CompareExtentBlockCount
);
4117 /* Print all overlapping extents structure */
4118 for (i
=0; i
<numOverlapExtents
; i
++) {
4119 extentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4120 plog ("%d: fileID = %d, startBlock = %d, blockCount = %d\n", i
, extentInfo
->fileID
, extentInfo
->startBlock
, extentInfo
->blockCount
);
4124 /* Pre-allocate free space for all overlapping extents */
4125 for (i
=0; i
<numOverlapExtents
; i
++) {
4126 extentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4127 err
= AllocateContigBitmapBits (GPtr
->calculatedVCB
, extentInfo
->blockCount
, &(extentInfo
->newStartBlock
));
4128 if ((err
!= noErr
)) {
4129 /* Not enough disk space */
4130 status
|= S_DISKFULL
;
4132 plog ("%s: Not enough disk space to allocate extent for fileID = %d (start=%d, count=%d)\n", __FUNCTION__
, extentInfo
->fileID
, extentInfo
->startBlock
, extentInfo
->blockCount
);
4137 /* For every extent info, copy the extent into new location and create symlink */
4138 for (i
=0; i
<numOverlapExtents
; i
++) {
4139 extentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4141 /* Do not repair this extent as no new extent was allocated */
4142 if (extentInfo
->newStartBlock
== 0) {
4146 /* Move extent data to new location */
4147 err
= MoveExtent(GPtr
, extentInfo
);
4149 extentInfo
->didRepair
= false;
4151 plog ("%s: Extent move failed for extent for fileID = %u (old=%u, new=%u, count=%u) (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, extentInfo
->startBlock
, extentInfo
->newStartBlock
, extentInfo
->blockCount
, err
);
4154 /* Mark the overlapping extent as repaired */
4155 extentInfo
->didRepair
= true;
4156 status
|= S_MOVEEXTENT
;
4158 plog ("%s: Extent move success for extent for fileID = %u (old=%u, new=%u, count=%u)\n", __FUNCTION__
, extentInfo
->fileID
, extentInfo
->startBlock
, extentInfo
->newStartBlock
, extentInfo
->blockCount
);
4162 /* Create symlink for the corrupt file */
4163 err
= CreateCorruptFileSymlink(GPtr
, extentInfo
->fileID
);
4166 plog ("%s: Error in creating symlink for fileID = %d (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4170 plog ("%s: Created symlink for fileID = %u (old=%u, new=%u, count=%u)\n", __FUNCTION__
, extentInfo
->fileID
, extentInfo
->startBlock
, extentInfo
->newStartBlock
, extentInfo
->blockCount
);
4176 /* Release all blocks used by overlap extents that are repaired */
4177 for (i
=0; i
<numOverlapExtents
; i
++) {
4178 extentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4179 if (extentInfo
->didRepair
== true) {
4180 ReleaseBitmapBits (extentInfo
->startBlock
, extentInfo
->blockCount
);
4184 /* For all un-repaired extents,
4185 * 1. Release all blocks allocated for new extent.
4186 * 2. Mark all blocks used for the old extent (since the overlapping region
4187 * have been marked free in the for loop above.
4189 for (i
=0; i
<numOverlapExtents
; i
++) {
4190 extentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4191 if (extentInfo
->didRepair
== false) {
4192 CaptureBitmapBits (extentInfo
->startBlock
, extentInfo
->blockCount
);
4194 if (extentInfo
->newStartBlock
!= 0) {
4195 ReleaseBitmapBits (extentInfo
->newStartBlock
, extentInfo
->blockCount
);
4200 /* Update the volume free block count since the release and alloc above might
4201 * have worked on same bit multiple times.
4203 UpdateFreeBlockCount (GPtr
);
4205 /* Print correct status messages */
4206 if (status
& S_DISKFULL
) {
4207 fsckPrint(GPtr
->context
, E_DiskFull
);
4210 /* If moving of even one extent succeeds, return success */
4211 if (status
& S_MOVEEXTENT
) {
4216 } /* FixOverlappingExtents */
4218 /* Function: CompareExtentBlockCount
4220 * Description: Compares the blockCount from two ExtentInfo and return the
4221 * comparison result. (since we have to arrange in descending order)
4224 * first and second - void pointers to ExtentInfo structure.
4227 * <0 if first > second
4228 * =0 if first == second
4229 * >0 if first < second
4231 static int CompareExtentBlockCount(const void *first
, const void *second
)
4233 return (((ExtentInfo
*)second
)->blockCount
-
4234 ((ExtentInfo
*)first
)->blockCount
);
4235 } /* CompareExtentBlockCount */
4237 /* Function: MoveExtent
4239 * Description: Move data from old extent to new extent and update corresponding
4241 * 1. Search the extent record for the overlapping extent.
4242 * If the fileID < kHFSFirstUserCatalogNodeID,
4243 * Ignore repair for BadBlock, RepairCatalog, BogusExtent files.
4244 * Search for extent record in volume header.
4246 * Search for extent record in catalog BTree. If the extent list does
4247 * not end in catalog record and extent record not found in catalog
4248 * record, search in extents BTree.
4249 * 2. If found, copy disk blocks from old extent to new extent.
4250 * 3. If it succeeds, update extent record with new start block and write back
4252 * This function does not take care to deallocate blocks from old start block.
4255 * GPtr - Global Scavenger structure pointer
4256 * extentInfo - Current overlapping extent details.
4259 * err: zero on success, non-zero on failure
4260 * paramErr - Invalid paramter, ex. file ID is less than
4261 * kHFSFirstUserCatalogNodeID.
4263 static OSErr
MoveExtent(SGlobPtr GPtr
, ExtentInfo
*extentInfo
)
4268 CatalogRecord catRecord
;
4270 HFSPlusExtentKey extentKey
;
4271 HFSPlusExtentRecord extentData
;
4272 HFSPlusAttrKey attrKey
;
4273 HFSPlusAttrRecord attrRecord
;
4276 enum locationTypes
{volumeHeader
, catalogBTree
, extentsBTree
, attributeBTree
} foundLocation
;
4278 UInt32 foundExtentIndex
= 0;
4279 Boolean noMoreExtents
= true;
4281 isHFSPlus
= VolumeObjectIsHFSPlus();
4283 /* Find correct location of this overlapping extent */
4284 if (extentInfo
->forkType
== kEAData
) {
4285 assert(isHFSPlus
== true);
4287 /* Search extent in attribute btree */
4288 err
= SearchExtentInAttributeBT (GPtr
, extentInfo
, &attrKey
, &attrRecord
,
4289 &recordSize
, &foundExtentIndex
);
4293 foundLocation
= attributeBTree
;
4294 } else { /* kDataFork or kRsrcFork */
4295 if (extentInfo
->fileID
< kHFSFirstUserCatalogNodeID
) {
4296 /* Ignore these fileIDs in repair. Bad block file blocks should
4297 * never be moved. kHFSRepairCatalogFileID and kHFSBogusExtentFileID
4298 * are temporary runtime files. We need to return error to the caller
4299 * to deallocate disk blocks preallocated during preflight
4300 * to move the overlapping extents. Any other extent that overlaps
4301 * with these extents might have moved successfully, thus repairing
4304 if ((extentInfo
->fileID
== kHFSBadBlockFileID
) ||
4305 (extentInfo
->fileID
== kHFSBogusExtentFileID
) ||
4306 (extentInfo
->fileID
== kHFSRepairCatalogFileID
)) {
4311 /* Search for extent record in the volume header */
4312 err
= SearchExtentInVH (GPtr
, extentInfo
, &foundExtentIndex
, &noMoreExtents
);
4313 foundLocation
= volumeHeader
;
4315 /* Search the extent record from the catalog btree */
4316 err
= SearchExtentInCatalogBT (GPtr
, extentInfo
, &catKey
, &catRecord
,
4317 &recordSize
, &foundExtentIndex
, &noMoreExtents
);
4318 foundLocation
= catalogBTree
;
4321 if (noMoreExtents
== false) {
4322 /* search extent in extents overflow btree */
4323 err
= SearchExtentInExtentBT (GPtr
, extentInfo
, &extentKey
,
4324 &extentData
, &recordSize
, &foundExtentIndex
);
4325 foundLocation
= extentsBTree
;
4327 DPRINTF (d_error
|d_overlap
, "%s: No matching extent record found in extents btree for fileID = %d (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4331 /* No more extents exist for this file */
4332 DPRINTF (d_error
|d_overlap
, "%s: No matching extent record found for fileID = %d\n", __FUNCTION__
, extentInfo
->fileID
);
4337 /* Copy disk blocks from old extent to new extent */
4338 err
= CopyDiskBlocks(GPtr
, extentInfo
->startBlock
, extentInfo
->blockCount
,
4339 extentInfo
->newStartBlock
);
4341 DPRINTF (d_error
|d_overlap
, "%s: Error in copying disk blocks for fileID = %d (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4345 /* Replace the old start block in extent record with new start block */
4346 if (foundLocation
== catalogBTree
) {
4347 err
= UpdateExtentInCatalogBT(GPtr
, extentInfo
, &catKey
, &catRecord
,
4348 &recordSize
, foundExtentIndex
);
4349 } else if (foundLocation
== volumeHeader
) {
4350 err
= UpdateExtentInVH(GPtr
, extentInfo
, foundExtentIndex
);
4351 } else if (foundLocation
== extentsBTree
) {
4352 extentData
[foundExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4353 err
= UpdateExtentRecord(GPtr
->calculatedVCB
, NULL
, &extentKey
, extentData
, kNoHint
);
4354 } else if (foundLocation
== attributeBTree
) {
4355 err
= UpdateExtentInAttributeBT(GPtr
, extentInfo
, &attrKey
, &attrRecord
,
4356 &recordSize
, foundExtentIndex
);
4360 DPRINTF (d_error
|d_overlap
, "%s: Error in updating extent record for fileID = %d (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4368 /* Function: CreateCorruptFileSymlink
4370 * Description: Create symlink to point to the corrupt files that might
4371 * have data loss due to repair (overlapping extents, bad extents)
4373 * The function looks up directory with name "DamagedFiles" in the
4374 * root of the file system being repaired. If it does not exists, it
4375 * creates the directory. The symlink to damaged file is created in this
4378 * If fileID >= kHFSFirstUserCatalogNodeID,
4379 * Lookup the filename and path to the file based on file ID. Create the
4380 * new file name as "fileID filename" and data as the relative path of the file
4381 * from the root of the volume.
4383 * the volume is plain HFS, or
4384 * the length of the path pointed by data is greater than PATH_MAX, or
4385 * the length of any intermediate path component is greater than NAME_MAX,
4386 * Create a plain file with given data.
4390 * Find the name of file based on ID (ie. Catalog BTree, etc), and create plain
4391 * regular file with name "fileID filename" and data as "System File:
4395 * 1. GPtr - global scavenger structure pointer.
4396 * 2. fileID - fileID of the source for creating symlink
4399 * returns zero on success, non-zero on failure.
4400 * memFullErr - Not enough memory
4402 static OSErr
CreateCorruptFileSymlink(SGlobPtr GPtr
, UInt32 fileID
)
4406 char *filename
= NULL
;
4407 unsigned int filenamelen
;
4409 unsigned int datalen
;
4410 unsigned int filenameoffset
;
4411 unsigned int dataoffset
;
4412 UInt32 damagedDirID
;
4416 isHFSPlus
= VolumeObjectIsHFSPlus();
4418 /* Lookup and create, if required, the DamagedFiles folder */
4419 damagedDirID
= CreateDirByName(GPtr
, (u_char
*)"DamagedFiles", kHFSRootFolderID
);
4420 if (damagedDirID
== 0) {
4424 /* Allocate (kHFSPlusMaxFileNameChars * 4) for unicode - utf8 conversion */
4425 filenamelen
= kHFSPlusMaxFileNameChars
* 4;
4426 filename
= malloc(filenamelen
);
4432 /* Allocate (PATH_MAX * 4) instead of PATH_MAX for unicode - utf8 conversion */
4433 datalen
= PATH_MAX
* 4;
4434 data
= malloc(datalen
);
4440 /* Find filename, path for fileID >= 16 and determine new fileType */
4441 if (fileID
>= kHFSFirstUserCatalogNodeID
) {
4445 /* construct symlink data with .. prefix */
4446 dataoffset
= sprintf (data
, "..");
4447 path
= data
+ dataoffset
;
4448 datalen
-= dataoffset
;
4450 /* construct new filename prefix with fileID<space> */
4451 filenameoffset
= sprintf (filename
, "%08x ", fileID
);
4452 name
= filename
+ filenameoffset
;
4453 filenamelen
-= filenameoffset
;
4455 /* find file name and path (data for symlink) for fileID */
4456 err
= GetFileNamePathByID(GPtr
, fileID
, path
, &datalen
,
4457 name
, &filenamelen
, &status
);
4460 plog ("%s: Error in getting name/path for fileID = %d (err=%d)\n", __FUNCTION__
, fileID
, err
);
4465 /* update length of path and filename */
4466 filenamelen
+= filenameoffset
;
4467 datalen
+= dataoffset
;
4470 * (1) the volume is plain HFS volume, or
4471 * (2) any intermediate component in path was more than NAME_MAX bytes, or
4472 * (3) the entire path was greater than PATH_MAX bytes
4473 * then create regular file
4474 * else create symlink.
4476 if (!isHFSPlus
|| (status
& FPATH_BIGNAME
) || (datalen
> PATH_MAX
)) {
4480 /* create symlink */
4484 /* for fileID < 16, create regular file */
4487 /* construct the name of the file */
4488 filenameoffset
= sprintf (filename
, "%08x ", fileID
);
4489 filenamelen
-= filenameoffset
;
4490 err
= GetSystemFileName (fileID
, (filename
+ filenameoffset
), &filenamelen
);
4491 filenamelen
+= filenameoffset
;
4493 /* construct the data of the file */
4494 dataoffset
= sprintf (data
, "System File: ");
4495 datalen
-= dataoffset
;
4496 err
= GetSystemFileName (fileID
, (data
+ dataoffset
), &datalen
);
4497 datalen
+= dataoffset
;
4500 /* Create new file */
4501 err
= CreateFileByName (GPtr
, damagedDirID
, fileType
, (u_char
*)filename
,
4502 filenamelen
, (u_char
*)data
, datalen
);
4503 /* Mask error if file already exists */
4504 if (err
== EEXIST
) {
4509 plog ("%s: Error in creating fileType = %d for fileID = %d (err=%d)\n", __FUNCTION__
, fileType
, fileID
, err
);
4516 if ((GPtr
->PrintStat
& S_SymlinkCreate
) == 0) {
4517 fsckPrint(GPtr
->context
, E_SymlinkCreate
);
4518 GPtr
->PrintStat
|= S_SymlinkCreate
;
4521 if ((GPtr
->PrintStat
& S_DamagedDir
) == 0) {
4522 fsckPrint(GPtr
->context
, fsckCorruptFilesDirectory
, "DamagedFiles");
4523 GPtr
->PrintStat
|= S_DamagedDir
;
4535 } /* CreateCorruptFileSymlink */
4537 /* Function: SearchExtentInAttributeBT
4539 * Description: Search extent with given information (fileID, attribute name,
4540 * startBlock, blockCount) in the attribute BTree.
4543 * 1. GPtr - global scavenger structure pointer.
4544 * 2. extentInfo - Information about extent to be searched.
4547 * Returns zero on success, fnfErr on failure.
4548 * 1. *attrKey - Attribute key for given fileID and attribute name, if found.
4549 * 2. *attrRecord - Attribute record for given fileID and attribute name, if found.
4550 * 3. *recordSize - Size of the record being returned.
4551 * 4. *foundExtentIndex - Index in extent record which matches the input data.
4553 static OSErr
SearchExtentInAttributeBT(SGlobPtr GPtr
, ExtentInfo
*extentInfo
,
4554 HFSPlusAttrKey
*attrKey
, HFSPlusAttrRecord
*attrRecord
,
4555 UInt16
*recordSize
, UInt32
*foundExtentIndex
)
4557 OSErr result
= fnfErr
;
4558 BTreeIterator iterator
;
4559 FSBufferDescriptor btRecord
;
4560 HFSPlusAttrKey
*key
;
4561 Boolean noMoreExtents
;
4562 unsigned char *attrname
= NULL
;
4565 assert((extentInfo
->attrname
!= NULL
));
4567 attrname
= malloc (XATTR_MAXNAMELEN
+ 1);
4569 result
= memFullErr
;
4573 /* Initialize the iterator, attribute record buffer, and attribute key */
4574 ClearMemory(&iterator
, sizeof(BTreeIterator
));
4575 key
= (HFSPlusAttrKey
*)&iterator
.key
;
4576 attrnamelen
= strlen(extentInfo
->attrname
);
4577 BuildAttributeKey(extentInfo
->fileID
, 0, (unsigned char *)extentInfo
->attrname
, attrnamelen
, key
);
4579 btRecord
.bufferAddress
= attrRecord
;
4580 btRecord
.itemCount
= 1;
4581 btRecord
.itemSize
= sizeof(HFSPlusAttrRecord
);
4583 /* Search for the attribute record
4584 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4585 * truncated on read! (4425232). This function only uses recordType
4586 * field from inline attribute record.
4588 result
= BTSearchRecord(GPtr
->calculatedAttributesFCB
, &iterator
,
4589 kInvalidMRUCacheKey
, &btRecord
, recordSize
, &iterator
);
4591 DPRINTF (d_error
|d_overlap
, "%s: Error finding attribute record (err=%d) for fileID = %d, attrname = %d\n", __FUNCTION__
, result
, extentInfo
->fileID
, extentInfo
->attrname
);
4595 /* Search the attribute record for overlapping extent. If found, return
4596 * success. If not, iterate to the next record. If it is a valid
4597 * attribute extent record belonging to the same attribute, search
4598 * for the desired extent.
4601 if (attrRecord
->recordType
== kHFSPlusAttrForkData
) {
4602 result
= FindExtentInExtentRec(true, extentInfo
->startBlock
,
4603 extentInfo
->blockCount
, attrRecord
->forkData
.theFork
.extents
,
4604 foundExtentIndex
, &noMoreExtents
);
4605 if ((result
== noErr
) || (noMoreExtents
== true)) {
4608 } else if (attrRecord
->recordType
== kHFSPlusAttrExtents
) {
4609 result
= FindExtentInExtentRec(true, extentInfo
->startBlock
,
4610 extentInfo
->blockCount
, attrRecord
->overflowExtents
.extents
,
4611 foundExtentIndex
, &noMoreExtents
);
4612 if ((result
== noErr
) || (noMoreExtents
== true)) {
4616 /* Invalid attribute record. This function should not find any
4617 * attribute record except forkData and AttrExtents.
4623 /* Iterate to the next record
4624 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4625 * truncated on read! (4425232). This function only uses recordType
4626 * field from inline attribute record.
4628 result
= BTIterateRecord(GPtr
->calculatedAttributesFCB
, kBTreeNextRecord
,
4629 &iterator
, &btRecord
, recordSize
);
4634 (void) utf_encodestr(key
->attrName
, key
->attrNameLen
* 2, attrname
, &attrnamelen
, XATTR_MAXNAMELEN
+ 1);
4635 attrname
[attrnamelen
] = '\0';
4637 /* Check if the attribute record belongs to the same attribute */
4638 if ((key
->fileID
!= extentInfo
->fileID
) ||
4639 (strcmp((char *)attrname
, extentInfo
->attrname
))) {
4640 /* The attribute record belongs to another attribute */
4647 /* Copy the correct key to the caller */
4648 if (result
== noErr
) {
4649 CopyMemory(key
, attrKey
, sizeof(HFSPlusAttrKey
));
4652 if (attrname
!= NULL
) {
4659 /* Function: UpdateExtentInAttributeBT
4661 * Description: Update extent record with given information (fileID, startBlock,
4662 * blockCount) in attribute BTree.
4665 * 1. GPtr - global scavenger structure pointer.
4666 * 2. extentInfo - Information about extent to be searched.
4667 * 3. *attrKey - Attribute key for record to update.
4668 * 4. *attrRecord - Attribute record to update.
4669 * 5. *recordSize - Size of the record.
4670 * 6. foundExtentIndex - Index in extent record to update.
4673 * Returns zero on success, non-zero on failure.
4675 static OSErr
UpdateExtentInAttributeBT (SGlobPtr GPtr
, ExtentInfo
*extentInfo
,
4676 HFSPlusAttrKey
*attrKey
, HFSPlusAttrRecord
*attrRecord
,
4677 UInt16
*recordSize
, UInt32 foundInExtentIndex
)
4682 assert ((attrRecord
->recordType
== kHFSPlusAttrForkData
) ||
4683 (attrRecord
->recordType
== kHFSPlusAttrExtents
));
4685 /* Update the new start block count in the extent */
4686 if (attrRecord
->recordType
== kHFSPlusAttrForkData
) {
4687 attrRecord
->forkData
.theFork
.extents
[foundInExtentIndex
].startBlock
=
4688 extentInfo
->newStartBlock
;
4689 } else if (attrRecord
->recordType
== kHFSPlusAttrExtents
) {
4690 attrRecord
->overflowExtents
.extents
[foundInExtentIndex
].startBlock
=
4691 extentInfo
->newStartBlock
;
4694 /* Replace the attribute record.
4695 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4696 * truncated on read! (4425232).
4698 err
= ReplaceBTreeRecord (GPtr
->calculatedAttributesFCB
, attrKey
, kNoHint
,
4699 attrRecord
, *recordSize
, &foundHint
);
4704 /* Function: SearchExtentInVH
4706 * Description: Search extent with given information (fileID, startBlock,
4707 * blockCount) in volume header.
4710 * 1. GPtr - global scavenger structure pointer.
4711 * 2. extentInfo - Information about extent to be searched.
4714 * Returns zero on success, fnfErr on failure.
4715 * 1. *foundExtentIndex - Index in extent record which matches the input data.
4716 * 2. *noMoreExtents - Indicates that no more extents will exist for this
4717 * fileID in extents BTree.
4719 static OSErr
SearchExtentInVH(SGlobPtr GPtr
, ExtentInfo
*extentInfo
, UInt32
*foundExtentIndex
, Boolean
*noMoreExtents
)
4725 isHFSPlus
= VolumeObjectIsHFSPlus();
4726 *noMoreExtents
= true;
4728 /* Find correct FCB structure */
4729 switch (extentInfo
->fileID
) {
4730 case kHFSExtentsFileID
:
4731 fcb
= GPtr
->calculatedVCB
->vcbExtentsFile
;
4734 case kHFSCatalogFileID
:
4735 fcb
= GPtr
->calculatedVCB
->vcbCatalogFile
;
4738 case kHFSAllocationFileID
:
4739 fcb
= GPtr
->calculatedVCB
->vcbAllocationFile
;
4742 case kHFSStartupFileID
:
4743 fcb
= GPtr
->calculatedVCB
->vcbStartupFile
;
4746 case kHFSAttributesFileID
:
4747 fcb
= GPtr
->calculatedVCB
->vcbAttributesFile
;
4751 /* If extent found, find correct extent index */
4754 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
4755 extentInfo
->blockCount
, fcb
->fcbExtents32
,
4756 foundExtentIndex
, noMoreExtents
);
4758 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
4759 extentInfo
->blockCount
,
4760 (*(HFSPlusExtentRecord
*)fcb
->fcbExtents16
),
4761 foundExtentIndex
, noMoreExtents
);
4765 } /* SearchExtentInVH */
4767 /* Function: UpdateExtentInVH
4769 * Description: Update the extent record for given fileID and index in the
4770 * volume header with new start block.
4773 * 1. GPtr - global scavenger structure pointer.
4774 * 2. extentInfo - Information about extent to be searched.
4775 * 3. foundExtentIndex - Index in extent record to update.
4778 * Returns zero on success, fnfErr on failure. This function will fail an
4779 * incorrect fileID is passed.
4781 static OSErr
UpdateExtentInVH (SGlobPtr GPtr
, ExtentInfo
*extentInfo
, UInt32 foundExtentIndex
)
4787 isHFSPlus
= VolumeObjectIsHFSPlus();
4789 /* Find correct FCB structure */
4790 switch (extentInfo
->fileID
) {
4791 case kHFSExtentsFileID
:
4792 fcb
= GPtr
->calculatedVCB
->vcbExtentsFile
;
4795 case kHFSCatalogFileID
:
4796 fcb
= GPtr
->calculatedVCB
->vcbCatalogFile
;
4799 case kHFSAllocationFileID
:
4800 fcb
= GPtr
->calculatedVCB
->vcbAllocationFile
;
4803 case kHFSStartupFileID
:
4804 fcb
= GPtr
->calculatedVCB
->vcbStartupFile
;
4807 case kHFSAttributesFileID
:
4808 fcb
= GPtr
->calculatedVCB
->vcbAttributesFile
;
4812 /* If extent found, find correct extent index */
4815 fcb
->fcbExtents32
[foundExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4817 fcb
->fcbExtents16
[foundExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4819 MarkVCBDirty(GPtr
->calculatedVCB
);
4823 } /* UpdateExtentInVH */
4825 /* Function: SearchExtentInCatalogBT
4827 * Description: Search extent with given information (fileID, startBlock,
4828 * blockCount) in catalog BTree.
4831 * 1. GPtr - global scavenger structure pointer.
4832 * 2. extentInfo - Information about extent to be searched.
4835 * Returns zero on success, non-zero on failure.
4836 * 1. *catKey - Catalog key for given fileID, if found.
4837 * 2. *catRecord - Catalog record for given fileID, if found.
4838 * 3. *recordSize - Size of the record being returned.
4839 * 4. *foundExtentIndex - Index in extent record which matches the input data.
4840 * 5. *noMoreExtents - Indicates that no more extents will exist for this
4841 * fileID in extents BTree.
4843 static OSErr
SearchExtentInCatalogBT(SGlobPtr GPtr
, ExtentInfo
*extentInfo
, CatalogKey
*catKey
, CatalogRecord
*catRecord
, UInt16
*recordSize
, UInt32
*foundExtentIndex
, Boolean
*noMoreExtents
)
4848 isHFSPlus
= VolumeObjectIsHFSPlus();
4850 /* Search catalog btree for this file ID */
4851 err
= GetCatalogRecord(GPtr
, extentInfo
->fileID
, isHFSPlus
, catKey
, catRecord
,
4855 plog ("%s: No matching catalog record found for fileID = %d (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4862 if (extentInfo
->forkType
== kDataFork
) {
4864 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
4865 extentInfo
->blockCount
,
4866 catRecord
->hfsPlusFile
.dataFork
.extents
,
4867 foundExtentIndex
, noMoreExtents
);
4870 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
4871 extentInfo
->blockCount
,
4872 catRecord
->hfsPlusFile
.resourceFork
.extents
,
4873 foundExtentIndex
, noMoreExtents
);
4877 if (extentInfo
->forkType
== kDataFork
) {
4879 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
4880 extentInfo
->blockCount
,
4881 (*(HFSPlusExtentRecord
*)catRecord
->hfsFile
.dataExtents
),
4882 foundExtentIndex
, noMoreExtents
);
4885 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
4886 extentInfo
->blockCount
,
4887 (*(HFSPlusExtentRecord
*)catRecord
->hfsFile
.rsrcExtents
),
4888 foundExtentIndex
, noMoreExtents
);
4894 } /* SearchExtentInCatalogBT */
4896 /* Function: UpdateExtentInCatalogBT
4898 * Description: Update extent record with given information (fileID, startBlock,
4899 * blockCount) in catalog BTree.
4902 * 1. GPtr - global scavenger structure pointer.
4903 * 2. extentInfo - Information about extent to be searched.
4904 * 3. *catKey - Catalog key for record to update.
4905 * 4. *catRecord - Catalog record to update.
4906 * 5. *recordSize - Size of the record.
4907 * 6. foundExtentIndex - Index in extent record to update.
4910 * Returns zero on success, non-zero on failure.
4912 static OSErr
UpdateExtentInCatalogBT (SGlobPtr GPtr
, ExtentInfo
*extentInfo
, CatalogKey
*catKey
, CatalogRecord
*catRecord
, UInt16
*recordSize
, UInt32 foundInExtentIndex
)
4918 isHFSPlus
= VolumeObjectIsHFSPlus();
4920 /* Modify the catalog record */
4922 if (extentInfo
->forkType
== kDataFork
) {
4923 catRecord
->hfsPlusFile
.dataFork
.extents
[foundInExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4925 catRecord
->hfsPlusFile
.resourceFork
.extents
[foundInExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4928 if (extentInfo
->forkType
== kDataFork
) {
4929 catRecord
->hfsFile
.dataExtents
[foundInExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4931 catRecord
->hfsFile
.rsrcExtents
[foundInExtentIndex
].startBlock
= extentInfo
->newStartBlock
;
4935 /* Replace the catalog record */
4936 err
= ReplaceBTreeRecord (GPtr
->calculatedCatalogFCB
, catKey
, kNoHint
,
4937 catRecord
, *recordSize
, &foundHint
);
4940 plog ("%s: Error in replacing catalog record for fileID = %d (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4944 } /* UpdateExtentInCatalogBT */
4946 /* Function: SearchExtentInExtentBT
4948 * Description: Search extent with given information (fileID, startBlock,
4949 * blockCount) in Extent BTree.
4952 * 1. GPtr - global scavenger structure pointer.
4953 * 2. extentInfo - Information about extent to be searched.
4956 * Returns zero on success, non-zero on failure.
4957 * fnfErr - desired extent record was not found.
4958 * 1. *extentKey - Extent key, if found.
4959 * 2. *extentRecord - Extent record, if found.
4960 * 3. *recordSize - Size of the record being returned.
4961 * 4. *foundExtentIndex - Index in extent record which matches the input data.
4963 static OSErr
SearchExtentInExtentBT(SGlobPtr GPtr
, ExtentInfo
*extentInfo
, HFSPlusExtentKey
*extentKey
, HFSPlusExtentRecord
*extentRecord
, UInt16
*recordSize
, UInt32
*foundExtentIndex
)
4967 Boolean noMoreExtents
= true;
4970 isHFSPlus
= VolumeObjectIsHFSPlus();
4972 /* set up extent key */
4973 BuildExtentKey (isHFSPlus
, extentInfo
->forkType
, extentInfo
->fileID
, 0, extentKey
);
4974 err
= SearchBTreeRecord (GPtr
->calculatedExtentsFCB
, extentKey
, kNoHint
,
4975 extentKey
, extentRecord
, recordSize
, &hint
);
4976 if ((err
!= noErr
) && (err
!= btNotFound
)) {
4978 plog ("%s: Error on searching first record for fileID = %d in Extents Btree (err=%d)\n", __FUNCTION__
, extentInfo
->fileID
, err
);
4983 if (err
== btNotFound
)
4985 /* Position to the first record for the given fileID */
4986 err
= GetBTreeRecord (GPtr
->calculatedExtentsFCB
, 1, extentKey
,
4987 extentRecord
, recordSize
, &hint
);
4990 while (err
== noErr
)
4992 /* Break out if we see different fileID, forkType in the BTree */
4994 if ((extentKey
->fileID
!= extentInfo
->fileID
) ||
4995 (extentKey
->forkType
!= extentInfo
->forkType
)) {
5000 if ((((HFSExtentKey
*)extentKey
)->fileID
!= extentInfo
->fileID
) ||
5001 (((HFSExtentKey
*)extentKey
)->forkType
!= extentInfo
->forkType
)) {
5007 /* Check the extents record for corresponding startBlock, blockCount */
5008 err
= FindExtentInExtentRec(isHFSPlus
, extentInfo
->startBlock
,
5009 extentInfo
->blockCount
, *extentRecord
,
5010 foundExtentIndex
, &noMoreExtents
);
5014 if (noMoreExtents
== true) {
5019 /* Try next record for this fileID and forkType */
5020 err
= GetBTreeRecord (GPtr
->calculatedExtentsFCB
, 1, extentKey
,
5021 extentRecord
, recordSize
, &hint
);
5026 } /* SearchExtentInExtentBT */
5028 /* Function: FindExtentInExtentRec
5030 * Description: Traverse the given extent record (size based on if the volume is
5031 * HFS or HFSPlus volume) and find the index location if the given startBlock
5032 * and blockCount match.
5035 * 1. isHFSPlus - If the volume is plain HFS or HFS Plus.
5036 * 2. startBlock - startBlock to be searched in extent record.
5037 * 3. blockCount - blockCount to be searched in extent record.
5038 * 4. extentData - Extent Record to be searched.
5041 * Returns zero if the match is found, else fnfErr on failure.
5042 * 1. *foundExtentIndex - Index in extent record which matches the input data.
5043 * 2. *noMoreExtents - Indicates that no more extents exist after this extent
5046 static OSErr
FindExtentInExtentRec (Boolean isHFSPlus
, UInt32 startBlock
, UInt32 blockCount
, const HFSPlusExtentRecord extentData
, UInt32
*foundExtentIndex
, Boolean
*noMoreExtents
)
5049 UInt32 numOfExtents
;
5050 Boolean foundExtent
;
5053 foundExtent
= false;
5054 *noMoreExtents
= false;
5055 *foundExtentIndex
= 0;
5058 numOfExtents
= kHFSPlusExtentDensity
;
5060 numOfExtents
= kHFSExtentDensity
;
5063 for (i
=0; i
<numOfExtents
; i
++) {
5064 if (extentData
[i
].blockCount
== 0) {
5065 /* no more extents left to check */
5066 *noMoreExtents
= true;
5069 if ((startBlock
== extentData
[i
].startBlock
) &&
5070 (blockCount
== extentData
[i
].blockCount
)) {
5072 *foundExtentIndex
= i
;
5077 if (foundExtent
== false) {
5082 } /* FindExtentInExtentRec */
5084 /* Function: GetSystemFileName
5086 * Description: Return the name of the system file based on fileID
5089 * 1. fileID - fileID whose name is to be returned.
5090 * 2. *filenamelen - length of filename buffer.
5093 * 1. *filename - filename, is limited by the length of filename buffer passed
5095 * 2. *filenamelen - length of the filename
5096 * Always returns zero.
5098 OSErr
GetSystemFileName(UInt32 fileID
, char *filename
, unsigned int *filenamelen
)
5104 len
= *filenamelen
- 1;
5106 case kHFSExtentsFileID
:
5107 strncpy (filename
, "Extents Overflow BTree", len
);
5110 case kHFSCatalogFileID
:
5111 strncpy (filename
, "Catalog BTree", len
);
5114 case kHFSAllocationFileID
:
5115 strncpy (filename
, "Allocation File", len
);
5118 case kHFSStartupFileID
:
5119 strncpy (filename
, "Startup File", len
);
5122 case kHFSAttributesFileID
:
5123 strncpy (filename
, "Attributes BTree", len
);
5126 case kHFSBadBlockFileID
:
5127 strncpy (filename
, "Bad Allocation File", len
);
5130 case kHFSRepairCatalogFileID
:
5131 strncpy (filename
, "Repair Catalog File", len
);
5134 case kHFSBogusExtentFileID
:
5135 strncpy (filename
, "Bogus Extents File", len
);
5139 strncpy (filename
, "Unknown File", len
);
5142 filename
[len
] = '\0';
5143 *filenamelen
= strlen (filename
);
5148 /* structure to store the intermediate path components during BTree traversal.
5149 * This is used as a LIFO linked list
5154 unsigned int namelen
;
5155 struct fsPathString
*childPtr
;
5158 /* Function: GetFileNamePathByID
5160 * Description: Return the file/directory name and/or full path by ID. The
5161 * length of the strings returned is limited by string lengths passed as
5163 * The function lookups catalog thread record for given fileID and its parents
5164 * until it reaches the Root Folder.
5167 * 1. The path returned currently does not return mangled names.
5168 * 2. Either one or both of fullPath and fileName can be NULL.
5169 * 3. fullPath and fileName are returned as NULL-terminated UTF8 strings.
5170 * 4. Returns error if fileID < kHFSFirstUserCatalogID.
5173 * 1. GPtr - global scavenger structure pointer
5174 * 2. fileID - fileID for the target file/directory for finding the path
5175 * 3. fullPathLen - size of array to return full path
5176 * 4. fileNameLen - size of array to return file name
5179 * Return value: zero on success, non-zero on failure
5180 * memFullErr - Not enough memory
5181 * paramErr - Invalid paramter
5183 * The data in *fileNameLen and *fullPathLen is undefined on error.
5185 * 1. fullPath - If fullPath is non-NULL, full path of file/directory is
5186 * returned (size limited by fullPathLen)
5187 * 2. *fullPathLen - length of fullPath returned.
5188 * 3. fileName - If fileName is non-NULL, file name of fileID is returned (size
5189 * limited by fileNameLen).
5190 * 4. *fileNameLen - length of fileName returned.
5191 * 5. *status - status of operation, any of the following bits can be set
5192 * (defined in dfalib/Scavenger.h).
5193 * FNAME_BUF2SMALL - filename buffer is too small.
5194 * FNAME_BIGNAME - filename is more than NAME_MAX bytes.
5195 * FPATH_BUF2SMALL - path buffer is too small.
5196 * FPATH_BIGNAME - one or more intermediate path component is greater
5197 * than NAME_MAX bytes.
5198 * F_RESERVE_FILEID- fileID is less than kHFSFirstUserCatalogNodeID.
5200 OSErr
GetFileNamePathByID(SGlobPtr GPtr
, UInt32 fileID
, char *fullPath
, unsigned int *fullPathLen
, char *fileName
, unsigned int *fileNameLen
, u_int16_t
*status
)
5205 UInt16 curStatus
= 0;
5208 CatalogRecord catRecord
;
5209 struct fsPathString
*listHeadPtr
= NULL
;
5210 struct fsPathString
*listTailPtr
= NULL
;
5211 struct fsPathString
*curPtr
= NULL
;
5212 u_char
*filename
= NULL
;
5215 if (!fullPath
&& !fileName
) {
5219 if (fileID
< kHFSFirstUserCatalogNodeID
) {
5220 curStatus
= F_RESERVE_FILEID
;
5225 isHFSPlus
= VolumeObjectIsHFSPlus();
5228 filename
= malloc(kHFSPlusMaxFileNameChars
* 3 + 1);
5230 filename
= malloc(kHFSMaxFileNameChars
+ 1);
5235 plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__
, err
);
5240 while (fileID
!= kHFSRootFolderID
) {
5241 /* lookup for thread record for this fileID */
5242 BuildCatalogKey(fileID
, NULL
, isHFSPlus
, &catKey
);
5243 err
= SearchBTreeRecord(GPtr
->calculatedCatalogFCB
, &catKey
, kNoHint
,
5244 &catKey
, &catRecord
, &recordSize
, &hint
);
5247 plog ("%s: Error finding thread record for fileID = %d (err=%d)\n", __FUNCTION__
, fileID
, err
);
5252 /* Check if this is indeed a thread record */
5253 if ((catRecord
.hfsPlusThread
.recordType
!= kHFSPlusFileThreadRecord
) &&
5254 (catRecord
.hfsPlusThread
.recordType
!= kHFSPlusFolderThreadRecord
) &&
5255 (catRecord
.hfsThread
.recordType
!= kHFSFileThreadRecord
) &&
5256 (catRecord
.hfsThread
.recordType
!= kHFSFolderThreadRecord
)) {
5259 plog ("%s: Error finding valid thread record for fileID = %d\n", __FUNCTION__
, fileID
);
5264 /* Convert the name string to utf8 */
5266 (void) utf_encodestr(catRecord
.hfsPlusThread
.nodeName
.unicode
,
5267 catRecord
.hfsPlusThread
.nodeName
.length
* 2,
5268 filename
, &namelen
, kHFSPlusMaxFileNameChars
* 3 + 1);
5270 namelen
= catRecord
.hfsThread
.nodeName
[0];
5271 memcpy (filename
, catKey
.hfs
.nodeName
, namelen
);
5274 /* Store the path name in LIFO linked list */
5275 curPtr
= malloc(sizeof(struct fsPathString
));
5279 plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__
, err
);
5284 /* Do not NULL terminate the string */
5285 curPtr
->namelen
= namelen
;
5286 curPtr
->name
= malloc(namelen
);
5287 if (!curPtr
->name
) {
5290 plog ("%s: Not enough memory (err=%d)\n", __FUNCTION__
, err
);
5293 memcpy (curPtr
->name
, filename
, namelen
);
5294 curPtr
->childPtr
= listHeadPtr
;
5295 listHeadPtr
= curPtr
;
5296 if (listTailPtr
== NULL
) {
5297 listTailPtr
= curPtr
;
5300 /* lookup for parentID */
5302 fileID
= catRecord
.hfsPlusThread
.parentID
;
5304 fileID
= catRecord
.hfsThread
.parentID
;
5307 /* no need to find entire path, bail out */
5308 if (fullPath
== NULL
) {
5313 /* return the name of the file/directory */
5315 /* Do not overflow the buffer length passed */
5316 if (*fileNameLen
< (listTailPtr
->namelen
+ 1)) {
5317 *fileNameLen
= *fileNameLen
- 1;
5318 curStatus
|= FNAME_BUF2SMALL
;
5320 *fileNameLen
= listTailPtr
->namelen
;
5322 if (*fileNameLen
> NAME_MAX
) {
5323 curStatus
|= FNAME_BIGNAME
;
5325 memcpy (fileName
, listTailPtr
->name
, *fileNameLen
);
5326 fileName
[*fileNameLen
] = '\0';
5329 /* return the full path of the file/directory */
5331 /* Do not overflow the buffer length passed and reserve last byte for NULL termination */
5332 unsigned int bytesRemain
= *fullPathLen
- 1;
5335 while (listHeadPtr
!= NULL
) {
5336 if (bytesRemain
== 0) {
5339 memcpy ((fullPath
+ *fullPathLen
), "/", 1);
5343 if (bytesRemain
== 0) {
5346 if (bytesRemain
< listHeadPtr
->namelen
) {
5347 namelen
= bytesRemain
;
5348 curStatus
|= FPATH_BUF2SMALL
;
5350 namelen
= listHeadPtr
->namelen
;
5352 if (namelen
> NAME_MAX
) {
5353 curStatus
|= FPATH_BIGNAME
;
5355 memcpy ((fullPath
+ *fullPathLen
), listHeadPtr
->name
, namelen
);
5356 *fullPathLen
+= namelen
;
5357 bytesRemain
-= namelen
;
5359 curPtr
= listHeadPtr
;
5360 listHeadPtr
= listHeadPtr
->childPtr
;
5365 fullPath
[*fullPathLen
] = '\0';
5372 *status
= curStatus
;
5375 /* free any remaining allocated memory */
5376 while (listHeadPtr
!= NULL
) {
5377 curPtr
= listHeadPtr
;
5378 listHeadPtr
= listHeadPtr
->childPtr
;
5380 free (curPtr
->name
);
5389 } /* GetFileNamePathByID */
5391 /* Function: CopyDiskBlocks
5393 * Description: Copy data from source extent to destination extent
5394 * for blockCount on the disk.
5397 * 1. GPtr - pointer to global scavenger structure.
5398 * 2. startAllocationBlock - startBlock for old extent
5399 * 3. blockCount - total blocks to copy
5400 * 4. newStartAllocationBlock - startBlock for new extent
5403 * err, zero on success, non-zero on failure.
5405 OSErr
CopyDiskBlocks(SGlobPtr GPtr
, const UInt32 startAllocationBlock
, const UInt32 blockCount
, const UInt32 newStartAllocationBlock
)
5409 uint64_t old_offset
;
5410 uint64_t new_offset
;
5411 uint32_t sectorsPerBlock
;
5413 vcb
= GPtr
->calculatedVCB
;
5414 sectorsPerBlock
= vcb
->vcbBlockSize
/ Blk_Size
;
5416 old_offset
= (vcb
->vcbAlBlSt
+ (sectorsPerBlock
* startAllocationBlock
)) << Log2BlkLo
;
5417 new_offset
= (vcb
->vcbAlBlSt
+ (sectorsPerBlock
* newStartAllocationBlock
)) << Log2BlkLo
;
5419 err
= CacheCopyDiskBlocks (vcb
->vcbBlockCache
, old_offset
, new_offset
,
5420 blockCount
* vcb
->vcbBlockSize
);
5422 } /* CopyDiskBlocks */
5424 /* Function: WriteBufferToDisk
5426 * Description: Write given buffer data to disk blocks.
5427 * If the length of the buffer is not a multiple of allocation block size,
5428 * the disk is filled with zero from the length of buffer upto the
5429 * end of allocation blocks (specified by blockCount).
5432 * 1. GPtr - global scavenger structure pointer
5433 * 2. startBlock - starting block number for writing data.
5434 * 3. blockCount - total number of contiguous blocks to be written
5435 * 4. buffer - data buffer to be written to disk
5436 * 5. bufLen - length of data buffer to be written to disk.
5439 * returns zero on success, non-zero on failure.
5441 OSErr
WriteBufferToDisk(SGlobPtr GPtr
, UInt32 startBlock
, UInt32 blockCount
, u_char
*buffer
, int bufLen
)
5448 vcb
= GPtr
->calculatedVCB
;
5450 /* Calculate offset and length */
5451 offset
= (vcb
->vcbAlBlSt
+ ((vcb
->vcbBlockSize
/ Blk_Size
) * startBlock
)) << Log2BlkLo
;
5452 write_len
= blockCount
* vcb
->vcbBlockSize
;
5454 /* Write buffer to disk */
5455 err
= CacheWriteBufferToDisk (vcb
->vcbBlockCache
, offset
, write_len
, buffer
, bufLen
);
5458 } /* WriteBufferToDisk */
5460 // 2210409, in System 8.1, moving file or folder would cause HFS+ thread records to be
5461 // 520 bytes in size. We only shrink the threads if other repairs are needed.
5462 static OSErr
FixBloatedThreadRecords( SGlob
*GPtr
)
5464 CatalogRecord record
;
5465 CatalogKey foundKey
;
5470 SInt16 selCode
= 0x8001; // Start with 1st record
5472 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &foundKey
, &record
, &recordSize
, &hint
);
5473 ReturnIfError( err
);
5475 selCode
= 1; // Get next record from now on
5479 if ( ++i
> 10 ) { (void) CheckForStop( GPtr
); i
= 0; } // Spin the cursor every 10 entries
5481 if ( (recordSize
== sizeof(HFSPlusCatalogThread
)) && ((record
.recordType
== kHFSPlusFolderThreadRecord
) || (record
.recordType
== kHFSPlusFileThreadRecord
)) )
5483 // HFS Plus has varaible sized threads so adjust to actual length
5484 recordSize
-= ( sizeof(record
.hfsPlusThread
.nodeName
.unicode
) - (record
.hfsPlusThread
.nodeName
.length
* sizeof(UniChar
)) );
5486 err
= ReplaceBTreeRecord( GPtr
->calculatedCatalogFCB
, &foundKey
, hint
, &record
, recordSize
, &hint
);
5487 ReturnIfError( err
);
5490 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &foundKey
, &record
, &recordSize
, &hint
);
5491 } while ( err
== noErr
);
5493 if ( err
== btNotFound
)
5501 FixMissingThreadRecords( SGlob
*GPtr
)
5503 struct MissingThread
* mtp
;
5504 FSBufferDescriptor btRecord
;
5505 BTreeIterator iterator
;
5509 UInt32 lostAndFoundDirID
;
5511 lostAndFoundDirID
= 0;
5513 for (mtp
= GPtr
->missingThreadList
; mtp
!= NULL
; mtp
= mtp
->link
) {
5514 if ( mtp
->threadID
== 0 )
5517 // if the thread record information in the MissingThread struct is not there
5518 // then we have a missing directory in addition to a missing thread record
5519 // for that directory. We will recreate the missing directory in our
5520 // lost+found directory.
5521 if ( mtp
->thread
.parentID
== 0 ) {
5522 if (embedded
== 1 && debug
== 0) {
5525 if ( lostAndFoundDirID
== 0 )
5526 lostAndFoundDirID
= CreateDirByName( GPtr
, (u_char
*)"lost+found", kHFSRootFolderID
);
5527 if ( lostAndFoundDirID
== 0 ) {
5528 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
5529 plog( "\tCould not create lost+found directory \n" );
5532 fsckPrint(GPtr
->context
, E_NoDir
, mtp
->threadID
);
5533 result
= FixMissingDirectory( GPtr
, mtp
->threadID
, lostAndFoundDirID
);
5534 if ( result
!= 0 ) {
5535 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
5536 plog( "\tCould not recreate a missing directory (error %d)\n", result
);
5544 dataSize
= 10 + (mtp
->thread
.nodeName
.length
* 2);
5545 btRecord
.bufferAddress
= (void *)&mtp
->thread
;
5546 btRecord
.itemSize
= dataSize
;
5547 btRecord
.itemCount
= 1;
5548 iterator
.hint
.nodeNum
= 0;
5549 BuildCatalogKey(mtp
->threadID
, NULL
, true, (CatalogKey
*)&iterator
.key
);
5551 result
= BTInsertRecord(GPtr
->calculatedCatalogFCB
, &iterator
, &btRecord
, dataSize
);
5553 return (IntError(GPtr
, R_IntErr
));
5557 fsckPrint(GPtr
->context
, fsckLostFoundDirectory
, "lost+found");
5564 FixMissingDirectory( SGlob
*GPtr
, UInt32 theObjID
, UInt32 theParID
)
5571 char myString
[ 32 ];
5573 CatalogRecord catRec
;
5574 CatalogKey myKey
, myThreadKey
;
5576 isHFSPlus
= VolumeObjectIsHFSPlus( );
5578 // we will use the object ID of the missing directory as the name since we have
5579 // no way to find the original name and this should make it unique within our
5580 // lost+found directory.
5581 sprintf( myString
, "%ld", (long)theObjID
);
5582 nameLen
= strlen( myString
);
5587 myName
.ustr
.length
= nameLen
;
5588 for ( i
= 0; i
< myName
.ustr
.length
; i
++ )
5589 myName
.ustr
.unicode
[ i
] = (u_int16_t
) myString
[ i
];
5593 myName
.pstr
[0] = nameLen
;
5594 memcpy( &myName
.pstr
[1], &myString
[0], nameLen
);
5597 // make sure the name is not already used
5598 BuildCatalogKey( theParID
, &myName
, isHFSPlus
, &myKey
);
5599 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &myKey
, kNoHint
,
5600 NULL
, &catRec
, &recSize
, &hint
);
5601 if ( result
== noErr
)
5604 // insert new directory and thread record into the catalog
5605 recSize
= BuildThreadRec( &myKey
, &catRec
, isHFSPlus
, true );
5606 BuildCatalogKey( theObjID
, NULL
, isHFSPlus
, &myThreadKey
);
5607 result
= InsertBTreeRecord( GPtr
->calculatedCatalogFCB
, &myThreadKey
, &catRec
, recSize
, &hint
);
5608 if ( result
!= noErr
)
5611 recSize
= BuildFolderRec( GPtr
, 01777, theObjID
, isHFSPlus
, &catRec
);
5613 result
= InsertBTreeRecord( GPtr
->calculatedCatalogFCB
, &myKey
, &catRec
, recSize
, &hint
);
5614 if ( result
!= noErr
)
5617 /* update parent directory to reflect addition of new directory */
5618 result
= UpdateFolderCount( GPtr
->calculatedVCB
, theParID
, NULL
,
5619 ((isHFSPlus
) ? kHFSPlusFolderRecord
: kHFSFolderRecord
),
5622 /* update our header node on disk from our BTreeControlBlock */
5623 UpdateBTreeHeader( GPtr
->calculatedCatalogFCB
);
5627 } /* FixMissingDirectory */
5630 static HFSCatalogNodeID
5631 GetObjectID( CatalogRecord
* theRecPtr
)
5633 HFSCatalogNodeID myObjID
;
5635 switch ( theRecPtr
->recordType
) {
5636 case kHFSPlusFolderRecord
:
5637 myObjID
= theRecPtr
->hfsPlusFolder
.folderID
;
5639 case kHFSPlusFileRecord
:
5640 myObjID
= theRecPtr
->hfsPlusFile
.fileID
;
5642 case kHFSFolderRecord
:
5643 myObjID
= theRecPtr
->hfsFolder
.folderID
;
5645 case kHFSFileRecord
:
5646 myObjID
= theRecPtr
->hfsFile
.fileID
;
5656 /* Function: CreateFileByName
5658 * Description: Create a file with given fileName of type fileType containing
5659 * data of length dataLen. This function assumes that the name of symlink
5660 * to be created is passed as UTF8
5663 * 1. GPtr - pointer to global scavenger structure
5664 * 2. parentID - ID of parent directory to create the new file.
5665 * 3. fileName - name of the file to create in UTF8 format.
5666 * 4. fileNameLen - length of the filename to be created.
5667 * If the volume is HFS Plus, the filename is delimited to
5668 * kHFSPlusMaxFileNameChars characters.
5669 * If the volume is plain HFS, the filename is delimited to
5670 * kHFSMaxFileNameChars characters.
5671 * 5. fileType - file type (currently supported S_IFREG, S_IFLNK).
5672 * 6. data - data content of first data fork of the file
5673 * 7. dataLen - length of data to be written
5676 * returns zero on success, non-zero on failure.
5677 * memFullErr - Not enough memory
5678 * paramErr - Invalid paramter
5680 OSErr
CreateFileByName(SGlobPtr GPtr
, UInt32 parentID
, UInt16 fileType
, u_char
*fileName
, unsigned int filenameLen
, u_char
*data
, unsigned int dataLen
)
5684 Boolean isCatUpdated
= false;
5687 CatalogRecord catRecord
;
5689 CatalogKey threadKey
;
5693 UInt32 startBlock
= 0;
5694 UInt32 blockCount
= 0;
5697 isHFSPlus
= VolumeObjectIsHFSPlus();
5699 /* Construct unicode name for file name to construct catalog key */
5701 /* Convert utf8 filename to Unicode filename */
5704 if (filenameLen
< kHFSPlusMaxFileNameChars
) {
5705 (void) utf_decodestr (fileName
, filenameLen
, fName
.ustr
.unicode
, &namelen
, sizeof(fName
.ustr
.unicode
));
5707 fName
.ustr
.length
= namelen
;
5709 /* The resulting may result in more than kHFSPlusMaxFileNameChars chars */
5710 UInt16
*unicodename
;
5712 /* Allocate a large array to convert the utf-8 to utf-16 */
5713 unicodename
= malloc (filenameLen
* 4);
5714 if (unicodename
== NULL
) {
5719 (void) utf_decodestr (fileName
, filenameLen
, unicodename
, &namelen
, filenameLen
* 4);
5722 /* Chopping unicode string based on length might affect unicode
5723 * chars that take more than one UInt16 - very rare possiblity.
5725 if (namelen
> kHFSPlusMaxFileNameChars
) {
5726 namelen
= kHFSPlusMaxFileNameChars
;
5729 memcpy (fName
.ustr
.unicode
, unicodename
, (namelen
* 2));
5731 fName
.ustr
.length
= namelen
;
5734 if (filenameLen
> kHFSMaxFileNameChars
) {
5735 filenameLen
= kHFSMaxFileNameChars
;
5737 fName
.pstr
[0] = filenameLen
;
5738 memcpy(&fName
.pstr
[1], fileName
, filenameLen
);
5741 /* Make sure that a file with same name does not exist in parent dir */
5742 BuildCatalogKey(parentID
, &fName
, isHFSPlus
, &catKey
);
5743 err
= SearchBTreeRecord(GPtr
->calculatedCatalogFCB
, &catKey
, kNoHint
, NULL
,
5744 &catRecord
, &recordSize
, &hint
);
5745 if (err
!= fsBTRecordNotFoundErr
) {
5747 plog ("%s: %s probably exists in dirID = %d (err=%d)\n", __FUNCTION__
, fileName
, parentID
, err
);
5754 /* Calculate correct number of blocks required for data */
5755 if (dataLen
% (GPtr
->calculatedVCB
->vcbBlockSize
)) {
5756 blockCount
= (dataLen
/ (GPtr
->calculatedVCB
->vcbBlockSize
)) + 1;
5758 blockCount
= dataLen
/ (GPtr
->calculatedVCB
->vcbBlockSize
);
5762 /* Allocate disk space for the data */
5763 err
= AllocateContigBitmapBits (GPtr
->calculatedVCB
, blockCount
, &startBlock
);
5766 plog ("%s: Not enough disk space (err=%d)\n", __FUNCTION__
, err
);
5771 /* Write the data to the blocks */
5772 err
= WriteBufferToDisk(GPtr
, startBlock
, blockCount
, data
, dataLen
);
5775 plog ("%s: Error in writing data of %s to disk (err=%d)\n", __FUNCTION__
, fileName
, err
);
5782 /* Build and insert thread record */
5783 nextCNID
= GPtr
->calculatedVCB
->vcbNextCatalogID
;
5784 if (!isHFSPlus
&& nextCNID
== 0xffffFFFF) {
5787 recordSize
= BuildThreadRec(&catKey
, &catRecord
, isHFSPlus
, false);
5789 BuildCatalogKey(nextCNID
, NULL
, isHFSPlus
, &threadKey
);
5790 err
= InsertBTreeRecord(GPtr
->calculatedCatalogFCB
, &threadKey
, &catRecord
,
5791 recordSize
, &hint
);
5792 if (err
== fsBTDuplicateRecordErr
&& isHFSPlus
) {
5793 /* Allow CNIDs on HFS Plus volumes to wrap around */
5795 if (nextCNID
< kHFSFirstUserCatalogNodeID
) {
5796 GPtr
->calculatedVCB
->vcbAttributes
|= kHFSCatalogNodeIDsReusedMask
;
5797 MarkVCBDirty(GPtr
->calculatedVCB
);
5798 nextCNID
= kHFSFirstUserCatalogNodeID
;
5806 plog ("%s: Error inserting thread record for file = %s (err=%d)\n", __FUNCTION__
, fileName
, err
);
5811 /* Build file record */
5812 recordSize
= BuildFileRec(fileType
, 0666, nextCNID
, isHFSPlus
, &catRecord
);
5813 if (recordSize
== 0) {
5815 plog ("%s: Incorrect fileType\n", __FUNCTION__
);
5818 /* Remove the thread record inserted above */
5819 err
= DeleteBTreeRecord (GPtr
->calculatedCatalogFCB
, &threadKey
);
5822 plog ("%s: Error in removing thread record\n", __FUNCTION__
);
5829 /* Update startBlock, blockCount, etc */
5831 catRecord
.hfsPlusFile
.dataFork
.logicalSize
= dataLen
;
5832 catRecord
.hfsPlusFile
.dataFork
.totalBlocks
= blockCount
;
5833 catRecord
.hfsPlusFile
.dataFork
.extents
[0].startBlock
= startBlock
;
5834 catRecord
.hfsPlusFile
.dataFork
.extents
[0].blockCount
= blockCount
;
5836 catRecord
.hfsFile
.dataLogicalSize
= dataLen
;
5837 catRecord
.hfsFile
.dataPhysicalSize
= blockCount
* GPtr
->calculatedVCB
->vcbBlockSize
;
5838 catRecord
.hfsFile
.dataExtents
[0].startBlock
= startBlock
;
5839 catRecord
.hfsFile
.dataExtents
[0].blockCount
= blockCount
;
5842 /* Insert catalog file record */
5843 err
= InsertBTreeRecord(GPtr
->calculatedCatalogFCB
, &catKey
, &catRecord
, recordSize
, &hint
);
5845 isCatUpdated
= true;
5848 plog ("Created \"%s\" with ID = %d startBlock = %d, blockCount = %d, dataLen = %d\n", fileName
, nextCNID
, startBlock
, blockCount
, dataLen
);
5852 plog ("%s: Error in inserting file record for file = %s (err=%d)\n", __FUNCTION__
, fileName
, err
);
5855 /* remove the thread record inserted above */
5856 err
= DeleteBTreeRecord (GPtr
->calculatedCatalogFCB
, &threadKey
);
5859 plog ("%s: Error in removing thread record\n", __FUNCTION__
);
5866 /* Update volume header */
5867 GPtr
->calculatedVCB
->vcbNextCatalogID
= nextCNID
+ 1;
5868 if (GPtr
->calculatedVCB
->vcbNextCatalogID
< kHFSFirstUserCatalogNodeID
) {
5869 GPtr
->calculatedVCB
->vcbAttributes
|= kHFSCatalogNodeIDsReusedMask
;
5870 GPtr
->calculatedVCB
->vcbNextCatalogID
= kHFSFirstUserCatalogNodeID
;
5872 MarkVCBDirty( GPtr
->calculatedVCB
);
5874 /* update our header node on disk from our BTreeControlBlock */
5875 UpdateBTreeHeader(GPtr
->calculatedCatalogFCB
);
5877 /* update parent directory to reflect addition of new file */
5878 err
= UpdateFolderCount(GPtr
->calculatedVCB
, parentID
, NULL
, kHFSPlusFileRecord
, kNoHint
, 1);
5881 plog ("%s: Error in updating parent folder count (err=%d)\n", __FUNCTION__
, err
);
5887 /* On error, if catalog record was not inserted and disk block were allocated,
5888 * deallocate the blocks
5890 if (err
&& (isCatUpdated
== false) && startBlock
) {
5891 ReleaseBitmapBits (startBlock
, blockCount
);
5895 } /* CreateFileByName */
5897 /* Function: CreateDirByName
5899 * Description: Create directory with name dirName in a directory with ID as
5900 * parentID. The function assumes that the dirName passed is ASCII.
5903 * GPtr - global scavenger structure pointer
5904 * dirName - name of directory to be created
5905 * parentID - dirID of the parent directory for new directory
5908 * on success, ID of the new directory created.
5912 UInt32
CreateDirByName(SGlob
*GPtr
, const u_char
*dirName
, const UInt32 parentID
)
5924 CatalogRecord catRec
;
5926 isHFSPlus
= VolumeObjectIsHFSPlus( );
5927 fcbPtr
= GPtr
->calculatedCatalogFCB
;
5928 nameLen
= strlen( (char *)dirName
);
5933 myName
.ustr
.length
= nameLen
;
5934 for ( i
= 0; i
< myName
.ustr
.length
; i
++ )
5935 myName
.ustr
.unicode
[ i
] = (u_int16_t
) dirName
[ i
];
5939 myName
.pstr
[0] = nameLen
;
5940 memcpy( &myName
.pstr
[1], &dirName
[0], nameLen
);
5943 // see if we already have a lost and found directory
5944 BuildCatalogKey( parentID
, &myName
, isHFSPlus
, &myKey
);
5945 result
= SearchBTreeRecord( fcbPtr
, &myKey
, kNoHint
, NULL
, &catRec
, &recSize
, &hint
);
5946 if ( result
== noErr
) {
5948 if ( catRec
.recordType
== kHFSPlusFolderRecord
)
5949 return( catRec
.hfsPlusFolder
.folderID
);
5951 else if ( catRec
.recordType
== kHFSFolderRecord
)
5952 return( catRec
.hfsFolder
.folderID
);
5953 return( 0 ); // something already there but not a directory
5956 // insert new directory and thread record into the catalog
5957 nextCNID
= GPtr
->calculatedVCB
->vcbNextCatalogID
;
5958 if ( !isHFSPlus
&& nextCNID
== 0xFFFFFFFF )
5961 recSize
= BuildThreadRec( &myKey
, &catRec
, isHFSPlus
, true );
5965 BuildCatalogKey( nextCNID
, NULL
, isHFSPlus
, &key
);
5966 result
= InsertBTreeRecord( fcbPtr
, &key
, &catRec
, recSize
, &hint
);
5967 if ( result
== fsBTDuplicateRecordErr
&& isHFSPlus
) {
5969 * Allow CNIDs on HFS Plus volumes to wrap around
5972 if ( nextCNID
< kHFSFirstUserCatalogNodeID
) {
5973 GPtr
->calculatedVCB
->vcbAttributes
|= kHFSCatalogNodeIDsReusedMask
;
5974 MarkVCBDirty( GPtr
->calculatedVCB
);
5975 nextCNID
= kHFSFirstUserCatalogNodeID
;
5984 myMode
= ( GPtr
->lostAndFoundMode
== 0 ) ? 01777 : GPtr
->lostAndFoundMode
;
5985 recSize
= BuildFolderRec( GPtr
, myMode
, nextCNID
, isHFSPlus
, &catRec
);
5986 result
= InsertBTreeRecord( fcbPtr
, &myKey
, &catRec
, recSize
, &hint
);
5990 /* Update volume header */
5991 GPtr
->calculatedVCB
->vcbNextCatalogID
= nextCNID
+ 1;
5992 if ( GPtr
->calculatedVCB
->vcbNextCatalogID
< kHFSFirstUserCatalogNodeID
) {
5993 GPtr
->calculatedVCB
->vcbAttributes
|= kHFSCatalogNodeIDsReusedMask
;
5994 GPtr
->calculatedVCB
->vcbNextCatalogID
= kHFSFirstUserCatalogNodeID
;
5996 MarkVCBDirty( GPtr
->calculatedVCB
);
5998 /* update parent directory to reflect addition of new directory */
5999 result
= UpdateFolderCount( GPtr
->calculatedVCB
, parentID
, NULL
, kHFSPlusFolderRecord
, kNoHint
, 1 );
6001 /* update our header node on disk from our BTreeControlBlock */
6002 UpdateBTreeHeader( GPtr
->calculatedCatalogFCB
);
6006 } /* CreateDirByName */
6009 CountFolderItems(SGlobPtr GPtr
, UInt32 folderID
, Boolean isHFSPlus
, UInt32
*itemCount
, UInt32
*folderCount
)
6011 SFCB
*fcb
= GPtr
->calculatedCatalogFCB
;
6013 BTreeIterator iterator
;
6014 FSBufferDescriptor btRecord
;
6016 HFSPlusCatalogFolder catRecord
;
6017 HFSPlusCatalogFile catFile
;
6019 HFSPlusCatalogKey
*key
;
6020 UInt16 recordSize
= 0;
6021 int fCount
= 0, iCount
= 0;
6023 ClearMemory(&iterator
, sizeof(iterator
));
6024 key
= (HFSPlusCatalogKey
*)&iterator
.key
;
6025 BuildCatalogKey(folderID
, NULL
, isHFSPlus
, (CatalogKey
*)key
);
6026 btRecord
.bufferAddress
= &catRecord
;
6027 btRecord
.itemCount
= 1;
6028 btRecord
.itemSize
= sizeof(catRecord
);
6030 for (err
= BTSearchRecord(fcb
, &iterator
, kNoHint
, &btRecord
, &recordSize
, &iterator
);
6032 err
= BTIterateRecord(fcb
, kBTreeNextRecord
, &iterator
, &btRecord
, &recordSize
)) {
6033 if (catRecord
.catRecord
.recordType
== kHFSPlusFolderThreadRecord
||
6034 catRecord
.catRecord
.recordType
== kHFSPlusFileThreadRecord
||
6035 catRecord
.catRecord
.recordType
== kHFSFolderThreadRecord
||
6036 catRecord
.catRecord
.recordType
== kHFSFileThreadRecord
)
6038 if (key
->parentID
!= folderID
)
6041 (catRecord
.catRecord
.recordType
== kHFSPlusFileRecord
) &&
6042 (catRecord
.catFile
.flags
& kHFSHasLinkChainMask
) &&
6043 (catRecord
.catFile
.userInfo
.fdType
== kHFSAliasType
) &&
6044 (catRecord
.catFile
.userInfo
.fdCreator
== kHFSAliasCreator
) &&
6045 (key
->parentID
!= GPtr
->filelink_priv_dir_id
)) {
6046 // It's a directory hard link, which counts as a directory here
6049 if (catRecord
.catRecord
.recordType
== kHFSPlusFolderRecord
)
6054 *itemCount
= iCount
;
6056 *folderCount
= fCount
;
6060 * Build a catalog node folder record with the given input.
6063 BuildFolderRec( SGlob
*GPtr
, u_int16_t theMode
, UInt32 theObjID
, Boolean isHFSPlus
, CatalogRecord
* theRecPtr
)
6067 UInt32 vCount
= 0, fCount
= 0;
6069 ClearMemory( (Ptr
)theRecPtr
, sizeof(*theRecPtr
) );
6071 CountFolderItems(GPtr
, theObjID
, isHFSPlus
, &vCount
, &fCount
);
6073 createTime
= GetTimeUTC();
6074 theRecPtr
->hfsPlusFolder
.recordType
= kHFSPlusFolderRecord
;
6075 theRecPtr
->hfsPlusFolder
.folderID
= theObjID
;
6076 theRecPtr
->hfsPlusFolder
.createDate
= createTime
;
6077 theRecPtr
->hfsPlusFolder
.contentModDate
= createTime
;
6078 theRecPtr
->hfsPlusFolder
.attributeModDate
= createTime
;
6079 theRecPtr
->hfsPlusFolder
.bsdInfo
.ownerID
= getuid( );
6080 theRecPtr
->hfsPlusFolder
.bsdInfo
.groupID
= getgid( );
6081 theRecPtr
->hfsPlusFolder
.bsdInfo
.fileMode
= S_IFDIR
;
6082 theRecPtr
->hfsPlusFolder
.bsdInfo
.fileMode
|= theMode
;
6083 theRecPtr
->hfsPlusFolder
.valence
= vCount
;
6084 recSize
= sizeof(HFSPlusCatalogFolder
);
6085 if (VolumeObjectIsHFSX(GPtr
)) {
6086 theRecPtr
->hfsPlusFolder
.flags
|= kHFSHasFolderCountMask
;
6087 theRecPtr
->hfsPlusFolder
.folderCount
= fCount
;
6091 createTime
= GetTimeLocal( true );
6092 theRecPtr
->hfsFolder
.recordType
= kHFSFolderRecord
;
6093 theRecPtr
->hfsFolder
.folderID
= theObjID
;
6094 theRecPtr
->hfsFolder
.createDate
= createTime
;
6095 theRecPtr
->hfsFolder
.modifyDate
= createTime
;
6096 theRecPtr
->hfsFolder
.valence
= vCount
;
6097 recSize
= sizeof(HFSCatalogFolder
);
6102 } /* BuildFolderRec */
6106 * Build a catalog node thread record from a catalog key
6107 * and return the size of the record.
6110 BuildThreadRec( CatalogKey
* theKeyPtr
, CatalogRecord
* theRecPtr
,
6111 Boolean isHFSPlus
, Boolean isDirectory
)
6116 HFSPlusCatalogKey
*key
= (HFSPlusCatalogKey
*)theKeyPtr
;
6117 HFSPlusCatalogThread
*rec
= (HFSPlusCatalogThread
*)theRecPtr
;
6119 size
= sizeof(HFSPlusCatalogThread
);
6121 rec
->recordType
= kHFSPlusFolderThreadRecord
;
6123 rec
->recordType
= kHFSPlusFileThreadRecord
;
6125 rec
->parentID
= key
->parentID
;
6126 bcopy(&key
->nodeName
, &rec
->nodeName
,
6127 sizeof(UniChar
) * (key
->nodeName
.length
+ 1));
6129 /* HFS Plus has varaible sized thread records */
6130 size
-= (sizeof(rec
->nodeName
.unicode
) -
6131 (rec
->nodeName
.length
* sizeof(UniChar
)));
6133 else /* HFS standard */ {
6134 HFSCatalogKey
*key
= (HFSCatalogKey
*)theKeyPtr
;
6135 HFSCatalogThread
*rec
= (HFSCatalogThread
*)theRecPtr
;
6137 size
= sizeof(HFSCatalogThread
);
6140 rec
->recordType
= kHFSFolderThreadRecord
;
6142 rec
->recordType
= kHFSFileThreadRecord
;
6143 rec
->parentID
= key
->parentID
;
6144 bcopy(key
->nodeName
, rec
->nodeName
, key
->nodeName
[0]+1);
6149 } /* BuildThreadRec */
6151 /* Function: BuildFileRec
6153 * Description: Build a catalog file record with given fileID, fileType
6157 * 1. fileType - currently supports S_IFREG, S_IFLNK
6158 * 2. fileMode - file mode desired.
6159 * 3. fileID - file ID
6160 * 4. isHFSPlus - indicates whether the record is being created for
6161 * HFSPlus volume or plain HFS volume.
6162 * 5. catRecord - pointer to catalog record
6165 * returns size of the catalog record.
6166 * on success, non-zero value; on failure, zero.
6168 static int BuildFileRec(UInt16 fileType
, UInt16 fileMode
, UInt32 fileID
, Boolean isHFSPlus
, CatalogRecord
*catRecord
)
6170 UInt16 recordSize
= 0;
6173 /* We only support creating S_IFREG and S_IFLNK and S_IFLNK is not supported
6176 if (((fileType
!= S_IFREG
) && (fileType
!= S_IFLNK
)) ||
6177 ((isHFSPlus
== false) && (fileType
== S_IFLNK
))) {
6181 ClearMemory((Ptr
)catRecord
, sizeof(*catRecord
));
6184 createTime
= GetTimeUTC();
6185 catRecord
->hfsPlusFile
.recordType
= kHFSPlusFileRecord
;
6186 catRecord
->hfsPlusFile
.fileID
= fileID
;
6187 catRecord
->hfsPlusFile
.createDate
= createTime
;
6188 catRecord
->hfsPlusFile
.contentModDate
= createTime
;
6189 catRecord
->hfsPlusFile
.attributeModDate
= createTime
;
6190 catRecord
->hfsPlusFile
.bsdInfo
.ownerID
= getuid();
6191 catRecord
->hfsPlusFile
.bsdInfo
.groupID
= getgid();
6192 catRecord
->hfsPlusFile
.bsdInfo
.fileMode
= fileType
;
6193 catRecord
->hfsPlusFile
.bsdInfo
.fileMode
|= fileMode
;
6194 if (fileType
== S_IFLNK
) {
6195 catRecord
->hfsPlusFile
.userInfo
.fdType
= kSymLinkFileType
;
6196 catRecord
->hfsPlusFile
.userInfo
.fdCreator
= kSymLinkCreator
;
6198 catRecord
->hfsPlusFile
.userInfo
.fdType
= kTextFileType
;
6199 catRecord
->hfsPlusFile
.userInfo
.fdCreator
= kTextFileCreator
;
6201 recordSize
= sizeof(HFSPlusCatalogFile
);
6204 createTime
= GetTimeLocal(true);
6205 catRecord
->hfsFile
.recordType
= kHFSFileRecord
;
6206 catRecord
->hfsFile
.fileID
= fileID
;
6207 catRecord
->hfsFile
.createDate
= createTime
;
6208 catRecord
->hfsFile
.modifyDate
= createTime
;
6209 catRecord
->hfsFile
.userInfo
.fdType
= kTextFileType
;
6210 catRecord
->hfsFile
.userInfo
.fdCreator
= kTextFileCreator
;
6211 recordSize
= sizeof(HFSCatalogFile
);
6216 } /* BuildFileRec */
6218 /* Function: BuildAttributeKey
6220 * Build attribute key based on given information like -
6221 * fileID, startBlock, attribute name and attribute name length.
6223 * Note that the attribute name is the UTF-8 format string.
6225 static void BuildAttributeKey(u_int32_t fileID
, u_int32_t startBlock
,
6226 unsigned char *attrName
, u_int16_t attrNameLen
, HFSPlusAttrKey
*key
)
6228 size_t attrNameLenBytes
;
6230 assert(VolumeObjectIsHFSPlus() == true);
6233 key
->fileID
= fileID
;
6234 key
->startBlock
= startBlock
;
6236 /* Convert UTF-8 attribute name to unicode */
6237 (void) utf_decodestr(attrName
, attrNameLen
, key
->attrName
, &attrNameLenBytes
, sizeof(key
->attrName
));
6238 key
->attrNameLen
= attrNameLenBytes
/ 2;
6240 key
->keyLength
= kHFSPlusAttrKeyMinimumLength
+ attrNameLenBytes
;
6243 /* Delete catalog record and thread record for given ID. On successful
6244 * deletion, this function also updates the valence and folder count for
6245 * the parent directory and the file/folder count in the volume header.
6247 * Returns - zero on success, non-zero on failure.
6249 static int DeleteCatalogRecordByID(SGlobPtr GPtr
, uint32_t id
, Boolean for_rename
)
6257 isHFSPlus
= VolumeObjectIsHFSPlus();
6259 /* Lookup the catalog record to move */
6260 retval
= GetCatalogRecordByID(GPtr
, id
, isHFSPlus
, &key
, &rec
, &recsize
);
6265 /* Delete the record */
6267 retval
= DeleteCatalogNode(GPtr
->calculatedVCB
,
6268 key
.hfsPlus
.parentID
,
6269 (const CatalogName
*)&key
.hfsPlus
.nodeName
,
6270 kNoHint
, for_rename
);
6272 retval
= DeleteCatalogNode(GPtr
->calculatedVCB
,
6274 (const CatalogName
*)&key
.hfs
.nodeName
,
6275 kNoHint
, for_rename
);
6278 /* If deletion of record succeeded, and the operation was not
6279 * being performed for rename, and the volume is HFS+, try
6280 * deleting all extended attributes for this file/folder
6282 if ((retval
== 0) && (for_rename
== false) && (isHFSPlus
== true)) {
6283 /* Delete all attributes associated with this ID */
6284 retval
= DeleteAllAttrsByID(GPtr
, id
);
6290 /* Move a catalog record with given ID to a new parent directory with given
6291 * parentID. This function should only be called for HFS+ volumes.
6292 * This function removes the catalog record from old parent and inserts
6293 * it back with the new parent ID. It also takes care of updating the
6294 * parent directory counts. Note that this function clears kHFSHasLinkChainBit
6295 * from the catalog record flags.
6297 * On success, returns zero. On failure, returns non-zero.
6299 static int MoveCatalogRecordByID(SGlobPtr GPtr
, uint32_t id
, uint32_t new_parentid
)
6306 Boolean isFolder
= false;
6307 BTreeIterator iterator
;
6309 assert (VolumeObjectIsHFSPlus() == true);
6311 /* Lookup the catalog record to move */
6312 retval
= GetCatalogRecordByID(GPtr
, id
, true, &key
, &rec
, &recsize
);
6317 /* Delete the record and its thread from original location.
6318 * For file records, do not deallocate original extents.
6320 retval
= DeleteCatalogRecordByID(GPtr
, id
, true);
6325 key
.hfsPlus
.parentID
= new_parentid
;
6326 /* The record being moved should not have linkChainMask set */
6327 if (rec
.recordType
== kHFSPlusFolderRecord
) {
6328 rec
.hfsPlusFolder
.flags
&= ~kHFSHasLinkChainMask
;
6330 } else if (rec
.recordType
== kHFSPlusFileRecord
) {
6331 rec
.hfsPlusFile
.flags
&= ~kHFSHasLinkChainMask
;
6335 /* Insert the catalog record with new parent */
6336 retval
= InsertBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
, &rec
,
6342 /* Insert the new thread record */
6343 recsize
= BuildThreadRec(&key
, &rec
, true, isFolder
);
6344 BuildCatalogKey(id
, NULL
, true, &key
);
6345 retval
= InsertBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
, &rec
,
6351 /* Update the counts in the new parent directory and volume header */
6352 ClearMemory(&iterator
, sizeof(iterator
));
6353 retval
= GetCatalogRecordByID(GPtr
, new_parentid
, true, &key
, &rec
, &recsize
);
6355 if ((retval
== btNotFound
) && (GPtr
->CBTStat
& S_Orphan
)) {
6356 /* No need for re-repair minor repair order because
6357 * we are failing on updating the parent directory.
6363 if (rec
.recordType
!= kHFSPlusFolderRecord
) {
6367 rec
.hfsPlusFolder
.valence
++;
6368 if ((isFolder
== true) &&
6369 (rec
.hfsPlusFolder
.flags
& kHFSHasFolderCountMask
)) {
6370 rec
.hfsPlusFolder
.folderCount
++;
6373 retval
= ReplaceBTreeRecord(GPtr
->calculatedCatalogFCB
, &key
,
6374 kNoHint
, &rec
, recsize
, &hint
);
6379 if (isFolder
== true) {
6380 GPtr
->calculatedVCB
->vcbFolderCount
++;
6382 GPtr
->calculatedVCB
->vcbFileCount
++;
6384 GPtr
->VIStat
|= S_MDB
;
6385 GPtr
->CBTStat
|= S_BTH
; /* leaf record count changed */
6391 /* The function deletes all extended attributes associated with a given
6392 * file/folder ID. The function takes care of deallocating allocation blocks
6393 * associated with extent based attributes.
6395 * Note: This function deletes *all* attributes for a given file/folder.
6396 * To delete a single attribute record using a key, use delete_attr_record().
6398 * On success, returns zero. On failure, returns non-zero.
6400 static int DeleteAllAttrsByID(SGlobPtr GPtr
, uint32_t id
)
6403 BTreeIterator iterator
;
6404 FSBufferDescriptor btrec
;
6405 HFSPlusAttrKey
*attr_key
;
6406 HFSPlusAttrRecord attr_record
;
6409 /* Initialize the iterator, attribute key, and attribute record */
6410 ClearMemory(&iterator
, sizeof(BTreeIterator
));
6411 attr_key
= (HFSPlusAttrKey
*)&iterator
.key
;
6412 attr_key
->keyLength
= kHFSPlusAttrKeyMinimumLength
;
6413 attr_key
->fileID
= id
;
6415 ClearMemory(&btrec
, sizeof(FSBufferDescriptor
));
6416 btrec
.bufferAddress
= &attr_record
;
6417 btrec
.itemCount
= 1;
6418 btrec
.itemSize
= sizeof(HFSPlusAttrRecord
);
6420 /* Search for attribute with NULL name which will place the
6421 * iterator just before the first record for given id.
6423 retval
= BTSearchRecord(GPtr
->calculatedAttributesFCB
, &iterator
,
6424 kInvalidMRUCacheKey
, &btrec
, &record_size
, &iterator
);
6425 if ((retval
!= 0) && (retval
!= btNotFound
)) {
6429 retval
= BTIterateRecord(GPtr
->calculatedAttributesFCB
, kBTreeNextRecord
,
6430 &iterator
, &btrec
, &record_size
);
6431 while ((retval
== 0) && (attr_key
->fileID
== id
)) {
6432 /* Delete attribute record and deallocate extents, if any */
6433 retval
= delete_attr_record(GPtr
, attr_key
, &attr_record
);
6438 retval
= BTIterateRecord(GPtr
->calculatedAttributesFCB
,
6439 kBTreeNextRecord
, &iterator
, &btrec
, &record_size
);
6442 if (retval
== btNotFound
) {
6450 /* The function deletes an extented attribute record when the corresponding
6451 * record and key are provided. If the record is an extent-based attribute,
6452 * it also takes care to deallocate all allocation blocks associated with
6455 * Note: This function does not delete all attribute records associated
6456 * with the file/folder ID in the attribute key. To delete all attributes
6457 * for given file/folder ID, use DeleteAllAttrsByID().
6459 * On success, returns zero. On failure, returns non-zero.
6461 static int delete_attr_record(SGlobPtr GPtr
, HFSPlusAttrKey
*attr_key
, HFSPlusAttrRecord
*attr_record
)
6464 UInt32 num_blocks_freed
;
6465 Boolean last_extent
;
6467 retval
= DeleteBTreeRecord(GPtr
->calculatedAttributesFCB
, attr_key
);
6469 /* Set bits to write back attribute btree header and map */
6470 GPtr
->ABTStat
|= S_BTH
+ S_BTM
;
6472 if (attr_record
->recordType
== kHFSPlusAttrForkData
) {
6473 retval
= ReleaseExtents(GPtr
->calculatedVCB
,
6474 attr_record
->forkData
.theFork
.extents
,
6475 &num_blocks_freed
, &last_extent
);
6476 } else if (attr_record
->recordType
== kHFSPlusAttrExtents
) {
6477 retval
= ReleaseExtents(GPtr
->calculatedVCB
,
6478 attr_record
->overflowExtents
.extents
,
6479 &num_blocks_freed
, &last_extent
);
6486 /*------------------------------------------------------------------------------
6488 Routine: ZeroFillUnusedNodes
6490 Function: Write zeroes to all unused nodes of a given B-tree.
6492 Input: GPtr - pointer to scavenger global area
6493 fileRefNum - refnum of BTree file
6495 Output: ZeroFillUnusedNodes - function result:
6498 ------------------------------------------------------------------------------*/
6500 static int ZeroFillUnusedNodes(SGlobPtr GPtr
, short fileRefNum
)
6502 BTreeControlBlock
*btcb
= GetBTreeControlBlock(fileRefNum
);
6503 unsigned char *bitmap
= (unsigned char *) ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
;
6504 unsigned char mask
= 0x80;
6507 BlockDescriptor node
;
6511 for (nodeNum
= 0; nodeNum
< btcb
->totalNodes
; ++nodeNum
)
6513 if ((*bitmap
& mask
) == 0)
6515 /* Read the raw node, without going through hfs_swap_BTNode. */
6516 err
= btcb
->getBlockProc(btcb
->fcbPtr
, nodeNum
, kGetBlock
|kGetEmptyBlock
, &node
);
6519 if (debug
) plog("Couldn't read node #%u\n", nodeNum
);
6523 /* Fill the node with zeroes. */
6524 bzero(node
.buffer
, node
.blockSize
);
6526 /* Release and write the node without going through hfs_swap_BTNode. */
6527 (void) btcb
->releaseBlockProc(btcb
->fcbPtr
, &node
, kReleaseBlock
|kMarkBlockDirty
);
6531 /* Move to the next bit in the bitmap. */
6541 } /* end ZeroFillUnusedNodes */